Commit Graph
942 Commits
Author SHA1 Message Date
x01dc bb643cac4e test config file
CI for csaxs_bec / test (push) Failing after 3m49s
2026-07-24 12:08:54 +02:00
x01dcandClaude Sonnet 5 f4bd20e4e8 fix: include content changes for vmbpy->Aravis swap
CI for csaxs_bec / test (push) Failing after 2m21s
The prior commit only captured the file renames (a botched `git add`
dropped the actual edits). This commit carries the real diff: Aravis
GObject-introspection backend in base_integration/camera.py, renamed
class/config references, and the rewritten README.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 16:13:07 +02:00
x01dcandClaude Sonnet 5 b644cf786b refactor(devices): swap AlliedVision camera backend from vmbpy to Aravis
Replaces the vendor-SDK-based vmbpy backend with Aravis, an open-source
GenICam/GigE Vision library that implements the network protocol itself,
so no Allied Vision account/license/SDK download is needed - just
PyGObject plus the system-packaged Aravis library. Renames
AlliedVisionVmbCamera -> AlliedVisionAravisCamera (and matching
file/sim/test names) to reflect the new backend; low-level Camera
wrapper keeps the same public interface, only its internals changed.
README rewritten with Aravis install steps and camera-ID discovery via
arv-tool-0.8 / Aravis.get_device_id().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 16:12:39 +02:00
x01dcandClaude Sonnet 5 f3148c897c feat(devices): add native vmbpy integration for Allied Vision Alvium camera
CI for csaxs_bec / test (push) Successful in 1m41s
Adds AlliedVisionVmbCamera, talking to the Alvium G1-507m directly over
GigE Vision via vmbpy (Vimba X SDK) instead of the existing dead EPICS
IOC-based AlliedVisionCamera, which is left in place for now. Includes
the low-level Camera wrapper, simulation backend, device config example,
tests, and a README covering setup/testing/vmbpy install steps.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 08:06:45 +02:00
x01dcandClaude Sonnet 5 353718d196 feat(webpage): add force_start_webpage() to manually override account-match check
CI for csaxs_bec / test (pull_request) Successful in 2m37s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 2m25s
Lets testing/debugging sessions running under a mismatched BEC account
still exercise the real upload path on demand -- e.g. to confirm uploads
work from the allowed subnet, or to confirm the remote server's IP
whitelist rejects uploads from outside it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 10:08:20 +02:00
x01dcandClaude Sonnet 5 25422d07a4 fix(webpage): remove duplicate EPS uploader, allow .svg uploads
CI for csaxs_bec / test (pull_request) Successful in 1m37s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 1m37s
EpsStatusGenerator was given its own upload_url when embedded in
WebpageGeneratorBase, spinning up a second HttpUploader that redundantly
re-scanned and re-POSTed the same shared output directory the base
generator's own uploader already covers every cycle. It now always embeds
with upload_url=None. Also adds .svg to HttpUploader's suffix whitelist so
eps_synoptic.svg is actually picked up (matches a corresponding fix already
applied server-side in upload.php/auth_gate.php).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 20:37:49 +02:00
x01dcandClaude Sonnet 5 6ba277abee feat(webpage): integrate EPS/machine-status pages into lamni/flomni webpage
CI for csaxs_bec / test (push) Successful in 4m51s
Lands the standalone EPS status prototype (OMNY_shared/eps/) as tracked
files and wires it into WebpageGeneratorBase so it starts alongside the
experiment status page, sharing one output dir/port. Demo-vs-real data is
now auto-detected from the host's subnet (129.129.122.x = on the X12SA
beamline) instead of requiring an explicit --demo flag, independent of the
existing account-mismatch local-only fallback. Cross-links the two page
families via a new EPS nav button in status.html's header (the EPS pages
already linked back). Also moves HttpUploader/LocalHttpServer into a
shared web_common.py, picking up an SSRF fix (allow_redirects=False) that
the inline copy lacked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 19:40:04 +02:00
x01dcandClaude Sonnet 5 10ef93557b fix/stop lamni tests from leaking a fake bec global across the suite
CI for csaxs_bec / test (pull_request) Successful in 3m9s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 3m13s
test_lamni_account_change_reset.py and test_lamni_tomo_angles.py set
builtins.__dict__["bec"] directly with no cleanup, so a leaked fake
account ("e22222") from an earlier test file was still in place when
test_x_ray_eye_align.py later constructed a real LamNI(), tripping
_maybe_reset_params_on_account_change()'s interactive yesno() prompt
and failing under pytest's captured stdin. Use monkeypatch.setitem so
pytest reverts the global after each test, matching the pattern already
used in test_lamni_tomo_alignment_scan.py.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:57:06 +02:00
x01dcandClaude Sonnet 5 9198638f9b docs/add file-loaded at_each_angle hook example (exposure + step size)
CI for csaxs_bec / test (push) Successful in 2m23s
CI for csaxs_bec / test (pull_request) Failing after 2m3s
Adds a worked example to both lamni.md and flomni.md showing a hook
loaded from a file that temporarily bumps tomo_countingtime and
tomo_shellstep every 5th projection within sub-tomogram 2 (tomo_type 1),
restoring both in a finally block -- demonstrating that a hook can
change any scan parameter, not just move devices like the existing
polarizer example. Notes that progress["subtomo"]/["subtomo_projection"]
work the same way for tomo_types 2/3, just with an open-ended
sub-tomogram count there instead of type 1's fixed 8.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:38:00 +02:00
x01dcandClaude Sonnet 5 1362a63a85 fix/disable lamni's broken upload_last_pon.sh call
Confirmed this script no longer works and needs a rewrite. Commented
out the subprocess.run() call in write_pdf_report(), mirroring Flomni
which already has the identical call disabled the same way. omny.py's
own copy is left untouched (out of scope for this migration).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:24:33 +02:00
x01dcandClaude Sonnet 5 a64d7cff93 refactor/move add_sample_database() into shared TomoQueueMixin
Flomni.add_sample_database() and LamNI.add_sample_database() were
byte-for-byte identical -- both just forward to
self.tomo_id_manager.register(), a TomoIDManager from
omny/omny_general_tools.py that each setup already constructs
independently in its own __init__. Moved the method into
TomoQueueMixin (OMNY_shared), the base class both already inherit,
same "moved code, not changed logic" precedent as the tomo-queue
backend and webpage generator. omny.py's own separate, older
add_sample_database() (different URL, no e-account fallback) is
untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:21:12 +02:00
x01dcandClaude Sonnet 5 f524021cce feat/show estimated finish time and active hook in lamni progress ring
Flomni's ring-progress center label already showed "Est. finish" and
the active at-each-angle hook name; LamNI's equivalent
(_lamnigui_update_progress()) was missing both. Ported the same
formatting/display logic over so the two setups' progress displays
match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:21:05 +02:00
x01dcandClaude Sonnet 5 5be3d0719b feat/include at-each-angle hook in lamni's start-of-scan PDF/scilog
write_pdf_report() didn't mention the active at_each_angle_hook at all,
unlike Flomni's equivalent which already appends the hook's description
and source to both the PDF and its scilog entry. Ported that pattern
over using the same _describe_active_hook()/_active_hook_source() shared
helpers lamni already uses elsewhere (end-of-scan summary,
tomo_parameters() printout) -- no new logic needed, just the missing
call site.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:03:20 +02:00
x01dcandClaude Sonnet 5 636dd0ec88 fix/stop LamNI per-sub-tomogram scilog entries
LamNI's tomo_scan() wrote a separate scilog entry ("Starting subtomo:
N...") every time the sub-tomogram number changed -- 8 extra entries
per tomo_type-1 scan, and one per sub-tomogram traversed for types 2/3
-- leftover behavior carried over from omny.py during the flomni->lamni
port. Flomni itself never does this: it only writes at scan start (PDF
report) and scan end (timing summary). Removed the three
_write_subtomo_to_scilog() call sites and the now-unused method so
LamNI's scilog behavior matches Flomni's.

