Commit Graph
1282 Commits
Author SHA1 Message Date
wakonig_k f2e60da39e fix: specify python command for zensical build in Read the Docs configuration
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (pull_request) Successful in 3m6s
CI for csaxs_bec / test (push) Successful in 3m7s
2026-10-14 09:56:44 +02:00
wakonig_k bf8036b72d docs: migrate to zensical
CI for csaxs_bec / test (pull_request) Successful in 3m6s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 3m3s
2026-10-14 09:43:31 +02:00
x12saandClaude Opus 5.5 ca96f41a45 fix(webpage): name the setup's own client in the web password hint
CI for csaxs_bec / test (pull_request) Successful in 3m6s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 3m5s
The "no web authentication yet" hint always said
flomni.set_web_password(), which doesn't exist in a LamNI session. Each
generator now declares CLIENT_NAME and the hint uses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALDnH16dC6iyHjhe5Ey4w9
2026-10-13 10:22:15 +02:00
x12saandClaude Opus 5.5 636eb55e96 feat(lamni): refit the X-ray eye sine fit without bad clicks
lamni.xrayeye_refit([angles]) redoes the sinusoidal X-ray eye fit from the
last alignment's points, leaving out the given angles (misclicks, sample
barely visible). Prints each point's deviation from the new fit, shows the
kept points on the GUI Fit tab and asks before replacing tomo_fit_xray_eye.
The raw offsets are kept in the lamni_xeye_fit_data global var (cleared with
the fit); archived alignments can be refitted via file=.

Also clear omny_xray_gui.fit_params_x/y before submitting new alignment
points, so a previous run's fit is not taken as the new one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALDnH16dC6iyHjhe5Ey4w9
2026-10-13 09:50:40 +02:00
hollerandClaude Sonnet 5.5 59f30613c9 feat(xrayeye): rotatable, angle-lockable elliptical ROI for LamNI rotation-centre calibration
CI for csaxs_bec / test (push) Successful in 3m5s
Ellipse tool is hidden by default and only enabled by the LamNI client in the
extended/smear rotation-centre modes after the FZP click. Adds angle lock
toggle/field, rotation-aware geometry, and roi_angle_{step} submit signal.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 07:14:32 +02:00
x12saandClaude Opus 5.5 966452ffe0 test: fix rotation-center and LamNI angle-file tests
CI for csaxs_bec / test (pull_request) Successful in 3m6s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 2m58s
- test_x_ray_eye_align: mock mvx/mvy as 0 so find_rotation_center() sees no
  pending arrow press; add a test that arrow shifts during the isolated
  calibration are applied with angle=0.0 and subtracted from the clicks.
- test_lamni_tomo_angle_file: drop the "Tomo type 4" scilog assertion; that
  entry was removed in ae30453.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:27:17 +02:00
x12saandClaude Opus 5.5 2e877b315e feat(lamni): arrow keys in isolated rotation-center calibration
CI for csaxs_bec / test (push) Failing after 2m58s
CI for csaxs_bec / test (pull_request) Failing after 2m53s
The GUI arrow buttons were enabled during find_rotation_center() but never
polled. In isolated mode they now move lsamx/lsamy via
lamni_move_to_scan_center(angle=0.0); the running shift is subtracted from
the 0 and 180 deg clicks before computing the axis midpoint, so the new
center stays correct when the particle had to be brought into the FOV.
Extended mode now disables the (non-functional) arrow buttons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:19:35 +02:00
x12saandClaude Opus 5.5 0100350dc2 config(lamni): update OSA in position
CI for csaxs_bec / test (push) Failing after 2m53s
losax in: -1.442 -> -1.582, losay in: -0.171 -> -0.131

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155LWmbktVgWdnRZqtveqQv
2026-10-12 19:12:19 +02:00
x12saandClaude Opus 5.5 1b06729dba feat(lamni): skip opaque pixels in the smear min-projection
CI for csaxs_bec / test (push) Failing after 2m59s
With the min-projection, the opaque sample holder rotating through the
X-ray eye view blackened every pixel it passed at any angle and shrank the
usable field of view. SmearComposite now ignores, per frame, pixels darker
than opaque_level (default 0.25) times the frame's 99th-percentile value
and takes the minimum over the remaining frames; pixels opaque at every
angle fall back to the plain minimum, and opaque_level=0 is the plain
minimum. opaque_level is passed through from
lamni.xrayeye_rotation_center_calibration_smear_experimental() and stored
in the calibration HDF5.

