Feat/omny sample ioc #334
No Reviewers
Labels
Clear labels
issue::not confirmed
issue::not reproducible
issue::reproducible
priority::critical
priority::needed
priority::nice to have
scope::backend
scope::frontend
stage::in development
stage::on hold
stage::pre_release
stage::ready for review
stage::selected for development
stage::weekly backlog
type::bug
type::discussion
type::doc
type::feature
type::improvement
type::test
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
Used if an issue has not yet been confirmed by a developer
Used if an issue is not reproducible
Used if a developer was able to reproduce the issue
Should be done as soon as possible
An important update to the code base
Not really needed but a good improvement
An issue regarding the backend services
An issue regarding the user-facing services
This issue is currently being worked on
Something is blocking the development
For issues that have been merged into a pre-release branch and are waiting for their release.
For issues that have been resolved and are ready for review
This issue has been selected for development but has not been scheduled yet
This issue has been selected for development for the next development cycle
It's a bug!
A discussion is wanted/needed
Improvements to the documentation
A new feature
Neither a feature nor a bug but an improvement to the code base
Missing tests or improvements to the test suite
Something is not working
This issue or pull request already exists
New feature
Need some help
Something is wrong
More information is needed
This won't be fixed
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Assignees
acerbo_a (acerbo_a)
bec-gitea
chen_h2 (chen_h2)
debenjak_l (debenjak_l)
diaz (diaz)
divall_e (divall_e)
guizar_m (guizar_m)
haghta_s (haghta_s)
holler (holler)
lombosi_c (lombosi_c)
malu_s (malu_s)
menzel (menzel)
perl_d (perl_d)
usov_i (usov_i)
wakonig_k (wakonig_k)
wang_x1 (wang_x1)
wyzula_j (wyzula_j)
zamofing_t (zamofing_t)
zhang_y7 (zhang_y7)
Clear assignees
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: bec/csaxs_bec#334
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
- sim_galil.py: default unseeded analog-input channels to a voltage that reads back as a realistic ~30 degC motor temperature instead of the previous 0.0V/150 degC default (real motors run well under 60 degC). - rt_omny_ophyd.py: feedback_enable_with_reset() now tells the user the current angle and warns it might take a while when osamroy has more than 5 deg to rotate back to zero, matching flomni's style of heads-up. - omny_sample_transfer_mixin.py: otransfer_put_sample() now calls reset_correction()/reset_tomo_alignment_fit() when mounting into the sample stage (pin_position == 0), mirroring flomni's ftransfer_put_sample() -> ftransfer_flomni_stage_in() pattern. Fixes an AttributeError ('OMNY' object has no attribute 'corr_pos_x') hit when calling tomo_scan_projection() after a sample transfer.bec.active_account is a Redis-backed pgroup, only set via bec-set-account -- a fresh dev/test session normally has it unset. _is_superuser_account() now falls back to os.getenv("USER") in that case, matching the account/system-user comparison convention already used in OMNY_shared/omny_general_tools.py's _accounts_match(). Lets gac-x01dc's superuser grant take effect without requiring bec-set-account first.Two real bugs found while investigating a recurring AttributeError: 'OMNY' object has no attribute 'corr_pos_x' from tomo_scan_projection(). 1. default_correction_file/default_correction_file_x were bare relative filenames ("correction_omny_202204.txt"), unlike flomni's equivalent which resolves an absolute path via Path(csaxs_bec.__file__). open() would only ever succeed if bec were launched from this exact plugin directory -- silently swallowed everywhere else by the existing FileNotFoundError handler in reset_correction(). The real correction files genuinely exist on disk next to the plugin code and were very likely never actually being loaded. 2. corr_pos_x/corr_angle_x/corr_pos_y/corr_angle_y/corr_pos_y_2/ corr_angle_y_2 were plain instance attributes, only ever assigned inside reset_correction() (itself only called from otransfer_put_sample() during a sample transfer) -- hence the AttributeError when calling tomo_scan_projection() beforehand. Converted to global-var-backed properties defaulting to [] when unset, mirroring flomni's corr_pos_y/corr_angle_y/etc. exactly. This supersedes a shallower getattr()-based band-aid from an earlier, uncommitted attempt at this fix. As a bonus, correction data now persists across BEC client restarts, same as flomni's. Note flomni has no x-correction concept at all (only corr_pos_y/ corr_pos_y_2) -- omny's x-correction is its own thing, not something copied from flomni. 17 new/updated tests in test_omny_alignment_mixin.py, including an end-to-end check that reset_correction(use_default_correction=True) now actually loads the real correction_omny_202204*.txt files.Mirko is testing live from his own machine as OS user "holler" with no active_account set, so the superuser fallback (os.getenv("USER")) needs "holler" in the allow-list to see the advanced tomo_parameters() options. OPEN_ISSUES.md had gone stale relative to everything fixed this session: the rotation-offset section still described it as an open, hardware-review -only question (resolved days ago once the real DMC controller source confirmed the real hardware code was always correct -- only the simulator was missing the offset), and the checklist still said scans were blocked entirely (a full omny_fermat_scan now runs end to end). Rewrote both sections to reflect current reality, added a "Current status" summary at the top so a fresh session can orient in one read, and noted the _SUPERUSER_ACCOUNTS change and that advanced tomo mode testing is next. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLrD7sVYGLAzsQjLVJpCgtMirko wants the tomo params GUI widget available for OMNY before running real tomo scans. That widget (Phase 2, separate follow-up) needs a working job queue and scan progress visible outside the CLI session that started it -- neither existed for OMNY. OMNY now inherits TomoQueueMixin (OMNY_shared/tomo_queue_mixin.py), the same shared backend flomni/lamni already use, giving it tomo_queue_add/delete/move/clear/show/execute/ reacquire, at_each_angle_hook registration, and tomo_scan_resume() for the first time. self.progress is now a global-var-backed proxy (_ProgressProxy, byte-for- byte port of flomni's/lamni's own), not a plain in-memory dict -- the load-bearing change the queue mixin and the eventual widget's busy-banner both depend on. tomo_queue_mixin.py's tomo_queue_reacquire() hardcoded tomo_type == 1; generalized via a new _TOMO_TYPE_1_LIKE class attribute (default frozenset({1}), flomni/lamni unaffected) since OMNY has three "equally spaced sub-tomograms" flavors (types 1/4/5), not just one. Along the way, fixed several real bugs in tomo_scan()/write_pdf_report() that predate this session and would have crashed on a real account/ real energy read (bec.active_account.decode() -- active_account is str not bytes; dev.mokev doesn't exist for OMNY, only dev.ccm_energy; an unguarded logbook send; a stale "LamNI" logo/tag copy-paste leftover) -- all direct ports of fixes flomni/lamni already needed and got. Also added the missing unconditional omnygui_show_progress() call and heartbeat- clearing try/finally in tomo_scan(), matching both. Verification: 23 new tests (test_omny_tomo_queue.py, test_omny_tomo_scan.py) covering the mixin wiring, hook dispatch/resolution, tomo_scan_resume()/ _resolve_type1_projection()/reacquire() across all three sub-tomogram flavors, account handling, and heartbeat clearing on both normal completion and a mid-scan exception. Full suite: 702 passed. Not yet live-tested against a running session (the local bec-server may be in active use by Mirko's own parallel session) -- next step is his own pull-and-test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLrD7sVYGLAzsQjLVJpCgtExtends the sample-storage bookkeeping widget, previously FlOMNI-only, to also work against dev.omny_samples: shuttles A/B/C (6 slots each), the sparse fixed-O positions, stage, gripper, and a per-shuttle installed/active parking status (mirrors the oparkz-driven "active parking slot" concept from omny_sample_transfer_mixin.py). Setup detection follows the SETUP_PROFILES/_detect_setup() convention already used by tomo_params.py. Also fixes SimOMNYSampleStorage, which never cleared the fixed-O/shuttle-B-C /parking slots, so they read back as the mock-PV default "0" instead of the real empty sentinels ("-"/"none") -- SimFlomniSampleStorage already does this correctly for the same reason. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Mupip8x7Hean6E3FFvpbjMirko, once Phase 1 (tomo-queue backend) was live-confirmed working: "is the way to move to the widget phase" -- this adds OMNY as a third setup to the tomo params GUI widget, alongside flomni/lamni. Key design decision: replaced the module-level TOMO_TYPES dict (shared across setups, couldn't express "type 1 means 2 sub-tomograms here but 8 there") with a per-profile "tomo_types" dict: {tomo_type_int: {"label": str, "n_subtomos": int | None}}. n_subtomos is not None became the single, uniform test for "is this an equally-spaced type" everywhere (combo box, section visibility, validation, queue-table projections display), since OMNY has three such flavors (types 1/4/5 -- 2/4/8 sub-tomograms) instead of flomni's/lamni's one (always 8). Critical bug caught by verifying the design against the live file, not by the initial design itself: _validate() hardcoded `tomo_type not in (1, 2, 3)` -- would have made the rest of Phase 2 look like it worked (types 4/5 selectable, sections visible, math correct) while silently rejecting every actual submit/queue attempt for an OMNY type-4/5 job. Fixed to check profile membership instead. Also fixed three UI strings that hardcoded "8 equal sub-tomograms" to read the active type's real count. New _compute_type1_omny/_requested_to_stepsize_omny (parameterized by tomo_type via {1:2,4:4,5:8}, matching OMNY._equally_spaced_subtomo_angles()'s own mapping exactly) and _compute_fermat_positions_omny (calls OmnyFermatScan's real static method, same pattern as flomni's). _format_projections() gained a 3-way setup duck-type (OMNY's param set is a strict subset of flomni's -- no positively-unique OMNY-only key exists, so a negative-then-positive check is necessary) that resolves into SETUP_PROFILES rather than duplicating the type-count mapping a third time. Confirmed needing zero changes (verified, not assumed): _job_tooltip(), _build_type23_section(), compute_fermat_positions call sites, _detect_setup() (osamroy as OMNY's discriminator), the busy-banner detection methods (already generic via the shared tomo_progress global var from Phase 1), and the Qt Designer plugin registration files. New test_omny_tomo_params_widget_math.py (29 pure-function tests, no QApplication/QWidget, same style as the existing flomni/lamni test files). Full suite: 761 passed. Not live-tested (needs a real display, same as every GUI change this branch) -- next step is Mirko trying it against a real OMNY session. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLrD7sVYGLAzsQjLVJpCgt- Widget: reject assigning a shuttle to a parking slot already held by a different shuttle ("Slot in use", clear it first) instead of silently overwriting. - Widget: PDF report now shows only the 3 shuttles + their samples (title "OMNY Shuttle Samples") -- stage/gripper/fixed-O positions can't physically leave the vacuum chamber, so a printed report has nothing useful to say about them. The on-screen widget still shows/manages all of it. - Widget: fixed the active-parking-slot badge never showing for a loadlock-driven park. Root cause: otransfer_park_loadlock_slot() drives oparkz to a different target-position table (base -6, "leica") than the regular otransfer_park_slot() (base -0.9); the widget only recognized the regular one. Now checks both. - OMNYSampleStorage: measured status now packs status *and* the beamtime account into one VAL -- 0 = not measured, negative 5-digit = started, positive 5-digit = completed, falling back to FALLBACK_ACCOUNT (11111) for anything that doesn't parse as exactly 5 digits. No IOC schema change needed (same ai record). New get_sample_account()(+gripper/ samplestage variants); set_sample_status() now takes an account param; show_all()/help() updated. otransfer_* CLI wrappers updated to match. - Widget: new P-touch label-print offer, but only when clearing a *shuttle* slot (not fixed-O/stage/gripper -- those are bookkeeping corrections, not physical sample removal, since only shuttle samples leave the vacuum chamber). Uses a separate PTouchLabelPrinter instance at template 3 (not yet bench-tested against a real printer, unlike FlOMNI's confirmed-working template 1), same field names as FlOMNI. - AI_docs/OPEN_ISSUES.md: noted that the measured-status+account encoding should be ported to FlOMNI at some point (separate task, not this branch's scope -- FlOMNI's own status tracking is untouched, still the redis-based _MeasuredSampleLog). - New tests: encode/decode round-trips + fallback behavior for the status+account VAL scheme, plus oparkz leica-target coverage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Mupip8x7Hean6E3FFvpbj- omny_sample_transfer_mixin.py: add otransfer_confirm_dialog(), mirroring flomni.py's ftransfer_confirm_dialog() -- routes sample-transfer confirmations through the GUI console widget (already opened by _omnycam_parking()/_omnycam_samplestage()) when available, instead of always blocking on a terminal input() prompt. Falls back to the plain yesno() prompt if the camera view hasn't been opened yet. All 8 self.OMNYTools.yesno() call sites in the transfer flow now go through it. - simulated_omny.yaml: otransy's gripper-sensor check (_otransfer_check_sensor_connected(), @AN[8001]) always failed in sim -- it fell back to the simulated Galil's shared default for unseeded analog channels (-8.0V, tuned for motor-temperature registers elsewhere), against an expected -2.3V +-0.5V. Added sim_analog_inputs: {8001: -2.3} to seed that specific channel. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Mupip8x7Hean6E3FFvpbjThe bare YAML int key (8001:) broke config reload entirely: BEC's msgpack config transport uses strict_map_key=True, which rejects non-string map keys outright ("int is not allowed for map key"). Quoting it ("8001":) matches the existing convention already used in simulated_flomni.yaml's own sim_analog_inputs, and round-trips through msgpack correctly -- configure_axis() already does int(chan) internally, so the string key still resolves to the same channel. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Mupip8x7Hean6E3FFvpbj- gui_tools.py: _omnycam_parking()/_omnycam_samplestage() now share one _omnygui_show_omnycam_pair() helper that creates the two camera Image docks + console once and re-points them via Image.image(device=...) on later switches instead of tearing down and rebuilding the whole GUI every time (bec_widgets' Image.image() already supports being called again to swap the monitored device). Also stops live mode on the outgoing camera pair on a switch, and drops the dead _omnycam_check_device_exists no-op guard in favor of the `in dev` idiom already used for cam_xeye in this file. - omny_sample_transfer_mixin.py: the two remaining blocking input("Ready. Press Enter...") prompts in otransfer_put_sample/ otransfer_get_sample now go through otransfer_confirm_dialog() like every other transfer confirmation, so they show up in the GUI console instead of blocking on a terminal keypress; fixed the get_sample prompt's copy-pasted "mount" wording to "unmount". - simulated_omny.yaml: otransx sim_velocity 10->100 mm/s; oshield gained sim_velocity: 10.0 (previously unset, defaulting to the Smaract sim's 1 mm/s) -- both x10 per request. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Mupip8x7Hean6E3FFvpbj- sim_omny.py: SimOMNYSampleStorage now seeds shuttle A (which already holds the default sim_sample_1/2/3) as parked at slot 6 -- saves calling dev.omny_samples.set_shuttle_slot('A', 6) by hand at the start of every test session. (oshuttlealign already starts pre-referenced/down from the previous commit's sim_limit_high_active change -- confirmed still in place, no further change needed there.) - omny_general_tools.py: gui_yesno() no longer appends a "[y]/n?"-style text hint to the message -- the ConsoleButtonsWidget already shows actual Yes/No buttons, so the text was redundant (only the terminal-only yesno() prompt still needs it). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Mupip8x7Hean6E3FFvpbj