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.
Files changed (40) hide show
  1. ophyd_async/_version.py +2 -2
  2. ophyd_async/core/__init__.py +4 -4
  3. ophyd_async/core/_detector.py +74 -37
  4. ophyd_async/core/_device.py +6 -1
  5. ophyd_async/core/_flyer.py +5 -20
  6. ophyd_async/core/_table.py +101 -18
  7. ophyd_async/epics/adaravis/_aravis_controller.py +4 -4
  8. ophyd_async/epics/adcore/_core_logic.py +2 -2
  9. ophyd_async/epics/adkinetix/_kinetix_controller.py +3 -3
  10. ophyd_async/epics/adpilatus/_pilatus_controller.py +5 -3
  11. ophyd_async/epics/adsimdetector/_sim.py +1 -1
  12. ophyd_async/epics/adsimdetector/_sim_controller.py +3 -3
  13. ophyd_async/epics/advimba/_vimba_controller.py +3 -3
  14. ophyd_async/epics/eiger/_eiger_controller.py +3 -3
  15. ophyd_async/fastcs/panda/_block.py +7 -0
  16. ophyd_async/fastcs/panda/_control.py +2 -2
  17. ophyd_async/fastcs/panda/_table.py +3 -37
  18. ophyd_async/fastcs/panda/_trigger.py +3 -3
  19. ophyd_async/fastcs/panda/_writer.py +2 -2
  20. ophyd_async/plan_stubs/_fly.py +1 -3
  21. ophyd_async/sim/demo/_pattern_detector/_pattern_detector_controller.py +5 -3
  22. ophyd_async/tango/__init__.py +45 -0
  23. ophyd_async/tango/base_devices/__init__.py +4 -0
  24. ophyd_async/tango/base_devices/_base_device.py +225 -0
  25. ophyd_async/tango/base_devices/_tango_readable.py +33 -0
  26. ophyd_async/tango/demo/__init__.py +12 -0
  27. ophyd_async/tango/demo/_counter.py +37 -0
  28. ophyd_async/tango/demo/_detector.py +42 -0
  29. ophyd_async/tango/demo/_mover.py +77 -0
  30. ophyd_async/tango/demo/_tango/__init__.py +3 -0
  31. ophyd_async/tango/demo/_tango/_servers.py +108 -0
  32. ophyd_async/tango/signal/__init__.py +39 -0
  33. ophyd_async/tango/signal/_signal.py +223 -0
  34. ophyd_async/tango/signal/_tango_transport.py +764 -0
  35. {ophyd_async-0.6.0.dist-info → ophyd_async-0.7.0.dist-info}/METADATA +5 -1
  36. {ophyd_async-0.6.0.dist-info → ophyd_async-0.7.0.dist-info}/RECORD +40 -28
  37. {ophyd_async-0.6.0.dist-info → ophyd_async-0.7.0.dist-info}/WHEEL +1 -1
  38. {ophyd_async-0.6.0.dist-info → ophyd_async-0.7.0.dist-info}/LICENSE +0 -0
  39. {ophyd_async-0.6.0.dist-info → ophyd_async-0.7.0.dist-info}/entry_points.txt +0 -0
  40. {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}")