12.6. Іменовані канали¶
Ідентифікатор каналу у заголовку кожного пакету дозволяє до 32 незалежних потоків спільно використовувати один фізичний транспорт. Рівень каналів перетворює ці числові ідентифікатори на іменовані кінцеві точки, видимі застосунку, до яких хостовий код може звертатися за рядком.
12.6.1. Чотири вбудованих канали¶
Камера реєструє чотири канали під час завантаження, до виконання будь-якого коду застосунку:
stdin– байти скрипту, які хост надсилає до камери для виконання. IDE використовує цей канал для надсилання скрипту, що редагується;exec()у SDK хоста – еквівалентний виклик з програми Python.stdout– байти від викликівprint()на камері та трасування необроблених винятків. Серійна консоль IDE зчитує цей канал.stream– канал живого попереднього перегляду. IDE отримує з нього кадри JPEG; будь-який хостовий скрипт може робити те саме за допомогоюread_frame().profile– події профайлера, наявні лише тоді, коли камеру було зібрано з увімкненим профілюванням. Більшість релізних збірок його не містять.
Код застосунку рідко потребує взаємодії з будь-яким із вбудованих каналів; цікава робота відбувається на каналах, які реєструє сам застосунок.
12.6.2. Реєстрація каналу¶
Скрипт на стороні камери реєструє новий канал, викликаючи protocol.register() з іменем та об’єктом бекенду Python:
import json
import protocol
import time
trigger_count = 0
class StatusChannel:
def size(self):
# Refresh the snapshot on every host query.
self._buf = json.dumps({
'uptime_s': time.ticks_ms() // 1000,
'triggers': trigger_count,
}).encode()
return len(self._buf)
def read(self, offset, size):
return self._buf[offset:offset + size]
protocol.register(name='status', backend=StatusChannel())
Методи об’єкта бекенду визначають, що може робити канал. Бекенд лише з size та read є каналом даних лише для читання; додавання write робить його двонаправленим; додавання poll дозволяє хосту перевірити наявність нових даних перед оплатою зчитування. Вибірка даних всередині size – найпростіший шаблон, коли корисне навантаження достатньо мале, щоб вмістися в одному фрагменті – буфер генерується на вимогу, ніколи не кешується, ніколи не стикається із проблемами конкурентного доступу. Більші корисні навантаження – кадри зображення, трасування датчиків – потребують шаблону фіксації, що утримує буфер до завершення багатофрагментного зчитування хостом, що розглядається у розділі про канал кадрів.
Невелика кількість операцій ведення обліку відбувається автоматично:
Бібліотека призначає наступний вільний ідентифікатор каналу (між 0 та 31).
Прапори можливостей визначаються на основі присутніх методів:
CHANNEL_FLAG_READ, якщо визначеноread,CHANNEL_FLAG_WRITE, якщо визначеноwrite,CHANNEL_FLAG_LOCK, якщо визначеноlock/unlock.Пакет події
CHANNEL_REGISTEREDнадсилається до будь-якого підключеного хоста, щоб оновити його список каналів.
Повернене значення – це дескриптор protocol.ProtocolChannel, який застосунок може зберегти. Метод send_event() дескриптора є гачком на стороні камери для повідомлення хоста «на цьому каналі щось сталося без зміни читабельних даних» – спрацювало тригер, натиснуто кнопку, досягнуто певної кількості зразків.
12.6.3. Зчитування каналів із хоста¶
SDK хоста постачається як пакет openmv на PyPI (pip install openmv), побудований на pyserial для транспорту. Його клас openmv.camera.Camera надає доступ до іменованих каналів камери через методи високого рівня:
from openmv.camera import Camera
with Camera('/dev/ttyACM0', baudrate=921600) as cam:
cam.update_channels()
if cam.has_channel('status'):
size = cam.channel_size('status')
data = cam.channel_read('status', size)
Попередження
Пакет openmv потребує CPython 3.12 або новішої версії. Попередні інтерпретатори не мають функцій, від яких залежить SDK; встановіть збірку 3.12+ перед виконанням pip install openmv.
Кілька речей, на які варто звернути увагу під час налаштування:
Рядок послідовного порту – тут
/dev/ttyACM0– має виглядCOM3на Windows,/dev/cu.usbmodemXXXXна macOS і/dev/ttyACM*на Linux. Конкретний номер залежить від того, яким портом була зареєстрована камера.Швидкість передачі (бод) – це магічне значення протоколу
921600, яке USB-CDC стек камери розпізнає як «цей клієнт використовує протокол, а не REPL». Будь-яка інша швидкість відкочується до звичайної серійної лінії.Контекстний менеджер
with Camera(...) as cam:відкриває транспорт, виконуєPROTO_SYNC, обмінюється можливостями, а при виході коректно закриває порт. Явний викликupdate_channels()після входу оновлює локальний список каналів усіма каналами, які застосунок зареєстрував після завантаження.
channel_size() та channel_read() – це основні робочі методи; channel_write() передає буфер до камери, якщо бекенд має метод write; has_channel() – безпечний спосіб перевірити, що ім’я зареєстровано, перш ніж використовувати його. Ім’я каналу одноразово перетворюється на ідентифікатор каналу, призначений камерою під час register, і використовується у кожному пакеті надалі.
Кожна пара channel_size() / channel_read() коштує два зворотні зв’язки: один пакет для запиту розміру, один для запиту байтів. По USB-CDC обидва завершуються приблизно за мілісекунду разом; по UART той самий обмін займає довше пропорційно швидкості передачі серійної лінії (бод). Код застосунку, що зчитує у тісному циклі, повинен викликати channel_size() лише тоді, коли розмір може фактично змінитися – для даних фіксованого розміру значення з першого виклику можна кешувати.
12.6.4. Незалежність між каналами¶
Варто знати три речі про взаємодію каналів:
Незалежне керування потоком. Кожен канал має власний стан очікуваного зчитування, власні дані та власні зворотні виклики
size/read/write. Тривале зчитування на каналіstreamне блокує зчитування на каналіconfigзастосунку.Послідовність у межах каналу. Всередині одного каналу пакети доставляються в порядку надходження. Рівень надійності гарантує це навіть при повторних передачах.
Спільний транспорт, спільний бюджет повторних передач. Усі канали спільно використовують один фізичний канал зв’язку, тому значний трафік на одному каналі сповільнює інші, монополізуючи лінію. Механізм
CHANNEL_LOCKдозволяє одному каналу зарезервувати лінію для атомарного багатопакетного зчитування; бекенд підключається до цього, реалізуючи зворотні викликиlock/unlock.
Канал – це мінімальна поверхня взаємодії, на якій хостова програма та програма камери погоджуються співпрацювати. Ім’я, напрямність (читання, запис або обидва), методи зворотних викликів на стороні камери та відповідні виклики методів на стороні хоста – це весь контракт.