The smear capture-rate test now replaces the module's time instead of
patching time.time globally, which EPICS CA threads left running by other
tests also advanced.

Docs: smear opaque_level, LamNI optics in/out incl. central stop,
interferometer feedback commands, and moving to the scan centre including
the X-ray eye fit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155LWmbktVgWdnRZqtveqQv
2026-10-12 18:55:42 +02:00
x12saandClaude Opus 5.5 af83afeed4 feat(lamni): min-projection smear composite for absorption contrast
CI for csaxs_bec / test (push) Failing after 2m55s
The smear rotation-center aid built a max-projection composite, which suits
bright features on a dark background. On the X-ray eye the sample shows up
in absorption (dark on bright), and a max-projection washes those features
out because every pixel sees the bright background at some point during the
rotation.

Add projection="min"|"max" to _smear_sweep(),
find_rotation_center_smear_experimental() and
lamni.xrayeye_rotation_center_calibration_smear_experimental(), defaulting
to "min". Invalid values raise before any motion; the choice is stored in
the calibration HDF5. User and developer docs updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:34:28 +02:00
x12saandClaude Opus 5.5 447c51dae9 config: switch to LamNI setup with central stop and new FZP
CI for csaxs_bec / test (push) Failing after 2m53s
- main.yaml: load ptycho_lamni.yaml instead of ptycho_flomni.yaml
- ptycho_lamni.yaml: add lcsx/lcsy central stop stages; new 250 um / 30 nm
  FZP (Abe Tomas 05/2026) optics positions; updated lsamx/lsamy centers,
  X-ray eye in positions, cam_xeye rotation and pixel calibration
- ptycho_flomni.yaml: optics/OSA positions for the 250 um / 30 nm Tomas
  FZP; updated fttrx in/out
- bl_detectors.yaml: disable eiger2_si_500k

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:25:50 +02:00
x12saandClaude Opus 5.5 eac8336052 fix(lamni): call lamni_lights_on/off on the Galil controller
CI for csaxs_bec / test (push) Failing after 2m57s
The LamNI Galil controller only exposes lamni_lights_on()/lamni_lights_off();
lamni.lights_on()/lights_off() called non-existent lights_on()/lights_off().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:25:30 +02:00
x12saandClaude Opus 5.5 2f238a2cc3 fix(lamni): interferometer position readout, scan-center drift and central stop moves
CI for csaxs_bec / test (push) Failing after 3m14s
- show_interferometer_positions() now calls the controller's positions
  method (was calling show_feedback_status) and the method is exposed via
  USER_ACCESS; loop status is taken from J2 like feedback_status() instead
  of misreading the J4 status field (reported True with feedback off).
- lamni_new_scan_center_interferometer: a first-iteration drift above
  150 um is now discarded instead of being skipped in the first move but
  still added in the second iteration.
