Skip to content

feat(instrodaq): add support for sw timed background daemon - #325

Open
maxleblang wants to merge 26 commits into
mainfrom
instro-524-sw-timed-background-daemon
Open

feat(instrodaq): add support for sw timed background daemon#325
maxleblang wants to merge 26 commits into
mainfrom
instro-524-sw-timed-background-daemon

Conversation

@maxleblang

@maxleblang maxleblang commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds support for running a background daemon that is running software timed acquisition. The user can now configure a software timing sample rate and have a background daemon acquire samples from the DAQ at the defined rate (the work time of the daq read is the ceiling for sw sample rate).

We also add new examples highlighting this new functionality. Additionally, for DAQs that support multiple InstroDAQ configurations (NIDAQ) we support configuring a hw timed and a sw timed background daemon that can run in parallel.

Here are the 6 scenarios a user can now configure and start an InstroDAQ instance, and what they result in:

  1. User configures hw timing and calls start() -> DAQ hw timed acquisition started and background daemon started
  2. User configures hw timing and calls start(background=False) -> DAQ hw timed acquisition started
  3. User configures sw timing and calls start() -> background daemon started at specified sample rate
  4. User configures sw timing and calls start(background=False) -> ERROR RAISED: sw timed continuous acquisition can't be started with no background daemon
  5. User doesn't configure any timing and calls start() -> background daemon started at default (1 Hz) sample rate
  6. User doesn't configure any timing and calls start(background=False) -> ERROR RAISED: sw timed continuous acquisition can't be started with no background daemon

Closes INSTRO-524

Type of change

  • Bug fix (fix)
  • New feature (feat)
  • Breaking change (feat! / fix!)
  • Refactor (refactor)
  • Documentation (docs)
  • Chore / tooling (chore)

Verification

New examples behaved as expected. Test suite passes and new NIDAQ hardware test passes as well.

ver ver hardware

Tests

  • Unit tests added or updated
  • Existing tests cover this change
  • No tests — explain why:

Checklist

  • PR title follows Conventional Commits (e.g. feat(driver): add support for Keysight E36300)
  • I have read CONTRIBUTING.md
  • Documentation updated if user-facing behavior changed
  • Code follows the style/conventions of the surrounding code

Notes for reviewers

This has only been tested on an NI cDAQ and a LabJack T4. I also need test validation on a MCC and Keysight just to ensure the background software acquisition works as expected. This just entails running the daq_read_analog_sw_timed.py example for both these DAQs and ensuring that data is being read properly via software acquisition in the background.

@maxleblang
maxleblang requested review from hoehner and nhschwab July 29, 2026 15:56
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Claude finished @maxleblang's task in 2m 56s —— View job


Review

Architecture/ergonomics: Solid fit with repo conventions. SW timing is added entirely in InstroDAQ (instro/daq/daq.py) — no DAQDriverBase contract changes, no driver-side reach-back. Parallel HW+SW acquisition on one NI chassis is achieved by composing two InstroDAQ instances over two NIDAQDrivers (examples/daq/daq_hw_and_sw_timed_ni.py) rather than adding any shared-instance multiplexing — consistent with "drivers own their lifecycle" and the existing daq_multi_rate_ni.py pattern. Mutual exclusivity between HW/SW timing is enforced at configure_* time with a clear HWTimingException, and configure_ai_sample_rate is kept as a non-breaking alias for configure_ai_hw_sample_rate. Docs (daq.mdx) and the reference method table are updated in the same PR. Tests cover the new dispatch paths (mutual exclusivity, start/stop gating, warn-on-overrun, HW+SW-in-parallel) with a real _background_daemon run rather than mocked timing — good signal-to-noise.

