ophyd-async 0.10.0a3__py3-none-any.whl → 0.10.0a4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. ophyd_async/_version.py +2 -2
  2. ophyd_async/core/__init__.py +4 -0
  3. ophyd_async/core/_derived_signal.py +38 -10
  4. ophyd_async/core/_detector.py +4 -4
  5. ophyd_async/core/_signal.py +21 -5
  6. ophyd_async/core/_signal_backend.py +18 -8
  7. ophyd_async/core/_utils.py +31 -14
  8. ophyd_async/epics/adcore/_core_detector.py +2 -2
  9. ophyd_async/epics/adcore/_core_io.py +3 -3
  10. ophyd_async/epics/adcore/_utils.py +11 -2
  11. ophyd_async/epics/adpilatus/_pilatus.py +1 -1
  12. ophyd_async/epics/adpilatus/_pilatus_controller.py +4 -11
  13. ophyd_async/epics/core/_aioca.py +2 -2
  14. ophyd_async/epics/core/_util.py +21 -13
  15. ophyd_async/epics/eiger/_odin_io.py +12 -13
  16. ophyd_async/epics/motor.py +3 -2
  17. ophyd_async/fastcs/eiger/_eiger.py +11 -2
  18. ophyd_async/fastcs/eiger/_eiger_controller.py +7 -3
  19. ophyd_async/fastcs/eiger/_eiger_io.py +1 -0
  20. ophyd_async/sim/_motor.py +2 -2
  21. ophyd_async/tango/core/_base_device.py +2 -1
  22. ophyd_async/tango/core/_converters.py +2 -6
  23. ophyd_async/tango/core/_signal.py +8 -8
  24. ophyd_async/tango/core/_tango_transport.py +12 -12
  25. ophyd_async/tango/demo/_tango/_servers.py +0 -1
  26. ophyd_async/tango/testing/_one_of_everything.py +2 -2
  27. {ophyd_async-0.10.0a3.dist-info → ophyd_async-0.10.0a4.dist-info}/METADATA +1 -1
  28. {ophyd_async-0.10.0a3.dist-info → ophyd_async-0.10.0a4.dist-info}/RECORD +31 -31
  29. {ophyd_async-0.10.0a3.dist-info → ophyd_async-0.10.0a4.dist-info}/WHEEL +1 -1
  30. {ophyd_async-0.10.0a3.dist-info → ophyd_async-0.10.0a4.dist-info}/licenses/LICENSE +0 -0
  31. {ophyd_async-0.10.0a3.dist-info → ophyd_async-0.10.0a4.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.0a3'
21
- __version_tuple__ = version_tuple = (0, 10, 0, 'a3')
20
+ __version__ = version = '0.10.0a4'
21
+ __version_tuple__ = version_tuple = (0, 10, 0, 'a4')
@@ -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 = list(transform_cls.model_fields) + [
39
- x
40
- for x in get_type_hints(transform_cls.raw_to_derived)
41
- if x not in ["self", "return"]
42
- ]
43
- if set(expected) != set(raw_and_transform_devices):
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 {expected}, "
46
- f"got {list(raw_and_transform_devices)}"
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 = (
@@ -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
@@ -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
- self._describe, _ = await asyncio.gather(
332
- self._writer.open(self.name, value.exposures_per_event),
333
- self._controller.prepare(value),
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()
@@ -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
 
@@ -431,8 +436,8 @@ async def observe_signals_value(
431
436
  Call subscribe_value on all the signals at the start, and clear_sub on
432
437
  it at the end.
433
438
  :param timeout:
434
- If given, how long to wait for each updated value in seconds. If an
435
- update is not produced in this time then raise asyncio.TimeoutError.
439
+ If given, how long to wait for ANY updated value from shared queue in seconds.
440
+ If an update is not produced in this time then raise asyncio.TimeoutError.
436
441
  :param done_status:
437
442
  If this status is complete, stop observing and make the iterator return.
438
443
  If it raises an exception then this exception will be raised by the
@@ -454,8 +459,10 @@ async def observe_signals_value(
454
459
  q: asyncio.Queue[tuple[SignalR[SignalDatatypeT], SignalDatatypeT] | Status] = (
455
460
  asyncio.Queue()
456
461
  )
457
-
462
+ # dict to store signal subscription to remove it later
458
463
  cbs: dict[SignalR, Callback] = {}
464
+
465
+ # subscribe signal to update queue and fill cbs dict
459
466
  for signal in signals:
460
467
 
461
468
  def queue_value(value: SignalDatatypeT, signal=signal):
@@ -468,6 +475,7 @@ async def observe_signals_value(
468
475
  done_status.add_callback(q.put_nowait)
469
476
  overall_deadline = time.monotonic() + done_timeout if done_timeout else None
470
477
  try:
478
+ last_item = ()
471
479
  while True:
472
480
  if overall_deadline and time.monotonic() >= overall_deadline:
473
481
  raise asyncio.TimeoutError(
@@ -476,14 +484,22 @@ async def observe_signals_value(
476
484
  f"timeout {done_timeout}s"
477
485
  )
478
486
  iteration_timeout = _get_iteration_timeout(timeout, overall_deadline)
479
- item = await asyncio.wait_for(q.get(), iteration_timeout)
487
+ try:
488
+ item = await asyncio.wait_for(q.get(), iteration_timeout)
489
+ except asyncio.TimeoutError as exc:
490
+ raise asyncio.TimeoutError(
491
+ f"Timeout Error while waiting {iteration_timeout}s to update "
492
+ f"{[signal.source for signal in signals]}. "
493
+ f"Last observed signal and value were {last_item}"
494
+ ) from exc
480
495
  if done_status and item is done_status:
481
496
  if exc := done_status.exception():
482
497
  raise exc
483
498
  else:
484
499
  break
485
500
  else:
486
- yield cast(tuple[SignalR[SignalDatatypeT], SignalDatatypeT], item)
501
+ last_item = cast(tuple[SignalR[SignalDatatypeT], SignalDatatypeT], item)
502
+ yield last_item
487
503
  finally:
488
504
  for signal, cb in cbs.items():
489
505
  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
- | StrictEnum
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
- - A [](#StrictEnum) or [](#SubsetEnum) subclass
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 [](#StrictEnum) or [](#SubsetEnum) subclass
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=StrictEnum)
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, StrictEnum):
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, StrictEnum)
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
@@ -167,7 +177,7 @@ def _datakey_dtype_numpy(
167
177
 
168
178
 
169
179
  def _datakey_shape(value: SignalDatatype) -> list[int]:
170
- if type(value) in _primitive_dtype or isinstance(value, StrictEnum):
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)
@@ -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 Any, Generic, Literal, ParamSpec, TypeVar, get_args, get_origin
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 StrictEnumMeta(EnumMeta):
23
- def __new__(metacls, *args, **kwargs):
24
- ret = super().__new__(metacls, *args, **kwargs)
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 StrictEnum(str, Enum, metaclass=StrictEnumMeta):
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 SubsetEnum(StrictEnum, metaclass=SubsetEnumMeta):
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[StrictEnum] | None:
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) subclass
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, StrictEnum):
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 name, plugin in plugins.items():
26
- setattr(self, name, plugin)
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
- total_array: A[SignalRW[float], PvSuffix.rbv("TotalArray")]
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[SignalRW[int], PvSuffix.rbv("ProfileSizeX")]
80
- profile_size_y: A[SignalRW[int], PvSuffix.rbv("ProfileSizeY")]
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,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(StrictEnum):
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
- return ad_dtype_to_np_dtype[ad_dtype]
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(self._get_trigger_mode(trigger_info.trigger)),
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]
@@ -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: dict[str, str]):
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))
@@ -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
- ) -> dict[str, str]:
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, SubsetEnum):
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
- if set(choices) != set(pv_choices):
39
- raise TypeError(error_msg + "to be strictly equal to them.")
40
-
41
- # Take order from the pv choices
42
- supported_values = {x: x for x in pv_choices}
43
- # But override those that we specify via the datatype
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}NumCapture_RBV")
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 set_and_wait_for_other_value(
104
- self._drv.capture,
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 wait_for_value(self._drv.meta_writing, "Writing", timeout=DEFAULT_TIMEOUT)
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
 
@@ -260,10 +260,11 @@ class Motor(
260
260
  (await self.acceleration_time.get_value()) * fly_velocity * 0.5
261
261
  )
262
262
 
263
- self._fly_completed_position = end_position + run_up_distance
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 AsyncStatus, PathProvider, StandardDetector, TriggerInfo
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(path_provider, self.odin),
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
- self._arm_status = self._drv.detector.arm.trigger(timeout=DEFAULT_TIMEOUT)
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
- if self._arm_status:
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()
@@ -39,6 +39,7 @@ class EigerDetectorIO(Device):
39
39
  omega_increment: SignalRW[float]
40
40
  arm: SignalX
41
41
  disarm: SignalX
42
+ trigger: SignalX
42
43
 
43
44
 
44
45
  class EigerDriverIO(Device):
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,
@@ -2,7 +2,6 @@ import asyncio
2
2
  import time
3
3
 
4
4
  import numpy as np
5
-
6
5
  from tango import AttrWriteType, DevState, GreenMode
7
6
  from tango.server import Device, attribute, command
8
7
 
@@ -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.0a3
3
+ Version: 0.10.0a4
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
@@ -1,12 +1,12 @@
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=-EBD05g6kq94GHd6lTD2fVHvQZpXxxsDi5077QEreeE,521
4
+ ophyd_async/_version.py,sha256=nAYruEsg-pmbrsaMZ5jXkxpaKnALiE0J3VPjge9G13A,521
5
5
  ophyd_async/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- ophyd_async/core/__init__.py,sha256=3hj-8M2bRbYUjr8lgjp-DLOOtVQZQnPVLEJKhnlskQs,4446
7
- ophyd_async/core/_derived_signal.py,sha256=s8VSOkjl8QVg5RTusCOAeGM_cAAJkfjO5e0cyTJ4qwg,10482
6
+ ophyd_async/core/__init__.py,sha256=mSoldXL3hou1UifwsUcos2Q3Vy_pAaNJJWdMc0VInyg,4516
7
+ ophyd_async/core/_derived_signal.py,sha256=sSb-nRkcZzjyo2eG_z4Rawadkt0QPZTEWtcV3a_TTBM,11439
8
8
  ophyd_async/core/_derived_signal_backend.py,sha256=pci61vJkGnsMYxB54AZHyOr4rJECRpigNFtUZTM0P20,12211
9
- ophyd_async/core/_detector.py,sha256=MUkZusj7yIUKiULW2H__PQ76-Mykm1pwEcRpxuh9ho4,15232
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
@@ -17,15 +17,15 @@ ophyd_async/core/_protocol.py,sha256=wQ_snxhTprHqEjQb1HgFwBljwolMY6A8C3xgV1PXwdU
17
17
  ophyd_async/core/_providers.py,sha256=0R--Slh_E16l4AQoNtessQZpOwIn7uBowvcnzh8jYgk,7538
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=kqIvQXRP3n34-uhgTmbwm2fTIz_phJYyTT4Rm7P-gSs,26412
21
- ophyd_async/core/_signal_backend.py,sha256=T8vJi-QuMfjB_uBB6iu_aApAY0j5pAFkDn5g2oSCbmI,6826
20
+ ophyd_async/core/_signal.py,sha256=isPya2iextwRcWAV1SOf7CUwyL0PJloAUVH8rp1G2tE,27134
21
+ ophyd_async/core/_signal_backend.py,sha256=9MIzT3ArlL7BJlmWQxr4Moxc4LGQhmTclfCQfIlq4Ps,6923
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=zEA3lLeOP0N17XGUH8irxvEW8hq0slkxY4gJV3f4YzE,11382
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=6AyLQmgqzabcJ4T2yTJBP1omSxxWeya5RbR_r4zYr10,10458
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=a7S9RViLXd9XnKFWFOhcMrSbubqZoxdQt09BZr8apUQ,2617
40
- ophyd_async/epics/adcore/_core_io.py,sha256=-MEsDO736idCrAxvT4BnX-NBWLvZGHTNO22c5H09_J8,7406
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
41
  ophyd_async/epics/adcore/_core_logic.py,sha256=CjzrfC5HiwoLI1igqRKIn2SSH86T_QmAFi1-kEdyGyM,8075
42
42
  ophyd_async/epics/adcore/_core_writer.py,sha256=S58plRmbNzig-r7gubKjDVDni_nf4CASaV1OJvudbHw,8019
43
43
  ophyd_async/epics/adcore/_hdf_writer.py,sha256=ns7T9tb54ReGnyYs0fITg3Cb_0DSz4EDKlKtjBkmG3k,5572
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=wGWpvm2hi-80N-RxbKOO0-GcMf-vs7B9ZDBxpzGd4mA,3952
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=SqNjGpZde_9RVSghjx6h9g2SnoJP-EWU4ew3TMXPayc,1511
54
- ophyd_async/epics/adpilatus/_pilatus_controller.py,sha256=kpdfTPt2_gmchrMP8S3vwLfr8ixbRRMYkbWeNRRFktU,2852
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=HcGYyYUQZmhdUgovf0M9xxL5Ly56ELJDAoRvyRCPq8U,13003
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=DcfX4VUTeqLrMhVlt1Q7cvBTuYzHePAA-jdR-OWcxMM,2056
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=g3mknGWSzMYzCggHCYR_nWf72VBlUHMZK5WobNzkZ1U,5568
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=tUjTIhDvnnnNQwMjlfmCTxc6Gbg2xzo1y_wJxCVUSG4,1120
93
- ophyd_async/fastcs/eiger/_eiger_controller.py,sha256=oeX16Vt4CRtrL87ntGbVtvFFtmCFNCeGOZpfjYDoXX0,2131
94
- ophyd_async/fastcs/eiger/_eiger_io.py,sha256=8cyhYGRSFdW2zTfUZS82bMucW0HVHZysGkG1pIdZLEc,1195
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=-YMANsq4ZvI7nZolrYaTYe44kMoIcvPKCpBbCeC7pxw,10167
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=mXQnhCzFAdSrynelubhz7iLx_akINJ6s1M08mikFFME,5032
125
- ophyd_async/tango/core/_converters.py,sha256=auBUdvbsEU8XXNDzX-fiBap0SvvID3ZOAUEP_wjHgBU,2201
126
- ophyd_async/tango/core/_signal.py,sha256=XZ38hPnThZ-__RDgwH8UfUk78NLcrZ8zVCH8sklr_3U,5798
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=FlvadKRW9H_ggcQ3MTiAGHE4HDTKu_rAk5WODMxwk_E,31903
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=nyuKgufKKqtngDeaufbUcHO6byV5MEElf9442ivtUk0,2992
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=rPAgOoNGvaHMVhB79Gz70NI9tWk3QV5iKg5wAlAtqxU,6571
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.0a3.dist-info/licenses/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
147
- ophyd_async-0.10.0a3.dist-info/METADATA,sha256=XnOI_0W1kd8zTWRvAnf0XQr96aAKyxuH6CiSUAqL2TE,7075
148
- ophyd_async-0.10.0a3.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
149
- ophyd_async-0.10.0a3.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
150
- ophyd_async-0.10.0a3.dist-info/RECORD,,
146
+ ophyd_async-0.10.0a4.dist-info/licenses/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
147
+ ophyd_async-0.10.0a4.dist-info/METADATA,sha256=xs88nrJXsGw254wkPyFNyvQiajcqFFARvmS59_szwmo,7075
148
+ ophyd_async-0.10.0a4.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
149
+ ophyd_async-0.10.0a4.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
150
+ ophyd_async-0.10.0a4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.7.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5