- loptics_in()/loptics_out() now also move the central stop; lcs_in()
  moves lcsx/lcsy to their configured "in" position and lcs_out() moves
  lcsy to its configured "out" position (was hardcoded 3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:49:34 +02:00
x12saandClaude Opus 5.5 d56a2a6d47 fix(eiger): log JungfrauJoch cancel and flag a Dectris server left armed
CI for csaxs_bec / test (push) Failing after 3m15s
On 2026-10-10 an interlock abort left the Eiger2 Si 500K Dectris server
armed (state ready, stream acquire), and every re-init then failed with
"Detector busy" until the fibres were re-seated. The cancel path logged
nothing on success, so it was not possible to tell what happened.

- stop() logs a successful cancel with its reason, then the Dectris status
- recover_from_error() adds a hint to the error when Dectris is still armed
- on_stop logs that it was called
- tests: block cancel to the production JungfrauJoch host from fixtures

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G1MYT2ByPys8XL6BdfApAF
2026-10-12 11:33:23 +02:00
x12saandClaude Sonnet 5.5 53f74c67f0 fix(eps): use FE-PSH1-EMLS:STATE for front-end shutter state
CI for csaxs_bec / test (push) Failing after 2m54s
X12SA-FE-PH1:OPEN_EPS/CLOSE_EPS no longer exist (unreachable with the machine
up). Drop them and derive the FE shutter open/closed values from
X12SA-FE-PSH1-EMLS:STATE.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYAZk48UNnhKsnzdz7nJNR
2026-10-10 10:48:51 +02:00
x12saandClaude Sonnet 5.5 ad04a40061 feat(eps): move alarm status section towards the end of the status page
CI for csaxs_bec / test (push) Failing after 2m57s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYAZk48UNnhKsnzdz7nJNR
2026-10-10 10:31:30 +02:00
x12saandClaude Sonnet 5.5 bad25d9237 fix(eps): read operator-message PVs so the status page shows them
OPMSG_PVS were never part of all_bulk_pvs(), so the generator's bulk cache
never held them and the page always said "no operator messages".

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYAZk48UNnhKsnzdz7nJNR
2026-10-10 10:31:30 +02:00
x12saandClaude Sonnet 5.5 303fdbe2e3 chore(flomni): remove FZP_30nm_Tomas_Abe_2026.pdf from docs
CI for csaxs_bec / test (push) Failing after 2m57s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYAZk48UNnhKsnzdz7nJNR
2026-10-08 11:49:26 +02:00
x12saandClaude Sonnet 5.5 ae3045322b fix(tomo): drop extra scilog entry for tomo type 4 (flomni, LamNI)
Type 4 wrote a separate scilog entry dumping the whole angle list at scan
start. The regular start entry already names the angle file, so remove the
extra entry and the now-unused _scilog_angle_file_record/_angle_file_scilog.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYAZk48UNnhKsnzdz7nJNR
2026-10-08 11:49:11 +02:00
x12saandClaude Sonnet 5.5 533467775d feat(eiger): log Dectris detector server status on detector error
Log the read-only Simplon status endpoints (detector, stream, monitor,
filewriter) of the Eiger2 Si 500K detector server as one warning line,
right before the EigerError is raised in on_complete and before the
auto re-init wipes the error state. Only enabled when dectris_url is
set, which is the case for the Eiger2 Si 500K only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G1MYT2ByPys8XL6BdfApAF
2026-10-08 11:47:24 +02:00
x12saandClaude Sonnet 5.5 8c6977428e feat(flomni): reset manual shifts after sample change
The manual x/y shifts belong to the previous sample, so zero them once the
new sample is mounted in ftransfer_sample_change().

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYAZk48UNnhKsnzdz7nJNR
2026-10-07 11:59:15 +02:00
x12saandClaude Sonnet 5.5 5112672368 feat(tomo): warn and confirm on manual tomo_scan_resume() when queue has an unfinished job
CI for csaxs_bec / test (push) Successful in 3m8s
A Ctrl-C abort leaves the queue job 'running'; resuming by hand bypasses the
queue and a later tomo_queue_resume() would resume it again. Manual
tomo_scan_resume() now lists the unfinished job, hints at tomo_queue_resume(),
and asks y/N. tomo_queue_execute() passes from_queue=True to skip the prompt.
OMNY port tracked in a TODO doc.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-05 22:41:22 +02:00
x12saandClaude Sonnet 5.5 acc5e3b477 config(detectors): use eiger2_si_500k with auto_reinitialize
Enable the Eiger 2 Si 500K (eiger_1_5 commented out) and set auto_reinitialize so
the detector is re-initialised on staging after a JungfrauJoch error.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YAEkW1wjZszVKkhQrD8Ad5
2026-10-05 22:41:09 +02:00
x12saandClaude Sonnet 5.5 bb6bd111d0 fix(eiger): expose auto_reinitialize as a device attribute
The device server applies deviceConfig entries via update_config, which rejects keys
that are not attributes of the device ("Unknown config parameter"). A constructor
argument alone is therefore not enough to set auto_reinitialize from the config.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YAEkW1wjZszVKkhQrD8Ad5
2026-10-05 22:39:01 +02:00
x12saandClaude Sonnet 5.5 1f795c399e feat(eiger): optional auto-reinitialize of JungfrauJoch after detector error
When the detector goes into Error state, JungfrauJoch stays in WrongDAQState until
it is initialised again, so every following scan aborts in on_stage. With the new
auto_reinitialize device option, on_stage re-initialises the detector (like the
WEB UI "Initialize" button) before staging; the scan_repeat retry of the failed
projection then succeeds.

- JungfrauJochClient.recover_from_error(), single attempt, only from Error state
- wait_for_idle reports the broker's HTTP 500 message instead of a pydantic traceback
- on_complete fails immediately if the detector is in Error state
- default off; no re-initialisation during an acquisition

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-05 22:30:25 +02:00
x01dcandClaude Sonnet 5.5 517b0f7e3a fix(tomo): type-4 progress GUI + tomo_params window size
CI for csaxs_bec / test (push) Successful in 3m5s
- flomni GUI progress: guard division by total_projections == 0 (type 4
  file without END, endless golden ratio); the error was swallowed and the
  progress window stayed on the reset defaults for the whole scan.
- Fill tomo type/total/subtomo for type 4 right after scan start, so the
  GUI is meaningful during empty frames and the 0-deg reference shot.
- tomo_parameters() window opens at 730x1170.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y5CiUfRUwnPyuAkq5Wyptm
2026-10-02 18:32:20 +02:00
x01dcandClaude Sonnet 5.5 28929b174c feat(lamni): tomo_type 4 (angles from file) via shared TomoAngleFileMixin
CI for csaxs_bec / test (push) Successful in 3m4s
Extract flomni's type-4 logic (tomo_angle_file property, parsing helpers,
per-line step/exptime/frames overrides, wait-for-growing-file loop) into
OMNY_shared/tomo_angle_file_mixin.py and use it from both Flomni and LamNI.

LamNI: type 4 in setter/params/queue/CLI/PDF/resume, overrides threaded
through tomo_scan_projection, progress reset before gates, no 180-deg
opposite-angle warning (laminography). Widget profile, webpage row, docs
and tests added.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y5CiUfRUwnPyuAkq5Wyptm
2026-10-02 17:01:07 +02:00
x01dcandClaude Sonnet 5 c557eb22d6 feat(lamni): add 30nm zone-plate table to lfzp_info(), matching flomni
CI for csaxs_bec / test (push) Successful in 3m4s
lfzp_info() only showed a 60nm outermost-zone-width table; flomni's
ffzp_info() has always had both a 60nm and a 30nm table. Since the zone
plate now mounted in LamNI is the same one mounted in Flomni, add the
matching 30nm table (diameters 140/170/200/250um, from ffzp_info()) and
relabel the 60nm table's title to match flomni's "Outermost zone width
NN nm" convention, so the two tables read consistently side by side.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 16:38:06 +02:00
x01dcandClaude Sonnet 5 796b78e78f fix(lamni): zero manual_shift_x/y at the start of a fresh alignment run
CI for csaxs_bec / test (push) Successful in 3m6s
LamNI's alignment-start reset (_align_impl(), documented as "the actual
start of a fresh alignment run") already resets reset_correction()/
reset_xray_eye_correction() there, but never touched manual_shift_x/y --
unlike flomni's equivalent, which this session's work already fixed to
zero manual_shift_x/y at the same point. Same risk: a leftover manual
shift from a previous sample would otherwise bias the next sample's
alignment tomogram.

