13.3.1.4. Vlastní kanály¶
Kanál je pojmenovaný obousměrný bajtový stream mezi skriptem na straně kamery a hostitelem. Kamera kanál registruje a poskytuje callbacky, které data produkují nebo spotřebovávají; hostitel z tohoto kanálu čte a do něj zapisuje podle jména. Tentýž mechanismus, který balíček interně používá pro kanál stream přenášející snímky, kanál stdout přenášející výstup skriptu a kanál stdin přenášející nahrávání skriptu, je vystaven i uživatelským skriptům, takže jakákoli data specifická pro aplikaci, která hostitel potřebuje, mohou jezdit po témže USB připojení bez vymýšlení druhého protokolu.
Je to nejužitečnější funkce balíčku a zároveň ta, kterou standardní dokumentace pokrývá nejhůře, takže tato stránka ji prochází od začátku do konce.
13.3.1.4.1. Dvě poloviny¶
Vlastní kanál potřebuje spolupracující kód na obou stranách. Skript na straně kamery importuje protocol, definuje třídu se třemi metodami (size(), read(), poll()) a volitelnou write() a volá protocol.register(name=..., backend=...) pro publikování kanálu pod zvoleným jménem:
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())
Metoda size() vrací, kolik bajtů má kanál aktuálně k dispozici. read() je producent: dostane offset a size požadované hostitelem a vrátí bajty (nebo řetězec, který protokolová vrstva zakóduje). poll() vrací True, když je k dispozici něco ke čtení – protokolová vrstva to používá k označení kanálu jako připraveného v read_status().
Program na straně hostitele používá čtyři metody třídy openmv.Camera: has_channel() pro kontrolu existence kanálu, channel_size() pro dotaz, kolik dat čeká, channel_read() pro vytažení bajtů ven a channel_write() pro vložení bajtů dovnitř. read_status() dotazuje všechny kanály najednou:
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()}")
Hostitelská smyčka dotazuje read_status(); když je kanál ticks připraven, zavolá channel_read() bez size, aby vytáhla cokoli je k dispozici. Metoda TicksChannel.poll() kamery vrací True při každé kontrole, takže kanál je vždy „připraven“ a hostitel získá čerstvou hodnotu ticku při každém dotazu.
13.3.1.4.2. Obousměrný kanál¶
Pro hostitele, který potřebuje posílat data zpět, přidá třída na straně kamery metodu write(), která přijímá příchozí bajty:
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())
Hostitel zapisuje do kanálu pomocí channel_write() a čte odpověď zpět obvyklým vzorem read_status() / channel_read()
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. Co tím aplikace získá¶
Vlastní kanály jsou ten správný nástroj, kdykoli chce aplikace využít stávající USB připojení pro data, která nejsou snímky ani výpisy: telemetrické čítače, konfigurační nastavení streamovaná živě z uživatelského rozhraní na hostiteli, řídicí příkazy posílané opačným směrem, výsledky měření, které kamera vypočítala a které se nehodí do „obrazového“ rámcování předpokládaného kanálem stream. Protokolová vrstva se stará o rámcování, fragmentaci, potvrzování a opakování; skript musí pouze implementovat backend se čtyřmi metodami a hostitel potřebuje znát jen jméno kanálu a tvar dat.
Příznak --channel NAME v CLI je rychlý způsob, jak ověřit vlastní kanál z terminálu bez psaní programu na straně hostitele: CLI dotazuje pojmenovaný kanál a vypisuje prvních deset bajtů každé aktualizace.
Limit velikosti jednoho volání channel_read() nebo channel_write() je protokolem vyjednaná hodnota max_payload – výchozí 4096 bajtů. Metody na straně hostitele automaticky rozdělí větší zápisy na správný počet paketů, takže aplikace může předávat libovolně velké buffery; fragmentace je neviditelná.