add: arbitrary-axis HKL chain and profile-driven IOC RSM editor (2/4, closes #132) - #137
Conversation
There was a problem hiding this comment.
For issue 137 on a previously existing profile, I try to reseed, so add the IOC_RSM_PARAMETER onto the profile. It adds it to the profile, but when I attempt to open the HKL Setup I get this error below.
ValueError: IOC_RSM_PARAMETER.SAMPLE_AXES must be a list of axis tables
Traceback (most recent call last):
File "", line 198, in _run_module_as_main
File "", line 88, in _run_code
File "/home/beams0/OODIASEIGIEHON/DashPVA/src/dashpva/consumers/ioc_rsm_parameter.py", line 26, in
import dashpva.settings as app_settings
File "/home/beams0/OODIASEIGIEHON/DashPVA/src/dashpva/settings.py", line 533, in
reload()
File "/home/beams0/OODIASEIGIEHON/DashPVA/src/dashpva/settings.py", line 325, in reload
cfg = resolve_profile_config(raw_cfg)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/beams0/OODIASEIGIEHON/DashPVA/src/dashpva/utils/config/resolver.py", line 115, in resolve_profile_config
for index, axis in enumerate(_canonical_axes(parameters, key), start=1):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/beams0/OODIASEIGIEHON/DashPVA/src/dashpva/utils/config/resolver.py", line 69, in _canonical_axes
raise ValueError(f"IOC_RSM_PARAMETER.{key} must be a list of axis tables")
ValueError: IOC_RSM_PARAMETER.SAMPLE_AXES must be a list of axis tables
… paths Follow-up to the review fixes on #137. Empty tables were still corrupted. The previous commit taught the config tree to round-trip lists, but a table with no children is indistinguishable from a leaf, so its blank display text was read back as ''. That turned `[HKL]` -- empty by design once IOC_RSM_PARAMETER generates it -- into a string, and because the same commit added pre-save validation the result was Apply & Save failing outright with "Refusing to save an unloadable profile: HKL must be a table" on the very layout this stack ships. Group items now carry a marker so an empty table extracts as {} rather than falling into the leaf path. The list round-trip also worked only by accident. PyQt stores a dict as a key-sorted QVariantMap, so a list of axis tables came back with its fields reordered and the "text still matches str(original)" guard never matched; recovery happened via the ast.literal_eval fallback instead, and every save silently rewrote the axis tables with alphabetised keys. Values now travel in an opaque wrapper Qt passes through untouched, and edits are detected by comparing against the text actually rendered. The round-trip is now an exact identity, key order included. Removes two redundant configuration paths. AGENTS.md is explicit that the database is the config source and TOML is the import/export format; both of these were a third way: - HKL Setup's "Import legacy JSON..." button, along with migrate_legacy_json and the ~/.config path constant. The Config tab's Import TOML Config and Export Config to File already round-trip the whole profile, including IOC_RSM_PARAMETER. - The --snapshot temp file. The IOC child was handed the entire profile as a serialized JSON file; it now reads the database through the ConfigSource path it already had as a fallback. Activation only happens after a successful compare-and-swap save, so the database already is the approved snapshot -- the file added a format and a cleanup path for nothing. The stdin/stdout JSON pipe to the IOC child stays: it predates this stack and carries live PV readings, not configuration. Adds test_workflow_config_tree.py (25 tests) driving the real QTreeWidget, since the mechanism under test is how PyQt stores item data. Asserts the shipped sample_config round-trips identically and still resolves -- Osayi's crash as a regression test -- plus the same for a pre-canonical profile with a populated [HKL] and no IOC_RSM_PARAMETER, empty tables staying tables, every list-valued key keeping type and value, axis field order surviving, and an edited list recovering structurally.
|
Thanks @Osayi-ANL — reproduced exactly, and it's ours, not something pre-existing you tripped over. It was broader than reseed. Fixed across two commits:
New 480 tests pass on the stack tip, The stack is also rebased so #138/#139 show clean diffs again — Worth a |
|
Traced this to the exact commit: the traceback matches Both were fixed the same day, ~20–60 min after this review:
I reproduced your exact scenario against current tip (legacy profile → reseed → adds Current tip is |
3bff25c to
742a441
Compare
91924cf to
f0a44b2
Compare
769f952 to
336ce0d
Compare
Stack 2 of 4. Base
rsm/01-config-geometry— retarget tomainafter PR 1 merges. Simulator only, no beam time.Closes #132.
The problem
Making the IOC editor generic is necessary but not sufficient. Five other places capped the geometry at 4 sample + 2 detector circles, and every one failed silently — wrong physics, no error:
hdf5_writer.py:202rsm_converter.py:200range(1,5)/range(1,3)on readhpc_rsm_consumer.py:197pv_name.endswith('Position')area_det_viewer.py:1889settings.py:78-81sample_phi/chi/eta/mu, no detector links at allA beamline whose PV ends in
.RBVlost that circle. A kappa goniometer was unrepresentable.What this changes
Chain.
resolved_axis_groupsdiscovers numbered groups per role, sorted by integer suffix so_10follows_9, with the legacyMU/ETA/CHI/PHIlayout kept as a per-role fallback. Dispatch is on semantic config keys (DIRECTION_AXIS,POSITION), never PV-name shape. NeXus transformation links are generated per axis from the stableRECORD_NAME, and detector circles finally get links too. Axis metadata is preflighted before the output file is created, so a missing direction fails by name instead of dropping a circle.Editor. Ordered
SAMPLE_AXES/DETECTOR_AXESarrays;LABELsplit from an EPICS-safeRECORD_NAMEso renaming an axis in the GUI doesn't rename its PV; per-axisDIRECTIONandANGLE_UNITS;ENERGY_UNITS;SAMPLE_ORIENTATION;SCHEMA_VERSION. Add/remove/rename/reorder, any circle count including zero for a role when the orientation makes that valid. Sample axes accept[xyzk][+-], so kappa goniometers work.One staged
Apply & Save: validate → show the first-adoption diff → CAS-save → read back → restart the IOC, which re-reads the profile from the database. A revision conflict applies nothing and sends no IOC commands.Per AGENTS.md the database is the config source and TOML is the import/export format, so this PR keeps exactly those two: the Config tab's Import TOML Config / Export Config to File already round-trip the whole profile including
IOC_RSM_PARAMETER. There is no separate JSON import, and the IOC child is no longer handed a serialized snapshot file.Verification
Simulator IOC; 0/3/8-axis geometries;
_10ordering; fifth-axis HDF5 round trip; kappa vectors; cross-roleRECORD_NAMEuniqueness;.RBV-suffixed sources; CAS conflict behavior; old profile and old file compatibility. Full suite green,ruffclean.