Commit Graph
835 Commits
Author SHA1 Message Date
Mirko Holler 30b5119a2f feat(flomni): add command jobs to the tomo queue
Type-tags queue jobs with a "kind" field (back-compat: missing kind
defaults to "tomo"), and adds "command" jobs that reconfigure the
beamline (currently: absolute moves on an allow-listed device, plus an
optimize_idgap stub) between tomograms instead of running a scan.

Command jobs are added via tomo_queue_add_command() against a curated
named-action registry (_TOMO_QUEUE_ACTIONS) - not arbitrary code - so
every queued action is auditable and safe to persist across a kernel
restart. Validation is two-layer: schema checks at add time, and each
action re-checks its own arguments against live hardware at execution
time, which is the actual safety boundary. Crash-resume for a command
job re-runs the whole step sequence from the top if every step is
idempotent, or prompts the operator otherwise.

flomni_webpage_generator.py is guarded against command jobs, which
have no "params" key.

See AI_docs/TOMO_QUEUE_COMMAND_JOBS_PLAN.md for the full design.
2026-07-13 09:05:29 +02:00
Mirko HollerandClaude Sonnet 5 ff50711dc9 test(flomni): add tomo-queue e2e harness against the simulated flOMNI
Sections A-C of TOMO_QUEUE_TESTING.md's checklist, run against a live sim
session: params-restored-per-job, legacy queue migration, empty/all-done
no-ops, start_index semantics, exception/SIGKILL crash-resume, resume-
before-fresh ordering, and concurrent queue edits from a second client.
All 10 tests pass together with no cross-test interference.

The flomni_sim fixture and _bootstrap.py factor out what it takes to
construct a live Flomni against the sim (builtins/reload bootstrap,
side-effect neutralization, RT-feedback/fsamx setup) for reuse by both
in-process tests and the SIGKILL-based subprocess tests.

Also adds the AI_docs/ handoff docs (testing checklist results, command-
jobs plan with the action-registry decisions) that this work updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 23:25:33 +02:00
Mirko HollerandClaude Sonnet 5 cf44d09a12 refactor(flomni): rename _TOMO_QUEUE_PARAM_NAMES to _TOMO_SCAN_PARAM_NAMES
The constant lists live scan properties snapshotted/restored by the tomo
queue, not queue state itself -- the old name was misleading. Confirmed
with Mirko. Updates the matching comment in tomo_params.py, which mirrors
this tuple as QUEUE_PARAM_NAMES (unchanged name, own mirror constant).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 23:24:42 +02:00
Mirko Holler afb81f247c first iteration
CI for csaxs_bec / test (push) Successful in 1m35s
2026-07-12 23:16:18 +02:00
x01dc 3dbd9313ed test(lamni): stub xeyegui instead of the read-only gui property
CI for csaxs_bec / test (pull_request) Successful in 1m31s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 1m32s
XrayEyeAlign.gui is a property delegating to lamni.xeyegui and has no
setter. Mock lamni.xeyegui in the update_frame tests; drop the redundant
alignment_images reset, which __init__ already handles.
2026-07-12 18:38:44 +02:00
x01dc 69b89fcd85 test(lamni): update x-ray eye align tests to post-EPICS interface
CI for csaxs_bec / test (push) Failing after 1m26s
CI for csaxs_bec / test (pull_request) Failing after 1m24s
Drop test_save_frame (method no longer exists). Rewrite test_update_frame
against dev.fsh/dev.cam_xeye instead of the removed LamNI.alignment module.
Patch the module-level umv in test_tomo_rotate rather than builtins.
2026-07-12 18:33:13 +02:00
x01dc 6d29e05087 fix(lamni): tolerate clients without a gui attribute in set_client
CI for csaxs_bec / test (push) Failing after 1m25s
CI for csaxs_bec / test (pull_request) Failing after 1m29s
LamNI.__init__ calls set_client(), which unconditionally accessed
client.gui. This broke test_x_ray_eye_align.py under ClientMock, which
has no gui attribute. Use getattr with a None default; behaviour on a
real BEC client is unchanged.
2026-07-12 18:23:13 +02:00
x01dc b76593e4ff fix(lamni): correct X-ray eye alignment DAP forwarding, offset reference point, and pixel calibration
CI for csaxs_bec / test (push) Failing after 1m36s
CI for csaxs_bec / test (pull_request) Failing after 1m23s
- Enable DAP fit parameter forwarding (self.gui.set_dap_params_forwarding),
  never called before -- without it the dap_params_update signal was never
  connected, so no fit data could ever reach fit_params_x/y regardless of
  how long anything waited. Disabled again once the fit is read (or on
  abort, via the existing KeyboardInterrupt cleanup in lamni.py).
