feat: opt-in large acquisition mode — pause on low disk space, operator pause - #648
Draft
hongquanli wants to merge 2 commits into
Draft
hongquanli wants to merge 2 commits into
hongquanli wants to merge 2 commits into
Conversation
…or pause First of three stacked PRs for large acquisitions (data offloaded to a NAS during the run; design in AI-docs Squid/to-do/2026-09-14-large-acquisitions-offload-and-pause-design.md). Strictly opt-in: with LARGE_ACQUISITION_MODE off (default) and no per-run opt-in, the acquisition loop, result draining, GUI, TCP status payload, watchdog states and on-disk layout are unchanged. The mode is enabled in Settings > Acquisition > Large Acquisitions, or for one run from the "Not Enough Disk Space" pre-flight dialog (whose Cancel path stays as today). When on: - PauseGate (control/core/pause_gate.py): single pause control point per run; holders "disk_space" and "operator"; paused while any holder is active; abort-aware wait. - DiskSpaceGuard (control/core/disk_space.py): at FOV and timepoint checkpoints requires reserve + queued save bytes + 2 FOVs of free space, holds the gate below that, releases with one FOV of hysteresis; settings read live; dev SIMULATED_DISK_CAPACITY_GB derives free space from the experiment folder size so tests never depend on the host disk. - Worker: checkpoints at the FOV-loop top (after the result drain, before the stage moves) and the timepoint-loop top; keeps heart-beating the watchdog with the new "paused" run status; a schedule anchor shifted by the paused time keeps the dt skip-catch-up loop from dropping timepoints after a long pause; mode-on runs drain all queued job results per FOV. - Watchdog: "paused" treated like "running" (crash/hang checks unchanged). - Controller: request_pause/request_resume/pause_state/disk_status; gate created only when the run's effective mode (per-run flag OR setting, resolved in build_params and recorded in acquisition.yaml) is on. - TCP/MCP: pause_acquisition, resume_acquisition; get_acquisition_status reports pause reasons/times and the disk block only for mode-on runs. - GUI: Pause/Resume button + status line on the multipoint widgets, shown only for mode-on runs; Preferences "Large Acquisitions" group; Dev tab simulated disk capacity; pre-flight decision function + dialog. - Slack: notify_acquisition_paused/resumed (SlackNotifications.NOTIFY_ON_PAUSE). - YAML: acquisition.large_acquisition_mode per-run flag (absent = unchanged). Tests: pause gate, disk guard, worker checkpoints (incl. mode-off no-op and drain policy), five end-to-end simulated runs (mode off, operator pause/resume with anchor shift, abort while paused, low-disk pause then resume when space frees, global setting), pre-flight decision, watchdog, breadcrumbs, Slack, server, YAML loader, acquisition settings, preferences. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7SDD9fYF47FUoL8M1dfvF
build_params now resolves the run's effective large acquisition mode from the controller's per-run flag; the laser-AF offset tests build params through a stub that bypasses __init__, so the stub needs the attribute. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7SDD9fYF47FUoL8M1dfvF
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.
Summary
First of three stacked PRs for large acquisitions (data offloaded to a NAS during the run; design: AI-docs
Squid/to-do/2026-09-14-large-acquisitions-offload-and-pause-design.md). This PR adds an opt-in "large acquisition mode" that pauses the acquisition instead of aborting when the save disk is nearly full, and gives the operator a Pause/Resume control through the same mechanism.Strictly opt-in — nothing changes with default settings. With
LARGE_ACQUISITION_MODE = False(the default) and no per-run opt-in, the acquisition loop, result draining, GUI, TCP status payload, watchdog states and on-disk layout are byte-for-byte today's. The mode is enabled either in Settings › Acquisition › Large Acquisitions, or for one run from the existing "Not Enough Disk Space" pre-flight dialog (which keeps its Cancel path as today).What it does when the mode is on
PauseGate(control/core/pause_gate.py): the single pause control point for a run. Holders by reason (disk_space,operator); paused while any holder is active, so an operator Resume cannot override a full disk and freed space cannot override an operator pause. Abort-aware wait (polls the abort flag, so app shutdown still joins in time).DiskSpaceGuard(control/core/disk_space.py): at every FOV and timepoint checkpoint, requiresreserve + bytes still queued for saving + 2 × one FOVof free space; holds the gate below that, releases with one FOV of hysteresis. Settings are read live so they can be changed mid-run.pausedrun status, re-checks the disk everyDISK_SPACE_POLL_INTERVAL_S, and refreshes the GUI.run.jsonstatuspausedis treated likerunning(dead pid → crash, stale heartbeat → hang), never a false hang.pause_acquisition,resume_acquisition, andget_acquisition_statusgainspause_reasons/paused_since/paused_total_s/disk{…}only for mode-on runs; Slack messages on pause and resume (SlackNotifications.NOTIFY_ON_PAUSE).acquisition.large_acquisition_modefrom the file (absent = unchanged).SIMULATED_DISK_CAPACITY_GB(Dev tab) computes free space as capacity minus the experiment folder size, so deleting files raises free space — this is how the tests and the GUI walkthrough exercise the disk path without filling a real disk.Settings (
[GENERAL])large_acquisition_modefalsedisk_space_reserve_gb10.0disk_space_poll_interval_s5.0simulated_disk_capacity_gb0.0Tests
New:
test_pause_gate.py(12),test_disk_space.py(25),test_MultiPointWorker_pause.py(14, incl. mode-off no-op and drain policy),test_MultiPointController_pause.py(5 end-to-end simulated runs: mode off, operator pause/resume with anchor shift, abort while paused, low-disk pause → resume when space frees, global setting),test_preflight_disk_check.py(10), plus additions to the watchdog, breadcrumb, Slack, server, YAML loader, acquisition-settings, preferences and_deftests. Every touched or new test module passes locally; a 322-test regression subset (engine, controller, settings, watchdog, Slack, server, backpressure) passes. CI (Linux) is the authoritative full-suite run: local macOS full-suite runs abort in a pre-existing memory-profiler test (test_widgets.py::TestMultiPointControllerMemoryMonitoring::test_ram_widget_connect_to_multipoint_monitor, the sampler spawns afootprintsubprocess from a background thread; passes alone, aborts in suite order, Linux never takes that path). Tracked as an AI-docs action item; not touched by this PR.Note for reviewers running locally: the end-to-end tests use the simulated disk so they never depend on the host's free space.
Out of scope / follow-ups
tools/upload_acquisition.pyNAS tool. PR 3: OME-TIFF per-timepoint split (+ ndviewer_light PR).SlackNotifications.NOTIFY_ON_PAUSEis code/INI only, not yet in the Slack settings widget.🤖 Generated with Claude Code
https://claude.ai/code/session_01F7SDD9fYF47FUoL8M1dfvF