ophyd-async 0.8.0a2__py3-none-any.whl → 0.8.0a4__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 +9 -1
- ophyd_async/core/_device.py +71 -49
- ophyd_async/core/_device_filler.py +208 -130
- ophyd_async/core/_mock_signal_backend.py +10 -7
- ophyd_async/core/_mock_signal_utils.py +14 -11
- ophyd_async/core/_readable.py +128 -129
- ophyd_async/core/_signal.py +22 -24
- ophyd_async/core/_soft_signal_backend.py +2 -0
- ophyd_async/core/_utils.py +64 -11
- ophyd_async/epics/adaravis/_aravis_io.py +1 -1
- ophyd_async/epics/adcore/_core_io.py +1 -1
- ophyd_async/epics/adcore/_single_trigger.py +6 -10
- ophyd_async/epics/adkinetix/_kinetix_io.py +1 -1
- ophyd_async/epics/adpilatus/_pilatus_io.py +1 -1
- ophyd_async/epics/advimba/_vimba_io.py +1 -1
- ophyd_async/epics/core/__init__.py +26 -0
- ophyd_async/epics/{signal → core}/_aioca.py +3 -6
- ophyd_async/epics/core/_epics_connector.py +53 -0
- ophyd_async/epics/core/_epics_device.py +13 -0
- ophyd_async/epics/{signal → core}/_p4p.py +3 -6
- ophyd_async/epics/core/_pvi_connector.py +91 -0
- ophyd_async/epics/{signal → core}/_signal.py +31 -16
- ophyd_async/epics/{signal/_common.py → core/_util.py} +19 -1
- ophyd_async/epics/demo/_mover.py +4 -5
- ophyd_async/epics/demo/_sensor.py +9 -12
- ophyd_async/epics/eiger/_eiger_io.py +1 -1
- ophyd_async/epics/eiger/_odin_io.py +1 -1
- ophyd_async/epics/motor.py +4 -5
- ophyd_async/epics/signal.py +11 -0
- ophyd_async/fastcs/core.py +2 -2
- ophyd_async/plan_stubs/_ensure_connected.py +2 -4
- ophyd_async/sim/demo/_sim_motor.py +3 -4
- ophyd_async/tango/base_devices/_base_device.py +48 -48
- ophyd_async/tango/demo/_counter.py +6 -16
- ophyd_async/tango/demo/_mover.py +3 -4
- {ophyd_async-0.8.0a2.dist-info → ophyd_async-0.8.0a4.dist-info}/METADATA +1 -1
- {ophyd_async-0.8.0a2.dist-info → ophyd_async-0.8.0a4.dist-info}/RECORD +42 -40
- {ophyd_async-0.8.0a2.dist-info → ophyd_async-0.8.0a4.dist-info}/WHEEL +1 -1
- ophyd_async/epics/pvi/__init__.py +0 -3
- ophyd_async/epics/pvi/_pvi.py +0 -73
- ophyd_async/epics/signal/__init__.py +0 -20
- {ophyd_async-0.8.0a2.dist-info → ophyd_async-0.8.0a4.dist-info}/LICENSE +0 -0
- {ophyd_async-0.8.0a2.dist-info → ophyd_async-0.8.0a4.dist-info}/entry_points.txt +0 -0
- {ophyd_async-0.8.0a2.dist-info → ophyd_async-0.8.0a4.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from ophyd_async.core import StrictEnum
|
|
2
2
|
from ophyd_async.epics import adcore
|
|
3
|
-
from ophyd_async.epics.
|
|
3
|
+
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw_rbv
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class PilatusTriggerMode(StrictEnum):
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from ._epics_connector import EpicsDeviceConnector, PvSuffix
|
|
2
|
+
from ._epics_device import EpicsDevice
|
|
3
|
+
from ._pvi_connector import PviDeviceConnector
|
|
4
|
+
from ._signal import (
|
|
5
|
+
CaSignalBackend,
|
|
6
|
+
PvaSignalBackend,
|
|
7
|
+
epics_signal_r,
|
|
8
|
+
epics_signal_rw,
|
|
9
|
+
epics_signal_rw_rbv,
|
|
10
|
+
epics_signal_w,
|
|
11
|
+
epics_signal_x,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"PviDeviceConnector",
|
|
16
|
+
"EpicsDeviceConnector",
|
|
17
|
+
"PvSuffix",
|
|
18
|
+
"EpicsDevice",
|
|
19
|
+
"CaSignalBackend",
|
|
20
|
+
"PvaSignalBackend",
|
|
21
|
+
"epics_signal_r",
|
|
22
|
+
"epics_signal_rw",
|
|
23
|
+
"epics_signal_rw_rbv",
|
|
24
|
+
"epics_signal_w",
|
|
25
|
+
"epics_signal_x",
|
|
26
|
+
]
|
|
@@ -24,7 +24,6 @@ from ophyd_async.core import (
|
|
|
24
24
|
Array1D,
|
|
25
25
|
Callback,
|
|
26
26
|
NotConnected,
|
|
27
|
-
SignalBackend,
|
|
28
27
|
SignalDatatype,
|
|
29
28
|
SignalDatatypeT,
|
|
30
29
|
SignalMetadata,
|
|
@@ -34,7 +33,7 @@ from ophyd_async.core import (
|
|
|
34
33
|
wait_for_connection,
|
|
35
34
|
)
|
|
36
35
|
|
|
37
|
-
from .
|
|
36
|
+
from ._util import EpicsSignalBackend, format_datatype, get_supported_values
|
|
38
37
|
|
|
39
38
|
|
|
40
39
|
def _limits_from_augmented_value(value: AugmentedValue) -> Limits:
|
|
@@ -227,19 +226,17 @@ def _use_pyepics_context_if_imported():
|
|
|
227
226
|
_tried_pyepics = True
|
|
228
227
|
|
|
229
228
|
|
|
230
|
-
class CaSignalBackend(
|
|
229
|
+
class CaSignalBackend(EpicsSignalBackend[SignalDatatypeT]):
|
|
231
230
|
def __init__(
|
|
232
231
|
self,
|
|
233
232
|
datatype: type[SignalDatatypeT] | None,
|
|
234
233
|
read_pv: str = "",
|
|
235
234
|
write_pv: str = "",
|
|
236
235
|
):
|
|
237
|
-
self.read_pv = read_pv
|
|
238
|
-
self.write_pv = write_pv
|
|
239
236
|
self.converter: CaConverter = DisconnectedCaConverter(float, dbr.DBR_DOUBLE)
|
|
240
237
|
self.initial_values: dict[str, AugmentedValue] = {}
|
|
241
238
|
self.subscription: Subscription | None = None
|
|
242
|
-
super().__init__(datatype)
|
|
239
|
+
super().__init__(datatype, read_pv, write_pv)
|
|
243
240
|
|
|
244
241
|
def source(self, name: str, read: bool):
|
|
245
242
|
return f"ca://{self.read_pv if read else self.write_pv}"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from ophyd_async.core import Device, DeviceConnector, DeviceFiller
|
|
7
|
+
|
|
8
|
+
from ._signal import EpicsSignalBackend, get_signal_backend_type, split_protocol_from_pv
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class PvSuffix:
|
|
13
|
+
read_suffix: str
|
|
14
|
+
write_suffix: str | None = None
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def rbv(cls, write_suffix: str, rbv_suffix: str = "_RBV") -> PvSuffix:
|
|
18
|
+
return cls(write_suffix + rbv_suffix, write_suffix)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def fill_backend_with_prefix(
|
|
22
|
+
prefix: str, backend: EpicsSignalBackend, annotations: list[Any]
|
|
23
|
+
):
|
|
24
|
+
unhandled = []
|
|
25
|
+
while annotations:
|
|
26
|
+
annotation = annotations.pop(0)
|
|
27
|
+
if isinstance(annotation, PvSuffix):
|
|
28
|
+
backend.read_pv = prefix + annotation.read_suffix
|
|
29
|
+
backend.write_pv = prefix + (
|
|
30
|
+
annotation.write_suffix or annotation.read_suffix
|
|
31
|
+
)
|
|
32
|
+
else:
|
|
33
|
+
unhandled.append(annotation)
|
|
34
|
+
annotations.extend(unhandled)
|
|
35
|
+
# These leftover annotations will now be handled by the iterator
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class EpicsDeviceConnector(DeviceConnector):
|
|
39
|
+
def __init__(self, prefix: str) -> None:
|
|
40
|
+
self.prefix = prefix
|
|
41
|
+
|
|
42
|
+
def create_children_from_annotations(self, device: Device):
|
|
43
|
+
if not hasattr(self, "filler"):
|
|
44
|
+
protocol, prefix = split_protocol_from_pv(self.prefix)
|
|
45
|
+
self.filler = DeviceFiller(
|
|
46
|
+
device,
|
|
47
|
+
signal_backend_factory=get_signal_backend_type(protocol),
|
|
48
|
+
device_connector_factory=DeviceConnector,
|
|
49
|
+
)
|
|
50
|
+
for backend, annotations in self.filler.create_signals_from_annotations():
|
|
51
|
+
fill_backend_with_prefix(prefix, backend, annotations)
|
|
52
|
+
|
|
53
|
+
list(self.filler.create_devices_from_annotations())
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from ophyd_async.core import Device
|
|
2
|
+
|
|
3
|
+
from ._epics_connector import EpicsDeviceConnector
|
|
4
|
+
from ._pvi_connector import PviDeviceConnector
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EpicsDevice(Device):
|
|
8
|
+
def __init__(self, prefix: str, with_pvi: bool = False, name: str = ""):
|
|
9
|
+
if with_pvi:
|
|
10
|
+
connector = PviDeviceConnector(prefix)
|
|
11
|
+
else:
|
|
12
|
+
connector = EpicsDeviceConnector(prefix)
|
|
13
|
+
super().__init__(name=name, connector=connector)
|
|
@@ -18,7 +18,6 @@ from ophyd_async.core import (
|
|
|
18
18
|
Array1D,
|
|
19
19
|
Callback,
|
|
20
20
|
NotConnected,
|
|
21
|
-
SignalBackend,
|
|
22
21
|
SignalDatatype,
|
|
23
22
|
SignalDatatypeT,
|
|
24
23
|
SignalMetadata,
|
|
@@ -30,7 +29,7 @@ from ophyd_async.core import (
|
|
|
30
29
|
wait_for_connection,
|
|
31
30
|
)
|
|
32
31
|
|
|
33
|
-
from .
|
|
32
|
+
from ._util import EpicsSignalBackend, format_datatype, get_supported_values
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
def _limits_from_value(value: Any) -> Limits:
|
|
@@ -293,19 +292,17 @@ def _pva_request_string(fields: Sequence[str]) -> str:
|
|
|
293
292
|
return f"field({','.join(fields)})"
|
|
294
293
|
|
|
295
294
|
|
|
296
|
-
class PvaSignalBackend(
|
|
295
|
+
class PvaSignalBackend(EpicsSignalBackend[SignalDatatypeT]):
|
|
297
296
|
def __init__(
|
|
298
297
|
self,
|
|
299
298
|
datatype: type[SignalDatatypeT] | None,
|
|
300
299
|
read_pv: str = "",
|
|
301
300
|
write_pv: str = "",
|
|
302
301
|
):
|
|
303
|
-
self.read_pv = read_pv
|
|
304
|
-
self.write_pv = write_pv
|
|
305
302
|
self.converter: PvaConverter = DisconnectedPvaConverter(float)
|
|
306
303
|
self.initial_values: dict[str, Any] = {}
|
|
307
304
|
self.subscription: Subscription | None = None
|
|
308
|
-
super().__init__(datatype)
|
|
305
|
+
super().__init__(datatype, read_pv, write_pv)
|
|
309
306
|
|
|
310
307
|
def source(self, name: str, read: bool):
|
|
311
308
|
return f"pva://{self.read_pv if read else self.write_pv}"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import (
|
|
4
|
+
Device,
|
|
5
|
+
DeviceConnector,
|
|
6
|
+
DeviceFiller,
|
|
7
|
+
Signal,
|
|
8
|
+
SignalR,
|
|
9
|
+
SignalRW,
|
|
10
|
+
SignalX,
|
|
11
|
+
)
|
|
12
|
+
from ophyd_async.core._utils import LazyMock
|
|
13
|
+
|
|
14
|
+
from ._epics_connector import fill_backend_with_prefix
|
|
15
|
+
from ._signal import PvaSignalBackend, pvget_with_timeout
|
|
16
|
+
|
|
17
|
+
Entry = dict[str, str]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _get_signal_details(entry: Entry) -> tuple[type[Signal], str, str]:
|
|
21
|
+
match entry:
|
|
22
|
+
case {"r": read_pv}:
|
|
23
|
+
return SignalR, read_pv, read_pv
|
|
24
|
+
case {"r": read_pv, "w": write_pv}:
|
|
25
|
+
return SignalRW, read_pv, write_pv
|
|
26
|
+
case {"rw": read_write_pv}:
|
|
27
|
+
return SignalRW, read_write_pv, read_write_pv
|
|
28
|
+
case {"x": execute_pv}:
|
|
29
|
+
return SignalX, execute_pv, execute_pv
|
|
30
|
+
case _:
|
|
31
|
+
raise TypeError(f"Can't process entry {entry}")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PviDeviceConnector(DeviceConnector):
|
|
35
|
+
def __init__(self, prefix: str = "") -> None:
|
|
36
|
+
# TODO: what happens if we get a leading "pva://" here?
|
|
37
|
+
self.prefix = prefix
|
|
38
|
+
self.pvi_pv = prefix + "PVI"
|
|
39
|
+
|
|
40
|
+
def create_children_from_annotations(self, device: Device):
|
|
41
|
+
if not hasattr(self, "filler"):
|
|
42
|
+
self.filler = DeviceFiller(
|
|
43
|
+
device=device,
|
|
44
|
+
signal_backend_factory=PvaSignalBackend,
|
|
45
|
+
device_connector_factory=PviDeviceConnector,
|
|
46
|
+
)
|
|
47
|
+
# Devices will be created with unfilled PviDeviceConnectors
|
|
48
|
+
list(self.filler.create_devices_from_annotations(filled=False))
|
|
49
|
+
# Signals can be filled in with EpicsSignalSuffix and checked at runtime
|
|
50
|
+
for backend, annotations in self.filler.create_signals_from_annotations(
|
|
51
|
+
filled=False
|
|
52
|
+
):
|
|
53
|
+
fill_backend_with_prefix(self.prefix, backend, annotations)
|
|
54
|
+
self.filler.check_created()
|
|
55
|
+
|
|
56
|
+
def _fill_child(self, name: str, entry: Entry, vector_index: int | None = None):
|
|
57
|
+
if set(entry) == {"d"}:
|
|
58
|
+
connector = self.filler.fill_child_device(name, vector_index=vector_index)
|
|
59
|
+
connector.pvi_pv = entry["d"]
|
|
60
|
+
else:
|
|
61
|
+
signal_type, read_pv, write_pv = _get_signal_details(entry)
|
|
62
|
+
backend = self.filler.fill_child_signal(name, signal_type, vector_index)
|
|
63
|
+
backend.read_pv = read_pv
|
|
64
|
+
backend.write_pv = write_pv
|
|
65
|
+
|
|
66
|
+
async def connect_mock(self, device: Device, mock: LazyMock):
|
|
67
|
+
self.filler.create_device_vector_entries_to_mock(2)
|
|
68
|
+
# Set the name of the device to name all children
|
|
69
|
+
device.set_name(device.name)
|
|
70
|
+
return await super().connect_mock(device, mock)
|
|
71
|
+
|
|
72
|
+
async def connect_real(
|
|
73
|
+
self, device: Device, timeout: float, force_reconnect: bool
|
|
74
|
+
) -> None:
|
|
75
|
+
pvi_structure = await pvget_with_timeout(self.pvi_pv, timeout)
|
|
76
|
+
entries: dict[str, Entry | list[Entry | None]] = pvi_structure["value"].todict()
|
|
77
|
+
# Fill based on what PVI gives us
|
|
78
|
+
for name, entry in entries.items():
|
|
79
|
+
if isinstance(entry, dict):
|
|
80
|
+
# This is a child
|
|
81
|
+
self._fill_child(name, entry)
|
|
82
|
+
else:
|
|
83
|
+
# This is a DeviceVector of children
|
|
84
|
+
for i, e in enumerate(entry):
|
|
85
|
+
if e:
|
|
86
|
+
self._fill_child(name, e, i)
|
|
87
|
+
# Check that all the requested children have been filled
|
|
88
|
+
self.filler.check_filled(f"{self.pvi_pv}: {entries}")
|
|
89
|
+
# Set the name of the device to name all children
|
|
90
|
+
device.set_name(device.name)
|
|
91
|
+
return await super().connect_real(device, timeout, force_reconnect)
|
|
@@ -14,13 +14,7 @@ from ophyd_async.core import (
|
|
|
14
14
|
get_unique,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
def _make_unavailable_class(error: Exception) -> type:
|
|
19
|
-
class TransportNotAvailable:
|
|
20
|
-
def __init__(*args, **kwargs):
|
|
21
|
-
raise NotImplementedError("Transport not available") from error
|
|
22
|
-
|
|
23
|
-
return TransportNotAvailable
|
|
17
|
+
from ._util import EpicsSignalBackend
|
|
24
18
|
|
|
25
19
|
|
|
26
20
|
class EpicsProtocol(Enum):
|
|
@@ -30,10 +24,26 @@ class EpicsProtocol(Enum):
|
|
|
30
24
|
|
|
31
25
|
_default_epics_protocol = EpicsProtocol.CA
|
|
32
26
|
|
|
27
|
+
|
|
28
|
+
def _make_unavailable_function(error: Exception):
|
|
29
|
+
def transport_not_available(*args, **kwargs):
|
|
30
|
+
raise NotImplementedError("Transport not available") from error
|
|
31
|
+
|
|
32
|
+
return transport_not_available
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _make_unavailable_class(error: Exception) -> type[EpicsSignalBackend]:
|
|
36
|
+
class TransportNotAvailable(EpicsSignalBackend):
|
|
37
|
+
__init__ = _make_unavailable_function(error)
|
|
38
|
+
|
|
39
|
+
return TransportNotAvailable
|
|
40
|
+
|
|
41
|
+
|
|
33
42
|
try:
|
|
34
|
-
from ._p4p import PvaSignalBackend
|
|
43
|
+
from ._p4p import PvaSignalBackend, pvget_with_timeout
|
|
35
44
|
except ImportError as pva_error:
|
|
36
45
|
PvaSignalBackend = _make_unavailable_class(pva_error)
|
|
46
|
+
pvget_with_timeout = _make_unavailable_function(pva_error)
|
|
37
47
|
else:
|
|
38
48
|
_default_epics_protocol = EpicsProtocol.PVA
|
|
39
49
|
|
|
@@ -45,7 +55,7 @@ else:
|
|
|
45
55
|
_default_epics_protocol = EpicsProtocol.CA
|
|
46
56
|
|
|
47
57
|
|
|
48
|
-
def
|
|
58
|
+
def split_protocol_from_pv(pv: str) -> tuple[EpicsProtocol, str]:
|
|
49
59
|
split = pv.split("://", 1)
|
|
50
60
|
if len(split) > 1:
|
|
51
61
|
# We got something like pva://mydevice, so use specified comms mode
|
|
@@ -57,18 +67,23 @@ def _protocol_pv(pv: str) -> tuple[EpicsProtocol, str]:
|
|
|
57
67
|
return protocol, pv
|
|
58
68
|
|
|
59
69
|
|
|
70
|
+
def get_signal_backend_type(protocol: EpicsProtocol) -> type[EpicsSignalBackend]:
|
|
71
|
+
match protocol:
|
|
72
|
+
case EpicsProtocol.CA:
|
|
73
|
+
return CaSignalBackend
|
|
74
|
+
case EpicsProtocol.PVA:
|
|
75
|
+
return PvaSignalBackend
|
|
76
|
+
|
|
77
|
+
|
|
60
78
|
def _epics_signal_backend(
|
|
61
79
|
datatype: type[SignalDatatypeT] | None, read_pv: str, write_pv: str
|
|
62
80
|
) -> SignalBackend[SignalDatatypeT]:
|
|
63
81
|
"""Create an epics signal backend."""
|
|
64
|
-
r_protocol, r_pv =
|
|
65
|
-
w_protocol, w_pv =
|
|
82
|
+
r_protocol, r_pv = split_protocol_from_pv(read_pv)
|
|
83
|
+
w_protocol, w_pv = split_protocol_from_pv(write_pv)
|
|
66
84
|
protocol = get_unique({read_pv: r_protocol, write_pv: w_protocol}, "protocols")
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return CaSignalBackend(datatype, r_pv, w_pv)
|
|
70
|
-
case EpicsProtocol.PVA:
|
|
71
|
-
return PvaSignalBackend(datatype, r_pv, w_pv)
|
|
85
|
+
signal_backend_type = get_signal_backend_type(protocol)
|
|
86
|
+
return signal_backend_type(datatype, r_pv, w_pv)
|
|
72
87
|
|
|
73
88
|
|
|
74
89
|
def epics_signal_rw(
|
|
@@ -3,7 +3,13 @@ from typing import Any, get_args, get_origin
|
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
6
|
-
from ophyd_async.core import
|
|
6
|
+
from ophyd_async.core import (
|
|
7
|
+
SignalBackend,
|
|
8
|
+
SignalDatatypeT,
|
|
9
|
+
SubsetEnum,
|
|
10
|
+
get_dtype,
|
|
11
|
+
get_enum_cls,
|
|
12
|
+
)
|
|
7
13
|
|
|
8
14
|
|
|
9
15
|
def get_supported_values(
|
|
@@ -41,3 +47,15 @@ def format_datatype(datatype: Any) -> str:
|
|
|
41
47
|
return datatype.__name__
|
|
42
48
|
else:
|
|
43
49
|
return str(datatype)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class EpicsSignalBackend(SignalBackend[SignalDatatypeT]):
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
datatype: type[SignalDatatypeT] | None,
|
|
56
|
+
read_pv: str = "",
|
|
57
|
+
write_pv: str = "",
|
|
58
|
+
):
|
|
59
|
+
self.read_pv = read_pv
|
|
60
|
+
self.write_pv = write_pv
|
|
61
|
+
super().__init__(datatype)
|
ophyd_async/epics/demo/_mover.py
CHANGED
|
@@ -8,15 +8,14 @@ from ophyd_async.core import (
|
|
|
8
8
|
DEFAULT_TIMEOUT,
|
|
9
9
|
AsyncStatus,
|
|
10
10
|
CalculatableTimeout,
|
|
11
|
-
ConfigSignal,
|
|
12
11
|
Device,
|
|
13
|
-
HintedSignal,
|
|
14
12
|
StandardReadable,
|
|
15
13
|
WatchableAsyncStatus,
|
|
16
14
|
WatcherUpdate,
|
|
17
15
|
observe_value,
|
|
18
16
|
)
|
|
19
|
-
from ophyd_async.
|
|
17
|
+
from ophyd_async.core import StandardReadableFormat as Format
|
|
18
|
+
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw, epics_signal_x
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
class Mover(StandardReadable, Movable, Stoppable):
|
|
@@ -24,9 +23,9 @@ class Mover(StandardReadable, Movable, Stoppable):
|
|
|
24
23
|
|
|
25
24
|
def __init__(self, prefix: str, name="") -> None:
|
|
26
25
|
# Define some signals
|
|
27
|
-
with self.add_children_as_readables(
|
|
26
|
+
with self.add_children_as_readables(Format.HINTED_SIGNAL):
|
|
28
27
|
self.readback = epics_signal_r(float, prefix + "Readback")
|
|
29
|
-
with self.add_children_as_readables(
|
|
28
|
+
with self.add_children_as_readables(Format.CONFIG_SIGNAL):
|
|
30
29
|
self.velocity = epics_signal_rw(float, prefix + "Velocity")
|
|
31
30
|
self.units = epics_signal_r(str, prefix + "Readback.EGU")
|
|
32
31
|
self.setpoint = epics_signal_rw(float, prefix + "Setpoint")
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
from typing import Annotated as A
|
|
2
|
+
|
|
1
3
|
from ophyd_async.core import (
|
|
2
|
-
ConfigSignal,
|
|
3
4
|
DeviceVector,
|
|
4
|
-
|
|
5
|
+
SignalR,
|
|
6
|
+
SignalRW,
|
|
5
7
|
StandardReadable,
|
|
6
8
|
StrictEnum,
|
|
7
9
|
)
|
|
8
|
-
from ophyd_async.
|
|
10
|
+
from ophyd_async.core import StandardReadableFormat as Format
|
|
11
|
+
from ophyd_async.epics.core import EpicsDevice, PvSuffix
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
class EnergyMode(StrictEnum):
|
|
@@ -17,17 +20,11 @@ class EnergyMode(StrictEnum):
|
|
|
17
20
|
high = "High Energy"
|
|
18
21
|
|
|
19
22
|
|
|
20
|
-
class Sensor(StandardReadable):
|
|
23
|
+
class Sensor(StandardReadable, EpicsDevice):
|
|
21
24
|
"""A demo sensor that produces a scalar value based on X and Y Movers"""
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
with self.add_children_as_readables(HintedSignal):
|
|
26
|
-
self.value = epics_signal_r(float, prefix + "Value")
|
|
27
|
-
with self.add_children_as_readables(ConfigSignal):
|
|
28
|
-
self.mode = epics_signal_rw(EnergyMode, prefix + "Mode")
|
|
29
|
-
|
|
30
|
-
super().__init__(name=name)
|
|
26
|
+
value: A[SignalR[float], PvSuffix("Value"), Format.HINTED_SIGNAL]
|
|
27
|
+
mode: A[SignalRW[EnergyMode], PvSuffix("Mode"), Format.CONFIG_SIGNAL]
|
|
31
28
|
|
|
32
29
|
|
|
33
30
|
class SensorGroup(StandardReadable):
|
ophyd_async/epics/motor.py
CHANGED
|
@@ -14,14 +14,13 @@ from ophyd_async.core import (
|
|
|
14
14
|
DEFAULT_TIMEOUT,
|
|
15
15
|
AsyncStatus,
|
|
16
16
|
CalculatableTimeout,
|
|
17
|
-
ConfigSignal,
|
|
18
|
-
HintedSignal,
|
|
19
17
|
StandardReadable,
|
|
20
18
|
WatchableAsyncStatus,
|
|
21
19
|
WatcherUpdate,
|
|
22
20
|
observe_value,
|
|
23
21
|
)
|
|
24
|
-
from ophyd_async.
|
|
22
|
+
from ophyd_async.core import StandardReadableFormat as Format
|
|
23
|
+
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw, epics_signal_x
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
class MotorLimitsException(Exception):
|
|
@@ -61,11 +60,11 @@ class Motor(StandardReadable, Locatable, Stoppable, Flyable, Preparable):
|
|
|
61
60
|
|
|
62
61
|
def __init__(self, prefix: str, name="") -> None:
|
|
63
62
|
# Define some signals
|
|
64
|
-
with self.add_children_as_readables(
|
|
63
|
+
with self.add_children_as_readables(Format.CONFIG_SIGNAL):
|
|
65
64
|
self.motor_egu = epics_signal_r(str, prefix + ".EGU")
|
|
66
65
|
self.velocity = epics_signal_rw(float, prefix + ".VELO")
|
|
67
66
|
|
|
68
|
-
with self.add_children_as_readables(
|
|
67
|
+
with self.add_children_as_readables(Format.HINTED_SIGNAL):
|
|
69
68
|
self.user_readback = epics_signal_r(float, prefix + ".RBV")
|
|
70
69
|
|
|
71
70
|
self.user_setpoint = epics_signal_rw(float, prefix + ".VAL")
|
ophyd_async/fastcs/core.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from ophyd_async.core import Device, DeviceConnector
|
|
2
|
-
from ophyd_async.epics.
|
|
2
|
+
from ophyd_async.epics.core import PviDeviceConnector
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
def fastcs_connector(device: Device, uri: str) -> DeviceConnector:
|
|
6
6
|
# TODO: add Tango support based on uri scheme
|
|
7
|
-
connector = PviDeviceConnector(uri
|
|
7
|
+
connector = PviDeviceConnector(uri)
|
|
8
8
|
connector.create_children_from_annotations(device)
|
|
9
9
|
return connector
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
from unittest.mock import Mock
|
|
2
|
-
|
|
3
1
|
import bluesky.plan_stubs as bps
|
|
4
2
|
|
|
5
|
-
from ophyd_async.core import DEFAULT_TIMEOUT, Device, wait_for_connection
|
|
3
|
+
from ophyd_async.core import DEFAULT_TIMEOUT, Device, LazyMock, wait_for_connection
|
|
6
4
|
|
|
7
5
|
|
|
8
6
|
def ensure_connected(
|
|
9
7
|
*devices: Device,
|
|
10
|
-
mock: bool |
|
|
8
|
+
mock: bool | LazyMock = False,
|
|
11
9
|
timeout: float = DEFAULT_TIMEOUT,
|
|
12
10
|
force_reconnect=False,
|
|
13
11
|
):
|
|
@@ -6,8 +6,6 @@ from bluesky.protocols import Movable, Stoppable
|
|
|
6
6
|
|
|
7
7
|
from ophyd_async.core import (
|
|
8
8
|
AsyncStatus,
|
|
9
|
-
ConfigSignal,
|
|
10
|
-
HintedSignal,
|
|
11
9
|
StandardReadable,
|
|
12
10
|
WatchableAsyncStatus,
|
|
13
11
|
WatcherUpdate,
|
|
@@ -15,6 +13,7 @@ from ophyd_async.core import (
|
|
|
15
13
|
soft_signal_r_and_setter,
|
|
16
14
|
soft_signal_rw,
|
|
17
15
|
)
|
|
16
|
+
from ophyd_async.core import StandardReadableFormat as Format
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
class SimMotor(StandardReadable, Movable, Stoppable):
|
|
@@ -28,11 +27,11 @@ class SimMotor(StandardReadable, Movable, Stoppable):
|
|
|
28
27
|
- instant: bool: whether to move instantly, or with a delay
|
|
29
28
|
"""
|
|
30
29
|
# Define some signals
|
|
31
|
-
with self.add_children_as_readables(
|
|
30
|
+
with self.add_children_as_readables(Format.HINTED_SIGNAL):
|
|
32
31
|
self.user_readback, self._user_readback_set = soft_signal_r_and_setter(
|
|
33
32
|
float, 0
|
|
34
33
|
)
|
|
35
|
-
with self.add_children_as_readables(
|
|
34
|
+
with self.add_children_as_readables(Format.CONFIG_SIGNAL):
|
|
36
35
|
self.velocity = soft_signal_rw(float, 0 if instant else 1.0)
|
|
37
36
|
self.units = soft_signal_rw(str, "mm")
|
|
38
37
|
self.user_setpoint = soft_signal_rw(float, 0)
|