13.3.1.5. Tapahtumat

Tähän mennessä sivut kutsuvat kameraan: lataavat skriptin, lukevat kehyksen, kirjoittavat kanavaan. Jokainen näistä toiminnoista on isännän aloittama – isäntä kysyy, kamera vastaa. Protokolla toimii myös toiseen suuntaan. Kamera voi työntää tapahtumia isännälle ilman pyyntöä, ja isäntä-SDK toimittaa jokaisen takaisinkutsulle, jonka sovellus voi ohittaa.

Tämä on oikea työkalu aina, kun sovellus haluaa reagoida johonkin, jonka kamera huomasi ennen kuin se kysyy. Ilman tapahtumia ainoa tapa saada tieto on kutsua jatkuvasti read_status() silmukassa.

13.3.1.5.1. Oletustakaisinkutsu

Camera tilaa tapahtumat jo sisäisesti. _handle_event() on takaisinkutsu, jonka kuljetuskerros suorittaa aina, kun tapahtumapaketti saapuu. Oletus käsittelee kolme järjestelmätapahtumaa:

  • CHANNEL_REGISTERED – uusi kanava ilmestyi kameralle sen jälkeen, kun isäntä yhdisti. Kehys päivittää kanavavälimuistinsa, jotta seuraava has_channel() -haku löytää sen.

  • CHANNEL_UNREGISTERED – kanava katosi.

  • SOFT_REBOOT – kamera käynnistyi uudelleen omasta aloitteestaan (vahtikoira, vakava virhe, tarkoituksellinen machine.reset()).

Se myös seuraa stream-kanavan kehys-valmis-tapahtumaa suoratoistopolulle ja stdin-kanavan skriptin käynnistystä / pysäytystä stdout-puskurointia varten. Rakentajan events=True-oletus pitää kaiken tämän päällä; sovellus, joka ei halua mitään näistä, voi välittää events=False luokalle Camera, jolloin tapahtuma-alijärjestelmä pysyy hiljaisena.

13.3.1.5.2. Aliluokitus reagointia varten

Käsitelläksesi sovelluskohtaisia tapahtumia, jotka kamera nostaa, aliluokita Camera ja ohita _handle_event(). Kutsu ensin vanhempaa säilyttääksesi oletustoiminnan, ja lähetä sitten sovelluksen kiinnostavat tapahtumat:

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")

Allekirjoitus on (channel_id, event). channel_id on 0 järjestelmätapahtumille ja muutoin sen kanavan numeerinen tunnus, joka nosti sen; event on kokonaisluku, jonka kameran skripti valitsi. EventType-luettelo antaa nimet kolmelle järjestelmätapahtumalle; kanavatapahtumat käyttävät mitä tahansa arvoja, jotka kameran taustajärjestelmä määrittelee.

Kanavatapahtumat palaavat numeerisen tunnuksen, ei nimen, perusteella avaintettuina. Välimuistissa oleva channels_by_id-sanakirja on se, jota yllä oleva ohitus käyttää nimen hakemiseen; channels_by_name on sen peilikuva, avaintettuna toiseen suuntaan.

13.3.1.5.3. Kameran puolisko

Kameran skripti nostaa tapahtuman kutsumalla send_event() kahvasta, joka palautettiin kutsusta 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)

Tapahtumanumero on sovelluksen valitsema kokonaisluku. Mikä tahansa arvo, jonka isännän ohitus on valmis käsittelemään, on sallittua; protokollakerros käsittelee sitä läpinäkymättömänä hyötykuormana. Oletuksena kutsu laukaisee ja unohtaa; anna wait_ack=True estääksesi suorituksen, kunnes isäntä kuittaa, kun tapahtuman perille saapumisen tietäminen on tärkeämpää kuin edestakaisen matkan viive.

Kanava, joka vain laukaisee tapahtumia eikä kuljeta luettavaa dataa, on kelvollinen malli – size palauttaa 0 ja read palauttaa tyhjät tavut. Protokollakirjasto tarvitsee silti molemmat metodit läsnä merkitäkseen kanavan luettavaksi; kameran skripti ei vain koskaan laita siihen dataa.

13.3.1.5.4. Vastaanottopolun ajaminen joutilaana

Tapahtumat saapuvat samalla yhteydellä kuin kaikki muukin, joten mikä tahansa isäntäkutsu, joka lähettää tai vastaanottaa tavuja, antaa kuljetuskerrokselle mahdollisuuden käsitellä odottavat tapahtumat sisäkkäin. Kyselysilmukka, joka jo kutsuu read_status() tai read_frame() kerran sykliä kohden, ei tarvitse mitään ylimääräistä.

Ohjelmille, jotka kuluttavat minuutteja ilman muuta I/O:ta, poll_events() ajaa vastaanottopolun kerran ilman komennon lähettämistä. Se palaa heti, kun saapuva puskuri on tyhjä, joten tiukka silmukka sen ympärillä – tai lyhyt ajastin GUI:n tapahtumasilmukassa – pitää käsittelijät reagoivina.

13.3.1.5.5. Täydellinen silmukka

Päästä päähän malli on: kameran skripti rekisteröi kanavan ja kutsuu send_event(), kun jotain tapahtuu; isäntäpuolen aliluokka ohittaa _handle_event() ja lähettää. Isäntäsilmukka, joka ei tee mitään muuta kuin palvelee tapahtumia, näyttää tältä:

with MyCamera('/dev/ttyACM0') as cam:
    cam.stop()
    cam.exec(open('motion_cam.py').read())

    while True:
        cam.poll_events()

Kamera kaappaa, päättää ja nostaa tapahtumia. Isäntä istuu sisällä metodissa poll_events(), kunnes yksi saapuu, ja sitten on_motion suoritetaan. Mitään read_status() -kutsua ei suoriteta, kun mitään ei ole tapahtunut, eikä yhtään kehystä vedetä USB:n yli, kun kameralla ei ole mitään raportoitavaa.