Also fixed a stale test (test_tomo_queue_reacquire_rejects_when_another_job_is_incomplete)
that encoded the old, buggy tomo_queue_reacquire() invariant (rejecting
on ANY other job being incomplete/running) rather than the fixed one
from the prior commit (only an EARLIER job is a real conflict) --
split it into two tests covering both directions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 15:59:02 +02:00
x01dcandClaude Sonnet 5 887c732865 fix/tomo queue GUI projection count for tomo_type 3
_format_projections() computed type 3's count with type 1's fixed
"N*8 sub-tomograms" formula, ignoring golden_max_number_of_projections
-- the value that actually bounds a type-3 run (lamni.py/flomni.py's
type-3 scan loop has no sub-tomogram cap, it stops only once
ii > golden_max_number_of_projections, same as type 2). This produced
an ~8x-inflated "Projections" column (e.g. 240 instead of 30) for any
type-3 job. Merged type 3 into type 2's branch since both share the
same uncapped/golden_max_number_of_projections model.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 15:41:25 +02:00
x01dcandClaude Sonnet 5 a001bc20bd fix/tomo_queue_reacquire wrongly blocked on a later incomplete job
The single-resumable-job invariant check scanned all other jobs, but
jobs after job_index get force-reset to pending anyway -- so a later
incomplete/running job was never a real conflict, only an earlier one
is (since earlier jobs are left untouched).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 15:12:40 +02:00
x01dcandClaude Sonnet 5 d963cd6dfd feat/auto-refresh stale at_each_angle hooks on redefinition
register_at_each_angle_hook() stores a function object by reference, so
editing a hook's def and forgetting to re-register left the old version
running silently -- easy to miss mid-beamtime. Detect it via
func.__globals__ (a redefined top-level def, or a reloaded module,
mutates the same namespace dict in place) and auto-adopt the new
definition with a printed note instead of failing or staying stale.

