dls-dodal 1.45.0__py3-none-any.whl → 1.47.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.45.0.dist-info → dls_dodal-1.47.0.dist-info}/METADATA +2 -2
- {dls_dodal-1.45.0.dist-info → dls_dodal-1.47.0.dist-info}/RECORD +76 -64
- {dls_dodal-1.45.0.dist-info → dls_dodal-1.47.0.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/__init__.py +0 -1
- dodal/beamlines/b07.py +2 -6
- dodal/beamlines/b07_1.py +1 -3
- dodal/beamlines/i03.py +16 -19
- dodal/beamlines/i04.py +49 -17
- dodal/beamlines/i09.py +1 -3
- dodal/beamlines/i09_1.py +1 -3
- dodal/beamlines/i18.py +7 -4
- dodal/beamlines/i22.py +3 -3
- dodal/beamlines/i23.py +75 -4
- dodal/beamlines/p38.py +4 -4
- dodal/beamlines/p60.py +2 -6
- dodal/beamlines/p99.py +48 -4
- dodal/common/beamlines/beamline_parameters.py +1 -2
- dodal/common/beamlines/beamline_utils.py +5 -0
- dodal/common/data_util.py +4 -0
- dodal/devices/aperturescatterguard.py +47 -47
- dodal/devices/common_dcm.py +77 -0
- dodal/devices/current_amplifiers/struck_scaler_counter.py +1 -1
- dodal/devices/diamond_filter.py +5 -17
- dodal/devices/eiger.py +1 -1
- dodal/devices/electron_analyser/__init__.py +8 -0
- dodal/devices/electron_analyser/abstract/__init__.py +28 -0
- dodal/devices/electron_analyser/abstract/base_detector.py +210 -0
- dodal/devices/electron_analyser/abstract/base_driver_io.py +121 -0
- dodal/devices/electron_analyser/{abstract_region.py → abstract/base_region.py} +2 -9
- dodal/devices/electron_analyser/specs/__init__.py +11 -0
- dodal/devices/electron_analyser/specs/detector.py +29 -0
- dodal/devices/electron_analyser/specs/driver_io.py +64 -0
- dodal/devices/electron_analyser/{specs_region.py → specs/region.py} +1 -1
- dodal/devices/electron_analyser/types.py +6 -0
- dodal/devices/electron_analyser/util.py +13 -0
- dodal/devices/electron_analyser/vgscienta/__init__.py +12 -0
- dodal/devices/electron_analyser/vgscienta/detector.py +36 -0
- dodal/devices/electron_analyser/vgscienta/driver_io.py +39 -0
- dodal/devices/electron_analyser/{vgscienta_region.py → vgscienta/region.py} +1 -1
- dodal/devices/fast_grid_scan.py +7 -9
- dodal/devices/i03/__init__.py +3 -0
- dodal/devices/{dcm.py → i03/dcm.py} +8 -12
- dodal/devices/{undulator_dcm.py → i03/undulator_dcm.py} +6 -4
- dodal/devices/i04/__init__.py +3 -0
- dodal/devices/i04/constants.py +9 -0
- dodal/devices/i04/murko_results.py +195 -0
- dodal/devices/i10/diagnostics.py +9 -61
- dodal/devices/i13_1/merlin.py +3 -4
- dodal/devices/i13_1/merlin_controller.py +1 -1
- dodal/devices/i22/dcm.py +10 -12
- dodal/devices/i24/dcm.py +8 -17
- dodal/devices/i24/focus_mirrors.py +9 -13
- dodal/devices/i24/pilatus_metadata.py +9 -9
- dodal/devices/i24/pmac.py +19 -14
- dodal/devices/{i03 → mx_phase1}/beamstop.py +6 -12
- dodal/devices/oav/oav_calculations.py +2 -2
- dodal/devices/oav/oav_detector.py +32 -22
- dodal/devices/oav/utils.py +2 -2
- dodal/devices/p99/andor2_point.py +41 -0
- dodal/devices/positioner.py +49 -0
- dodal/devices/tetramm.py +8 -6
- dodal/devices/turbo_slit.py +2 -2
- dodal/devices/util/adjuster_plans.py +1 -1
- dodal/devices/zebra/zebra.py +4 -0
- dodal/devices/zebra/zebra_constants_mapping.py +1 -1
- dodal/devices/zocalo/__init__.py +0 -3
- dodal/devices/zocalo/zocalo_results.py +6 -32
- dodal/log.py +14 -14
- dodal/plan_stubs/data_session.py +10 -1
- dodal/plan_stubs/electron_analyser/__init__.py +3 -0
- dodal/plan_stubs/electron_analyser/{configure_controller.py → configure_driver.py} +30 -18
- dodal/plans/verify_undulator_gap.py +2 -2
- dodal/common/signal_utils.py +0 -88
- dodal/devices/electron_analyser/abstract_analyser_io.py +0 -47
- dodal/devices/electron_analyser/specs_analyser_io.py +0 -19
- dodal/devices/electron_analyser/vgscienta_analyser_io.py +0 -26
- dodal/devices/logging_ophyd_device.py +0 -17
- {dls_dodal-1.45.0.dist-info → dls_dodal-1.47.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.45.0.dist-info → dls_dodal-1.47.0.dist-info}/licenses/LICENSE +0 -0
- {dls_dodal-1.45.0.dist-info → dls_dodal-1.47.0.dist-info}/top_level.txt +0 -0
dodal/beamlines/i09_1.py
CHANGED
|
@@ -2,9 +2,7 @@ from dodal.common.beamlines.beamline_utils import (
|
|
|
2
2
|
device_factory,
|
|
3
3
|
)
|
|
4
4
|
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
|
|
5
|
-
from dodal.devices.electron_analyser.
|
|
6
|
-
SpecsAnalyserDriverIO,
|
|
7
|
-
)
|
|
5
|
+
from dodal.devices.electron_analyser.specs import SpecsAnalyserDriverIO
|
|
8
6
|
from dodal.devices.synchrotron import Synchrotron
|
|
9
7
|
from dodal.log import set_beamline as set_log_beamline
|
|
10
8
|
from dodal.utils import BeamlinePrefix, get_beamline_name
|
dodal/beamlines/i18.py
CHANGED
|
@@ -12,7 +12,7 @@ from dodal.common.visit import (
|
|
|
12
12
|
LocalDirectoryServiceClient,
|
|
13
13
|
StaticVisitPathProvider,
|
|
14
14
|
)
|
|
15
|
-
from dodal.devices.
|
|
15
|
+
from dodal.devices.common_dcm import BaseDCM, PitchAndRollCrystal, RollCrystal
|
|
16
16
|
from dodal.devices.i18.diode import Diode
|
|
17
17
|
from dodal.devices.i18.KBMirror import KBMirror
|
|
18
18
|
from dodal.devices.i18.table import Table
|
|
@@ -54,12 +54,15 @@ def undulator() -> Undulator:
|
|
|
54
54
|
return Undulator(f"{PREFIX.insertion_prefix}-MO-SERVC-01:")
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
# See https://github.com/DiamondLightSource/dodal/issues/1180
|
|
58
|
+
@device_factory(skip=True)
|
|
59
|
+
def dcm() -> BaseDCM[RollCrystal, PitchAndRollCrystal]:
|
|
59
60
|
# once spacing is added Si111 d-spacing is 3.135 angsterm , and Si311 is 1.637
|
|
60
61
|
# calculations are in gda/config/lookupTables/Si111/eV_Deg_converter.xml
|
|
61
|
-
return
|
|
62
|
+
return BaseDCM(
|
|
62
63
|
prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:",
|
|
64
|
+
xtal_1=RollCrystal,
|
|
65
|
+
xtal_2=PitchAndRollCrystal,
|
|
63
66
|
)
|
|
64
67
|
|
|
65
68
|
|
dodal/beamlines/i22.py
CHANGED
|
@@ -18,7 +18,7 @@ from dodal.common.crystal_metadata import (
|
|
|
18
18
|
from dodal.common.visit import RemoteDirectoryServiceClient, StaticVisitPathProvider
|
|
19
19
|
from dodal.devices.bimorph_mirror import BimorphMirror
|
|
20
20
|
from dodal.devices.focusing_mirror import FocusingMirror
|
|
21
|
-
from dodal.devices.i22.dcm import
|
|
21
|
+
from dodal.devices.i22.dcm import DCM
|
|
22
22
|
from dodal.devices.i22.fswitch import FSwitch
|
|
23
23
|
from dodal.devices.i22.nxsas import NXSasMetadataHolder, NXSasOAV, NXSasPilatus
|
|
24
24
|
from dodal.devices.linkam3 import Linkam3
|
|
@@ -141,8 +141,8 @@ def bimorph_vfm() -> BimorphMirror:
|
|
|
141
141
|
|
|
142
142
|
|
|
143
143
|
@device_factory()
|
|
144
|
-
def dcm() ->
|
|
145
|
-
return
|
|
144
|
+
def dcm() -> DCM:
|
|
145
|
+
return DCM(
|
|
146
146
|
prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:",
|
|
147
147
|
temperature_prefix=f"{PREFIX.beamline_prefix}-DI-DCM-01:",
|
|
148
148
|
crystal_1_metadata=make_crystal_metadata_from_material(
|
dodal/beamlines/i23.py
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
|
-
from
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import StrictEnum
|
|
4
|
+
from ophyd_async.epics.adpilatus import PilatusDetector
|
|
5
|
+
|
|
6
|
+
from dodal.common.beamlines.beamline_utils import (
|
|
7
|
+
device_factory,
|
|
8
|
+
get_path_provider,
|
|
9
|
+
set_path_provider,
|
|
10
|
+
)
|
|
2
11
|
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
|
|
12
|
+
from dodal.common.beamlines.device_helpers import HDF5_SUFFIX
|
|
13
|
+
from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider
|
|
3
14
|
from dodal.devices.motors import SixAxisGonio
|
|
4
15
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
16
|
+
from dodal.devices.positioner import Positioner1D
|
|
17
|
+
from dodal.devices.zebra.zebra import Zebra
|
|
18
|
+
from dodal.devices.zebra.zebra_constants_mapping import (
|
|
19
|
+
ZebraMapping,
|
|
20
|
+
ZebraSources,
|
|
21
|
+
ZebraTTLOutputs,
|
|
22
|
+
)
|
|
23
|
+
from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
|
|
5
24
|
from dodal.log import set_beamline as set_log_beamline
|
|
6
25
|
from dodal.utils import BeamlinePrefix, get_beamline_name, get_hostname
|
|
7
26
|
|
|
@@ -9,8 +28,27 @@ BL = get_beamline_name("i23")
|
|
|
9
28
|
set_log_beamline(BL)
|
|
10
29
|
set_utils_beamline(BL)
|
|
11
30
|
|
|
31
|
+
set_path_provider(
|
|
32
|
+
StaticVisitPathProvider(
|
|
33
|
+
BL,
|
|
34
|
+
Path("/tmp"),
|
|
35
|
+
client=LocalDirectoryServiceClient(),
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
|
|
12
39
|
PREFIX = BeamlinePrefix(BL)
|
|
13
40
|
|
|
41
|
+
I23_ZEBRA_MAPPING = ZebraMapping(
|
|
42
|
+
outputs=ZebraTTLOutputs(TTL_DETECTOR=1, TTL_SHUTTER=4),
|
|
43
|
+
sources=ZebraSources(),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class I23DetectorPositions(StrictEnum):
|
|
48
|
+
IN = "In"
|
|
49
|
+
OUT = "Out"
|
|
50
|
+
SAMPLE_CHANGE = "sample change"
|
|
51
|
+
|
|
14
52
|
|
|
15
53
|
def _is_i23_machine():
|
|
16
54
|
"""
|
|
@@ -23,7 +61,7 @@ def _is_i23_machine():
|
|
|
23
61
|
|
|
24
62
|
@device_factory(skip=lambda: not _is_i23_machine())
|
|
25
63
|
def oav_pin_tip_detection() -> PinTipDetection:
|
|
26
|
-
"""Get the i23 OAV pin-tip detection device"""
|
|
64
|
+
"""Get the i23 OAV pin-tip detection device."""
|
|
27
65
|
|
|
28
66
|
return PinTipDetection(
|
|
29
67
|
f"{PREFIX.beamline_prefix}-DI-OAV-01:",
|
|
@@ -31,10 +69,43 @@ def oav_pin_tip_detection() -> PinTipDetection:
|
|
|
31
69
|
)
|
|
32
70
|
|
|
33
71
|
|
|
72
|
+
@device_factory()
|
|
73
|
+
def shutter() -> ZebraShutter:
|
|
74
|
+
"""Get the i23 zebra controlled shutter."""
|
|
75
|
+
return ZebraShutter(f"{PREFIX.beamline_prefix}-EA-SHTR-01:", "shutter")
|
|
76
|
+
|
|
77
|
+
|
|
34
78
|
@device_factory()
|
|
35
79
|
def gonio() -> SixAxisGonio:
|
|
36
80
|
"""Get the i23 goniometer"""
|
|
81
|
+
return SixAxisGonio(f"{PREFIX.beamline_prefix}-MO-GONIO-01:")
|
|
37
82
|
|
|
38
|
-
|
|
39
|
-
|
|
83
|
+
|
|
84
|
+
@device_factory()
|
|
85
|
+
def zebra() -> Zebra:
|
|
86
|
+
"""Get the i23 zebra"""
|
|
87
|
+
return Zebra(
|
|
88
|
+
name="zebra",
|
|
89
|
+
prefix=f"{PREFIX.beamline_prefix}-EA-ZEBRA-01:ZEBRA:",
|
|
90
|
+
mapping=I23_ZEBRA_MAPPING,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@device_factory()
|
|
95
|
+
def pilatus() -> PilatusDetector:
|
|
96
|
+
"""Get the i23 pilatus"""
|
|
97
|
+
return PilatusDetector(
|
|
98
|
+
prefix=f"{PREFIX.beamline_prefix}-EA-PILAT-01:",
|
|
99
|
+
path_provider=get_path_provider(),
|
|
100
|
+
drv_suffix="cam1:",
|
|
101
|
+
fileio_suffix=HDF5_SUFFIX,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@device_factory()
|
|
106
|
+
def detector_motion() -> Positioner1D[I23DetectorPositions]:
|
|
107
|
+
"""Get the i23 detector"""
|
|
108
|
+
return Positioner1D[I23DetectorPositions](
|
|
109
|
+
f"{PREFIX.beamline_prefix}-EA-DET-01:Z",
|
|
110
|
+
datatype=I23DetectorPositions,
|
|
40
111
|
)
|
dodal/beamlines/p38.py
CHANGED
|
@@ -16,7 +16,7 @@ from dodal.common.crystal_metadata import (
|
|
|
16
16
|
)
|
|
17
17
|
from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider
|
|
18
18
|
from dodal.devices.focusing_mirror import FocusingMirror
|
|
19
|
-
from dodal.devices.i22.dcm import
|
|
19
|
+
from dodal.devices.i22.dcm import DCM
|
|
20
20
|
from dodal.devices.i22.fswitch import FSwitch
|
|
21
21
|
from dodal.devices.linkam3 import Linkam3
|
|
22
22
|
from dodal.devices.pressure_jump_cell import PressureJumpCell
|
|
@@ -40,7 +40,7 @@ set_utils_beamline(BL)
|
|
|
40
40
|
set_path_provider(
|
|
41
41
|
StaticVisitPathProvider(
|
|
42
42
|
BL,
|
|
43
|
-
Path("/dls/p38/data/
|
|
43
|
+
Path("/dls/p38/data/2025/cm40650-2/bluesky"),
|
|
44
44
|
client=LocalDirectoryServiceClient(),
|
|
45
45
|
)
|
|
46
46
|
)
|
|
@@ -143,8 +143,8 @@ def hfm() -> FocusingMirror:
|
|
|
143
143
|
|
|
144
144
|
|
|
145
145
|
@device_factory(mock=True)
|
|
146
|
-
def dcm() ->
|
|
147
|
-
return
|
|
146
|
+
def dcm() -> DCM:
|
|
147
|
+
return DCM(
|
|
148
148
|
temperature_prefix=f"{PREFIX.beamline_prefix}-DI-DCM-01:",
|
|
149
149
|
crystal_1_metadata=make_crystal_metadata_from_material(
|
|
150
150
|
MaterialsEnum.Si, (1, 1, 1)
|
dodal/beamlines/p60.py
CHANGED
|
@@ -2,9 +2,7 @@ from dodal.common.beamlines.beamline_utils import (
|
|
|
2
2
|
device_factory,
|
|
3
3
|
)
|
|
4
4
|
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
|
|
5
|
-
from dodal.devices.electron_analyser.
|
|
6
|
-
VGScientaAnalyserDriverIO,
|
|
7
|
-
)
|
|
5
|
+
from dodal.devices.electron_analyser.vgscienta import VGScientaAnalyserDriverIO
|
|
8
6
|
from dodal.log import set_beamline as set_log_beamline
|
|
9
7
|
from dodal.utils import BeamlinePrefix, get_beamline_name
|
|
10
8
|
|
|
@@ -16,6 +14,4 @@ set_utils_beamline(BL)
|
|
|
16
14
|
|
|
17
15
|
@device_factory()
|
|
18
16
|
def analyser_driver() -> VGScientaAnalyserDriverIO:
|
|
19
|
-
return VGScientaAnalyserDriverIO(
|
|
20
|
-
name="analyser_driver", prefix=f"{PREFIX.beamline_prefix}-EA-DET-01:CAM:"
|
|
21
|
-
)
|
|
17
|
+
return VGScientaAnalyserDriverIO(prefix=f"{PREFIX.beamline_prefix}-EA-DET-01:CAM:")
|
dodal/beamlines/p99.py
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
from
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from ophyd_async.epics.adandor import Andor2Detector
|
|
4
|
+
|
|
5
|
+
from dodal.common.beamlines.beamline_utils import (
|
|
6
|
+
device_factory,
|
|
7
|
+
get_path_provider,
|
|
8
|
+
set_beamline,
|
|
9
|
+
set_path_provider,
|
|
10
|
+
)
|
|
11
|
+
from dodal.common.beamlines.device_helpers import CAM_SUFFIX, HDF5_SUFFIX
|
|
12
|
+
from dodal.common.visit import (
|
|
13
|
+
LocalDirectoryServiceClient,
|
|
14
|
+
StaticVisitPathProvider,
|
|
15
|
+
)
|
|
2
16
|
from dodal.devices.attenuator.filter import FilterMotor
|
|
3
17
|
from dodal.devices.attenuator.filter_selections import P99FilterSelections
|
|
4
18
|
from dodal.devices.motors import XYZPositioner
|
|
19
|
+
from dodal.devices.p99.andor2_point import Andor2Point
|
|
5
20
|
from dodal.devices.p99.sample_stage import SampleAngleStage
|
|
6
21
|
from dodal.log import set_beamline as set_log_beamline
|
|
7
22
|
from dodal.utils import BeamlinePrefix, get_beamline_name
|
|
@@ -19,9 +34,7 @@ def angle_stage() -> SampleAngleStage:
|
|
|
19
34
|
|
|
20
35
|
@device_factory()
|
|
21
36
|
def filter() -> FilterMotor:
|
|
22
|
-
return FilterMotor(
|
|
23
|
-
f"{PREFIX.beamline_prefix}-MO-STAGE-02:MP:SELECT", P99FilterSelections
|
|
24
|
-
)
|
|
37
|
+
return FilterMotor(f"{PREFIX.beamline_prefix}-MO-STAGE-02:MP:", P99FilterSelections)
|
|
25
38
|
|
|
26
39
|
|
|
27
40
|
@device_factory()
|
|
@@ -32,3 +45,34 @@ def sample_stage() -> XYZPositioner:
|
|
|
32
45
|
@device_factory()
|
|
33
46
|
def lab_stage() -> XYZPositioner:
|
|
34
47
|
return XYZPositioner(f"{PREFIX.beamline_prefix}-MO-STAGE-02:LAB:")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
set_path_provider(
|
|
51
|
+
StaticVisitPathProvider(
|
|
52
|
+
BL,
|
|
53
|
+
Path("/dls/p99/data/2024/cm37284-2/processing/writenData"),
|
|
54
|
+
client=LocalDirectoryServiceClient(), # RemoteDirectoryServiceClient("http://p99-control:8088/api"),
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@device_factory()
|
|
60
|
+
def andor2_det() -> Andor2Detector:
|
|
61
|
+
"""Andor model:DU897_BV."""
|
|
62
|
+
return Andor2Detector(
|
|
63
|
+
prefix=f"{PREFIX.beamline_prefix}-EA-DET-03:",
|
|
64
|
+
path_provider=get_path_provider(),
|
|
65
|
+
drv_suffix=CAM_SUFFIX,
|
|
66
|
+
fileio_suffix=HDF5_SUFFIX,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@device_factory()
|
|
71
|
+
def andor2_point() -> Andor2Point:
|
|
72
|
+
"""Using the andor2 as if it is a massive point detector, read the meanValue and total after
|
|
73
|
+
a picture is taken."""
|
|
74
|
+
return Andor2Point(
|
|
75
|
+
prefix=f"{PREFIX.beamline_prefix}-EA-DET-03:",
|
|
76
|
+
drv_suffix=CAM_SUFFIX,
|
|
77
|
+
read_uncached={"mean": "STAT:MeanValue_RBV", "total": "STAT:Total_RBV"},
|
|
78
|
+
)
|
|
@@ -8,7 +8,6 @@ BEAMLINE_PARAMETER_KEYWORDS = ["FB", "FULL", "deadtime"]
|
|
|
8
8
|
BEAMLINE_PARAMETER_PATHS = {
|
|
9
9
|
"i03": "/dls_sw/i03/software/daq_configuration/domain/beamlineParameters",
|
|
10
10
|
"i04": "/dls_sw/i04/software/gda_versions/gda_9_34/workspace_git/gda-mx.git/configurations/i04-config/scripts/beamlineParameters",
|
|
11
|
-
"s03": "tests/test_data/test_beamline_parameters.txt",
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
|
|
@@ -92,7 +91,7 @@ def get_beamline_parameters(beamline_param_path: str | None = None):
|
|
|
92
91
|
"""Loads the beamline parameters from the specified path, or according to the
|
|
93
92
|
environment variable if none is given"""
|
|
94
93
|
if not beamline_param_path:
|
|
95
|
-
beamline_name = get_beamline_name("
|
|
94
|
+
beamline_name = get_beamline_name("i03")
|
|
96
95
|
beamline_param_path = BEAMLINE_PARAMETER_PATHS.get(beamline_name)
|
|
97
96
|
if beamline_param_path is None:
|
|
98
97
|
raise KeyError(
|
dodal/common/data_util.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from typing import TypeVar
|
|
2
3
|
|
|
3
4
|
from pydantic import BaseModel
|
|
@@ -9,6 +10,9 @@ def load_json_file_to_class(
|
|
|
9
10
|
t: type[TBaseModel],
|
|
10
11
|
file: str,
|
|
11
12
|
) -> TBaseModel:
|
|
13
|
+
if not os.path.isfile(file):
|
|
14
|
+
raise FileNotFoundError(f"Cannot find file {file}")
|
|
15
|
+
|
|
12
16
|
with open(file) as f:
|
|
13
17
|
json_obj = f.read()
|
|
14
18
|
cls = t.model_validate_json(json_obj)
|
|
@@ -2,17 +2,18 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
|
|
5
|
-
from bluesky.protocols import
|
|
5
|
+
from bluesky.protocols import Preparable
|
|
6
6
|
from ophyd_async.core import (
|
|
7
7
|
AsyncStatus,
|
|
8
8
|
StandardReadable,
|
|
9
9
|
StandardReadableFormat,
|
|
10
10
|
StrictEnum,
|
|
11
|
+
derived_signal_r,
|
|
12
|
+
derived_signal_rw,
|
|
11
13
|
)
|
|
12
14
|
from pydantic import BaseModel, Field
|
|
13
15
|
|
|
14
16
|
from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters
|
|
15
|
-
from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
|
|
16
17
|
from dodal.devices.aperture import Aperture
|
|
17
18
|
from dodal.devices.scatterguard import Scatterguard
|
|
18
19
|
|
|
@@ -123,21 +124,21 @@ def load_positions_from_beamline_parameters(
|
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
|
|
126
|
-
class ApertureScatterguard(StandardReadable,
|
|
127
|
+
class ApertureScatterguard(StandardReadable, Preparable):
|
|
127
128
|
"""Move the aperture and scatterguard assembly in a safe way. There are two ways to
|
|
128
129
|
interact with the device depending on if you want simplicity or move flexibility.
|
|
129
130
|
|
|
130
131
|
Examples:
|
|
131
132
|
The simple interface is using::
|
|
132
133
|
|
|
133
|
-
await aperture_scatterguard.set(ApertureValue.LARGE)
|
|
134
|
+
await aperture_scatterguard.selected_aperture.set(ApertureValue.LARGE)
|
|
134
135
|
|
|
135
136
|
This will move the assembly so that the large aperture is in the beam, regardless
|
|
136
137
|
of where the assembly currently is.
|
|
137
138
|
|
|
138
139
|
We may also want to move the assembly out of the beam with::
|
|
139
140
|
|
|
140
|
-
await aperture_scatterguard.set(ApertureValue.OUT_OF_BEAM)
|
|
141
|
+
await aperture_scatterguard.selected_aperture.set(ApertureValue.OUT_OF_BEAM)
|
|
141
142
|
|
|
142
143
|
Note, to make sure we do this as quickly as possible, the scatterguard will stay
|
|
143
144
|
in the same position relative to the aperture.
|
|
@@ -149,7 +150,7 @@ class ApertureScatterguard(StandardReadable, Movable[ApertureValue], Preparable)
|
|
|
149
150
|
|
|
150
151
|
Then, at a later time, move back into the beam::
|
|
151
152
|
|
|
152
|
-
await
|
|
153
|
+
await aperture_scatterguard.selected_aperture.set(ApertureValue.LARGE)
|
|
153
154
|
|
|
154
155
|
Given the prepare has been done this move will now be faster as only the y is
|
|
155
156
|
left to move.
|
|
@@ -164,11 +165,24 @@ class ApertureScatterguard(StandardReadable, Movable[ApertureValue], Preparable)
|
|
|
164
165
|
) -> None:
|
|
165
166
|
self.aperture = Aperture(prefix + "-MO-MAPT-01:")
|
|
166
167
|
self.scatterguard = Scatterguard(prefix + "-MO-SCAT-01:")
|
|
167
|
-
self.radius = create_r_hardware_backed_soft_signal(
|
|
168
|
-
float, self._get_current_radius, units="µm"
|
|
169
|
-
)
|
|
170
168
|
self._loaded_positions = loaded_positions
|
|
171
169
|
self._tolerances = tolerances
|
|
170
|
+
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
171
|
+
self.selected_aperture = derived_signal_rw(
|
|
172
|
+
self._get_current_aperture_position,
|
|
173
|
+
self._set_current_aperture_position,
|
|
174
|
+
large=self.aperture.large,
|
|
175
|
+
medium=self.aperture.medium,
|
|
176
|
+
small=self.aperture.small,
|
|
177
|
+
current_ap_y=self.aperture.y.user_readback,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
self.radius = derived_signal_r(
|
|
181
|
+
self._get_current_radius,
|
|
182
|
+
current_aperture=self.selected_aperture,
|
|
183
|
+
derived_units="µm",
|
|
184
|
+
)
|
|
185
|
+
|
|
172
186
|
self.add_readables(
|
|
173
187
|
[
|
|
174
188
|
self.aperture.x.user_readback,
|
|
@@ -180,17 +194,9 @@ class ApertureScatterguard(StandardReadable, Movable[ApertureValue], Preparable)
|
|
|
180
194
|
],
|
|
181
195
|
)
|
|
182
196
|
|
|
183
|
-
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
184
|
-
self.selected_aperture = create_r_hardware_backed_soft_signal(
|
|
185
|
-
ApertureValue, self._get_current_aperture_position
|
|
186
|
-
)
|
|
187
|
-
|
|
188
197
|
super().__init__(name)
|
|
189
198
|
|
|
190
|
-
|
|
191
|
-
async def set(self, value: ApertureValue):
|
|
192
|
-
"""This set will move the aperture into the beam or move the whole assembly out"""
|
|
193
|
-
|
|
199
|
+
async def _set_current_aperture_position(self, value: ApertureValue) -> None:
|
|
194
200
|
position = self._loaded_positions[value]
|
|
195
201
|
await self._check_safe_to_move(position.aperture_z)
|
|
196
202
|
|
|
@@ -231,6 +237,27 @@ class ApertureScatterguard(StandardReadable, Movable[ApertureValue], Preparable)
|
|
|
231
237
|
"triggering another move."
|
|
232
238
|
)
|
|
233
239
|
|
|
240
|
+
def _get_current_radius(self, current_aperture: ApertureValue) -> float:
|
|
241
|
+
return self._loaded_positions[current_aperture].radius
|
|
242
|
+
|
|
243
|
+
def _is_out_of_beam(self, current_ap_y: float) -> bool:
|
|
244
|
+
out_ap_y = self._loaded_positions[ApertureValue.OUT_OF_BEAM].aperture_y
|
|
245
|
+
return current_ap_y <= out_ap_y + self._tolerances.aperture_y
|
|
246
|
+
|
|
247
|
+
def _get_current_aperture_position(
|
|
248
|
+
self, large: float, medium: float, small: float, current_ap_y: float
|
|
249
|
+
) -> ApertureValue:
|
|
250
|
+
if large == 1:
|
|
251
|
+
return ApertureValue.LARGE
|
|
252
|
+
elif medium == 1:
|
|
253
|
+
return ApertureValue.MEDIUM
|
|
254
|
+
elif small == 1:
|
|
255
|
+
return ApertureValue.SMALL
|
|
256
|
+
elif self._is_out_of_beam(current_ap_y):
|
|
257
|
+
return ApertureValue.OUT_OF_BEAM
|
|
258
|
+
|
|
259
|
+
raise InvalidApertureMove("Current aperture/scatterguard state unrecognised")
|
|
260
|
+
|
|
234
261
|
async def _safe_move_whilst_in_beam(self, position: AperturePosition):
|
|
235
262
|
"""
|
|
236
263
|
Move the aperture and scatterguard combo safely to a new position.
|
|
@@ -282,33 +309,6 @@ class ApertureScatterguard(StandardReadable, Movable[ApertureValue], Preparable)
|
|
|
282
309
|
self.scatterguard.y.set(scatterguard_y),
|
|
283
310
|
)
|
|
284
311
|
|
|
285
|
-
async def _is_out_of_beam(self) -> bool:
|
|
286
|
-
current_ap_y = await self.aperture.y.user_readback.get_value()
|
|
287
|
-
out_ap_y = self._loaded_positions[ApertureValue.OUT_OF_BEAM].aperture_y
|
|
288
|
-
return current_ap_y <= out_ap_y + self._tolerances.aperture_y
|
|
289
|
-
|
|
290
|
-
async def _get_current_aperture_position(self) -> ApertureValue:
|
|
291
|
-
"""
|
|
292
|
-
Returns the current aperture position using readback values
|
|
293
|
-
for SMALL, MEDIUM, LARGE. ROBOT_LOAD position defined when
|
|
294
|
-
mini aperture y <= ROBOT_LOAD.location.aperture_y + tolerance.
|
|
295
|
-
If no position is found then raises InvalidApertureMove.
|
|
296
|
-
"""
|
|
297
|
-
if await self.aperture.large.get_value(cached=False) == 1:
|
|
298
|
-
return ApertureValue.LARGE
|
|
299
|
-
elif await self.aperture.medium.get_value(cached=False) == 1:
|
|
300
|
-
return ApertureValue.MEDIUM
|
|
301
|
-
elif await self.aperture.small.get_value(cached=False) == 1:
|
|
302
|
-
return ApertureValue.SMALL
|
|
303
|
-
elif await self._is_out_of_beam():
|
|
304
|
-
return ApertureValue.OUT_OF_BEAM
|
|
305
|
-
|
|
306
|
-
raise InvalidApertureMove("Current aperture/scatterguard state unrecognised")
|
|
307
|
-
|
|
308
|
-
async def _get_current_radius(self) -> float:
|
|
309
|
-
current_value = await self._get_current_aperture_position()
|
|
310
|
-
return self._loaded_positions[current_value].radius
|
|
311
|
-
|
|
312
312
|
@AsyncStatus.wrap
|
|
313
313
|
async def prepare(self, value: ApertureValue):
|
|
314
314
|
"""Moves the assembly to the position for the specified aperture, whilst keeping
|
|
@@ -317,7 +317,7 @@ class ApertureScatterguard(StandardReadable, Movable[ApertureValue], Preparable)
|
|
|
317
317
|
Moving the assembly whilst out of the beam has no collision risk so we can just
|
|
318
318
|
move all the motors together.
|
|
319
319
|
"""
|
|
320
|
-
if await self.
|
|
320
|
+
if self._is_out_of_beam(await self.aperture.y.user_readback.get_value()):
|
|
321
321
|
aperture_x, _, aperture_z, scatterguard_x, scatterguard_y = (
|
|
322
322
|
self._loaded_positions[value].values
|
|
323
323
|
)
|
|
@@ -329,4 +329,4 @@ class ApertureScatterguard(StandardReadable, Movable[ApertureValue], Preparable)
|
|
|
329
329
|
self.scatterguard.y.set(scatterguard_y),
|
|
330
330
|
)
|
|
331
331
|
else:
|
|
332
|
-
await self.set(value)
|
|
332
|
+
await self.selected_aperture.set(value)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from typing import Generic, TypeVar
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import (
|
|
4
|
+
StandardReadable,
|
|
5
|
+
)
|
|
6
|
+
from ophyd_async.epics.core import epics_signal_r
|
|
7
|
+
from ophyd_async.epics.motor import Motor
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StationaryCrystal(StandardReadable):
|
|
11
|
+
def __init__(self, prefix):
|
|
12
|
+
super().__init__(prefix)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RollCrystal(StationaryCrystal):
|
|
16
|
+
def __init__(self, prefix):
|
|
17
|
+
with self.add_children_as_readables():
|
|
18
|
+
self.roll_in_mrad = Motor(prefix + "ROLL")
|
|
19
|
+
super().__init__(prefix)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class PitchAndRollCrystal(StationaryCrystal):
|
|
23
|
+
def __init__(self, prefix):
|
|
24
|
+
with self.add_children_as_readables():
|
|
25
|
+
self.pitch_in_mrad = Motor(prefix + "PITCH")
|
|
26
|
+
self.roll_in_mrad = Motor(prefix + "ROLL")
|
|
27
|
+
super().__init__(prefix)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
Xtal_1 = TypeVar("Xtal_1", bound=StationaryCrystal)
|
|
31
|
+
Xtal_2 = TypeVar("Xtal_2", bound=StationaryCrystal)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class BaseDCM(StandardReadable, Generic[Xtal_1, Xtal_2]):
|
|
35
|
+
"""
|
|
36
|
+
Common device for the double crystal monochromator (DCM), used to select the energy of the beam.
|
|
37
|
+
|
|
38
|
+
Features common across all DCM's should include virtual motors to set energy/wavelength and contain two crystals,
|
|
39
|
+
each of which can be movable. Some DCM's contain crystals with roll motors, and some contain crystals with roll and pitch motors.
|
|
40
|
+
This base device accounts for all combinations of this.
|
|
41
|
+
|
|
42
|
+
This device should act as a parent for beamline-specific DCM's, in which any other missing signals can be added.
|
|
43
|
+
|
|
44
|
+
Bluesky plans using DCM's should be typed to specify which types of crystals are required. For example, a plan
|
|
45
|
+
which only requires one crystal which can roll should be typed 'def my_plan(dcm: BaseDCM[RollCrystal, StationaryCrystal])`
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self, prefix: str, xtal_1: type[Xtal_1], xtal_2: type[Xtal_2], name: str = ""
|
|
50
|
+
) -> None:
|
|
51
|
+
with self.add_children_as_readables():
|
|
52
|
+
# Virtual motor PV's which set the physical motors so that the DCM produces requested
|
|
53
|
+
# wavelength/energy
|
|
54
|
+
self.energy_in_kev = Motor(prefix + "ENERGY")
|
|
55
|
+
self.wavelength_in_a = Motor(prefix + "WAVELENGTH")
|
|
56
|
+
|
|
57
|
+
# Real motors
|
|
58
|
+
self.bragg_in_degrees = Motor(prefix + "BRAGG")
|
|
59
|
+
# Offset ensures that the beam exits the DCM at the same point, regardless of energy.
|
|
60
|
+
self.offset_in_mm = Motor(prefix + "OFFSET")
|
|
61
|
+
|
|
62
|
+
self.crystal_metadata_d_spacing_a = epics_signal_r(
|
|
63
|
+
float, prefix + "DSPACING:RBV"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
self._make_crystals(prefix, xtal_1, xtal_2)
|
|
67
|
+
|
|
68
|
+
super().__init__(name)
|
|
69
|
+
|
|
70
|
+
# Prefix convention is different depending on whether there are one or two controllable crystals
|
|
71
|
+
def _make_crystals(self, prefix: str, xtal_1: type[Xtal_1], xtal_2: type[Xtal_2]):
|
|
72
|
+
if StationaryCrystal not in [xtal_1, xtal_2]:
|
|
73
|
+
self.xtal_1 = xtal_1(f"{prefix}XTAL1:")
|
|
74
|
+
self.xtal_2 = xtal_2(f"{prefix}XTAL2:")
|
|
75
|
+
else:
|
|
76
|
+
self.xtal_1 = xtal_1(prefix)
|
|
77
|
+
self.xtal_2 = xtal_2(prefix)
|
dodal/devices/diamond_filter.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import TypeVar
|
|
2
2
|
|
|
3
|
-
from ophyd_async.core import
|
|
4
|
-
|
|
5
|
-
from
|
|
3
|
+
from ophyd_async.core import StrictEnum
|
|
4
|
+
|
|
5
|
+
from dodal.devices.positioner import Positioner1D
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class _Filters(StrictEnum):
|
|
@@ -25,22 +25,10 @@ class I04Filters(_Filters):
|
|
|
25
25
|
T = TypeVar("T", bound=_Filters)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
class DiamondFilter(
|
|
28
|
+
class DiamondFilter(Positioner1D[T]):
|
|
29
29
|
"""
|
|
30
30
|
A filter set that is used to reduce the heat load on the monochromator.
|
|
31
31
|
|
|
32
32
|
It has 4 slots that can contain filters of different thickness. Changing the thickness
|
|
33
33
|
signal will move the filter set to select this filter.
|
|
34
34
|
"""
|
|
35
|
-
|
|
36
|
-
def __init__(
|
|
37
|
-
self,
|
|
38
|
-
prefix: str,
|
|
39
|
-
data_type: type[T],
|
|
40
|
-
name: str = "",
|
|
41
|
-
) -> None:
|
|
42
|
-
with self.add_children_as_readables():
|
|
43
|
-
self.y_motor = Motor(prefix + "Y")
|
|
44
|
-
self.thickness = epics_signal_rw(data_type, f"{prefix}Y:MP:SELECT")
|
|
45
|
-
|
|
46
|
-
super().__init__(name)
|
dodal/devices/eiger.py
CHANGED
|
@@ -35,7 +35,7 @@ class InternalEigerTriggerMode(Enum):
|
|
|
35
35
|
AVAILABLE_TIMEOUTS = {
|
|
36
36
|
"i03": EigerTimeouts(
|
|
37
37
|
stale_params_timeout=60,
|
|
38
|
-
general_status_timeout=
|
|
38
|
+
general_status_timeout=20,
|
|
39
39
|
meta_file_ready_timeout=30,
|
|
40
40
|
all_frames_timeout=120, # Long timeout for meta file to compensate for filesystem issues
|
|
41
41
|
arming_timeout=60,
|