ophyd-async 0.3a4__py3-none-any.whl → 0.3a5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ophyd_async/_version.py +1 -1
- ophyd_async/core/__init__.py +2 -1
- ophyd_async/core/async_status.py +96 -35
- ophyd_async/core/detector.py +22 -28
- ophyd_async/core/device.py +30 -7
- ophyd_async/core/mock_signal_backend.py +14 -15
- ophyd_async/core/mock_signal_utils.py +7 -7
- ophyd_async/core/signal.py +56 -12
- ophyd_async/core/utils.py +19 -0
- ophyd_async/epics/areadetector/aravis.py +1 -5
- ophyd_async/epics/areadetector/controllers/aravis_controller.py +6 -1
- ophyd_async/epics/areadetector/drivers/aravis_driver.py +2 -120
- ophyd_async/epics/demo/__init__.py +27 -32
- ophyd_async/epics/motion/motor.py +39 -40
- ophyd_async/epics/pvi/pvi.py +2 -2
- ophyd_async/epics/signal/__init__.py +8 -1
- ophyd_async/planstubs/__init__.py +5 -1
- ophyd_async/planstubs/ensure_connected.py +22 -0
- ophyd_async/protocols.py +32 -2
- ophyd_async/sim/demo/sim_motor.py +47 -32
- {ophyd_async-0.3a4.dist-info → ophyd_async-0.3a5.dist-info}/METADATA +1 -1
- {ophyd_async-0.3a4.dist-info → ophyd_async-0.3a5.dist-info}/RECORD +26 -25
- {ophyd_async-0.3a4.dist-info → ophyd_async-0.3a5.dist-info}/LICENSE +0 -0
- {ophyd_async-0.3a4.dist-info → ophyd_async-0.3a5.dist-info}/WHEEL +0 -0
- {ophyd_async-0.3a4.dist-info → ophyd_async-0.3a5.dist-info}/entry_points.txt +0 -0
- {ophyd_async-0.3a4.dist-info → ophyd_async-0.3a5.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Literal
|
|
3
3
|
|
|
4
4
|
from ophyd_async.epics.areadetector.drivers import ADBase
|
|
5
|
-
from ophyd_async.epics.signal.signal import
|
|
5
|
+
from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class AravisTriggerMode(str, Enum):
|
|
@@ -22,113 +22,6 @@ class AravisTriggerMode(str, Enum):
|
|
|
22
22
|
AravisTriggerSource = Literal["Freerun", "Line1", "Line2", "Line3", "Line4"]
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def _reverse_lookup(
|
|
26
|
-
model_deadtimes: Dict[float, Tuple[str, ...]],
|
|
27
|
-
) -> Callable[[str], float]:
|
|
28
|
-
def inner(pixel_format: str, model_name: str) -> float:
|
|
29
|
-
for deadtime, pixel_formats in model_deadtimes.items():
|
|
30
|
-
if pixel_format in pixel_formats:
|
|
31
|
-
return deadtime
|
|
32
|
-
raise ValueError(
|
|
33
|
-
f"Model {model_name} does not have a defined deadtime "
|
|
34
|
-
f"for pixel format {pixel_format}"
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
return inner
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
_deadtimes: Dict[str, Callable[[str, str], float]] = {
|
|
41
|
-
# cite: https://cdn.alliedvision.com/fileadmin/content/documents/products/cameras/Manta/techman/Manta_TechMan.pdf retrieved 2024-04-05 # noqa: E501
|
|
42
|
-
"Manta G-125": lambda _, __: 63e-6,
|
|
43
|
-
"Manta G-145": lambda _, __: 106e-6,
|
|
44
|
-
"Manta G-235": _reverse_lookup(
|
|
45
|
-
{
|
|
46
|
-
118e-6: (
|
|
47
|
-
"Mono8",
|
|
48
|
-
"Mono12Packed",
|
|
49
|
-
"BayerRG8",
|
|
50
|
-
"BayerRG12",
|
|
51
|
-
"BayerRG12Packed",
|
|
52
|
-
"YUV411Packed",
|
|
53
|
-
),
|
|
54
|
-
256e-6: ("Mono12", "BayerRG12", "YUV422Packed"),
|
|
55
|
-
390e-6: ("RGB8Packed", "BGR8Packed", "YUV444Packed"),
|
|
56
|
-
}
|
|
57
|
-
),
|
|
58
|
-
"Manta G-895": _reverse_lookup(
|
|
59
|
-
{
|
|
60
|
-
404e-6: (
|
|
61
|
-
"Mono8",
|
|
62
|
-
"Mono12Packed",
|
|
63
|
-
"BayerRG8",
|
|
64
|
-
"BayerRG12Packed",
|
|
65
|
-
"YUV411Packed",
|
|
66
|
-
),
|
|
67
|
-
542e-6: ("Mono12", "BayerRG12", "YUV422Packed"),
|
|
68
|
-
822e-6: ("RGB8Packed", "BGR8Packed", "YUV444Packed"),
|
|
69
|
-
}
|
|
70
|
-
),
|
|
71
|
-
"Manta G-2460": _reverse_lookup(
|
|
72
|
-
{
|
|
73
|
-
979e-6: (
|
|
74
|
-
"Mono8",
|
|
75
|
-
"Mono12Packed",
|
|
76
|
-
"BayerRG8",
|
|
77
|
-
"BayerRG12Packed",
|
|
78
|
-
"YUV411Packed",
|
|
79
|
-
),
|
|
80
|
-
1304e-6: ("Mono12", "BayerRG12", "YUV422Packed"),
|
|
81
|
-
1961e-6: ("RGB8Packed", "BGR8Packed", "YUV444Packed"),
|
|
82
|
-
}
|
|
83
|
-
),
|
|
84
|
-
# cite: https://cdn.alliedvision.com/fileadmin/content/documents/products/cameras/various/appnote/GigE/GigE-Cameras_AppNote_PIV-Min-Time-Between-Exposures.pdf retrieved 2024-04-05 # noqa: E501
|
|
85
|
-
"Manta G-609": lambda _, __: 47e-6,
|
|
86
|
-
# cite: https://cdn.alliedvision.com/fileadmin/content/documents/products/cameras/Mako/techman/Mako_TechMan_en.pdf retrieved 2024-04-05 # noqa: E501
|
|
87
|
-
"Mako G-040": _reverse_lookup(
|
|
88
|
-
{
|
|
89
|
-
101e-6: (
|
|
90
|
-
"Mono8",
|
|
91
|
-
"Mono12Packed",
|
|
92
|
-
"BayerRG8",
|
|
93
|
-
"BayerRG12Packed",
|
|
94
|
-
"YUV411Packed",
|
|
95
|
-
),
|
|
96
|
-
140e-6: ("Mono12", "BayerRG12", "YUV422Packed"),
|
|
97
|
-
217e-6: ("RGB8Packed", "BGR8Packed", "YUV444Packed"),
|
|
98
|
-
}
|
|
99
|
-
),
|
|
100
|
-
"Mako G-125": lambda _, __: 70e-6,
|
|
101
|
-
# Assume 12 bits: 10 bits = 275e-6
|
|
102
|
-
"Mako G-234": _reverse_lookup(
|
|
103
|
-
{
|
|
104
|
-
356e-6: (
|
|
105
|
-
"Mono8",
|
|
106
|
-
"BayerRG8",
|
|
107
|
-
"BayerRG12",
|
|
108
|
-
"BayerRG12Packed",
|
|
109
|
-
"YUV411Packed",
|
|
110
|
-
"YUV422Packed",
|
|
111
|
-
),
|
|
112
|
-
# Assume 12 bits: 10 bits = 563e-6
|
|
113
|
-
726e-6: ("RGB8Packed", "BRG8Packed", "YUV444Packed"),
|
|
114
|
-
}
|
|
115
|
-
),
|
|
116
|
-
"Mako G-507": _reverse_lookup(
|
|
117
|
-
{
|
|
118
|
-
270e-6: (
|
|
119
|
-
"Mono8",
|
|
120
|
-
"Mono12Packed",
|
|
121
|
-
"BayerRG8",
|
|
122
|
-
"BayerRG12Packed",
|
|
123
|
-
"YUV411Packed",
|
|
124
|
-
),
|
|
125
|
-
363e-6: ("Mono12", "BayerRG12", "YUV422Packed"),
|
|
126
|
-
554e-6: ("RGB8Packed", "BGR8Packed", "YUV444Packed"),
|
|
127
|
-
}
|
|
128
|
-
),
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
25
|
class AravisDriver(ADBase):
|
|
133
26
|
# If instantiating a new instance, ensure it is supported in the _deadtimes dict
|
|
134
27
|
"""Generic Driver supporting the Manta and Mako drivers.
|
|
@@ -142,15 +35,4 @@ class AravisDriver(ADBase):
|
|
|
142
35
|
AravisTriggerMode, prefix + "TriggerMode"
|
|
143
36
|
)
|
|
144
37
|
self.trigger_source = epics_signal_rw_rbv(str, prefix + "TriggerSource")
|
|
145
|
-
self.model = epics_signal_r(str, prefix + "Model_RBV")
|
|
146
|
-
self.pixel_format = epics_signal_rw_rbv(str, prefix + "PixelFormat")
|
|
147
|
-
self.dead_time: Optional[float] = None
|
|
148
38
|
super().__init__(prefix, name=name)
|
|
149
|
-
|
|
150
|
-
async def fetch_deadtime(self) -> None:
|
|
151
|
-
# All known in-use version B/C have same deadtime as non-B/C
|
|
152
|
-
model: str = (await self.model.get_value()).removesuffix("B").removesuffix("C")
|
|
153
|
-
if model not in _deadtimes:
|
|
154
|
-
raise ValueError(f"Model {model} does not have defined deadtimes")
|
|
155
|
-
pixel_format: str = await self.pixel_format.get_value()
|
|
156
|
-
self.dead_time = _deadtimes.get(model)(pixel_format, model)
|
|
@@ -6,23 +6,23 @@ import random
|
|
|
6
6
|
import string
|
|
7
7
|
import subprocess
|
|
8
8
|
import sys
|
|
9
|
-
import
|
|
9
|
+
from dataclasses import replace
|
|
10
10
|
from enum import Enum
|
|
11
11
|
from pathlib import Path
|
|
12
|
-
from typing import Callable, List, Optional
|
|
13
12
|
|
|
14
13
|
import numpy as np
|
|
15
14
|
from bluesky.protocols import Movable, Stoppable
|
|
16
15
|
|
|
17
16
|
from ophyd_async.core import (
|
|
18
|
-
AsyncStatus,
|
|
19
17
|
ConfigSignal,
|
|
20
18
|
Device,
|
|
21
19
|
DeviceVector,
|
|
22
20
|
HintedSignal,
|
|
23
21
|
StandardReadable,
|
|
22
|
+
WatchableAsyncStatus,
|
|
24
23
|
observe_value,
|
|
25
24
|
)
|
|
25
|
+
from ophyd_async.core.utils import WatcherUpdate
|
|
26
26
|
|
|
27
27
|
from ..signal.signal import epics_signal_r, epics_signal_rw, epics_signal_x
|
|
28
28
|
|
|
@@ -85,46 +85,41 @@ class Mover(StandardReadable, Movable, Stoppable):
|
|
|
85
85
|
# Readback should be named the same as its parent in read()
|
|
86
86
|
self.readback.set_name(name)
|
|
87
87
|
|
|
88
|
-
async def _move(self, new_position: float
|
|
88
|
+
async def _move(self, new_position: float):
|
|
89
89
|
self._set_success = True
|
|
90
90
|
# time.monotonic won't go backwards in case of NTP corrections
|
|
91
|
-
start = time.monotonic()
|
|
92
91
|
old_position, units, precision = await asyncio.gather(
|
|
93
92
|
self.setpoint.get_value(),
|
|
94
93
|
self.units.get_value(),
|
|
95
94
|
self.precision.get_value(),
|
|
96
95
|
)
|
|
97
96
|
# Wait for the value to set, but don't wait for put completion callback
|
|
98
|
-
|
|
99
|
-
async for current_position in observe_value(self.readback):
|
|
100
|
-
for watcher in watchers:
|
|
101
|
-
watcher(
|
|
102
|
-
name=self.name,
|
|
103
|
-
current=current_position,
|
|
104
|
-
initial=old_position,
|
|
105
|
-
target=new_position,
|
|
106
|
-
unit=units,
|
|
107
|
-
precision=precision,
|
|
108
|
-
time_elapsed=time.monotonic() - start,
|
|
109
|
-
)
|
|
110
|
-
if np.isclose(current_position, new_position):
|
|
111
|
-
break
|
|
97
|
+
move_status = self.setpoint.set(new_position, wait=True)
|
|
112
98
|
if not self._set_success:
|
|
113
99
|
raise RuntimeError("Motor was stopped")
|
|
100
|
+
# return a template to set() which it can use to yield progress updates
|
|
101
|
+
return (
|
|
102
|
+
WatcherUpdate(
|
|
103
|
+
initial=old_position,
|
|
104
|
+
current=old_position,
|
|
105
|
+
target=new_position,
|
|
106
|
+
unit=units,
|
|
107
|
+
precision=precision,
|
|
108
|
+
),
|
|
109
|
+
move_status,
|
|
110
|
+
)
|
|
114
111
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
coro = asyncio.wait_for(self._move(new_position, watchers), timeout=timeout)
|
|
127
|
-
return AsyncStatus(coro, watchers)
|
|
112
|
+
@WatchableAsyncStatus.wrap # uses the timeout argument from the function it wraps
|
|
113
|
+
async def set(self, new_position: float, timeout: float | None = None):
|
|
114
|
+
update, _ = await self._move(new_position)
|
|
115
|
+
async for current_position in observe_value(self.readback):
|
|
116
|
+
yield replace(
|
|
117
|
+
update,
|
|
118
|
+
name=self.name,
|
|
119
|
+
current=current_position,
|
|
120
|
+
)
|
|
121
|
+
if np.isclose(current_position, new_position):
|
|
122
|
+
break
|
|
128
123
|
|
|
129
124
|
async def stop(self, success=True):
|
|
130
125
|
self._set_success = success
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import
|
|
3
|
-
from typing import Callable, List, Optional
|
|
2
|
+
from dataclasses import replace
|
|
4
3
|
|
|
5
4
|
from bluesky.protocols import Movable, Stoppable
|
|
6
5
|
|
|
7
|
-
from ophyd_async.core import
|
|
6
|
+
from ophyd_async.core import (
|
|
7
|
+
AsyncStatus,
|
|
8
|
+
ConfigSignal,
|
|
9
|
+
HintedSignal,
|
|
10
|
+
StandardReadable,
|
|
11
|
+
WatchableAsyncStatus,
|
|
12
|
+
)
|
|
13
|
+
from ophyd_async.core.signal import observe_value
|
|
14
|
+
from ophyd_async.core.utils import WatcherUpdate
|
|
8
15
|
|
|
9
16
|
from ..signal.signal import epics_signal_r, epics_signal_rw, epics_signal_x
|
|
10
17
|
|
|
@@ -41,54 +48,46 @@ class Motor(StandardReadable, Movable, Stoppable):
|
|
|
41
48
|
self.user_readback.set_name(name)
|
|
42
49
|
|
|
43
50
|
async def _move(
|
|
44
|
-
self, new_position: float
|
|
45
|
-
):
|
|
46
|
-
if watchers is None:
|
|
47
|
-
watchers = []
|
|
51
|
+
self, new_position: float
|
|
52
|
+
) -> tuple[WatcherUpdate[float], AsyncStatus]:
|
|
48
53
|
self._set_success = True
|
|
49
|
-
start = time.monotonic()
|
|
50
54
|
old_position, units, precision = await asyncio.gather(
|
|
51
55
|
self.user_setpoint.get_value(),
|
|
52
56
|
self.motor_egu.get_value(),
|
|
53
57
|
self.precision.get_value(),
|
|
54
58
|
)
|
|
55
|
-
|
|
56
|
-
def update_watchers(current_position: float):
|
|
57
|
-
for watcher in watchers:
|
|
58
|
-
watcher(
|
|
59
|
-
name=self.name,
|
|
60
|
-
current=current_position,
|
|
61
|
-
initial=old_position,
|
|
62
|
-
target=new_position,
|
|
63
|
-
unit=units,
|
|
64
|
-
precision=precision,
|
|
65
|
-
time_elapsed=time.monotonic() - start,
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
self.user_readback.subscribe_value(update_watchers)
|
|
69
|
-
try:
|
|
70
|
-
await self.user_setpoint.set(new_position)
|
|
71
|
-
finally:
|
|
72
|
-
self.user_readback.clear_sub(update_watchers)
|
|
59
|
+
move_status = self.user_setpoint.set(new_position, wait=True)
|
|
73
60
|
if not self._set_success:
|
|
74
61
|
raise RuntimeError("Motor was stopped")
|
|
62
|
+
return (
|
|
63
|
+
WatcherUpdate(
|
|
64
|
+
initial=old_position,
|
|
65
|
+
current=old_position,
|
|
66
|
+
target=new_position,
|
|
67
|
+
unit=units,
|
|
68
|
+
precision=precision,
|
|
69
|
+
),
|
|
70
|
+
move_status,
|
|
71
|
+
)
|
|
75
72
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
73
|
+
@WatchableAsyncStatus.wrap
|
|
74
|
+
async def set(self, new_position: float, timeout: float | None = None):
|
|
75
|
+
update, move_status = await self._move(new_position)
|
|
76
|
+
async for current_position in observe_value(
|
|
77
|
+
self.user_readback, done_status=move_status
|
|
78
|
+
):
|
|
79
|
+
if not self._set_success:
|
|
80
|
+
raise RuntimeError("Motor was stopped")
|
|
81
|
+
yield replace(
|
|
82
|
+
update,
|
|
83
|
+
name=self.name,
|
|
84
|
+
current=current_position,
|
|
85
|
+
)
|
|
88
86
|
|
|
89
87
|
async def stop(self, success=False):
|
|
90
88
|
self._set_success = success
|
|
91
89
|
# Put with completion will never complete as we are waiting for completion on
|
|
92
90
|
# the move above, so need to pass wait=False
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
await self.motor_stop.trigger(wait=False)
|
|
92
|
+
# Trigger any callbacks
|
|
93
|
+
await self.user_readback._backend.put(await self.user_readback.get_value())
|
ophyd_async/epics/pvi/pvi.py
CHANGED
|
@@ -91,7 +91,7 @@ def _verify_common_blocks(entry: PVIEntry, common_device: Type[Device]):
|
|
|
91
91
|
return
|
|
92
92
|
common_sub_devices = get_type_hints(common_device)
|
|
93
93
|
for sub_name, sub_device in common_sub_devices.items():
|
|
94
|
-
if sub_name
|
|
94
|
+
if sub_name.startswith("_") or sub_name == "parent":
|
|
95
95
|
continue
|
|
96
96
|
assert entry.sub_entries
|
|
97
97
|
device_t, is_optional = _strip_union(sub_device)
|
|
@@ -161,7 +161,7 @@ def _mock_common_blocks(device: Device, stripped_type: Optional[Type] = None):
|
|
|
161
161
|
sub_devices = (
|
|
162
162
|
(field, field_type)
|
|
163
163
|
for field, field_type in get_type_hints(device_t).items()
|
|
164
|
-
if
|
|
164
|
+
if not field.startswith("_") and field != "parent"
|
|
165
165
|
)
|
|
166
166
|
|
|
167
167
|
for device_name, device_cls in sub_devices:
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
from .signal import
|
|
1
|
+
from .signal import (
|
|
2
|
+
epics_signal_r,
|
|
3
|
+
epics_signal_rw,
|
|
4
|
+
epics_signal_rw_rbv,
|
|
5
|
+
epics_signal_w,
|
|
6
|
+
epics_signal_x,
|
|
7
|
+
)
|
|
2
8
|
|
|
3
9
|
__all__ = [
|
|
4
10
|
"epics_signal_r",
|
|
5
11
|
"epics_signal_rw",
|
|
12
|
+
"epics_signal_rw_rbv",
|
|
6
13
|
"epics_signal_w",
|
|
7
14
|
"epics_signal_x",
|
|
8
15
|
]
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
from .ensure_connected import ensure_connected
|
|
1
2
|
from .prepare_trigger_and_dets import (
|
|
2
3
|
prepare_static_seq_table_flyer_and_detectors_with_same_trigger,
|
|
3
4
|
)
|
|
4
5
|
|
|
5
|
-
__all__ = [
|
|
6
|
+
__all__ = [
|
|
7
|
+
"prepare_static_seq_table_flyer_and_detectors_with_same_trigger",
|
|
8
|
+
"ensure_connected",
|
|
9
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import bluesky.plan_stubs as bps
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core.device import Device
|
|
4
|
+
from ophyd_async.core.utils import DEFAULT_TIMEOUT, wait_for_connection
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def ensure_connected(
|
|
8
|
+
*devices: Device,
|
|
9
|
+
mock: bool = False,
|
|
10
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
11
|
+
force_reconnect=False,
|
|
12
|
+
):
|
|
13
|
+
yield from bps.wait_for(
|
|
14
|
+
[
|
|
15
|
+
lambda: wait_for_connection(
|
|
16
|
+
**{
|
|
17
|
+
device.name: device.connect(mock, timeout, force_reconnect)
|
|
18
|
+
for device in devices
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
]
|
|
22
|
+
)
|
ophyd_async/protocols.py
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from abc import abstractmethod
|
|
2
|
-
from typing import
|
|
4
|
+
from typing import (
|
|
5
|
+
TYPE_CHECKING,
|
|
6
|
+
Any,
|
|
7
|
+
Dict,
|
|
8
|
+
Generic,
|
|
9
|
+
Protocol,
|
|
10
|
+
TypeVar,
|
|
11
|
+
runtime_checkable,
|
|
12
|
+
)
|
|
3
13
|
|
|
4
14
|
from bluesky.protocols import DataKey, HasName, Reading
|
|
5
15
|
|
|
6
|
-
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ophyd_async.core.async_status import AsyncStatus
|
|
7
18
|
|
|
8
19
|
|
|
9
20
|
@runtime_checkable
|
|
@@ -94,3 +105,22 @@ class AsyncStageable(Protocol):
|
|
|
94
105
|
unstaging.
|
|
95
106
|
"""
|
|
96
107
|
...
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
C = TypeVar("C", contravariant=True)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class Watcher(Protocol, Generic[C]):
|
|
114
|
+
@staticmethod
|
|
115
|
+
def __call__(
|
|
116
|
+
*,
|
|
117
|
+
current: C,
|
|
118
|
+
initial: C,
|
|
119
|
+
target: C,
|
|
120
|
+
name: str | None,
|
|
121
|
+
unit: str | None,
|
|
122
|
+
precision: float | None,
|
|
123
|
+
fraction: float | None,
|
|
124
|
+
time_elapsed: float | None,
|
|
125
|
+
time_remaining: float | None,
|
|
126
|
+
) -> Any: ...
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import time
|
|
3
|
-
from
|
|
3
|
+
from dataclasses import replace
|
|
4
4
|
|
|
5
5
|
from bluesky.protocols import Movable, Stoppable
|
|
6
6
|
|
|
7
7
|
from ophyd_async.core import StandardReadable
|
|
8
|
-
from ophyd_async.core.async_status import AsyncStatus
|
|
9
|
-
from ophyd_async.core.signal import
|
|
8
|
+
from ophyd_async.core.async_status import AsyncStatus, WatchableAsyncStatus
|
|
9
|
+
from ophyd_async.core.signal import (
|
|
10
|
+
observe_value,
|
|
11
|
+
soft_signal_r_and_setter,
|
|
12
|
+
soft_signal_rw,
|
|
13
|
+
)
|
|
10
14
|
from ophyd_async.core.standard_readable import ConfigSignal, HintedSignal
|
|
15
|
+
from ophyd_async.core.utils import WatcherUpdate
|
|
11
16
|
|
|
12
17
|
|
|
13
18
|
class SimMotor(StandardReadable, Movable, Stoppable):
|
|
@@ -27,10 +32,10 @@ class SimMotor(StandardReadable, Movable, Stoppable):
|
|
|
27
32
|
|
|
28
33
|
with self.add_children_as_readables(ConfigSignal):
|
|
29
34
|
self.velocity = soft_signal_rw(float, 1.0)
|
|
30
|
-
self.egu = soft_signal_rw(
|
|
35
|
+
self.egu = soft_signal_rw(str, "mm")
|
|
31
36
|
|
|
32
37
|
self._instant = instant
|
|
33
|
-
self.
|
|
38
|
+
self._move_status: AsyncStatus | None = None
|
|
34
39
|
|
|
35
40
|
# Define some signals
|
|
36
41
|
self.user_setpoint = soft_signal_rw(float, 0)
|
|
@@ -44,21 +49,37 @@ class SimMotor(StandardReadable, Movable, Stoppable):
|
|
|
44
49
|
"""
|
|
45
50
|
Stop the motor if it is moving
|
|
46
51
|
"""
|
|
47
|
-
if self.
|
|
48
|
-
self.
|
|
49
|
-
self.
|
|
52
|
+
if self._move_status:
|
|
53
|
+
self._move_status.task.cancel()
|
|
54
|
+
self._move_status = None
|
|
55
|
+
|
|
56
|
+
async def trigger_callbacks():
|
|
57
|
+
await self.user_readback._backend.put(
|
|
58
|
+
await self.user_readback._backend.get_value()
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
asyncio.create_task(trigger_callbacks())
|
|
50
62
|
|
|
51
63
|
self._set_success = success
|
|
52
64
|
|
|
53
|
-
|
|
65
|
+
@WatchableAsyncStatus.wrap
|
|
66
|
+
async def set(self, new_position: float, timeout: float | None = None):
|
|
54
67
|
"""
|
|
55
68
|
Asynchronously move the motor to a new position.
|
|
56
69
|
"""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
70
|
+
update, move_status = await self._move(new_position, timeout)
|
|
71
|
+
async for current_position in observe_value(
|
|
72
|
+
self.user_readback, done_status=move_status
|
|
73
|
+
):
|
|
74
|
+
if not self._set_success:
|
|
75
|
+
raise RuntimeError("Motor was stopped")
|
|
76
|
+
yield replace(
|
|
77
|
+
update,
|
|
78
|
+
name=self.name,
|
|
79
|
+
current=current_position,
|
|
80
|
+
)
|
|
60
81
|
|
|
61
|
-
async def _move(self, new_position: float,
|
|
82
|
+
async def _move(self, new_position: float, timeout: float | None = None):
|
|
62
83
|
"""
|
|
63
84
|
Start the motor moving to a new position.
|
|
64
85
|
|
|
@@ -67,6 +88,7 @@ class SimMotor(StandardReadable, Movable, Stoppable):
|
|
|
67
88
|
"""
|
|
68
89
|
self.stop()
|
|
69
90
|
start = time.monotonic()
|
|
91
|
+
self._set_success = True
|
|
70
92
|
|
|
71
93
|
current_position = await self.user_readback.get_value()
|
|
72
94
|
distance = abs(new_position - current_position)
|
|
@@ -94,25 +116,18 @@ class SimMotor(StandardReadable, Movable, Stoppable):
|
|
|
94
116
|
|
|
95
117
|
self._user_readback_set(current_position)
|
|
96
118
|
|
|
97
|
-
# notify watchers of the new position
|
|
98
|
-
for watcher in watchers:
|
|
99
|
-
watcher(
|
|
100
|
-
name=self.name,
|
|
101
|
-
current=current_position,
|
|
102
|
-
initial=old_position,
|
|
103
|
-
target=new_position,
|
|
104
|
-
unit=units,
|
|
105
|
-
time_elapsed=time.monotonic() - start,
|
|
106
|
-
)
|
|
107
|
-
|
|
108
119
|
# 10hz update loop
|
|
109
120
|
await asyncio.sleep(0.1)
|
|
110
121
|
|
|
111
|
-
# set up a task that updates the motor position at 10hz
|
|
112
|
-
self.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
# set up a task that updates the motor position at ~10hz
|
|
123
|
+
self._move_status = AsyncStatus(asyncio.wait_for(update_position(), timeout))
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
WatcherUpdate(
|
|
127
|
+
initial=old_position,
|
|
128
|
+
current=old_position,
|
|
129
|
+
target=new_position,
|
|
130
|
+
unit=units,
|
|
131
|
+
),
|
|
132
|
+
self._move_status,
|
|
133
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ophyd-async
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3a5
|
|
4
4
|
Summary: Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango
|
|
5
5
|
Author-email: Tom Cobb <tom.cobb@diamond.ac.uk>
|
|
6
6
|
License: BSD 3-Clause License
|