Skip to content

Conversation

@X9X0
Copy link
Owner

@X9X0 X9X0 commented Dec 2, 2025

Overview

Fixes multiple device discovery issues and adds automatic hardware identification based on USB VID:PID. Previously, only the first device was detected; now all connected devices (USB, serial, TCPIP) are discovered in parallel.

Key Features

1. Multiple Device Discovery ✅

  • Async parallel scanning with controlled concurrency (max 3 concurrent queries)
  • Detects all connected devices simultaneously (BK 9205B, BK 1685B, RIGOL oscilloscope, etc.)
  • Real-time progress feedback via WebSocket streaming to client UI

2. USB Hardware Identification Database 🆔

  • Automatic device identification from USB VID:PID without querying device
  • Pre-populated database for BK Precision, Rigol, Keysight equipment
  • Works for both direct USB (USB::) and USB-to-serial (ASRL::) devices
  • Confidence scoring system (0.0-1.0) for identification accuracy

3. Serial Device Support (ASRL) 📡

  • BK 1685B and other USB-to-serial devices now detected
  • Extracts USB VID:PID from /sys/class/tty/ via sysfs
  • Handles devices with proprietary protocols (don't respond to *IDN?)
  • Docker container mounts /sys:/sys:ro for USB ID extraction

4. Auto-Selection in Connection Dialog 🎯

  • Automatically selects equipment type and model when device identified
  • Visual confidence indicators (green = high, yellow = medium confidence)
  • Reduces user error when connecting to equipment

5. Performance Optimization ⚡

  • Disabled auto-discovery by default to prevent interference with measurements
  • Increased background scan intervals: VISA 30s→300s (5 min), mDNS 60s→600s (10 min)
  • Discovery still runs once at startup and on manual user request
  • Reduces CPU/USB overhead during active measurements

6. Protocol Fixes 🔧

  • BK 9205B: Rewrote driver to use standard SCPI commands (not proprietary protocol)
  • Proper termination characters and timeout handling for USB/SCPI communication

Technical Changes

New Files

  • server/discovery/usb_hardware_db.py - USB VID:PID to device model mapping

Modified Files

  • server/discovery/visa_scanner.py - Async scanning, parallel processing, serial device support
  • server/discovery/models.py - Updated defaults for serial scanning and intervals
  • server/config/settings.py - Fixed defaults to enable serial device discovery
  • server/equipment/bk_power_supply.py - BK 9205B now uses SCPI instead of proprietary protocol
  • server/main.py - Added progress callbacks for WebSocket streaming
  • client/ui/connect_dialog.py - Auto-selection based on USB hardware ID
  • docker-compose.yml - Mount /sys for serial device USB ID extraction

Bug Fixes

  • Fixed serial devices (BK 1685B) not appearing in discovery
  • Fixed Pydantic serialization errors in API responses
  • Fixed datetime scoping issues
  • Fixed lambda exception variable scoping in client
  • Fixed missing discovery_method field in fallback devices

Testing

Tested with:

  • ✅ BK Precision 9205B (USB0, VID:PID 2ec7:9200)
  • ✅ BK Precision 1685B (ASRL/dev/ttyUSB0, VID:PID 10c4:ea60 via Silicon Labs CP210x)
  • ✅ RIGOL DS1104Z Plus oscilloscope (USB0, VID:PID 6833:1230)

Breaking Changes

None. All changes are backward compatible.

Configuration Changes

Default values changed (can be overridden via environment variables or config file):

  • enable_auto_discovery: TrueFalse
  • mdns_scan_interval_sec: 60600
  • visa_scan_interval_sec: 30300
  • discovery_scan_serial: FalseTrue

Deployment Notes

After merging, rebuild Docker container to apply changes:

docker compose down
docker compose build --no-cache
docker compose up -d

claude added 19 commits December 1, 2025 16:49
…fication

This commit addresses several critical issues with device discovery and
adds comprehensive improvements to make the process more robust and user-friendly.

## Key Features

### 1. USB Hardware ID Database
- Created comprehensive USB VID:PID to device model mapping database
- Supports automatic device identification for:
  - B&K Precision power supplies (1685B, 9205B, 9206B, 9130B, 1902B)
  - Rigol oscilloscopes (DS1000Z, MSO2072A, DS1102D)
  - Rigol electronic loads (DL3021A)
  - Keysight/Agilent equipment
  - Common USB-to-Serial bridges (CP210x, FTDI)
- Provides fallback identification when *IDN? queries fail

### 2. Async Parallel Device Discovery
- Converted VISA scanner to fully async implementation
- Parallel device queries with semaphore-controlled concurrency (max 3 concurrent)
- Non-blocking *IDN? queries using asyncio executors
- Eliminates sequential bottlenecks that caused only first device to be detected

### 3. Real-time Progress Streaming
- WebSocket-based progress updates during discovery
- Detailed feedback at each stage:
  - Resource enumeration
  - Device querying (with current/total count)
  - Device identification (manufacturer/model)
  - Error reporting
- Prevents "python3: is not responding" errors in PyQt6 GUI

### 4. Enhanced User Experience
- Progress dialog in PyQt6 client shows live discovery status
- Percentage-based progress bar (0-100%)
- Clear status messages for each discovery phase
- Graceful error handling with user-friendly messages

## Technical Implementation

### USB Hardware Database
- New file: `server/discovery/usb_hardware_db.py`
- Parses USB VID:PID from VISA resource strings
- Returns device metadata (manufacturer, model, type, specs)
- Used as fallback when SCPI communication fails

### Async VISA Scanner
- `scan()` method is now fully async
- Uses `asyncio.gather()` for parallel processing
- Runs blocking PyVISA calls in thread executor
- Progress callbacks at each stage
- Controlled concurrency to avoid overwhelming devices

### Discovery Manager Integration
- Progress callback mechanism for real-time updates
- Forwards VISA scanner progress to WebSocket clients
- Maintains backward compatibility with existing API

### WebSocket Progress Streaming
- Discovery progress messages broadcast to all connected clients
- Message format: `{type: "discovery_progress", event_type: str, data: dict}`
- Client-side signal/slot architecture for thread-safe GUI updates

## Bug Fixes

- Fixed issue where only first device was detected due to sequential blocking
- Fixed "not responding" errors caused by synchronous VISA operations
- Fixed missing identification for USB devices when *IDN? times out
- Fixed lack of user feedback during long-running discovery operations

## Testing Notes

This fix specifically addresses the issue where:
- BK 1685 Power Supply (first device) was detected
- BK 9205B Power Supply (second device) was not detected

With these changes:
- Both devices should be discovered in parallel
- Even if *IDN? fails, USB VID:PID provides identification
- User sees real-time progress: "Querying device 1/2", "Querying device 2/2", etc.
- No GUI freezing or "not responding" errors

## Files Changed

- `server/discovery/usb_hardware_db.py`: New USB hardware database
- `server/discovery/visa_scanner.py`: Async parallel scanning
- `server/discovery/manager.py`: Progress callback integration
- `server/main.py`: WebSocket progress streaming setup
- `client/ui/equipment_panel.py`: Progress dialog and WebSocket handlers

Resolves multiple device discovery issues.
Fixed scoping error where datetime was being used in discovery_progress_callback
but was only imported later in a conditional block. This was causing the error:
"cannot access free variable 'datetime' where it is not associated with a value
in enclosing scope" and preventing any device discovery from working.

The datetime import is now at the top of the file with other standard imports.
Created comprehensive test script to verify basic VISA/SCPI communication
with the BK Precision 9205B power supply. This helps isolate whether issues
are with device communication, SCPI command compatibility, or driver implementation.

Features:
- Auto-discovery of VISA resources
- USB VID:PID detection (2ec7:9200)
- Tests standard SCPI commands (*IDN?, MEAS:VOLT?, MEAS:CURR?, etc.)
- Safe write command testing (only when output is OFF)
- Detailed error reporting and summary

Usage: python3 test_bk9205b.py
Fixed device selection to properly parse USB resource strings and convert
VID:PID from decimal to hex format. The script now:
- Correctly identifies BK 9205B by matching decimal IDs (11975:37376)
- Shows hex and decimal VID:PID for all USB devices
- Skips serial devices (ASRL) to avoid connecting to wrong device
- Provides better diagnostic output

This fixes the issue where the script was connecting to ASRL/dev/ttyUSB0
instead of the actual USB device.
The BK 9205B doesn't support the device.clear() operation (causes
VI_ERROR_NSUP_OPER). Removed clear() calls before queries.

