ophyd-async 0.3.4a1__py3-none-any.whl → 0.5.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 (103) hide show
  1. ophyd_async/_version.py +2 -2
  2. ophyd_async/core/__init__.py +86 -63
  3. ophyd_async/core/{detector.py → _detector.py} +18 -23
  4. ophyd_async/core/{device.py → _device.py} +19 -7
  5. ophyd_async/core/{device_save_loader.py → _device_save_loader.py} +3 -3
  6. ophyd_async/core/{flyer.py → _flyer.py} +6 -8
  7. ophyd_async/core/_hdf_dataset.py +97 -0
  8. ophyd_async/{log.py → core/_log.py} +11 -3
  9. ophyd_async/core/{mock_signal_backend.py → _mock_signal_backend.py} +3 -3
  10. ophyd_async/core/{mock_signal_utils.py → _mock_signal_utils.py} +3 -4
  11. ophyd_async/{protocols.py → core/_protocol.py} +1 -1
  12. ophyd_async/core/_providers.py +186 -24
  13. ophyd_async/core/{standard_readable.py → _readable.py} +6 -16
  14. ophyd_async/core/{signal.py → _signal.py} +39 -16
  15. ophyd_async/core/{signal_backend.py → _signal_backend.py} +4 -13
  16. ophyd_async/core/{soft_signal_backend.py → _soft_signal_backend.py} +24 -18
  17. ophyd_async/core/{async_status.py → _status.py} +3 -11
  18. ophyd_async/epics/adaravis/__init__.py +9 -0
  19. ophyd_async/epics/{areadetector/aravis.py → adaravis/_aravis.py} +12 -14
  20. ophyd_async/epics/{areadetector/controllers/aravis_controller.py → adaravis/_aravis_controller.py} +8 -10
  21. ophyd_async/epics/{areadetector/drivers/aravis_driver.py → adaravis/_aravis_io.py} +6 -3
  22. ophyd_async/epics/adcore/__init__.py +36 -0
  23. ophyd_async/epics/adcore/_core_io.py +114 -0
  24. ophyd_async/epics/{areadetector/drivers/ad_base.py → adcore/_core_logic.py} +17 -52
  25. ophyd_async/epics/{areadetector/writers/hdf_writer.py → adcore/_hdf_writer.py} +36 -18
  26. ophyd_async/epics/{areadetector/single_trigger_det.py → adcore/_single_trigger.py} +5 -6
  27. ophyd_async/epics/{areadetector/utils.py → adcore/_utils.py} +29 -0
  28. ophyd_async/epics/adkinetix/__init__.py +9 -0
  29. ophyd_async/epics/{areadetector/kinetix.py → adkinetix/_kinetix.py} +12 -14
  30. ophyd_async/epics/{areadetector/controllers/kinetix_controller.py → adkinetix/_kinetix_controller.py} +6 -9
  31. ophyd_async/epics/{areadetector/drivers/kinetix_driver.py → adkinetix/_kinetix_io.py} +5 -4
  32. ophyd_async/epics/adpilatus/__init__.py +11 -0
  33. ophyd_async/epics/{areadetector/pilatus.py → adpilatus/_pilatus.py} +12 -16
  34. ophyd_async/epics/{areadetector/controllers/pilatus_controller.py → adpilatus/_pilatus_controller.py} +14 -16
  35. ophyd_async/epics/{areadetector/drivers/pilatus_driver.py → adpilatus/_pilatus_io.py} +5 -3
  36. ophyd_async/epics/adsimdetector/__init__.py +7 -0
  37. ophyd_async/epics/adsimdetector/_sim.py +34 -0
  38. ophyd_async/epics/{areadetector/controllers/ad_sim_controller.py → adsimdetector/_sim_controller.py} +8 -14
  39. ophyd_async/epics/advimba/__init__.py +9 -0
  40. ophyd_async/epics/advimba/_vimba.py +43 -0
  41. ophyd_async/epics/{areadetector/controllers/vimba_controller.py → advimba/_vimba_controller.py} +6 -14
  42. ophyd_async/epics/{areadetector/drivers/vimba_driver.py → advimba/_vimba_io.py} +5 -4
  43. ophyd_async/epics/demo/__init__.py +9 -132
  44. ophyd_async/epics/demo/_mover.py +97 -0
  45. ophyd_async/epics/demo/_sensor.py +36 -0
  46. ophyd_async/epics/motor.py +228 -0
  47. ophyd_async/epics/pvi/__init__.py +2 -2
  48. ophyd_async/epics/pvi/{pvi.py → _pvi.py} +17 -14
  49. ophyd_async/epics/signal/__init__.py +7 -1
  50. ophyd_async/epics/{_backend → signal}/_aioca.py +6 -2
  51. ophyd_async/epics/{_backend/common.py → signal/_common.py} +4 -2
  52. ophyd_async/epics/signal/_epics_transport.py +3 -3
  53. ophyd_async/epics/{_backend → signal}/_p4p.py +53 -4
  54. ophyd_async/epics/signal/{signal.py → _signal.py} +10 -9
  55. ophyd_async/fastcs/odin/__init__.py +0 -0
  56. ophyd_async/{panda → fastcs/panda}/__init__.py +28 -9
  57. ophyd_async/{panda → fastcs/panda}/_common_blocks.py +24 -3
  58. ophyd_async/{panda → fastcs/panda}/_hdf_panda.py +6 -9
  59. ophyd_async/{panda/writers → fastcs/panda}/_hdf_writer.py +24 -14
  60. ophyd_async/{panda → fastcs/panda}/_panda_controller.py +2 -1
  61. ophyd_async/{panda → fastcs/panda}/_table.py +20 -18
  62. ophyd_async/fastcs/panda/_trigger.py +90 -0
  63. ophyd_async/plan_stubs/__init__.py +2 -2
  64. ophyd_async/plan_stubs/_ensure_connected.py +26 -0
  65. ophyd_async/plan_stubs/{fly.py → _fly.py} +67 -12
  66. ophyd_async/sim/__init__.py +0 -11
  67. ophyd_async/sim/demo/__init__.py +18 -2
  68. ophyd_async/sim/demo/_pattern_detector/__init__.py +13 -0
  69. ophyd_async/sim/demo/_pattern_detector/_pattern_detector.py +42 -0
  70. ophyd_async/sim/{sim_pattern_detector_control.py → demo/_pattern_detector/_pattern_detector_controller.py} +6 -7
  71. ophyd_async/sim/{sim_pattern_detector_writer.py → demo/_pattern_detector/_pattern_detector_writer.py} +12 -8
  72. ophyd_async/sim/demo/_pattern_detector/_pattern_generator.py +211 -0
  73. ophyd_async/sim/demo/{sim_motor.py → _sim_motor.py} +7 -5
  74. ophyd_async/sim/testing/__init__.py +0 -0
  75. ophyd_async/tango/__init__.py +0 -0
  76. {ophyd_async-0.3.4a1.dist-info → ophyd_async-0.5.0.dist-info}/METADATA +7 -2
  77. ophyd_async-0.5.0.dist-info/RECORD +89 -0
  78. {ophyd_async-0.3.4a1.dist-info → ophyd_async-0.5.0.dist-info}/WHEEL +1 -1
  79. ophyd_async/epics/areadetector/__init__.py +0 -23
  80. ophyd_async/epics/areadetector/controllers/__init__.py +0 -5
  81. ophyd_async/epics/areadetector/drivers/__init__.py +0 -23
  82. ophyd_async/epics/areadetector/vimba.py +0 -43
  83. ophyd_async/epics/areadetector/writers/__init__.py +0 -5
  84. ophyd_async/epics/areadetector/writers/_hdfdataset.py +0 -10
  85. ophyd_async/epics/areadetector/writers/_hdffile.py +0 -54
  86. ophyd_async/epics/areadetector/writers/nd_file_hdf.py +0 -40
  87. ophyd_async/epics/areadetector/writers/nd_plugin.py +0 -38
  88. ophyd_async/epics/demo/demo_ad_sim_detector.py +0 -35
  89. ophyd_async/epics/motion/__init__.py +0 -3
  90. ophyd_async/epics/motion/motor.py +0 -97
  91. ophyd_async/panda/_trigger.py +0 -39
  92. ophyd_async/panda/writers/__init__.py +0 -3
  93. ophyd_async/panda/writers/_panda_hdf_file.py +0 -54
  94. ophyd_async/plan_stubs/ensure_connected.py +0 -22
  95. ophyd_async/sim/pattern_generator.py +0 -318
  96. ophyd_async/sim/sim_pattern_generator.py +0 -35
  97. ophyd_async-0.3.4a1.dist-info/RECORD +0 -86
  98. /ophyd_async/core/{utils.py → _utils.py} +0 -0
  99. /ophyd_async/{epics/_backend → fastcs}/__init__.py +0 -0
  100. /ophyd_async/{panda → fastcs/panda}/_utils.py +0 -0
  101. {ophyd_async-0.3.4a1.dist-info → ophyd_async-0.5.0.dist-info}/LICENSE +0 -0
  102. {ophyd_async-0.3.4a1.dist-info → ophyd_async-0.5.0.dist-info}/entry_points.txt +0 -0
  103. {ophyd_async-0.3.4a1.dist-info → ophyd_async-0.5.0.dist-info}/top_level.txt +0 -0
