14.3.2. Watchdog¶
Watchdog ฮาร์ดแวร์คือพื้นฐานที่ตัวเลือกการทำให้ระบบแข็งแกร่งอื่น ๆ ทั้งหมดอยู่บน มันคือตัวจับเวลาอิสระขนาดเล็กที่รีเซ็ตโปรเซสเซอร์เมื่อไม่ได้รับการบอกเป็นอย่างอื่นนานเกินไป สคริปต์ที่ติดอยู่กับเซนเซอร์ที่ไม่เสถียร การเรียกเครือข่ายที่บล็อกเกินการหมดเวลา ตัวจัดสรรหน่วยความจำที่ติดอยู่ในมุมของ heap ข้อยกเว้นที่หลุดออกจาก loop ไม่มีสิ่งใดหยุด watchdog ได้ ตัวจับเวลานับถอยหลังไม่ว่ากรณีใด และกล้องก็รีบูต
สำหรับผลิตภัณฑ์ที่จัดส่ง watchdog ไม่ใช่ตัวเลือก หากไม่มีมัน รูปแบบความล้มเหลวใด ๆ ข้างต้นจะทำให้กล้องหยุดทำงานจนกว่าจะมีคนสังเกตเห็นและรีสตาร์ทด้วยการตัดไฟ เมื่อมีมัน กล้องจะฟื้นตัวขึ้นมาเองและหลักฐานเดียวของความล้มเหลวคือหนึ่งบรรทัดในบันทึก
See also
หน้า watchdog timer ของบทฮาร์ดแวร์ครอบคลุมว่า watchdog คืออะไรในระดับฮาร์ดแวร์และพื้นฐานของ API machine.WDT หน้านี้ครอบคลุมสิ่งที่เปลี่ยนแปลงสำหรับการใช้งานในระดับการผลิต
14.3.2.1. การเริ่ม watchdog¶
machine.WDT คือ API มันรองรับฮาร์ดแวร์: เมื่อสร้างแล้ว ตัวจับเวลาจะทำงานจนถึงการรีเซ็ตครั้งถัดไป ไม่มี stop(), ไม่มี deinit(), ไม่มี Ctrl-C หลีกหนี นั่นคือจุดประสงค์
การตั้งค่าทั่วไปที่ด้านบนของ main.py ทันทีก่อน loop ที่มันปกป้อง:
from machine import WDT
wdt = WDT(timeout=10_000) # milliseconds
main.py คือตำแหน่งที่เหมาะสมสำหรับ watchdog เพราะนั่นคือที่ที่ loop อยู่ การรีเซ็ต watchdog คือการรีเซ็ตฮาร์ดแวร์ ดังนั้นเส้นทางบูตเย็นจะรันใหม่และ main.py กลับเข้า loop เองโดยไม่ต้องมีการเชื่อมต่อใน boot.py การเริ่ม watchdog ใน boot.py แทนหมายความว่าทุก soft reset (Ctrl-D ของนักพัฒนาเป็นต้น) มอบตัวจับเวลาฮาร์ดแวร์ที่แอปพลิเคชันไม่มีทางหยุดได้ให้กับแอปพลิเคชัน ซึ่งเป็นสิ่งที่น่ารำคาญบนม้านั่งและเป็นกับดักในโค้ดการตั้งค่าการผลิตที่รันก่อน loop พร้อม
เลือก timeout ให้ยาวกว่าเวลาวนซ้ำที่สังเกตได้แย่ที่สุดของ main loop 2 ถึง 3 เท่า ความสั่นของอัตราเฟรม การอ่านเซนเซอร์ช้าบนเซนเซอร์เย็น การหยุดชะงักของ Wi-Fi สั้น ๆ ไม่มีสิ่งเหล่านี้ควรทริก watchdog การหยุดทำงานจริง (infinite loop, การเรียก I/O ที่บล็อก) ควรทริก timeout ที่สั้นเกินไปทำให้ watchdog กลายเป็นแหล่งของการรีเซ็ตที่ผิดพลาด timeout ที่ยาวเกินไปปล่อยให้กล้องไม่ตอบสนองเป็นนาทีก่อนที่การกู้คืนจะทำงาน
14.3.2.2. การ feed¶
wdt.feed() รีเซ็ตการนับถอยหลัง เรียกใช้หนึ่งครั้งต่อการวนซ้ำของ main loop ที่ ด้านบน ของ loop body เพื่อให้การ feed เกิดขึ้นโดยไม่มีเงื่อนไขก่อนงานใด ๆ ที่อาจหยุดทำงาน:
while True:
wdt.feed()
frame = csi0.snapshot()
process(frame)
14.3.2.3. การรอดจากข้อยกเว้น¶
Watchdog จัดการการหยุดทำงาน ข้อยกเว้นคือรูปแบบความล้มเหลวที่แตกต่างกัน ข้อยกเว้นที่ไม่ได้จัดการจะฟองขึ้นไปถึงระดับบนสุดของสคริปต์ main.py ออก และกล้องลงไปที่ REPL จากนั้น watchdog ทริกหลังจาก timeout เพราะไม่มีอะไร feed มันจาก REPL กล้องรีเซ็ต และ main.py รันอีกครั้ง ดังนั้นการกู้คืนทำงาน แต่ภาคสนามต้องจ่ายค่า timeout เต็มบวกการรีบูตสำหรับทุก crash, traceback ไปที่ USB stdout ที่ไม่มีอะไรอ่าน และสถานะ in-memory ที่แอปพลิเคชันเก็บอยู่ก็หายไป
การห่อ main loop ด้วย try / except ระดับบนสุดเปลี่ยน crash เป็นเหตุการณ์ที่บันทึกไว้ที่แอปพลิเคชันดำเนินการต่อโดยไม่ต้องจ่ายค่าการรีเซ็ต:
import logging
log = logging.getLogger(__name__)
while True:
wdt.feed()
try:
frame = csi0.snapshot()
process(frame)
except Exception:
log.exception("frame loop iteration failed")
การจับ Exception (ไม่ใช่ BaseException) ทำให้ KeyboardInterrupt และ SystemExit ยังคงทำงาน ซึ่งเป็นสิ่งที่นักพัฒนาที่เชื่อมต่อผ่าน USB ต้องการ
รูปแบบนี้คือครึ่งซอฟต์แวร์ของความมีชีวิต: watchdog จับการหยุดทำงาน wrapper จับ crash และบันทึกบันทึกสิ่งที่ทั้งสองจับได้
14.3.2.4. การรู้ว่าทำไมการบูตจึงเกิดขึ้น¶
ทุก soft reset และทุก watchdog reset ในที่สุดจะปรากฏเป็นการบูตใหม่ ตัวช่วย boot-time diagnostics บันทึก machine.reset_cause() ทุกการเริ่มต้นแบบ cold boot บรรทัด reset cause คือสิ่งที่บอกภาคสนามว่าการกู้คืนทำงานจริงหรือกล้องแค่รีสตาร์ทด้วยไฟตามปกติ
บรรทัด reset-cause คือสิ่งที่ทำให้งานของ watchdog มองเห็นได้ในบันทึก บันทึกที่เต็มไปด้วย watchdog timeout resets บอกว่าแอปพลิเคชันกำลังหยุดทำงานและ watchdog กำลังกู้คืนมัน บันทึกที่ไม่มีบอกว่า watchdog ไม่ต้องทำงาน ซึ่งโดยปกติหมายความว่าแอปพลิเคชันมีสุขภาพดี แต่ยังอาจหมายความว่า timeout ตั้งยาวเกินไปจนจับ hang ที่เกิดขึ้นจริงไม่ได้
14.3.2.5. Starter ครบชุด¶
main.py ที่รวม watchdog, การตั้งค่าการบันทึก, boot-time diagnostics และ wrapper เข้าด้วยกันมีลักษณะดังนี้:
import logging
from machine import WDT
from app.logging_setup import setup_logging, log_boot_diagnostics
setup_logging('/sdcard/logs/app.log')
log_boot_diagnostics()
log = logging.getLogger(__name__)
wdt = WDT(timeout=10_000)
while True:
wdt.feed()
try:
step()
except Exception:
log.exception("loop iteration failed")
step() คืองานต่อการวนซ้ำของแอปพลิเคชัน ส่วนที่เหลือของ scaffold นี้ไม่เปลี่ยนแปลงระหว่างผลิตภัณฑ์ การทำให้แข็งแกร่งคือ watchdog หนึ่งตัว wrapper หนึ่งตัว และการบันทึกการบูตทุก cold start ไม่ใช่โค้ดมาก และความแตกต่างระหว่างกล้องที่กู้คืนเองได้และกล้องที่ต้องโทรหาช่างบริการ