12.9. การไหลสองทิศทาง¶
ช่องไม่ได้เป็นทิศทางเดียว แบ็กเอนด์ที่ใช้งาน write ให้โฮสต์ส่งไบต์ ไปยัง กล้องได้ และกล้องจะตอบสนอง นั่นคือรูปแบบเบื้องหลังเครื่องมือโต้ตอบจริงทุกตัว: ผู้ปฏิบัติงานหมุนปุ่มบน GUI ของโฮสต์ โฮสต์เขียนค่าใหม่ไปยังช่อง config กล้องอ่านค่าในครั้งถัดไปที่ถ่าย
12.9.1. ช่อง config¶
เพิ่มสคริปต์ฝั่งกล้องสตรีมมิ่ง โดยเปิดเผยช่องที่สองสำหรับคุณภาพ JPEG:
class ConfigChannel:
def __init__(self):
self.quality = 85
def size(self):
return 0
def read(self, offset, size):
# Not used for "host writes to cam" -- but the library
# still needs the method present.
return b''
def write(self, offset, data):
# data is a bytearray view into the protocol buffer.
# Copy out the contents before doing anything with it.
new_q = int(bytes(data))
if 1 <= new_q <= 100:
self.quality = new_q
return len(data)
config = ConfigChannel()
protocol.register(name='config', backend=config)
ลูปการถ่ายภาพอ่านจาก config.quality เมื่อใดก็ตามที่บีบอัดเฟรม:
while True:
img = csi0.snapshot()
latest_jpeg = bytes(
img.compress(quality=config.quality).bytearray()
)
ch.send_event(0x01)
โฮสต์ตอนนี้มีปุ่มควบคุม ตั้งค่าเป็น 50 และเฟรมถัดไปจะเล็กลง (และดูไม่สวย) ตั้งค่าเป็น 95 และเฟรมถัดไปจะใหญ่ขึ้น (และคมชัดขึ้น) กล้องยังคงถ่ายต่อไปโดยไม่ต้องรีสตาร์ท โฮสต์ไม่ต้องส่งสคริปต์ใหม่
12.9.2. การเรียก write จากโฮสต์¶
ฝั่งโฮสต์ channel_write() ส่งไบต์ไปยังช่องที่มีชื่อ:
cam.channel_write('config', b'50')
SDK ของโฮสต์เข้ารหัสไบต์เป็นแพ็กเก็ต CHANNEL_WRITE เดี่ยว (หรือหลายส่วน) ชั้นโปรโตคอลส่งมอบไปยังกล้อง write(offset=0, data=...) ของกล้องทำงาน และฝั่งกล้องตอบรับ เมื่อการเรียกคืนค่าแล้ว กล้องได้รับและยอมรับค่าใหม่แล้ว
การเขียนเป็นแบบ atomic จากมุมมองของกล้อง -- ไลบรารีโปรโตคอลรับประกันว่า write ของแบ็กเอนด์ทำงานจนเสร็จสมบูรณ์ก่อนที่การดำเนินการอื่น ๆ บนช่องนั้นจะดำเนินต่อ โค้ดแอปพลิเคชันสามารถอ่าน config.quality จากภายในลูปการถ่ายภาพโดยไม่ต้องกังวลว่าโฮสต์จะมาแทรกกลางสแนปช็อต
12.9.3. ขนาด stub และการอ่านบนช่องที่เขียนอย่างเดียว¶
ช่องที่เขียนอย่างเดียวยังต้องการ size และ read ที่กำหนดไว้ แม้ว่าจะเป็นแค่ stub ที่คืน 0 และ b'' ไลบรารีใช้ การมีอยู่ ของเมธอดเพื่อหาค่าแฟล็กความสามารถของช่อง แบ็กเอนด์ที่ขาด read จะไม่ได้รับการตั้งค่า CHANNEL_FLAG_READ และโฮสต์จะปฏิเสธการพยายามอ่าน
ไบต์ที่คืนมาจาก read บนช่องที่เขียนอย่างเดียวมีประโยชน์สำหรับจุดประสงค์ที่แตกต่างออกไป: การสะท้อนค่าปัจจุบันกลับมาเพื่อให้โฮสต์ที่เพิ่งเชื่อมต่อสามารถถามกล้องว่า "การตั้งค่าปัจจุบันคืออะไร?" แทนที่จะเริ่มจากค่าเริ่มต้น เพื่อให้สิ่งนี้ทำงานได้ทั้งสองทิศทางต้องตกลงกันเรื่องการซีเรียลไลเซชัน การแยกวิเคราะห์ raw-bytes int(bytes(data)) ในตัวอย่างก่อนหน้าทำงานได้สำหรับฟิลด์จำนวนเต็มเดี่ยวแต่จะไม่ขยายเมื่อมีปุ่มที่สองต้องตั้งค่า การเปลี่ยน write ให้แยกวิเคราะห์ JSON และจับคู่กับ read ที่คืน JSON dump ที่ตรงกันเปลี่ยนช่องให้กลายเป็นพื้นที่เก็บค่า config แบบ round-trip จริง:
import json
class ConfigChannel:
def __init__(self):
self.quality = 85
self._buf = b''
def size(self):
self._buf = json.dumps({'quality': self.quality}).encode()
return len(self._buf)
def read(self, offset, size):
return self._buf[offset:offset + size]
def write(self, offset, data):
new = json.loads(bytes(data))
if 'quality' in new:
self.quality = int(new['quality'])
return len(data)
ตอนนี้โฮสต์เขียน cam.channel_write('config', b'{"quality": 50}') เพื่อตั้งค่าและ cam.channel_read('config') เพื่ออ่านสถานะปัจจุบันกลับมา กล้องซีเรียลไลซ์ JSON dump ใหม่ในทุกการอ่านเพื่อให้โฮสต์เห็นค่าล่าสุดเสมอ และการเพิ่มปุ่มอีกอัน (threshold, exposure, orientation) คือหนึ่งบรรทัดใน dict JSON ในแต่ละฝั่ง
12.9.4. ลูปสมบูรณ์¶
ด้วยช่องเฟรมสำหรับข้อมูลกล้อง → โฮสต์ ช่อง config สำหรับควบคุมโฮสต์ → กล้อง และกาวเล็กน้อย แอปพลิเคชันกลายเป็นเครื่องมือโต้ตอบ:
โฮสต์เปิดกล้อง เริ่มดึงเฟรม และแสดงในหน้าต่าง
เมื่อผู้ปฏิบัติงานลาก slider โฮสต์เขียนค่าใหม่บน
configลูปการถ่ายภาพของกล้องรับค่าในเฟรมถัดไป
เฟรมใหม่ไหลผ่านช่อง
frameเดิม
นั่นคือโมเดลทั้งหมด สองช่อง สองคอลแบ็กแต่ละช่อง ลูปการถ่ายภาพบนกล้อง ลูปอ่านและเขียนบนโฮสต์ ไม่มีตรรกะการจัดกรอบให้เห็น ไม่มีการจัดการข้อผิดพลาดให้เห็น -- ไลบรารีโปรโตคอลทำให้การเคลื่อนย้ายไบต์ที่เชื่อถือได้หายไป
ทุกอย่างหลังจากจุดนี้คือโค้ดแอปพลิเคชัน การเพิ่มช่องที่สามสำหรับฮิสโตแกรม ช่องที่สี่สำหรับข้อมูลวัด หรือช่องที่ห้าสำหรับทริกเกอร์เซนเซอร์คือสูตร backend-class-and-protocol.register เดิม ที่ทำซ้ำ เมื่อโปรเจกต์กล้องถึงจุดนี้ โปรโตคอลก็หยุดเป็นปัญหาที่น่าสนใจ ตรรกะของแอปพลิเคชันเองต่างหากที่ทำ