@@ -2,8 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  from enum import Enum
4
4
 
5
- from ophyd_async.core import Device, DeviceVector, SignalR, SignalRW
6
- from ophyd_async.panda._table import DatasetTable, SeqTable
5
+ from ophyd_async.core import Device, DeviceVector, SignalR, SignalRW, SubsetEnum
6
+
7
+ from ._table import DatasetTable, SeqTable
7
8
 
8
9
 
9
10
  class DataBlock(Device):
@@ -22,6 +23,25 @@ class PulseBlock(Device):
22
23
  width: SignalRW[float]
23
24
 
24
25
 
26
+ class PcompDirectionOptions(str, Enum):
27
+ positive = "Positive"
28
+ negative = "Negative"
29
+ either = "Either"
30
+
31
+
32
+ EnableDisableOptions = SubsetEnum["ZERO", "ONE"]
33
+
34
+
35
+ class PcompBlock(Device):
36
+ active: SignalR[bool]
37
+ dir: SignalRW[PcompDirectionOptions]
38
+ enable: SignalRW[EnableDisableOptions]
39
+ pulses: SignalRW[int]
40
+ start: SignalRW[int]
41
+ step: SignalRW[int]
42
+ width: SignalRW[int]
43
+
44
+
25
45
  class TimeUnits(str, Enum):
