time --- دوال متعلقة بالوقت

توفر وحدة time دوالاً للحصول على الوقت والتاريخ الحاليين، وقياس الفترات الزمنية، وللتأخيرات.

حقبة الوقت: تستخدم كاميرات OpenMV Cam المبنية على Alif و i.MX RT حقبة POSIX وهي 1970-01-01 00:00:00 UTC. وتستخدم كاميرات OpenMV Cam المبنية على STM32 حقبة 2000-01-01 00:00:00 UTC. يمكن تحديد سنة الحقبة في وقت التشغيل باستخدام gmtime(0)[0].

الحفاظ على التاريخ/الوقت التقويمي الفعلي: يتطلب هذا ساعة زمن حقيقي (RTC). على OpenMV Cam يُوفَّر وقت النظام بواسطة كائن machine.RTC. يمكن ضبط الوقت التقويمي الحالي باستخدام machine.RTC().datetime(tuple) ويُحافَظ عليه بواسطة إحدى الطرق التالية:

  • بطارية احتياطية (مكوّن اختياري على بعض كاميرات OpenMV Cam).

  • بروتوكول وقت شبكي مثل ntptime (يتطلب اتصالاً بالشبكة).

  • ضبطه يدوياً عند كل تشغيل. يُحافَظ على الـ RTC عادةً عبر عمليات إعادة التعيين البرمجية، لكنه يُفقد عند انقطاع الطاقة ما لم تكن هناك بطارية احتياطية مركّبة.

إذا لم يُحافَظ على الوقت التقويمي، فإن الدوال أدناه التي تشير إلى الوقت المطلق الحالي لن تتصرف كما هو متوقع.

الدوال

time.gmtime(secs: int | None = None) Tuple[int, int, int, int, int, int, int, int]
time.localtime(secs: int | None = None) Tuple[int, int, int, int, int, int, int, int]

يحوّل الوقت secs المعبّر عنه بالثواني منذ الحقبة (انظر أعلاه) إلى صف tuple من 8 عناصر يحتوي على: (year, month, mday, hour, minute, second, weekday, yearday) إذا لم يُوفَّر secs أو كان None، فسيُستخدم الوقت الحالي من الـ RTC.

تعيد دالة gmtime() صف tuple للتاريخ والوقت بالتوقيت العالمي UTC، وتعيد localtime() صف tuple للتاريخ والوقت بالتوقيت المحلي.

صيغة الإدخالات في صف tuple المكوّن من 8 عناصر هي:

  • السنة تشمل القرن (على سبيل المثال 2014).

  • month يكون من 1 إلى 12

  • mday يكون من 1 إلى 31

  • hour يكون من 0 إلى 23

  • minute يكون من 0 إلى 59

  • second يكون من 0 إلى 59

  • weekday يكون من 0 إلى 6 للأيام من الاثنين إلى الأحد

  • yearday يكون من 1 إلى 366

time.mktime(date_time_tuple: Tuple[int, int, int, int, int, int, int, int]) int

هذه هي الدالة العكسية لـ localtime. وسيطها هو صف tuple كامل من 8 عناصر يعبّر عن وقت وفق localtime. وتعيد عدداً صحيحاً هو عدد الثواني منذ حقبة الوقت.

time.sleep(seconds: float) None

ينام لعدد الثواني المعطى. قد يكون seconds رقماً ذا فاصلة عائمة، للنوم لعدد كسري من الثواني. للتأخيرات الأدق أو ذات الأعداد الصحيحة فقط استخدم الدالتين sleep_ms() و sleep_us().

استدعاء sleep()، بما في ذلك sleep(0)، مضمون أن يستدعي دوال رد النداء المعلّقة.

time.sleep_ms(ms: int) None

يؤخّر لعدد معطى من المللي ثانية، ويجب أن يكون موجباً أو 0.