- Replace the fixed 5s sleep before reading the DAP fit with
  _wait_for_dap_fit(), which polls fit_params_x/y for the real converged
  shape (up to 30s) instead of guessing a duration.
- Fix write_output()'s offset reference: was using one of the 8 angle
  points as its own zero-reference (first via an off-by-one at k=1, then
  at k=2), which forces that point's offset to exactly 0 by construction
  and biases the fit. Now references alignment_values[0] (the FZP centre,
  the only angle-independent point in the procedure), matching both
  FlOMNI's own reference point and the original SPEC macro
  (l_xrayeyealign.mac) exactly, including the asymmetric x/y sign
  convention.
- pixel_calibration is now a property reading the cam_xeye device's
  pixel_calibration user parameter, matching FlOMNI's camera-sourced
  calibration, with the old hardcoded constant kept only as a fallback
  (PIXEL_CALIBRATION_DEFAULT) -- by design, to match FlOMNI's own pattern.
- lamni_optics_mixin.py: lfzp_in() and loptics_out() now print before/after
  re-establishing interferometer feedback, previously silent.
- device_configs: add the missing userParameter.pixel_calibration to
  cam_xeye in ptycho_lamni.yaml and simulated_omny/simulated_lamni.yaml
  (present on the FlOMNI side already; this is what the pixel_calibration
  property above actually reads at runtime).
2026-07-12 18:02:10 +02:00
x01dc 48f1ebb95f check list, to remove later
CI for csaxs_bec / test (push) Failing after 1m24s
2026-07-12 08:07:50 +02:00
x01dc 8b4018359c feat(lamni): align gui_tools with flomni's current parity fixes
- Add lamnigui_stop_gui(), matching flomnigui_stop_gui()
- lamnigui_show_xeyealign(): check/enable cam_xeye live mode on every
  call, not just when the xeyegui widget is first created -- matches
  flomnigui_show_xeyealign()
- lamnigui_idle(): refresh the splash text on every call rather than
  only at first creation -- matches flomnigui_idle()
