13.3.1.5. Események¶
Az eddigi oldalak a kamera felé hívnak: szkript feltöltése, képkocka olvasása, csatornára írás. Ezen műveletek mindegyikét a gazdagép kezdeményezi – a gazdagép kérdez, a kamera válaszol. A protokoll a másik irányban is működik. A kamera eseményeket tolhat a gazdagépnek anélkül, hogy megkérdeznék, és a gazda SDK mindegyiket eljuttatja egy visszahíváshoz, amelyet az alkalmazás felülírhat.
Ez a megfelelő eszköz, amikor az alkalmazás reagálni szeretne valamire, amit a kamera észrevett, mielőtt rákérdezne. Események nélkül az egyetlen mód a kiderítésére az, hogy folyamatosan, ciklusban hívjuk a read_status() metódust.
13.3.1.5.1. Az alapértelmezett visszahívás¶
A Camera belsőleg már feliratkozik az eseményekre. A _handle_event() az a visszahívás, amelyet a transzportréteg lefuttat, valahányszor egy eseménycsomag megérkezik. Az alapértelmezett három rendszereseményt kezel:
CHANNEL_REGISTERED– egy új csatorna jelent meg a kamerán, miután a gazdagép csatlakozott. A keretrendszer frissíti a csatorna-gyorsítótárát, így a következőhas_channel()keresés megtalálja azt.CHANNEL_UNREGISTERED– egy csatorna eltűnt.SOFT_REBOOT– a kamera magától újraindult (watchdog, hardverhiba, szándékosmachine.reset()).
Nyomon követi a stream csatorna képkocka-kész eseményét is a streamelési útvonalhoz, valamint a stdin csatorna szkriptindítását/-leállítását az stdout-pufferezéshez. A konstruktor events=True alapértelmezése mindezt bekapcsolva tartja; egy alkalmazás, amely semmit sem akar belőle, átadhatja az events=False értéket a Camera osztálynak, és az eseményalrendszer csendben marad.
13.3.1.5.2. Alosztályozás a reagáláshoz¶
A kamera által kiváltott alkalmazásspecifikus események kezeléséhez hozz létre alosztályt a Camera osztályból, és írd felül a _handle_event() metódust. Hívd először a szülőt az alapértelmezett viselkedés megtartásához, majd irányítsd el azokat az eseményeket, amelyek az alkalmazást érdeklik:
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")
Az aláírás (channel_id, event). A channel_id rendszeresemények esetén 0, egyébként pedig annak a csatornának a numerikus azonosítója, amely kiváltotta; az event egy egész szám, amelyet a kameraoldali szkript választott. A EventType enum neveket ad a három rendszereseménynek; a csatornaesemények bármilyen értéket használnak, amelyet a kameraoldali backend definiál.
A csatornaesemények numerikus azonosító, nem pedig név szerint kulcsolva érkeznek vissza. A gyorsítótárazott channels_by_id szótár az, amelyet a fenti felülírás a név kikereséséhez használ; a channels_by_name ennek a tükre, a másik irányban kulcsolva.
13.3.1.5.3. A kameraoldali fél¶
A kameraoldali szkript egy esemény kiváltásához meghívja a send_event() metódust a protocol.register() által visszaadott azonosítón:
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)
Az eseményszám egy, az alkalmazás által választott egész szám. Bármilyen érték megengedett, amelyet a gazdagép felülírása kezelni képes; a protokollréteg átlátszatlan hasznos teherként kezeli. Alapértelmezés szerint a hívás kiküld és elfelejt; add meg a wait_ack=True értéket, hogy blokkoljon, amíg a gazdagép visszaigazol, amikor annak ismerete, hogy az esemény megérkezett, fontosabb, mint az oda-vissza út késleltetése.
Egy csatorna, amely csak eseményeket vált ki és nem hordoz olvasható adatot, érvényes minta – a size 0 értéket ad vissza, a read pedig üres bájtokat. A protokollkönyvtárnak ennek ellenére mindkét metódusra szüksége van ahhoz, hogy a csatornát olvashatóként jelölje; a kameraoldali szkript egyszerűen soha nem tesz bele adatot.
13.3.1.5.4. A fogadási útvonal hajtása tétlenség közben¶
Az események ugyanazon a kapcsolaton érkeznek, mint minden más, így bármely gazdahívás, amely bájtokat küld vagy fogad, lehetőséget ad a transzportrétegnek a függőben lévő események menet közbeni feldolgozására. Egy lekérdező ciklus, amely már ciklusonként egyszer meghívja a read_status() vagy a read_frame() metódust, nem igényel semmi extrát.
Olyan programok esetén, amelyek percekig elvannak más I/O nélkül, a poll_events() egyszer lefuttatja a fogadási útvonalat parancs küldése nélkül. Amint a bejövő puffer kiürül, visszatér, így egy körülötte futó szoros ciklus – vagy egy rövid időzítő egy GUI eseményciklusban – az, ami a kezelőket reagálóképesen tartja.
13.3.1.5.5. Egy teljes ciklus¶
Végponttól végpontig a minta a következő: a kameraoldali szkript regisztrál egy csatornát, és meghívja a send_event() metódust, amikor valami történik; a gazdaoldali alosztály felülírja a _handle_event() metódust és elirányítja. Egy gazdaciklus, amely nem tesz mást, csak az eseményeket szolgálja ki, így néz ki:
with MyCamera('/dev/ttyACM0') as cam:
cam.stop()
cam.exec(open('motion_cam.py').read())
while True:
cam.poll_events()
A kamera rögzít, dönt és eseményeket vált ki. A gazdagép a poll_events() metóduson belül ül, amíg egy meg nem érkezik, majd lefut az on_motion. Egyetlen read_status() hívás sem fut, amikor semmi nem történt, és egyetlen képkockát sem húzunk át az USB-n, amikor a kamerának nincs semmi jelentenivalója.