dls-dodal 1.30.0__py3-none-any.whl → 1.31.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.
- {dls_dodal-1.30.0.dist-info → dls_dodal-1.31.0.dist-info}/METADATA +4 -4
- {dls_dodal-1.30.0.dist-info → dls_dodal-1.31.0.dist-info}/RECORD +64 -62
- {dls_dodal-1.30.0.dist-info → dls_dodal-1.31.0.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamline_specific_utils/i03.py +1 -4
- dodal/beamlines/__init__.py +4 -0
- dodal/beamlines/i03.py +8 -8
- dodal/beamlines/i04.py +10 -9
- dodal/beamlines/i13_1.py +7 -7
- dodal/beamlines/i22.py +18 -18
- dodal/beamlines/p38.py +14 -14
- dodal/beamlines/p45.py +11 -11
- dodal/beamlines/training_rig.py +64 -0
- dodal/common/beamlines/beamline_parameters.py +5 -4
- dodal/common/beamlines/beamline_utils.py +9 -9
- dodal/common/types.py +4 -2
- dodal/common/udc_directory_provider.py +29 -22
- dodal/common/visit.py +59 -60
- dodal/devices/CTAB.py +1 -1
- dodal/devices/aperture.py +1 -1
- dodal/devices/aperturescatterguard.py +140 -188
- dodal/devices/areadetector/plugins/MJPG.py +2 -1
- dodal/devices/backlight.py +12 -1
- dodal/devices/dcm.py +1 -1
- dodal/devices/detector/detector.py +31 -30
- dodal/devices/detector/detector_motion.py +1 -1
- dodal/devices/fast_grid_scan.py +14 -24
- dodal/devices/focusing_mirror.py +2 -2
- dodal/devices/i22/dcm.py +1 -1
- dodal/devices/i22/fswitch.py +6 -2
- dodal/devices/i22/nxsas.py +32 -11
- dodal/devices/i24/aperture.py +1 -1
- dodal/devices/i24/beamstop.py +1 -1
- dodal/devices/i24/dcm.py +1 -1
- dodal/devices/i24/i24_detector_motion.py +1 -1
- dodal/devices/i24/pmac.py +24 -8
- dodal/devices/linkam3.py +1 -1
- dodal/devices/motors.py +1 -1
- dodal/devices/oav/oav_to_redis_forwarder.py +46 -17
- dodal/devices/robot.py +1 -2
- dodal/devices/scatterguard.py +1 -1
- dodal/devices/scintillator.py +1 -1
- dodal/devices/slits.py +1 -1
- dodal/devices/smargon.py +1 -1
- dodal/devices/tetramm.py +20 -16
- dodal/devices/training_rig/__init__.py +0 -0
- dodal/devices/training_rig/sample_stage.py +10 -0
- dodal/devices/turbo_slit.py +1 -1
- dodal/devices/undulator.py +1 -1
- dodal/devices/util/adjuster_plans.py +1 -1
- dodal/devices/util/save_panda.py +1 -1
- dodal/devices/util/test_utils.py +1 -1
- dodal/devices/xbpm_feedback.py +1 -2
- dodal/devices/xspress3/xspress3.py +1 -1
- dodal/devices/zebra.py +5 -0
- dodal/devices/zebra_controlled_shutter.py +24 -9
- dodal/devices/zocalo/zocalo_results.py +6 -2
- dodal/log.py +32 -10
- dodal/plans/check_topup.py +65 -10
- dodal/plans/data_session_metadata.py +8 -10
- dodal/plans/motor_util_plans.py +1 -1
- dodal/devices/beamstop.py +0 -8
- {dls_dodal-1.30.0.dist-info → dls_dodal-1.31.0.dist-info}/LICENSE +0 -0
- {dls_dodal-1.30.0.dist-info → dls_dodal-1.31.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.30.0.dist-info → dls_dodal-1.31.0.dist-info}/top_level.txt +0 -0
dodal/devices/i22/dcm.py
CHANGED
|
@@ -6,7 +6,7 @@ from typing import Literal
|
|
|
6
6
|
from bluesky.protocols import Reading
|
|
7
7
|
from event_model.documents.event_descriptor import DataKey
|
|
8
8
|
from ophyd_async.core import ConfigSignal, StandardReadable, soft_signal_r_and_setter
|
|
9
|
-
from ophyd_async.epics.
|
|
9
|
+
from ophyd_async.epics.motor import Motor
|
|
10
10
|
from ophyd_async.epics.signal import epics_signal_r
|
|
11
11
|
|
|
12
12
|
# Conversion constant for energy and wavelength, taken from the X-Ray data booklet
|
dodal/devices/i22/fswitch.py
CHANGED
|
@@ -4,8 +4,12 @@ from enum import Enum
|
|
|
4
4
|
|
|
5
5
|
from bluesky.protocols import Reading
|
|
6
6
|
from event_model import DataKey
|
|
7
|
-
from ophyd_async.core import
|
|
8
|
-
|
|
7
|
+
from ophyd_async.core import (
|
|
8
|
+
ConfigSignal,
|
|
9
|
+
DeviceVector,
|
|
10
|
+
StandardReadable,
|
|
11
|
+
soft_signal_r_and_setter,
|
|
12
|
+
)
|
|
9
13
|
from ophyd_async.epics.signal import epics_signal_r
|
|
10
14
|
|
|
11
15
|
|
dodal/devices/i22/nxsas.py
CHANGED
|
@@ -1,12 +1,33 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from collections.abc import Awaitable, Iterable
|
|
1
3
|
from dataclasses import dataclass, fields
|
|
4
|
+
from typing import TypeVar
|
|
2
5
|
|
|
3
6
|
from bluesky.protocols import Reading
|
|
4
7
|
from event_model.documents.event_descriptor import DataKey
|
|
5
|
-
from ophyd_async.core import
|
|
6
|
-
from ophyd_async.epics.
|
|
7
|
-
from ophyd_async.epics.
|
|
8
|
+
from ophyd_async.core import PathProvider
|
|
9
|
+
from ophyd_async.epics.adaravis import AravisController, AravisDetector
|
|
10
|
+
from ophyd_async.epics.adpilatus import PilatusDetector
|
|
8
11
|
|
|
9
12
|
ValueAndUnits = tuple[float, str]
|
|
13
|
+
T = TypeVar("T")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# TODO: Remove this file as part of github.com/DiamondLightSource/dodal/issues/595
|
|
17
|
+
# Until which, temporarily duplicated non-public method from ophyd_async
|
|
18
|
+
async def _merge_gathered_dicts(
|
|
19
|
+
coros: Iterable[Awaitable[dict[str, T]]],
|
|
20
|
+
) -> dict[str, T]:
|
|
21
|
+
"""Merge dictionaries produced by a sequence of coroutines.
|
|
22
|
+
|
|
23
|
+
Can be used for merging ``read()`` or ``describe``. For instance::
|
|
24
|
+
|
|
25
|
+
combined_read = await merge_gathered_dicts(s.read() for s in signals)
|
|
26
|
+
"""
|
|
27
|
+
ret: dict[str, T] = {}
|
|
28
|
+
for result in await asyncio.gather(*coros):
|
|
29
|
+
ret.update(result)
|
|
30
|
+
return ret
|
|
10
31
|
|
|
11
32
|
|
|
12
33
|
@dataclass
|
|
@@ -80,7 +101,7 @@ class NXSasPilatus(PilatusDetector):
|
|
|
80
101
|
def __init__(
|
|
81
102
|
self,
|
|
82
103
|
prefix: str,
|
|
83
|
-
|
|
104
|
+
path_provider: PathProvider,
|
|
84
105
|
drv_suffix: str,
|
|
85
106
|
hdf_suffix: str,
|
|
86
107
|
metadata_holder: NXSasMetadataHolder,
|
|
@@ -93,7 +114,7 @@ class NXSasPilatus(PilatusDetector):
|
|
|
93
114
|
Writes hdf5 files."""
|
|
94
115
|
super().__init__(
|
|
95
116
|
prefix,
|
|
96
|
-
|
|
117
|
+
path_provider,
|
|
97
118
|
drv_suffix=drv_suffix,
|
|
98
119
|
hdf_suffix=hdf_suffix,
|
|
99
120
|
name=name,
|
|
@@ -101,7 +122,7 @@ class NXSasPilatus(PilatusDetector):
|
|
|
101
122
|
self._metadata_holder = metadata_holder
|
|
102
123
|
|
|
103
124
|
async def read_configuration(self) -> dict[str, Reading]:
|
|
104
|
-
return await
|
|
125
|
+
return await _merge_gathered_dicts(
|
|
105
126
|
r
|
|
106
127
|
for r in (
|
|
107
128
|
super().read_configuration(),
|
|
@@ -110,7 +131,7 @@ class NXSasPilatus(PilatusDetector):
|
|
|
110
131
|
)
|
|
111
132
|
|
|
112
133
|
async def describe_configuration(self) -> dict[str, DataKey]:
|
|
113
|
-
return await
|
|
134
|
+
return await _merge_gathered_dicts(
|
|
114
135
|
r
|
|
115
136
|
for r in (
|
|
116
137
|
super().describe_configuration(),
|
|
@@ -123,7 +144,7 @@ class NXSasOAV(AravisDetector):
|
|
|
123
144
|
def __init__(
|
|
124
145
|
self,
|
|
125
146
|
prefix: str,
|
|
126
|
-
|
|
147
|
+
path_provider: PathProvider,
|
|
127
148
|
drv_suffix: str,
|
|
128
149
|
hdf_suffix: str,
|
|
129
150
|
metadata_holder: NXSasMetadataHolder,
|
|
@@ -137,7 +158,7 @@ class NXSasOAV(AravisDetector):
|
|
|
137
158
|
Writes hdf5 files."""
|
|
138
159
|
super().__init__(
|
|
139
160
|
prefix,
|
|
140
|
-
|
|
161
|
+
path_provider,
|
|
141
162
|
drv_suffix=drv_suffix,
|
|
142
163
|
hdf_suffix=hdf_suffix,
|
|
143
164
|
name=name,
|
|
@@ -146,7 +167,7 @@ class NXSasOAV(AravisDetector):
|
|
|
146
167
|
self._metadata_holder = metadata_holder
|
|
147
168
|
|
|
148
169
|
async def read_configuration(self) -> dict[str, Reading]:
|
|
149
|
-
return await
|
|
170
|
+
return await _merge_gathered_dicts(
|
|
150
171
|
r
|
|
151
172
|
for r in (
|
|
152
173
|
super().read_configuration(),
|
|
@@ -155,7 +176,7 @@ class NXSasOAV(AravisDetector):
|
|
|
155
176
|
)
|
|
156
177
|
|
|
157
178
|
async def describe_configuration(self) -> dict[str, DataKey]:
|
|
158
|
-
return await
|
|
179
|
+
return await _merge_gathered_dicts(
|
|
159
180
|
r
|
|
160
181
|
for r in (
|
|
161
182
|
super().describe_configuration(),
|
dodal/devices/i24/aperture.py
CHANGED
dodal/devices/i24/beamstop.py
CHANGED
dodal/devices/i24/dcm.py
CHANGED
dodal/devices/i24/pmac.py
CHANGED
|
@@ -2,12 +2,18 @@ from enum import Enum, IntEnum
|
|
|
2
2
|
from typing import SupportsFloat
|
|
3
3
|
|
|
4
4
|
from bluesky.protocols import Triggerable
|
|
5
|
-
from ophyd_async.core import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
from ophyd_async.core import (
|
|
6
|
+
DEFAULT_TIMEOUT,
|
|
7
|
+
AsyncStatus,
|
|
8
|
+
CalculateTimeout,
|
|
9
|
+
SignalBackend,
|
|
10
|
+
SignalR,
|
|
11
|
+
SignalRW,
|
|
12
|
+
SoftSignalBackend,
|
|
13
|
+
StandardReadable,
|
|
14
|
+
wait_for_value,
|
|
15
|
+
)
|
|
16
|
+
from ophyd_async.epics.motor import Motor
|
|
11
17
|
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
|
|
12
18
|
|
|
13
19
|
HOME_STR = r"\#1hmz\#2hmz\#3hmz" # Command to home the PMAC motors
|
|
@@ -79,7 +85,12 @@ class PMACStringLaser(SignalRW):
|
|
|
79
85
|
super().__init__(backend, timeout, name)
|
|
80
86
|
|
|
81
87
|
@AsyncStatus.wrap
|
|
82
|
-
async def set(
|
|
88
|
+
async def set(
|
|
89
|
+
self,
|
|
90
|
+
value: LaserSettings,
|
|
91
|
+
wait=True,
|
|
92
|
+
timeout=CalculateTimeout,
|
|
93
|
+
):
|
|
83
94
|
await self.signal.set(value.value, wait, timeout)
|
|
84
95
|
|
|
85
96
|
|
|
@@ -97,7 +108,12 @@ class PMACStringEncReset(SignalRW):
|
|
|
97
108
|
super().__init__(backend, timeout, name)
|
|
98
109
|
|
|
99
110
|
@AsyncStatus.wrap
|
|
100
|
-
async def set(
|
|
111
|
+
async def set(
|
|
112
|
+
self,
|
|
113
|
+
value: EncReset,
|
|
114
|
+
wait=True,
|
|
115
|
+
timeout=CalculateTimeout,
|
|
116
|
+
):
|
|
101
117
|
await self.signal.set(value.value, wait, timeout)
|
|
102
118
|
|
|
103
119
|
|
dodal/devices/linkam3.py
CHANGED
|
@@ -8,9 +8,9 @@ from ophyd_async.core import (
|
|
|
8
8
|
HintedSignal,
|
|
9
9
|
StandardReadable,
|
|
10
10
|
WatchableAsyncStatus,
|
|
11
|
+
WatcherUpdate,
|
|
11
12
|
observe_value,
|
|
12
13
|
)
|
|
13
|
-
from ophyd_async.core.utils import WatcherUpdate
|
|
14
14
|
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
|
|
15
15
|
|
|
16
16
|
|
dodal/devices/motors.py
CHANGED
|
@@ -2,12 +2,18 @@ import asyncio
|
|
|
2
2
|
import io
|
|
3
3
|
import pickle
|
|
4
4
|
import uuid
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
6
|
+
from datetime import timedelta
|
|
5
7
|
|
|
6
8
|
import numpy as np
|
|
7
9
|
from aiohttp import ClientResponse, ClientSession
|
|
8
|
-
from bluesky.protocols import Flyable
|
|
9
|
-
from ophyd_async.core import
|
|
10
|
-
|
|
10
|
+
from bluesky.protocols import Flyable, Stoppable
|
|
11
|
+
from ophyd_async.core import (
|
|
12
|
+
AsyncStatus,
|
|
13
|
+
StandardReadable,
|
|
14
|
+
soft_signal_r_and_setter,
|
|
15
|
+
soft_signal_rw,
|
|
16
|
+
)
|
|
11
17
|
from ophyd_async.epics.signal import epics_signal_r
|
|
12
18
|
from PIL import Image
|
|
13
19
|
from redis.asyncio import StrictRedis
|
|
@@ -24,7 +30,7 @@ async def get_next_jpeg(response: ClientResponse) -> bytes:
|
|
|
24
30
|
return line + await response.content.readuntil(JPEG_STOP_BYTE)
|
|
25
31
|
|
|
26
32
|
|
|
27
|
-
class OAVToRedisForwarder(StandardReadable, Flyable):
|
|
33
|
+
class OAVToRedisForwarder(StandardReadable, Flyable, Stoppable):
|
|
28
34
|
"""Forwards OAV image data to redis. To use call:
|
|
29
35
|
|
|
30
36
|
> bps.kickoff(oav_forwarder)
|
|
@@ -33,6 +39,8 @@ class OAVToRedisForwarder(StandardReadable, Flyable):
|
|
|
33
39
|
|
|
34
40
|
"""
|
|
35
41
|
|
|
42
|
+
DATA_EXPIRY_DAYS = 7
|
|
43
|
+
|
|
36
44
|
def __init__(
|
|
37
45
|
self,
|
|
38
46
|
prefix: str,
|
|
@@ -40,7 +48,6 @@ class OAVToRedisForwarder(StandardReadable, Flyable):
|
|
|
40
48
|
redis_password: str,
|
|
41
49
|
redis_db: int = 0,
|
|
42
50
|
name: str = "",
|
|
43
|
-
redis_key: str = "test-image",
|
|
44
51
|
) -> None:
|
|
45
52
|
"""Reads image data from the MJPEG stream on an OAV and forwards it into a
|
|
46
53
|
redis database. This is currently only used for murko integration.
|
|
@@ -51,9 +58,8 @@ class OAVToRedisForwarder(StandardReadable, Flyable):
|
|
|
51
58
|
redis_password: str the password for the redis database
|
|
52
59
|
redis_db: int which redis database to connect to, defaults to 0
|
|
53
60
|
name: str the name of this device
|
|
54
|
-
redis_key: str the key to store data in, defaults to "test-image"
|
|
55
61
|
"""
|
|
56
|
-
self.stream_url = epics_signal_r(str, f"{prefix}
|
|
62
|
+
self.stream_url = epics_signal_r(str, f"{prefix}MJPG:MJPG_URL_RBV")
|
|
57
63
|
|
|
58
64
|
with self.add_children_as_readables():
|
|
59
65
|
self.uuid, self.uuid_setter = soft_signal_r_and_setter(str)
|
|
@@ -63,7 +69,9 @@ class OAVToRedisForwarder(StandardReadable, Flyable):
|
|
|
63
69
|
host=redis_host, password=redis_password, db=redis_db
|
|
64
70
|
)
|
|
65
71
|
|
|
66
|
-
self.
|
|
72
|
+
self._stop_flag = False
|
|
73
|
+
|
|
74
|
+
self.sample_id = soft_signal_rw(int, initial_value=0)
|
|
67
75
|
|
|
68
76
|
# The uuid that images are being saved under, this should be monitored for
|
|
69
77
|
# callbacks to correlate the data
|
|
@@ -79,22 +87,43 @@ class OAVToRedisForwarder(StandardReadable, Flyable):
|
|
|
79
87
|
self.uuid_setter(image_uuid := str(uuid.uuid4()))
|
|
80
88
|
img = Image.open(io.BytesIO(jpeg_bytes))
|
|
81
89
|
image_data = pickle.dumps(np.asarray(img))
|
|
82
|
-
await self.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
90
|
+
sample_id = str(await self.sample_id.get_value())
|
|
91
|
+
await self.redis_client.hset(sample_id, image_uuid, image_data) # type: ignore
|
|
92
|
+
await self.redis_client.expire(sample_id, timedelta(days=self.DATA_EXPIRY_DAYS))
|
|
93
|
+
LOGGER.debug(f"Sent frame to redis key {sample_id} with uuid {image_uuid}")
|
|
94
|
+
|
|
95
|
+
async def _open_connection_and_do_function(
|
|
96
|
+
self, function_to_do: Callable[[ClientResponse, str | None], Awaitable]
|
|
97
|
+
):
|
|
86
98
|
stream_url = await self.stream_url.get_value()
|
|
87
99
|
async with ClientSession() as session:
|
|
88
100
|
async with session.get(stream_url) as response:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
await function_to_do(response, stream_url)
|
|
102
|
+
|
|
103
|
+
async def _stream_to_redis(self, response, _):
|
|
104
|
+
while not self._stop_flag:
|
|
105
|
+
await self._get_frame_and_put_to_redis(response)
|
|
106
|
+
await asyncio.sleep(0.01)
|
|
107
|
+
|
|
108
|
+
async def _confirm_mjpg_stream(self, response, stream_url):
|
|
109
|
+
if response.content_type != "multipart/x-mixed-replace":
|
|
110
|
+
raise ValueError(f"{stream_url} is not an MJPG stream")
|
|
92
111
|
|
|
93
112
|
@AsyncStatus.wrap
|
|
94
113
|
async def kickoff(self):
|
|
95
|
-
self.
|
|
114
|
+
self._stop_flag = False
|
|
115
|
+
await self._open_connection_and_do_function(self._confirm_mjpg_stream)
|
|
116
|
+
self.forwarding_task = asyncio.create_task(
|
|
117
|
+
self._open_connection_and_do_function(self._stream_to_redis)
|
|
118
|
+
)
|
|
96
119
|
|
|
97
120
|
@AsyncStatus.wrap
|
|
98
121
|
async def complete(self):
|
|
99
122
|
assert self.forwarding_task, "Device not kicked off"
|
|
100
|
-
self.
|
|
123
|
+
await self.stop()
|
|
124
|
+
|
|
125
|
+
@AsyncStatus.wrap
|
|
126
|
+
async def stop(self, success=True):
|
|
127
|
+
if self.forwarding_task:
|
|
128
|
+
self._stop_flag = True
|
|
129
|
+
await self.forwarding_task
|
dodal/devices/robot.py
CHANGED
|
@@ -10,8 +10,7 @@ from ophyd_async.core import (
|
|
|
10
10
|
set_and_wait_for_value,
|
|
11
11
|
wait_for_value,
|
|
12
12
|
)
|
|
13
|
-
from ophyd_async.epics.signal import epics_signal_r, epics_signal_x
|
|
14
|
-
from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
|
|
13
|
+
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw_rbv, epics_signal_x
|
|
15
14
|
|
|
16
15
|
from dodal.log import LOGGER
|
|
17
16
|
|
dodal/devices/scatterguard.py
CHANGED
dodal/devices/scintillator.py
CHANGED
dodal/devices/slits.py
CHANGED
dodal/devices/smargon.py
CHANGED
|
@@ -7,7 +7,7 @@ from typing import cast
|
|
|
7
7
|
from bluesky import plan_stubs as bps
|
|
8
8
|
from bluesky.utils import Msg
|
|
9
9
|
from ophyd_async.core import AsyncStatus, Device, StandardReadable, wait_for_value
|
|
10
|
-
from ophyd_async.epics.
|
|
10
|
+
from ophyd_async.epics.motor import Motor
|
|
11
11
|
from ophyd_async.epics.signal import epics_signal_r
|
|
12
12
|
|
|
13
13
|
from dodal.devices.util.epics_util import SetWhenEnabled
|
dodal/devices/tetramm.py
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from collections.abc import Sequence
|
|
3
2
|
from enum import Enum
|
|
4
3
|
|
|
5
4
|
from bluesky.protocols import Hints
|
|
6
5
|
from ophyd_async.core import (
|
|
7
6
|
AsyncStatus,
|
|
7
|
+
DatasetDescriber,
|
|
8
8
|
DetectorControl,
|
|
9
9
|
DetectorTrigger,
|
|
10
10
|
Device,
|
|
11
|
-
|
|
12
|
-
ShapeProvider,
|
|
11
|
+
PathProvider,
|
|
13
12
|
StandardDetector,
|
|
14
13
|
set_and_wait_for_value,
|
|
15
14
|
soft_signal_r_and_setter,
|
|
16
15
|
)
|
|
17
|
-
from ophyd_async.epics.
|
|
18
|
-
from ophyd_async.epics.
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
from ophyd_async.epics.adcore import ADHDFWriter, NDFileHDFIO, stop_busy_record
|
|
17
|
+
from ophyd_async.epics.signal import (
|
|
18
|
+
epics_signal_r,
|
|
19
|
+
epics_signal_rw,
|
|
20
|
+
epics_signal_rw_rbv,
|
|
21
|
+
)
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class TetrammRange(str, Enum):
|
|
@@ -108,7 +109,7 @@ class TetrammController(DetectorControl):
|
|
|
108
109
|
self.minimum_values_per_reading = minimum_values_per_reading
|
|
109
110
|
self.readings_per_frame = readings_per_frame
|
|
110
111
|
|
|
111
|
-
def get_deadtime(self, exposure: float) -> float:
|
|
112
|
+
def get_deadtime(self, exposure: float | None) -> float:
|
|
112
113
|
# 2 internal clock cycles. Best effort approximation
|
|
113
114
|
return 2 / self.base_sample_rate
|
|
114
115
|
|
|
@@ -204,14 +205,17 @@ class TetrammController(DetectorControl):
|
|
|
204
205
|
)
|
|
205
206
|
|
|
206
207
|
|
|
207
|
-
class
|
|
208
|
+
class TetrammDatasetDescriber(DatasetDescriber):
|
|
208
209
|
max_channels = 11
|
|
209
210
|
|
|
210
211
|
def __init__(self, controller: TetrammController) -> None:
|
|
211
212
|
self.controller = controller
|
|
212
213
|
|
|
213
|
-
async def
|
|
214
|
-
return
|
|
214
|
+
async def np_datatype(self) -> str:
|
|
215
|
+
return "<f8" # IEEE 754 double precision floating point
|
|
216
|
+
|
|
217
|
+
async def shape(self) -> tuple[int, int]:
|
|
218
|
+
return (self.max_channels, self.controller.readings_per_frame)
|
|
215
219
|
|
|
216
220
|
|
|
217
221
|
# TODO: Support MeanValue signals https://github.com/DiamondLightSource/dodal/issues/337
|
|
@@ -219,13 +223,13 @@ class TetrammDetector(StandardDetector):
|
|
|
219
223
|
def __init__(
|
|
220
224
|
self,
|
|
221
225
|
prefix: str,
|
|
222
|
-
|
|
226
|
+
path_provider: PathProvider,
|
|
223
227
|
name: str,
|
|
224
228
|
type: str | None = None,
|
|
225
229
|
**scalar_sigs: str,
|
|
226
230
|
) -> None:
|
|
227
231
|
self.drv = TetrammDriver(prefix + "DRV:")
|
|
228
|
-
self.hdf =
|
|
232
|
+
self.hdf = NDFileHDFIO(prefix + "HDF5:")
|
|
229
233
|
controller = TetrammController(self.drv)
|
|
230
234
|
config_signals = [
|
|
231
235
|
self.drv.values_per_reading,
|
|
@@ -239,11 +243,11 @@ class TetrammDetector(StandardDetector):
|
|
|
239
243
|
self.type = None
|
|
240
244
|
super().__init__(
|
|
241
245
|
controller,
|
|
242
|
-
|
|
246
|
+
ADHDFWriter(
|
|
243
247
|
self.hdf,
|
|
244
|
-
|
|
248
|
+
path_provider,
|
|
245
249
|
lambda: self.name,
|
|
246
|
-
|
|
250
|
+
TetrammDatasetDescriber(controller),
|
|
247
251
|
**scalar_sigs,
|
|
248
252
|
),
|
|
249
253
|
config_signals,
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from ophyd_async.core import StandardReadable
|
|
2
|
+
from ophyd_async.epics.motor import Motor
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TrainingRigSampleStage(StandardReadable):
|
|
6
|
+
def __init__(self, prefix: str, name: str = ""):
|
|
7
|
+
with self.add_children_as_readables():
|
|
8
|
+
self.x = Motor(prefix + "X")
|
|
9
|
+
self.theta = Motor(prefix + "A")
|
|
10
|
+
super().__init__(name)
|
dodal/devices/turbo_slit.py
CHANGED
dodal/devices/undulator.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
3
|
from ophyd_async.core import ConfigSignal, StandardReadable, soft_signal_r_and_setter
|
|
4
|
-
from ophyd_async.epics.
|
|
4
|
+
from ophyd_async.epics.motor import Motor
|
|
5
5
|
from ophyd_async.epics.signal import epics_signal_r
|
|
6
6
|
|
|
7
7
|
# The acceptable difference, in mm, between the undulator gap and the DCM
|
|
@@ -8,7 +8,7 @@ from collections.abc import Callable, Generator
|
|
|
8
8
|
from bluesky import plan_stubs as bps
|
|
9
9
|
from bluesky.utils import Msg
|
|
10
10
|
from ophyd.epics_motor import EpicsMotor
|
|
11
|
-
from ophyd_async.epics.
|
|
11
|
+
from ophyd_async.epics.motor import Motor
|
|
12
12
|
|
|
13
13
|
from dodal.log import LOGGER
|
|
14
14
|
|
dodal/devices/util/save_panda.py
CHANGED
|
@@ -7,7 +7,7 @@ from typing import cast
|
|
|
7
7
|
|
|
8
8
|
from bluesky.run_engine import RunEngine
|
|
9
9
|
from ophyd_async.core import Device, save_device
|
|
10
|
-
from ophyd_async.panda import phase_sorter
|
|
10
|
+
from ophyd_async.fastcs.panda import phase_sorter
|
|
11
11
|
|
|
12
12
|
from dodal.beamlines import module_name_for_beamline
|
|
13
13
|
from dodal.utils import make_device
|
dodal/devices/util/test_utils.py
CHANGED
dodal/devices/xbpm_feedback.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
3
|
from bluesky.protocols import Triggerable
|
|
4
|
-
from ophyd_async.core import Device, observe_value
|
|
5
|
-
from ophyd_async.core.async_status import AsyncStatus
|
|
4
|
+
from ophyd_async.core import AsyncStatus, Device, observe_value
|
|
6
5
|
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
|
|
7
6
|
|
|
8
7
|
|
dodal/devices/zebra.py
CHANGED
|
@@ -37,6 +37,11 @@ TTL_SHUTTER = 2
|
|
|
37
37
|
TTL_XSPRESS3 = 3
|
|
38
38
|
TTL_PANDA = 4
|
|
39
39
|
|
|
40
|
+
# The AND gate that controls the automatic shutter
|
|
41
|
+
AUTO_SHUTTER_GATE = 2
|
|
42
|
+
# The input that triggers the automatic shutter
|
|
43
|
+
AUTO_SHUTTER_INPUT = 1
|
|
44
|
+
|
|
40
45
|
|
|
41
46
|
class ArmSource(str, Enum):
|
|
42
47
|
SOFT = "Soft"
|
|
@@ -7,7 +7,7 @@ from ophyd_async.core import (
|
|
|
7
7
|
StandardReadable,
|
|
8
8
|
wait_for_value,
|
|
9
9
|
)
|
|
10
|
-
from ophyd_async.epics.signal import epics_signal_r, epics_signal_w
|
|
10
|
+
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw, epics_signal_w
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class ZebraShutterState(str, Enum):
|
|
@@ -15,22 +15,37 @@ class ZebraShutterState(str, Enum):
|
|
|
15
15
|
OPEN = "Open"
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
class ZebraShutterControl(str, Enum):
|
|
19
|
+
MANUAL = "Manual"
|
|
20
|
+
AUTO = "Auto"
|
|
21
|
+
|
|
22
|
+
|
|
18
23
|
class ZebraShutter(StandardReadable, Movable):
|
|
24
|
+
"""The shutter on most MX beamlines is controlled by the zebra.
|
|
25
|
+
|
|
26
|
+
Internally in the zebra there are two AND gates, one for manual control and one for
|
|
27
|
+
automatic control. A soft input (aliased to control_mode) will switch between
|
|
28
|
+
which of these AND gates to use. For the manual gate the shutter is then controlled
|
|
29
|
+
by a different soft input (aliased to _manual_position_setpoint). Both these AND
|
|
30
|
+
gates then feed into an OR gate, which then feeds to the shutter."""
|
|
31
|
+
|
|
19
32
|
def __init__(self, prefix: str, name: str):
|
|
20
|
-
self.
|
|
21
|
-
|
|
22
|
-
datatype=ZebraShutterState,
|
|
33
|
+
self._manual_position_setpoint = epics_signal_w(
|
|
34
|
+
ZebraShutterState, prefix + "CTRL2"
|
|
23
35
|
)
|
|
36
|
+
self.control_mode = epics_signal_rw(ZebraShutterControl, prefix + "CTRL1")
|
|
37
|
+
|
|
24
38
|
with self.add_children_as_readables():
|
|
25
|
-
self.position_readback = epics_signal_r(
|
|
26
|
-
read_pv=prefix + "STA",
|
|
27
|
-
datatype=ZebraShutterState,
|
|
28
|
-
)
|
|
39
|
+
self.position_readback = epics_signal_r(ZebraShutterState, prefix + "STA")
|
|
29
40
|
super().__init__(name=name)
|
|
30
41
|
|
|
31
42
|
@AsyncStatus.wrap
|
|
32
43
|
async def set(self, value: ZebraShutterState):
|
|
33
|
-
await self.
|
|
44
|
+
if await self.control_mode.get_value() == ZebraShutterControl.AUTO:
|
|
45
|
+
raise UserWarning(
|
|
46
|
+
f"Tried to set shutter to {value.value} but the shutter is in auto mode."
|
|
47
|
+
)
|
|
48
|
+
await self._manual_position_setpoint.set(value)
|
|
34
49
|
return await wait_for_value(
|
|
35
50
|
signal=self.position_readback,
|
|
36
51
|
match=value,
|