Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.lagerdata.com/llms.txt

Use this file to discover all available pages before exploring further.

Bug Fixes

  • Keithley 2281S dual-role nets now switch cleanly between supply and battery roles. When the same physical Keithley 2281S is configured with both a power-supply net (e.g. supply1) and a battery net (e.g. battery1), the box now opens exactly one pyvisa session per VISA address and both driver classes wrap that one session — instead of each driver opening its own session and the second hitting [Errno 16] Resource busy. Implemented as a process-wide shared-resource cache (_visa_resources keyed by VISA address) in box/lager/hardware_service.py, plus a raw_resource= factory kwarg on box/lager/power/supply/keithley.py:create_device and box/lager/power/battery/keithley.py:create_device. Both Keithley driver constructors track an _owns_resource flag so close() does not release the underlying USB claim while the sibling driver still needs it. SCPI serialization moved to a per-address lock (was per (device_name, address) cache key) so a supply command followed by a battery command against the same Keithley serialize correctly on the USB bus. This resolves the sequential half of the dual-role known limitation in v0.16.7 — a script can now alternate lager supply <net> and lager battery <net> commands against the same Keithley without restarting the box service. The instrument’s two operating modes (Power Supply via :ENTR:FUNC POW, Battery Simulator via :ENTR:FUNC BATT) remain mutually exclusive in firmware, so genuinely concurrent supply + battery operation against one Keithley is still not supported by the hardware itself; see Known Limitations.
  • Concurrent battery TUI + CLI on the Keithley 2281S no longer fails with [Errno 16] Resource busy. The stale-VISA-session retry path in box/lager/hardware_service.py:/invoke now calls _close_device(old_device, cache_key) before invoking module.create_device(net_info). Previously the popped driver instance stayed alive in the Python process and kept libusb’s USB claim, so the recreated session’s pyvisa.ResourceManager().open_resource(addr) failed with Resource busy — surfaced as Could not open instrument at .... Closing the old session before opening a new one fixes this for any driver whose retry path fires; for Keithley shared-resource drivers the underlying pyvisa session is also reopened so both supply and battery drivers get a fresh handle. This resolves the concurrent battery TUI + CLI known limitation documented in v0.16.7.
  • Keithley 2281S supply commands no longer crash with TypeError. box/lager/http_handlers/supply.py is modeled on multi-channel drivers (Rigol DP800) and calls supply-driver methods with a channel= kwarg or positional channel. The Keithley 2281S supply driver follows the SupplyNet abstract (no channel parameter — the 2281S is single-channel), so the very first call hit TypeError: Keithley2281S.output_is_enabled() got an unexpected keyword argument 'channel'. The handler treated that as a hardware failure and triggered /cache/clear, which tore down the shared pyvisa session this release had just opened for dual-role mode. Keithley2281S.output_is_enabled now accepts (and ignores) a channel=None kwarg, and six new public OCP/OVP wrapper methods (set_overcurrent_protection_value, enable_overcurrent_protection, set_overvoltage_protection_value, enable_overvoltage_protection, clear_overcurrent_protection_trip, clear_overvoltage_protection_trip) delegate to the existing private _set_ocp / _set_ovp and public clear_ocp / clear_ovp methods so the supply handler can call them without AttributeError. No new SCPI logic — the wrappers exist purely so a single-channel driver can satisfy the multi-channel calling convention used elsewhere.
  • lager battery <net> state no longer collides with the shared pyvisa session. The battery CLI sends action='print_state' (matching the dispatcher function name), but /battery/command previously only recognized action='state' (matching the supply handler). The mismatched action returned HTTP 400, the CLI’s _run_backend fell through to the python:5000 dispatcher path, and that subprocess opened a second pyvisa session against the same Keithley — colliding with the shared session that hardware_service had just opened in the previous lager supply command and surfacing as Could not open instrument at USB0::...: failed to set configuration [Errno 16] Resource busy. /battery/command now accepts both 'state' and 'print_state', keeping the CLI on the WebSocket → hardware_service path so the shared pyvisa session this release introduces is actually reused for sequential supply→battery CLI workflows.
  • lager python no longer wipes hardware_service’s cache on every script exit. cli/commands/development/python.py was POSTing /cache/clear on script normal exit, Ctrl+C, and BrokenPipeError — a v0.16.5 band-aid that pre-dates Phase 2’s per-address shared session. With v0.16.9, hardware_service is the single owner of the pyvisa session for each USB device and that session is meant to persist for the container’s lifetime. Clearing it on every script exit defeated the design and re-introduced the very [Errno 16] Resource busy race that Phase 2 set out to eliminate. The clears are removed; if you really need to force a reload (e.g., a script that opens its own pyvisa session out-of-band), you can still curl -X POST http://<box>:8080/cache/clear manually.
  • Resilient first open against libusb’s release-interface timing race. hardware_service._get_or_open_visa_resource now retries open_resource() on [Errno 16] Resource busy with an exponential backoff (0.2, 0.5, 1.0, 2.0 s) before giving up. pyvisa-py + libusb on Linux releases the USB interface asynchronously, so opening the same device too quickly after a close (e.g. after a manual /cache/clear or a TUI exit) could fail the first time and succeed the second. The retries hide the kernel’s catch-up window without masking genuine “device unplugged” failures.
  • POST /cache/clear preserves shared pyvisa sessions; new POST /cache/clear_all for the old behavior. The endpoint still drops cached driver wrappers from device_cache (so a wedged driver gets a fresh load on the next /invoke), but the per-VISA-address shared session that this release relies on is no longer torn down. This was the missing piece that caused V.5/V.6 hardware verification to fail even after the script-exit clear was removed from lager python — older clients (lager-cli ≤ 0.16.7) still POST /cache/clear on every script exit, and that was nuking the shared session out from under hardware_service. With the endpoint now safe under Phase 2, those older clients no longer break dual-role workflows. If you actually need to force-close a shared session (e.g. you unplugged the instrument), POST /cache/clear_all does what /cache/clear used to do.
  • Cross-role concurrent use on a single Keithley 2281S now fails fast with a clear error. Running lager supply <net> tui and a concurrent lager battery <net> command (or vice-versa) against the same physical Keithley used to surface as cryptic SCPI timeouts or [Errno 16] Resource busy errors, because the 2281S’s Power Supply (:ENTR:FUNC POW) and Battery Simulator (:ENTR:FUNC BATT) entry functions are mutually exclusive in firmware and the two clients were fighting over the entry function on every poll. The box now tracks the active monitoring sessions per role (box/lager/http_handlers/state.py:conflicting_other_role_session), records the resolved VISA address when a TUI starts, and refuses an opposite-role command at /supply/command, /battery/command, start_supply_monitor, and start_battery_monitor with a message that names the conflicting net and explains the hardware limitation. Sequential CLI cross-role workflows are unaffected and continue to work cleanly via Phase 2’s shared pyvisa session.

