12.5. Betrouwbaarheid – sequenties, ACK’s, hertransmissies

De framinglaag detecteert corruptie met zijn CRC’s. De betrouwbaarheidslaag zet “gedetecteerde corruptie” om in “de applicatie ziet nooit beschadigde data” door hertransmissies te onderhandelen telkens wanneer een pakket niet intact aankomt.

12.5.1. Sequentienummers

Elke pakketheader draagt een sequentienummer van een byte, afzonderlijk voor elke richting. De zender verhoogt de teller voor het verzenden; de ontvanger controleert of de sequentie van elk ontvangen pakket de vorige plus een is (modulo 256).

Drie dingen kunnen bij de ontvanger opduiken in plaats van een schoon, geordend pakket:

  • Het verwachte sequentienummer, met een geldige CRC. Het pakket wordt doorgegeven aan de volgende laag.

  • Het verwachte sequentienummer, met een foute CRC. De ontvanger laat het pakket vallen en stuurt (als ACK’s zijn onderhandeld) een NAK met het verzoek om een hertransmissie.

  • Een sequentienummer dat een hoger is dan verwacht, met een geldige CRC. De ontvanger weet dat het vorige pakket verloren is gegaan; hij stuurt een NAK die verwijst naar de gemiste sequentie en bewaart het nieuwe pakket.

Het duplicaatgeval (een hertransmissie die aankomt nadat het origineel eindelijk toch is doorgekomen) wordt afgehandeld door te controleren tegen de verwachte teller: als de sequentie achter de verwachte ligt, is het pakket een duplicaat en gooit de ontvanger het weg nadat hij de ACK heeft verstuurd die de zender de eerste keer duidelijk niet kreeg.

12.5.2. ACK en NAK

Twee vlagbits in de pakketheader dragen het betrouwbaarheidsverkeer zelf:

  • ACK_REQ gezet op een uitgaand pakket betekent “Ik wil een bevestiging terug.” Datapakketten zetten dit normaal gesproken; statuspings en eenmalige gebeurtenissen mogelijk niet.

  • ACK gezet op een pakket betekent “dit pakket is de bevestiging voor het sequentienummer in de header.” Het draagt geen eigen payload.

  • NAK gezet betekent “dit pakket wijst een eerder pakket af” – meestal vanwege een foute CRC of een gat in de sequentienummers. De header wijst de zender aan welke sequentie opnieuw verzonden moet worden.

De zender draait een stop-and-wait-lus: hij verzendt een pakket dat bevestiging vereist, en wacht dan op de bijbehorende ACK (of NAK) voordat hij het volgende verzendt. Het model met één pakket onderweg houdt de zenderstatus begrensd – een paar honderd bytes op de kleinste cams – en past bij de rol van het protocol als besturingskanaal tussen twee eindpunten in plaats van een op doorvoer geoptimaliseerde pijplijn. Bij een NAK verzendt de zender hetzelfde pakket opnieuw met de RTX-vlag gezet, zodat de ontvanger weet dat het een herhaling is.

12.5.3. Timing van hertransmissie

Als binnen de hertransmissie-time-out geen ACK noch NAK aankomt, verzendt de zender het pakket dat onderweg is uit eigen beweging opnieuw. De time-out is standaard 500 ms en verdubbelt bij elke opeenvolgende poging (1 s, 2 s, …). Na het ingestelde aantal pogingen – standaard drie – geeft de zender het op en meldt een transportfout aan de applicatie.

Het verdubbelen van de time-out is het standaardpatroon van exponential backoff. Een korte eerste time-out vangt verloren pakketten snel op; de verdubbeling betekent dat een host die een paar honderd milliseconden bezig is geen stortvloed aan duplicaten veroorzaakt die de belasting verder opvoeren.

12.5.4. Betrouwbaarheid configureren

Beide uiteinden kunnen onderdelen van de betrouwbaarheidslaag uitschakelen, in onderling overleg, wanneer de applicatie het zich kan veroorloven data te verliezen:

  • protocol.init(ack=False) schakelt ACK’s per pakket uit. De zender verstuurt en vergeet; de ontvanger levert af wat er binnenkomt. Goed voor het streamen van sensordata waarbij een verouderd sample acceptabel is.

  • protocol.init(seq=False) schakelt het bijhouden van sequentienummers uit, wat impliceert dat ook ACK’s uit staan. Alleen nuttig op perfect betrouwbare transporten.

  • protocol.init(crc=False) schakelt de CRC-validatie uit maar laat de rest van de framing intact. Alleen de moeite waard wanneer het transport zelf robuust genoeg is dat CRC-fouten niet voorkomen.

De standaardinstellingen – alles aan – vormen het juiste startpunt voor elke debugsessie tussen host en cam. Zodra de applicatie in productie is, worden de afwegingen specifiek voor de data en het transport.

12.5.5. De statuscodes

Wanneer een transportfout wel doorstroomt naar applicatiecode, arriveert die als een statuscode. De protocolbibliotheek definieert er tien:

  • SUCCESS – bewerking voltooid.

  • FAILED – commando mislukt om een ongespecificeerde reden.

  • INVALID – de ontvanger heeft het commando of een van de argumenten afgewezen.

  • TIMEOUT – een herhalingstimer is afgelopen.

  • BUSY – de cam is bezig (meestal een vergrendeld kanaal).

  • CHECKSUM – de header- of payload-CRC kwam niet overeen.

  • SEQUENCE – het sequentienummer was buiten de volgorde, verder dan wat de laag kan herstellen.

  • OVERFLOW – een payload overschreed het onderhandelde maximum.

  • FRAGMENT – een bericht met meerdere fragmenten arriveerde met ontbrekende delen.

  • UNKNOWN – een defensieve vangnetwaarde voor werkelijk onverwachte omstandigheden.

Hostcode die channel_read() aanroept ziet deze als Python-excepties; cam-zijdige applicatiecode die heeft gekozen voor aangepaste foutafhandeling ziet ze als retourwaarden van de backend-callbacks. De meeste cam-apps hoeven helemaal niet naar de statuscodes te kijken – de bibliotheek handelt de herhaling af, en alleen werkelijk onherstelbare fouten (bijv. het transport zelf is weg) bereiken de applicatie.

Met framing aanwezig om corruptie te detecteren en betrouwbaarheid aanwezig om ervan te herstellen, is het werk op draadniveau gedaan. Applicatiecode ziet geframede, geordende, intacte pakketten; de bytes erin zijn vrij om te betekenen wat het kanaal erboven maar wil.