Skip to content

Rework StandardMovable and StandardFlyable around a single logic, add fly() and ensure_stopped() - #21

Draft
coretl wants to merge 1 commit into
split/Afrom
split/F
Draft

coretl wants to merge 1 commit into
split/Afrom
split/F

Conversation

@coretl

@coretl coretl commented Nov 10, 2026 •

Copy link
Copy Markdown
Owner

Second of six stacked draft PRs that together land the detector rewrite. It reworks the Movable, Flyable and status machinery the detector rewrite is built on, and rewrites no detector. Next: #22

What changes

StandardMovable and StandardFlyable are rebuilt around one logic property and a state machine (ADR 0021, ADR 0022):

  • A Device that moves and flies declares one logic instead of movable_logic and flyable_logic. Logic is the shared base of MovableLogic and FlyableLogic.
  • FlyableLogic.prepare_to_fly() and fly() are async generators. prepare_to_fly yields the context then WatcherUpdates for progress; fly yields KICKED_OFF once flying then progress. Everything a scan starts lives inside the generator, so cancelling it stops it.
  • MovableLogic.stop() becomes the abstract ensure_stopped(), which waits until the hardware has stopped. unstage() cancels a running prepare() or fly, then always calls it.
  • A flyer must be staged before it is prepared, and prepare(), kickoff() and complete() must come in that order, else TransitionError. Staging an already-staged Standard device raises.
  • StoppedError replaces the bare "was stopped" RuntimeError.
  • Motor, SimMotor, the PMAC trajectory and the PandA fly logic are ported. SimMotor becomes a StandardFlyable.
  • WatchableAsyncStatus closes the generator it drives as soon as it is cancelled or fails.
  • add_children_as_readables() registers children in the order they were declared.
  • Adds watch_readback(), cancel_and_wait(), and fixes for cancellations swallowed on Python 3.11.

The full list of breaking changes, additions and fixes is in the CHANGELOG diff.

Breaking changes

Out-of-tree code that subclasses StandardMovable, StandardFlyable, MovableLogic or FlyableLogic must change: rename movable_logic/flyable_logic to logic; replace on_prepare/on_kickoff/on_complete/on_stage/on_unstage/stop with prepare_to_fly/fly/ensure_ready/ensure_stopped, each of which raises TypeError naming its replacement if still defined. Plans that fly a stock bp.fly must now stage and prepare the flyer first. ophyd_async now needs typing-extensions>=4.14. Every item is listed under "Breaking changes" in the CHANGELOG.

Points to question

  • MovableLogic.calculate_timeout defaults to None, so a move has no timeout unless the logic overrides it. This is unchanged from main, where the docs wrongly said the default was DEFAULT_TIMEOUT.
  • A subclass that forgets its logic is now caught both ways: pyright reports reportAbstractUsage at instantiation, and instantiating it raises TypeError: Can't instantiate abstract class ... with abstract method logic. On main only pyright caught it.

How to review

Start with ADR 0021 and ADR 0022, which state the decisions, then core/_standard_base.py (the state machine), core/_flyable.py, core/_movable.py and core/_status.py. The motor, PMAC, PandA and sim ports are mechanical once the core is understood. The behaviour is tested through the public interface: one happy path per device, with small unhappy-path tests for ordering and failure.

Position in the stack

Previous: https://github.com/coretl/ophyd-async/pull/26. Next: #22

AI Disclaimer

The code and this description were generated with AI and reviewed/tweaked by the author.

… fly() and ensure_stopped()

StandardMovable and StandardFlyable take one `logic` property instead of
movable_logic and flyable_logic (ADR 0021). FlyableLogic's hooks become a
prepare_to_fly() generator, a fly() generator that yields KICKED_OFF, and
an abstract ensure_stopped() (ADR 0022). Standard devices raise
TransitionError for a verb in the wrong state and StoppedError when
stop() or a new set() ends a move, prepare or fly early, and unstage()
cancels whatever is running before it stops the hardware.

The motor, pmac, panda and sim motor ports move onto it. The old detector
keeps running on it unchanged.

Assisted-by: Claude Opus 5.5
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.

1 participant