12.7. Povratni pozivi kanala

Pozadinski objekt predan funkciji protocol.register() je Python klasa. Protokolna biblioteka ne pita klasu koje metode implementira; ona pregledava instancu i povezuje one koje pronađe. Upravo to introspekcijsko pregledavanje čini sučelje pozadinskog objekta fleksibilnim: najmanji koristan pozadinski objekt ima dvije metode, najsloženiji ih ima dvanaest, a aplikacija se uključuje u svaku mogućnost jednu po jednu metodu.

12.7.1. Pravila introspekcije

Kada se protocol.register() izvrši, biblioteka prolazi kroz fiksni popis imena pozivnih funkcija i veže svaku koju pronađe na instanci pozadinskog objekta:

  • Dodavanje read u klasu uključuje CHANNEL_FLAG_READ. Poziv s domaćina prema channel_read() dolazi do pozadinskog objekta samo ako je ova zastavica postavljena.

  • Dodavanje write uključuje CHANNEL_FLAG_WRITE, čime se omogućuje channel_write().

  • Dodavanje lock i unlock uključuje CHANNEL_FLAG_LOCK, čime se domaćinu omogućuje zaključavanje kanala za atomarno čitanje višestrukih paketa.

  • Dodavanje poll omogućuje domaćinu da jeftino upita „je li nešto spremno?”, bez prisiljavanja na potpuno čitanje.

Nedostajuće metode nisu pogreške – protokolna biblioteka jednostavno ostavlja odgovarajuću mogućnost onemogućenom. Pozadinski objekt sa samo size i read posve je valjan; to je podatkovni kanal samo za čitanje.

12.7.2. Senzorski kanal samo za čitanje

Senzorski kanal koji objavljuje svježe očitanje svaki put kada domaćin upita, odbijajući zapise s domaćina, koristi četiri povratna poziva:

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))

Prolazak kroz ono što svaka metoda radi:

  • poll vraća zastavicu svježine. Domaćin je poziva prije čitanja i u potpunosti preskače čitanje kada ona vrati False. Time se uštedi trošak povratnog putovanja za „još nema novih podataka”.

  • size regenerira međuspremnik na zahtjev i prijavljuje njegovu duljinu. Provođenje uzorkovanja ovdje znači da pozadinskom objektu nije potreban pozadinski zadatak – poziv s domaćina pokreće svako mjerenje.

  • read vraća isječak međuspremnika. Protokolna biblioteka može ga pozvati više puta kada je međuspremnik veći od dogovorenog maksimalnog korisnog tereta; argument offset prolazi kroz fragmente.

  • Nepostojanje metode write znači da se zapisi s domaćina odbijaju na sloju uokvirivanja, prije nego što se pozadinski objekt uopće uključi.

12.7.3. Potpuni skup povratnih poziva

Za referencu, svaka metoda koju biblioteka traži na pozadinskom objektu:

Metoda

Vraća

Svrha

init(self)

objekt

Neobavezna jednokratna inicijalizacija kada se kanal prvi put veže na domaćina. Vrati bilo koju vrijednost koja nije None u slučaju uspjeha.

poll(self)

bool

Vrati True kada su podaci dostupni.

lock(self)

bool

Preuzmi kanal za atomarni prijenos višestrukih paketa.

unlock(self)

bool

Otpusti prethodni lock.

size(self)

int

Broj bajtova koje je trenutačno moguće pročitati s kanala.

shape(self)

tuple

Do četiri cijela broja koja opisuju strukturu podataka (npr. visina slike, širina, broj bajtova). Domaćin ih koristi za raspakiranje tipiziranih međuspremnika.

read(self, offset, size)

bytes

Vrati do size bajtova počevši od offset. Poziva se jednom po fragmentu kada korisni teret premaši dogovoreni maksimum.

readp(self, offset, size)

bytes

Inačica metode read bez kopiranja: memorija međuspremnika mora ostati valjana tijekom trajanja prijenosa.

write(self, offset, data)

int

Domaćin je zapisao data na offset. data je bytearray pogled u prijemni međuspremnik protokolnog sloja – kopirajte ono što želite zadržati prije povratka.

ioctl(self, cmd, length, arg)

int

Aplikacijski definirani operacijski kod izvan modela čitanja/pisanja. Negativna povratna vrijednost je pogreška.

flush(self)

objekt

Odbaci sve podatke iz međuspremnika. Poziva se kada domaćin želi ponovno postaviti kanal.

is_active(self)

bool

Smisleno samo na pozadinskim objektima koji predstavljaju fizički transport (ugrađeni USB kanali). Aplikacijskim kanalima ovo nije potrebno.

To je cijelo sučelje pozadinskog objekta. Dvanaest imena metoda, sve neobavezne, a protokolna biblioteka odlučuje što svaki kanal može raditi na temelju toga koje su prisutne.