12.6. Imenovani kanali

ID kanala u zaglavlju svakog paketa omogućuje da do 32 neovisna toka dijele isti fizički transport. Sloj kanala pretvara te numeričke ID-ove u imenovane, aplikaciji vidljive krajnje točke na koje se kod domaćina može pozivati nizom znakova.

Jedna transportna žica s lijeve strane koja se grana u četiri označena kanala na strani kamere -- stdin, stdout, stream i korisnički registrirani statusni kanal -- svaki prikazan kao neovisna kutija.

12.6.1. Četiri ugrađena kanala

Kamera registrira četiri kanala pri pokretanju, prije nego što se izvrši bilo koji aplikacijski kod:

  • stdin – bajtovi skripte koje domaćin šalje kameri na izvršavanje. IDE koristi ovaj kanal za slanje skripte koja se uređuje; exec() na SDK-u domaćina ekvivalentan je poziv iz Python programa.

  • stdout – bajtovi iz poziva print() na kameri i tragovi neuhvaćenih iznimaka. Serijska konzola IDE-a čita ovaj kanal.

  • stream – kanal za prikaz uživo. IDE iz njega povlači JPEG sličice; bilo koja skripta domaćina može učiniti isto pomoću read_frame().

  • profile – događaji profilera, prisutni samo kada je kamera izgrađena s omogućenim profiliranjem. Većina izdanih verzija to izostavlja.

Aplikacijski kod rijetko treba dirati bilo koji od ugrađenih kanala; zanimljiv rad odvija se na kanalima koje aplikacija sama registrira.

12.6.2. Registriranje kanala

Skripta na strani kamere registrira novi kanal pozivanjem protocol.register() s imenom i Python pozadinskim objektom:

import json
import protocol
import time

trigger_count = 0

class StatusChannel:
    def size(self):
        # Refresh the snapshot on every host query.
        self._buf = json.dumps({
            'uptime_s': time.ticks_ms() // 1000,
            'triggers': trigger_count,
        }).encode()
        return len(self._buf)

    def read(self, offset, size):
        return self._buf[offset:offset + size]

protocol.register(name='status', backend=StatusChannel())

Metode pozadinskog objekta određuju što kanal može raditi. Pozadinski objekt sa samo size i read je podatkovni kanal samo za čitanje; dodajte write i postaje dvosmjeran; dodajte poll i domaćin može upitati jesu li novi podaci spremni prije nego što plati za čitanje. Uzorkovanje podataka unutar size najjednostavniji je obrazac kada je korisni teret dovoljno malen da stane u jedan fragment – međuspremnik se generira na zahtjev, nikad se ne sprema u predmemoriju, nikad ne dolazi do utrke. Veći korisni tereti – slikovne sličice, senzorski tragovi – trebaju obrazac zasuna koji drži međuspremnik dok domaćin ne završi svoje višefragmentno čitanje, što je obrađeno uz kanal sličica.

Mala količina vođenja evidencije odvija se automatski:

  • Biblioteka dodjeljuje sljedeći slobodan ID kanala (između 0 i 31).

  • Zastavice mogućnosti izvode se iz prisutnih metoda: CHANNEL_FLAG_READ ako je definirana read, CHANNEL_FLAG_WRITE ako je definirana write, CHANNEL_FLAG_LOCK ako su definirane lock / unlock.

  • Paket događaja CHANNEL_REGISTERED šalje se svakom povezanom domaćinu kako bi se njegov popis kanala ažurirao.

Povratna vrijednost je rukovatelj protocol.ProtocolChannel koji aplikacija može zadržati. Metoda rukovatelja send_event() predstavlja kuku na strani kamere za obavještavanje domaćina da se „nešto dogodilo na ovom kanalu bez promjene podataka za čitanje” – okidač je aktiviran, gumb je pritisnut, prijeđena je prekretnica u broju uzoraka.

12.6.3. Čitanje kanala s domaćina