Also added BK-specific commands to test:
- GETD (Get device data - used in original LabLink logs)
- GETS (Get status)
- GPAL (Get all parameters)
- ISET?, VSET? (alternate query commands)

This will help identify which command set the BK 9205B actually uses
instead of assuming it implements full SCPI.
The BK 9205B is a newer model that uses standard SCPI commands via USB,
unlike older BK models (1685B, 1902B) which use proprietary serial protocol.

Changes:
- BK9205B now inherits from BaseEquipment instead of BKPowerSupplyBase
- Removed proprietary commands (GETD, GETS, GMAX, GOUT)
- Implemented standard SCPI commands:
  * *IDN? for identification
  * VOLT/CURR for setting values
  * VOLT?/CURR? for querying setpoints
  * MEAS:VOLT?/MEAS:CURR? for actual measurements
  * OUTP ON/OFF for output control
  * SYST:ERR? for error checking
- Configure USB connection with \n terminators (not \r for serial)
- Remove device.clear() calls (not supported by BK 9205B)
- 5 second timeout for USB communication

This fixes the timeout errors where the driver was sending GETD commands
that the BK 9205B doesn't understand.
Implemented automatic selection of equipment type and model when devices
are clicked in the discovery dialog, based on USB VID:PID identification.

Changes:
- equipment_manager.discover_devices() now returns full DiscoveredDevice
  objects with manufacturer, model, device_type, and confidence score