Found via an audit of whether this session's flomni-side changes (+ the
merged fixes/flomni4 branch) called for any analogous LamNI changes --
this was the one confirmed real gap; everything else either already
existed on LamNI, is generic/shared infrastructure that needed no
changes, or is structurally inapplicable (LamNI is 360-degree-only
laminography).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 16:32:16 +02:00
x01dc f08c3ef7b7 Merge remote-tracking branch 'origin/fixes/flomni4' into feat/flomni_tomo_from_angles_file
CI for csaxs_bec / test (push) Successful in 3m7s
# Conflicts:
#	csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py
#	csaxs_bec/bec_widgets/widgets/tomo_params/tomo_params.py
2026-10-01 16:19:45 +02:00
x01dcandClaude Sonnet 5 8e2a320e58 feat(flomni): add "Edit command" for pending command jobs in the tomo queue
CI for csaxs_bec / test (push) Successful in 3m9s
The "Load into editor" button in the queue dialog is now dual-purpose:
for a single selected tomo job it behaves as before, but for a single
selected *pending* command job it relabels to "Edit command" and opens
CommandJobBuilderDialog pre-populated with that job's steps/label/
idempotent flag. On OK the job is replaced in place (same queue
position, same id/added_at); on Cancel the queued job is untouched.

Previously a command job could only be "edited" by deleting it and
rebuilding it from scratch via "Add command..." -- tedious for a
multi-step job and easy to get wrong.

Restricted to status == "pending" (reusing _MOVABLE_STATUSES, the same
set already used for "is this row safe to reorder") -- an "incomplete"
command job may have partially executed steps, a materially different
and riskier case than a never-started one.

Live-verified against the running simulated flomni session: button
correctly relabels per selection, editing a real queued command job
replaces it in place with the id/label preserved, and the session's
queue was left exactly as found afterward.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 16:04:22 +02:00
x01dcandClaude Sonnet 5 91eb55bf13 docs(flomni): "Compensating" -> "New" for move_foptz_with_shift's shift fields
CI for csaxs_bec / test (push) Successful in 3m15s
manual_shift_x/manual_shift_y are replaced outright by
move_foptz_with_shift (self.manual_shift_x = manual_shift_x), not adjusted
relative to the previous value -- "Compensating" wrongly implied the
latter. Wording-only change across the GUI field labels, the action's
help/docstring text, and the two AI_docs testing notes that describe them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 15:52:26 +02:00
x01dcandClaude Sonnet 5 47cbb08c13 fix(flomni): check OSA collision warning against fosaz's defined IN position
The foptz-target OSA-collision warning was checking fosaz's current live
readback, but fosaz is normally parked safely OUT except when actually in
use -- so the warning would almost never fire in practice. Check against
fosaz's "in" user_parameter instead (the same nominal position
FlomniOpticsMixin.fosa_in() itself checks before refusing a fosaz move),
since that's the real question: would this foptz target collide with the
OSA if it were swapped in.

Live-verified against the running simulated flomni session: fosaz's
defined IN position is 13.1mm; the warning now correctly engages right at
the real physical crossover (foptz=19 -> +0.90mm clearance, safe;
foptz=20 -> -0.10mm, warns), matching compute_osa_remaining_space()
exactly through the real dialog/client/device-RPC code path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 15:52:16 +02:00
x01dcandClaude Sonnet 5 a5fcc73049 feat(flomni): warn on OSA collision risk when picking a foptz target
CI for csaxs_bec / test (push) Successful in 2m58s
The tomo-queue "move_foptz_with_shift" dialog now shows a live warning
when the entered foptz target would leave <=0 mm OSA clearance against a
normal OMNY pin (same collision check fosa_in() already enforces for
fosaz moves, surfaced here at data-entry time instead). Advisory only --
the queued action itself stays a plain absolute move, not gated on this.

