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.
- ophyd_async/_version.py +2 -2
- ophyd_async/core/__init__.py +86 -63
- ophyd_async/core/{detector.py → _detector.py} +18 -23
- ophyd_async/core/{device.py → _device.py} +19 -7
- ophyd_async/core/{device_save_loader.py → _device_save_loader.py} +3 -3
- ophyd_async/core/{flyer.py → _flyer.py} +6 -8
- ophyd_async/core/_hdf_dataset.py +97 -0
- ophyd_async/{log.py → core/_log.py} +11 -3
- ophyd_async/core/{mock_signal_backend.py → _mock_signal_backend.py} +3 -3
- ophyd_async/core/{mock_signal_utils.py → _mock_signal_utils.py} +3 -4
- ophyd_async/{protocols.py → core/_protocol.py} +1 -1
- ophyd_async/core/_providers.py +186 -24
- ophyd_async/core/{standard_readable.py → _readable.py} +6 -16
- ophyd_async/core/{signal.py → _signal.py} +39 -16
- ophyd_async/core/{signal_backend.py → _signal_backend.py} +4 -13
- ophyd_async/core/{soft_signal_backend.py → _soft_signal_backend.py} +24 -18
- ophyd_async/core/{async_status.py → _status.py} +3 -11
- ophyd_async/epics/adaravis/__init__.py +9 -0
- ophyd_async/epics/{areadetector/aravis.py → adaravis/_aravis.py} +12 -14
- ophyd_async/epics/{areadetector/controllers/aravis_controller.py → adaravis/_aravis_controller.py} +8 -10
- ophyd_async/epics/{areadetector/drivers/aravis_driver.py → adaravis/_aravis_io.py} +6 -3
- ophyd_async/epics/adcore/__init__.py +36 -0
- ophyd_async/epics/adcore/_core_io.py +114 -0
- ophyd_async/epics/{areadetector/drivers/ad_base.py → adcore/_core_logic.py} +17 -52
- ophyd_async/epics/{areadetector/writers/hdf_writer.py → adcore/_hdf_writer.py} +36 -18
- ophyd_async/epics/{areadetector/single_trigger_det.py → adcore/_single_trigger.py} +5 -6
- ophyd_async/epics/{areadetector/utils.py → adcore/_utils.py} +29 -0
- ophyd_async/epics/adkinetix/__init__.py +9 -0
- ophyd_async/epics/{areadetector/kinetix.py → adkinetix/_kinetix.py} +12 -14
- ophyd_async/epics/{areadetector/controllers/kinetix_controller.py → adkinetix/_kinetix_controller.py} +6 -9
- ophyd_async/epics/{areadetector/drivers/kinetix_driver.py → adkinetix/_kinetix_io.py} +5 -4
- ophyd_async/epics/adpilatus/__init__.py +11 -0
- ophyd_async/epics/{areadetector/pilatus.py → adpilatus/_pilatus.py} +12 -16
- ophyd_async/epics/{areadetector/controllers/pilatus_controller.py → adpilatus/_pilatus_controller.py} +14 -16
- ophyd_async/epics/{areadetector/drivers/pilatus_driver.py → adpilatus/_pilatus_io.py} +5 -3
- ophyd_async/epics/adsimdetector/__init__.py +7 -0
- ophyd_async/epics/adsimdetector/_sim.py +34 -0
- ophyd_async/epics/{areadetector/controllers/ad_sim_controller.py → adsimdetector/_sim_controller.py} +8 -14
- ophyd_async/epics/advimba/__init__.py +9 -0
- ophyd_async/epics/advimba/_vimba.py +43 -0
- ophyd_async/epics/{areadetector/controllers/vimba_controller.py → advimba/_vimba_controller.py} +6 -14
- ophyd_async/epics/{areadetector/drivers/vimba_driver.py → advimba/_vimba_io.py} +5 -4
- ophyd_async/epics/demo/__init__.py +9 -132
- ophyd_async/epics/demo/_mover.py +97 -0
- ophyd_async/epics/demo/_sensor.py +36 -0
- ophyd_async/epics/motor.py +228 -0
- ophyd_async/epics/pvi/__init__.py +2 -2
- ophyd_async/epics/pvi/{pvi.py → _pvi.py} +17 -14
- ophyd_async/epics/signal/__init__.py +7 -1
- ophyd_async/epics/{_backend → signal}/_aioca.py +6 -2
- ophyd_async/epics/{_backend/common.py → signal/_common.py} +4 -2
- ophyd_async/epics/signal/_epics_transport.py +3 -3
- ophyd_async/epics/{_backend → signal}/_p4p.py +53 -4
- ophyd_async/epics/signal/{signal.py → _signal.py} +10 -9
- ophyd_async/fastcs/odin/__init__.py +0 -0
- ophyd_async/{panda → fastcs/panda}/__init__.py +28 -9
- ophyd_async/{panda → fastcs/panda}/_common_blocks.py +24 -3
- ophyd_async/{panda → fastcs/panda}/_hdf_panda.py +6 -9
- ophyd_async/{panda/writers → fastcs/panda}/_hdf_writer.py +24 -14
- ophyd_async/{panda → fastcs/panda}/_panda_controller.py +2 -1
- ophyd_async/{panda → fastcs/panda}/_table.py +20 -18
- ophyd_async/fastcs/panda/_trigger.py +90 -0
- ophyd_async/plan_stubs/__init__.py +2 -2
- ophyd_async/plan_stubs/_ensure_connected.py +26 -0
- ophyd_async/plan_stubs/{fly.py → _fly.py} +67 -12
- ophyd_async/sim/__init__.py +0 -11
- ophyd_async/sim/demo/__init__.py +18 -2
- ophyd_async/sim/demo/_pattern_detector/__init__.py +13 -0
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector.py +42 -0
- ophyd_async/sim/{sim_pattern_detector_control.py → demo/_pattern_detector/_pattern_detector_controller.py} +6 -7
- ophyd_async/sim/{sim_pattern_detector_writer.py → demo/_pattern_detector/_pattern_detector_writer.py} +12 -8
- ophyd_async/sim/demo/_pattern_detector/_pattern_generator.py +211 -0
- ophyd_async/sim/demo/{sim_motor.py → _sim_motor.py} +7 -5
- ophyd_async/sim/testing/__init__.py +0 -0
- ophyd_async/tango/__init__.py +0 -0
- {ophyd_async-0.3.4a1.dist-info → ophyd_async-0.5.0.dist-info}/METADATA +7 -2
- ophyd_async-0.5.0.dist-info/RECORD +89 -0
- {ophyd_async-0.3.4a1.dist-info → ophyd_async-0.5.0.dist-info}/WHEEL +1 -1
- ophyd_async/epics/areadetector/__init__.py +0 -23
- ophyd_async/epics/areadetector/controllers/__init__.py +0 -5
- ophyd_async/epics/areadetector/drivers/__init__.py +0 -23
- ophyd_async/epics/areadetector/vimba.py +0 -43
- ophyd_async/epics/areadetector/writers/__init__.py +0 -5
- ophyd_async/epics/areadetector/writers/_hdfdataset.py +0 -10
- ophyd_async/epics/areadetector/writers/_hdffile.py +0 -54
- ophyd_async/epics/areadetector/writers/nd_file_hdf.py +0 -40
- ophyd_async/epics/areadetector/writers/nd_plugin.py +0 -38
- ophyd_async/epics/demo/demo_ad_sim_detector.py +0 -35
- ophyd_async/epics/motion/__init__.py +0 -3
- ophyd_async/epics/motion/motor.py +0 -97
- ophyd_async/panda/_trigger.py +0 -39
- ophyd_async/panda/writers/__init__.py +0 -3
- ophyd_async/panda/writers/_panda_hdf_file.py +0 -54
- ophyd_async/plan_stubs/ensure_connected.py +0 -22
- ophyd_async/sim/pattern_generator.py +0 -318
- ophyd_async/sim/sim_pattern_generator.py +0 -35
- ophyd_async-0.3.4a1.dist-info/RECORD +0 -86
- /ophyd_async/core/{utils.py → _utils.py} +0 -0
- /ophyd_async/{epics/_backend → fastcs}/__init__.py +0 -0
- /ophyd_async/{panda → fastcs/panda}/_utils.py +0 -0
- {ophyd_async-0.3.4a1.dist-info → ophyd_async-0.5.0.dist-info}/LICENSE +0 -0
- {ophyd_async-0.3.4a1.dist-info → ophyd_async-0.5.0.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
|
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[
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
30
|
-
self.
|
|
31
|
-
self.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
124
|
-
self.
|
|
125
|
-
Path(await self.panda_device.data.
|
|
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():
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from typing import Optional, Sequence, Type,
|
|
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:
|
|
58
|
-
trigger: Sequence[SeqTrigger]
|
|
59
|
-
position:
|
|
60
|
-
time1:
|
|
61
|
-
outa1:
|
|
62
|
-
outb1:
|
|
63
|
-
outc1:
|
|
64
|
-
outd1:
|
|
65
|
-
oute1:
|
|
66
|
-
outf1:
|
|
67
|
-
time2:
|
|
68
|
-
outa2:
|
|
69
|
-
outb2:
|
|
70
|
-
outc2:
|
|
71
|
-
outd2:
|
|
72
|
-
oute2:
|
|
73
|
-
outf2:
|
|
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 .
|
|
2
|
-
from .
|
|
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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
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:
|
|
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:
|
|
173
|
+
flyer: StandardFlyer[SeqTableInfo],
|
|
119
174
|
detectors: List[StandardDetector],
|
|
120
175
|
number_of_frames: int,
|
|
121
176
|
exposure: float,
|
ophyd_async/sim/__init__.py
CHANGED
|
@@ -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
|
-
]
|
ophyd_async/sim/demo/__init__.py
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
from .
|
|
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__ = [
|
|
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
|
|
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
|
-
|
|
8
|
+
|
|
9
|
+
class PatternDetectorController(DetectorControl):
|
|
11
10
|
def __init__(
|
|
12
11
|
self,
|
|
13
12
|
pattern_generator: PatternGenerator,
|
|
14
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
9
|
+
|
|
10
|
+
class PatternDetectorWriter(DetectorWriter):
|
|
11
11
|
pattern_generator: PatternGenerator
|
|
12
12
|
|
|
13
13
|
def __init__(
|
|
14
|
-
self,
|
|
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.
|
|
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.
|
|
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.
|
|
38
|
+
return self.pattern_generator.image_counter
|