14.2.2.1. Zmrazení skriptů do firmwaru

Zmrazený (frozen) modul je soubor .py zkompilovaný do bytekódu a slinkovaný do obrazu firmwaru během sestavení. Běhové prostředí importuje zmrazený modul přímo z flash paměti, aniž by se kdy podívalo na souborový systém na disku. U dodávaného produktu je toto správné místo pro aplikační kód: koncový uživatel nemá co smazat, žádný zastaralý .py na SD kartě jej nemůže přepsat a kamera při každém spuštění spouští stejný kód bez ohledu na to, co (pokud vůbec něco) je na jejích discích.

Tato stránka popisuje spouštěcí sekvenci, kterou kamera prochází, a poté to, jak manifest.py a direktiva freeze zapečou aplikaci do sestavení.

14.2.2.1.1. Spouštěcí sekvence

Co se spouští a kdy na kameře vycházející z resetu:

  • Bootloader. Po zapnutí se vstoupí do krátkého okna DFU, které IDE používá k nahrávání aktualizací firmwaru. Okno se po několika sekundách zavře a bootloader předá řízení MicroPythonu. Běžící skript může do tohoto okna na vyžádání znovu vstoupit voláním machine.bootloader().

  • Inicializace zmrazeného souborového systému. Před spuštěním jakéhokoli aplikačního kódu běhové prostředí spustí souborové systémy. Interní flash paměť je připojena na /flash (a naformátována načisto, pokud tam nic není). Pokud je přítomna SD karta a na interní flash paměti neexistuje značkovací soubor s názvem SKIPSD, je SD karta připojena na /sdcard. ROMFS, pokud jej sestavení obsahuje, je automaticky připojen na /rom. Pracovní adresář je nastaven na spouštěcí adresář (/sdcard, pokud byla karta připojena, jinak /flash) a sys.path je naplněna hodnotami /flash, /flash/lib, /sdcard, /sdcard/lib, /rom a /rom/lib. Nastavení rezidentní ve flash paměti je obstaráno zmrazeným modulem s názvem _boot.py – jde o infrastrukturu portu a desky, nikoli o aplikační háček. Aplikace _boot.py nepřizpůsobují; dělá to sestavení. Vložení souboru SKIPSD na flash paměť z IDE je podporovaný způsob, jak přimět kameru spustit se z interní flash paměti namísto z SD karty.

  • Nastavení před REPL. boot.py se spustí při každém měkkém resetu – studeném startu, Ctrl-D z REPL, návratu běžícího skriptu i obnovení po watchdogu – předtím, než se REPL stane dostupným. Jeho úkolem je připravit prostředí, ve kterém běží zbytek systému: takový druh nastavení, které REPL, aplikace i jakékoli nástroje pro obnovu potřebují mít na svém místě, aby fungovaly. Není to místo, kde sídlí samotná aplikace. main.py je vstupní bod aplikace.

  • Hlavní smyčka. main.py je hlavní smyčka aplikace. Spustí se jednou při studeném startu, ihned po boot.py. Nespouští se znovu při následujících měkkých resetech – kamera místo toho přejde do REPL. Tato asymetrie je důležitá pro vývoj (Ctrl-D přejde do REPL bez opětovného spuštění smyčky, takže vývojář může zkontrolovat stav), ale nikoli pro produkci: nasazená kamera vidí zapnutí napájení, watchdog a tvrdé resety, což jsou vesměs hardwarové resety, které znovu vstupují do cesty studeného startu a opět spouštějí main.py.

14.2.2.1.2. Zmrazení do firmwaru

Sada zmrazených modulů desky je deklarována v souboru boards/<TARGET>/manifest.py ve stromu firmwaru. Manifest je malý soubor v Pythonu, který volá hrstku direktiv:

  • freeze("$(OMV_LIB_DIR)/", "foo.py") – zapeče jediný foo.py do sestavení.

  • package("mylib", base_path="...") – zapeče vícesouborový Python balíček a zachová jeho adresářové rozložení pod zadanou základní cestou.

  • include("...") – vtáhne jiný soubor manifestu. Manifesty desek to používají ke sdílení společných sad modulů.

  • require("logging") – vtáhne pojmenovaný upstreamový modul micropython-lib podle názvu.

