Skip to content

Commit

Permalink
Test ophyd async speedup (#895)
Browse files Browse the repository at this point in the history
* Pin to ophyd-async == 0.8.0a4

* Fix tests against the faster ophyd-async

* Remove accidentally added file

---------

Co-authored-by: Robert Tuck <robert.tuck@diamond.ac.uk>
  • Loading branch information
DominicOram and rtuck99 authored Nov 12, 2024
1 parent d28d4dc commit f896461
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 42 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ description = "Ophyd devices and other utils that could be used across DLS beaml
dependencies = [
"click",
"ophyd",
"ophyd-async==0.8.0a3",
"ophyd-async==0.8.0a4",
"bluesky",
"pyepics",
"dataclasses-json",
Expand Down
40 changes: 21 additions & 19 deletions src/dodal/devices/apple2_undulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from bluesky.protocols import Movable
from ophyd_async.core import (
AsyncStatus,
Reference,
StandardReadable,
StandardReadableFormat,
StrictEnum,
Expand Down Expand Up @@ -387,9 +388,9 @@ def __init__(

# Attributes are set after super call so they are not renamed to
# <name>-undulator, etc.
with self.add_children_as_readables():
self.gap = id_gap
self.phase = id_phase
self.gap = Reference(id_gap)
self.phase = Reference(id_phase)

with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
# Store the polarisation for readback.
self.polarisation, self._polarisation_set = soft_signal_r_and_setter(
Expand Down Expand Up @@ -436,27 +437,29 @@ async def _set(self, value: Apple2Val, energy: float) -> None:
"""

# Only need to check gap as the phase motors share both fault and gate with gap.
await self.gap.check_id_status()
await self.gap().check_id_status()
await asyncio.gather(
self.phase.top_outer.user_setpoint.set(value=value.top_outer),
self.phase.top_inner.user_setpoint.set(value=value.top_inner),
self.phase.btm_inner.user_setpoint.set(value=value.btm_inner),
self.phase.btm_outer.user_setpoint.set(value=value.btm_outer),
self.gap.user_setpoint.set(value=value.gap),
self.phase().top_outer.user_setpoint.set(value=value.top_outer),
self.phase().top_inner.user_setpoint.set(value=value.top_inner),
self.phase().btm_inner.user_setpoint.set(value=value.btm_inner),
self.phase().btm_outer.user_setpoint.set(value=value.btm_outer),
self.gap().user_setpoint.set(value=value.gap),
)
timeout = np.max(
await asyncio.gather(self.gap.get_timeout(), self.phase.get_timeout())
await asyncio.gather(self.gap().get_timeout(), self.phase().get_timeout())
)
LOGGER.info(
f"Moving f{self.name} energy and polorisation to {energy}, {self.pol}"
+ f"with motor position {value}, timeout = {timeout}"
)

await asyncio.gather(
self.gap.set_move.set(value=1, timeout=timeout),
self.phase.set_move.set(value=1, timeout=timeout),
self.gap().set_move.set(value=1, timeout=timeout),
self.phase().set_move.set(value=1, timeout=timeout),
)
await wait_for_value(
self.gap().gate, UndulatorGateStatus.close, timeout=timeout
)
await wait_for_value(self.gap.gate, UndulatorGateStatus.close, timeout=timeout)
self._energy_set(energy) # Update energy for after move for readback.

def _get_id_gap_phase(self, energy: float) -> tuple[float, float]:
Expand Down Expand Up @@ -521,12 +524,11 @@ async def determinePhaseFromHardware(self) -> tuple[str | None, float]:
(May be for future one can use the inverse poly to work out the energy and try to match it with the current energy
to workout the polarisation but during my test the inverse poly is too unstable for general use.)
"""
cur_loc = await self.read()
top_outer = cur_loc[self.phase.top_outer.user_setpoint_readback.name]["value"]
top_inner = cur_loc[self.phase.top_inner.user_setpoint_readback.name]["value"]
btm_inner = cur_loc[self.phase.btm_inner.user_setpoint_readback.name]["value"]
btm_outer = cur_loc[self.phase.btm_outer.user_setpoint_readback.name]["value"]
gap = cur_loc[self.gap.user_readback.name]["value"]
top_outer = await self.phase().top_outer.user_setpoint_readback.get_value()
top_inner = await self.phase().top_inner.user_setpoint_readback.get_value()
btm_inner = await self.phase().btm_inner.user_setpoint_readback.get_value()
btm_outer = await self.phase().btm_outer.user_setpoint_readback.get_value()
gap = await self.gap().user_readback.get_value()
if gap > MAXIMUM_GAP_MOTOR_POSITION:
raise RuntimeError(
f"{self.name} is not in use, close gap or set polarisation to use this ID"
Expand Down
8 changes: 4 additions & 4 deletions src/dodal/devices/i10/i10_apple2.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def __init__(
name=name,
)
with self.add_children_as_readables():
self.id_jaw_phase = id_jaw_phase
self.id_jaw_phase = Reference(id_jaw_phase)

@AsyncStatus.wrap
async def set(self, value: SupportsFloat) -> None:
Expand Down Expand Up @@ -148,8 +148,8 @@ async def set(self, value: SupportsFloat) -> None:
LOGGER.info(f"Setting polarisation to {self.pol}, with {id_set_val}")
await self._set(value=id_set_val, energy=value)
if self.pol != "la":
await self.id_jaw_phase.set(0)
await self.id_jaw_phase.set_move.set(1)
await self.id_jaw_phase().set(0)
await self.id_jaw_phase().set_move.set(1)

def update_lookuptable(self):
"""
Expand Down Expand Up @@ -299,7 +299,7 @@ async def set(self, value: SupportsFloat) -> None:
f"jaw_phase position for angle ({value}) is outside permitted range"
f" [-{self.jaw_phase_limit}, {self.jaw_phase_limit}]"
)
await self.id_ref().id_jaw_phase.set(jaw_phase)
await self.id_ref().id_jaw_phase().set(jaw_phase)
self._angle_set(value)


Expand Down
4 changes: 2 additions & 2 deletions src/dodal/devices/oav/oav_detector.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from enum import IntEnum

from ophyd_async.core import DEFAULT_TIMEOUT, AsyncStatus, StandardReadable
from ophyd_async.core import DEFAULT_TIMEOUT, AsyncStatus, LazyMock, StandardReadable
from ophyd_async.epics.core import epics_signal_rw

from dodal.common.signal_utils import create_hardware_backed_soft_signal
Expand Down Expand Up @@ -118,7 +118,7 @@ async def _get_beam_position(self, coord: int) -> int:

async def connect(
self,
mock: bool = False,
mock: bool | LazyMock = False,
timeout: float = DEFAULT_TIMEOUT,
force_reconnect: bool = False,
):
Expand Down
32 changes: 16 additions & 16 deletions tests/devices/i10/test_i10Apple2.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,10 @@ async def test_I10Apple2_determine_pol(
btm_inner_phase: float,
btm_outer_phase: float,
):
set_mock_value(mock_id.phase.top_inner.user_setpoint_readback, top_inner_phase)
set_mock_value(mock_id.phase.top_outer.user_setpoint_readback, top_outer_phase)
set_mock_value(mock_id.phase.btm_inner.user_setpoint_readback, btm_inner_phase)
set_mock_value(mock_id.phase.btm_outer.user_setpoint_readback, btm_outer_phase)
set_mock_value(mock_id.phase().top_inner.user_setpoint_readback, top_inner_phase)
set_mock_value(mock_id.phase().top_outer.user_setpoint_readback, top_outer_phase)
set_mock_value(mock_id.phase().btm_inner.user_setpoint_readback, btm_inner_phase)
set_mock_value(mock_id.phase().btm_outer.user_setpoint_readback, btm_outer_phase)

if pol is None:
with pytest.raises(ValueError):
Expand Down Expand Up @@ -234,7 +234,7 @@ async def test_fail_I10Apple2_set_lookup_gap_pol(mock_id: I10Apple2):


async def test_fail_I10Apple2_set_undefined_pol(mock_id: I10Apple2):
set_mock_value(mock_id.gap.user_readback, 101)
set_mock_value(mock_id.gap().user_readback, 101)
with pytest.raises(RuntimeError) as e:
await mock_id.set(600)
assert (
Expand All @@ -244,15 +244,15 @@ async def test_fail_I10Apple2_set_undefined_pol(mock_id: I10Apple2):


async def test_fail_I10Apple2_set_id_not_ready(mock_id: I10Apple2):
set_mock_value(mock_id.gap.fault, 1)
set_mock_value(mock_id.gap().fault, 1)
with pytest.raises(RuntimeError) as e:
await mock_id.set(600)
assert str(e.value) == mock_id.gap.name + " is in fault state"
set_mock_value(mock_id.gap.fault, 0)
set_mock_value(mock_id.gap.gate, UndulatorGateStatus.open)
assert str(e.value) == mock_id.gap().name + " is in fault state"
set_mock_value(mock_id.gap().fault, 0)
set_mock_value(mock_id.gap().gate, UndulatorGateStatus.open)
with pytest.raises(RuntimeError) as e:
await mock_id.set(600)
assert str(e.value) == mock_id.gap.name + " is already in motion."
assert str(e.value) == mock_id.gap().name + " is already in motion."


async def test_I10Apple2_RE_scan(mock_id: I10Apple2, RE: RunEngine):
Expand Down Expand Up @@ -341,23 +341,23 @@ async def test_I10Apple2_pol_set(
else:
await mock_id_pol.set(pol)
assert mock_id_pol.id.pol == pol
top_inner = get_mock_put(mock_id_pol.id.phase.top_inner.user_setpoint)
top_inner = get_mock_put(mock_id_pol.id.phase().top_inner.user_setpoint)
top_inner.assert_called_once()
assert float(top_inner.call_args[0][0]) == pytest.approx(expect_top_inner, 0.01)

top_outer = get_mock_put(mock_id_pol.id.phase.top_outer.user_setpoint)
top_outer = get_mock_put(mock_id_pol.id.phase().top_outer.user_setpoint)
top_outer.assert_called_once()
assert float(top_outer.call_args[0][0]) == pytest.approx(expect_top_outer, 0.01)

btm_inner = get_mock_put(mock_id_pol.id.phase.btm_inner.user_setpoint)
btm_inner = get_mock_put(mock_id_pol.id.phase().btm_inner.user_setpoint)
btm_inner.assert_called_once()
assert float(btm_inner.call_args[0][0]) == pytest.approx(expect_btm_inner, 0.01)

btm_outer = get_mock_put(mock_id_pol.id.phase.btm_outer.user_setpoint)
btm_outer = get_mock_put(mock_id_pol.id.phase().btm_outer.user_setpoint)
btm_outer.assert_called_once()
assert float(btm_outer.call_args[0][0]) == pytest.approx(expect_btm_outer, 0.01)

gap = get_mock_put(mock_id_pol.id.gap.user_setpoint)
gap = get_mock_put(mock_id_pol.id.gap().user_setpoint)
gap.assert_called_once()
assert float(gap.call_args[0][0]) == pytest.approx(expect_gap, 0.05)

Expand Down Expand Up @@ -428,7 +428,7 @@ def capture_emitted(name, doc):
assert_emitted(docs, start=1, descriptor=1, event=num_point, stop=1)

jaw_phase = get_mock_put(
mock_linear_arbitrary_angle.id_ref().id_jaw_phase.jaw_phase.user_setpoint
mock_linear_arbitrary_angle.id_ref().id_jaw_phase().jaw_phase.user_setpoint
)

poly = poly1d(
Expand Down

0 comments on commit f896461

Please sign in to comment.