ophyd-async 0.9.0a2__py3-none-any.whl → 0.10.0a2__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 (151) hide show
  1. ophyd_async/__init__.py +5 -8
  2. ophyd_async/_docs_parser.py +12 -0
  3. ophyd_async/_version.py +9 -4
  4. ophyd_async/core/__init__.py +97 -62
  5. ophyd_async/core/_derived_signal.py +271 -0
  6. ophyd_async/core/_derived_signal_backend.py +300 -0
  7. ophyd_async/core/_detector.py +106 -125
  8. ophyd_async/core/_device.py +69 -63
  9. ophyd_async/core/_device_filler.py +65 -1
  10. ophyd_async/core/_flyer.py +14 -5
  11. ophyd_async/core/_hdf_dataset.py +29 -22
  12. ophyd_async/core/_log.py +14 -23
  13. ophyd_async/core/_mock_signal_backend.py +11 -3
  14. ophyd_async/core/_protocol.py +65 -45
  15. ophyd_async/core/_providers.py +28 -9
  16. ophyd_async/core/_readable.py +44 -35
  17. ophyd_async/core/_settings.py +36 -27
  18. ophyd_async/core/_signal.py +262 -170
  19. ophyd_async/core/_signal_backend.py +56 -13
  20. ophyd_async/core/_soft_signal_backend.py +16 -11
  21. ophyd_async/core/_status.py +72 -24
  22. ophyd_async/core/_table.py +41 -11
  23. ophyd_async/core/_utils.py +96 -49
  24. ophyd_async/core/_yaml_settings.py +2 -0
  25. ophyd_async/epics/__init__.py +1 -0
  26. ophyd_async/epics/adandor/_andor.py +2 -2
  27. ophyd_async/epics/adandor/_andor_controller.py +4 -2
  28. ophyd_async/epics/adandor/_andor_io.py +2 -4
  29. ophyd_async/epics/adaravis/__init__.py +5 -0
  30. ophyd_async/epics/adaravis/_aravis.py +4 -8
  31. ophyd_async/epics/adaravis/_aravis_controller.py +20 -43
  32. ophyd_async/epics/adaravis/_aravis_io.py +13 -28
  33. ophyd_async/epics/adcore/__init__.py +23 -8
  34. ophyd_async/epics/adcore/_core_detector.py +42 -2
  35. ophyd_async/epics/adcore/_core_io.py +124 -99
  36. ophyd_async/epics/adcore/_core_logic.py +106 -27
  37. ophyd_async/epics/adcore/_core_writer.py +12 -8
  38. ophyd_async/epics/adcore/_hdf_writer.py +21 -38
  39. ophyd_async/epics/adcore/_single_trigger.py +2 -2
  40. ophyd_async/epics/adcore/_utils.py +2 -2
  41. ophyd_async/epics/adkinetix/__init__.py +2 -1
  42. ophyd_async/epics/adkinetix/_kinetix.py +3 -3
  43. ophyd_async/epics/adkinetix/_kinetix_controller.py +4 -2
  44. ophyd_async/epics/adkinetix/_kinetix_io.py +12 -13
  45. ophyd_async/epics/adpilatus/__init__.py +5 -0
  46. ophyd_async/epics/adpilatus/_pilatus.py +1 -1
  47. ophyd_async/epics/adpilatus/_pilatus_controller.py +5 -24
  48. ophyd_async/epics/adpilatus/_pilatus_io.py +11 -9
  49. ophyd_async/epics/adsimdetector/__init__.py +8 -1
  50. ophyd_async/epics/adsimdetector/_sim.py +4 -14
  51. ophyd_async/epics/adsimdetector/_sim_controller.py +17 -0
  52. ophyd_async/epics/adsimdetector/_sim_io.py +10 -0
  53. ophyd_async/epics/advimba/__init__.py +10 -1
  54. ophyd_async/epics/advimba/_vimba.py +3 -2
  55. ophyd_async/epics/advimba/_vimba_controller.py +4 -2
  56. ophyd_async/epics/advimba/_vimba_io.py +23 -28
  57. ophyd_async/epics/core/_aioca.py +35 -16
  58. ophyd_async/epics/core/_epics_connector.py +4 -0
  59. ophyd_async/epics/core/_epics_device.py +2 -0
  60. ophyd_async/epics/core/_p4p.py +10 -2
  61. ophyd_async/epics/core/_pvi_connector.py +65 -8
  62. ophyd_async/epics/core/_signal.py +51 -51
  63. ophyd_async/epics/core/_util.py +4 -4
  64. ophyd_async/epics/demo/__init__.py +16 -0
  65. ophyd_async/epics/demo/__main__.py +31 -0
  66. ophyd_async/epics/demo/_ioc.py +32 -0
  67. ophyd_async/epics/demo/_motor.py +82 -0
  68. ophyd_async/epics/demo/_point_detector.py +42 -0
  69. ophyd_async/epics/demo/_point_detector_channel.py +22 -0
  70. ophyd_async/epics/demo/_stage.py +15 -0
  71. ophyd_async/epics/{sim/mover.db → demo/motor.db} +2 -1
  72. ophyd_async/epics/demo/point_detector.db +59 -0
  73. ophyd_async/epics/demo/point_detector_channel.db +21 -0
  74. ophyd_async/epics/eiger/_eiger.py +1 -3
  75. ophyd_async/epics/eiger/_eiger_controller.py +11 -4
  76. ophyd_async/epics/eiger/_eiger_io.py +2 -0
  77. ophyd_async/epics/eiger/_odin_io.py +1 -2
  78. ophyd_async/epics/motor.py +65 -28
  79. ophyd_async/epics/signal.py +4 -1
  80. ophyd_async/epics/testing/_example_ioc.py +21 -9
  81. ophyd_async/epics/testing/_utils.py +3 -0
  82. ophyd_async/epics/testing/test_records.db +8 -0
  83. ophyd_async/epics/testing/test_records_pva.db +17 -16
  84. ophyd_async/fastcs/__init__.py +1 -0
  85. ophyd_async/fastcs/core.py +6 -0
  86. ophyd_async/fastcs/odin/__init__.py +1 -0
  87. ophyd_async/fastcs/panda/__init__.py +8 -6
  88. ophyd_async/fastcs/panda/_block.py +29 -9
  89. ophyd_async/fastcs/panda/_control.py +5 -0
  90. ophyd_async/fastcs/panda/_hdf_panda.py +2 -0
  91. ophyd_async/fastcs/panda/_table.py +9 -6
  92. ophyd_async/fastcs/panda/_trigger.py +23 -9
  93. ophyd_async/fastcs/panda/_writer.py +27 -30
  94. ophyd_async/plan_stubs/__init__.py +2 -0
  95. ophyd_async/plan_stubs/_ensure_connected.py +1 -0
  96. ophyd_async/plan_stubs/_fly.py +2 -4
  97. ophyd_async/plan_stubs/_nd_attributes.py +2 -0
  98. ophyd_async/plan_stubs/_panda.py +1 -0
  99. ophyd_async/plan_stubs/_settings.py +43 -16
  100. ophyd_async/plan_stubs/_utils.py +3 -0
  101. ophyd_async/plan_stubs/_wait_for_awaitable.py +1 -1
  102. ophyd_async/sim/__init__.py +24 -14
  103. ophyd_async/sim/__main__.py +43 -0
  104. ophyd_async/sim/_blob_detector.py +33 -0
  105. ophyd_async/sim/_blob_detector_controller.py +48 -0
  106. ophyd_async/sim/_blob_detector_writer.py +105 -0
  107. ophyd_async/sim/_mirror_horizontal.py +46 -0
  108. ophyd_async/sim/_mirror_vertical.py +74 -0
  109. ophyd_async/sim/_motor.py +233 -0
  110. ophyd_async/sim/_pattern_generator.py +124 -0
  111. ophyd_async/sim/_point_detector.py +86 -0
  112. ophyd_async/sim/_stage.py +19 -0
  113. ophyd_async/tango/__init__.py +1 -0
  114. ophyd_async/tango/core/__init__.py +6 -1
  115. ophyd_async/tango/core/_base_device.py +41 -33
  116. ophyd_async/tango/core/_converters.py +81 -0
  117. ophyd_async/tango/core/_signal.py +18 -32
  118. ophyd_async/tango/core/_tango_readable.py +2 -19
  119. ophyd_async/tango/core/_tango_transport.py +136 -60
  120. ophyd_async/tango/core/_utils.py +47 -0
  121. ophyd_async/tango/{sim → demo}/_counter.py +2 -0
  122. ophyd_async/tango/{sim → demo}/_detector.py +2 -0
  123. ophyd_async/tango/{sim → demo}/_mover.py +5 -4
  124. ophyd_async/tango/{sim → demo}/_tango/_servers.py +4 -0
  125. ophyd_async/tango/testing/__init__.py +6 -0
  126. ophyd_async/tango/testing/_one_of_everything.py +200 -0
  127. ophyd_async/testing/__init__.py +29 -7
  128. ophyd_async/testing/_assert.py +145 -83
  129. ophyd_async/testing/_mock_signal_utils.py +56 -70
  130. ophyd_async/testing/_one_of_everything.py +41 -21
  131. ophyd_async/testing/_single_derived.py +89 -0
  132. ophyd_async/testing/_utils.py +3 -0
  133. {ophyd_async-0.9.0a2.dist-info → ophyd_async-0.10.0a2.dist-info}/METADATA +25 -26
  134. ophyd_async-0.10.0a2.dist-info/RECORD +149 -0
  135. {ophyd_async-0.9.0a2.dist-info → ophyd_async-0.10.0a2.dist-info}/WHEEL +1 -1
  136. ophyd_async/epics/sim/__init__.py +0 -54
  137. ophyd_async/epics/sim/_ioc.py +0 -29
  138. ophyd_async/epics/sim/_mover.py +0 -101
  139. ophyd_async/epics/sim/_sensor.py +0 -37
  140. ophyd_async/epics/sim/sensor.db +0 -19
  141. ophyd_async/sim/_pattern_detector/__init__.py +0 -13
  142. ophyd_async/sim/_pattern_detector/_pattern_detector.py +0 -42
  143. ophyd_async/sim/_pattern_detector/_pattern_detector_controller.py +0 -69
  144. ophyd_async/sim/_pattern_detector/_pattern_detector_writer.py +0 -41
  145. ophyd_async/sim/_pattern_detector/_pattern_generator.py +0 -214
  146. ophyd_async/sim/_sim_motor.py +0 -107
  147. ophyd_async-0.9.0a2.dist-info/RECORD +0 -129
  148. /ophyd_async/tango/{sim → demo}/__init__.py +0 -0
  149. /ophyd_async/tango/{sim → demo}/_tango/__init__.py +0 -0
  150. {ophyd_async-0.9.0a2.dist-info → ophyd_async-0.10.0a2.dist-info/licenses}/LICENSE +0 -0
  151. {ophyd_async-0.9.0a2.dist-info → ophyd_async-0.10.0a2.dist-info}/top_level.txt +0 -0
