dls-dodal 1.34.1__py3-none-any.whl → 1.36.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.34.1.dist-info → dls_dodal-1.36.0.dist-info}/METADATA +4 -2
- dls_dodal-1.36.0.dist-info/RECORD +152 -0
- {dls_dodal-1.34.1.dist-info → dls_dodal-1.36.0.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/i22.py +24 -11
- dodal/beamlines/i24.py +4 -4
- dodal/beamlines/p38.py +23 -11
- dodal/common/beamlines/beamline_utils.py +1 -2
- dodal/common/crystal_metadata.py +61 -0
- dodal/common/signal_utils.py +10 -14
- dodal/devices/CTAB.py +1 -1
- dodal/devices/aperture.py +1 -1
- dodal/devices/aperturescatterguard.py +20 -8
- dodal/devices/apple2_undulator.py +30 -29
- dodal/devices/areadetector/plugins/CAM.py +3 -5
- dodal/devices/areadetector/plugins/MJPG.py +1 -1
- dodal/devices/attenuator.py +1 -1
- dodal/devices/backlight.py +4 -5
- dodal/devices/cryostream.py +3 -5
- dodal/devices/dcm.py +26 -2
- dodal/devices/detector/detector_motion.py +3 -5
- dodal/devices/diamond_filter.py +3 -4
- dodal/devices/eiger.py +88 -49
- dodal/devices/fast_grid_scan.py +1 -1
- dodal/devices/fluorescence_detector_motion.py +5 -7
- dodal/devices/focusing_mirror.py +12 -11
- dodal/devices/hutch_shutter.py +4 -5
- dodal/devices/i10/i10_apple2.py +20 -19
- dodal/devices/i10/i10_setting_data.py +2 -2
- dodal/devices/i22/dcm.py +43 -75
- dodal/devices/i22/fswitch.py +5 -5
- dodal/devices/i24/aperture.py +3 -5
- dodal/devices/i24/beamstop.py +3 -5
- dodal/devices/i24/dcm.py +1 -1
- dodal/devices/i24/dual_backlight.py +4 -6
- dodal/devices/i24/pmac.py +35 -46
- dodal/devices/i24/vgonio.py +16 -0
- dodal/devices/ipin.py +5 -3
- dodal/devices/linkam3.py +7 -7
- dodal/devices/oav/oav_detector.py +3 -3
- dodal/devices/oav/oav_to_redis_forwarder.py +8 -7
- dodal/devices/oav/pin_image_recognition/__init__.py +9 -7
- dodal/devices/oav/snapshots/grid_overlay.py +16 -16
- dodal/devices/oav/snapshots/snapshot_with_beam_centre.py +5 -5
- dodal/devices/oav/snapshots/snapshot_with_grid.py +6 -6
- dodal/devices/oav/utils.py +2 -2
- dodal/devices/p99/sample_stage.py +3 -5
- dodal/devices/pgm.py +5 -6
- dodal/devices/qbpm.py +1 -1
- dodal/devices/robot.py +3 -3
- dodal/devices/smargon.py +1 -1
- dodal/devices/synchrotron.py +9 -4
- dodal/devices/tetramm.py +7 -7
- dodal/devices/thawer.py +13 -7
- dodal/devices/undulator.py +5 -5
- dodal/devices/util/epics_util.py +1 -1
- dodal/devices/watsonmarlow323_pump.py +45 -0
- dodal/devices/webcam.py +9 -2
- dodal/devices/xbpm_feedback.py +3 -5
- dodal/devices/xspress3/xspress3.py +8 -9
- dodal/devices/xspress3/xspress3_channel.py +3 -5
- dodal/devices/zebra.py +7 -6
- dodal/devices/zebra_controlled_shutter.py +5 -6
- dodal/devices/zocalo/__init__.py +2 -2
- dodal/devices/zocalo/zocalo_constants.py +3 -0
- dodal/devices/zocalo/zocalo_interaction.py +2 -1
- dodal/devices/zocalo/zocalo_results.py +92 -79
- dodal/plan_stubs/__init__.py +0 -0
- dodal/{plans/data_session_metadata.py → plan_stubs/data_session.py} +2 -2
- dodal/{plans/motor_util_plans.py → plan_stubs/motor_utils.py} +2 -2
- dodal/plan_stubs/wrapped.py +150 -0
- dodal/plans/__init__.py +4 -0
- dodal/plans/scanspec.py +66 -0
- dodal/plans/wrapped.py +57 -0
- dodal/utils.py +4 -0
- dls_dodal-1.34.1.dist-info/RECORD +0 -144
- dodal/devices/i24/i24_vgonio.py +0 -17
- {dls_dodal-1.34.1.dist-info → dls_dodal-1.36.0.dist-info}/LICENSE +0 -0
- {dls_dodal-1.34.1.dist-info → dls_dodal-1.36.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.34.1.dist-info → dls_dodal-1.36.0.dist-info}/top_level.txt +0 -0
- /dodal/{plans → plan_stubs}/check_topup.py +0 -0
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import asyncio
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from enum import Enum
|
|
5
4
|
from typing import Any
|
|
6
5
|
|
|
7
6
|
import numpy as np
|
|
8
7
|
from bluesky.protocols import Movable
|
|
9
8
|
from ophyd_async.core import (
|
|
10
9
|
AsyncStatus,
|
|
11
|
-
|
|
12
|
-
HintedSignal,
|
|
10
|
+
Reference,
|
|
13
11
|
StandardReadable,
|
|
12
|
+
StandardReadableFormat,
|
|
13
|
+
StrictEnum,
|
|
14
14
|
soft_signal_r_and_setter,
|
|
15
15
|
wait_for_value,
|
|
16
16
|
)
|
|
17
|
-
from ophyd_async.epics.
|
|
17
|
+
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw, epics_signal_w
|
|
18
18
|
from pydantic import BaseModel, ConfigDict, RootModel
|
|
19
19
|
|
|
20
20
|
from dodal.log import LOGGER
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
class UndulatorGateStatus(
|
|
23
|
+
class UndulatorGateStatus(StrictEnum):
|
|
24
24
|
open = "Open"
|
|
25
25
|
close = "Closed"
|
|
26
26
|
|
|
@@ -128,12 +128,12 @@ class UndulatorGap(StandardReadable, Movable):
|
|
|
128
128
|
)
|
|
129
129
|
# This is calculated acceleration from speed
|
|
130
130
|
self.acceleration_time = epics_signal_r(float, prefix + "IDGSETACC")
|
|
131
|
-
with self.add_children_as_readables(
|
|
131
|
+
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
132
132
|
# Unit
|
|
133
133
|
self.motor_egu = epics_signal_r(str, prefix + "BLGAPMTR.EGU")
|
|
134
134
|
# Gap velocity
|
|
135
135
|
self.velocity = epics_signal_rw(float, prefix + "BLGSETVEL")
|
|
136
|
-
with self.add_children_as_readables(
|
|
136
|
+
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
137
137
|
# Gap readback value
|
|
138
138
|
self.user_readback = epics_signal_r(float, prefix + "CURRGAPD")
|
|
139
139
|
super().__init__(name)
|
|
@@ -187,10 +187,10 @@ class UndulatorPhaseMotor(StandardReadable):
|
|
|
187
187
|
self.user_setpoint_demand_readback = epics_signal_r(float, fullPV + "DMD")
|
|
188
188
|
|
|
189
189
|
fullPV = fullPV + "MTR"
|
|
190
|
-
with self.add_children_as_readables(
|
|
190
|
+
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
191
191
|
self.user_setpoint_readback = epics_signal_r(float, fullPV + ".RBV")
|
|
192
192
|
|
|
193
|
-
with self.add_children_as_readables(
|
|
193
|
+
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
194
194
|
self.motor_egu = epics_signal_r(str, fullPV + ".EGU")
|
|
195
195
|
self.velocity = epics_signal_rw(float, fullPV + ".VELO")
|
|
196
196
|
|
|
@@ -388,10 +388,10 @@ class Apple2(StandardReadable, Movable):
|
|
|
388
388
|
|
|
389
389
|
# Attributes are set after super call so they are not renamed to
|
|
390
390
|
# <name>-undulator, etc.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
with self.add_children_as_readables(
|
|
391
|
+
self.gap = Reference(id_gap)
|
|
392
|
+
self.phase = Reference(id_phase)
|
|
393
|
+
|
|
394
|
+
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
395
395
|
# Store the polarisation for readback.
|
|
396
396
|
self.polarisation, self._polarisation_set = soft_signal_r_and_setter(
|
|
397
397
|
str, initial_value=None
|
|
@@ -437,16 +437,16 @@ class Apple2(StandardReadable, Movable):
|
|
|
437
437
|
"""
|
|
438
438
|
|
|
439
439
|
# Only need to check gap as the phase motors share both fault and gate with gap.
|
|
440
|
-
await self.gap.check_id_status()
|
|
440
|
+
await self.gap().check_id_status()
|
|
441
441
|
await asyncio.gather(
|
|
442
|
-
self.phase.top_outer.user_setpoint.set(value=value.top_outer),
|
|
443
|
-
self.phase.top_inner.user_setpoint.set(value=value.top_inner),
|
|
444
|
-
self.phase.btm_inner.user_setpoint.set(value=value.btm_inner),
|
|
445
|
-
self.phase.btm_outer.user_setpoint.set(value=value.btm_outer),
|
|
446
|
-
self.gap.user_setpoint.set(value=value.gap),
|
|
442
|
+
self.phase().top_outer.user_setpoint.set(value=value.top_outer),
|
|
443
|
+
self.phase().top_inner.user_setpoint.set(value=value.top_inner),
|
|
444
|
+
self.phase().btm_inner.user_setpoint.set(value=value.btm_inner),
|
|
445
|
+
self.phase().btm_outer.user_setpoint.set(value=value.btm_outer),
|
|
446
|
+
self.gap().user_setpoint.set(value=value.gap),
|
|
447
447
|
)
|
|
448
448
|
timeout = np.max(
|
|
449
|
-
await asyncio.gather(self.gap.get_timeout(), self.phase.get_timeout())
|
|
449
|
+
await asyncio.gather(self.gap().get_timeout(), self.phase().get_timeout())
|
|
450
450
|
)
|
|
451
451
|
LOGGER.info(
|
|
452
452
|
f"Moving f{self.name} energy and polorisation to {energy}, {self.pol}"
|
|
@@ -454,10 +454,12 @@ class Apple2(StandardReadable, Movable):
|
|
|
454
454
|
)
|
|
455
455
|
|
|
456
456
|
await asyncio.gather(
|
|
457
|
-
self.gap.set_move.set(value=1, timeout=timeout),
|
|
458
|
-
self.phase.set_move.set(value=1, timeout=timeout),
|
|
457
|
+
self.gap().set_move.set(value=1, timeout=timeout),
|
|
458
|
+
self.phase().set_move.set(value=1, timeout=timeout),
|
|
459
|
+
)
|
|
460
|
+
await wait_for_value(
|
|
461
|
+
self.gap().gate, UndulatorGateStatus.close, timeout=timeout
|
|
459
462
|
)
|
|
460
|
-
await wait_for_value(self.gap.gate, UndulatorGateStatus.close, timeout=timeout)
|
|
461
463
|
self._energy_set(energy) # Update energy for after move for readback.
|
|
462
464
|
|
|
463
465
|
def _get_id_gap_phase(self, energy: float) -> tuple[float, float]:
|
|
@@ -522,12 +524,11 @@ class Apple2(StandardReadable, Movable):
|
|
|
522
524
|
(May be for future one can use the inverse poly to work out the energy and try to match it with the current energy
|
|
523
525
|
to workout the polarisation but during my test the inverse poly is too unstable for general use.)
|
|
524
526
|
"""
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
gap = cur_loc[self.gap.user_readback.name]["value"]
|
|
527
|
+
top_outer = await self.phase().top_outer.user_setpoint_readback.get_value()
|
|
528
|
+
top_inner = await self.phase().top_inner.user_setpoint_readback.get_value()
|
|
529
|
+
btm_inner = await self.phase().btm_inner.user_setpoint_readback.get_value()
|
|
530
|
+
btm_outer = await self.phase().btm_outer.user_setpoint_readback.get_value()
|
|
531
|
+
gap = await self.gap().user_readback.get_value()
|
|
531
532
|
if gap > MAXIMUM_GAP_MOTOR_POSITION:
|
|
532
533
|
raise RuntimeError(
|
|
533
534
|
f"{self.name} is not in use, close gap or set polarisation to use this ID"
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
from
|
|
1
|
+
from ophyd_async.core import StandardReadable, StrictEnum
|
|
2
|
+
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
2
3
|
|
|
3
|
-
from ophyd_async.core import StandardReadable
|
|
4
|
-
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
class ColorMode(str, Enum):
|
|
5
|
+
class ColorMode(StrictEnum):
|
|
8
6
|
"""
|
|
9
7
|
Enum to store the various color modes of the camera. We use RGB1.
|
|
10
8
|
"""
|
|
@@ -6,7 +6,7 @@ import aiofiles
|
|
|
6
6
|
from aiohttp import ClientSession
|
|
7
7
|
from bluesky.protocols import Triggerable
|
|
8
8
|
from ophyd_async.core import AsyncStatus, StandardReadable, soft_signal_rw
|
|
9
|
-
from ophyd_async.epics.
|
|
9
|
+
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
10
10
|
from PIL import Image
|
|
11
11
|
|
|
12
12
|
from dodal.log import LOGGER
|
dodal/devices/attenuator.py
CHANGED
|
@@ -9,7 +9,7 @@ from ophyd_async.core import (
|
|
|
9
9
|
StandardReadable,
|
|
10
10
|
wait_for_value,
|
|
11
11
|
)
|
|
12
|
-
from ophyd_async.epics.
|
|
12
|
+
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw, epics_signal_x
|
|
13
13
|
|
|
14
14
|
from dodal.log import LOGGER
|
|
15
15
|
|
dodal/devices/backlight.py
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
from asyncio import sleep
|
|
2
|
-
from enum import Enum
|
|
3
2
|
|
|
4
3
|
from bluesky.protocols import Movable
|
|
5
|
-
from ophyd_async.core import AsyncStatus, StandardReadable
|
|
6
|
-
from ophyd_async.epics.
|
|
4
|
+
from ophyd_async.core import AsyncStatus, StandardReadable, StrictEnum
|
|
5
|
+
from ophyd_async.epics.core import epics_signal_rw
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
class BacklightPower(
|
|
8
|
+
class BacklightPower(StrictEnum):
|
|
10
9
|
ON = "On"
|
|
11
10
|
OFF = "Off"
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
class BacklightPosition(
|
|
13
|
+
class BacklightPosition(StrictEnum):
|
|
15
14
|
IN = "In"
|
|
16
15
|
OUT = "Out"
|
|
17
16
|
|
dodal/devices/cryostream.py
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
from
|
|
1
|
+
from ophyd_async.core import StandardReadable, StrictEnum
|
|
2
|
+
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
2
3
|
|
|
3
|
-
from ophyd_async.core import StandardReadable
|
|
4
|
-
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
class InOut(str, Enum):
|
|
5
|
+
class InOut(StrictEnum):
|
|
8
6
|
IN = "In"
|
|
9
7
|
OUT = "Out"
|
|
10
8
|
|
dodal/devices/dcm.py
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numpy.typing import NDArray
|
|
3
|
+
from ophyd_async.core import StandardReadable, soft_signal_r_and_setter
|
|
4
|
+
from ophyd_async.epics.core import epics_signal_r
|
|
2
5
|
from ophyd_async.epics.motor import Motor
|
|
3
|
-
|
|
6
|
+
|
|
7
|
+
from dodal.common.crystal_metadata import (
|
|
8
|
+
CrystalMetadata,
|
|
9
|
+
MaterialsEnum,
|
|
10
|
+
make_crystal_metadata_from_material,
|
|
11
|
+
)
|
|
4
12
|
|
|
5
13
|
|
|
6
14
|
class DCM(StandardReadable):
|
|
@@ -17,7 +25,11 @@ class DCM(StandardReadable):
|
|
|
17
25
|
self,
|
|
18
26
|
prefix: str,
|
|
19
27
|
name: str = "",
|
|
28
|
+
crystal_metadata: CrystalMetadata | None = None,
|
|
20
29
|
) -> None:
|
|
30
|
+
cm = crystal_metadata or make_crystal_metadata_from_material(
|
|
31
|
+
MaterialsEnum.Si, (1, 1, 1)
|
|
32
|
+
)
|
|
21
33
|
with self.add_children_as_readables():
|
|
22
34
|
self.bragg_in_degrees = Motor(prefix + "BRAGG")
|
|
23
35
|
self.roll_in_mrad = Motor(prefix + "ROLL")
|
|
@@ -36,4 +48,16 @@ class DCM(StandardReadable):
|
|
|
36
48
|
self.perp_temp = epics_signal_r(float, prefix + "TEMP6")
|
|
37
49
|
self.perp_sub_assembly_temp = epics_signal_r(float, prefix + "TEMP7")
|
|
38
50
|
|
|
51
|
+
self.crystal_metadata_usage, _ = soft_signal_r_and_setter(
|
|
52
|
+
str, initial_value=cm.usage
|
|
53
|
+
)
|
|
54
|
+
self.crystal_metadata_type, _ = soft_signal_r_and_setter(
|
|
55
|
+
str, initial_value=cm.type
|
|
56
|
+
)
|
|
57
|
+
reflection_array = np.array(cm.reflection)
|
|
58
|
+
self.crystal_metadata_reflection, _ = soft_signal_r_and_setter(
|
|
59
|
+
NDArray[np.uint64],
|
|
60
|
+
initial_value=reflection_array,
|
|
61
|
+
)
|
|
62
|
+
self.crystal_metadata_d_spacing = epics_signal_r(float, "DSPACING:RBV")
|
|
39
63
|
super().__init__(name)
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from ophyd_async.core import Device
|
|
1
|
+
from ophyd_async.core import Device, StrictEnum
|
|
2
|
+
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
4
3
|
from ophyd_async.epics.motor import Motor
|
|
5
|
-
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
|
|
6
4
|
|
|
7
5
|
|
|
8
|
-
class ShutterState(
|
|
6
|
+
class ShutterState(StrictEnum):
|
|
9
7
|
CLOSED = "Closed"
|
|
10
8
|
OPEN = "Open"
|
|
11
9
|
|
dodal/devices/diamond_filter.py
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
from enum import Enum
|
|
2
1
|
from typing import Generic, TypeVar
|
|
3
2
|
|
|
4
|
-
from ophyd_async.core import StandardReadable
|
|
3
|
+
from ophyd_async.core import StandardReadable, StrictEnum
|
|
4
|
+
from ophyd_async.epics.core import epics_signal_rw
|
|
5
5
|
from ophyd_async.epics.motor import Motor
|
|
6
|
-
from ophyd_async.epics.signal import epics_signal_rw
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
class _Filters(
|
|
8
|
+
class _Filters(StrictEnum):
|
|
10
9
|
pass
|
|
11
10
|
|
|
12
11
|
|
dodal/devices/eiger.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# type: ignore # Eiger will soon be ophyd-async https://github.com/DiamondLightSource/dodal/issues/700
|
|
2
|
+
from dataclasses import dataclass
|
|
2
3
|
from enum import Enum
|
|
3
4
|
|
|
4
5
|
from ophyd import Component, Device, EpicsSignalRO, Signal
|
|
@@ -14,6 +15,15 @@ from dodal.log import LOGGER
|
|
|
14
15
|
FREE_RUN_MAX_IMAGES = 1000000
|
|
15
16
|
|
|
16
17
|
|
|
18
|
+
@dataclass
|
|
19
|
+
class EigerTimeouts:
|
|
20
|
+
stale_params_timeout: int = 60
|
|
21
|
+
general_status_timeout: int = 10
|
|
22
|
+
meta_file_ready_timeout: int = 30
|
|
23
|
+
all_frames_timeout: int = 120
|
|
24
|
+
arming_timeout: int = 60
|
|
25
|
+
|
|
26
|
+
|
|
17
27
|
class InternalEigerTriggerMode(Enum):
|
|
18
28
|
INTERNAL_SERIES = 0
|
|
19
29
|
INTERNAL_ENABLE = 1
|
|
@@ -21,6 +31,17 @@ class InternalEigerTriggerMode(Enum):
|
|
|
21
31
|
EXTERNAL_ENABLE = 3
|
|
22
32
|
|
|
23
33
|
|
|
34
|
+
AVAILABLE_TIMEOUTS = {
|
|
35
|
+
"i03": EigerTimeouts(
|
|
36
|
+
stale_params_timeout=60,
|
|
37
|
+
general_status_timeout=10,
|
|
38
|
+
meta_file_ready_timeout=30,
|
|
39
|
+
all_frames_timeout=120, # Long timeout for meta file to compensate for filesystem issues
|
|
40
|
+
arming_timeout=60,
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
24
45
|
class EigerDetector(Device):
|
|
25
46
|
class ArmingSignal(Signal):
|
|
26
47
|
def set(self, value, *, timeout=None, settle_time=None, **kwargs):
|
|
@@ -34,13 +55,6 @@ class EigerDetector(Device):
|
|
|
34
55
|
stale_params = Component(EpicsSignalRO, "CAM:StaleParameters_RBV")
|
|
35
56
|
bit_depth = Component(EpicsSignalRO, "CAM:BitDepthImage_RBV")
|
|
36
57
|
|
|
37
|
-
STALE_PARAMS_TIMEOUT = 60
|
|
38
|
-
GENERAL_STATUS_TIMEOUT = 10
|
|
39
|
-
# Long timeout for meta file to compensate for filesystem issues
|
|
40
|
-
META_FILE_READY_TIMEOUT = 30
|
|
41
|
-
ALL_FRAMES_TIMEOUT = 120
|
|
42
|
-
ARMING_TIMEOUT = 60
|
|
43
|
-
|
|
44
58
|
filewriters_finished: StatusBase
|
|
45
59
|
|
|
46
60
|
detector_params: DetectorParams | None = None
|
|
@@ -48,13 +62,20 @@ class EigerDetector(Device):
|
|
|
48
62
|
arming_status = Status()
|
|
49
63
|
arming_status.set_finished()
|
|
50
64
|
|
|
65
|
+
def __init__(self, beamline: str = "i03", *args, **kwargs):
|
|
66
|
+
super().__init__(*args, **kwargs)
|
|
67
|
+
self.beamline = beamline
|
|
68
|
+
# using i03 timeouts as default
|
|
69
|
+
self.timeouts = AVAILABLE_TIMEOUTS.get(beamline, AVAILABLE_TIMEOUTS["i03"])
|
|
70
|
+
|
|
51
71
|
@classmethod
|
|
52
72
|
def with_params(
|
|
53
73
|
cls,
|
|
54
74
|
params: DetectorParams,
|
|
55
75
|
name: str = "EigerDetector",
|
|
76
|
+
beamline: str = "i03",
|
|
56
77
|
):
|
|
57
|
-
det = cls(name=name)
|
|
78
|
+
det = cls(name=name, beamline=beamline)
|
|
58
79
|
det.set_detector_parameters(params)
|
|
59
80
|
return det
|
|
60
81
|
|
|
@@ -82,7 +103,7 @@ class EigerDetector(Device):
|
|
|
82
103
|
def async_stage(self):
|
|
83
104
|
self.odin.nodes.clear_odin_errors()
|
|
84
105
|
status_ok, error_message = self.odin.wait_for_odin_initialised(
|
|
85
|
-
self.
|
|
106
|
+
self.timeouts.general_status_timeout
|
|
86
107
|
)
|
|
87
108
|
if not status_ok:
|
|
88
109
|
raise Exception(f"Odin not initialised: {error_message}")
|
|
@@ -96,14 +117,14 @@ class EigerDetector(Device):
|
|
|
96
117
|
def wait_on_arming_if_started(self):
|
|
97
118
|
if not self.arming_status.done:
|
|
98
119
|
LOGGER.info("Waiting for arming to finish")
|
|
99
|
-
self.arming_status.wait(self.
|
|
120
|
+
self.arming_status.wait(self.timeouts.arming_timeout)
|
|
100
121
|
|
|
101
122
|
def stage(self):
|
|
102
123
|
self.wait_on_arming_if_started()
|
|
103
124
|
if not self.is_armed():
|
|
104
125
|
LOGGER.info("Eiger not armed, arming")
|
|
105
126
|
|
|
106
|
-
self.async_stage().wait(timeout=self.
|
|
127
|
+
self.async_stage().wait(timeout=self.timeouts.arming_timeout)
|
|
107
128
|
|
|
108
129
|
def stop_odin_when_all_frames_collected(self):
|
|
109
130
|
LOGGER.info("Waiting on all frames")
|
|
@@ -111,7 +132,7 @@ class EigerDetector(Device):
|
|
|
111
132
|
await_value(
|
|
112
133
|
self.odin.file_writer.num_captured,
|
|
113
134
|
self.detector_params.full_number_of_images,
|
|
114
|
-
).wait(self.
|
|
135
|
+
).wait(self.timeouts.all_frames_timeout)
|
|
115
136
|
finally:
|
|
116
137
|
LOGGER.info("Stopping Odin")
|
|
117
138
|
self.odin.stop().wait(5)
|
|
@@ -124,7 +145,9 @@ class EigerDetector(Device):
|
|
|
124
145
|
# In free run mode we have to manually stop odin
|
|
125
146
|
self.stop_odin_when_all_frames_collected()
|
|
126
147
|
|
|
127
|
-
self.odin.file_writer.start_timeout.set(1).wait(
|
|
148
|
+
self.odin.file_writer.start_timeout.set(1).wait(
|
|
149
|
+
self.timeouts.general_status_timeout
|
|
150
|
+
)
|
|
128
151
|
LOGGER.info("Waiting on filewriter to finish")
|
|
129
152
|
self.filewriters_finished.wait(30)
|
|
130
153
|
|
|
@@ -132,7 +155,7 @@ class EigerDetector(Device):
|
|
|
132
155
|
finally:
|
|
133
156
|
self.disarm_detector()
|
|
134
157
|
status_ok = self.odin.check_and_wait_for_odin_state(
|
|
135
|
-
self.
|
|
158
|
+
self.timeouts.general_status_timeout
|
|
136
159
|
)
|
|
137
160
|
self.disable_roi_mode()
|
|
138
161
|
return status_ok
|
|
@@ -142,10 +165,12 @@ class EigerDetector(Device):
|
|
|
142
165
|
LOGGER.info("Eiger stop() called - cleaning up...")
|
|
143
166
|
self.wait_on_arming_if_started()
|
|
144
167
|
stop_status = self.odin.stop()
|
|
145
|
-
self.odin.file_writer.start_timeout.set(1).wait(
|
|
168
|
+
self.odin.file_writer.start_timeout.set(1).wait(
|
|
169
|
+
self.timeouts.general_status_timeout
|
|
170
|
+
)
|
|
146
171
|
self.disarm_detector()
|
|
147
172
|
stop_status &= self.disable_roi_mode()
|
|
148
|
-
stop_status.wait(self.
|
|
173
|
+
stop_status.wait(self.timeouts.general_status_timeout)
|
|
149
174
|
# See https://github.com/DiamondLightSource/hyperion/issues/1395
|
|
150
175
|
LOGGER.info("Turning off Eiger dev/shm streaming")
|
|
151
176
|
self.odin.fan.dev_shm_enable.set(0).wait()
|
|
@@ -166,19 +191,19 @@ class EigerDetector(Device):
|
|
|
166
191
|
)
|
|
167
192
|
|
|
168
193
|
status = self.cam.roi_mode.set(
|
|
169
|
-
1 if enable else 0, timeout=self.
|
|
194
|
+
1 if enable else 0, timeout=self.timeouts.general_status_timeout
|
|
170
195
|
)
|
|
171
196
|
status &= self.odin.file_writer.image_height.set(
|
|
172
|
-
detector_dimensions.height, timeout=self.
|
|
197
|
+
detector_dimensions.height, timeout=self.timeouts.general_status_timeout
|
|
173
198
|
)
|
|
174
199
|
status &= self.odin.file_writer.image_width.set(
|
|
175
|
-
detector_dimensions.width, timeout=self.
|
|
200
|
+
detector_dimensions.width, timeout=self.timeouts.general_status_timeout
|
|
176
201
|
)
|
|
177
202
|
status &= self.odin.file_writer.num_row_chunks.set(
|
|
178
|
-
detector_dimensions.height, timeout=self.
|
|
203
|
+
detector_dimensions.height, timeout=self.timeouts.general_status_timeout
|
|
179
204
|
)
|
|
180
205
|
status &= self.odin.file_writer.num_col_chunks.set(
|
|
181
|
-
detector_dimensions.width, timeout=self.
|
|
206
|
+
detector_dimensions.width, timeout=self.timeouts.general_status_timeout
|
|
182
207
|
)
|
|
183
208
|
|
|
184
209
|
return status
|
|
@@ -186,25 +211,29 @@ class EigerDetector(Device):
|
|
|
186
211
|
def set_cam_pvs(self) -> AndStatus:
|
|
187
212
|
assert self.detector_params is not None
|
|
188
213
|
status = self.cam.acquire_time.set(
|
|
189
|
-
self.detector_params.exposure_time,
|
|
214
|
+
self.detector_params.exposure_time,
|
|
215
|
+
timeout=self.timeouts.general_status_timeout,
|
|
190
216
|
)
|
|
191
217
|
status &= self.cam.acquire_period.set(
|
|
192
|
-
self.detector_params.exposure_time,
|
|
218
|
+
self.detector_params.exposure_time,
|
|
219
|
+
timeout=self.timeouts.general_status_timeout,
|
|
220
|
+
)
|
|
221
|
+
status &= self.cam.num_exposures.set(
|
|
222
|
+
1, timeout=self.timeouts.general_status_timeout
|
|
193
223
|
)
|
|
194
|
-
status &= self.cam.num_exposures.set(1, timeout=self.GENERAL_STATUS_TIMEOUT)
|
|
195
224
|
status &= self.cam.image_mode.set(
|
|
196
|
-
self.cam.ImageMode.MULTIPLE, timeout=self.
|
|
225
|
+
self.cam.ImageMode.MULTIPLE, timeout=self.timeouts.general_status_timeout
|
|
197
226
|
)
|
|
198
227
|
status &= self.cam.trigger_mode.set(
|
|
199
228
|
InternalEigerTriggerMode.EXTERNAL_SERIES.value,
|
|
200
|
-
timeout=self.
|
|
229
|
+
timeout=self.timeouts.general_status_timeout,
|
|
201
230
|
)
|
|
202
231
|
return status
|
|
203
232
|
|
|
204
233
|
def set_odin_number_of_frame_chunks(self) -> Status:
|
|
205
234
|
assert self.detector_params is not None
|
|
206
235
|
status = self.odin.file_writer.num_frames_chunks.set(
|
|
207
|
-
1, timeout=self.
|
|
236
|
+
1, timeout=self.timeouts.general_status_timeout
|
|
208
237
|
)
|
|
209
238
|
return status
|
|
210
239
|
|
|
@@ -212,16 +241,20 @@ class EigerDetector(Device):
|
|
|
212
241
|
assert self.detector_params is not None
|
|
213
242
|
file_prefix = self.detector_params.full_filename
|
|
214
243
|
status = self.odin.file_writer.file_path.set(
|
|
215
|
-
self.detector_params.directory, timeout=self.
|
|
244
|
+
self.detector_params.directory, timeout=self.timeouts.general_status_timeout
|
|
216
245
|
)
|
|
217
246
|
status &= self.odin.file_writer.file_name.set(
|
|
218
|
-
file_prefix, timeout=self.
|
|
247
|
+
file_prefix, timeout=self.timeouts.general_status_timeout
|
|
219
248
|
)
|
|
220
249
|
status &= await_value(
|
|
221
|
-
self.odin.meta.file_name,
|
|
250
|
+
self.odin.meta.file_name,
|
|
251
|
+
file_prefix,
|
|
252
|
+
timeout=self.timeouts.general_status_timeout,
|
|
222
253
|
)
|
|
223
254
|
status &= await_value(
|
|
224
|
-
self.odin.file_writer.id,
|
|
255
|
+
self.odin.file_writer.id,
|
|
256
|
+
file_prefix,
|
|
257
|
+
timeout=self.timeouts.general_status_timeout,
|
|
225
258
|
)
|
|
226
259
|
return status
|
|
227
260
|
|
|
@@ -231,19 +264,22 @@ class EigerDetector(Device):
|
|
|
231
264
|
self.detector_params.detector_distance
|
|
232
265
|
)
|
|
233
266
|
status = self.cam.beam_center_x.set(
|
|
234
|
-
beam_x_pixels, timeout=self.
|
|
267
|
+
beam_x_pixels, timeout=self.timeouts.general_status_timeout
|
|
235
268
|
)
|
|
236
269
|
status &= self.cam.beam_center_y.set(
|
|
237
|
-
beam_y_pixels, timeout=self.
|
|
270
|
+
beam_y_pixels, timeout=self.timeouts.general_status_timeout
|
|
238
271
|
)
|
|
239
272
|
status &= self.cam.det_distance.set(
|
|
240
|
-
self.detector_params.detector_distance,
|
|
273
|
+
self.detector_params.detector_distance,
|
|
274
|
+
timeout=self.timeouts.general_status_timeout,
|
|
241
275
|
)
|
|
242
276
|
status &= self.cam.omega_start.set(
|
|
243
|
-
self.detector_params.omega_start,
|
|
277
|
+
self.detector_params.omega_start,
|
|
278
|
+
timeout=self.timeouts.general_status_timeout,
|
|
244
279
|
)
|
|
245
280
|
status &= self.cam.omega_incr.set(
|
|
246
|
-
self.detector_params.omega_increment,
|
|
281
|
+
self.detector_params.omega_increment,
|
|
282
|
+
timeout=self.timeouts.general_status_timeout,
|
|
247
283
|
)
|
|
248
284
|
return status
|
|
249
285
|
|
|
@@ -259,7 +295,7 @@ class EigerDetector(Device):
|
|
|
259
295
|
current_energy = self.cam.photon_energy.get()
|
|
260
296
|
if abs(current_energy - energy) > tolerance:
|
|
261
297
|
return self.cam.photon_energy.set(
|
|
262
|
-
energy, timeout=self.
|
|
298
|
+
energy, timeout=self.timeouts.general_status_timeout
|
|
263
299
|
)
|
|
264
300
|
else:
|
|
265
301
|
status = Status()
|
|
@@ -275,45 +311,46 @@ class EigerDetector(Device):
|
|
|
275
311
|
assert self.detector_params is not None
|
|
276
312
|
status = self.cam.num_images.set(
|
|
277
313
|
self.detector_params.num_images_per_trigger,
|
|
278
|
-
timeout=self.
|
|
314
|
+
timeout=self.timeouts.general_status_timeout,
|
|
279
315
|
)
|
|
280
316
|
if self.detector_params.trigger_mode == TriggerMode.FREE_RUN:
|
|
281
317
|
# The Eiger can't actually free run so we set a very large number of frames
|
|
282
318
|
status &= self.cam.num_triggers.set(
|
|
283
|
-
FREE_RUN_MAX_IMAGES, timeout=self.
|
|
319
|
+
FREE_RUN_MAX_IMAGES, timeout=self.timeouts.general_status_timeout
|
|
284
320
|
)
|
|
285
321
|
# Setting Odin to write 0 frames tells it to write until externally stopped
|
|
286
322
|
status &= self.odin.file_writer.num_capture.set(
|
|
287
|
-
0, timeout=self.
|
|
323
|
+
0, timeout=self.timeouts.general_status_timeout
|
|
288
324
|
)
|
|
289
325
|
elif self.detector_params.trigger_mode == TriggerMode.SET_FRAMES:
|
|
290
326
|
status &= self.cam.num_triggers.set(
|
|
291
|
-
self.detector_params.num_triggers,
|
|
327
|
+
self.detector_params.num_triggers,
|
|
328
|
+
timeout=self.timeouts.general_status_timeout,
|
|
292
329
|
)
|
|
293
330
|
status &= self.odin.file_writer.num_capture.set(
|
|
294
331
|
self.detector_params.full_number_of_images,
|
|
295
|
-
timeout=self.
|
|
332
|
+
timeout=self.timeouts.general_status_timeout,
|
|
296
333
|
)
|
|
297
334
|
|
|
298
335
|
return status
|
|
299
336
|
|
|
300
337
|
def _wait_for_odin_status(self) -> StatusBase:
|
|
301
338
|
self.forward_bit_depth_to_filewriter()
|
|
302
|
-
await_value(self.odin.meta.active, 1).wait(self.
|
|
339
|
+
await_value(self.odin.meta.active, 1).wait(self.timeouts.general_status_timeout)
|
|
303
340
|
|
|
304
341
|
status = self.odin.file_writer.capture.set(
|
|
305
|
-
1, timeout=self.
|
|
342
|
+
1, timeout=self.timeouts.general_status_timeout
|
|
306
343
|
)
|
|
307
344
|
LOGGER.info("Eiger staging: awaiting odin metadata")
|
|
308
345
|
status &= await_value(
|
|
309
|
-
self.odin.meta.ready, 1, timeout=self.
|
|
346
|
+
self.odin.meta.ready, 1, timeout=self.timeouts.meta_file_ready_timeout
|
|
310
347
|
)
|
|
311
348
|
return status
|
|
312
349
|
|
|
313
350
|
def _wait_fan_ready(self) -> StatusBase:
|
|
314
351
|
self.filewriters_finished = self.odin.create_finished_status()
|
|
315
352
|
LOGGER.info("Eiger staging: awaiting odin fan ready")
|
|
316
|
-
return await_value(self.odin.fan.ready, 1, self.
|
|
353
|
+
return await_value(self.odin.fan.ready, 1, self.timeouts.general_status_timeout)
|
|
317
354
|
|
|
318
355
|
def _finish_arm(self) -> Status:
|
|
319
356
|
LOGGER.info("Eiger staging: Finishing arming")
|
|
@@ -324,7 +361,7 @@ class EigerDetector(Device):
|
|
|
324
361
|
def forward_bit_depth_to_filewriter(self):
|
|
325
362
|
bit_depth = self.bit_depth.get()
|
|
326
363
|
self.odin.file_writer.data_type.set(f"UInt{bit_depth}").wait(
|
|
327
|
-
self.
|
|
364
|
+
self.timeouts.general_status_timeout
|
|
328
365
|
)
|
|
329
366
|
|
|
330
367
|
def change_dev_shm(self, enable_dev_shm: bool):
|
|
@@ -332,7 +369,7 @@ class EigerDetector(Device):
|
|
|
332
369
|
return self.odin.fan.dev_shm_enable.set(1 if enable_dev_shm else 0)
|
|
333
370
|
|
|
334
371
|
def disarm_detector(self):
|
|
335
|
-
self.cam.acquire.set(0).wait(self.
|
|
372
|
+
self.cam.acquire.set(0).wait(self.timeouts.general_status_timeout)
|
|
336
373
|
|
|
337
374
|
def do_arming_chain(self) -> Status:
|
|
338
375
|
functions_to_do_arm = []
|
|
@@ -355,7 +392,9 @@ class EigerDetector(Device):
|
|
|
355
392
|
self.set_num_triggers_and_captures,
|
|
356
393
|
lambda: await_value(self.stale_params, 0, 60),
|
|
357
394
|
self._wait_for_odin_status,
|
|
358
|
-
lambda: self.cam.acquire.set(
|
|
395
|
+
lambda: self.cam.acquire.set(
|
|
396
|
+
1, timeout=self.timeouts.general_status_timeout
|
|
397
|
+
),
|
|
359
398
|
self._wait_fan_ready,
|
|
360
399
|
self._finish_arm,
|
|
361
400
|
]
|
dodal/devices/fast_grid_scan.py
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
from
|
|
1
|
+
from ophyd_async.core import StandardReadable, StrictEnum
|
|
2
|
+
from ophyd_async.epics.core import epics_signal_r
|
|
2
3
|
|
|
3
|
-
from ophyd_async.core import StandardReadable
|
|
4
|
-
from ophyd_async.epics.signal import epics_signal_r
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
IN = 1
|
|
5
|
+
class FluorescenceDetectorControlState(StrictEnum):
|
|
6
|
+
OUT = "Out"
|
|
7
|
+
IN = "In"
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
class FluorescenceDetector(StandardReadable):
|