ophyd-async 0.3a1__py3-none-any.whl → 0.3a3__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 (69) hide show
  1. ophyd_async/__init__.py +1 -4
  2. ophyd_async/_version.py +1 -1
  3. ophyd_async/core/__init__.py +23 -3
  4. ophyd_async/core/_providers.py +3 -1
  5. ophyd_async/core/detector.py +72 -46
  6. ophyd_async/core/device.py +8 -0
  7. ophyd_async/core/flyer.py +12 -21
  8. ophyd_async/core/signal.py +134 -20
  9. ophyd_async/core/signal_backend.py +6 -3
  10. ophyd_async/core/sim_signal_backend.py +32 -20
  11. ophyd_async/core/standard_readable.py +212 -23
  12. ophyd_async/core/utils.py +18 -1
  13. ophyd_async/epics/_backend/_aioca.py +17 -15
  14. ophyd_async/epics/_backend/_p4p.py +34 -25
  15. ophyd_async/epics/_backend/common.py +16 -11
  16. ophyd_async/epics/areadetector/__init__.py +8 -0
  17. ophyd_async/epics/areadetector/aravis.py +67 -0
  18. ophyd_async/epics/areadetector/controllers/__init__.py +2 -1
  19. ophyd_async/epics/areadetector/controllers/aravis_controller.py +73 -0
  20. ophyd_async/epics/areadetector/controllers/kinetix_controller.py +49 -0
  21. ophyd_async/epics/areadetector/controllers/pilatus_controller.py +36 -24
  22. ophyd_async/epics/areadetector/controllers/vimba_controller.py +66 -0
  23. ophyd_async/epics/areadetector/drivers/__init__.py +6 -0
  24. ophyd_async/epics/areadetector/drivers/aravis_driver.py +154 -0
  25. ophyd_async/epics/areadetector/drivers/kinetix_driver.py +24 -0
  26. ophyd_async/epics/areadetector/drivers/pilatus_driver.py +4 -4
  27. ophyd_async/epics/areadetector/drivers/vimba_driver.py +58 -0
  28. ophyd_async/epics/areadetector/kinetix.py +46 -0
  29. ophyd_async/epics/areadetector/pilatus.py +45 -0
  30. ophyd_async/epics/areadetector/single_trigger_det.py +14 -6
  31. ophyd_async/epics/areadetector/vimba.py +43 -0
  32. ophyd_async/epics/areadetector/writers/_hdffile.py +4 -4
  33. ophyd_async/epics/areadetector/writers/hdf_writer.py +12 -4
  34. ophyd_async/epics/areadetector/writers/nd_file_hdf.py +1 -0
  35. ophyd_async/epics/demo/__init__.py +45 -18
  36. ophyd_async/epics/motion/motor.py +24 -19
  37. ophyd_async/epics/pvi/__init__.py +3 -0
  38. ophyd_async/epics/pvi/pvi.py +318 -0
  39. ophyd_async/epics/signal/signal.py +26 -9
  40. ophyd_async/log.py +130 -0
  41. ophyd_async/panda/__init__.py +17 -6
  42. ophyd_async/panda/_common_blocks.py +49 -0
  43. ophyd_async/panda/_hdf_panda.py +48 -0
  44. ophyd_async/panda/{panda_controller.py → _panda_controller.py} +3 -7
  45. ophyd_async/panda/_trigger.py +39 -0
  46. ophyd_async/panda/writers/__init__.py +3 -0
  47. ophyd_async/panda/writers/_hdf_writer.py +220 -0
  48. ophyd_async/panda/writers/_panda_hdf_file.py +58 -0
  49. ophyd_async/planstubs/__init__.py +5 -0
  50. ophyd_async/planstubs/prepare_trigger_and_dets.py +57 -0
  51. ophyd_async/protocols.py +96 -0
  52. ophyd_async/sim/__init__.py +11 -0
  53. ophyd_async/sim/demo/__init__.py +3 -0
  54. ophyd_async/sim/demo/sim_motor.py +118 -0
  55. ophyd_async/sim/pattern_generator.py +318 -0
  56. ophyd_async/sim/sim_pattern_detector_control.py +55 -0
  57. ophyd_async/sim/sim_pattern_detector_writer.py +34 -0
  58. ophyd_async/sim/sim_pattern_generator.py +37 -0
  59. {ophyd_async-0.3a1.dist-info → ophyd_async-0.3a3.dist-info}/METADATA +30 -69
  60. ophyd_async-0.3a3.dist-info/RECORD +83 -0
  61. ophyd_async/epics/pvi.py +0 -70
  62. ophyd_async/panda/panda.py +0 -241
  63. ophyd_async-0.3a1.dist-info/RECORD +0 -56
  64. /ophyd_async/panda/{table.py → _table.py} +0 -0
  65. /ophyd_async/panda/{utils.py → _utils.py} +0 -0
  66. {ophyd_async-0.3a1.dist-info → ophyd_async-0.3a3.dist-info}/LICENSE +0 -0
  67. {ophyd_async-0.3a1.dist-info → ophyd_async-0.3a3.dist-info}/WHEEL +0 -0
  68. {ophyd_async-0.3a1.dist-info → ophyd_async-0.3a3.dist-info}/entry_points.txt +0 -0
  69. {ophyd_async-0.3a1.dist-info → ophyd_async-0.3a3.dist-info}/top_level.txt +0 -0