- API /discover endpoint now returns devices array with full info
- ConnectDeviceDialog enhancements:
  * Display manufacturer, model, and device type for each resource
  * Highlight devices with high confidence (green=*IDN?, yellow=USB DB)
  * Auto-populate equipment type dropdown when device is selected
  * Auto-populate model dropdown when device is selected
  * Add tooltips showing identification method
  * Larger dialog size (700x450) to accommodate richer information
- equipment_panel updated to use new devices format

Benefits:
- Prevents wrong model selection (e.g., 9206B vs 9205B confusion)
- Improves user experience with visual confidence indicators
- Reduces manual input by auto-filling known device information
- Makes device identification status clear to users

Fixes the issue where BK 9205B was being mis-identified as 9206B due
to hardcoded default values in the dialog.
Fixed two issues preventing discovery from working:

Server-side:
- Added missing discovery_method field to fallback DiscoveredDevice
  objects in equipment_manager.py
- This was causing 500 Internal Server Error when discovery_manager
  wasn't available and fallback VISA scan was used

Client-side:
- Fixed scoping error in equipment_panel.py exception handler
- Lambda was trying to access 'e' from outer scope after it expired
- Now captures error message in closure variable before lambda

The server was returning 500 because DiscoveredDevice requires
discovery_method as a non-optional field, but the fallback code
wasn't providing it.
Fixed Pydantic validation error in /api/equipment/discover endpoint.
FastAPI was unable to serialize DiscoveredDevice objects directly in
the response model.

Changes:
- Removed response_model=DiscoverDevicesResponse from /discover endpoint
- Convert DiscoveredDevice objects to dicts using model_dump()
- Return plain dict response: {"devices": [dict, dict, ...]}