Extracted the clearance formula out of FlomniOpticsMixin._osa_remaining_space
into a module-level compute_osa_remaining_space(foptz_val, fosaz_val) so
the GUI (a separate process with only RPC device access, no Flomni
instance to call a bound method on) can reuse the exact same formula
instead of duplicating it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 15:38:56 +02:00
x01dcandClaude Sonnet 5 437d9e7559 feat(flomni): fix tomo-queue move allow-list, drop idgap stub, add foptz+shift action
CI for csaxs_bec / test (push) Successful in 3m10s
Queue-control "command job" changes:
- Remove the "optimize_idgap" stub action (not commissioned, always a
  no-op) from both Flomni's and LamNI's action registry, and delete the
  now-dead TomoQueueMixin.optimize_idgap() method.
- Fix the "move" action's device allow-list: "mokev" doesn't exist in the
  current device config at all -- the real energy device is "ccm_energy"
  (LamNI's copy already had this right). Also drop "ftray" (sample
  transfer tray), which was only ever there as a sim-testable move target.
- Add a new "move_foptz_with_shift" action: moves foptz (the FZP
  optics-Z stage) and sets the compensating manual_shift_x/manual_shift_y
  in one atomic step. foptz is deliberately NOT in the plain move
  allow-list -- moving it shifts the aligned scan center (interferometer
  mirrors aren't perfectly parallel to the beam), so this is the only way
  to queue a foptz move, specifically so the compensating shift can never
  be forgotten.
- The foptz target is validated against the device's real, live limits
  (not a hand-duplicated static number) both in the GUI spinbox's range
  and in the CLI's add-time validation, so a queued job can't raise when
  it executes unattended later; a defense-in-depth check also exists at
  execution time.

Retargeted the e2e command-job tests from ftray to foptz (now the only
sim-testable move-adjacent path) and updated the AI_docs testing/GUI-testing
notes to match the new action set.

Also fixes a pre-existing, unrelated broken import
(plugins.omny.omny_general_tools -> plugins.OMNY_shared.omny_general_tools,
already broken on main) in _bootstrap.py/test_tomo_queue.py/
test_tomo_queue_command_jobs.py that was blocking omny_e2e_tests collection
entirely, needed to actually verify this change's own test rewrites.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 15:11:49 +02:00
x01dcandClaude Sonnet 5 cf66e0d1ec feat(flomni): info popup for manual shift x in tomo parameters GUI
CI for csaxs_bec / test (push) Successful in 3m2s
Add a per-field "?" help button, same pattern already used for the
offsets section and Fermat asymmetry. Explains that manual_shift_x is
a constant, angle-independent offset applied on top of the alignment,
only needed in special cases (e.g. when the FZP-to-sample distance is
changed after alignment).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 13:11:15 +02:00
x01dcandClaude Sonnet 5 2a91f94c06 feat(flomni): add manual_shift_x fine-adjustment parameter
CI for csaxs_bec / test (push) Successful in 2m59s
Mirrors the existing manual_shift_y: a persistent, GUI/CLI-settable x
offset applied to the scan center in tomo_scan_projection() and
tomo_acquire_at_angle(), included in tomo-queue snapshots, and reset to
0 at the start of every x-ray-eye alignment run (and on account-change
defaults) so a leftover fine adjustment never biases the next sample's
alignment tomogram. Bounded to +/-10um in the GUI and CLI, narrower
than manual_shift_y's +/-50um range.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 12:58:34 +02:00
x12saandClaude Sonnet 5.5 b4cec42211 fix(tomo_params): enable 'run all sub-tomos forward' on entering edit mode
CI for csaxs_bec / test (push) Successful in 2m58s
_enter_edit_mode_with() called _set_fields_enabled(True) before setting
_edit_mode, so the always-forward checkbox was evaluated with
_edit_mode=False and stayed disabled until the angle range was toggled.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 12:54:28 +02:00
x12saandClaude Sonnet 5.5 b3830dc3e5 feat(flomni): option to run all type-1 sub-tomos forward (0->180)
CI for csaxs_bec / test (push) Successful in 3m5s
Add tomo_type1_always_forward (180 deg range only, default off). Every
sub-tomo sweeps 0->180 with an unchanged angle set, so the 0 deg
reference (zero_deg_reference_at_each_subtomo) is taken before all 8
sub-tomos instead of every second one.

Exposed in tomo_parameters(), queue params and the tomo params widget;
resume/reacquire use the same plan. Tests and docs updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 12:33:49 +02:00
x01dcandClaude Sonnet 5 c9bf65adda docs(flomni): state the angle-file column-1 range as 0-360
CI for csaxs_bec / test (push) Successful in 3m1s
Keep the user-facing docs at the simple, nominal 0-360 sweep rather
than advertising the stage's -2/362 physical overtravel margin that
_tomo_scan_at_angle()/angle_file.py actually tolerate -- that margin is
a safety/robustness allowance, not something to document as an
intended input range.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 12:06:25 +02:00
x01dcandClaude Sonnet 5 d119bd2ce8 fix(flomni): widen tomo_type-4 angle range to stage overtravel; fix GUI clipping
CI for csaxs_bec / test (push) Successful in 3m4s
Angle files now accept -2 to 362 degrees (the fsamroy rotation stage's
physical overtravel range) instead of the nominal 0-360 sweep. This
required widening both the parser's bounds (angle_file.py) and
_tomo_scan_at_angle()'s own gate (a new _min_scan_angle(), alongside the
existing _max_scan_angle()) -- without the latter, an angle just outside
[0, 360] would have parsed fine but then been silently skipped during
acquisition with no error, which is worse than rejecting it upfront.

