Repository navigation
Add lablink scan to discover and identify attached devices - #5
Merged
Merged
Conversation
After `pip install`, the first thing a new user had to do was hand-author a TOML file with a VISA resource string found via a `python -c "import pyvisa; ..."` one-liner copied out of the README. That is the narrowest point in the funnel, and it is work LabLink can do itself. `lablink scan` enumerates VISA resources and serial ports, asks each candidate `*IDN?` with a short per-probe timeout, and prints resource string, interface type, the parsed identity fields, and a suggested `<vendor>_<model>` alias. Design notes: - Discovery crosses the visa and serial drivers, so it is a shared subsystem (`lablink/discovery.py`, models in `base.py`) and a shared CLI command alongside `diagnose`/`list` — not a driver method. The CLI command only renders; the sweep is reachable from tests without click. - A candidate that cannot be opened, or never answers, is still reported as found-but-unidentified. Not everything on a serial bus speaks SCPI, and a hidden device is worse than an honest "did not identify". - Each sweep lazy-imports its library and, on ImportError, contributes an install action item instead of failing — the same degradation `diagnose()` already provides. `scan` with no extras installed prints two install steps and an explanation of the empty result, not a traceback. Discovery and reporting only; writing configs is a separate change. 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.
Committed on
feat/scan-discovery-20260918as5fb2d98. Not pushed.What changed
lablink scanis a new shared CLI command that enumerates what is physically attached, asks each candidate who it is, and prints a table.lablink/discovery.py(new) — the sweep.scan()enumerates VISA resources viapyvisa.ResourceManager(...).list_resources()and serial ports viaserial.tools.list_ports.comports(), probes each candidate (open →*IDN?→ read → close) with a 2 s per-probe timeout, and returns aScanResult. Pure helpersparse_idn()andsuggest_alias()are unit-testable on their own.lablink/base.py—DiscoveredDeviceandScanResultdataclasses, following the topology subsystem's split (§15.1): models inbase.py, logic in its own module.lablink/cli.py— thescancommand, next todiagnoseandlist. It only formats: table to stdout, counts/install steps/per-device detail to stderr, per the repo's CLI output rule.tests/test_discovery.py(new, 21 tests) — resource found and identified; opens but never answers*IDN?; raises on open;list_resources()itself failing;pyvisamissing;pyserialmissing; both missing; nothing found at all; serial port identified with VID/PID metadata; silent serial port;*IDN?parsing and alias slugging; plus CLI rendering of the table, the empty-scan guidance, and action items.lablink scan(with sample output) and thepython -c "import pyvisa; ..."one-liner is gone;scanadded to the CLI reference; the VISA troubleshooting entry reworded in terms ofscan;examples/configs/visa_scope.tomlcomment updated. CHANGELOG entry under[Unreleased].docs/ARCHITECTURE.mdgains §17 (module placement, probe contract, degradation) plus directory-structure and core-component entries.docs/agent_docs/agent_development.mdrecords two generalizable rules: CLI-only commands still keep logic in a module, andmonkeypatch.setitem(sys.modules, "pyvisa", None)is how to test a lazy-import fallback.Behavior on missing drivers follows
diagnose()rather than inventing a second pattern: each sweep lazy-imports its library and, onImportError, contributes oneaction_itemsentry naming the extra (pip install lablink-mcp[visa]). The other sweeps still run.Verification
.venv/bin/python -m pytest -q→ 371 passed. The suite was green before this change; no pre-existing failures.lablink scanfor real on the dev machine: found 8 candidates (4 serial ports, each also visible as anASRLVISA resource), 0 identified, each with its "no*IDN?reply" reason — no traceback, no stall.lablink scanwith bothpyvisaandserialimports blocked: exit 0, two install action items, and the empty-scan explanation.lablink-sim: the VISA probe identifiedLabLink,FG-100,SIMFG001,0.2.0over the real TCP socket (suggested aliaslablink_fg_100) andDAQ-8on 5026; the serial probe identifiedDAQ-8over the simulator's pty.Assumptions and notes
SOCKETresource and a pty are not returned bylist_resources()orcomports(), solablink scancannot find the sim on its own — I verified the identify path against it by calling the probe with the sim's resource string directly. That is a property of those buses, not ofscan; it's stated in §17.3. Real USB/GPIB/ASRL instruments (and LAN instruments via pyvisa-py's VXI-11 discovery) do enumerate.--write-configsdeliberately absent, per the task scope. The footer line tells the user where to write the config; wiring that up is the follow-up.--timeoutflag. The task specified "about 2 seconds";scan(timeout_s=...)takes the parameter for tests and future use, but exposing a flag would be scope beyond the ask. Worth adding if slow GPIB instruments need it.ASRL/dev/...::INSTR(visa driver) and once as/dev/...(serial driver). I left both rows: they are different resource strings served by different drivers, so each is separately actionable. Deduping would mean guessing which driver the user wants.*IDN?-discovery paragraph in the MCP server instructions (lablink/mcp_server.py) to namelablink scanfirst, keeping thepython -cone-liner as the fallback. The instructions already make the agent responsible for writing device configs, so leaving it pointing only at the one-liner would have been stale the moment this merged.scanitself is not an MCP tool — every MCP tool is alias-addressed, and scanning happens before any alias exists.🤖 Generated with Claude Code