Ohara Lab

DeviceProxy と Factory の利用

開発ガイドへ戻る

対象と実装の区別

ここでは、 ese774_frame.clients.device_proxy、grpc_frame.device_proxy、および独立した device_proxy_factory の core.py / factories.py / typing_generator.py を区別して説明する。いずれもクライアント生成の仕組みであり、デバイスサーバーの起動や DeviceClass の実装を代行しない。

1. ese774 Frame の DeviceProxy

register_device_proxy() で名前とクライアントクラスを登録し、DeviceProxy() でインスタンスを生成する。次は登録と生成の構文を示す例であり、ExampleSyncClient 等は利用するデバイスパッケージが提供するクライアントクラスに置き換える。

from ese774_frame.clients.device_proxy import DeviceProxy, register_device_proxy
from your_device.clients import ExampleAsyncClient, ExampleSyncClient

register_device_proxy(
    "ExampleDevice",
    async_client_cls=ExampleAsyncClient,
    sync_client_cls=ExampleSyncClient,
    default_async_mode=True,
)

sync_client = DeviceProxy("ExampleDevice", "http://127.0.0.1:8000", async_mode=False)
async_client = DeviceProxy("ExampleDevice", "http://127.0.0.1:8000", async_mode=True)

async_mode=None は登録時の default_async_mode を使用する。専用クライアントが未登録の場合は、api_spec と object_name を使い、汎用 SyncDeviceClient / AsyncDeviceClient にフォールバックする。create_device_proxy("ExampleDevice") はデバイス名を固定した呼び出し可能オブジェクトを返す。

from ese774_frame.clients.device_proxy import create_device_proxy

ExampleProxy = create_device_proxy("ExampleDevice")
client = ExampleProxy("http://127.0.0.1:8000", async_mode=False)

上記の URL は例示であり、実際の引数は登録したクライアントのコンストラクタに従う。

2. gRPC Frame の DeviceProxy

gRPC Frame にも同名の API があるが、レジストリは ese774 Frame と別である。専用クライアントを登録する例を示す。

from grpc_frame.device_proxy import DeviceProxy, register_device_proxy
from your_device.grpc_clients import ExampleAsyncGrpcClient, ExampleSyncGrpcClient

register_device_proxy(
    "ExampleDevice",
    async_client_cls=ExampleAsyncGrpcClient,
    sync_client_cls=ExampleSyncGrpcClient,
    default_async_mode=False,
)
client = DeviceProxy("ExampleDevice", "127.0.0.1:50051", async_mode=False)

gRPC 版は api_spec 等のメタデータを受け付けるが、実行時のメソッド解決は Describe RPC を使用する。専用クライアントがない場合、SyncGrpcClient / AsyncGrpcClient を選択する。登録時には少なくとも専用クライアントクラスか api_spec が必要である。接続先引数は実際のクライアント定義に合わせる。

3. 通信 Frame に依存しない Factory(試験的)

device_proxy_factory.core の DeviceProxy は、Frame ごとの DeviceProxy とは別の関数である。共通 Factory はデバイス名と creator を登録し、呼び出し時に引数を解釈せず creator に転送する。HTTP / gRPC / TANGO の選択や async_mode の解釈は共通 DeviceProxy の責務ではない。

multiple_devices.py に対応する例:

from device_proxy_factory import (
    DeviceProxy,
    register_device_proxy,
    make_async_sync_factory,
)

class SyncExampleClient:
    def __init__(self, address):
        self.address = address

class AsyncExampleClient(SyncExampleClient):
    pass

class TangoExampleClient:
    def __init__(self, url):
        self.url = url

register_device_proxy(
    "ExampleHttp",
    make_async_sync_factory(
        async_client_cls=AsyncExampleClient,
        sync_client_cls=SyncExampleClient,
    ),
)
register_device_proxy("ExampleTango", TangoExampleClient)

http = DeviceProxy("ExampleHttp", "127.0.0.1", async_mode=False)
tango = DeviceProxy("ExampleTango", "tango://example")

make_async_sync_factory() は async_mode が None の場合に default_async_mode を使い、指定された同期・非同期クラスを生成する。選択されたクラスが未登録なら ValueError、async_mode が bool / None 以外なら TypeError となる。共通レジストリでは同名の再登録が ValueError、未登録名の取得は KeyError になる。Frame ごとのレジストリと自動的には共有されない。

create_device_proxy(device_class) は登録済みデバイス名を固定するラッパーを返す。これもクライアントの通信機能そのものを生成するわけではない。

4. 型情報(.pyi)

共通 Factory の typing_generator.py は、JSON の devices 配列から core.pyi を生成する。実行時レジストリから自動収集するものではない。例えば:

{
  "devices": [
    {
      "device_class": "ExampleHttp",
      "async_client": "your_device.clients.AsyncExampleClient",
      "sync_client": "your_device.clients.SyncExampleClient",
      "default_async_mode": true
    },
    {
      "device_class": "ExampleTango",
      "client": "your_device.clients.TangoExampleClient"
    }
  ]
}
python -m device_proxy_factory.typing_generator devices.json path/to/core.pyi

生成器は Literal と overload を用いてデバイス名・async_mode に応じた戻り値型を表現する。専用クライアントの .pyi はコピーしないため、各デバイスパッケージ側で型定義を提供する必要がある。付属の core.pyi は汎用宣言のみで、具体的なデバイス型の overload は含まない。

5. 適用上の注意

共通 Factory はクライアントの生成方法を登録するものであり、通信の統一、API の同一性、DeviceClass の自動公開、接続の健全性、エラーの転送を保証しない。実際の通信・エラー伝達は各 Frame とクライアントが担当する。Factory の運用実績は現時点でゼロであるため、導入前に登録順序、重複登録、型スタブの配置、同期・非同期の選択、接続断などを個別に検証する。