ستؤخّر هذه الدالة لمدة لا تقل عن عدد المللي ثانية المعطى، لكنها قد تستغرق وقتاً أطول إذا توجّب إجراء معالجة أخرى، على سبيل المثال معالِجات المقاطعة أو خيوط أخرى. تمرير 0 لـ ms سيظل يسمح بحدوث هذه المعالجة الأخرى. استخدم sleep_us() للتأخيرات الأدق.

استدعاء sleep_ms()، بما في ذلك sleep_ms(0)، مضمون أن يستدعي دوال رد النداء المعلّقة.

time.sleep_us(us: int) None

يؤخّر لعدد معطى من الميكرو ثانية، ويجب أن يكون موجباً أو 0.

تحاول هذه الدالة توفير تأخير دقيق لا يقل عن us ميكرو ثانية، لكنها قد تستغرق وقتاً أطول إذا كان لدى النظام معالجة أخرى ذات أولوية أعلى لتنفيذها.

time.ticks_ms() int

يعيد عدّاد مللي ثانية متزايداً ذا نقطة مرجعية اعتباطية، يلتفّ بعد قيمة معينة.

لا تُكشف قيمة الالتفاف صراحةً، لكننا سنشير إليها بـ TICKS_MAX لتبسيط النقاش. دورة القيم هي TICKS_PERIOD = TICKS_MAX + 1. ويُضمن أن تكون TICKS_PERIOD قوة للعدد اثنين، لكنها بخلاف ذلك قد تختلف من منفذ إلى آخر. تُستخدم قيمة الدورة نفسها لجميع دوال ticks_ms() و ticks_us() و ticks_cpu() (للتبسيط). وبالتالي، ستعيد هذه الدوال قيمة في النطاق [0 .. TICKS_MAX]، شاملاً الطرفين، بإجمالي TICKS_PERIOD قيمة. لاحظ أن القيم غير السالبة فقط هي المستخدمة. في معظم الأحيان، يجب أن تتعامل مع القيم التي تعيدها هذه الدوال على أنها معتمة. العمليات الوحيدة المتاحة لها هي دالتا ticks_diff() و ticks_add() الموصوفتان أدناه.

ملاحظة: إجراء عمليات رياضية قياسية (+، -) أو عوامل علاقية (<، <=، >، >=) مباشرة على هذه القيم سيؤدي إلى نتيجة غير صالحة. كما أن إجراء عمليات رياضية ثم تمرير نتائجها كوسائط إلى ticks_diff() أو ticks_add() سيؤدي أيضاً إلى نتائج غير صالحة من هاتين الدالتين الأخيرتين.

time.ticks_us() int

تماماً مثل ticks_ms() أعلاه، لكن بالميكرو ثانية.

time.ticks_cpu() int

مشابهة لـ ticks_ms() و ticks_us()، لكن بأعلى دقة ممكنة في النظام. وهذا عادةً ساعات وحدة المعالجة المركزية، ولهذا سُمّيت الدالة بهذه الطريقة. لكن لا يلزم أن تكون ساعة وحدة معالجة مركزية، إذ يمكن استخدام مصدر توقيت آخر متاح في النظام (مثل مؤقت عالي الدقة) بدلاً من ذلك. لا تُحدَّد وحدة التوقيت الدقيقة (الدقة) لهذه الدالة على مستوى وحدة time، لكن قد توفر وثائق منفذ معين معلومات أكثر تحديداً. تُخصَّص هذه الدالة للقياس الدقيق جداً للأداء أو لحلقات الزمن الحقيقي الضيقة جداً. تجنّب استخدامها في الكود المحمول. وهي متوفرة على جميع كاميرات OpenMV Cam.

time.ticks_add(ticks: int, delta: int) int

يزيح قيمة الـ ticks بعدد معطى، يمكن أن يكون موجباً أو سالباً. بمعطى قيمة ticks، تتيح هذه الدالة حساب قيمة ticks بمقدار delta من الـ ticks قبلها أو بعدها، وفق تعريف الحساب النمطي لقيم الـ ticks (انظر ticks_ms() أعلاه). يجب أن يكون المعامل ticks نتيجة مباشرة لاستدعاء دوال ticks_ms() أو ticks_us() أو ticks_cpu() (أو من استدعاء سابق لـ ticks_add()). أما delta فيمكن أن يكون أي عدد صحيح اعتباطي أو تعبيراً عددياً. تفيد ticks_add() في حساب المواعيد النهائية للأحداث/المهام. (ملاحظة: يجب استخدام دالة ticks_diff() للتعامل مع المواعيد النهائية.)

