12.7. Kanavan takaisinkutsut

protocol.register() -funktiolle annettu backend-objekti on Python-luokka. Protokollakirjasto ei kysy luokalta, mitä metodeja se toteuttaa; se tarkastelee instanssia ja kytkee löytämänsä metodit. Juuri tämä introspektio tekee backend-rajapinnasta joustavan: pienin hyödyllinen backend on kaksi metodia, monimutkaisin on kaksitoista, ja sovellus ottaa kunkin ominaisuuden käyttöön yksi metodi kerrallaan.

12.7.1. Introspektion säännöt

Kun protocol.register() suoritetaan, kirjasto käy läpi kiinteän listan kutsuttavien metodien nimiä ja sitoo jokaisen, jonka se löytää backend-instanssista:

  • read-metodin lisääminen luokkaan kytkee päälle CHANNEL_FLAG_READ-lipun. Isäntäkutsu channel_read() -metodiin tavoittaa backendin vain, jos tämä lippu on asetettu.

  • write-metodin lisääminen kytkee päälle CHANNEL_FLAG_WRITE-lipun, mikä ottaa käyttöön channel_write() -metodin.

  • lock- ja unlock-metodien lisääminen kytkee päälle CHANNEL_FLAG_LOCK-lipun, jolloin isäntä voi lukita kanavan usean paketin atomista lukua varten.

  • poll-metodin lisääminen antaa isännän kysyä edullisesti ”onko mitään valmiina?”, ilman että koko luku pakotetaan suorittamaan.

Puuttuvat metodit eivät ole virheitä – protokollakirjasto jättää vain vastaavan ominaisuuden pois käytöstä. Backend, jossa on vain size ja read, on täysin kelvollinen; se on vain luettava datakanava.

12.7.2. Vain luettava sensorikanava

Sensorikanava, joka julkaisee tuoreen lukeman aina kun isäntä pyytää ja torjuu isännän kirjoitukset, käyttää neljää takaisinkutsuista:

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

Käydään läpi, mitä kukin metodi tekee:

  • poll palauttaa tuoreuslipun. Isäntä kutsuu sitä ennen lukemista ja ohittaa luvun kokonaan, kun se palauttaa False. Tämä säästää edestakaisen viestinnän kustannuksen tapauksessa ”ei vielä uutta dataa”.

  • size luo puskurin uudelleen pyynnöstä ja ilmoittaa sen pituuden. Näytteenoton tekeminen tässä tarkoittaa, ettei backend tarvitse taustatehtävää – isäntäkutsu ohjaa jokaista mittausta.

  • read palauttaa siivun puskurista. Protokollakirjasto saattaa kutsua sitä useammin kuin kerran, kun puskuri on suurempi kuin neuvoteltu enimmäishyötykuorma; offset-argumentti kulkee fragmenttien läpi.

  • write-metodin puuttuminen tarkoittaa, että isännän kirjoitukset torjutaan kehystyskerroksessa, ennen kuin backend on mukana.

12.7.3. Täysi takaisinkutsujen joukko

Viitteeksi jokainen metodi, jota kirjasto etsii backendista:

Metodi

Palauttaa

Tarkoitus

init(self)

object

Valinnainen kertaluonteinen alustus, kun kanava ensimmäisen kerran sitoutuu isäntään. Palauta mikä tahansa muu kuin None -arvo onnistumisen merkiksi.

poll(self)

bool

Palauta True, kun dataa on saatavilla.

lock(self)

bool

Varaa kanava atomista usean paketin siirtoa varten.

unlock(self)

bool

Vapauta aiempi lock.

size(self)

int

Kanavasta tällä hetkellä luettavissa olevien tavujen määrä.

shape(self)

tuple

Enintään neljä kokonaislukua, jotka kuvaavat datarakennetta (esim. kuvan korkeus, leveys, tavumäärä). Isäntä käyttää näitä tyypitettyjen puskurien purkamiseen.

read(self, offset, size)

bytes

Palauta enintään size tavua alkaen kohdasta offset. Kutsutaan kerran fragmenttia kohden, kun hyötykuorma ylittää neuvotellun enimmäismäärän.

readp(self, offset, size)

bytes

read-metodin nollakopiointivariantti: puskurin muistin on pysyttävä kelvollisena koko siirron ajan.

write(self, offset, data)

int

Isäntä kirjoitti data kohtaan offset. data on bytearray-näkymä protokollakerroksen vastaanottopuskuriin – kopioi haluamasi talteen ennen paluuta.

ioctl(self, cmd, length, arg)

int

Sovelluskohtainen operaatiokoodi luku/kirjoitus-mallin ulkopuolella. Negatiivinen paluuarvo on virhe.

flush(self)

object

Hylkää kaikki puskuroitu data. Kutsutaan, kun isäntä haluaa nollata kanavan.

is_active(self)

bool

Merkityksellinen vain backendeissä, jotka edustavat fyysistä siirtotietä (sisäänrakennetut USB-kanavat). Sovelluskanavat eivät tarvitse tätä.

Tässä on koko backend-rajapinta. Kaksitoista metodinimeä, kaikki valinnaisia, ja protokollakirjasto päättää, mitä kukin kanava voi tehdä sen perusteella, mitkä niistä ovat läsnä.