SDK domaćina isporučuje se kao paket openmv na PyPI-ju (pip install openmv), izgrađen na pyserial za transport. Njegova klasa openmv.camera.Camera izlaže imenovane kanale kamere putem visokorazinskih metoda:

from openmv.camera import Camera

with Camera('/dev/ttyACM0', baudrate=921600) as cam:
    cam.update_channels()
    if cam.has_channel('status'):
        size = cam.channel_size('status')
        data = cam.channel_read('status', size)

Upozorenje

Paket openmv zahtijeva CPython 3.12 ili noviji. Raniji interpreteri nemaju značajke o kojima SDK ovisi; instalirajte verziju 3.12+ prije pip install openmv.

Nekoliko stvari koje treba primijetiti u vezi s postavljanjem:

  • Niz znakova serijskog priključka – ovdje /dev/ttyACM0 – u stilu je COM3 na sustavu Windows, /dev/cu.usbmodemXXXX na macOS-u i /dev/ttyACM* na Linuxu. Stvarni broj ovisi o tome kao koji se priključak kamera nabrojala.

  • Brzina prijenosa je magična vrijednost protokola 921600, koju USB-CDC stog kamere prepoznaje kao „ovaj klijent govori protokolom, a ne REPL-om”. Bilo koja druga brzina vraća se na običnu serijsku liniju.

  • Upravitelj konteksta with Camera(...) as cam: otvara transport, izvodi PROTO_SYNC, razmjenjuje mogućnosti i pri izlazu uredno zatvara priključak. Eksplicitan poziv update_channels() nakon ulaska osvježava lokalni popis kanala svim kanalima koje je aplikacija registrirala nakon pokretanja.

channel_size() i channel_read() su radne metode; channel_write() šalje međuspremnik kameri i natrag ako pozadinski objekt ima metodu write; has_channel() siguran je način provjere je li ime registrirano prije njegove uporabe. Ime kanala traži se jednom u ID kanala koji je kamera dodijelila tijekom register i koristi se u svakom paketu od tada nadalje.

Svaki par channel_size() / channel_read() košta dva povratna putovanja: jedan paket za upit o veličini, jedan za upit o bajtovima. Preko USB-CDC-a oba završavaju zajedno za otprilike jednu milisekundu; preko UART-a ista razmjena traje dulje razmjerno brzini prijenosa serijske linije. Aplikacijski kod koji čita u uskoj petlji trebao bi pozivati channel_size() samo kada se veličina zaista može promijeniti – za podatke fiksne veličine, veličina iz prvog poziva može se spremiti u predmemoriju.

12.6.4. Neovisnost među kanalima

Vrijedi znati tri stvari o tome kako kanali međusobno djeluju:

  • Neovisna kontrola toka. Svaki kanal ima vlastito stanje čitanja na čekanju, vlastite podatke i vlastite povratne pozive size / read / write. Dugotrajno čitanje na kanalu stream ne blokira čitanja na aplikacijskom kanalu config.

  • Sekvencijalno po kanalu. Unutar jednog kanala paketi se isporučuju po redu. Sloj pouzdanosti to jamči čak i kada su uključeni ponovni prijenosi.

  • Dijeljeni transport, dijeljeni proračun za ponovni prijenos. Svi kanali dijele jednu fizičku vezu, pa bujica prometa na jednom kanalu usporava ostale zaposjedanjem žice. Mehanizam CHANNEL_LOCK omogućuje jednom kanalu da rezervira žicu za atomarno čitanje višestrukih paketa; pozadinski objekt se uključuje implementiranjem povratnih poziva lock / unlock.

Kanal je minimalna površina na kojoj se program domaćina i program kamere dogovaraju o suradnji. Ime, smjer (čitanje ili pisanje ili oboje), metode povratnih poziva na strani kamere i odgovarajući pozivi metoda na strani domaćina čine cijeli ugovor.