@@ -3,17 +3,15 @@ from collections.abc import AsyncIterator
3
3
  from pathlib import Path
4
4
  from xml.etree import ElementTree as ET
5
5
 
6
- from bluesky.protocols import Hints, StreamAsset
6
+ from bluesky.protocols import StreamAsset
7
7
  from event_model import DataKey
8
8
 
9
9
  from ophyd_async.core import (
10
- DEFAULT_TIMEOUT,
11
10
  DatasetDescriber,
12
- HDFDataset,
13
- HDFFile,
11
+ HDFDatasetDescription,
12
+ HDFDocumentComposer,
14
13
  NameProvider,
15
14
  PathProvider,
16
- wait_for_value,
17
15
  )
18
16
 
19
17
  from ._core_io import NDFileHDFIO, NDPluginBaseIO
@@ -25,6 +23,8 @@ from ._utils import (
25
23
 
26
24
 
27
25
  class ADHDFWriter(ADWriter[NDFileHDFIO]):
26
+ """Allow `NDFileHDFIO` to be used within `StandardDetector`."""
27
+
28
28
  default_suffix: str = "HDF1:"
29
29
 
30
30
  def __init__(
@@ -44,12 +44,12 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
44
44
  file_extension=".h5",
45
45
  mimetype="application/x-hdf5",
46
46
  )
47
- self._datasets: list[HDFDataset] = []
48
- self._file: HDFFile | None = None
49
- self._include_file_number = False
47
+ self._datasets: list[HDFDatasetDescription] = []
48
+ self._composer: HDFDocumentComposer | None = None
49
+ self._filename_template = "%s%s"
50
50
 
51
51
  async def open(self, multiplier: int = 1) -> dict[str, DataKey]:
52
- self._file = None
52
+ self._composer = None
53
53
 
54
54
  # Setting HDF writer specific signals
55
55
 
@@ -63,11 +63,6 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
63
63
  self.fileio.xml_file_name.set(""),
64
64
  )