Minimální aplikační manifest přidává jeden řádek freeze na každý skript nejvyšší úrovně a jeden řádek package na každý balíček, na kterém aplikace závisí.

14.2.2.1.2.1. Kde se nachází zdrojový kód

Zdrojový kód aplikace se nachází v scripts/libraries/ ve stromu firmwaru, vedle modulů, které sestavení již zmrazuje. Proměnná manifestu $(OMV_LIB_DIR) se rozvine na tuto cestu, takže záznamy v manifestu zůstávají krátké. Úprava manifestu je již operací uvnitř stromu, takže ponechání zdrojového kódu uvnitř stromu vás zbaví žonglování se samostatným repozitářem projektu při řešení cest.

Typické rozložení pro aplikaci, která dodává jediný main.py plus podpůrný balíček:

scripts/libraries/
    main.py
    my_lib/
        __init__.py
        helpers.py

A v souboru boards/<TARGET>/manifest.py desky jeden řádek freeze pro skript a jeden řádek package pro balíček:

freeze("$(OMV_LIB_DIR)/", "main.py")
package("my_lib", base_path="$(OMV_LIB_DIR)/my_lib")

Jednosouborové skripty – zde main.py, ale stejné pravidlo platí pro boot.py nebo jakýkoli samostatný pomocný soubor – používají freeze. Vícesouborové balíčky používají package. Přidání dalšího skriptu znamená jeden řádek freeze navíc; přidání dalšího balíčku znamená jeden řádek package navíc.

14.2.2.1.2.2. Sestavení a flashování

Jakmile je manifest na svém místě, sestavte firmware přesně tak, jak popisuje kapitola o firmwaru

make -j$(nproc) -C lib/micropython/mpy-cross   # once, builds the cross-compiler
make -j$(nproc) TARGET=<TARGET>                # builds the firmware

Výstup přistane v build/<TARGET>/bin/

build/<TARGET>/bin/
    firmware.bin     # flash through the IDE
    romfs0.img       # flash through the IDE in a separate step

Flashování .bin a .img přes IDE vytvoří kameru, jejíž aplikace je součástí sestavení.

Výše uvedená spouštěcí sekvence je tím, co činí zapečení účinným: běhové prostředí překládá boot.py a main.py na zmrazené kopie ještě předtím, než vůbec zkontroluje souborový systém, takže dodaná kamera spouští kód ze sestavení, i když SD karta obsahuje zastaralý boot.py ponechaný z vývoje.

14.2.2.1.2.3. Pořadí vyhledávání

Sémantika přepisování je odlišná pro cestu spouštění boot.py / main.py a pro běžné příkazy import. Vědět, co je co, je důležité pro produkci i pro vývoj:

  • Pro boot.py a main.py: běhové prostředí nejprve hledá zmrazenou kopii a teprve poté souborový systém. Zmrazený boot.py nelze přepsat vložením jiného na SD kartu – kdokoli kameru drží v ruce, nemůže změnit vstupní bod bez opětovného flashování.

  • Pro import foo: běhové prostředí nejprve prohledá sys.path – která pokrývá /flash, /sdcard, /rom a jejich podadresáře lib – a poté zmrazené moduly. Stejnojmenný foo.py na flash paměti nebo SD kartě zmrazený foo skutečně přepíše. To je vývojová výhoda: vložte opravený modul na kartu, proveďte měkký reset a uvidíte změnu bez opětovného flashování.

Dodávaný produkt, který chce u importů potlačit chování, kdy souborový systém přepisuje zmrazené moduly, může vyčistit sys.path na začátku boot.py

import sys

sys.path.clear()

Když je sys.path prázdná, všechny importy se rozpoznávají pouze ze zmrazených modulů; nic na flash paměti, SD kartě ani ROMFS je nemůže zastínit.

14.2.2.1.2.4. Problém s aktivy

Zmrazení je skvělé pro kód. Není skvělé pro velká binární aktiva: soubory modelů strojového učení, tabulky štítků, konfiguraci JSON, šablony obrazů. Vložení těchto dat jako literálů v Pythonu nafoukne zdrojový kód, zpomalí rekompilaci a zbytečně plýtvá kontejnerem bytekódu na data, která interpret stejně jen přečte v surové podobě. Stránka Sestavení obrazu ROMFS popisuje souborový systém ve flash paměti určený jen pro čtení, který tuto mezeru vyplňuje.