diff options
| author | Christian Kolset <christian.kolset@gmail.com> | 2026-04-21 22:58:30 -0600 |
|---|---|---|
| committer | Christian Kolset <christian.kolset@gmail.com> | 2026-04-21 22:58:30 -0600 |
| commit | 4db119fb9c04428e7aef6f1a278c1639aa867baf (patch) | |
| tree | e0e0645559637e593f1bbfb9d713256028668527 /CLAUDE.md | |
| parent | 97e15f69b835af03b071c4eeb4547c65a02e6f80 (diff) | |
Major update to Output control
Including:
- Control panel
- Change status of simulation after devices creation.
- Arduino serial conflict with multiple channels.
- Claude instructions.
- Update to arduino on board code.
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5347d9d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,81 @@ +# CLAUDE.md + +Guidance for Claude Code (claude.ai/code) working in this repo. + +## Running the app + +```bash +python main.py +``` + +No build step. Simulation mode default (no hardware needed). + +## Dependencies + +```bash +pip install PyQt6 pyqtgraph numpy pyserial +pip install nidaqmx # optional — only for real NI hardware +``` + +## Architecture + +### Data flow + +``` +Hardware / Simulation + ↓ + api_layers/ ArduinoLayer, NidaqmxLayer — low-level I/O, background threads + ↓ + devices/ BaseDevice subclasses — wrap api_layers, expose read_channels() / write_channel() + ↓ + core/acquisition.py AcquisitionEngine — polls devices at 100 ms, fills ChannelBuffers, emits new_data signal + ↓ + core/signal_processor.py SignalProcessor — filters + derived channels, re-emits processed_data + ↓ + ui/strip_chart.py StripChartWidget — consumes processed_data, renders via pyqtgraph +``` + +Control widgets (left panel) go other direction: UI → `ControlWidget._write()` → `DeviceRegistry.get_instance()` → `device.write_channel()` → api_layer. + +### Key design patterns + +**Shared serial port** — `api_layers/port_registry.py` holds module-level `port_registry` singleton. When multiple devices share one Arduino (e.g. `AnalogInputDevice` + `DigitalIODevice` on same port), both call `port_registry.get_layer(port, baud)`, get same `ArduinoLayer` instance. Never construct `ArduinoLayer` directly in device code. + +**Device auto-discovery** — `DeviceRegistry._discover()` scans `devices/` with `pkgutil`, imports every module, registers any class subclassing `BaseDevice`. New device type = drop file in `devices/`, no registration needed. + +**Backend switching at runtime** — `AnalogInputDevice` and `DigitalIODevice` both have `switch_backend(backend, simulate, ...)` — disconnects, reconfigures, reconnects without restart. Called from "Apply & Reconnect" in config dialog. + +**Profile persistence** — `core/profile.py` serialises full operator state (controls, plot layout, signal pipelines, derived channels) to `.labdaq` JSON files. `ProfileManager` handles save/load. + +### Directory map + +| Path | Purpose | +|------|---------| +| `api_layers/arduino_layer.py` | Serial protocol + simulation; `ARDUINO_FIRMWARE` string is the uploadable sketch | +| `api_layers/nidaqmx_layer.py` | NI-DAQmx wrapper with simulation fallback | +| `api_layers/port_registry.py` | Shared `ArduinoLayer` singleton per (port, baud) | +| `devices/base_device.py` | `BaseDevice`, `ChannelConfig`, `DeviceInfo`, `DeviceStatus` | +| `devices/analog_input.py` | Analog input — NI or Arduino backend | +| `devices/digital_io.py` | Digital I/O — NI or Arduino backend | +| `devices/serial_device.py` | Generic UART device | +| `core/acquisition.py` | `AcquisitionEngine` + `ChannelBuffer` | +| `core/signal_processor.py` | Filter chain + derived/virtual channels | +| `core/profile.py` | `.labdaq` profile save/load | +| `ui/main_window.py` | Top-level window, toolbar, demo device init | +| `ui/control_panel.py` | Left panel output widgets (`OnOffSwitch`, `MotorControl`, etc.) | +| `ui/strip_chart.py` | Live pyqtgraph chart, config-driven by `LayoutConfig` | +| `ui/windows/` | Floating tool windows (Devices, Signals, Plot, Settings) | +| `ui/style_dark.qss` / `style_light.qss` | Full app theme | + +### Arduino firmware + +Firmware embedded as `ARDUINO_FIRMWARE` in `api_layers/arduino_layer.py`. When editing: set `N_DIG_OUT` to match digital output pins in use (default 0 — leaves pins in INPUT mode, causes inverted write behaviour). Upload via Arduino IDE. + +Serial protocol: `A0:3.14,D2:1\n` stream from Arduino; `W:D7:1\n` / `P:D9:128\n` commands from PC. + +### Adding a new device type + +1. Subclass `BaseDevice` in new file under `devices/` +2. Implement: `connect()`, `disconnect()`, `read_channels() → Dict[str, float]`, `write_channel(channel_id, value) → bool`, `get_config_widget() → QWidget` +3. Include `switch_backend()` if device supports runtime reconfiguration +4. `DeviceRegistry` discovers it automatically on next run
\ No newline at end of file |
