12.2. I quattro livelli¶
La libreria del protocollo è costruita come uno stack di quattro livelli, ciascuno dei quali risolve un singolo problema e si basa sul livello sottostante. Il resto del capitolo percorre lo stack dal basso verso l’alto.
12.2.1. Trasporto¶
In fondo c’è il tubo di byte tra la cam e l’host. La libreria del protocollo non si preoccupa di quale dei due trasporti i byte:
USB-CDC sulla porta USB a cui è collegata la cam. L’opzione predefinita e con la banda più ampia per ogni cam.
UART su una coppia di pin GPIO sulla cam collegati a un adattatore seriale sull’host. Utile per deployment headless dove la porta USB è occupata o non è fisicamente accessibile.
L’unico compito del trasporto è «i byte entrano, i byte escono, in ordine». Tutto ciò che sta sopra questo livello presuppone che il trasporto consegni i byte nell’ordine in cui sono stati scritti, ma ammette che i byte stessi possano essere corrotti o che il collegamento cada del tutto. Le raffiche con perdite (alcuni byte mancanti) e le interruzioni nette (l’intero collegamento perso per un po”, poi ripristinato) sono entrambe gestite ai livelli superiori.
12.2.2. Framing¶
Il livello successivo impone una struttura al flusso di byte. Ogni messaggio diventa un pacchetto: un header di 10 byte seguito da un payload seguito da un trailer di 4 byte. L’header contiene:
Una parola di sincronizzazione di 2 byte (
0xD5AA) che consente a un ricevitore di ritrovare l’inizio di un pacchetto dopo una desincronizzazione.Un numero di sequenza di 1 byte usato dal livello di affidabilità.
Un ID di canale di 1 byte che indica a quale flusso logico appartiene il pacchetto.
Un campo flag di 1 byte per i bit ACK / NAK / fragment / event.
Un opcode di 1 byte che distingue i comandi del protocollo, i comandi di sistema e i comandi dei canali.
Una lunghezza del payload di 2 byte.
Un CRC di 2 byte sui precedenti otto byte dell’header.
Segue il payload, poi un CRC di 4 byte sul payload stesso. I due CRC rilevano la corruzione in modo indipendente: un bit invertito nell’header invalida il CRC dell’header, e il ricevitore può scartare il pacchetto senza dover mai leggere il payload.
12.2.3. Affidabilità¶
Il livello di affidabilità trasforma i «pacchetti che potrebbero arrivare» in «pacchetti che sono arrivati.» Tiene traccia dei numeri di sequenza nell’header, chiede all’altra parte di inviare conferme per ogni pacchetto che ne richiede una e ritrasmette quando una conferma non arriva entro un timeout. Per impostazione predefinita il timeout di ritrasmissione parte da 500 ms e raddoppia a ogni nuovo tentativo, con tre tentativi prima di arrendersi.
Ciascuno di questi comportamenti è configurabile nella chiamata protocol.init(): l’ACK può essere disattivato per i flussi unidirezionali, la validazione CRC può essere saltata su trasporti perfettamente puliti e i parametri di ritrasmissione possono essere regolati per collegamenti lenti o ad alta latenza.
12.2.4. Canali¶
Il livello superiore è ciò che vede il codice applicativo. Un canale è un flusso logico con nome identificato da un ID di canale da 0 a 31. Fino a 32 canali possono coesistere su un trasporto; ciascuno è indipendente dagli altri, indirizzato dal suo ID nell’header di ogni pacchetto. La cam si avvia con quattro canali integrati – stdin, stdout, stream e profile – e il codice applicativo ne registra altri sopra di essi chiamando protocol.register() con una classe Python.
I quattro livelli non mescolano le competenze. Il framing non sa nulla dei canali; l’affidabilità non sa nulla del contenuto dei pacchetti; il livello dei canali non sa come arrivano i byte. Questa separazione è il motivo per cui uno scambio di trasporto (da USB a UART, ad esempio) non si ripercuote verso l’alto nel codice dei canali, ed è ciò che rende il resto del capitolo percorribile un livello alla volta.