Skip to content

Beam suspender: replace the plain SuspendFloor with one that checks the ring's operating mode - #93

Merged
Anthony Sligar (sligara7) merged 3 commits into
NSLS2:mainfrom
sligara7:hex-beam-suspender-ops-mode
Oct 12, 2026
Merged

Anthony Sligar (sligara7) merged 3 commits into
NSLS2:mainfrom
sligara7:hex-beam-suspender-ops-mode

Conversation

@sligara7

@sligara7 Anthony Sligar (sligara7) commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Replaces the plain beam-current suspender in the profile with a mode-aware one that checks the storage ring's operating mode every time it is evaluated, so low beam does not suspend the RunEngine while the ring is in maintenance or shutdown.

Why

main installs SuspendFloor(storage_ring.beam_current, 100, resume_thresh=390) unconditionally, so scans suspend on low beam even when the ring is down for maintenance or shutdown. An earlier approach (on a working branch, not on main) instead read the mode once at startup and skipped installing the suspender; that never sees a mode change later in the session. Per Jakub's review, the check now lives inside the suspender.

Changes

  • hextools.suspenders.SuspendFloorUnlessOpsMode: a SuspendFloor that also subscribes to the ring's operating mode and folds it into _should_suspend / _should_resume:
    • normal operation: suspend below 100 mA, resume above 390 mA, as before;
    • MAINTENANCE / SHUTDOWN: low beam does not suspend, and entering one of these modes releases a tripped suspender;
    • returning to operations with beam still low trips it straight away;
    • until the mode has been read, it behaves like a plain SuspendFloor;
    • both the beam and the mode signal may be ophyd-async signals or ophyd-style ones, as SuspendFloor allows.
  • profiles/collection.py: installs it in place of the plain SuspendFloor, with skip_modes=(MAINTENANCE, SHUTDOWN).

How it was verified: mock

  • tests/test_suspenders.py (9 tests, mock NSLS2StorageRing and ophyd Signals): trips on low beam in operations; ignores low beam in maintenance and in shutdown; releases when the ring enters maintenance while tripped; trips when operations resume with beam still low; resumes on beam recovery; works with ophyd-style beam, mode, or both. The mode tests and the ophyd-style mode tests were each observed failing before their fix.
  • Full suite: 128 passed; the 9 failures are the same ones that fail on main (60acf66), so CI will show them here too.
  • Not yet run against the real ring PVs, the simulator, or a beamline.

Assisted-by: copilot:claude-opus-4-8

…is in maintenance or shutdown

Per Jakub's review of the startup-only guard (a8feb5c / d35b1c9): checking the ring's operating mode once at profile load means a mode change later in the session is never seen, and a ring in maintenance at startup leaves no suspender at all once operations resume. SuspendFloorUnlessOpsMode (hextools.suspenders) subscribes to the operating mode and checks it every time it is evaluated: low beam in MAINTENANCE/SHUTDOWN does not suspend; entering such a mode releases a tripped suspender; returning to operations with beam still low trips it. Until the mode is read it behaves like a plain SuspendFloor. The profile installs it unconditionally.

Verification: mock - tests/test_suspenders.py (6 tests; the 4 mode tests were observed failing against a plain SuspendFloor first). Full suite: 125 passed, the same 9 failures as main.

Assisted-by: copilot:claude-opus-4-8
Copilot AI balanced review requested due to automatic review settings October 5, 2026 21:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Legacy ophyd signals are reevaluated using the wrong callback form, and the PR description conflicts with the base diff.

Review effort: Balanced
Findings: 1 Medium severity · 1 Low severity

Open (2)
What changed in this PR

Adds mode-aware beam-current suspension that reacts to storage-ring mode changes.

Changes:

  • Adds SuspendFloorUnlessOpsMode.
  • Installs it in the collection profile.
  • Adds mode-transition tests.
File Description
src/​hextools/​suspenders.py Implements mode-aware suspension.
src/​hextools/​profiles/​collection.py Configures the new suspender.
tests/​test_suspenders.py Tests current and mode transitions.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/hextools/suspenders.py Outdated
Comment thread src/hextools/profiles/collection.py
…th value=

PR 93 review. Measured with new tests using ophyd Signals: a legacy beam signal already worked (SuspenderBase.__call__ takes value positionally), but a legacy ops-mode signal failed with AttributeError, because install/remove assumed the mode signal has subscribe_reading/clear_sub. The mode signal now follows the same two callback styles SuspendFloor supports for its own signal (Subscribable -> subscribe_reading on the RE loop; otherwise subscribe(run=True)/unsubscribe(cid)). Re-evaluation passes value= by keyword. test_legacy_ophyd_signals[mode|both] observed failing before the fix; 9/9 suspender tests pass; full suite 128 passed, same 9 failures as main.

Assisted-by: copilot:claude-opus-4-8
Copilot AI balanced review requested due to automatic review settings October 5, 2026 22:08
@sligara7 Anthony Sligar (sligara7) changed the title Beam suspender: always installed, check the ring's operating mode every time Beam suspender: replace the plain SuspendFloor with one that checks the ring's operating mode Oct 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Concurrent mode and beam callbacks can leave the cached beam value stale.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (2)

Comment thread src/hextools/suspenders.py Outdated
A ring-mode change re-evaluated the suspender by reading _last_value and replaying it outside any lock. A beam reading arriving on another thread in between (mixed ophyd / ophyd-async signals call back on different threads) was overwritten by the stale replay, so the next mode change judged the wrong beam current. Both paths now hold a subclass-owned RLock. The base SuspenderBase._lock is a plain Lock, and _set_mode calls back into __call__, so taking that lock here would deadlock.

Verified with mock tests: test_beam_update_during_mode_change_is_not_lost forces the interleaving and failed before this change.

Assisted-by: copilot:claude-opus-4-8
Copilot AI balanced review requested due to automatic review settings October 6, 2026 14:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The concurrency-sensitive behavior affects live RunEngine suspension and has not been validated against real ring signals.

Review effort: Balanced
Findings: None

Resolved since last review (1)

@sligara7
Anthony Sligar (sligara7) merged commit dabc0bd into NSLS2:main Oct 12, 2026
0 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants