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 ไบต์ตามค่าเริ่มต้น เมธอดฝั่งโฮสต์แบ่งการเขียนขนาดใหญ่ขึ้นโดยอัตโนมัติเป็นจำนวนแพ็กเก็ตที่เหมาะสม ดังนั้นแอปพลิเคชันสามารถส่งบัฟเฟอร์ขนาดใหญ่ตามอำเภอใจ; การแตกส่วนเป็นสิ่งที่มองไม่เห็น