12.7. Kanalåteranrop

Backend-objektet som skickas till protocol.register() är en Python-klass. Protokollbiblioteket frågar inte klassen vilka metoder den implementerar; det inspekterar instansen och kopplar in dem den hittar. Den introspektionen är vad som gör backend-gränssnittet flexibelt: den minsta användbara backenden är två metoder, den mest avancerade är tolv, och applikationen ansluter sig till varje förmåga en metod i taget.

12.7.1. Introspektionsreglerna

När protocol.register() körs går biblioteket igenom en fast lista med anropbara namn och binder varje sådant som det hittar på backend-instansen:

  • Att lägga till read i klassen slår på CHANNEL_FLAG_READ. Ett värdanrop till channel_read() når bara backenden om denna flagga är satt.

  • Att lägga till write slår på CHANNEL_FLAG_WRITE och aktiverar channel_write().

  • Att lägga till lock och unlock slår på CHANNEL_FLAG_LOCK och låter värden låsa kanalen för en atomär läsning över flera paket.

  • Att lägga till poll låter värden billigt fråga ”finns det något klart?” utan att tvinga fram en fullständig läsning.

Saknade metoder är inte fel – protokollbiblioteket lämnar bara motsvarande förmåga inaktiverad. En backend med endast size och read är helt giltig; det är en skrivskyddad datakanal.

12.7.2. En skrivskyddad sensorkanal

En sensorkanal som publicerar en färsk avläsning varje gång värden frågar och vägrar skrivningar från värden använder fyra av återanropen:

import protocol
import struct

class TempChannel:
    def __init__(self, read_sensor):
        self._read_sensor = read_sensor
        self._buf = b''
        self._fresh = False

    def poll(self):
        # Tell the host whether a reading is waiting.
        return self._fresh

    def size(self):
        # Sample fresh data on every host-side size query.
        value = self._read_sensor()
        self._buf = struct.pack('<f', value)
        self._fresh = True
        return len(self._buf)

    def read(self, offset, size):
        end = offset + size
        if end >= len(self._buf):
            self._fresh = False
        return self._buf[offset:end]

protocol.register(name='temp', backend=TempChannel(read_temperature))

En genomgång av vad varje metod gör:

  • poll returnerar färskhetsflaggan. Värden anropar den före läsning och hoppar över läsningen helt när den returnerar False. Det sparar in tur-och-retur-kostnaden för ”ingen ny data ännu”.

  • size regenererar bufferten vid behov och rapporterar dess längd. Att utföra samplingen här innebär att backenden inte behöver någon bakgrundsuppgift – ett värdanrop driver varje mätning.

  • read returnerar en del av bufferten. Protokollbiblioteket kan anropa den mer än en gång när bufferten är större än den förhandlade maximala nyttolasten; argumentet offset går igenom fragmenten.

  • Avsaknad av write innebär att skrivningar från värden avvisas i ramnivån, innan backenden är inblandad.

12.7.3. Den fullständiga uppsättningen återanrop

För referens, varje metod som biblioteket letar efter på en backend:

Metod

Returnerar

Syfte

init(self)

objekt

Valfri engångsinitialisering när kanalen först binds till en värd. Returnera valfritt värde som inte är None vid lyckat resultat.

poll(self)

bool

Returnera True när data är tillgänglig.

lock(self)

bool

Reservera kanalen för en atomär överföring över flera paket.

unlock(self)

bool

Frigör ett tidigare lock.

size(self)

int

Antal byte som för närvarande kan läsas från kanalen.

shape(self)

tuple

Upp till fyra heltal som beskriver datastrukturen (t.ex. bildhöjd, bredd, antal byte). Används av värden för att packa upp typade buffertar.

read(self, offset, size)

bytes

Returnera upp till size byte med start vid offset. Anropas en gång per fragment när nyttolasten överskrider det förhandlade maximumet.

readp(self, offset, size)

bytes

Nollkopieringsvariant av read: buffertens minne måste förbli giltigt under hela överföringen.

write(self, offset, data)

int

Värden skrev data vid offset. data är en bytearray-vy in i protokollagrets mottagningsbuffert – kopiera ut det du vill behålla innan du returnerar.

ioctl(self, cmd, length, arg)

int

Applikationsdefinierad opkod utanför läs-/skrivmodellen. Negativ retur är ett fel.

flush(self)

objekt

Kasta all buffrad data. Anropas när värden vill återställa kanalen.

is_active(self)

bool

Endast meningsfull på backendar som representerar en fysisk transport (de inbyggda USB-kanalerna). Applikationskanaler behöver inte detta.

Det är hela backend-gränssnittet. Tolv metodnamn, alla valfria, och protokollbiblioteket avgör vad varje kanal kan göra utifrån vilka som finns.