Centralizes the shared lookup/error logic (previously duplicated in
lamni.py and flomni.py) into TomoQueueMixin._resolve_at_each_angle_hook().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 15:08:11 +02:00
x01dc f25eea9488 newline removed 2026-07-21 14:23:35 +02:00
x01dcandClaude Sonnet 5 df19ce917a docs/update session summary through e3c979a
Add items 8-12 (timing log, account-change reset, optics
skip-if-in-position, OSA collision warning, tomo_alignment_scan) plus
the BEC_ptycho_align naming fix, updated test count (458), and
corresponding manual-check lines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 13:25:19 +02:00
x01dcandClaude Sonnet 5 e3c979a375 fix/correct matlab tool name to BEC_ptycho_align in tomo_alignment_scan
The tool is called BEC_ptycho_align now, not SPEC_ptycho_align.m as
the previous docs/comments said -- fixes the references introduced in
the previous commit (12b2538) in lamni.py's docstrings/print message
and the user docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 13:24:37 +02:00
x01dcandClaude Sonnet 5 12b2538333 feat/add lamni.tomo_alignment_scan() -- dedicated fine-alignment scan
Replaces the awkward documented workaround (configure a full tomo_type
1 setup with 96 projections, then launch just sub_tomo_scan(1, 0)) with
a dedicated command, ported from flomni's tomo_alignment_scan(): adjust
tomo_parameters() (FOV/step/counting time), then call
lamni.tomo_alignment_scan() directly -- no tomo_type/sub-tomogram
bookkeeping involved, matching flomni's clean two-step workflow.

