12.7. Channel-Callbacks¶
Das Backend-Objekt, das an protocol.register() übergeben wird, ist eine Python-Klasse. Die Protokollbibliothek fragt die Klasse nicht, welche Methoden sie implementiert; sie inspiziert die Instanz und verdrahtet diejenigen, die sie findet. Diese Introspektion ist es, die die Backend-Schnittstelle flexibel macht: Das kleinste sinnvolle Backend besteht aus zwei Methoden, das aufwendigste aus zwölf, und die Anwendung aktiviert jede Fähigkeit einzeln, Methode für Methode.
12.7.1. Die Introspektionsregeln¶
Wenn protocol.register() läuft, durchläuft die Bibliothek eine feste Liste von aufrufbaren Namen und bindet jeden, den sie auf der Backend-Instanz findet:
Das Hinzufügen von
readzur Klasse aktiviertCHANNEL_FLAG_READ. Ein Host-Aufruf vonchannel_read()erreicht das Backend nur, wenn dieses Flag gesetzt ist.Das Hinzufügen von
writeaktiviertCHANNEL_FLAG_WRITEund ermöglichtchannel_write().Das Hinzufügen von
lockundunlockaktiviertCHANNEL_FLAG_LOCKund ermöglicht es dem Host, den Channel für einen atomaren Lesevorgang über mehrere Pakete zu sperren.Das Hinzufügen von
pollermöglicht es dem Host, kostengünstig zu fragen „ist etwas bereit?“, ohne einen vollständigen Lesevorgang zu erzwingen.
Fehlende Methoden sind keine Fehler – die Protokollbibliothek lässt die entsprechende Fähigkeit einfach deaktiviert. Ein Backend mit nur size und read ist völlig gültig; es ist ein schreibgeschützter Datenchannel.
12.7.2. Ein schreibgeschützter Sensor-Channel¶
Ein Sensor-Channel, der bei jeder Host-Anfrage einen frischen Messwert veröffentlicht und Host-Schreibvorgänge ablehnt, nutzt vier der Callbacks:
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))
Durchgehen, was jede Methode tut:
pollgibt das Aktualitäts-Flag zurück. Der Host ruft es vor dem Lesen auf und überspringt den Lesevorgang vollständig, wenn esFalsezurückgibt. Das spart die Round-Trip-Kosten für „noch keine neuen Daten“.sizeerzeugt den Puffer bei Bedarf neu und meldet dessen Länge. Die Abtastung hier durchzuführen bedeutet, dass das Backend keine Hintergrundaufgabe benötigt – ein Host-Aufruf treibt jede Messung an.readgibt einen Ausschnitt des Puffers zurück. Die Protokollbibliothek kann es mehr als einmal aufrufen, wenn der Puffer größer als die ausgehandelte maximale Nutzlast ist; das Argumentoffsetdurchläuft die Fragmente.Kein
writebedeutet, dass Host-Schreibvorgänge auf der Framing-Ebene abgelehnt werden, bevor das Backend überhaupt beteiligt ist.
12.7.3. Der vollständige Callback-Satz¶
Zur Referenz: jede Methode, nach der die Bibliothek auf einem Backend sucht:
Methode |
Rückgabe |
Zweck |
|---|---|---|
|
object |
Optionale einmalige Initialisierung, wenn der Channel sich erstmals an einen Host bindet. Gibt bei Erfolg einen beliebigen Wert ungleich |
|
bool |
Gibt |
|
bool |
Erwirbt den Channel für eine atomare Übertragung über mehrere Pakete. |
|
bool |
Gibt ein vorheriges |
|
int |
Anzahl der aktuell aus dem Channel lesbaren Bytes. |
|
tuple |
Bis zu vier Ganzzahlen, die die Datenstruktur beschreiben (z. B. Bildhöhe, -breite, Byteanzahl). Wird vom Host zum Entpacken typisierter Puffer verwendet. |
|
bytes |
Gibt bis zu size Bytes ab offset zurück. Wird einmal pro Fragment aufgerufen, wenn die Nutzlast das ausgehandelte Maximum überschreitet. |
|
bytes |
Zero-Copy-Variante von |
|
int |
Der Host hat data an offset geschrieben. |
|
int |
Anwendungsdefinierter Opcode außerhalb des Lese-/Schreibmodells. Ein negativer Rückgabewert ist ein Fehler. |
|
object |
Verwirft alle gepufferten Daten. Wird aufgerufen, wenn der Host den Channel zurücksetzen möchte. |
|
bool |
Nur sinnvoll bei Backends, die einen physischen Transport repräsentieren (die eingebauten USB-Channels). Anwendungs-Channels benötigen dies nicht. |
Das ist die gesamte Backend-Schnittstelle. Zwölf Methodennamen, alle optional, und die Protokollbibliothek entscheidet anhand der vorhandenen Methoden, was jeder Channel tun kann.