dls-dodal 1.55.1__py3-none-any.whl → 1.57.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.55.1.dist-info → dls_dodal-1.57.0.dist-info}/METADATA +3 -3
- {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/RECORD +101 -87
- dodal/_version.py +16 -3
- dodal/beamlines/b01_1.py +6 -1
- dodal/beamlines/b07.py +2 -1
- dodal/beamlines/b07_1.py +2 -1
- dodal/beamlines/b21.py +4 -24
- dodal/beamlines/i03.py +53 -53
- dodal/beamlines/i04.py +16 -38
- dodal/beamlines/i09.py +3 -2
- dodal/beamlines/i09_1.py +2 -1
- dodal/beamlines/i11.py +143 -0
- dodal/beamlines/i17.py +37 -0
- dodal/beamlines/i19_1.py +1 -0
- dodal/beamlines/i19_2.py +7 -0
- dodal/beamlines/i22.py +5 -5
- dodal/beamlines/i23.py +3 -3
- dodal/beamlines/i24.py +6 -33
- dodal/beamlines/p38.py +1 -0
- dodal/beamlines/p60.py +3 -2
- dodal/cli.py +11 -1
- dodal/common/__init__.py +4 -0
- dodal/common/beamlines/beamline_parameters.py +1 -1
- dodal/common/beamlines/beamline_utils.py +5 -1
- dodal/common/enums.py +19 -0
- dodal/common/watcher_utils.py +83 -0
- dodal/devices/aithre_lasershaping/laser_robot.py +4 -9
- dodal/devices/aperturescatterguard.py +52 -12
- dodal/devices/apple2_undulator.py +0 -1
- dodal/devices/b16/detector.py +1 -10
- dodal/devices/backlight.py +8 -20
- dodal/devices/bimorph_mirror.py +4 -7
- dodal/devices/collimation_table.py +36 -0
- dodal/devices/controllers.py +21 -0
- dodal/devices/cryostream.py +97 -7
- dodal/devices/current_amplifiers/femto.py +1 -1
- dodal/devices/detector/detector_motion.py +1 -7
- dodal/devices/eiger.py +22 -8
- dodal/devices/eiger_odin.py +2 -0
- dodal/devices/electron_analyser/__init__.py +2 -1
- dodal/devices/electron_analyser/abstract/__init__.py +0 -1
- dodal/devices/electron_analyser/abstract/base_detector.py +3 -25
- dodal/devices/electron_analyser/abstract/base_driver_io.py +18 -9
- dodal/devices/electron_analyser/abstract/base_region.py +34 -3
- dodal/devices/electron_analyser/detector.py +24 -0
- dodal/devices/electron_analyser/enums.py +5 -0
- dodal/devices/electron_analyser/specs/detector.py +2 -1
- dodal/devices/electron_analyser/specs/driver_io.py +21 -26
- dodal/devices/electron_analyser/specs/region.py +1 -1
- dodal/devices/electron_analyser/util.py +20 -0
- dodal/devices/electron_analyser/vgscienta/__init__.py +3 -3
- dodal/devices/electron_analyser/vgscienta/detector.py +2 -1
- dodal/devices/electron_analyser/vgscienta/driver_io.py +24 -32
- dodal/devices/electron_analyser/vgscienta/enums.py +0 -8
- dodal/devices/electron_analyser/vgscienta/region.py +2 -31
- dodal/devices/eurotherm.py +126 -0
- dodal/devices/fluorescence_detector_motion.py +3 -10
- dodal/devices/focusing_mirror.py +1 -1
- dodal/devices/i03/undulator_dcm.py +0 -1
- dodal/devices/i09/enums.py +8 -8
- dodal/devices/i10/diagnostics.py +4 -4
- dodal/devices/i10/i10_apple2.py +3 -6
- dodal/devices/i11/cyberstar_blower.py +34 -0
- dodal/devices/i11/diff_stages.py +55 -0
- dodal/devices/i11/mythen.py +165 -0
- dodal/devices/i11/nx100robot.py +153 -0
- dodal/devices/i11/spinner.py +30 -0
- dodal/devices/i13_1/merlin_controller.py +4 -4
- dodal/devices/i19/diffractometer.py +34 -0
- dodal/devices/i19/shutter.py +11 -1
- dodal/devices/i22/dcm.py +1 -1
- dodal/devices/i22/fswitch.py +3 -12
- dodal/devices/i24/aperture.py +3 -3
- dodal/devices/i24/beam_center.py +1 -2
- dodal/devices/i24/dcm.py +11 -15
- dodal/devices/i24/dual_backlight.py +11 -12
- dodal/devices/i24/pmac.py +8 -7
- dodal/devices/mx_phase1/beamstop.py +10 -11
- dodal/devices/oav/pin_image_recognition/__init__.py +0 -3
- dodal/devices/p60/enums.py +8 -8
- dodal/devices/p60/lab_xray_source.py +3 -2
- dodal/devices/pressure_jump_cell.py +77 -123
- dodal/devices/scintillator.py +76 -4
- dodal/devices/smargon.py +35 -18
- dodal/devices/synchrotron.py +1 -2
- dodal/devices/thawer.py +22 -15
- dodal/devices/undulator.py +3 -8
- dodal/devices/util/epics_util.py +1 -1
- dodal/devices/watsonmarlow323_pump.py +7 -7
- dodal/devices/webcam.py +1 -0
- dodal/devices/xbpm_feedback.py +4 -6
- dodal/devices/xspress3/xspress3.py +0 -5
- dodal/devices/zocalo/zocalo_results.py +1 -3
- dodal/testing/__init__.py +3 -0
- dodal/testing/electron_analyser/__init__.py +6 -0
- dodal/testing/electron_analyser/device_factory.py +59 -0
- dodal/testing/setup.py +67 -0
- dodal/devices/CTAB.py +0 -41
- dodal/devices/i24/pilatus_metadata.py +0 -44
- dodal/devices/util/test_utils.py +0 -37
- {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/WHEEL +0 -0
- {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/licenses/LICENSE +0 -0
- {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/top_level.txt +0 -0
dodal/beamlines/i22.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
3
|
from ophyd_async.epics.adaravis import AravisDetector
|
|
4
|
-
from ophyd_async.epics.adcore import
|
|
4
|
+
from ophyd_async.epics.adcore import NDPluginBaseIO
|
|
5
5
|
from ophyd_async.epics.adpilatus import PilatusDetector
|
|
6
6
|
from ophyd_async.fastcs.panda import HDFPanda
|
|
7
7
|
|
|
@@ -45,7 +45,7 @@ set_utils_beamline(BL)
|
|
|
45
45
|
set_path_provider(
|
|
46
46
|
StaticVisitPathProvider(
|
|
47
47
|
BL,
|
|
48
|
-
Path("/dls/i22/data/
|
|
48
|
+
Path("/dls/i22/data/2025/cm40643-4/"),
|
|
49
49
|
client=RemoteDirectoryServiceClient("http://i22-control:8088/api"),
|
|
50
50
|
)
|
|
51
51
|
)
|
|
@@ -103,7 +103,7 @@ def i0() -> TetrammDetector:
|
|
|
103
103
|
path_provider=get_path_provider(),
|
|
104
104
|
type="Cividec Diamond XBPM",
|
|
105
105
|
plugins={
|
|
106
|
-
"stats":
|
|
106
|
+
"stats": NDPluginBaseIO(
|
|
107
107
|
prefix=f"{PREFIX.beamline_prefix}-EA-XBPM-02:SumAll:"
|
|
108
108
|
)
|
|
109
109
|
},
|
|
@@ -117,7 +117,7 @@ def it() -> TetrammDetector:
|
|
|
117
117
|
path_provider=get_path_provider(),
|
|
118
118
|
type="PIN Diode",
|
|
119
119
|
plugins={
|
|
120
|
-
"stats":
|
|
120
|
+
"stats": NDPluginBaseIO(
|
|
121
121
|
prefix=f"{PREFIX.beamline_prefix}-EA-TTRM-02:SumAll:"
|
|
122
122
|
)
|
|
123
123
|
},
|
|
@@ -279,5 +279,5 @@ def ppump() -> WatsonMarlow323Pump:
|
|
|
279
279
|
|
|
280
280
|
|
|
281
281
|
@device_factory()
|
|
282
|
-
def
|
|
282
|
+
def base() -> XYPitchStage:
|
|
283
283
|
return XYPitchStage(f"{PREFIX.beamline_prefix}-MO-STABL-01:")
|
dodal/beamlines/i23.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
|
-
from ophyd_async.core import StrictEnum
|
|
3
|
+
from ophyd_async.core import InOut, StrictEnum
|
|
4
4
|
from ophyd_async.epics.adpilatus import PilatusDetector
|
|
5
5
|
|
|
6
6
|
from dodal.common.beamlines.beamline_utils import (
|
|
@@ -45,8 +45,8 @@ I23_ZEBRA_MAPPING = ZebraMapping(
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
class I23DetectorPositions(StrictEnum):
|
|
48
|
-
IN =
|
|
49
|
-
OUT =
|
|
48
|
+
IN = InOut.IN.value
|
|
49
|
+
OUT = InOut.OUT.value
|
|
50
50
|
SAMPLE_CHANGE = "sample change"
|
|
51
51
|
|
|
52
52
|
|
dodal/beamlines/i24.py
CHANGED
|
@@ -11,7 +11,6 @@ from dodal.devices.i24.beamstop import Beamstop
|
|
|
11
11
|
from dodal.devices.i24.dcm import DCM
|
|
12
12
|
from dodal.devices.i24.dual_backlight import DualBacklight
|
|
13
13
|
from dodal.devices.i24.focus_mirrors import FocusMirrorsMode
|
|
14
|
-
from dodal.devices.i24.pilatus_metadata import PilatusMetadata
|
|
15
14
|
from dodal.devices.i24.pmac import PMAC
|
|
16
15
|
from dodal.devices.i24.vgonio import VerticalGoniometer
|
|
17
16
|
from dodal.devices.motors import YZStage
|
|
@@ -100,7 +99,8 @@ def dcm() -> DCM:
|
|
|
100
99
|
If this is called when already instantiated in i24, it will return the existing object.
|
|
101
100
|
"""
|
|
102
101
|
return DCM(
|
|
103
|
-
prefix=PREFIX.beamline_prefix,
|
|
102
|
+
prefix=f"{PREFIX.beamline_prefix}-DI-DCM-01:",
|
|
103
|
+
motion_prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:",
|
|
104
104
|
)
|
|
105
105
|
|
|
106
106
|
|
|
@@ -136,10 +136,7 @@ def pmac() -> PMAC:
|
|
|
136
136
|
"""Get the i24 PMAC device, instantiate it if it hasn't already been.
|
|
137
137
|
If this is called when already instantiated in i24, it will return the existing object.
|
|
138
138
|
"""
|
|
139
|
-
|
|
140
|
-
return PMAC(
|
|
141
|
-
"ME14E-MO-CHIP-01:",
|
|
142
|
-
)
|
|
139
|
+
return PMAC(PREFIX.beamline_prefix)
|
|
143
140
|
|
|
144
141
|
|
|
145
142
|
@device_factory()
|
|
@@ -155,9 +152,7 @@ def vgonio() -> VerticalGoniometer:
|
|
|
155
152
|
"""Get the i24 vertical goniometer device, instantiate it if it hasn't already been.
|
|
156
153
|
If this is called when already instantiated, it will return the existing object.
|
|
157
154
|
"""
|
|
158
|
-
return VerticalGoniometer(
|
|
159
|
-
f"{PREFIX.beamline_prefix}-MO-VGON-01:",
|
|
160
|
-
)
|
|
155
|
+
return VerticalGoniometer(f"{PREFIX.beamline_prefix}-MO-VGON-01:")
|
|
161
156
|
|
|
162
157
|
|
|
163
158
|
@device_factory()
|
|
@@ -176,17 +171,13 @@ def shutter() -> HutchShutter:
|
|
|
176
171
|
"""Get the i24 hutch shutter device, instantiate it if it hasn't already been.
|
|
177
172
|
If this is called when already instantiated, it will return the existing object.
|
|
178
173
|
"""
|
|
179
|
-
return HutchShutter(
|
|
180
|
-
f"{PREFIX.beamline_prefix}-PS-SHTR-01:",
|
|
181
|
-
)
|
|
174
|
+
return HutchShutter(f"{PREFIX.beamline_prefix}-PS-SHTR-01:")
|
|
182
175
|
|
|
183
176
|
|
|
184
177
|
@device_factory()
|
|
185
178
|
def focus_mirrors() -> FocusMirrorsMode:
|
|
186
179
|
"""Get the i24 focus mirror devise to find the beam size."""
|
|
187
|
-
return FocusMirrorsMode(
|
|
188
|
-
f"{PREFIX.beamline_prefix}-OP-MFM-01:",
|
|
189
|
-
)
|
|
180
|
+
return FocusMirrorsMode(f"{PREFIX.beamline_prefix}-OP-MFM-01:")
|
|
190
181
|
|
|
191
182
|
|
|
192
183
|
@device_factory()
|
|
@@ -196,21 +187,3 @@ def eiger_beam_center() -> DetectorBeamCenter:
|
|
|
196
187
|
f"{PREFIX.beamline_prefix}-EA-EIGER-01:CAM:",
|
|
197
188
|
"eiger_bc",
|
|
198
189
|
)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
@device_factory()
|
|
202
|
-
def pilatus_beam_center() -> DetectorBeamCenter:
|
|
203
|
-
"""A device for setting/reading the beamcenter from the pilatus on i24."""
|
|
204
|
-
return DetectorBeamCenter(
|
|
205
|
-
f"{PREFIX.beamline_prefix}-EA-PILAT-01:cam1:",
|
|
206
|
-
"pilatus_bc",
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
@device_factory()
|
|
211
|
-
def pilatus_metadata() -> PilatusMetadata:
|
|
212
|
-
"""A small pilatus driver device for figuring out the filename template."""
|
|
213
|
-
return PilatusMetadata(
|
|
214
|
-
f"{PREFIX.beamline_prefix}-EA-PILAT-01:",
|
|
215
|
-
"pilatus_meta",
|
|
216
|
-
)
|
dodal/beamlines/p38.py
CHANGED
|
@@ -145,6 +145,7 @@ def hfm() -> FocusingMirror:
|
|
|
145
145
|
@device_factory(mock=True)
|
|
146
146
|
def dcm() -> DCM:
|
|
147
147
|
return DCM(
|
|
148
|
+
prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:",
|
|
148
149
|
temperature_prefix=f"{PREFIX.beamline_prefix}-DI-DCM-01:",
|
|
149
150
|
crystal_1_metadata=make_crystal_metadata_from_material(
|
|
150
151
|
MaterialsEnum.Si, (1, 1, 1)
|
dodal/beamlines/p60.py
CHANGED
|
@@ -2,6 +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 import SelectedSource
|
|
5
6
|
from dodal.devices.electron_analyser.vgscienta import VGScientaAnalyserDriverIO
|
|
6
7
|
from dodal.devices.p60 import (
|
|
7
8
|
LabXraySource,
|
|
@@ -34,8 +35,8 @@ def mg_kalpha_source() -> LabXraySourceReadable:
|
|
|
34
35
|
@device_factory()
|
|
35
36
|
def analyser_driver() -> VGScientaAnalyserDriverIO[LensMode, PsuMode, PassEnergy]:
|
|
36
37
|
energy_sources = {
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
SelectedSource.SOURCE1: al_kalpha_source().energy_ev,
|
|
39
|
+
SelectedSource.SOURCE2: mg_kalpha_source().energy_ev,
|
|
39
40
|
}
|
|
40
41
|
return VGScientaAnalyserDriverIO[LensMode, PsuMode, PassEnergy](
|
|
41
42
|
prefix=f"{PREFIX.beamline_prefix}-EA-DET-01:CAM:",
|
dodal/cli.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from collections.abc import Mapping
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
import click
|
|
5
6
|
from bluesky.run_engine import RunEngine
|
|
6
|
-
from ophyd_async.core import NotConnected
|
|
7
|
+
from ophyd_async.core import NotConnected, StaticPathProvider, UUIDFilenameProvider
|
|
7
8
|
from ophyd_async.plan_stubs import ensure_connected
|
|
8
9
|
|
|
9
10
|
from dodal.beamlines import all_beamline_names, module_name_for_beamline
|
|
11
|
+
from dodal.common.beamlines.beamline_utils import set_path_provider
|
|
10
12
|
from dodal.utils import AnyDevice, filter_ophyd_devices, make_all_devices
|
|
11
13
|
|
|
12
14
|
from . import __version__
|
|
@@ -47,6 +49,10 @@ def connect(beamline: str, all: bool, sim_backend: bool) -> None:
|
|
|
47
49
|
|
|
48
50
|
os.environ["BEAMLINE"] = beamline
|
|
49
51
|
|
|
52
|
+
# We need to make a fake path provider for any detectors that need one,
|
|
53
|
+
# it is not used in dodal connect
|
|
54
|
+
_spoof_path_provider()
|
|
55
|
+
|
|
50
56
|
module_name = module_name_for_beamline(beamline)
|
|
51
57
|
full_module_path = f"dodal.beamlines.{module_name}"
|
|
52
58
|
|
|
@@ -115,3 +121,7 @@ def _connect_devices(
|
|
|
115
121
|
name: device for name, device in devices.items() if name not in exceptions
|
|
116
122
|
}
|
|
117
123
|
return successful_devices, exceptions
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _spoof_path_provider() -> None:
|
|
127
|
+
set_path_provider(StaticPathProvider(UUIDFilenameProvider(), Path("/tmp")))
|
dodal/common/__init__.py
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
from .coordination import group_uuid, inject
|
|
2
|
+
from .enums import EnabledDisabledUpper, InOutUpper, OnOffUpper
|
|
2
3
|
from .maths import in_micros, step_to_num
|
|
3
4
|
from .types import MsgGenerator, PlanGenerator
|
|
4
5
|
|
|
5
6
|
__all__ = [
|
|
6
7
|
"group_uuid",
|
|
7
8
|
"inject",
|
|
9
|
+
"EnabledDisabledUpper",
|
|
10
|
+
"InOutUpper",
|
|
11
|
+
"OnOffUpper",
|
|
8
12
|
"in_micros",
|
|
9
13
|
"MsgGenerator",
|
|
10
14
|
"PlanGenerator",
|
|
@@ -8,7 +8,7 @@ BEAMLINE_PARAMETER_KEYWORDS = ["FB", "FULL", "deadtime"]
|
|
|
8
8
|
|
|
9
9
|
BEAMLINE_PARAMETER_PATHS = {
|
|
10
10
|
"i03": "/dls_sw/i03/software/daq_configuration/domain/beamlineParameters",
|
|
11
|
-
"i04": "/dls_sw/i04/software/
|
|
11
|
+
"i04": "/dls_sw/i04/software/daq_configuration/domain/beamlineParameters",
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
|
|
@@ -169,4 +169,8 @@ def get_path_provider() -> PathProvider:
|
|
|
169
169
|
|
|
170
170
|
def clear_path_provider() -> None:
|
|
171
171
|
global PATH_PROVIDER
|
|
172
|
-
|
|
172
|
+
try:
|
|
173
|
+
del PATH_PROVIDER
|
|
174
|
+
except NameError:
|
|
175
|
+
# In this case the path provider was never set so we can do nothing
|
|
176
|
+
pass
|
dodal/common/enums.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from ophyd_async.core import StrictEnum
|
|
2
|
+
|
|
3
|
+
# Any capitalised enums needs to be removed and replaced with ones from ophyd-async.core
|
|
4
|
+
# https://github.com/DiamondLightSource/dodal/issues/1416
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class OnOffUpper(StrictEnum):
|
|
8
|
+
ON = "ON"
|
|
9
|
+
OFF = "OFF"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EnabledDisabledUpper(StrictEnum):
|
|
13
|
+
ENABLED = "ENABLED"
|
|
14
|
+
DISABLED = "DISABLED"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class InOutUpper(StrictEnum):
|
|
18
|
+
IN = "IN"
|
|
19
|
+
OUT = "OUT"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from ophyd_async.core import WatchableAsyncStatus, Watcher
|
|
2
|
+
|
|
3
|
+
from dodal.log import LOGGER
|
|
4
|
+
|
|
5
|
+
Number = int | float
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class _LogOnPercentageProgressWatcher(Watcher[Number]):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
status: WatchableAsyncStatus[Number],
|
|
12
|
+
message_prefix: str,
|
|
13
|
+
percent_interval: Number = 25,
|
|
14
|
+
):
|
|
15
|
+
status.watch(self)
|
|
16
|
+
self.percent_interval = percent_interval
|
|
17
|
+
self._current_percent_interval = 0
|
|
18
|
+
self.message_prefix = message_prefix
|
|
19
|
+
if self.percent_interval <= 0:
|
|
20
|
+
raise ValueError(
|
|
21
|
+
f"Percent interval on class _LogOnPercentageProgressWatcher must be a positive number, but received {self.percent_interval}"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def __call__(
|
|
25
|
+
self,
|
|
26
|
+
current: Number | None = None,
|
|
27
|
+
initial: Number | None = None,
|
|
28
|
+
target: Number | None = None,
|
|
29
|
+
name: str | None = None,
|
|
30
|
+
unit: str | None = None,
|
|
31
|
+
precision: int | None = None,
|
|
32
|
+
fraction: float | None = None,
|
|
33
|
+
time_elapsed: float | None = None,
|
|
34
|
+
time_remaining: float | None = None,
|
|
35
|
+
):
|
|
36
|
+
if (
|
|
37
|
+
isinstance(current, Number)
|
|
38
|
+
and isinstance(target, Number)
|
|
39
|
+
and isinstance(initial, Number)
|
|
40
|
+
and target != initial
|
|
41
|
+
):
|
|
42
|
+
current_percent = int(((current - initial) / (target - initial)) * 100)
|
|
43
|
+
if (
|
|
44
|
+
current_percent
|
|
45
|
+
>= (self._current_percent_interval + 1) * self.percent_interval
|
|
46
|
+
):
|
|
47
|
+
LOGGER.info(f"{self.message_prefix}: {current_percent}%")
|
|
48
|
+
self._current_percent_interval = (
|
|
49
|
+
current_percent // self.percent_interval
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def log_on_percentage_complete(
|
|
54
|
+
status: WatchableAsyncStatus[int | float],
|
|
55
|
+
message_prefix: str,
|
|
56
|
+
percent_interval: int = 25,
|
|
57
|
+
):
|
|
58
|
+
"""
|
|
59
|
+
Add watcher to a WatchableAsyncStatus status which will periodically log a message based on percentage completion
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
status: A WatchableAsyncStatus. For example, Ophyd-async produces this status from a Motor.set method
|
|
63
|
+
|
|
64
|
+
message_prefix: The string at the start of each of the produced logging messages
|
|
65
|
+
|
|
66
|
+
percent_interval: How often to produce logging message, in terms of percentage completion
|
|
67
|
+
of the status.
|
|
68
|
+
|
|
69
|
+
Note that when using with Bluesky plan stubs you will need to cast the status (as of
|
|
70
|
+
Bluesky v1.14.2), since a Bluesky status doesn't use generics - see https://github.com/bluesky/bluesky/issues/1948.
|
|
71
|
+
|
|
72
|
+
When running Bluesky plans using an interactive terminal, it is better to use the standard bluesky progress
|
|
73
|
+
bar instead of this function. See https://blueskyproject.io/bluesky/main/progress-bar.html#progress-bar
|
|
74
|
+
|
|
75
|
+
Example usage within a Bluesky plan:
|
|
76
|
+
yield from bps.kickoff(my_detector)
|
|
77
|
+
status = yield from bps.complete(my_detector, group="collection complete")
|
|
78
|
+
status = cast(WatchableAsyncStatus, status)
|
|
79
|
+
log_on_percentage_complete(status, "Data collection triggers received", 10)
|
|
80
|
+
yield from bps.wait("collection complete")
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
_LogOnPercentageProgressWatcher(status, message_prefix, percent_interval)
|
|
@@ -1,24 +1,19 @@
|
|
|
1
|
-
from ophyd_async.core import StrictEnum
|
|
1
|
+
from ophyd_async.core import EnabledDisabled, OnOff, StrictEnum
|
|
2
2
|
from ophyd_async.epics.core import epics_signal_rw
|
|
3
3
|
|
|
4
4
|
from dodal.devices.robot import BartRobot
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class ForceBit(StrictEnum):
|
|
8
|
-
ON =
|
|
8
|
+
ON = OnOff.ON.value
|
|
9
9
|
NO = "No"
|
|
10
|
-
OFF =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class LidHeatEnable(StrictEnum):
|
|
14
|
-
ENABLED = "Enabled"
|
|
15
|
-
DISABLED = "Disabled"
|
|
10
|
+
OFF = OnOff.OFF.value
|
|
16
11
|
|
|
17
12
|
|
|
18
13
|
class LaserRobot(BartRobot):
|
|
19
14
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
20
15
|
self.dewar_lid_heater = epics_signal_rw(
|
|
21
|
-
|
|
16
|
+
EnabledDisabled, prefix + "DW_1_ENABLED", prefix + "DW_1_CTRL"
|
|
22
17
|
)
|
|
23
18
|
self.cryojet_retract = epics_signal_rw(ForceBit, prefix + "OP_24_FORCE_OPTION")
|
|
24
19
|
self.set_beamline_safe = epics_signal_rw(
|
|
@@ -29,6 +29,7 @@ class _GDAParamApertureValue(StrictEnum):
|
|
|
29
29
|
SMALL = "SMALL_APERTURE"
|
|
30
30
|
MEDIUM = "MEDIUM_APERTURE"
|
|
31
31
|
LARGE = "LARGE_APERTURE"
|
|
32
|
+
MANUAL_LOAD = "MANUAL_LOAD"
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
class AperturePosition(BaseModel):
|
|
@@ -92,7 +93,7 @@ class AperturePosition(BaseModel):
|
|
|
92
93
|
class ApertureValue(StrictEnum):
|
|
93
94
|
"""The possible apertures that can be selected.
|
|
94
95
|
|
|
95
|
-
Changing these means changing the external
|
|
96
|
+
Changing these means changing the external parameter model of Hyperion.
|
|
96
97
|
See https://github.com/DiamondLightSource/mx-bluesky/issues/760
|
|
97
98
|
"""
|
|
98
99
|
|
|
@@ -100,6 +101,7 @@ class ApertureValue(StrictEnum):
|
|
|
100
101
|
MEDIUM = "MEDIUM_APERTURE"
|
|
101
102
|
LARGE = "LARGE_APERTURE"
|
|
102
103
|
OUT_OF_BEAM = "Out of beam"
|
|
104
|
+
PARKED = "Parked" # Parked under the collimation table for manual load
|
|
103
105
|
|
|
104
106
|
def __str__(self):
|
|
105
107
|
return self.name.capitalize()
|
|
@@ -121,6 +123,9 @@ def load_positions_from_beamline_parameters(
|
|
|
121
123
|
ApertureValue.LARGE: AperturePosition.from_gda_params(
|
|
122
124
|
_GDAParamApertureValue.LARGE, 100, params
|
|
123
125
|
),
|
|
126
|
+
ApertureValue.PARKED: AperturePosition.from_gda_params(
|
|
127
|
+
_GDAParamApertureValue.MANUAL_LOAD, 0, params
|
|
128
|
+
),
|
|
124
129
|
}
|
|
125
130
|
|
|
126
131
|
|
|
@@ -158,13 +163,14 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
158
163
|
|
|
159
164
|
def __init__(
|
|
160
165
|
self,
|
|
166
|
+
aperture_prefix: str,
|
|
167
|
+
scatterguard_prefix: str,
|
|
161
168
|
loaded_positions: dict[ApertureValue, AperturePosition],
|
|
162
169
|
tolerances: AperturePosition,
|
|
163
|
-
prefix: str = "",
|
|
164
170
|
name: str = "",
|
|
165
171
|
) -> None:
|
|
166
|
-
self.aperture = Aperture(
|
|
167
|
-
self.scatterguard = XYStage(
|
|
172
|
+
self.aperture = Aperture(aperture_prefix)
|
|
173
|
+
self.scatterguard = XYStage(scatterguard_prefix)
|
|
168
174
|
self._loaded_positions = loaded_positions
|
|
169
175
|
self._tolerances = tolerances
|
|
170
176
|
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
@@ -175,6 +181,7 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
175
181
|
medium=self.aperture.medium,
|
|
176
182
|
small=self.aperture.small,
|
|
177
183
|
current_ap_y=self.aperture.y.user_readback,
|
|
184
|
+
current_ap_z=self.aperture.z.user_readback,
|
|
178
185
|
)
|
|
179
186
|
|
|
180
187
|
self.radius = derived_signal_r(
|
|
@@ -196,13 +203,30 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
196
203
|
|
|
197
204
|
super().__init__(name)
|
|
198
205
|
|
|
206
|
+
async def _unpark(self, position_to_move_to: ApertureValue):
|
|
207
|
+
"""When the aperture is parked it is under the collimation table. It needs to be
|
|
208
|
+
moved out from under the table before it is moved up to beam height.
|
|
209
|
+
"""
|
|
210
|
+
position = self._loaded_positions[position_to_move_to]
|
|
211
|
+
await self.aperture.z.set(position.aperture_z)
|
|
212
|
+
|
|
199
213
|
async def _set_current_aperture_position(self, value: ApertureValue) -> None:
|
|
214
|
+
if value == ApertureValue.PARKED:
|
|
215
|
+
raise NotImplementedError(
|
|
216
|
+
"Currently not able to park aperture/scatterguard, see https://github.com/DiamondLightSource/mx-bluesky/issues/1197"
|
|
217
|
+
)
|
|
218
|
+
|
|
200
219
|
position = self._loaded_positions[value]
|
|
220
|
+
|
|
221
|
+
current_ap_y = await self.aperture.y.user_readback.get_value()
|
|
222
|
+
current_ap_z = await self.aperture.z.user_readback.get_value()
|
|
223
|
+
if self._is_in_position(ApertureValue.PARKED, current_ap_y, current_ap_z):
|
|
224
|
+
await self._unpark(value)
|
|
225
|
+
|
|
201
226
|
await self._check_safe_to_move(position.aperture_z)
|
|
202
227
|
|
|
203
228
|
if value == ApertureValue.OUT_OF_BEAM:
|
|
204
|
-
|
|
205
|
-
await self.aperture.y.set(out_y)
|
|
229
|
+
await self.aperture.y.set(position.aperture_y)
|
|
206
230
|
else:
|
|
207
231
|
await self._safe_move_whilst_in_beam(position)
|
|
208
232
|
|
|
@@ -240,12 +264,22 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
240
264
|
def _get_current_radius(self, current_aperture: ApertureValue) -> float:
|
|
241
265
|
return self._loaded_positions[current_aperture].radius
|
|
242
266
|
|
|
243
|
-
def
|
|
244
|
-
|
|
245
|
-
|
|
267
|
+
def _is_in_position(
|
|
268
|
+
self, position: ApertureValue, current_ap_y: float, current_ap_z: float
|
|
269
|
+
) -> bool:
|
|
270
|
+
position_y = self._loaded_positions[position].aperture_y
|
|
271
|
+
position_z = self._loaded_positions[position].aperture_z
|
|
272
|
+
y_matches = abs(current_ap_y - position_y) <= self._tolerances.aperture_y
|
|
273
|
+
z_matches = abs(current_ap_z - position_z) <= self._tolerances.aperture_z
|
|
274
|
+
return y_matches and z_matches
|
|
246
275
|
|
|
247
276
|
def _get_current_aperture_position(
|
|
248
|
-
self,
|
|
277
|
+
self,
|
|
278
|
+
large: float,
|
|
279
|
+
medium: float,
|
|
280
|
+
small: float,
|
|
281
|
+
current_ap_y: float,
|
|
282
|
+
current_ap_z: float,
|
|
249
283
|
) -> ApertureValue:
|
|
250
284
|
if large == 1:
|
|
251
285
|
return ApertureValue.LARGE
|
|
@@ -253,7 +287,11 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
253
287
|
return ApertureValue.MEDIUM
|
|
254
288
|
elif small == 1:
|
|
255
289
|
return ApertureValue.SMALL
|
|
256
|
-
elif self.
|
|
290
|
+
elif self._is_in_position(ApertureValue.PARKED, current_ap_y, current_ap_z):
|
|
291
|
+
return ApertureValue.PARKED
|
|
292
|
+
elif self._is_in_position(
|
|
293
|
+
ApertureValue.OUT_OF_BEAM, current_ap_y, current_ap_z
|
|
294
|
+
):
|
|
257
295
|
return ApertureValue.OUT_OF_BEAM
|
|
258
296
|
|
|
259
297
|
raise InvalidApertureMove("Current aperture/scatterguard state unrecognised")
|
|
@@ -317,7 +355,9 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
317
355
|
Moving the assembly whilst out of the beam has no collision risk so we can just
|
|
318
356
|
move all the motors together.
|
|
319
357
|
"""
|
|
320
|
-
|
|
358
|
+
current_y = await self.aperture.y.user_readback.get_value()
|
|
359
|
+
current_z = await self.aperture.z.user_readback.get_value()
|
|
360
|
+
if self._is_in_position(ApertureValue.OUT_OF_BEAM, current_y, current_z):
|
|
321
361
|
aperture_x, _, aperture_z, scatterguard_x, scatterguard_y = (
|
|
322
362
|
self._loaded_positions[value].values
|
|
323
363
|
)
|
dodal/devices/b16/detector.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from ophyd_async.epics.adcore import (
|
|
2
|
-
ADBaseController,
|
|
3
2
|
ADBaseIO,
|
|
4
3
|
ADTIFFWriter,
|
|
5
4
|
AreaDetector,
|
|
@@ -7,15 +6,7 @@ from ophyd_async.epics.adcore import (
|
|
|
7
6
|
|
|
8
7
|
from dodal.common.beamlines.beamline_utils import get_path_provider
|
|
9
8
|
from dodal.common.beamlines.device_helpers import CAM_SUFFIX, TIFF_SUFFIX
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ConstantDeadTimeController(ADBaseController):
|
|
13
|
-
def __init__(self, driver: ADBaseIO, deadtime: float):
|
|
14
|
-
super().__init__(driver)
|
|
15
|
-
self.deadtime = deadtime
|
|
16
|
-
|
|
17
|
-
def get_deadtime(self, exposure: float | None) -> float:
|
|
18
|
-
return self.deadtime
|
|
9
|
+
from dodal.devices.controllers import ConstantDeadTimeController
|
|
19
10
|
|
|
20
11
|
|
|
21
12
|
def software_triggered_tiff_area_detector(prefix: str, deadtime: float = 0.0):
|
dodal/devices/backlight.py
CHANGED
|
@@ -1,35 +1,23 @@
|
|
|
1
1
|
from asyncio import sleep
|
|
2
2
|
|
|
3
3
|
from bluesky.protocols import Movable
|
|
4
|
-
from ophyd_async.core import AsyncStatus,
|
|
4
|
+
from ophyd_async.core import AsyncStatus, InOut, OnOff, StandardReadable
|
|
5
5
|
from ophyd_async.epics.core import epics_signal_rw
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class
|
|
9
|
-
ON = "On"
|
|
10
|
-
OFF = "Off"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class BacklightPosition(StrictEnum):
|
|
14
|
-
IN = "In"
|
|
15
|
-
OUT = "Out"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class Backlight(StandardReadable, Movable[BacklightPosition]):
|
|
8
|
+
class Backlight(StandardReadable, Movable[InOut]):
|
|
19
9
|
"""Simple device to trigger the pneumatic in/out."""
|
|
20
10
|
|
|
21
11
|
TIME_TO_MOVE_S = 1.0 # Tested using a stopwatch on the beamline 09/2024
|
|
22
12
|
|
|
23
13
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
24
14
|
with self.add_children_as_readables():
|
|
25
|
-
self.power = epics_signal_rw(
|
|
26
|
-
self.position = epics_signal_rw(
|
|
27
|
-
BacklightPosition, prefix + "-EA-BL-01:CTRL"
|
|
28
|
-
)
|
|
15
|
+
self.power = epics_signal_rw(OnOff, prefix + "-EA-BLIT-01:TOGGLE")
|
|
16
|
+
self.position = epics_signal_rw(InOut, prefix + "-EA-BL-01:CTRL")
|
|
29
17
|
super().__init__(name)
|
|
30
18
|
|
|
31
19
|
@AsyncStatus.wrap
|
|
32
|
-
async def set(self, value:
|
|
20
|
+
async def set(self, value: InOut):
|
|
33
21
|
"""This setter will turn the backlight on when we move it in to the beam and off
|
|
34
22
|
when we move it out.
|
|
35
23
|
|
|
@@ -39,9 +27,9 @@ class Backlight(StandardReadable, Movable[BacklightPosition]):
|
|
|
39
27
|
"""
|
|
40
28
|
old_position = await self.position.get_value()
|
|
41
29
|
await self.position.set(value)
|
|
42
|
-
if value ==
|
|
43
|
-
await self.power.set(
|
|
30
|
+
if value == InOut.OUT:
|
|
31
|
+
await self.power.set(OnOff.OFF)
|
|
44
32
|
else:
|
|
45
|
-
await self.power.set(
|
|
33
|
+
await self.power.set(OnOff.ON)
|
|
46
34
|
if old_position != value:
|
|
47
35
|
await sleep(self.TIME_TO_MOVE_S)
|
dodal/devices/bimorph_mirror.py
CHANGED
|
@@ -22,12 +22,9 @@ from ophyd_async.epics.core import (
|
|
|
22
22
|
epics_signal_x,
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
from dodal.common.enums import OnOffUpper
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
ON = "ON"
|
|
30
|
-
OFF = "OFF"
|
|
27
|
+
DEFAULT_TIMEOUT = 60
|
|
31
28
|
|
|
32
29
|
|
|
33
30
|
class BimorphMirrorMode(StrictEnum):
|
|
@@ -54,7 +51,7 @@ class BimorphMirrorChannel(StandardReadable, EpicsDevice):
|
|
|
54
51
|
|
|
55
52
|
target_voltage: A[SignalRW[float], PvSuffix.rbv("VTRGT"), Format.CONFIG_SIGNAL]
|
|
56
53
|
output_voltage: A[SignalRW[float], PvSuffix.rbv("VOUT"), Format.HINTED_SIGNAL]
|
|
57
|
-
status: A[SignalR[
|
|
54
|
+
status: A[SignalR[OnOffUpper], PvSuffix("STATUS"), Format.CONFIG_SIGNAL]
|
|
58
55
|
shift: A[SignalW[float], PvSuffix("SHIFT")]
|
|
59
56
|
|
|
60
57
|
|
|
@@ -87,7 +84,7 @@ class BimorphMirror(StandardReadable, Movable[list[float]]):
|
|
|
87
84
|
for i in range(1, number_of_channels + 1)
|
|
88
85
|
}
|
|
89
86
|
)
|
|
90
|
-
self.enabled = epics_signal_w(
|
|
87
|
+
self.enabled = epics_signal_w(OnOffUpper, f"{prefix}ONOFF")
|
|
91
88
|
self.commit_target_voltages = epics_signal_x(f"{prefix}ALLTRGT.PROC")
|
|
92
89
|
self.status = epics_signal_r(BimorphMirrorStatus, f"{prefix}STATUS")
|
|
93
90
|
self.err = epics_signal_r(str, f"{prefix}ERR")
|