Known Limitations

  • Concurrent supply and battery operation on the same Keithley 2281S is not supported by the instrument itself. The 2281S has two mutually-exclusive entry functions — Power Supply (:ENTR:FUNC POW) and Battery Simulator (:ENTR:FUNC BATT). Each Lager driver flips the entry function to its preferred mode before every SCPI command, so running a supply TUI in one terminal while running a battery CLI command in another terminal causes the two clients to fight over the entry function on every poll, producing intermittent SCPI errors and Resource busy events. Configure either the supply role or the battery role on the Keithley 2281S, not both — or use them strictly sequentially in a single workflow (which v0.16.9’s shared-pyvisa-session work makes fast and clean). This is a property of the instrument, not Lager.

Internal

  • Drivers that share a single pyvisa session per VISA address are listed in box/lager/hardware_service.py:_SHARED_VISA_DEVICE_NAMES (currently keithley, keithley_battery). Adding a future dual-role instrument means adding its supply and battery device_name strings here and giving each create_device factory the raw_resource= kwarg pattern.
  • Keithley2281S.__init__ and KeithleyBattery.__init__ accept a new _owns_resource kwarg (default True for backward compatibility). When False, close() drops the wrapper reference without closing the underlying pyvisa session.
  • Single-role drivers (Rigol DP800/DP821, Keysight E36xxx, EA PSB, etc.) are unchanged — they continue to use the legacy per-driver-opens-its-own-session path.

Installation

To install this version:
pip install lager-cli==0.16.9
To upgrade from a previous version:
pip install --upgrade lager-cli

Resources

View Release on PyPI