ophyd-async 0.2.0__py3-none-any.whl → 0.3.0__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 (79) hide show
  1. ophyd_async/__init__.py +1 -4
  2. ophyd_async/_version.py +2 -2
  3. ophyd_async/core/__init__.py +52 -19
  4. ophyd_async/core/_providers.py +38 -5
  5. ophyd_async/core/async_status.py +86 -40
  6. ophyd_async/core/detector.py +214 -72
  7. ophyd_async/core/device.py +91 -50
  8. ophyd_async/core/device_save_loader.py +96 -23
  9. ophyd_async/core/flyer.py +32 -246
  10. ophyd_async/core/mock_signal_backend.py +82 -0
  11. ophyd_async/core/mock_signal_utils.py +145 -0
  12. ophyd_async/core/signal.py +225 -58
  13. ophyd_async/core/signal_backend.py +8 -5
  14. ophyd_async/core/{sim_signal_backend.py → soft_signal_backend.py} +51 -49
  15. ophyd_async/core/standard_readable.py +212 -23
  16. ophyd_async/core/utils.py +123 -30
  17. ophyd_async/epics/_backend/_aioca.py +42 -44
  18. ophyd_async/epics/_backend/_p4p.py +96 -52
  19. ophyd_async/epics/_backend/common.py +25 -0
  20. ophyd_async/epics/areadetector/__init__.py +8 -4
  21. ophyd_async/epics/areadetector/aravis.py +63 -0
  22. ophyd_async/epics/areadetector/controllers/__init__.py +2 -1
  23. ophyd_async/epics/areadetector/controllers/ad_sim_controller.py +1 -1
  24. ophyd_async/epics/areadetector/controllers/aravis_controller.py +78 -0
  25. ophyd_async/epics/areadetector/controllers/kinetix_controller.py +49 -0
  26. ophyd_async/epics/areadetector/controllers/pilatus_controller.py +37 -25
  27. ophyd_async/epics/areadetector/controllers/vimba_controller.py +66 -0
  28. ophyd_async/epics/areadetector/drivers/__init__.py +6 -0
  29. ophyd_async/epics/areadetector/drivers/ad_base.py +8 -12
  30. ophyd_async/epics/areadetector/drivers/aravis_driver.py +38 -0
  31. ophyd_async/epics/areadetector/drivers/kinetix_driver.py +27 -0
  32. ophyd_async/epics/areadetector/drivers/pilatus_driver.py +8 -5
  33. ophyd_async/epics/areadetector/drivers/vimba_driver.py +63 -0
  34. ophyd_async/epics/areadetector/kinetix.py +46 -0
  35. ophyd_async/epics/areadetector/pilatus.py +45 -0
  36. ophyd_async/epics/areadetector/single_trigger_det.py +14 -6
  37. ophyd_async/epics/areadetector/utils.py +2 -12
  38. ophyd_async/epics/areadetector/vimba.py +43 -0
  39. ophyd_async/epics/areadetector/writers/_hdffile.py +21 -7
  40. ophyd_async/epics/areadetector/writers/hdf_writer.py +32 -17
  41. ophyd_async/epics/areadetector/writers/nd_file_hdf.py +19 -18
  42. ophyd_async/epics/areadetector/writers/nd_plugin.py +15 -7
  43. ophyd_async/epics/demo/__init__.py +75 -49
  44. ophyd_async/epics/motion/motor.py +67 -53
  45. ophyd_async/epics/pvi/__init__.py +3 -0
  46. ophyd_async/epics/pvi/pvi.py +318 -0
  47. ophyd_async/epics/signal/__init__.py +8 -3
  48. ophyd_async/epics/signal/signal.py +26 -9
  49. ophyd_async/log.py +130 -0
  50. ophyd_async/panda/__init__.py +21 -5
  51. ophyd_async/panda/_common_blocks.py +49 -0
  52. ophyd_async/panda/_hdf_panda.py +48 -0
  53. ophyd_async/panda/_panda_controller.py +37 -0
  54. ophyd_async/panda/_trigger.py +39 -0
  55. ophyd_async/panda/_utils.py +15 -0
  56. ophyd_async/panda/writers/__init__.py +3 -0
  57. ophyd_async/panda/writers/_hdf_writer.py +220 -0
  58. ophyd_async/panda/writers/_panda_hdf_file.py +58 -0
  59. ophyd_async/plan_stubs/__init__.py +13 -0
  60. ophyd_async/plan_stubs/ensure_connected.py +22 -0
  61. ophyd_async/plan_stubs/fly.py +149 -0
  62. ophyd_async/protocols.py +126 -0
  63. ophyd_async/sim/__init__.py +11 -0
  64. ophyd_async/sim/demo/__init__.py +3 -0
  65. ophyd_async/sim/demo/sim_motor.py +103 -0
  66. ophyd_async/sim/pattern_generator.py +318 -0
  67. ophyd_async/sim/sim_pattern_detector_control.py +55 -0
  68. ophyd_async/sim/sim_pattern_detector_writer.py +34 -0
  69. ophyd_async/sim/sim_pattern_generator.py +37 -0
  70. {ophyd_async-0.2.0.dist-info → ophyd_async-0.3.0.dist-info}/METADATA +31 -70
  71. ophyd_async-0.3.0.dist-info/RECORD +86 -0
  72. {ophyd_async-0.2.0.dist-info → ophyd_async-0.3.0.dist-info}/WHEEL +1 -1
  73. ophyd_async/epics/signal/pvi_get.py +0 -22
  74. ophyd_async/panda/panda.py +0 -294
  75. ophyd_async-0.2.0.dist-info/RECORD +0 -53
  76. /ophyd_async/panda/{table.py → _table.py} +0 -0
  77. {ophyd_async-0.2.0.dist-info → ophyd_async-0.3.0.dist-info}/LICENSE +0 -0
  78. {ophyd_async-0.2.0.dist-info → ophyd_async-0.3.0.dist-info}/entry_points.txt +0 -0
  79. {ophyd_async-0.2.0.dist-info → ophyd_async-0.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,49 @@
1
+ import asyncio
2
+ from typing import Optional
3
+
4
+ from ophyd_async.core import AsyncStatus, DetectorControl, DetectorTrigger
5
+ from ophyd_async.epics.areadetector.drivers.ad_base import (
6
+ start_acquiring_driver_and_ensure_status,
7
+ )
8
+
9
+ from ..drivers.kinetix_driver import KinetixDriver, KinetixTriggerMode
10
+ from ..utils import ImageMode, stop_busy_record
11
+
12
+ KINETIX_TRIGGER_MODE_MAP = {
13
+ DetectorTrigger.internal: KinetixTriggerMode.internal,
14
+ DetectorTrigger.constant_gate: KinetixTriggerMode.gate,
15
+ DetectorTrigger.variable_gate: KinetixTriggerMode.gate,
16
+ DetectorTrigger.edge_trigger: KinetixTriggerMode.edge,
17
+ }
18
+
19
+
20
+ class KinetixController(DetectorControl):
21
+ def __init__(
22
+ self,
23
+ driver: KinetixDriver,
24
+ ) -> None:
25
+ self._drv = driver
26
+
27
+ def get_deadtime(self, exposure: float) -> float:
28
+ return 0.001
29
+
30
+ async def arm(
31
+ self,
32
+ num: int,
33
+ trigger: DetectorTrigger = DetectorTrigger.internal,
34
+ exposure: Optional[float] = None,
35
+ ) -> AsyncStatus:
36
+ await asyncio.gather(
37
+ self._drv.trigger_mode.set(KINETIX_TRIGGER_MODE_MAP[trigger]),
38
+ self._drv.num_images.set(num),
39
+ self._drv.image_mode.set(ImageMode.multiple),
40
+ )
41
+ if exposure is not None and trigger not in [
42
+ DetectorTrigger.variable_gate,
43
+ DetectorTrigger.constant_gate,
44
+ ]:
45
+ await self._drv.acquire_time.set(exposure)
46
+ return await start_acquiring_driver_and_ensure_status(self._drv)
47
+
48
+ async def disarm(self):
49
+ await stop_busy_record(self._drv.acquire, False, timeout=1)
@@ -1,49 +1,61 @@
1
1
  import asyncio
2
- from typing import Optional, Set
2
+ from typing import Optional
3
3
 
4
- from ophyd_async.core import AsyncStatus, DetectorControl, DetectorTrigger
4
+ from ophyd_async.core.async_status import AsyncStatus
5
+ from ophyd_async.core.detector import DetectorControl, DetectorTrigger
5
6
  from ophyd_async.epics.areadetector.drivers.ad_base import (
6
- DEFAULT_GOOD_STATES,
7
- DetectorState,
8
7
  start_acquiring_driver_and_ensure_status,
9
8
  )
10
-
11
- from ..drivers.pilatus_driver import PilatusDriver, TriggerMode
12
- from ..utils import ImageMode, stop_busy_record
13
-
14
- TRIGGER_MODE = {
15
- DetectorTrigger.internal: TriggerMode.internal,
16
- DetectorTrigger.constant_gate: TriggerMode.ext_enable,
17
- DetectorTrigger.variable_gate: TriggerMode.ext_enable,
18
- }
9
+ from ophyd_async.epics.areadetector.drivers.pilatus_driver import (
10
+ PilatusDriver,
11
+ PilatusTriggerMode,
12
+ )
13
+ from ophyd_async.epics.areadetector.utils import ImageMode, stop_busy_record
19
14
 
20
15
 
21
16
  class PilatusController(DetectorControl):
17
+ _supported_trigger_types = {
18
+ DetectorTrigger.internal: PilatusTriggerMode.internal,
19
+ DetectorTrigger.constant_gate: PilatusTriggerMode.ext_enable,
20
+ DetectorTrigger.variable_gate: PilatusTriggerMode.ext_enable,
21
+ }
22
+
22
23
  def __init__(
23
24
  self,
24
25
  driver: PilatusDriver,
25
- good_states: Set[DetectorState] = set(DEFAULT_GOOD_STATES),
26
26
  ) -> None:
27
- self.driver = driver
28
- self.good_states = good_states
27
+ self._drv = driver
29
28
 
30
29
  def get_deadtime(self, exposure: float) -> float:
31
- return 0.001
30
+ # Cite: https://media.dectris.com/User_Manual-PILATUS2-V1_4.pdf
31
+ """The required minimum time difference between ExpPeriod and ExpTime
32
+ (readout time) is 2.28 ms"""
33
+ return 2.28e-3
32
34
 
33
35
  async def arm(
34
36
  self,
37
+ num: int,
35
38
  trigger: DetectorTrigger = DetectorTrigger.internal,
36
- num: int = 0,
37
39
  exposure: Optional[float] = None,
38
40
  ) -> AsyncStatus:
41
+ if exposure is not None:
42
+ await self._drv.acquire_time.set(exposure)
39
43
  await asyncio.gather(
40
- self.driver.trigger_mode.set(TRIGGER_MODE[trigger]),
41
- self.driver.num_images.set(999_999 if num == 0 else num),
42
- self.driver.image_mode.set(ImageMode.multiple),
43
- )
44
- return await start_acquiring_driver_and_ensure_status(
45
- self.driver, good_states=self.good_states
44
+ self._drv.trigger_mode.set(self._get_trigger_mode(trigger)),
45
+ self._drv.num_images.set(999_999 if num == 0 else num),
46
+ self._drv.image_mode.set(ImageMode.multiple),
46
47
  )
48
+ return await start_acquiring_driver_and_ensure_status(self._drv)
49
+
50
+ @classmethod
51
+ def _get_trigger_mode(cls, trigger: DetectorTrigger) -> PilatusTriggerMode:
52
+ if trigger not in cls._supported_trigger_types.keys():
53
+ raise ValueError(
54
+ f"{cls.__name__} only supports the following trigger "
55
+ f"types: {cls._supported_trigger_types.keys()} but was asked to "
56
+ f"use {trigger}"
57
+ )
58
+ return cls._supported_trigger_types[trigger]
47
59
 
48
60
  async def disarm(self):
49
- await stop_busy_record(self.driver.acquire, False, timeout=1)
61
+ await stop_busy_record(self._drv.acquire, False, timeout=1)
@@ -0,0 +1,66 @@
1
+ import asyncio
2
+ from typing import Optional
3
+
4
+ from ophyd_async.core import AsyncStatus, DetectorControl, DetectorTrigger
5
+ from ophyd_async.epics.areadetector.drivers.ad_base import (
6
+ start_acquiring_driver_and_ensure_status,
7
+ )
8
+
9
+ from ..drivers.vimba_driver import (
10
+ VimbaDriver,
11
+ VimbaExposeOutMode,
12
+ VimbaOnOff,
13
+ VimbaTriggerSource,
14
+ )
15
+ from ..utils import ImageMode, stop_busy_record
16
+
17
+ TRIGGER_MODE = {
18
+ DetectorTrigger.internal: VimbaOnOff.off,
19
+ DetectorTrigger.constant_gate: VimbaOnOff.on,
20
+ DetectorTrigger.variable_gate: VimbaOnOff.on,
21
+ DetectorTrigger.edge_trigger: VimbaOnOff.on,
22
+ }
23
+
24
+ EXPOSE_OUT_MODE = {
25
+ DetectorTrigger.internal: VimbaExposeOutMode.timed,
26
+ DetectorTrigger.constant_gate: VimbaExposeOutMode.trigger_width,
27
+ DetectorTrigger.variable_gate: VimbaExposeOutMode.trigger_width,
28
+ DetectorTrigger.edge_trigger: VimbaExposeOutMode.timed,
29
+ }
30
+
31
+
32
+ class VimbaController(DetectorControl):
33
+ def __init__(
34
+ self,
35
+ driver: VimbaDriver,
36
+ ) -> None:
37
+ self._drv = driver
38
+
39
+ def get_deadtime(self, exposure: float) -> float:
40
+ return 0.001
41
+
42
+ async def arm(
43
+ self,
44
+ num: int,
45
+ trigger: DetectorTrigger = DetectorTrigger.internal,
46
+ exposure: Optional[float] = None,
47
+ ) -> AsyncStatus:
48
+ await asyncio.gather(
49
+ self._drv.trigger_mode.set(TRIGGER_MODE[trigger]),
50
+ self._drv.expose_mode.set(EXPOSE_OUT_MODE[trigger]),
51
+ self._drv.num_images.set(num),
52
+ self._drv.image_mode.set(ImageMode.multiple),
53
+ )
54
+ if exposure is not None and trigger not in [
55
+ DetectorTrigger.variable_gate,
56
+ DetectorTrigger.constant_gate,
57
+ ]:
58
+ await self._drv.acquire_time.set(exposure)
59
+ if trigger != DetectorTrigger.internal:
60
+ self._drv.trig_source.set(VimbaTriggerSource.line1)
61
+ else:
62
+ self._drv.trig_source.set(VimbaTriggerSource.freerun)
63
+ return await start_acquiring_driver_and_ensure_status(self._drv)
64
+
65
+ async def disarm(self):
66
+ await stop_busy_record(self._drv.acquire, False, timeout=1)
@@ -4,12 +4,18 @@ from .ad_base import (
4
4
  DetectorState,
5
5
  start_acquiring_driver_and_ensure_status,
6
6
  )
7
+ from .aravis_driver import AravisDriver
8
+ from .kinetix_driver import KinetixDriver
7
9
  from .pilatus_driver import PilatusDriver
10
+ from .vimba_driver import VimbaDriver
8
11
 
9
12
  __all__ = [
10
13
  "ADBase",
11
14
  "ADBaseShapeProvider",
12
15
  "PilatusDriver",
16
+ "AravisDriver",
17
+ "KinetixDriver",
18
+ "VimbaDriver",
13
19
  "start_acquiring_driver_and_ensure_status",
14
20
  "DetectorState",
15
21
  ]
@@ -9,8 +9,8 @@ from ophyd_async.core import (
9
9
  set_and_wait_for_value,
10
10
  )
11
11
 
12
- from ...signal.signal import epics_signal_rw
13
- from ..utils import ImageMode, ad_r, ad_rw
12
+ from ...signal.signal import epics_signal_r, epics_signal_rw_rbv
13
+ from ..utils import ImageMode
14
14
  from ..writers.nd_plugin import NDArrayBase
15
15
 
16
16
 
@@ -43,16 +43,12 @@ DEFAULT_GOOD_STATES: FrozenSet[DetectorState] = frozenset(
43
43
  class ADBase(NDArrayBase):
44
44
  def __init__(self, prefix: str, name: str = "") -> None:
45
45
  # Define some signals
46
- self.acquire = ad_rw(bool, prefix + "Acquire")
47
- self.acquire_time = ad_rw(float, prefix + "AcquireTime")
48
- self.num_images = ad_rw(int, prefix + "NumImages")
49
- self.image_mode = ad_rw(ImageMode, prefix + "ImageMode")
50
- self.array_counter = ad_rw(int, prefix + "ArrayCounter")
51
- self.array_size_x = ad_r(int, prefix + "ArraySizeX")
52
- self.array_size_y = ad_r(int, prefix + "ArraySizeY")
53
- self.detector_state = ad_r(DetectorState, prefix + "DetectorState")
54
- # There is no _RBV for this one
55
- self.wait_for_plugins = epics_signal_rw(bool, prefix + "WaitForPlugins")
46
+ self.acquire_time = epics_signal_rw_rbv(float, prefix + "AcquireTime")
47
+ self.num_images = epics_signal_rw_rbv(int, prefix + "NumImages")
48
+ self.image_mode = epics_signal_rw_rbv(ImageMode, prefix + "ImageMode")
49
+ self.detector_state = epics_signal_r(
50
+ DetectorState, prefix + "DetectorState_RBV"
51
+ )
56
52
  super().__init__(prefix, name=name)
57
53
 
58
54
 
@@ -0,0 +1,38 @@
1
+ from enum import Enum
2
+ from typing import Literal
3
+
4
+ from ophyd_async.epics.areadetector.drivers import ADBase
5
+ from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
6
+
7
+
8
+ class AravisTriggerMode(str, Enum):
9
+ """GigEVision GenICAM standard: on=externally triggered"""
10
+
11
+ on = "On"
12
+ off = "Off"
13
+
14
+
15
+ """A minimal set of TriggerSources that must be supported by the underlying record.
16
+ To enable hardware triggered scanning, line_N must support each N in GPIO_NUMBER.
17
+ To enable software triggered scanning, freerun must be supported.
18
+ Other enumerated values may or may not be preset.
19
+ To prevent requiring one Enum class per possible configuration, we set as this Enum
20
+ but read from the underlying signal as a str.
21
+ """
22
+ AravisTriggerSource = Literal["Freerun", "Line1", "Line2", "Line3", "Line4"]
23
+
24
+
25
+ class AravisDriver(ADBase):
26
+ # If instantiating a new instance, ensure it is supported in the _deadtimes dict
27
+ """Generic Driver supporting the Manta and Mako drivers.
28
+ Fetches deadtime prior to use in a Streaming scan.
29
+ Requires driver firmware up to date:
30
+ - Model_RBV must be of the form "^(Mako|Manta) (model)$"
31
+ """
32
+
33
+ def __init__(self, prefix: str, name: str = "") -> None:
34
+ self.trigger_mode = epics_signal_rw_rbv(
35
+ AravisTriggerMode, prefix + "TriggerMode"
36
+ )
37
+ self.trigger_source = epics_signal_rw_rbv(str, prefix + "TriggerSource")
38
+ super().__init__(prefix, name=name)
@@ -0,0 +1,27 @@
1
+ from enum import Enum
2
+
3
+ from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
4
+
5
+ from .ad_base import ADBase
6
+
7
+
8
+ class KinetixTriggerMode(str, Enum):
9
+ internal = "Internal"
10
+ edge = "Rising Edge"
11
+ gate = "Exp. Gate"
12
+
13
+
14
+ class KinetixReadoutMode(str, Enum):
15
+ sensitivity = 1
16
+ speed = 2
17
+ dynamic_range = 3
18
+
19
+
20
+ class KinetixDriver(ADBase):
21
+ def __init__(self, prefix: str, name: str = "") -> None:
22
+ # self.pixel_format = epics_signal_rw_rbv(PixelFormat, prefix + "PixelFormat")
23
+ self.trigger_mode = epics_signal_rw_rbv(
24
+ KinetixTriggerMode, prefix + "TriggerMode"
25
+ )
26
+ self.mode = epics_signal_rw_rbv(KinetixReadoutMode, prefix + "ReadoutPortIdx")
27
+ super().__init__(prefix, name)
@@ -1,10 +1,11 @@
1
1
  from enum import Enum
2
2
 
3
- from ..utils import ad_rw
3
+ from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
4
+
4
5
  from .ad_base import ADBase
5
6
 
6
7
 
7
- class TriggerMode(str, Enum):
8
+ class PilatusTriggerMode(str, Enum):
8
9
  internal = "Internal"
9
10
  ext_enable = "Ext. Enable"
10
11
  ext_trigger = "Ext. Trigger"
@@ -13,6 +14,8 @@ class TriggerMode(str, Enum):
13
14
 
14
15
 
15
16
  class PilatusDriver(ADBase):
16
- def __init__(self, prefix: str) -> None:
17
- self.trigger_mode = ad_rw(TriggerMode, prefix + "TriggerMode")
18
- super().__init__(prefix)
17
+ def __init__(self, prefix: str, name: str = "") -> None:
18
+ self.trigger_mode = epics_signal_rw_rbv(
19
+ PilatusTriggerMode, prefix + "TriggerMode"
20
+ )
21
+ super().__init__(prefix, name)
@@ -0,0 +1,63 @@
1
+ from enum import Enum
2
+
3
+ from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
4
+
5
+ from .ad_base import ADBase
6
+
7
+
8
+ class VimbaPixelFormat(str, Enum):
9
+ internal = "Mono8"
10
+ ext_enable = "Mono12"
11
+ ext_trigger = "Ext. Trigger"
12
+ mult_trigger = "Mult. Trigger"
13
+ alignment = "Alignment"
14
+
15
+
16
+ class VimbaConvertFormat(str, Enum):
17
+ none = "None"
18
+ mono8 = "Mono8"
19
+ mono16 = "Mono16"
20
+ rgb8 = "RGB8"
21
+ rgb16 = "RGB16"
22
+
23
+
24
+ class VimbaTriggerSource(str, Enum):
25
+ freerun = "Freerun"
26
+ line1 = "Line1"
27
+ line2 = "Line2"
28
+ fixed_rate = "FixedRate"
29
+ software = "Software"
30
+ action0 = "Action0"
31
+ action1 = "Action1"
32
+
33
+
34
+ class VimbaOverlap(str, Enum):
35
+ off = "Off"
36
+ prev_frame = "PreviousFrame"
37
+
38
+
39
+ class VimbaOnOff(str, Enum):
40
+ on = "On"
41
+ off = "Off"
42
+
43
+
44
+ class VimbaExposeOutMode(str, Enum):
45
+ timed = "Timed" # Use ExposureTime PV
46
+ trigger_width = "TriggerWidth" # Expose for length of high signal
47
+
48
+
49
+ class VimbaDriver(ADBase):
50
+ def __init__(self, prefix: str, name: str = "") -> None:
51
+ # self.pixel_format = epics_signal_rw_rbv(PixelFormat, prefix + "PixelFormat")
52
+ self.convert_format = epics_signal_rw_rbv(
53
+ VimbaConvertFormat, prefix + "ConvertPixelFormat"
54
+ ) # Pixel format of data outputted to AD
55
+ self.trig_source = epics_signal_rw_rbv(
56
+ VimbaTriggerSource, prefix + "TriggerSource"
57
+ )
58
+ self.trigger_mode = epics_signal_rw_rbv(VimbaOnOff, prefix + "TriggerMode")
59
+ self.overlap = epics_signal_rw_rbv(VimbaOverlap, prefix + "TriggerOverlap")
60
+ self.expose_mode = epics_signal_rw_rbv(
61
+ VimbaExposeOutMode, prefix + "ExposureMode"
62
+ )
63
+ super().__init__(prefix, name)
@@ -0,0 +1,46 @@
1
+ from bluesky.protocols import HasHints, Hints
2
+
3
+ from ophyd_async.core import DirectoryProvider, StandardDetector
4
+ from ophyd_async.epics.areadetector.controllers.kinetix_controller import (
5
+ KinetixController,
6
+ )
7
+ from ophyd_async.epics.areadetector.drivers import ADBaseShapeProvider
8
+ from ophyd_async.epics.areadetector.drivers.kinetix_driver import KinetixDriver
9
+ from ophyd_async.epics.areadetector.writers import HDFWriter, NDFileHDF
10
+
11
+
12
+ class KinetixDetector(StandardDetector, HasHints):
13
+ """
14
+ Ophyd-async implementation of an ADKinetix Detector.
15
+ https://github.com/NSLS-II/ADKinetix
16
+ """
17
+
18
+ _controller: KinetixController
19
+ _writer: HDFWriter
20
+
21
+ def __init__(
22
+ self,
23
+ prefix: str,
24
+ directory_provider: DirectoryProvider,
25
+ drv_suffix="cam1:",
26
+ hdf_suffix="HDF1:",
27
+ name="",
28
+ ):
29
+ self.drv = KinetixDriver(prefix + drv_suffix)
30
+ self.hdf = NDFileHDF(prefix + hdf_suffix)
31
+
32
+ super().__init__(
33
+ KinetixController(self.drv),
34
+ HDFWriter(
35
+ self.hdf,
36
+ directory_provider,
37
+ lambda: self.name,
38
+ ADBaseShapeProvider(self.drv),
39
+ ),
40
+ config_sigs=(self.drv.acquire_time,),
41
+ name=name,
42
+ )
43
+
44
+ @property
45
+ def hints(self) -> Hints:
46
+ return self._writer.hints
@@ -0,0 +1,45 @@
1
+ from bluesky.protocols import Hints
2
+
3
+ from ophyd_async.core import DirectoryProvider
4
+ from ophyd_async.core.detector import StandardDetector
5
+ from ophyd_async.epics.areadetector.controllers.pilatus_controller import (
6
+ PilatusController,
7
+ )
8
+ from ophyd_async.epics.areadetector.drivers.ad_base import ADBaseShapeProvider
9
+ from ophyd_async.epics.areadetector.drivers.pilatus_driver import PilatusDriver
10
+ from ophyd_async.epics.areadetector.writers.hdf_writer import HDFWriter
11
+ from ophyd_async.epics.areadetector.writers.nd_file_hdf import NDFileHDF
12
+
13
+
14
+ class PilatusDetector(StandardDetector):
15
+ """A Pilatus StandardDetector writing HDF files"""
16
+
17
+ _controller: PilatusController
18
+ _writer: HDFWriter
19
+
20
+ def __init__(
21
+ self,
22
+ prefix: str,
23
+ directory_provider: DirectoryProvider,
24
+ drv_suffix="cam1:",
25
+ hdf_suffix="HDF1:",
26
+ name="",
27
+ ):
28
+ self.drv = PilatusDriver(prefix + drv_suffix)
29
+ self.hdf = NDFileHDF(prefix + hdf_suffix)
30
+
31
+ super().__init__(
32
+ PilatusController(self.drv),
33
+ HDFWriter(
34
+ self.hdf,
35
+ directory_provider,
36
+ lambda: self.name,
37
+ ADBaseShapeProvider(self.drv),
38
+ ),
39
+ config_sigs=(self.drv.acquire_time,),
40
+ name=name,
41
+ )
42
+
43
+ @property
44
+ def hints(self) -> Hints:
45
+ return self._writer.hints
@@ -3,7 +3,13 @@ from typing import Sequence
3
3
 
4
4
  from bluesky.protocols import Triggerable
5
5
 
6
- from ophyd_async.core import AsyncStatus, SignalR, StandardReadable
6
+ from ophyd_async.core import (
7
+ AsyncStatus,
8
+ ConfigSignal,
9
+ HintedSignal,
10
+ SignalR,
11
+ StandardReadable,
12
+ )
7
13
 
8
14
  from .drivers.ad_base import ADBase
9
15
  from .utils import ImageMode
@@ -20,12 +26,14 @@ class SingleTriggerDet(StandardReadable, Triggerable):
20
26
  ) -> None:
21
27
  self.drv = drv
22
28
  self.__dict__.update(plugins)
23
- self.set_readable_signals(
24
- # Can't subscribe to read signals as race between monitor coming back and
25
- # caput callback on acquire
26
- read_uncached=[self.drv.array_counter] + list(read_uncached),
27
- config=[self.drv.acquire_time],
29
+
30
+ self.add_readables(
31
+ [self.drv.array_counter, *read_uncached],
32
+ wrapper=HintedSignal.uncached,
28
33
  )
34
+
35
+ self.add_readables([self.drv.acquire_time], wrapper=ConfigSignal)
36
+
29
37
  super().__init__(name=name)
30
38
 
31
39
  @AsyncStatus.wrap
@@ -1,18 +1,8 @@
1
1
  from enum import Enum
2
- from typing import Optional, Type
2
+ from typing import Optional
3
3
  from xml.etree import cElementTree as ET
4
4
 
5
- from ophyd_async.core import DEFAULT_TIMEOUT, SignalR, SignalRW, T, wait_for_value
6
-
7
- from ..signal.signal import epics_signal_r, epics_signal_rw
8
-
9
-
10
- def ad_rw(datatype: Type[T], prefix: str) -> SignalRW[T]:
11
- return epics_signal_rw(datatype, prefix + "_RBV", prefix)
12
-
13
-
14
- def ad_r(datatype: Type[T], prefix: str) -> SignalR[T]:
15
- return epics_signal_r(datatype, prefix + "_RBV")
5
+ from ophyd_async.core import DEFAULT_TIMEOUT, SignalRW, T, wait_for_value
16
6
 
17
7
 
18
8
  class FileWriteMode(str, Enum):
@@ -0,0 +1,43 @@
1
+ from bluesky.protocols import HasHints, Hints
2
+
3
+ from ophyd_async.core import DirectoryProvider, StandardDetector
4
+ from ophyd_async.epics.areadetector.controllers.vimba_controller import VimbaController
5
+ from ophyd_async.epics.areadetector.drivers import ADBaseShapeProvider
6
+ from ophyd_async.epics.areadetector.drivers.vimba_driver import VimbaDriver
7
+ from ophyd_async.epics.areadetector.writers import HDFWriter, NDFileHDF
8
+
9
+
10
+ class VimbaDetector(StandardDetector, HasHints):
11
+ """
12
+ Ophyd-async implementation of an ADVimba Detector.
13
+ """
14
+
15
+ _controller: VimbaController
16
+ _writer: HDFWriter
17
+
18
+ def __init__(
19
+ self,
20
+ prefix: str,
21
+ directory_provider: DirectoryProvider,
22
+ drv_suffix="cam1:",
23
+ hdf_suffix="HDF1:",
24
+ name="",
25
+ ):
26
+ self.drv = VimbaDriver(prefix + drv_suffix)
27
+ self.hdf = NDFileHDF(prefix + hdf_suffix)
28
+
29
+ super().__init__(
30
+ VimbaController(self.drv),
31
+ HDFWriter(
32
+ self.hdf,
33
+ directory_provider,
34
+ lambda: self.name,
35
+ ADBaseShapeProvider(self.drv),
36
+ ),
37
+ config_sigs=(self.drv.acquire_time,),
38
+ name=name,
39
+ )
40
+
41
+ @property
42
+ def hints(self) -> Hints:
43
+ return self._writer.hints
@@ -1,19 +1,33 @@
1
+ from pathlib import Path
1
2
  from typing import Iterator, List
2
3
 
3
4
  from event_model import StreamDatum, StreamResource, compose_stream_resource
4
5
 
6
+ from ophyd_async.core import DirectoryInfo
7
+
5
8
  from ._hdfdataset import _HDFDataset
6
9
 
7
10
 
8
11
  class _HDFFile:
9
- def __init__(self, full_file_name: str, datasets: List[_HDFDataset]) -> None:
12
+ """
13
+ :param directory_info: Contains information about how to construct a StreamResource
14
+ :param full_file_name: Absolute path to the file to be written
15
+ :param datasets: Datasets to write into the file
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ directory_info: DirectoryInfo,
21
+ full_file_name: Path,
22
+ datasets: List[_HDFDataset],
23
+ ) -> None:
10
24
  self._last_emitted = 0
11
25
  self._bundles = [
12
26
  compose_stream_resource(
13
27
  spec="AD_HDF5_SWMR_SLICE",
14
- root="/",
28
+ root=str(directory_info.root),
15
29
  data_key=ds.name,
16
- resource_path=full_file_name,
30
+ resource_path=str(full_file_name.relative_to(directory_info.root)),
17
31
  resource_kwargs={
18
32
  "path": ds.path,
19
33
  "multiplier": ds.multiplier,
@@ -30,10 +44,10 @@ class _HDFFile:
30
44
  def stream_data(self, indices_written: int) -> Iterator[StreamDatum]:
31
45
  # Indices are relative to resource
32
46
  if indices_written > self._last_emitted:
33
- indices = dict(
34
- start=self._last_emitted,
35
- stop=indices_written,
36
- )
47
+ indices = {
48
+ "start": self._last_emitted,
49
+ "stop": indices_written,
50
+ }
37
51
  self._last_emitted = indices_written
38
52
  for bundle in self._bundles:
39
53
  yield bundle.compose_stream_datum(indices)