pytweezer.servers.device_client module¶
Generic device RPC-client factory.
Every device in CONFIG["Devices"] is served by a sipyco RPC server. Rather than
hand-writing a client class per device, get_device() looks the device up by
its config name, resolves the endpoint that serves it, and returns a transparent
sipyco proxy:
cam = get_device("Rb HamCam")
cam.acquire() # remote method call, transparently proxied
cam.close_rpc() # release the socket when done
Every device is addressed by its own name, whether it has a server to itself or
shares one with other devices. A composite device (an entry with a "devices"
sub-dict) runs several devices in one process so they can drive each other without RPC, but its
sub-devices are named in config exactly like any other device and reached the same
way — the caller never needs to know which process a device lives in, or that
sipyco targets exist:
cam = get_device("Rb Feedback Cam") # sub-device of the "Rb Feedback Rig" composite
dac = get_device("Rb Feedback DAC") # ...same process, same port, different target
rig = get_device("Rb Feedback Rig") # the composite itself -> its coordinator
target_name is resolved from the config and only needs passing to reach a target
the config doesn’t name.
For driving two or more device servers concurrently (e.g. starting two MotMaster
sequences in parallel), prefer pytweezer.parallel.run_parallel() — it runs
blocking get_device() clients in threads with no async/await and
works from the GUI. get_device_async() is the lower-level asyncio
counterpart if you want to drive the servers with coroutines directly:
async def main():
mm1 = await get_device_async("Rb MotMaster Server")
mm2 = await get_device_async("CaF MotMaster Server")
try:
await asyncio.gather(
mm1.start_motmaster_experiment(),
mm2.start_motmaster_experiment(),
)
finally:
await mm1.close_rpc()
await mm2.close_rpc()
asyncio.run(main())
- pytweezer.servers.device_client.get_device(name, host=None, port=None, target_name=<class 'sipyco.pc_rpc.AutoTarget'>, timeout=None)[source]¶
Build a sipyco RPC client for the device named
namein the config.- Parameters:
name – Any device name: a top-level
CONFIG["Devices"]key ("Rb HamCam"), a composite’s sub-device ("Rb Feedback Cam"), or a composite itself (which resolves to its coordinator).host – Optional overrides; default to the serving process’s
host/port.port – Optional overrides; default to the serving process’s
host/port.target_name – RPC target to bind. Defaults to
AutoTarget, which selects the right target automatically — the server’s sole target for a plain device, or the one belonging tonameon a composite. Override only to reach a target the config doesn’t name.timeout – Socket timeout in seconds (
None= block indefinitely).
- Returns:
A transparent proxy; remote methods are called as normal attributes. Call
.close_rpc()when finished to release the socket.- Return type:
sipyco.pc_rpc.Client
- async pytweezer.servers.device_client.get_device_async(name, host=None, port=None, target_name=<class 'sipyco.pc_rpc.AutoTarget'>)[source]¶
Build an
AsyncioClientfor the device namedname.Same config lookup as
get_device(), but every RPC method on the returned client is a coroutine. Use this (withasyncio.gather) to drive two independent device servers concurrently — e.g. starting two MotMaster sequences in parallel — since a blockingget_device()client waits for the full RPC round trip, including the remote call’s execution time, before the next line runs.Call
await client.close_rpc()when finished to release the socket.
- pytweezer.servers.device_client.get_device_config(name)[source]¶
Return the config entry for the device named
name.Matches leniently (whitespace-/case-insensitively), and finds composite sub-devices as well as top-level ones. Raises
KeyErrorlisting the known devices if nothing matches.A sub-device’s entry carries only its own driver settings;
host/portbelong to the composite that serves it. Useget_device()(ordevice_server.resolve_address) rather than reading them off this dict.