@@ -7,14 +7,19 @@ def get_supported_enum_class(
7
7
  datatype: Optional[Type[Enum]],
8
8
  pv_choices: Tuple[Any, ...],
9
9
  ) -> Type[Enum]:
10
- if datatype:
11
- if not issubclass(datatype, Enum):
12
- raise TypeError(f"{pv} has type Enum not {datatype.__name__}")
13
- if not issubclass(datatype, str):
14
- raise TypeError(f"{pv} has type Enum but doesn't inherit from String")
15
- choices = tuple(v.value for v in datatype)
16
- if set(choices).difference(pv_choices):
17
- raise TypeError(f"{pv} has choices {pv_choices}: not all in {choices}")
18
- return Enum(
19
- "GeneratedChoices", {x or "_": x for x in pv_choices}, type=str
20
- ) # type: ignore
10
+ if not datatype:
11
+ return Enum("GeneratedChoices", {x or "_": x for x in pv_choices}, type=str) # type: ignore
12
+
13
+ if not issubclass(datatype, Enum):
14
+ raise TypeError(f"{pv} has type Enum not {datatype.__name__}")
15
+ if not issubclass(datatype, str):
16
+ raise TypeError(f"{pv} has type Enum but doesn't inherit from String")
17
+ choices = tuple(v.value for v in datatype)
18
+ if set(choices) != set(pv_choices):
19
+ raise TypeError(
20
+ (
21
+ f"{pv} has choices {pv_choices}, "
22
+ f"which do not match {datatype}, which has {choices}"
23
+ )
24
+ )
25
+ return datatype
@@ -1,3 +1,6 @@
1
+ from .aravis import AravisDetector
2
+ from .kinetix import KinetixDetector
3
+ from .pilatus import PilatusDetector
1
4
  from .single_trigger_det import SingleTriggerDet