26
46
  min = "min"
27
47
  s = "s"
@@ -35,7 +55,7 @@ class SeqBlock(Device):
35
55
  repeats: SignalRW[int]
36
56
  prescale: SignalRW[float]
37
57
  prescale_units: SignalRW[TimeUnits]
38
- enable: SignalRW[str]
58
+ enable: SignalRW[EnableDisableOptions]
39
59
 
40
60
 
41
61
  class PcapBlock(Device):
@@ -46,5 +66,6 @@ class PcapBlock(Device):
46
66
  class CommonPandaBlocks(Device):
47
67
  pulse: DeviceVector[PulseBlock]
48
68
  seq: DeviceVector[SeqBlock]
69
+ pcomp: DeviceVector[PcompBlock]
49
70
  pcap: PcapBlock
50
71
  data: DataBlock
@@ -2,24 +2,19 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Sequence
4
4
 
5
- from ophyd_async.core import (
6
- DEFAULT_TIMEOUT,
7
- DirectoryProvider,
8
- SignalR,
9
- StandardDetector,
10
- )
5
+ from ophyd_async.core import DEFAULT_TIMEOUT, PathProvider, SignalR, StandardDetector
11
6
  from ophyd_async.epics.pvi import create_children_from_annotations, fill_pvi_entries
12
7
 
13
8
  from ._common_blocks import CommonPandaBlocks
9
+ from ._hdf_writer import PandaHDFWriter
14
10
  from ._panda_controller import PandaPcapController
15
- from .writers._hdf_writer import PandaHDFWriter
16
11
 
17
12
 
18
13
  class HDFPanda(CommonPandaBlocks, StandardDetector):
19
14
  def __init__(
20
15
  self,
21
16
  prefix: str,
22
- directory_provider: DirectoryProvider,
17
+ path_provider: PathProvider,
23
18
  config_sigs: Sequence[SignalR] = (),
24
19
  name: str = "",
25
20
  ):
@@ -28,7 +23,9 @@ class HDFPanda(CommonPandaBlocks, StandardDetector):
28
23
  create_children_from_annotations(self)
29
24
  controller = PandaPcapController(pcap=self.pcap)
30
25
  writer = PandaHDFWriter(
31
- directory_provider=directory_provider,
26
+ prefix=prefix,
27
+ path_provider=path_provider,
28
+ name_provider=lambda: name,
32
29
  panda_device=self,
33
30
  )