أمثلة:

# Find out what ticks value there was 100ms ago
print(ticks_add(time.ticks_ms(), -100))

# Calculate deadline for operation and test for it
deadline = ticks_add(time.ticks_ms(), 200)
while ticks_diff(deadline, time.ticks_ms()) > 0:
    do_a_little_of_something()

# Find out TICKS_MAX used by this port
print(ticks_add(0, -1))
time.ticks_diff(ticks1: int, ticks2: int) int

تقيس فرق الـ ticks بين القيم المعادة من دوال ticks_ms() أو ticks_us() أو ticks_cpu()، كقيمة ذات إشارة قد تلتفّ.

ترتيب الوسائط هو نفسه عامل الطرح، فـ ticks_diff(ticks1, ticks2) لها نفس معنى ticks1 - ticks2. لكن القيم المعادة من دوال ticks_ms() وما إليها قد تلتفّ، لذا فإن استخدام الطرح مباشرة عليها سينتج نتيجة غير صحيحة. ولهذا السبب تكون ticks_diff() ضرورية، فهي تنفّذ حساباً نمطياً (أو بشكل أكثر تحديداً، حساباً حلقياً) لإنتاج نتيجة صحيحة حتى لقيم الالتفاف (طالما أنها ليست متباعدة جداً فيما بينها، انظر أدناه). تعيد الدالة قيمة ذات إشارة في النطاق [-TICKS_PERIOD/2 .. TICKS_PERIOD/2-1] (وهذا تعريف نطاق نموذجي للأعداد الصحيحة الثنائية ذات الإشارة بنظام المتمم الثنائي). إذا كانت النتيجة سالبة، فهذا يعني أن ticks1 حدثت في وقت أبكر من ticks2. وإلا، فهذا يعني أن ticks1 حدثت بعد ticks2. وهذا يصحّ فقط إذا كانت ticks1 و ticks2 متباعدتين عن بعضهما بما لا يزيد عن TICKS_PERIOD/2-1 من الـ ticks. إذا لم يتحقق ذلك، فستُعاد نتيجة غير صحيحة. وعلى وجه التحديد، إذا كانت قيمتا الـ ticks متباعدتين بمقدار TICKS_PERIOD/2-1 من الـ ticks، فستعيد الدالة تلك القيمة. أما إذا مرّ TICKS_PERIOD/2 من ticks الزمن الحقيقي بينهما، فستعيد الدالة -TICKS_PERIOD/2 بدلاً من ذلك، أي أن قيمة النتيجة ستلتفّ إلى النطاق السالب من القيم الممكنة.

تبرير غير رسمي للقيود أعلاه: افترض أنك محبوس في غرفة دون أي وسيلة لمراقبة مرور الوقت سوى ساعة قياسية ذات 12 علامة. عندئذٍ إذا نظرت إلى قرص الساعة الآن، ولم تنظر مجدداً لمدة 13 ساعة أخرى (مثلاً إذا غططت في نوم طويل)، فعندما تنظر أخيراً مرة أخرى، قد يبدو لك أن ساعة واحدة فقط قد مضت. لتجنب هذا الخطأ، انظر إلى الساعة بانتظام فحسب. ويجب أن يفعل تطبيقك الشيء نفسه. كما أن استعارة "النوم الطويل جداً" تنطبق مباشرة على سلوك التطبيق: لا تدع تطبيقك يشغّل أي مهمة واحدة لفترة طويلة جداً. شغّل المهام على خطوات، واحفظ الوقت بينها.

