12.7. チャネルコールバック¶
protocol.register() に渡されるバックエンドオブジェクトは Python のクラスです。プロトコルライブラリは、そのクラスがどのメソッドを実装しているかをクラス自身に問い合わせることはしません。インスタンスを検査して、見つかったメソッドを結び付けます。このイントロスペクションこそが、バックエンドインターフェースを柔軟にしているものです。最小限で役立つバックエンドは 2 つのメソッドからなり、最も精巧なものは 12 個になり、アプリケーションは 1 度に 1 つのメソッドずつ、各機能をオプトインで利用します。
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. 読み取り専用のセンサーチャネル¶
ホストが要求するたびに新しい読み取り値を発行し、ホストからの書き込みを拒否するセンサーチャネルは、コールバックのうち 4 つを利用します:
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 |
データ構造を記述する最大 4 つの整数(例: 画像の高さ、幅、バイト数)。型付きバッファをアンパックするためにホストが使用します。 |
|
bytes |
offset から始まる最大 size バイトを返します。ペイロードがネゴシエートされた最大値を超える場合、フラグメントごとに 1 回ずつ呼び出されます。 |
|
bytes |
|
|
int |
ホストが offset に data を書き込みました。 |
|
int |
読み取り/書き込みモデルの外にあるアプリケーション定義のオペコード。負の戻り値はエラーです。 |
|
object |
バッファされたデータをすべて破棄します。ホストがチャネルをリセットしたいときに呼び出されます。 |
|
bool |
物理的なトランスポート(組み込みの USB チャネル)を表すバックエンドでのみ意味を持ちます。アプリケーションチャネルにはこれは不要です。 |
これがバックエンドインターフェースの全体です。12 個のメソッド名はすべて省略可能で、プロトコルライブラリはどれが存在するかに基づいて各チャネルが何をできるかを決定します。