13.3.1.5. เหตุการณ์

หน้าที่ผ่านมาเรียกใช้กล้อง: อัปโหลดสคริปต์ อ่านเฟรม เขียนไปยังช่องสัญญาณ ทุกการดำเนินการเหล่านั้นเริ่มต้นโดยโฮสต์ -- โฮสต์ถาม กล้องตอบ โปรโตคอลยังทำงานในทิศทางอื่นด้วย กล้องสามารถ push เหตุการณ์ไปยังโฮสต์โดยไม่ถูกถาม และ SDK โฮสต์ส่งแต่ละอันไปยังคอลแบ็กที่แอปพลิเคชันสามารถแทนที่ได้

นี่คือเครื่องมือที่เหมาะสมเมื่อแอปพลิเคชันต้องการตอบสนองต่อสิ่งที่กล้องสังเกตเห็นก่อนที่จะถาม หากไม่มีเหตุการณ์ วิธีเดียวที่จะหาคือต้องเรียก read_status() ในลูปต่อเนื่อง

13.3.1.5.1. คอลแบ็กเริ่มต้น

Camera สมัครสมาชิกเหตุการณ์ภายในแล้ว _handle_event() คือคอลแบ็กที่ transport รันเมื่อใดก็ตามที่แพ็กเก็ตเหตุการณ์มาถึง ค่าเริ่มต้นจัดการเหตุการณ์ระบบสาม:

  • CHANNEL_REGISTERED -- ช่องสัญญาณใหม่ปรากฏบนกล้องหลังจากโฮสต์เชื่อมต่อ framework รีเฟรชแคชช่องสัญญาณเพื่อให้การค้นหา has_channel() ถัดไปพบมัน

  • CHANNEL_UNREGISTERED -- ช่องสัญญาณหายไป

  • SOFT_REBOOT -- กล้องรีบูตเอง (watchdog, hard fault, machine.reset() ที่ตั้งใจ)

นอกจากนี้ยังติดตามเหตุการณ์เฟรมพร้อมของช่องสัญญาณ stream สำหรับเส้นทางสตรีมมิ่งและการเริ่ม/หยุดสคริปต์ของช่องสัญญาณ stdin สำหรับการบัฟเฟอร์ stdout ค่าเริ่มต้น events=True ของ constructor เปิดสิ่งนี้ทั้งหมด; แอปพลิเคชันที่ไม่ต้องการสิ่งใดสามารถส่ง events=False ไปยัง Camera และระบบย่อยเหตุการณ์จะเงียบ

13.3.1.5.2. การสร้าง subclass เพื่อตอบสนอง

เพื่อจัดการเหตุการณ์เฉพาะแอปพลิเคชันที่กล้องยกขึ้น ให้สร้าง subclass Camera และแทนที่ _handle_event() เรียก parent ก่อนเพื่อรักษาพฤติกรรมเริ่มต้น จากนั้น dispatch เหตุการณ์ที่แอปพลิเคชันสนใจ:

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

ลายเซ็นคือ (channel_id, event) channel_id คือ 0 สำหรับเหตุการณ์ระบบและมิฉะนั้นเป็น ID ตัวเลขของช่องสัญญาณที่ยกมัน; event คือจำนวนเต็มที่สคริปต์ฝั่งกล้องเลือก enum EventType ตั้งชื่อให้กับเหตุการณ์ระบบสาม; เหตุการณ์ช่องสัญญาณใช้ค่าใดก็ตามที่ backend ฝั่งกล้องกำหนด

เหตุการณ์ช่องสัญญาณกลับมาโดยตรวจสอบด้วย ID ตัวเลข ไม่ใช่ชื่อ dict channels_by_id ที่แคชไว้คือสิ่งที่ override ข้างต้นใช้เพื่อค้นหาชื่อ; channels_by_name คือกระจกของมัน โดยมีกุญแจอีกทาง

13.3.1.5.3. ฝั่งกล้อง

สคริปต์ฝั่งกล้องยกเหตุการณ์โดยเรียก send_event() บน handle ที่ส่งคืนจาก 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)

หมายเลขเหตุการณ์เป็นจำนวนเต็มที่แอปพลิเคชันเลือก ค่าใดก็ตามที่ override ของโฮสต์พร้อมรองรับเป็นสิ่งที่ยอมรับได้; ชั้นโปรโตคอลถือว่ามันเป็นเพย์โหลดทึบ ตามค่าเริ่มต้นการเรียกจะ fire และลืม; ส่ง wait_ack=True เพื่อบล็อกจนกว่าโฮสต์จะยอมรับ เมื่อการรู้ว่าเหตุการณ์ถึงสำคัญกว่าเวลาแฝงของรอบ

ช่องสัญญาณที่ fire เหตุการณ์เท่านั้นและไม่มีข้อมูลที่อ่านได้เป็นรูปแบบที่ถูกต้อง -- size ส่งคืน 0 และ read ส่งคืนไบต์ว่าง ไลบรารีโปรโตคอลยังต้องการทั้งสองเมธอดเพื่อทำเครื่องหมายช่องสัญญาณว่าอ่านได้; สคริปต์ฝั่งกล้องเพียงแค่ไม่เคยใส่ข้อมูลในมัน

13.3.1.5.4. ขับเส้นทางรับขณะไม่ได้ใช้งาน

เหตุการณ์มาถึงบนการเชื่อมต่อเดิมกับทุกอย่างอื่น ดังนั้นการเรียกโฮสต์ใดก็ตามที่ส่งหรือรับไบต์ให้ transport มีโอกาสประมวลผลเหตุการณ์ที่รออยู่แบบ inline ลูปโพลที่เรียก read_status() หรือ read_frame() หนึ่งครั้งต่อรอบไม่ต้องการอะไรพิเศษ

สำหรับโปรแกรมที่ไปหลายนาทีโดยไม่มี I/O อื่น poll_events() รันเส้นทางรับหนึ่งครั้งโดยไม่ส่งคำสั่ง มันส่งคืนทันทีที่บัฟเฟอร์ขาเข้าว่าง ดังนั้นลูปที่แน่นรอบมัน -- หรือตัวจับเวลาสั้นในลูปเหตุการณ์ GUI -- คือสิ่งที่ทำให้ handler ตอบสนอง

13.3.1.5.5. ลูปสมบูรณ์

แบบ end-to-end รูปแบบคือ: สคริปต์ฝั่งกล้องลงทะเบียนช่องสัญญาณและเรียก send_event() เมื่อมีบางอย่างเกิดขึ้น; subclass ฝั่งโฮสต์แทนที่ _handle_event() และ dispatch ลูปโฮสต์ที่ไม่ทำอะไรนอกจากรับเหตุการณ์มีลักษณะดังนี้:

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

    while True:
        cam.poll_events()

กล้องจับภาพ ตัดสิน และยกเหตุการณ์ โฮสต์นั่งใน poll_events() จนกว่าจะมาถึง จากนั้น on_motion รัน ไม่มีการเรียก read_status() เมื่อไม่มีอะไรเกิดขึ้น และไม่มีเฟรมถูกดึงข้าม USB เมื่อกล้องไม่มีอะไรรายงาน