ophyd-async 0.8.0a4__py3-none-any.whl → 0.8.0a6__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 -0
- ophyd_async/core/_device.py +9 -2
- ophyd_async/core/_signal.py +86 -16
- ophyd_async/core/_soft_signal_backend.py +6 -2
- ophyd_async/core/_table.py +9 -4
- ophyd_async/core/_utils.py +24 -19
- ophyd_async/epics/adcore/_core_logic.py +3 -1
- ophyd_async/epics/core/_aioca.py +1 -1
- ophyd_async/epics/core/_p4p.py +1 -0
- ophyd_async/epics/eiger/_eiger_controller.py +6 -1
- ophyd_async/epics/testing/__init__.py +24 -0
- ophyd_async/epics/testing/_example_ioc.py +105 -0
- ophyd_async/epics/testing/_utils.py +78 -0
- ophyd_async/epics/testing/test_records.db +152 -0
- ophyd_async/epics/testing/test_records_pva.db +177 -0
- ophyd_async/fastcs/panda/_table.py +1 -1
- ophyd_async/plan_stubs/_ensure_connected.py +18 -11
- ophyd_async/tango/__init__.py +0 -43
- ophyd_async/tango/{signal → core}/__init__.py +7 -2
- ophyd_async/tango/{base_devices → core}/_base_device.py +38 -64
- ophyd_async/tango/{signal → core}/_signal.py +13 -3
- ophyd_async/tango/{base_devices → core}/_tango_readable.py +3 -4
- ophyd_async/tango/demo/_counter.py +6 -7
- ophyd_async/tango/demo/_mover.py +8 -7
- {ophyd_async-0.8.0a4.dist-info → ophyd_async-0.8.0a6.dist-info}/METADATA +47 -47
- {ophyd_async-0.8.0a4.dist-info → ophyd_async-0.8.0a6.dist-info}/RECORD +32 -28
- {ophyd_async-0.8.0a4.dist-info → ophyd_async-0.8.0a6.dist-info}/WHEEL +1 -1
- ophyd_async/tango/base_devices/__init__.py +0 -4
- /ophyd_async/tango/{signal → core}/_tango_transport.py +0 -0
- {ophyd_async-0.8.0a4.dist-info → ophyd_async-0.8.0a6.dist-info}/LICENSE +0 -0
- {ophyd_async-0.8.0a4.dist-info → ophyd_async-0.8.0a6.dist-info}/entry_points.txt +0 -0
- {ophyd_async-0.8.0a4.dist-info → ophyd_async-0.8.0a6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
record(bo, "$(device)bool") {
|
|
2
|
+
field(ZNAM, "No")
|
|
3
|
+
field(ONAM, "Yes")
|
|
4
|
+
field(VAL, "1")
|
|
5
|
+
field(PINI, "YES")
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
record(bo, "$(device)bool_unnamed") {
|
|
9
|
+
field(VAL, "1")
|
|
10
|
+
field(PINI, "YES")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
record(longout, "$(device)int") {
|
|
14
|
+
field(LLSV, "MAJOR") # LOLO is alarm
|
|
15
|
+
field(LSV, "MINOR") # LOW is warning
|
|
16
|
+
field(HSV, "MINOR") # HIGH is warning
|
|
17
|
+
field(HHSV, "MAJOR") # HIHI is alarm
|
|
18
|
+
field(HOPR, "100")
|
|
19
|
+
field(HIHI, "98")
|
|
20
|
+
field(HIGH, "96")
|
|
21
|
+
field(DRVH, "90")
|
|
22
|
+
field(DRVL, "10")
|
|
23
|
+
field(LOW, "5")
|
|
24
|
+
field(LOLO, "2")
|
|
25
|
+
field(LOPR, "0")
|
|
26
|
+
field(VAL, "42")
|
|
27
|
+
field(PINI, "YES")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
record(longout, "$(device)partialint") {
|
|
31
|
+
field(LLSV, "MAJOR") # LOLO is alarm
|
|
32
|
+
field(HHSV, "MAJOR") # HIHI is alarm
|
|
33
|
+
field(HOPR, "100")
|
|
34
|
+
field(HIHI, "98")
|
|
35
|
+
field(DRVH, "90")
|
|
36
|
+
field(DRVL, "10")
|
|
37
|
+
field(LOLO, "2")
|
|
38
|
+
field(LOPR, "0")
|
|
39
|
+
field(VAL, "42")
|
|
40
|
+
field(PINI, "YES")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
record(longout, "$(device)lessint") {
|
|
44
|
+
field(HSV, "MINOR") # LOW is warning
|
|
45
|
+
field(LSV, "MINOR") # HIGH is warning
|
|
46
|
+
field(HOPR, "100")
|
|
47
|
+
field(HIGH, "98")
|
|
48
|
+
field(LOW, "2")
|
|
49
|
+
field(LOPR, "0")
|
|
50
|
+
field(VAL, "42")
|
|
51
|
+
field(PINI, "YES")
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
record(ao, "$(device)float") {
|
|
55
|
+
field(PREC, "1")
|
|
56
|
+
field(EGU, "mm")
|
|
57
|
+
field(VAL, "3.141")
|
|
58
|
+
field(PINI, "YES")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
record(ao, "$(device)float_prec_0") {
|
|
62
|
+
field(PREC, "0")
|
|
63
|
+
field(EGU, "mm")
|
|
64
|
+
field(VAL, "3")
|
|
65
|
+
field(PINI, "YES")
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
record(ao, "$(device)float_prec_1") {
|
|
69
|
+
field(PREC, "1")
|
|
70
|
+
field(EGU, "mm")
|
|
71
|
+
field(VAL, "3")
|
|
72
|
+
field(PINI, "YES")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
record(stringout, "$(device)str") {
|
|
76
|
+
field(VAL, "hello")
|
|
77
|
+
field(PINI, "YES")
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
record(mbbo, "$(device)enum") {
|
|
81
|
+
field(ZRST, "Aaa")
|
|
82
|
+
field(ZRVL, "5")
|
|
83
|
+
field(ONST, "Bbb")
|
|
84
|
+
field(ONVL, "6")
|
|
85
|
+
field(TWST, "Ccc")
|
|
86
|
+
field(TWVL, "7")
|
|
87
|
+
field(VAL, "1")
|
|
88
|
+
field(PINI, "YES")
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
record(mbbo, "$(device)enum2") {
|
|
92
|
+
field(ZRST, "Aaa")
|
|
93
|
+
field(ONST, "Bbb")
|
|
94
|
+
field(TWST, "Ccc")
|
|
95
|
+
field(VAL, "1")
|
|
96
|
+
field(PINI, "YES")
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
record(waveform, "$(device)uint8a") {
|
|
100
|
+
field(NELM, "3")
|
|
101
|
+
field(FTVL, "UCHAR")
|
|
102
|
+
field(INP, {const:[0, 255]})
|
|
103
|
+
field(PINI, "YES")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
record(waveform, "$(device)int16a") {
|
|
107
|
+
field(NELM, "3")
|
|
108
|
+
field(FTVL, "SHORT")
|
|
109
|
+
field(INP, {const:[-32768, 32767]})
|
|
110
|
+
field(PINI, "YES")
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
record(waveform, "$(device)int32a") {
|
|
114
|
+
field(NELM, "3")
|
|
115
|
+
field(FTVL, "LONG")
|
|
116
|
+
field(INP, {const:[-2147483648, 2147483647]})
|
|
117
|
+
field(PINI, "YES")
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
record(waveform, "$(device)float32a") {
|
|
121
|
+
field(NELM, "3")
|
|
122
|
+
field(FTVL, "FLOAT")
|
|
123
|
+
field(INP, {const:[0.000002, -123.123]})
|
|
124
|
+
field(PINI, "YES")
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
record(waveform, "$(device)float64a") {
|
|
128
|
+
field(NELM, "3")
|
|
129
|
+
field(FTVL, "DOUBLE")
|
|
130
|
+
field(INP, {const:[0.1, -12345678.123]})
|
|
131
|
+
field(PINI, "YES")
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
record(waveform, "$(device)stra") {
|
|
135
|
+
field(NELM, "3")
|
|
136
|
+
field(FTVL, "STRING")
|
|
137
|
+
field(INP, {const:["five", "six", "seven"]})
|
|
138
|
+
field(PINI, "YES")
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
record(waveform, "$(device)longstr") {
|
|
142
|
+
field(NELM, "80")
|
|
143
|
+
field(FTVL, "CHAR")
|
|
144
|
+
field(INP, {const:"a string that is just longer than forty characters"})
|
|
145
|
+
field(PINI, "YES")
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
record(lsi, "$(device)longstr2") {
|
|
149
|
+
field(SIZV, "80")
|
|
150
|
+
field(INP, {const:"a string that is just longer than forty characters"})
|
|
151
|
+
field(PINI, "YES")
|
|
152
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
record(waveform, "$(device)int8a") {
|
|
2
|
+
field(NELM, "3")
|
|
3
|
+
field(FTVL, "CHAR")
|
|
4
|
+
field(INP, {const:[-128, 127]})
|
|
5
|
+
field(PINI, "YES")
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
record(waveform, "$(device)uint16a") {
|
|
9
|
+
field(NELM, "3")
|
|
10
|
+
field(FTVL, "USHORT")
|
|
11
|
+
field(INP, {const:[0, 65535]})
|
|
12
|
+
field(PINI, "YES")
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
record(waveform, "$(device)uint32a") {
|
|
16
|
+
field(NELM, "3")
|
|
17
|
+
field(FTVL, "ULONG")
|
|
18
|
+
field(INP, {const:[0, 4294967295]})
|
|
19
|
+
field(PINI, "YES")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
record(waveform, "$(device)int64a") {
|
|
23
|
+
field(NELM, "3")
|
|
24
|
+
field(FTVL, "INT64")
|
|
25
|
+
# Can't do 64-bit int with JSON numbers in a const link...
|
|
26
|
+
field(INP, {const:[-2147483649, 2147483648]})
|
|
27
|
+
field(PINI, "YES")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
record(waveform, "$(device)uint64a") {
|
|
31
|
+
field(NELM, "3")
|
|
32
|
+
field(FTVL, "UINT64")
|
|
33
|
+
field(INP, {const:[0, 4294967297]})
|
|
34
|
+
field(PINI, "YES")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
record(waveform, "$(device)table:labels") {
|
|
38
|
+
field(FTVL, "STRING")
|
|
39
|
+
field(NELM, "5")
|
|
40
|
+
field(INP, {const:["Bool", "Int", "Float", "Str", "Enum"]})
|
|
41
|
+
field(PINI, "YES")
|
|
42
|
+
info(Q:group, {
|
|
43
|
+
"$(device)table": {
|
|
44
|
+
"+id": "epics:nt/NTTable:1.0",
|
|
45
|
+
"labels": {
|
|
46
|
+
"+type": "plain",
|
|
47
|
+
"+channel": "VAL"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
record(waveform, "$(device)table:bool")
|
|
54
|
+
{
|
|
55
|
+
field(FTVL, "UCHAR")
|
|
56
|
+
field(NELM, "4096")
|
|
57
|
+
field(INP, {const:[false, false, true, true]})
|
|
58
|
+
field(PINI, "YES")
|
|
59
|
+
info(Q:group, {
|
|
60
|
+
"$(device)table": {
|
|
61
|
+
"value.bool": {
|
|
62
|
+
"+type": "plain",
|
|
63
|
+
"+channel": "VAL",
|
|
64
|
+
"+putorder": 1
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
record(waveform, "$(device)table:int")
|
|
71
|
+
{
|
|
72
|
+
field(FTVL, "LONG")
|
|
73
|
+
field(NELM, "4096")
|
|
74
|
+
field(INP, {const:[1, 8, -9, 32]})
|
|
75
|
+
field(PINI, "YES")
|
|
76
|
+
info(Q:group, {
|
|
77
|
+
"$(device)table": {
|
|
78
|
+
"value.int": {
|
|
79
|
+
"+type": "plain",
|
|
80
|
+
"+channel": "VAL",
|
|
81
|
+
"+putorder": 2
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
record(waveform, "$(device)table:float")
|
|
88
|
+
{
|
|
89
|
+
field(FTVL, "DOUBLE")
|
|
90
|
+
field(NELM, "4096")
|
|
91
|
+
field(INP, {const:[1.8, 8.2, -6, 32.9887]})
|
|
92
|
+
field(PINI, "YES")
|
|
93
|
+
info(Q:group, {
|
|
94
|
+
"$(device)table": {
|
|
95
|
+
"value.float": {
|
|
96
|
+
"+type": "plain",
|
|
97
|
+
"+channel": "VAL",
|
|
98
|
+
"+putorder": 3
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
record(waveform, "$(device)table:str")
|
|
105
|
+
{
|
|
106
|
+
field(FTVL, "STRING")
|
|
107
|
+
field(NELM, "4096")
|
|
108
|
+
field(INP, {const:["Hello", "World", "Foo", "Bar"]})
|
|
109
|
+
field(PINI, "YES")
|
|
110
|
+
info(Q:group, {
|
|
111
|
+
"$(device)table": {
|
|
112
|
+
"value.str": {
|
|
113
|
+
"+type": "plain",
|
|
114
|
+
"+channel": "VAL",
|
|
115
|
+
"+putorder": 4
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
record(waveform, "$(device)table:enum")
|
|
122
|
+
{
|
|
123
|
+
field(FTVL, "STRING")
|
|
124
|
+
field(NELM, "4096")
|
|
125
|
+
field(INP, {const:["Aaa", "Bbb", "Aaa", "Ccc"]})
|
|
126
|
+
field(PINI, "YES")
|
|
127
|
+
info(Q:group, {
|
|
128
|
+
"$(device)table": {
|
|
129
|
+
"value.enum": {
|
|
130
|
+
"+type": "plain",
|
|
131
|
+
"+channel": "VAL",
|
|
132
|
+
"+putorder": 5,
|
|
133
|
+
"+trigger": "*",
|
|
134
|
+
},
|
|
135
|
+
"": {"+type": "meta", "+channel": "VAL"}
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
record(longout, "$(device)ntndarray:ArraySize0_RBV") {
|
|
141
|
+
field(VAL, "3")
|
|
142
|
+
field(PINI, "YES")
|
|
143
|
+
info(Q:group, {
|
|
144
|
+
"$(device)ntndarray":{
|
|
145
|
+
"dimension[0].size":{+channel:"VAL", +type:"plain", +putorder:0}
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
record(longout, "$(device)ntndarray:ArraySize1_RBV") {
|
|
151
|
+
field(VAL, "2")
|
|
152
|
+
field(PINI, "YES")
|
|
153
|
+
info(Q:group, {
|
|
154
|
+
"$(device)ntndarray":{
|
|
155
|
+
"dimension[1].size":{+channel:"VAL", +type:"plain", +putorder:0}
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
record(waveform, "$(device)ntndarray:data")
|
|
161
|
+
{
|
|
162
|
+
field(FTVL, "INT64")
|
|
163
|
+
field(NELM, "6")
|
|
164
|
+
field(INP, {const:[0, 0, 0, 0, 0, 0]})
|
|
165
|
+
field(PINI, "YES")
|
|
166
|
+
info(Q:group, {
|
|
167
|
+
"$(device)ntndarray":{
|
|
168
|
+
+id:"epics:nt/NTNDArray:1.0",
|
|
169
|
+
"value":{
|
|
170
|
+
+type:"any",
|
|
171
|
+
+channel:"VAL",
|
|
172
|
+
+trigger:"*",
|
|
173
|
+
},
|
|
174
|
+
"": {+type:"meta", +channel:"SEVR"}
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from collections.abc import Awaitable
|
|
2
|
+
|
|
1
3
|
import bluesky.plan_stubs as bps
|
|
2
4
|
|
|
3
5
|
from ophyd_async.core import DEFAULT_TIMEOUT, Device, LazyMock, wait_for_connection
|
|
@@ -9,18 +11,23 @@ def ensure_connected(
|
|
|
9
11
|
timeout: float = DEFAULT_TIMEOUT,
|
|
10
12
|
force_reconnect=False,
|
|
11
13
|
):
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
device_names = [device.name for device in devices]
|
|
15
|
+
non_unique = {
|
|
16
|
+
device: device.name for device in devices if device_names.count(device.name) > 1
|
|
17
|
+
}
|
|
18
|
+
if non_unique:
|
|
19
|
+
raise ValueError(f"Devices do not have unique names {non_unique}")
|
|
20
|
+
|
|
21
|
+
def connect_devices() -> Awaitable[None]:
|
|
22
|
+
coros = {
|
|
23
|
+
device.name: device.connect(
|
|
24
|
+
mock=mock, timeout=timeout, force_reconnect=force_reconnect
|
|
21
25
|
)
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
for device in devices
|
|
27
|
+
}
|
|
28
|
+
return wait_for_connection(**coros)
|
|
29
|
+
|
|
30
|
+
(connect_task,) = yield from bps.wait_for([connect_devices])
|
|
24
31
|
|
|
25
32
|
if connect_task and connect_task.exception() is not None:
|
|
26
33
|
raise connect_task.exception()
|
ophyd_async/tango/__init__.py
CHANGED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
from .base_devices import (
|
|
2
|
-
TangoDevice,
|
|
3
|
-
TangoReadable,
|
|
4
|
-
tango_polling,
|
|
5
|
-
)
|
|
6
|
-
from .signal import (
|
|
7
|
-
AttributeProxy,
|
|
8
|
-
CommandProxy,
|
|
9
|
-
TangoSignalBackend,
|
|
10
|
-
ensure_proper_executor,
|
|
11
|
-
get_dtype_extended,
|
|
12
|
-
get_python_type,
|
|
13
|
-
get_tango_trl,
|
|
14
|
-
get_trl_descriptor,
|
|
15
|
-
infer_python_type,
|
|
16
|
-
infer_signal_type,
|
|
17
|
-
make_backend,
|
|
18
|
-
tango_signal_r,
|
|
19
|
-
tango_signal_rw,
|
|
20
|
-
tango_signal_w,
|
|
21
|
-
tango_signal_x,
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
__all__ = [
|
|
25
|
-
"TangoDevice",
|
|
26
|
-
"TangoReadable",
|
|
27
|
-
"tango_polling",
|
|
28
|
-
"TangoSignalBackend",
|
|
29
|
-
"get_python_type",
|
|
30
|
-
"get_dtype_extended",
|
|
31
|
-
"get_trl_descriptor",
|
|
32
|
-
"get_tango_trl",
|
|
33
|
-
"infer_python_type",
|
|
34
|
-
"infer_signal_type",
|
|
35
|
-
"make_backend",
|
|
36
|
-
"AttributeProxy",
|
|
37
|
-
"CommandProxy",
|
|
38
|
-
"ensure_proper_executor",
|
|
39
|
-
"tango_signal_r",
|
|
40
|
-
"tango_signal_rw",
|
|
41
|
-
"tango_signal_w",
|
|
42
|
-
"tango_signal_x",
|
|
43
|
-
]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from ._base_device import TangoDevice, TangoPolling
|
|
1
2
|
from ._signal import (
|
|
2
3
|
infer_python_type,
|
|
3
4
|
infer_signal_type,
|
|
@@ -7,6 +8,7 @@ from ._signal import (
|
|
|
7
8
|
tango_signal_w,
|
|
8
9
|
tango_signal_x,
|
|
9
10
|
)
|
|
11
|
+
from ._tango_readable import TangoReadable
|
|
10
12
|
from ._tango_transport import (
|
|
11
13
|
AttributeProxy,
|
|
12
14
|
CommandProxy,
|
|
@@ -18,7 +20,7 @@ from ._tango_transport import (
|
|
|
18
20
|
get_trl_descriptor,
|
|
19
21
|
)
|
|
20
22
|
|
|
21
|
-
__all__ =
|
|
23
|
+
__all__ = [
|
|
22
24
|
"AttributeProxy",
|
|
23
25
|
"CommandProxy",
|
|
24
26
|
"ensure_proper_executor",
|
|
@@ -34,4 +36,7 @@ __all__ = (
|
|
|
34
36
|
"tango_signal_rw",
|
|
35
37
|
"tango_signal_w",
|
|
36
38
|
"tango_signal_x",
|
|
37
|
-
|
|
39
|
+
"TangoDevice",
|
|
40
|
+
"TangoReadable",
|
|
41
|
+
"TangoPolling",
|
|
42
|
+
]
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
from ophyd_async.core
|
|
7
|
-
from ophyd_async.tango.signal import (
|
|
8
|
-
TangoSignalBackend,
|
|
9
|
-
infer_python_type,
|
|
10
|
-
infer_signal_type,
|
|
11
|
-
)
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Generic, TypeVar
|
|
5
|
+
|
|
6
|
+
from ophyd_async.core import Device, DeviceConnector, DeviceFiller, LazyMock
|
|
12
7
|
from tango import DeviceProxy as DeviceProxy
|
|
13
8
|
from tango.asyncio import DeviceProxy as AsyncDeviceProxy
|
|
14
9
|
|
|
10
|
+
from ._signal import TangoSignalBackend, infer_python_type, infer_signal_type
|
|
11
|
+
|
|
15
12
|
T = TypeVar("T")
|
|
16
13
|
|
|
17
14
|
|
|
@@ -32,63 +29,45 @@ class TangoDevice(Device):
|
|
|
32
29
|
|
|
33
30
|
trl: str = ""
|
|
34
31
|
proxy: DeviceProxy | None = None
|
|
35
|
-
_polling: tuple[bool, float, float | None, float | None] = (False, 0.1, None, 0.1)
|
|
36
|
-
_signal_polling: dict[str, tuple[bool, float, float, float]] = {}
|
|
37
|
-
_poll_only_annotated_signals: bool = True
|
|
38
32
|
|
|
39
33
|
def __init__(
|
|
40
34
|
self,
|
|
41
35
|
trl: str | None = None,
|
|
42
36
|
device_proxy: DeviceProxy | None = None,
|
|
37
|
+
support_events: bool = False,
|
|
43
38
|
name: str = "",
|
|
44
39
|
) -> None:
|
|
45
40
|
connector = TangoDeviceConnector(
|
|
46
|
-
trl=trl,
|
|
47
|
-
device_proxy=device_proxy,
|
|
48
|
-
polling=self._polling,
|
|
49
|
-
signal_polling=self._signal_polling,
|
|
41
|
+
trl=trl, device_proxy=device_proxy, support_events=support_events
|
|
50
42
|
)
|
|
51
43
|
super().__init__(name=name, connector=connector)
|
|
52
44
|
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
| None = None
|
|
58
|
-
|
|
59
|
-
):
|
|
60
|
-
"""
|
|
61
|
-
Class decorator to configure polling for Tango devices.
|
|
62
|
-
|
|
63
|
-
This decorator allows for the configuration of both device-level and signal-level
|
|
64
|
-
polling for Tango devices. Polling is useful for device servers that do not support
|
|
65
|
-
event-driven updates.
|
|
66
|
-
|
|
67
|
-
Parameters
|
|
68
|
-
----------
|
|
69
|
-
polling : Optional[Union[Tuple[float, float, float],
|
|
70
|
-
Dict[str, Tuple[float, float, float]]]], optional
|
|
71
|
-
Device-level polling configuration as a tuple of three floats representing the
|
|
72
|
-
polling interval, polling timeout, and polling delay. Alternatively,
|
|
73
|
-
a dictionary can be provided to specify signal-level polling configurations
|
|
74
|
-
directly.
|
|
75
|
-
signal_polling : Optional[Dict[str, Tuple[float, float, float]]], optional
|
|
76
|
-
Signal-level polling configuration as a dictionary where keys are signal names
|
|
77
|
-
and values are tuples of three floats representing the polling interval, polling
|
|
78
|
-
timeout, and polling delay.
|
|
79
|
-
"""
|
|
80
|
-
if isinstance(polling, dict):
|
|
81
|
-
signal_polling = polling
|
|
82
|
-
polling = None
|
|
46
|
+
@dataclass
|
|
47
|
+
class TangoPolling(Generic[T]):
|
|
48
|
+
ophyd_polling_period: float = 0.1
|
|
49
|
+
abs_change: T | None = None
|
|
50
|
+
rel_change: T | None = None
|
|
83
51
|
|
|
84
|
-
def decorator(cls):
|
|
85
|
-
if polling is not None:
|
|
86
|
-
cls._polling = (True, *polling)
|
|
87
|
-
if signal_polling is not None:
|
|
88
|
-
cls._signal_polling = {k: (True, *v) for k, v in signal_polling.items()}
|
|
89
|
-
return cls
|
|
90
52
|
|
|
91
|
-
|
|
53
|
+
def fill_backend_with_polling(
|
|
54
|
+
support_events: bool, backend: TangoSignalBackend, annotations: list[Any]
|
|
55
|
+
):
|
|
56
|
+
unhandled = []
|
|
57
|
+
while annotations:
|
|
58
|
+
annotation = annotations.pop(0)
|
|
59
|
+
backend.allow_events(support_events)
|
|
60
|
+
if isinstance(annotation, TangoPolling):
|
|
61
|
+
backend.set_polling(
|
|
62
|
+
not support_events,
|
|
63
|
+
annotation.ophyd_polling_period,
|
|
64
|
+
annotation.abs_change,
|
|
65
|
+
annotation.rel_change,
|
|
66
|
+
)
|
|
67
|
+
else:
|
|
68
|
+
unhandled.append(annotation)
|
|
69
|
+
annotations.extend(unhandled)
|
|
70
|
+
# These leftover annotations will now be handled by the iterator
|
|
92
71
|
|
|
93
72
|
|
|
94
73
|
class TangoDeviceConnector(DeviceConnector):
|
|
@@ -96,13 +75,11 @@ class TangoDeviceConnector(DeviceConnector):
|
|
|
96
75
|
self,
|
|
97
76
|
trl: str | None,
|
|
98
77
|
device_proxy: DeviceProxy | None,
|
|
99
|
-
|
|
100
|
-
signal_polling: dict[str, tuple[bool, float, float, float]],
|
|
78
|
+
support_events: bool,
|
|
101
79
|
) -> None:
|
|
102
80
|
self.trl = trl
|
|
103
81
|
self.proxy = device_proxy
|
|
104
|
-
self.
|
|
105
|
-
self._signal_polling = signal_polling
|
|
82
|
+
self._support_events = support_events
|
|
106
83
|
|
|
107
84
|
def create_children_from_annotations(self, device: Device):
|
|
108
85
|
if not hasattr(self, "filler"):
|
|
@@ -110,11 +87,14 @@ class TangoDeviceConnector(DeviceConnector):
|
|
|
110
87
|
device=device,
|
|
111
88
|
signal_backend_factory=TangoSignalBackend,
|
|
112
89
|
device_connector_factory=lambda: TangoDeviceConnector(
|
|
113
|
-
None, None,
|
|
90
|
+
None, None, self._support_events
|
|
114
91
|
),
|
|
115
92
|
)
|
|
116
93
|
list(self.filler.create_devices_from_annotations(filled=False))
|
|
117
|
-
|
|
94
|
+
for backend, annotations in self.filler.create_signals_from_annotations(
|
|
95
|
+
filled=False
|
|
96
|
+
):
|
|
97
|
+
fill_backend_with_polling(self._support_events, backend, annotations)
|
|
118
98
|
self.filler.check_created()
|
|
119
99
|
|
|
120
100
|
async def connect_mock(self, device: Device, mock: LazyMock):
|
|
@@ -145,12 +125,6 @@ class TangoDeviceConnector(DeviceConnector):
|
|
|
145
125
|
backend = self.filler.fill_child_signal(name, signal_type)
|
|
146
126
|
backend.datatype = await infer_python_type(full_trl, self.proxy)
|
|
147
127
|
backend.set_trl(full_trl)
|
|
148
|
-
if polling := self._signal_polling.get(name, ()):
|
|
149
|
-
backend.set_polling(*polling)
|
|
150
|
-
backend.allow_events(False)
|
|
151
|
-
elif self._polling[0]:
|
|
152
|
-
backend.set_polling(*self._polling)
|
|
153
|
-
backend.allow_events(False)
|
|
154
128
|
# Check that all the requested children have been filled
|
|
155
129
|
self.filler.check_filled(f"{self.trl}: {children}")
|
|
156
130
|
# Set the name of the device to name all children
|
|
@@ -16,7 +16,14 @@ from ophyd_async.core import (
|
|
|
16
16
|
SignalW,
|
|
17
17
|
SignalX,
|
|
18
18
|
)
|
|
19
|
-
from tango import
|
|
19
|
+
from tango import (
|
|
20
|
+
AttrDataFormat,
|
|
21
|
+
AttrWriteType,
|
|
22
|
+
CmdArgType,
|
|
23
|
+
DeviceProxy,
|
|
24
|
+
DevState,
|
|
25
|
+
NonSupportedFeature, # type: ignore
|
|
26
|
+
)
|
|
20
27
|
from tango.asyncio import DeviceProxy as AsyncDeviceProxy
|
|
21
28
|
|
|
22
29
|
from ._tango_transport import TangoSignalBackend, get_python_type
|
|
@@ -174,8 +181,11 @@ async def infer_signal_type(
|
|
|
174
181
|
else:
|
|
175
182
|
dev_proxy = proxy
|
|
176
183
|
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
try:
|
|
185
|
+
if tr_name in dev_proxy.get_pipe_list():
|
|
186
|
+
raise NotImplementedError("Pipes are not supported")
|
|
187
|
+
except NonSupportedFeature: # type: ignore
|
|
188
|
+
pass
|
|
179
189
|
|
|
180
190
|
if tr_name not in dev_proxy.get_attribute_list():
|
|
181
191
|
if tr_name not in dev_proxy.get_command_list():
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from ophyd_async.core import
|
|
4
|
-
StandardReadable,
|
|
5
|
-
)
|
|
6
|
-
from ophyd_async.tango.base_devices._base_device import TangoDevice
|
|
3
|
+
from ophyd_async.core import StandardReadable
|
|
7
4
|
from tango import DeviceProxy
|
|
8
5
|
|
|
6
|
+
from ._base_device import TangoDevice
|
|
7
|
+
|
|
9
8
|
|
|
10
9
|
class TangoReadable(TangoDevice, StandardReadable):
|
|
11
10
|
"""
|
|
@@ -2,18 +2,17 @@ from typing import Annotated as A
|
|
|
2
2
|
|
|
3
3
|
from ophyd_async.core import DEFAULT_TIMEOUT, AsyncStatus, SignalR, SignalRW, SignalX
|
|
4
4
|
from ophyd_async.core import StandardReadableFormat as Format
|
|
5
|
-
from ophyd_async.tango import
|
|
5
|
+
from ophyd_async.tango.core import TangoPolling, TangoReadable
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
# Enable device level polling, useful for servers that do not support events
|
|
9
|
-
# Polling for individual signal can be enabled with a dict
|
|
10
|
-
@tango_polling({"counts": (1.0, 0.1, 0.1), "sample_time": (0.1, 0.1, 0.1)})
|
|
11
8
|
class TangoCounter(TangoReadable):
|
|
12
9
|
# Enter the name and type of the signals you want to use
|
|
13
|
-
# If
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
# If the server doesn't support events, the TangoPolling annotation gives
|
|
11
|
+
# the parameters for ophyd to poll instead
|
|
12
|
+
counts: A[SignalR[int], Format.HINTED_SIGNAL, TangoPolling(1.0, 0.1, 0.1)]
|
|
13
|
+
sample_time: A[SignalRW[float], Format.CONFIG_SIGNAL, TangoPolling(0.1, 0.1, 0.1)]
|
|
16
14
|
start: SignalX
|
|
15
|
+
# If a tango name clashes with a bluesky verb, add a trailing underscore
|
|
17
16
|
reset_: SignalX
|
|
18
17
|
|
|
19
18
|
@AsyncStatus.wrap
|