ophyd-async 0.13.1__py3-none-any.whl → 0.13.2__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 +2 -1
- ophyd_async/core/_signal.py +7 -0
- ophyd_async/epics/core/_epics_connector.py +14 -1
- ophyd_async/epics/motor.py +21 -16
- ophyd_async/epics/{eiger → odin}/_odin_io.py +5 -3
- ophyd_async/epics/pmac/__init__.py +2 -0
- ophyd_async/epics/pmac/_pmac_io.py +2 -2
- ophyd_async/epics/pmac/_pmac_trajectory.py +116 -0
- ophyd_async/epics/pmac/_utils.py +671 -55
- ophyd_async/fastcs/eiger/_eiger.py +1 -1
- ophyd_async/fastcs/jungfrau/__init__.py +29 -0
- ophyd_async/fastcs/jungfrau/_controller.py +139 -0
- ophyd_async/fastcs/jungfrau/_jungfrau.py +30 -0
- ophyd_async/fastcs/jungfrau/_signals.py +94 -0
- ophyd_async/fastcs/jungfrau/_utils.py +79 -0
- ophyd_async/sim/_motor.py +11 -3
- ophyd_async/sim/_point_detector.py +6 -3
- ophyd_async/sim/_stage.py +14 -3
- {ophyd_async-0.13.1.dist-info → ophyd_async-0.13.2.dist-info}/METADATA +2 -1
- {ophyd_async-0.13.1.dist-info → ophyd_async-0.13.2.dist-info}/RECORD +25 -19
- /ophyd_async/epics/{eiger → odin}/__init__.py +0 -0
- {ophyd_async-0.13.1.dist-info → ophyd_async-0.13.2.dist-info}/WHEEL +0 -0
- {ophyd_async-0.13.1.dist-info → ophyd_async-0.13.2.dist-info}/licenses/LICENSE +0 -0
- {ophyd_async-0.13.1.dist-info → ophyd_async-0.13.2.dist-info}/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ from ophyd_async.core import (
|
|
|
4
4
|
StandardDetector,
|
|
5
5
|
TriggerInfo,
|
|
6
6
|
)
|
|
7
|
-
from ophyd_async.epics.
|
|
7
|
+
from ophyd_async.epics.odin import Odin, OdinWriter
|
|
8
8
|
|
|
9
9
|
from ._eiger_controller import EigerController
|
|
10
10
|
from ._eiger_io import EigerDriverIO
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from ._controller import JungfrauController
|
|
2
|
+
from ._jungfrau import Jungfrau
|
|
3
|
+
from ._signals import (
|
|
4
|
+
AcquisitionType,
|
|
5
|
+
DetectorStatus,
|
|
6
|
+
GainMode,
|
|
7
|
+
JungfrauDriverIO,
|
|
8
|
+
JungfrauTriggerMode,
|
|
9
|
+
PedestalMode,
|
|
10
|
+
)
|
|
11
|
+
from ._utils import (
|
|
12
|
+
create_jungfrau_external_triggering_info,
|
|
13
|
+
create_jungfrau_internal_triggering_info,
|
|
14
|
+
create_jungfrau_pedestal_triggering_info,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"Jungfrau",
|
|
19
|
+
"DetectorStatus",
|
|
20
|
+
"create_jungfrau_external_triggering_info",
|
|
21
|
+
"create_jungfrau_internal_triggering_info",
|
|
22
|
+
"create_jungfrau_pedestal_triggering_info",
|
|
23
|
+
"JungfrauController",
|
|
24
|
+
"JungfrauDriverIO",
|
|
25
|
+
"JungfrauTriggerMode",
|
|
26
|
+
"AcquisitionType",
|
|
27
|
+
"GainMode",
|
|
28
|
+
"PedestalMode",
|
|
29
|
+
]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from ophyd_async.core import (
|
|
5
|
+
DEFAULT_TIMEOUT,
|
|
6
|
+
DetectorController,
|
|
7
|
+
DetectorTrigger,
|
|
8
|
+
TriggerInfo,
|
|
9
|
+
wait_for_value,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from ._signals import (
|
|
13
|
+
JUNGFRAU_TRIGGER_MODE_MAP,
|
|
14
|
+
AcquisitionType,
|
|
15
|
+
DetectorStatus,
|
|
16
|
+
JungfrauDriverIO,
|
|
17
|
+
PedestalMode,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Deadtime is dependant on a wide combination of settings and on trigger mode
|
|
21
|
+
# but this is safe upper-limit
|
|
22
|
+
JUNGFRAU_DEADTIME_S = 2e-5
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger("ophyd_async")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class JungfrauController(DetectorController):
|
|
28
|
+
def __init__(self, driver: JungfrauDriverIO):
|
|
29
|
+
self._driver = driver
|
|
30
|
+
|
|
31
|
+
def get_deadtime(self, exposure: float | None = None) -> float:
|
|
32
|
+
return JUNGFRAU_DEADTIME_S
|
|
33
|
+
|
|
34
|
+
async def prepare(self, trigger_info: TriggerInfo) -> None:
|
|
35
|
+
# ValueErrors and warnings in this function come from the jungfrau operation
|
|
36
|
+
# docs: https://rtd.xfel.eu/docs/jungfrau-detector-documentation/en/latest/operation.html
|
|
37
|
+
|
|
38
|
+
# Deadtime here is really used as "time between frames"
|
|
39
|
+
|
|
40
|
+
acquisition_type = await self._driver.acquisition_type.get_value()
|
|
41
|
+
logger.info(f"Preparing Jungfrau in {acquisition_type} mode.")
|
|
42
|
+
|
|
43
|
+
if trigger_info.trigger not in (
|
|
44
|
+
DetectorTrigger.INTERNAL,
|
|
45
|
+
DetectorTrigger.EDGE_TRIGGER,
|
|
46
|
+
):
|
|
47
|
+
raise ValueError(
|
|
48
|
+
"The trigger method can only be called with internal or edge triggering"
|
|
49
|
+
)
|
|
50
|
+
if (
|
|
51
|
+
acquisition_type == AcquisitionType.PEDESTAL
|
|
52
|
+
and trigger_info.trigger != DetectorTrigger.INTERNAL
|
|
53
|
+
):
|
|
54
|
+
raise ValueError(
|
|
55
|
+
"Jungfrau must be triggered internally while in pedestal mode."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if not isinstance(trigger_info.number_of_events, int):
|
|
59
|
+
raise TypeError("Number of events must be an integer")
|
|
60
|
+
|
|
61
|
+
if acquisition_type != AcquisitionType.PEDESTAL:
|
|
62
|
+
if (
|
|
63
|
+
trigger_info.trigger == DetectorTrigger.INTERNAL
|
|
64
|
+
and trigger_info.number_of_events != 1
|
|
65
|
+
):
|
|
66
|
+
raise ValueError(
|
|
67
|
+
"Number of events must be set to 1 in internal trigger mode during "
|
|
68
|
+
"standard acquisitions."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
trigger_info.trigger == DetectorTrigger.EDGE_TRIGGER
|
|
73
|
+
and trigger_info.exposures_per_event != 1
|
|
74
|
+
):
|
|
75
|
+
raise ValueError(
|
|
76
|
+
"Exposures per event must be set to 1 in edge trigger mode "
|
|
77
|
+
"during standard acquisitions."
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if not trigger_info.livetime:
|
|
81
|
+
raise ValueError("Must set TriggerInfo.livetime")
|
|
82
|
+
|
|
83
|
+
if trigger_info.livetime < 2e-6:
|
|
84
|
+
logger.warning("Exposure time shorter than 2μs is not recommended")
|
|
85
|
+
|
|
86
|
+
period_between_frames = trigger_info.livetime + trigger_info.deadtime
|
|
87
|
+
|
|
88
|
+
if period_between_frames < self.get_deadtime():
|
|
89
|
+
raise ValueError(
|
|
90
|
+
f"Period between frames (exposure time - deadtime) = "
|
|
91
|
+
f"{period_between_frames}s cannot be lower than minimum detector "
|
|
92
|
+
f"deadtime {self.get_deadtime()}"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
coros = [
|
|
96
|
+
self._driver.trigger_mode.set(
|
|
97
|
+
JUNGFRAU_TRIGGER_MODE_MAP[trigger_info.trigger]
|
|
98
|
+
),
|
|
99
|
+
self._driver.period_between_frames.set(period_between_frames),
|
|
100
|
+
self._driver.exposure_time.set(trigger_info.livetime),
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
match acquisition_type:
|
|
104
|
+
case AcquisitionType.STANDARD:
|
|
105
|
+
frames_signal = (
|
|
106
|
+
trigger_info.exposures_per_event
|
|
107
|
+
if trigger_info.trigger is DetectorTrigger.INTERNAL
|
|
108
|
+
else trigger_info.number_of_events
|
|
109
|
+
)
|
|
110
|
+
coros.extend(
|
|
111
|
+
[
|
|
112
|
+
self._driver.frames_per_acq.set(frames_signal),
|
|
113
|
+
]
|
|
114
|
+
)
|
|
115
|
+
case AcquisitionType.PEDESTAL:
|
|
116
|
+
coros.extend(
|
|
117
|
+
[
|
|
118
|
+
self._driver.pedestal_mode_frames.set(
|
|
119
|
+
trigger_info.exposures_per_event
|
|
120
|
+
),
|
|
121
|
+
self._driver.pedestal_mode_loops.set(
|
|
122
|
+
trigger_info.number_of_events
|
|
123
|
+
),
|
|
124
|
+
self._driver.pedestal_mode_state.set(PedestalMode.ON),
|
|
125
|
+
]
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
await asyncio.gather(*coros)
|
|
129
|
+
|
|
130
|
+
async def arm(self):
|
|
131
|
+
await self._driver.acquisition_start.trigger()
|
|
132
|
+
|
|
133
|
+
async def wait_for_idle(self):
|
|
134
|
+
await wait_for_value(
|
|
135
|
+
self._driver.detector_status, DetectorStatus.IDLE, timeout=DEFAULT_TIMEOUT
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
async def disarm(self):
|
|
139
|
+
await self._driver.acquisition_stop.trigger()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from ophyd_async.core import (
|
|
2
|
+
PathProvider,
|
|
3
|
+
StandardDetector,
|
|
4
|
+
)
|
|
5
|
+
from ophyd_async.epics.odin import Odin, OdinWriter
|
|
6
|
+
from ophyd_async.fastcs.jungfrau._controller import JungfrauController
|
|
7
|
+
from ophyd_async.fastcs.jungfrau._signals import JungfrauDriverIO
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Jungfrau(StandardDetector[JungfrauController, OdinWriter]):
|
|
11
|
+
"""Ophyd-async implementation of a Jungfrau Detector."""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
prefix: str,
|
|
16
|
+
path_provider: PathProvider,
|
|
17
|
+
drv_suffix: str,
|
|
18
|
+
hdf_suffix: str,
|
|
19
|
+
odin_nodes: int,
|
|
20
|
+
name="",
|
|
21
|
+
):
|
|
22
|
+
self.drv = JungfrauDriverIO(prefix + drv_suffix)
|
|
23
|
+
self.odin = Odin(prefix + hdf_suffix, nodes=odin_nodes)
|
|
24
|
+
writer = OdinWriter(
|
|
25
|
+
path_provider,
|
|
26
|
+
self.odin,
|
|
27
|
+
self.drv.bit_depth,
|
|
28
|
+
)
|
|
29
|
+
controller = JungfrauController(self.drv)
|
|
30
|
+
super().__init__(controller, writer, name=name)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from pydantic import NonNegativeInt
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import (
|
|
4
|
+
DetectorTrigger,
|
|
5
|
+
Device,
|
|
6
|
+
SignalR,
|
|
7
|
+
SignalRW,
|
|
8
|
+
SignalX,
|
|
9
|
+
StrictEnum,
|
|
10
|
+
soft_signal_rw,
|
|
11
|
+
)
|
|
12
|
+
from ophyd_async.fastcs.core import fastcs_connector
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class JungfrauTriggerMode(StrictEnum):
|
|
16
|
+
INTERNAL = "Internal"
|
|
17
|
+
|
|
18
|
+
# Detector waits for external trigger to start frame series, but still
|
|
19
|
+
# controls exposure time and frame period internally
|
|
20
|
+
EXTERNAL = "External"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DetectorStatus(StrictEnum):
|
|
24
|
+
IDLE = "Idle"
|
|
25
|
+
ERROR = "Error"
|
|
26
|
+
WAITING = "Waiting"
|
|
27
|
+
RUN_FINISHED = "RunFinished"
|
|
28
|
+
TRANSMITTING = "Transmitting"
|
|
29
|
+
RUNNING = "Running"
|
|
30
|
+
STOPPED = "Stopped"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class GainMode(StrictEnum):
|
|
34
|
+
DYNAMIC = "Dynamic"
|
|
35
|
+
FORCE_SWITCH_G1 = "ForceSwitchG1"
|
|
36
|
+
FORCE_SWITCH_G2 = "ForceSwitchG2"
|
|
37
|
+
FIX_G1 = "FixG1"
|
|
38
|
+
FIX_G2 = "FixG2"
|
|
39
|
+
|
|
40
|
+
# Use with caution - this may damage the detector
|
|
41
|
+
FIX_G0 = "FixG0"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class PedestalMode(StrictEnum):
|
|
45
|
+
ON = "On"
|
|
46
|
+
OFF = "Off"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AcquisitionType(StrictEnum):
|
|
50
|
+
STANDARD = "Standard"
|
|
51
|
+
PEDESTAL = "Pedestal"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
JUNGFRAU_TRIGGER_MODE_MAP = {
|
|
55
|
+
DetectorTrigger.EDGE_TRIGGER: JungfrauTriggerMode.EXTERNAL,
|
|
56
|
+
DetectorTrigger.INTERNAL: JungfrauTriggerMode.INTERNAL,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class JungfrauDriverIO(Device):
|
|
61
|
+
"""Contains signals for handling IO on the Jungfrau detector."""
|
|
62
|
+
|
|
63
|
+
exposure_time: SignalRW[float] # in s
|
|
64
|
+
|
|
65
|
+
# Includes deadtime
|
|
66
|
+
period_between_frames: SignalRW[float] # in s
|
|
67
|
+
|
|
68
|
+
# Sets the delay for the beginning of the exposure time after
|
|
69
|
+
# trigger input
|
|
70
|
+
delay_after_trigger: SignalRW[float] # in s
|
|
71
|
+
|
|
72
|
+
# In internal trigger mode, this is frames per trigger. In external trigger mode,
|
|
73
|
+
# this is frames per overall acquisition. In pedestal mode, this signal is not set.
|
|
74
|
+
frames_per_acq: SignalRW[NonNegativeInt]
|
|
75
|
+
|
|
76
|
+
pedestal_mode_state: SignalRW[PedestalMode]
|
|
77
|
+
pedestal_mode_frames: SignalRW[NonNegativeInt]
|
|
78
|
+
pedestal_mode_loops: SignalRW[NonNegativeInt]
|
|
79
|
+
|
|
80
|
+
gain_mode: SignalRW[GainMode]
|
|
81
|
+
|
|
82
|
+
acquisition_start: SignalX
|
|
83
|
+
|
|
84
|
+
acquisition_stop: SignalX
|
|
85
|
+
bit_depth: SignalR[int]
|
|
86
|
+
trigger_mode: SignalRW[JungfrauTriggerMode]
|
|
87
|
+
detector_status: SignalR[DetectorStatus]
|
|
88
|
+
|
|
89
|
+
def __init__(self, uri: str, name: str = ""):
|
|
90
|
+
# Determines how the TriggerInfo gets mapped to the Jungfrau during prepare
|
|
91
|
+
self.acquisition_type = soft_signal_rw(
|
|
92
|
+
AcquisitionType, AcquisitionType.STANDARD
|
|
93
|
+
)
|
|
94
|
+
super().__init__(name=name, connector=fastcs_connector(self, uri))
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from pydantic import PositiveInt
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import DetectorTrigger, TriggerInfo
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def create_jungfrau_external_triggering_info(
|
|
7
|
+
total_triggers: PositiveInt,
|
|
8
|
+
exposure_time_s: float,
|
|
9
|
+
) -> TriggerInfo:
|
|
10
|
+
"""Create safe Jungfrau TriggerInfo for external triggering.
|
|
11
|
+
|
|
12
|
+
Uses parameters which more closely-align with Jungfrau terminology
|
|
13
|
+
to create TriggerInfo. This device currently only supports one frame per trigger
|
|
14
|
+
when being externally triggered, but support for this can be added if needed
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
total_triggers: Total external triggers expected before ending acquisition.
|
|
18
|
+
exposure_time_s: How long to expose the detector for each of its frames.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
`TriggerInfo`
|
|
22
|
+
"""
|
|
23
|
+
return TriggerInfo(
|
|
24
|
+
number_of_events=total_triggers,
|
|
25
|
+
trigger=DetectorTrigger.EDGE_TRIGGER,
|
|
26
|
+
livetime=exposure_time_s,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def create_jungfrau_internal_triggering_info(
|
|
31
|
+
number_of_frames: PositiveInt, exposure_time_s: float
|
|
32
|
+
) -> TriggerInfo:
|
|
33
|
+
"""Create safe Jungfrau TriggerInfo for internal triggering.
|
|
34
|
+
|
|
35
|
+
Uses parameters which more closely-align with Jungfrau terminology
|
|
36
|
+
to create TriggerInfo.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
number_of_frames: Total frames taken after starting acquisition.
|
|
40
|
+
exposure_time_s: How long to expose the detector for each of its frames.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
`TriggerInfo`
|
|
44
|
+
"""
|
|
45
|
+
return TriggerInfo(
|
|
46
|
+
number_of_events=1,
|
|
47
|
+
trigger=DetectorTrigger.INTERNAL,
|
|
48
|
+
livetime=exposure_time_s,
|
|
49
|
+
exposures_per_event=number_of_frames,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def create_jungfrau_pedestal_triggering_info(
|
|
54
|
+
exposure_time_s: float,
|
|
55
|
+
pedestal_frames: PositiveInt,
|
|
56
|
+
pedestal_loops: PositiveInt,
|
|
57
|
+
):
|
|
58
|
+
"""Create safe Jungfrau TriggerInfo for pedestal triggering.
|
|
59
|
+
|
|
60
|
+
Uses parameters which more closely-align with Jungfrau terminology
|
|
61
|
+
to create TriggerInfo.
|
|
62
|
+
|
|
63
|
+
NOTE: To trigger the jungfrau in pedestal mode, you must first set the
|
|
64
|
+
jungfrau acquisition_type signal to AcquisitionType.PEDESTAL!
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
exposure_time_s: How long to expose the detector for each of its frames.
|
|
68
|
+
pedestal_frames: Number of frames taken once triggering begins
|
|
69
|
+
pedestal_loops: Number of repeats of the pedestal scan before detector disarms.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
`TriggerInfo`
|
|
73
|
+
"""
|
|
74
|
+
return TriggerInfo(
|
|
75
|
+
number_of_events=pedestal_loops,
|
|
76
|
+
exposures_per_event=pedestal_frames,
|
|
77
|
+
trigger=DetectorTrigger.INTERNAL,
|
|
78
|
+
livetime=exposure_time_s,
|
|
79
|
+
)
|
ophyd_async/sim/_motor.py
CHANGED
|
@@ -23,11 +23,19 @@ from ophyd_async.core import StandardReadableFormat as Format
|
|
|
23
23
|
class SimMotor(StandardReadable, Stoppable, Subscribable[float], Locatable[float]):
|
|
24
24
|
"""For usage when simulating a motor."""
|
|
25
25
|
|
|
26
|
-
def __init__(
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
name: str = "",
|
|
29
|
+
instant: bool = True,
|
|
30
|
+
initial_value: float = 0.0,
|
|
31
|
+
units: str = "mm",
|
|
32
|
+
) -> None:
|
|
27
33
|
"""Simulate a motor, with optional velocity.
|
|
28
34
|
|
|
29
35
|
:param name: name of device
|
|
30
36
|
:param instant: whether to move instantly or calculate move time using velocity
|
|
37
|
+
:param initial_value: initial position of the motor
|
|
38
|
+
:param units: units of the motor position
|
|
31
39
|
"""
|
|
32
40
|
# Define some signals
|
|
33
41
|
with self.add_children_as_readables(Format.HINTED_SIGNAL):
|
|
@@ -37,8 +45,8 @@ class SimMotor(StandardReadable, Stoppable, Subscribable[float], Locatable[float
|
|
|
37
45
|
with self.add_children_as_readables(Format.CONFIG_SIGNAL):
|
|
38
46
|
self.velocity = soft_signal_rw(float, 0 if instant else 1.0)
|
|
39
47
|
self.acceleration_time = soft_signal_rw(float, 0.5)
|
|
40
|
-
self.units = soft_signal_rw(str,
|
|
41
|
-
self.user_setpoint = soft_signal_rw(float,
|
|
48
|
+
self.units = soft_signal_rw(str, units)
|
|
49
|
+
self.user_setpoint = soft_signal_rw(float, initial_value)
|
|
42
50
|
|
|
43
51
|
# Whether set() should complete successfully or not
|
|
44
52
|
self._set_success = True
|
|
@@ -41,9 +41,12 @@ class SimPointDetector(StandardReadable):
|
|
|
41
41
|
"""Simalutes a point detector with multiple channels."""
|
|
42
42
|
|
|
43
43
|
def __init__(
|
|
44
|
-
self,
|
|
44
|
+
self,
|
|
45
|
+
pattern_generator: PatternGenerator | None,
|
|
46
|
+
num_channels: int = 3,
|
|
47
|
+
name: str = "",
|
|
45
48
|
) -> None:
|
|
46
|
-
self.
|
|
49
|
+
self.pattern_generator = pattern_generator or PatternGenerator()
|
|
47
50
|
self.acquire_time = soft_signal_rw(float, 0.1)
|
|
48
51
|
self.acquiring, self._set_acquiring = soft_signal_r_and_setter(bool)
|
|
49
52
|
self._value_signals = dict(
|
|
@@ -75,7 +78,7 @@ class SimPointDetector(StandardReadable):
|
|
|
75
78
|
# Update the channel value
|
|
76
79
|
for i, channel in self.channel.items():
|
|
77
80
|
high_energy = modes[channel] == EnergyMode.HIGH
|
|
78
|
-
point = self.
|
|
81
|
+
point = self.pattern_generator.generate_point(i, high_energy)
|
|
79
82
|
setter = self._value_signals[channel.value]
|
|
80
83
|
setter(int(point * 10000 * update_time))
|
|
81
84
|
|
ophyd_async/sim/_stage.py
CHANGED
|
@@ -8,12 +8,23 @@ class SimStage(StandardReadable):
|
|
|
8
8
|
"""A simulated sample stage with X and Y movables."""
|
|
9
9
|
|
|
10
10
|
def __init__(self, pattern_generator: PatternGenerator, name="") -> None:
|
|
11
|
+
self.pattern_generator = pattern_generator
|
|
11
12
|
# Define some child Devices
|
|
12
13
|
with self.add_children_as_readables():
|
|
13
14
|
self.x = SimMotor(instant=False)
|
|
14
15
|
self.y = SimMotor(instant=False)
|
|
15
|
-
# Tell the pattern generator about the motor positions
|
|
16
|
-
self.x.user_readback.subscribe_value(pattern_generator.set_x)
|
|
17
|
-
self.y.user_readback.subscribe_value(pattern_generator.set_y)
|
|
18
16
|
# Set name of device and child devices
|
|
19
17
|
super().__init__(name=name)
|
|
18
|
+
|
|
19
|
+
def stage(self):
|
|
20
|
+
"""Stage the motors and report the position to the pattern generator."""
|
|
21
|
+
# Tell the pattern generator about the motor positions
|
|
22
|
+
self.x.user_readback.subscribe_value(self.pattern_generator.set_x)
|
|
23
|
+
self.y.user_readback.subscribe_value(self.pattern_generator.set_y)
|
|
24
|
+
return super().stage()
|
|
25
|
+
|
|
26
|
+
def unstage(self):
|
|
27
|
+
"""Unstage the motors and remove the position subscription."""
|
|
28
|
+
self.x.user_readback.clear_sub(self.pattern_generator.set_x)
|
|
29
|
+
self.y.user_readback.clear_sub(self.pattern_generator.set_y)
|
|
30
|
+
return super().unstage()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ophyd-async
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.2
|
|
4
4
|
Summary: Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango
|
|
5
5
|
Author-email: Tom Cobb <tom.cobb@diamond.ac.uk>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -50,6 +50,7 @@ Requires-Dist: pydantic>=2.0
|
|
|
50
50
|
Requires-Dist: pydantic-numpy
|
|
51
51
|
Requires-Dist: stamina>=23.1.0
|
|
52
52
|
Requires-Dist: scanspec>=1.0a1
|
|
53
|
+
Requires-Dist: velocity-profile
|
|
53
54
|
Provides-Extra: sim
|
|
54
55
|
Requires-Dist: h5py; extra == "sim"
|
|
55
56
|
Provides-Extra: ca
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
ophyd_async/__init__.py,sha256=dcAA3qsj1nNIMe5l-v2tlduZ_ypwBmyuHe45Lsq4k4w,206
|
|
2
2
|
ophyd_async/__main__.py,sha256=n_U4O9bgm97OuboUB_9eK7eFiwy8BZSgXJ0OzbE0DqU,481
|
|
3
3
|
ophyd_async/_docs_parser.py,sha256=gPYrigfSbYCF7QoSf2UvE-cpQu4snSssl7ZWN-kKDzI,352
|
|
4
|
-
ophyd_async/_version.py,sha256=
|
|
4
|
+
ophyd_async/_version.py,sha256=XZecU3ohLLhnQNT8_JmdvW2idqqOh08RIDcm2qzc5t8,706
|
|
5
5
|
ophyd_async/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
ophyd_async/core/__init__.py,sha256=
|
|
6
|
+
ophyd_async/core/__init__.py,sha256=KBj8v-Fxh8V1QKJYR0xAjoziqo1vjyblx45H7Jrikd0,4979
|
|
7
7
|
ophyd_async/core/_derived_signal.py,sha256=TuZza_j3J1Bw4QSqBYB9Ta2FyQP5BycO3nSHVtJ890Q,13015
|
|
8
8
|
ophyd_async/core/_derived_signal_backend.py,sha256=PYyyont_nUR9LBC9eqVwueHCMwLJfQ_F7R_14sivBTU,12510
|
|
9
9
|
ophyd_async/core/_detector.py,sha256=bPTQYLTjbxs7-wjV8szAZ0TQI20fV12vkq1OaA5L_z8,14938
|
|
@@ -18,7 +18,7 @@ ophyd_async/core/_protocol.py,sha256=wQ_snxhTprHqEjQb1HgFwBljwolMY6A8C3xgV1PXwdU
|
|
|
18
18
|
ophyd_async/core/_providers.py,sha256=WBht3QCgvGc0stNcwH6z4Zr6hAz3e01-88NjsYI2w6I,9740
|
|
19
19
|
ophyd_async/core/_readable.py,sha256=iBo1YwA5bsAbzLbznvmSnzKDWUuGkLh850Br3BXsgeU,11707
|
|
20
20
|
ophyd_async/core/_settings.py,sha256=_ZccbXKP7j5rG6-bMKk7aaLr8hChdRDAPY_YSR71XXM,4213
|
|
21
|
-
ophyd_async/core/_signal.py,sha256=
|
|
21
|
+
ophyd_async/core/_signal.py,sha256=ghgxtfUG9vkwgvuu6qXa_ECux5-3j9WwxB5mN5G6jqU,28421
|
|
22
22
|
ophyd_async/core/_signal_backend.py,sha256=PvwTbbSVEGqM-2s5BNRrKGwM_MiYL71qMxYAgyZ7wRM,6930
|
|
23
23
|
ophyd_async/core/_soft_signal_backend.py,sha256=NJUuyaCKtBZjggt8WKi7_lKQRHasToxviuQvl5xbhLU,6222
|
|
24
24
|
ophyd_async/core/_status.py,sha256=h4TtWFM7wFtpxxyAYYSITgcVzArYZdYBHbya6qIX5t0,6553
|
|
@@ -26,7 +26,7 @@ ophyd_async/core/_table.py,sha256=ai-_W-_WMZcy9f69BDYRv9vjVl-AVeOPN_uHYoGCSsc,69
|
|
|
26
26
|
ophyd_async/core/_utils.py,sha256=-iKbqsvVR7P29E6VpOgI5WfDwGBRdud_gvExIoUVlG4,12495
|
|
27
27
|
ophyd_async/core/_yaml_settings.py,sha256=Qojhku9l5kPSkTnEylCRWTe0gpw6S_XP5av5dPpqFgQ,2089
|
|
28
28
|
ophyd_async/epics/__init__.py,sha256=ou4yEaH9VZHz70e8oM614-arLMQvUfQyXhRJsnEpWn8,60
|
|
29
|
-
ophyd_async/epics/motor.py,sha256=
|
|
29
|
+
ophyd_async/epics/motor.py,sha256=oRAAW-KWbMnAxHdOMKMt1ZrzgD5KzHX9iRwd0TYZXwI,8747
|
|
30
30
|
ophyd_async/epics/signal.py,sha256=0A-supp9ajr63O6aD7F9oG0-Q26YmRjk-ZGh57-jo1Y,239
|
|
31
31
|
ophyd_async/epics/adandor/__init__.py,sha256=dlitllrAdhvh16PAcVMUSSEytTDNMu6_HuYk8KD1EoY,343
|
|
32
32
|
ophyd_async/epics/adandor/_andor.py,sha256=TijGjNVxuH-P0X7UACPt9eLLQ449DwMyVhbn1kV7Le8,1245
|
|
@@ -64,7 +64,7 @@ ophyd_async/epics/advimba/_vimba_controller.py,sha256=KSbP4LHqYkCDplpmBk7hdf0Yz9
|
|
|
64
64
|
ophyd_async/epics/advimba/_vimba_io.py,sha256=E3XlCKLQbGOWho0dQPeD4xeEl5pZGINyYstGD43qNrM,1492
|
|
65
65
|
ophyd_async/epics/core/__init__.py,sha256=q73i4aJ_0HApVNmf3eAw-q30XuazAyZW2MW5TXk-pOY,648
|
|
66
66
|
ophyd_async/epics/core/_aioca.py,sha256=38aW5dd3MzwhoweNMjkOfnfZHI2JZFateO0YABGVSfQ,13230
|
|
67
|
-
ophyd_async/epics/core/_epics_connector.py,sha256=
|
|
67
|
+
ophyd_async/epics/core/_epics_connector.py,sha256=KXQ1WVpNrgQ30KiiXTYRwfoHdaU5oWv1jVZAvx3tGig,2557
|
|
68
68
|
ophyd_async/epics/core/_epics_device.py,sha256=wGdR24I7GSPh3HmM7jsWKZhBZgt4IyLrCn4Ut7Wx_xo,510
|
|
69
69
|
ophyd_async/epics/core/_p4p.py,sha256=Q6o4rlyrg2VXlqi1uHkGbnYc1gZ8YX5ZaJDcL0eAr_8,16385
|
|
70
70
|
ophyd_async/epics/core/_pvi_connector.py,sha256=SfKkZqGCRvJJtQpJQzmfuLJQqOge0wBsMeuTSQ-KPjs,5553
|
|
@@ -80,11 +80,12 @@ ophyd_async/epics/demo/_stage.py,sha256=KPnwr5EX8f_0xxkNWT-70a0AqB0D9DoiTbxMmy0i
|
|
|
80
80
|
ophyd_async/epics/demo/motor.db,sha256=3xb6WTXo4crrvk-M8Y16G9pUidp27vD5vIKKBpLTUlk,1017
|
|
81
81
|
ophyd_async/epics/demo/point_detector.db,sha256=8kBa3XKpmfXCxetT4tq5_RFXa_XqS1Z2ZNzsa2AtLds,1366
|
|
82
82
|
ophyd_async/epics/demo/point_detector_channel.db,sha256=FZ9H6HjqplhcF2jgimv_dT1nn-CBlfjs7Y--iCfHp5Y,632
|
|
83
|
-
ophyd_async/epics/
|
|
84
|
-
ophyd_async/epics/
|
|
85
|
-
ophyd_async/epics/pmac/__init__.py,sha256=
|
|
86
|
-
ophyd_async/epics/pmac/_pmac_io.py,sha256=
|
|
87
|
-
ophyd_async/epics/pmac/
|
|
83
|
+
ophyd_async/epics/odin/__init__.py,sha256=7kRqVzwoD8PVtp7Nj9iQWlgbLeoWE_8oiq-B0kixwTE,93
|
|
84
|
+
ophyd_async/epics/odin/_odin_io.py,sha256=YDBrS15PnEKe5SHmz397Emh--lZSQEnbR3G7p8pbShY,6533
|
|
85
|
+
ophyd_async/epics/pmac/__init__.py,sha256=GqJTiJudqE9pu050ZNED09F9tKRfazn0wBsojsMH2gg,273
|
|
86
|
+
ophyd_async/epics/pmac/_pmac_io.py,sha256=Nnenk7HHyzzqe0jIg2FAtxFohFnr_ByDZ0IAEI4_Ls4,4023
|
|
87
|
+
ophyd_async/epics/pmac/_pmac_trajectory.py,sha256=Q9Ry2xzL7FqszehhfBfhu-5-fFyPcJiD2Iu86c9pQ7w,4167
|
|
88
|
+
ophyd_async/epics/pmac/_utils.py,sha256=ahN5zMfO3-gNV7H1fQ5wsDeGUL9IpMQa2g27rhVf0uU,34326
|
|
88
89
|
ophyd_async/epics/testing/__init__.py,sha256=aTIv4D2DYrpnGco5RQF8QuLG1SfFkIlTyM2uYEKXltA,522
|
|
89
90
|
ophyd_async/epics/testing/_example_ioc.py,sha256=zb4ZEUzuB2MrSw5ETPLIiHhf-2BRU1Bdxco6Kh4iI1I,3880
|
|
90
91
|
ophyd_async/epics/testing/_utils.py,sha256=9gxpwaWX0HGtacu1LTupcw7viXN8G78RmuNciU_-cjs,1702
|
|
@@ -93,9 +94,14 @@ ophyd_async/epics/testing/test_records_pva.db,sha256=HJAJSvLtPWG5B5dKv8OZ0_hPJxR
|
|
|
93
94
|
ophyd_async/fastcs/__init__.py,sha256=qlIM9-pjJ8yWfnzTM9-T9cw7zQLKjeeNROQTni5Dr6M,80
|
|
94
95
|
ophyd_async/fastcs/core.py,sha256=pL_srtTrfuoBHUjDFpxES92owFq9M4Jve0Skk1oeuFA,517
|
|
95
96
|
ophyd_async/fastcs/eiger/__init__.py,sha256=RxwOFjERKy5tUD_IDGCGuMh716FaZgCq7R9elPixBwo,312
|
|
96
|
-
ophyd_async/fastcs/eiger/_eiger.py,sha256=
|
|
97
|
+
ophyd_async/fastcs/eiger/_eiger.py,sha256=jo3K5dM3Co_RDYIyO6poCVDqp2g_1z4MqnYftwnMhUk,1103
|
|
97
98
|
ophyd_async/fastcs/eiger/_eiger_controller.py,sha256=Cucj-1M-1CaxSJxHZmHs3f_OXwtTIspcqUFhRNGzn_E,2361
|
|
98
99
|
ophyd_async/fastcs/eiger/_eiger_io.py,sha256=yNozTKX4CMoqIaFvyKJEs5RXXGJ4VITP_T4TIFHWRmc,1242
|
|
100
|
+
ophyd_async/fastcs/jungfrau/__init__.py,sha256=6BV6czpOWmDuTAKR-47tRFdM3Q5dQenHnPPAYiWOFk0,717
|
|
101
|
+
ophyd_async/fastcs/jungfrau/_controller.py,sha256=OiPCVwYtY_0bx5His9qVsRHzG8-Rjg087B6c71gO5Q8,4836
|
|
102
|
+
ophyd_async/fastcs/jungfrau/_jungfrau.py,sha256=KAHCmRHMyzIh-r2JXVJcQOGLkCOOdW5Mao_KChITO2s,929
|
|
103
|
+
ophyd_async/fastcs/jungfrau/_signals.py,sha256=8seZCkKTb-xJL0IdB2el8VTEbWNaCvZIhpP0609GpxI,2500
|
|
104
|
+
ophyd_async/fastcs/jungfrau/_utils.py,sha256=-NeIlUW7Mx9llUC6FZORc6e-IAAOL9lwzzl07wc8lTc,2509
|
|
99
105
|
ophyd_async/fastcs/odin/__init__.py,sha256=da1PTClDMl-IBkrSvq6JC1lnS-K_BASzCvxVhNxN5Ls,13
|
|
100
106
|
ophyd_async/fastcs/panda/__init__.py,sha256=ugrScVm4HPQFc-d1kTAfZ5UUzW9T3SPgTi0OD2s8ZH0,1003
|
|
101
107
|
ophyd_async/fastcs/panda/_block.py,sha256=SM7NaWCRwLz2Pl4wgjZMrDgx3ZLdGPTw6nU0bA-65yA,2394
|
|
@@ -119,10 +125,10 @@ ophyd_async/sim/_blob_detector_controller.py,sha256=y1aSNQJUPnsT2qnj2sk254Mp18an
|
|
|
119
125
|
ophyd_async/sim/_blob_detector_writer.py,sha256=_Pd0OaP4_mZfwxtUF35v7hsktLP_wYljR4nylr5CzJo,3346
|
|
120
126
|
ophyd_async/sim/_mirror_horizontal.py,sha256=Jsqa8Snjy1jQDboZtAQFJjGor5uKk8FBC7OCe-GoZDw,1478
|
|
121
127
|
ophyd_async/sim/_mirror_vertical.py,sha256=HUD44mYT0jQ0GKiQKxD7k_7y6o6OdE6TztgdPUJIK_g,2085
|
|
122
|
-
ophyd_async/sim/_motor.py,sha256=
|
|
128
|
+
ophyd_async/sim/_motor.py,sha256=EUykjGfQgBnf0Pmr66XeDlhz8DU0eAIXUvIgbtJccoA,9226
|
|
123
129
|
ophyd_async/sim/_pattern_generator.py,sha256=kuxvyX2gIxrywhQRhaO1g8YluBT7LBkE20IsurZS-6o,3734
|
|
124
|
-
ophyd_async/sim/_point_detector.py,sha256=
|
|
125
|
-
ophyd_async/sim/_stage.py,sha256=
|
|
130
|
+
ophyd_async/sim/_point_detector.py,sha256=wMG_ncvm99WMCPihlFyuMEf3UknAxCpB1hpk3uKiENE,3024
|
|
131
|
+
ophyd_async/sim/_stage.py,sha256=fOR5HGWfqWw47-0HrMmQ5rait_qPVNneF329wCx8KKY,1233
|
|
126
132
|
ophyd_async/tango/__init__.py,sha256=g9xzjlzPpUAP12YI-kYwfAoLSYPAQdL1S11R2c-cius,60
|
|
127
133
|
ophyd_async/tango/core/__init__.py,sha256=IMvQ7MWcTof99h_pr483KWKvQV2-h7zo_iRpLA2PUYQ,1108
|
|
128
134
|
ophyd_async/tango/core/_base_device.py,sha256=e9oqSL-fDOj8r9nUUFZkbibhRGbI6HYtlnZjK5B_2fE,5033
|
|
@@ -147,8 +153,8 @@ ophyd_async/testing/_one_of_everything.py,sha256=U9ui7B-iNHDM3H3hIWUuaCb8Gc2eLlU
|
|
|
147
153
|
ophyd_async/testing/_single_derived.py,sha256=5-HOTzgePcZ354NK_ssVpyIbJoJmKyjVQCxSwQXUC-4,2730
|
|
148
154
|
ophyd_async/testing/_utils.py,sha256=zClRo5ve8RGia7wQnby41W-Zprj-slOA5da1LfYnuhw,45
|
|
149
155
|
ophyd_async/testing/_wait_for_pending.py,sha256=YZAR48n-CW0GsPey3zFRzMJ4byDAr3HvMIoawjmTrHw,732
|
|
150
|
-
ophyd_async-0.13.
|
|
151
|
-
ophyd_async-0.13.
|
|
152
|
-
ophyd_async-0.13.
|
|
153
|
-
ophyd_async-0.13.
|
|
154
|
-
ophyd_async-0.13.
|
|
156
|
+
ophyd_async-0.13.2.dist-info/licenses/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
|
|
157
|
+
ophyd_async-0.13.2.dist-info/METADATA,sha256=GwFtb0s8P44XeDTXV6U55G1tro_OERT4Vj-9aVrKomE,7177
|
|
158
|
+
ophyd_async-0.13.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
159
|
+
ophyd_async-0.13.2.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
|
|
160
|
+
ophyd_async-0.13.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|