dls-dodal 1.66.0__py3-none-any.whl → 1.68.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.66.0.dist-info → dls_dodal-1.68.0.dist-info}/METADATA +2 -2
- {dls_dodal-1.66.0.dist-info → dls_dodal-1.68.0.dist-info}/RECORD +75 -65
- dodal/_version.py +2 -2
- dodal/beamlines/b07.py +1 -1
- dodal/beamlines/b07_1.py +1 -1
- dodal/beamlines/i03.py +92 -208
- dodal/beamlines/i04.py +22 -1
- dodal/beamlines/i05.py +1 -1
- dodal/beamlines/i06.py +1 -1
- dodal/beamlines/i09.py +1 -1
- dodal/beamlines/i09_1.py +27 -3
- dodal/beamlines/i09_2.py +58 -2
- dodal/beamlines/i10_optics.py +44 -25
- dodal/beamlines/i16.py +23 -0
- dodal/beamlines/i17.py +7 -3
- dodal/beamlines/i19_1.py +26 -14
- dodal/beamlines/i19_2.py +49 -38
- dodal/beamlines/i21.py +61 -2
- dodal/beamlines/i22.py +16 -1
- dodal/beamlines/p60.py +1 -1
- dodal/beamlines/training_rig.py +0 -16
- dodal/cli.py +26 -12
- dodal/common/coordination.py +3 -2
- dodal/device_manager.py +604 -0
- dodal/devices/cryostream.py +28 -57
- dodal/devices/eiger.py +41 -27
- dodal/devices/electron_analyser/__init__.py +0 -33
- dodal/devices/electron_analyser/base/__init__.py +58 -0
- dodal/devices/electron_analyser/base/base_controller.py +73 -0
- dodal/devices/electron_analyser/base/base_detector.py +214 -0
- dodal/devices/electron_analyser/{abstract → base}/base_driver_io.py +23 -42
- dodal/devices/electron_analyser/{abstract → base}/base_region.py +47 -11
- dodal/devices/electron_analyser/{util.py → base/base_util.py} +1 -1
- dodal/devices/electron_analyser/{energy_sources.py → base/energy_sources.py} +1 -1
- dodal/devices/electron_analyser/specs/__init__.py +4 -4
- dodal/devices/electron_analyser/specs/specs_detector.py +46 -0
- dodal/devices/electron_analyser/specs/{driver_io.py → specs_driver_io.py} +23 -26
- dodal/devices/electron_analyser/specs/{region.py → specs_region.py} +4 -3
- dodal/devices/electron_analyser/vgscienta/__init__.py +4 -4
- dodal/devices/electron_analyser/vgscienta/vgscienta_detector.py +52 -0
- dodal/devices/electron_analyser/vgscienta/{driver_io.py → vgscienta_driver_io.py} +25 -31
- dodal/devices/electron_analyser/vgscienta/{region.py → vgscienta_region.py} +6 -6
- dodal/devices/i04/max_pixel.py +38 -0
- dodal/devices/i09_1_shared/__init__.py +8 -1
- dodal/devices/i09_1_shared/hard_energy.py +112 -0
- dodal/devices/i09_2_shared/__init__.py +0 -0
- dodal/devices/i09_2_shared/i09_apple2.py +14 -0
- dodal/devices/i10/i10_apple2.py +24 -22
- dodal/devices/i17/i17_apple2.py +32 -20
- dodal/devices/i19/access_controlled/attenuator_motor_squad.py +61 -0
- dodal/devices/i19/access_controlled/blueapi_device.py +9 -1
- dodal/devices/i19/access_controlled/shutter.py +2 -4
- dodal/devices/i21/__init__.py +3 -1
- dodal/devices/insertion_device/__init__.py +58 -0
- dodal/devices/{apple2_undulator.py → insertion_device/apple2_undulator.py} +102 -44
- dodal/devices/insertion_device/energy_motor_lookup.py +88 -0
- dodal/devices/insertion_device/id_enum.py +17 -0
- dodal/devices/insertion_device/lookup_table_models.py +317 -0
- dodal/devices/motors.py +14 -0
- dodal/devices/robot.py +16 -11
- dodal/plans/__init__.py +1 -1
- dodal/plans/configure_arm_trigger_and_disarm_detector.py +2 -4
- dodal/testing/electron_analyser/device_factory.py +4 -4
- dodal/testing/fixtures/devices/__init__.py +0 -0
- dodal/testing/fixtures/devices/apple2.py +78 -0
- dodal/testing/fixtures/run_engine.py +4 -0
- dodal/utils.py +6 -3
- dodal/devices/electron_analyser/abstract/__init__.py +0 -25
- dodal/devices/electron_analyser/abstract/base_detector.py +0 -63
- dodal/devices/electron_analyser/abstract/types.py +0 -12
- dodal/devices/electron_analyser/detector.py +0 -143
- dodal/devices/electron_analyser/specs/detector.py +0 -34
- dodal/devices/electron_analyser/types.py +0 -57
- dodal/devices/electron_analyser/vgscienta/detector.py +0 -48
- dodal/devices/util/lookup_tables_apple2.py +0 -390
- {dls_dodal-1.66.0.dist-info → dls_dodal-1.68.0.dist-info}/WHEEL +0 -0
- {dls_dodal-1.66.0.dist-info → dls_dodal-1.68.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.66.0.dist-info → dls_dodal-1.68.0.dist-info}/licenses/LICENSE +0 -0
- {dls_dodal-1.66.0.dist-info → dls_dodal-1.68.0.dist-info}/top_level.txt +0 -0
- /dodal/devices/electron_analyser/{enums.py → base/base_enums.py} +0 -0
- /dodal/devices/electron_analyser/specs/{enums.py → specs_enums.py} +0 -0
- /dodal/devices/electron_analyser/vgscienta/{enums.py → vgscienta_enums.py} +0 -0
- /dodal/plans/{scanspec.py → spec_path.py} +0 -0
dodal/devices/cryostream.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from ophyd_async.core import (
|
|
2
|
-
EnabledDisabled,
|
|
3
2
|
InOut,
|
|
4
3
|
StandardReadable,
|
|
5
4
|
StandardReadableFormat,
|
|
@@ -12,20 +11,6 @@ from ophyd_async.epics.core import (
|
|
|
12
11
|
)
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
class CryoStream(StandardReadable):
|
|
16
|
-
MAX_TEMP_K = 110
|
|
17
|
-
MAX_PRESSURE_BAR = 0.1
|
|
18
|
-
|
|
19
|
-
def __init__(self, prefix: str, name: str = ""):
|
|
20
|
-
self.course = epics_signal_rw(InOut, f"{prefix}-EA-CJET-01:COARSE:CTRL")
|
|
21
|
-
self.fine = epics_signal_rw(InOut, f"{prefix}-EA-CJET-01:FINE:CTRL")
|
|
22
|
-
self.temperature_k = epics_signal_r(float, f"{prefix}-EA-CSTRM-01:TEMP")
|
|
23
|
-
self.back_pressure_bar = epics_signal_r(
|
|
24
|
-
float, f"{prefix}-EA-CSTRM-01:BACKPRESS"
|
|
25
|
-
)
|
|
26
|
-
super().__init__(name)
|
|
27
|
-
|
|
28
|
-
|
|
29
14
|
class TurboEnum(StrictEnum):
|
|
30
15
|
OFF = "Off"
|
|
31
16
|
ON = "On"
|
|
@@ -37,59 +22,33 @@ class CryoStreamSelection(StrictEnum):
|
|
|
37
22
|
HC1 = "HC1"
|
|
38
23
|
|
|
39
24
|
|
|
40
|
-
class
|
|
25
|
+
class OxfordCryoStream(StandardReadable):
|
|
41
26
|
def __init__(self, prefix: str, name: str = ""):
|
|
27
|
+
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
28
|
+
self.temp = epics_signal_r(float, f"{prefix}TEMP")
|
|
29
|
+
|
|
42
30
|
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
43
31
|
# Any signals that should be read once at the start of the scan
|
|
32
|
+
|
|
44
33
|
self.turbo = epics_signal_rw(str, f"{prefix}TURBO")
|
|
45
34
|
self.turbo_mode = epics_signal_rw(TurboEnum, f"{prefix}TURBOMODE")
|
|
46
|
-
|
|
47
|
-
self.serial_comms = epics_signal_rw(EnabledDisabled, f"{prefix}DISABLE")
|
|
48
35
|
self.status = epics_signal_r(str, f"{prefix}STATUS.SEVR")
|
|
49
|
-
|
|
50
|
-
with self.add_children_as_readables():
|
|
51
|
-
# Any signals that should be read at every point in the scan
|
|
52
|
-
|
|
53
|
-
self.purge = epics_signal_x(f"{prefix}PURGE.PROC")
|
|
54
|
-
self.hold = epics_signal_x(f"{prefix}HOLD.PROC")
|
|
55
|
-
self.start = epics_signal_x(f"{prefix}RESTART.PROC")
|
|
56
|
-
self.pause = epics_signal_x(f"{prefix}PAUSE.PROC")
|
|
57
|
-
self.resume = epics_signal_x(f"{prefix}RESUME.PROC")
|
|
58
|
-
self.end = epics_signal_x(f"{prefix}END.PROC")
|
|
59
|
-
self.stop = epics_signal_x(f"{prefix}STOP.PROC")
|
|
60
|
-
|
|
61
|
-
self.ramp_rate = epics_signal_rw(float, f"{prefix}RRATE")
|
|
62
|
-
self.ramp_temp = epics_signal_rw(float, f"{prefix}RTEMP")
|
|
63
|
-
self.ramp = epics_signal_x(f"{prefix}RAMP.PROC")
|
|
64
|
-
|
|
65
|
-
self.plat_time = epics_signal_rw(float, f"{prefix}PTIME")
|
|
66
|
-
self.plat = epics_signal_x(f"{prefix}PLAT.PROC")
|
|
67
|
-
|
|
68
|
-
self.cool_temp = epics_signal_rw(float, f"{prefix}CTEMP")
|
|
69
|
-
self.cool = epics_signal_x(f"{prefix}COOL.PROC")
|
|
70
|
-
|
|
71
|
-
self.end_rate = epics_signal_rw(float, f"{prefix}ERATE")
|
|
72
|
-
|
|
73
|
-
super().__init__(name)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class OxfordCryoStreamStatus(StandardReadable):
|
|
77
|
-
def __init__(self, prefix: str, name: str = ""):
|
|
78
|
-
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
79
|
-
# Any signals that should be read once at the start of the scan
|
|
80
|
-
|
|
81
36
|
self.pump_uptime = epics_signal_r(float, f"{prefix}RUNTIME")
|
|
82
37
|
self.controller_number = epics_signal_r(float, f"{prefix}CTRLNUM")
|
|
83
38
|
self.software_version = epics_signal_r(float, f"{prefix}VER")
|
|
84
39
|
self.evap_adjust = epics_signal_r(float, f"{prefix}EVAPADJUST")
|
|
85
40
|
self.series = epics_signal_r(str, f"{prefix}SERIES")
|
|
41
|
+
self.error = epics_signal_r(float, f"{prefix}ERROR")
|
|
42
|
+
self.mode = epics_signal_r(str, f"{prefix}RUNMODE")
|
|
86
43
|
|
|
87
44
|
with self.add_children_as_readables():
|
|
88
45
|
# Any signals that should be read at every point in the scan
|
|
46
|
+
self.ramp_rate = epics_signal_rw(float, f"{prefix}RRATE")
|
|
47
|
+
self.ramp_temp = epics_signal_rw(float, f"{prefix}RTEMP")
|
|
48
|
+
self.plat_time = epics_signal_rw(float, f"{prefix}PTIME")
|
|
49
|
+
self.cool_temp = epics_signal_rw(float, f"{prefix}CTEMP")
|
|
50
|
+
self.end_rate = epics_signal_rw(float, f"{prefix}ERATE")
|
|
89
51
|
self.setpoint = epics_signal_r(float, f"{prefix}SETPOINT")
|
|
90
|
-
self.temp = epics_signal_r(float, f"{prefix}TEMP")
|
|
91
|
-
self.error = epics_signal_r(float, f"{prefix}ERROR")
|
|
92
|
-
self.mode = epics_signal_r(str, f"{prefix}RUNMODE")
|
|
93
52
|
self.phase = epics_signal_r(str, f"{prefix}PHASE")
|
|
94
53
|
self.ramp_rate_setpoint = epics_signal_r(float, f"{prefix}RAMPRATE")
|
|
95
54
|
self.target_temp = epics_signal_r(float, f"{prefix}TARGETTEMP")
|
|
@@ -102,14 +61,27 @@ class OxfordCryoStreamStatus(StandardReadable):
|
|
|
102
61
|
self.suct_heat = epics_signal_r(float, f"{prefix}SUCTHEAT")
|
|
103
62
|
self.back_pressure = epics_signal_r(float, f"{prefix}BACKPRESS")
|
|
104
63
|
|
|
64
|
+
self.purge = epics_signal_x(f"{prefix}PURGE.PROC")
|
|
65
|
+
self.hold = epics_signal_x(f"{prefix}HOLD.PROC")
|
|
66
|
+
self.start = epics_signal_x(f"{prefix}RESTART.PROC")
|
|
67
|
+
self.pause = epics_signal_x(f"{prefix}PAUSE.PROC")
|
|
68
|
+
self.resume = epics_signal_x(f"{prefix}RESUME.PROC")
|
|
69
|
+
self.end = epics_signal_x(f"{prefix}END.PROC")
|
|
70
|
+
self.stop = epics_signal_x(f"{prefix}STOP.PROC")
|
|
71
|
+
self.plat = epics_signal_x(f"{prefix}PLAT.PROC")
|
|
72
|
+
self.cool = epics_signal_x(f"{prefix}COOL.PROC")
|
|
73
|
+
self.ramp = epics_signal_x(f"{prefix}RAMP.PROC")
|
|
74
|
+
|
|
105
75
|
super().__init__(name)
|
|
106
76
|
|
|
107
77
|
|
|
108
|
-
class
|
|
78
|
+
class OxfordCryoJet(StandardReadable):
|
|
79
|
+
# TODO: https://github.com/DiamondLightSource/dodal/issues/1486
|
|
80
|
+
# This is a placeholder implementation to get it working with I03, the actual cryojet has many more PVs
|
|
109
81
|
def __init__(self, prefix: str, name=""):
|
|
110
82
|
with self.add_children_as_readables():
|
|
111
|
-
self.
|
|
112
|
-
self.
|
|
83
|
+
self.coarse = epics_signal_rw(InOut, f"{prefix}COARSE:CTRL")
|
|
84
|
+
self.fine = epics_signal_rw(InOut, f"{prefix}FINE:CTRL")
|
|
113
85
|
|
|
114
86
|
super().__init__(name)
|
|
115
87
|
|
|
@@ -126,5 +98,4 @@ class CryoStreamGantry(StandardReadable):
|
|
|
126
98
|
self.cryostream_selected = epics_signal_r(
|
|
127
99
|
int, f"{prefix}-MO-STEP-02:GPIO_INP_BITS.B3"
|
|
128
100
|
)
|
|
129
|
-
|
|
130
101
|
super().__init__(name)
|
dodal/devices/eiger.py
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
# type: ignore # Eiger will soon be ophyd-async https://github.com/DiamondLightSource/dodal/issues/700
|
|
2
1
|
from dataclasses import dataclass
|
|
3
2
|
from enum import Enum
|
|
4
3
|
|
|
5
4
|
from bluesky.protocols import Stageable
|
|
6
5
|
from ophyd import Component, Device, EpicsSignalRO, Signal
|
|
7
6
|
from ophyd.areadetector.cam import EigerDetectorCam
|
|
8
|
-
from ophyd.status import AndStatus, Status, StatusBase
|
|
7
|
+
from ophyd.status import AndStatus, Status, StatusBase, SubscriptionStatus
|
|
9
8
|
|
|
10
9
|
from dodal.devices.detector import DetectorParams, TriggerMode
|
|
11
10
|
from dodal.devices.eiger_odin import EigerOdin
|
|
@@ -64,14 +63,12 @@ class EigerDetector(Device, Stageable):
|
|
|
64
63
|
arming_status = Status()
|
|
65
64
|
arming_status.set_finished()
|
|
66
65
|
|
|
67
|
-
disarming_status = Status()
|
|
68
|
-
disarming_status.set_finished()
|
|
69
|
-
|
|
70
66
|
def __init__(self, beamline: str = "i03", *args, **kwargs):
|
|
71
67
|
super().__init__(*args, **kwargs)
|
|
72
68
|
self.beamline = beamline
|
|
73
69
|
# using i03 timeouts as default
|
|
74
70
|
self.timeouts = AVAILABLE_TIMEOUTS.get(beamline, AVAILABLE_TIMEOUTS["i03"])
|
|
71
|
+
self.disarming_status = None
|
|
75
72
|
|
|
76
73
|
@classmethod
|
|
77
74
|
def with_params(
|
|
@@ -106,6 +103,7 @@ class EigerDetector(Device, Stageable):
|
|
|
106
103
|
raise Exception("\n".join(errors))
|
|
107
104
|
|
|
108
105
|
def async_stage(self):
|
|
106
|
+
self.disarming_status = None
|
|
109
107
|
self.odin.nodes.clear_odin_errors()
|
|
110
108
|
status_ok, error_message = self.odin.wait_for_odin_initialised(
|
|
111
109
|
self.timeouts.general_status_timeout
|
|
@@ -124,7 +122,7 @@ class EigerDetector(Device, Stageable):
|
|
|
124
122
|
LOGGER.info("Waiting for arming to finish")
|
|
125
123
|
self.arming_status.wait(self.timeouts.arming_timeout)
|
|
126
124
|
|
|
127
|
-
def stage(self):
|
|
125
|
+
def stage(self): # pyright: ignore[reportIncompatibleMethodOverride]
|
|
128
126
|
self.wait_on_arming_if_started()
|
|
129
127
|
if not self.is_armed():
|
|
130
128
|
LOGGER.info("Eiger not armed, arming")
|
|
@@ -133,6 +131,7 @@ class EigerDetector(Device, Stageable):
|
|
|
133
131
|
|
|
134
132
|
def stop_odin_when_all_frames_collected(self):
|
|
135
133
|
LOGGER.info("Waiting on all frames")
|
|
134
|
+
assert self.detector_params
|
|
136
135
|
try:
|
|
137
136
|
await_value(
|
|
138
137
|
self.odin.file_writer.num_captured,
|
|
@@ -142,7 +141,7 @@ class EigerDetector(Device, Stageable):
|
|
|
142
141
|
LOGGER.info("Stopping Odin")
|
|
143
142
|
self.odin.stop().wait(self.timeouts.odin_stop_timeout)
|
|
144
143
|
|
|
145
|
-
def unstage(self) -> bool:
|
|
144
|
+
def unstage(self) -> bool: # pyright: ignore[reportIncompatibleMethodOverride]
|
|
146
145
|
assert self.detector_params is not None
|
|
147
146
|
try:
|
|
148
147
|
self.disarming_status = Status()
|
|
@@ -167,25 +166,34 @@ class EigerDetector(Device, Stageable):
|
|
|
167
166
|
self.disarming_status.set_finished()
|
|
168
167
|
return status_ok
|
|
169
168
|
|
|
170
|
-
def stop(self, *args):
|
|
169
|
+
def stop(self, *args): # pyright: ignore[reportIncompatibleMethodOverride]
|
|
171
170
|
"""Emergency stop the device, mainly used to clean up after error."""
|
|
172
171
|
LOGGER.info("Eiger stop() called - cleaning up...")
|
|
173
|
-
if not self.disarming_status.done:
|
|
172
|
+
if self.disarming_status and not self.disarming_status.done:
|
|
174
173
|
LOGGER.info("Eiger still disarming, waiting on disarm")
|
|
175
174
|
self.disarming_status.wait(self.timeouts.arming_timeout)
|
|
175
|
+
elif not self.disarming_status:
|
|
176
|
+
self.disarming_status = Status()
|
|
177
|
+
try:
|
|
178
|
+
self.wait_on_arming_if_started()
|
|
179
|
+
stop_status = self.odin.stop()
|
|
180
|
+
self.odin.file_writer.start_timeout.set(1).wait(
|
|
181
|
+
self.timeouts.general_status_timeout
|
|
182
|
+
)
|
|
183
|
+
self.disarm_detector()
|
|
184
|
+
stop_status &= self.disable_roi_mode()
|
|
185
|
+
LOGGER.info("Waiting on stop status")
|
|
186
|
+
stop_status.wait(self.timeouts.general_status_timeout)
|
|
187
|
+
# See https://github.com/DiamondLightSource/hyperion/issues/1395
|
|
188
|
+
LOGGER.info("Turning off Eiger dev/shm streaming")
|
|
189
|
+
self.odin.fan.dev_shm_enable.set(0).wait(
|
|
190
|
+
self.timeouts.general_status_timeout
|
|
191
|
+
)
|
|
192
|
+
LOGGER.info("Eiger has successfully been stopped")
|
|
193
|
+
finally:
|
|
194
|
+
self.disarming_status.set_finished()
|
|
176
195
|
else:
|
|
177
|
-
|
|
178
|
-
stop_status = self.odin.stop()
|
|
179
|
-
self.odin.file_writer.start_timeout.set(1).wait(
|
|
180
|
-
self.timeouts.general_status_timeout
|
|
181
|
-
)
|
|
182
|
-
self.disarm_detector()
|
|
183
|
-
stop_status &= self.disable_roi_mode()
|
|
184
|
-
stop_status.wait(self.timeouts.general_status_timeout)
|
|
185
|
-
# See https://github.com/DiamondLightSource/hyperion/issues/1395
|
|
186
|
-
LOGGER.info("Turning off Eiger dev/shm streaming")
|
|
187
|
-
self.odin.fan.dev_shm_enable.set(0).wait()
|
|
188
|
-
LOGGER.info("Eiger has successfully been stopped")
|
|
196
|
+
LOGGER.info("Already disarmed, doing nothing")
|
|
189
197
|
|
|
190
198
|
def disable_roi_mode(self):
|
|
191
199
|
return self.change_roi_mode(False)
|
|
@@ -194,13 +202,13 @@ class EigerDetector(Device, Stageable):
|
|
|
194
202
|
return self.change_roi_mode(True)
|
|
195
203
|
|
|
196
204
|
def change_roi_mode(self, enable: bool) -> StatusBase:
|
|
205
|
+
LOGGER.info(f"Changing ROI mode to {enable}")
|
|
197
206
|
assert self.detector_params is not None
|
|
198
207
|
detector_dimensions = (
|
|
199
208
|
self.detector_params.detector_size_constants.roi_size_pixels
|
|
200
209
|
if enable
|
|
201
210
|
else self.detector_params.detector_size_constants.det_size_pixels
|
|
202
211
|
)
|
|
203
|
-
|
|
204
212
|
status = self.cam.roi_mode.set(
|
|
205
213
|
1 if enable else 0, timeout=self.timeouts.general_status_timeout
|
|
206
214
|
)
|
|
@@ -216,7 +224,6 @@ class EigerDetector(Device, Stageable):
|
|
|
216
224
|
status &= self.odin.file_writer.num_col_chunks.set(
|
|
217
225
|
detector_dimensions.width, timeout=self.timeouts.general_status_timeout
|
|
218
226
|
)
|
|
219
|
-
|
|
220
227
|
return status
|
|
221
228
|
|
|
222
229
|
def set_cam_pvs(self) -> AndStatus:
|
|
@@ -234,7 +241,8 @@ class EigerDetector(Device, Stageable):
|
|
|
234
241
|
1, timeout=self.timeouts.general_status_timeout
|
|
235
242
|
)
|
|
236
243
|
status &= self.cam.image_mode.set(
|
|
237
|
-
self.cam.ImageMode.MULTIPLE,
|
|
244
|
+
self.cam.ImageMode.MULTIPLE, # pyright: ignore[reportAttributeAccessIssue]
|
|
245
|
+
timeout=self.timeouts.general_status_timeout,
|
|
238
246
|
)
|
|
239
247
|
status &= self.cam.trigger_mode.set(
|
|
240
248
|
InternalEigerTriggerMode.EXTERNAL_SERIES.value,
|
|
@@ -296,6 +304,7 @@ class EigerDetector(Device, Stageable):
|
|
|
296
304
|
self.detector_params.omega_increment,
|
|
297
305
|
timeout=self.timeouts.general_status_timeout,
|
|
298
306
|
)
|
|
307
|
+
|
|
299
308
|
return status
|
|
300
309
|
|
|
301
310
|
def set_detector_threshold(self, energy: float, tolerance: float = 0.1) -> Status:
|
|
@@ -307,7 +316,7 @@ class EigerDetector(Device, Stageable):
|
|
|
307
316
|
this tolerance it is not set again. Defaults to 0.1eV.
|
|
308
317
|
"""
|
|
309
318
|
|
|
310
|
-
current_energy = self.cam.photon_energy.get()
|
|
319
|
+
current_energy = float(self.cam.photon_energy.get())
|
|
311
320
|
if abs(current_energy - energy) > tolerance:
|
|
312
321
|
LOGGER.info(f"Setting detector threshold to {energy}")
|
|
313
322
|
return self.cam.photon_energy.set(
|
|
@@ -390,7 +399,7 @@ class EigerDetector(Device, Stageable):
|
|
|
390
399
|
def disarm_detector(self):
|
|
391
400
|
self.cam.acquire.set(0).wait(self.timeouts.general_status_timeout)
|
|
392
401
|
|
|
393
|
-
def wait_for_stale_params(self) ->
|
|
402
|
+
def wait_for_stale_params(self) -> SubscriptionStatus:
|
|
394
403
|
LOGGER.info("Eiger arming: Waiting for stale params...")
|
|
395
404
|
return await_value(self.stale_params, 0, 60)
|
|
396
405
|
|
|
@@ -404,6 +413,11 @@ class EigerDetector(Device, Stageable):
|
|
|
404
413
|
detector_params: DetectorParams = self.detector_params
|
|
405
414
|
if detector_params.use_roi_mode:
|
|
406
415
|
functions_to_do_arm.append(self.enable_roi_mode)
|
|
416
|
+
threshold_energy = (
|
|
417
|
+
detector_params.expected_energy_ev
|
|
418
|
+
if detector_params.expected_energy_ev
|
|
419
|
+
else float(self.cam.photon_energy.get())
|
|
420
|
+
)
|
|
407
421
|
|
|
408
422
|
arming_sequence_funcs = [
|
|
409
423
|
# If a beam dump occurs after arming the eiger but prior to eiger staging,
|
|
@@ -411,7 +425,7 @@ class EigerDetector(Device, Stageable):
|
|
|
411
425
|
# if this previously completed successfully we must reset the odin first
|
|
412
426
|
self.odin.stop,
|
|
413
427
|
lambda: self.change_dev_shm(detector_params.enable_dev_shm),
|
|
414
|
-
lambda: self.set_detector_threshold(
|
|
428
|
+
lambda: self.set_detector_threshold(threshold_energy),
|
|
415
429
|
self.set_cam_pvs,
|
|
416
430
|
self.set_odin_number_of_frame_chunks,
|
|
417
431
|
self.set_odin_pvs,
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
from .detector import (
|
|
2
|
-
ElectronAnalyserDetector,
|
|
3
|
-
ElectronAnalyserRegionDetector,
|
|
4
|
-
TElectronAnalyserDetector,
|
|
5
|
-
TElectronAnalyserRegionDetector,
|
|
6
|
-
)
|
|
7
|
-
from .energy_sources import DualEnergySource, EnergySource
|
|
8
|
-
from .enums import EnergyMode, SelectedSource
|
|
9
|
-
from .types import (
|
|
10
|
-
ElectronAnalyserDetectorImpl,
|
|
11
|
-
ElectronAnalyserDriverImpl,
|
|
12
|
-
GenericElectronAnalyserDetector,
|
|
13
|
-
GenericElectronAnalyserRegionDetector,
|
|
14
|
-
)
|
|
15
|
-
from .util import to_binding_energy, to_kinetic_energy
|
|
16
|
-
|
|
17
|
-
__all__ = [
|
|
18
|
-
"to_binding_energy",
|
|
19
|
-
"to_kinetic_energy",
|
|
20
|
-
"DualEnergySource",
|
|
21
|
-
"SelectedSource",
|
|
22
|
-
"EnergySource",
|
|
23
|
-
"EnergyMode",
|
|
24
|
-
"SelectedSource",
|
|
25
|
-
"ElectronAnalyserDetector",
|
|
26
|
-
"ElectronAnalyserDetectorImpl",
|
|
27
|
-
"ElectronAnalyserDriverImpl",
|
|
28
|
-
"TElectronAnalyserDetector",
|
|
29
|
-
"ElectronAnalyserRegionDetector",
|
|
30
|
-
"TElectronAnalyserRegionDetector",
|
|
31
|
-
"GenericElectronAnalyserDetector",
|
|
32
|
-
"GenericElectronAnalyserRegionDetector",
|
|
33
|
-
]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from .base_controller import (
|
|
2
|
+
ElectronAnalyserController,
|
|
3
|
+
GenericElectronAnalyserController,
|
|
4
|
+
)
|
|
5
|
+
from .base_detector import (
|
|
6
|
+
BaseElectronAnalyserDetector,
|
|
7
|
+
ElectronAnalyserDetector,
|
|
8
|
+
ElectronAnalyserRegionDetector,
|
|
9
|
+
GenericBaseElectronAnalyserDetector,
|
|
10
|
+
GenericElectronAnalyserDetector,
|
|
11
|
+
GenericElectronAnalyserRegionDetector,
|
|
12
|
+
)
|
|
13
|
+
from .base_driver_io import (
|
|
14
|
+
AbstractAnalyserDriverIO,
|
|
15
|
+
GenericAnalyserDriverIO,
|
|
16
|
+
TAbstractAnalyserDriverIO,
|
|
17
|
+
)
|
|
18
|
+
from .base_enums import EnergyMode, SelectedSource
|
|
19
|
+
from .base_region import (
|
|
20
|
+
AbstractBaseRegion,
|
|
21
|
+
AbstractBaseSequence,
|
|
22
|
+
GenericRegion,
|
|
23
|
+
GenericSequence,
|
|
24
|
+
TAbstractBaseRegion,
|
|
25
|
+
TAbstractBaseSequence,
|
|
26
|
+
TAcquisitionMode,
|
|
27
|
+
TLensMode,
|
|
28
|
+
)
|
|
29
|
+
from .base_util import to_binding_energy, to_kinetic_energy
|
|
30
|
+
from .energy_sources import DualEnergySource, EnergySource
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"ElectronAnalyserController",
|
|
34
|
+
"GenericElectronAnalyserController",
|
|
35
|
+
"BaseElectronAnalyserDetector",
|
|
36
|
+
"ElectronAnalyserDetector",
|
|
37
|
+
"ElectronAnalyserRegionDetector",
|
|
38
|
+
"GenericBaseElectronAnalyserDetector",
|
|
39
|
+
"GenericElectronAnalyserDetector",
|
|
40
|
+
"GenericElectronAnalyserRegionDetector",
|
|
41
|
+
"AbstractAnalyserDriverIO",
|
|
42
|
+
"GenericAnalyserDriverIO",
|
|
43
|
+
"TAbstractAnalyserDriverIO",
|
|
44
|
+
"EnergyMode",
|
|
45
|
+
"SelectedSource",
|
|
46
|
+
"AbstractBaseRegion",
|
|
47
|
+
"AbstractBaseSequence",
|
|
48
|
+
"GenericRegion",
|
|
49
|
+
"GenericSequence",
|
|
50
|
+
"TAbstractBaseRegion",
|
|
51
|
+
"TAbstractBaseSequence",
|
|
52
|
+
"TAcquisitionMode",
|
|
53
|
+
"TLensMode",
|
|
54
|
+
"to_binding_energy",
|
|
55
|
+
"to_kinetic_energy",
|
|
56
|
+
"DualEnergySource",
|
|
57
|
+
"EnergySource",
|
|
58
|
+
]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from typing import Generic, TypeVar
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import TriggerInfo
|
|
4
|
+
from ophyd_async.epics.adcore import ADImageMode
|
|
5
|
+
|
|
6
|
+
from dodal.devices.controllers import ConstantDeadTimeController
|
|
7
|
+
from dodal.devices.electron_analyser.base.base_driver_io import (
|
|
8
|
+
GenericAnalyserDriverIO,
|
|
9
|
+
TAbstractAnalyserDriverIO,
|
|
10
|
+
)
|
|
11
|
+
from dodal.devices.electron_analyser.base.base_region import (
|
|
12
|
+
GenericRegion,
|
|
13
|
+
TAbstractBaseRegion,
|
|
14
|
+
)
|
|
15
|
+
from dodal.devices.electron_analyser.base.energy_sources import (
|
|
16
|
+
AbstractEnergySource,
|
|
17
|
+
DualEnergySource,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ElectronAnalyserController(
|
|
22
|
+
ConstantDeadTimeController[TAbstractAnalyserDriverIO],
|
|
23
|
+
Generic[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Specialised controller for the electron analysers to provide additional setup logic
|
|
27
|
+
such as selecting the energy source to use from requested region and giving the
|
|
28
|
+
driver the correct region parameters.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
driver: TAbstractAnalyserDriverIO,
|
|
34
|
+
energy_source: AbstractEnergySource,
|
|
35
|
+
deadtime: float,
|
|
36
|
+
image_mode: ADImageMode = ADImageMode.SINGLE,
|
|
37
|
+
):
|
|
38
|
+
"""
|
|
39
|
+
Parameters:
|
|
40
|
+
driver: The electron analyser driver to wrap around that holds the PV's.
|
|
41
|
+
energy_source: Device that holds the excitation energy and ability to switch
|
|
42
|
+
between sources.
|
|
43
|
+
deadtime: For a given exposure, what is the safest minimum time between
|
|
44
|
+
exposures that can be determined without reading signals.
|
|
45
|
+
image_mode: The image mode to configure the driver with before measuring.
|
|
46
|
+
"""
|
|
47
|
+
self.energy_source = energy_source
|
|
48
|
+
super().__init__(driver, deadtime, image_mode)
|
|
49
|
+
|
|
50
|
+
async def setup_with_region(self, region: TAbstractBaseRegion):
|
|
51
|
+
"""Logic to set the driver with a region."""
|
|
52
|
+
|
|
53
|
+
if isinstance(self.energy_source, DualEnergySource):
|
|
54
|
+
self.energy_source.selected_source.set(region.excitation_energy_source)
|
|
55
|
+
excitation_energy = await self.energy_source.energy.get_value()
|
|
56
|
+
epics_region = region.prepare_for_epics(excitation_energy)
|
|
57
|
+
await self.driver.set(epics_region)
|
|
58
|
+
|
|
59
|
+
async def prepare(self, trigger_info: TriggerInfo) -> None:
|
|
60
|
+
"""Do all necessary steps to prepare the detector for triggers."""
|
|
61
|
+
# Let the driver know the excitation energy before measuring for binding energy
|
|
62
|
+
# axis calculation.
|
|
63
|
+
excitation_energy = await self.energy_source.energy.get_value()
|
|
64
|
+
await self.driver.cached_excitation_energy.set(excitation_energy)
|
|
65
|
+
await super().prepare(trigger_info)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
GenericElectronAnalyserController = ElectronAnalyserController[
|
|
69
|
+
GenericAnalyserDriverIO, GenericRegion
|
|
70
|
+
]
|
|
71
|
+
TElectronAnalyserController = TypeVar(
|
|
72
|
+
"TElectronAnalyserController", bound=ElectronAnalyserController
|
|
73
|
+
)
|