14.3.2. Vakthunden¶
Hårdvaruvakthunden är golvet som alla andra härdningsval vilar på. Det är en liten oberoende timer som återställer processorn när den inte fått besked om något annat under för lång tid. Ett skript som fastnar på en opålitlig sensor, ett nätverksanrop som blockerar förbi sin timeout, en minnesallokerare fast i ett hörn av högen, ett undantag som slank ut ur loopen – ingen av dem stoppar vakthunden. Timern räknar ner oavsett, och kameran startar om.
För en levererad produkt är en vakthund inte valfri. Utan den lämnar något av felscenarierna ovan kameran död tills någon märker det och slår av och på strömmen. Med den kommer kameran tillbaka av sig själv och det enda beviset på felet är en rad i loggen.
Se även
Sidan om watchdog-timer i hårdvarukapitlet täcker vad en vakthund är på hårdvarunivå och grunderna i machine.WDT-API:et. Den här sidan täcker vad som ändras för en produktionsdistribution.
14.3.2.1. Starta vakthunden¶
machine.WDT är API:et. Det är hårdvarubaserat: när det väl konstruerats körs timern till nästa återställning. Det finns ingen stop(), ingen deinit(), ingen Ctrl-C-utväg. Det är hela poängen.
En typisk konfiguration högst upp i main.py, omedelbart före loopen den skyddar:
from machine import WDT
wdt = WDT(timeout=10_000) # milliseconds
main.py är rätt hemvist för vakthunden eftersom det är där loopen bor. En vakthundsåterställning är en hårdvaruåterställning, så kallstartsvägen körs på nytt och main.py går in i loopen igen på egen hand – återhämtning fungerar utan någon koppling i boot.py. Att i stället starta vakthunden i boot.py innebär att varje mjuk återställning (en utvecklares Ctrl-D, till exempel) lämnar applikationen en hårdvarutimer som den inte har något sätt att stoppa, vilket är en irritation vid arbetsbänken och en fälla i produktionsuppsättningskod som körs innan loopen är redo.
Välj timeouten så att den är 2 till 3 gånger längre än den värsta observerade iterationstiden för huvudloopen. Jitter i bildhastigheten, en långsam sensorläsning på en kall sensor, en kort Wi-Fi-hicka – inget av det bör utlösa vakthunden. En verklig låsning (en oändlig loop, ett blockerat I/O-anrop) bör göra det. För korta timeouts gör vakthunden till en källa till falska återställningar; för långa timeouts låter kameran sitta oresponsiv i flera minuter innan återhämtningen utlöses.
14.3.2.2. Att mata den¶
wdt.feed() återställer nedräkningen. Anropa den en gång per iteration av huvudloopen, högst upp i loopkroppen så att matningen sker villkorslöst före något arbete som kan låsa sig:
while True:
wdt.feed()
frame = csi0.snapshot()
process(frame)
14.3.2.3. Att överleva undantag¶
Vakthunden hanterar låsningar. Undantag är ett annat felscenario. Ett ohanterat undantag bubblar upp till skriptets toppnivå, main.py avslutas, och kameran faller ner till REPL:en. Vakthunden utlöses sedan efter sin timeout eftersom ingenting matar den från REPL:en, kameran återställs, och main.py körs igen – så återhämtning fungerar visserligen, men fältet betalar en hel timeout plus omstart för varje krasch, spårningen går till USB-stdout som ingenting läser, och allt minnesresident tillstånd som applikationen höll är borta.
Att linda in huvudloopen i en try / except på toppnivå förvandlar en krasch till en loggad händelse som applikationen fortsätter förbi, utan att betala för en återställning:
import logging
log = logging.getLogger(__name__)
while True:
wdt.feed()
try:
frame = csi0.snapshot()
process(frame)
except Exception:
log.exception("frame loop iteration failed")
Att fånga Exception (inte BaseException) håller KeyboardInterrupt och SystemExit fungerande, vilket är vad en utvecklare ansluten över USB vill ha.
Det här mönstret är den mjukvarumässiga halvan av livskraft: vakthunden fångar låsningarna, omslaget fångar krascherna, och loggen registrerar vad någondera av dem fångade.
14.3.2.4. Att veta varför en uppstart skedde¶
Varje mjuk återställning och varje vakthundsåterställning visar sig så småningom som en ny uppstart. Hjälparen för diagnostik vid uppstart loggar machine.reset_cause() vid varje kallstart; raden reset cause är det som talar om för fältet huruvida återhämtningen faktiskt utlöstes kontra att kameran bara slogs av och på normalt.
Raden med återställningsorsak är det som gör vakthundens arbete synligt i loggen. En logg full av watchdog timeout-återställningar säger att applikationen har låst sig och att vakthunden har återställt den. En logg utan dem säger att vakthunden inte har behövt utlösas – vilket vanligtvis betyder att applikationen är frisk, men det kan också betyda att timeouten är satt för lång för att fånga de låsningar som faktiskt inträffar.
14.3.2.5. En komplett startmall¶
En main.py som drar samman vakthund, loggningskonfiguration, diagnostik vid uppstart och omslaget ser ut så här:
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() är applikationens arbete per iteration; resten av denna byggnadsställning ändras inte mellan produkter. Härdning är en vakthund, ett omslag och en loggad uppstart vid varje kallstart – inte särskilt mycket kod, och skillnaden mellan en kamera som återhämtar sig på egen hand och en som behöver ett servicebesök.