12.6. Canaux nommés¶
L’identifiant de canal présent dans l’en-tête de chaque paquet permet à jusqu’à 32 flux indépendants de partager le même transport physique. La couche de canaux transforme ces identifiants numériques en points de terminaison nommés et visibles par l’application, auxquels le code hôte peut faire référence par une chaîne de caractères.
12.6.1. Les quatre canaux intégrés¶
La caméra enregistre quatre canaux au démarrage, avant l’exécution de tout code applicatif :
stdin– les octets de script que l’hôte envoie à la caméra pour exécution. L’IDE utilise ce canal pour envoyer le script en cours d’édition ;exec()sur le SDK hôte est l’appel équivalent depuis un programme Python.stdout– les octets provenant des appelsprint()de la caméra et des traces d’exceptions non capturées. La console série de l’IDE lit ce canal.stream– le canal d’aperçu en direct. L’IDE en récupère des trames JPEG ; tout script hôte peut faire de même avecread_frame().profile– les événements du profileur, présents uniquement lorsque la caméra a été compilée avec le profilage activé. La plupart des versions de production l’omettent.
Le code applicatif a rarement besoin de toucher à l’un des canaux intégrés ; le travail intéressant se déroule sur les canaux que l’application enregistre elle-même.
12.6.2. Enregistrement d’un canal¶
Un script côté caméra enregistre un nouveau canal en appelant protocol.register() avec un nom et un objet backend 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())
Les méthodes de l’objet backend décident de ce que le canal peut faire. Un backend doté uniquement de size et read est un canal de données en lecture seule ; ajoutez write et il devient bidirectionnel ; ajoutez poll et l’hôte peut demander si de nouvelles données sont prêtes avant de payer le coût d’une lecture. Échantillonner les données à l’intérieur de size est le modèle le plus simple lorsque la charge utile est suffisamment petite pour tenir dans un seul fragment – le tampon est généré à la demande, jamais mis en cache, jamais sujet à une situation de concurrence. Les charges utiles plus importantes – trames d’image, traces de capteur – nécessitent un modèle de verrouillage qui maintient le tampon jusqu’à ce que l’hôte termine sa lecture sur plusieurs fragments, abordé avec le canal de trames.
Une petite quantité de comptabilité se produit automatiquement :
La bibliothèque attribue le prochain identifiant de canal libre (entre 0 et 31).
Les indicateurs de capacité sont déduits des méthodes présentes :
CHANNEL_FLAG_READsireadest défini,CHANNEL_FLAG_WRITEsiwriteest défini,CHANNEL_FLAG_LOCKsilock/unlocksont définis.Un paquet d’événement
CHANNEL_REGISTEREDest envoyé à tout hôte connecté afin que sa liste de canaux se mette à jour.
La valeur de retour est un descripteur protocol.ProtocolChannel que l’application peut conserver. La méthode send_event() du descripteur est le point d’accroche côté caméra permettant d’indiquer à l’hôte « quelque chose s’est produit sur ce canal sans modifier les données lisibles » – un déclencheur s’est activé, un bouton a été pressé, un palier de nombre d’échantillons a été franchi.
12.6.3. Lecture des canaux depuis l’hôte¶
Le SDK hôte est distribué sous forme du paquet openmv sur PyPI (pip install openmv), construit sur pyserial pour le transport. Sa classe openmv.camera.Camera expose les canaux nommés de la caméra au moyen de méthodes de haut niveau
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)
Avertissement
Le paquet openmv requiert CPython 3.12 ou plus récent. Les interpréteurs antérieurs ne disposent pas des fonctionnalités dont dépend le SDK ; installez une version 3.12+ avant pip install openmv.
Quelques points à noter concernant la configuration :
La chaîne du port série –
/dev/ttyACM0ici – est de styleCOM3sous Windows,/dev/cu.usbmodemXXXXsous macOS et/dev/ttyACM*sous Linux. Le numéro réel dépend du port sous lequel la caméra a été énumérée.Le débit en bauds est la valeur magique du protocole
921600, que la pile USB-CDC de la caméra reconnaît comme « ce client parle le protocole, pas le REPL ». Tout autre débit retombe sur une simple ligne série.Le gestionnaire de contexte
with Camera(...) as cam:ouvre le transport, exécutePROTO_SYNC, échange les capacités et, à la sortie, ferme proprement le port. L’appel expliciteupdate_channels()après l’entrée rafraîchit la liste locale des canaux avec tout canal que l’application a enregistré après le démarrage.
channel_size() et channel_read() sont les méthodes de travail principales ; channel_write() effectue un aller-retour d’un tampon vers la caméra si le backend possède une méthode write ; has_channel() est le moyen sûr de vérifier qu’un nom est enregistré avant de l’utiliser. Le nom du canal est résolu une fois en l’identifiant de canal que la caméra a attribué lors de register, puis utilisé dans chaque paquet par la suite.
Chaque paire channel_size() / channel_read() coûte deux allers-retours : un paquet pour demander la taille, un pour demander les octets. Sur USB-CDC, les deux se terminent en environ une milliseconde au total ; sur UART, le même échange prend plus de temps proportionnellement au débit en bauds de la ligne série. Le code applicatif qui lit dans une boucle serrée ne devrait appeler channel_size() que lorsque la taille peut réellement changer – pour des données de taille fixe, la taille du premier appel peut être mise en cache.
12.6.4. Indépendance entre les canaux¶
Trois choses méritent d’être connues sur la façon dont les canaux interagissent :
Contrôle de flux indépendant. Chaque canal possède son propre état de lecture en attente, ses propres données et ses propres fonctions de rappel
size/read/write. Une lecture de longue durée sur le canalstreamne bloque pas les lectures sur le canalconfigde l’application.Séquentiel par canal. Au sein d’un même canal, les paquets sont livrés dans l’ordre. La couche de fiabilité le garantit même lorsque des retransmissions sont impliquées.
Transport partagé, budget de retransmission partagé. Tous les canaux partagent l’unique liaison physique ; ainsi, un flot de trafic sur un canal ralentit les autres en monopolisant le fil. Le mécanisme
CHANNEL_LOCKpermet à un canal de réserver le fil pour une lecture atomique sur plusieurs paquets ; le backend y adhère en implémentant les fonctions de rappellock/unlock.
Un canal est la surface minimale sur laquelle un programme hôte et un programme caméra conviennent de coopérer. Le nom, le sens de circulation (lecture, écriture ou les deux), les méthodes de rappel côté caméra et les appels de méthode correspondants côté hôte constituent l’intégralité du contrat.