Update id logic to use standard movable - #2161
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2161 +/- ##
==========================================
- Coverage 99.16% 99.15% -0.01%
==========================================
Files 352 356 +4
Lines 13824 13871 +47
==========================================
+ Hits 13708 13754 +46
- Misses 116 117 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Relm-Arrowny
left a comment
There was a problem hiding this comment.
look good thanks for doing all the work.
| await set_move.set(value=1, timeout=timeout) | ||
| await wait_for_value(gate, UndulatorGateStatus.CLOSE, timeout=timeout) |
There was a problem hiding this comment.
We probably have not test it with the real system enough, should add to avoid racing condition. :
| await set_move.set(value=1, timeout=timeout) | |
| await wait_for_value(gate, UndulatorGateStatus.CLOSE, timeout=timeout) | |
| await set_move.set(value=1, timeout=timeout) | |
| await wait_for_value(gate, UndulatorGateStatus.OPEN, timeout=5) | |
| await wait_for_value(gate, UndulatorGateStatus.CLOSE, timeout=timeout) |
|
|
||
| def __init__(self, prefix: str, name: str = ""): | ||
| super().__init__(prefix=prefix, name=name) | ||
| self.set_move = epics_signal_rw(int, prefix + "BLGSETP") |
There was a problem hiding this comment.
nit: we made set move SignalW through out so I am not sure which we want to change. I probably does not matter in practice.
| self.set_move = epics_signal_rw(int, prefix + "BLGSETP") | |
| self.set_move = epics_signal_w(int, prefix + "BLGSETP") |
| btm_inner: str, | ||
| name: str = "", | ||
| ): | ||
| # Gap demand set point and readback |
There was a problem hiding this comment.
Could:Not gap
| # Gap demand set point and readback |
| self.top_outer = UndulatorPhaseMotor(prefix=f"{prefix}BL{top_outer}") | ||
| self.btm_inner = UndulatorPhaseMotor(prefix=f"{prefix}BL{btm_inner}") | ||
| # Nothing move until this is set to 1 and it will return to 0 when done. | ||
| self.set_move = epics_signal_rw(int, f"{prefix}BL{top_outer}" + "MOVE") |
There was a problem hiding this comment.
nit:?
| self.set_move = epics_signal_rw(int, f"{prefix}BL{top_outer}" + "MOVE") | |
| self.set_move = epics_signal_w(int, f"{prefix}BL{top_outer}" + "MOVE") |
| def __init__(self, prefix: str, name: str = ""): | ||
| motor_pv = f"{prefix}MTR" | ||
| super().__init__(motor_pv, prefix + "SET", name=name) | ||
| del self.motor_stop |
There was a problem hiding this comment.
should: matching gap and its safer.
| del self.motor_stop | |
| self.motor_stop = None |
| class UndulatorGap(SafeUndulatorMoverBase[float], Flyable, Preparable): | ||
| """Apple2 undulator gap device. | ||
|
|
||
| Wraps an internal :class:`UndulatorGapMotor` and exposes the Apple2 | ||
| controller interface, where demand positions are written separately from | ||
| triggering motion via a dedicated move PV. | ||
|
|
||
| Supports standard moves as well as fly scanning. | ||
|
|
||
| Args: | ||
| prefix: Beamline-specific PV prefix. | ||
| name: Device name. | ||
| """ | ||
|
|
||
| def __init__(self, prefix: str, name: str = ""): | ||
| super().__init__(prefix=prefix, name=name) | ||
| self.set_move = epics_signal_rw(int, prefix + "BLGSETP") | ||
| with self.add_children_as_readables(): | ||
| self.motor = UndulatorGapMotor( | ||
| prefix, self.gate, self.status, self.set_move | ||
| ) | ||
|
|
||
| async def set_demand_positions(self, value: float) -> None: | ||
| await self.motor.user_setpoint.set(value) | ||
|
|
||
| async def get_timeout(self) -> float: | ||
| return await estimate_motor_timeout_from_signals( | ||
| self.motor.user_setpoint, self.motor.user_readback, self.motor.velocity | ||
| ) | ||
|
|
||
| @AsyncStatus.wrap | ||
| async def prepare(self, value: FlyMotorInfo) -> None: | ||
| """Prepare for a fly scan by moving to the run-up position at max velocity. | ||
| Stores fly info for later use in kickoff. | ||
| """ | ||
| await self.motor.prepare(value) | ||
|
|
||
| @AsyncStatus.wrap | ||
| async def kickoff(self): | ||
| await self.motor.kickoff() | ||
|
|
||
| def complete(self) -> WatchableAsyncStatus: | ||
| return self.motor.complete() |
There was a problem hiding this comment.
I think the safe SafeUndulatorMoverBase is already built in the UndulatorGapMotor
this: #2167
Fixes #2158
This fixes the insertion device logic to be compatible with
StandardMovable, specifically theUndulatorGap. This has also restructured the ID logic so that we now these new files:Other notable change is
MotorStringSetpointwhich allows us to still useMotorwhich have setpoints as str still take floats using a derived signal, allowing the ID logic to not break when usingMotorMovableLogic(soon to beMotorFlyableMoveLogic).This has made the logic much more modular and easier to understand. Please note I have not altered the actual logic or the tests so this hasn't changed the ID behaviour. This was necessary because
Motorhas changed to useStandardMovable(and in ophyd-async 0.21.1StandardFlyable) which made the current API conflicting as it was overriding methods it shouldn't of. This will make the migration to ophyd-async 0.21.1 much easier to manage. I've also added more documentation to classes to make it easier for someone else to maintain in the future.Instructions to reviewer on how to test:
Checks for reviewer
dodal connect ${BEAMLINE}