Repository navigation
Abort the scan when a mask camera stops (or never starts) streaming - #299
Draft
boringethan wants to merge 1 commit into
Draft
boringethan wants to merge 1 commit into
boringethan wants to merge 1 commit into
Conversation
A mask-enabled camera that never delivers, or dies mid-scan while its side keeps streaming, freezes SideAverageStage's cross-camera watermark. Every later corrected side-average frame (the only reduced-mode DB record) then waits in memory until on_scan_stop. FrameClassificationStage already raises CameraStreamGap after 9 missing packets. It now also raises one CameraDropoutTimeout per camera once the gap has lasted camera_dropout_abort_s of packet time (not a frame count, so it holds at any trigger rate), recording whether the camera ever delivered. CameraDropoutWatchdogSink turns the first timeout into a scan abort through the same _stop_evt path as a mid-scan disconnect. The normal teardown flushes and persists everything up to the abort, and the worker then reports a CameraDropoutError via last_scan_error / on_error. ScanRequest.camera_dropout_abort_s defaults to 5 s. run_collection_scan (calibration and contact-quality sub-scans) sets it to None, because those collectors judge each camera themselves. A whole side going silent is #192's scope. Refs #298 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017sfVYYF9YDDyUtNHCR5abT Signed-off-by: Claude <noreply@anthropic.com>
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.
Refs #298
Problem
SideAverageStageemits corrected side-average frames (cam_id=-1) only below a cross-camera watermark. A mask camera that never streams keeps that watermark atNone; a camera that dies mid-scan freezes it. Every later side-average frame then waits inself._frames[side]untilon_scan_stop. In reduced (clinical) mode those rows are the only DB record, so:Approach: abort, instead of averaging without the camera
This PR takes the abort route over re-computing the watermark without stale cameras (
stale_lag_frames). With the abort, a stall lasts at mostcamera_dropout_abort_s.FrameClassificationStagealready emitsCameraStreamGapafter 9 missing packets. It now also emits oneCameraDropoutTimeoutper camera per scan once an alerted gap has lastedcamera_dropout_abort_s.timestamp_s - first_missing_timestamp_s), not a frame count, so the limit holds at any trigger rate.never_delivereddistinguishes "never started" from "stopped".CameraDropoutWatchdogSink(diagnostics channel) calls back on the first timeout.ScanWorkflow._on_camera_dropoutthen trips_stop_evt, the same path a mid-scan disconnect uses.on_scan_stopflushes the buffered side average, so data up to the abort is persisted.CameraDropoutError(side, cam_id, never_delivered, silent_s, threshold_s). It surfaces vialast_scan_errorandScanRequest.on_error.last_scan_canceledstaysFalse: this is an error, not a user cancel.ScanRequest.camera_dropout_abort_sdefaults to 5 s. It is passed throughdefault_pipeline(camera_dropout_abort_s=...);Noneturns it off.run_collection_scan(calibration and contact-quality sub-scans) passesNone, because those collectors judge each camera themselves.calibration/safety_hardware.py) keeps the default, so a dead camera fails that stage with the dropout error.docs/API.md(request field and abort behaviour) anddocs/SciencePipeline.md§5.1 (event table and timing rule).Tradeoff
The operator loses the rest of the session rather than getting a scan averaged over fewer cameras. The partial record up to the abort is kept.
Out of scope
CameraStreamGapor timeout. That is Histogram stream has no failure reporting: a side that stops delivering frames mid-scan is never detected #192 (fix PR feat: detect a histogram stream that stops delivering frames #193, not merged).CameraDropoutErrorfromon_erroras a distinct scan-aborted reason.Tests (no hardware)
tests/test_pipeline/test_camera_dropout.py(new, 11 tests):reset()re-arms;tests/test_scan_workflow.py:CameraDropoutErrorthroughon_error/last_scan_errorand is not marked canceled;Nonedisables the abort;tests/test_run_collection_scan.py: sub-scans disable the abort.New tests fail against
nextand pass with the change. Targeted run oftests/test_pipeline,test_reduced_mode.py,test_run_collection_scan.pyandtest_scan_workflow.py: 337 passed, 1 failed. The failure,test_start_scan_aborts_synchronously_when_scan_db_readonly, fails identically onnextin this container: it runs as root, so the chmod'd read-only DB stays writable.Not verified on hardware. Bench check: start a reduced-mode scan with one mask camera unplugged or unpowered (never delivers), and one where a camera is killed mid-scan. Confirm the scan stops about 5 s after the loss, names the right side and camera, and the DB holds side-average rows up to the abort.
🤖 Generated with Claude Code
https://claude.ai/code/session_017sfVYYF9YDDyUtNHCR5abT
Generated by Claude Code