20. A built-in positioner¶
Date: 2026-10-03
Status¶
Accepted
Context¶
Every session that moves a stage by hand needs a presenter that moves its
axes and a view to drive them. Each plugin wrote both for its own device
class, and the views took the names of the axes as given: an arrow pad for
x and y, a pair of buttons for z. A device with other axes, or other
names for them, needed another view.
Nothing about moving an axis by hand depends on the device. ophyd-async
already says what an axis can do: set moves it, locate reports where it
is, subscribe follows its readback, and a Stoppable one can be stopped.
Speed, acceleration and the like are the device's configuration, read with
describe_configuration.
Decision¶
redsun ships PositionerPresenter and PositionerView, registered under
the plugin id positioner.
- An axis is found by protocol on a device and its descendants: it is
AsyncLocatableandSubscribable, and not aSignal, since every writable signal can be set and located too. The search stops at the first axis on each branch. Axes are never chosen by name or by count. - The view has one row per axis, grouped by device: readback, step buttons repeating while held, step size, go-to, and per device its state, a Stop button for stoppable axes, and saved positions kept in the session's settings.
- The readback of a
StandardMovableis the signal itsmovable_logicnames; hints are read only for other axes, whose first hinted field is taken as the readback. - Limits come from the readback descriptor's
limits, when a device reports them. An offset or a limit written elsewhere moves them, so the presenter reads them again after each configuration write to the device and after a move that is refused or fails; a change reaches the view onsig_limits. An axis that is notCheckablehas them read before each move as well. ACheckableaxis checks its targets when it moves, as the EPICS motor does against its soft limits, so reading them first would only cost each step a round trip. - The configuration of every axis is shown in a
DescriptorTreeViewand written through the presenter. A signal a device does not declare as configuration is not looked for by attribute name. - Every target passes the presenter's
check, a step's as well as a go-to's: a target must be a finite number within the axis' limits, and a subclass refuses more by overriding it. A step starts from the setpoint, and from the readback after a stop or a failure. - A plan's locks reach the presenter as well as the view: a held device takes no hand move, waiting go-to or configuration write.
- The links between the two components, with the
DescribesAxesprotocol the view asks for, are their whole interface, so either can be replaced.PositionerGroupis public for components of your own. - Reading a device goes in
redsun.utils.devices, apart from the presenter: finding its axes, describing an axis, its limits and its configuration with what can be written. Any component can use them.
Consequences¶
- The names of both components, their signals and slots,
DescribesAxes,PositionerGroupand the helpers ofredsun.utils.devicesare public API. - Both components can be subclassed: the presenter is a dataclass whose
public slots are overridden, and the view takes a
group_classand exposes itstabs. Their private methods are not promised to subclasses. - A device's own behaviour stays in the device: its
MovableLogicdecides the tolerance of a move, what stopping does and how long a move may take. - A device shows a setting in the Configuration tab by declaring it
CONFIG_SIGNAL. - No velocity, acceleration or limit controls exist beyond what devices declare.