12.2. As quatro camadas

A biblioteca de protocolo é construída como uma pilha de quatro camadas, cada uma resolvendo um único problema e assente na camada abaixo. O resto do capítulo percorre a pilha de baixo para cima.

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. Transporte

Na base está o pipe de bytes entre a câmara e o host. A biblioteca de protocolo não se preocupa com qual transporta os bytes:

  • USB-CDC pela porta USB à qual a câmara está ligada. A opção predefinida e de maior largura de banda para todas as câmaras.

  • UART por um par de pinos GPIO da câmara ligados a um adaptador série no host. Útil em implementações sem interface gráfica onde a porta USB está ocupada ou não é fisicamente acessível.

O único trabalho do transporte é «bytes entram, bytes saem, por ordem». Tudo acima desta camada pressupõe que o transporte entrega os bytes na ordem em que foram escritos, mas admite que os próprios bytes possam ser corrompidos ou que a ligação caia completamente. Rajadas com perdas (alguns bytes em falta) e quedas limpas (toda a ligação desaparece durante algum tempo e depois regressa) são tratadas nas camadas superiores.

12.2.2. Enquadramento

A camada seguinte impõe estrutura ao fluxo de bytes. Cada mensagem torna-se um pacote – um cabeçalho de 10 bytes seguido de um payload seguido de um trailer de 4 bytes. O cabeçalho transporta:

  • Uma palavra de sincronização de 2 bytes (0xD5AA) que permite ao recetor encontrar novamente o início de um pacote após um dessincronismo.

  • Um número de sequência de 1 byte usado pela camada de fiabilidade.

  • Um ID de canal de 1 byte que indica a que fluxo lógico o pacote pertence.

  • Um campo de sinalizadores de 1 byte para bits de ACK / NAK / fragmento / evento.

  • Um opcode de 1 byte que distingue comandos de protocolo, comandos de sistema e comandos de canal.

  • Um comprimento de payload de 2 bytes.

  • Um CRC de 2 bytes sobre os oito bytes anteriores do cabeçalho.

O payload vem a seguir, depois um CRC de 4 bytes sobre o próprio payload. Os dois CRCs detetam corrupção de forma independente: um bit invertido no cabeçalho invalida o CRC do cabeçalho, e o recetor pode descartar o pacote sem precisar de ler o payload.

12.2.3. Fiabilidade

A camada de fiabilidade transforma «pacotes que podem chegar» em «pacotes que chegaram.» Regista os números de sequência no cabeçalho, pede ao outro lado que envie acknowledgements para cada pacote que o requeira, e retransmite quando um acknowledgement não chega dentro de um timeout. Por predefinição, o timeout de retransmissão começa em 500 ms e duplica a cada tentativa, com três tentativas antes de desistir.

Cada um desses comportamentos é configurável na chamada protocol.init(): o ACK pode ser desativado para fluxos unidirecionais, a validação de CRC pode ser ignorada em transportes perfeitamente limpos, e os parâmetros de retransmissão podem ser ajustados para ligações lentas ou de elevada latência.

12.2.4. Canais

A camada superior é o que o código de aplicação vê. Um canal é um fluxo lógico nomeado identificado por um ID de canal de 0 a 31. Até 32 canais podem coexistir num único transporte; cada um é independente dos outros, endereçado pelo seu ID no cabeçalho de cada pacote. A câmara arranca com quatro canais integrados – stdin, stdout, stream e profile – e o código de aplicação regista mais por cima chamando protocol.register() com uma classe Python.

As quatro camadas não misturam preocupações. O enquadramento não sabe nada sobre canais; a fiabilidade não sabe nada sobre o conteúdo dos pacotes; a camada de canais não sabe como chegam os bytes. É essa separação que faz com que uma troca de transporte (de USB para UART, por exemplo) não se propague até ao código de canais, e que torna o resto do capítulo percorrível uma camada de cada vez.