Skip to content

Acquisition core refactor (Phase 1 of 4): single-loop acquisition, decoupled tracking, design for recording/focus/UI phases#1

Draft
Sebzdead wants to merge 13 commits into
masterfrom
refactor/acquisition-core-adr0001
Draft

Acquisition core refactor (Phase 1 of 4): single-loop acquisition, decoupled tracking, design for recording/focus/UI phases#1
Sebzdead wants to merge 13 commits into
masterfrom
refactor/acquisition-core-adr0001

Conversation

@Sebzdead

Copy link
Copy Markdown
Owner

Summary

Stability + UX refactor of the WormsPy microscope control software, driven by a grilling/code-review session against the published design. Phase 1 is implemented; Phases 2–4 are drafted as executable plans on this branch. Opened as a draft — Phase 1's hardware adapters and app.py integration are not yet verified on the rig, and recording/focus/manual are intentionally deferred to later phases.

Why

The original app.py ran each camera inside its own Flask MJPEG generator, so the two streams could drift without bound, the Zaber motors only existed while the left feed streamed (crashing move_to_center/autofocus/manual on browser disconnect), recorded playback speed relied on a hand-typed FPS constant, the stage-limit check was an always-true no-op, and "FocusLock" was a PID that couldn't hill-climb an unsigned sharpness metric. See CONTEXT.md and docs/adr/0001-0003 for the decisions.

What changed (Phase 1 — implemented, ADR 0001)

  • New hardware-independent WormSpy/backend/code/core/ package:
    • frames (thread-safe FrameHub), cameras + cameras_spinnaker, stage + stage_zaber, acquisition (single loop, shared monotonic frame_index), tracking (helpers + decoupled Tracker thread).
  • app.py rewired: acquisition owned at startup (not in HTTP handlers), Zaber connected once with graceful NullStage fallback, display served from the FrameHub. Eliminates loop drift and motor-on-disconnect.
  • Real stage-limit clamp (clamp_native) replacing the no-op or check.
  • 38 unit tests, all passing on dev with fakes (hardware libs lazily imported so the suite needs no camera/motor).

Design + plans included

  • CONTEXT.md — glossary (frame-sync, loop drift, Hold Focus).
  • docs/adr/0001-0003 — acquisition architecture, Hold Focus (why autofocus is impossible on this rig), recording integrity.
  • docs/superpowers/plans/ — four executable phase plans:
    • Phase 1 — acquisition core (implemented)
    • Phase 2 — recording integrity: CSV-as-spine, intra-frame codec, 16-bit TIFF, bounded queues
    • Phase 3 — restore tracking/manual + Hold Focus
    • Phase 4 — /status endpoint, UI reflects backend truth, error toasts, cleanup

Reviewer notes / known caveats

  • Accepted temporary regression: after Phase 1, the app does dual display + Center only. Recording is a no-op; toggle_af/toggle_manual return 500; auto-tracking does nothing — all restored in Phases 2–3. Documented in the Phase 1 plan + the Task 9 commit.
  • Unverified on hardware: the FLIR/Zaber adapters and the app.py integration need a rig check (plan steps 7.5 / 8.4 / 9.5). No cameras/motors in CI.
  • A dev-env note: while running the skimage-dependent tests, a local scipy was pip-upgraded outside the repo (not part of any commit).

Test plan

  • cd WormSpy/backend/code && python -m pytest → 38 passing (use PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 if a local napari pytest plugin is present)
  • Rig: live feed shows both cameras from the hub; browser refresh does not stop acquisition; "Center" moves the stage after a refresh
  • Rig: connect_stage/SpinnakerCamera smoke checks (plan steps 7.5, 8.4)

🤖 Generated with Claude Code

Sebzdead and others added 13 commits June 21, 2026 09:46
…lan)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the core acquisition loop (ADR 0001): reads both cameras
per tick, assigns a single contiguous frame_index, publishes to FrameHub
for display/tracking, and forwards AcquiredPairs to an optional sink.
A camera failure on one side yields None for that slot without breaking
the index sequence, enabling downstream drop detection via index gaps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements find_worm_cms (largest-blob centroid via skimage), simple_to_center
(pixel offset to mm move), and Tracker (injected compute callback wired to
stage.move_relative), keeping the tracking thread fully decoupled from the
AcquisitionEngine per ADR 0001.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements core/stage_zaber.py with ZaberStage (lazy zaber_motion imports,
opens serial connections once at construction) and connect_stage factory that
degrades to NullStage on any connection failure so the server boots without hardware.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the Camera ABC for FLIR/Spinnaker hardware via EasyPySpin.
EasyPySpin is imported lazily inside __init__ so the module is
parseable/importable without the SDK installed. The camera is configured
as the acquisition clock: fixed AcquisitionFrameRate and exposure with
auto-exposure/auto-gain disabled, matching ADR 0001 decision #2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…R 0001)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: beb73f15-1385-43f5-bfdd-1cf697c44bc7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/acquisition-core-adr0001

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.

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.

1 participant