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-supplynet (e.g.supply1) and abatterynet (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_resourceskeyed by VISA address) inbox/lager/hardware_service.py, plus araw_resource=factory kwarg onbox/lager/power/supply/keithley.py:create_deviceandbox/lager/power/battery/keithley.py:create_device. Both Keithley driver constructors track an_owns_resourceflag soclose()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 alternatelager supply <net>andlager 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 inbox/lager/hardware_service.py:/invokenow calls_close_device(old_device, cache_key)before invokingmodule.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’spyvisa.ResourceManager().open_resource(addr)failed withResource busy— surfaced asCould 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.pyis modeled on multi-channel drivers (Rigol DP800) and calls supply-driver methods with achannel=kwarg or positional channel. The Keithley 2281S supply driver follows theSupplyNetabstract (nochannelparameter — the 2281S is single-channel), so the very first call hitTypeError: 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_enablednow accepts (and ignores) achannel=Nonekwarg, 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_ovpand publicclear_ocp/clear_ovpmethods so the supply handler can call them withoutAttributeError. 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> stateno longer collides with the shared pyvisa session. The battery CLI sendsaction='print_state'(matching the dispatcher function name), but/battery/commandpreviously only recognizedaction='state'(matching the supply handler). The mismatched action returned HTTP 400, the CLI’s_run_backendfell 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 previouslager supplycommand and surfacing asCould not open instrument at USB0::...: failed to set configuration [Errno 16] Resource busy./battery/commandnow 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 pythonno longer wipes hardware_service’s cache on every script exit.cli/commands/development/python.pywas POSTing/cache/clearon 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 busyrace 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 stillcurl -X POST http://<box>:8080/cache/clearmanually.- Resilient first open against libusb’s release-interface timing race.
hardware_service._get_or_open_visa_resourcenow retriesopen_resource()on[Errno 16] Resource busywith an exponential backoff (0.2, 0.5, 1.0, 2.0s) 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/clearor 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/clearpreserves shared pyvisa sessions; newPOST /cache/clear_allfor the old behavior. The endpoint still drops cached driver wrappers fromdevice_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 fromlager python— older clients (lager-cli≤ 0.16.7) still POST/cache/clearon 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_alldoes what/cache/clearused to do.- Cross-role concurrent use on a single Keithley 2281S now fails fast with a clear error. Running
lager supply <net> tuiand a concurrentlager battery <net>command (or vice-versa) against the same physical Keithley used to surface as cryptic SCPI timeouts or[Errno 16] Resource busyerrors, 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, andstart_battery_monitorwith 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(currentlykeithley,keithley_battery). Adding a future dual-role instrument means adding its supply and batterydevice_namestrings here and giving eachcreate_devicefactory theraw_resource=kwarg pattern. Keithley2281S.__init__andKeithleyBattery.__init__accept a new_owns_resourcekwarg (defaultTruefor backward compatibility). WhenFalse,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.