Also fixes the type-4 GUI section's "Content:" label clipping its second
wrapped line: QFormLayout sizes that row from the label's sizeHint() at
construction (holding the short "---" placeholder) and doesn't reliably
recompute it when setText() later swaps in longer, word-wrapped content.
_set_angle_file_summary() now recomputes the label's minimum height from
heightForWidth() after every update.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 11:49:49 +02:00
x01dcandClaude Sonnet 5 0332f1c681 fix(flomni): reset tomo progress before pre-flight gates, not after
CI for csaxs_bec / test (push) Successful in 3m18s
tomo_scan() only reset the progress dict (projection/subtomo/angle/
tomo_start_time/heartbeat/...) inside the "new scan" block, which runs
after the eye/optics-in and filter-out-of-beam gates. If either gate
declined (e.g. the setup isn't in measurement condition, which disabling
rt feedback can trigger) or the tomo_type-4 angle-file check raised,
tomo_scan() returned/raised having never touched progress, leaving the
GUI/webpage showing projection/angle/start-time from whatever scan last
completed -- arbitrarily old, and easy to mistake for a scan still
running.

Hoists the "is this a new scan" check to the top of tomo_scan() and
calls progress.reset() there, before any gate runs; the heavier new-scan
actions (sample DB registration, PDF report, collect_empty_frames(), the
0-deg reference shot) stay exactly where they were, after the gates.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 11:22:45 +02:00
x01dcandClaude Sonnet 5 67884c0c3f feat(flomni): tomo_type 4 -- live-appendable angle files, per-line overrides
CI for csaxs_bec / test (push) Successful in 2m59s
Angle files can now be grown while a scan is running: tomo_scan() polls
and re-reads the file whenever it catches up to the current end,
picking up newly appended lines automatically instead of requiring
tomo_scan_resume(). A file is "complete" only once it ends with an
explicit END sentinel line -- without one the scan waits indefinitely
at EOF rather than stopping, so a static/legacy angle file needs END
appended or a scan against it will hang at the end.