This fixes the 500 Internal Server Error with validation errors:
"Input should be a valid dictionary or instance of DiscoveredDevice"

The client already expects dict objects, so this change is compatible.
- Extract USB VID:PID from serial port devices via sysfs
- Check USB hardware database for both USB and ASRL (serial) interfaces
- Return serial devices even if they don't respond to *IDN? (proprietary protocols)
- Added BK 1685B serial variant (VID:PID 2ec7:168b)
- Improved Silicon Labs CP210x detection with B&K device hints
- Mark unidentified serial devices as AVAILABLE with lower confidence

This fixes discovery of BK 1685B power supply which uses Silicon Labs
USB-to-UART bridge and doesn't respond to standard SCPI *IDN? queries.
- Log all VISA resources found with their full resource strings
- Log which interface type each resource is being processed as
- Log sysfs path attempts when looking up USB IDs for serial ports
- Warn when resources are filtered out or serial port USB IDs can't be found
- Log when resources are skipped due to disabled interface types

This will help diagnose why BK 1685B is not being discovered.
The DiscoveredDevice model already has default_factory=datetime.now for
discovered_at and last_seen fields. Explicitly passing datetime.now() in
the constructor was causing scoping issues in async context.

Let Pydantic handle timestamp generation via default_factory instead.
- Log when serial (ASRL) devices start processing
- Log USB hardware DB lookup results for serial devices
- Log when generic USB-to-Serial chip is found
- Log final device status before returning (manufacturer, model, status, confidence)

This will help diagnose why ASRL/dev/ttyUSB0 (BK 1685B) is being filtered out.
Show whether serial devices are being skipped due to config or actually being
processed. Logs will show either:
- ⚠️ Skipping ASRL resource (DISABLED in config)
- ✓ Processing ASRL (serial) device

This will tell us definitively why the BK 1685B at ASRL/dev/ttyUSB0 is being
filtered out of discovery results.
Serial scanning was disabled by default (scan_serial=False), causing devices
like the BK 1685B that use USB-to-serial bridges to be skipped during auto-
discovery at startup.

Changed default to scan_serial=True so serial/ASRL devices are discovered
automatically along with USB and TCP/IP devices.

This fixes the issue where BK 1685B was showing in lsusb but not in LabLink
device discovery.
- Disable background auto-discovery (enable_auto_discovery=False) to avoid
  interference with active measurements
- Increase VISA scan interval from 30s to 300s (5 min)
- Increase mDNS scan interval from 60s to 600s (10 min)
- Discovery still runs once at startup and on manual user request
- Reduces CPU/USB overhead and prevents device query conflicts
The discovery system needs access to sysfs to extract USB VID:PID from
serial devices (ASRL resources like /dev/ttyUSB0). The code in
usb_hardware_db.py reads from paths like:
  /sys/class/tty/ttyUSB0/device/../../idVendor
  /sys/class/tty/ttyUSB0/device/../../idProduct

Without this mount, serial devices like the BK 1685B (which uses a
Silicon Labs CP210x USB-to-serial bridge) cannot be automatically
identified from their USB hardware IDs.

Changes:
- Add /sys:/sys:ro volume mount to lablink-server in docker-compose.yml
- Mount as read-only for security

This fix enables automatic identification of serial-based devices
like the BK 1685B power supply.
CRITICAL BUG FIX: The discovery_scan_serial default was set to False in
server/config/settings.py, overriding the True default we set in
server/discovery/models.py.

The Settings class values take precedence because main.py explicitly
passes settings.discovery_scan_serial to DiscoveryConfig(), bypassing
the model's default values.

This was causing serial devices like BK 1685B to be filtered out even
after we "fixed" the issue in models.py.

Changes:
- discovery_scan_serial: False → True
- enable_auto_discovery: True → False (performance optimization)
- mdns_scan_interval_sec: 60 → 600 (10 minutes)
- visa_scan_interval_sec: 30 → 300 (5 minutes)

