12.7. Функции обратного вызова канала¶
Объект бэкенда, передаваемый в protocol.register(), представляет собой класс Python. Библиотека протокола не спрашивает у класса, какие методы он реализует; она исследует экземпляр и подключает те методы, которые находит. Именно это самоисследование делает интерфейс бэкенда гибким: минимальный полезный бэкенд состоит из двух методов, самый сложный – из двенадцати, и приложение подключает каждую возможность по одному методу за раз.
12.7.1. Правила самоисследования¶
При выполнении protocol.register() библиотека проходит по фиксированному списку имён вызываемых объектов и привязывает каждый из них, который находит в экземпляре бэкенда:
Добавление
readв класс включает флагCHANNEL_FLAG_READ. Вызов с хостаchannel_read()достигает бэкенда только в том случае, если этот флаг установлен.Добавление
writeвключает флагCHANNEL_FLAG_WRITE, активируяchannel_write().Добавление
lockиunlockвключает флагCHANNEL_FLAG_LOCK, позволяя хосту заблокировать канал для атомарного многопакетного чтения.Добавление
pollпозволяет хосту дёшево спросить «есть ли что-нибудь готовое?», не выполняя полное чтение.
Отсутствие методов не является ошибкой – библиотека протокола просто оставляет соответствующую возможность отключённой. Бэкенд только с size и read вполне допустим; это канал данных, доступный только для чтения.
12.7.2. Канал датчика, доступный только для чтения¶
Канал датчика, который публикует свежее показание каждый раз, когда хост запрашивает, и отклоняет запись с хоста, использует четыре из функций обратного вызова:
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))
Разберём, что делает каждый метод:
pollвозвращает флаг свежести данных. Хост вызывает его перед чтением и полностью пропускает чтение, когда он возвращаетFalse. Это экономит затраты на обращение в случае «новых данных пока нет».sizeпересоздаёт буфер по запросу и сообщает его длину. Выполнение выборки здесь означает, что бэкенду не нужна фоновая задача – каждое измерение запускается вызовом с хоста.readвозвращает фрагмент буфера. Библиотека протокола может вызвать его более одного раза, когда буфер больше согласованной максимальной полезной нагрузки; аргументoffsetпроходит по фрагментам.Отсутствие
writeозначает, что запись с хоста отклоняется на уровне формирования кадров, до того как задействуется бэкенд.
12.7.3. Полный набор функций обратного вызова¶
Для справки – каждый метод, который библиотека ищет в бэкенде:
Метод |
Возвращает |
Назначение |
|---|---|---|
|
object |
Необязательная однократная инициализация при первой привязке канала к хосту. Возвращает любое значение, отличное от |
|
bool |
Возвращает |
|
bool |
Захватывает канал для атомарной многопакетной передачи. |
|
bool |
Освобождает ранее установленную блокировку |
|
int |
Количество байтов, доступных для чтения из канала в данный момент. |
|
tuple |
До четырёх целых чисел, описывающих структуру данных (например, высота изображения, ширина, количество байтов). Используется хостом для распаковки типизированных буферов. |
|
bytes |
Возвращает до size байтов, начиная с offset. Вызывается один раз для каждого фрагмента, когда полезная нагрузка превышает согласованный максимум. |
|
bytes |
Вариант |
|
int |
Хост записал data по смещению offset. |
|
int |
Определяемый приложением код операции вне модели чтения/записи. Отрицательное возвращаемое значение означает ошибку. |
|
object |
Сбрасывает любые буферизованные данные. Вызывается, когда хост хочет сбросить канал. |
|
bool |
Имеет смысл только для бэкендов, представляющих физический транспорт (встроенные USB-каналы). Каналам приложения это не нужно. |
Это весь интерфейс бэкенда. Двенадцать имён методов, все необязательные, и библиотека протокола решает, что может делать каждый канал, на основе того, какие из них присутствуют.