Add config fields for Trigger mode for GenTL#78
Draft
C-Achard wants to merge 14 commits into
Draft
Conversation
Add generic hardware-trigger support for GenTL cameras and expose trigger settings in CameraSettings. - Introduce CameraTriggerSettings model (config.py) with role/input/output/timeout/strict and helpers to coerce and serialize. - Allow CameraSettings to read/write backend-specific trigger options. - GenTLCameraBackend: import and use trigger settings, apply trigger timeout override, persist actual trigger config, and mark hardware_trigger capability as BEST_EFFORT. - Implement trigger configuration paths: off, external/follower (input), and master (output). Add helper methods (_set_enum_node, _node, _node_symbolics, _trigger_attr, _trigger_to_dict) to safely interact with GenICam nodes. - Improve lifecycle handling: configure trigger after other settings, restore a safe non-triggering state on stop, and provide waits_for_hardware_trigger property and clearer timeout messages when waiting for hardware triggers. - Add DEFAULT_CAPABILITIES entry for hardware_trigger in base defaults. These changes enable safer and configurable hardware-trigger operation for GenTL backends and improve error reporting and shutdown behavior.
Populate gentl trigger defaults on save and allow saving configs with empty DLCLive model path. Add _with_camera_defaults_for_save to ensure CameraTriggerSettings are present for gentl cameras and thread through allow_empty_model_path in _dlc_settings_from_ui/_current_config so configs can be saved without a model while preserving existing DLC fields. Improve multi-camera runtime robustness: treat TimeoutError from hardware-trigger backends as an expected 'no trigger' event (don't count as a camera failure) and add _trigger_role_from_settings/_camera_start_priority helpers. Start active cameras sorted by trigger role so trigger-waiting (external/follower) devices are armed before masters. Also extend DLCLive configuration error handling to include RuntimeError.
Enhance Qt app signal handling to support SIGTERM and SIGBREAK, and make Ctrl+C shutdown more robust. Adds a quitting flag and a two-stage interrupt: first interrupt triggers window close and schedules app.quit, second interrupt forces immediate exit (os._exit(130)). Parents the keepalive QTimer to the QApplication, sets a 100ms interval, and safely stops any previous timer to avoid duplicates. Adds logging and exception handling around window close and timer cleanup, and uses QTimer.singleShot to ensure Qt leaves its event loop even if closeEvent cleanup is asynchronous.
Add a comprehensive test suite for GenTL hardware trigger handling (tests/cameras/backends/test_gentl_trigger.py). Tests cover trigger roles (off/external/follower/master), selector/source/activation settings, strict vs non-strict behavior for invalid sources, master output configuration, alias mapping, timeout handling and error messaging, and persistence of trigger_actual for debugging. Update the test conftest fake node map (tests/cameras/backends/conftest.py) to include Trigger* and Line* nodes (AcquisitionMode, TriggerSelector, TriggerMode, TriggerSource, TriggerActivation, LineSelector, LineMode, LineSource) so the tests can exercise trigger and GPIO-related configuration.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds GenTL hardware trigger configuration support and related GUI/configuration handling for saving trigger defaults and managing trigger-aware multi-camera startup/shutdown behavior.
Changes:
- Introduces
CameraTriggerSettingsand trigger capability reporting. - Adds GenTL trigger configuration, timeout handling, and shutdown restore logic.
- Updates GUI/config save behavior and multi-camera worker handling for hardware-triggered cameras.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
dlclivegui/config.py |
Adds trigger settings model and camera helpers. |
dlclivegui/cameras/base.py |
Adds default hardware trigger capability flag. |
dlclivegui/cameras/backends/gentl_backend.py |
Implements GenTL trigger roles, configuration, timeout messages, and restore behavior. |
dlclivegui/services/multi_camera_controller.py |
Handles trigger timeouts and orders camera startup by trigger role. |
dlclivegui/gui/main_window.py |
Adds GenTL trigger defaults during config save and relaxes save-time model path validation. |
dlclivegui/main.py |
Improves signal handling for graceful/forced shutdown. |
tests/cameras/backends/conftest.py |
Extends fake GenTL node map with trigger/GPIO nodes. |
tests/cameras/backends/test_gentl_trigger.py |
Adds GenTL trigger behavior tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Use the user-configured camera order for display/tiling and ensure tiling geometry matches displayed frames. Changes: - GUI: populate the inference camera dropdown from active cameras in the configured order and only add entries for cameras that are actually running. - MultiCameraController: store the user display order, derive startup order by sorting for trigger safety (followers/external first, master last), emit frames/timestamps in display order (with any unexpected IDs appended deterministically), clear display order on stop, and handle no-active-cameras early. - Utils: compute_tiling_geometry now uses the frames' display order (insertion order) rather than sorted keys so tile dimensions and overlays align with the tiled frame; updated docstrings to reflect this. These changes ensure consistent tiling, overlay transforms, and UI behavior that follow the user's configured ordering.
Update display tests to assert that tiling and tile computations preserve frame insertion/display order (no longer sorting by camera ID) and add coverage for tile offsets, scaling, and tiled frame content. Add a suite of unit tests for MultiCameraController utilities and behavior: get_camera_id, trigger role aliasing, camera start priority, preserving user display order on start, frame_ready emission order, clearing display order on stop, hardware trigger timeouts (non-fatal), and non-trigger timeouts (fatal). Also import newly-tested helper functions from multi_camera_controller.
Patch tests/gui/test_app_entrypoint.py to monkeypatch appmod._maybe_allow_keyboard_interrupt with a MagicMock in both test_main_with_splash and test_main_without_splash. This prevents the real interrupt-handling helper from running during GUI tests and avoids side effects on global keyboard/signal handling.
GenTLCameraBackend: add handling for a 'strict' flag when parsing GenTL trigger config so invalid configs raise in strict mode but fall back with a warning otherwise. Preserve the original exception when raising, and improve the warning text to mention strict mode. Return whether LineSelector was actually set and skip configuring trigger output if selection failed to avoid driving an unintended GPIO line. DLCLiveMainWindow: introduce _with_camera_trigger_defaults_for_save to ensure gentl trigger defaults are stored per camera, refactor _with_camera_defaults_for_save to apply that per-camera, and adjust _current_config to use the first camera with defaults applied. This ensures trigger settings are persisted and validated consistently.
Add with_save_defaults helpers to CameraSettings and MultiCameraSettings and use them when serializing ApplicationSettings so gentl "trigger" defaults are applied to saved configs. Remove duplicated camera-default helper logic from DLCLiveMainWindow and simplify _current_config to rely on model methods. Add unit tests to verify gentl trigger defaults are included for top-level and multi-camera cases.
Remove sorting when building available_ids so the original frame key order is preserved. The DLC camera selection relies on the first active camera in frame_data.frames; using list(...) keeps the insertion/order semantics (dict order) instead of reordering keys with sorted(). This avoids unintended changes to camera priority caused by alphabetical/numeric sorting.
This was
linked to
issues
May 28, 2026
Initialize an existing DLC config (falling back to DEFAULT_CONFIG if unset) and use its model_copy(update=...) to return settings. This replaces explicit field-by-field construction so only model_path (and model_type when set) are changed, preserving other DLC options and avoiding duplication.
Comment on lines
+125
to
+127
| trigger_timeout = self._positive_float(self._trigger_attr(self._trigger, "timeout", None)) | ||
| if trigger_timeout is not None: | ||
| self._timeout = float(trigger_timeout) |
Add a warning log when setting GenTL TriggerMode to 'On' fails in non-strict mode. Previously the code only raised an exception in strict mode; now it emits a warning to inform users that the trigger mode may not be correctly configured when continuing without strict enforcement.
Make GenTLCameraBackend trigger configuration more robust and adjust tests. - Use the waits_for_hardware_trigger property when converting GenTL timeouts to user-facing errors instead of inferring from the role string. - Read and record the configured trigger role early in _configure_trigger_input. - Check results when setting TriggerSelector, TriggerSource and TriggerActivation (selector_ok, source_ok, activation_ok). If selector/source routing fails in non-strict mode, disable the trigger, reset internal trigger state and log a warning to avoid arming the camera on a previous/default input line. If activation fails, warn and continue using the camera default. - If enabling TriggerMode=On fails, disable the trigger and reset internal state instead of only warning. - Validate LineMode and LineSource when configuring master output and log if configuration is incomplete. Tests updated to reflect safety changes: - Expect waits_for_hardware_trigger to be set for external input configuration. - Rename and change a non-strict invalid-source test to assert the trigger is disabled, waits_for_hardware_trigger is False, and trigger_actual is persisted as off. - Add a new test ensuring an invalid selector in non-strict mode disables the trigger and persists the off state. These changes prevent the camera from being left armed on an unintended/default input if routing nodes could not be applied, improving safety and predictability.
Introduce a MAX_HARDWARE_TRIGGER_FETCH_TIMEOUT and cap Harvester.fetch() timeouts when the trigger role waits for external hardware (roles: external, follower) to keep individual fetch calls short and allow prompt shutdown. Preserve legacy behavior for non-waiting roles (e.g. master). Remove a noisy Trigger input LOG.info call. Update tests to expect the capped fetch timeout, verify the original requested timeout is still persisted in trigger_actual, add a test that master mode is not capped, and make test resource cleanup more robust (use try/finally around open/close).
Comment on lines
+503
to
+509
| active_cameras = multi_camera.get_active_cameras() | ||
|
|
||
| if active_cameras: | ||
| camera = active_cameras[0].model_copy(deep=True) | ||
| else: | ||
| camera = self.camera.with_save_defaults() | ||
|
|
Comment on lines
+109
to
+116
| if bool(getattr(self._backend, "waits_for_hardware_trigger", False)): | ||
| LOGGER.debug( | ||
| "[Worker %s] waiting for hardware trigger: %s", | ||
| self._camera_id, | ||
| exc, | ||
| ) | ||
| consecutive_errors = 0 | ||
| continue |
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.
Introduces a framework for hardware trigger support in the GenTL camera backend, along with related improvements to camera configuration management and GUI handling.
The main focus is on enabling configurable hardware trigger roles (off, external, master, follower), robust trigger configuration, and safe shutdown behavior.
It also introduces a new
CameraTriggerSettingsmodel, utilities for accessing and updating trigger settings, and ensures that configuration files include appropriate trigger defaults.GenTL Camera Backend: Hardware Trigger Support and Configuration
off,external,master,follower) in GenTL backend, with robust configuration and error handling using the newCameraTriggerSettingsmodel. This includes methods for configuring trigger modes, input/output settings, and restoring safe states after acquisition. [1] [2] [3] [4] [5]Configuration Model and Utilities
CameraTriggerSettingsas a pydantic model with validation and serialization utilities, supporting flexible trigger configuration in camera settings. Added methods toCameraSettingsfor accessing and updating trigger settings per backend.TriggerRoleandTriggerActivationliterals for type safety in trigger configuration.GUI and Configuration Management
Other
hardware_triggeras unsupported by default.