ここでは、 ese774_frame.clients.device_proxy、grpc_frame.device_proxy、および独立した device_proxy_factory の core.py / factories.py / typing_generator.py を区別して説明する。いずれもクライアント生成の仕組みであり、デバイスサーバーの起動や DeviceClass の実装を代行しない。
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 は例示であり、実際の引数は登録したクライアントのコンストラクタに従う。
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 が必要である。接続先引数は実際のクライアント定義に合わせる。
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) は登録済みデバイス名を固定するラッパーを返す。これもクライアントの通信機能そのものを生成するわけではない。
.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 は含まない。
共通 Factory はクライアントの生成方法を登録するものであり、通信の統一、API の同一性、DeviceClass の自動公開、接続の健全性、エラーの転送を保証しない。実際の通信・エラー伝達は各 Frame とクライアントが担当する。Factory の運用実績は現時点でゼロであるため、導入前に登録順序、重複登録、型スタブの配置、同期・非同期の選択、接続断などを個別に検証する。