3.27. مؤقت الحارس (Watchdog)

مؤقت الحارس (watchdog timer) قطعة عتاد تُعيد تعيين المتحكم الدقيق إذا توقف البرنامج النصي الجاري عن مناغشته بشكل دوري. يُطعِم البرنامج النصي الحارس من مكان يعلم أنه يشغّل شِفرة سليمة؛ فإذا منع خللٌ أو تعليقٌ أو استثناء غير متوقع الكاميرا من إطعام الحارس ضمن مهلة مُهيّأة، تُعيد الشريحة تعيين نفسها ويبدأ البرنامج النصي من جديد.

على جهاز منشور لا يوجد بشر قربه لإجراء دورة طاقة له، يكون هذا هو الفارق بين خلل عابر يتعافى خلال ثوانٍ وبين جهاز معطّل يحتاج إلى زيارة خدمة.

A graph of a watchdog counter over time. The counter starts at the timeout value, drops linearly toward zero, and is reloaded to the timeout each time the script calls feed(). After three successful feed() calls, a fourth interval has no feed() and the counter reaches zero, triggering an MCU reset.

يَعُدّ عدّاد الحارس تنازليًا من قيمة مهلته. كل feed() يُعيد تحميله؛ وإذا وصل إلى الصفر، تُعيد الشريحة تعيين نفسها.

3.27.1. الفئة machine.WDT

تُمكّن machine.WDT الحارس وتُتيح تابعًا واحدًا، هو feed(). وبمجرد بدء تشغيل الحارس لا يمكن إيقافه -- والمخرجان الوحيدان هما إطعامه في موعده أو تركه يُعيد تعيين الشريحة:

from machine import WDT

wdt = WDT(timeout=2000)    # reset if not fed within 2 seconds

while True:
    do_work()
    wdt.feed()

قيمة timeout بالميلي ثانية. وتعتمد القيمة الصحيحة على المدة التي تستغرقها أطول تكرارة مشروعة للحلقة الرئيسية، مع هامش مريح -- فحلقة مدتها 100 ميلي ثانية بمهلة قدرها ثانيتان تملك هامشًا وفيرًا لتكرارة بطيئة دون إعادات تعيين مزعجة.

3.27.2. أين تُستدعى feed()

موضع feed() هو القرار التصميمي الحاسم؛ فالحارس لا يلتقط إلا الأخطاء في أجزاء الشِفرة التي لا تعمل بين الإطعامات.

  • استدعها من الحلقة الرئيسية، في أعلاها أو أسفلها. هذا هو النمط الأكثر شيوعًا. يلتقط الحارس أي شيء يُعلّق الحلقة الرئيسية -- جمود، أو while لا نهائي، أو طرفية لا تعود أبدًا -- ويُعيد تعيين الشريحة لتعود إلى الحلقة.

  • لا تستدعها من معالج مقاطعة. الغاية من الحارس هي التقاط حالات التعليق في مسار الشِفرة الطبيعي. فمعالج مقاطعة (ISR) يُطلَق بغض النظر عمّا إذا كانت الحلقة الرئيسية عالقة سيظل يُطعم حارسًا كان ينبغي أن يُطلَق.

  • لا تستدعها من داخل عملية حجب طويلة. طلب شبكي أو قراءة مستشعر تستغرق عشر ثوانٍ هما تحديدًا نوع التعليق الذي ينبغي للحارس التقاطه. ووضع feed() بداخلها يُبطل الحماية.

ثمة إرشاد يصلح لمعظم البرامج: أطعِم مرة واحدة في كل تكرارة للحلقة الرئيسية، مع ضبط المهلة على أضعاف مدة الحلقة المتوقعة. فإذا احتاجت تكرارة واحدة بصورة مشروعة إلى وقت أطول من المهلة -- مرحلة معايرة متعمدة مثلًا -- فهيكل تلك المرحلة كسلسلة من القطع الأصغر مع feed() بينها، أو غيّر المهلة باستخدام timeout_ms() (حيثما كان مدعومًا) قبل الدخول إليها.

3.27.3. التوفّر

الحارس متاح على معظم كاميرات OpenMV لكن ليس كلها -- فالعتاد موجود في كل جزء، لكن واجهة Python البرمجية لم تُوصَّل بعد في كل مكان. تحقق من لوحات OpenMV أو حاول بناء نسخة من WDT والتقاط AttributeError إن لم يكن مدعومًا.

حتى على الكاميرات التي لا تُتاح فيها WDT، يمكن لجهاز منشور ميدانيًا استخدام مكافئ برمجي -- مهمة منفصلة أو خطوة في الحلقة الرئيسية تراقب التقدّم وتُطلِق machine.reset() إذا بدا أي شيء عالقًا. وهو أقل متانة من الحارس العتادي (فمعالج مقاطعة عالق قد يُسقط المراقب البرمجي أيضًا) لكنه يغطي الحالات نفسها على مستوى التطبيق.