12.5. Надійність – послідовності, підтвердження, повторні передачі

Рівень формування кадрів виявляє пошкодження за допомогою CRC. Рівень надійності перетворює «виявлене пошкодження» на «застосунок ніколи не бачить зламаних даних», погоджуючи повторні передачі щоразу, коли пакет не надходить цілим.

12.5.1. Порядкові номери

Кожен заголовок пакета містить однобайтовий порядковий номер, окремий для кожного напрямку передачі. Відправник збільшує лічильник перед передачею; отримувач перевіряє, що порядковий номер кожного отриманого пакета дорівнює попередньому плюс один (за модулем 256).

На стороні отримувача замість чистого пакета в правильному порядку можуть з’явитися три варіанти:

  • Очікуваний порядковий номер із правильним CRC. Пакет передається на наступний рівень.

  • Очікуваний порядковий номер із неправильним CRC. Отримувач відкидає пакет і (якщо узгоджено підтвердження) надсилає NAK із запитом на повторну передачу.

  • Порядковий номер на одиницю більший за очікуваний із правильним CRC. Отримувач знає, що попередній пакет загубився; він надсилає NAK із посиланням на пропущений порядковий номер і зберігає новий пакет.

Випадок дублювання (повторна передача, що надійшла після того, як оригінал нарешті пройшов) обробляється перевіркою відносно очікуваного лічильника: якщо порядковий номер менший за очікуваний, пакет є дублікатом, і отримувач відкидає його після надсилання підтвердження, яке відправник явно не отримав першого разу.

12.5.2. ACK і NAK

Два бітові прапори в заголовку пакета несуть власне трафік надійності:

  • ACK_REQ, встановлений у вихідному пакеті, означає «Я хочу отримати підтвердження у відповідь.» Пакети даних зазвичай встановлюють цей прапор; статусні пінги та одноразові події можуть не встановлювати.

  • ACK, встановлений у пакеті, означає «цей пакет є підтвердженням для порядкового номера в заголовку.» Він не несе власного корисного навантаження.

  • NAK, встановлений у пакеті, означає «цей пакет відхиляє попередній» – зазвичай через поганий CRC або розрив у порядкових номерах. Заголовок вказує відправнику, яку послідовність потрібно повторно передати.

Відправник виконує цикл зупинки й очікування: він передає один пакет, що вимагає підтвердження, потім чекає відповідного ACK (або NAK) перед відправленням наступного. Модель одного пакета в польоті утримує стан відправника обмеженим – кілька сотень байтів на найменших камерах – і відповідає ролі протоколу як каналу керування між двома кінцевими точками, а не оптимізованому для пропускної здатності каналу. За NAK відправник повторно передає той самий пакет із встановленим прапором RTX, щоб отримувач знав, що це повторна спроба.

12.5.3. Тайм-аут повторної передачі

Якщо ні ACK, ні NAK не надходять протягом тайм-ауту повторної передачі, відправник самостійно повторно передає пакет, що перебуває в польоті. Тайм-аут за замовчуванням становить 500 ms і подвоюється при кожній наступній спробі (1 с, 2 с, …). Після налаштованої кількості спроб – за замовчуванням трьох – відправник здається та повідомляє транспортну помилку застосунку.

Подвоєння тайм-ауту є стандартним шаблоном експоненційного відступу. Короткий перший тайм-аут швидко виявляє загублені пакети; подвоєння означає, що хост, який зайнятий кілька сотень мілісекунд, не спричиняє шторм дублікатів, що збільшують навантаження.

12.5.4. Налаштування надійності

Обидва кінці можуть вимикати частини рівня надійності за погодженням, коли застосунок може дозволити собі втрату даних:

  • protocol.init(ack=False) вимикає підтвердження для кожного пакета. Відправник надсилає і забуває; отримувач доставляє все, що надходить. Підходить для потокової передачі даних датчиків, де застаріла вибірка прийнятна.

  • protocol.init(seq=False) вимикає відстеження порядкових номерів, що також вимикає підтвердження. Корисно лише на абсолютно надійних транспортах.

  • protocol.init(crc=False) вимикає перевірку CRC, але залишає решту формування кадрів без змін. Варто робити лише тоді, коли сам транспорт достатньо надійний, щоб помилки CRC не виникали.

Налаштування за замовчуванням – усе увімкнено – є правильною відправною точкою для будь-якого сеансу налагодження між хостом і камерою. Коли застосунок виходить у виробництво, компроміси стають специфічними для його даних і транспорту.

12.5.5. Коди статусу

Коли транспортна помилка все ж поширюється до коду застосунку, вона надходить як код статусу. Бібліотека протоколу визначає десять:

  • SUCCESS – операція завершена.

  • FAILED – команда не виконана з невизначеної причини.

  • INVALID – отримувач відхилив команду або один із її аргументів.

  • TIMEOUT – вийшов тайм-аут таймера повторних спроб.

  • BUSY – камера зайнята (зазвичай заблокований канал).

  • CHECKSUM – CRC заголовка або корисного навантаження не збігся.

  • SEQUENCE – порядковий номер вийшов за межі того, що рівень може відновити.

  • OVERFLOW – корисне навантаження перевищило узгоджений максимум.

  • FRAGMENT – багатофрагментне повідомлення надійшло з відсутніми частинами.

  • UNKNOWN – захисний збірний код для справді несподіваних умов.

Код хоста, що викликає channel_read(), бачить їх як виключення Python; код застосунку на стороні камери, що обрав власну обробку помилок, бачить їх як значення, що повертаються зворотними викликами бекенду. Більшість застосунків камери взагалі не потребують перегляду кодів статусу – бібліотека обробляє повторну спробу, і лише справді невідновні збої (наприклад, сам транспорт відсутній) досягають застосунку.

Після того, як формування кадрів забезпечує виявлення пошкодження, а рівень надійності – відновлення від нього, робота на рівні проводу завершена. Код застосунку бачить відформатовані, впорядковані, цілісні пакети; байти всередині них вільні означати все, що захоче канал вище.