14.3.2. Le chien de garde¶
Le chien de garde matériel est la base sur laquelle repose tout autre choix de durcissement. C’est un minuteur indépendant minuscule qui réinitialise le processeur lorsqu’on ne lui a rien signalé pendant trop longtemps. Un script qui se bloque sur un capteur instable, un appel réseau qui dépasse son délai d’attente, un allocateur de mémoire coincé dans un recoin du tas, une exception qui a échappé à la boucle – aucun d’eux n’arrête le chien de garde. Le minuteur décompte malgré tout, et la caméra redémarre.
Pour un produit commercialisé, un chien de garde n’est pas facultatif. Sans lui, n’importe lequel des modes de défaillance ci-dessus laisse la caméra morte jusqu’à ce que quelqu’un s’en aperçoive et coupe puis rétablisse l’alimentation. Avec lui, la caméra se relance d’elle-même et la seule trace de la défaillance est une ligne dans le journal.
Voir aussi
La page minuteur du chien de garde du chapitre matériel explique ce qu’est un chien de garde au niveau matériel et les bases de l’API machine.WDT. Cette page traite de ce qui change pour un déploiement en production.
14.3.2.1. Démarrer le chien de garde¶
machine.WDT est l’API. Elle est adossée au matériel : une fois construit, le minuteur tourne jusqu’à la prochaine réinitialisation. Il n’y a pas de stop(), pas de deinit(), pas d’échappatoire par Ctrl-C. C’est précisément le but.
Une configuration typique en tête de main.py, juste avant la boucle qu’elle protège
from machine import WDT
wdt = WDT(timeout=10_000) # milliseconds
main.py est le bon endroit pour le chien de garde car c’est là que se trouve la boucle. Une réinitialisation par le chien de garde est une réinitialisation matérielle, donc le chemin de démarrage à froid se ré-exécute et main.py ré-entre dans la boucle de lui-même – la récupération fonctionne sans aucun câblage dans boot.py. Démarrer le chien de garde dans boot.py signifie en revanche que chaque réinitialisation logicielle (un Ctrl-D de développeur, par exemple) confie à l’application un minuteur matériel qu’elle n’a aucun moyen d’arrêter, ce qui est une nuisance sur l’établi et un piège dans le code de configuration de production qui s’exécute avant que la boucle ne soit prête.
Choisissez un délai d’attente de 2 à 3 fois plus long que le pire temps d’itération observé de la boucle principale. La gigue de la cadence d’images, une lecture lente sur un capteur froid, un bref hoquet du Wi-Fi – rien de tout cela ne devrait déclencher le chien de garde. Un vrai blocage (une boucle infinie, un appel d’E/S bloqué) le devrait. Des délais trop courts transforment le chien de garde en source de fausses réinitialisations ; des délais trop longs laissent la caméra inactive pendant des minutes avant que la récupération ne se déclenche.
14.3.2.2. L’alimenter¶
wdt.feed() réinitialise le décompte. Appelez-le une fois par itération de la boucle principale, en tête du corps de la boucle afin que l’alimentation se produise inconditionnellement avant tout travail susceptible de se bloquer
while True:
wdt.feed()
frame = csi0.snapshot()
process(frame)
14.3.2.3. Survivre aux exceptions¶
Le chien de garde gère les blocages. Les exceptions sont un mode de défaillance différent. Une exception non gérée remonte jusqu’au niveau supérieur du script, main.py se termine, et la caméra retombe sur le REPL. Le chien de garde se déclenche alors après son délai d’attente car plus rien ne l’alimente depuis le REPL, la caméra se réinitialise et main.py s’exécute à nouveau – la récupération fonctionne donc bien, mais le terrain paie un délai d’attente complet plus un redémarrage pour chaque plantage, la trace d’appel part vers la sortie standard USB que personne ne lit, et tout état en mémoire que l’application conservait est perdu.
Envelopper la boucle principale dans un try / except de niveau supérieur transforme un plantage en un événement journalisé que l’application traverse sans payer de réinitialisation
import logging
log = logging.getLogger(__name__)
while True:
wdt.feed()
try:
frame = csi0.snapshot()
process(frame)
except Exception:
log.exception("frame loop iteration failed")
Capturer Exception (et non BaseException) maintient le fonctionnement de KeyboardInterrupt et de SystemExit, ce qui est ce que veut un développeur connecté via USB.
Ce modèle constitue la moitié logicielle de la disponibilité : le chien de garde capture les blocages, l’enveloppe capture les plantages, et le journal enregistre ce que l’un ou l’autre a capturé.
14.3.2.4. Savoir pourquoi un démarrage a eu lieu¶
Chaque réinitialisation logicielle et chaque réinitialisation par le chien de garde finit par apparaître comme un nouveau démarrage. L’utilitaire de diagnostic au démarrage journalise machine.reset_cause() à chaque démarrage à froid ; la ligne reset cause est ce qui indique au terrain si la récupération s’est réellement déclenchée ou si la caméra a simplement subi une coupure-rétablissement d’alimentation normale.
La ligne de cause de réinitialisation est ce qui rend visible dans le journal le travail du chien de garde. Un journal rempli de réinitialisations watchdog timeout indique que l’application se bloque et que le chien de garde la récupère. Un journal sans ces lignes indique que le chien de garde n’a pas eu à se déclencher – ce qui signifie généralement que l’application est saine, mais peut aussi signifier que le délai d’attente est réglé trop long pour capturer les blocages qui se produisent réellement.
14.3.2.5. Un point de départ complet¶
Un main.py qui rassemble chien de garde, configuration de la journalisation, diagnostics au démarrage et enveloppe ressemble à ceci
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() est le travail par itération de l’application ; le reste de cet échafaudage ne change pas d’un produit à l’autre. Le durcissement, c’est un chien de garde, une enveloppe et un démarrage journalisé à chaque démarrage à froid – pas grand-chose comme code, et toute la différence entre une caméra qui se rétablit d’elle-même et une qui nécessite une intervention de maintenance.