LIB Logo
  • Install
  • Quick Start
    • A Device
    • An Experiment
    • An Analyzer
    • In a Terminal
  • Concepts
    • Where a Measurement’s Data Goes
    • Preflight: is the instrument ready to run?
    • Packages and Layers
    • Interface Contracts
    • Design Decisions
    • Dependencies
  • Device
    • Base Device
    • Parameter System
    • Function System
    • Composite Device
    • Traffic Manager
    • Command Solver
    • Remote Access
    • Error Handling
  • Experiment Framework
    • Writing an Experiment
    • The Command Line
    • Checking the Devices Before a Run
    • The Setup File
    • Data on a Shared Disk
    • Stopping and Continuing a Run
    • Reading a Run
  • Analyzer Framework
    • Writing an Analyzer
    • Reports
    • SpectrumFit
  • Viewing a Run
    • The Viewer: Viz
    • The Run’s Data: RunData
    • Plots and Drawables
    • Ready-Made Plots
    • Shell Framework
  • Common to All Modules
    • The .env File
    • YAML Configuration
    • Logging System
    • Data & Metadata Schemas
  • Testing
    • Testing a Device
    • Device Test Helpers
    • Testing an Experiment
    • Testing an Analyzer
    • Testing a Viewer
    • Testing on the Instrument
    • Testing Over the Network
    • Testing Devices That Share a Computer
    • Simulation
  • Contributing
  • API Reference
    • plesty.lib
      • plesty.lib.analyzer
        • plesty.lib.analyzer.base_analyzer
        • plesty.lib.analyzer.spectrum_fit
      • plesty.lib.data
        • plesty.lib.data.array
        • plesty.lib.data.ctype_manager
        • plesty.lib.data.experiment_data
        • plesty.lib.data.io
        • plesty.lib.data.spectrum
        • plesty.lib.data.table
        • plesty.lib.data.types
        • plesty.lib.data.units
      • plesty.lib.device
        • plesty.lib.device.async_wrapper
        • plesty.lib.device.base_apt_device
        • plesty.lib.device.base_device_sync
        • plesty.lib.device.base_tcp_scpi_device
        • plesty.lib.device.base_visa_scpi_device
        • plesty.lib.device.composite_device
        • plesty.lib.device.device_utils
        • plesty.lib.device.doc
        • plesty.lib.device.funcs
        • plesty.lib.device.params
        • plesty.lib.device.telemetry
      • plesty.lib.experiment
        • plesty.lib.experiment.base_experiment
        • plesty.lib.experiment.cli
        • plesty.lib.experiment.control
        • plesty.lib.experiment.journal
        • plesty.lib.experiment.manifest
        • plesty.lib.experiment.preflight
        • plesty.lib.experiment.runs
        • plesty.lib.experiment.schedule
        • plesty.lib.experiment.setup
      • plesty.lib.monitor
        • plesty.lib.monitor.base_monitor
        • plesty.lib.monitor.common
          • plesty.lib.monitor.common.series
          • plesty.lib.monitor.common.spectrum
        • plesty.lib.monitor.data
        • plesty.lib.monitor.plots
        • plesty.lib.monitor.sources
        • plesty.lib.monitor.viz
      • plesty.lib.report
        • plesty.lib.report.blocks
        • plesty.lib.report.html
        • plesty.lib.report.latex
        • plesty.lib.report.markup
        • plesty.lib.report.svg
        • plesty.lib.report.tables
      • plesty.lib.service
        • plesty.lib.service.resource_manager
        • plesty.lib.service.tcp_ip_client
        • plesty.lib.service.tcp_ip_server
      • plesty.lib.sim
        • plesty.lib.sim.apt
        • plesty.lib.sim.data_generator
        • plesty.lib.sim.demo_device
      • plesty.lib.solver
        • plesty.lib.solver.iceblock
        • plesty.lib.solver.scpi
      • plesty.lib.test
        • plesty.lib.test.analyzer_pipeline
        • plesty.lib.test.client_field_test
        • plesty.lib.test.device_func_system
        • plesty.lib.test.device_param_system
        • plesty.lib.test.device_pipeline
        • plesty.lib.test.experiment_pipeline
        • plesty.lib.test.field_test
        • plesty.lib.test.field_test_concurrency
        • plesty.lib.test.grouped_param_keys
        • plesty.lib.test.report_artifact
        • plesty.lib.test.resource_allocation
        • plesty.lib.test.schema_params
        • plesty.lib.test.schema_refresh
      • plesty.lib.traffic
        • plesty.lib.traffic.apt
        • plesty.lib.traffic.apt_d2xx
        • plesty.lib.traffic.apt_protocol
        • plesty.lib.traffic.preflight
        • plesty.lib.traffic.serial
        • plesty.lib.traffic.serial_ports
        • plesty.lib.traffic.tcp_ip
        • plesty.lib.traffic.usb_utils
        • plesty.lib.traffic.utils
        • plesty.lib.traffic.visa
      • plesty.lib.ui
        • plesty.lib.ui.device_link
        • plesty.lib.ui.device_panel
        • plesty.lib.ui.monitor_panel
        • plesty.lib.ui.panel
        • plesty.lib.ui.plot_panel
        • plesty.lib.ui.qt
          • plesty.lib.ui.qt.app
          • plesty.lib.ui.qt.form
          • plesty.lib.ui.qt.plot
          • plesty.lib.ui.qt.recorder
          • plesty.lib.ui.qt.shell
        • plesty.lib.ui.readings
        • plesty.lib.ui.theme
      • plesty.lib.utils
        • plesty.lib.utils.config
        • plesty.lib.utils.dll_utils
        • plesty.lib.utils.error_utils
        • plesty.lib.utils.logger
        • plesty.lib.utils.registry
        • plesty.lib.utils.settings
LIB
  • API Reference
  • plesty.lib
  • plesty.lib.ui
  • plesty.lib.ui.device_link
  • View page source

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 LocalHandle

  • in the bench console, a DeviceTCPIPClient — it satisfies the protocol as written

  • from 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

JOIN_TIMEOUT_S

Classes

DeviceHandle

The four things a panel asks of a device, wherever the device is.

LocalHandle

A DeviceHandle over a device object in this process.

Result

One finished call, waiting to be applied on the GUI thread.

_Request

One queued call.

DeviceWorker

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: Protocol

The four things a panel asks of a device, wherever the device is.

DeviceTCPIPClient satisfies this as written; an in-process device reaches it through LocalHandle; 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 DeviceHandle over a device object in this process.

The device already answers query() and write(); what it does not have is the other two, because in-process callers use doc_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 describe reply 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 write returns.

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 None when 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'
_requests: queue.Queue[_Request | None]
_results: queue.Queue[Result]
_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 Result carries 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:

True when the request was queued, False when 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

_answer(request: _Request) → Result

Run one request and wrap whatever it does into a Result.

Every exception is caught: a panel reports a device that refused a write in its title bar, and a worker thread that died would instead leave the panel silently frozen.

Parameters:

request (_Request) – The queued call.

Returns:

The result to hand back.

Return type:

Result

Previous Next

© Copyright 2026, Plesty Development Team.

Built with Sphinx using a theme provided by Read the Docs.