Skip to content

B2 · Manual mode — dual-camera + laser-preset browser + timelapse form#75

Closed
pskeshu wants to merge 11 commits into
feature/session-plan-linkingfrom
feature/manual-mode-dual-camera
Closed

B2 · Manual mode — dual-camera + laser-preset browser + timelapse form#75
pskeshu wants to merge 11 commits into
feature/session-plan-linkingfrom
feature/manual-mode-dual-camera

Conversation

@pskeshu

@pskeshu pskeshu commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Sub-project B2 (replaces #70, recovering CI). Laser-preset browser, dual-camera (side A/B) config, manual timelapse form. Includes post-testing fixes: single LED toggle + laser on/off, live-view memory hardening + side-B galvo/piezo, timelapse accordion, Gallery tab + volume-acquired toast. Whole-branch reviewed; ruff+format clean. Live dual-view/acquisition rig-deferred. Stacked on feature/session-plan-linking.

pskeshu added 11 commits June 29, 2026 08:18
Add POST /api/devices/laser/config proxy (require_control, 400 on
missing/empty config, mirrors /laser/off client+error pattern).
Add <select id="devices-laser-preset"> in the Illumination group;
populateLaserPresets() fetches the 7 presets on manual-view entry and
wires onchange → setLaserPreset(). Entry-safety I3 guard (setLaserOff)
fires before populate — preset selection is an explicit post-entry
override. CSS styles for ls-laser-row/label/select added to main.css.
7 TDD tests; node --check clean; Chrome-MCP harness verified.
Register DiSPIMCamera("HamCam2") as devices["camera_b"] defensively in
device_factory — only when HamCam2 is in core.getLoadedDevices(); single-
camera rigs skip and log a warning.

Add "side" ('A'|'B', default 'A') to _ls_params; _ensure_lightsheet_sequence_sync
selects camera/camera_b by side and restarts the sequence on side change
(mirrors the exposure-change restart path). Side B without camera_b falls
back to A. New GET /api/cameras endpoint (device-layer + UI proxy) lists
available camera roles. UI: #devices-ls-side select in the manual rail,
hidden on single-camera rigs, shown when GET /api/devices/cameras lists 'B'.
postLightsheetParams includes side on every call. populateCameraRoles()
called on manual-view entry alongside populateLaserPresets.

RIG-DEFERRED: live Multi-Camera dual view + dual-side acquisition.
TDD: 16 tests, all pass; node --check exit 0; no new suite failures.
Adds POST /api/devices/timelapse/start (require_control) that validates
params and calls orchestrator.start via server.agent_bridge.agent.timelapse_orchestrator.
Adds a collapsible Timelapse panel to #devices-view-manual with cadence,
stop condition, embryo IDs, monitoring mode, and volume geometry fields.
devices.js populates defaults from GET /api/devices/scan_geometry +
/api/devices/laser/configs on manual-view entry and POSTs on submit.
16 TDD tests; node --check passes; galvo/piezo motion is rig-deferred.
… app CSS)

Add 8 CSS classes to main.css that the timelapse panel (#devices-view-manual)
references but were never defined in the app: ls-collapsible-head/body/arrow,
ls-sub-row/label/section/section-label, ls-tl-status. Panel was rendering
unstyled in the real app; scratchpad harness had masked this with inline CSS.
Classes mirror the existing .ls-row/.ls-group/.ls-label pattern (same CSS
variables, spacing, dark instrument aesthetic). [hidden] fights prevented via
explicit [hidden]{display:none} scoped rules. Verified via Chrome MCP harness
loading the real main.css with no harness-local ls-* overrides.

Also: TODO comment in timelapse_start route noting UI path skips plan auto-linking.
…lvo/piezo

FIX 1 — buffer hardening:
- Add snap-loop mode (default): _grab_lightsheet_frame_sync uses
  snapImage()+getImage() per frame; no circular buffer, hard memory ceiling.
- Continuous mode (opt-in via _ls_params["mode"]="continuous"): cap buffer
  with setCircularBufferMemoryFootprint(256) before start; drain with
  clearCircularBuffer() after each getLastImage() peek.
- _stop_lightsheet_sequence_sync: always calls clearCircularBuffer() on every
  exit path (disconnect, error, pause, view-exit via finally block).

FIX 2 — side-B galvo/piezo:
- device_factory.py: defensively register scanner_b (Scanner:CD:33) and
  piezo_b (PiezoStage:Q:35) when present in getLoadedDevices(); skip+log
  on single-side rigs.
- _park_lightsheet_sync: select scanner_b/piezo_b for side B, fall back
  to A with warning when absent; single-camera rigs unaffected.

TDD: 12 tests red → green; 40 target tests pass; 0 new failures in full suite.
…ection states

Restructure the flat timelapse panel into 3 independently collapsible
accordion sections — Schedule (open by default), Targets, Volume geometry.

Active/inactive state:
- Each section header starts muted (hollow 5-px dot, dimmed label).
- Any user input/change event in a section flips it active: dot fills
  accent cyan, label turns accent, a one-line summary appears inline.
- Summaries: "60 s · 5 frames" / "all · Idle (plain imaging)" /
  "80 sl · 10 ms · ALL OFF".
- The outer Timelapse panel header shows an accent dot once any section
  is set; the Start Timelapse button turns accent-bordered when ready.
- Re-entering the manual view (initTlForm) resets all touched state and
  clears visual active state.

Payload unchanged: all field IDs preserved, POST body identical.
node --check: exit 0.
- Add Gallery to TABS constant (utils.js) and wire switchTab + hash
  routing in app.js; lazy-inits GalleryTab.init() on first visit
- Add Gallery nav button in the Library group (index.html) and the
  gallery-content tab panel with embryo/type filter selects
- Add GalleryTab controller in gallery.js: fetches /api/snapshots
  (which already includes volumes), sorts newest-first, renders a
  responsive thumbnail grid grouped by embryo/type filters; clicking
  opens existing Lightbox
- Add showGentlyToast() in gallery.js: fixed bottom-center toast with
  optional action button and auto-dismiss after 6 s
- Show "Volume acquired / View in Gallery" toast in devices.js after
  successful Snap Volume or Burst — no inline preview to keep manual
  mode uncluttered; "View in Gallery" calls switchTab(TABS.GALLERY)
- Add gallery tab + toast CSS to main.css (gallery-tab-container grid,
  gently-toast styles); reuses --accent/--bg-card/--border vars
@pskeshu

pskeshu commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by #72 — the temperature + Operations/tactics suite and the new Operate (bottom-cam→SPIM) surface are consolidated onto feature/temperature-operations-all (now targeting development). This increment's work is included there; its feature branch was deleted. Closing to keep a single main PR.

@pskeshu pskeshu closed this Jul 1, 2026
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