Skip to content

Commit

Permalink
(#117) (#45) wip changing motor and detector sets to new iterator thing
Browse files Browse the repository at this point in the history
  • Loading branch information
dperl-dls committed Apr 10, 2024
1 parent dce34dc commit e9d073f
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 80 deletions.
31 changes: 14 additions & 17 deletions src/ophyd_async/core/detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from .async_status import AsyncStatus, WatchableAsyncStatus
from .device import Device
from .signal import SignalR
from .utils import DEFAULT_TIMEOUT, merge_gathered_dicts
from .utils import DEFAULT_TIMEOUT, WatcherUpdate, merge_gathered_dicts

T = TypeVar("T")

Expand Down Expand Up @@ -271,28 +271,25 @@ async def _prepare(self, value: T) -> None:
exposure=self._trigger_info.livetime,
)

@AsyncStatus.wrap
async def kickoff(self) -> None:
self._fly_status = WatchableAsyncStatus(self._fly(), self._watchers)
async def kickoff(self):
self._fly_start = time.monotonic()

async def _fly(self) -> None:
await self._observe_writer_indicies(self._last_frame)
return WatchableAsyncStatus(
self._observe_writer_indicies(self._last_frame), self._watchers
)

async def _observe_writer_indicies(self, end_observation: int):
async for index in self.writer.observe_indices_written(
self._frame_writing_timeout
):
for watcher in self._watchers:
watcher(
name=self.name,
current=index,
initial=self._initial_frame,
target=end_observation,
unit="",
precision=0,
time_elapsed=time.monotonic() - self._fly_start,
)
yield WatcherUpdate(
name=self.name,
current=index,
initial=self._initial_frame,
target=end_observation,
unit="",
precision=0,
time_elapsed=time.monotonic() - self._fly_start,
)
if index >= end_observation:
break

Expand Down
24 changes: 12 additions & 12 deletions src/ophyd_async/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ def __str__(self) -> str:
class WatcherUpdate(Generic[T]):
"""A dataclass such that, when expanded, it provides the kwargs for a watcher"""

name: str
current: T
initial: T
target: T
units: str
precision: float
fraction: float
time_elapsed: float
time_remaining: float
name: str | None = None
unit: str | None = None
precision: float | None = None
fraction: float | None = None
time_elapsed: float | None = None
time_remaining: float | None = None


C = TypeVar("C", contravariant=True)
Expand All @@ -105,15 +105,15 @@ class Watcher(Protocol, Generic[C]):
@staticmethod
def __call__(
*,
name: str,
current: C,
initial: C,
target: C,
units: str,
precision: float,
fraction: float,
time_elapsed: float,
time_remaining: float,
name: str | None,
unit: str | None,
precision: float | None,
fraction: float | None,
time_elapsed: float | None,
time_remaining: float | None,
) -> Any: ...


Expand Down
48 changes: 21 additions & 27 deletions src/ophyd_async/epics/motion/motor.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import asyncio
import time
from typing import Callable, List, Optional
from dataclasses import replace
from typing import Optional

from bluesky.protocols import Movable, Stoppable

from ophyd_async.core import StandardReadable, WatchableAsyncStatus
from ophyd_async.core.signal import observe_value
from ophyd_async.core.utils import WatcherUpdate

from ..signal.signal import epics_signal_r, epics_signal_rw, epics_signal_x

Expand Down Expand Up @@ -35,34 +38,23 @@ def set_name(self, name: str):
# Readback should be named the same as its parent in read()
self.readback.set_name(name)

async def _move(self, new_position: float, watchers: List[Callable] = []):
async def _move(self, new_position: float) -> WatcherUpdate[float]:
self._set_success = True
start = time.monotonic()
old_position, units, precision = await asyncio.gather(
self.setpoint.get_value(),
self.units.get_value(),
self.precision.get_value(),
)

def update_watchers(current_position: float):
for watcher in watchers:
watcher(
name=self.name,
current=current_position,
initial=old_position,
target=new_position,
unit=units,
precision=precision,
time_elapsed=time.monotonic() - start,
)

