ophyd-async 0.3a3__py3-none-any.whl → 0.3a4__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 +24 -10
- ophyd_async/core/async_status.py +2 -0
- ophyd_async/core/device.py +22 -9
- ophyd_async/core/mock_signal_backend.py +86 -0
- ophyd_async/core/mock_signal_utils.py +149 -0
- ophyd_async/core/signal.py +38 -42
- ophyd_async/core/{sim_signal_backend.py → soft_signal_backend.py} +23 -33
- ophyd_async/epics/_backend/_aioca.py +11 -7
- ophyd_async/epics/_backend/_p4p.py +11 -7
- ophyd_async/epics/_backend/common.py +17 -17
- ophyd_async/epics/areadetector/__init__.py +0 -4
- ophyd_async/epics/areadetector/drivers/ad_base.py +12 -10
- ophyd_async/epics/areadetector/drivers/aravis_driver.py +7 -5
- ophyd_async/epics/areadetector/drivers/kinetix_driver.py +7 -4
- ophyd_async/epics/areadetector/drivers/pilatus_driver.py +5 -2
- ophyd_async/epics/areadetector/drivers/vimba_driver.py +12 -7
- ophyd_async/epics/areadetector/utils.py +2 -12
- ophyd_async/epics/areadetector/writers/nd_file_hdf.py +21 -19
- ophyd_async/epics/areadetector/writers/nd_plugin.py +6 -7
- ophyd_async/epics/motion/motor.py +5 -1
- ophyd_async/epics/pvi/pvi.py +11 -11
- ophyd_async/panda/_hdf_panda.py +3 -3
- ophyd_async/sim/demo/sim_motor.py +4 -4
- ophyd_async/sim/pattern_generator.py +5 -5
- {ophyd_async-0.3a3.dist-info → ophyd_async-0.3a4.dist-info}/METADATA +2 -2
- {ophyd_async-0.3a3.dist-info → ophyd_async-0.3a4.dist-info}/RECORD +31 -29
- {ophyd_async-0.3a3.dist-info → ophyd_async-0.3a4.dist-info}/LICENSE +0 -0
- {ophyd_async-0.3a3.dist-info → ophyd_async-0.3a4.dist-info}/WHEEL +0 -0
- {ophyd_async-0.3a3.dist-info → ophyd_async-0.3a4.dist-info}/entry_points.txt +0 -0
- {ophyd_async-0.3a3.dist-info → ophyd_async-0.3a4.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import inspect
|
|
5
4
|
import time
|
|
6
5
|
from collections import abc
|
|
7
6
|
from dataclasses import dataclass
|
|
8
7
|
from enum import Enum
|
|
9
|
-
from typing import
|
|
8
|
+
from typing import Dict, Generic, Optional, Type, Union, cast, get_origin
|
|
10
9
|
|
|
11
10
|
import numpy as np
|
|
12
11
|
from bluesky.protocols import DataKey, Dtype, Reading
|
|
@@ -22,7 +21,7 @@ primitive_dtypes: Dict[type, Dtype] = {
|
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
class
|
|
24
|
+
class SoftConverter(Generic[T]):
|
|
26
25
|
def value(self, value: T) -> T:
|
|
27
26
|
return value
|
|
28
27
|
|
|
@@ -55,7 +54,7 @@ class SimConverter(Generic[T]):
|
|
|
55
54
|
return datatype()
|
|
56
55
|
|
|
57
56
|
|
|
58
|
-
class
|
|
57
|
+
class SoftArrayConverter(SoftConverter):
|
|
59
58
|
def get_datakey(self, source: str, value) -> DataKey:
|
|
60
59
|
return {"source": source, "dtype": "array", "shape": [len(value)]}
|
|
61
60
|
|
|
@@ -70,7 +69,7 @@ class SimArrayConverter(SimConverter):
|
|
|
70
69
|
|
|
71
70
|
|
|
72
71
|
@dataclass
|
|
73
|
-
class
|
|
72
|
+
class SoftEnumConverter(SoftConverter):
|
|
74
73
|
enum_class: Type[Enum]
|
|
75
74
|
|
|
76
75
|
def write_value(self, value: Union[Enum, str]) -> Enum:
|
|
@@ -90,26 +89,21 @@ class SimEnumConverter(SimConverter):
|
|
|
90
89
|
return cast(T, list(datatype.__members__.values())[0]) # type: ignore
|
|
91
90
|
|
|
92
91
|
|
|
93
|
-
class DisconnectedSimConverter(SimConverter):
|
|
94
|
-
def __getattribute__(self, __name: str) -> Any:
|
|
95
|
-
raise NotImplementedError("No PV has been set as connect() has not been called")
|
|
96
|
-
|
|
97
|
-
|
|
98
92
|
def make_converter(datatype):
|
|
99
93
|
is_array = get_dtype(datatype) is not None
|
|
100
94
|
is_sequence = get_origin(datatype) == abc.Sequence
|
|
101
95
|
is_enum = issubclass(datatype, Enum) if inspect.isclass(datatype) else False
|
|
102
96
|
|
|
103
97
|
if is_array or is_sequence:
|
|
104
|
-
return
|
|
98
|
+
return SoftArrayConverter()
|
|
105
99
|
if is_enum:
|
|
106
|
-
return
|
|
100
|
+
return SoftEnumConverter(datatype)
|
|
107
101
|
|
|
108
|
-
return
|
|
102
|
+
return SoftConverter()
|
|
109
103
|
|
|
110
104
|
|
|
111
|
-
class
|
|
112
|
-
"""An
|
|
105
|
+
class SoftSignalBackend(SignalBackend[T]):
|
|
106
|
+
"""An backend to a soft Signal, for test signals see ``MockSignalBackend``."""
|
|
113
107
|
|
|
114
108
|
_value: T
|
|
115
109
|
_initial_value: Optional[T]
|
|
@@ -122,25 +116,23 @@ class SimSignalBackend(SignalBackend[T]):
|
|
|
122
116
|
initial_value: Optional[T] = None,
|
|
123
117
|
) -> None:
|
|
124
118
|
self.datatype = datatype
|
|
125
|
-
self.converter: SimConverter = DisconnectedSimConverter()
|
|
126
119
|
self._initial_value = initial_value
|
|
127
|
-
self.
|
|
128
|
-
self.put_proceeds.set()
|
|
129
|
-
self.callback: Optional[ReadingValueCallback[T]] = None
|
|
130
|
-
|
|
131
|
-
def source(self, name: str) -> str:
|
|
132
|
-
return f"soft://{name}"
|
|
133
|
-
|
|
134
|
-
async def connect(self, timeout: float = DEFAULT_TIMEOUT) -> None:
|
|
135
|
-
self.converter = make_converter(self.datatype)
|
|
120
|
+
self.converter: SoftConverter = make_converter(datatype)
|
|
136
121
|
if self._initial_value is None:
|
|
137
122
|
self._initial_value = self.converter.make_initial_value(self.datatype)
|
|
138
123
|
else:
|
|
139
|
-
# convert potentially unconverted initial value passed to init method
|
|
140
124
|
self._initial_value = self.converter.write_value(self._initial_value)
|
|
125
|
+
|
|
126
|
+
self.callback: Optional[ReadingValueCallback[T]] = None
|
|
141
127
|
self._severity = 0
|
|
128
|
+
self.set_value(self._initial_value)
|
|
142
129
|
|
|
143
|
-
|
|
130
|
+
def source(self, name: str) -> str:
|
|
131
|
+
return f"soft://{name}"
|
|
132
|
+
|
|
133
|
+
async def connect(self, timeout: float = DEFAULT_TIMEOUT) -> None:
|
|
134
|
+
"""Connection isn't required for soft signals."""
|
|
135
|
+
pass
|
|
144
136
|
|
|
145
137
|
async def put(self, value: Optional[T], wait=True, timeout=None):
|
|
146
138
|
write_value = (
|
|
@@ -148,13 +140,11 @@ class SimSignalBackend(SignalBackend[T]):
|
|
|
148
140
|
if value is not None
|
|
149
141
|
else self._initial_value
|
|
150
142
|
)
|
|
151
|
-
self._set_value(write_value)
|
|
152
143
|
|
|
153
|
-
|
|
154
|
-
await asyncio.wait_for(self.put_proceeds.wait(), timeout)
|
|
144
|
+
self.set_value(write_value)
|
|
155
145
|
|
|
156
|
-
def
|
|
157
|
-
"""Method to bypass asynchronous logic
|
|
146
|
+
def set_value(self, value: T):
|
|
147
|
+
"""Method to bypass asynchronous logic."""
|
|
158
148
|
self._value = value
|
|
159
149
|
self._timestamp = time.monotonic()
|
|
160
150
|
reading: Reading = self.converter.reading(
|
|
@@ -174,7 +164,7 @@ class SimSignalBackend(SignalBackend[T]):
|
|
|
174
164
|
return self.converter.value(self._value)
|
|
175
165
|
|
|
176
166
|
async def get_setpoint(self) -> T:
|
|
177
|
-
"""For a
|
|
167
|
+
"""For a soft signal, the setpoint and readback values are the same."""
|
|
178
168
|
return await self.get_value()
|
|
179
169
|
|
|
180
170
|
def set_callback(self, callback: Optional[ReadingValueCallback[T]]) -> None:
|
|
@@ -28,7 +28,7 @@ from ophyd_async.core import (
|
|
|
28
28
|
)
|
|
29
29
|
from ophyd_async.core.utils import DEFAULT_TIMEOUT, NotConnected
|
|
30
30
|
|
|
31
|
-
from .common import
|
|
31
|
+
from .common import get_supported_values
|
|
32
32
|
|
|
33
33
|
dbr_to_dtype: Dict[Dbr, Dtype] = {
|
|
34
34
|
dbr.DBR_STRING: "string",
|
|
@@ -79,7 +79,7 @@ class CaArrayConverter(CaConverter):
|
|
|
79
79
|
|
|
80
80
|
@dataclass
|
|
81
81
|
class CaEnumConverter(CaConverter):
|
|
82
|
-
|
|
82
|
+
choices: dict[str, str]
|
|
83
83
|
|
|
84
84
|
def write_value(self, value: Union[Enum, str]):
|
|
85
85
|
if isinstance(value, Enum):
|
|
@@ -88,11 +88,15 @@ class CaEnumConverter(CaConverter):
|
|
|
88
88
|
return value
|
|
89
89
|
|
|
90
90
|
def value(self, value: AugmentedValue):
|
|
91
|
-
return self.
|
|
91
|
+
return self.choices[value]
|
|
92
92
|
|
|
93
93
|
def get_datakey(self, source: str, value: AugmentedValue) -> DataKey:
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
return {
|
|
95
|
+
"source": source,
|
|
96
|
+
"dtype": "string",
|
|
97
|
+
"shape": [],
|
|
98
|
+
"choices": list(self.choices),
|
|
99
|
+
}
|
|
96
100
|
|
|
97
101
|
|
|
98
102
|
class DisconnectedCaConverter(CaConverter):
|
|
@@ -138,8 +142,8 @@ def make_converter(
|
|
|
138
142
|
pv_choices = get_unique(
|
|
139
143
|
{k: tuple(v.enums) for k, v in values.items()}, "choices"
|
|
140
144
|
)
|
|
141
|
-
|
|
142
|
-
return CaEnumConverter(dbr.DBR_STRING, None,
|
|
145
|
+
supported_values = get_supported_values(pv, datatype, pv_choices)
|
|
146
|
+
return CaEnumConverter(dbr.DBR_STRING, None, supported_values)
|
|
143
147
|
else:
|
|
144
148
|
value = list(values.values())[0]
|
|
145
149
|
# Done the dbr check, so enough to check one of the values
|
|
@@ -20,7 +20,7 @@ from ophyd_async.core import (
|
|
|
20
20
|
)
|
|
21
21
|
from ophyd_async.core.utils import DEFAULT_TIMEOUT, NotConnected
|
|
22
22
|
|
|
23
|
-
from .common import
|
|
23
|
+
from .common import get_supported_values
|
|
24
24
|
|
|
25
25
|
# https://mdavidsaver.github.io/p4p/values.html
|
|
26
26
|
specifier_to_dtype: Dict[str, Dtype] = {
|
|
@@ -109,7 +109,8 @@ class PvaNDArrayConverter(PvaConverter):
|
|
|
109
109
|
|
|
110
110
|
@dataclass
|
|
111
111
|
class PvaEnumConverter(PvaConverter):
|
|
112
|
-
|
|
112
|
+
def __init__(self, choices: dict[str, str]):
|
|
113
|
+
self.choices = tuple(choices.values())
|
|
113
114
|
|
|
114
115
|
def write_value(self, value: Union[Enum, str]):
|
|
115
116
|
if isinstance(value, Enum):
|
|
@@ -118,11 +119,15 @@ class PvaEnumConverter(PvaConverter):
|
|
|
118
119
|
return value
|
|
119
120
|
|
|
120
121
|
def value(self, value):
|
|
121
|
-
return
|
|
122
|
+
return self.choices[value["value"]["index"]]
|
|
122
123
|
|
|
123
124
|
def get_datakey(self, source: str, value) -> DataKey:
|
|
124
|
-
|
|
125
|
-
|
|
125
|
+
return {
|
|
126
|
+
"source": source,
|
|
127
|
+
"dtype": "string",
|
|
128
|
+
"shape": [],
|
|
129
|
+
"choices": list(self.choices),
|
|
130
|
+
}
|
|
126
131
|
|
|
127
132
|
|
|
128
133
|
class PvaEnumBoolConverter(PvaConverter):
|
|
@@ -214,7 +219,7 @@ def make_converter(datatype: Optional[Type], values: Dict[str, Any]) -> PvaConve
|
|
|
214
219
|
pv_choices = get_unique(
|
|
215
220
|
{k: tuple(v["value"]["choices"]) for k, v in values.items()}, "choices"
|
|
216
221
|
)
|
|
217
|
-
return PvaEnumConverter(
|
|
222
|
+
return PvaEnumConverter(get_supported_values(pv, datatype, pv_choices))
|
|
218
223
|
elif "NTScalar" in typeid:
|
|
219
224
|
if (
|
|
220
225
|
datatype
|
|
@@ -244,7 +249,6 @@ class PvaSignalBackend(SignalBackend[T]):
|
|
|
244
249
|
self.converter: PvaConverter = DisconnectedPvaConverter()
|
|
245
250
|
self.subscription: Optional[Subscription] = None
|
|
246
251
|
|
|
247
|
-
@property
|
|
248
252
|
def source(self, name: str):
|
|
249
253
|
return f"pva://{self.read_pv}"
|
|
250
254
|
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Dict, Optional, Tuple, Type
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
def
|
|
5
|
+
def get_supported_values(
|
|
6
6
|
pv: str,
|
|
7
|
-
datatype: Optional[Type[
|
|
8
|
-
pv_choices: Tuple[
|
|
9
|
-
) ->
|
|
7
|
+
datatype: Optional[Type[str]],
|
|
8
|
+
pv_choices: Tuple[str, ...],
|
|
9
|
+
) -> Dict[str, str]:
|
|
10
10
|
if not datatype:
|
|
11
|
-
return
|
|
11
|
+
return {x: x or "_" for x in pv_choices}
|
|
12
12
|
|
|
13
|
-
if not issubclass(datatype, Enum):
|
|
14
|
-
raise TypeError(f"{pv} has type Enum not {datatype.__name__}")
|
|
15
13
|
if not issubclass(datatype, str):
|
|
16
|
-
raise TypeError(f"{pv}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
(
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
raise TypeError(f"{pv} is type Enum but doesn't inherit from String")
|
|
15
|
+
if issubclass(datatype, Enum):
|
|
16
|
+
choices = tuple(v.value for v in datatype)
|
|
17
|
+
if set(choices) != set(pv_choices):
|
|
18
|
+
raise TypeError(
|
|
19
|
+
(
|
|
20
|
+
f"{pv} has choices {pv_choices}, "
|
|
21
|
+
f"which do not match {datatype}, which has {choices}"
|
|
22
|
+
)
|
|
23
23
|
)
|
|
24
|
-
)
|
|
25
|
-
return
|
|
24
|
+
return {x: datatype(x) for x in pv_choices}
|
|
25
|
+
return {x: x for x in pv_choices}
|
|
@@ -7,8 +7,6 @@ from .utils import (
|
|
|
7
7
|
ImageMode,
|
|
8
8
|
NDAttributeDataType,
|
|
9
9
|
NDAttributesXML,
|
|
10
|
-
ad_r,
|
|
11
|
-
ad_rw,
|
|
12
10
|
)
|
|
13
11
|
from .vimba import VimbaDetector
|
|
14
12
|
|
|
@@ -19,8 +17,6 @@ __all__ = [
|
|
|
19
17
|
"SingleTriggerDet",
|
|
20
18
|
"FileWriteMode",
|
|
21
19
|
"ImageMode",
|
|
22
|
-
"ad_r",
|
|
23
|
-
"ad_rw",
|
|
24
20
|
"NDAttributeDataType",
|
|
25
21
|
"NDAttributesXML",
|
|
26
22
|
"PilatusDetector",
|
|
@@ -9,8 +9,8 @@ from ophyd_async.core import (
|
|
|
9
9
|
set_and_wait_for_value,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
from ...signal.signal import epics_signal_rw
|
|
13
|
-
from ..utils import ImageMode
|
|
12
|
+
from ...signal.signal import epics_signal_r, epics_signal_rw, epics_signal_rw_rbv
|
|
13
|
+
from ..utils import ImageMode
|
|
14
14
|
from ..writers.nd_plugin import NDArrayBase
|
|
15
15
|
|
|
16
16
|
|
|
@@ -43,14 +43,16 @@ DEFAULT_GOOD_STATES: FrozenSet[DetectorState] = frozenset(
|
|
|
43
43
|
class ADBase(NDArrayBase):
|
|
44
44
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
45
45
|
# Define some signals
|
|
46
|
-
self.acquire =
|
|
47
|
-
self.acquire_time =
|
|
48
|
-
self.num_images =
|
|
49
|
-
self.image_mode =
|
|
50
|
-
self.array_counter =
|
|
51
|
-
self.array_size_x =
|
|
52
|
-
self.array_size_y =
|
|
53
|
-
self.detector_state =
|
|
46
|
+
self.acquire = epics_signal_rw_rbv(bool, prefix + "Acquire")
|
|
47
|
+
self.acquire_time = epics_signal_rw_rbv(float, prefix + "AcquireTime")
|
|
48
|
+
self.num_images = epics_signal_rw_rbv(int, prefix + "NumImages")
|
|
49
|
+
self.image_mode = epics_signal_rw_rbv(ImageMode, prefix + "ImageMode")
|
|
50
|
+
self.array_counter = epics_signal_rw_rbv(int, prefix + "ArrayCounter")
|
|
51
|
+
self.array_size_x = epics_signal_r(int, prefix + "ArraySizeX_RBV")
|
|
52
|
+
self.array_size_y = epics_signal_r(int, prefix + "ArraySizeY_RBV")
|
|
53
|
+
self.detector_state = epics_signal_r(
|
|
54
|
+
DetectorState, prefix + "DetectorState_RBV"
|
|
55
|
+
)
|
|
54
56
|
# There is no _RBV for this one
|
|
55
57
|
self.wait_for_plugins = epics_signal_rw(bool, prefix + "WaitForPlugins")
|
|
56
58
|
super().__init__(prefix, name=name)
|
|
@@ -2,7 +2,7 @@ from enum import Enum
|
|
|
2
2
|
from typing import Callable, Dict, Literal, Optional, Tuple
|
|
3
3
|
|
|
4
4
|
from ophyd_async.epics.areadetector.drivers import ADBase
|
|
5
|
-
from ophyd_async.epics.
|
|
5
|
+
from ophyd_async.epics.signal.signal import epics_signal_r, epics_signal_rw_rbv
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class AravisTriggerMode(str, Enum):
|
|
@@ -138,10 +138,12 @@ class AravisDriver(ADBase):
|
|
|
138
138
|
"""
|
|
139
139
|
|
|
140
140
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
141
|
-
self.trigger_mode =
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
self.
|
|
141
|
+
self.trigger_mode = epics_signal_rw_rbv(
|
|
142
|
+
AravisTriggerMode, prefix + "TriggerMode"
|
|
143
|
+
)
|
|
144
|
+
self.trigger_source = epics_signal_rw_rbv(str, prefix + "TriggerSource")
|
|
145
|
+
self.model = epics_signal_r(str, prefix + "Model_RBV")
|
|
146
|
+
self.pixel_format = epics_signal_rw_rbv(str, prefix + "PixelFormat")
|
|
145
147
|
self.dead_time: Optional[float] = None
|
|
146
148
|
super().__init__(prefix, name=name)
|
|
147
149
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
|
|
4
|
+
|
|
4
5
|
from .ad_base import ADBase
|
|
5
6
|
|
|
6
7
|
|
|
@@ -18,7 +19,9 @@ class KinetixReadoutMode(str, Enum):
|
|
|
18
19
|
|
|
19
20
|
class KinetixDriver(ADBase):
|
|
20
21
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
21
|
-
# self.pixel_format =
|
|
22
|
-
self.trigger_mode =
|
|
23
|
-
|
|
22
|
+
# self.pixel_format = epics_signal_rw_rbv(PixelFormat, prefix + "PixelFormat")
|
|
23
|
+
self.trigger_mode = epics_signal_rw_rbv(
|
|
24
|
+
KinetixTriggerMode, prefix + "TriggerMode"
|
|
25
|
+
)
|
|
26
|
+
self.mode = epics_signal_rw_rbv(KinetixReadoutMode, prefix + "ReadoutPortIdx")
|
|
24
27
|
super().__init__(prefix, name)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
|
|
4
|
+
|
|
4
5
|
from .ad_base import ADBase
|
|
5
6
|
|
|
6
7
|
|
|
@@ -14,5 +15,7 @@ class PilatusTriggerMode(str, Enum):
|
|
|
14
15
|
|
|
15
16
|
class PilatusDriver(ADBase):
|
|
16
17
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
17
|
-
self.trigger_mode =
|
|
18
|
+
self.trigger_mode = epics_signal_rw_rbv(
|
|
19
|
+
PilatusTriggerMode, prefix + "TriggerMode"
|
|
20
|
+
)
|
|
18
21
|
super().__init__(prefix, name)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
|
|
4
|
+
|
|
4
5
|
from .ad_base import ADBase
|
|
5
6
|
|
|
6
7
|
|
|
@@ -47,12 +48,16 @@ class VimbaExposeOutMode(str, Enum):
|
|
|
47
48
|
|
|
48
49
|
class VimbaDriver(ADBase):
|
|
49
50
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
50
|
-
# self.pixel_format =
|
|
51
|
-
self.convert_format =
|
|
51
|
+
# self.pixel_format = epics_signal_rw_rbv(PixelFormat, prefix + "PixelFormat")
|
|
52
|
+
self.convert_format = epics_signal_rw_rbv(
|
|
52
53
|
VimbaConvertFormat, prefix + "ConvertPixelFormat"
|
|
53
54
|
) # Pixel format of data outputted to AD
|
|
54
|
-
self.trig_source =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
self.
|
|
55
|
+
self.trig_source = epics_signal_rw_rbv(
|
|
56
|
+
VimbaTriggerSource, prefix + "TriggerSource"
|
|
57
|
+
)
|
|
58
|
+
self.trigger_mode = epics_signal_rw_rbv(VimbaOnOff, prefix + "TriggerMode")
|
|
59
|
+
self.overlap = epics_signal_rw_rbv(VimbaOverlap, prefix + "TriggerOverlap")
|
|
60
|
+
self.expose_mode = epics_signal_rw_rbv(
|
|
61
|
+
VimbaExposeOutMode, prefix + "ExposureMode"
|
|
62
|
+
)
|
|
58
63
|
super().__init__(prefix, name)
|
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import Optional
|
|
3
3
|
from xml.etree import cElementTree as ET
|
|
4
4
|
|
|
5
|
-
from ophyd_async.core import DEFAULT_TIMEOUT,
|
|
6
|
-
|
|
7
|
-
from ..signal.signal import epics_signal_r, epics_signal_rw
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def ad_rw(datatype: Type[T], prefix: str) -> SignalRW[T]:
|
|
11
|
-
return epics_signal_rw(datatype, prefix + "_RBV", prefix)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def ad_r(datatype: Type[T], prefix: str) -> SignalR[T]:
|
|
15
|
-
return epics_signal_r(datatype, prefix + "_RBV")
|
|
5
|
+
from ophyd_async.core import DEFAULT_TIMEOUT, SignalRW, T, wait_for_value
|
|
16
6
|
|
|
17
7
|
|
|
18
8
|
class FileWriteMode(str, Enum):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
|
-
from ...signal.signal import epics_signal_rw
|
|
4
|
-
from ..utils import FileWriteMode
|
|
3
|
+
from ...signal.signal import epics_signal_r, epics_signal_rw, epics_signal_rw_rbv
|
|
4
|
+
from ..utils import FileWriteMode
|
|
5
5
|
from .nd_plugin import NDPluginBase
|
|
6
6
|
|
|
7
7
|
|
|
@@ -19,22 +19,24 @@ class Compression(str, Enum):
|
|
|
19
19
|
class NDFileHDF(NDPluginBase):
|
|
20
20
|
def __init__(self, prefix: str, name="") -> None:
|
|
21
21
|
# Define some signals
|
|
22
|
-
self.position_mode =
|
|
23
|
-
self.compression =
|
|
24
|
-
self.num_extra_dims =
|
|
25
|
-
self.file_path =
|
|
26
|
-
self.file_name =
|
|
27
|
-
self.file_path_exists =
|
|
28
|
-
self.file_template =
|
|
29
|
-
self.full_file_name =
|
|
30
|
-
self.file_write_mode =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
self.
|
|
34
|
-
self.
|
|
35
|
-
self.
|
|
22
|
+
self.position_mode = epics_signal_rw_rbv(bool, prefix + "PositionMode")
|
|
23
|
+
self.compression = epics_signal_rw_rbv(Compression, prefix + "Compression")
|
|
24
|
+
self.num_extra_dims = epics_signal_rw_rbv(int, prefix + "NumExtraDims")
|
|
25
|
+
self.file_path = epics_signal_rw_rbv(str, prefix + "FilePath")
|
|
26
|
+
self.file_name = epics_signal_rw_rbv(str, prefix + "FileName")
|
|
27
|
+
self.file_path_exists = epics_signal_r(bool, prefix + "FilePathExists_RBV")
|
|
28
|
+
self.file_template = epics_signal_rw_rbv(str, prefix + "FileTemplate")
|
|
29
|
+
self.full_file_name = epics_signal_r(str, prefix + "FullFileName_RBV")
|
|
30
|
+
self.file_write_mode = epics_signal_rw_rbv(
|
|
31
|
+
FileWriteMode, prefix + "FileWriteMode"
|
|
32
|
+
)
|
|
33
|
+
self.num_capture = epics_signal_rw_rbv(int, prefix + "NumCapture")
|
|
34
|
+
self.num_captured = epics_signal_r(int, prefix + "NumCaptured_RBV")
|
|
35
|
+
self.swmr_mode = epics_signal_rw_rbv(bool, prefix + "SWMRMode")
|
|
36
|
+
self.lazy_open = epics_signal_rw_rbv(bool, prefix + "LazyOpen")
|
|
37
|
+
self.capture = epics_signal_rw_rbv(bool, prefix + "Capture")
|
|
36
38
|
self.flush_now = epics_signal_rw(bool, prefix + "FlushNow")
|
|
37
|
-
self.array_size0 =
|
|
38
|
-
self.array_size1 =
|
|
39
|
-
self.xml_file_name =
|
|
39
|
+
self.array_size0 = epics_signal_r(int, prefix + "ArraySize0_RBV")
|
|
40
|
+
self.array_size1 = epics_signal_r(int, prefix + "ArraySize1_RBV")
|
|
41
|
+
self.xml_file_name = epics_signal_rw_rbv(str, prefix + "XMLFileName")
|
|
40
42
|
super().__init__(prefix, name)
|
|
@@ -2,8 +2,7 @@ from enum import Enum
|
|
|
2
2
|
|
|
3
3
|
from ophyd_async.core import Device
|
|
4
4
|
from ophyd_async.epics.signal import epics_signal_rw
|
|
5
|
-
|
|
6
|
-
from ..utils import ad_r, ad_rw
|
|
5
|
+
from ophyd_async.epics.signal.signal import epics_signal_r, epics_signal_rw_rbv
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
class Callback(str, Enum):
|
|
@@ -13,16 +12,16 @@ class Callback(str, Enum):
|
|
|
13
12
|
|
|
14
13
|
class NDArrayBase(Device):
|
|
15
14
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
16
|
-
self.unique_id =
|
|
15
|
+
self.unique_id = epics_signal_r(int, prefix + "UniqueId_RBV")
|
|
17
16
|
self.nd_attributes_file = epics_signal_rw(str, prefix + "NDAttributesFile")
|
|
18
|
-
super().__init__(name)
|
|
17
|
+
super().__init__(name=name)
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
class NDPluginBase(NDArrayBase):
|
|
22
21
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
23
|
-
self.nd_array_port =
|
|
24
|
-
self.enable_callback =
|
|
25
|
-
self.nd_array_address =
|
|
22
|
+
self.nd_array_port = epics_signal_rw_rbv(str, prefix + "NDArrayPort")
|
|
23
|
+
self.enable_callback = epics_signal_rw_rbv(Callback, prefix + "EnableCallbacks")
|
|
24
|
+
self.nd_array_address = epics_signal_rw_rbv(int, prefix + "NDArrayAddress")
|
|
26
25
|
super().__init__(prefix, name)
|
|
27
26
|
|
|
28
27
|
|
|
@@ -40,7 +40,11 @@ class Motor(StandardReadable, Movable, Stoppable):
|
|
|
40
40
|
# Readback should be named the same as its parent in read()
|
|
41
41
|
self.user_readback.set_name(name)
|
|
42
42
|
|
|
43
|
-
async def _move(
|
|
43
|
+
async def _move(
|
|
44
|
+
self, new_position: float, watchers: Optional[List[Callable]] = None
|
|
45
|
+
):
|
|
46
|
+
if watchers is None:
|
|
47
|
+
watchers = []
|
|
44
48
|
self._set_success = True
|
|
45
49
|
start = time.monotonic()
|
|
46
50
|
old_position, units, precision = await asyncio.gather(
|
ophyd_async/epics/pvi/pvi.py
CHANGED
|
@@ -17,7 +17,7 @@ from typing import (
|
|
|
17
17
|
get_type_hints,
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
-
from ophyd_async.core import Device, DeviceVector,
|
|
20
|
+
from ophyd_async.core import Device, DeviceVector, SoftSignalBackend
|
|
21
21
|
from ophyd_async.core.signal import Signal
|
|
22
22
|
from ophyd_async.core.utils import DEFAULT_TIMEOUT
|
|
23
23
|
from ophyd_async.epics._backend._p4p import PvaSignalBackend
|
|
@@ -156,7 +156,7 @@ def _parse_type(
|
|
|
156
156
|
return is_device_vector, is_signal, signal_dtype, device_cls
|
|
157
157
|
|
|
158
158
|
|
|
159
|
-
def
|
|
159
|
+
def _mock_common_blocks(device: Device, stripped_type: Optional[Type] = None):
|
|
160
160
|
device_t = stripped_type or type(device)
|
|
161
161
|
sub_devices = (
|
|
162
162
|
(field, field_type)
|
|
@@ -175,23 +175,23 @@ def _sim_common_blocks(device: Device, stripped_type: Optional[Type] = None):
|
|
|
175
175
|
|
|
176
176
|
if is_device_vector:
|
|
177
177
|
if is_signal:
|
|
178
|
-
sub_device_1 = device_cls(
|
|
179
|
-
sub_device_2 = device_cls(
|
|
178
|
+
sub_device_1 = device_cls(SoftSignalBackend(signal_dtype))
|
|
179
|
+
sub_device_2 = device_cls(SoftSignalBackend(signal_dtype))
|
|
180
180
|
sub_device = DeviceVector({1: sub_device_1, 2: sub_device_2})
|
|
181
181
|
else:
|
|
182
182
|
sub_device = DeviceVector({1: device_cls(), 2: device_cls()})
|
|
183
183
|
|
|
184
184
|
for sub_device_in_vector in sub_device.values():
|
|
185
|
-
|
|
185
|
+
_mock_common_blocks(sub_device_in_vector, stripped_type=device_cls)
|
|
186
186
|
|
|
187
187
|
for value in sub_device.values():
|
|
188
188
|
value.parent = sub_device
|
|
189
189
|
else:
|
|
190
190
|
if is_signal:
|
|
191
|
-
sub_device = device_cls(
|
|
191
|
+
sub_device = device_cls(SoftSignalBackend(signal_dtype))
|
|
192
192
|
else:
|
|
193
193
|
sub_device = getattr(device, device_name, device_cls())
|
|
194
|
-
|
|
194
|
+
_mock_common_blocks(sub_device, stripped_type=device_cls)
|
|
195
195
|
|
|
196
196
|
setattr(device, device_name, sub_device)
|
|
197
197
|
sub_device.parent = device
|
|
@@ -269,16 +269,16 @@ def _set_device_attributes(entry: PVIEntry):
|
|
|
269
269
|
|
|
270
270
|
|
|
271
271
|
async def fill_pvi_entries(
|
|
272
|
-
device: Device, root_pv: str, timeout=DEFAULT_TIMEOUT,
|
|
272
|
+
device: Device, root_pv: str, timeout=DEFAULT_TIMEOUT, mock=False
|
|
273
273
|
):
|
|
274
274
|
"""
|
|
275
275
|
Fills a ``device`` with signals from a the ``root_pvi:PVI`` table.
|
|
276
276
|
|
|
277
277
|
If the device names match with parent devices of ``device`` then types are used.
|
|
278
278
|
"""
|
|
279
|
-
if
|
|
280
|
-
# set up
|
|
281
|
-
|
|
279
|
+
if mock:
|
|
280
|
+
# set up mock signals for the common annotations
|
|
281
|
+
_mock_common_blocks(device)
|
|
282
282
|
else:
|
|
283
283
|
# check the pvi table for devices and fill the device with them
|
|
284
284
|
root_entry = PVIEntry(
|
ophyd_async/panda/_hdf_panda.py
CHANGED
|
@@ -42,7 +42,7 @@ class HDFPanda(CommonPandaBlocks, StandardDetector):
|
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
async def connect(
|
|
45
|
-
self,
|
|
45
|
+
self, mock: bool = False, timeout: float = DEFAULT_TIMEOUT
|
|
46
46
|
) -> None:
|
|
47
|
-
await fill_pvi_entries(self, self._prefix + "PVI", timeout=timeout,
|
|
48
|
-
await super().connect(
|
|
47
|
+
await fill_pvi_entries(self, self._prefix + "PVI", timeout=timeout, mock=mock)
|
|
48
|
+
await super().connect(mock=mock, timeout=timeout)
|