14.3.2. A watchdog#

A hardveres watchdog az az alap, amelyen minden más megerősítési döntés nyugszik. Ez egy apró, független időzítő, amely visszaállítja a processzort, ha túl sokáig nem kap más utasítást. Egy szkript, amely beragad egy megbízhatatlan érzékelőn, egy hálózati hívás, amely túllépi az időkorlátját, egy memóriafoglaló, amely beragad a halom egy sarkába, egy kivétel, amely megszökött a ciklusból – egyik sem állítja meg a watchdogot. Az időzítő ettől függetlenül számol visszafelé, és a kamera újraindul.

Egy leszállított termék esetében a watchdog nem opcionális. Nélküle a fenti meghibásodási módok bármelyike halott állapotban hagyja a kamerát, amíg valaki észre nem veszi és újra nem indítja az áramellátást. Vele a kamera magától visszatér, és a meghibásodás egyetlen bizonyítéka egy sor a naplóban.

Lásd még

A hardver fejezet watchdog időzítő oldala azt tárgyalja, mi a watchdog hardver szinten, és a machine.WDT API alapjait. Ez az oldal azt tárgyalja, mi változik egy gyártási telepítés esetén.

14.3.2.1. A watchdog elindítása#

A machine.WDT az API. Hardveresen támogatott: a létrehozása után az időzítő a következő visszaállításig fut. Nincs stop(), nincs deinit(), nincs Ctrl-C kilépés. Ez a lényeg.

Egy tipikus beállítás a main.py tetején, közvetlenül az általa védett ciklus előtt:

from machine import WDT

wdt = WDT(timeout=10_000)              # milliseconds

A main.py a megfelelő hely a watchdog számára, mert ott él a ciklus. A watchdog visszaállítás hardveres visszaállítás, így a hidegindítási útvonal újrafut, és a main.py magától újra belép a ciklusba – a helyreállítás működik a boot.py bármilyen vezetékezése nélkül. A watchdog elindítása a boot.py-ban ehelyett azt jelenti, hogy minden szoftveres visszaállítás (egy fejlesztő Ctrl-D billentyűkombinációja például) egy hardveres időzítőt ad az alkalmazásnak, amelyet semmilyen módon nem tud leállítani, ami bosszúság a munkapadon és csapda a gyártási beállító kódban, amely a ciklus készenléte előtt fut.

Válassza meg az időkorlátot úgy, hogy 2-3-szor hosszabb legyen, mint a fő ciklus legrosszabb megfigyelt iterációs ideje. A képkockasebesség ingadozása, egy hideg érzékelő lassú olvasása, egy rövid Wi-Fi akadozás – ezek egyike sem szabad, hogy aktiválja a watchdogot. Egy valódi beakadásnak (egy végtelen ciklus, egy blokkolt I/O hívás) viszont kell. A túl rövid időkorlátok a watchdogot téves visszaállítások forrásává teszik; a túl hosszú időkorlátok hagyják, hogy a kamera percekig válasz nélkül üljön, mielőtt a helyreállítás bekapcsol.

14.3.2.2. A watchdog etetése#

A wdt.feed() visszaállítja a visszaszámlálót. Hívja meg egyszer a fő ciklus minden iterációjában, a ciklustörzs tetején, hogy az etetés feltétel nélkül megtörténjen minden olyan munka előtt, amely beakadhat:

while True:
    wdt.feed()
    frame = csi0.snapshot()
    process(frame)

14.3.2.3. Kivételek túlélése#

A watchdog a beakadásokat kezeli. A kivételek más meghibásodási mód. Egy kezeletlen kivétel felbuborékol a szkript legfelső szintjére, a main.py kilép, és a kamera leesik a REPL-be. A watchdog ezután aktiválódik az időkorlátja után, mert semmi sem eteti a REPL-ből, a kamera visszaáll, és a main.py újra fut – tehát a helyreállítás működik, de a terepen minden összeomlásért egy teljes időkorlátot plusz újraindítást fizetnek, a visszakövetés a USB stdout-ra megy, amelyet semmi sem olvas, és minden memóriabeli állapot, amelyet az alkalmazás megtartott, elveszik.

A fő ciklus becsomagolása egy legfelső szintű try / except blokkba egy összeomlást olyan naplózott eseménnyé alakít, amelyen az alkalmazás továbbhalad, anélkül, hogy egy visszaállításért fizetne:

import logging

log = logging.getLogger(__name__)

while True:
    wdt.feed()
    try:
        frame = csi0.snapshot()
        process(frame)
    except Exception:
        log.exception("frame loop iteration failed")

Az Exception (nem a BaseException) elkapása működésben tartja a KeyboardInterrupt és a SystemExit kivételeket, ami az, amit egy USB-n keresztül csatlakozó fejlesztő szeretne.

Ez a minta a folyamatos működés szoftveres fele: a watchdog elkapja a beakadásokat, a csomagoló elkapja az összeomlásokat, és a napló rögzíti, amit bármelyikük elkapott.

14.3.2.4. Annak ismerete, miért történt egy indítás#

Minden szoftveres visszaállítás és minden watchdog visszaállítás végül friss indításként jelenik meg. Az indításidejű diagnosztikai segédprogram minden hidegindításkor naplózza a machine.reset_cause() értékét; a reset cause sor az, ami megmondja a terepen, hogy a helyreállítás valóban bekapcsolt-e, szemben azzal, hogy a kamera csak normálisan újraindult az áramellátással.

A reset-cause sor az, ami láthatóvá teszi a watchdog munkáját a naplóban. Egy watchdog timeout visszaállításokkal teli napló azt mondja, hogy az alkalmazás beakadt, és a watchdog helyreállította azt. Egy ezek nélküli napló azt mondja, hogy a watchdognak nem kellett aktiválódnia – ami általában azt jelenti, hogy az alkalmazás egészséges, de azt is jelentheti, hogy az időkorlát túl hosszúra van állítva ahhoz, hogy elkapja a ténylegesen előforduló beakadásokat.

14.3.2.5. Egy teljes kezdő váz#

Egy main.py, amely összefogja a watchdogot, a naplózás beállítását, az indításidejű diagnosztikát és a csomagolót, így néz ki:

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")

A step() az alkalmazás iterációnkénti munkája; a váz többi része nem változik a termékek között. A megerősítés egy watchdog, egy csomagoló és egy naplózott indítás minden hidegindításkor – nem sok kód, és ez a különbség egy magától helyreálló kamera és egy szervizhívást igénylő kamera között.