plesty.lib.ui.device_link
How a panel reaches a device, and why it never does so on the GUI thread.
A monitor reads a run directory; a device panel drives an instrument, and the instrument may be in this process, in another one, or on another machine. The three cases differ only in the handle:
in a module’s own GUI, the device object, wrapped by
LocalHandlein the bench console, a
DeviceTCPIPClient— it satisfies the protocol as writtenfrom a remote
plesty-bench, the same calls relayed by the bench agent
DeviceHandle is that shared shape, so DevicePanel
is written once and the transport is somebody else’s problem.
DeviceWorker is the second half. Panels are ticked on the shell’s
clock, which is the GUI thread, and a device read is a round trip to hardware:
a shell holding a panel per device would spend its frame budget waiting on
serial lines. Every call therefore goes to a worker thread and every result
comes back through a queue the panel drains on its next tick — the shell’s own
clock does the hand-over, so nothing here imports a toolkit or needs a signal.
Attributes
Classes
The four things a panel asks of a device, wherever the device is. |
|
A |
|
One finished call, waiting to be applied on the GUI thread. |
|
One queued call. |
|
Runs a handle's calls on one thread and hands the results back by queue. |
Module Contents
- plesty.lib.ui.device_link.JOIN_TIMEOUT_S = 2.0
- class plesty.lib.ui.device_link.DeviceHandle
Bases:
ProtocolThe four things a panel asks of a device, wherever the device is.
DeviceTCPIPClientsatisfies this as written; an in-process device reaches it throughLocalHandle; a remote bench relays the same four calls through its agent.- describe() dict[str, Any]
Return
{"methods": [...], "doc": {...} | None}for the device.- Return type:
dict[str, Any]
- query(key: str) Any
Read configuration parameter key from the device.
- Parameters:
key (str)
- Return type:
Any
- write(key: str, value: Any) Any
Write value to configuration parameter key.
- Parameters:
key (str)
value (Any)
- Return type:
Any
- call(op: str, **kwargs: Any) Any
Run operation op with keyword arguments.
- Parameters:
op (str)
kwargs (Any)
- Return type:
Any
- class plesty.lib.ui.device_link.LocalHandle(device: Any)
A
DeviceHandleover a device object in this process.The device already answers
query()andwrite(); what it does not have is the other two, because in-process callers usedoc_model()and call operations as attributes. Going through the same serialized doc payload the wire uses is deliberate — a module’s own GUI then exercises exactly what the console and a remote client will see, so a hole in the payload shows up at the bench and not only across a network.Wrap device.
- Parameters:
device (Any) – A
BaseDeviceSyncModel.
- device
- describe() dict[str, Any]
Return the device’s methods and its serialized doc model.
- Returns:
The same shape the device server’s
describereply carries.- Return type:
dict[str, Any]
- query(key: str) Any
Read parameter key.
- Parameters:
key (str) – Configuration key.
- Returns:
The value the device reported.
- Return type:
Any
- write(key: str, value: Any) Any
Write value to parameter key.
- Parameters:
key (str) – Configuration key.
value (Any) – The value to set.
- Returns:
Whatever the device’s
writereturns.- Return type:
Any
- call(op: str, **kwargs: Any) Any
Run operation op.
- Parameters:
op (str) – Operation name.
**kwargs (Any) – The operation’s arguments.
- Returns:
The operation’s result.
- Return type:
Any
- class plesty.lib.ui.device_link.Result
One finished call, waiting to be applied on the GUI thread.
- Variables:
tag – What the panel asked for, as it labelled the request.
value – The return value, or
Nonewhen the call raised.error – The exception message, or
""when the call succeeded.duration – How long the call took, in milliseconds.
- tag: Any
- value: Any = None
- error: str = ''
- duration: float = 0.0
- property ok: bool
Whether the call returned rather than raised.
- Return type:
bool
- class plesty.lib.ui.device_link._Request
One queued call.
- tag: Any
- verb: str
- args: tuple[Any, Ellipsis] = ()
- kwargs: dict[str, Any]
- class plesty.lib.ui.device_link.DeviceWorker(handle: DeviceHandle, name: str = 'device')
Runs a handle’s calls on one thread and hands the results back by queue.
One worker per panel, not one per shell. A device server that has died answers nothing until its client times out, and a shared worker would make that one device stall every panel in the window; a worker of its own means the dead device’s panel goes stale and the rest keep their clock.
Requests are coalesced by tag: asking again for something already queued or in flight is dropped rather than appended. Without it a device slower than the shell’s refresh accumulates a poll backlog it can never work off, and the panel ends up showing readings from minutes ago.
Prepare a worker over handle; it starts on
start().- Parameters:
handle (DeviceHandle) – What the worker calls.
name (str) – Used for the thread name and log lines.
- handle
- name = 'device'
- _inflight: set[Any]
- _lock
- _thread: threading.Thread | None = None
- start() None
Start the worker thread; calling it twice is harmless.
- Return type:
None
- stop(timeout: float = JOIN_TIMEOUT_S) None
Ask the worker to finish and wait briefly for it.
The thread is a daemon and the wait is short on purpose: a call already carries the device’s own timeout, and closing a window must not hang on an instrument that stopped answering.
- Parameters:
timeout (float) – Seconds to wait for the thread to end.
- Return type:
None
- property running: bool
Whether the worker thread is alive.
- Return type:
bool
- submit(tag: Any, verb: str, *args: Any, **kwargs: Any) bool
Queue a call, unless one with the same tag is already outstanding.
- Parameters:
tag (Any) – Label the matching
Resultcarries back, and the key requests are coalesced on.verb (str) –
"describe","query","write"or"call".*args (Any) – Positional arguments for the verb.
**kwargs (Any) – Keyword arguments for the verb.
- Returns:
Truewhen the request was queued,Falsewhen an identical tag was already outstanding and this one was dropped.- Return type:
bool
- results() list[Result]
Take every finished call off the queue without blocking.
- Returns:
The results that arrived since the last drain, in order.
- Return type:
list[Result]
- property pending: int
How many calls are queued or in flight.
- Return type:
int
- _run() None
Take requests until asked to stop.
- Return type:
None