65
65
 
66
- # By default, don't add file number to filename
67
- self._filename_template = "%s%s"
68
- if self._include_file_number:
69
- self._filename_template += "_%6.6d"
70
-
71
66
  # Set common AD file plugin params, begin capturing
72
67
  await self.begin_capture()
73
68
 
@@ -82,7 +77,7 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
82
77
 
83
78
  # Add the main data
84
79
  self._datasets = [
85
- HDFDataset(
80
+ HDFDatasetDescription(
86
81
  data_key=name,
87
82
  dataset="/entry/data/data",
88
83
  shape=detector_shape,
@@ -99,7 +94,7 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
99
94
  if "<Attributes>" in maybe_xml:
100
95
  root = ET.fromstring(maybe_xml)
101
96
  for child in root:
102
- datakey = child.attrib["name"]
97
+ data_key = child.attrib["name"]
103
98
  if child.attrib.get("type", "EPICS_PV") == "EPICS_PV":
104
99
  np_datatype = convert_pv_dtype_to_np(
105
100
  child.attrib.get("dbrtype", "DBR_NATIVE")
@@ -109,15 +104,15 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
109
104
  child.attrib.get("datatype", "INT")
110
105
  )
111
106
  self._datasets.append(
112
- HDFDataset(
113
- datakey,
114
- f"/entry/instrument/NDAttributes/{datakey}",
115
- (),
116
- np_datatype,
117
- multiplier,
107
+ HDFDatasetDescription(
108
+ data_key=data_key,
109
+ dataset=f"/entry/instrument/NDAttributes/{data_key}",
110
+ shape=(),
111
+ dtype_numpy=np_datatype,
118
112
  # NDAttributes appear to always be configured with
119
113
  # this chunk size
120
114
  chunk_shape=(16384,),
115
+ multiplier=multiplier,
121
116
  )
122
117
  )
123
118
 
@@ -139,9 +134,9 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
139
134
  # TODO: fail if we get dropped frames
140
135
  await self.fileio.flush_now.set(True)
141
136
  if indices_written:
142
- if not self._file:
137
+ if not self._composer:
143
138
  path = Path(await self.fileio.full_file_name.get_value())
144
- self._file = HDFFile(
139
+ self._composer = HDFDocumentComposer(
145
140
  # See https://github.com/bluesky/ophyd-async/issues/122
146
141
  path,
147
142
  self._datasets,
@@ -150,19 +145,7 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
150
145
  # stream datum says "here are N frames in that stream resource",
151
146
  # you get one stream resource and many stream datums per scan
152
147
 
153
- for doc in self._file.stream_resources():
148
+ for doc in self._composer.stream_resources():
154
149
  yield "stream_resource", doc
155
- for doc in self._file.stream_data(indices_written):
150
+ for doc in self._composer.stream_data(indices_written):
156
151
  yield "stream_datum", doc
157
-
158
- async def close(self):
159
- # Already done a caput callback in _capture_status, so can't do one here
160
- await self.fileio.capture.set(False, wait=False)
161
- await wait_for_value(self.fileio.capture, False, DEFAULT_TIMEOUT)
162
- if self._capture_status:
163
- # We kicked off an open, so wait for it to return
164
- await self._capture_status
165
-
166
- @property
167
- def hints(self) -> Hints:
168
- return {"fields": [self._name_provider()]}
@@ -7,7 +7,7 @@ from ophyd_async.core import AsyncStatus, SignalR, StandardReadable
7
7
  from ophyd_async.core import StandardReadableFormat as Format
8
8
 
9
9
  from ._core_io import ADBaseIO, NDPluginBaseIO
10
- from ._utils import ImageMode
10
+ from ._utils import ADImageMode
11
11
 
12
12
 
13
13
  class SingleTriggerDetector(StandardReadable, Triggerable):
@@ -35,7 +35,7 @@ class SingleTriggerDetector(StandardReadable, Triggerable):
35
35
  @AsyncStatus.wrap
36
36
  async def stage(self) -> None:
37
37
  await asyncio.gather(
38
- self.drv.image_mode.set(ImageMode.SINGLE),
38
+ self.drv.image_mode.set(ADImageMode.SINGLE),
39
39
  self.drv.wait_for_plugins.set(True),
40
40
  )
41
41
  await super().stage()
@@ -79,13 +79,13 @@ def convert_param_dtype_to_np(datatype: str) -> str:
79
79
  return np_datatype
80
80
 
81
81
 
82
- class FileWriteMode(StrictEnum):
82
+ class ADFileWriteMode(StrictEnum):
83
83
  SINGLE = "Single"
84
84
  CAPTURE = "Capture"
85
85
  STREAM = "Stream"
86
86
 
87
87
 
88
- class ImageMode(SubsetEnum):
88
+ class ADImageMode(SubsetEnum):
89
89
  SINGLE = "Single"
90
90
  MULTIPLE = "Multiple"
91
91
  CONTINUOUS = "Continuous"
@@ -1,10 +1,11 @@
1
1
  from ._kinetix import KinetixDetector
2
2
  from ._kinetix_controller import KinetixController
3
- from ._kinetix_io import KinetixDriverIO, KinetixTriggerMode
3
+ from ._kinetix_io import KinetixDriverIO, KinetixReadoutMode, KinetixTriggerMode
4
4
 
5
5
  __all__ = [
6
6
  "KinetixDetector",
7
7
  "KinetixController",
8
8
  "KinetixDriverIO",
9
9
  "KinetixTriggerMode",
10
+ "KinetixReadoutMode",
10
11
  ]
@@ -13,9 +13,9 @@ from ._kinetix_io import KinetixDriverIO
13
13
 
14
14
 
15
15
  class KinetixDetector(AreaDetector[KinetixController]):
16
- """
17
- Ophyd-async implementation of an ADKinetix Detector.
18
- https://github.com/NSLS-II/ADKinetix
16
+ """Ophyd-async implementation of an ADKinetix Detector.
17
+
18
+ https://github.com/NSLS-II/ADKinetix.
19
19
  """
20
20
 
21
21
  def __init__(
@@ -17,10 +17,12 @@ KINETIX_TRIGGER_MODE_MAP = {
17
17
 
18
18
 
19
19
  class KinetixController(adcore.ADBaseController[KinetixDriverIO]):
20
+ """Controller for adkinetix detector."""
21
+
20
22
  def __init__(
21
23
  self,
22
24
  driver: KinetixDriverIO,
23
- good_states: frozenset[adcore.DetectorState] = adcore.DEFAULT_GOOD_STATES,
25
+ good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
24
26
  ) -> None:
25
27
  super().__init__(driver, good_states=good_states)
26
28
 
@@ -33,7 +35,7 @@ class KinetixController(adcore.ADBaseController[KinetixDriverIO]):
33
35
  KINETIX_TRIGGER_MODE_MAP[trigger_info.trigger]
34
36
  ),
35
37
  self.driver.num_images.set(trigger_info.total_number_of_triggers),
36
- self.driver.image_mode.set(adcore.ImageMode.MULTIPLE),
38
+ self.driver.image_mode.set(adcore.ADImageMode.MULTIPLE),
37
39
  )
38
40
  if trigger_info.livetime is not None and trigger_info.trigger not in [
39
41
  DetectorTrigger.VARIABLE_GATE,
@@ -1,15 +1,21 @@
1
- from ophyd_async.core import StrictEnum
1
+ from typing import Annotated as A
2
+
3
+ from ophyd_async.core import SignalRW, StrictEnum
2
4
  from ophyd_async.epics import adcore
3
- from ophyd_async.epics.core import epics_signal_rw_rbv
5
+ from ophyd_async.epics.core import PvSuffix
4
6
 
5
7
 
6
8
  class KinetixTriggerMode(StrictEnum):
9
+ """Trigger mode for ADKinetix detector."""
10
+
7
11
  INTERNAL = "Internal"
8
12
  EDGE = "Rising Edge"
9
13
  GATE = "Exp. Gate"
10
14
 
11
15
 
12
16
  class KinetixReadoutMode(StrictEnum):
17
+ """Readout mode for ADKinetix detector."""
18
+
13
19
  SENSITIVITY = 1
14
20
  SPEED = 2
15
21
  DYNAMIC_RANGE = 3
@@ -17,14 +23,7 @@ class KinetixReadoutMode(StrictEnum):
17
23
 
18
24
 
19
25
  class KinetixDriverIO(adcore.ADBaseIO):
20
- """This mirrors the interface provided by ADKinetix/db/ADKinetix.template."""
21
-
22
- def __init__(self, prefix: str, name: str = "") -> None:
23
- # self.pixel_format = epics_signal_rw_rbv(PixelFormat, prefix + "PixelFormat")
24
- self.trigger_mode = epics_signal_rw_rbv(
25
- KinetixTriggerMode, prefix + "TriggerMode"
26
- )
27
- self.readout_port_idx = epics_signal_rw_rbv(
28
- KinetixReadoutMode, prefix + "ReadoutPortIdx"
29
- )
30
- super().__init__(prefix, name)
26
+ """Mirrors the interface provided by ADKinetix/db/ADKinetix.template."""
27
+
28
+ trigger_mode: A[SignalRW[KinetixTriggerMode], PvSuffix("TriggerMode")]
29
+ readout_port_idx: A[SignalRW[KinetixReadoutMode], PvSuffix("ReadoutPortIdx")]
@@ -1,3 +1,8 @@
1
+ """Support for the ADPilatus areaDetector driver.
2
+
3
+ https://github.com/areaDetector/ADPilatus
4
+ """
5
+
1
6
  from ._pilatus import PilatusDetector
2
7
  from ._pilatus_controller import PilatusController, PilatusReadoutTime
3
8
  from ._pilatus_io import PilatusDriverIO, PilatusTriggerMode
@@ -12,7 +12,7 @@ from ._pilatus_io import PilatusDriverIO
12
12
 
13
13
 
14
14
  class PilatusDetector(AreaDetector[PilatusController]):
15
- """A Pilatus StandardDetector writing HDF files"""
15
+ """A Pilatus StandardDetector writing HDF files."""
16
16
 
17
17
  def __init__(
18
18
  self,
@@ -1,6 +1,5 @@
1
1
  import asyncio
2
2
  from enum import Enum
3
- from typing import TypeVar, get_args
4
3
 
5
4
  from ophyd_async.core import (
6
5
  DEFAULT_TIMEOUT,
@@ -13,12 +12,8 @@ from ophyd_async.epics import adcore
13
12
  from ._pilatus_io import PilatusDriverIO, PilatusTriggerMode
14
13
 
15
14
 
16
- #: Cite: https://media.dectris.com/User_Manual-PILATUS2-V1_4.pdf
17
- #: The required minimum time difference between ExpPeriod and ExpTime
18
- #: (readout time) is 2.28 ms
19
- #: We provide an option to override for newer Pilatus models
20
15
  class PilatusReadoutTime(float, Enum):
21
- """Pilatus readout time per model in ms"""
16
+ """Pilatus readout time per model in ms."""
22
17
 
23
18
  # Cite: https://media.dectris.com/User_Manual-PILATUS2-V1_4.pdf
24
19
  PILATUS2 = 2.28e-3
@@ -27,10 +22,9 @@ class PilatusReadoutTime(float, Enum):
27
22
  PILATUS3 = 0.95e-3
28
23
 
29
24
 
30
- PilatusControllerT = TypeVar("PilatusControllerT", bound="PilatusController")
31
-
32
-
33
25
  class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
26
+ """`DetectorController` for a `PilatusDriverIO`."""
27
+
34
28
  _supported_trigger_types = {
35
29
  DetectorTrigger.INTERNAL: PilatusTriggerMode.INTERNAL,
36
30
  DetectorTrigger.CONSTANT_GATE: PilatusTriggerMode.EXT_ENABLE,
@@ -40,25 +34,12 @@ class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
40
34
  def __init__(
41
35
  self,
42
36
  driver: PilatusDriverIO,
43
- good_states: frozenset[adcore.DetectorState] = adcore.DEFAULT_GOOD_STATES,
37
+ good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
44
38
  readout_time: float = PilatusReadoutTime.PILATUS3,
45
39
  ) -> None:
46
40
  super().__init__(driver, good_states=good_states)
47
41
  self._readout_time = readout_time
48
42
 
49
- @classmethod
50
- def controller_and_drv(
51
- cls: type[PilatusControllerT],
52
- prefix: str,
53
- good_states: frozenset[adcore.DetectorState] = adcore.DEFAULT_GOOD_STATES,
54
- name: str = "",
55
- readout_time: float = PilatusReadoutTime.PILATUS3,
56
- ) -> tuple[PilatusControllerT, PilatusDriverIO]:
57
- driver_cls = get_args(cls.__orig_bases__[0])[0] # type: ignore
58
- driver = driver_cls(prefix, name=name)
59
- controller = cls(driver, good_states=good_states, readout_time=readout_time)
60
- return controller, driver
61
-
62
43
  def get_deadtime(self, exposure: float | None) -> float:
63
44
  return self._readout_time
64
45
 
@@ -74,7 +55,7 @@ class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
74
55
  if trigger_info.total_number_of_triggers == 0
75
56
  else trigger_info.total_number_of_triggers
76
57
  ),
77
- self.driver.image_mode.set(adcore.ImageMode.MULTIPLE),
58
+ self.driver.image_mode.set(adcore.ADImageMode.MULTIPLE),
78
59
  )
79
60
 
80
61
  async def arm(self):
@@ -1,9 +1,13 @@
1
- from ophyd_async.core import StrictEnum
1
+ from typing import Annotated as A
2
+
3
+ from ophyd_async.core import SignalR, SignalRW, StrictEnum
2
4
  from ophyd_async.epics import adcore
3
- from ophyd_async.epics.core import epics_signal_r, epics_signal_rw_rbv
5
+ from ophyd_async.epics.core import PvSuffix
4
6
 
5
7
 
6
8
  class PilatusTriggerMode(StrictEnum):
9
+ """Trigger modes for ADPilatus detector."""
10
+
7
11
  INTERNAL = "Internal"
8
12
  EXT_ENABLE = "Ext. Enable"
9
13
  EXT_TRIGGER = "Ext. Trigger"
@@ -12,11 +16,9 @@ class PilatusTriggerMode(StrictEnum):
12
16
 
13
17
 
14
18
  class PilatusDriverIO(adcore.ADBaseIO):
15
- """This mirrors the interface provided by ADPilatus/db/pilatus.template."""
19
+ """Driver for the Pilatus pixel array detectors."""
16
20
 
17
- def __init__(self, prefix: str, name: str = "") -> None:
18
- self.trigger_mode = epics_signal_rw_rbv(
19
- PilatusTriggerMode, prefix + "TriggerMode"
20
- )
21
- self.armed = epics_signal_r(bool, prefix + "Armed")
22
- super().__init__(prefix, name)
21
+ """This mirrors the interface provided by ADPilatus/db/pilatus.template."""
22
+ """See HTML docs at https://areadetector.github.io/areaDetector/ADPilatus/pilatusDoc.html"""
23
+ trigger_mode: A[SignalRW[PilatusTriggerMode], PvSuffix.rbv("TriggerMode")]
24
+ armed: A[SignalR[bool], PvSuffix.rbv("Armed_RBV")]
@@ -1,4 +1,11 @@
1
- from ._sim import SimController, SimDetector, SimDriverIO
1
+ """Support for the ADAravis areaDetector driver.
2
+
3
+ https://github.com/areaDetector/ADSimDetector
4
+ """
5
+
6
+ from ._sim import SimDetector
7
+ from ._sim_controller import SimController
8
+ from ._sim_io import SimDriverIO
2
9
 
3
10
  __all__ = [
4
11
  "SimDriverIO",
@@ -3,23 +3,13 @@ from collections.abc import Sequence
3
3
  from ophyd_async.core import PathProvider, SignalR
4
4
  from ophyd_async.epics import adcore
5
5
 
6
-
7
- class SimDriverIO(adcore.ADBaseIO): ...
8
-
9
-
10
- class SimController(adcore.ADBaseController[SimDriverIO]):
11
- def __init__(
12
- self,
13
- driver: SimDriverIO,
14
- good_states: frozenset[adcore.DetectorState] = adcore.DEFAULT_GOOD_STATES,
15
- ) -> None:
16
- super().__init__(driver, good_states=good_states)
17
-
18
- def get_deadtime(self, exposure: float | None) -> float:
19
- return 0.001
6
+ from ._sim_controller import SimController
7
+ from ._sim_io import SimDriverIO
20
8
 
21
9
 
22
10
  class SimDetector(adcore.AreaDetector[SimController]):
11
+ """Detector for simulated Areadetector."""
12
+
23
13
  def __init__(
24
14
  self,
25
15
  prefix: str,
@@ -0,0 +1,17 @@
1
+ from ophyd_async.epics import adcore
2
+
3
+ from ._sim_io import SimDriverIO
4
+
5
+
6
+ class SimController(adcore.ADBaseController[SimDriverIO]):
7
+ """Controller for simulated Areadetector."""
8
+
9
+ def __init__(
10
+ self,
11
+ driver: SimDriverIO,
12
+ good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
13
+ ) -> None:
14
+ super().__init__(driver, good_states=good_states)
15
+
16
+ def get_deadtime(self, exposure: float | None) -> float:
17
+ return 0.001
@@ -0,0 +1,10 @@
1
+ from ophyd_async.epics import adcore
2
+
3
+
4
+ class SimDriverIO(adcore.ADBaseIO):
5
+ """Base class for driving simulated Areadetector IO.
6
+
7
+ This mirrors the interface provided by ADSimDetector/db/simDetector.template.
8
+ """
9
+
10
+ pass
@@ -1,6 +1,13 @@
1
1
  from ._vimba import VimbaDetector
2
2
  from ._vimba_controller import VimbaController
3
- from ._vimba_io import VimbaDriverIO, VimbaExposeOutMode, VimbaOnOff, VimbaTriggerSource
3
+ from ._vimba_io import (
4
+ VimbaConvertFormat,
5
+ VimbaDriverIO,
6
+ VimbaExposeOutMode,
7
+ VimbaOnOff,
8
+ VimbaOverlap,
9
+ VimbaTriggerSource,
10
+ )
4
11
 
5
12
  __all__ = [
6
13
  "VimbaDetector",
@@ -9,4 +16,6 @@ __all__ = [
9
16
  "VimbaExposeOutMode",
10
17
  "VimbaOnOff",
11
18
  "VimbaTriggerSource",
19
+ "VimbaOverlap",
20
+ "VimbaConvertFormat",
12
21
  ]
@@ -8,8 +8,9 @@ from ._vimba_io import VimbaDriverIO
8
8
 
9
9
 
10
10
  class VimbaDetector(adcore.AreaDetector[VimbaController]):
11
- """
12
- Ophyd-async implementation of an ADVimba Detector.
11
+ """Ophyd-async implementation of an ADVimba Detector.
12
+
13
+ https://github.com/areaDetector/ADVimba
13
14
  """
14
15
 
15
16
  def __init__(
@@ -24,10 +24,12 @@ EXPOSE_OUT_MODE = {
24
24
 
25
25
 
26
26
  class VimbaController(adcore.ADBaseController[VimbaDriverIO]):
27
+ """Controller for the Vimba detector."""
28
+
27
29
  def __init__(
28
30
  self,
29
31
  driver: VimbaDriverIO,
30
- good_states: frozenset[adcore.DetectorState] = adcore.DEFAULT_GOOD_STATES,
32
+ good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
31
33
  ) -> None:
32
34
  super().__init__(driver, good_states=good_states)
33
35
 
@@ -39,7 +41,7 @@ class VimbaController(adcore.ADBaseController[VimbaDriverIO]):
39
41
  self.driver.trigger_mode.set(TRIGGER_MODE[trigger_info.trigger]),
40
42
  self.driver.exposure_mode.set(EXPOSE_OUT_MODE[trigger_info.trigger]),
41
43
  self.driver.num_images.set(trigger_info.total_number_of_triggers),
42
- self.driver.image_mode.set(adcore.ImageMode.MULTIPLE),
44
+ self.driver.image_mode.set(adcore.ADImageMode.MULTIPLE),
43
45
  )
44
46
  if trigger_info.livetime is not None and trigger_info.trigger not in [
45
47
  DetectorTrigger.VARIABLE_GATE,
@@ -1,17 +1,13 @@
1
- from ophyd_async.core import StrictEnum
2
- from ophyd_async.epics import adcore
3
- from ophyd_async.epics.core import epics_signal_rw_rbv
4
-
1
+ from typing import Annotated as A
5
2
 
6
- class VimbaPixelFormat(StrictEnum):
7
- INTERNAL = "Mono8"
8
- EXT_ENABLE = "Mono12"
9
- EXT_TRIGGER = "Ext. Trigger"
10
- MULT_TRIGGER = "Mult. Trigger"
11
- ALIGNMENT = "Alignment"
3
+ from ophyd_async.core import SignalRW, StrictEnum
4
+ from ophyd_async.epics import adcore
5
+ from ophyd_async.epics.core import PvSuffix
12
6
 
13
7
 
14
8
  class VimbaConvertFormat(StrictEnum):
9
+ """Convert pixel format for the Vimba detector."""
10
+
15
11
  NONE = "None"
16
12
  MONO8 = "Mono8"
17
13
  MONO16 = "Mono16"
@@ -20,6 +16,8 @@ class VimbaConvertFormat(StrictEnum):
20
16
 
21
17
 
22
18
  class VimbaTriggerSource(StrictEnum):
19
+ """Mode for the source of triggers on the Vimbda."""
20
+
23
21
  FREERUN = "Freerun"
24
22
  LINE1 = "Line1"
25
23
  LINE2 = "Line2"
@@ -30,36 +28,33 @@ class VimbaTriggerSource(StrictEnum):
30
28
 
31
29
 
32
30
  class VimbaOverlap(StrictEnum):
31
+ """Overlap modes for the Vimba detector."""
32
+
33
33
  OFF = "Off"
34
34
  PREV_FRAME = "PreviousFrame"
35
35
 
36
36
 
37
37
  class VimbaOnOff(StrictEnum):
38
+ """On/Off modes on the Vimba detector."""
39
+
38
40
  ON = "On"
39
41
  OFF = "Off"
40
42
 
41
43
 
42
44
  class VimbaExposeOutMode(StrictEnum):
45
+ """Exposure control modes for Vimba detectors."""
46
+
43
47
  TIMED = "Timed" # Use ExposureTime PV
44
48
  TRIGGER_WIDTH = "TriggerWidth" # Expose for length of high signal
45
49
 
46
50
 
47
51
  class VimbaDriverIO(adcore.ADBaseIO):
48
- """This mirrors the interface provided by ADVimba/db/vimba.template."""
49
-
50
- def __init__(self, prefix: str, name: str = "") -> None:
51
- # self.pixel_format = epics_signal_rw_rbv(PixelFormat, prefix + "PixelFormat")
52
- self.convert_pixel_format = epics_signal_rw_rbv(
53
- VimbaConvertFormat, prefix + "ConvertPixelFormat"
54
- ) # Pixel format of data outputted to AD
55
- self.trigger_source = epics_signal_rw_rbv(
56
- VimbaTriggerSource, prefix + "TriggerSource"
57
- )
58
- self.trigger_mode = epics_signal_rw_rbv(VimbaOnOff, prefix + "TriggerMode")
59
- self.trigger_overlap = epics_signal_rw_rbv(
60
- VimbaOverlap, prefix + "TriggerOverlap"
61
- )
62
- self.exposure_mode = epics_signal_rw_rbv(
63
- VimbaExposeOutMode, prefix + "ExposureMode"
64
- )
65
- super().__init__(prefix, name)
52
+ """Mirrors the interface provided by ADVimba/db/vimba.template."""
53
+
54
+ convert_pixel_format: A[
55
+ SignalRW[VimbaConvertFormat], PvSuffix("ConvertPixelFormat")
56
+ ]
57
+ trigger_source: A[SignalRW[VimbaTriggerSource], PvSuffix("TriggerSource")]
58
+ trigger_mode: A[SignalRW[VimbaOnOff], PvSuffix("TriggerMode")]
59
+ trigger_overlap: A[SignalRW[VimbaOverlap], PvSuffix("TriggerOverlap")]
60
+ exposure_mode: A[SignalRW[VimbaExposeOutMode], PvSuffix("ExposureMode")]