2
5
  from .utils import (
3
6
  FileWriteMode,
@@ -7,8 +10,12 @@ from .utils import (
7
10
  ad_r,
8
11
  ad_rw,
9
12
  )
13
+ from .vimba import VimbaDetector
10
14
 
11
15
  __all__ = [
16
+ "AravisDetector",
17
+ "KinetixDetector",
18
+ "VimbaDetector",
12
19
  "SingleTriggerDet",
13
20
  "FileWriteMode",
14
21
  "ImageMode",
@@ -16,4 +23,5 @@ __all__ = [
16
23
  "ad_rw",
17
24
  "NDAttributeDataType",
18
25
  "NDAttributesXML",
26
+ "PilatusDetector",
19
27
  ]
@@ -0,0 +1,67 @@
1
+ from typing import get_args
2
+
3
+ from bluesky.protocols import HasHints, Hints
4
+
5
+ from ophyd_async.core import DirectoryProvider, StandardDetector, TriggerInfo
6
+ from ophyd_async.epics.areadetector.controllers.aravis_controller import (
7
+ AravisController,
8
+ )
9
+ from ophyd_async.epics.areadetector.drivers import ADBaseShapeProvider
10
+ from ophyd_async.epics.areadetector.drivers.aravis_driver import AravisDriver
11
+ from ophyd_async.epics.areadetector.writers import HDFWriter, NDFileHDF
12
+
13
+
14
+ class AravisDetector(StandardDetector, HasHints):
15
+ """
16
+ Ophyd-async implementation of an ADAravis Detector.
17
+ The detector may be configured for an external trigger on a GPIO port,
18
+ which must be done prior to preparing the detector
19
+ """
20
+
21
+ _controller: AravisController
22
+ _writer: HDFWriter
23
+
24
+ def __init__(
25
+ self,
26
+ prefix: str,
27
+ directory_provider: DirectoryProvider,
28
+ drv_suffix="cam1:",
29
+ hdf_suffix="HDF1:",
30
+ name="",
31
+ gpio_number: AravisController.GPIO_NUMBER = 1,
32
+ ):
33
+ self.drv = AravisDriver(prefix + drv_suffix)
34
+ self.hdf = NDFileHDF(prefix + hdf_suffix)
35
+
36
+ super().__init__(
37
+ AravisController(self.drv, gpio_number=gpio_number),
38
+ HDFWriter(
39
+ self.hdf,
40
+ directory_provider,
41
+ lambda: self.name,
42
+ ADBaseShapeProvider(self.drv),
43
+ ),
44
+ config_sigs=(self.drv.acquire_time,),
45
+ name=name,
46
+ )
47
+
48
+ async def _prepare(self, value: TriggerInfo) -> None:
49
+ await self.drv.fetch_deadtime()
50
+ await super()._prepare(value)
51
+
52
+ def get_external_trigger_gpio(self):
53
+ return self._controller.gpio_number
54
+
55
+ def set_external_trigger_gpio(self, gpio_number: AravisController.GPIO_NUMBER):
56
+ supported_gpio_numbers = get_args(AravisController.GPIO_NUMBER)
57
+ if gpio_number not in supported_gpio_numbers:
58
+ raise ValueError(
59
+ f"{self.__class__.__name__} only supports the following GPIO "
60
+ f"indices: {supported_gpio_numbers} but was asked to "
61
+ f"use {gpio_number}"
62
+ )
63
+ self._controller.gpio_number = gpio_number
64
+
65
+ @property
66
+ def hints(self) -> Hints:
67
+ return self._writer.hints
@@ -1,4 +1,5 @@
1
1
  from .ad_sim_controller import ADSimController
2
+ from .aravis_controller import AravisController
2
3
  from .pilatus_controller import PilatusController
3
4
 
4
- __all__ = ["PilatusController", "ADSimController"]
5
+ __all__ = ["PilatusController", "ADSimController", "AravisController"]
@@ -0,0 +1,73 @@
1
+ import asyncio
2
+ from typing import Literal, Optional, Tuple
3
+
4
+ from ophyd_async.core import (
5
+ AsyncStatus,
6
+ DetectorControl,
7
+ DetectorTrigger,
8
+ set_and_wait_for_value,
9
+ )
10
+ from ophyd_async.epics.areadetector.drivers.aravis_driver import (
11
+ AravisDriver,
12
+ AravisTriggerMode,
13
+ AravisTriggerSource,
14
+ )
15
+ from ophyd_async.epics.areadetector.utils import ImageMode, stop_busy_record
16
+
17
+
18
+ class AravisController(DetectorControl):
19
+ GPIO_NUMBER = Literal[1, 2, 3, 4]
20
+
21
+ def __init__(self, driver: AravisDriver, gpio_number: GPIO_NUMBER) -> None:
22
+ self._drv = driver
23
+ self.gpio_number = gpio_number
24
+
25
+ def get_deadtime(self, exposure: float) -> float:
26
+ return self._drv.dead_time or 0
27
+
28
+ async def arm(
29
+ self,
30
+ num: int = 0,
31
+ trigger: DetectorTrigger = DetectorTrigger.internal,
32
+ exposure: Optional[float] = None,
33
+ ) -> AsyncStatus:
34
+ if num == 0:
35
+ image_mode = ImageMode.continuous
36
+ else:
37
+ image_mode = ImageMode.multiple
38
+ if exposure is not None:
39
+ await self._drv.acquire_time.set(exposure)
40
+
41
+ trigger_mode, trigger_source = self._get_trigger_info(trigger)
42
+ # trigger mode must be set first and on it's own!
43
+ await self._drv.trigger_mode.set(trigger_mode)
44
+
45
+ await asyncio.gather(
46
+ self._drv.trigger_source.set(trigger_source),
47
+ self._drv.num_images.set(num),
48
+ self._drv.image_mode.set(image_mode),
49
+ )
50
+
51
+ status = await set_and_wait_for_value(self._drv.acquire, True)
52
+ return status
53
+
54
+ def _get_trigger_info(
55
+ self, trigger: DetectorTrigger
56
+ ) -> Tuple[AravisTriggerMode, AravisTriggerSource]:
57
+ supported_trigger_types = (
58
+ DetectorTrigger.constant_gate,
59
+ DetectorTrigger.edge_trigger,
60
+ )
61
+ if trigger not in supported_trigger_types:
62
+ raise ValueError(
63
+ f"{self.__class__.__name__} only supports the following trigger "
64
+ f"types: {supported_trigger_types} but was asked to "
65
+ f"use {trigger}"
66
+ )
67
+ if trigger == DetectorTrigger.internal:
68
+ return AravisTriggerMode.off, "Freerun"
69
+ else:
70
+ return (AravisTriggerMode.on, f"Line{self.gpio_number}")
71
+
72
+ async def disarm(self):
73
+ await stop_busy_record(self._drv.acquire, False, timeout=1)
@@ -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,34 +1,36 @@
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,
@@ -36,14 +38,24 @@ class PilatusController(DetectorControl):
36
38
  trigger: DetectorTrigger = DetectorTrigger.internal,
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
  ]
@@ -0,0 +1,154 @@
1
+ from enum import Enum
2
+ from typing import Callable, Dict, Literal, Optional, Tuple
3
+
4
+ from ophyd_async.epics.areadetector.drivers import ADBase
5
+ from ophyd_async.epics.areadetector.utils import ad_r, ad_rw
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
+ def _reverse_lookup(
26
+ model_deadtimes: Dict[float, Tuple[str, ...]],
27
+ ) -> Callable[[str], float]:
28
+ def inner(pixel_format: str, model_name: str) -> float:
29
+ for deadtime, pixel_formats in model_deadtimes.items():
30
+ if pixel_format in pixel_formats:
31
+ return deadtime
32
+ raise ValueError(
33
+ f"Model {model_name} does not have a defined deadtime "
34
+ f"for pixel format {pixel_format}"
35
+ )
36
+
37
+ return inner
38
+
39
+
40
+ _deadtimes: Dict[str, Callable[[str, str], float]] = {
41
+ # cite: https://cdn.alliedvision.com/fileadmin/content/documents/products/cameras/Manta/techman/Manta_TechMan.pdf retrieved 2024-04-05 # noqa: E501
42
+ "Manta G-125": lambda _, __: 63e-6,
43
+ "Manta G-145": lambda _, __: 106e-6,
44
+ "Manta G-235": _reverse_lookup(
45
+ {
46
+ 118e-6: (
47
+ "Mono8",
48
+ "Mono12Packed",
49
+ "BayerRG8",
50
+ "BayerRG12",
51
+ "BayerRG12Packed",
52
+ "YUV411Packed",
53
+ ),
54
+ 256e-6: ("Mono12", "BayerRG12", "YUV422Packed"),
55
+ 390e-6: ("RGB8Packed", "BGR8Packed", "YUV444Packed"),
56
+ }
57
+ ),
58
+ "Manta G-895": _reverse_lookup(
59
+ {
60
+ 404e-6: (
61
+ "Mono8",
62
+ "Mono12Packed",
63
+ "BayerRG8",
64
+ "BayerRG12Packed",
65
+ "YUV411Packed",
66
+ ),
67
+ 542e-6: ("Mono12", "BayerRG12", "YUV422Packed"),
68
+ 822e-6: ("RGB8Packed", "BGR8Packed", "YUV444Packed"),
69
+ }
70
+ ),
71
+ "Manta G-2460": _reverse_lookup(
72
+ {
73
+ 979e-6: (
74
+ "Mono8",
75
+ "Mono12Packed",
76
+ "BayerRG8",
77
+ "BayerRG12Packed",
78
+ "YUV411Packed",
79
+ ),
80
+ 1304e-6: ("Mono12", "BayerRG12", "YUV422Packed"),
81
+ 1961e-6: ("RGB8Packed", "BGR8Packed", "YUV444Packed"),
82
+ }
83
+ ),
84
+ # cite: https://cdn.alliedvision.com/fileadmin/content/documents/products/cameras/various/appnote/GigE/GigE-Cameras_AppNote_PIV-Min-Time-Between-Exposures.pdf retrieved 2024-04-05 # noqa: E501
85
+ "Manta G-609": lambda _, __: 47e-6,
86
+ # cite: https://cdn.alliedvision.com/fileadmin/content/documents/products/cameras/Mako/techman/Mako_TechMan_en.pdf retrieved 2024-04-05 # noqa: E501
87
+ "Mako G-040": _reverse_lookup(
88
+ {
89
+ 101e-6: (
90
+ "Mono8",
91
+ "Mono12Packed",
92
+ "BayerRG8",
93
+ "BayerRG12Packed",
94
+ "YUV411Packed",
95
+ ),
96
+ 140e-6: ("Mono12", "BayerRG12", "YUV422Packed"),
97
+ 217e-6: ("RGB8Packed", "BGR8Packed", "YUV444Packed"),
98
+ }
99
+ ),
100
+ "Mako G-125": lambda _, __: 70e-6,
101
+ # Assume 12 bits: 10 bits = 275e-6
102
+ "Mako G-234": _reverse_lookup(
103
+ {
104
+ 356e-6: (
105
+ "Mono8",
106
+ "BayerRG8",
107
+ "BayerRG12",
108
+ "BayerRG12Packed",
109
+ "YUV411Packed",
110
+ "YUV422Packed",
111
+ ),
112
+ # Assume 12 bits: 10 bits = 563e-6
113
+ 726e-6: ("RGB8Packed", "BRG8Packed", "YUV444Packed"),
114
+ }
115
+ ),
116
+ "Mako G-507": _reverse_lookup(
117
+ {
118
+ 270e-6: (
119
+ "Mono8",
120
+ "Mono12Packed",
121
+ "BayerRG8",
122
+ "BayerRG12Packed",
123
+ "YUV411Packed",
124
+ ),
125
+ 363e-6: ("Mono12", "BayerRG12", "YUV422Packed"),
126
+ 554e-6: ("RGB8Packed", "BGR8Packed", "YUV444Packed"),
127
+ }
128
+ ),
129
+ }
130
+
131
+
132
+ class AravisDriver(ADBase):
133
+ # If instantiating a new instance, ensure it is supported in the _deadtimes dict
134
+ """Generic Driver supporting the Manta and Mako drivers.
135
+ Fetches deadtime prior to use in a Streaming scan.
136
+ Requires driver firmware up to date:
137
+ - Model_RBV must be of the form "^(Mako|Manta) (model)$"
138
+ """
139
+
140
+ def __init__(self, prefix: str, name: str = "") -> None:
141
+ self.trigger_mode = ad_rw(AravisTriggerMode, prefix + "TriggerMode")
142
+ self.trigger_source = ad_rw(str, prefix + "TriggerSource")
143
+ self.model = ad_r(str, prefix + "Model")
144
+ self.pixel_format = ad_rw(str, prefix + "PixelFormat")
145
+ self.dead_time: Optional[float] = None
146
+ super().__init__(prefix, name=name)
147
+
148
+ async def fetch_deadtime(self) -> None:
149
+ # All known in-use version B/C have same deadtime as non-B/C
150
+ model: str = (await self.model.get_value()).removesuffix("B").removesuffix("C")
151
+ if model not in _deadtimes:
152
+ raise ValueError(f"Model {model} does not have defined deadtimes")
153
+ pixel_format: str = await self.pixel_format.get_value()
154
+ self.dead_time = _deadtimes.get(model)(pixel_format, model)
@@ -0,0 +1,24 @@
1
+ from enum import Enum
2
+
3
+ from ..utils import ad_rw
4
+ from .ad_base import ADBase
5
+
6
+
7
+ class KinetixTriggerMode(str, Enum):
8
+ internal = "Internal"
9
+ edge = "Rising Edge"
10
+ gate = "Exp. Gate"
11
+
12
+
13
+ class KinetixReadoutMode(str, Enum):
14
+ sensitivity = 1
15
+ speed = 2
16
+ dynamic_range = 3
17
+
18
+
19
+ class KinetixDriver(ADBase):
20
+ def __init__(self, prefix: str, name: str = "") -> None:
21
+ # self.pixel_format = ad_rw(PixelFormat, prefix + "PixelFormat")
22
+ self.trigger_mode = ad_rw(KinetixTriggerMode, prefix + "TriggerMode")
23
+ self.mode = ad_rw(KinetixReadoutMode, prefix + "ReadoutPortIdx")
24
+ super().__init__(prefix, name)
@@ -4,7 +4,7 @@ from ..utils import ad_rw
4
4
  from .ad_base import ADBase
5
5
 
6
6
 
7
- class TriggerMode(str, Enum):
7
+ class PilatusTriggerMode(str, Enum):
8
8
  internal = "Internal"
9
9
  ext_enable = "Ext. Enable"
10
10
  ext_trigger = "Ext. Trigger"
@@ -13,6 +13,6 @@ class TriggerMode(str, Enum):
13
13
 
14
14
 
15
15
  class PilatusDriver(ADBase):
16
- def __init__(self, prefix: str) -> None:
17
- self.trigger_mode = ad_rw(TriggerMode, prefix + "TriggerMode")
18
- super().__init__(prefix)
16
+ def __init__(self, prefix: str, name: str = "") -> None:
17
+ self.trigger_mode = ad_rw(PilatusTriggerMode, prefix + "TriggerMode")
18
+ super().__init__(prefix, name)