13.3.1.5. Događaji¶
Stranice do sada pozivaju kameru: prenose skriptu, čitaju sličicu, pišu u kanal. Svaka od tih operacija pokreće se od strane domaćina – domaćin pita, kamera odgovara. Protokol radi i u suprotnom smjeru. Kamera može gurati događaje domaćinu bez da je upitana, a SDK domaćina isporučuje svaki od njih povratnom pozivu koji aplikacija može nadjačati.
Ovo je pravi alat kad god aplikacija želi reagirati na nešto što je kamera primijetila prije nego što ona to upita. Bez događaja, jedini način da se sazna jest neprekidno pozivanje read_status() u petlji.
13.3.1.5.1. Zadani povratni poziv¶
Klasa Camera već se interno pretplaćuje na događaje. _handle_event() je povratni poziv koji prijenosni sloj pokreće kad god stigne paket događaja. Zadana implementacija obrađuje tri sistemska događaja:
CHANNEL_REGISTERED– novi kanal pojavio se na kameri nakon što se domaćin povezao. Okvir osvježava svoju predmemoriju kanala kako bi ga sljedeći upithas_channel()pronašao.CHANNEL_UNREGISTERED– kanal je nestao.SOFT_REBOOT– kamera se sama ponovno pokrenula (watchdog, ozbiljna pogreška, namjernimachine.reset()).
Također prati događaj spremnosti sličice kanala stream za put prijenosa te pokretanje/zaustavljanje skripte kanala stdin za međuspremanje stdout-a. Zadana vrijednost events=True u konstruktoru drži sve to uključenim; aplikacija koja ništa od toga ne želi može proslijediti events=False klasi Camera i podsustav događaja ostaje tih.
13.3.1.5.2. Podklasiranje radi reakcije¶
Za obradu događaja specifičnih za aplikaciju koje kamera podiže, podklasirajte Camera i nadjačajte _handle_event(). Prvo pozovite roditelja da zadržite zadano ponašanje, zatim raspodijelite događaje do kojih je aplikaciji stalo:
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")
Potpis je (channel_id, event). channel_id je 0 za sistemske događaje, a inače numerički ID kanala koji ga je podigao; event je cijeli broj koji je odabrala skripta na strani kamere. Enumeracija EventType daje nazive trima sistemskim događajima; događaji kanala koriste vrijednosti koje definira pozadina na strani kamere.
Događaji kanala vraćaju se s ključem numeričkog ID-a, ne naziva. Predmemorirani rječnik channels_by_id jest ono što gornje nadjačavanje koristi za traženje naziva; channels_by_name je njegovo zrcalo, s ključem u drugom smjeru.
13.3.1.5.3. Polovica na strani kamere¶
Skripta na strani kamere podiže događaj pozivom send_event() na ručki vraćenoj iz 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)
Broj događaja cijeli je broj koji bira aplikacija. Bilo koja vrijednost koju je nadjačavanje na domaćinu spremno obraditi je dopuštena; sloj protokola tretira je kao neproziran korisni sadržaj. Prema zadanim postavkama poziv se pošalje i zaboravi; proslijedite wait_ack=True da blokirate dok domaćin ne potvrdi, kada je spoznaja da je događaj stigao važnija od kašnjenja povratnog putovanja.
Kanal koji samo pokreće događaje i ne nosi čitljive podatke valjan je obrazac – size vraća 0, a read vraća prazne bajtove. Biblioteci protokola i dalje trebaju obje metode prisutne kako bi označila kanal čitljivim; skripta na strani kamere u njega jednostavno nikada ne stavlja podatke.
13.3.1.5.4. Pogon prijemnog puta dok je u mirovanju¶
Događaji stižu istom vezom kao i sve ostalo, pa svaki poziv domaćina koji šalje ili prima bajtove daje prijenosnom sloju priliku da usput obradi događaje na čekanju. Petlja ispitivanja koja već poziva read_status() ili read_frame() jednom po ciklusu ne treba ništa dodatno.
Za programe koji prolaze minutama bez drugog U/I-a, poll_events() pokreće prijemni put jednom bez slanja naredbe. Vraća se čim je dolazni međuspremnik prazan, pa je uska petlja oko njega – ili kratki mjerač vremena u GUI petlji događaja – ono što obrađivače održava reaktivnima.
13.3.1.5.5. Potpuna petlja¶
Od početka do kraja, obrazac je: skripta na strani kamere registrira kanal i poziva send_event() kada se nešto dogodi; podklasa na strani domaćina nadjačava _handle_event() i raspodjeljuje. Petlja domaćina koja ne radi ništa osim opsluživanja događaja izgleda ovako:
with MyCamera('/dev/ttyACM0') as cam:
cam.stop()
cam.exec(open('motion_cam.py').read())
while True:
cam.poll_events()
Kamera snima, odlučuje i podiže događaje. Domaćin sjedi unutar poll_events() dok neki ne stigne, a zatim se pokreće on_motion. Nijedan poziv read_status() ne pokreće se kada se ništa nije dogodilo i nijedna sličica ne prenosi se preko USB-a kada kamera nema ništa za prijaviti.