Ohara Lab

gRPC Frame Development Guide

Device-server development / gRPC Quick Start / Home

This guide covers gRPC server and client development. Device control remains in the independent DeviceClass. Installation and packaging are documented separately in the installation guide.

Streaming and event guides

1. Dynamic dispatch: Describe / Call

create_grpc_server(ctrl_obj=...) receives a DeviceClass instance. GrpcClient obtains public methods using Describe, dynamically binds them, and invokes them through Call. Per-device RPC methods do not have to be added individually to .proto files. Serialization, exposed methods, and exception propagation still require testing.

from grpc_frame.grpc_server import create_grpc_server
server = create_grpc_server(ctrl_obj=ctrl, max_workers=10)

2. Synchronous and specialized clients

from grpc_frame.grpc_client import GrpcClient

class InstrumentGrpcClient(GrpcClient):
    def __init__(self, server_ip: str, server_port: int):
        super().__init__(server_ip=server_ip, server_port=server_port)

    def read_and_report(self):
        return self.read_value()  # Public DeviceClass method

The added method is a client-side convenience method, not a framework RPC. See aandd_reader_grpc_client.py for a real implementation. Close the client with close().

3. Async client

The aandd_reader package provides AsyncAanddReaderGrpcClient, used as await client.meas(). Its configuration must already contain the server address and port. Check the actual client implementation for its asynchronous execution model.

import asyncio
from aandd_reader import Config
from aandd_reader.server_grpc import AsyncAanddReaderGrpcClient

async def main():
    client = AsyncAanddReaderGrpcClient(config=Config)
    try:
        print(await client.meas())
    finally:
        client.close()

asyncio.run(main())

4. StreamCall: streaming data

StreamCall handles streamed responses rather than a single Call result. In MiniPIX, _MiniPixStream bridges acquisition callbacks into a queue and yields frames as they arrive. The server can use a separate stream_obj alongside ctrl_obj:

server = create_grpc_server(ctrl_obj=ctrl, stream_obj=stream_obj)

Review pixet_grpc_server.py and async_pixet_grpc_client.py for the exact stream methods and client usage. Validate cancellation, queue growth, exceptions, and disconnect handling.

5. Event subscription

The framework provides an EventBus-based event channel, with GrpcClient.subscribe(topic=..., once=...). Streaming measurement frames and subscribing to event notifications are different operations. In aandd_reader, the device-specific client groups setup into prepare_event_measurement():

client.prepare_event_measurement(
    topic="weight_threshold",
    threshold_g=10.0,
    above=True,
    once=True,
    trigger_mode="raw",
)
event = client.wait_event()
print(event)
client.stream_stop()

These are aandd_reader-specific convenience methods, not universal GrpcClient methods. Internally, the client invokes stream_start(), event_set_config(), set_trigger_mode(), and event_arm(); wait_event() subscribes to the selected topic. Distinguish the once parameter for arming from the subscription’s own once behavior.

6. DeviceProxy and type stubs

Generated .pyi files should reflect both remotely exposed methods and client-side additions/overrides. The MiniPIX and aandd_reader packages contain server_grpc/make_pyi.py examples. Direct client use does not require DeviceProxy. See DeviceProxy and Factory.

7. Validation and operations

Check the return value of add_insecure_port, start the server, and test connectivity. The aandd_reader server uses channel_ready_future, calls server.stop(...), and closes the device. insecure_channel does not provide encryption; consider network exposure. Review gRPC message size options for large payloads.

8. Implementations

See gRPC implementation examples for source excerpts.