Repository navigation
Add simulated bench and release v0.2.0 - #1
Merged
Merged
Conversation
Ship a two-instrument simulated bench behind four real protocol front-ends so LabLink can be evaluated without a lab. An FG-100 generator and a DAQ-8 acquisition module share one Bench, served over SCPI/TCP, a JSON API, a pty, and direct import. The simulator is not a mock: it speaks real protocols on real sockets, so the visa, rest and serial drivers reach it with ordinary configs and no special-case code path. The generator's CH1 is patched into the DAQ's CH0, so setting a waveform changes what the DAQ measures — which gives system_topology something to describe on a bench with no cables in it. Installed via the new `demo` extra, which has no dependencies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cut the first release since 0.1.0, covering system_topology, Home Assistant support via the rest driver, event-log credential redaction, and the new simulated bench. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The simulated instruments are served on separate connections, each handled on its own thread. A write-only call returned as soon as the bytes were sent, so a read issued against the other instrument could race ahead of the write being applied — the VISA test saw the noise floor instead of the driven waveform on a loaded CI runner. Round-trip a query before crossing to the other connection, which is the SCPI idiom for this: _scpi() now appends *OPC? when a call contains no query, and the VISA test asserts OUTP1? before polling the DAQ. Both turn an ordering assumption into an ordering guarantee. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SerialServer.stop() closed the pty master while the reader thread was blocked in os.read() on that same fd. On macOS this leaves the process in an uninterruptible wait that neither SIGTERM nor SIGKILL reaps, so `lablink-sim` could not be stopped with Ctrl-C — on the onboarding path, of all places. Poll with select() so the reader observes the stop event and returns on its own, and join it in stop() before closing either fd. Verified that SIGTERM and SIGINT both now exit cleanly with no leftover processes, and added a regression test asserting the reader is dead before the fds close. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ships a two-instrument simulated bench so LabLink can be evaluated with no hardware, and cuts v0.2.0.
lablink-simstarts one sharedBenchbehind four real protocol front-ends at once:Nothing is mocked. The simulator speaks real protocols on real sockets, so the
visa,restandserialdrivers reach it with ordinary configs and no special-case code path — what an evaluator runs is the shipped code. Nothing inlablink/outsidelablink/demo/is aware the simulator exists.The patch cable is what makes
system_topologydemonstrable without hardware. Driven end to end through the MCP tools,connect()reports the wiring and its safety constraint before a single command is sent:State is shared across protocols: a
POSTto the REST generator endpoint is immediately visible toSOUR1:FUNC?over SCPI and toMEAS:VOLT?over the serial pty.Changes
lablink/demo/—instruments.py(Bench, DAQ, WaveformGen, Patch),scpi.py,rest.py,serial_port.py,cli.pydemoextra — no dependencies, stdlib onlylablink-simconsole scriptexamples/topology_sim.toml— mirrors the default patchtests/test_demo_bench.py— 27 integration tests over real socketspyproject.toml/server.json; CHANGELOG cutTwo bugs found and fixed after the first push
77938a7— race between generator writes and DAQ reads (caught by CI). The instruments are served on separate connections, each on its own handler thread, so a write-only call returned before the write was necessarily applied and a read on the other connection could overtake it. CI saw the noise floor instead of the driven waveform. Fixed by round-tripping a query before crossing connections —*OPC?, the SCPI idiom for exactly this — which converts an ordering assumption into an ordering guarantee.9baf2fd— unkillable process on shutdown (caught by running the demo).SerialServer.stop()closed the pty master while the reader thread was blocked inos.read()on that fd, leaving the process in an uninterruptible wait that neither SIGTERM nor SIGKILL reaps — solablink-simcould not be stopped with Ctrl-C. Fixed by polling withselect()so the reader returns on its own, and joining it before closing either fd.Realism
Proper SCPI error queue with standard codes (
-222out of range,-113undefined header,-224illegal parameter, cleared on read),*RST/*CLS/*OPC?, input-range clamping, output clipping, per-channel history, and a Gaussian noise floor.Testing
349 passed(322 existing + 27 new), green on 3.10 / 3.11 / 3.12 / 3.13. Verified end to end through the LabLink MCP tools, the CLI (lablink visa query,lablink rest get,lablink topology show), raw pyvisa, pyserial and curl. Demo-bench tests run 10/10 clean unloaded and 5/5 under 16-core saturation. SIGTERM and SIGINT both confirmed to exit cleanly with no leftover processes. Wheel build confirmed to includelablink/demo/and thelablink-simentry point.Notes
serial_port.pyis POSIX only; it degrades gracefully on Windows but is untested there.examples/topology_sim.tomlmirrorsBench.patches— change one and change the other.🤖 Generated with Claude Code