صُمّمت ticks_diff() لاستيعاب أنماط استخدام متنوعة، من بينها:

  • الاستقصاء مع مهلة. في هذه الحالة، يكون ترتيب الأحداث معروفاً، وستتعامل فقط مع النتائج الموجبة لـ ticks_diff()

    # Wait for GPIO pin to be asserted, but at most 500us
    start = time.ticks_us()
    while pin.value() == 0:
        if time.ticks_diff(time.ticks_us(), start) > 500:
            raise TimeoutError
    
  • جدولة الأحداث. في هذه الحالة، قد تكون نتيجة ticks_diff() سالبة إذا كان الحدث متأخراً:

    # This code snippet is not optimized
    now = time.ticks_ms()
    scheduled_time = task.scheduled_time()
    if ticks_diff(scheduled_time, now) > 0:
        print("Too early, let's nap")
        sleep_ms(ticks_diff(scheduled_time, now))
        task.run()
    elif ticks_diff(scheduled_time, now) == 0:
        print("Right at time!")
        task.run()
    elif ticks_diff(scheduled_time, now) < 0:
        print("Oops, running late, tell task to run faster!")
        task.run(run_faster=true)
    

ملاحظة: لا تمرّر قيم time() إلى ticks_diff()، فيجب عليك استخدام العمليات الرياضية العادية عليها. لكن لاحظ أن time() قد (وسوف) تفيض أيضاً. يُعرف هذا بـ https://en.wikipedia.org/wiki/Year_2038_problem .

time.time() int

يعيد عدد الثواني، كعدد صحيح، منذ الحقبة، بافتراض أن الـ RTC الأساسي مضبوط ومُحافَظ عليه كما هو موصوف أعلاه. إذا لم يكن الـ RTC مضبوطاً، تعيد هذه الدالة عدد الثواني منذ نقطة مرجعية زمنية خاصة بالمنفذ (للوحات المضمّنة بدون RTC مدعوم ببطارية، عادةً منذ التشغيل أو إعادة التعيين). إذا أردت تطوير تطبيق MicroPython محمول، فلا ينبغي أن تعتمد على هذه الدالة لتوفير دقة أعلى من الثانية. إذا كنت تحتاج إلى دقة أعلى وطوابع زمنية مطلقة، استخدم time_ns(). وإذا كانت الأوقات النسبية مقبولة، فاستخدم الدالتين ticks_ms() و ticks_us(). وإذا كنت تحتاج إلى الوقت التقويمي، فإن gmtime() أو localtime() دون وسيط هي خيار أفضل.

الفرق عن CPython

في CPython تعيد هذه الدالة عدد الثواني منذ حقبة Unix (1970-01-01 00:00 UTC) كقيمة ذات فاصلة عائمة، عادةً بدقة الميكرو ثانية. على OpenMV Cam تعيد عدداً صحيحاً بدقة الثانية الواحدة -- إذ لا يمكن للعتاد تمثيل نطاق زمني طويل ودقة أقل من الثانية معاً في رقم عائم -- وتختلف الحقبة حسب اللوحة (انظر حقبة الوقت أعلاه). فبدون RTC مدعوم ببطارية ومضبوط، تعدّ بدلاً من ذلك الثواني منذ التشغيل/إعادة التعيين.

time.time_ns() int

مشابهة لـ time() لكنها تعيد النانو ثانية منذ الحقبة، كعدد صحيح (عادةً عدد صحيح كبير، لذا ستحجز ذاكرة في الكومة).

البنّاءات

class time.clock

تعيد كائن ساعة.

الطرائق

tick() None

تبدأ تتبّع الوقت المنقضي.

fps() float

توقف تتبّع الوقت المنقضي وتعيد معدل الإطارات الحالي FPS (الإطارات في الثانية).

استدعِ tick دائماً أولاً قبل استدعاء هذه الدالة.

avg() float

توقف تتبّع الوقت المنقضي وتعيد متوسط الوقت المنقضي الحالي بالمللي ثانية.

استدعِ tick دائماً أولاً قبل استدعاء هذه الدالة.

reset() None

تعيد ضبط كائن الساعة.