13.3.1.5. Události¶
Dosavadní stránky volají do kamery: nahrát skript, přečíst snímek, zapsat do kanálu. Každá z těchto operací je iniciovaná hostitelem – hostitel se ptá, kamera odpovídá. Protokol funguje i opačným směrem. Kamera může hostiteli posílat události, aniž by byla dotázána, a hostitelské SDK doručí každou z nich do callbacku, který může aplikace přepsat.
Je to ten správný nástroj, kdykoli chce aplikace reagovat na něco, co kamera zaznamenala, dříve než se zeptá. Bez událostí je jediný způsob, jak to zjistit, opakované volání read_status() ve smyčce.
13.3.1.5.1. Výchozí callback¶
Třída Camera se k událostem interně přihlašuje sama. _handle_event() je callback, který transport spustí, kdykoli dorazí paket události. Výchozí implementace zpracovává tři systémové události:
CHANNEL_REGISTERED– na kameře se objevil nový kanál poté, co se hostitel připojil. Framework obnoví svou cache kanálů, takže další vyhledáníhas_channel()jej najde.CHANNEL_UNREGISTERED– kanál zmizel.SOFT_REBOOT– kamera se restartovala sama (watchdog, hard fault, záměrnémachine.reset()).
Sleduje také událost frame-ready kanálu stream pro streamovací cestu a start/stop skriptu kanálu stdin pro bufferování stdout. Výchozí hodnota konstruktoru events=True ponechává toto vše zapnuté; aplikace, která z toho nechce nic, může předat events=False do Camera a subsystém událostí zůstane potichu.
13.3.1.5.2. Reakce pomocí podtřídy¶
Pro zpracování událostí specifických pro aplikaci, které kamera vyvolává, vytvořte podtřídu Camera a přepište _handle_event(). Nejprve zavolejte rodiče, abyste zachovali výchozí chování, a poté rozešlete události, které aplikaci zajímají:
from openmv import Camera
class MyCamera(Camera):
def _handle_event(self, channel_id, event):
super()._handle_event(channel_id, event)
name = self.channels_by_id.get(
channel_id, {}).get('name')
if name == 'motion' and event == 1:
self.on_motion()
def on_motion(self):
print("motion detected")
Signatura je (channel_id, event). channel_id je 0 pro systémové události a jinak číselné ID kanálu, který je vyvolal; event je celé číslo zvolené skriptem na straně kamery. Výčet EventType dává jména třem systémovým událostem; události kanálů používají jakékoli hodnoty, které definuje backend na straně kamery.
Události kanálů se vracejí klíčované číselným ID, nikoli jménem. Cachovaný slovník channels_by_id je to, co výše uvedené přepsání používá k vyhledání jména; channels_by_name je jeho zrcadlo, klíčované opačně.
13.3.1.5.3. Polovina na straně kamery¶
Skript na straně kamery vyvolá událost voláním send_event() na handle vráceném z protocol.register()
import protocol
class MotionChannel:
def size(self):
return 0
def read(self, offset, size):
return b''
def poll(self):
return False
ch = protocol.register(
name='motion', backend=MotionChannel())
while True:
if detect_motion():
ch.send_event(1)
Číslo události je celé číslo zvolené aplikací. Jakákoli hodnota, na kterou je přepsání na straně hostitele připraveno, je v pořádku; protokolová vrstva ji považuje za neprůhledný payload. Ve výchozím nastavení volání odešle a zapomene; předáním wait_ack=True zablokujete běh, dokud hostitel nepotvrdí, když je jistota, že událost dorazila, důležitější než latence cesty tam a zpět.
Kanál, který pouze vyvolává události a nepřenáší žádná čitelná data, je platný vzor – size vrací 0 a read vrací prázdné bajty. Protokolová knihovna stále potřebuje, aby obě metody byly přítomny, aby kanál označila za čitelný; skript na straně kamery do něj jen nikdy nevkládá data.
13.3.1.5.4. Obsluha přijímací cesty v nečinnosti¶
Události přicházejí po stejném připojení jako vše ostatní, takže jakékoli hostitelské volání, které odesílá nebo přijímá bajty, dává transportu příležitost zpracovat čekající události za běhu. Dotazovací smyčka, která už jednou za cyklus volá read_status() nebo read_frame(), nepotřebuje nic navíc.
Pro programy, které jsou minuty bez jiného I/O, metoda poll_events() spustí přijímací cestu jednou bez odeslání příkazu. Vrací se, jakmile je příchozí buffer prázdný, takže těsná smyčka kolem ní – nebo krátký časovač v GUI smyčce událostí – je to, co udržuje obsluhy reaktivní.
13.3.1.5.5. Kompletní smyčka¶
Od začátku do konce je vzor následující: skript na straně kamery registruje kanál a volá send_event(), když se něco stane; podtřída na straně hostitele přepisuje _handle_event() a rozesílá. Hostitelská smyčka, která nedělá nic jiného než obsluhuje události, vypadá takto:
with MyCamera('/dev/ttyACM0') as cam:
cam.stop()
cam.exec(open('motion_cam.py').read())
while True:
cam.poll_events()
Kamera zachytává, rozhoduje a vyvolává události. Hostitel sedí uvnitř poll_events(), dokud nějaká nedorazí, a poté se spustí on_motion. Když se nic neděje, neproběhne žádné volání read_status() a po USB se nepřetahuje žádný snímek, když kamera nemá co hlásit.