All settings now match the values in discovery/models.py.

This fix is required for BK 1685B and other USB-to-serial devices to
appear in discovery results.
@X9X0 X9X0 merged commit cbc42cb into main Dec 2, 2025
25 checks passed
@X9X0 X9X0 deleted the claude/fix-multiple-device-discovery-01HqshYU6Sr9M8ce7nWv3Yvd branch December 2, 2025 16:12
X9X0 added a commit that referenced this pull request Dec 7, 2025
…ecture

## Summary
Unified version management across all components (server, client, launcher, Docker)
using a single VERSION file as the source of truth. Updated to v1.2.0 to accurately
reflect work completed since v1.0.0 (30+ PRs merged).

## Version System Changes

### Unified to v1.2.0
- Analyzed git history from v1.0.0 through current main
- Determined v1.2.0 accurately represents features and fixes delivered
- All components now read from single VERSION file

### Components Updated
- VERSION file: 0.28.0 → 1.2.0
- README badge: 1.0.1 → 1.2.0
- Server: Now reads VERSION (already implemented)
- Client: Added dynamic VERSION reading at startup
- Launcher: Added __version__ variable from VERSION
- Docker images: Labels updated to 1.2.0
- Copyright: Updated to © 2025 (project start year)

## CHANGELOG Updates

Added comprehensive release notes for missing versions:

### v1.2.0 (2025-12-06)
- Server update system with stable/development modes (#114, #118, #119)
- Smart branch filtering and UI consolidation (#120)
- Enhanced dropdown visibility
- Multiple bug fixes (#105, #106, #108, #121)

### v1.0.1 (2025-11-28)
- Equipment control panel (#104)
- GUI system launcher with diagnostics (#70-74)
- Raspberry Pi image builder (#75-76)
- Waveform analysis tools (#79)
- Automated test sequence builder (#80)
- Remote firmware update (#81)
- Equipment diagnostics system (#84-87)
- WebSocket integration completion (#77)

## Automation & Documentation

### Created scripts/bump_version.py
Automated version bumping tool with:
- Support for major/minor/patch increments
- Automatic VERSION file updates
- CHANGELOG.md section generation
- Git commit and tag creation
- Dry-run mode for testing

Usage:
  python scripts/bump_version.py patch   # 1.2.0 → 1.2.1
  python scripts/bump_version.py minor   # 1.2.0 → 1.3.0
  python scripts/bump_version.py major   # 1.2.0 → 2.0.0

### Created docs/VERSIONING.md
Complete versioning system documentation covering:
- Single-source architecture
- Semantic versioning guidelines
- Automated bumping procedures
- Manual update processes
- Troubleshooting guide
- Best practices

## Files Modified

Core Version Files:
  - VERSION (0.28.0 → 1.2.0)
  - CHANGELOG.md (added v1.0.1 and v1.2.0 entries)
  - README.md (badge updated to 1.2.0)

Component Updates:
  - client/main.py (dynamic VERSION reading)
  - lablink.py (added __version__ from VERSION)
  - docker/Dockerfile.server (label 1.2.0)
  - docker/Dockerfile.web (label 1.2.0)

Copyright Updates (© 2025):
  - docs/USER_GUIDE.md
  - client/ui/main_window.py

New Files:
  + scripts/bump_version.py (version management tool)
  + docs/VERSIONING.md (complete documentation)

## Benefits

✅ Single source of truth for version (VERSION file)
✅ Consistent versioning across all components
✅ Accurate version history in CHANGELOG
✅ Automated version bumping workflow
✅ Complete documentation for future updates
✅ Git-tagged releases (v1.2.0)

## Testing

Verified:
- VERSION file: 1.2.0 ✓
- Server reads VERSION correctly ✓
- Client would read VERSION at startup ✓
- Launcher reads VERSION correctly ✓
- bump_version.py dry-run works ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants