dls-dodal 1.36.0__py3-none-any.whl → 1.36.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.
- {dls_dodal-1.36.0.dist-info → dls_dodal-1.36.2.dist-info}/METADATA +33 -33
- {dls_dodal-1.36.0.dist-info → dls_dodal-1.36.2.dist-info}/RECORD +39 -39
- {dls_dodal-1.36.0.dist-info → dls_dodal-1.36.2.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/__init__.py +1 -0
- dodal/beamlines/adsim.py +75 -0
- dodal/beamlines/b01_1.py +16 -31
- dodal/beamlines/i22.py +124 -265
- dodal/beamlines/i24.py +72 -7
- dodal/beamlines/p38.py +16 -1
- dodal/beamlines/p99.py +22 -53
- dodal/beamlines/training_rig.py +16 -26
- dodal/cli.py +54 -8
- dodal/common/beamlines/beamline_utils.py +32 -2
- dodal/common/beamlines/device_helpers.py +2 -0
- dodal/devices/adsim.py +10 -10
- dodal/devices/attenuator.py +15 -5
- dodal/devices/dcm.py +5 -4
- dodal/devices/fast_grid_scan.py +21 -46
- dodal/devices/focusing_mirror.py +20 -6
- dodal/devices/i24/beam_center.py +12 -0
- dodal/devices/i24/focus_mirrors.py +60 -0
- dodal/devices/i24/pilatus_metadata.py +44 -0
- dodal/devices/linkam3.py +1 -1
- dodal/devices/motors.py +14 -10
- dodal/devices/oav/oav_detector.py +2 -2
- dodal/devices/oav/pin_image_recognition/__init__.py +4 -7
- dodal/devices/oav/utils.py +1 -0
- dodal/devices/p99/sample_stage.py +12 -16
- dodal/devices/pressure_jump_cell.py +299 -0
- dodal/devices/robot.py +1 -1
- dodal/devices/tetramm.py +1 -1
- dodal/devices/undulator.py +4 -1
- dodal/devices/undulator_dcm.py +7 -19
- dodal/devices/zocalo/zocalo_results.py +7 -7
- dodal/utils.py +151 -2
- dodal/adsim.py +0 -17
- dodal/devices/areadetector/__init__.py +0 -10
- dodal/devices/areadetector/adaravis.py +0 -101
- dodal/devices/areadetector/adsim.py +0 -47
- dodal/devices/areadetector/adutils.py +0 -81
- {dls_dodal-1.36.0.dist-info → dls_dodal-1.36.2.dist-info}/LICENSE +0 -0
- {dls_dodal-1.36.0.dist-info → dls_dodal-1.36.2.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.36.0.dist-info → dls_dodal-1.36.2.dist-info}/top_level.txt +0 -0
dodal/beamlines/p99.py
CHANGED
|
@@ -1,61 +1,30 @@
|
|
|
1
|
-
from dodal.common.beamlines.beamline_utils import
|
|
1
|
+
from dodal.common.beamlines.beamline_utils import device_factory, set_beamline
|
|
2
2
|
from dodal.devices.motors import XYZPositioner
|
|
3
3
|
from dodal.devices.p99.sample_stage import FilterMotor, SampleAngleStage
|
|
4
4
|
from dodal.log import set_beamline as set_log_beamline
|
|
5
|
-
from dodal.utils import get_beamline_name
|
|
5
|
+
from dodal.utils import BeamlinePrefix, get_beamline_name
|
|
6
6
|
|
|
7
|
-
BL = get_beamline_name("
|
|
7
|
+
BL = get_beamline_name("p99")
|
|
8
|
+
PREFIX = BeamlinePrefix(BL)
|
|
8
9
|
set_log_beamline(BL)
|
|
9
10
|
set_beamline(BL)
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
) ->
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return device_instantiation(
|
|
32
|
-
FilterMotor,
|
|
33
|
-
prefix="-MO-STAGE-02:MP:SELECT",
|
|
34
|
-
name="sample_stage_filer",
|
|
35
|
-
wait=wait_for_connection,
|
|
36
|
-
fake=fake_with_ophyd_sim,
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def sample_xyz_stage(
|
|
41
|
-
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
|
|
42
|
-
) -> XYZPositioner:
|
|
43
|
-
return device_instantiation(
|
|
44
|
-
FilterMotor,
|
|
45
|
-
prefix="-MO-STAGE-02:",
|
|
46
|
-
name="sample_xyz_stage",
|
|
47
|
-
wait=wait_for_connection,
|
|
48
|
-
fake=fake_with_ophyd_sim,
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def sample_lab_xyz_stage(
|
|
53
|
-
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
|
|
54
|
-
) -> XYZPositioner:
|
|
55
|
-
return device_instantiation(
|
|
56
|
-
FilterMotor,
|
|
57
|
-
prefix="-MO-STAGE-02:LAB:",
|
|
58
|
-
name="sample_lab_xyz_stage",
|
|
59
|
-
wait=wait_for_connection,
|
|
60
|
-
fake=fake_with_ophyd_sim,
|
|
61
|
-
)
|
|
13
|
+
@device_factory()
|
|
14
|
+
def angle_stage() -> SampleAngleStage:
|
|
15
|
+
return SampleAngleStage(f"{PREFIX.beamline_prefix}-MO-STAGE-01:")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@device_factory()
|
|
19
|
+
def filter() -> FilterMotor:
|
|
20
|
+
return FilterMotor(f"{PREFIX.beamline_prefix}-MO-STAGE-02:MP:SELECT")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@device_factory()
|
|
24
|
+
def sample_stage() -> XYZPositioner:
|
|
25
|
+
return XYZPositioner(f"{PREFIX.beamline_prefix}-MO-STAGE-02:")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@device_factory()
|
|
29
|
+
def lab_stage() -> XYZPositioner:
|
|
30
|
+
return XYZPositioner(f"{PREFIX.beamline_prefix}-MO-STAGE-02:LAB:")
|
dodal/beamlines/training_rig.py
CHANGED
|
@@ -3,15 +3,16 @@ from pathlib import Path
|
|
|
3
3
|
from ophyd_async.epics.adaravis import AravisDetector
|
|
4
4
|
|
|
5
5
|
from dodal.common.beamlines.beamline_utils import (
|
|
6
|
-
|
|
6
|
+
device_factory,
|
|
7
7
|
get_path_provider,
|
|
8
8
|
set_path_provider,
|
|
9
9
|
)
|
|
10
10
|
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
|
|
11
|
+
from dodal.common.beamlines.device_helpers import HDF5_PREFIX
|
|
11
12
|
from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider
|
|
12
13
|
from dodal.devices.training_rig.sample_stage import TrainingRigSampleStage
|
|
13
14
|
from dodal.log import set_beamline as set_log_beamline
|
|
14
|
-
from dodal.utils import get_beamline_name
|
|
15
|
+
from dodal.utils import BeamlinePrefix, get_beamline_name
|
|
15
16
|
|
|
16
17
|
#
|
|
17
18
|
# HTSS Training Rig
|
|
@@ -20,11 +21,12 @@ from dodal.utils import get_beamline_name
|
|
|
20
21
|
# simple motors, a GigE camera and a PandA.
|
|
21
22
|
# Since there are multiple rigs whose PVs are identical aside from the prefix,
|
|
22
23
|
# this module can be used for any rig. It should fill in the prefix automatically
|
|
23
|
-
# if the ${BEAMLINE} environment variable is correctly set
|
|
24
|
-
# to
|
|
24
|
+
# if the ${BEAMLINE} environment variable is correctly set, else defaulting
|
|
25
|
+
# to p46, which is known to be in good working order.
|
|
25
26
|
#
|
|
26
27
|
|
|
27
|
-
BL = get_beamline_name("
|
|
28
|
+
BL = get_beamline_name("p46")
|
|
29
|
+
PREFIX = BeamlinePrefix(BL)
|
|
28
30
|
set_log_beamline(BL)
|
|
29
31
|
set_utils_beamline(BL)
|
|
30
32
|
|
|
@@ -37,28 +39,16 @@ set_path_provider(
|
|
|
37
39
|
)
|
|
38
40
|
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return device_instantiation(
|
|
44
|
-
TrainingRigSampleStage,
|
|
45
|
-
"sample_stage",
|
|
46
|
-
"-MO-MAP-01:STAGE:",
|
|
47
|
-
wait_for_connection,
|
|
48
|
-
fake_with_ophyd_sim,
|
|
49
|
-
)
|
|
42
|
+
@device_factory()
|
|
43
|
+
def sample_stage() -> TrainingRigSampleStage:
|
|
44
|
+
return TrainingRigSampleStage(f"{PREFIX.beamline_prefix}-MO-MAP-01:STAGE:")
|
|
50
45
|
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
AravisDetector,
|
|
57
|
-
"det",
|
|
58
|
-
"-EA-DET-01:",
|
|
59
|
-
wait_for_connection,
|
|
60
|
-
fake_with_ophyd_sim,
|
|
61
|
-
drv_suffix="DET:",
|
|
62
|
-
hdf_suffix="HDF5:",
|
|
47
|
+
@device_factory()
|
|
48
|
+
def det() -> AravisDetector:
|
|
49
|
+
return AravisDetector(
|
|
50
|
+
f"{PREFIX.beamline_prefix}-EA-DET-01:",
|
|
63
51
|
path_provider=get_path_provider(),
|
|
52
|
+
drv_suffix="DET:",
|
|
53
|
+
hdf_suffix=HDF5_PREFIX,
|
|
64
54
|
)
|
dodal/cli.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from collections.abc import Mapping
|
|
2
3
|
|
|
3
4
|
import click
|
|
4
5
|
from bluesky.run_engine import RunEngine
|
|
5
6
|
from ophyd_async.core import NotConnected
|
|
7
|
+
from ophyd_async.plan_stubs import ensure_connected
|
|
6
8
|
|
|
7
9
|
from dodal.beamlines import all_beamline_names, module_name_for_beamline
|
|
8
|
-
from dodal.utils import make_all_devices
|
|
10
|
+
from dodal.utils import AnyDevice, filter_ophyd_devices, make_all_devices
|
|
9
11
|
|
|
10
12
|
from . import __version__
|
|
11
13
|
|
|
@@ -50,22 +52,66 @@ def connect(beamline: str, all: bool, sim_backend: bool) -> None:
|
|
|
50
52
|
|
|
51
53
|
# We need to make a RunEngine to allow ophyd-async devices to connect.
|
|
52
54
|
# See https://blueskyproject.io/ophyd-async/main/explanations/event-loop-choice.html
|
|
53
|
-
RunEngine()
|
|
55
|
+
RE = RunEngine(call_returns_result=True)
|
|
54
56
|
|
|
55
57
|
print(f"Attempting connection to {beamline} (using {full_module_path})")
|
|
56
|
-
|
|
58
|
+
|
|
59
|
+
# Force all devices to be lazy (don't connect to PVs on instantiation) and do
|
|
60
|
+
# connection as an extra step, because the alternatives is handling the fact
|
|
61
|
+
# that only some devices may be lazy.
|
|
62
|
+
devices, instance_exceptions = make_all_devices(
|
|
57
63
|
full_module_path,
|
|
58
64
|
include_skipped=all,
|
|
59
65
|
fake_with_ophyd_sim=sim_backend,
|
|
66
|
+
wait_for_connection=False,
|
|
60
67
|
)
|
|
61
|
-
|
|
68
|
+
devices, connect_exceptions = _connect_devices(RE, devices, sim_backend)
|
|
62
69
|
|
|
63
|
-
|
|
70
|
+
# Inform user of successful connections
|
|
71
|
+
_report_successful_devices(devices, sim_backend)
|
|
72
|
+
|
|
73
|
+
# If exceptions have occurred, this will print details of the relevant PVs
|
|
74
|
+
exceptions = {**instance_exceptions, **connect_exceptions}
|
|
75
|
+
if len(exceptions) > 0:
|
|
76
|
+
raise NotConnected(exceptions)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _report_successful_devices(
|
|
80
|
+
devices: Mapping[str, AnyDevice],
|
|
81
|
+
sim_backend: bool,
|
|
82
|
+
) -> None:
|
|
83
|
+
sim_statement = " (sim mode)" if sim_backend else ""
|
|
64
84
|
connected_devices = "\n".join(
|
|
65
85
|
sorted([f"\t{device_name}" for device_name in devices.keys()])
|
|
66
86
|
)
|
|
87
|
+
|
|
88
|
+
print(f"{len(devices)} devices connected{sim_statement}:")
|
|
67
89
|
print(connected_devices)
|
|
68
90
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
91
|
+
|
|
92
|
+
def _connect_devices(
|
|
93
|
+
RE: RunEngine,
|
|
94
|
+
devices: Mapping[str, AnyDevice],
|
|
95
|
+
sim_backend: bool,
|
|
96
|
+
) -> tuple[Mapping[str, AnyDevice], Mapping[str, Exception]]:
|
|
97
|
+
ophyd_devices, ophyd_async_devices = filter_ophyd_devices(devices)
|
|
98
|
+
exceptions = {}
|
|
99
|
+
|
|
100
|
+
# Connect ophyd devices
|
|
101
|
+
for name, device in ophyd_devices.items():
|
|
102
|
+
try:
|
|
103
|
+
device.wait_for_connection()
|
|
104
|
+
except Exception as ex:
|
|
105
|
+
exceptions[name] = ex
|
|
106
|
+
|
|
107
|
+
# Connect ophyd-async devices
|
|
108
|
+
try:
|
|
109
|
+
RE(ensure_connected(*ophyd_async_devices.values(), mock=sim_backend))
|
|
110
|
+
except NotConnected as ex:
|
|
111
|
+
exceptions = {**exceptions, **ex.sub_errors}
|
|
112
|
+
|
|
113
|
+
# Only return the subset of devices that haven't raised an exception
|
|
114
|
+
successful_devices = {
|
|
115
|
+
name: device for name, device in devices.items() if name not in exceptions
|
|
116
|
+
}
|
|
117
|
+
return successful_devices, exceptions
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import inspect
|
|
2
2
|
from collections.abc import Callable
|
|
3
|
-
from typing import Final, TypeVar, cast
|
|
3
|
+
from typing import Annotated, Final, TypeVar, cast
|
|
4
4
|
|
|
5
5
|
from bluesky.run_engine import call_in_bluesky_event_loop
|
|
6
6
|
from ophyd import Device as OphydV1Device
|
|
7
7
|
from ophyd.sim import make_fake_device
|
|
8
|
+
from ophyd_async.core import DEFAULT_TIMEOUT
|
|
8
9
|
from ophyd_async.core import Device as OphydV2Device
|
|
9
10
|
from ophyd_async.core import wait_for_connection as v2_device_wait_for_connection
|
|
10
11
|
|
|
11
12
|
from dodal.common.types import UpdatingPathProvider
|
|
12
|
-
from dodal.utils import
|
|
13
|
+
from dodal.utils import (
|
|
14
|
+
AnyDevice,
|
|
15
|
+
BeamlinePrefix,
|
|
16
|
+
D,
|
|
17
|
+
DeviceInitializationController,
|
|
18
|
+
SkipType,
|
|
19
|
+
skip_device,
|
|
20
|
+
)
|
|
13
21
|
|
|
14
22
|
DEFAULT_CONNECTION_TIMEOUT: Final[float] = 5.0
|
|
15
23
|
|
|
@@ -124,6 +132,28 @@ def device_instantiation(
|
|
|
124
132
|
return device_instance
|
|
125
133
|
|
|
126
134
|
|
|
135
|
+
def device_factory(
|
|
136
|
+
*,
|
|
137
|
+
use_factory_name: Annotated[bool, "Use factory name as name of device"] = True,
|
|
138
|
+
timeout: Annotated[float, "Timeout for connecting to the device"] = DEFAULT_TIMEOUT,
|
|
139
|
+
mock: Annotated[bool, "Use Signals with mock backends for device"] = False,
|
|
140
|
+
skip: Annotated[
|
|
141
|
+
SkipType,
|
|
142
|
+
"mark the factory to be (conditionally) skipped when beamline is imported by external program",
|
|
143
|
+
] = False,
|
|
144
|
+
) -> Callable[[Callable[[], D]], DeviceInitializationController[D]]:
|
|
145
|
+
def decorator(factory: Callable[[], D]) -> DeviceInitializationController[D]:
|
|
146
|
+
return DeviceInitializationController(
|
|
147
|
+
factory,
|
|
148
|
+
use_factory_name,
|
|
149
|
+
timeout,
|
|
150
|
+
mock,
|
|
151
|
+
skip,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
return decorator
|
|
155
|
+
|
|
156
|
+
|
|
127
157
|
def set_path_provider(provider: UpdatingPathProvider):
|
|
128
158
|
global PATH_PROVIDER
|
|
129
159
|
|
dodal/devices/adsim.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
from
|
|
1
|
+
from ophyd_async.core import StandardReadable
|
|
2
|
+
from ophyd_async.epics.motor import Motor
|
|
2
3
|
|
|
3
4
|
|
|
4
|
-
class SimStage(
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
"""
|
|
5
|
+
class SimStage(StandardReadable):
|
|
6
|
+
"""Simulated Sample Stage for use with the containerised simulated beamline
|
|
7
|
+
https://github.com/epics-containers/example-services"""
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
def __init__(self, prefix: str, name: str = "sim"):
|
|
10
|
+
with self.add_children_as_readables():
|
|
11
|
+
self.x = Motor(prefix + "M1")
|
|
12
|
+
self.y = Motor(prefix + "M2")
|
|
13
|
+
super().__init__(name=name)
|
dodal/devices/attenuator.py
CHANGED
|
@@ -14,7 +14,20 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw, epics_signal
|
|
|
14
14
|
from dodal.log import LOGGER
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class
|
|
17
|
+
class ReadOnlyAttenuator(StandardReadable):
|
|
18
|
+
"""A read-only attenuator class with a minimum set of PVs for reading.
|
|
19
|
+
|
|
20
|
+
The actual_transmission will return a fractional transmission between 0-1.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
24
|
+
with self.add_children_as_readables():
|
|
25
|
+
self.actual_transmission = epics_signal_r(float, prefix + "MATCH")
|
|
26
|
+
|
|
27
|
+
super().__init__(name)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Attenuator(ReadOnlyAttenuator, Movable):
|
|
18
31
|
"""The attenuator will insert filters into the beam to reduce its transmission.
|
|
19
32
|
|
|
20
33
|
This device should be set with:
|
|
@@ -42,10 +55,7 @@ class Attenuator(StandardReadable, Movable):
|
|
|
42
55
|
self._use_current_energy = epics_signal_x(prefix + "E2WL:USECURRENTENERGY.PROC")
|
|
43
56
|
self._change = epics_signal_x(prefix + "FANOUT")
|
|
44
57
|
|
|
45
|
-
|
|
46
|
-
self.actual_transmission = epics_signal_r(float, prefix + "MATCH")
|
|
47
|
-
|
|
48
|
-
super().__init__(name)
|
|
58
|
+
super().__init__(prefix, name)
|
|
49
59
|
|
|
50
60
|
@AsyncStatus.wrap
|
|
51
61
|
async def set(self, value: float):
|
dodal/devices/dcm.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
-
from
|
|
3
|
-
from ophyd_async.core import StandardReadable, soft_signal_r_and_setter
|
|
2
|
+
from ophyd_async.core import Array1D, StandardReadable, soft_signal_r_and_setter
|
|
4
3
|
from ophyd_async.epics.core import epics_signal_r
|
|
5
4
|
from ophyd_async.epics.motor import Motor
|
|
6
5
|
|
|
@@ -56,8 +55,10 @@ class DCM(StandardReadable):
|
|
|
56
55
|
)
|
|
57
56
|
reflection_array = np.array(cm.reflection)
|
|
58
57
|
self.crystal_metadata_reflection, _ = soft_signal_r_and_setter(
|
|
59
|
-
|
|
58
|
+
Array1D[np.uint64],
|
|
60
59
|
initial_value=reflection_array,
|
|
61
60
|
)
|
|
62
|
-
self.crystal_metadata_d_spacing = epics_signal_r(
|
|
61
|
+
self.crystal_metadata_d_spacing = epics_signal_r(
|
|
62
|
+
float, prefix + "DSPACING:RBV"
|
|
63
|
+
)
|
|
63
64
|
super().__init__(name)
|
dodal/devices/fast_grid_scan.py
CHANGED
|
@@ -31,12 +31,12 @@ from dodal.parameters.experiment_parameter_base import AbstractExperimentWithBea
|
|
|
31
31
|
@dataclass
|
|
32
32
|
class GridAxis:
|
|
33
33
|
start: float
|
|
34
|
-
|
|
34
|
+
step_size_mm: float
|
|
35
35
|
full_steps: int
|
|
36
36
|
|
|
37
37
|
def steps_to_motor_position(self, steps):
|
|
38
38
|
"""Gives the motor position based on steps, where steps are 0 indexed"""
|
|
39
|
-
return self.start + self.
|
|
39
|
+
return self.start + self.step_size_mm * steps
|
|
40
40
|
|
|
41
41
|
@property
|
|
42
42
|
def end(self):
|
|
@@ -62,44 +62,29 @@ class GridScanParamsCommon(AbstractExperimentWithBeamParams):
|
|
|
62
62
|
x_steps: int = 1
|
|
63
63
|
y_steps: int = 1
|
|
64
64
|
z_steps: int = 0
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
x_step_size_mm: float = 0.1
|
|
66
|
+
y_step_size_mm: float = 0.1
|
|
67
|
+
z_step_size_mm: float = 0.1
|
|
68
|
+
x_start_mm: float = 0.1
|
|
69
|
+
y1_start_mm: float = 0.1
|
|
70
|
+
y2_start_mm: float = 0.1
|
|
71
|
+
z1_start_mm: float = 0.1
|
|
72
|
+
z2_start_mm: float = 0.1
|
|
73
73
|
|
|
74
74
|
# Whether to set the stub offsets after centering
|
|
75
75
|
set_stub_offsets: bool = False
|
|
76
76
|
|
|
77
|
-
def get_param_positions(self) -> dict:
|
|
78
|
-
return {
|
|
79
|
-
"x_steps": self.x_steps,
|
|
80
|
-
"y_steps": self.y_steps,
|
|
81
|
-
"z_steps": self.z_steps,
|
|
82
|
-
"x_step_size": self.x_step_size,
|
|
83
|
-
"y_step_size": self.y_step_size,
|
|
84
|
-
"z_step_size": self.z_step_size,
|
|
85
|
-
"x_start": self.x_start,
|
|
86
|
-
"y1_start": self.y1_start,
|
|
87
|
-
"y2_start": self.y2_start,
|
|
88
|
-
"z1_start": self.z1_start,
|
|
89
|
-
"z2_start": self.z2_start,
|
|
90
|
-
}
|
|
91
|
-
|
|
92
77
|
@property
|
|
93
78
|
def x_axis(self) -> GridAxis:
|
|
94
|
-
return GridAxis(self.
|
|
79
|
+
return GridAxis(self.x_start_mm, self.x_step_size_mm, self.x_steps)
|
|
95
80
|
|
|
96
81
|
@property
|
|
97
82
|
def y_axis(self) -> GridAxis:
|
|
98
|
-
return GridAxis(self.
|
|
83
|
+
return GridAxis(self.y1_start_mm, self.y_step_size_mm, self.y_steps)
|
|
99
84
|
|
|
100
85
|
@property
|
|
101
86
|
def z_axis(self) -> GridAxis:
|
|
102
|
-
return GridAxis(self.
|
|
87
|
+
return GridAxis(self.z2_start_mm, self.z_step_size_mm, self.z_steps)
|
|
103
88
|
|
|
104
89
|
def get_num_images(self):
|
|
105
90
|
return self.x_steps * (self.y_steps + self.z_steps)
|
|
@@ -140,11 +125,6 @@ class ZebraGridScanParams(GridScanParamsCommon):
|
|
|
140
125
|
|
|
141
126
|
dwell_time_ms: float = 10
|
|
142
127
|
|
|
143
|
-
def get_param_positions(self):
|
|
144
|
-
param_positions = super().get_param_positions()
|
|
145
|
-
param_positions["dwell_time_ms"] = self.dwell_time_ms
|
|
146
|
-
return param_positions
|
|
147
|
-
|
|
148
128
|
@field_validator("dwell_time_ms")
|
|
149
129
|
@classmethod
|
|
150
130
|
def non_integer_dwell_time(cls, dwell_time_ms: float) -> float:
|
|
@@ -166,11 +146,6 @@ class PandAGridScanParams(GridScanParamsCommon):
|
|
|
166
146
|
|
|
167
147
|
run_up_distance_mm: float = 0.17
|
|
168
148
|
|
|
169
|
-
def get_param_positions(self):
|
|
170
|
-
param_positions = super().get_param_positions()
|
|
171
|
-
param_positions["run_up_distance_mm"] = self.run_up_distance_mm
|
|
172
|
-
return param_positions
|
|
173
|
-
|
|
174
149
|
|
|
175
150
|
class MotionProgram(Device):
|
|
176
151
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
@@ -241,14 +216,14 @@ class FastGridScanCommon(StandardReadable, Flyable, ABC, Generic[ParamType]):
|
|
|
241
216
|
"x_steps": self.x_steps,
|
|
242
217
|
"y_steps": self.y_steps,
|
|
243
218
|
"z_steps": self.z_steps,
|
|
244
|
-
"
|
|
245
|
-
"
|
|
246
|
-
"
|
|
247
|
-
"
|
|
248
|
-
"
|
|
249
|
-
"
|
|
250
|
-
"
|
|
251
|
-
"
|
|
219
|
+
"x_step_size_mm": self.x_step_size,
|
|
220
|
+
"y_step_size_mm": self.y_step_size,
|
|
221
|
+
"z_step_size_mm": self.z_step_size,
|
|
222
|
+
"x_start_mm": self.x_start,
|
|
223
|
+
"y1_start_mm": self.y1_start,
|
|
224
|
+
"y2_start_mm": self.y2_start,
|
|
225
|
+
"z1_start_mm": self.z1_start,
|
|
226
|
+
"z2_start_mm": self.z2_start,
|
|
252
227
|
}
|
|
253
228
|
super().__init__(name)
|
|
254
229
|
|
dodal/devices/focusing_mirror.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import TypedDict
|
|
2
|
+
|
|
1
3
|
from ophyd_async.core import (
|
|
2
4
|
AsyncStatus,
|
|
3
5
|
Device,
|
|
@@ -36,6 +38,12 @@ class MirrorStripe(StrictEnum):
|
|
|
36
38
|
PLATINUM = "Platinum"
|
|
37
39
|
|
|
38
40
|
|
|
41
|
+
class MirrorStripeConfiguration(TypedDict):
|
|
42
|
+
stripe: MirrorStripe
|
|
43
|
+
yaw_mrad: float
|
|
44
|
+
lat_mm: float
|
|
45
|
+
|
|
46
|
+
|
|
39
47
|
class MirrorVoltageDemand(StrictEnum):
|
|
40
48
|
N_A = "N/A"
|
|
41
49
|
OK = "OK"
|
|
@@ -130,7 +138,12 @@ class FocusingMirror(StandardReadable):
|
|
|
130
138
|
"""Focusing Mirror"""
|
|
131
139
|
|
|
132
140
|
def __init__(
|
|
133
|
-
self,
|
|
141
|
+
self,
|
|
142
|
+
prefix: str,
|
|
143
|
+
name: str = "",
|
|
144
|
+
bragg_to_lat_lut_path: str | None = None,
|
|
145
|
+
x_suffix: str = "X",
|
|
146
|
+
y_suffix: str = "Y",
|
|
134
147
|
):
|
|
135
148
|
self.bragg_to_lat_lookup_table_path = bragg_to_lat_lut_path
|
|
136
149
|
self.yaw_mrad = Motor(prefix + "YAW")
|
|
@@ -161,16 +174,17 @@ class FocusingMirrorWithStripes(FocusingMirror):
|
|
|
161
174
|
"""A focusing mirror where the stripe material can be changed. This is usually done
|
|
162
175
|
based on the energy of the beamline."""
|
|
163
176
|
|
|
164
|
-
def __init__(self,
|
|
177
|
+
def __init__(self, prefix: str, name: str = "", *args, **kwargs):
|
|
165
178
|
self.stripe = epics_signal_rw(MirrorStripe, prefix + "STRP:DVAL")
|
|
166
179
|
# apply the current set stripe setting
|
|
167
180
|
self.apply_stripe = epics_signal_x(prefix + "CHANGE.PROC")
|
|
168
181
|
|
|
169
|
-
super().__init__(
|
|
182
|
+
super().__init__(prefix, name, *args, **kwargs)
|
|
170
183
|
|
|
171
|
-
def energy_to_stripe(self, energy_kev) ->
|
|
184
|
+
def energy_to_stripe(self, energy_kev) -> MirrorStripeConfiguration:
|
|
185
|
+
"""Return the stripe, yaw angle and lateral position for the specified energy"""
|
|
172
186
|
# In future, this should be configurable per-mirror
|
|
173
187
|
if energy_kev < 7:
|
|
174
|
-
return MirrorStripe.BARE
|
|
188
|
+
return {"stripe": MirrorStripe.BARE, "yaw_mrad": 6.2, "lat_mm": 0.0}
|
|
175
189
|
else:
|
|
176
|
-
return MirrorStripe.RHODIUM
|
|
190
|
+
return {"stripe": MirrorStripe.RHODIUM, "yaw_mrad": 0.0, "lat_mm": 10.0}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""A small temporary device to get the beam center positions from \
|
|
2
|
+
eiger or pilatus detector on i24"""
|
|
3
|
+
|
|
4
|
+
from ophyd_async.core import StandardReadable
|
|
5
|
+
from ophyd_async.epics.core import epics_signal_rw
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DetectorBeamCenter(StandardReadable):
|
|
9
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
10
|
+
self.beam_x = epics_signal_rw(float, prefix + "BeamX")
|
|
11
|
+
self.beam_y = epics_signal_rw(float, prefix + "BeamY")
|
|
12
|
+
super().__init__(name)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from ophyd_async.core import StandardReadable, StrictEnum
|
|
2
|
+
from ophyd_async.epics.core import epics_signal_rw
|
|
3
|
+
|
|
4
|
+
from dodal.common.signal_utils import create_hardware_backed_soft_signal
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HFocusMode(StrictEnum):
|
|
8
|
+
focus10 = "HMFMfocus10"
|
|
9
|
+
focus20d = "HMFMfocus20d"
|
|
10
|
+
focus30d = "HMFMfocus30d"
|
|
11
|
+
focus50d = "HMFMfocus50d"
|
|
12
|
+
focus1050d = "HMFMfocus1030d"
|
|
13
|
+
focus3010d = "HMFMfocus3010d"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class VFocusMode(StrictEnum):
|
|
17
|
+
focus10 = "VMFMfocus10"
|
|
18
|
+
focus20d = "VMFMfocus20d"
|
|
19
|
+
focus30d = "VMFMfocus30d"
|
|
20
|
+
focus50d = "VMFMfocus50d"
|
|
21
|
+
focus1030d = "VMFMfocus1030d"
|
|
22
|
+
focus3010d = "VMFMfocus3010d"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
BEAM_SIZES = {
|
|
26
|
+
"focus10": [7, 7],
|
|
27
|
+
"focus20d": [20, 20],
|
|
28
|
+
"focus30d": [30, 30],
|
|
29
|
+
"focus50d": [50, 50],
|
|
30
|
+
"focus1030d": [10, 30],
|
|
31
|
+
"focus3010d": [30, 10],
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class FocusMirrorsMode(StandardReadable):
|
|
36
|
+
"""A small device to read the focus mode and work out the beam size."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
39
|
+
self.horizontal = epics_signal_rw(HFocusMode, prefix + "G1:TARGETAPPLY")
|
|
40
|
+
self.vertical = epics_signal_rw(VFocusMode, prefix + "G0:TARGETAPPLY")
|
|
41
|
+
|
|
42
|
+
with self.add_children_as_readables():
|
|
43
|
+
self.beam_size_x = create_hardware_backed_soft_signal(
|
|
44
|
+
int, self._get_beam_size_x, units="um"
|
|
45
|
+
)
|
|
46
|
+
self.beam_size_y = create_hardware_backed_soft_signal(
|
|
47
|
+
int, self._get_beam_size_y, units="um"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
super().__init__(name)
|
|
51
|
+
|
|
52
|
+
async def _get_beam_size_x(self) -> int:
|
|
53
|
+
h_mode = await self.horizontal.get_value()
|
|
54
|
+
beam_x = BEAM_SIZES[h_mode.removeprefix("HMFM")][0]
|
|
55
|
+
return beam_x
|
|
56
|
+
|
|
57
|
+
async def _get_beam_size_y(self) -> int:
|
|
58
|
+
v_mode = await self.vertical.get_value()
|
|
59
|
+
beam_y = BEAM_SIZES[v_mode.removeprefix("VMFM")][1]
|
|
60
|
+
return beam_y
|