34
31
  super().__init__(
@@ -8,13 +8,15 @@ from p4p.client.thread import Context
8
8
  from ophyd_async.core import (
9
9
  DEFAULT_TIMEOUT,
10
10
  DetectorWriter,
11
- DirectoryProvider,
11
+ HDFDataset,
12
+ HDFFile,
13
+ NameProvider,
14
+ PathProvider,
15
+ observe_value,
12
16
  wait_for_value,
13
17
  )
14
- from ophyd_async.core.signal import observe_value
15
18
 
16
- from .._common_blocks import CommonPandaBlocks
17
- from ._panda_hdf_file import _HDFDataset, _HDFFile
19
+ from ._common_blocks import CommonPandaBlocks
18
20
 
19
21
 
20
22
  class PandaHDFWriter(DetectorWriter):
@@ -22,13 +24,17 @@ class PandaHDFWriter(DetectorWriter):
22
24
 
23
25
  def __init__(
24
26
  self,
25
- directory_provider: DirectoryProvider,
27
+ prefix: str,
28
+ path_provider: PathProvider,
29
+ name_provider: NameProvider,
26
30
  panda_device: CommonPandaBlocks,
27
31
  ) -> None:
28
32
  self.panda_device = panda_device
29
- self._directory_provider = directory_provider
30
- self._datasets: List[_HDFDataset] = []
31
- self._file: Optional[_HDFFile] = None
33
+ self._prefix = prefix
34
+ self._path_provider = path_provider
35
+ self._name_provider = name_provider
36
+ self._datasets: List[HDFDataset] = []
37
+ self._file: Optional[HDFFile] = None
32
38
  self._multiplier = 1
33
39
 
34
40
  # Triggered on PCAP arm
@@ -39,16 +45,18 @@ class PandaHDFWriter(DetectorWriter):
39
45
  await self.panda_device.data.flush_period.set(0)
40
46
 
41
47
  self._file = None
42
- info = self._directory_provider()
48
+ info = self._path_provider(device_name=self.panda_device.name)
43
49
  # Set the initial values
44
50
  await asyncio.gather(
45
51
  self.panda_device.data.hdf_directory.set(
46
52
  str(info.root / info.resource_dir)
47
53
  ),
48
54
  self.panda_device.data.hdf_file_name.set(
49
- f"{info.prefix}{self.panda_device.name}{info.suffix}.h5",
55
+ f"{info.filename}.h5",
50
56
  ),
51
57
  self.panda_device.data.num_capture.set(0),
58
+ # TODO: Set create_dir_depth once available
59
+ # https://github.com/bluesky/ophyd-async/issues/317
52
60
  )
53
61
 
54
62
  # Wait for it to start, stashing the status that tells us when it finishes
@@ -71,6 +79,7 @@ class PandaHDFWriter(DetectorWriter):
71
79
  source=self.panda_device.data.hdf_directory.source,
72
80
  shape=ds.shape,
73
81
  dtype="array" if ds.shape != [1] else "number",
82
+ dtype_numpy="<f8", # PandA data should always be written as Float64
74
83
  external="STREAM:",
75
84
  )
76
85
  for ds in self._datasets
@@ -85,7 +94,7 @@ class PandaHDFWriter(DetectorWriter):
85
94
 
86
95
  capture_table = await self.panda_device.data.datasets.get_value()
87
96
  self._datasets = [
88
- _HDFDataset(dataset_name, "/" + dataset_name, [1], multiplier=1)
97
+ HDFDataset(dataset_name, "/" + dataset_name, [1], multiplier=1)
89
98
  for dataset_name in capture_table["name"]
90
99
  ]
91
100
 
@@ -120,9 +129,10 @@ class PandaHDFWriter(DetectorWriter):
120
129
  # TODO: fail if we get dropped frames
121
130
  if indices_written:
122
131
  if not self._file:
