12.5. Luotettavuus – sekvenssit, ACK-kuittaukset ja uudelleenlähetykset

Kehystyskerros havaitsee korruptoituneen datan CRC-tarkistuksilla. Luotettavuuskerros muuntaa ”havaitun korruption” muotoon ”sovellus ei koskaan näe rikkinäistä dataa” neuvottelemalla uudelleenlähetyksistä aina, kun paketti ei saavu ehjänä perille.

12.5.1. Sekvenssinumerot

Jokainen paketin otsake kantaa yhden tavun sekvenssinumeron, erikseen kumpaankin kulkusuuntaan. Lähettäjä kasvattaa laskuria ennen lähetystä; vastaanottaja tarkistaa, että jokaisen vastaanotetun paketin sekvenssi on edellinen plus yksi (modulo 256).

Vastaanottajalle voi siistin järjestyksessä saapuvan paketin sijaan ilmestyä kolme asiaa:

  • Odotettu sekvenssinumero kelvollisella CRC:llä. Paketti toimitetaan seuraavalle kerrokselle.

  • Odotettu sekvenssinumero virheellisellä CRC:llä. Vastaanottaja hylkää paketin ja (jos ACK-kuittaukset on neuvoteltu käyttöön) lähettää NAK-viestin, joka pyytää uudelleenlähetystä.

  • Odotettua yhtä suurempi sekvenssinumero kelvollisella CRC:llä. Vastaanottaja tietää, että edellinen paketti katosi; se lähettää NAK-viestin, joka viittaa puuttuvaan sekvenssiin, ja tallettaa uuden paketin sivuun.

Kaksoiskappaletapaus (uudelleenlähetys saapuu sen jälkeen, kun alkuperäinen viimein pääsi perille) hoidetaan vertaamalla odotettuun laskuriin: jos sekvenssi on jäljessä odotettua, paketti on kaksoiskappale, ja vastaanottaja hylkää sen lähetettyään ACK-kuittauksen, jota lähettäjä ei ilmeisesti ensimmäisellä kerralla saanut.

12.5.2. ACK ja NAK

Paketin otsakkeen kaksi lippubittiä kantavat itse luotettavuusliikenteen:

  • Lähtevään pakettiin asetettu ACK_REQ tarkoittaa ”haluan kuittauksen takaisin.” Datapaketit asettavat tämän yleensä; tilakyselyt ja kertaluonteiset tapahtumat eivät välttämättä.

  • Pakettiin asetettu ACK tarkoittaa ”tämä paketti on kuittaus otsakkeessa olevalle sekvenssinumerolle.” Sillä ei ole omaa hyötykuormaa.

  • Asetettu NAK tarkoittaa ”tämä paketti hylkää aiemman paketin” – yleensä virheellisen CRC:n tai sekvenssinumeroaukon vuoksi. Otsake osoittaa lähettäjälle, mikä sekvenssi pitää lähettää uudelleen.

Lähettäjä ajaa stop-and-wait-silmukkaa: se lähettää yhden kuittausta vaativan paketin ja odottaa sitten vastaavaa ACK-kuittausta (tai NAK-viestiä) ennen seuraavan lähettämistä. Yhden paketin kerrallaan kuljettava malli pitää lähettäjän tilan rajattuna – muutama sata tavua pienimmissä kameroissa – ja sopii protokollan rooliin kahden päätepisteen välisenä ohjauskanavana sen sijaan, että se olisi läpäisykykyyn optimoitu putki. NAK-viestin saadessaan lähettäjä lähettää saman paketin uudelleen RTX-lippu asetettuna, jotta vastaanottaja tietää sen olevan uudelleenyritys.

12.5.3. Uudelleenlähetyksen ajoitus

Jos uudelleenlähetyksen aikakatkaisun sisällä ei saavu ACK-kuittausta eikä NAK-viestiä, lähettäjä lähettää kuljetuksessa olevan paketin uudelleen omasta aloitteestaan. Aikakatkaisun oletusarvo on 500 ms ja se kaksinkertaistuu jokaisella peräkkäisellä uudelleenyrityksellä (1 s, 2 s, …). Määritetyn uudelleenyritysmäärän jälkeen – oletuksena kolme – lähettäjä luovuttaa ja raportoi sovellukselle kuljetusvirheestä.

