Two independent fixes:
- GUI "beamline busy" indicators (TomoParamsWidget's banner,
FlomniWebpageGenerator's status page) purely wait for
progress["heartbeat"] to go stale (120s / 90s respectively) - there was
no explicit "scan finished" signal to react to instead, since
tomo_scan() wrote a heartbeat at the start of every projection but
never cleared it on exit. Wrapped the scan loop in try/finally so
heartbeat is cleared on every exit path (normal completion, exception,
or interrupt/abort), not just at the next scan's start - both
busy-detectors now see this on their very next poll instead of waiting
out the timeout. Live-verified: heartbeat is None immediately after
tomo_scan() returns.
- Found while investigating a related report ("angular step of the final
combined tomogram shown different between 180 and 360 mode, although
it has to be identical" + "GUI shows projection count doubling when
switching 180->360"): TomoParamsWidget's _compute_type1()/
_requested_to_stepsize() (tomo_params.py) and the generated status
webpage's calcProjections() JS (flomni_webpage_generator.py) are both
independent, un-synced duplicates of the exact old N=int(angle_range/
stepsize) formula fixed in flomni.py's own _tomo_type1_actual_grid()
two commits ago - they were never updated when that fix landed, so the
GUI and webpage kept reporting double the projection count for 360
mode. Fixed both to match flomni.py: N/step/total are always computed
against a fixed 180 degrees, independent of angle_range. Also fixed
the "angular step of the final (combined) tomogram" CLI/wizard lines
in flomni.py itself, which used tomo_angle_range/total_projections
(correct for 180 mode by coincidence, wrong for 360 mode - the true
combined resolution is always 180/total_projections, identical
between modes). Added a regression test asserting the widget's
formulas match flomni.py's for both modes, to catch this exact kind of
drift if it recurs. Live-verified against the running sim: both modes
now report identical total_projections and combined-tomogram step for
the same stepsize.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Drop the "effective (mod-180) reconstruction resolution" line added in
the previous commit -- the projection count already says everything
that matters; a derived resolution figure just adds noise. Replaced
with a plain "There are no duplicate angles." note in both display
spots (tomo_parameters() and the interactive wizard).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous commit's 360-degree phase assignment gave the low and high
halves the SAME phase per pair (1,2)/(3,4)/(5,6)/(7,8), so each pair
concatenated into one continuous evenly-spaced range -- but that means
every low-half angle is exactly 180deg from a high-half angle, which is
exactly the redundant-measurement bug this was supposed to fix, just
reintroduced between subtomos instead of within one.
Corrected: the low half (subtomos 1,4,5,8) now uses the even eighths of
the original 8-way phase_eighths table, and the high half (2,3,6,7) uses
the odd eighths -- complementary, not shared. Folded mod 180, the two
halves interleave into exactly the same 8-way, step/8 grid that 180-mode
itself produces: every position is measured exactly once, using its full
0-360 physical range, with zero redundant measurements anywhere. Total
projection count is unchanged (still identical to 180 mode for a given
tomo_angle_stepsize).
Verified: pure-math unit tests confirm the 360-mode combined set, folded
mod 180, is an exact set match (same count, no repeated residue) against
180-mode's own set. Live-verified against the running flomni sim: real
motor motion for both modes, folding the observed 360-mode angles mod
180 exactly reproduces the observed 180-mode angles.
Also updated the two "angular step of the final combined tomogram"
CLI/parameter-wizard print statements to additionally report the
effective mod-180 reconstruction resolution for 360 mode, since it's
now finer than the raw per-half spacing they already printed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three independent fixes:
- sub_tomo_scan(): in 360-degree tomo_type-1 mode, every sub-tomogram used
to sweep the full 0-360 range, so each one's own fine grid contained
angle pairs exactly 180deg apart -- redundant tomographic information.
Sub-tomograms now each cover a 180deg span, split into low/high halves
by subtomo_number % 4 with a bit-reversal-of-4 phase table, so adjacent
pairs (1,2)/(3,4)/(5,6)/(7,8), either quartet, and all 8 combined each
independently form a complete, evenly-spaced 360deg tomogram at
successively finer spacing. Total projection count for a given
tomo_angle_stepsize is now identical between 180 and 360 mode (same N,
no longer doubled). Updated the 4 other consumers of the old N/step
formula (zero-deg reference gating, _tomo_type1_actual_grid, the
parameter wizard, the PDF report) to match. 180-degree mode is
unchanged. Live-verified against the running flomni sim: real motor
motion traces the expected boustrophedon path with no duplicate or
180deg-apart angles.
- Sample storage: added an owner field, packed into the same EPICS DESC
field as the sample name ("name | owner", via new sample_desc_codec.py)
since there's no separate PV for it. Wired through
FlomniSampleStorage, the CLI (flomni_modify_storage_non_interactive,
ftransfer_modify_storage), the two transfer routines that forward a
raw DESC value across a gripper move (now unpacked/repacked so owner
survives the move instead of being dropped or double-packed), and
SampleStorageWidget. Scoped to flomni only this session; OMNY's
storage/transfer mixin is unchanged.
- ConsoleButtonsWidget's ABORT button used to send SIGINT then, 500ms
later, a stop_devices() broadcast to ALL devices with no stop_id --
an un-suppressed error from that broadcast landing on a queue-tracked
instruction could kill the scan worker thread outright, requiring a
full BEC restart. Replaced with: queue.request_scan_abortion() (safe
no-op if idle, but registers a stop_id so expected errors are
suppressed), then SIGINT, then a direct, immediate Galil hard stop via
new GalilController.hard_abort_and_restore_positioning_mode() -- the
same method ftransfer_abort() now delegates to, so the CLI and GUI
paths can't drift apart again. SIGINT is sent before the hard stop:
live testing showed that if the hard stop's mntprgs-clearing side
effect lands first, a same-session polling loop can mistake it for
normal completion and fall through into ensure_gripper_up(), which
must not happen mid-transfer -- sending SIGINT first (near-instant)
gives that loop's own KeyboardInterrupt handler a head start before
the hard stop's own multi-step sequence completes. The button is
labeled per beamline (e.g. "Flomni Motion Stop") and disabled rather
than silently inert when no hard-stop device is configured/enabled.
Scoped to flomni only this session (OMNY/LamNI wiring deferred).
Live-verified against the running sim, including the exact
stop-lands-mid-queued-instruction scenario that previously crashed
the scan worker.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rewrites both status headers (plan doc + GUI test checklist) now that
every item (1-55) has been clicked through live and passes. Fixes two
remaining stale "not yet click-tested" references in section 1 (items
9/10) left over from before sections 6c/6d were tested. No open
checklist items remain; section 7's known limitations are scope cuts,
not bugs, and are the documented starting point for future work.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GUI test checklist: sections 6c/6d/6e (items 37-52) all passed
real-session testing, including the stale-"running" override fixing
an actual stuck job found live -- status line and section 1 items
updated. New section 6f (items 53-55) for the "Add current params to
queue" mid-edit warning, plus section 1 item 13.
Plan doc section 6.1: records the second live-vs-unsaved-edit
confusion (a second window, not just the panel's own button) and why
it's a warning, not a block, unlike Submit.
User manual: new paragraph distinguishing the params panel's "Add to
queue" from the queue window's "Add current params to queue", and
when the warning fires.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Real footgun: edit a value in the params panel but don't click its own
"Add to queue" (or Submit) -- then use the *separate* Queue control
window's "Add current params to queue" button, expecting it to queue
what was just typed. It doesn't: that button reads live global vars
(TOMO_QUEUE_COMMAND_JOBS_PLAN.md section 6.1's rule -- only the panel's
own Add-to-queue reads the form), so it silently queues the stale,
pre-edit values with no indication anything's off. Now checks the
parent params panel's edit-mode state and, if an edit is in progress,
warns explicitly and points at the correct button before proceeding
-- a warning rather than a hard block, since queuing the live params
alongside an unrelated in-progress edit elsewhere is still a
legitimate thing to want.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
User manual: notes that declining the fermat-scan confirmation now
fails the job instead of silently skipping a projection, plus how to
recover a job stuck at "running" (tomo_queue_delete has no status
guard; update_by_id to fix the status directly; the GUI's staleness
override).
AI_docs: plan section 3.4 records the second real incident (declined
confirmation silently returning) and its fix; section 6.3 records the
stale-"running" GUI guard fix. GUI test checklist gets section 6e
(test steps 48-52) and section 1 item 12, status line updated to flag
6c/6d/6e as not yet clicked through.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Real incident: a job got stuck at status "running" after its process
died mid-scan (Ctrl-C during a repeated confirmation-prompt loop, a
separate bug fixed alongside this one) with no live process behind it
-- and the GUI's delete/clear guard treated "running" as an absolute
block, with no way to escape from the GUI at all (the CLI's
tomo_queue_delete() has no such guard and already worked, but that's
not obvious/discoverable from the GUI).
Adds a staleness check using the same tomo_progress heartbeat signal
TomoParamsWidget._is_tomo_running() already uses: if a "running" job's
heartbeat is missing or older than 120s, it's very likely orphaned,
not actively executing. Delete/clear now offer an explicit
confirmation ("looks stale -- proceed anyway?") in that case instead
of an unconditional refusal; a genuinely fresh heartbeat still blocks
outright as before.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
tomo_scan_projection() always runs a Fermat scan; when
single_point_instead_of_fermat_scan is on and _internal isn't passed,
it asks "Run a fermat scan anyway?" and, on "no", used to just print
"Aborted." and return -- no exception. Real incident: a custom
at_each_angle hook called this without _internal=True, got asked at
every projection angle, declining silently skipped each one, and the
eventual Ctrl-C (KeyboardInterrupt, which tomo_queue_execute()'s
`except Exception` does not catch) left the job stuck at status
"running" forever with no live process behind it. Declining now raises
FlomniError, which (with the earlier scan_repeat exc_handler fix)
isn't retried and correctly fails the job, marking it "incomplete" and
pausing the queue -- the crash-resume contract tomo_queue_execute()
already documents, instead of silent data loss followed by an
untraceable stuck state. Sim-verified: direct decline raises with a
clear message; through the queue via a hook missing _internal=True,
the job ends up "incomplete", not stuck "running" or silently "done".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
User manual: new paragraph in the queueing section explaining the
button and that Submit/"Add to queue" still gate the actual write.
AI_docs: plan section 6.8 records the design (not a third write path,
confirm-before-discard, button-eligibility gating) and the
_enter_edit_mode_with() refactor; GUI test checklist gets section 6d
(test steps 42-47) and a new section 1 item 11, with the status line
updated to flag 6c/6d as not yet clicked through.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Select a single tomo job (command jobs have no params, so they're
excluded) in the queue dialog and click "Load into editor" to enter
edit mode on the params panel populated from that job's saved
snapshot instead of the live backend. Reuses the existing edit-mode
machinery (_enter_edit_mode_with(), factored out of enter_edit_mode())
so the operator reviews/tweaks and then uses the normal Submit or "Add
to queue" paths -- no new write path, nothing touches live params or
the queue itself just by loading. If an edit is already in progress,
confirms before discarding it. The button is disabled outside a
single-tomo-job selection and during sort mode, and re-evaluated on
selection change and on every table refresh.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
User manual: notes that the hook (name + source, when registered)
appears in tomo_parameters(), the scilog entry, the PDF report, and
the progress-ring label, and clarifies that unregistering a hook does
not clear at_each_angle_hook -- confirmed expected behavior.
AI_docs test checklist: records that sections 3-6b passed real-session
testing (updating the stale "not yet clicked through" status), adds
section 6c (test steps 37-41) for the fixes found during that testing,
and updates section 1's summary (new item 10).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Qt's own vertical header (1-based) sat to the left of the dialog's own
0-based "#" column (which matches the CLI's indices -- tomo_queue_show(),
tomo_queue_delete(), tomo_queue_move()) -- two differently-based
indices next to each other read as confusing/wrong. Hides Qt's
gutter, keeping only the one that actually means something.
Also shows a tomo job's active at_each_angle_hook in the Details
column (e.g. "hook: name" or "hook: name (not registered)", checked
against the published tomo_at_each_angle_hooks list) -- previously
Details was always blank for tomo jobs, so a hook-driven queued job
looked identical to a plain one without expanding the row tooltip.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
flomnigui_show_progress()'s center label now includes a "Hook: name"
line when at_each_angle_hook is set, same condition/wording as
tomo_parameters()'s CLI note -- an operator watching the progress GUI
during an unattended queue run can tell a hook is active without
switching to a CLI session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds _describe_active_hook() (flags a name that isn't currently
registered, e.g. after unregister_at_each_angle_hook() while
at_each_angle_hook still references it -- confirmed this is expected:
unregistering only removes it from the runtime dict, the property
itself is untouched) and _active_hook_source() (inspect.getsource(),
None if unavailable). Wired into:
- tomo_parameters()'s printed hook line, now flags "NOT registered"
- the end-of-scan scilog entry (name in the printed/scilog text,
source code appended to the scilog text only, not the console)
- write_pdf_report() (name as a report line, source appended to both
the PDF file and the scilog entry it sends)
So a hook-driven measurement's actual behavior is part of the
permanent record, not just inferable from a name that might not even
resolve anymore. Sim-verified all six states (unset, registered,
unregistered) for both helpers.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Previously the tomo_acquire_at_angle()-vs-tomo_scan_projection()
reminder only printed when tomo_parameters() happened to be called
afterward -- easy to miss if you just flip
single_point_instead_of_fermat_scan directly. Its setter now prints
the same note immediately when the value transitions False->True.
Gated on the transition (checked via the property's own getter before
the write) so restoring an already-True value across consecutive
queued jobs doesn't reprint it every time -- sim-verified: silent on
False->False, True->True (repeat), and True->False; prints exactly
once per False->True flip.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two additions to the at_each_angle hook writeup: registering a hook
stores the function object at that moment, not a live link to its
name, so editing the function later requires calling
register_at_each_angle_hook() again -- redefining it alone does
nothing. And tomo_scan_projection()/tomo_acquire_at_angle() are not
interchangeable (Fermat vs single-point); the example hook now calls
tomo_scan_projection() plainly (no more confusing _internal=True in
user-facing code) with a clear note on when to use the other one
instead, matching the new CLI/GUI warnings.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a wrapped, styled warning label under "Random shift max" in the
params panel, shown whenever "Single-point scan" is checked and hidden
otherwise -- same reminder tomo_parameters() now prints on the CLI
side: use tomo_acquire_at_angle(angle), not tomo_scan_projection(angle),
when single-point mode is on. Toggled from the existing
_on_single_point_changed() handler, so it updates on both a live edit
and every params-panel refresh (the widget's checkbox state already
drives this handler either way).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
tomo_scan_projection() always runs a Fermat scan and tomo_acquire_at_angle()
always runs a single-point acquisition -- they aren't interchangeable, and
calling tomo_scan_projection() directly while single_point_instead_of_fermat_scan
is on triggers an interactive confirmation prompt that would hang an
unattended queue run. tomo_parameters() now prints a reminder of which one
to call (including inside a custom at_each_angle hook) whenever single-point
mode is enabled, surfacing this proactively instead of only at the point of
the runtime prompt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
User manual: explains the new "At-each-angle hook" dropdown and its
"None (default)"/"(not registered in this session)" states, plus that
an active hook shows on the status webpage.
AI_docs: updates section 3.4 (GUI widget added, webpage display
added, extended sim-validation note for the global-var publishing and
webpage payload) and the plan's top status line. GUI test checklist
gets a new section 6b (test steps 28-35: dropdown population/refresh,
Submit vs Add-to-queue behaviour, the "not registered" label, and the
webpage's queue-detail + Current-measurement rendering) and an updated
section 1 item 9 + top status line noting none of this is click-tested
yet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TQ_PARAM_DISPLAY (the queue job detail table) and _CURRENT_PARAM_KEYS
(the live 'Current measurement' section) are both hardcoded mirrors of
which tomo params to render -- at_each_angle_hook wasn't in either, so
a hook-driven job wouldn't crash the page but also wouldn't show that
anything non-standard was running. Adds at_each_angle_hook to
_CURRENT_PARAM_KEYS (Python side, gates inclusion in the JSON payload
on the value actually being set) and a conditional row in
buildParamRows() (JS side, only rendered when the value is present --
deliberately left out of TQ_PARAM_DISPLAY so a normal job doesn't grow
a "-" row nobody needs). Sim-validated: current_params includes
at_each_angle_hook when set and omits it when not.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A new "At-each-angle hook" combo box in the params panel, built from
the live tomo_at_each_angle_hooks global var (no hardcoded mirror of
registered names). Rebuilt on every populate (i.e. every 2s poll while
not editing, and on entering edit mode), so hooks registered/
unregistered from the CLI while the dialog is open show up
automatically. The currently-set hook is always shown even if it
isn't in the published list (e.g. registered in a different or
since-restarted session), labeled accordingly, so it's never silently
hidden. Special-cased in _populate_fields()/_read_fields() like
tomo_type -- the generic QComboBox handling assumes integer data,
which doesn't fit a hook name string or None. This also makes the
manual params["at_each_angle_hook"] pass-through in
add_edited_to_queue() (added when there was no widget for this field)
redundant; removed, since _read_fields() now supplies it directly
from the dropdown.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds tomo_at_each_angle_hooks (a plain list of names), republished on
every register_at_each_angle_hook()/unregister_at_each_angle_hook()
call and reset to empty in __init__ (hooks are session-only, so a
stale list from a prior session must not linger). Same reasoning as
tomo_queue_actions for the command-job registry: a GUI (or any other
client) can build a dropdown from this directly, no hardcoded mirror.
Sim-validated: register/unregister updates the published list
correctly and the webpage generator's current_params payload picks up
an active hook.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Notes what was actually exercised against the live sim (registration/
list/unregister, per-job param snapshot including the reset-to-None
case, the hook firing during a real tomo_queue_execute() run, and the
unregistered-hook incomplete/resume path) and the scan_repeat retry
bug found and fixed along the way.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found by testing the new at_each_angle_hook feature against the live
sim: _tomo_scan_at_angle is wrapped in @scan_repeat(max_repeats=10,
default=True), which retries *any* exception, including a FlomniError
raised for an unregistered hook -- a condition that will never
succeed by retrying. The operator saw 10 silent retries before a
generic "TooManyScanRestarts", with the actual, actionable error
message buried in __cause__ instead of reaching
tomo_queue_execute()'s printed failure message. Adds an exc_handler
that skips the retry for any FlomniError (a definite, non-transient
failure by convention throughout this file), letting the real message
surface immediately. General fix, not narrowly scoped to hooks --
every other FlomniError this method could raise gets the same benefit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a "Custom behavior at each projection angle" subsection to the
Tomography section of the flOMNI user manual (docs/user/ptychography/flomni.md):
what a hook function looks like, a polarizer-modulation example, and
how to register/activate/deactivate/list/unregister one, with an
explicit warning about resetting at_each_angle_hook before queuing a
normal follow-up job. Also links it from the quick-start queueing
section. AI_docs/TOMO_QUEUE_COMMAND_JOBS_PLAN.md gets a new section
3.4 recording the design decision (session-only registry, not a
hardcoded one; a new _TOMO_SCAN_PARAM_NAMES entry, not a new job kind;
the GUI param-snapshot leak found and fixed while wiring this up; no
GUI widget yet).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TomoParamsWidget/TomoQueueDialog mirror flomni.py's snapshotted-param
list by hand (QUEUE_PARAM_NAMES/DEFAULTS) and it hadn't picked up the
new at_each_angle_hook parameter. Left as-is, a GUI-added job's params
dict would have no at_each_angle_hook key at all, so the executor's
per-job setattr loop would never touch it -- silently leaving whatever
hook the *previous* queued job had active in place for a job that
never asked for one. Adds the key to QUEUE_PARAM_NAMES/DEFAULTS (fixes
"Add current params to queue", which reads live global vars) and has
add_edited_to_queue() explicitly pass the live value through (there's
no editable widget for it yet, so it can't come from the form fields).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Flomni._at_each_angle() already had an escape hatch for custom
per-projection behaviour (a flomni_at_each_angle function checked via
builtins), but it's invisible to the tomo queue -- not a snapshotted
parameter, so a queued job had no way to select it. Adds a
session-only registry (register_at_each_angle_hook/unregister/list)
populated from the IPython session, plus a new snapshotted
at_each_angle_hook parameter so a tomo-queue job can carry its own
hook selection through add/execute/resume like any other tomo param.
Curated means the queue only ever stores a hook *name*, never code --
_at_each_angle() raises a clear error rather than silently falling
back to default behaviour if a job's named hook isn't registered in
the session running the queue (hooks don't survive a kernel restart).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Updates plan section 6.4 and the GUI test checklist (item 5, new test
step 23a) for narrowing sort-mode/tomo_queue_move() reordering to
pending-only, with running and incomplete both acting as a floor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sort mode and tomo_queue_move() let a pending job be dragged/moved
visually ahead of an incomplete one. Harmless to actual run order --
tomo_queue_execute()'s pick-next loop always resumes the first
running/incomplete job it finds regardless of list position -- but
confusing to read, since list order looked like run order and wasn't.
Narrows the GUI's _MOVABLE_STATUSES to ("pending",) and the CLI's
floor check to the highest index among all running/incomplete jobs,
not just the running one. Found while testing the reorder feature
against a real interrupted scan.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
feye_out() unconditionally moved dev.fttrx1 out of the way, raising
an unhandled DeviceConfigError on any session (e.g. sim) where that
device isn't configured, after already having closed the shutter and
moved other optics. Now checks "fttrx1" in dev first (same pattern
already used for rtx elsewhere in this file) and, if absent, asks via
OMNYTools.yesno() whether to continue without moving it instead of
crashing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Updates plan section 6.1 (marks the two-write-paths rule as actually
implemented now) and the GUI test checklist (section 1 item 8, and
flags section 3 item 4 as needing re-testing since its old passing
result was against the since-replaced soft-warn Submit behaviour).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Previously the only way to queue a new tomo parameter set was to
Submit it live first (writing the running scan's global vars) or use
the separate queue dialog's "Add current params to queue", which
snapshots the live vars and silently ignores any unsubmitted edit --
so there was no way to prepare a new parameter set in the GUI while a
scan was running without perturbing it.
Edit mode now shows an "Add to queue" button that packages the
current (possibly unsubmitted) form fields as a new pending tomo job,
writing only tomo_queue -- never the live param vars -- so it's always
allowed. Submit becomes a real hard block while the beamline is busy
instead of a Yes/Cancel confirmation, now that there's somewhere else
to put an in-progress edit (TOMO_QUEUE_COMMAND_JOBS_PLAN.md section
6.1's two-write-paths rule, finally enforced as written).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
tomo_scan() called bec.messaging.scilog directly with no guard, so a
session without scilog enabled raised RuntimeError from inside
tomo_queue_execute() -- marking the job "incomplete" and pausing the
whole queue over a logging side effect unrelated to whether the
tomogram succeeded. Adds _scilog_write(), mirroring the enabled-check
already used for the PDF report entry, and routes the three unguarded
call sites (tomo_scan, scilog_last_ptycho_scans, the alignment-scan
entry) through it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Updates plan section 6.7 and the GUI test checklist (section 1 item 7,
new test step 27) for the Label/Details stretch columns and the
dropped Added-at column.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
All 9 columns previously shared equal Stretch width, squeezing the two
free-text columns (Label, command-step Details) down to the same width
as short fixed-content ones like Type or Exp (s). Short columns now
auto-fit their content; Label and Details stretch to fill what's left
and left-align instead of centering. Also drops the Added-at column --
that value is still available via the row tooltip added earlier.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Previously start() did nothing at all when the BEC account didn't
match the system user (e.g. a dev/sim session run under a different
account) -- now it starts a status page on a separate fallback port
(default 8081) for local debugging, while still skipping everything
that assumes the active account is the "real" one for this host: the
cross-session singleton lock, remote upload, and session.htpasswd
changes. Not yet tested against a live/sim session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Updates the command-jobs plan (status line, section 6.4's
implementation note, new section 6.7, commit breakdown) and the GUI
test checklist (section 1's what-was-built list, a new section 6a
with test steps 19-26, and the corrected known-limitations entry) to
reflect tomo_queue_move and the sort-mode/tooltip GUI work. Neither
has been validated against a live session yet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a checkable "Sort queue..." mode: other queue-mutating buttons
disable, the table goes single-select, and Move up/down swap the
selected row with an adjacent one -- but only between two
pending/incomplete rows, so a running or done neighbour blocks the
swap (gets the running-job floor and done-row pinning from
TOMO_QUEUE_COMMAND_JOBS_PLAN.md sections 6.3/6.4 without drag-and-drop
math). Also gives every row a full-detail tooltip (label, status,
steps/params, added-at) so hovering surfaces what the Label/Details
columns truncate at typical dialog widths.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lets a queued job be moved to a new position via the CLI, refusing to
touch a running/done job and refusing any move that would displace the
currently running job (a hard floor, per the mid-run-reorder design in
AI_docs/TOMO_QUEUE_COMMAND_JOBS_PLAN.md section 6.4).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TOMO_QUEUE_GUI_TESTING.md is the manual verification plan for the new
command-job builder: baseline regression checks, the builder itself,
the status-keyed delete/clear guards, and an end-to-end mixed-queue
run, plus this iteration's known gaps (no mid-run reorder, one device
per move step, no live limit validation). Nobody has clicked through
the actual widget against a live session yet - it's built and
smoke-tested at the logic level only, not GUI-validated.
Section 8 covers continuing this work after leaving the current coding
session: a Claude Project with AI_docs/ attached as project knowledge
is for planning/discussion, a Claude Code session attached to this
repo is what's actually needed to keep editing tomo_params.py - and a
ready-to-paste prompt for starting that next session.
Also updates TOMO_QUEUE_COMMAND_JOBS_PLAN.md's status line: Step 3 has
a first iteration built, not the "not started" it said before.
First iteration of Step 3 (TOMO_QUEUE_COMMAND_JOBS_PLAN.md section 6)
on top of the existing TomoParamsWidget/TomoQueueDialog baseline:
- CommandJobBuilderDialog: a structured step-by-step form (action
dropdown, per-action fields) built entirely from the live
tomo_queue_actions global var, so it tracks flomni.py's action
registry and device allow-list with no hardcoded mirror. Produces a
job dict identical to what tomo_queue_add_command() writes.
- TomoQueueDialog's queue table gained a "Details" column and renders
command jobs (CMD + step summary + idempotency marker) the same way
flomni.tomo_queue_show() does.
- "Add current params to queue" now stamps kind="tomo" and a fresh id
on the job, matching the CLI's own self-describing format.
- Delete/clear now refuse when a "running" job is involved instead of
silently allowing it (plan section 6.3).
Mid-run drag-reorder of queue jobs (plan section 6.4), multi-device
single move steps, and live device-limit validation are deliberately
not in this iteration - see TOMO_QUEUE_GUI_TESTING.md.
Generalizes the patterns and gotchas learned building the flOMNI
tomo-queue e2e suite (main-thread-only live-update machinery, the
builtins/reload import-order bootstrap, neutralizing real side
effects, the demo-config trap in pytest_bec_e2e's shipped fixtures,
the omny_e2e_tests/ folder convention, and the Redis
cross-test-contamination fixture bug) into a standalone reference for
adding e2e coverage to any plugin in this repo, not just flomni.
Intended to be added to a Claude Project alongside this repo so a
coding assistant finds it and follows these patterns instead of
re-deriving them from scratch.
Adds a "Command jobs" reference section covering
tomo_queue_add_command(), the move/optimize_idgap action registry, the
mokev/idgap device allow-list, tomo_queue_show() output format, and
the idempotent-vs-prompt crash-resume behaviour, plus a pointer to it
from the quick-start queueing section.
TOMO_QUEUE_COMMAND_JOBS_PLAN.md and TOMO_QUEUE_TESTING.md were written
while command jobs (Step 2) were still in progress; update both now
that it's implemented and sim-tested (16/16 e2e tests passing,
including the 6 new command-job ones - see TOMO_QUEUE_TESTING.md
section 4D for what each covers), and record the Redis test-state
contamination gotcha found while testing it.
Also fixes the harness-script path references in SIMULATED_ENDSTATIONS.md
and the two scripts' own docstrings, stale after tests/e2e and
tests/sim_*_harness.py moved to omny_e2e_tests/ (in the previous
commit) - kept out of tests/ specifically so gitea's CI, which runs
pytest against ./csaxs_bec/tests/ unconditionally, doesn't sweep up
tests that need a live sim + Redis.