ophyd-async 0.7.0a1__py3-none-any.whl → 0.8.0a3__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 +2 -2
- ophyd_async/core/__init__.py +30 -9
- ophyd_async/core/_detector.py +5 -10
- ophyd_async/core/_device.py +146 -67
- ophyd_async/core/_device_filler.py +269 -0
- ophyd_async/core/_device_save_loader.py +6 -7
- ophyd_async/core/_mock_signal_backend.py +32 -40
- ophyd_async/core/_mock_signal_utils.py +22 -16
- ophyd_async/core/_protocol.py +28 -8
- ophyd_async/core/_readable.py +133 -134
- ophyd_async/core/_signal.py +140 -152
- ophyd_async/core/_signal_backend.py +131 -64
- ophyd_async/core/_soft_signal_backend.py +125 -194
- ophyd_async/core/_status.py +22 -6
- ophyd_async/core/_table.py +97 -100
- ophyd_async/core/_utils.py +79 -18
- ophyd_async/epics/adaravis/_aravis_controller.py +2 -2
- ophyd_async/epics/adaravis/_aravis_io.py +8 -6
- ophyd_async/epics/adcore/_core_io.py +5 -7
- ophyd_async/epics/adcore/_hdf_writer.py +2 -2
- ophyd_async/epics/adcore/_single_trigger.py +4 -9
- ophyd_async/epics/adcore/_utils.py +15 -10
- ophyd_async/epics/adkinetix/__init__.py +2 -1
- ophyd_async/epics/adkinetix/_kinetix_controller.py +6 -3
- ophyd_async/epics/adkinetix/_kinetix_io.py +4 -5
- ophyd_async/epics/adpilatus/_pilatus_controller.py +2 -2
- ophyd_async/epics/adpilatus/_pilatus_io.py +3 -4
- ophyd_async/epics/adsimdetector/_sim_controller.py +2 -2
- ophyd_async/epics/advimba/__init__.py +4 -1
- ophyd_async/epics/advimba/_vimba_controller.py +6 -3
- ophyd_async/epics/advimba/_vimba_io.py +8 -9
- ophyd_async/epics/core/__init__.py +26 -0
- ophyd_async/epics/core/_aioca.py +323 -0
- ophyd_async/epics/core/_epics_connector.py +53 -0
- ophyd_async/epics/core/_epics_device.py +13 -0
- ophyd_async/epics/core/_p4p.py +382 -0
- ophyd_async/epics/core/_pvi_connector.py +92 -0
- ophyd_async/epics/core/_signal.py +171 -0
- ophyd_async/epics/core/_util.py +61 -0
- ophyd_async/epics/demo/_mover.py +4 -5
- ophyd_async/epics/demo/_sensor.py +14 -13
- ophyd_async/epics/eiger/_eiger.py +1 -2
- ophyd_async/epics/eiger/_eiger_controller.py +1 -1
- ophyd_async/epics/eiger/_eiger_io.py +3 -5
- ophyd_async/epics/eiger/_odin_io.py +5 -5
- ophyd_async/epics/motor.py +4 -5
- ophyd_async/epics/signal.py +11 -0
- ophyd_async/fastcs/core.py +9 -0
- ophyd_async/fastcs/panda/__init__.py +4 -4
- ophyd_async/fastcs/panda/_block.py +23 -11
- ophyd_async/fastcs/panda/_control.py +3 -5
- ophyd_async/fastcs/panda/_hdf_panda.py +5 -19
- ophyd_async/fastcs/panda/_table.py +29 -51
- ophyd_async/fastcs/panda/_trigger.py +8 -8
- ophyd_async/fastcs/panda/_writer.py +4 -7
- ophyd_async/plan_stubs/_ensure_connected.py +3 -1
- ophyd_async/plan_stubs/_fly.py +2 -2
- ophyd_async/plan_stubs/_nd_attributes.py +5 -4
- ophyd_async/py.typed +0 -0
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector_controller.py +1 -2
- ophyd_async/sim/demo/_sim_motor.py +3 -4
- ophyd_async/tango/__init__.py +2 -4
- ophyd_async/tango/base_devices/_base_device.py +76 -144
- ophyd_async/tango/demo/_counter.py +8 -18
- ophyd_async/tango/demo/_mover.py +5 -6
- ophyd_async/tango/signal/__init__.py +2 -4
- ophyd_async/tango/signal/_signal.py +29 -50
- ophyd_async/tango/signal/_tango_transport.py +38 -40
- {ophyd_async-0.7.0a1.dist-info → ophyd_async-0.8.0a3.dist-info}/METADATA +8 -12
- ophyd_async-0.8.0a3.dist-info/RECORD +112 -0
- {ophyd_async-0.7.0a1.dist-info → ophyd_async-0.8.0a3.dist-info}/WHEEL +1 -1
- ophyd_async/epics/pvi/__init__.py +0 -3
- ophyd_async/epics/pvi/_pvi.py +0 -338
- ophyd_async/epics/signal/__init__.py +0 -21
- ophyd_async/epics/signal/_aioca.py +0 -378
- ophyd_async/epics/signal/_common.py +0 -57
- ophyd_async/epics/signal/_epics_transport.py +0 -34
- ophyd_async/epics/signal/_p4p.py +0 -518
- ophyd_async/epics/signal/_signal.py +0 -114
- ophyd_async-0.7.0a1.dist-info/RECORD +0 -108
- {ophyd_async-0.7.0a1.dist-info → ophyd_async-0.8.0a3.dist-info}/LICENSE +0 -0
- {ophyd_async-0.7.0a1.dist-info → ophyd_async-0.8.0a3.dist-info}/entry_points.txt +0 -0
- {ophyd_async-0.7.0a1.dist-info → ophyd_async-0.8.0a3.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from unittest.mock import Mock
|
|
2
|
+
|
|
1
3
|
import bluesky.plan_stubs as bps
|
|
2
4
|
|
|
3
5
|
from ophyd_async.core import DEFAULT_TIMEOUT, Device, wait_for_connection
|
|
@@ -5,7 +7,7 @@ from ophyd_async.core import DEFAULT_TIMEOUT, Device, wait_for_connection
|
|
|
5
7
|
|
|
6
8
|
def ensure_connected(
|
|
7
9
|
*devices: Device,
|
|
8
|
-
mock: bool = False,
|
|
10
|
+
mock: bool | Mock = False,
|
|
9
11
|
timeout: float = DEFAULT_TIMEOUT,
|
|
10
12
|
force_reconnect=False,
|
|
11
13
|
):
|
ophyd_async/plan_stubs/_fly.py
CHANGED
|
@@ -9,7 +9,7 @@ from ophyd_async.core import (
|
|
|
9
9
|
in_micros,
|
|
10
10
|
)
|
|
11
11
|
from ophyd_async.fastcs.panda import (
|
|
12
|
-
|
|
12
|
+
PcompDirection,
|
|
13
13
|
PcompInfo,
|
|
14
14
|
SeqTable,
|
|
15
15
|
SeqTableInfo,
|
|
@@ -147,7 +147,7 @@ def fly_and_collect_with_static_pcomp(
|
|
|
147
147
|
number_of_pulses: int,
|
|
148
148
|
pulse_width: int,
|
|
149
149
|
rising_edge_step: int,
|
|
150
|
-
direction:
|
|
150
|
+
direction: PcompDirection,
|
|
151
151
|
trigger_info: TriggerInfo,
|
|
152
152
|
):
|
|
153
153
|
# Set up scan and prepare trigger
|
|
@@ -16,12 +16,12 @@ from ophyd_async.epics.adcore import (
|
|
|
16
16
|
def setup_ndattributes(
|
|
17
17
|
device: NDArrayBaseIO, ndattributes: Sequence[NDAttributePv | NDAttributeParam]
|
|
18
18
|
):
|
|
19
|
-
|
|
19
|
+
root = ET.Element("Attributes")
|
|
20
20
|
|
|
21
21
|
for ndattribute in ndattributes:
|
|
22
22
|
if isinstance(ndattribute, NDAttributeParam):
|
|
23
23
|
ET.SubElement(
|
|
24
|
-
|
|
24
|
+
root,
|
|
25
25
|
"Attribute",
|
|
26
26
|
name=ndattribute.name,
|
|
27
27
|
type="PARAM",
|
|
@@ -32,7 +32,7 @@ def setup_ndattributes(
|
|
|
32
32
|
)
|
|
33
33
|
elif isinstance(ndattribute, NDAttributePv):
|
|
34
34
|
ET.SubElement(
|
|
35
|
-
|
|
35
|
+
root,
|
|
36
36
|
"Attribute",
|
|
37
37
|
name=ndattribute.name,
|
|
38
38
|
type="EPICS_PV",
|
|
@@ -45,7 +45,8 @@ def setup_ndattributes(
|
|
|
45
45
|
f"Invalid type for ndattributes: {type(ndattribute)}. "
|
|
46
46
|
"Expected NDAttributePv or NDAttributeParam."
|
|
47
47
|
)
|
|
48
|
-
|
|
48
|
+
xml_text = ET.tostring(root, encoding="unicode")
|
|
49
|
+
yield from bps.abs_set(device.nd_attributes_file, xml_text, wait=True)
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
def setup_ndstats_sum(detector: Device):
|
ophyd_async/py.typed
ADDED
|
File without changes
|
|
@@ -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)
|
ophyd_async/tango/__init__.py
CHANGED
|
@@ -7,14 +7,13 @@ from .signal import (
|
|
|
7
7
|
AttributeProxy,
|
|
8
8
|
CommandProxy,
|
|
9
9
|
TangoSignalBackend,
|
|
10
|
-
__tango_signal_auto,
|
|
11
10
|
ensure_proper_executor,
|
|
12
11
|
get_dtype_extended,
|
|
13
12
|
get_python_type,
|
|
14
13
|
get_tango_trl,
|
|
15
14
|
get_trl_descriptor,
|
|
16
15
|
infer_python_type,
|
|
17
|
-
|
|
16
|
+
infer_signal_type,
|
|
18
17
|
make_backend,
|
|
19
18
|
tango_signal_r,
|
|
20
19
|
tango_signal_rw,
|
|
@@ -32,12 +31,11 @@ __all__ = [
|
|
|
32
31
|
"get_trl_descriptor",
|
|
33
32
|
"get_tango_trl",
|
|
34
33
|
"infer_python_type",
|
|
35
|
-
"
|
|
34
|
+
"infer_signal_type",
|
|
36
35
|
"make_backend",
|
|
37
36
|
"AttributeProxy",
|
|
38
37
|
"CommandProxy",
|
|
39
38
|
"ensure_proper_executor",
|
|
40
|
-
"__tango_signal_auto",
|
|
41
39
|
"tango_signal_r",
|
|
42
40
|
"tango_signal_rw",
|
|
43
41
|
"tango_signal_w",
|
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
4
|
-
|
|
5
|
-
get_args,
|
|
6
|
-
get_origin,
|
|
7
|
-
get_type_hints,
|
|
8
|
-
)
|
|
3
|
+
from typing import TypeVar
|
|
4
|
+
from unittest.mock import Mock
|
|
9
5
|
|
|
10
|
-
from ophyd_async.core import
|
|
11
|
-
DEFAULT_TIMEOUT,
|
|
12
|
-
Device,
|
|
13
|
-
Signal,
|
|
14
|
-
)
|
|
6
|
+
from ophyd_async.core import Device, DeviceConnector, DeviceFiller
|
|
15
7
|
from ophyd_async.tango.signal import (
|
|
16
8
|
TangoSignalBackend,
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
infer_python_type,
|
|
10
|
+
infer_signal_type,
|
|
19
11
|
)
|
|
20
12
|
from tango import DeviceProxy as DeviceProxy
|
|
21
13
|
from tango.asyncio import DeviceProxy as AsyncDeviceProxy
|
|
@@ -50,64 +42,13 @@ class TangoDevice(Device):
|
|
|
50
42
|
device_proxy: DeviceProxy | None = None,
|
|
51
43
|
name: str = "",
|
|
52
44
|
) -> None:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if not isinstance(trl, str):
|
|
61
|
-
raise ValueError("TRL must be a string.")
|
|
62
|
-
self.trl = trl
|
|
63
|
-
|
|
64
|
-
async def connect(
|
|
65
|
-
self,
|
|
66
|
-
mock: bool = False,
|
|
67
|
-
timeout: float = DEFAULT_TIMEOUT,
|
|
68
|
-
force_reconnect: bool = False,
|
|
69
|
-
):
|
|
70
|
-
if self.trl and self.proxy is None:
|
|
71
|
-
self.proxy = await AsyncDeviceProxy(self.trl)
|
|
72
|
-
elif self.proxy and not self.trl:
|
|
73
|
-
self.trl = self.proxy.name()
|
|
74
|
-
|
|
75
|
-
# Set the trl of the signal backends
|
|
76
|
-
for child in self.children():
|
|
77
|
-
if isinstance(child[1], Signal):
|
|
78
|
-
if isinstance(child[1]._backend, TangoSignalBackend): # noqa: SLF001
|
|
79
|
-
resource_name = child[0].lstrip("_")
|
|
80
|
-
read_trl = f"{self.trl}/{resource_name}"
|
|
81
|
-
child[1]._backend.set_trl(read_trl, read_trl) # noqa: SLF001
|
|
82
|
-
|
|
83
|
-
if self.proxy is not None:
|
|
84
|
-
self.register_signals()
|
|
85
|
-
await _fill_proxy_entries(self)
|
|
86
|
-
|
|
87
|
-
# set_name should be called again to propagate the new signal names
|
|
88
|
-
self.set_name(self.name)
|
|
89
|
-
|
|
90
|
-
# Set the polling configuration
|
|
91
|
-
if self._polling[0]:
|
|
92
|
-
for child in self.children():
|
|
93
|
-
child_type = type(child[1])
|
|
94
|
-
if issubclass(child_type, Signal):
|
|
95
|
-
if isinstance(child[1]._backend, TangoSignalBackend): # noqa: SLF001 # type: ignore
|
|
96
|
-
child[1]._backend.set_polling(*self._polling) # noqa: SLF001 # type: ignore
|
|
97
|
-
child[1]._backend.allow_events(False) # noqa: SLF001 # type: ignore
|
|
98
|
-
if self._signal_polling:
|
|
99
|
-
for signal_name, polling in self._signal_polling.items():
|
|
100
|
-
if hasattr(self, signal_name):
|
|
101
|
-
attr = getattr(self, signal_name)
|
|
102
|
-
if isinstance(attr._backend, TangoSignalBackend): # noqa: SLF001
|
|
103
|
-
attr._backend.set_polling(*polling) # noqa: SLF001
|
|
104
|
-
attr._backend.allow_events(False) # noqa: SLF001
|
|
105
|
-
|
|
106
|
-
await super().connect(mock=mock, timeout=timeout)
|
|
107
|
-
|
|
108
|
-
# Users can override this method to register new signals
|
|
109
|
-
def register_signals(self):
|
|
110
|
-
pass
|
|
45
|
+
connector = TangoDeviceConnector(
|
|
46
|
+
trl=trl,
|
|
47
|
+
device_proxy=device_proxy,
|
|
48
|
+
polling=self._polling,
|
|
49
|
+
signal_polling=self._signal_polling,
|
|
50
|
+
)
|
|
51
|
+
super().__init__(name=name, connector=connector)
|
|
111
52
|
|
|
112
53
|
|
|
113
54
|
def tango_polling(
|
|
@@ -150,76 +91,67 @@ def tango_polling(
|
|
|
150
91
|
return decorator
|
|
151
92
|
|
|
152
93
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
#
|
|
213
|
-
|
|
214
|
-
#
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
# if arg is not type(None):
|
|
218
|
-
# return arg, is_optional
|
|
219
|
-
# return field, False
|
|
220
|
-
#
|
|
221
|
-
#
|
|
222
|
-
# def _strip_device_vector(field: type[Device]) -> tuple[bool, type[Device]]:
|
|
223
|
-
# if get_origin(field) is DeviceVector:
|
|
224
|
-
# return True, get_args(field)[0]
|
|
225
|
-
# return False, field
|
|
94
|
+
class TangoDeviceConnector(DeviceConnector):
|
|
95
|
+
def __init__(
|
|
96
|
+
self,
|
|
97
|
+
trl: str | None,
|
|
98
|
+
device_proxy: DeviceProxy | None,
|
|
99
|
+
polling: tuple[bool, float, float | None, float | None],
|
|
100
|
+
signal_polling: dict[str, tuple[bool, float, float, float]],
|
|
101
|
+
) -> None:
|
|
102
|
+
self.trl = trl
|
|
103
|
+
self.proxy = device_proxy
|
|
104
|
+
self._polling = polling
|
|
105
|
+
self._signal_polling = signal_polling
|
|
106
|
+
|
|
107
|
+
def create_children_from_annotations(self, device: Device):
|
|
108
|
+
if not hasattr(self, "filler"):
|
|
109
|
+
self.filler = DeviceFiller(
|
|
110
|
+
device=device,
|
|
111
|
+
signal_backend_factory=TangoSignalBackend,
|
|
112
|
+
device_connector_factory=lambda: TangoDeviceConnector(
|
|
113
|
+
None, None, (False, 0.1, None, None), {}
|
|
114
|
+
),
|
|
115
|
+
)
|
|
116
|
+
list(self.filler.create_devices_from_annotations(filled=False))
|
|
117
|
+
list(self.filler.create_signals_from_annotations(filled=False))
|
|
118
|
+
self.filler.check_created()
|
|
119
|
+
|
|
120
|
+
async def connect(
|
|
121
|
+
self, device: Device, mock: bool | Mock, timeout: float, force_reconnect: bool
|
|
122
|
+
) -> None:
|
|
123
|
+
if mock:
|
|
124
|
+
# Make 2 entries for each DeviceVector
|
|
125
|
+
self.filler.create_device_vector_entries_to_mock(2)
|
|
126
|
+
else:
|
|
127
|
+
if self.trl and self.proxy is None:
|
|
128
|
+
self.proxy = await AsyncDeviceProxy(self.trl)
|
|
129
|
+
elif self.proxy and not self.trl:
|
|
130
|
+
self.trl = self.proxy.name()
|
|
131
|
+
else:
|
|
132
|
+
raise TypeError("Neither proxy nor trl supplied")
|
|
133
|
+
|
|
134
|
+
children = sorted(
|
|
135
|
+
set()
|
|
136
|
+
.union(self.proxy.get_attribute_list())
|
|
137
|
+
.union(self.proxy.get_command_list())
|
|
138
|
+
)
|
|
139
|
+
for name in children:
|
|
140
|
+
# TODO: strip attribute name
|
|
141
|
+
full_trl = f"{self.trl}/{name}"
|
|
142
|
+
signal_type = await infer_signal_type(full_trl, self.proxy)
|
|
143
|
+
if signal_type:
|
|
144
|
+
backend = self.filler.fill_child_signal(name, signal_type)
|
|
145
|
+
backend.datatype = await infer_python_type(full_trl, self.proxy)
|
|
146
|
+
backend.set_trl(full_trl)
|
|
147
|
+
if polling := self._signal_polling.get(name, ()):
|
|
148
|
+
backend.set_polling(*polling)
|
|
149
|
+
backend.allow_events(False)
|
|
150
|
+
elif self._polling[0]:
|
|
151
|
+
backend.set_polling(*self._polling)
|
|
152
|
+
backend.allow_events(False)
|
|
153
|
+
# Check that all the requested children have been filled
|
|
154
|
+
self.filler.check_filled(f"{self.trl}: {children}")
|
|
155
|
+
# Set the name of the device to name all children
|
|
156
|
+
device.set_name(device.name)
|
|
157
|
+
return await super().connect(device, mock, timeout, force_reconnect)
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
HintedSignal,
|
|
6
|
-
SignalR,
|
|
7
|
-
SignalRW,
|
|
8
|
-
SignalX,
|
|
9
|
-
)
|
|
1
|
+
from typing import Annotated as A
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import DEFAULT_TIMEOUT, AsyncStatus, SignalR, SignalRW, SignalX
|
|
4
|
+
from ophyd_async.core import StandardReadableFormat as Format
|
|
10
5
|
from ophyd_async.tango import TangoReadable, tango_polling
|
|
11
6
|
|
|
12
7
|
|
|
@@ -16,15 +11,10 @@ from ophyd_async.tango import TangoReadable, tango_polling
|
|
|
16
11
|
class TangoCounter(TangoReadable):
|
|
17
12
|
# Enter the name and type of the signals you want to use
|
|
18
13
|
# If type is None or Signal, the type will be inferred from the Tango device
|
|
19
|
-
counts: SignalR[int]
|
|
20
|
-
sample_time: SignalRW[float]
|
|
14
|
+
counts: A[SignalR[int], Format.HINTED_SIGNAL]
|
|
15
|
+
sample_time: A[SignalRW[float], Format.CONFIG_SIGNAL]
|
|
21
16
|
start: SignalX
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def __init__(self, trl: str | None = "", name=""):
|
|
25
|
-
super().__init__(trl, name=name)
|
|
26
|
-
self.add_readables([self.counts], HintedSignal)
|
|
27
|
-
self.add_readables([self.sample_time], ConfigSignal)
|
|
17
|
+
reset_: SignalX
|
|
28
18
|
|
|
29
19
|
@AsyncStatus.wrap
|
|
30
20
|
async def trigger(self) -> None:
|
|
@@ -34,4 +24,4 @@ class TangoCounter(TangoReadable):
|
|
|
34
24
|
|
|
35
25
|
@AsyncStatus.wrap
|
|
36
26
|
async def reset(self) -> None:
|
|
37
|
-
await self.
|
|
27
|
+
await self.reset_.trigger(wait=True, timeout=DEFAULT_TIMEOUT)
|
ophyd_async/tango/demo/_mover.py
CHANGED
|
@@ -7,8 +7,6 @@ from ophyd_async.core import (
|
|
|
7
7
|
DEFAULT_TIMEOUT,
|
|
8
8
|
AsyncStatus,
|
|
9
9
|
CalculatableTimeout,
|
|
10
|
-
ConfigSignal,
|
|
11
|
-
HintedSignal,
|
|
12
10
|
SignalR,
|
|
13
11
|
SignalRW,
|
|
14
12
|
SignalX,
|
|
@@ -17,6 +15,7 @@ from ophyd_async.core import (
|
|
|
17
15
|
observe_value,
|
|
18
16
|
wait_for_value,
|
|
19
17
|
)
|
|
18
|
+
from ophyd_async.core import StandardReadableFormat as Format
|
|
20
19
|
from ophyd_async.tango import TangoReadable, tango_polling
|
|
21
20
|
from tango import DevState
|
|
22
21
|
|
|
@@ -29,12 +28,12 @@ class TangoMover(TangoReadable, Movable, Stoppable):
|
|
|
29
28
|
position: SignalRW[float]
|
|
30
29
|
velocity: SignalRW[float]
|
|
31
30
|
state: SignalR[DevState]
|
|
32
|
-
|
|
31
|
+
stop_: SignalX
|
|
33
32
|
|
|
34
33
|
def __init__(self, trl: str | None = "", name=""):
|
|
35
34
|
super().__init__(trl, name=name)
|
|
36
|
-
self.add_readables([self.position],
|
|
37
|
-
self.add_readables([self.velocity],
|
|
35
|
+
self.add_readables([self.position], Format.HINTED_SIGNAL)
|
|
36
|
+
self.add_readables([self.velocity], Format.CONFIG_SIGNAL)
|
|
38
37
|
self._set_success = True
|
|
39
38
|
|
|
40
39
|
@WatchableAsyncStatus.wrap
|
|
@@ -74,4 +73,4 @@ class TangoMover(TangoReadable, Movable, Stoppable):
|
|
|
74
73
|
|
|
75
74
|
def stop(self, success: bool = True) -> AsyncStatus:
|
|
76
75
|
self._set_success = success
|
|
77
|
-
return self.
|
|
76
|
+
return self.stop_.trigger()
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from ._signal import (
|
|
2
|
-
__tango_signal_auto,
|
|
3
2
|
infer_python_type,
|
|
4
|
-
|
|
3
|
+
infer_signal_type,
|
|
5
4
|
make_backend,
|
|
6
5
|
tango_signal_r,
|
|
7
6
|
tango_signal_rw,
|
|
@@ -29,11 +28,10 @@ __all__ = (
|
|
|
29
28
|
"get_trl_descriptor",
|
|
30
29
|
"get_tango_trl",
|
|
31
30
|
"infer_python_type",
|
|
32
|
-
"
|
|
31
|
+
"infer_signal_type",
|
|
33
32
|
"make_backend",
|
|
34
33
|
"tango_signal_r",
|
|
35
34
|
"tango_signal_rw",
|
|
36
35
|
"tango_signal_w",
|
|
37
36
|
"tango_signal_x",
|
|
38
|
-
"__tango_signal_auto",
|
|
39
37
|
)
|
|
@@ -2,21 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import logging
|
|
5
6
|
from enum import Enum, IntEnum
|
|
6
7
|
|
|
7
8
|
import numpy.typing as npt
|
|
8
9
|
|
|
9
|
-
from ophyd_async.core import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
from ophyd_async.core import (
|
|
11
|
+
DEFAULT_TIMEOUT,
|
|
12
|
+
Signal,
|
|
13
|
+
SignalDatatypeT,
|
|
14
|
+
SignalR,
|
|
15
|
+
SignalRW,
|
|
16
|
+
SignalW,
|
|
17
|
+
SignalX,
|
|
13
18
|
)
|
|
14
19
|
from tango import AttrDataFormat, AttrWriteType, CmdArgType, DeviceProxy, DevState
|
|
15
20
|
from tango.asyncio import DeviceProxy as AsyncDeviceProxy
|
|
16
21
|
|
|
22
|
+
from ._tango_transport import TangoSignalBackend, get_python_type
|
|
23
|
+
|
|
17
24
|
|
|
18
25
|
def make_backend(
|
|
19
|
-
datatype: type[
|
|
26
|
+
datatype: type[SignalDatatypeT] | None,
|
|
20
27
|
read_trl: str = "",
|
|
21
28
|
write_trl: str = "",
|
|
22
29
|
device_proxy: DeviceProxy | None = None,
|
|
@@ -25,13 +32,13 @@ def make_backend(
|
|
|
25
32
|
|
|
26
33
|
|
|
27
34
|
def tango_signal_rw(
|
|
28
|
-
datatype: type[
|
|
35
|
+
datatype: type[SignalDatatypeT],
|
|
29
36
|
read_trl: str,
|
|
30
37
|
write_trl: str = "",
|
|
31
38
|
device_proxy: DeviceProxy | None = None,
|
|
32
39
|
timeout: float = DEFAULT_TIMEOUT,
|
|
33
40
|
name: str = "",
|
|
34
|
-
) -> SignalRW[
|
|
41
|
+
) -> SignalRW[SignalDatatypeT]:
|
|
35
42
|
"""Create a `SignalRW` backed by 1 or 2 Tango Attribute/Command
|
|
36
43
|
|
|
37
44
|
Parameters
|
|
@@ -54,12 +61,12 @@ def tango_signal_rw(
|
|
|
54
61
|
|
|
55
62
|
|
|
56
63
|
def tango_signal_r(
|
|
57
|
-
datatype: type[
|
|
64
|
+
datatype: type[SignalDatatypeT],
|
|
58
65
|
read_trl: str,
|
|
59
66
|
device_proxy: DeviceProxy | None = None,
|
|
60
67
|
timeout: float = DEFAULT_TIMEOUT,
|
|
61
68
|
name: str = "",
|
|
62
|
-
) -> SignalR[
|
|
69
|
+
) -> SignalR[SignalDatatypeT]:
|
|
63
70
|
"""Create a `SignalR` backed by 1 Tango Attribute/Command
|
|
64
71
|
|
|
65
72
|
Parameters
|
|
@@ -80,12 +87,12 @@ def tango_signal_r(
|
|
|
80
87
|
|
|
81
88
|
|
|
82
89
|
def tango_signal_w(
|
|
83
|
-
datatype: type[
|
|
90
|
+
datatype: type[SignalDatatypeT],
|
|
84
91
|
write_trl: str,
|
|
85
92
|
device_proxy: DeviceProxy | None = None,
|
|
86
93
|
timeout: float = DEFAULT_TIMEOUT,
|
|
87
94
|
name: str = "",
|
|
88
|
-
) -> SignalW[
|
|
95
|
+
) -> SignalW[SignalDatatypeT]:
|
|
89
96
|
"""Create a `SignalW` backed by 1 Tango Attribute/Command
|
|
90
97
|
|
|
91
98
|
Parameters
|
|
@@ -128,39 +135,10 @@ def tango_signal_x(
|
|
|
128
135
|
return SignalX(backend, timeout=timeout, name=name)
|
|
129
136
|
|
|
130
137
|
|
|
131
|
-
async def __tango_signal_auto(
|
|
132
|
-
datatype: type[T] | None = None,
|
|
133
|
-
*,
|
|
134
|
-
trl: str,
|
|
135
|
-
device_proxy: DeviceProxy | None,
|
|
136
|
-
timeout: float = DEFAULT_TIMEOUT,
|
|
137
|
-
name: str = "",
|
|
138
|
-
) -> SignalW | SignalX | SignalR | SignalRW | None:
|
|
139
|
-
try:
|
|
140
|
-
signal_character = await infer_signal_character(trl, device_proxy)
|
|
141
|
-
except RuntimeError as e:
|
|
142
|
-
if "Commands with different in and out dtypes" in str(e):
|
|
143
|
-
return None
|
|
144
|
-
else:
|
|
145
|
-
raise e
|
|
146
|
-
|
|
147
|
-
if datatype is None:
|
|
148
|
-
datatype = await infer_python_type(trl, device_proxy)
|
|
149
|
-
|
|
150
|
-
backend = make_backend(datatype, trl, trl, device_proxy)
|
|
151
|
-
if signal_character == "RW":
|
|
152
|
-
return SignalRW(backend=backend, timeout=timeout, name=name)
|
|
153
|
-
if signal_character == "R":
|
|
154
|
-
return SignalR(backend=backend, timeout=timeout, name=name)
|
|
155
|
-
if signal_character == "W":
|
|
156
|
-
return SignalW(backend=backend, timeout=timeout, name=name)
|
|
157
|
-
if signal_character == "X":
|
|
158
|
-
return SignalX(backend=backend, timeout=timeout, name=name)
|
|
159
|
-
|
|
160
|
-
|
|
161
138
|
async def infer_python_type(
|
|
162
139
|
trl: str = "", proxy: DeviceProxy | None = None
|
|
163
140
|
) -> object | npt.NDArray | type[DevState] | IntEnum:
|
|
141
|
+
# TODO: work out if this is still needed
|
|
164
142
|
device_trl, tr_name = trl.rsplit("/", 1)
|
|
165
143
|
if proxy is None:
|
|
166
144
|
dev_proxy = await AsyncDeviceProxy(device_trl)
|
|
@@ -187,7 +165,9 @@ async def infer_python_type(
|
|
|
187
165
|
return npt.NDArray[py_type] if isarray else py_type
|
|
188
166
|
|
|
189
167
|
|
|
190
|
-
async def
|
|
168
|
+
async def infer_signal_type(
|
|
169
|
+
trl, proxy: DeviceProxy | None = None
|
|
170
|
+
) -> type[Signal] | None:
|
|
191
171
|
device_trl, tr_name = trl.rsplit("/", 1)
|
|
192
172
|
if proxy is None:
|
|
193
173
|
dev_proxy = await AsyncDeviceProxy(device_trl)
|
|
@@ -204,20 +184,19 @@ async def infer_signal_character(trl, proxy: DeviceProxy | None = None) -> str:
|
|
|
204
184
|
if tr_name in dev_proxy.get_attribute_list():
|
|
205
185
|
config = await dev_proxy.get_attribute_config(tr_name)
|
|
206
186
|
if config.writable in [AttrWriteType.READ_WRITE, AttrWriteType.READ_WITH_WRITE]:
|
|
207
|
-
return
|
|
187
|
+
return SignalRW
|
|
208
188
|
elif config.writable == AttrWriteType.READ:
|
|
209
|
-
return
|
|
189
|
+
return SignalR
|
|
210
190
|
else:
|
|
211
|
-
return
|
|
191
|
+
return SignalW
|
|
212
192
|
|
|
213
193
|
if tr_name in dev_proxy.get_command_list():
|
|
214
194
|
config = await dev_proxy.get_command_config(tr_name)
|
|
215
195
|
if config.in_type == CmdArgType.DevVoid:
|
|
216
|
-
return
|
|
196
|
+
return SignalX
|
|
217
197
|
elif config.in_type != config.out_type:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
)
|
|
198
|
+
logging.debug("Commands with different in and out dtypes are not supported")
|
|
199
|
+
return None
|
|
221
200
|
else:
|
|
222
|
-
return
|
|
201
|
+
return SignalRW
|
|
223
202
|
raise RuntimeError(f"Unable to infer signal character for {trl}")
|