14.2.2.1. Skriptien jäädyttäminen laiteohjelmistoon

Jäädytetty moduuli on .py-tiedosto, joka on käännetty tavukoodiksi ja linkitetty laiteohjelmiston levykuvaan käännösaikana. Suoritusympäristö tuo jäädytetyn moduulin suoraan flash-muistista, koskematta lainkaan levyllä olevaan tiedostojärjestelmään. Toimitettavan tuotteen sovelluskoodille tämä on oikea paikka: loppukäyttäjällä ei ole mitään poistettavaa, SD-kortilla oleva vanhentunut .py ei voi ohittaa mitään, ja kamera suorittaa joka käynnistyksellä samaa koodia riippumatta siitä, mitä (jos mitään) sen levyillä on.

Tämä sivu käsittelee kameran noudattaman käynnistyssekvenssin ja sen jälkeen, miten manifest.py ja freeze-direktiivi leipovat sovelluksen käännökseen.

14.2.2.1.1. Käynnistyssekvenssi

Mitä suoritetaan ja milloin, kun kamera tulee ulos nollauksesta:

  • Käynnistyslatain. Virran kytkeminen avaa lyhyen DFU-ikkunan, jonka IDE käyttää laiteohjelmistopäivitysten lähettämiseen. Ikkuna sulkeutuu muutaman sekunnin kuluttua, ja käynnistyslatain luovuttaa hallinnan MicroPythonille. Käynnissä oleva skripti voi palata tähän ikkunaan tarvittaessa kutsumalla machine.bootloader().

  • Jäädytetyn tiedostojärjestelmän alustus. Ennen kuin mitään sovelluskoodia suoritetaan, suoritusympäristö tuo tiedostojärjestelmät käyttöön. Sisäinen flash-muisti liitetään polkuun /flash (ja alustetaan tyhjäksi, jos siellä ei ole mitään). Jos SD-kortti on läsnä ja sisäisessä flash-muistissa ei ole SKIPSD-nimistä merkkitiedostoa, SD-kortti liitetään polkuun /sdcard. Kun käännös sisältää ROMFS:n, se liitetään automaattisesti polkuun /rom. Työhakemistoksi asetetaan käynnistyshakemisto (/sdcard, jos kortti liitettiin, muutoin /flash), ja sys.path täytetään poluilla /flash, /flash/lib, /sdcard, /sdcard/lib, /rom ja /rom/lib. Flash-muistissa sijaitsevan alustuksen hoitaa jäädytetty moduuli nimeltä _boot.py – portin ja kortin infrastruktuuria, ei sovelluksen koukkua. Sovellukset eivät mukauta _boot.py-tiedostoa; käännös tekee sen. SKIPSD-tiedoston pudottaminen flash-muistiin IDE:stä on tuettu tapa saada kamera käynnistymään sisäisestä flash-muistista SD-kortin sijaan.

  • REPL:iä edeltävä alustus. boot.py suoritetaan jokaisessa pehmeässä nollauksessa – kylmäkäynnistys, Ctrl-D REPL:istä, käynnissä olevan skriptin palautuminen ja vahtikoiran palautuminen – ennen kuin REPL tulee tavoitettavaksi. Sen tehtävänä on valmistella ympäristö, jossa muu järjestelmä toimii: sellainen alustus, jonka REPL, sovellus ja kaikki palautustyökalut tarvitsevat paikalleen toimiakseen. Se ei ole paikka, jossa itse sovellus sijaitsee. main.py on sovelluksen aloituspiste.

  • Pääsilmukka. main.py on sovelluksen pääsilmukka. Suoritetaan kerran kylmäkäynnistyksessä, heti boot.py-tiedoston jälkeen. Sitä ei suoriteta uudelleen myöhemmissä pehmeissä nollauksissa – kamera siirtyy sen sijaan REPL:iin. Tällä epäsymmetrialla on merkitystä kehityksessä (Ctrl-D siirtyy REPL:iin suorittamatta silmukkaa uudelleen, joten kehittäjä voi tarkastella tilaa), mutta ei tuotannossa: kentällä oleva kamera kokee virrankytkentöjä, vahtikoiranollauksia ja kovia nollauksia, jotka kaikki ovat laitteistonollauksia, jotka palaavat kylmäkäynnistyspolulle ja suorittavat main.py-tiedoston uudelleen.

14.2.2.1.2. Jäädyttäminen laiteohjelmistoon

Kortin jäädytettyjen moduulien joukko määritellään laiteohjelmistopuussa tiedostossa boards/<TARGET>/manifest.py. Manifesti on pieni Python-tiedosto, joka kutsuu kourallista direktiivejä:

  • freeze("$(OMV_LIB_DIR)/", "foo.py") – leipoo yhden foo.py-tiedoston käännökseen.

  • package("mylib", base_path="...") – leipoo monitiedostoisen Python-paketin säilyttäen sen hakemistorakenteen annetun peruspolun alla.

  • include("...") – vetää mukaan toisen manifestitiedoston. Korttien manifestit käyttävät tätä yhteisten moduulijoukkojen jakamiseen.

  • require("logging") – vetää mukaan nimetyn ylävirran micropython-lib-moduulin nimen perusteella.

