13.3.1.4. ช่องสัญญาณกำหนดเอง

ช่องสัญญาณ คือสตรีมไบต์สองทิศทางที่มีชื่อระหว่างสคริปต์ฝั่งกล้องและโฮสต์ กล้องลงทะเบียนช่องสัญญาณและให้คอลแบ็กที่สร้างหรือบริโภคข้อมูล; โฮสต์อ่านจากและเขียนไปยังช่องสัญญาณนั้นตามชื่อ กลไกเดียวกันที่แพ็กเกจใช้ภายในสำหรับช่องสัญญาณ stream ที่นำเฟรม ช่องสัญญาณ stdout ที่นำเอาต์พุตสคริปต์ และช่องสัญญาณ stdin ที่นำการอัปโหลดสคริปต์นั้นเปิดเผยให้กับสคริปต์ผู้ใช้ ดังนั้นข้อมูลเฉพาะแอปพลิเคชันใดๆ ที่โฮสต์ต้องการสามารถใช้การเชื่อมต่อ USB เดิมโดยไม่ต้องประดิษฐ์โปรโตคอลที่สอง

นี่คือคุณลักษณะที่มีประโยชน์ที่สุดของแพ็กเกจและที่เอกสารมาตรฐานครอบคลุมน้อยที่สุด ดังนั้นหน้านี้จะผ่านกระบวนการตั้งแต่ต้นจนจบ

13.3.1.4.1. สองส่วน

ช่องสัญญาณกำหนดเองต้องการโค้ดที่ทำงานร่วมกันทั้งสองฝั่ง สคริปต์ฝั่งกล้อง นำเข้า protocol กำหนดคลาสที่มีสามเมธอด (size(), read(), poll()) บวกกับ write() เสริม และเรียก protocol.register(name=..., backend=...) เพื่อเผยแพร่ช่องสัญญาณภายใต้ชื่อที่เลือก:

import protocol
import time

class TicksChannel:
    def size(self):
        return 10

    def read(self, offset, size):
        return f'{time.ticks_ms():010d}'

    def poll(self):
        return True

protocol.register(name='ticks', backend=TicksChannel())

เมธอด size() ส่งคืนจำนวนไบต์ที่ช่องสัญญาณมีพร้อมในขณะนี้ read() คือผู้ผลิต: ให้ offset และ size ที่โฮสต์ร้องขอ มันส่งคืนไบต์ (หรือสตริงที่ชั้นโปรโตคอลเข้ารหัส) poll() ส่งคืน True เมื่อมีสิ่งที่จะอ่าน -- ชั้นโปรโตคอลใช้สิ่งนี้เพื่อทำเครื่องหมายช่องสัญญาณว่าพร้อมใน read_status()

โปรแกรมฝั่งโฮสต์ ใช้สี่เมธอด openmv.Camera: has_channel() เพื่อตรวจสอบว่าช่องสัญญาณมีอยู่, channel_size() เพื่อถามว่ามีข้อมูลรออยู่เท่าไร, channel_read() เพื่อดึงไบต์ออก และ channel_write() เพื่อดันไบต์เข้า read_status() โพลทุกช่องสัญญาณพร้อมกัน:

from openmv import Camera

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

    while True:
        status = cam.read_status()

        if status.get('ticks'):
            data = cam.channel_read('ticks')
            print(f"ticks: {data.decode()}")

ลูปโฮสต์โพล read_status(); เมื่อช่องสัญญาณ ticks พร้อมก็จะเรียก channel_read() โดยไม่มี size เพื่อดึงสิ่งที่พร้อมใช้งาน TicksChannel.poll() ของกล้องส่งคืน True ในทุกการตรวจสอบ ดังนั้นช่องสัญญาณจึง "พร้อม" เสมอและโฮสต์ได้รับค่า tick ใหม่ทุกครั้งที่โพล

13.3.1.4.2. ช่องสัญญาณสองทิศทาง

สำหรับโฮสต์ที่ต้องการส่งข้อมูลกลับ คลาสฝั่งกล้องเพิ่มเมธอด write() ที่รับไบต์ที่เข้ามา:

import protocol

class CommandChannel:
    def __init__(self):
        self.last_command = b''
        self.replied = False

    def size(self):
        return len(self.last_command)

    def read(self, offset, size):
        self.replied = True
        return self.last_command

    def write(self, offset, data):
        self.last_command = b'echo: ' + bytes(data)
        self.replied = False

    def poll(self):
        return not self.replied and len(self.last_command) > 0

protocol.register(name='echo', backend=CommandChannel())

โฮสต์เขียนไปยังช่องสัญญาณด้วย channel_write() และอ่านการตอบกลับผ่านรูปแบบปกติ read_status() / channel_read()

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

    cam.channel_write('echo', b'hello')

    while True:
        if cam.read_status().get('echo'):
            print(cam.channel_read('echo').decode())
            break

13.3.1.4.3. สิ่งที่แอปพลิเคชันได้รับจากนี้

ช่องสัญญาณกำหนดเองเป็นเครื่องมือที่เหมาะสมเมื่อแอปพลิเคชันต้องการใช้การเชื่อมต่อ USB ที่มีอยู่สำหรับข้อมูลที่ไม่ใช่เฟรม ไม่ใช่การพิมพ์: ตัวนับการวัด ปุ่มกำหนดค่าที่สตรีมสดจาก UI บนโฮสต์ คำสั่งควบคุมที่ส่งไปอีกทาง ผลลัพธ์ของการวัดที่กล้องคำนวณซึ่งไม่เหมาะกับการกรอบ "image" ที่ช่องสัญญาณสตรีมกำหนด ชั้นโปรโตคอลจัดการการกรอบ การแตกส่วน การรับทราบ และการลองใหม่; สคริปต์เพียงแค่ต้องใช้ backend สี่เมธอด และโฮสต์เพียงแค่ต้องรู้ชื่อช่องสัญญาณและรูปร่างข้อมูล

แฟล็ก --channel NAME ของ CLI เป็นวิธีที่รวดเร็วในการตรวจสอบช่องสัญญาณกำหนดเองจากเทอร์มินัลโดยไม่ต้องเขียนโปรแกรมฝั่งโฮสต์: CLI โพลช่องสัญญาณที่ระบุชื่อและพิมพ์สิบไบต์แรกของแต่ละการอัปเดต

ขีดจำกัดขนาดบนการเรียก channel_read() หรือ channel_write() เดี่ยวคือ max_payload ที่เจรจาของโปรโตคอล -- 4096 ไบต์ตามค่าเริ่มต้น เมธอดฝั่งโฮสต์แบ่งการเขียนขนาดใหญ่ขึ้นโดยอัตโนมัติเป็นจำนวนแพ็กเก็ตที่เหมาะสม ดังนั้นแอปพลิเคชันสามารถส่งบัฟเฟอร์ขนาดใหญ่ตามอำเภอใจ; การแตกส่วนเป็นสิ่งที่มองไม่เห็น