Minor, left inline:

  • Stray unrelated LabJack serial-number swap in two examples (daq_read_analog_hw_timed.py:24, daq_read_analog_sw_timed.py:22) — likely a personal-hardware leftover, not part of this feature.
  • examples/daq/daq_multi_rate_ni.py picked up a 100644 → 100755 mode change with no shebang added — looks like an accidental chmod +x (couldn't leave this inline since the diff has no changed line to attach to).

Nothing else stood out: _fetch_analog_sw_timed/_fetch_analog_hw_timed split is clean, no new abstractions/mixins introduced, and the new hardware test is properly gated behind @pytest.mark.hardware.

@mintlify

mintlify Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
instro 🟢 Ready View Preview Jul 29, 2026, 3:58 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Comment thread examples/daq/daq_read_analog_hw_timed.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the previous daq_read_analog_sw_timed.py examples

Comment thread instro/daq/daq.py Outdated
Comment thread instro/daq/daq.py Outdated
Comment thread instro/daq/daq.py Outdated
Comment thread instro/daq/daq.py Outdated
Comment thread instro/daq/daq.py
Comment thread Cargo.lock
Comment thread uv.lock
maxleblang and others added 5 commits July 30, 2026 11:08
Merge origin/main into instro-524-sw-timed-background-daemon

Resolved conflicts in instro/daq/daq.py; sw-timed start/stop now sets
the _running guard added on main, and the running-guard tests configure
hw timing before start(background=False).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@
Comment thread instro/daq/__init__.py Outdated
Comment thread instro/daq/daq.py Outdated
Comment thread instro/daq/daq.py
Comment thread instro/daq/daq.py Outdated
Comment thread instro/daq/daq.py Outdated
Comment thread instro/daq/daq.py Outdated
@cartercanedy

Copy link
Copy Markdown
Collaborator

@greptileai

@greptile-apps

greptile-apps Bot commented Aug 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds software-timed background acquisition to InstroDAQ, including configurable polling, default 1 Hz timing, lifecycle handling, documentation, examples, and tests.

  • Splits hardware- and software-timed sample-rate configuration while retaining the existing hardware-timing alias.
  • Selects the appropriate analog-read function for the background daemon.
  • Adds NI examples demonstrating parallel hardware- and software-timed DAQ instances.
  • Updates DAQ documentation and lifecycle tests for the new acquisition modes.

Confidence Score: 4/5

The invalid software-timed start paths should raise before merging so callers cannot mistake a no-op for a successfully started acquisition.

Both background-free software acquisition branches merely log and return, leaving callers with no programmatic indication that continuous acquisition never started.

Files Needing Attention: instro/daq/daq.py, tests/daq/test_daq_drivers.py

Important Files Changed

Filename Overview
instro/daq/daq.py Adds software-timed daemon configuration and lifecycle dispatch, but invalid background-free software starts return successfully instead of raising.
instro/daq/init.py Exposes the new timing exception classes through the DAQ package API.
tests/daq/test_daq_drivers.py Adds broad software-timing and parallel-acquisition coverage, while asserting logging rather than the documented exception contract.
examples/daq/daq_hw_and_sw_timed_ni.py Demonstrates independent hardware- and software-timed acquisitions on non-overlapping NI channels.
docs/guides/instrumentation/daq.mdx Documents manual, background software-timed, and hardware-timed acquisition modes.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A["InstroDAQ.start(background)"] --> B{"Timing configured?"}
  B -->|No| C{"background?"}
  C -->|Yes| D["Configure default 1 Hz software timing"]
  C -->|No| E["Log error and return"]
  B -->|Hardware| F["Start device acquisition"]
  B -->|Software| G{"background?"}
  D --> G
  G -->|Yes| H["Start daemon with software-timed reads"]
  G -->|No| E
  F --> I{"background?"}
  I -->|Yes| J["Start daemon with hardware-buffer fetches"]
  I -->|No| K["Caller fetches manually"]
Loading
Prompt To Fix All With AI
### Issue 1
instro/daq/daq.py:957-963
**Software starts silently no-op**

When `start(background=False)` is called without timing or with software timing configured, these branches only log and return, causing callers to continue as though continuous acquisition started while no samples are collected.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "InstroDAQ doesn't manage background metr..." | Re-trigger Greptile

Comment thread instro/daq/daq.py
@maxleblang

Copy link
Copy Markdown
Contributor Author

Closes #305

maxleblang and others added 2 commits August 4, 2026 11:22
configure_ai_hw_sample_rate -> configure_hw_sample_rate
configure_ai_sw_sample_rate -> configure_sw_sample_rate
DAQDriverBase.configure_ai_hw_timing -> configure_hw_timing

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread tests/daq/ni/test_nidaq_hw_and_sw_timed_hardware.py Outdated
Comment thread docs/guides/instrumentation/daq.mdx Outdated
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.

4 participants