Runs 12 points evenly spaced across the full 360 degrees (lamni has no
180-degree symmetry the way flomni does, so unlike flomni's 5-point/
180-degree scan, this covers the full circle -- point count matches
what the old workaround's docs defaulted to, endpoint=False since
360==0 degrees). Aborts if x-ray-eye alignment hasn't been done yet
(tomo_fit_xray_eye unset), mirroring flomni's equivalent guard.
write_alignment_scan_numbers() writes the same 4-line scan-number/
angle/offset log flomni's version does, to
~/data/raw/logs/ptychotomoalign_scannum.txt for SPEC_ptycho_align.m,
also printed at the console (flomni's own console-print equivalent is
dead/commented-out code; lamni's actually prints).

Scope note: flomni's version conditionally skips its eye-out/optics-in
transition when already in measurement condition with feedback
running, to avoid an unneeded interferometer reset -- lamni has no
equivalent helpers for that check, so this calls leye_out()
unconditionally instead. Left as a possible follow-up, not in scope
here.

Item 6 of csaxs_bec/bec_ipython_client/plugins/LamNI/AI_docs/
FLOMNI_LAMNI_FEATURE_GAPS_2026-07.md. Documented in
docs/user/ptychography/lamni.md's "Fine alignment" section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 13:22:01 +02:00
x01dcandClaude Sonnet 5 db18929742 feat/add OSA collision-clearance warning to lamni lfzp_info()
Ports flomni's ffzp_info() OSA collision-clearance section (compares
live losaz against nominal "in" position and a collision-boundary
constant, warns if closer to collision than nominal or reports
clearance at IN) -- same formula shape and sign convention as flomni
(motion directions confirmed identical between the two setups).

lamni's collision-boundary constant has never been measured, so it's
read from loptz's device config (userParameter.collision_offset) via
a new _get_user_param_optional() helper -- unlike the existing
_get_user_param_safe(), it returns None instead of raising when the
parameter is undefined (as it currently is for every lamni device
config, including the simulated one), since absence here means
"not yet commissioned" rather than a config error. lfzp_info() prints
a clear warning and skips the numeric estimate entirely until someone
adds the real measured value to the config -- deliberately not a
CLI-settable/global-var parameter, since this is deployment-level
hardware calibration.

Item 7 of csaxs_bec/bec_ipython_client/plugins/LamNI/AI_docs/
FLOMNI_LAMNI_FEATURE_GAPS_2026-07.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 12:58:24 +02:00
x01dcandClaude Sonnet 5 7863b8fd37 feat/skip lfzp_in/losa_in/losa_out moves when already in position
Ports flomni's ffzp_in()/fosa_in() "skip the move (and, for the FZP,
the expensive feedback-disable + reset-enable cycle) if already there"
optimization to lamni's lfzp_in()/losa_in(). Also adds the same
skip-if-already-there check to losa_out(), which has no flomni
equivalent to mirror (fosa_out() always moves unconditionally there)
-- added as a lamni-side enhancement per discussion.

Checked every existing caller of lfzp_in()/loptics_in() before making
this change, since a skip changes behavior for callers that used to
get an unconditional reset. Found one real risk:
x_ray_eye_align.py's "Step 0: FZP centre" (the start of a fresh
alignment run) had its manual feedback disable/enable calls already
commented out, meaning it relied entirely on lfzp_in()'s old
unconditional reset to establish a correct interferometer zero.
flomni's equivalent call site doesn't have this problem because
flomni's alignment flow does its own explicit
feedback_enable_with_reset() independently beforehand -- lamni's
doesn't. Fixed by passing force_feedback_reset=True at that one call
site so it keeps its current always-reset behavior; no other lamni
caller needed this.

Item 4 of csaxs_bec/bec_ipython_client/plugins/LamNI/AI_docs/
FLOMNI_LAMNI_FEATURE_GAPS_2026-07.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 12:40:45 +02:00
x01dcandClaude Sonnet 5 b4c262d41b feat/offer tomo-param reset on lamni experiment-account change
Ports flomni's _maybe_reset_params_on_account_change()/
_set_default_tomo_params() to lamni: at LamNI.__init__, if the active
BEC account differs from the one defaults were last applied for
(persisted via defaults_applied_for_account), interactively offer to
reset tomo params to defaults -- preventing a new experiment silently
inheriting the previous one's tuned FOV/stitch/etc. Same account is a
silent no-op, so restarting a client mid-experiment never disturbs
tuned parameters.