Pienin mahdollinen sovellusmanifesti lisää yhden freeze-rivin kutakin ylätason skriptiä kohden ja yhden package-rivin kutakin pakettia kohden, josta sovellus on riippuvainen.

14.2.2.1.2.1. Missä lähdekoodi sijaitsee

Sovelluksen lähdekoodi sijaitsee laiteohjelmistopuussa hakemiston scripts/libraries/ alla, niiden moduulien rinnalla, jotka käännös jo jäädyttää. Manifestimuuttuja $(OMV_LIB_DIR) laajenee tähän polkuun, joten manifestimerkinnät pysyvät lyhyinä. Manifestin muokkaaminen on jo valmiiksi puunsisäinen operaatio, joten lähdekoodin pitäminen puun sisällä välttää erillisen projektivaraston jonglöörauksen polun selvityksessä.

Tyypillinen rakenne sovellukselle, joka toimittaa yhden main.py-tiedoston sekä tukipaketin:

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

Ja kortin boards/<TARGET>/manifest.py-tiedostossa yksi freeze-rivi skriptille ja yksi package-rivi paketille:

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

Yksittäiset skriptit – tässä main.py, mutta sama sääntö koskee boot.py-tiedostoa tai mitä tahansa erillistä apuohjelmaa – käyttävät freeze-direktiiviä. Monitiedostoiset paketit käyttävät package-direktiiviä. Toisen skriptin lisääminen on yksi freeze-rivi lisää; toisen paketin lisääminen on yksi package-rivi lisää.

14.2.2.1.2.2. Kääntäminen ja flashaus

Kun manifesti on paikallaan, käännä laiteohjelmisto täsmälleen niin kuin laiteohjelmistoluku kuvaa:

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

Tuloste päätyy hakemistoon build/<TARGET>/bin/

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

.bin- ja .img-tiedostojen flashaus IDE:n kautta tuottaa kameran, jonka sovellus on osa käännöstä.

Yllä kuvattu käynnistyssekvenssi on se, mikä tekee sisäänleipomisesta tehokkaan: suoritusympäristö selvittää boot.py- ja main.py-tiedostot jäädytetyiksi kopioiksi ennen kuin se edes tarkistaa tiedostojärjestelmää, joten toimitettu kamera suorittaa käännöksen koodia, vaikka SD-kortilla olisi kehityksestä jäänyt vanhentunut boot.py.

14.2.2.1.2.3. Hakujärjestys

Ohitussemantiikka on erilainen boot.py / main.py -suorituspolulla ja tavallisissa import-lauseissa. Sen tietäminen, kumpi on kumpi, on tärkeää sekä tuotannolle että kehitykselle:

  • boot.py- ja main.py-tiedostoille: suoritusympäristö etsii ensin jäädytetyn kopion, sitten tiedostojärjestelmän. Jäädytettyä boot.py-tiedostoa ei voi ohittaa pudottamalla sellaista SD-kortille – se, jolla kamera on hallussaan, ei voi muuttaa aloituspistettä ilman uudelleenflashausta.

  • import foo -lauseelle: suoritusympäristö etsii ensin polusta sys.path – joka kattaa polut /flash, /sdcard, /rom ja niiden lib-alihakemistot – sitten jäädytetyt moduulit. Samanniminen foo.py flash-muistissa tai SD-kortilla kyllä ohittaa jäädytetyn foo-moduulin. Tämä on kehityksen mukavuus: pudota korjattu moduuli kortille, tee pehmeä nollaus, näe muutos ilman uudelleenflashausta.

Toimitettava tuote, joka haluaa estää tiedostojärjestelmän-ohittaa-jäädytetyn-käyttäytymisen tuonneissa, voi tyhjentää polun sys.path varhaisessa vaiheessa boot.py-tiedostossa:

import sys

sys.path.clear()

Kun sys.path on tyhjä, kaikki tuonnit selviävät vain jäädytetyistä moduuleista; mikään flash-muistissa, SD-kortilla tai ROMFS:ssä ei voi varjostaa niitä.

14.2.2.1.2.4. Resurssiongelma

Jäädyttäminen on erinomaista koodille. Se ei ole erinomaista suurille binääriresursseille: koneoppimismallien tiedostoille, luokkanimitaulukoille, JSON-konfiguraatiolle, kuvamalleille. Niiden upottaminen Python-literaaleina paisuttaa lähdekoodia, kääntää uudelleen hitaasti ja tuhlaa tavukoodisäiliön dataan, jonka tulkki kuitenkin vain lukee raakana. ROMFS-levykuvan rakentaminen-sivu käsittelee vain luettavan flash-tiedostojärjestelmän, joka täyttää tämän aukon.