dls-dodal 1.32.0__py3-none-any.whl → 1.34.1__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.32.0.dist-info → dls_dodal-1.34.1.dist-info}/METADATA +3 -3
- {dls_dodal-1.32.0.dist-info → dls_dodal-1.34.1.dist-info}/RECORD +53 -43
- {dls_dodal-1.32.0.dist-info → dls_dodal-1.34.1.dist-info}/WHEEL +1 -1
- dodal/__init__.py +8 -0
- dodal/_version.py +2 -2
- dodal/beamline_specific_utils/i03.py +6 -2
- dodal/beamlines/__init__.py +2 -3
- dodal/beamlines/b01_1.py +77 -0
- dodal/beamlines/i03.py +41 -9
- dodal/beamlines/i04.py +26 -4
- dodal/beamlines/i10.py +257 -0
- dodal/beamlines/i22.py +1 -2
- dodal/beamlines/i24.py +7 -7
- dodal/beamlines/p38.py +1 -2
- dodal/common/signal_utils.py +53 -0
- dodal/common/types.py +2 -7
- dodal/devices/aperturescatterguard.py +12 -15
- dodal/devices/apple2_undulator.py +602 -0
- dodal/devices/areadetector/plugins/CAM.py +31 -0
- dodal/devices/areadetector/plugins/MJPG.py +51 -106
- dodal/devices/backlight.py +7 -6
- dodal/devices/diamond_filter.py +47 -0
- dodal/devices/eiger.py +6 -2
- dodal/devices/eiger_odin.py +48 -39
- dodal/devices/focusing_mirror.py +14 -8
- dodal/devices/i10/i10_apple2.py +398 -0
- dodal/devices/i10/i10_setting_data.py +7 -0
- dodal/devices/i22/dcm.py +7 -8
- dodal/devices/i24/dual_backlight.py +5 -5
- dodal/devices/oav/oav_calculations.py +22 -0
- dodal/devices/oav/oav_detector.py +118 -97
- dodal/devices/oav/oav_parameters.py +50 -104
- dodal/devices/oav/oav_to_redis_forwarder.py +75 -34
- dodal/devices/oav/{grid_overlay.py → snapshots/grid_overlay.py} +0 -43
- dodal/devices/oav/snapshots/snapshot_with_beam_centre.py +64 -0
- dodal/devices/oav/snapshots/snapshot_with_grid.py +57 -0
- dodal/devices/oav/utils.py +26 -25
- dodal/devices/pgm.py +41 -0
- dodal/devices/qbpm.py +18 -0
- dodal/devices/robot.py +2 -2
- dodal/devices/smargon.py +2 -2
- dodal/devices/tetramm.py +2 -2
- dodal/devices/undulator.py +2 -1
- dodal/devices/util/adjuster_plans.py +1 -1
- dodal/devices/util/lookup_tables.py +4 -5
- dodal/devices/zebra.py +5 -2
- dodal/devices/zocalo/zocalo_results.py +13 -10
- dodal/plans/data_session_metadata.py +2 -2
- dodal/plans/motor_util_plans.py +11 -9
- dodal/utils.py +7 -0
- dodal/beamlines/i04_1.py +0 -140
- dodal/devices/oav/oav_errors.py +0 -35
- {dls_dodal-1.32.0.dist-info → dls_dodal-1.34.1.dist-info}/LICENSE +0 -0
- {dls_dodal-1.32.0.dist-info → dls_dodal-1.34.1.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.32.0.dist-info → dls_dodal-1.34.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from ophyd_async.core import SignalR
|
|
2
|
+
from PIL import Image, ImageDraw
|
|
3
|
+
|
|
4
|
+
from dodal.devices.areadetector.plugins.MJPG import MJPG
|
|
5
|
+
|
|
6
|
+
CROSSHAIR_LENGTH_PX = 20
|
|
7
|
+
CROSSHAIR_OUTLINE_COLOUR = "Black"
|
|
8
|
+
CROSSHAIR_FILL_COLOUR = "White"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def draw_crosshair(image: Image.Image, beam_x: int, beam_y: int):
|
|
12
|
+
draw = ImageDraw.Draw(image)
|
|
13
|
+
OUTLINE_WIDTH = 1
|
|
14
|
+
HALF_LEN = CROSSHAIR_LENGTH_PX / 2
|
|
15
|
+
draw.rectangle(
|
|
16
|
+
[
|
|
17
|
+
beam_x - OUTLINE_WIDTH,
|
|
18
|
+
beam_y - HALF_LEN - OUTLINE_WIDTH,
|
|
19
|
+
beam_x + OUTLINE_WIDTH,
|
|
20
|
+
beam_y + HALF_LEN + OUTLINE_WIDTH,
|
|
21
|
+
],
|
|
22
|
+
fill=CROSSHAIR_OUTLINE_COLOUR,
|
|
23
|
+
)
|
|
24
|
+
draw.rectangle(
|
|
25
|
+
[
|
|
26
|
+
beam_x - HALF_LEN - OUTLINE_WIDTH,
|
|
27
|
+
beam_y - OUTLINE_WIDTH,
|
|
28
|
+
beam_x + HALF_LEN + OUTLINE_WIDTH,
|
|
29
|
+
beam_y + OUTLINE_WIDTH,
|
|
30
|
+
],
|
|
31
|
+
fill=CROSSHAIR_OUTLINE_COLOUR,
|
|
32
|
+
)
|
|
33
|
+
draw.line(
|
|
34
|
+
((beam_x, beam_y - HALF_LEN), (beam_x, beam_y + HALF_LEN)),
|
|
35
|
+
fill=CROSSHAIR_FILL_COLOUR,
|
|
36
|
+
)
|
|
37
|
+
draw.line(
|
|
38
|
+
((beam_x - HALF_LEN, beam_y), (beam_x + HALF_LEN, beam_y)),
|
|
39
|
+
fill=CROSSHAIR_FILL_COLOUR,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class SnapshotWithBeamCentre(MJPG):
|
|
44
|
+
"""A child of MJPG which, when triggered, draws an outlined crosshair at the beam
|
|
45
|
+
centre in the image and saves the image to disk."""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
prefix: str,
|
|
50
|
+
beam_x_signal: SignalR,
|
|
51
|
+
beam_y_signal: SignalR,
|
|
52
|
+
name: str = "",
|
|
53
|
+
) -> None:
|
|
54
|
+
with self.add_children_as_readables():
|
|
55
|
+
self.beam_centre_i = beam_x_signal
|
|
56
|
+
self.beam_centre_j = beam_y_signal
|
|
57
|
+
super().__init__(prefix, name)
|
|
58
|
+
|
|
59
|
+
async def post_processing(self, image: Image.Image):
|
|
60
|
+
beam_x = await self.beam_centre_i.get_value()
|
|
61
|
+
beam_y = await self.beam_centre_j.get_value()
|
|
62
|
+
draw_crosshair(image, beam_x, beam_y)
|
|
63
|
+
|
|
64
|
+
await self._save_image(image)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from os.path import join as path_join
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import soft_signal_rw
|
|
4
|
+
from PIL.Image import Image
|
|
5
|
+
|
|
6
|
+
from dodal.devices.areadetector.plugins.MJPG import IMG_FORMAT, MJPG, asyncio_save_image
|
|
7
|
+
from dodal.devices.oav.snapshots.grid_overlay import (
|
|
8
|
+
add_grid_border_overlay_to_image,
|
|
9
|
+
add_grid_overlay_to_image,
|
|
10
|
+
)
|
|
11
|
+
from dodal.log import LOGGER
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SnapshotWithGrid(MJPG):
|
|
15
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
16
|
+
with self.add_children_as_readables():
|
|
17
|
+
self.top_left_x = soft_signal_rw(int)
|
|
18
|
+
self.top_left_y = soft_signal_rw(int)
|
|
19
|
+
self.box_width = soft_signal_rw(int)
|
|
20
|
+
self.num_boxes_x = soft_signal_rw(int)
|
|
21
|
+
self.num_boxes_y = soft_signal_rw(int)
|
|
22
|
+
|
|
23
|
+
self.last_path_outer = soft_signal_rw(str)
|
|
24
|
+
self.last_path_full_overlay = soft_signal_rw(str)
|
|
25
|
+
|
|
26
|
+
super().__init__(prefix, name)
|
|
27
|
+
|
|
28
|
+
async def post_processing(self, image: Image):
|
|
29
|
+
# Save an unmodified image with no suffix
|
|
30
|
+
await self._save_image(image)
|
|
31
|
+
|
|
32
|
+
top_left_x = await self.top_left_x.get_value()
|
|
33
|
+
top_left_y = await self.top_left_y.get_value()
|
|
34
|
+
box_witdh = await self.box_width.get_value()
|
|
35
|
+
num_boxes_x = await self.num_boxes_x.get_value()
|
|
36
|
+
num_boxes_y = await self.num_boxes_y.get_value()
|
|
37
|
+
|
|
38
|
+
assert isinstance(filename_str := await self.filename.get_value(), str)
|
|
39
|
+
assert isinstance(directory_str := await self.directory.get_value(), str)
|
|
40
|
+
|
|
41
|
+
add_grid_border_overlay_to_image(
|
|
42
|
+
image, top_left_x, top_left_y, box_witdh, num_boxes_x, num_boxes_y
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
path = path_join(directory_str, f"{filename_str}_outer_overlay.{IMG_FORMAT}")
|
|
46
|
+
await self.last_path_outer.set(path, wait=True)
|
|
47
|
+
LOGGER.info(f"Saving grid outer edge at {path}")
|
|
48
|
+
await asyncio_save_image(image, path)
|
|
49
|
+
|
|
50
|
+
add_grid_overlay_to_image(
|
|
51
|
+
image, top_left_x, top_left_y, box_witdh, num_boxes_x, num_boxes_y
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
path = path_join(directory_str, f"{filename_str}_grid_overlay.{IMG_FORMAT}")
|
|
55
|
+
await self.last_path_full_overlay.set(path, wait=True)
|
|
56
|
+
LOGGER.info(f"Saving full grid overlay at {path}")
|
|
57
|
+
await asyncio_save_image(image, path)
|
dodal/devices/oav/utils.py
CHANGED
|
@@ -5,8 +5,11 @@ import bluesky.plan_stubs as bps
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
from bluesky.utils import Msg
|
|
7
7
|
|
|
8
|
-
from dodal.devices.oav.oav_calculations import
|
|
9
|
-
|
|
8
|
+
from dodal.devices.oav.oav_calculations import (
|
|
9
|
+
calculate_beam_distance,
|
|
10
|
+
camera_coordinates_to_xyz,
|
|
11
|
+
)
|
|
12
|
+
from dodal.devices.oav.oav_detector import OAV
|
|
10
13
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
11
14
|
from dodal.devices.smargon import Smargon
|
|
12
15
|
|
|
@@ -36,21 +39,6 @@ def bottom_right_from_top_left(
|
|
|
36
39
|
)
|
|
37
40
|
|
|
38
41
|
|
|
39
|
-
class ColorMode(IntEnum):
|
|
40
|
-
"""
|
|
41
|
-
Enum to store the various color modes of the camera. We use RGB1.
|
|
42
|
-
"""
|
|
43
|
-
|
|
44
|
-
MONO = 0
|
|
45
|
-
BAYER = 1
|
|
46
|
-
RGB1 = 2
|
|
47
|
-
RGB2 = 3
|
|
48
|
-
RGB3 = 4
|
|
49
|
-
YUV444 = 5
|
|
50
|
-
YUV422 = 6
|
|
51
|
-
YUV421 = 7
|
|
52
|
-
|
|
53
|
-
|
|
54
42
|
class EdgeOutputArrayImageType(IntEnum):
|
|
55
43
|
"""
|
|
56
44
|
Enum to store the types of image to tweak the output array. We use Original.
|
|
@@ -64,7 +52,7 @@ class EdgeOutputArrayImageType(IntEnum):
|
|
|
64
52
|
|
|
65
53
|
|
|
66
54
|
def get_move_required_so_that_beam_is_at_pixel(
|
|
67
|
-
smargon: Smargon, pixel: Pixel,
|
|
55
|
+
smargon: Smargon, pixel: Pixel, oav: OAV
|
|
68
56
|
) -> Generator[Msg, None, np.ndarray]:
|
|
69
57
|
"""Calculate the required move so that the given pixel is in the centre of the beam."""
|
|
70
58
|
|
|
@@ -78,22 +66,35 @@ def get_move_required_so_that_beam_is_at_pixel(
|
|
|
78
66
|
)
|
|
79
67
|
current_angle = yield from bps.rd(smargon.omega)
|
|
80
68
|
|
|
81
|
-
|
|
69
|
+
beam_x = yield from bps.rd(oav.beam_centre_i)
|
|
70
|
+
beam_y = yield from bps.rd(oav.beam_centre_j)
|
|
71
|
+
microns_per_pixel_x = yield from bps.rd(oav.microns_per_pixel_x)
|
|
72
|
+
microns_per_pixel_y = yield from bps.rd(oav.microns_per_pixel_y)
|
|
73
|
+
|
|
74
|
+
return calculate_x_y_z_of_pixel(
|
|
75
|
+
current_motor_xyz,
|
|
76
|
+
current_angle,
|
|
77
|
+
pixel,
|
|
78
|
+
(beam_x, beam_y),
|
|
79
|
+
(microns_per_pixel_x, microns_per_pixel_y),
|
|
80
|
+
)
|
|
82
81
|
|
|
83
82
|
|
|
84
83
|
def calculate_x_y_z_of_pixel(
|
|
85
|
-
current_x_y_z,
|
|
84
|
+
current_x_y_z,
|
|
85
|
+
current_omega,
|
|
86
|
+
pixel: Pixel,
|
|
87
|
+
beam_centre: tuple[int, int],
|
|
88
|
+
microns_per_pixel: tuple[float, float],
|
|
86
89
|
) -> np.ndarray:
|
|
87
|
-
beam_distance_px: Pixel =
|
|
90
|
+
beam_distance_px: Pixel = calculate_beam_distance(beam_centre, *pixel)
|
|
88
91
|
|
|
89
|
-
assert oav_params.micronsPerXPixel
|
|
90
|
-
assert oav_params.micronsPerYPixel
|
|
91
92
|
return current_x_y_z + camera_coordinates_to_xyz(
|
|
92
93
|
beam_distance_px[0],
|
|
93
94
|
beam_distance_px[1],
|
|
94
95
|
current_omega,
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
microns_per_pixel[0],
|
|
97
|
+
microns_per_pixel[1],
|
|
97
98
|
)
|
|
98
99
|
|
|
99
100
|
|
dodal/devices/pgm.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import (
|
|
4
|
+
ConfigSignal,
|
|
5
|
+
StandardReadable,
|
|
6
|
+
)
|
|
7
|
+
from ophyd_async.epics.motor import Motor
|
|
8
|
+
from ophyd_async.epics.signal import epics_signal_rw
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PGM(StandardReadable):
|
|
12
|
+
"""
|
|
13
|
+
Plane grating monochromator, it is use in soft x-ray beamline to generate monochromic beam.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
prefix: str,
|
|
19
|
+
grating: type[Enum],
|
|
20
|
+
gratingPv: str,
|
|
21
|
+
name: str = "",
|
|
22
|
+
) -> None:
|
|
23
|
+
"""
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
prefix:
|
|
27
|
+
Beamline specific part of the PV
|
|
28
|
+
grating:
|
|
29
|
+
The Enum for the grating table.
|
|
30
|
+
gratingPv:
|
|
31
|
+
The suffix pv part of grating Pv
|
|
32
|
+
name:
|
|
33
|
+
Name of the device
|
|
34
|
+
"""
|
|
35
|
+
with self.add_children_as_readables():
|
|
36
|
+
self.energy = Motor(prefix + "ENERGY")
|
|
37
|
+
with self.add_children_as_readables(ConfigSignal):
|
|
38
|
+
self.grating = epics_signal_rw(grating, prefix + gratingPv)
|
|
39
|
+
self.cff = epics_signal_rw(float, prefix + "CFF")
|
|
40
|
+
|
|
41
|
+
super().__init__(name=name)
|
dodal/devices/qbpm.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from ophyd_async.core import StandardReadable
|
|
2
|
+
from ophyd_async.epics.signal import epics_signal_r
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class QBPM(StandardReadable):
|
|
6
|
+
"""
|
|
7
|
+
A beam position monitor that gives a position and intensity of the beam.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
prefix: str,
|
|
13
|
+
name: str = "",
|
|
14
|
+
) -> None:
|
|
15
|
+
with self.add_children_as_readables():
|
|
16
|
+
self.intensity_uA = epics_signal_r(float, f"{prefix}INTEN")
|
|
17
|
+
|
|
18
|
+
super().__init__(name)
|
dodal/devices/robot.py
CHANGED
|
@@ -63,8 +63,8 @@ class BartRobot(StandardReadable, Movable):
|
|
|
63
63
|
self.current_puck = epics_signal_r(float, prefix + "CURRENT_PUCK_RBV")
|
|
64
64
|
self.current_pin = epics_signal_r(float, prefix + "CURRENT_PIN_RBV")
|
|
65
65
|
|
|
66
|
-
self.next_sample_id = epics_signal_rw_rbv(
|
|
67
|
-
self.sample_id = epics_signal_r(
|
|
66
|
+
self.next_sample_id = epics_signal_rw_rbv(int, prefix + "NEXT_ID")
|
|
67
|
+
self.sample_id = epics_signal_r(int, prefix + "CURRENT_ID_RBV")
|
|
68
68
|
|
|
69
69
|
self.load = epics_signal_x(prefix + "LOAD.PROC")
|
|
70
70
|
self.program_running = epics_signal_r(bool, prefix + "PROGRAM_RUNNING")
|
dodal/devices/smargon.py
CHANGED
|
@@ -40,8 +40,8 @@ class StubOffsets(Device):
|
|
|
40
40
|
super().__init__(name)
|
|
41
41
|
|
|
42
42
|
@AsyncStatus.wrap
|
|
43
|
-
async def set(self,
|
|
44
|
-
if
|
|
43
|
+
async def set(self, value: StubPosition):
|
|
44
|
+
if value == StubPosition.CURRENT_AS_CENTER:
|
|
45
45
|
await self.center_at_current_position.set(1)
|
|
46
46
|
smargon = cast(Smargon, self.parent)
|
|
47
47
|
await wait_for_value(
|
dodal/devices/tetramm.py
CHANGED
|
@@ -4,7 +4,7 @@ from enum import Enum
|
|
|
4
4
|
from bluesky.protocols import Hints
|
|
5
5
|
from ophyd_async.core import (
|
|
6
6
|
DatasetDescriber,
|
|
7
|
-
|
|
7
|
+
DetectorController,
|
|
8
8
|
DetectorTrigger,
|
|
9
9
|
Device,
|
|
10
10
|
PathProvider,
|
|
@@ -80,7 +80,7 @@ class TetrammDriver(Device):
|
|
|
80
80
|
super().__init__(name=name)
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
class TetrammController(
|
|
83
|
+
class TetrammController(DetectorController):
|
|
84
84
|
"""Controller for a TetrAMM current monitor
|
|
85
85
|
|
|
86
86
|
Attributes:
|
dodal/devices/undulator.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from enum import Enum
|
|
2
3
|
|
|
3
4
|
import numpy as np
|
|
@@ -54,7 +55,7 @@ class Undulator(StandardReadable, Movable):
|
|
|
54
55
|
def __init__(
|
|
55
56
|
self,
|
|
56
57
|
prefix: str,
|
|
57
|
-
id_gap_lookup_table_path: str,
|
|
58
|
+
id_gap_lookup_table_path: str = os.devnull,
|
|
58
59
|
name: str = "",
|
|
59
60
|
poles: int | None = None,
|
|
60
61
|
length: float | None = None,
|
|
@@ -21,6 +21,6 @@ def lookup_table_adjuster(
|
|
|
21
21
|
def adjust(group=None) -> Generator[Msg, None, None]:
|
|
22
22
|
setpoint = lookup_table(input)
|
|
23
23
|
LOGGER.info(f"lookup_table_adjuster setting {output_device.name} to {setpoint}")
|
|
24
|
-
yield from bps.abs_set(output_device, setpoint, group=group)
|
|
24
|
+
yield from bps.abs_set(output_device, setpoint, group=group) # type: ignore # See: https://github.com/DiamondLightSource/dodal/issues/827
|
|
25
25
|
|
|
26
26
|
return adjust
|
|
@@ -33,7 +33,10 @@ async def energy_distance_table(lookup_table_path: str) -> np.ndarray:
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
def linear_interpolation_lut(filename: str) -> Callable[[float], float]:
|
|
36
|
-
"""Returns a callable that converts values by linear interpolation of lookup table
|
|
36
|
+
"""Returns a callable that converts values by linear interpolation of lookup table
|
|
37
|
+
values.
|
|
38
|
+
|
|
39
|
+
If the value falls outside the lookup table then the closest value will be used."""
|
|
37
40
|
LOGGER.info(f"Using lookup table {filename}")
|
|
38
41
|
s_and_t_vals = zip(*loadtxt(filename, comments=["#", "Units"]), strict=False)
|
|
39
42
|
|
|
@@ -54,10 +57,6 @@ def linear_interpolation_lut(filename: str) -> Callable[[float], float]:
|
|
|
54
57
|
)
|
|
55
58
|
|
|
56
59
|
def s_to_t2(s: float) -> float:
|
|
57
|
-
if s < s_values[0] or s > s_values[len(s_values) - 1]:
|
|
58
|
-
raise ValueError(
|
|
59
|
-
f"Lookup table does not support extrapolation from file {filename}, s={s}"
|
|
60
|
-
)
|
|
61
60
|
return float(interp(s, s_values, t_values))
|
|
62
61
|
|
|
63
62
|
return s_to_t2
|
dodal/devices/zebra.py
CHANGED
|
@@ -13,6 +13,7 @@ from ophyd_async.core import (
|
|
|
13
13
|
)
|
|
14
14
|
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
|
|
15
15
|
|
|
16
|
+
# These constants refer to I03's Zebra. See https://github.com/DiamondLightSource/dodal/issues/772
|
|
16
17
|
# Sources
|
|
17
18
|
DISCONNECT = 0
|
|
18
19
|
IN1_TTL = 1
|
|
@@ -39,8 +40,10 @@ TTL_PANDA = 4
|
|
|
39
40
|
|
|
40
41
|
# The AND gate that controls the automatic shutter
|
|
41
42
|
AUTO_SHUTTER_GATE = 2
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
|
|
44
|
+
# The first two inputs of the auto shutter gate.
|
|
45
|
+
AUTO_SHUTTER_INPUT_1 = 1
|
|
46
|
+
AUTO_SHUTTER_INPUT_2 = 2
|
|
44
47
|
|
|
45
48
|
|
|
46
49
|
class ArmSource(str, Enum):
|
|
@@ -229,16 +229,19 @@ class ZocaloResults(StandardReadable, Triggerable):
|
|
|
229
229
|
source_of_second_results = source_from_results(
|
|
230
230
|
raw_results_two_sources[1]
|
|
231
231
|
)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
232
|
+
first_results = raw_results_two_sources[0]["results"]
|
|
233
|
+
second_results = raw_results_two_sources[1]["results"]
|
|
234
|
+
|
|
235
|
+
if first_results and second_results:
|
|
236
|
+
# Compare results from both sources and warn if they aren't the same
|
|
237
|
+
differences_str = get_dict_differences(
|
|
238
|
+
first_results[0],
|
|
239
|
+
source_of_first_results,
|
|
240
|
+
second_results[0],
|
|
241
|
+
source_of_second_results,
|
|
242
|
+
)
|
|
243
|
+
if differences_str:
|
|
244
|
+
LOGGER.warning(differences_str)
|
|
242
245
|
|
|
243
246
|
# Always use CPU results
|
|
244
247
|
raw_results = (
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from bluesky import plan_stubs as bps
|
|
2
2
|
from bluesky import preprocessors as bpp
|
|
3
|
-
from bluesky.utils import make_decorator
|
|
3
|
+
from bluesky.utils import MsgGenerator, make_decorator
|
|
4
4
|
|
|
5
5
|
from dodal.common.beamlines import beamline_utils
|
|
6
|
-
from dodal.common.types import
|
|
6
|
+
from dodal.common.types import UpdatingPathProvider
|
|
7
7
|
|
|
8
8
|
DATA_SESSION = "data_session"
|
|
9
9
|
DATA_GROUPS = "data_groups"
|
dodal/plans/motor_util_plans.py
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import uuid
|
|
2
2
|
from collections.abc import Generator
|
|
3
|
-
from typing import Any, TypeVar
|
|
3
|
+
from typing import Any, TypeVar, cast
|
|
4
4
|
|
|
5
5
|
from bluesky import plan_stubs as bps
|
|
6
6
|
from bluesky.preprocessors import finalize_wrapper, pchain
|
|
7
|
-
from bluesky.utils import Msg, make_decorator
|
|
7
|
+
from bluesky.utils import Msg, MsgGenerator, make_decorator
|
|
8
8
|
from ophyd_async.core import Device
|
|
9
9
|
from ophyd_async.epics.motor import Motor
|
|
10
10
|
|
|
11
|
-
from dodal.
|
|
11
|
+
from dodal.utils import MovableReadable
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
MovableReadableDevice = TypeVar("MovableReadableDevice", bound=MovableReadable)
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class MoveTooLarge(Exception):
|
|
17
17
|
def __init__(
|
|
18
|
-
self, axis:
|
|
18
|
+
self, axis: MovableReadable, maximum_move: float, position: float, *args: object
|
|
19
19
|
) -> None:
|
|
20
20
|
self.axis = axis
|
|
21
21
|
self.maximum_move = maximum_move
|
|
@@ -24,10 +24,10 @@ class MoveTooLarge(Exception):
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def _check_and_cache_values(
|
|
27
|
-
devices_and_positions: dict[
|
|
27
|
+
devices_and_positions: dict[MovableReadableDevice, float],
|
|
28
28
|
smallest_move: float,
|
|
29
29
|
maximum_move: float,
|
|
30
|
-
) -> Generator[Msg, Any, dict[
|
|
30
|
+
) -> Generator[Msg, Any, dict[MovableReadableDevice, float]]:
|
|
31
31
|
"""Caches the positions of all Motors on specified device if they are within
|
|
32
32
|
smallest_move of home_position. Throws MoveTooLarge if they are outside maximum_move
|
|
33
33
|
of the home_position
|
|
@@ -51,7 +51,9 @@ def home_and_reset_wrapper(
|
|
|
51
51
|
wait_for_all: bool = True,
|
|
52
52
|
) -> MsgGenerator:
|
|
53
53
|
home_positions = {
|
|
54
|
-
axis: 0.0
|
|
54
|
+
cast(MovableReadable, axis): 0.0
|
|
55
|
+
for _, axis in device.children()
|
|
56
|
+
if isinstance(axis, Motor)
|
|
55
57
|
}
|
|
56
58
|
return move_and_reset_wrapper(
|
|
57
59
|
plan, home_positions, smallest_move, maximum_move, group, wait_for_all
|
|
@@ -60,7 +62,7 @@ def home_and_reset_wrapper(
|
|
|
60
62
|
|
|
61
63
|
def move_and_reset_wrapper(
|
|
62
64
|
plan: MsgGenerator,
|
|
63
|
-
device_and_positions: dict[
|
|
65
|
+
device_and_positions: dict[MovableReadable, float],
|
|
64
66
|
smallest_move: float,
|
|
65
67
|
maximum_move: float,
|
|
66
68
|
group: str | None = None,
|
dodal/utils.py
CHANGED
|
@@ -13,8 +13,10 @@ from os import environ
|
|
|
13
13
|
from types import ModuleType
|
|
14
14
|
from typing import (
|
|
15
15
|
Any,
|
|
16
|
+
Protocol,
|
|
16
17
|
TypeGuard,
|
|
17
18
|
TypeVar,
|
|
19
|
+
runtime_checkable,
|
|
18
20
|
)
|
|
19
21
|
|
|
20
22
|
from bluesky.protocols import (
|
|
@@ -62,6 +64,11 @@ BLUESKY_PROTOCOLS = [
|
|
|
62
64
|
Triggerable,
|
|
63
65
|
]
|
|
64
66
|
|
|
67
|
+
|
|
68
|
+
@runtime_checkable
|
|
69
|
+
class MovableReadable(Movable, Readable, Protocol): ...
|
|
70
|
+
|
|
71
|
+
|
|
65
72
|
AnyDevice: TypeAlias = OphydV1Device | OphydV2Device
|
|
66
73
|
V1DeviceFactory: TypeAlias = Callable[..., OphydV1Device]
|
|
67
74
|
V2DeviceFactory: TypeAlias = Callable[..., OphydV2Device]
|
dodal/beamlines/i04_1.py
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
from dodal.common.beamlines.beamline_utils import device_instantiation
|
|
2
|
-
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
|
|
3
|
-
from dodal.devices.backlight import Backlight
|
|
4
|
-
from dodal.devices.detector import DetectorParams
|
|
5
|
-
from dodal.devices.eiger import EigerDetector
|
|
6
|
-
from dodal.devices.oav.oav_detector import OAV, OAVConfigParams
|
|
7
|
-
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
8
|
-
from dodal.devices.synchrotron import Synchrotron
|
|
9
|
-
from dodal.devices.undulator import Undulator
|
|
10
|
-
from dodal.devices.zebra import Zebra
|
|
11
|
-
from dodal.log import set_beamline as set_log_beamline
|
|
12
|
-
from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device
|
|
13
|
-
|
|
14
|
-
ZOOM_PARAMS_FILE = "/dls_sw/i04-1/software/gda/config/xml/jCameraManZoomLevels.xml"
|
|
15
|
-
DISPLAY_CONFIG = "/dls_sw/i04-1/software/gda_versions/var/display.configuration"
|
|
16
|
-
|
|
17
|
-
_simulator_beamline_fallback = "s04_1"
|
|
18
|
-
BL = get_beamline_name(_simulator_beamline_fallback)
|
|
19
|
-
set_log_beamline(BL)
|
|
20
|
-
set_utils_beamline(BL)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def _check_for_simulation():
|
|
24
|
-
return BL == _simulator_beamline_fallback
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def backlight(
|
|
28
|
-
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
|
|
29
|
-
) -> Backlight:
|
|
30
|
-
"""Get the i04_1 backlight device, instantiate it if it hasn't already been.
|
|
31
|
-
If this is called when already instantiated in i04_1, it will return the existing object.
|
|
32
|
-
"""
|
|
33
|
-
return device_instantiation(
|
|
34
|
-
device_factory=Backlight,
|
|
35
|
-
name="backlight",
|
|
36
|
-
prefix="",
|
|
37
|
-
wait=wait_for_connection,
|
|
38
|
-
fake=fake_with_ophyd_sim,
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@skip_device(_check_for_simulation)
|
|
43
|
-
def eiger(
|
|
44
|
-
wait_for_connection: bool = True,
|
|
45
|
-
fake_with_ophyd_sim: bool = False,
|
|
46
|
-
params: DetectorParams | None = None,
|
|
47
|
-
) -> EigerDetector:
|
|
48
|
-
"""Get the i04_1 Eiger device, instantiate it if it hasn't already been.
|
|
49
|
-
If this is called when already instantiated in i04_1, it will return the existing object.
|
|
50
|
-
If called with params, will update those params to the Eiger object.
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
def set_params(eiger: EigerDetector):
|
|
54
|
-
if params is not None:
|
|
55
|
-
eiger.set_detector_parameters(params)
|
|
56
|
-
|
|
57
|
-
return device_instantiation(
|
|
58
|
-
device_factory=EigerDetector,
|
|
59
|
-
name="eiger",
|
|
60
|
-
prefix="-EA-EIGER-01:",
|
|
61
|
-
wait=wait_for_connection,
|
|
62
|
-
fake=fake_with_ophyd_sim,
|
|
63
|
-
post_create=set_params,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@skip_device(_check_for_simulation)
|
|
68
|
-
def oav(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> OAV:
|
|
69
|
-
"""Get the i04_1 OAV device, instantiate it if it hasn't already been.
|
|
70
|
-
If this is called when already instantiated in i04_1, it will return the existing object.
|
|
71
|
-
"""
|
|
72
|
-
return device_instantiation(
|
|
73
|
-
OAV,
|
|
74
|
-
"oav",
|
|
75
|
-
"",
|
|
76
|
-
wait_for_connection,
|
|
77
|
-
fake_with_ophyd_sim,
|
|
78
|
-
params=OAVConfigParams(ZOOM_PARAMS_FILE, DISPLAY_CONFIG),
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def s4_slit_gaps(
|
|
83
|
-
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
|
|
84
|
-
) -> S4SlitGaps:
|
|
85
|
-
"""Get the i04_1 s4_slit_gaps device, instantiate it if it hasn't already been.
|
|
86
|
-
If this is called when already instantiated in i04_1, it will return the existing object.
|
|
87
|
-
"""
|
|
88
|
-
return device_instantiation(
|
|
89
|
-
S4SlitGaps,
|
|
90
|
-
"s4_slit_gaps",
|
|
91
|
-
"-AL-SLITS-04:",
|
|
92
|
-
wait_for_connection,
|
|
93
|
-
fake_with_ophyd_sim,
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
@skip_device(_check_for_simulation)
|
|
98
|
-
def synchrotron(
|
|
99
|
-
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
|
|
100
|
-
) -> Synchrotron:
|
|
101
|
-
"""Get the i04_1 synchrotron device, instantiate it if it hasn't already been.
|
|
102
|
-
If this is called when already instantiated in i04_1, it will return the existing object.
|
|
103
|
-
"""
|
|
104
|
-
return device_instantiation(
|
|
105
|
-
Synchrotron,
|
|
106
|
-
"synchrotron",
|
|
107
|
-
"",
|
|
108
|
-
wait_for_connection,
|
|
109
|
-
fake_with_ophyd_sim,
|
|
110
|
-
bl_prefix=False,
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def undulator(
|
|
115
|
-
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
|
|
116
|
-
) -> Undulator:
|
|
117
|
-
"""Get the i04_1 undulator device, instantiate it if it hasn't already been.
|
|
118
|
-
If this is called when already instantiated in i04_1, it will return the existing object.
|
|
119
|
-
"""
|
|
120
|
-
return device_instantiation(
|
|
121
|
-
Undulator,
|
|
122
|
-
"undulator",
|
|
123
|
-
f"{BeamlinePrefix(BL).insertion_prefix}-MO-SERVC-01:",
|
|
124
|
-
wait_for_connection,
|
|
125
|
-
fake_with_ophyd_sim,
|
|
126
|
-
bl_prefix=False,
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def zebra(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> Zebra:
|
|
131
|
-
"""Get the i04_1 zebra device, instantiate it if it hasn't already been.
|
|
132
|
-
If this is called when already instantiated in i04_1, it will return the existing object.
|
|
133
|
-
"""
|
|
134
|
-
return device_instantiation(
|
|
135
|
-
Zebra,
|
|
136
|
-
"zebra",
|
|
137
|
-
"-EA-ZEBRA-01:",
|
|
138
|
-
wait_for_connection,
|
|
139
|
-
fake_with_ophyd_sim,
|
|
140
|
-
)
|