12.6. Elnevezett csatornák

Az egyes csomagok fejlécében lévő csatornaazonosító lehetővé teszi, hogy legfeljebb 32 független adatfolyam ossza meg ugyanazt a fizikai átvitelt. A csatornaréteg ezeket a numerikus azonosítókat elnevezett, az alkalmazás számára látható végpontokká alakítja, amelyekre a gazdagép kódja sztring alapján hivatkozhat.

Egy átviteli vezeték a bal oldalon, amely négy feliratozott csatornává ágazik szét a kamera oldalán -- stdin, stdout, stream és egy felhasználó által regisztrált állapotcsatorna -- mindegyik független dobozként megjelenítve.

12.6.1. A négy beépített csatorna

A kamera négy csatornát regisztrál indításkor, mielőtt bármilyen alkalmazáskód lefutna:

  • stdin – szkriptbájtok, amelyeket a gazdagép a kamerára küld végrehajtásra. Az IDE ezt a csatornát használja a szerkesztett szkript elküldésére; a exec() a gazdagép SDK-ban az ezzel egyenértékű hívás egy Python programból.

  • stdout – bájtok a kamera print() hívásaiból és az elkapatlan kivételek visszakövetéseiből. Az IDE soros konzolja ezt a csatornát olvassa.

  • stream – az élő előnézeti csatorna. Az IDE JPEG képkockákat húz ki belőle; bármely gazdagép-szkript ugyanezt megteheti a read_frame() segítségével.

  • profile – profilozási események, amelyek csak akkor vannak jelen, ha a kamera profilozás engedélyezésével lett lefordítva. A legtöbb kiadási build elhagyja.

Az alkalmazáskódnak ritkán kell hozzányúlnia bármelyik beépített csatornához; az érdekes munka azokon a csatornákon történik, amelyeket az alkalmazás maga regisztrál.

12.6.2. Csatorna regisztrálása

Egy kamera oldali szkript úgy regisztrál új csatornát, hogy meghívja a protocol.register() függvényt egy névvel és egy Python backend objektummal:

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())

A backend objektum metódusai döntik el, hogy mit tehet a csatorna. Egy olyan backend, amelynek csak size és read metódusa van, egy csak olvasható adatcsatorna; adja hozzá a write metódust, és kétirányúvá válik; adja hozzá a poll metódust, és a gazdagép megkérdezheti, hogy van-e új adat készen, mielőtt megfizetné egy olvasás árát. Az adat mintavételezése a size belsejében a legegyszerűbb minta, amikor a hasznos terhelés elég kicsi ahhoz, hogy egy töredékbe férjen – a puffer igény szerint generálódik, soha nem gyorsítótárazódik, soha nem kerül versenyhelyzetbe. A nagyobb hasznos terhelések – képkockák, érzékelőnyomok – reteszelő mintát igényelnek, amely a puffert addig tartja, amíg a gazdagép be nem fejezi a több töredékből álló olvasását, amit a képkocka-csatorna tárgyal.

Egy kevés könyvelés automatikusan megtörténik:

  • A könyvtár hozzárendeli a következő szabad csatornaazonosítót (0 és 31 között).

  • A képességjelzők a jelen lévő metódusokból származnak: CHANNEL_FLAG_READ, ha a read definiált, CHANNEL_FLAG_WRITE, ha a write definiált, CHANNEL_FLAG_LOCK, ha a lock / unlock definiált.

  • Egy CHANNEL_REGISTERED eseménycsomag kerül elküldésre minden csatlakoztatott gazdagépnek, hogy a csatornalistája frissüljön.

A visszatérési érték egy protocol.ProtocolChannel leíró, amelyet az alkalmazás megtarthat. A leíró send_event() metódusa a kamera oldali horog, amellyel közölheti a gazdagéppel, hogy „valami történt ezen a csatornán anélkül, hogy az olvasható adat megváltozott volna” – egy trigger elsült, egy gombot megnyomtak, egy mintaszám mérföldkő elérkezett.

12.6.3. Csatornák olvasása a gazdagépről

