ophyd-async 0.9.0a1__py3-none-any.whl → 0.10.0a1__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 (157) 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 +102 -74
  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 +158 -153
  8. ophyd_async/core/_device.py +143 -115
  9. ophyd_async/core/_device_filler.py +82 -9
  10. ophyd_async/core/_flyer.py +16 -7
  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 +74 -58
  17. ophyd_async/core/_settings.py +113 -0
  18. ophyd_async/core/_signal.py +304 -174
  19. ophyd_async/core/_signal_backend.py +60 -14
  20. ophyd_async/core/_soft_signal_backend.py +18 -12
  21. ophyd_async/core/_status.py +72 -24
  22. ophyd_async/core/_table.py +54 -17
  23. ophyd_async/core/_utils.py +101 -52
  24. ophyd_async/core/_yaml_settings.py +66 -0
  25. ophyd_async/epics/__init__.py +1 -0
  26. ophyd_async/epics/adandor/__init__.py +9 -0
  27. ophyd_async/epics/adandor/_andor.py +45 -0
  28. ophyd_async/epics/adandor/_andor_controller.py +51 -0
  29. ophyd_async/epics/adandor/_andor_io.py +34 -0
  30. ophyd_async/epics/adaravis/__init__.py +8 -1
  31. ophyd_async/epics/adaravis/_aravis.py +23 -41
  32. ophyd_async/epics/adaravis/_aravis_controller.py +23 -55
  33. ophyd_async/epics/adaravis/_aravis_io.py +13 -28
  34. ophyd_async/epics/adcore/__init__.py +36 -14
  35. ophyd_async/epics/adcore/_core_detector.py +81 -0
  36. ophyd_async/epics/adcore/_core_io.py +145 -95
  37. ophyd_async/epics/adcore/_core_logic.py +179 -88
  38. ophyd_async/epics/adcore/_core_writer.py +223 -0
  39. ophyd_async/epics/adcore/_hdf_writer.py +51 -92
  40. ophyd_async/epics/adcore/_jpeg_writer.py +26 -0
  41. ophyd_async/epics/adcore/_single_trigger.py +6 -5
  42. ophyd_async/epics/adcore/_tiff_writer.py +26 -0
  43. ophyd_async/epics/adcore/_utils.py +3 -2
  44. ophyd_async/epics/adkinetix/__init__.py +2 -1
  45. ophyd_async/epics/adkinetix/_kinetix.py +32 -27
  46. ophyd_async/epics/adkinetix/_kinetix_controller.py +11 -21
  47. ophyd_async/epics/adkinetix/_kinetix_io.py +12 -13
  48. ophyd_async/epics/adpilatus/__init__.py +7 -2
  49. ophyd_async/epics/adpilatus/_pilatus.py +28 -40
  50. ophyd_async/epics/adpilatus/_pilatus_controller.py +25 -22
  51. ophyd_async/epics/adpilatus/_pilatus_io.py +11 -9
  52. ophyd_async/epics/adsimdetector/__init__.py +8 -1
  53. ophyd_async/epics/adsimdetector/_sim.py +22 -16
  54. ophyd_async/epics/adsimdetector/_sim_controller.py +9 -43
  55. ophyd_async/epics/adsimdetector/_sim_io.py +10 -0
  56. ophyd_async/epics/advimba/__init__.py +10 -1
  57. ophyd_async/epics/advimba/_vimba.py +26 -25
  58. ophyd_async/epics/advimba/_vimba_controller.py +12 -24
  59. ophyd_async/epics/advimba/_vimba_io.py +23 -28
  60. ophyd_async/epics/core/_aioca.py +66 -30
  61. ophyd_async/epics/core/_epics_connector.py +4 -0
  62. ophyd_async/epics/core/_epics_device.py +2 -0
  63. ophyd_async/epics/core/_p4p.py +50 -18
  64. ophyd_async/epics/core/_pvi_connector.py +65 -8
  65. ophyd_async/epics/core/_signal.py +51 -51
  66. ophyd_async/epics/core/_util.py +5 -5
  67. ophyd_async/epics/demo/__init__.py +11 -49
  68. ophyd_async/epics/demo/__main__.py +31 -0
  69. ophyd_async/epics/demo/_ioc.py +32 -0
  70. ophyd_async/epics/demo/_motor.py +82 -0
  71. ophyd_async/epics/demo/_point_detector.py +42 -0
  72. ophyd_async/epics/demo/_point_detector_channel.py +22 -0
  73. ophyd_async/epics/demo/_stage.py +15 -0
  74. ophyd_async/epics/demo/{mover.db → motor.db} +2 -1
  75. ophyd_async/epics/demo/point_detector.db +59 -0
  76. ophyd_async/epics/demo/point_detector_channel.db +21 -0
  77. ophyd_async/epics/eiger/_eiger.py +1 -3
  78. ophyd_async/epics/eiger/_eiger_controller.py +11 -4
  79. ophyd_async/epics/eiger/_eiger_io.py +2 -0
  80. ophyd_async/epics/eiger/_odin_io.py +1 -2
  81. ophyd_async/epics/motor.py +83 -38
  82. ophyd_async/epics/signal.py +4 -1
  83. ophyd_async/epics/testing/__init__.py +14 -14
  84. ophyd_async/epics/testing/_example_ioc.py +68 -73
  85. ophyd_async/epics/testing/_utils.py +19 -44
  86. ophyd_async/epics/testing/test_records.db +16 -0
  87. ophyd_async/epics/testing/test_records_pva.db +17 -16
  88. ophyd_async/fastcs/__init__.py +1 -0
  89. ophyd_async/fastcs/core.py +6 -0
  90. ophyd_async/fastcs/odin/__init__.py +1 -0
  91. ophyd_async/fastcs/panda/__init__.py +8 -8
  92. ophyd_async/fastcs/panda/_block.py +29 -9
  93. ophyd_async/fastcs/panda/_control.py +12 -2
  94. ophyd_async/fastcs/panda/_hdf_panda.py +5 -1
  95. ophyd_async/fastcs/panda/_table.py +13 -7
  96. ophyd_async/fastcs/panda/_trigger.py +23 -9
  97. ophyd_async/fastcs/panda/_writer.py +27 -30
  98. ophyd_async/plan_stubs/__init__.py +16 -0
  99. ophyd_async/plan_stubs/_ensure_connected.py +12 -17
  100. ophyd_async/plan_stubs/_fly.py +3 -5
  101. ophyd_async/plan_stubs/_nd_attributes.py +9 -5
  102. ophyd_async/plan_stubs/_panda.py +14 -0
  103. ophyd_async/plan_stubs/_settings.py +152 -0
  104. ophyd_async/plan_stubs/_utils.py +3 -0
  105. ophyd_async/plan_stubs/_wait_for_awaitable.py +13 -0
  106. ophyd_async/sim/__init__.py +29 -0
  107. ophyd_async/sim/__main__.py +43 -0
  108. ophyd_async/sim/_blob_detector.py +33 -0
  109. ophyd_async/sim/_blob_detector_controller.py +48 -0
  110. ophyd_async/sim/_blob_detector_writer.py +105 -0
  111. ophyd_async/sim/_mirror_horizontal.py +46 -0
  112. ophyd_async/sim/_mirror_vertical.py +74 -0
  113. ophyd_async/sim/_motor.py +233 -0
  114. ophyd_async/sim/_pattern_generator.py +124 -0
  115. ophyd_async/sim/_point_detector.py +86 -0
  116. ophyd_async/sim/_stage.py +19 -0
  117. ophyd_async/tango/__init__.py +1 -0
  118. ophyd_async/tango/core/__init__.py +6 -1
  119. ophyd_async/tango/core/_base_device.py +41 -33
  120. ophyd_async/tango/core/_converters.py +81 -0
  121. ophyd_async/tango/core/_signal.py +21 -33
  122. ophyd_async/tango/core/_tango_readable.py +2 -19
  123. ophyd_async/tango/core/_tango_transport.py +148 -74
  124. ophyd_async/tango/core/_utils.py +47 -0
  125. ophyd_async/tango/demo/_counter.py +2 -0
  126. ophyd_async/tango/demo/_detector.py +2 -0
  127. ophyd_async/tango/demo/_mover.py +10 -6
  128. ophyd_async/tango/demo/_tango/_servers.py +4 -0
  129. ophyd_async/tango/testing/__init__.py +6 -0
  130. ophyd_async/tango/testing/_one_of_everything.py +200 -0
  131. ophyd_async/testing/__init__.py +48 -7
  132. ophyd_async/testing/__pytest_assert_rewrite.py +4 -0
  133. ophyd_async/testing/_assert.py +200 -96
  134. ophyd_async/testing/_mock_signal_utils.py +59 -73
  135. ophyd_async/testing/_one_of_everything.py +146 -0
  136. ophyd_async/testing/_single_derived.py +87 -0
  137. ophyd_async/testing/_utils.py +3 -0
  138. {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/METADATA +25 -26
  139. ophyd_async-0.10.0a1.dist-info/RECORD +149 -0
  140. {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/WHEEL +1 -1
  141. ophyd_async/core/_device_save_loader.py +0 -274
  142. ophyd_async/epics/demo/_mover.py +0 -95
  143. ophyd_async/epics/demo/_sensor.py +0 -37
  144. ophyd_async/epics/demo/sensor.db +0 -19
  145. ophyd_async/fastcs/panda/_utils.py +0 -16
  146. ophyd_async/sim/demo/__init__.py +0 -19
  147. ophyd_async/sim/demo/_pattern_detector/__init__.py +0 -13
  148. ophyd_async/sim/demo/_pattern_detector/_pattern_detector.py +0 -42
  149. ophyd_async/sim/demo/_pattern_detector/_pattern_detector_controller.py +0 -62
  150. ophyd_async/sim/demo/_pattern_detector/_pattern_detector_writer.py +0 -41
  151. ophyd_async/sim/demo/_pattern_detector/_pattern_generator.py +0 -207
  152. ophyd_async/sim/demo/_sim_motor.py +0 -107
  153. ophyd_async/sim/testing/__init__.py +0 -0
  154. ophyd_async-0.9.0a1.dist-info/RECORD +0 -119
  155. ophyd_async-0.9.0a1.dist-info/entry_points.txt +0 -2
  156. {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info/licenses}/LICENSE +0 -0
  157. {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,8 @@
1
1
  import asyncio
2
+ from enum import Enum
2
3
 
3
4
  from ophyd_async.core import (
4
5
  DEFAULT_TIMEOUT,
5
- AsyncStatus,
6
- DetectorController,
7
6
  DetectorTrigger,
8
7
  TriggerInfo,
9
8
  wait_for_value,
@@ -13,7 +12,19 @@ from ophyd_async.epics import adcore
13
12
  from ._pilatus_io import PilatusDriverIO, PilatusTriggerMode
14
13
 
15
14
 
16
- class PilatusController(DetectorController):
15
+ class PilatusReadoutTime(float, Enum):
16
+ """Pilatus readout time per model in ms."""
17
+
18
+ # Cite: https://media.dectris.com/User_Manual-PILATUS2-V1_4.pdf
19
+ PILATUS2 = 2.28e-3
20
+
21
+ # Cite: https://media.dectris.com/user-manual-pilatus3-2020.pdf
22
+ PILATUS3 = 0.95e-3
23
+
24
+
25
+ class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
26
+ """`DetectorController` for a `PilatusDriverIO`."""
27
+
17
28
  _supported_trigger_types = {
18
29
  DetectorTrigger.INTERNAL: PilatusTriggerMode.INTERNAL,
19
30
  DetectorTrigger.CONSTANT_GATE: PilatusTriggerMode.EXT_ENABLE,
@@ -23,48 +34,43 @@ class PilatusController(DetectorController):
23
34
  def __init__(
24
35
  self,
25
36
  driver: PilatusDriverIO,
26
- readout_time: float,
37
+ good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
38
+ readout_time: float = PilatusReadoutTime.PILATUS3,
27
39
  ) -> None:
28
- self._drv = driver
40
+ super().__init__(driver, good_states=good_states)
29
41
  self._readout_time = readout_time
30
- self._arm_status: AsyncStatus | None = None
31
42
 
32
43
  def get_deadtime(self, exposure: float | None) -> float:
33
44
  return self._readout_time
34
45
 
35
46
  async def prepare(self, trigger_info: TriggerInfo):
36
47
  if trigger_info.livetime is not None:
37
- await adcore.set_exposure_time_and_acquire_period_if_supplied(
38
- self, self._drv, trigger_info.livetime
48
+ await self.set_exposure_time_and_acquire_period_if_supplied(
49
+ trigger_info.livetime
39
50
  )
40
51
  await asyncio.gather(
41
- self._drv.trigger_mode.set(self._get_trigger_mode(trigger_info.trigger)),
42
- self._drv.num_images.set(
52
+ self.driver.trigger_mode.set(self._get_trigger_mode(trigger_info.trigger)),
53
+ self.driver.num_images.set(
43
54
  999_999
44
55
  if trigger_info.total_number_of_triggers == 0
45
56
  else trigger_info.total_number_of_triggers
46
57
  ),
47
- self._drv.image_mode.set(adcore.ImageMode.MULTIPLE),
58
+ self.driver.image_mode.set(adcore.ADImageMode.MULTIPLE),
48
59
  )
49
60
 
50
61
  async def arm(self):
51
62
  # Standard arm the detector and wait for the acquire PV to be True
52
- self._arm_status = await adcore.start_acquiring_driver_and_ensure_status(
53
- self._drv
54
- )
63
+ self._arm_status = await self.start_acquiring_driver_and_ensure_status()
64
+
55
65
  # The pilatus has an additional PV that goes True when the camserver
56
66
  # is actually ready. Should wait for that too or we risk dropping
57
67
  # a frame
58
68
  await wait_for_value(
59
- self._drv.armed,
69
+ self.driver.armed,
60
70
  True,
61
71
  timeout=DEFAULT_TIMEOUT,
62
72
  )
63
73
 
64
- async def wait_for_idle(self):
65
- if self._arm_status:
66
- await self._arm_status
67
-
68
74
  @classmethod
69
75
  def _get_trigger_mode(cls, trigger: DetectorTrigger) -> PilatusTriggerMode:
70
76
  if trigger not in cls._supported_trigger_types.keys():
@@ -74,6 +80,3 @@ class PilatusController(DetectorController):
74
80
  f"use {trigger}"
75
81
  )
76
82
  return cls._supported_trigger_types[trigger]
77
-
78
- async def disarm(self):
79
- await adcore.stop_busy_record(self._drv.acquire, False, timeout=1)
@@ -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,7 +1,14 @@
1
+ """Support for the ADAravis areaDetector driver.
2
+
3
+ https://github.com/areaDetector/ADSimDetector
4
+ """
5
+
1
6
  from ._sim import SimDetector
2
7
  from ._sim_controller import SimController
8
+ from ._sim_io import SimDriverIO
3
9
 
4
10
  __all__ = [
5
- "SimDetector",
11
+ "SimDriverIO",
6
12
  "SimController",
13
+ "SimDetector",
7
14
  ]
@@ -1,35 +1,41 @@
1
1
  from collections.abc import Sequence
2
2
 
3
- from ophyd_async.core import PathProvider, SignalR, StandardDetector
3
+ from ophyd_async.core import PathProvider, SignalR
4
4
  from ophyd_async.epics import adcore
5
5
 
6
6
  from ._sim_controller import SimController
7
+ from ._sim_io import SimDriverIO
7
8
 
8
9
 
9
- class SimDetector(StandardDetector):
10
- _controller: SimController
11
- _writer: adcore.ADHDFWriter
10
+ class SimDetector(adcore.AreaDetector[SimController]):
11
+ """Detector for simulated Areadetector."""
12
12
 
13
13
  def __init__(
14
14
  self,
15
15
  prefix: str,
16
16
  path_provider: PathProvider,
17
17
  drv_suffix="cam1:",
18
- hdf_suffix="HDF1:",
19
- name: str = "",
18
+ writer_cls: type[adcore.ADWriter] = adcore.ADHDFWriter,
19
+ fileio_suffix: str | None = None,
20
+ name="",
20
21
  config_sigs: Sequence[SignalR] = (),
22
+ plugins: dict[str, adcore.NDPluginBaseIO] | None = None,
21
23
  ):
22
- self.drv = adcore.ADBaseIO(prefix + drv_suffix)
23
- self.hdf = adcore.NDFileHDFIO(prefix + hdf_suffix)
24
+ driver = SimDriverIO(prefix + drv_suffix)
25
+ controller = SimController(driver)
26
+
27
+ writer = writer_cls.with_io(
28
+ prefix,
29
+ path_provider,
30
+ dataset_source=driver,
31
+ fileio_suffix=fileio_suffix,
32
+ plugins=plugins,
33
+ )
24
34
 
25
35
  super().__init__(
26
- SimController(self.drv),
27
- adcore.ADHDFWriter(
28
- self.hdf,
29
- path_provider,
30
- lambda: self.name,
31
- adcore.ADBaseDatasetDescriber(self.drv),
32
- ),
33
- config_sigs=(self.drv.acquire_period, self.drv.acquire_time, *config_sigs),
36
+ controller=controller,
37
+ writer=writer,
38
+ plugins=plugins,
34
39
  name=name,
40
+ config_sigs=config_sigs,
35
41
  )
@@ -1,51 +1,17 @@
1
- import asyncio
2
-
3
- from ophyd_async.core import (
4
- DEFAULT_TIMEOUT,
5
- AsyncStatus,
6
- DetectorController,
7
- DetectorTrigger,
8
- TriggerInfo,
9
- )
10
1
  from ophyd_async.epics import adcore
11
2
 
3
+ from ._sim_io import SimDriverIO
4
+
5
+
6
+ class SimController(adcore.ADBaseController[SimDriverIO]):
7
+ """Controller for simulated Areadetector."""
12
8
 
13
- class SimController(DetectorController):
14
9
  def __init__(
15
10
  self,
16
- driver: adcore.ADBaseIO,
17
- good_states: frozenset[adcore.DetectorState] = adcore.DEFAULT_GOOD_STATES,
11
+ driver: SimDriverIO,
12
+ good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
18
13
  ) -> None:
19
- self.driver = driver
20
- self.good_states = good_states
21
- self.frame_timeout: float
22
- self._arm_status: AsyncStatus | None = None
14
+ super().__init__(driver, good_states=good_states)
23
15
 
24
16
  def get_deadtime(self, exposure: float | None) -> float:
25
- return 0.002
26
-
27
- async def prepare(self, trigger_info: TriggerInfo):
28
- assert (
29
- trigger_info.trigger == DetectorTrigger.INTERNAL
30
- ), "fly scanning (i.e. external triggering) is not supported for this device"
31
- self.frame_timeout = (
32
- DEFAULT_TIMEOUT + await self.driver.acquire_time.get_value()
33
- )
34
- await asyncio.gather(
35
- self.driver.num_images.set(trigger_info.total_number_of_triggers),
36
- self.driver.image_mode.set(adcore.ImageMode.MULTIPLE),
37
- )
38
-
39
- async def arm(self):
40
- self._arm_status = await adcore.start_acquiring_driver_and_ensure_status(
41
- self.driver, good_states=self.good_states, timeout=self.frame_timeout
42
- )
43
-
44
- async def wait_for_idle(self):
45
- if self._arm_status:
46
- await self._arm_status
47
-
48
- async def disarm(self):
49
- # We can't use caput callback as we already used it in arm() and we can't have
50
- # 2 or they will deadlock
51
- await adcore.stop_busy_record(self.driver.acquire, False, timeout=1)
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
  ]
@@ -1,43 +1,44 @@
1
- from bluesky.protocols import HasHints, Hints
1
+ from collections.abc import Sequence
2
2
 
3
- from ophyd_async.core import PathProvider, StandardDetector
3
+ from ophyd_async.core import PathProvider, SignalR
4
4
  from ophyd_async.epics import adcore
5
5
 
6
6
  from ._vimba_controller import VimbaController
7
7
  from ._vimba_io import VimbaDriverIO
8
8
 
9
9
 
10
- class VimbaDetector(StandardDetector, HasHints):
11
- """
12
- Ophyd-async implementation of an ADVimba Detector.
13
- """
10
+ class VimbaDetector(adcore.AreaDetector[VimbaController]):
11
+ """Ophyd-async implementation of an ADVimba Detector.
14
12
 
15
- _controller: VimbaController
16
- _writer: adcore.ADHDFWriter
13
+ https://github.com/areaDetector/ADVimba
14
+ """
17
15
 
18
16
  def __init__(
19
17
  self,
20
18
  prefix: str,
21
19
  path_provider: PathProvider,
22
- drv_suffix="cam1:",
23
- hdf_suffix="HDF1:",
24
- name="",
20
+ drv_suffix: str = "cam1:",
21
+ writer_cls: type[adcore.ADWriter] = adcore.ADHDFWriter,
22
+ fileio_suffix: str | None = None,
23
+ name: str = "",
24
+ plugins: dict[str, adcore.NDPluginBaseIO] | None = None,
25
+ config_sigs: Sequence[SignalR] = (),
25
26
  ):
26
- self.drv = VimbaDriverIO(prefix + drv_suffix)
27
- self.hdf = adcore.NDFileHDFIO(prefix + hdf_suffix)
27
+ driver = VimbaDriverIO(prefix + drv_suffix)
28
+ controller = VimbaController(driver)
29
+
30
+ writer = writer_cls.with_io(
31
+ prefix,
32
+ path_provider,
33
+ dataset_source=driver,
34
+ fileio_suffix=fileio_suffix,
35
+ plugins=plugins,
36
+ )
28
37
 
29
38
  super().__init__(
30
- VimbaController(self.drv),
31
- adcore.ADHDFWriter(
32
- self.hdf,
33
- path_provider,
34
- lambda: self.name,
35
- adcore.ADBaseDatasetDescriber(self.drv),
36
- ),
37
- config_sigs=(self.drv.acquire_time,),
39
+ controller=controller,
40
+ writer=writer,
41
+ plugins=plugins,
38
42
  name=name,
43
+ config_sigs=config_sigs,
39
44
  )
40
-
41
- @property
42
- def hints(self) -> Hints:
43
- return self._writer.hints
@@ -1,8 +1,6 @@
1
1
  import asyncio
2
2
 
3
3
  from ophyd_async.core import (
4
- AsyncStatus,
5
- DetectorController,
6
4
  DetectorTrigger,
7
5
  TriggerInfo,
8
6
  )
@@ -25,42 +23,32 @@ EXPOSE_OUT_MODE = {
25
23
  }
26
24
 
27
25
 
28
- class VimbaController(DetectorController):
26
+ class VimbaController(adcore.ADBaseController[VimbaDriverIO]):
27
+ """Controller for the Vimba detector."""
28
+
29
29
  def __init__(
30
30
  self,
31
31
  driver: VimbaDriverIO,
32
+ good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
32
33
  ) -> None:
33
- self._drv = driver
34
- self._arm_status: AsyncStatus | None = None
34
+ super().__init__(driver, good_states=good_states)
35
35
 
36
36
  def get_deadtime(self, exposure: float | None) -> float:
37
37
  return 0.001
38
38
 
39
39
  async def prepare(self, trigger_info: TriggerInfo):
40
40
  await asyncio.gather(
41
- self._drv.trigger_mode.set(TRIGGER_MODE[trigger_info.trigger]),
42
- self._drv.exposure_mode.set(EXPOSE_OUT_MODE[trigger_info.trigger]),
43
- self._drv.num_images.set(trigger_info.total_number_of_triggers),
44
- self._drv.image_mode.set(adcore.ImageMode.MULTIPLE),
41
+ self.driver.trigger_mode.set(TRIGGER_MODE[trigger_info.trigger]),
42
+ self.driver.exposure_mode.set(EXPOSE_OUT_MODE[trigger_info.trigger]),
43
+ self.driver.num_images.set(trigger_info.total_number_of_triggers),
44
+ self.driver.image_mode.set(adcore.ADImageMode.MULTIPLE),
45
45
  )
46
46
  if trigger_info.livetime is not None and trigger_info.trigger not in [
47
47
  DetectorTrigger.VARIABLE_GATE,
48
48
  DetectorTrigger.CONSTANT_GATE,
49
49
  ]:
50
- await self._drv.acquire_time.set(trigger_info.livetime)
50
+ await self.driver.acquire_time.set(trigger_info.livetime)
51
51
  if trigger_info.trigger != DetectorTrigger.INTERNAL:
52
- self._drv.trigger_source.set(VimbaTriggerSource.LINE1)
52
+ self.driver.trigger_source.set(VimbaTriggerSource.LINE1)
53
53
  else:
54
- self._drv.trigger_source.set(VimbaTriggerSource.FREERUN)
55
-
56
- async def arm(self):
57
- self._arm_status = await adcore.start_acquiring_driver_and_ensure_status(
58
- self._drv
59
- )
60
-
61
- async def wait_for_idle(self):
62
- if self._arm_status:
63
- await self._arm_status
64
-
65
- async def disarm(self):
66
- await adcore.stop_busy_record(self._drv.acquire, False, timeout=1)
54
+ self.driver.trigger_source.set(VimbaTriggerSource.FREERUN)
@@ -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")]