12.7. Wywołania zwrotne kanału¶
Obiekt backendu przekazywany do protocol.register() jest klasą Pythona. Biblioteka protokołu nie pyta klasy, które metody implementuje; sprawdza instancję i podłącza te, które znajdzie. Ta introspekcja sprawia, że interfejs backendu jest elastyczny: najmniejszy użyteczny backend to dwie metody, najbardziej rozbudowany to dwanaście, a aplikacja włącza każdą zdolność po jednej metodzie naraz.
12.7.1. Reguły introspekcji¶
Gdy uruchamia się protocol.register(), biblioteka przechodzi przez stałą listę nazw wywoływalnych i wiąże każdą z nich, którą znajdzie w instancji backendu:
Dodanie
readdo klasy włączaCHANNEL_FLAG_READ. Wywołanie hostachannel_read()dociera do backendu tylko wtedy, gdy ta flaga jest ustawiona.Dodanie
writewłączaCHANNEL_FLAG_WRITE, umożliwiającchannel_write().Dodanie
lockiunlockwłączaCHANNEL_FLAG_LOCK, umożliwiając hostowi zablokowanie kanału w celu atomowego odczytu wielu pakietów.Dodanie
pollpozwala hostowi tanio zapytać „czy coś jest gotowe?”, bez wymuszania pełnego odczytu.
Brakujące metody nie są błędami – biblioteka protokołu po prostu pozostawia odpowiednią zdolność wyłączoną. Backend mający tylko size i read jest całkowicie poprawny; jest to kanał danych tylko do odczytu.
12.7.2. Kanał sensora tylko do odczytu¶
Kanał sensora, który publikuje świeży odczyt za każdym razem, gdy host o niego prosi, odrzucając zapisy hosta, wykorzystuje cztery z wywołań zwrotnych:
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))
Omówienie, co robi każda metoda:
pollzwraca flagę świeżości. Host wywołuje ją przed odczytem i całkowicie pomija odczyt, gdy zwracaFalse. Oszczędza to koszt rundy w obie strony dla „jeszcze nie ma nowych danych”.sizeregeneruje bufor na żądanie i raportuje jego długość. Wykonywanie próbkowania w tym miejscu oznacza, że backend nie potrzebuje zadania w tle – każdy pomiar napędza wywołanie hosta.readzwraca wycinek bufora. Biblioteka protokołu może wywołać ją więcej niż raz, gdy bufor jest większy niż wynegocjowany maksymalny ładunek; argumentoffsetprzechodzi przez fragmenty.Brak
writeoznacza, że zapisy hosta są odrzucane na warstwie ramkowania, zanim backend zostanie zaangażowany.
12.7.3. Pełny zestaw wywołań zwrotnych¶
Dla odniesienia, każda metoda, której biblioteka szuka w backendzie:
Metoda |
Zwraca |
Cel |
|---|---|---|
|
object |
Opcjonalna jednorazowa inicjalizacja przy pierwszym powiązaniu kanału z hostem. W przypadku powodzenia zwróć dowolną wartość różną od |
|
bool |
Zwraca |
|
bool |
Przejmuje kanał dla atomowego transferu wielu pakietów. |
|
bool |
Zwalnia wcześniejszy |
|
int |
Liczba bajtów aktualnie możliwych do odczytu z kanału. |
|
tuple |
Do czterech liczb całkowitych opisujących strukturę danych (np. wysokość obrazu, szerokość, liczba bajtów). Używane przez hosta do rozpakowania typowanych buforów. |
|
bytes |
Zwraca do size bajtów począwszy od offset. Wywoływana raz na fragment, gdy ładunek przekracza wynegocjowane maksimum. |
|
bytes |
Wariant |
|
int |
Host zapisał data pod adresem offset. |
|
int |
Kod operacji zdefiniowany przez aplikację, poza modelem odczyt/zapis. Ujemny zwrot oznacza błąd. |
|
object |
Odrzuca wszelkie zbuforowane dane. Wywoływana, gdy host chce zresetować kanał. |
|
bool |
Znacząca tylko dla backendów reprezentujących fizyczny transport (wbudowane kanały USB). Kanały aplikacji tego nie potrzebują. |
To cały interfejs backendu. Dwanaście nazw metod, wszystkie opcjonalne, a biblioteka protokołu decyduje, co każdy kanał może zrobić, na podstawie tego, które z nich są obecne.