ophyd-async 0.6.0__py3-none-any.whl → 0.7.0__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 +4 -4
- ophyd_async/core/_detector.py +74 -37
- ophyd_async/core/_device.py +6 -1
- ophyd_async/core/_flyer.py +5 -20
- ophyd_async/core/_table.py +101 -18
- ophyd_async/epics/adaravis/_aravis_controller.py +4 -4
- ophyd_async/epics/adcore/_core_logic.py +2 -2
- ophyd_async/epics/adkinetix/_kinetix_controller.py +3 -3
- ophyd_async/epics/adpilatus/_pilatus_controller.py +5 -3
- ophyd_async/epics/adsimdetector/_sim.py +1 -1
- ophyd_async/epics/adsimdetector/_sim_controller.py +3 -3
- ophyd_async/epics/advimba/_vimba_controller.py +3 -3
- ophyd_async/epics/eiger/_eiger_controller.py +3 -3
- ophyd_async/fastcs/panda/_block.py +7 -0
- ophyd_async/fastcs/panda/_control.py +2 -2
- ophyd_async/fastcs/panda/_table.py +3 -37
- ophyd_async/fastcs/panda/_trigger.py +3 -3
- ophyd_async/fastcs/panda/_writer.py +2 -2
- ophyd_async/plan_stubs/_fly.py +1 -3
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector_controller.py +5 -3
- ophyd_async/tango/__init__.py +45 -0
- ophyd_async/tango/base_devices/__init__.py +4 -0
- ophyd_async/tango/base_devices/_base_device.py +225 -0
- ophyd_async/tango/base_devices/_tango_readable.py +33 -0
- ophyd_async/tango/demo/__init__.py +12 -0
- ophyd_async/tango/demo/_counter.py +37 -0
- ophyd_async/tango/demo/_detector.py +42 -0
- ophyd_async/tango/demo/_mover.py +77 -0
- ophyd_async/tango/demo/_tango/__init__.py +3 -0
- ophyd_async/tango/demo/_tango/_servers.py +108 -0
- ophyd_async/tango/signal/__init__.py +39 -0
- ophyd_async/tango/signal/_signal.py +223 -0
- ophyd_async/tango/signal/_tango_transport.py +764 -0
- {ophyd_async-0.6.0.dist-info → ophyd_async-0.7.0.dist-info}/METADATA +5 -1
- {ophyd_async-0.6.0.dist-info → ophyd_async-0.7.0.dist-info}/RECORD +40 -28
- {ophyd_async-0.6.0.dist-info → ophyd_async-0.7.0.dist-info}/WHEEL +1 -1
- {ophyd_async-0.6.0.dist-info → ophyd_async-0.7.0.dist-info}/LICENSE +0 -0
- {ophyd_async-0.6.0.dist-info → ophyd_async-0.7.0.dist-info}/entry_points.txt +0 -0
- {ophyd_async-0.6.0.dist-info → ophyd_async-0.7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from tango import AttrWriteType, DevState, GreenMode
|
|
7
|
+
from tango.server import Device, attribute, command
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DemoMover(Device):
|
|
11
|
+
green_mode = GreenMode.Asyncio
|
|
12
|
+
_position = 0.0
|
|
13
|
+
_setpoint = 0.0
|
|
14
|
+
_velocity = 0.5
|
|
15
|
+
_acceleration = 0.5
|
|
16
|
+
_precision = 0.1
|
|
17
|
+
_stop = False
|
|
18
|
+
DEVICE_CLASS_INITIAL_STATE = DevState.ON
|
|
19
|
+
|
|
20
|
+
@attribute(dtype=float, access=AttrWriteType.READ_WRITE)
|
|
21
|
+
async def position(self):
|
|
22
|
+
return self._position
|
|
23
|
+
|
|
24
|
+
async def write_position(self, new_position):
|
|
25
|
+
self._setpoint = new_position
|
|
26
|
+
await self.move()
|
|
27
|
+
|
|
28
|
+
@attribute(dtype=float, access=AttrWriteType.READ_WRITE)
|
|
29
|
+
async def velocity(self):
|
|
30
|
+
return self._velocity
|
|
31
|
+
|
|
32
|
+
async def write_velocity(self, value: float):
|
|
33
|
+
self._velocity = value
|
|
34
|
+
|
|
35
|
+
@attribute(dtype=DevState, access=AttrWriteType.READ)
|
|
36
|
+
async def state(self):
|
|
37
|
+
return self.get_state()
|
|
38
|
+
|
|
39
|
+
@command
|
|
40
|
+
async def stop(self):
|
|
41
|
+
self._stop = True
|
|
42
|
+
|
|
43
|
+
@command
|
|
44
|
+
async def move(self):
|
|
45
|
+
self.set_state(DevState.MOVING)
|
|
46
|
+
await self._move(self._setpoint)
|
|
47
|
+
self.set_state(DevState.ON)
|
|
48
|
+
|
|
49
|
+
async def _move(self, new_position):
|
|
50
|
+
self._setpoint = new_position
|
|
51
|
+
self._stop = False
|
|
52
|
+
step = 0.1
|
|
53
|
+
while True:
|
|
54
|
+
if self._stop:
|
|
55
|
+
self._stop = False
|
|
56
|
+
break
|
|
57
|
+
if self._position < new_position:
|
|
58
|
+
self._position = self._position + self._velocity * step
|
|
59
|
+
else:
|
|
60
|
+
self._position = self._position - self._velocity * step
|
|
61
|
+
if abs(self._position - new_position) < self._precision:
|
|
62
|
+
self._position = new_position
|
|
63
|
+
break
|
|
64
|
+
await asyncio.sleep(step)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class DemoCounter(Device):
|
|
68
|
+
green_mode = GreenMode.Asyncio
|
|
69
|
+
_counts = 0
|
|
70
|
+
_sample_time = 1.0
|
|
71
|
+
|
|
72
|
+
@attribute(dtype=int, access=AttrWriteType.READ)
|
|
73
|
+
async def counts(self):
|
|
74
|
+
return self._counts
|
|
75
|
+
|
|
76
|
+
@attribute(dtype=float, access=AttrWriteType.READ_WRITE)
|
|
77
|
+
async def sample_time(self):
|
|
78
|
+
return self._sample_time
|
|
79
|
+
|
|
80
|
+
async def write_sample_time(self, value: float):
|
|
81
|
+
self._sample_time = value
|
|
82
|
+
|
|
83
|
+
@attribute(dtype=DevState, access=AttrWriteType.READ)
|
|
84
|
+
async def state(self):
|
|
85
|
+
return self.get_state()
|
|
86
|
+
|
|
87
|
+
@command
|
|
88
|
+
async def reset(self):
|
|
89
|
+
self._counts = 0
|
|
90
|
+
return self._counts
|
|
91
|
+
|
|
92
|
+
@command
|
|
93
|
+
async def start(self):
|
|
94
|
+
self._counts = 0
|
|
95
|
+
if self._sample_time <= 0.0:
|
|
96
|
+
return
|
|
97
|
+
self.set_state(DevState.MOVING)
|
|
98
|
+
await self._trigger()
|
|
99
|
+
self.set_state(DevState.ON)
|
|
100
|
+
|
|
101
|
+
async def _trigger(self):
|
|
102
|
+
st = time.time()
|
|
103
|
+
while True:
|
|
104
|
+
ct = time.time()
|
|
105
|
+
if ct - st > self._sample_time:
|
|
106
|
+
break
|
|
107
|
+
self._counts += int(np.random.normal(1000, 100))
|
|
108
|
+
await asyncio.sleep(0.1)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from ._signal import (
|
|
2
|
+
__tango_signal_auto,
|
|
3
|
+
infer_python_type,
|
|
4
|
+
infer_signal_character,
|
|
5
|
+
make_backend,
|
|
6
|
+
tango_signal_r,
|
|
7
|
+
tango_signal_rw,
|
|
8
|
+
tango_signal_w,
|
|
9
|
+
tango_signal_x,
|
|
10
|
+
)
|
|
11
|
+
from ._tango_transport import (
|
|
12
|
+
AttributeProxy,
|
|
13
|
+
CommandProxy,
|
|
14
|
+
TangoSignalBackend,
|
|
15
|
+
ensure_proper_executor,
|
|
16
|
+
get_dtype_extended,
|
|
17
|
+
get_python_type,
|
|
18
|
+
get_tango_trl,
|
|
19
|
+
get_trl_descriptor,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = (
|
|
23
|
+
"AttributeProxy",
|
|
24
|
+
"CommandProxy",
|
|
25
|
+
"ensure_proper_executor",
|
|
26
|
+
"TangoSignalBackend",
|
|
27
|
+
"get_python_type",
|
|
28
|
+
"get_dtype_extended",
|
|
29
|
+
"get_trl_descriptor",
|
|
30
|
+
"get_tango_trl",
|
|
31
|
+
"infer_python_type",
|
|
32
|
+
"infer_signal_character",
|
|
33
|
+
"make_backend",
|
|
34
|
+
"tango_signal_r",
|
|
35
|
+
"tango_signal_rw",
|
|
36
|
+
"tango_signal_w",
|
|
37
|
+
"tango_signal_x",
|
|
38
|
+
"__tango_signal_auto",
|
|
39
|
+
)
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""Tango Signals over Pytango"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import Enum, IntEnum
|
|
6
|
+
|
|
7
|
+
import numpy.typing as npt
|
|
8
|
+
|
|
9
|
+
from ophyd_async.core import DEFAULT_TIMEOUT, SignalR, SignalRW, SignalW, SignalX, T
|
|
10
|
+
from ophyd_async.tango.signal._tango_transport import (
|
|
11
|
+
TangoSignalBackend,
|
|
12
|
+
get_python_type,
|
|
13
|
+
)
|
|
14
|
+
from tango import AttrDataFormat, AttrWriteType, CmdArgType, DeviceProxy, DevState
|
|
15
|
+
from tango.asyncio import DeviceProxy as AsyncDeviceProxy
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def make_backend(
|
|
19
|
+
datatype: type[T] | None,
|
|
20
|
+
read_trl: str = "",
|
|
21
|
+
write_trl: str = "",
|
|
22
|
+
device_proxy: DeviceProxy | None = None,
|
|
23
|
+
) -> TangoSignalBackend:
|
|
24
|
+
return TangoSignalBackend(datatype, read_trl, write_trl, device_proxy)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def tango_signal_rw(
|
|
28
|
+
datatype: type[T],
|
|
29
|
+
read_trl: str,
|
|
30
|
+
write_trl: str = "",
|
|
31
|
+
device_proxy: DeviceProxy | None = None,
|
|
32
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
33
|
+
name: str = "",
|
|
34
|
+
) -> SignalRW[T]:
|
|
35
|
+
"""Create a `SignalRW` backed by 1 or 2 Tango Attribute/Command
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
datatype:
|
|
40
|
+
Check that the Attribute/Command is of this type
|
|
41
|
+
read_trl:
|
|
42
|
+
The Attribute/Command to read and monitor
|
|
43
|
+
write_trl:
|
|
44
|
+
If given, use this Attribute/Command to write to, otherwise use read_trl
|
|
45
|
+
device_proxy:
|
|
46
|
+
If given, this DeviceProxy will be used
|
|
47
|
+
timeout:
|
|
48
|
+
The timeout for the read and write operations
|
|
49
|
+
name:
|
|
50
|
+
The name of the Signal
|
|
51
|
+
"""
|
|
52
|
+
backend = make_backend(datatype, read_trl, write_trl or read_trl, device_proxy)
|
|
53
|
+
return SignalRW(backend, timeout=timeout, name=name)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def tango_signal_r(
|
|
57
|
+
datatype: type[T],
|
|
58
|
+
read_trl: str,
|
|
59
|
+
device_proxy: DeviceProxy | None = None,
|
|
60
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
61
|
+
name: str = "",
|
|
62
|
+
) -> SignalR[T]:
|
|
63
|
+
"""Create a `SignalR` backed by 1 Tango Attribute/Command
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
datatype:
|
|
68
|
+
Check that the Attribute/Command is of this type
|
|
69
|
+
read_trl:
|
|
70
|
+
The Attribute/Command to read and monitor
|
|
71
|
+
device_proxy:
|
|
72
|
+
If given, this DeviceProxy will be used
|
|
73
|
+
timeout:
|
|
74
|
+
The timeout for the read operation
|
|
75
|
+
name:
|
|
76
|
+
The name of the Signal
|
|
77
|
+
"""
|
|
78
|
+
backend = make_backend(datatype, read_trl, read_trl, device_proxy)
|
|
79
|
+
return SignalR(backend, timeout=timeout, name=name)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def tango_signal_w(
|
|
83
|
+
datatype: type[T],
|
|
84
|
+
write_trl: str,
|
|
85
|
+
device_proxy: DeviceProxy | None = None,
|
|
86
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
87
|
+
name: str = "",
|
|
88
|
+
) -> SignalW[T]:
|
|
89
|
+
"""Create a `SignalW` backed by 1 Tango Attribute/Command
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
datatype:
|
|
94
|
+
Check that the Attribute/Command is of this type
|
|
95
|
+
write_trl:
|
|
96
|
+
The Attribute/Command to write to
|
|
97
|
+
device_proxy:
|
|
98
|
+
If given, this DeviceProxy will be used
|
|
99
|
+
timeout:
|
|
100
|
+
The timeout for the write operation
|
|
101
|
+
name:
|
|
102
|
+
The name of the Signal
|
|
103
|
+
"""
|
|
104
|
+
backend = make_backend(datatype, write_trl, write_trl, device_proxy)
|
|
105
|
+
return SignalW(backend, timeout=timeout, name=name)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def tango_signal_x(
|
|
109
|
+
write_trl: str,
|
|
110
|
+
device_proxy: DeviceProxy | None = None,
|
|
111
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
112
|
+
name: str = "",
|
|
113
|
+
) -> SignalX:
|
|
114
|
+
"""Create a `SignalX` backed by 1 Tango Attribute/Command
|
|
115
|
+
|
|
116
|
+
Parameters
|
|
117
|
+
----------
|
|
118
|
+
write_trl:
|
|
119
|
+
The Attribute/Command to write its initial value to on execute
|
|
120
|
+
device_proxy:
|
|
121
|
+
If given, this DeviceProxy will be used
|
|
122
|
+
timeout:
|
|
123
|
+
The timeout for the command operation
|
|
124
|
+
name:
|
|
125
|
+
The name of the Signal
|
|
126
|
+
"""
|
|
127
|
+
backend = make_backend(None, write_trl, write_trl, device_proxy)
|
|
128
|
+
return SignalX(backend, timeout=timeout, name=name)
|
|
129
|
+
|
|
130
|
+
|
|
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
|
+
async def infer_python_type(
|
|
162
|
+
trl: str = "", proxy: DeviceProxy | None = None
|
|
163
|
+
) -> object | npt.NDArray | type[DevState] | IntEnum:
|
|
164
|
+
device_trl, tr_name = trl.rsplit("/", 1)
|
|
165
|
+
if proxy is None:
|
|
166
|
+
dev_proxy = await AsyncDeviceProxy(device_trl)
|
|
167
|
+
else:
|
|
168
|
+
dev_proxy = proxy
|
|
169
|
+
|
|
170
|
+
if tr_name in dev_proxy.get_command_list():
|
|
171
|
+
config = await dev_proxy.get_command_config(tr_name)
|
|
172
|
+
isarray, py_type, _ = get_python_type(config.in_type)
|
|
173
|
+
elif tr_name in dev_proxy.get_attribute_list():
|
|
174
|
+
config = await dev_proxy.get_attribute_config(tr_name)
|
|
175
|
+
isarray, py_type, _ = get_python_type(config.data_type)
|
|
176
|
+
if py_type is Enum:
|
|
177
|
+
enum_dict = {label: i for i, label in enumerate(config.enum_labels)}
|
|
178
|
+
py_type = IntEnum("TangoEnum", enum_dict)
|
|
179
|
+
if config.data_format in [AttrDataFormat.SPECTRUM, AttrDataFormat.IMAGE]:
|
|
180
|
+
isarray = True
|
|
181
|
+
else:
|
|
182
|
+
raise RuntimeError(f"Cannot find {tr_name} in {device_trl}")
|
|
183
|
+
|
|
184
|
+
if py_type is CmdArgType.DevState:
|
|
185
|
+
py_type = DevState
|
|
186
|
+
|
|
187
|
+
return npt.NDArray[py_type] if isarray else py_type
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
async def infer_signal_character(trl, proxy: DeviceProxy | None = None) -> str:
|
|
191
|
+
device_trl, tr_name = trl.rsplit("/", 1)
|
|
192
|
+
if proxy is None:
|
|
193
|
+
dev_proxy = await AsyncDeviceProxy(device_trl)
|
|
194
|
+
else:
|
|
195
|
+
dev_proxy = proxy
|
|
196
|
+
|
|
197
|
+
if tr_name in dev_proxy.get_pipe_list():
|
|
198
|
+
raise NotImplementedError("Pipes are not supported")
|
|
199
|
+
|
|
200
|
+
if tr_name not in dev_proxy.get_attribute_list():
|
|
201
|
+
if tr_name not in dev_proxy.get_command_list():
|
|
202
|
+
raise RuntimeError(f"Cannot find {tr_name} in {device_trl}")
|
|
203
|
+
|
|
204
|
+
if tr_name in dev_proxy.get_attribute_list():
|
|
205
|
+
config = await dev_proxy.get_attribute_config(tr_name)
|
|
206
|
+
if config.writable in [AttrWriteType.READ_WRITE, AttrWriteType.READ_WITH_WRITE]:
|
|
207
|
+
return "RW"
|
|
208
|
+
elif config.writable == AttrWriteType.READ:
|
|
209
|
+
return "R"
|
|
210
|
+
else:
|
|
211
|
+
return "W"
|
|
212
|
+
|
|
213
|
+
if tr_name in dev_proxy.get_command_list():
|
|
214
|
+
config = await dev_proxy.get_command_config(tr_name)
|
|
215
|
+
if config.in_type == CmdArgType.DevVoid:
|
|
216
|
+
return "X"
|
|
217
|
+
elif config.in_type != config.out_type:
|
|
218
|
+
raise RuntimeError(
|
|
219
|
+
"Commands with different in and out dtypes are not" " supported"
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
return "RW"
|
|
223
|
+
raise RuntimeError(f"Unable to infer signal character for {trl}")
|