13.3.1.5. Olaylar

Şimdiye kadarki sayfalar kameraya çağrı yapar: bir betik yükle, bir çerçeve oku, bir kanala yaz. Bu işlemlerin her biri ana makine kaynaklıdır; ana makine sorar, kamera yanıtlar. Protokol diğer yönde de çalışır. Kamera, istenmeden ana makineye olaylar itebilir ve ana makine SDK’sı her birini uygulamanın geçersiz kılabileceği bir geri çağırmaya (callback) iletir.

Bu, uygulama kameranın fark ettiği bir şeye, o şeyi sormadan önce tepki vermek istediğinde doğru araçtır. Olaylar olmadan, öğrenmenin tek yolu bir döngüde read_status() çağırmayı sürdürmektir.

13.3.1.5.1. Varsayılan geri çağırma

Camera, olaylara dahili olarak zaten abone olur. _handle_event(), bir olay paketi geldiğinde aktarım katmanının çalıştırdığı geri çağırmadır. Varsayılan, üç sistem olayını işler:

  • CHANNEL_REGISTERED; ana makine bağlandıktan sonra kamerada yeni bir kanal belirdi. Çerçeve, bir sonraki has_channel() aramasının kanalı bulması için kanal önbelleğini yeniler.

  • CHANNEL_UNREGISTERED; bir kanal kayboldu.

  • SOFT_REBOOT; kamera kendi başına yeniden başlatıldı (bekçi köpeği, ağır hata, kasıtlı machine.reset()).

Ayrıca akış yolu için stream kanalının çerçeve-hazır olayını ve stdout arabelleğe alma için stdin kanalının betik başlat / durdur olayını izler. Kurucunun events=True varsayılanı bunların tümünü açık tutar; bunların hiçbirini istemeyen bir uygulama Camera sınıfına events=False geçirebilir ve olay alt sistemi sessiz kalır.

13.3.1.5.2. Tepki vermek için alt sınıflandırma

Kameranın çıkardığı uygulamaya özgü olayları işlemek için Camera sınıfından türetin ve _handle_event() metodunu geçersiz kılın. Varsayılan davranışı korumak için önce üst sınıfı çağırın, ardından uygulamanın önemsediği olayları yönlendirin:

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

İmza (channel_id, event) şeklindedir. channel_id, sistem olayları için 0, aksi takdirde olayı çıkaran kanalın sayısal kimliğidir; event, kamera tarafı betiğin seçtiği bir tam sayıdır. EventType enum’ı üç sistem olayına ad verir; kanal olayları ise kamera tarafı arka ucun tanımladığı değerleri kullanır.

Kanal olayları, ada göre değil sayısal kimliğe göre anahtarlanmış olarak döner. Önbelleğe alınmış channels_by_id sözlüğü, yukarıdaki geçersiz kılmanın adı aramak için kullandığı şeydir; channels_by_name ise onun diğer yönde anahtarlanmış aynasıdır.

13.3.1.5.3. Kamera tarafı yarı

Kamera tarafı betik, protocol.register() çağrısından dönen tutamaç üzerinde send_event() çağırarak bir olay çıkarır:

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)

Olay numarası, uygulamanın seçtiği bir tam sayıdır. Ana makinenin geçersiz kılmasının işlemeye hazır olduğu herhangi bir değer kullanılabilir; protokol katmanı onu opak yük olarak ele alır. Varsayılan olarak çağrı ateşler ve unutur; gidiş-dönüşün gecikmesinden çok olayın ulaştığını bilmek önemli olduğunda, ana makine onaylayana kadar bloke olmak için wait_ack=True geçirin.

Yalnızca olay ateşleyen ve okunabilir veri taşımayan bir kanal geçerli bir örüntüdür; size, 0 döndürür ve read, boş bayt döndürür. Protokol kütüphanesinin yine de kanalı okunabilir olarak işaretlemek için her iki metodun da bulunmasına ihtiyacı vardır; kamera tarafı betik yalnızca içine hiç veri koymaz.

13.3.1.5.4. Boştayken alma yolunu sürme

Olaylar her şeyle aynı bağlantı üzerinden gelir; bu yüzden bayt gönderen veya alan herhangi bir ana makine çağrısı, aktarım katmanına bekleyen olayları satır içinde işleme fırsatı verir. Döngü başına bir kez read_status() veya read_frame() çağıran bir yoklama döngüsünün fazladan bir şeye ihtiyacı yoktur.

Başka bir I/O olmadan dakikalarca geçen programlar için, poll_events(), bir komut göndermeden alma yolunu bir kez çalıştırır. Gelen arabellek boşalır boşalmaz döner; bu yüzden onun etrafındaki sıkı bir döngü, ya da bir GUI olay döngüsündeki kısa bir zamanlayıcı, işleyicileri tepkisel tutan şeydir.

13.3.1.5.5. Eksiksiz bir döngü

Uçtan uca örüntü şöyledir: kamera tarafı betik bir kanal kaydeder ve bir şey olduğunda send_event() çağırır; ana makine tarafı alt sınıf _handle_event() metodunu geçersiz kılar ve yönlendirir. Olaylardan başka hiçbir şey yapmayan bir ana makine döngüsü şöyle görünür:

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

    while True:
        cam.poll_events()

Kamera yakalar, karar verir ve olaylar çıkarır. Ana makine, biri gelene kadar poll_events() içinde oturur, ardından on_motion çalışır. Hiçbir şey olmadığında hiçbir read_status() çağrısı çalışmaz ve kameranın bildirecek bir şeyi olmadığında USB üzerinden hiçbir çerçeve çekilmez.