self.readback.subscribe_value(update_watchers)
try:
await self.setpoint.set(new_position)
finally:
self.readback.clear_sub(update_watchers)
await self.setpoint.set(new_position)
if not self._set_success:
raise RuntimeError("Motor was stopped")
return WatcherUpdate(
initial=old_position,
current=old_position,
target=new_position,
unit=units,
precision=precision,
)

def move(self, new_position: float, timeout: Optional[float] = None):
"""Commandline only synchronous move of a Motor"""
Expand All @@ -72,12 +64,14 @@ def move(self, new_position: float, timeout: Optional[float] = None):
raise RuntimeError("Will deadlock run engine if run in a plan")
call_in_bluesky_event_loop(self._move(new_position), timeout) # type: ignore

def set(
self, new_position: float, timeout: Optional[float] = None
) -> WatchableAsyncStatus:
watchers: List[Callable] = []
coro = asyncio.wait_for(self._move(new_position, watchers), timeout=timeout)
return WatchableAsyncStatus(coro, watchers)
@WatchableAsyncStatus.wrap
async def set(self, new_position: float, timeout: Optional[float] = None):
start_time = time.monotonic()
update: WatcherUpdate[float] = await self._move(new_position)
async for readback in observe_value(self.readback):
yield replace(
update, current=readback, time_elapsed=start_time - time.monotonic()
)

async def stop(self, success=False):
self._set_success = success
Expand Down
44 changes: 22 additions & 22 deletions tests/core/test_async_status_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,23 @@ class SetFailed(Exception):
def watcher_test(
storage: list[WatcherUpdate],
*,
name: str,
current: int,
initial: int,
target: int,
units: str,
precision: float,
fraction: float,
time_elapsed: float,
time_remaining: float,
name: str | None,
current: int | None,
initial: int | None,
target: int | None,
unit: str | None,
precision: float | None,
fraction: float | None,
time_elapsed: float | None,
time_remaining: float | None,
):
storage.append(
WatcherUpdate(
name=name,
current=current,
initial=initial,
target=target,
units=units,
unit=unit,
precision=precision,
fraction=fraction,
time_elapsed=time_elapsed,
Expand All @@ -50,17 +50,17 @@ class TWatcher:
def __call__(
self,
*,
name: str,
current: int,
initial: int,
target: int,
units: str,
precision: float,
fraction: float,
time_elapsed: float,
time_remaining: float,
name: str | None,
current: int | None,
initial: int | None,
target: int | None,
unit: str | None,
precision: float | None,
fraction: float | None,
time_elapsed: float | None,
time_remaining: float | None,
) -> None:
self.updates.append(current)
self.updates.append(current or -1)


class ASTestDevice(StandardReadable, Movable):
Expand Down Expand Up @@ -102,7 +102,7 @@ async def set(self, val) -> AsyncIterator:
current=point,
initial=self._initial,
target=val,
units="dimensionless",
unit="dimensionless",
precision=0.0,
time_elapsed=0,
time_remaining=0,
Expand All @@ -115,7 +115,7 @@ async def set(self, val) -> AsyncIterator:
current=val,
initial=self._initial,
target=val,
units="dimensionless",
unit="dimensionless",
precision=0.0,
time_elapsed=0,
time_remaining=0,
Expand Down
5 changes: 3 additions & 2 deletions tests/epics/motion/test_motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from bluesky.protocols import Reading

from ophyd_async.core import DeviceCollector, set_sim_put_proceeds, set_sim_value
from ophyd_async.core.utils import Watcher
from ophyd_async.epics.motion import motor

# Long enough for multiple asyncio event loop cycles to run so
Expand All @@ -29,8 +30,8 @@ async def sim_motor():
async def test_motor_moving_well(sim_motor: motor.Motor) -> None:
set_sim_put_proceeds(sim_motor.setpoint, False)
s = sim_motor.set(0.55)
watcher = Mock()
s.watch([watcher])
watcher = Mock(spec=Watcher)
s.watch(watcher)
done = Mock()
s.add_callback(done)
await asyncio.sleep(A_BIT)
Expand Down

0 comments on commit e9d073f

Please sign in to comment.