12.7. Kanal geri çağırmaları

protocol.register() işlevine verilen backend nesnesi bir Python sınıfıdır. Protokol kütüphanesi sınıfa hangi metotları uyguladığını sormaz; örneği inceler ve bulduklarını bağlar. Backend arayüzünü esnek kılan da bu inceleme işlemidir: en küçük kullanışlı backend iki metottan oluşur, en ayrıntılısı on iki metottan oluşur ve uygulama her yeteneği birer metot olarak teker teker etkinleştirir.

12.7.1. İnceleme kuralları

protocol.register() çalıştığında, kütüphane sabit bir çağrılabilir ad listesini dolaşır ve backend örneğinde bulduğu her birini bağlar:

  • Sınıfa read eklemek CHANNEL_FLAG_READ bayrağını açar. Ana makinenin channel_read() çağrısı yalnızca bu bayrak ayarlıysa backend’e ulaşır.

  • write eklemek CHANNEL_FLAG_WRITE bayrağını açarak channel_write() işlevini etkinleştirir.

  • lock ve unlock eklemek CHANNEL_FLAG_LOCK bayrağını açarak ana makinenin kanalı çok paketli atomik bir okuma için kilitlemesine olanak tanır.

  • poll eklemek, ana makinenin tam bir okuma yapmaya zorlanmadan, ucuza “hazır bir şey var mı?” diye sormasını sağlar.

Eksik metotlar hata değildir – protokol kütüphanesi yalnızca ilgili yeteneği devre dışı bırakır. Yalnızca size ve read içeren bir backend tamamen geçerlidir; bu salt okunur bir veri kanalıdır.

12.7.2. Salt okunur bir sensör kanalı

Ana makine her sorduğunda yeni bir okuma yayımlayan, ana makine yazmalarını reddeden bir sensör kanalı, geri çağırmalardan dördünü kullanır:

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

Her metodun ne yaptığını adım adım inceleyelim:

  • poll tazelik bayrağını döndürür. Ana makine okumadan önce bunu çağırır ve False döndürdüğünde okumayı tamamen atlar. Bu, “henüz yeni veri yok” durumunda gidiş-dönüş maliyetinden tasarruf sağlar.

  • size arabelleği istek üzerine yeniden oluşturur ve uzunluğunu bildirir. Örneklemeyi burada yapmak, backend’in bir arka plan görevine ihtiyaç duymaması anlamına gelir – her ölçümü bir ana makine çağrısı tetikler.

  • read arabelleğin bir dilimini döndürür. Arabellek, görüşülen maksimum yükten daha büyük olduğunda protokol kütüphanesi bunu birden fazla kez çağırabilir; offset argümanı parçalar arasında ilerler.

  • write olmaması, ana makine yazmalarının backend devreye girmeden önce çerçeveleme katmanında reddedildiği anlamına gelir.

12.7.3. Tam geri çağırma kümesi

Başvuru için, kütüphanenin bir backend üzerinde aradığı her metot:

Metot

Döndürür

Amaç

init(self)

object

Kanal bir ana makineye ilk bağlandığında isteğe bağlı tek seferlik başlatma. Başarı durumunda None olmayan herhangi bir değer döndürün.

poll(self)

bool

Veri kullanılabilir olduğunda True döndürün.

lock(self)

bool

Atomik çok paketli bir aktarım için kanalı edinin.

unlock(self)

bool

Önceki bir lock işlemini serbest bırakın.

size(self)

int

Kanaldan şu anda okunabilir bayt sayısı.

shape(self)

tuple

Veri yapısını tanımlayan en fazla dört tam sayı (ör. görüntü yüksekliği, genişliği, bayt sayısı). Ana makine tarafından tipli arabellekleri açmak için kullanılır.

read(self, offset, size)

bytes

offset konumundan başlayarak en fazla size bayt döndürün. Yük, görüşülen maksimumu aştığında parça başına bir kez çağrılır.

readp(self, offset, size)

bytes

read işlevinin sıfır kopyalı çeşidi: arabelleğin belleği aktarım süresi boyunca geçerli kalmalıdır.

write(self, offset, data)

int

Ana makine offset konumuna data yazdı. data, protokol katmanının alım arabelleğine açılan bir bytearray görünümüdür – saklamak istediğinizi geri dönmeden önce kopyalayıp çıkarın.

ioctl(self, cmd, length, arg)

int

Okuma/yazma modelinin dışında, uygulama tanımlı bir işlem kodu. Negatif dönüş bir hatadır.

flush(self)

object

Arabelleğe alınmış herhangi bir veriyi atın. Ana makine kanalı sıfırlamak istediğinde çağrılır.

is_active(self)

bool

Yalnızca fiziksel bir aktarımı temsil eden backend’lerde (yerleşik USB kanalları) anlamlıdır. Uygulama kanallarının buna ihtiyacı yoktur.

Backend arayüzünün tamamı budur. On iki metot adı, tümü isteğe bağlı ve protokol kütüphanesi her kanalın ne yapabileceğine hangilerinin mevcut olduğuna göre karar verir.