12.2. Четыре уровня¶
Библиотека протокола построена как стек из четырёх уровней, каждый из которых решает одну задачу и опирается на нижележащий уровень. Оставшаяся часть главы проходит по стеку снизу вверх.
12.2.1. Транспорт¶
В самом низу находится байтовый канал между камерой и хостом. Библиотеке протокола неважно, кто именно переносит байты:
USB-CDC через USB-порт, в который подключена камера. Вариант по умолчанию и с наибольшей пропускной способностью для каждой камеры.
UART через пару выводов GPIO на камере, подключённых к последовательному адаптеру на хосте. Полезно для автономных развёртываний, где USB-порт занят или физически недоступен.
Единственная задача транспорта – «байты входят, байты выходят, по порядку». Всё, что находится выше этого уровня, предполагает, что транспорт доставляет байты в том порядке, в каком они были записаны, но допускает, что сами байты могут быть повреждены или что связь может полностью пропасть. И всплески потерь (несколько пропавших байтов), и чистые обрывы (вся связь пропадает на какое-то время, затем восстанавливается) обрабатываются на более высоком уровне.
12.2.2. Формирование кадров¶
Следующий уровень накладывает структуру на поток байтов. Каждое сообщение становится пакетом – 10-байтовый заголовок, за которым следует полезная нагрузка, а за ней 4-байтовый завершитель. Заголовок содержит:
2-байтовое синхрослово (
0xD5AA), которое позволяет приёмнику заново найти начало пакета после рассинхронизации.1-байтовый порядковый номер, используемый уровнем надёжности.
1-байтовый идентификатор канала, указывающий, какому логическому потоку принадлежит пакет.
1-байтовое поле флагов для битов ACK / NAK / фрагмента / события.
1-байтовый код операции, который различает команды протокола, системные команды и команды каналов.
2-байтовую длину полезной нагрузки.
2-байтовый CRC по предыдущим восьми байтам заголовка.
Далее следует полезная нагрузка, затем 4-байтовый CRC по самой полезной нагрузке. Две контрольные суммы CRC выявляют повреждения независимо: перевёрнутый бит в заголовке делает CRC заголовка недействительным, и приёмник может отбросить пакет, даже не читая полезную нагрузку.
12.2.3. Надёжность¶
Уровень надёжности превращает «пакеты, которые могут прийти» в «пакеты, которые пришли». Он отслеживает порядковые номера в заголовке, просит другую сторону отправлять подтверждения для каждого пакета, который их требует, и повторяет передачу, когда подтверждение не приходит в течение тайм-аута. По умолчанию тайм-аут повторной передачи начинается с 500 мс и удваивается при каждой попытке, с тремя повторами перед отказом.
Каждое из этих поведений настраивается в вызове protocol.init(): ACK можно отключить для однонаправленных потоков, проверку CRC можно пропустить на абсолютно чистых транспортах, а параметры повторной передачи можно настроить для медленных или высоколатентных линий.
12.2.4. Каналы¶
Верхний уровень – это то, что видит код приложения. Канал – это именованный логический поток, идентифицируемый идентификатором канала от 0 до 31. На одном транспорте может сосуществовать до 32 каналов; каждый из них независим от остальных и адресуется по своему идентификатору в заголовке каждого пакета. Камера загружается с четырьмя встроенными каналами – stdin, stdout, stream и profile – а код приложения регистрирует дополнительные поверх них, вызывая protocol.register() с классом Python.
Четыре уровня не смешивают задачи. Формирование кадров ничего не знает о каналах; надёжность ничего не знает о содержимом пакетов; уровень каналов ничего не знает о том, как приходят байты. Именно это разделение является причиной того, что замена транспорта (например, USB на UART) не отражается на коде каналов, и именно оно позволяет проходить оставшуюся часть главы по одному уровню за раз.