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.

A vertical stack of four labelled layers: transport at the bottom (USB or UART), framing above it (packet header with CRC), reliability above that (sequence numbers, ACK / NAK, retransmits), and channels at the top (named logical streams).

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.