Skip to content

[feat] driver tucsen: support stopping acquisition at any time#3332

Merged
pieleric merged 1 commit intodelmic:masterfrom
pieleric:feat-driver-tucsen-support-stopping-acquisition-at-any-time
Mar 27, 2026
Merged

[feat] driver tucsen: support stopping acquisition at any time#3332
pieleric merged 1 commit intodelmic:masterfrom
pieleric:feat-driver-tucsen-support-stopping-acquisition-at-any-time

Conversation

@pieleric
Copy link
Copy Markdown
Member

@pieleric pieleric commented Jan 26, 2026

Change acquisition structure to use a callback when the camera sends a frame. This way, it's possible to stop at any time. It is also the suggested way by Tucsen to optimise for speed.

Copilot AI review requested due to automatic review settings January 26, 2026 16:58
@pieleric pieleric force-pushed the feat-driver-tucsen-support-stopping-acquisition-at-any-time branch from d7ac480 to 451810c Compare January 26, 2026 16:59
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 26, 2026

📝 Walkthrough

Walkthrough

This PR converts the Tucsen driver to a callback-driven capture model. It adds register_data_callback() to TUCamDLL and FakeTUCamDLL, implements an SDK-side buffer callback that produces NumPy arrays and invokes a Python handler, and adapts TUCam acquisition to receive frames via a new AcqMessage.FRAME signal and _on_frame handler. The fake DLL was updated to run a background capture thread that dispatches frames to the registered callback. A unit test exercising callback registration, start/stop, and frame delivery was added.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant TUCam as TUCam (wrapper)
    participant DLL as TUCamDLL / FakeTUCamDLL
    participant Cam as Camera SDK / Hardware
    participant Flow as DataFlow

    App->>TUCam: start_acquisition()
    activate TUCam
    TUCam->>DLL: register_data_callback(on_frame)
    activate DLL
    DLL->>Cam: install C callback (or start fake thread)
    deactivate DLL
    TUCam->>Cam: start_capture()
    deactivate TUCam

    Note over Cam: camera produces frames asynchronously
    Cam->>DLL: invoke C callback(frame_ptr)
    activate DLL
    DLL->>TUCam: call Python on_frame(numpy_array)
    deactivate DLL
    activate TUCam
    TUCam->>Flow: emit frame + metadata
    TUCam->>TUCam: post AcqMessage.FRAME
    deactivate TUCam

    App->>TUCam: acquisition loop processes FRAME
    activate TUCam
    TUCam->>App: deliver frame via DataFlow
    deactivate TUCam
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: refactoring the Tucsen driver's acquisition structure to support stopping at any time via callback-driven frame handling.
Description check ✅ Passed The description is directly related to the changeset, explaining the callback-based acquisition approach that enables stopping at any time and aligns with Tucsen's recommendations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pieleric pieleric requested review from ilyushkin and removed request for nandishjpatel January 26, 2026 17:01
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@src/odemis/driver/test/tucsen_test.py`:
- Around line 51-72: The test TestCallBack.test_callback_registration currently
instantiates TUCamDLL unconditionally, has no assertions that the on_frame
callback ran, and may leak camera resources on failure; update the test to
respect TEST_NOHW by using existing KWARGS/KWARGS_SIM selection when creating
TUCamDLL (use KWARGS_SIM if TEST_NOHW is set), add a thread-safe counter or
threading.Event updated by on_frame to assert that at least one frame was
received, and ensure camera open/close and capture start/end are done in
setUp/tearDown or a try/finally so resources are always released (refer to
TestCallBack, on_frame, test_callback_registration, and TUCamDLL for where to
apply these changes).

In `@src/odemis/driver/tucsen.py`:
- Around line 1884-1893: capture_frame currently blocks twice for the exposure
period (time.sleep then self._capture_stopped.wait), causing a 2x delay; remove
the redundant sleep and rely on self._capture_stopped.wait(self._exposure_time)
to both wait the exposure time and detect aborts so capture_frame behavior
matches _capture_loop. Reference: function capture_frame and synchronization
primitive self._capture_stopped.wait.
- Around line 1580-1597: The _data_callback currently only catches TUCamError so
exceptions from numpy operations or the user callback (self._on_data) can escape
into the C thread; update _data_callback to catch all exceptions (e.g., a bare
except Exception) around the whole body after invoking TUCAM_Buf_GetData and the
numpy conversion/callback invocation, log the full exception (including
traceback) via logging.exception, and ensure the function returns cleanly on any
error; keep the existing TUCamError handling behavior but broaden it to
Exception to prevent crashes in the C-thread context while still calling the
user callback when safe.
🧹 Nitpick comments (2)
src/odemis/driver/tucsen.py (2)

1208-1208: Type hint uses lowercase callable instead of Callable from typing.

The type hint Optional[callable] uses the lowercase callable which is a built-in function, not a type. For proper type hints, use Callable from the typing module (already imported on line 33).

Suggested fix
-        self._on_data: Optional[callable] = None
+        self._on_data: Optional[Callable] = None

Also update line 1599 and line 1811 similarly.


2444-2453: Consider implementing a timeout for frame reception.

The TODO comment on line 2447 raises a valid concern. If the camera hardware fails silently and stops sending frames, the acquisition loop will hang indefinitely. Consider adding a timeout based on a multiple of the expected frame time (e.g., 3 * exp) and logging a warning or raising an error if triggered.

Would you like me to propose an implementation with a watchdog timeout?

Comment thread src/odemis/driver/test/tucsen_test.py
Comment thread src/odemis/driver/tucsen.py Outdated
Comment thread src/odemis/driver/tucsen.py
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the Tucsen camera driver to use a callback-based acquisition structure instead of polling for frames. This change enables stopping acquisition at any time and follows Tucsen's recommended approach for optimal performance.

Changes:

  • Modified the frame acquisition architecture to use SDK callbacks instead of polling with capture_frame()
  • Added callback registration and handling in both real and simulated camera implementations
  • Updated acquisition thread logic to work with the callback-based approach using message passing
  • Added test for callback mechanism

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 21 comments.

File Description
src/odemis/driver/tucsen.py Refactored acquisition to use callbacks; added _data_callback, register_data_callback methods; updated FakeTUCamDLL to simulate async capture with threading; modified acquisition loop to use message-based synchronization
src/odemis/driver/test/tucsen_test.py Added TestCallBack class to test callback registration and frame capture; updated logging format to include line numbers

Comment thread src/odemis/driver/tucsen.py Outdated
Comment thread src/odemis/driver/tucsen.py
Comment thread src/odemis/driver/test/tucsen_test.py
Comment thread src/odemis/driver/tucsen.py Outdated
Comment thread src/odemis/driver/tucsen.py Outdated
Comment thread src/odemis/driver/tucsen.py
Comment thread src/odemis/driver/tucsen.py
Comment thread src/odemis/driver/tucsen.py
Comment thread src/odemis/driver/tucsen.py
Comment thread src/odemis/driver/tucsen.py
@pieleric pieleric force-pushed the feat-driver-tucsen-support-stopping-acquisition-at-any-time branch from 451810c to e9e0f1f Compare January 28, 2026 11:00
Comment thread src/odemis/driver/tucsen.py
Comment thread src/odemis/driver/tucsen.py
Change acquisition structure to use a callback when the camera sends a
frame. This way, it's possible to stop at any time. It is also the
suggested way by Tucsen to optimise for speed.
@pieleric pieleric force-pushed the feat-driver-tucsen-support-stopping-acquisition-at-any-time branch from e9e0f1f to 29e6356 Compare March 19, 2026 08:14
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/odemis/driver/tucsen.py (1)

1205-1210: Use Callable instead of callable for type hints.

The lowercase callable is a built-in function, not a type. For type annotations, use Callable from the typing module.

Suggested fix

Add to imports at line 33:

from typing import Tuple, Optional, Dict, Any, Callable

Then update line 1208:

-        self._on_data: Optional[callable] = None
+        self._on_data: Optional[Callable[[numpy.ndarray], None]] = None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/odemis/driver/tucsen.py` around lines 1205 - 1210, Replace the incorrect
use of the builtin `callable` in the type annotation with the typing `Callable`:
update the import list to include `Callable` from `typing` and change the
attribute annotation `self._on_data: Optional[callable] = None` to
`Optional[Callable]`; ensure this change is applied near the definitions of
`_callback_func`, `_callback_none`, `_on_data`, and `m_raw_header` (symbols:
BUFFER_CALLBACK, _data_callback, _callback_none, _on_data, TUCAM_RAWIMG_HEADER).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/odemis/driver/tucsen.py`:
- Around line 1205-1210: Replace the incorrect use of the builtin `callable` in
the type annotation with the typing `Callable`: update the import list to
include `Callable` from `typing` and change the attribute annotation
`self._on_data: Optional[callable] = None` to `Optional[Callable]`; ensure this
change is applied near the definitions of `_callback_func`, `_callback_none`,
`_on_data`, and `m_raw_header` (symbols: BUFFER_CALLBACK, _data_callback,
_callback_none, _on_data, TUCAM_RAWIMG_HEADER).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 449b6031-7e5f-4f15-8f63-92d237656b0e

📥 Commits

Reviewing files that changed from the base of the PR and between e9e0f1f and 29e6356.

📒 Files selected for processing (2)
  • src/odemis/driver/test/tucsen_test.py
  • src/odemis/driver/tucsen.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/odemis/driver/test/tucsen_test.py

@pieleric pieleric merged commit a5d91a1 into delmic:master Mar 27, 2026
5 checks passed
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