2026-07-12 08:07:50 +02:00
x01dc e3ff5ffd48 feat(lamni): persist tomo scan progress via BEC global var
Replace the plain self.progress dict with a global-var-backed
_ProgressProxy (same shape and key as FlOMNI's), so progress survives
a client restart. Adds heartbeat + idle-time-gap detection in
_tomo_scan_at_angle, ETA computation in _print_progress, and
tomo_start_time/reset handling in tomo_scan(). Adds
tomo_progress_reset() and _format_duration() for parity.

gui_tools.py's progress display already read these fields; no GUI
change needed.

feat(lamni): persist alignment lookup-table corrections

corr_pos_x/corr_pos_y/corr_angle (+ _2 iteration) become
global-var-backed properties (lamni_corr_pos_x, lamni_corr_pos_y,
lamni_corr_angle, ..._2) instead of plain instance lists that reset
on every client restart. No default correction file exists for
LamNI, so reset_correction()/reset_correction_2() just clear to
empty.

feat(lamni): add frames_per_trigger (burst mode) support

LamNIFermatScan already inherits from AsyncFlyScanBase and already
accepts/passes through frames_per_trigger -- only the client-side
plumbing was missing. Adds a validated, global-var-backed
frames_per_trigger property, threads it into
tomo_scan_projection()'s lamni_fermat_scan() call, and exposes it in
tomo_parameters(). Also folds it into the idle-time cadence estimate
alongside stitch tile count.

feat(lamni): add crosshair and structured HDF5 output to X-ray eye alignment

Set the alignment crosshair at the FZP centre (step k=0, raw pixel
coords), hidden on normal completion and on Ctrl-C abort.

update_frame() now actually captures and stores each frame
(previously not stored at all); roi_pixel_data is now collected at
every submit. write_output() additionally writes a timestamped HDF5
file (alignment_values, alignment_images, roi_pixel_data,
alignment_fit) alongside the existing plain-text archival file.

fix(lamni): stop resetting correction state on every XrayEyeAlign instantiation

A fresh XrayEyeAlign is constructed on every call to both
xrayeye_alignment_start() and xrayeye_update_frame(); resetting
corr_pos_x/y/angle and tomo_fit_xray_eye in __init__ meant the
latter -- meant as a lightweight frame check -- silently wiped valid
persisted correction data every time. Reset moved to the start of
align() itself.

fix(lamni): use dev.fsh instead of dev.omnyfsh for the alignment shutter

The shared XRayEye widget monitors and controls dev.fsh; the
alignment script was opening/closing a different device, which
would desync the GUI's shutter toggle from the actual hardware
state.

fix(lamni): correct stale self.align reference in MagLamNI.rotate_slowly

self.align no longer exists as an attribute now that
LamNIAlignmentMixin is mixed directly into LamNI. Use
self.tomo_fovx_offset/self.tomo_fovy_offset directly.
2026-07-12 08:07:50 +02:00
x01dc 610bb6bac5 fix typo 2026-07-12 08:07:50 +02:00
x01dc e5425318b0 fix omny docs 2026-07-12 08:07:50 +02:00
appel_candx01dc dbad0d00b8 wip for tests 2026-07-12 08:07:50 +02:00
x01dc 680a7c5813 fix test 2026-07-12 08:07:50 +02:00
x01dc f2cff67800 refactor 2026-07-12 08:07:50 +02:00
x01dc ed1a47ceed path corrections 2026-07-12 08:07:50 +02:00
x01dc 0061aa1d32 refactor 2026-07-12 08:07:50 +02:00
x01dc 797fbf32bd documentation update flomni fzp 2026-07-12 08:07:50 +02:00
x01dc d9004bffa7 feat(LamNI): migrate X-ray eye alignment from LabView/EPICS to BEC GUI
- Add x_ray_eye_align.py: GUI-based alignment procedure collecting sample
  centre at 8 angles (0-315 deg, full 360 deg rotation), submitting x/y
  offsets to XRayEye widget fit tab, reading sinusoidal fit parameters back
  via omny_xray_gui device signals
- Update gui_tools.py: modernise to current BEC widget API (single new()
  call with object_name, _is_deleted() existence checks, add_ring() progress
  bar API, start/eta time display); add lamnigui_show_xeyealign_fittab()
- Update alignment.py: add read_xray_eye_correction_from_gui(), remove
  obsolete GUI-procedure methods now in x_ray_eye_align.py, fix __init__
  (remove orphaned _reset_init_values() call)
- Update lamni.py: add set_client() call in __init__, add
  xrayeye_alignment_start(keep_shutter_open) and xrayeye_update_frame()
  matching FlOMNI naming convention, import XrayEyeAlignGUI
- Fix LamNIMoveToScanCenter: replace arg_input/arg_bundle_size device-bundle
  pattern with required_kwargs; float dict keys caused msgpack >= 1.0
  ValueError with strict_map_key=True
- Update all lamni_move_to_scan_center() call sites to keyword arguments
- Update docs/user/ptychography/lamni.md: document new commands, remove
  MATLAB/LabView references
2026-07-12 08:07:49 +02:00
x01dcandholler afcbbf66cc fix/galil back to original code
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 1m30s
2026-07-12 07:52:37 +02:00
x01dcandholler 074294000d remove sleep in socket set, required for progress bar in sim endstations 2026-07-12 07:52:37 +02:00
x01dcandholler 6533226924 sim next version, including lamni and omny 2026-07-12 07:52:37 +02:00
x01dcandholler bcedf794a5 fixes/version2 sim devices
s Please enter the commit message for your changes. Lines starting
2026-07-12 07:52:37 +02:00
x01dcandholler 704826c94a feat(sim): add simulated flOMNI endstation (galil, smaract, rt, cameras)
Protocol-level simulation of the flOMNI hardware injected via socket_cls:
real device/controller classes run unchanged against state machines
implementing fgalil.dmc, galil_micos_upr.dmc, the Smaract MCS protocol
and the Orchestra CommunicationServer. Cameras reuse the real device
classes with a synthetic frame source. Includes simulated_flomni device
config (boots referenced at 'in' positions) and an offline harness
covering moves, referencing, rt feedback/tracker, flyer scan readout
and the gripper transfer routine.
2026-07-12 07:52:37 +02:00
x12saandholler 6722d1573a reset manual shift and auto set fovx after xray align
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 1m34s
2026-07-12 07:28:10 +02:00
x12saandholler e7cc2205cd comment on 0 z shift added 2026-07-12 07:28:10 +02:00
x12saandholler 621cd5aaef feat(flomni): include measurement ID in end-of-tomo scilog summary 2026-07-12 07:28:10 +02:00
x12saandholler 01f74ea4f7 refactor(flomni): consolidate global-var params into _GlobalVarParam descriptor
Replace 11 identical global-var-backed getter/setter property pairs with a
single _GlobalVarParam descriptor. Behavior is unchanged: same keys, defaults,
and None-handling; stitch_x/stitch_y retain int enforcement via the descriptor's
type_ guard (typeguard.check_type). Net -57 lines with no functional change.
2026-07-12 07:28:10 +02:00
x12saandholler 4fe8d02ba0 fix(flomni): warn on invalid beamline states before manual xray-eye frame; fix sample-remove slot handling
xrayeye_update_frame() now checks all configured beamline states and, if any
are not valid, warns the user (the frame may be empty when the beam is down)
and asks before taking a frame. The automated alignment path is unaffected.

ftransfer_sample_change(): clarify the remove-only prompt to "sample currently
in the sample stage", and include tray slot 20 in the empty-slot scan
(range(1, 20) -> range(1, 21)) in both the remove and mount branches, matching
check_position_is_valid's 0-20 range.
2026-07-12 07:28:10 +02:00
x12saandholler f435739c7f docs fix 2026-07-12 07:28:10 +02:00
x12saandholler 90ded9305a print green bold 2026-07-12 07:28:10 +02:00
x12saandholler 5ac695afe6 print fov bold 2026-07-12 07:28:10 +02:00
x12saandholler 0facd37860 better timing printout end of tomogram 2026-07-12 07:28:10 +02:00
x12saandholler 019693c5c8 scilog command changed 2026-07-12 07:28:10 +02:00
x12saandholler eef4e84bc9 feat(flomni): log projection timing from direct tomo_scan_projection calls 2026-07-12 07:28:10 +02:00
x12saandholler 0fe92c3efb feat(flomni): skip feye_out in tomo_alignment_scan when scan-ready
When the eye is already out, optics in, and feedback running, skip the
eye-out/optics-in transition to avoid an interferometer reset. Enables
repeated alignment scans and interleaving alignment into a tomogram
without leaving the current measurement position.
2026-07-12 07:28:10 +02:00
x12saandholler 6d5feb0d49 fix(flomni): skip feedback reset in ffzp_in when FZP already in
feedback_disable/enable_with_reset cycle re-zeros the interferometers
and moves off the current sample position. Only run it when the FZP
actually needs to move; add force_feedback_reset kwarg and _ffzp_is_in
helper.
2026-07-12 07:28:10 +02:00
x12saandholler 38431d9ebf feat(flomni): live ROI size readout and camera-sourced pixel calibration
X-Ray Eye alignment GUI + script now share a single, device-configured
pixel calibration and the GUI shows live ROI dimensions.

GUI (x_ray_eye.py):
- Add read-only "ROI size" field below the message box, showing the active
  ROI's x/y extent, updated live via each ROI's sigRegionChanged. Rectangles
  report width/height; circular ROIs read raw state["size"] so an ellipse
  state would be reflected (currently aspect-locked upstream).
- Add "Calibration" field showing microns/pixel, or "uncalibrated (pixels)"
  when the camera parameter is absent.
- Resolve mm/pixel from dev.cam_xeye.user_parameter["pixel_calibration"];
  fall back to raw pixels (scale 1.0, unit "px") if unavailable.
- Enlarge the message field from 60 to 90 px height.

Alignment script (x_ray_eye_align.py):
- Replace the hard-coded PIXEL_CALIBRATION constant with a pixel_calibration
  property reading the same cam_xeye user parameter; fall back to
  PIXEL_CALIBRATION_DEFAULT if unavailable.
- Fold the recurring factor of 2 into the calibration: xval_x_* come straight
  from raw ROI pixels on submit, so the true scale is 0.05/113 mm/pixel.
  Default is now 0.05/113 and the three /2 factors (FZP center x, height
  centering, FOV) are removed. Computed physical values are unchanged.

Device config (cam_xeye_pixel_calibration.yaml):
- Add pixel_calibration userParameter (0.05/113 mm per raw ROI pixel) to
  cam_xeye. Merge the userParameter block into the existing device entry.
2026-07-12 07:28:10 +02:00
x12saandholler 4181a4fcb2 feat(flomni): comment-first scilog_last_scans, comment-only mode, no logo
scilog_last_scans now puts the user comment at the top of the entry and
drops the flOMNI logo attachment. number_of_scans accepts 0 to send a
comment-only entry. When fewer projection records exist than requested
(e.g. non-ptycho scans, which write no timing record), the available ones
are used and a note reports how many of the requested count were found.
2026-07-12 07:28:10 +02:00
x12saandholler 475704c16b feat(flomni): offer tomo-param reset on account change at session start
At BEC session start, compare bec.active_account against the new global
var defaults_applied_for_account. On a genuine account change, interactively
offer to reset all tomo scan parameters to their defaults for the new
experiment; a client restart within the same account is a silent no-op.
Add _set_default_tomo_params() writing all 20 scan parameters via the
property setters.
2026-07-12 07:28:10 +02:00
x12saandholler bedf61e0ab fix/recon folder name from global var was not set when changed in gui 2026-07-12 07:28:10 +02:00
x12saandholler 47e3d59164 added ES shutter to ES config 2026-07-12 07:28:10 +02:00
x12saandholler f8f655aa08 config update 2026-07-12 07:28:10 +02:00
x12saandholler 7e437ae24f replace yesno with gui yesno 2026-07-12 07:28:10 +02:00
x12saandholler 956d1ad1bc - ConsoleButtonsWidget ABORT: SIGINT to the client (only if the parent
process looks like a BEC client), plus a delayed (500 ms) backup
  stop_devices request, defaulting to stop-all so the widget doubles as
  a generic emergency stop when opened standalone from the launcher menu
2026-07-12 07:28:10 +02:00
x12saandholler 27932cc552 feat(flomni): make sample transfer abortable via Ctrl+C and GUI abort button
The gripper routines (#GRGET/#GRPUT) run autonomously on the Galil
(thread 3); Ctrl+C previously only killed the BEC-side polling loop
while the controller kept executing, including ongoing motion.

- add FlomniSampleTransferMixin.ftransfer_abort(): dev.ftransy.stop()
  (stop_devices endpoint -> device server -> XQ#STOP), wait for thread 3
  to halt, then put the controller back in posmode; deliberately no
  ensure_gripper_up() - recovery after an abort is manual
- wrap the mntprgs polling loops in ftransfer_get_sample/put_sample in
  try/except KeyboardInterrupt -> ftransfer_abort()
- answering "No" at a confirmation point now also hard-aborts the
  controller routine instead of leaving it parked in #CONFIRL
- ConsoleButtonsWidget ABORT: keep SIGINT to the client, plus a delayed
  (500 ms) backup stop_devices request so the hardware stops even if
  the client is hung; delay ensures the controlled client-side abort
  wins the race against #STOP clearing mntprgs
2026-07-12 07:28:10 +02:00
x12saandholler f583bb336b feat(bec_widgets): grey out Yes/No in ConsoleButtonsWidget when no message
Yes/No start disabled and are enabled only while a message is set; ABORT
stays always enabled. Clear visual cue that there's nothing to confirm.
2026-07-12 07:28:10 +02:00
x12saandholler c11cc24fcd minor fix gui confirm 2026-07-12 07:28:10 +02:00
x12saandholler c044d2b6c1 feat(flomni): add GUI confirm/abort console for sample transfer
Add a small ConsoleButtonsWidget (Yes/No/Abort + message label) as a
GUI alternative to console yesno() prompts during sample transfer,
docked below the live camera view.

- bec_widgets: new ConsoleButtonsWidget (csaxs_bec/bec_widgets/widgets/
  console_buttons/). Yes/No write to a response property polled by the
  caller; Abort sends a real SIGINT to the BEC client process (captured
  via os.getppid() at construction), stopping any blocking call
  (including an in-progress motor move) exactly like a console Ctrl+C.

- omny_general_tools: add OMNYTools.gui_yesno(), a GUI-backed
  alternative to yesno() with the same message/default/autoconfirm
  semantics, polling the widget's response instead of blocking on
  input(). Not yet used as a general yesno() replacement.

- gui_tools: flomnigui_show_cameras() now also opens the console widget
  docked below the two camera views (set_layout_ratios for relative
  sizing, since dock_area.new() has no pixel width/height kwarg).
  flomnigui_show_gui() now passes an explicit window geometry, right-
  aligned on a 2560x1440 screen.

- flomni: add ftransfer_confirm_dialog(), which uses the GUI console
  when available (falling back to the plain CLI yesno() otherwise) and
  wire it into ftransfer_confirm()'s per-step "All OK? Continue?"
  prompt during sample transfer. Other yesno() call sites (stage-in
  flight-tube check, gripper-move stage-out check) intentionally left
  on the CLI for now.

Known issue: requested window height in flomnigui_show_gui() is not
taking effect as expected at startup; needs follow-up.
2026-07-12 07:28:10 +02:00