123
- self._file = _HDFFile(
124
- self._directory_provider(),
125
- Path(await self.panda_device.data.hdf_file_name.get_value()),
132
+ self._file = HDFFile(
133
+ self._path_provider(),
134
+ Path(await self.panda_device.data.hdf_directory.get_value())
135
+ / Path(await self.panda_device.data.hdf_file_name.get_value()),
126
136
  self._datasets,
127
137
  )
128
138
  for doc in self._file.stream_resources():
@@ -7,7 +7,8 @@ from ophyd_async.core import (
7
7
  DetectorTrigger,
8
8
  wait_for_value,
9
9
  )
10
- from ophyd_async.panda import PcapBlock
10
+
11
+ from ._common_blocks import PcapBlock
11
12
 
12
13
 
13
14
  class PandaPcapController(DetectorControl):
@@ -1,9 +1,11 @@
1
1
  from dataclasses import dataclass
2
2
  from enum import Enum
3
- from typing import Optional, Sequence, Type, TypedDict, TypeVar
3
+ from typing import Optional, Sequence, Type, TypeVar
4
4
 
5
5
  import numpy as np
6
6
  import numpy.typing as npt
7
+ import pydantic_numpy.typing as pnd
8
+ from typing_extensions import NotRequired, TypedDict
7
9
 
8
10
 
9
11
  class PandaHdf5DatasetType(str, Enum):
@@ -54,23 +56,23 @@ class SeqTableRow:
54
56
 
55
57
 
56
58
  class SeqTable(TypedDict):
57
- repeats: npt.NDArray[np.uint16]
58
- trigger: Sequence[SeqTrigger]
59
- position: npt.NDArray[np.int32]
60
- time1: npt.NDArray[np.uint32]
61
- outa1: npt.NDArray[np.bool_]
62
- outb1: npt.NDArray[np.bool_]
63
- outc1: npt.NDArray[np.bool_]
64
- outd1: npt.NDArray[np.bool_]
65
- oute1: npt.NDArray[np.bool_]
66
- outf1: npt.NDArray[np.bool_]
67
- time2: npt.NDArray[np.uint32]
68
- outa2: npt.NDArray[np.bool_]
69
- outb2: npt.NDArray[np.bool_]
70
- outc2: npt.NDArray[np.bool_]
71
- outd2: npt.NDArray[np.bool_]
72
- oute2: npt.NDArray[np.bool_]
73
- outf2: npt.NDArray[np.bool_]
59
+ repeats: NotRequired[pnd.Np1DArrayUint16]
60
+ trigger: NotRequired[Sequence[SeqTrigger]]
61
+ position: NotRequired[pnd.Np1DArrayInt32]
62
+ time1: NotRequired[pnd.Np1DArrayUint32]
63
+ outa1: NotRequired[pnd.Np1DArrayBool]
64
+ outb1: NotRequired[pnd.Np1DArrayBool]
65
+ outc1: NotRequired[pnd.Np1DArrayBool]
66
+ outd1: NotRequired[pnd.Np1DArrayBool]
67
+ oute1: NotRequired[pnd.Np1DArrayBool]
68
+ outf1: NotRequired[pnd.Np1DArrayBool]
69
+ time2: NotRequired[pnd.Np1DArrayUint32]
70
+ outa2: NotRequired[pnd.Np1DArrayBool]
71
+ outb2: NotRequired[pnd.Np1DArrayBool]
72
+ outc2: NotRequired[pnd.Np1DArrayBool]
73
+ outd2: NotRequired[pnd.Np1DArrayBool]
74
+ oute2: NotRequired[pnd.Np1DArrayBool]
75
+ outf2: NotRequired[pnd.Np1DArrayBool]
74
76
 
75
77
 
76
78
  def seq_table_from_rows(*rows: SeqTableRow):
@@ -0,0 +1,90 @@
1
+ import asyncio
2
+ from typing import Optional
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ from ophyd_async.core import TriggerLogic, wait_for_value
7
+
8
+ from ._common_blocks import PcompBlock, PcompDirectionOptions, SeqBlock, TimeUnits
9
+ from ._table import SeqTable
10
+
11
+
12
+ class SeqTableInfo(BaseModel):
13
+ sequence_table: SeqTable = Field(strict=True)
14
+ repeats: int = Field(ge=0)
15
+ prescale_as_us: float = Field(default=1, ge=0) # microseconds
16
+
17
+
18
+ class StaticSeqTableTriggerLogic(TriggerLogic[SeqTableInfo]):
19
+ def __init__(self, seq: SeqBlock) -> None:
20
+ self.seq = seq
21
+
22
+ async def prepare(self, value: SeqTableInfo):
23
+ await asyncio.gather(
24
+ self.seq.prescale_units.set(TimeUnits.us),
25
+ self.seq.enable.set("ZERO"),
26
+ )
27
+ await asyncio.gather(
28
+ self.seq.prescale.set(value.prescale_as_us),
29
+ self.seq.repeats.set(value.repeats),
30
+ self.seq.table.set(value.sequence_table),
31
+ )
32
+
33
+ async def kickoff(self) -> None:
34
+ await self.seq.enable.set("ONE")
35
+ await wait_for_value(self.seq.active, True, timeout=1)
36
+
37
+ async def complete(self) -> None:
38
+ await wait_for_value(self.seq.active, False, timeout=None)
39
+
40
+ async def stop(self):
41
+ await self.seq.enable.set("ZERO")
42
+ await wait_for_value(self.seq.active, False, timeout=1)
43
+
44
+
45
+ class PcompInfo(BaseModel):
46
+ start_postion: int = Field(description="start position in counts")
47
+ pulse_width: int = Field(description="width of a single pulse in counts", gt=0)
48
+ rising_edge_step: int = Field(
49
+ description="step between rising edges of pulses in counts", gt=0
50
+ ) #
51
+ number_of_pulses: int = Field(
52
+ description=(
53
+ "Number of pulses to send before the PCOMP block is disarmed. "
54
+ "0 means infinite."
55
+ ),
56
+ ge=0,
57
+ )
58
+ direction: PcompDirectionOptions = Field(
59
+ description=(
60
+ "Specifies which direction the motor counts should be "
61
+ "moving. Pulses won't be sent unless the values are moving in "
62
+ "this direction."
63
+ )
64
+ )
65
+
66
+
67
+ class StaticPcompTriggerLogic(TriggerLogic[PcompInfo]):
68
+ def __init__(self, pcomp: PcompBlock) -> None:
69
+ self.pcomp = pcomp
70
+
71
+ async def prepare(self, value: PcompInfo):
72
+ await self.pcomp.enable.set("ZERO")
73
+ await asyncio.gather(
74
+ self.pcomp.start.set(value.start_postion),
75
+ self.pcomp.width.set(value.pulse_width),
76
+ self.pcomp.step.set(value.rising_edge_step),
77
+ self.pcomp.pulses.set(value.number_of_pulses),
78
+ self.pcomp.dir.set(value.direction),
79
+ )
80
+
81
+ async def kickoff(self) -> None:
82
+ await self.pcomp.enable.set("ONE")
83
+ await wait_for_value(self.pcomp.active, True, timeout=1)
84
+
85
+ async def complete(self, timeout: Optional[float] = None) -> None:
86
+ await wait_for_value(self.pcomp.active, False, timeout=timeout)
87
+
88
+ async def stop(self):
89
+ await self.pcomp.enable.set("ZERO")
90
+ await wait_for_value(self.pcomp.active, False, timeout=1)
@@ -1,5 +1,5 @@
1
- from .ensure_connected import ensure_connected
2
- from .fly import (
1
+ from ._ensure_connected import ensure_connected
2
+ from ._fly import (
3
3
  fly_and_collect,
4
4
  prepare_static_seq_table_flyer_and_detectors_with_same_trigger,
5
5
  time_resolved_fly_and_collect_with_static_seq_table,
@@ -0,0 +1,26 @@
1
+ import bluesky.plan_stubs as bps
2
+
3
+ from ophyd_async.core import DEFAULT_TIMEOUT, Device, wait_for_connection
4
+
5
+
6
+ def ensure_connected(
7
+ *devices: Device,
8
+ mock: bool = False,
9
+ timeout: float = DEFAULT_TIMEOUT,
10
+ force_reconnect=False,
11
+ ):
12
+ (connect_task,) = yield from bps.wait_for(
13
+ [
14
+ lambda: wait_for_connection(
15
+ **{
16
+ device.name: device.connect(
17
+ mock=mock, timeout=timeout, force_reconnect=force_reconnect
18
+ )
19
+ for device in devices
20
+ }
21
+ )
22
+ ]
23
+ )
24
+
25
+ if connect_task and connect_task.exception() is not None:
26
+ raise connect_task.exception()
@@ -1,24 +1,53 @@
1
- from typing import List
1
+ from typing import List, Optional
2
2
 
3
3
  import bluesky.plan_stubs as bps
4
4
  from bluesky.utils import short_uid
5
5
 
6
- from ophyd_async.core.detector import DetectorTrigger, StandardDetector, TriggerInfo
7
- from ophyd_async.core.flyer import HardwareTriggeredFlyable
8
- from ophyd_async.core.utils import in_micros
9
- from ophyd_async.panda._table import SeqTable, SeqTableRow, seq_table_from_rows
10
- from ophyd_async.panda._trigger import SeqTableInfo
6
+ from ophyd_async.core import (
7
+ DetectorTrigger,
8
+ StandardDetector,
9
+ StandardFlyer,
10
+ TriggerInfo,
11
+ in_micros,
12
+ )
13
+ from ophyd_async.fastcs.panda import (
14
+ PcompDirectionOptions,
15
+ PcompInfo,
16
+ SeqTable,
17
+ SeqTableInfo,
18
+ SeqTableRow,
19
+ seq_table_from_rows,
20
+ )
21
+
22
+
23
+ def prepare_static_pcomp_flyer_and_detectors(
24
+ flyer: StandardFlyer[PcompInfo],
25
+ detectors: List[StandardDetector],
26
+ pcomp_info: PcompInfo,
27
+ trigger_info: TriggerInfo,
28
+ ):
29
+ """Prepare a hardware triggered flyable and one or more detectors.
30
+
31
+ Prepare a hardware triggered flyable and one or more detectors with the
32
+ same trigger.
33
+
34
+ """
35
+
36
+ for det in detectors:
37
+ yield from bps.prepare(det, trigger_info, wait=False, group="prep")
38
+ yield from bps.prepare(flyer, pcomp_info, wait=False, group="prep")
39
+ yield from bps.wait(group="prep")
11
40
 
12
41
 
13
42
  def prepare_static_seq_table_flyer_and_detectors_with_same_trigger(
14
- flyer: HardwareTriggeredFlyable[SeqTableInfo],
43
+ flyer: StandardFlyer[SeqTableInfo],
15
44
  detectors: List[StandardDetector],
16
45
  number_of_frames: int,
17
46
  exposure: float,
18
47
  shutter_time: float,
19
48
  repeats: int = 1,
20
49
  period: float = 0.0,
21
- frame_timeout: float | None = None,
50
+ frame_timeout: Optional[float] = None,
22
51
  ):
23
52
  """Prepare a hardware triggered flyable and one or more detectors.
24
53
 
@@ -36,7 +65,7 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger(
36
65
  deadtime = max(det.controller.get_deadtime(exposure) for det in detectors)
37
66
 
38
67
  trigger_info = TriggerInfo(
39
- num=number_of_frames * repeats,
68
+ number=number_of_frames * repeats,
40
69
  trigger=DetectorTrigger.constant_gate,
41
70
  deadtime=deadtime,
42
71
  livetime=exposure,
@@ -65,7 +94,7 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger(
65
94
  SeqTableRow(time2=in_micros(shutter_time)),
66
95
  )
67
96
 
68
- table_info = SeqTableInfo(table, repeats)
97
+ table_info = SeqTableInfo(sequence_table=table, repeats=repeats)
69
98
 
70
99
  for det in detectors:
71
100
  yield from bps.prepare(det, trigger_info, wait=False, group="prep")
@@ -75,7 +104,7 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger(
75
104
 
76
105
  def fly_and_collect(
77
106
  stream_name: str,
78
- flyer: HardwareTriggeredFlyable[SeqTableInfo],
107
+ flyer: StandardFlyer[SeqTableInfo] | StandardFlyer[PcompInfo],
79
108
  detectors: List[StandardDetector],
80
109
  ):
81
110
  """Kickoff, complete and collect with a flyer and multiple detectors.
@@ -113,9 +142,35 @@ def fly_and_collect(
113
142
  yield from bps.wait(group=group)
114
143
 
115
144
 
145
+ def fly_and_collect_with_static_pcomp(
146
+ stream_name: str,
147
+ flyer: StandardFlyer[PcompInfo],
148
+ detectors: List[StandardDetector],
149
+ number_of_pulses: int,
150
+ pulse_width: int,
151
+ rising_edge_step: int,
152
+ direction: PcompDirectionOptions,
153
+ trigger_info: TriggerInfo,
154
+ ):
155
+ # Set up scan and prepare trigger
156
+ pcomp_info = PcompInfo(
157
+ start_postion=0,
158
+ pulse_width=pulse_width,
159
+ rising_edge_step=rising_edge_step,
160
+ number_of_pulses=number_of_pulses,
161
+ direction=direction,
162
+ )
163
+ yield from prepare_static_pcomp_flyer_and_detectors(
164
+ flyer, detectors, pcomp_info, trigger_info
165
+ )
166
+
167
+ # Run the fly scan
168
+ yield from fly_and_collect(stream_name, flyer, detectors)
169
+
170
+
116
171
  def time_resolved_fly_and_collect_with_static_seq_table(
117
172
  stream_name: str,
118
- flyer: HardwareTriggeredFlyable[SeqTableInfo],
173
+ flyer: StandardFlyer[SeqTableInfo],
119
174
  detectors: List[StandardDetector],
120
175
  number_of_frames: int,
121
176
  exposure: float,
@@ -1,11 +0,0 @@
1
- from .pattern_generator import PatternGenerator
2
- from .sim_pattern_detector_control import SimPatternDetectorControl
3
- from .sim_pattern_detector_writer import SimPatternDetectorWriter
4
- from .sim_pattern_generator import SimPatternDetector
5
-
6
- __all__ = [
7
- "PatternGenerator",
8
- "SimPatternDetectorControl",
9
- "SimPatternDetectorWriter",
10
- "SimPatternDetector",
11
- ]
@@ -1,3 +1,19 @@
1
- from .sim_motor import SimMotor
1
+ from ._pattern_detector import (
2
+ DATA_PATH,
3
+ SUM_PATH,
4
+ PatternDetector,
5
+ PatternDetectorController,
6
+ PatternDetectorWriter,
7
+ PatternGenerator,
8
+ )
9
+ from ._sim_motor import SimMotor
2
10
 
3
- __all__ = ["SimMotor"]
11
+ __all__ = [
12
+ "DATA_PATH",
13
+ "SUM_PATH",
14
+ "PatternGenerator",
15
+ "PatternDetector",
16
+ "PatternDetectorController",
17
+ "PatternDetectorWriter",
18
+ "SimMotor",
19
+ ]
@@ -0,0 +1,13 @@
1
+ from ._pattern_detector import PatternDetector
2
+ from ._pattern_detector_controller import PatternDetectorController
3
+ from ._pattern_detector_writer import PatternDetectorWriter
4
+ from ._pattern_generator import DATA_PATH, SUM_PATH, PatternGenerator
5
+
6
+ __all__ = [
7
+ "PatternDetector",
8
+ "PatternDetectorController",
9
+ "PatternDetectorWriter",
10
+ "DATA_PATH",
11
+ "SUM_PATH",
12
+ "PatternGenerator",
13
+ ]
@@ -0,0 +1,42 @@
1
+ from pathlib import Path
2
+ from typing import Sequence
3
+
4
+ from ophyd_async.core import (
5
+ AsyncReadable,
6
+ FilenameProvider,
7
+ PathProvider,
8
+ StandardDetector,
9
+ StaticFilenameProvider,
10
+ StaticPathProvider,
11
+ )
12
+
13
+ from ._pattern_detector_controller import PatternDetectorController
14
+ from ._pattern_detector_writer import PatternDetectorWriter
15
+ from ._pattern_generator import PatternGenerator
16
+
17
+
18
+ class PatternDetector(StandardDetector):
19
+ def __init__(
20
+ self,
21
+ path: Path,
22
+ config_sigs: Sequence[AsyncReadable] = [],
23
+ name: str = "",
24
+ ) -> None:
25
+ fp: FilenameProvider = StaticFilenameProvider(name)
26
+ self.path_provider: PathProvider = StaticPathProvider(fp, path)
27
+ self.pattern_generator = PatternGenerator()
28
+ writer = PatternDetectorWriter(
29
+ pattern_generator=self.pattern_generator,
30
+ path_provider=self.path_provider,
31
+ name_provider=lambda: self.name,
32
+ )
33
+ controller = PatternDetectorController(
34
+ pattern_generator=self.pattern_generator,
35
+ path_provider=self.path_provider,
36
+ )
37
+ super().__init__(
38
+ controller=controller,
39
+ writer=writer,
40
+ config_sigs=config_sigs,
41
+ name=name,
42
+ )
@@ -1,22 +1,21 @@
1
1
  import asyncio
2
2
  from typing import Optional
3
3
 
4
- from ophyd_async.core import DirectoryProvider
5
- from ophyd_async.core.async_status import AsyncStatus
6
- from ophyd_async.core.detector import DetectorControl, DetectorTrigger
7
- from ophyd_async.sim.pattern_generator import PatternGenerator
4
+ from ophyd_async.core import AsyncStatus, DetectorControl, DetectorTrigger, PathProvider
8
5
 
6
+ from ._pattern_generator import PatternGenerator
9
7
 
10
- class SimPatternDetectorControl(DetectorControl):
8
+
9
+ class PatternDetectorController(DetectorControl):
11
10
  def __init__(
12
11
  self,
13
12
  pattern_generator: PatternGenerator,
14
- directory_provider: DirectoryProvider,
13
+ path_provider: PathProvider,
15
14
  exposure: float = 0.1,
16
15
  ) -> None:
17
16
  self.pattern_generator: PatternGenerator = pattern_generator
18
17
  self.pattern_generator.set_exposure(exposure)
19
- self.directory_provider: DirectoryProvider = directory_provider
18
+ self.path_provider: PathProvider = path_provider
20
19
  self.task: Optional[asyncio.Task] = None
21
20
  super().__init__()
22
21
 
@@ -2,23 +2,27 @@ from typing import AsyncGenerator, AsyncIterator, Dict
2
2
 
3
3
  from bluesky.protocols import DataKey
4
4
 
5
- from ophyd_async.core import DirectoryProvider
6
- from ophyd_async.core.detector import DetectorWriter
7
- from ophyd_async.sim.pattern_generator import PatternGenerator
5
+ from ophyd_async.core import DetectorWriter, NameProvider, PathProvider
8
6
 
7
+ from ._pattern_generator import PatternGenerator
9
8
 
10
- class SimPatternDetectorWriter(DetectorWriter):
9
+
10
+ class PatternDetectorWriter(DetectorWriter):
11
11
  pattern_generator: PatternGenerator
12
12
 
13
13
  def __init__(
14
- self, pattern_generator: PatternGenerator, directoryProvider: DirectoryProvider
14
+ self,
15
+ pattern_generator: PatternGenerator,
16
+ path_provider: PathProvider,
17
+ name_provider: NameProvider,
15
18
  ) -> None:
16
19
  self.pattern_generator = pattern_generator
17
- self.directory_provider = directoryProvider
20
+ self.path_provider = path_provider
21
+ self.name_provider = name_provider
18
22
 
19
23
  async def open(self, multiplier: int = 1) -> Dict[str, DataKey]:
20
24
  return await self.pattern_generator.open_file(
21
- self.directory_provider, multiplier
25
+ self.path_provider, self.name_provider(), multiplier
22
26
  )
23
27
 
24
28
  async def close(self) -> None:
@@ -31,4 +35,4 @@ class SimPatternDetectorWriter(DetectorWriter):
31
35
  return self.pattern_generator.observe_indices_written()
32
36
 
33
37
  async def get_indices_written(self) -> int:
34
- return self.pattern_generator.written_images_counter
38
+ return self.pattern_generator.image_counter