Each line may also carry optional step=/exptime=/frames= key=value
tokens overriding the Fermat step size, exposure time, and burst frame
count for that projection and (sticky) every one after it, falling
back to the live tomo_parameters() configuration whenever not set.
Threaded through as kwargs that default to None and are omitted when
empty, so every existing tomo_type 1/2/3 call site and test stub is
unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPZ9gzN2rt3wgiMnTKyrug
2026-10-01 11:01:16 +02:00
x01dcandClaude Opus 5.5 9373fe307a fix(flomni): no 0-deg damage-estimation shots for tomo_type 4
CI for csaxs_bec / test (push) Successful in 3m4s
Sub-tomogram numbers in a user angle file are just labels, so inserting
extra 0-deg projections at odd sub-tomogram transitions is surprising.
tomo_type 4 now ignores golden_projections_at_0_deg_for_damage_estimation;
the CLI no longer prompts for it and the widget hides it for type 4.
The unconditional 0-deg reference shot at the start of a new tomogram
is unchanged; further 0-deg shots can be put in the file directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:57:11 +02:00
x01dcandClaude Opus 5.5 35ac94cfd5 feat(flomni): tomo_type 4 -- angles read from a file
CI for csaxs_bec / test (push) Successful in 3m7s
New angular sampling mode: tomo_angle_file points to a plain-text file
with one angle (0-360 deg) per line, optional 2nd column = sub-tomogram
number, '#' comments. Only the path is stored; the file is re-read at
every tomo_scan() start, resume and queued-job run, so an edited file
always takes effect on the next (re)start. The parsed angle list is
logged to scilog at scan start.

- OMNY_shared/angle_file.py: shared pure parser + summary, incl.
  warning for 180-deg-opposite/duplicate angles
- flomni: tomo_type 4 branch in tomo_scan()/tomo_scan_resume(), bad
  file fails before any acquisition, angles >180 not gated by
  tomo_angle_range, CLI prompt/display, PDF report
- tomo queue: path snapshotted per job; setter does not validate so
  stale paths never break non-type-4 jobs; reacquire bounds by file
- TomoParams widget: type 4 (flomni only) with Browse... file dialog,
  live content preview, validation blocking Submit/Add to queue
- webpage: show angle file in param tables

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:46:56 +02:00
x12saandClaude Sonnet 5.5 b15b236673 revert(config): drop shutter_eh1 and restore shutter_es name
CI for csaxs_bec / test (push) Successful in 3m5s
Reverts 7ebc33c and c8f96d5.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-29 14:06:36 +02:00
x12saandClaude Sonnet 5.5 7ebc33c3e1 refactor(config): rename shutter_es to shutter_oh (optics hutch shutter)
CI for csaxs_bec / test (push) Successful in 3m0s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-29 14:04:34 +02:00
x12saandClaude Sonnet 5.5 c8f96d5942 feat(config): add EH1 endstation shutter A open status (shutter_eh1)
CI for csaxs_bec / test (push) Successful in 3m3s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-29 14:00:56 +02:00
x12saandClaude Sonnet 5.5 2e05d1e456 feat(webcam): add exposure, auto-exposure, gain and brightness control to WebcamViewer
CI for csaxs_bec / test (push) Successful in 2m58s
Settings are posted to the camera server's /camera/settings endpoint and read
back on startup in a background thread, so an unreachable server cannot block
device loading. set_exposure_time() disables auto exposure first.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-29 13:49:17 +02:00
x12saandClaude Sonnet 5.5 7cb553414a fix(tests): give leye_out shutter test a csaxs and client stub
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 2m56s
leye_out() now reads self.csaxs and self.client (slit/filter handling), which
the bare LamNIOpticsMixin subclass in the test did not define.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-29 13:07:46 +02:00