13.3.1.4. Egyéni csatornák

A csatorna egy elnevezett, kétirányú bájtfolyam egy kameraoldali szkript és a gazdagép között. A kamera regisztrál egy csatornát, és visszahívásokat biztosít, amelyek adatot állítanak elő vagy fogyasztanak; a gazdagép név szerint olvas erről a csatornáról és ír rá. Ugyanazt a mechanizmust, amelyet a csomag belsőleg a képkockákat hordozó stream csatornához, a szkriptkimenetet hordozó stdout csatornához és a szkriptfeltöltést hordozó stdin csatornához használ, a felhasználói szkriptek számára is elérhetővé teszi, így bármilyen alkalmazásspecifikus adat, amelyre a gazdagépnek szüksége van, ugyanazon az USB-kapcsolaton utazhat, anélkül, hogy egy második protokollt kellene kitalálni.

Ez a csomag leghasznosabb funkciója, és az, amelyet a szabványos dokumentáció a legkevésbé jól ismertet, így ez az oldal végigvezet rajta végponttól végpontig.

13.3.1.4.1. A két fél

Egy egyéni csatornához mindkét oldalon együttműködő kódra van szükség. A kameraoldali szkript importálja a protocol modult, definiál egy három metódust (size(), read(), poll()) tartalmazó osztályt egy opcionális write() metódussal kiegészítve, és meghívja a protocol.register(name=..., backend=...) függvényt, hogy a csatornát egy választott néven közzétegye:

import protocol
import time

class TicksChannel:
    def size(self):
        return 10

    def read(self, offset, size):
        return f'{time.ticks_ms():010d}'

    def poll(self):
        return True

protocol.register(name='ticks', backend=TicksChannel())

A size() metódus visszaadja, hány bájt érhető el jelenleg a csatornán. A read() a termelő: a gazdagép által kért offset és size alapján visszaadja a bájtokat (vagy egy karakterláncot, amelyet a protokollréteg kódol). A poll() True értéket ad vissza, ha van olvasnivaló – a protokollréteg ezt használja arra, hogy a csatornát készként jelölje a read_status() metódusban.

A gazdaoldali program négy openmv.Camera metódust használ: a has_channel() metódust a csatorna létezésének ellenőrzéséhez, a channel_size() metódust annak megkérdezéséhez, mennyi adat várakozik, a channel_read() metódust a bájtok kihúzásához, és a channel_write() metódust a bájtok betolásához. A read_status() egyszerre kérdezi le az összes csatornát:

from openmv import Camera

with Camera('/dev/ttyACM0') as cam:
    cam.stop()
    cam.exec(open('ticks_cam.py').read())

    while True:
        status = cam.read_status()

        if status.get('ticks'):
            data = cam.channel_read('ticks')
            print(f"ticks: {data.decode()}")

A gazdaciklus lekérdezi a read_status() metódust; amikor a ticks csatorna készen áll, meghívja a channel_read() metódust size nélkül, hogy kihúzza, ami éppen elérhető. A kamera TicksChannel.poll() metódusa minden ellenőrzéskor True értéket ad vissza, így a csatorna mindig „kész”, és a gazdagép minden lekérdezéskor friss tick-értéket kap.

13.3.1.4.2. Kétirányú csatorna

Egy olyan gazdagéphez, amelynek adatot kell visszatolnia, a kameraoldali osztály egy write() metódust ad hozzá, amely fogadja a bejövő bájtokat:

import protocol

class CommandChannel:
    def __init__(self):
        self.last_command = b''
        self.replied = False

    def size(self):
        return len(self.last_command)

    def read(self, offset, size):
        self.replied = True
        return self.last_command

    def write(self, offset, data):
        self.last_command = b'echo: ' + bytes(data)
        self.replied = False

    def poll(self):
        return not self.replied and len(self.last_command) > 0

protocol.register(name='echo', backend=CommandChannel())

A gazdagép a channel_write() metódussal ír a csatornára, és a választ a szokásos read_status() / channel_read() mintán keresztül olvassa vissza:

with Camera('/dev/ttyACM0') as cam:
    cam.stop()
    cam.exec(open('echo_cam.py').read())

    cam.channel_write('echo', b'hello')

    while True:
        if cam.read_status().get('echo'):
            print(cam.channel_read('echo').decode())
            break

13.3.1.4.3. Mit nyer ezzel az alkalmazás

Az egyéni csatornák a megfelelő eszköz, amikor egy alkalmazás a meglévő USB-kapcsolatot szeretné felhasználni nem képkocka és nem kiírás jellegű adatokhoz: telemetriaszámlálók, a gazdagépen lévő felületről élőben streamelt konfigurációs gombok, a másik irányba küldött vezérlőparancsok, vagy a kamera által kiszámított mérés eredményei, amelyek nem férnek bele a „kép” keretezésbe, amelyet a stream csatorna feltételez. A protokollréteg kezeli a keretezést, a darabolást, a visszaigazolást és az újrapróbálkozást; a szkriptnek csak a négymetódusos backendet kell megvalósítania, a gazdagépnek pedig csak a csatorna nevét és az adat alakját kell ismernie.

A CLI --channel NAME kapcsolója gyors módja egy egyéni csatorna ellenőrzésének a terminálból, gazdaoldali program írása nélkül: a CLI lekérdezi a megnevezett csatornát, és minden frissítés első tíz bájtját kiírja.

Egyetlen channel_read() vagy channel_write() hívás méretkorlátja a protokoll által kialkudott max_payload – alapértelmezés szerint 4096 bájt. A gazdaoldali metódusok automatikusan a megfelelő számú csomagra osztják a nagyobb írásokat, így az alkalmazás tetszőlegesen nagy puffereket adhat át; a darabolás láthatatlan.