14.3.2. Vahtikoira

Laitteistovahtikoira on lattia, jonka päällä jokainen muu kovennusvalinta lepää. Se on pieni itsenäinen ajastin, joka nollaa prosessorin, kun sitä ei ole muutoin käsketty liian pitkään aikaan. Skripti, joka jumiutuu epävakaaseen sensoriin, verkkokutsu, joka jää jumiin aikakatkaisunsa yli, muistinvaraaja, joka juuttuu keon nurkkaan, poikkeus, joka pakeni silmukasta – mikään niistä ei pysäytä vahtikoiraa. Ajastin laskee alaspäin siitä huolimatta, ja kamera käynnistyy uudelleen.

Toimitetulle tuotteelle vahtikoira ei ole valinnainen. Ilman sitä mikä tahansa yllä olevista vikatiloista jättää kameran kuolleeksi, kunnes joku huomaa ja käy virtakierron. Sen kanssa kamera palaa itsestään takaisin toimintaan, ja ainoa todiste viasta on yksi rivi lokissa.

Katso myös

Laitteistoluvun vahtikoira-ajastin -sivu käsittelee, mikä vahtikoira on laitteistotasolla, sekä machine.WDT -API:n perusteet. Tämä sivu käsittelee, mikä muuttuu tuotantokäyttöönotossa.

14.3.2.1. Vahtikoiran käynnistäminen

machine.WDT on API. Se on laitteistotuettu: kerran luotuna ajastin käy seuraavaan nollaukseen asti. Ei ole stop()-, deinit()- eikä Ctrl-C-pakokeinoa. Se on koko pointti.

Tyypillinen asetus main.py-tiedoston alussa, juuri ennen suojattavaa silmukkaa:

from machine import WDT

wdt = WDT(timeout=10_000)              # milliseconds

main.py on oikea koti vahtikoiralle, koska siellä silmukka sijaitsee. Vahtikoiran nollaus on laitteistonollaus, joten kylmäkäynnistyspolku ajetaan uudelleen ja main.py palaa silmukkaan itsestään – palautuminen toimii ilman mitään kytkentää boot.py-tiedostossa. Vahtikoiran käynnistäminen boot.py-tiedostossa sen sijaan tarkoittaa, että jokainen pehmeä nollaus (kehittäjän Ctrl-D esimerkiksi) antaa sovellukselle laitteistoajastimen, jota se ei voi mitenkään pysäyttää, mikä on harmi työpöydällä ja ansa tuotannon asetuskoodissa, joka ajetaan ennen kuin silmukka on valmis.

Valitse aikakatkaisu 2-3 kertaa pidemmäksi kuin pääsilmukan pahin havaittu iteraatioaika. Kehysnopeuden värinä, hidas sensorin luku kylmästä sensorista, lyhyt Wi-Fi-katkos – minkään niistä ei pitäisi laukaista vahtikoiraa. Aidon jumin (ikuinen silmukka, jumiutunut I/O-kutsu) pitäisi. Liian lyhyet aikakatkaisut muuttavat vahtikoiran väärien nollausten lähteeksi; liian pitkät aikakatkaisut antavat kameran istua reagoimattomana minuutteja ennen kuin palautuminen laukeaa.

14.3.2.2. Sen syöttäminen

wdt.feed() nollaa laskennan. Kutsu sitä kerran pääsilmukan iteraatiota kohti, silmukan rungon yläosassa, jotta syöttö tapahtuu ehdottomasti ennen mitään työtä, joka saattaa jumiutua:

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

14.3.2.3. Poikkeuksista selviäminen

Vahtikoira hoitaa jumit. Poikkeukset ovat eri vikatila. Käsittelemätön poikkeus kuplii ylös skriptin ylätasolle, main.py päättyy ja kamera putoaa REPL:iin. Vahtikoira laukeaa sitten aikakatkaisunsa jälkeen, koska mikään ei syötä sitä REPL:stä, kamera nollautuu ja main.py ajetaan uudelleen – joten palautuminen kyllä toimii, mutta kenttä maksaa täyden aikakatkaisun ja uudelleenkäynnistyksen jokaisesta kaatumisesta, jäljitys menee USB:n stdoutiin, jota mikään ei lue, ja kaikki muistinvarainen tila, jota sovellus piti, on poissa.

Pääsilmukan käärimisen ylätason try / except -lohkoon muuttaa kaatumisen lokitetuksi tapahtumaksi, jonka läpi sovellus jatkaa, maksamatta nollauksesta:

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-poikkeuksen (ei BaseException) nappaaminen pitää KeyboardInterrupt- ja SystemExit -poikkeukset toimivina, mikä on sitä, mitä USB:n yli kytketty kehittäjä haluaa.

Tämä malli on elossapidon ohjelmistopuolisko: vahtikoira nappaa jumit, kääre nappaa kaatumiset, ja loki kirjaa, mitä kumpi tahansa niistä nappasi.

14.3.2.4. Käynnistyksen syyn tietäminen

Jokainen pehmeä nollaus ja jokainen vahtikoiran nollaus näkyy lopulta tuoreena käynnistyksenä. Käynnistysaikainen diagnostiikka-apuri lokittaa machine.reset_cause() jokaisella kylmäkäynnistyksellä; reset cause -rivi on se, joka kertoo kentälle, laukesiko palautuminen todella vai kävikö kamera vain normaalin virtakierron.

Reset-cause-rivi on se, joka tekee vahtikoiran työn näkyväksi lokissa. Loki, joka on täynnä watchdog timeout -nollauksia, kertoo, että sovellus on jumiutunut ja vahtikoira on palauttanut sen. Loki ilman niitä kertoo, ettei vahtikoiran ole tarvinnut laueta – mikä yleensä tarkoittaa, että sovellus on terve, mutta voi myös tarkoittaa, että aikakatkaisu on asetettu liian pitkäksi nappaamaan jumit, joita todella tapahtuu.

14.3.2.5. Täydellinen aloitus

main.py, joka kokoaa vahtikoiran, lokituksen asetukset, käynnistysaikaisen diagnostiikan ja kääreen yhteen, näyttää tältä:

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() on sovelluksen iteraatiokohtainen työ; loput tästä tukirakenteesta ei muutu tuotteiden välillä. Kovennus on yksi vahtikoira, yksi kääre ja lokitettu käynnistys jokaisella kylmäkäynnistyksellä – ei paljon koodia, ja ero kameran, joka palautuu itsestään, ja sellaisen, joka tarvitsee huoltokäynnin, välillä.