12.3. Le format des paquets

Chaque octet qui traverse le fil entre la caméra et l’hôte fait partie d’un paquet. Un paquet commence par un en-tête de 10 octets, comporte une charge utile de longueur variable et se termine par un CRC final de 4 octets. Aucun autre octet n’apparaît sur le fil – une fois que l’hôte a vu le mot de synchronisation de 2 octets, les octets suivants forment un en-tête dans cette séquence exacte.

Une disposition horizontale d'un paquet de protocole montrant l'en-tête de 10 octets (mot de synchronisation, numéro de séquence, ID de canal, indicateurs, code d'opération, longueur de charge utile, CRC d'en-tête) suivi de la charge utile de longueur variable et d'un CRC de charge utile de 4 octets.

12.3.1. L’en-tête

Dix octets, compactés sans remplissage. Chaque champ :

  • sync – le mot de 16 bits 0xD5AA en ordre petit-boutiste. L’octet 0 sur le fil est 0xAA, l’octet 1 est 0xD5. Un récepteur qui scanne les octets peut trouver le début d’un paquet en recherchant la paire AA D5 ; tout ce qui précède est traité comme du rebut. Le choix de la valeur est délibéré : 0xAA et 0xD5 apparaissent rarement dans du texte imprimable, et la paire est peu susceptible de survenir par accident au milieu d’une charge utile.

  • seq – un octet. Un compteur qui s’incrémente de un pour chaque paquet envoyé dans une direction donnée. Le récepteur vérifie que le numéro de séquence du paquet suivant est celui attendu ; sinon, la couche de fiabilité demande une retransmission.

  • chan – un octet. L’ID du canal auquel appartient ce paquet. Les canaux 0..31 sont utilisables ; les canaux intégrés stdin, stdout, stream et (en option) profile occupent des ID fixes que la caméra réserve.

  • flags – un octet. Un champ de bits indiquant au récepteur comment interpréter le paquet :

    • bit 0 ACK – ce paquet est un accusé de réception d’un paquet précédent.

    • bit 1 NAK – ce paquet rejette un paquet précédent.

    • bit 2 RTX – ce paquet est une retransmission.

    • bit 3 ACK_REQ – l’expéditeur souhaite que ce paquet soit acquitté.

    • bit 4 FRAGMENT – d’autres fragments suivent celui-ci dans un message plus grand.

    • bit 5 EVENT – ce paquet transporte un événement de canal plutôt que des données.

    • les bits 6 et 7 sont réservés.

  • opcode – un octet. Le code de commande ou de réponse. La bibliothèque de protocole réserve des plages de codes d’opération selon leur usage :

    • 0x00..0x0F – commandes de protocole (SYNC, GET_CAPS, SET_CAPS, STATS, VERSION).

    • 0x10..0x1F – commandes système (RESET, BOOT, INFO, EVENT, MEMORY).

    • 0x20..0x2F – commandes de canal (LIST, POLL, LOCK, UNLOCK, SHAPE, SIZE, READ, WRITE, IOCTL, EVENT).

  • len – deux octets, petit-boutiste. Le nombre d’octets de charge utile qui suivent l’en-tête. Une longueur de zéro est valide – de nombreux accusés de réception et petites commandes ne transportent aucune charge utile.

  • crc – deux octets. Un CRC-16 sur les huit octets d’en-tête précédents. Un récepteur qui reçoit un en-tête avec un CRC erroné rejette le paquet entier sans même regarder la charge utile.

12.3.2. La charge utile

Zéro octet ou plus, traités comme opaques par la couche de découpage en trames. Le contenu de la charge utile dépend du code d’opération : pour une réponse CHANNEL_READ, ce sont les données réelles du canal ; pour une réponse GET_CAPS, c’est une petite structure fixe ; pour une écriture de canal, c’est ce que l’hôte a envoyé.

La taille maximale de charge utile dépend de la taille du tampon de protocole de la caméra (reportez-vous au tableau par carte dans protocol.init()). Les messages plus longs que la limite sont divisés en fragments avec l’indicateur FRAGMENT positionné sur tous sauf le dernier.

12.3.3. Le CRC final

Quatre octets, un CRC-32 sur la charge utile. Il détecte la corruption que le CRC d’en-tête ne peut pas voir, en particulier sur les longues charges utiles où une erreur sur un seul bit en milieu de trame passerait autrement inaperçue.

Répartir le contrôle d’intégrité sur deux CRC est délibéré. Le CRC d’en-tête protège les champs de trame eux-mêmes – en particulier la longueur de la charge utile. Sans un CRC d’en-tête distinct, une seule inversion de bit dans l’octet de longueur ferait lire au récepteur le mauvais nombre d’octets pour la charge utile et le désynchroniserait entièrement du flux d’octets ; avec lui, un en-tête endommagé est rejeté d’emblée et le récepteur recherche à nouveau le prochain mot de synchronisation. Le CRC de charge utile protège ensuite le corps du message comme une préoccupation distincte, de sorte qu’une inversion de bit dans les données est signalée comme une charge utile corrompue plutôt que confondue avec une erreur de trame.

Le format est suffisamment petit pour être parcouru octet par octet, et le fait que chaque paquet ait la même disposition – synchronisation, puis en-tête, puis charge utile, puis CRC – signifie qu’un analyseur écrit à la main tient dans un écran de code. C’est pourquoi une petite implémentation hôte en C, Python ou Rust est un projet de week-end ; la bibliothèque de protocole est la version Python maintenue de chaque côté.