_set_default_tomo_params() is a direct port adapted to lamni's own
param names (tomo_circfov/lamni_stitch_x/y/lamni_piezo_range_x/y
instead of fovx/fovy/stitch_x/y, no tomo_angle_range/
single_point_random_shift_max -- lamni has neither concept). Per-
sample alignment state is deliberately not touched, matching flomni.

Item 2 of csaxs_bec/bec_ipython_client/plugins/LamNI/AI_docs/
FLOMNI_LAMNI_FEATURE_GAPS_2026-07.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 11:37:37 +02:00
x01dcandClaude Sonnet 5 e59a42f978 feat/port timing-statistics log + scilog_last_ptycho_scans() to lamni
Ports flomni's per-projection/tomogram JSONL timing log
(~/data/raw/logs/timing_statistics/*.jsonl, feeding a future scan-time
prediction model) and the scilog_last_ptycho_scans(n) user command to
lamni. Direct port adapted only for lamni's own param names (no
fovx/fovy/stitch_x/stitch_y/single_point_instead_of_fermat_scan --
uses tomo_circfov/lamni_stitch_x/y/lamni_piezo_range_x/y instead, no
single-point concept at all).

_log_projection_timing() is only called on a clean (non-error_caught)
completion of _tomo_scan_at_angle()'s retry loop, so an
AlarmBase/TimeoutError attempt (no reliable duration measurement)
doesn't pollute the log.

Item 1 of csaxs_bec/bec_ipython_client/plugins/LamNI/AI_docs/
FLOMNI_LAMNI_FEATURE_GAPS_2026-07.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:50:08 +02:00
x01dcandClaude Sonnet 5 4f938fc543 docs/document remaining flomni->lamni feature gaps for triage
Follow-up sweep after this session's ports (timing overview, scan
interruption handling, tooltip filtering, constant shift, hook
display, zero-deg reference). Found and verified 9 items: a missing
per-projection/tomogram timing log + scilog_last_ptycho_scans()
command, no reset-tomo-params-on-account-change offer, no
collect_empty_frames() flat-field acquisition, no skip-if-already-in
optimization on lfzp_in(), no hard-stop button wired into lamni's
GUI, tomo_alignment_scan() with no clear lamni equivalent (lamni has
a different lookup-table correction scheme instead -- needs
discussion, not necessarily a gap), no OSA-collision-clearance
warning in lfzp_info(), a large confirmed-hardware-specific block
(sample-transfer/gripper/laser-tracker methods, not portable), and a
webpage-display opportunity that's symmetric on both setups rather
than a one-sided gap. No code changes -- findings only, for review.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 04:33:29 +02:00
x01dcandClaude Sonnet 5 8733b6cf91 docs/update session summary with zero-deg reference feature
Extend commit range to c3877ec, add item 7 (zero_deg_reference_at_each_subtomo
for lamni tomo_type 1), update expected test count to 423, and add a
manual-check line for the new feature.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 04:19:01 +02:00
x01dcandClaude Sonnet 5 c3877ecdef feat/add zero-deg radiation-damage reference shot for lamni tomo_type 1
Ports flomni's zero_deg_reference_at_each_subtomo to lamni's "8 equally
spaced sub-tomograms" mode: an extra dedicated projection at exactly 0
degrees before every sub-tomogram where the rotation naturally passes
back through 0, plus one final shot once the tomogram completes, for
tracking radiation damage over a long acquisition. lamni's tomo_type
2/3 already had the equivalent (golden_projections_at_0_deg_for_damage_estimation,
byte-for-byte identical to flomni's) -- only tomo_type 1 was missing.

_subtomo_starts_near_zero() uses subtomo_number % 2 (every odd
sub-tomogram) rather than flomni's 360-mode subtomo_number % 4 == 1 --
confirmed against lamni's actual rotation behavior rather than derived
from the position-array math, which doesn't cleanly split odd/even.

The GUI (tomo_params.py) needed no new UI code: _build_type1_section()
already gated this control on a per-setup has_zero_deg_reference flag,
so enabling it for lamni was just flipping that flag plus adding the
param name/default, mirroring flomni's entries exactly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 04:18:23 +02:00
x01dcandClaude Sonnet 5 5ee00c997a docs/add session summary + test checklist for this branch's fixes
Brief AI_docs note covering commits 6c3d603..8a7a350: what changed and
what to test (automated + manual/simulated-session) to confirm the
branch is in good shape before relying on it at the beamline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 03:58:33 +02:00
x01dcandClaude Sonnet 5 8a7a350280 feat/warn early about Fermat scans below the minimum point count
FlomniFermatScan/LamNIFermatScan already refuse to run with fewer than
20 positions (_check_min_positions(), raises ScanAbortion), but only
once the scan actually starts -- too late for an unattended tomo-queue
run, where the job just aborts mid-run and pauses the queue. Surface
the same prediction earlier instead, as a non-blocking warning:

- Extracted the position-generating methods on both scan classes
  (get_flomni_fermat_spiral_pos, and lamni's chain --
  _lamni_compute_scan_center, _lamni_compute_stitch_center,
  _compute_total_shift, _lamni_check_pos_in_fov_range_and_circ_fov,
  get_lamni_fermat_spiral_pos) into pure @staticmethods, so the exact
  same algorithm the scan server runs can also be called from
  client-side code without a live scan session. Behavior-preserving --
  verified against the existing exact-position/instruction assertion
  tests for both classes. Lifted the hardcoded "20" into a _MIN_POSITIONS
  class attribute on each, so client code references the same threshold.
- lamni.py/flomni.py: new _expected_fermat_position_count() calls the
  real scan-class algorithm with the live tomo parameters and prints a
  warning line in tomo_parameters() when below the minimum.
- tomo_params.py: a new live-updating "Estimated Fermat scan points"
  field (mirroring the existing achievable-step preview's styling),
  wired to the fov/step/stitch/piezo-range fields, flagged orange below
  the minimum -- never blocks Submit/Add-to-queue.

lamni's circular-FOV crop is angle/stitch-dependent, so the estimate is
representative (center tile, angle 0 for the CLI; the live stitch tile
for the GUI, which has those fields right there) rather than an exact
per-projection guarantee -- documented as such in both places.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 03:52:44 +02:00
x01dcandClaude Sonnet 5 4ada8e85ce docs/document tomo_queue_reacquire and tomo_queue_resume
Add both to the tomo-queue command reference in flomni.md and
lamni.md: tomo_queue_resume() as an alias for tomo_queue_execute(),
and tomo_queue_reacquire(job_index, projection_number) for reopening
a job (including an already-"done" one) at an earlier projection and
cascading later jobs to pending, mirroring the code added in the
previous commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 03:20:55 +02:00
x01dcandClaude Sonnet 5 abc2a3d041 feat/tomo queue reacquire from an earlier projection
Add tomo_queue_reacquire(job_index, projection_number) to the shared
TomoQueueMixin: reopens any queue job (even an already-"done" one) at
an earlier projection than its own recorded progress, and resets every
job queued after it -- tomo and command jobs alike -- to "pending", so
a following tomo_queue_resume() (a new alias for tomo_queue_execute(),
added for naming symmetry with tomo_scan_resume()) re-runs everything
from there forward in order. Reuses the existing tomo_scan_resume()
machinery by writing the requested resume point into the shared
progress global var, rather than adding new parameters to
tomo_scan()/tomo_queue_execute(). Guards against clobbering another
job's progress if one is already "incomplete"/"running" elsewhere in
the queue.

A flat projection number is used as the resume-point unit for all
three tomo_types, including type 1 (8 equally-spaced sub-tomograms),
via a new per-setup _resolve_type1_projection() that maps it to
(subtomo_start, start_angle). flomni's version reuses its existing
_subtomo_angle_plan(); lamni's inline angle math in sub_tomo_scan() was
extracted into an analogous _subtomo_angle_plan() static method first
(behavior-preserving -- verified against the existing angle-math test
suite) so both setups share the same pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 22:00:22 +02:00
x01dcandClaude Sonnet 5 80f6972a31 feat/show at_each_angle_hook in lamni tomo_parameters()
flomni's tomo_parameters() CLI printout already showed the active
at_each_angle_hook; lamni's didn't, making it easy to have a custom
per-projection hook active without noticing from the parameter dump.
Added the identical guarded print, reusing the shared
TomoQueueMixin._describe_active_hook() helper both classes already
inherit. Also switched lamni's tomo_scan() end-of-scan summary to use
that same helper instead of the raw attribute, matching flomni's
equivalent block and correctly flagging a stale/unregistered hook name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 21:37:51 +02:00
x01dcandClaude Sonnet 5 382e325093 feat/print constant shift alongside per-projection corrections
Correction 1, correction 2, and the x-ray-eye correction were already
printed for every projection, but the manual/constant shift
(manual_shift_x/manual_shift_y) applied in the same offset math wasn't
-- added on both setups, right after the existing correction calls in
lamni's tomo_scan_projection() and flomni's tomo_scan_projection() /
tomo_acquire_at_angle().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 21:27:42 +02:00
x01dcandClaude Sonnet 5 cdfc02335d fix/tomo queue tooltip hides fields irrelevant to a job's tomo_type
Hovering a queue entry showed every snapshotted param unconditionally,
including golden-ratio settings on a type-1 (non-golden) job -- and
vice versa, type-1-only fields (angle range, zero-deg reference) on a
golden-ratio job. Reuses _update_type_visibility()'s existing
per-tomo_type section/field gating (already used for the live edit
form) to filter _job_tooltip()'s params list the same way, for both
flomni and lamni jobs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 21:19:10 +02:00
x01dcandClaude Sonnet 5 6c3d60306d feat/lamni tomogram timing overview + repeat-on-interruption
Port two flomni tomo_scan() behaviors lamni was missing: an end-of-scan
timing overview (total time, time excluding gaps, time lost to gaps,
sample/hook info) printed and written to scilog, and automatic repeat
of an entire projection when the beamline interlock trips mid-scan.

The latter needed two pieces together: enabling
bec.builtin_actors.scan_interlock at tomo_scan() start (previously
never enabled for lamni), and wrapping _tomo_scan_at_angle in
@scan_repeat so the resulting ScanRestart (or any other transient
exception) redoes the whole projection instead of aborting the scan.
A new LamNIError marks the one non-retryable case (unregistered
at_each_angle_hook), replacing a plain ValueError there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 21:13:21 +02:00
x01dc 55f6f944f7 fix/improve doc
CI for csaxs_bec / test (push) Successful in 1m34s
2026-07-20 14:37:00 +02:00
x01dc c0fdba0e6c feat/smaract_show_all
CI for csaxs_bec / test (push) Successful in 1m35s
2026-07-20 14:35:09 +02:00
x01dc 6f07c636ad remove data10 references 2026-07-20 14:31:15 +02:00
x01dc f7a1bf4a98 update/docs 2026-07-20 14:22:53 +02:00
x01dc cdc4ae02b5 fix/delays after newpar. lamni needs more time
CI for csaxs_bec / test (push) Successful in 1m35s
2026-07-20 14:18:26 +02:00
x01dc 3c46555df4 fix/increase delay after NEWPAR in FES
CI for csaxs_bec / test (push) Successful in 1m38s
2026-07-20 14:04:20 +02:00
x01dc 64170812a9 config adjustments 2026-07-20 14:04:00 +02:00
x01dcandholler 2863cd51c4 fix(bec_widgets): require sample owner in sample-storage widget
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 2m3s
The owner prompt was labeled and treated as optional, but an occupied
slot must always have an owner recorded. Both the rename and new-sample
dialogs now validate the owner the same way the name field already is:
cancelling or leaving it empty aborts the write instead of silently
falling back to a blank/previous owner.
2026-07-16 16:28:12 +02:00
x01dcandholler d6c0c85a60 fix(bec_widgets): use cached device reads for sample-storage & slit polling
Both widgets' polling loops were issuing live EPICS round-trips every
2s even though the underlying signals are auto_monitor=True and BEC
already keeps a fresh monitored value locally. Switching to
read(cached=True) removes that redundant hardware traffic; for the
slit widget this also makes the old round-robin "selected slit every
tick, others in rotation" strategy unnecessary, so it now polls all
six slits every tick.
2026-07-16 16:28:12 +02:00
x01dcandClaude Sonnet 5 5421c95d26 fix(flomni): unpack sample owner on the status webpage
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 1m36s
FlomniWebpageGenerator._collect_setup_data() read the raw
flomni_samples.sample_names.sample0 DESC signal directly, so once
7e8e807 started packing "name | owner" into that same field (there's
no separate PV for owner), the status page showed the raw
delimiter-joined string as the sample name instead of unpacking it --
same gap FlomniSampleStorage.get_sample_name_and_owner()/show_all()
already handle correctly. Now unpacks via sample_desc_codec.unpack_desc()
and appends "(owner: ...)" when set, mirroring show_all()'s own format.

Lamni has no equivalent gap: it has no automatic sample changer or
storage, so its sample_name global var was never given an owner field.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 15:40:14 +02:00
x01dcandClaude Sonnet 5 940d1c5d75 feat(lamni,flomni): finish lamni webpage generator, share base via OMNY_shared
Extracts WebpageGeneratorBase, HttpUploader, LocalHttpServer, and
make_webpage_generator() out of flomni_webpage_generator.py into
OMNY_shared/webpage_generator_base.py, mirroring the tomo_queue_mixin
precedent, then finishes LamNI_webpage_generator.py (fixes its broken
import, corrects TOMO_TYPES to match lamni's real 3-mode tomo_scan(),
enables HAS_TOMO_QUEUE now that the queue mixin backs it, adds lamni's
own TQ_PARAM_DISPLAY, implements _collect_setup_data()) and wires it
into LamNI.__init__.

Also fixes a real bug surfaced by running two setups against the same
shared status-page directory: each setup's logo is now copied to its
own filename (e.g. flOMNI.png / LamNI.png) instead of a shared
logo.png, since a fixed filename let a browser serve a stale, cached
logo from the other instrument's prior session.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 15:36:39 +02:00
x01dcandClaude Sonnet 5 6744908983 docs(lamni): mark flomni-to-lamni migration status, only webpage left
CI for csaxs_bec / test (pull_request) Successful in 2m34s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 1m50s
FLOMNI_TO_LAMNI_COMPARISON.md was written before any porting work started
and read as a stale plan once the tomo-queue backend and tomo-params GUI
were both done. Add a status pointer at the top so a fresh session can
find what's actually left (the webpage generator) without re-deriving it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 14:22:45 +02:00
x01dcandClaude Sonnet 5 46cadc8894 fix(lamni): show progress GUI on scan start, clear busy heartbeat on exit
Mirrors two fixes already made to Flomni.tomo_scan(): LamNI.tomo_scan()
never opened the progress GUI automatically, and left a stale "busy"
heartbeat for up to 120s after a scan had already finished (or crashed),
since it was only ever reset at the start of the next scan.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 14:21:45 +02:00
x01dcandClaude Sonnet 5 f3c90c5b91 fix(lamni): fix three real tomo_scan() crashes, mirroring flomni's fixes
bec.active_account.decode() crashed on every real tomo_scan() call --
active_account is a plain str, not bytes. Flomni's equivalent never had
the .decode() call and already guards an empty active_account (e.g. a
dev/sim session) by skipping sample-database registration instead of
crashing. Same bug fixed in DataDrivenLamNI.tomo_scan() (extra_tomo.py),
which had an identical inline copy.

write_pdf_report() read a nonexistent "mokev" device; the real device is
ccm_energy. Reads it for real now, wrapped in a broad try/except falling
back to "N/A" if unavailable, rather than a hardcoded placeholder.

write_pdf_report()'s logbook/scilog write crashed when scilog isn't
configured -- lamni.py already had this exact tolerance pattern one
method over, in write_to_scilog(); applied the same try/except here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 13:59:12 +02:00