12.9. Obousměrný tok¶
Kanály nejsou jednosměrné. Backend, který implementuje write, umožňuje hostu poslat bajty do kamery a kamera na ně reaguje. To je vzor, který stojí za každým skutečným interaktivním nástrojem: operátor otočí knoflíkem v hostitelském GUI, host zapíše novou hodnotu do konfiguračního kanálu a kamera ji přečte, až bude příště pořizovat snímek.
12.9.1. Konfigurační kanál¶
Přidejte do streamovacího skriptu na straně kamery druhý kanál pro kvalitu JPEG:
class ConfigChannel:
def __init__(self):
self.quality = 85
def size(self):
return 0
def read(self, offset, size):
# Not used for "host writes to cam" -- but the library
# still needs the method present.
return b''
def write(self, offset, data):
# data is a bytearray view into the protocol buffer.
# Copy out the contents before doing anything with it.
new_q = int(bytes(data))
if 1 <= new_q <= 100:
self.quality = new_q
return len(data)
config = ConfigChannel()
protocol.register(name='config', backend=config)
Snímací smyčka čte z config.quality pokaždé, když komprimuje snímek:
while True:
img = csi0.snapshot()
latest_jpeg = bytes(
img.compress(quality=config.quality).bytearray()
)
ch.send_event(0x01)
Host nyní má knoflík. Nastavte jej na 50 a další snímek bude menší (a ošklivější); nastavte jej na 95 a další snímek bude větší (a ostřejší). Kamera snímá dál bez restartu; host nemusí posílat nový skript.
12.9.2. Volání zápisu z hosta¶
Na straně hosta posílá channel_write() bajty do pojmenovaného kanálu:
cam.channel_write('config', b'50')
Hostitelský SDK zakóduje bajty jako jeden (nebo fragmentovaný) paket CHANNEL_WRITE, protokolová vrstva jej doručí kameře, na kameře se spustí write(offset=0, data=...) a strana kamery potvrdí. Než se volání vrátí, kamera novou hodnotu přijala a akceptovala.
Zápis je z pohledu kamery atomický – knihovna protokolu zaručuje, že write backendu doběhne do konce, než na tomtéž kanálu pokročí jakákoli jiná operace. Kód aplikace může číst config.quality zevnitř snímací smyčky, aniž by se musel obávat, že do toho host vstoupí uprostřed snímku.
12.9.3. Velikost stubu a čtení na kanálu pouze pro zápis¶
Čistě zapisovací kanál stále potřebuje definované size a read, i kdyby to byly stuby vracející 0 a b''. Knihovna používá přítomnost metod k odvození příznaků schopností kanálu; backendu, kterému chybí read, nebude nastaven CHANNEL_FLAG_READ a host pokus o čtení odmítne.
Bajty vrácené z read na kanálu pouze pro zápis jsou nicméně užitečné pro jiný účel: odeslání aktuální hodnoty zpět, aby se host, který se právě připojil, mohl kamery zeptat „jaké je aktuální nastavení?“ místo toho, aby vycházel z výchozí hodnoty. Aby to fungovalo, musí se oba směry dohodnout na serializaci. Parsování surových bajtů int(bytes(data)) z předchozího příkladu funguje pro jediné celočíselné pole, ale neobstojí, jakmile je třeba nastavit druhý knoflík. Přepnutí write na parsování JSON a jeho spárování s read, který vrací odpovídající JSON výpis, promění kanál ve skutečné obousměrné úložiště konfigurace:
import json
class ConfigChannel:
def __init__(self):
self.quality = 85
self._buf = b''
def size(self):
self._buf = json.dumps({'quality': self.quality}).encode()
return len(self._buf)
def read(self, offset, size):
return self._buf[offset:offset + size]
def write(self, offset, data):
new = json.loads(bytes(data))
if 'quality' in new:
self.quality = int(new['quality'])
return len(data)
Host nyní zapisuje cam.channel_write('config', b'{"quality": 50}') k nastavení hodnoty a cam.channel_read('config') ke zpětnému čtení aktuálního stavu. Kamera při každém čtení serializuje čerstvý JSON výpis, takže host vždy vidí nejnovější hodnoty, a přidání dalšího knoflíku (threshold, exposure, orientation) je jeden řádek v JSON slovníku na každé straně.
12.9.4. Kompletní smyčka¶
Se snímkovým kanálem pro data kamera → host, konfiguračním kanálem pro řízení host → kamera a malým množstvím lepicího kódu je aplikace interaktivním nástrojem:
Host otevře kameru, začne stahovat snímky a zobrazuje je v okně.
Když operátor přetáhne posuvník, host zapíše novou hodnotu na
config.Snímací smyčka kamery hodnotu vyzvedne při dalším snímku.
Nové snímky proudí stejným kanálem
frame.
To je celý model. Dva kanály, dva callbacky každý, snímací smyčka na kameře, smyčka čtení a zápisu na hostu. Žádná viditelná logika rámcování, žádné viditelné zpracování chyb – knihovna protokolu nechá spolehlivý přesun bajtů zmizet.
Vše za tímto bodem je kód aplikace. Přidání třetího kanálu pro histogram, čtvrtého pro telemetrii nebo pátého pro spouštěče senzoru je tentýž recept třídy backendu a protocol.register, opakovaný. Jakmile projekt s kamerou dosáhne tohoto bodu, protokol přestane být tím zajímavým problémem; tím se stane vlastní logika aplikace.