A gazdagép SDK az openmv csomagként szállítódik a PyPI-n (pip install openmv), amely a pyserial könyvtárra épül az átvitelhez. Az openmv.camera.Camera osztálya magas szintű metódusokon keresztül teszi elérhetővé a kamera elnevezett csatornáit:

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)

Figyelem

Az openmv csomag CPython 3.12-t vagy újabbat igényel. A korábbi értelmezőkből hiányoznak azok a funkciók, amelyekre az SDK támaszkodik; telepítsen egy 3.12+ buildet az pip install openmv előtt.

Néhány dolog, amit érdemes megfigyelni a beállításnál:

  • A soros port sztringje – itt /dev/ttyACM0COM3 stílusú Windowson, /dev/cu.usbmodemXXXX macOS-en és /dev/ttyACM* Linuxon. A tényleges szám attól függ, hogy a kamera melyik portként számozódott fel.

  • Az átviteli sebesség a protokoll varázsértéke, a 921600, amelyet a kamera USB-CDC verme úgy ismer fel, hogy „ez a kliens a protokollt beszéli, nem a REPL-t.” Bármilyen más sebesség egyszerű soros vonalra esik vissza.

  • A with Camera(...) as cam: kontextuskezelő megnyitja az átvitelt, lefuttatja a PROTO_SYNC-et, kicseréli a képességeket, és kilépéskor tisztán bezárja a portot. A belépés utáni explicit update_channels() hívás frissíti a helyi csatornalistát minden olyan csatornával, amelyet az alkalmazás indítás után regisztrált.

A channel_size() és a channel_read() az igáslómetódusok; a channel_write() oda-vissza küld egy puffert a kamerára, ha a backendnek van write metódusa; a has_channel() a biztonságos módja annak, hogy ellenőrizze, egy név regisztrálva van-e, mielőtt használná. A csatornanevet egyszer keresi ki arra a csatornaazonosítóra, amelyet a kamera a register során hozzárendelt, és attól kezdve minden csomagban azt használja.

Minden channel_size() / channel_read() páros két oda-vissza utat igényel: egy csomagot a méret lekérdezésére, egyet a bájtok lekérdezésére. USB-CDC-n keresztül mindkettő összesen körülbelül egy ezredmásodperc alatt befejeződik; UART-on keresztül ugyanaz a csere a soros vonal átviteli sebességével arányosan tovább tart. Az olyan alkalmazáskódnak, amely szoros ciklusban olvas, csak akkor szabad meghívnia a channel_size() metódust, amikor a méret ténylegesen változhat – rögzített méretű adatoknál az első hívás mérete gyorsítótárazható.

12.6.4. Csatornák közötti függetlenség

Három dolgot érdemes tudni arról, hogyan lépnek kölcsönhatásba a csatornák:

  • Független folyamatvezérlés. Minden csatornának saját függőben lévő olvasási állapota, saját adata és saját size / read / write visszahívásai vannak. A stream csatornán futó hosszú olvasás nem blokkolja az alkalmazás config csatornáján történő olvasásokat.

  • Csatornánként szekvenciális. Egyetlen csatornán belül a csomagok sorrendben kézbesülnek. A megbízhatósági réteg ezt akkor is garantálja, ha újraküldések vannak érintve.

  • Megosztott átvitel, megosztott újraküldési keret. Minden csatorna osztozik az egyetlen fizikai kapcsolaton, így az egyik csatornán zúduló forgalom lelassítja a többit azzal, hogy lefoglalja a vezetéket. A CHANNEL_LOCK mechanizmus lehetővé teszi, hogy egy csatorna lefoglalja a vezetéket egy atomi, több csomagból álló olvasáshoz; a backend a lock / unlock visszahívások megvalósításával iratkozik fel erre.

A csatorna a minimális felület, amelyen egy gazdagép-program és egy kamera-program megegyezik az együttműködésben. A név, az irányultság (olvasás vagy írás vagy mindkettő), a kamera oldali visszahívás-metódusok és a hozzájuk illő metódushívások a gazdagép oldalán alkotják a teljes szerződést.