Repository navigation
Commit d5cc3f8
Feature: triggered cameras, master and follower scheme (#96)
* Add GenTL hardware trigger support
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.
* Handle camera trigger defaults & trigger-aware startup
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.
* Improve signal handling and graceful shutdown
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 GenTL trigger tests and fake node map
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.
* Respect user camera order for display/tiling
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.
* tests: preserve display order and add MultiCamera tests
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.
* Mock _maybe_allow_keyboard_interrupt in GUI tests
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.
* Support strict GenTL trigger and defaults
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.
* Apply gentl trigger defaults when saving
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.
* Don't sort available camera IDs
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.
* Preserve DLC config using model_copy
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.
* Warn when GenTL TriggerMode fails to enable
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.
* Improve GenTL trigger routing safety and tests
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.
* Cap hardware-trigger fetch timeout and update tests
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).
* Interruptible camera waits; simplify config save
Make SingleCameraWorker sleep calls interruptible by using self._stop_event.wait() instead of time.sleep(), and add a small _trigger_timeout_delay to allow early exit during trigger/wait cycles. This prevents the worker from being unresponsive to stop requests during retry and trigger waits. Also simplify ApplicationSettings.to_dict() to always start from self.camera.with_save_defaults() and stop overriding it with active multi-camera selection, ensuring consistent camera settings are saved.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Fix broken suggestion
* Resolve 'auto' trigger source in GenTL backend
Add resolution logic for trigger source: implement _resolve_trigger_source to map the model-level default 'auto' to the first supported camera line (Line0/Line1/Line2/Any), return whether the resolved value is supported, and log/warn or raise depending on strict mode. Integrate this into _configure_pixel_format/_configure trigger flow so TriggerSource is only set if resolution succeeded. Change CameraTriggerSettings default source to 'auto' and add a field validator to coerce common aliases (e.g. 'default', 'automatic', 'device', 'camera') to 'auto'. Update tests and fixtures: adjust fake node default TriggerSource to Line1, update expectations, and add unit tests for auto-selection behavior and strict-mode error. This makes trigger configuration more robust across cameras with differing GenICam enum symbolics.
* Fix display tests
* Update test_multicam_controller.py
* Fix duplication
* Refine GenTL trigger timeout handling
Refactors GenTL timeout logic to keep a base fetch timeout separate from trigger-requested timeout, then computes the effective timeout based on trigger role. External/follower roles remain capped to avoid long hardware-trigger waits, master uses the trigger timeout directly, and non-trigger modes fall back to the base timeout.
Also updates trigger persistence to store both requested and actual trigger settings, including `fetch_timeout` and `requested_timeout`, and improves trigger serialization by supporting objects that expose `to_properties()`.
* Use CameraTriggerSettings for trigger role
Replace manual trigger-role normalization in `multi_camera_controller` with `CameraTriggerSettings.from_any(...)` and normalize via its parsed role. This centralizes alias handling in config parsing and keeps a safe fallback to `"off"` when trigger settings are invalid.
* Fix incorrect test expectations
* Update multi_camera_controller.py
* Add trigger settings dialog and UI button
Introduce a TriggerConfigDialog to edit per-camera hardware trigger settings (CameraTriggerSettings). The dialog provides fields for role, selector, source, activation, output line/source, timeout and strict mode, and persists settings into camera.properties[<backend>]['trigger']. Also add a "Trigger Settings…" button to the camera settings UI (disabled by default) with tooltip and icon; wiring to open the dialog can be connected elsewhere.
* Add per-camera hardware trigger settings
Introduce support for per-camera hardware trigger configuration in the camera config dialog. Imports TriggerConfigDialog and CameraTriggerSettings, wires the trigger settings button to open a modal, and adds _open_trigger_settings_dialog to commit edits, show the dialog, apply updates, and restart the preview if needed. Adds helpers: _ensure_default_trigger_config to initialize gentl.trigger defaults, _trigger_role_for_label to show trigger role in camera list labels, and _trigger_dict_for_cam to compare trigger settings when deciding to restart previews. Also integrates the hardware trigger field into the settings summary and ensures new/loaded cameras get a default trigger config.
* Update trigger_config_dialog.py
* Use _is_preview_live and clear trigger timeout
Replace direct checks of self._preview.state == PreviewState.ACTIVE with self._is_preview_live() in camera_config_dialog to centralize preview-active logic and ensure consistent behavior when restarting the preview after trigger or camera setting changes. In trigger_config_dialog, only set payload["timeout"] for external/follower roles when timeout > 0, and explicitly set payload["timeout"] = None when role == "off" to clear any stale timeout when disabling the trigger.
* Restart active previews; ignore bad trigger
Allow pending preview restarts to proceed when the preview is ACTIVE (previously only IDLE) and return early to avoid double UI sync in camera_config_dialog.py. Also wrap CameraTriggerSettings loading in a try/except and fall back to a default instance when parsing fails, making trigger_config_dialog.py tolerant of malformed or missing trigger data.
* Improve trigger dialog defaults and error handling
Make trigger dialog friendlier and more robust: recommend using 'auto' for trigger source and switch default/fallback source from "Line0" to "auto", update source placeholder and info/timeout tooltips, and import QMessageBox. Wrap CameraTriggerSettings.from_any in a try/except to show a critical error dialog on failure and abort apply. Store trigger settings via trigger.to_properties() instead of model_dump(exclude_none=True). These changes provide clearer defaults and better error feedback when applying trigger settings.
* Add worker timing logs to camera worker
Introduce WorkerTimingStats (utils/stats.py) to collect simple timing counters (per-named section totals, frame/timeouts/errors) and periodically emit debug logs. Integrate it into SingleCameraWorker: create a timing instance (configurable via new SINGLE_CAMERA_WORKER_DO_LOG_TIMING in config.py), wrap backend.read and frame emit calls with timed sections, and call note_frame/note_timeout/note_error + maybe_log to accumulate and flush stats. Also update imports accordingly and use a 1s default log interval.
* Add per-camera timing to MultiCameraController
Introduce MULTI_CAMERA_WORKER_DO_LOG_TIMING and disable single-worker timing by default (SINGLE_CAMERA_WORKER_DO_LOG_TIMING=false). Add per-camera WorkerTimingStats storage and a _timing_for_camera factory that respects the new config. Wrap _on_frame_captured processing in timed sections (total, apply_transforms, update_latest), call note_frame/maybe_log per camera, and emit frames as before. Also adjust SingleCameraWorker timing labels from GenTL.* to Single.* for clearer logs.
* Add pretty/str/repr to CameraSettings
Introduce a human-readable representation for CameraSettings by adding a pretty() method and overriding __str__ and __repr__. The pretty output formats key fields (name, index, backend, enabled, fps, size, exposure, gain, rotation) and displays a readable crop region (showing 'none' or coordinate range with 'edge' fallbacks). This aids debugging and logging without changing existing validation or behavior.
* Reduce lock scope and refactor frame handling
Refactor _on_frame_captured to minimize time spent under _frame_lock and improve timing granularity. Introduces a frame_data local, renames timing labels (e.g. Multi.apply_transforms, Multi.store_latest, Multi.build_ordered, Multi.construct_frame_data), and moves frame_data construction inside measured blocks. The frame_ready emit is now performed outside the lock and guarded by a None check to avoid holding the lock during signal emission. Also small whitespace and cleanup changes.
* Add GenTL strobe support and trigger debugging
Add debugging helpers for GenTL trigger and frame-rate nodes and log their values during camera configuration. Improve GenTL trigger input handling by treating TriggerSource as best-effort (supporting read-only/auto cases) and emitting clearer warnings. Implement a dedicated master/output path for TIS/DMK 37U cameras using Strobe* nodes (StrobeEnable/Polarity/Operation/Duration/Delay) with a fallback to generic Line* configuration; respect strict mode and surface informative errors. Extend CameraTriggerSettings with strobe fields (polarity, operation, duration, delay) and validation/coercion. Update the trigger configuration dialog to expose strobe controls, tooltips, UI sync logic, and include strobe values in the saved payload.
* Add _node_value helper for safe node reads
Introduce a static _node_value(node_map, name, default) helper that performs a best-effort read of GenICam node values. It looks up the node, returns default if missing, then tries node.value and falls back to node.GetValue() while swallowing exceptions. This prevents debug helpers and open() from failing when test/SDK nodes expose different accessors or raise errors.
* Refactor GenTL trigger source handling and logging
Replace manual TriggerSource symbol checks with _resolve_trigger_source and only set TriggerSource when supported. Delay setting TriggerActivation until after source resolution and use non-strict writes for activation. Add safety check: do not arm TriggerMode=On unless both TriggerSelector and TriggerSource succeeded (avoids waiting on a previous/default input). Improve log messages to include selector_ok, source_ok, requested/ resolved source and activation for clearer diagnostics.
* Update multi_camera_controller.py
* pre-commit
* Add GenTL telemetry and frame-rate debugging
Add helpers and telemetry to better read and debug GenTL/GenICam cameras.
- New debug helper _debug_frame_rate_nodes to log many common frame-rate/exposure/throughput nodes.
- Added robust node accessors: _node_value, _node_float, _node_str to safely read various GenICam node types and try multiple node names.
- Enhance frame-rate configuration to log before/after values when enabling rate control and when setting AcquisitionFrameRate/AcquisitionFrameRateAbs; record any accepted frame-rate as _actual_fps.
- After starting acquisition, attempt to read telemetry and run FPS debug logging; warn (but continue) if telemetry read fails.
- Improve _read_telemetry to prefer resulting frame-rate nodes over requested ones, and to populate actual_fps, actual_exposure, actual_gain and other useful properties (pixel format, throughput, resolution, etc.) into the settings namespace for GUI/debugging.
These changes make frame-rate/exposure behavior more observable and more tolerant across different camera implementations.
* Allow zero node values and reduce log level
Change LOG.info to LOG.debug to reduce noise when printing node information. Add an allow_zero parameter to _node_float so callers can accept zero as a valid value (previously only positive values were returned). Update callers for exposure, gain, and DeviceLinkThroughputLimit to pass allow_zero=True so reported zeros are treated as real readings while preserving the original behavior by default.
* Split multi-camera frame signals; throttle display
Introduce a separate, throttled UI/display path for multi-camera frames. Add GUI_MAX_DISPLAY_FPS config and a new display_ready signal that is emitted at most at that rate, while frame_ready remains full-rate for recording/inference. Implement _should_emit_display_ready, wire the MultiCameraController to emit both signals, and reset throttling state when starting. Update the main window to handle processing and display paths via _on_multi_frame_processing_ready and _on_multi_frame_display_ready, and adjust tests accordingly. Also add GUI/debug timing config keys and a temporary timing-only early path in SingleCameraWorker (marked as FIXME).
* Remove temp debug block
* Fix missing signal wiring
Fix signal hookup and disable main-window timing flag. Replace the second connection of multi_camera_controller.frame_ready to _on_multi_frame_display_ready with multi_camera_controller.display_ready to separate processing-ready and display-ready events. Also comment out MAIN_WINDOW_DO_LOG_TIMING in config.py to disable main-window timing logging.
* Revert debug logging to disabled
* Update main_window.py
* Fix DLC model_type regression from detected backend
Update `_dlc_settings_from_ui` to rebuild settings via `model_dump` + `model_validate` instead of `model_copy(update=...)`, ensuring Pydantic validation/coercion runs on UI-derived updates. This fixes cases where detected backends (e.g., `Engine.PYTORCH`) were not stored as validated string values. Added a GUI test that mocks backend detection and asserts `model_type` is normalized to the string `"pytorch"`.
* Add Basler trigger support and timeout handling
Implement comprehensive trigger support for the Basler backend: import CameraTriggerSettings, parse trigger config (roles: off, external/follower, software, master), and persist trigger_actual into the namespace. Add trigger configuration helpers (_configure_trigger*, _resolve_trigger_source, _restore_trigger_idle), software trigger execution (trigger_once), and many feature/enum/numeric helper methods with debug logging. Make RetrieveResult use a configurable _retrieve_timeout_ms (derived from trigger.timeout) and limit it for hardware-triggered cameras to allow prompt shutdown; raise a TimeoutError when waiting for hardware triggers. Expose hardware_trigger capability as BEST_EFFORT and add an env var-based pylon emulation toggle for testing. Misc: add debug dumps of trigger-related nodes and best-effort restore of trigger state on close.
* Remove PYLON_CAMEMU test comment
Delete commented-out code that forced pypylon to create emulation virtual cameras (PYLON_CAMEMU), which was only intended for testing and should not be enabled for release. Also remove an extraneous blank line to tidy up the file.
* Make trigger config dialog backend-aware
Introduce a TriggerUiProfile dataclass and trigger_ui_profile_for_backend() to drive dialog presentation per backend. Replace free-text source field with an editable QComboBox providing backend suggestions and defaults. Add profile-driven visibility/enabling for input, master, software and strobe/line output fields, plus helper methods to manage form rows and combo text. Only include strobe-related payload fields when the backend profile exposes them. Misc: expand info/help text, show backend in group title, increase dialog min-width, refine tooltips, and improve model↔UI mapping and payload construction.
* Support basler backend in trigger defaults
Previously the default trigger configuration was only applied for backend 'gentl' and always stored under the 'gentl' properties key. This change treats the backend name dynamically (accepting both 'gentl' and 'basler'), and stores the default trigger settings under the actual backend key in cam.properties. Also preserves behavior when cam.properties or the backend namespace is not a dict.
* Throttle hardware-trigger wait logs
Reduce log flooding when waiting for hardware triggers by adding throttled logging.
Changes in dlclivegui/services/multi_camera_controller.py:
- Import time.
- Add a new _log_interval_while_waiting_for_trigger_s attribute to SingleCameraWorker.
- Replace the direct LOGGER.debug call for expected trigger wait timeouts with a call to _log_trigger_wait_throttled.
- Implement _log_trigger_wait_throttled to suppress repeated timeout messages, emit a consolidated debug message, and report how many repeated logs were suppressed.
This prevents high-frequency expected poll-timeout logs (common in trigger-waiting modes) from overwhelming the logs.
* Enhance FakePylon and add Basler backend tests
Extend the test conftest FakePylon to better emulate pypylon: add FakePylonTimeoutException, richer _Feature (symbolics, min/max/inc, read/write checks, call tracking), _EnumEntry, expanded _DeviceInfo, GrabResult.release tracking, and a more complete InstantCamera (timeouts, software trigger, trigger/line features, buffer and grab controls, test knobs). Reset the fake factory and provide default fake devices and a basler_settings_factory fixture. Patch the basler SDK fixture to use FakePylon. Add new test suite tests/cameras/backends/test_basler_backend.py covering lifecycle (open/read/close, fast-start, idempotent close), discovery/rebind, resolution/exposure/gain/fps handling, and comprehensive trigger behavior (follower/master/software/external) to validate backend logic.
* Throttle trigger-wait logs, mark Basler test xfail
Add throttled logging for hardware-trigger wait timeouts in SingleCameraWorker to avoid noisy repeated timeout messages. Introduce _trigger_wait_log_interval, _last_trigger_wait_log and _trigger_wait_suppressed_count and move _log_trigger_wait_throttled into the worker; remove the duplicate implementation from MultiCameraController. Also mark the Basler software-trigger test as xfail because software trigger support is not implemented yet.
* Update multi_camera_controller.py
* Basler backend: add timing and FPS config
Integrates optional WorkerTimingStats into the Basler camera backend and refactors FPS handling and frame retrieval. Adds a _configure_frame_rate() helper to centralize AcquisitionFrameRate enabling, setting and readbacks (logs many related nodes and records actual_fps). Initializes a WorkerTimingStats instance (controlled by SINGLE_CAMERA_WORKER_DO_LOG_TIMING) and wraps RetrieveResult/convert/get array/release steps with timing measurements, improved error handling, proper grab_result release on exceptions, and frame counting/logging. Overall improves observability and robustness when setting frame rates and reading frames from Basler cameras.
* style: apply pre-commit
* Update basler_backend.py
* Fix multi-camera shutdown synchronization
Refactors `MultiCameraController.stop()` to track a dedicated stopping state and finalize shutdown only after all camera threads are actually stopped. It now delays `all_stopped` emission until cleanup is truly complete, keeps references to threads that refuse to terminate, and avoids the previous premature "stopped" state. The main window now explicitly calls `stop(wait=True)` during camera/project shutdown so UI transitions block until camera teardown is done.
* Handle only PyPylon timeout errors
Import `genicam` alongside `pylon` and add a helper to detect `genicam.TimeoutException` safely. The hardware-trigger timeout path now converts exceptions to `TimeoutError` only for true SDK timeout exceptions, avoiding incorrect timeout handling for unrelated grab failures.
* Test Basler timeout mapping for trigger reads
Add coverage for hardware-triggered Basler reads to ensure SDK timeout errors are re-raised as `TimeoutError` with the expected message and exception cause. Update the Basler SDK test patching to also inject a fake `genicam.TimeoutException`, so timeout handling paths can be exercised reliably in tests.
* Harden multi-camera shutdown lifecycle
Tightens controller start/stop state handling to prevent unsafe restarts while shutdown is in progress, and finalizes shutdown only after all camera threads actually exit. Cleanup now resets runtime state consistently, `all_stopped` emission is guarded against duplicates, and stop requests only fan out to workers when actively running. Camera stop handling was also refined so full initialization failure and last-camera runtime shutdown both transition into the same safe finalization path, with clearer logging around incomplete shutdowns.
* Do not use software as auto mode for source
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Preserve runtime info when clearing workers
Stop clearing `_runtime_info` as it is not currently defined
* Stabilize Basler timeout mapping test
Update the hardware-trigger timeout test to raise `bb.genicam.TimeoutException` directly instead of a fake timeout class, and assert that this real pylon exception is preserved as the `__cause__` of the mapped `TimeoutError`. This keeps the test aligned with actual backend behavior.
* Stop injecting GenTL trigger save defaults
Simplify `CameraSettings.with_save_defaults()` to return only a deep copy, removing the special-case logic that auto-populated `properties.gentl.trigger`. This avoids silently adding GenTL trigger settings during save.
* Stop auto-initializing camera trigger config
Removes the helper that injected default trigger settings for GenTL/Basler cameras and drops its call sites when adding or loading cameras. This avoids silently mutating camera properties with trigger defaults unless they are explicitly configured.
* Revise trigger config behavior tests
Updates `tests/test_config.py` to reflect the new trigger configuration semantics. The tests now verify that missing trigger config defaults to `role="off"` and `source="auto"`, explicit trigger settings round-trip through `ApplicationSettings` for both `gentl` and `basler`, and serialization does not inject implicit trigger blocks into camera properties.
* Disable CAMEMU debug setting
* Fix model_type coercion in Engine parser
Adjust `Engine.from_model_type` to normalize non-string `model_type` values by converting either `.value` or the original object with `str(...)` in a single step. This avoids eager fallback conversion inside `getattr` and makes enum-like inputs more reliably coercible.
* Disable Strobe in master role
* Handle stop requests during camera startup
Fixes a race where stopping preview during camera initialization could be ignored. `_stop_preview` now checks `is_active()` instead of only `is_running()`, and `SingleCameraWorker` now exits cleanly if a stop is requested right after backend open. The controller also adds `is_active()`/`is_starting()` state helpers and clarifies `is_running()` semantics.
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>1 parent 36a3d56 commit d5cc3f8
22 files changed
Lines changed: 4299 additions & 261 deletions
File tree
- dlclivegui
- cameras
- backends
- gui
- camera_config
- services
- temp
- utils
- tests
- cameras/backends
- gui
- services
- utils
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
| 73 | + | |
73 | 74 | | |
74 | 75 | | |
75 | 76 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
15 | 30 | | |
16 | 31 | | |
17 | 32 | | |
| |||
38 | 53 | | |
39 | 54 | | |
40 | 55 | | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
41 | 77 | | |
42 | 78 | | |
43 | 79 | | |
| |||
168 | 204 | | |
169 | 205 | | |
170 | 206 | | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
171 | 349 | | |
172 | 350 | | |
173 | 351 | | |
| |||
206 | 384 | | |
207 | 385 | | |
208 | 386 | | |
| 387 | + | |
209 | 388 | | |
210 | | - | |
211 | | - | |
212 | | - | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
213 | 392 | | |
214 | 393 | | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
215 | 400 | | |
216 | 401 | | |
217 | 402 | | |
| |||
377 | 562 | | |
378 | 563 | | |
379 | 564 | | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
380 | 568 | | |
381 | 569 | | |
382 | | - | |
383 | | - | |
| 570 | + | |
| 571 | + | |
384 | 572 | | |
385 | 573 | | |
386 | 574 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
| |||
328 | 329 | | |
329 | 330 | | |
330 | 331 | | |
| 332 | + | |
331 | 333 | | |
332 | 334 | | |
333 | 335 | | |
| |||
451 | 453 | | |
452 | 454 | | |
453 | 455 | | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
454 | 465 | | |
455 | 466 | | |
456 | 467 | | |
457 | 468 | | |
458 | | - | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
459 | 474 | | |
460 | 475 | | |
461 | 476 | | |
| |||
514 | 529 | | |
515 | 530 | | |
516 | 531 | | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
517 | 535 | | |
518 | 536 | | |
519 | 537 | | |
| |||
1029 | 1047 | | |
1030 | 1048 | | |
1031 | 1049 | | |
| 1050 | + | |
| 1051 | + | |
| 1052 | + | |
| 1053 | + | |
| 1054 | + | |
| 1055 | + | |
| 1056 | + | |
| 1057 | + | |
| 1058 | + | |
| 1059 | + | |
| 1060 | + | |
| 1061 | + | |
| 1062 | + | |
| 1063 | + | |
| 1064 | + | |
| 1065 | + | |
| 1066 | + | |
| 1067 | + | |
| 1068 | + | |
| 1069 | + | |
| 1070 | + | |
| 1071 | + | |
| 1072 | + | |
| 1073 | + | |
| 1074 | + | |
| 1075 | + | |
| 1076 | + | |
| 1077 | + | |
| 1078 | + | |
| 1079 | + | |
| 1080 | + | |
| 1081 | + | |
| 1082 | + | |
1032 | 1083 | | |
1033 | 1084 | | |
1034 | 1085 | | |
| |||
1085 | 1136 | | |
1086 | 1137 | | |
1087 | 1138 | | |
1088 | | - | |
1089 | | - | |
1090 | | - | |
| 1139 | + | |
1091 | 1140 | | |
1092 | 1141 | | |
1093 | 1142 | | |
| |||
1099 | 1148 | | |
1100 | 1149 | | |
1101 | 1150 | | |
1102 | | - | |
| 1151 | + | |
1103 | 1152 | | |
1104 | 1153 | | |
1105 | 1154 | | |
| |||
1597 | 1646 | | |
1598 | 1647 | | |
1599 | 1648 | | |
| 1649 | + | |
| 1650 | + | |
| 1651 | + | |
| 1652 | + | |
| 1653 | + | |
| 1654 | + | |
| 1655 | + | |
1600 | 1656 | | |
1601 | 1657 | | |
1602 | 1658 | | |
| |||
1612 | 1668 | | |
1613 | 1669 | | |
1614 | 1670 | | |
| 1671 | + | |
| 1672 | + | |
| 1673 | + | |
1615 | 1674 | | |
1616 | 1675 | | |
1617 | 1676 | | |
| |||
1756 | 1815 | | |
1757 | 1816 | | |
1758 | 1817 | | |
1759 | | - | |
| 1818 | + | |
1760 | 1819 | | |
1761 | 1820 | | |
| 1821 | + | |
1762 | 1822 | | |
1763 | 1823 | | |
1764 | 1824 | | |
| |||
0 commit comments