Aikakatkaisun kaksinkertaistaminen on tavanomainen eksponentiaalisen perääntymisen malli. Lyhyt ensimmäinen aikakatkaisu havaitsee kadonneet paketit nopeasti; kaksinkertaistuminen tarkoittaa, ettei muutaman sadan millisekunnin ajan kiireinen isäntä laukaise kaksoiskappaleiden myrskyä, joka kasvattaisi kuormaa entisestään.

12.5.4. Luotettavuuden määrittäminen

Molemmat päät voivat sopimalla kytkeä luotettavuuskerroksen osia pois päältä, kun sovellus voi sietää datan menettämistä:

  • protocol.init(ack=False) poistaa pakettikohtaiset ACK-kuittaukset käytöstä. Lähettäjä lähettää ja unohtaa; vastaanottaja toimittaa sen, mitä saapuu. Hyödyllinen virtautettaessa sensoridataa, jossa vanhentunut näyte on hyväksyttävä.

  • protocol.init(seq=False) kytkee sekvenssinumeroiden seurannan pois päältä, mikä tarkoittaa myös ACK-kuittausten poiskytkemistä. Hyödyllinen vain täysin luotettavissa kuljetuksissa.

  • protocol.init(crc=False) kytkee CRC-validoinnin pois päältä mutta jättää muun kehystyksen ennalleen. Kannattaa tehdä vain, kun kuljetus itsessään on niin vakaa, ettei CRC-virheitä tapahdu.

Oletukset – kaikki päällä – ovat oikea lähtökohta mihin tahansa isännän ja kameran väliseen virheenetsintäistuntoon. Kun sovellus on tuotannossa, kompromissit määräytyvät sen datan ja kuljetuksen mukaan.

12.5.5. Tilakoodit

Kun kuljetusvirhe etenee sovelluskoodiin asti, se saapuu tilakoodina. Protokollakirjasto määrittelee kymmenen:

  • SUCCESS – toiminto valmistui.

  • FAILED – komento epäonnistui määrittelemättömästä syystä.

  • INVALID – vastaanottaja hylkäsi komennon tai jonkin sen argumenteista.

  • TIMEOUT – uudelleenyritysajastin umpeutui.

  • BUSY – kamera on varattu (tyypillisesti lukittu kanava).

  • CHECKSUM – otsakkeen tai hyötykuorman CRC ei täsmännyt.

  • SEQUENCE – sekvenssinumero oli väärässä järjestyksessä enemmän kuin mistä kerros pystyy palautumaan.

  • OVERFLOW – hyötykuorma ylitti neuvotellun enimmäismäärän.

  • FRAGMENT – monifragmenttinen viesti saapui puuttuvilla osilla.

  • UNKNOWN – puolustava yleiskaatoluokka aidosti odottamattomille tilanteille.

Isäntäkoodi, joka kutsuu channel_read(), näkee nämä Python-poikkeuksina; kameran puolen sovelluskoodi, joka on valinnut mukautetun virheenkäsittelyn, näkee ne taustaosan takaisinkutsujen paluuarvoina. Useimpien kamerasovellusten ei tarvitse katsoa tilakoodeja lainkaan – kirjasto hoitaa uudelleenyritykset, ja vain aidosti palautumattomat virheet (esim. itse kuljetus on kadonnut) yltävät sovellukseen asti.

Kun kehystys on paikallaan havaitsemassa korruptiota ja luotettavuus paikallaan toipumassa siitä, väylätason työ on tehty. Sovelluskoodi näkee kehystetyt, järjestyksessä olevat, ehjät paketit; niiden sisällä olevat tavut saavat tarkoittaa mitä tahansa, mitä yläpuolinen kanava haluaa niiden tarkoittavan.