Discover generic USB/RS-232 adaptors and stop auto-probing devices behind them#102
Merged
Conversation
Problem: Instruments behind a generic RS-232/USB converter (FTDI, Prolific, Silicon Labs CP210x, WCH CH34x) inherit the bridge's anonymous VID/PID and have no identity of their own. SerialPort could match a known VID/PID/serial (matchPorts) but had no way to list the generic-converter ports so a caller could discover the candidate adaptors. Solution: add genericSerialConverterVendors (VID -> chip name) plus two classmethods: isGenericSerialConverter(idVendor) and genericSerialConverterPorts(), which returns the connected ports whose vendor is a known converter chip as pyserial ListPortInfo objects (open via .device, disambiguate via .serial_number). Adds testSerialPortConverters.py, a hardware-free test that mocks comports(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem: Many instruments sit behind a stock FTDI 0x0403:0x6001 cable (FieldMaster, oscilloscope, Echo, IntelliDrive), so they all share the same VID/PID. DeviceManager could not tell them apart and, on every FTDI cable plugged in, tried initializeDevice() on each candidate class -- sending arbitrary protocol bytes to an unknown instrument (the same kind of blind probing that can wedge a device). Solution: add PhysicalDevice.usesGenericSerialConverter (default False). When set, vidpids() expands to every generic converter vendor (FTDI/Prolific/ CP210x/CH34x) with the product id wildcarded, sourced from SerialPort.genericSerialConverterVendors (single source of truth), and isCompatibleWith treats a None product id in a pair as a wildcard. So such a device is discoverable behind any generic cable, not just FTDI, and is identified by serial number rather than VID/PID. DeviceManager gains candidateClassesForAutoDiscovery(), which drops generic-converter classes so they are never auto-probed; they are constructed explicitly instead. FieldMaster, oscilloscope, Echo and IntelliDrive are flagged generic; DebugFieldMasterDevice keeps its own fake identity (flag False), and Thorlabs (custom-EEPROM FTDI PID 0xfaf0) stays a specific, unique identity. connectedUSBDevices omits the product id from usb.core.find when it is None so VID-only matching works end to end. Adds testGenericSerialConverter.py (hardware-free). Co-Authored-By: Claude Opus 4.8 (1M context) <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
Instruments behind a generic RS-232/USB converter (FTDI, Prolific, Silicon Labs CP210x, WCH CH34x) inherit the bridge's anonymous VID/PID and have no identity of their own — the Coherent FieldMaster is one. This branch adds (1) a way to discover those adaptors and (2) a mechanism so the framework treats such devices correctly instead of blindly probing them.
1. Discovery —
SerialPortgenericSerialConverterVendors— class attribute mapping USBidVendor→ chip name (extensible):{0x0403: "FTDI", 0x067b: "Prolific", 0x10c4: "Silicon Labs (CP210x)", 0x1a86: "WCH (CH340/CH341)"}.isGenericSerialConverter(idVendor) -> bool— pure predicate (handlesNone).genericSerialConverterPorts()— connected ports from a known converter chip, as pyserialListPortInfoobjects (open via.device, disambiguate via.serial_number).2. Mechanism —
PhysicalDevice.usesGenericSerialConverterThe real problem:
FieldMasterDevice,OscilloscopeDevice,EchoDeviceandIntelliDriveDeviceall declare0x0403:0x6001. On every FTDI cable,DeviceManagertriedinitializeDevice()on each candidate — sending arbitrary protocol bytes to an unknown instrument (the same blind probing that can wedge a device).usesGenericSerialConverter(defaultFalse).vidpids()expands to every generic-converter vendor with the product id wildcarded (sourced fromSerialPort.genericSerialConverterVendors— one source of truth). So the device is discoverable behind any generic cable, not just FTDI, and is identified by serial number.isCompatibleWithtreats aNoneproduct id in a pair as a wildcard (no change for concrete pairs).DeviceManager.candidateClassesForAutoDiscovery()drops generic-converter classes, so they are never auto-probed — they are constructed explicitly (FieldMasterDevice(serialNumber="FTFDLOTS")).DebugFieldMasterDevice(own fake identity) and Thorlabs (custom-EEPROM FTDI PID0xfaf0= a unique identity).connectedUSBDevicesomits the product id fromusb.core.findwhen it isNone(VID-only matching end to end).Design note
Matching is by VID only, restricted to four vendors that make essentially nothing but serial bridges. Microchip (0x04D8) / Cypress (0x04B4) are intentionally excluded to avoid false positives; add them as specific
(VID, PID)pairs if needed.Test plan (all hardware-free)
testSerialPortConverters.py— 5 passed (mockscomports())testGenericSerialConverter.py— flag,vidpids()expansion, wildcard compatibility, non-generic unaffected, and the auto-discovery guard (generic classes match but are excluded)testFieldMasterDevice.py— debug tests pass; hardware tests also passed live (FieldMaster still reads through the flagged path)genericSerialConverterPorts()detects the FTDI adaptor and excludes an STM32 VCP and BluetoothtestPhysicalDevice.pymonitoring tests open connected USB devices; the DeviceManager change is unit-tested in isolation instead🤖 Generated with Claude Code