ophyd-async 0.10.0a3__py3-none-any.whl → 0.10.1__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 -0
- ophyd_async/core/_derived_signal.py +39 -11
- ophyd_async/core/_derived_signal_backend.py +7 -5
- ophyd_async/core/_detector.py +4 -4
- ophyd_async/core/_hdf_dataset.py +1 -1
- ophyd_async/core/_providers.py +1 -1
- ophyd_async/core/_signal.py +27 -6
- ophyd_async/core/_signal_backend.py +19 -9
- ophyd_async/core/_utils.py +31 -14
- ophyd_async/epics/adcore/_core_detector.py +2 -2
- ophyd_async/epics/adcore/_core_io.py +3 -3
- ophyd_async/epics/adcore/_core_logic.py +34 -10
- ophyd_async/epics/adcore/_hdf_writer.py +15 -0
- ophyd_async/epics/adcore/_utils.py +11 -2
- ophyd_async/epics/adpilatus/_pilatus.py +1 -1
- ophyd_async/epics/adpilatus/_pilatus_controller.py +4 -11
- ophyd_async/epics/core/_aioca.py +2 -2
- ophyd_async/epics/core/_util.py +21 -13
- ophyd_async/epics/eiger/_odin_io.py +12 -13
- ophyd_async/epics/motor.py +3 -2
- ophyd_async/fastcs/eiger/_eiger.py +11 -2
- ophyd_async/fastcs/eiger/_eiger_controller.py +7 -3
- ophyd_async/fastcs/eiger/_eiger_io.py +1 -0
- ophyd_async/sim/_motor.py +2 -2
- ophyd_async/tango/core/_base_device.py +2 -1
- ophyd_async/tango/core/_converters.py +2 -6
- ophyd_async/tango/core/_signal.py +8 -8
- ophyd_async/tango/core/_tango_transport.py +12 -12
- ophyd_async/tango/demo/_tango/_servers.py +0 -1
- ophyd_async/tango/testing/_one_of_everything.py +2 -2
- {ophyd_async-0.10.0a3.dist-info → ophyd_async-0.10.1.dist-info}/METADATA +2 -2
- {ophyd_async-0.10.0a3.dist-info → ophyd_async-0.10.1.dist-info}/RECORD +36 -36
- {ophyd_async-0.10.0a3.dist-info → ophyd_async-0.10.1.dist-info}/WHEEL +1 -1
- {ophyd_async-0.10.0a3.dist-info → ophyd_async-0.10.1.dist-info}/licenses/LICENSE +0 -0
- {ophyd_async-0.10.0a3.dist-info → ophyd_async-0.10.1.dist-info}/top_level.txt +0 -0
ophyd_async/_version.py
CHANGED
|
@@ -17,5 +17,5 @@ __version__: str
|
|
|
17
17
|
__version_tuple__: VERSION_TUPLE
|
|
18
18
|
version_tuple: VERSION_TUPLE
|
|
19
19
|
|
|
20
|
-
__version__ = version = '0.10.
|
|
21
|
-
__version_tuple__ = version_tuple = (0, 10,
|
|
20
|
+
__version__ = version = '0.10.1'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 10, 1)
|
ophyd_async/core/__init__.py
CHANGED
|
@@ -75,11 +75,13 @@ from ._utils import (
|
|
|
75
75
|
DEFAULT_TIMEOUT,
|
|
76
76
|
CalculatableTimeout,
|
|
77
77
|
Callback,
|
|
78
|
+
EnumTypes,
|
|
78
79
|
LazyMock,
|
|
79
80
|
NotConnected,
|
|
80
81
|
Reference,
|
|
81
82
|
StrictEnum,
|
|
82
83
|
SubsetEnum,
|
|
84
|
+
SupersetEnum,
|
|
83
85
|
WatcherUpdate,
|
|
84
86
|
gather_dict,
|
|
85
87
|
get_dtype,
|
|
@@ -122,6 +124,8 @@ __all__ = [
|
|
|
122
124
|
"Array1D",
|
|
123
125
|
"StrictEnum",
|
|
124
126
|
"SubsetEnum",
|
|
127
|
+
"SupersetEnum",
|
|
128
|
+
"EnumTypes",
|
|
125
129
|
"Table",
|
|
126
130
|
"SignalMetadata",
|
|
127
131
|
# Soft signal
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from collections.abc import Awaitable, Callable
|
|
2
|
-
from typing import Any, Generic, get_type_hints, is_typeddict
|
|
2
|
+
from typing import Any, Generic, get_args, get_origin, get_type_hints, is_typeddict
|
|
3
|
+
|
|
4
|
+
from bluesky.protocols import Locatable
|
|
3
5
|
|
|
4
6
|
from ._derived_signal_backend import (
|
|
5
7
|
DerivedSignalBackend,
|
|
@@ -8,7 +10,7 @@ from ._derived_signal_backend import (
|
|
|
8
10
|
TransformT,
|
|
9
11
|
)
|
|
10
12
|
from ._device import Device
|
|
11
|
-
from ._signal import SignalR, SignalRW, SignalT, SignalW
|
|
13
|
+
from ._signal import Signal, SignalR, SignalRW, SignalT, SignalW
|
|
12
14
|
from ._signal_backend import SignalDatatypeT
|
|
13
15
|
|
|
14
16
|
|
|
@@ -35,15 +37,27 @@ class DerivedSignalFactory(Generic[TransformT]):
|
|
|
35
37
|
self._set_derived = set_derived
|
|
36
38
|
# Check the raw and transform devices match the input arguments of the Transform
|
|
37
39
|
if transform_cls is not Transform:
|
|
38
|
-
expected
|
|
39
|
-
|
|
40
|
-
for
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
# Populate expected parameters and types
|
|
41
|
+
expected = {
|
|
42
|
+
**{k: f.annotation for k, f in transform_cls.model_fields.items()},
|
|
43
|
+
**{
|
|
44
|
+
k: v
|
|
45
|
+
for k, v in get_type_hints(transform_cls.raw_to_derived).items()
|
|
46
|
+
if k not in {"self", "return"}
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Populate received parameters and types
|
|
51
|
+
# Use Signal datatype, or Locatable datatype, or set type as None
|
|
52
|
+
received = {
|
|
53
|
+
k: v.datatype if isinstance(v, Signal) else get_locatable_type(v)
|
|
54
|
+
for k, v in raw_and_transform_devices.items()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if expected != received:
|
|
44
58
|
msg = (
|
|
45
|
-
f"Expected devices to be passed as keyword arguments
|
|
46
|
-
f"got {
|
|
59
|
+
f"Expected devices to be passed as keyword arguments "
|
|
60
|
+
f"{expected}, got {received}"
|
|
47
61
|
)
|
|
48
62
|
raise TypeError(msg)
|
|
49
63
|
self._set_derived_takes_dict = (
|
|
@@ -234,7 +248,7 @@ def derived_signal_rw(
|
|
|
234
248
|
if raw_to_derived_datatype != set_derived_datatype:
|
|
235
249
|
msg = (
|
|
236
250
|
f"{raw_to_derived} has datatype {raw_to_derived_datatype} "
|
|
237
|
-
f"!= {set_derived_datatype}
|
|
251
|
+
f"!= {set_derived_datatype} datatype {set_derived_datatype}"
|
|
238
252
|
)
|
|
239
253
|
raise TypeError(msg)
|
|
240
254
|
|
|
@@ -269,3 +283,17 @@ def derived_signal_w(
|
|
|
269
283
|
units=derived_units,
|
|
270
284
|
precision=derived_precision,
|
|
271
285
|
)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def get_locatable_type(obj: object) -> type | None:
|
|
289
|
+
"""Extract datatype from Locatable parent class.
|
|
290
|
+
|
|
291
|
+
:param obj: Object with possible Locatable inheritance
|
|
292
|
+
:return: Type hint associated with Locatable, or None if not found.
|
|
293
|
+
"""
|
|
294
|
+
for base in getattr(obj.__class__, "__orig_bases__", []):
|
|
295
|
+
if get_origin(base) is Locatable:
|
|
296
|
+
args = get_args(base)
|
|
297
|
+
if args:
|
|
298
|
+
return args[0]
|
|
299
|
+
return None
|
|
@@ -61,7 +61,7 @@ class Transform(BaseModel, Generic[RawT, DerivedT]):
|
|
|
61
61
|
TransformT = TypeVar("TransformT", bound=Transform)
|
|
62
62
|
|
|
63
63
|
|
|
64
|
-
def
|
|
64
|
+
def validate_by_type(raw_devices: Mapping[str, Any], type_: type[T]) -> dict[str, T]:
|
|
65
65
|
filtered_devices: dict[str, T] = {}
|
|
66
66
|
for name, device in raw_devices.items():
|
|
67
67
|
if not isinstance(device, type_):
|
|
@@ -91,21 +91,23 @@ class SignalTransformer(Generic[TransformT]):
|
|
|
91
91
|
|
|
92
92
|
@cached_property
|
|
93
93
|
def raw_locatables(self) -> dict[str, AsyncLocatable]:
|
|
94
|
-
return
|
|
94
|
+
return validate_by_type(self._raw_devices, AsyncLocatable)
|
|
95
95
|
|
|
96
96
|
@cached_property
|
|
97
97
|
def transform_readables(self) -> dict[str, AsyncReadable]:
|
|
98
|
-
return
|
|
98
|
+
return validate_by_type(self._transform_devices, AsyncReadable)
|
|
99
99
|
|
|
100
100
|
@cached_property
|
|
101
101
|
def raw_and_transform_readables(self) -> dict[str, AsyncReadable]:
|
|
102
|
-
return
|
|
102
|
+
return validate_by_type(
|
|
103
103
|
self._raw_devices | self._transform_devices, AsyncReadable
|
|
104
104
|
)
|
|
105
105
|
|
|
106
106
|
@cached_property
|
|
107
107
|
def raw_and_transform_subscribables(self) -> dict[str, Subscribable]:
|
|
108
|
-
return
|
|
108
|
+
return validate_by_type(
|
|
109
|
+
self._raw_devices | self._transform_devices, Subscribable
|
|
110
|
+
)
|
|
109
111
|
|
|
110
112
|
def _complete_cached_reading(self) -> dict[str, Reading] | None:
|
|
111
113
|
if self._cached_readings and len(self._cached_readings) == len(
|
ophyd_async/core/_detector.py
CHANGED
|
@@ -328,10 +328,10 @@ class StandardDetector(
|
|
|
328
328
|
if isinstance(value.number_of_events, list)
|
|
329
329
|
else [value.number_of_events]
|
|
330
330
|
)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
331
|
+
|
|
332
|
+
await self._controller.prepare(value)
|
|
333
|
+
self._describe = await self._writer.open(self.name, value.exposures_per_event)
|
|
334
|
+
|
|
335
335
|
self._initial_frame = await self._writer.get_indices_written()
|
|
336
336
|
if value.trigger != DetectorTrigger.INTERNAL:
|
|
337
337
|
await self._controller.arm()
|
ophyd_async/core/_hdf_dataset.py
CHANGED
|
@@ -23,7 +23,7 @@ class HDFDatasetDescription(BaseModel):
|
|
|
23
23
|
"""The dataset name within the HDF file,
|
|
24
24
|
e.g. /entry/data/data or /entry/instrument/NDAttributes/sum"""
|
|
25
25
|
|
|
26
|
-
shape: tuple[int, ...] = Field(default_factory=tuple)
|
|
26
|
+
shape: tuple[int | None, ...] = Field(default_factory=tuple)
|
|
27
27
|
"""The shape of a single event's data in the HDF file,
|
|
28
28
|
e.g. (1, 768, 1024) for arrays or () for scalars"""
|
|
29
29
|
|
ophyd_async/core/_providers.py
CHANGED
ophyd_async/core/_signal.py
CHANGED
|
@@ -100,6 +100,11 @@ class Signal(Device, Generic[SignalDatatypeT]):
|
|
|
100
100
|
"""
|
|
101
101
|
return self._connector.backend.source(self.name, read=True)
|
|
102
102
|
|
|
103
|
+
@property
|
|
104
|
+
def datatype(self) -> type[SignalDatatypeT] | None:
|
|
105
|
+
"""Returns the datatype of the signal."""
|
|
106
|
+
return self._connector.backend.datatype
|
|
107
|
+
|
|
103
108
|
|
|
104
109
|
SignalT = TypeVar("SignalT", bound=Signal)
|
|
105
110
|
|
|
@@ -158,7 +163,12 @@ class _SignalCache(Generic[SignalDatatypeT]):
|
|
|
158
163
|
self._notify(function, want_value)
|
|
159
164
|
|
|
160
165
|
def unsubscribe(self, function: Callback) -> bool:
|
|
161
|
-
self._listeners.pop(function)
|
|
166
|
+
_listener = self._listeners.pop(function, None)
|
|
167
|
+
if not _listener:
|
|
168
|
+
self._signal.log.warning(
|
|
169
|
+
f"Unsubscribe failed: subscriber {function} was not found "
|
|
170
|
+
f" in listeners list: {list(self._listeners)}"
|
|
171
|
+
)
|
|
162
172
|
return self._staged or bool(self._listeners)
|
|
163
173
|
|
|
164
174
|
def set_staged(self, staged: bool) -> bool:
|
|
@@ -431,8 +441,8 @@ async def observe_signals_value(
|
|
|
431
441
|
Call subscribe_value on all the signals at the start, and clear_sub on
|
|
432
442
|
it at the end.
|
|
433
443
|
:param timeout:
|
|
434
|
-
If given, how long to wait for
|
|
435
|
-
update is not produced in this time then raise asyncio.TimeoutError.
|
|
444
|
+
If given, how long to wait for ANY updated value from shared queue in seconds.
|
|
445
|
+
If an update is not produced in this time then raise asyncio.TimeoutError.
|
|
436
446
|
:param done_status:
|
|
437
447
|
If this status is complete, stop observing and make the iterator return.
|
|
438
448
|
If it raises an exception then this exception will be raised by the
|
|
@@ -454,8 +464,10 @@ async def observe_signals_value(
|
|
|
454
464
|
q: asyncio.Queue[tuple[SignalR[SignalDatatypeT], SignalDatatypeT] | Status] = (
|
|
455
465
|
asyncio.Queue()
|
|
456
466
|
)
|
|
457
|
-
|
|
467
|
+
# dict to store signal subscription to remove it later
|
|
458
468
|
cbs: dict[SignalR, Callback] = {}
|
|
469
|
+
|
|
470
|
+
# subscribe signal to update queue and fill cbs dict
|
|
459
471
|
for signal in signals:
|
|
460
472
|
|
|
461
473
|
def queue_value(value: SignalDatatypeT, signal=signal):
|
|
@@ -468,6 +480,7 @@ async def observe_signals_value(
|
|
|
468
480
|
done_status.add_callback(q.put_nowait)
|
|
469
481
|
overall_deadline = time.monotonic() + done_timeout if done_timeout else None
|
|
470
482
|
try:
|
|
483
|
+
last_item = ()
|
|
471
484
|
while True:
|
|
472
485
|
if overall_deadline and time.monotonic() >= overall_deadline:
|
|
473
486
|
raise asyncio.TimeoutError(
|
|
@@ -476,14 +489,22 @@ async def observe_signals_value(
|
|
|
476
489
|
f"timeout {done_timeout}s"
|
|
477
490
|
)
|
|
478
491
|
iteration_timeout = _get_iteration_timeout(timeout, overall_deadline)
|
|
479
|
-
|
|
492
|
+
try:
|
|
493
|
+
item = await asyncio.wait_for(q.get(), iteration_timeout)
|
|
494
|
+
except asyncio.TimeoutError as exc:
|
|
495
|
+
raise asyncio.TimeoutError(
|
|
496
|
+
f"Timeout Error while waiting {iteration_timeout}s to update "
|
|
497
|
+
f"{[signal.source for signal in signals]}. "
|
|
498
|
+
f"Last observed signal and value were {last_item}"
|
|
499
|
+
) from exc
|
|
480
500
|
if done_status and item is done_status:
|
|
481
501
|
if exc := done_status.exception():
|
|
482
502
|
raise exc
|
|
483
503
|
else:
|
|
484
504
|
break
|
|
485
505
|
else:
|
|
486
|
-
|
|
506
|
+
last_item = cast(tuple[SignalR[SignalDatatypeT], SignalDatatypeT], item)
|
|
507
|
+
yield last_item
|
|
487
508
|
finally:
|
|
488
509
|
for signal, cb in cbs.items():
|
|
489
510
|
signal.clear_sub(cb)
|
|
@@ -6,8 +6,16 @@ import numpy as np
|
|
|
6
6
|
from bluesky.protocols import Reading
|
|
7
7
|
from event_model import DataKey, Dtype, Limits
|
|
8
8
|
|
|
9
|
+
from ophyd_async.core._utils import (
|
|
10
|
+
Callback,
|
|
11
|
+
EnumTypes,
|
|
12
|
+
StrictEnum,
|
|
13
|
+
SubsetEnum,
|
|
14
|
+
SupersetEnum,
|
|
15
|
+
get_enum_cls,
|
|
16
|
+
)
|
|
17
|
+
|
|
9
18
|
from ._table import Table
|
|
10
|
-
from ._utils import Callback, StrictEnum, get_enum_cls
|
|
11
19
|
|
|
12
20
|
DTypeScalar_co = TypeVar("DTypeScalar_co", covariant=True, bound=np.generic)
|
|
13
21
|
"""A numpy dtype like [](#numpy.float64)."""
|
|
@@ -24,7 +32,7 @@ E.g. `Array1D[np.float64]` is a 1D numpy array of 64-bit floats."""
|
|
|
24
32
|
Primitive = bool | int | float | str
|
|
25
33
|
SignalDatatype = (
|
|
26
34
|
Primitive
|
|
27
|
-
|
|
|
35
|
+
| EnumTypes
|
|
28
36
|
| Array1D[np.bool_]
|
|
29
37
|
| Array1D[np.int8]
|
|
30
38
|
| Array1D[np.uint8]
|
|
@@ -39,16 +47,18 @@ SignalDatatype = (
|
|
|
39
47
|
| np.ndarray
|
|
40
48
|
| Sequence[str]
|
|
41
49
|
| Sequence[StrictEnum]
|
|
50
|
+
| Sequence[SubsetEnum]
|
|
51
|
+
| Sequence[SupersetEnum]
|
|
42
52
|
| Table
|
|
43
53
|
)
|
|
44
54
|
"""The supported [](#Signal) datatypes:
|
|
45
55
|
|
|
46
56
|
- A python primitive [](#bool), [](#int), [](#float), [](#str)
|
|
47
|
-
-
|
|
57
|
+
- An [](#EnumTypes) subclass
|
|
48
58
|
- A fixed datatype [](#Array1D) of numpy bool, signed and unsigned integers or float
|
|
49
59
|
- A [](#numpy.ndarray) which can change dimensions and datatype at runtime
|
|
50
60
|
- A sequence of [](#str)
|
|
51
|
-
- A sequence of [](#
|
|
61
|
+
- A sequence of [](#EnumTypes) subclasses
|
|
52
62
|
- A [](#Table) subclass
|
|
53
63
|
"""
|
|
54
64
|
# TODO: These typevars will not be needed when we drop python 3.11
|
|
@@ -58,7 +68,7 @@ PrimitiveT = TypeVar("PrimitiveT", bound=Primitive)
|
|
|
58
68
|
SignalDatatypeT = TypeVar("SignalDatatypeT", bound=SignalDatatype)
|
|
59
69
|
"""A typevar for a [](#SignalDatatype)."""
|
|
60
70
|
SignalDatatypeV = TypeVar("SignalDatatypeV", bound=SignalDatatype)
|
|
61
|
-
EnumT = TypeVar("EnumT", bound=
|
|
71
|
+
EnumT = TypeVar("EnumT", bound=EnumTypes)
|
|
62
72
|
TableT = TypeVar("TableT", bound=Table)
|
|
63
73
|
|
|
64
74
|
|
|
@@ -136,7 +146,7 @@ def _datakey_dtype(datatype: type[SignalDatatype]) -> Dtype:
|
|
|
136
146
|
or issubclass(datatype, Table)
|
|
137
147
|
):
|
|
138
148
|
return "array"
|
|
139
|
-
elif issubclass(datatype,
|
|
149
|
+
elif issubclass(datatype, EnumTypes):
|
|
140
150
|
return "string"
|
|
141
151
|
elif issubclass(datatype, Primitive):
|
|
142
152
|
return _primitive_dtype[datatype]
|
|
@@ -153,7 +163,7 @@ def _datakey_dtype_numpy(
|
|
|
153
163
|
elif (
|
|
154
164
|
get_origin(datatype) is Sequence
|
|
155
165
|
or datatype is str
|
|
156
|
-
or issubclass(datatype,
|
|
166
|
+
or issubclass(datatype, EnumTypes)
|
|
157
167
|
):
|
|
158
168
|
# TODO: use np.dtypes.StringDType when we can use in structured arrays
|
|
159
169
|
# https://github.com/numpy/numpy/issues/25693
|
|
@@ -166,8 +176,8 @@ def _datakey_dtype_numpy(
|
|
|
166
176
|
raise TypeError(f"Can't make dtype_numpy for {datatype}")
|
|
167
177
|
|
|
168
178
|
|
|
169
|
-
def _datakey_shape(value: SignalDatatype) -> list[int]:
|
|
170
|
-
if type(value) in _primitive_dtype or isinstance(value,
|
|
179
|
+
def _datakey_shape(value: SignalDatatype) -> list[int | None]:
|
|
180
|
+
if type(value) in _primitive_dtype or isinstance(value, EnumTypes):
|
|
171
181
|
return []
|
|
172
182
|
elif isinstance(value, np.ndarray):
|
|
173
183
|
return list(value.shape)
|
ophyd_async/core/_utils.py
CHANGED
|
@@ -5,7 +5,15 @@ import logging
|
|
|
5
5
|
from collections.abc import Awaitable, Callable, Iterable, Mapping, Sequence
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from enum import Enum, EnumMeta
|
|
8
|
-
from typing import
|
|
8
|
+
from typing import (
|
|
9
|
+
Any,
|
|
10
|
+
Generic,
|
|
11
|
+
Literal,
|
|
12
|
+
ParamSpec,
|
|
13
|
+
TypeVar,
|
|
14
|
+
get_args,
|
|
15
|
+
get_origin,
|
|
16
|
+
)
|
|
9
17
|
from unittest.mock import Mock
|
|
10
18
|
|
|
11
19
|
import numpy as np
|
|
@@ -19,20 +27,16 @@ DEFAULT_TIMEOUT = 10.0
|
|
|
19
27
|
logger = logging.getLogger("ophyd_async")
|
|
20
28
|
|
|
21
29
|
|
|
22
|
-
class
|
|
23
|
-
def __new__(
|
|
24
|
-
ret = super().__new__(
|
|
30
|
+
class UppercaseNameEnumMeta(EnumMeta):
|
|
31
|
+
def __new__(cls, *args, **kwargs):
|
|
32
|
+
ret = super().__new__(cls, *args, **kwargs)
|
|
25
33
|
lowercase_names = [x.name for x in ret if not x.name.isupper()] # type: ignore
|
|
26
34
|
if lowercase_names:
|
|
27
35
|
raise TypeError(f"Names {lowercase_names} should be uppercase")
|
|
28
36
|
return ret
|
|
29
37
|
|
|
30
38
|
|
|
31
|
-
class
|
|
32
|
-
"""All members should exist in the Backend, and there will be no extras."""
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class SubsetEnumMeta(StrictEnumMeta):
|
|
39
|
+
class AnyStringUppercaseNameEnumMeta(UppercaseNameEnumMeta):
|
|
36
40
|
def __call__(self, value, *args, **kwargs): # type: ignore
|
|
37
41
|
"""Return given value if it is a string and not a member of the enum.
|
|
38
42
|
|
|
@@ -54,10 +58,21 @@ class SubsetEnumMeta(StrictEnumMeta):
|
|
|
54
58
|
return super().__call__(value, *args, **kwargs)
|
|
55
59
|
|
|
56
60
|
|
|
57
|
-
class
|
|
61
|
+
class StrictEnum(str, Enum, metaclass=UppercaseNameEnumMeta):
|
|
62
|
+
"""All members should exist in the Backend, and there will be no extras."""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class SubsetEnum(str, Enum, metaclass=AnyStringUppercaseNameEnumMeta):
|
|
58
66
|
"""All members should exist in the Backend, but there may be extras."""
|
|
59
67
|
|
|
60
68
|
|
|
69
|
+
class SupersetEnum(str, Enum, metaclass=UppercaseNameEnumMeta):
|
|
70
|
+
"""Some members should exist in the Backend, and there should be no extras."""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
EnumTypes = StrictEnum | SubsetEnum | SupersetEnum
|
|
74
|
+
|
|
75
|
+
|
|
61
76
|
CALCULATE_TIMEOUT = "CALCULATE_TIMEOUT"
|
|
62
77
|
"""Sentinel used to implement ``myfunc(timeout=CalculateTimeout)``
|
|
63
78
|
|
|
@@ -207,10 +222,11 @@ def get_dtype(datatype: type) -> np.dtype:
|
|
|
207
222
|
return np.dtype(get_args(get_args(datatype)[1])[0])
|
|
208
223
|
|
|
209
224
|
|
|
210
|
-
def get_enum_cls(datatype: type | None) -> type[
|
|
225
|
+
def get_enum_cls(datatype: type | None) -> type[EnumTypes] | None:
|
|
211
226
|
"""Get the enum class from a datatype.
|
|
212
227
|
|
|
213
|
-
:raises TypeError: if type is not a [](#StrictEnum) or [](#SubsetEnum)
|
|
228
|
+
:raises TypeError: if type is not a [](#StrictEnum) or [](#SubsetEnum)
|
|
229
|
+
or [](#SupersetEnum) subclass
|
|
214
230
|
```python
|
|
215
231
|
>>> from ophyd_async.core import StrictEnum
|
|
216
232
|
>>> from collections.abc import Sequence
|
|
@@ -227,10 +243,11 @@ def get_enum_cls(datatype: type | None) -> type[StrictEnum] | None:
|
|
|
227
243
|
if get_origin(datatype) is Sequence:
|
|
228
244
|
datatype = get_args(datatype)[0]
|
|
229
245
|
if datatype and issubclass(datatype, Enum):
|
|
230
|
-
if not issubclass(datatype,
|
|
246
|
+
if not issubclass(datatype, EnumTypes):
|
|
231
247
|
raise TypeError(
|
|
232
248
|
f"{datatype} should inherit from ophyd_async.core.SubsetEnum "
|
|
233
|
-
"or ophyd_async.core.StrictEnum"
|
|
249
|
+
"or ophyd_async.core.StrictEnum "
|
|
250
|
+
"or ophyd_async.core.SupersetEnum."
|
|
234
251
|
)
|
|
235
252
|
return datatype
|
|
236
253
|
return None
|
|
@@ -22,8 +22,8 @@ class AreaDetector(StandardDetector[ADBaseControllerT, ADWriter]):
|
|
|
22
22
|
self.fileio = writer.fileio
|
|
23
23
|
|
|
24
24
|
if plugins is not None:
|
|
25
|
-
for
|
|
26
|
-
setattr(self,
|
|
25
|
+
for plugin_name, plugin in plugins.items():
|
|
26
|
+
setattr(self, plugin_name, plugin)
|
|
27
27
|
|
|
28
28
|
super().__init__(
|
|
29
29
|
controller,
|
|
@@ -70,14 +70,14 @@ class NDPluginStatsIO(NDPluginBaseIO):
|
|
|
70
70
|
# Basic statistics
|
|
71
71
|
compute_statistics: A[SignalRW[bool], PvSuffix.rbv("ComputeStatistics")]
|
|
72
72
|
bgd_width: A[SignalRW[int], PvSuffix.rbv("BgdWidth")]
|
|
73
|
-
|
|
73
|
+
total: A[SignalR[float], PvSuffix.rbv("Total")]
|
|
74
74
|
# Centroid statistics
|
|
75
75
|
compute_centroid: A[SignalRW[bool], PvSuffix.rbv("ComputeCentroid")]
|
|
76
76
|
centroid_threshold: A[SignalRW[float], PvSuffix.rbv("CentroidThreshold")]
|
|
77
77
|
# X and Y Profiles
|
|
78
78
|
compute_profiles: A[SignalRW[bool], PvSuffix.rbv("ComputeProfiles")]
|
|
79
|
-
profile_size_x: A[
|
|
80
|
-
profile_size_y: A[
|
|
79
|
+
profile_size_x: A[SignalR[int], PvSuffix.rbv("ProfileSizeX")]
|
|
80
|
+
profile_size_y: A[SignalR[int], PvSuffix.rbv("ProfileSizeY")]
|
|
81
81
|
cursor_x: A[SignalRW[int], PvSuffix.rbv("CursorX")]
|
|
82
82
|
cursor_y: A[SignalRW[int], PvSuffix.rbv("CursorY")]
|
|
83
83
|
# Array Histogram
|
|
@@ -7,6 +7,7 @@ from ophyd_async.core import (
|
|
|
7
7
|
DetectorController,
|
|
8
8
|
DetectorTrigger,
|
|
9
9
|
TriggerInfo,
|
|
10
|
+
observe_value,
|
|
10
11
|
set_and_wait_for_value,
|
|
11
12
|
)
|
|
12
13
|
|
|
@@ -89,34 +90,57 @@ class ADBaseController(DetectorController, Generic[ADBaseIOT]):
|
|
|
89
90
|
self.driver.acquire_period.set(full_frame_time, timeout=timeout),
|
|
90
91
|
)
|
|
91
92
|
|
|
92
|
-
async def start_acquiring_driver_and_ensure_status(
|
|
93
|
+
async def start_acquiring_driver_and_ensure_status(
|
|
94
|
+
self,
|
|
95
|
+
start_timeout: float = DEFAULT_TIMEOUT,
|
|
96
|
+
state_timeout: float = DEFAULT_TIMEOUT,
|
|
97
|
+
) -> AsyncStatus:
|
|
93
98
|
"""Start acquiring driver, raising ValueError if the detector is in a bad state.
|
|
94
99
|
|
|
95
100
|
This sets driver.acquire to True, and waits for it to be True up to a timeout.
|
|
96
101
|
Then, it checks that the DetectorState PV is in DEFAULT_GOOD_STATES,
|
|
97
102
|
and otherwise raises a ValueError.
|
|
98
103
|
|
|
104
|
+
|
|
105
|
+
:param start_timeout:
|
|
106
|
+
Timeout used for waiting for the driver to start
|
|
107
|
+
acquiring.
|
|
108
|
+
:param state_timeout:
|
|
109
|
+
Timeout used for waiting for the detector to be in a good
|
|
110
|
+
state after it stops acquiring.
|
|
99
111
|
:returns AsyncStatus:
|
|
100
112
|
An AsyncStatus that can be awaited to set driver.acquire to True and perform
|
|
101
113
|
subsequent raising (if applicable) due to detector state.
|
|
114
|
+
|
|
102
115
|
"""
|
|
103
116
|
status = await set_and_wait_for_value(
|
|
104
117
|
self.driver.acquire,
|
|
105
118
|
True,
|
|
106
|
-
timeout=
|
|
119
|
+
timeout=start_timeout,
|
|
107
120
|
wait_for_set_completion=False,
|
|
108
121
|
)
|
|
109
122
|
|
|
110
123
|
async def complete_acquisition() -> None:
|
|
111
|
-
# NOTE: possible race condition here between the callback from
|
|
112
|
-
# set_and_wait_for_value and the detector state updating.
|
|
113
124
|
await status
|
|
114
|
-
state =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
125
|
+
state = None
|
|
126
|
+
try:
|
|
127
|
+
async for state in observe_value(
|
|
128
|
+
self.driver.detector_state, done_timeout=state_timeout
|
|
129
|
+
):
|
|
130
|
+
if state in self.good_states:
|
|
131
|
+
return
|
|
132
|
+
except asyncio.TimeoutError as exc:
|
|
133
|
+
if state is not None:
|
|
134
|
+
raise ValueError(
|
|
135
|
+
f"Final detector state {state.value} not in valid end "
|
|
136
|
+
f"states: {self.good_states}"
|
|
137
|
+
) from exc
|
|
138
|
+
else:
|
|
139
|
+
# No updates from the detector, something else is wrong
|
|
140
|
+
raise asyncio.TimeoutError(
|
|
141
|
+
"Could not monitor detector state: "
|
|
142
|
+
+ self.driver.detector_state.source
|
|
143
|
+
) from exc
|
|
120
144
|
|
|
121
145
|
return AsyncStatus(complete_acquisition())
|
|
122
146
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from collections.abc import AsyncIterator
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
from typing import TypeGuard
|
|
4
5
|
from xml.etree import ElementTree as ET
|
|
5
6
|
|
|
6
7
|
from bluesky.protocols import StreamAsset
|
|
@@ -22,6 +23,10 @@ from ._utils import (
|
|
|
22
23
|
)
|
|
23
24
|
|
|
24
25
|
|
|
26
|
+
def _is_fully_described(shape: tuple[int | None, ...]) -> TypeGuard[tuple[int, ...]]:
|
|
27
|
+
return None not in shape
|
|
28
|
+
|
|
29
|
+
|
|
25
30
|
class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
26
31
|
"""Allow `NDFileHDFIO` to be used within `StandardDetector`."""
|
|
27
32
|
|
|
@@ -75,6 +80,16 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
|
75
80
|
# Determine number of frames that will be saved per HDF chunk
|
|
76
81
|
frames_per_chunk = await self.fileio.num_frames_chunks.get_value()
|
|
77
82
|
|
|
83
|
+
if not _is_fully_described(detector_shape):
|
|
84
|
+
# Questions:
|
|
85
|
+
# - Can AreaDetector support this?
|
|
86
|
+
# - How to deal with chunking?
|
|
87
|
+
# Don't support for now - leave option open to support it later
|
|
88
|
+
raise ValueError(
|
|
89
|
+
"Datasets with partially unknown dimensionality "
|
|
90
|
+
"are not currently supported by ADHDFWriter."
|
|
91
|
+
)
|
|
92
|
+
|
|
78
93
|
# Add the main data
|
|
79
94
|
self._datasets = [
|
|
80
95
|
HDFDatasetDescription(
|
|
@@ -7,11 +7,12 @@ from ophyd_async.core import (
|
|
|
7
7
|
SignalRW,
|
|
8
8
|
StrictEnum,
|
|
9
9
|
SubsetEnum,
|
|
10
|
+
SupersetEnum,
|
|
10
11
|
wait_for_value,
|
|
11
12
|
)
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
class ADBaseDataType(
|
|
15
|
+
class ADBaseDataType(SupersetEnum):
|
|
15
16
|
INT8 = "Int8"
|
|
16
17
|
UINT8 = "UInt8"
|
|
17
18
|
INT16 = "Int16"
|
|
@@ -22,6 +23,9 @@ class ADBaseDataType(StrictEnum):
|
|
|
22
23
|
UINT64 = "UInt64"
|
|
23
24
|
FLOAT32 = "Float32"
|
|
24
25
|
FLOAT64 = "Float64"
|
|
26
|
+
# Driver database override will blank the enum string if it doesn't
|
|
27
|
+
# support a datatype
|
|
28
|
+
UNDEFINED = ""
|
|
25
29
|
|
|
26
30
|
|
|
27
31
|
def convert_ad_dtype_to_np(ad_dtype: ADBaseDataType) -> str:
|
|
@@ -37,7 +41,12 @@ def convert_ad_dtype_to_np(ad_dtype: ADBaseDataType) -> str:
|
|
|
37
41
|
ADBaseDataType.FLOAT32: "<f4",
|
|
38
42
|
ADBaseDataType.FLOAT64: "<f8",
|
|
39
43
|
}
|
|
40
|
-
|
|
44
|
+
np_type = ad_dtype_to_np_dtype.get(ad_dtype)
|
|
45
|
+
if np_type is None:
|
|
46
|
+
raise ValueError(
|
|
47
|
+
"Areadetector driver has a blank DataType, this is not supported"
|
|
48
|
+
)
|
|
49
|
+
return np_type
|
|
41
50
|
|
|
42
51
|
|
|
43
52
|
def convert_pv_dtype_to_np(datatype: str) -> str:
|
|
@@ -27,7 +27,7 @@ class PilatusDetector(AreaDetector[PilatusController]):
|
|
|
27
27
|
config_sigs: Sequence[SignalR] = (),
|
|
28
28
|
):
|
|
29
29
|
driver = PilatusDriverIO(prefix + drv_suffix)
|
|
30
|
-
controller = PilatusController(driver)
|
|
30
|
+
controller = PilatusController(driver, readout_time=readout_time)
|
|
31
31
|
|
|
32
32
|
writer = writer_cls.with_io(
|
|
33
33
|
prefix,
|
|
@@ -29,6 +29,7 @@ class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
|
|
|
29
29
|
DetectorTrigger.INTERNAL: PilatusTriggerMode.INTERNAL,
|
|
30
30
|
DetectorTrigger.CONSTANT_GATE: PilatusTriggerMode.EXT_ENABLE,
|
|
31
31
|
DetectorTrigger.VARIABLE_GATE: PilatusTriggerMode.EXT_ENABLE,
|
|
32
|
+
DetectorTrigger.EDGE_TRIGGER: PilatusTriggerMode.EXT_TRIGGER,
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
def __init__(
|
|
@@ -49,7 +50,9 @@ class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
|
|
|
49
50
|
trigger_info.livetime
|
|
50
51
|
)
|
|
51
52
|
await asyncio.gather(
|
|
52
|
-
self.driver.trigger_mode.set(
|
|
53
|
+
self.driver.trigger_mode.set(
|
|
54
|
+
self._supported_trigger_types[trigger_info.trigger]
|
|
55
|
+
),
|
|
53
56
|
self.driver.num_images.set(
|
|
54
57
|
999_999
|
|
55
58
|
if trigger_info.total_number_of_exposures == 0
|
|
@@ -70,13 +73,3 @@ class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
|
|
|
70
73
|
True,
|
|
71
74
|
timeout=DEFAULT_TIMEOUT,
|
|
72
75
|
)
|
|
73
|
-
|
|
74
|
-
@classmethod
|
|
75
|
-
def _get_trigger_mode(cls, trigger: DetectorTrigger) -> PilatusTriggerMode:
|
|
76
|
-
if trigger not in cls._supported_trigger_types.keys():
|
|
77
|
-
raise ValueError(
|
|
78
|
-
f"{cls.__name__} only supports the following trigger "
|
|
79
|
-
f"types: {cls._supported_trigger_types.keys()} but was asked to "
|
|
80
|
-
f"use {trigger}"
|
|
81
|
-
)
|
|
82
|
-
return cls._supported_trigger_types[trigger]
|
ophyd_async/epics/core/_aioca.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import sys
|
|
3
3
|
import typing
|
|
4
|
-
from collections.abc import Sequence
|
|
4
|
+
from collections.abc import Mapping, Sequence
|
|
5
5
|
from functools import cache
|
|
6
6
|
from math import isnan, nan
|
|
7
7
|
from typing import Any, Generic, cast
|
|
@@ -146,7 +146,7 @@ class CaBoolConverter(CaConverter[bool]):
|
|
|
146
146
|
|
|
147
147
|
|
|
148
148
|
class CaEnumConverter(CaConverter[str]):
|
|
149
|
-
def __init__(self, supported_values:
|
|
149
|
+
def __init__(self, supported_values: Mapping[str, str]):
|
|
150
150
|
self.supported_values = supported_values
|
|
151
151
|
super().__init__(
|
|
152
152
|
str, dbr.DBR_STRING, metadata=SignalMetadata(choices=list(supported_values))
|
ophyd_async/epics/core/_util.py
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
from collections.abc import Sequence
|
|
2
|
-
from typing import Any, get_args, get_origin
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
|
2
|
+
from typing import Any, TypeVar, get_args, get_origin
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
6
6
|
from ophyd_async.core import (
|
|
7
7
|
SignalBackend,
|
|
8
8
|
SignalDatatypeT,
|
|
9
|
+
StrictEnum,
|
|
9
10
|
SubsetEnum,
|
|
11
|
+
SupersetEnum,
|
|
10
12
|
get_dtype,
|
|
11
13
|
get_enum_cls,
|
|
12
14
|
)
|
|
13
15
|
|
|
16
|
+
T = TypeVar("T")
|
|
17
|
+
|
|
14
18
|
|
|
15
19
|
def get_pv_basename_and_field(pv: str) -> tuple[str, str | None]:
|
|
16
20
|
"""Split PV into record name and field."""
|
|
@@ -23,26 +27,30 @@ def get_pv_basename_and_field(pv: str) -> tuple[str, str | None]:
|
|
|
23
27
|
|
|
24
28
|
def get_supported_values(
|
|
25
29
|
pv: str,
|
|
26
|
-
datatype: type,
|
|
30
|
+
datatype: type[T],
|
|
27
31
|
pv_choices: Sequence[str],
|
|
28
|
-
) ->
|
|
32
|
+
) -> Mapping[str, T | str]:
|
|
29
33
|
enum_cls = get_enum_cls(datatype)
|
|
30
34
|
if not enum_cls:
|
|
31
35
|
raise TypeError(f"{datatype} is not an Enum")
|
|
32
36
|
choices = [v.value for v in enum_cls]
|
|
33
37
|
error_msg = f"{pv} has choices {pv_choices}, but {datatype} requested {choices} "
|
|
34
|
-
if issubclass(enum_cls,
|
|
38
|
+
if issubclass(enum_cls, StrictEnum):
|
|
39
|
+
if set(choices) != set(pv_choices):
|
|
40
|
+
raise TypeError(error_msg + "to be strictly equal to them.")
|
|
41
|
+
elif issubclass(enum_cls, SubsetEnum):
|
|
35
42
|
if not set(choices).issubset(pv_choices):
|
|
36
43
|
raise TypeError(error_msg + "to be a subset of them.")
|
|
44
|
+
elif issubclass(enum_cls, SupersetEnum):
|
|
45
|
+
if not set(pv_choices).issubset(choices):
|
|
46
|
+
raise TypeError(error_msg + "to be a superset of them.")
|
|
37
47
|
else:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
for v in enum_cls:
|
|
45
|
-
supported_values[v.value] = v
|
|
48
|
+
raise TypeError(f"{datatype} is not a StrictEnum, SubsetEnum, or SupersetEnum")
|
|
49
|
+
# Create a map from the string value to the enum instance
|
|
50
|
+
# For StrictEnum and SupersetEnum, all values here will be enum values
|
|
51
|
+
# For SubsetEnum, only the values in choices will be enum values, the rest will be
|
|
52
|
+
# strings
|
|
53
|
+
supported_values = {x: enum_cls(x) for x in pv_choices}
|
|
46
54
|
return supported_values
|
|
47
55
|
|
|
48
56
|
|
|
@@ -10,9 +10,10 @@ from ophyd_async.core import (
|
|
|
10
10
|
Device,
|
|
11
11
|
DeviceVector,
|
|
12
12
|
PathProvider,
|
|
13
|
+
Reference,
|
|
14
|
+
SignalR,
|
|
13
15
|
StrictEnum,
|
|
14
16
|
observe_value,
|
|
15
|
-
set_and_wait_for_other_value,
|
|
16
17
|
set_and_wait_for_value,
|
|
17
18
|
wait_for_value,
|
|
18
19
|
)
|
|
@@ -51,7 +52,7 @@ class Odin(Device):
|
|
|
51
52
|
|
|
52
53
|
self.capture = epics_signal_rw(Writing, f"{prefix}Capture")
|
|
53
54
|
self.capture_rbv = epics_signal_r(str, prefix + "Capture_RBV")
|
|
54
|
-
self.num_captured = epics_signal_r(int, f"{prefix}
|
|
55
|
+
self.num_captured = epics_signal_r(int, f"{prefix}NumCaptured_RBV")
|
|
55
56
|
self.num_to_capture = epics_signal_rw_rbv(int, f"{prefix}NumCapture")
|
|
56
57
|
|
|
57
58
|
self.start_timeout = epics_signal_rw(str, f"{prefix}StartTimeout")
|
|
@@ -80,9 +81,11 @@ class OdinWriter(DetectorWriter):
|
|
|
80
81
|
self,
|
|
81
82
|
path_provider: PathProvider,
|
|
82
83
|
odin_driver: Odin,
|
|
84
|
+
eiger_bit_depth: SignalR[int],
|
|
83
85
|
) -> None:
|
|
84
86
|
self._drv = odin_driver
|
|
85
87
|
self._path_provider = path_provider
|
|
88
|
+
self._eiger_bit_depth = Reference(eiger_bit_depth)
|
|
86
89
|
super().__init__()
|
|
87
90
|
|
|
88
91
|
async def open(self, name: str, exposures_per_event: int = 1) -> dict[str, DataKey]:
|
|
@@ -92,24 +95,20 @@ class OdinWriter(DetectorWriter):
|
|
|
92
95
|
await asyncio.gather(
|
|
93
96
|
self._drv.file_path.set(str(info.directory_path)),
|
|
94
97
|
self._drv.file_name.set(info.filename),
|
|
95
|
-
self._drv.data_type.set(
|
|
96
|
-
"UInt16"
|
|
97
|
-
), # TODO: Get from eiger https://github.com/bluesky/ophyd-async/issues/529
|
|
98
|
+
self._drv.data_type.set(f"UInt{await self._eiger_bit_depth().get_value()}"),
|
|
98
99
|
self._drv.num_to_capture.set(0),
|
|
99
100
|
)
|
|
100
101
|
|
|
101
102
|
await wait_for_value(self._drv.meta_active, "Active", timeout=DEFAULT_TIMEOUT)
|
|
102
103
|
|
|
103
|
-
await
|
|
104
|
-
|
|
105
|
-
Writing.CAPTURE,
|
|
106
|
-
self._drv.capture_rbv,
|
|
107
|
-
"Capturing",
|
|
108
|
-
set_timeout=None,
|
|
109
|
-
wait_for_set_completion=False,
|
|
104
|
+
await self._drv.capture.set(
|
|
105
|
+
Writing.CAPTURE, wait=False
|
|
110
106
|
) # TODO: Investigate why we do not get a put callback when setting capture pv https://github.com/bluesky/ophyd-async/issues/866
|
|
111
107
|
|
|
112
|
-
await
|
|
108
|
+
await asyncio.gather(
|
|
109
|
+
wait_for_value(self._drv.capture_rbv, "Capturing", timeout=DEFAULT_TIMEOUT),
|
|
110
|
+
wait_for_value(self._drv.meta_writing, "Writing", timeout=DEFAULT_TIMEOUT),
|
|
111
|
+
)
|
|
113
112
|
|
|
114
113
|
return await self._describe()
|
|
115
114
|
|
ophyd_async/epics/motor.py
CHANGED
|
@@ -260,10 +260,11 @@ class Motor(
|
|
|
260
260
|
(await self.acceleration_time.get_value()) * fly_velocity * 0.5
|
|
261
261
|
)
|
|
262
262
|
|
|
263
|
-
|
|
263
|
+
direction = 1 if end_position > start_position else -1
|
|
264
|
+
self._fly_completed_position = end_position + (run_up_distance * direction)
|
|
264
265
|
|
|
265
266
|
# Prepared position not used after prepare, so no need to store in self
|
|
266
|
-
fly_prepared_position = start_position - run_up_distance
|
|
267
|
+
fly_prepared_position = start_position - (run_up_distance * direction)
|
|
267
268
|
|
|
268
269
|
motor_lower_limit, motor_upper_limit, egu = await asyncio.gather(
|
|
269
270
|
self.low_limit_travel.get_value(),
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
from pydantic import Field
|
|
2
2
|
|
|
3
|
-
from ophyd_async.core import
|
|
3
|
+
from ophyd_async.core import (
|
|
4
|
+
AsyncStatus,
|
|
5
|
+
PathProvider,
|
|
6
|
+
StandardDetector,
|
|
7
|
+
TriggerInfo,
|
|
8
|
+
)
|
|
4
9
|
from ophyd_async.epics.eiger import Odin, OdinWriter
|
|
5
10
|
|
|
6
11
|
from ._eiger_controller import EigerController
|
|
@@ -30,7 +35,11 @@ class EigerDetector(StandardDetector):
|
|
|
30
35
|
|
|
31
36
|
super().__init__(
|
|
32
37
|
EigerController(self.drv),
|
|
33
|
-
OdinWriter(
|
|
38
|
+
OdinWriter(
|
|
39
|
+
path_provider,
|
|
40
|
+
self.odin,
|
|
41
|
+
self.drv.detector.bit_depth_readout,
|
|
42
|
+
),
|
|
34
43
|
name=name,
|
|
35
44
|
)
|
|
36
45
|
|
|
@@ -5,6 +5,7 @@ from ophyd_async.core import (
|
|
|
5
5
|
DetectorController,
|
|
6
6
|
DetectorTrigger,
|
|
7
7
|
TriggerInfo,
|
|
8
|
+
wait_for_value,
|
|
8
9
|
)
|
|
9
10
|
|
|
10
11
|
from ._eiger_io import EigerDriverIO, EigerTriggerMode
|
|
@@ -51,14 +52,17 @@ class EigerController(DetectorController):
|
|
|
51
52
|
self._drv.detector.frame_time.set(trigger_info.livetime),
|
|
52
53
|
]
|
|
53
54
|
)
|
|
55
|
+
|
|
54
56
|
await asyncio.gather(*coros)
|
|
55
57
|
|
|
56
58
|
async def arm(self):
|
|
57
|
-
|
|
59
|
+
# NOTE: This will return immedietly on FastCS 0.8.0,
|
|
60
|
+
# but will return after the Eiger has completed arming in 0.9.0.
|
|
61
|
+
# https://github.com/DiamondLightSource/FastCS/pull/141
|
|
62
|
+
await self._drv.detector.arm.trigger(timeout=DEFAULT_TIMEOUT)
|
|
58
63
|
|
|
59
64
|
async def wait_for_idle(self):
|
|
60
|
-
|
|
61
|
-
await self._arm_status
|
|
65
|
+
await wait_for_value(self._drv.detector.state, "idle", timeout=DEFAULT_TIMEOUT)
|
|
62
66
|
|
|
63
67
|
async def disarm(self):
|
|
64
68
|
await self._drv.detector.disarm.trigger()
|
ophyd_async/sim/_motor.py
CHANGED
|
@@ -3,7 +3,7 @@ import contextlib
|
|
|
3
3
|
import time
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
6
|
-
from bluesky.protocols import Location, Reading, Stoppable, Subscribable
|
|
6
|
+
from bluesky.protocols import Locatable, Location, Reading, Stoppable, Subscribable
|
|
7
7
|
from pydantic import BaseModel, ConfigDict, Field
|
|
8
8
|
|
|
9
9
|
from ophyd_async.core import (
|
|
@@ -50,7 +50,7 @@ class FlySimMotorInfo(BaseModel):
|
|
|
50
50
|
return self.cv_end + acceleration_time * self.velocity / 2
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
class SimMotor(StandardReadable, Stoppable, Subscribable[float]):
|
|
53
|
+
class SimMotor(StandardReadable, Stoppable, Subscribable[float], Locatable[float]):
|
|
54
54
|
"""For usage when simulating a motor."""
|
|
55
55
|
|
|
56
56
|
def __init__(self, name="", instant=True) -> None:
|
|
@@ -3,10 +3,11 @@ from __future__ import annotations
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import Any, Generic, TypeVar
|
|
5
5
|
|
|
6
|
-
from ophyd_async.core import Device, DeviceConnector, DeviceFiller, LazyMock
|
|
7
6
|
from tango import DeviceProxy
|
|
8
7
|
from tango.asyncio import DeviceProxy as AsyncDeviceProxy
|
|
9
8
|
|
|
9
|
+
from ophyd_async.core import Device, DeviceConnector, DeviceFiller, LazyMock
|
|
10
|
+
|
|
10
11
|
from ._signal import TangoSignalBackend, infer_python_type, infer_signal_type
|
|
11
12
|
from ._utils import get_full_attr_trl
|
|
12
13
|
|
|
@@ -2,13 +2,9 @@ from typing import Any, Generic
|
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
from numpy.typing import NDArray
|
|
5
|
+
from tango import DevState
|
|
5
6
|
|
|
6
|
-
from ophyd_async.core import
|
|
7
|
-
SignalDatatypeT,
|
|
8
|
-
)
|
|
9
|
-
from tango import (
|
|
10
|
-
DevState,
|
|
11
|
-
)
|
|
7
|
+
from ophyd_async.core import SignalDatatypeT
|
|
12
8
|
|
|
13
9
|
from ._utils import DevStateEnum
|
|
14
10
|
|
|
@@ -6,6 +6,14 @@ import logging
|
|
|
6
6
|
from enum import Enum, IntEnum
|
|
7
7
|
|
|
8
8
|
import numpy.typing as npt
|
|
9
|
+
from tango import (
|
|
10
|
+
AttrDataFormat,
|
|
11
|
+
AttrWriteType,
|
|
12
|
+
CmdArgType,
|
|
13
|
+
DeviceProxy,
|
|
14
|
+
DevState,
|
|
15
|
+
)
|
|
16
|
+
from tango.asyncio import DeviceProxy as AsyncDeviceProxy
|
|
9
17
|
|
|
10
18
|
from ophyd_async.core import (
|
|
11
19
|
DEFAULT_TIMEOUT,
|
|
@@ -16,14 +24,6 @@ from ophyd_async.core import (
|
|
|
16
24
|
SignalW,
|
|
17
25
|
SignalX,
|
|
18
26
|
)
|
|
19
|
-
from tango import (
|
|
20
|
-
AttrDataFormat,
|
|
21
|
-
AttrWriteType,
|
|
22
|
-
CmdArgType,
|
|
23
|
-
DeviceProxy,
|
|
24
|
-
DevState,
|
|
25
|
-
)
|
|
26
|
-
from tango.asyncio import DeviceProxy as AsyncDeviceProxy
|
|
27
27
|
|
|
28
28
|
from ._tango_transport import TangoSignalBackend, get_python_type
|
|
29
29
|
from ._utils import get_device_trl_and_attr
|
|
@@ -10,18 +10,6 @@ from typing import Any, ParamSpec, TypeVar, cast
|
|
|
10
10
|
import numpy as np
|
|
11
11
|
from bluesky.protocols import Reading
|
|
12
12
|
from event_model import DataKey
|
|
13
|
-
|
|
14
|
-
from ophyd_async.core import (
|
|
15
|
-
AsyncStatus,
|
|
16
|
-
Callback,
|
|
17
|
-
NotConnected,
|
|
18
|
-
SignalBackend,
|
|
19
|
-
SignalDatatypeT,
|
|
20
|
-
StrictEnum,
|
|
21
|
-
get_dtype,
|
|
22
|
-
get_unique,
|
|
23
|
-
wait_for_connection,
|
|
24
|
-
)
|
|
25
13
|
from tango import (
|
|
26
14
|
AttrDataFormat,
|
|
27
15
|
AttributeInfoEx,
|
|
@@ -40,6 +28,18 @@ from tango.asyncio_executor import (
|
|
|
40
28
|
)
|
|
41
29
|
from tango.utils import is_array, is_binary, is_bool, is_float, is_int, is_str
|
|
42
30
|
|
|
31
|
+
from ophyd_async.core import (
|
|
32
|
+
AsyncStatus,
|
|
33
|
+
Callback,
|
|
34
|
+
NotConnected,
|
|
35
|
+
SignalBackend,
|
|
36
|
+
SignalDatatypeT,
|
|
37
|
+
StrictEnum,
|
|
38
|
+
get_dtype,
|
|
39
|
+
get_unique,
|
|
40
|
+
wait_for_connection,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
43
|
from ._converters import (
|
|
44
44
|
TangoConverter,
|
|
45
45
|
TangoDevStateArrayConverter,
|
|
@@ -3,6 +3,8 @@ from dataclasses import dataclass
|
|
|
3
3
|
from typing import Any, Generic, TypeVar
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
6
|
+
from tango import AttrDataFormat, AttrWriteType, DevState
|
|
7
|
+
from tango.server import Device, attribute, command
|
|
6
8
|
|
|
7
9
|
from ophyd_async.core import (
|
|
8
10
|
Array1D,
|
|
@@ -10,8 +12,6 @@ from ophyd_async.core import (
|
|
|
10
12
|
StrictEnum,
|
|
11
13
|
)
|
|
12
14
|
from ophyd_async.testing import float_array_value, int_array_value
|
|
13
|
-
from tango import AttrDataFormat, AttrWriteType, DevState
|
|
14
|
-
from tango.server import Device, attribute, command
|
|
15
15
|
|
|
16
16
|
T = TypeVar("T")
|
|
17
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ophyd-async
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.1
|
|
4
4
|
Summary: Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango
|
|
5
5
|
Author-email: Tom Cobb <tom.cobb@diamond.ac.uk>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -43,7 +43,7 @@ Description-Content-Type: text/markdown
|
|
|
43
43
|
License-File: LICENSE
|
|
44
44
|
Requires-Dist: numpy
|
|
45
45
|
Requires-Dist: bluesky>=1.13.1rc2
|
|
46
|
-
Requires-Dist: event-model>=1.
|
|
46
|
+
Requires-Dist: event-model>=1.23
|
|
47
47
|
Requires-Dist: pyyaml
|
|
48
48
|
Requires-Dist: colorlog
|
|
49
49
|
Requires-Dist: pydantic>=2.0
|
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
ophyd_async/__init__.py,sha256=dcAA3qsj1nNIMe5l-v2tlduZ_ypwBmyuHe45Lsq4k4w,206
|
|
2
2
|
ophyd_async/__main__.py,sha256=n_U4O9bgm97OuboUB_9eK7eFiwy8BZSgXJ0OzbE0DqU,481
|
|
3
3
|
ophyd_async/_docs_parser.py,sha256=gPYrigfSbYCF7QoSf2UvE-cpQu4snSssl7ZWN-kKDzI,352
|
|
4
|
-
ophyd_async/_version.py,sha256
|
|
4
|
+
ophyd_async/_version.py,sha256=StiR6uxiq6hqMzT3MUIl_ZooIq2cetH9oWrHUI_qWFU,513
|
|
5
5
|
ophyd_async/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
ophyd_async/core/__init__.py,sha256=
|
|
7
|
-
ophyd_async/core/_derived_signal.py,sha256=
|
|
8
|
-
ophyd_async/core/_derived_signal_backend.py,sha256=
|
|
9
|
-
ophyd_async/core/_detector.py,sha256=
|
|
6
|
+
ophyd_async/core/__init__.py,sha256=mSoldXL3hou1UifwsUcos2Q3Vy_pAaNJJWdMc0VInyg,4516
|
|
7
|
+
ophyd_async/core/_derived_signal.py,sha256=7ZZrDUnS2DGVwJZNX6_u0u-lAHKHCC7bL5auIfYV9w8,11440
|
|
8
|
+
ophyd_async/core/_derived_signal_backend.py,sha256=AZpyMmt3-ssymtDIa3138qPp1srmaId137A_yzQuFD0,12243
|
|
9
|
+
ophyd_async/core/_detector.py,sha256=PbWpv7lyBvYEJV-prG9qU0DadmxhyzMhQ9oLC0N7vwQ,15193
|
|
10
10
|
ophyd_async/core/_device.py,sha256=QSOgjXkvBpPWE1VhQ21tIC8d9z4Oadr6C_1tk4J21W4,14617
|
|
11
11
|
ophyd_async/core/_device_filler.py,sha256=MDz8eQQ-eEAwo-UEMxfqPfpcBuMG01tLCGR6utwVnmE,14825
|
|
12
12
|
ophyd_async/core/_flyer.py,sha256=YPCGAokF0GofOOO2OLrsSLoZSXs_RzmmrNglBunA31o,1924
|
|
13
|
-
ophyd_async/core/_hdf_dataset.py,sha256=
|
|
13
|
+
ophyd_async/core/_hdf_dataset.py,sha256=MKgA-6uxtCg-cxpGKAIxdWOw55CZp66si8lsak5e9CA,2875
|
|
14
14
|
ophyd_async/core/_log.py,sha256=DxKR4Nz3SgTaTzKBZWqt-w48yT8WUAr_3Qr223TEWRw,3587
|
|
15
15
|
ophyd_async/core/_mock_signal_backend.py,sha256=SPdCbVWss6-iL9C3t9u0IvR_Ln9JeDypVd18WlivdjE,3156
|
|
16
16
|
ophyd_async/core/_protocol.py,sha256=wQ_snxhTprHqEjQb1HgFwBljwolMY6A8C3xgV1PXwdU,4051
|
|
17
|
-
ophyd_async/core/_providers.py,sha256=
|
|
17
|
+
ophyd_async/core/_providers.py,sha256=1XuLUw9sT1pKMfH_PsDEpIi1gulla7NfPSp3IR3KfEA,7545
|
|
18
18
|
ophyd_async/core/_readable.py,sha256=iBo1YwA5bsAbzLbznvmSnzKDWUuGkLh850Br3BXsgeU,11707
|
|
19
19
|
ophyd_async/core/_settings.py,sha256=_ZccbXKP7j5rG6-bMKk7aaLr8hChdRDAPY_YSR71XXM,4213
|
|
20
|
-
ophyd_async/core/_signal.py,sha256=
|
|
21
|
-
ophyd_async/core/_signal_backend.py,sha256=
|
|
20
|
+
ophyd_async/core/_signal.py,sha256=1XHhnYwXa0yusSKdaRAiPUoVA5atcWd0S1ztBSaEi9k,27369
|
|
21
|
+
ophyd_async/core/_signal_backend.py,sha256=PvwTbbSVEGqM-2s5BNRrKGwM_MiYL71qMxYAgyZ7wRM,6930
|
|
22
22
|
ophyd_async/core/_soft_signal_backend.py,sha256=zrE7H2ojHY6oQBucLkFgukszrkdvbIZuavLjEUqc_xM,6227
|
|
23
23
|
ophyd_async/core/_status.py,sha256=h4TtWFM7wFtpxxyAYYSITgcVzArYZdYBHbya6qIX5t0,6553
|
|
24
24
|
ophyd_async/core/_table.py,sha256=l5GzL3La68ZkXpcOOVntnYr8ofFV8kWGr1hzY1ek83w,6897
|
|
25
|
-
ophyd_async/core/_utils.py,sha256=
|
|
25
|
+
ophyd_async/core/_utils.py,sha256=742wGL3cJD4wlDlNlS-KAbaTN5FkYDY7RXU7m6wpYaw,11737
|
|
26
26
|
ophyd_async/core/_yaml_settings.py,sha256=txy4_Igbsr8Yo132yv8jnCcsLpal8klM7DcPNow2KBs,2078
|
|
27
27
|
ophyd_async/epics/__init__.py,sha256=ou4yEaH9VZHz70e8oM614-arLMQvUfQyXhRJsnEpWn8,60
|
|
28
|
-
ophyd_async/epics/motor.py,sha256=
|
|
28
|
+
ophyd_async/epics/motor.py,sha256=OgfETz_7wCLmG4WCYDpaYyUB4TomDFyCyZzwyhW1Tiw,10549
|
|
29
29
|
ophyd_async/epics/signal.py,sha256=0A-supp9ajr63O6aD7F9oG0-Q26YmRjk-ZGh57-jo1Y,239
|
|
30
30
|
ophyd_async/epics/adandor/__init__.py,sha256=qsBoZdljSaqM-kJSvTf0uq5TNwXAih9lP3yOZpKxOFQ,204
|
|
31
31
|
ophyd_async/epics/adandor/_andor.py,sha256=SxAIP9OLefUqKcxrxhjNzil5D8-59Ps0vADdR6scO44,1281
|
|
@@ -36,22 +36,22 @@ ophyd_async/epics/adaravis/_aravis.py,sha256=Ju2wuebz9_ovl-Kza39s5VQ1pV-Omt_BaIW
|
|
|
36
36
|
ophyd_async/epics/adaravis/_aravis_controller.py,sha256=WiFR7_FAAu6_88zG-yzGLsR9YcO4L6xR73Wnjw9n0i4,1908
|
|
37
37
|
ophyd_async/epics/adaravis/_aravis_io.py,sha256=af5RxeXF2ligvAXwMNMKHA4QHTR_WmNFz-f18qD2dbg,855
|
|
38
38
|
ophyd_async/epics/adcore/__init__.py,sha256=GipuBZwaAju4g15WjvGs78S4zjGVxmbPel4E29zHFvE,1583
|
|
39
|
-
ophyd_async/epics/adcore/_core_detector.py,sha256=
|
|
40
|
-
ophyd_async/epics/adcore/_core_io.py,sha256
|
|
41
|
-
ophyd_async/epics/adcore/_core_logic.py,sha256=
|
|
39
|
+
ophyd_async/epics/adcore/_core_detector.py,sha256=mRDaHgXCTZF-MIVsU1csoQx9jObutYDpMWayugx2-jI,2631
|
|
40
|
+
ophyd_async/epics/adcore/_core_io.py,sha256=jm4gQUSq727xnPriuH6jFHqlDBZceKxr_XyBNj5F65U,7392
|
|
41
|
+
ophyd_async/epics/adcore/_core_logic.py,sha256=IH7iOSVIsVj4e97ClhdBFpmdMkb8TznSaLkd3ohEhUs,8884
|
|
42
42
|
ophyd_async/epics/adcore/_core_writer.py,sha256=S58plRmbNzig-r7gubKjDVDni_nf4CASaV1OJvudbHw,8019
|
|
43
|
-
ophyd_async/epics/adcore/_hdf_writer.py,sha256=
|
|
43
|
+
ophyd_async/epics/adcore/_hdf_writer.py,sha256=GW_3NKRTF3kc9Wwz6vf5a7eqlnl81LXgk8afLgZaeNU,6134
|
|
44
44
|
ophyd_async/epics/adcore/_jpeg_writer.py,sha256=7XC4Twx_MaCBjeol27UA-hStOCQEjkEAb3ToVMPUlZ8,670
|
|
45
45
|
ophyd_async/epics/adcore/_single_trigger.py,sha256=tFGLT1b_rZzAvbqWP-hyCccxJMRY26T5IER-VAqKXmc,1275
|
|
46
46
|
ophyd_async/epics/adcore/_tiff_writer.py,sha256=Na30osfkgrq4VQhUzDcuS52Gy7FS08CzbgEmKwljTmk,671
|
|
47
|
-
ophyd_async/epics/adcore/_utils.py,sha256=
|
|
47
|
+
ophyd_async/epics/adcore/_utils.py,sha256=jMmZnyDwRDBq-N_x8qOWjW2RMN9uw2KuoAmukPbb404,4252
|
|
48
48
|
ophyd_async/epics/adkinetix/__init__.py,sha256=A9xq3lGMrmza9lfukRixC0Up_kUDVFII8JguLr2x7Bw,308
|
|
49
49
|
ophyd_async/epics/adkinetix/_kinetix.py,sha256=zZv0JZ8i1RSx7KBDn_1HGNOY0BoIP81mRK5TKq7d4eA,1302
|
|
50
50
|
ophyd_async/epics/adkinetix/_kinetix_controller.py,sha256=UI-XcQpGj7jq-_e1ceoMOZkyfejwG6H5wX-Ntp_NJjg,1481
|
|
51
51
|
ophyd_async/epics/adkinetix/_kinetix_io.py,sha256=5u4TknhcoCSjyp16HQIEp_aOmvfNAKOBoGlMRJoVxuw,770
|
|
52
52
|
ophyd_async/epics/adpilatus/__init__.py,sha256=So7PrLHij7LWOqMbsHUhfqRzKp9iKtrojuWTDS1lID8,406
|
|
53
|
-
ophyd_async/epics/adpilatus/_pilatus.py,sha256=
|
|
54
|
-
ophyd_async/epics/adpilatus/_pilatus_controller.py,sha256=
|
|
53
|
+
ophyd_async/epics/adpilatus/_pilatus.py,sha256=CaViuom2oKxYWRU4Y_GltHA-LxqguoTW9k67b76Q5SY,1538
|
|
54
|
+
ophyd_async/epics/adpilatus/_pilatus_controller.py,sha256=21NVM_xwS5De1R6Vi_4GKpbTfdxByMzmZNwU3qBV1-A,2515
|
|
55
55
|
ophyd_async/epics/adpilatus/_pilatus_io.py,sha256=EhCj--x4yYy5AtRBG6Ts3A6gsKS6Ut-LaMihAuYDvE0,819
|
|
56
56
|
ophyd_async/epics/adsimdetector/__init__.py,sha256=EQqxP5DUvZGLxpvSXPagTPy3ROwE-PikyRfcnZfD1KM,281
|
|
57
57
|
ophyd_async/epics/adsimdetector/_sim.py,sha256=r3SuBENGkOU-8X-i3zDyZljFxf0SmsUfryNPFvP1_os,1147
|
|
@@ -62,13 +62,13 @@ ophyd_async/epics/advimba/_vimba.py,sha256=4XlEnsJMGDzHLuYaIDUmaxx0gtOAehn5BKBZM
|
|
|
62
62
|
ophyd_async/epics/advimba/_vimba_controller.py,sha256=v0av2bGnaJ01w9Igksupt2IlkuBEFlAeRCPOVma-Xa4,1980
|
|
63
63
|
ophyd_async/epics/advimba/_vimba_io.py,sha256=cb2Nfp05fBZAcNVXpz-rqRIRS-TiZW5DPUJOmaFyAw0,1589
|
|
64
64
|
ophyd_async/epics/core/__init__.py,sha256=8NoQxEEc2Ny_L9nrD2fnGSf_2gJr1wCR1LwUeLNcIJo,588
|
|
65
|
-
ophyd_async/epics/core/_aioca.py,sha256=
|
|
65
|
+
ophyd_async/epics/core/_aioca.py,sha256=py5dM5hbT2OXah_Lzt_fAZ9RdaHhv8pdUtkOF8iOYO4,13015
|
|
66
66
|
ophyd_async/epics/core/_epics_connector.py,sha256=S4z_wbj-aogVcjqCyUgjhcq5Y4gDC7y6wXbsSz2nODY,1918
|
|
67
67
|
ophyd_async/epics/core/_epics_device.py,sha256=wGdR24I7GSPh3HmM7jsWKZhBZgt4IyLrCn4Ut7Wx_xo,510
|
|
68
68
|
ophyd_async/epics/core/_p4p.py,sha256=uWh3oWPme74G4YfeJ6k8ZlHdKOwcf8Xp1J82b9aa_JI,16407
|
|
69
69
|
ophyd_async/epics/core/_pvi_connector.py,sha256=nAReSiasZA3j_0f8XhuWVO4_ck0MrusnKR9Jg-RT-ok,5584
|
|
70
70
|
ophyd_async/epics/core/_signal.py,sha256=2Cp5f5Xb2junnVigypjb3hWu4MuMmbrcHOBgPl5Mhv4,5776
|
|
71
|
-
ophyd_async/epics/core/_util.py,sha256=
|
|
71
|
+
ophyd_async/epics/core/_util.py,sha256=G2kYfwsQ5iS3EgGrGuPA8bgC_PEN_XxO28oBurIqJFQ,2522
|
|
72
72
|
ophyd_async/epics/demo/__init__.py,sha256=WR2M3D8dbHcisJW2OIU2ManZu5SWez8ytZEp4jSBfDY,416
|
|
73
73
|
ophyd_async/epics/demo/__main__.py,sha256=o6M0FSWduPHe2lN9yNEdsXb48NckSd54-XJGoLe20Pc,1116
|
|
74
74
|
ophyd_async/epics/demo/_ioc.py,sha256=UtwTReKTR-1_tPIhdGBr7OxP_Vqi6xWyCcFABd9BYPY,1040
|
|
@@ -80,7 +80,7 @@ ophyd_async/epics/demo/motor.db,sha256=3xb6WTXo4crrvk-M8Y16G9pUidp27vD5vIKKBpLTU
|
|
|
80
80
|
ophyd_async/epics/demo/point_detector.db,sha256=8kBa3XKpmfXCxetT4tq5_RFXa_XqS1Z2ZNzsa2AtLds,1366
|
|
81
81
|
ophyd_async/epics/demo/point_detector_channel.db,sha256=FZ9H6HjqplhcF2jgimv_dT1nn-CBlfjs7Y--iCfHp5Y,632
|
|
82
82
|
ophyd_async/epics/eiger/__init__.py,sha256=7kRqVzwoD8PVtp7Nj9iQWlgbLeoWE_8oiq-B0kixwTE,93
|
|
83
|
-
ophyd_async/epics/eiger/_odin_io.py,sha256=
|
|
83
|
+
ophyd_async/epics/eiger/_odin_io.py,sha256=4JHHeQ2R6ukq_JROGFeWVpez1UZqKpmWP1jaDCA71gQ,5567
|
|
84
84
|
ophyd_async/epics/testing/__init__.py,sha256=aTIv4D2DYrpnGco5RQF8QuLG1SfFkIlTyM2uYEKXltA,522
|
|
85
85
|
ophyd_async/epics/testing/_example_ioc.py,sha256=uUmfMXV_Pd2SMFyb0y_4uTc6gkGRUqU1cJ-XQC2ROW8,3915
|
|
86
86
|
ophyd_async/epics/testing/_utils.py,sha256=6sqJ0BCwubSkK-WOJbmpKNqZKG0AmCoevzaMGaRmuJs,1702
|
|
@@ -89,9 +89,9 @@ ophyd_async/epics/testing/test_records_pva.db,sha256=HJAJSvLtPWG5B5dKv8OZ0_hPJxR
|
|
|
89
89
|
ophyd_async/fastcs/__init__.py,sha256=qlIM9-pjJ8yWfnzTM9-T9cw7zQLKjeeNROQTni5Dr6M,80
|
|
90
90
|
ophyd_async/fastcs/core.py,sha256=pL_srtTrfuoBHUjDFpxES92owFq9M4Jve0Skk1oeuFA,517
|
|
91
91
|
ophyd_async/fastcs/eiger/__init__.py,sha256=kwYs1PuetEOTeNcFNxtyhTvfbJvWCOu8wtEw3Knotuw,354
|
|
92
|
-
ophyd_async/fastcs/eiger/_eiger.py,sha256=
|
|
93
|
-
ophyd_async/fastcs/eiger/_eiger_controller.py,sha256=
|
|
94
|
-
ophyd_async/fastcs/eiger/_eiger_io.py,sha256=
|
|
92
|
+
ophyd_async/fastcs/eiger/_eiger.py,sha256=5mpao2j2Xc11OuecvimPTmZ9mkEndzgMlDj1x-K-5hg,1241
|
|
93
|
+
ophyd_async/fastcs/eiger/_eiger_controller.py,sha256=Cucj-1M-1CaxSJxHZmHs3f_OXwtTIspcqUFhRNGzn_E,2361
|
|
94
|
+
ophyd_async/fastcs/eiger/_eiger_io.py,sha256=KS1ppYt6PJvPpHiR2AaDaPAhkhYwC2ei8VuZnBBiBs8,1216
|
|
95
95
|
ophyd_async/fastcs/odin/__init__.py,sha256=da1PTClDMl-IBkrSvq6JC1lnS-K_BASzCvxVhNxN5Ls,13
|
|
96
96
|
ophyd_async/fastcs/panda/__init__.py,sha256=ugrScVm4HPQFc-d1kTAfZ5UUzW9T3SPgTi0OD2s8ZH0,1003
|
|
97
97
|
ophyd_async/fastcs/panda/_block.py,sha256=SM7NaWCRwLz2Pl4wgjZMrDgx3ZLdGPTw6nU0bA-65yA,2394
|
|
@@ -115,26 +115,26 @@ ophyd_async/sim/_blob_detector_controller.py,sha256=y1aSNQJUPnsT2qnj2sk254Mp18an
|
|
|
115
115
|
ophyd_async/sim/_blob_detector_writer.py,sha256=4sC3Kkyk4-sSaN7mSKk4A4zDH4IU_qkc1gaqt4QyTAM,3636
|
|
116
116
|
ophyd_async/sim/_mirror_horizontal.py,sha256=_AfsHxp5V3rYbK-goI0iwPuDDvx1BrNoUQ4smXzqFbc,1452
|
|
117
117
|
ophyd_async/sim/_mirror_vertical.py,sha256=PyWpyCrW_FCzzGN7Dk0tTB-SKbDIV4GD1fl8AXUJbEI,2029
|
|
118
|
-
ophyd_async/sim/_motor.py,sha256
|
|
118
|
+
ophyd_async/sim/_motor.py,sha256=jrI83A6kPCkZztHDKcd1XaXq7sc6GD2Ylkn7JxlULp0,10196
|
|
119
119
|
ophyd_async/sim/_pattern_generator.py,sha256=FjPEWiBQh_7tYP_8WPhbVXnTGPPOaV6By7Skz7YNIrY,3722
|
|
120
120
|
ophyd_async/sim/_point_detector.py,sha256=nXgL_1aJZciNBw8Zr2wMYaMbzzAEKXV3yV8FQz2nS_4,2940
|
|
121
121
|
ophyd_async/sim/_stage.py,sha256=qaeyZbUVL1v2pTHJiZxq-y6BKpA1l_DAKyzAQppQx70,772
|
|
122
122
|
ophyd_async/tango/__init__.py,sha256=g9xzjlzPpUAP12YI-kYwfAoLSYPAQdL1S11R2c-cius,60
|
|
123
123
|
ophyd_async/tango/core/__init__.py,sha256=IMvQ7MWcTof99h_pr483KWKvQV2-h7zo_iRpLA2PUYQ,1108
|
|
124
|
-
ophyd_async/tango/core/_base_device.py,sha256=
|
|
125
|
-
ophyd_async/tango/core/_converters.py,sha256=
|
|
126
|
-
ophyd_async/tango/core/_signal.py,sha256=
|
|
124
|
+
ophyd_async/tango/core/_base_device.py,sha256=e9oqSL-fDOj8r9nUUFZkbibhRGbI6HYtlnZjK5B_2fE,5033
|
|
125
|
+
ophyd_async/tango/core/_converters.py,sha256=xI_RhMR8dY6IVORUZVVCL9LdYnEE6TA6BBPX_lTu06w,2183
|
|
126
|
+
ophyd_async/tango/core/_signal.py,sha256=6KXDzV8Z08hrtCqG8J4AYnhemQjtL6Eofngo6yNo52o,5798
|
|
127
127
|
ophyd_async/tango/core/_tango_readable.py,sha256=ctR6YcBGGatW6Jp2kvddA1hVZ2v1CidPsF9FmJK9BYg,406
|
|
128
|
-
ophyd_async/tango/core/_tango_transport.py,sha256=
|
|
128
|
+
ophyd_async/tango/core/_tango_transport.py,sha256=6ngUybtSYqABFKsxZgA-rVms21LDmxIwfQ_VMS7h5qU,31903
|
|
129
129
|
ophyd_async/tango/core/_utils.py,sha256=FrX524jwa-0EQxpcGaKF6yqyHm5BWIFSE82FcIB0aAY,1430
|
|
130
130
|
ophyd_async/tango/demo/__init__.py,sha256=_j-UicTnckuIBp8PnieFMOMnLFGivnaKdmo9o0hYtzc,256
|
|
131
131
|
ophyd_async/tango/demo/_counter.py,sha256=2J4SCHnBWLF0O5mFWlJdO4tmnElvlx5sRrk4op_AC9U,1139
|
|
132
132
|
ophyd_async/tango/demo/_detector.py,sha256=X5YWHAjukKZ7iYF1fBNle4CBDj1X5rvj0lnPMOcnRCU,1340
|
|
133
133
|
ophyd_async/tango/demo/_mover.py,sha256=c-IxrWQN0YX-Iuf720xlKfsPbNfh7bUz_AVnbwYkW08,2984
|
|
134
134
|
ophyd_async/tango/demo/_tango/__init__.py,sha256=FfONT7vM49nNo3a1Lv-LcMZO9EHv6bv91yY-RnxIib4,85
|
|
135
|
-
ophyd_async/tango/demo/_tango/_servers.py,sha256=
|
|
135
|
+
ophyd_async/tango/demo/_tango/_servers.py,sha256=putvERDyibibaTbhdWyqZB_axj2fURXqzDsZb9oSW14,2991
|
|
136
136
|
ophyd_async/tango/testing/__init__.py,sha256=SYXPAS00ny3jlUMOJKpaewO4ljPjK1_z1smj7IfsBQg,148
|
|
137
|
-
ophyd_async/tango/testing/_one_of_everything.py,sha256=
|
|
137
|
+
ophyd_async/tango/testing/_one_of_everything.py,sha256=tsxWgy2f_m9f0FH4XCVO_I8OtEquKbJQZbr4KaXMJL4,6571
|
|
138
138
|
ophyd_async/testing/__init__.py,sha256=f53HUj2hpIfrza9OlcOpHmq5wnziQNwixawAK4F1xgc,1698
|
|
139
139
|
ophyd_async/testing/__pytest_assert_rewrite.py,sha256=_SU2UfChPgEf7CFY7aYH2B7MLp-07_qYnVLyu6QtDL8,129
|
|
140
140
|
ophyd_async/testing/_assert.py,sha256=FrMgMfo-ABq_cMT-hJmoaTxhAY4TwAvGe4ae1SF1rME,7657
|
|
@@ -143,8 +143,8 @@ ophyd_async/testing/_one_of_everything.py,sha256=Di0hPoKwrDOSsx50-2UdSHM2EbIKrPG
|
|
|
143
143
|
ophyd_async/testing/_single_derived.py,sha256=5-HOTzgePcZ354NK_ssVpyIbJoJmKyjVQCxSwQXUC-4,2730
|
|
144
144
|
ophyd_async/testing/_utils.py,sha256=zClRo5ve8RGia7wQnby41W-Zprj-slOA5da1LfYnuhw,45
|
|
145
145
|
ophyd_async/testing/_wait_for_pending.py,sha256=YZAR48n-CW0GsPey3zFRzMJ4byDAr3HvMIoawjmTrHw,732
|
|
146
|
-
ophyd_async-0.10.
|
|
147
|
-
ophyd_async-0.10.
|
|
148
|
-
ophyd_async-0.10.
|
|
149
|
-
ophyd_async-0.10.
|
|
150
|
-
ophyd_async-0.10.
|
|
146
|
+
ophyd_async-0.10.1.dist-info/licenses/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
|
|
147
|
+
ophyd_async-0.10.1.dist-info/METADATA,sha256=T43wg7KEoYRyA7xllFJLv9fM7OvNqYjuA047uX2zNlw,7071
|
|
148
|
+
ophyd_async-0.10.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
149
|
+
ophyd_async-0.10.1.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
|
|
150
|
+
ophyd_async-0.10.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|