mx-bluesky 1.4.7__py3-none-any.whl → 1.4.9__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.
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/aithre_lasershaping/__init__.py +8 -0
- mx_bluesky/beamlines/aithre_lasershaping/beamline_safe.py +36 -0
- mx_bluesky/beamlines/aithre_lasershaping/goniometer_controls.py +43 -0
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +4 -4
- mx_bluesky/beamlines/i04/thawing_plan.py +8 -2
- mx_bluesky/beamlines/i23/__init__.py +3 -0
- mx_bluesky/beamlines/i23/serial.py +71 -0
- mx_bluesky/beamlines/i24/serial/__init__.py +2 -0
- mx_bluesky/beamlines/i24/serial/blueapi_config.yaml +2 -1
- mx_bluesky/beamlines/i24/serial/dcid.py +5 -5
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DetStage.edl +2 -2
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +9 -9
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +25 -5
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DetStage.edl +2 -2
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +14 -14
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +5 -5
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +29 -60
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +7 -1
- mx_bluesky/beamlines/i24/serial/log.py +9 -10
- mx_bluesky/beamlines/i24/serial/parameters/utils.py +36 -7
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +0 -1
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv_abstract.py +4 -4
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +4 -12
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +2 -1
- mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +71 -11
- mx_bluesky/beamlines/i24/serial/write_nexus.py +3 -3
- mx_bluesky/{hyperion → common}/device_setup_plans/manipulate_sample.py +6 -14
- mx_bluesky/{hyperion → common}/device_setup_plans/setup_oav.py +12 -6
- mx_bluesky/{hyperion → common}/experiment_plans/change_aperture_then_move_plan.py +4 -5
- mx_bluesky/{hyperion → common}/experiment_plans/oav_grid_detection_plan.py +6 -6
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +6 -5
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +16 -47
- mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +4 -1
- mx_bluesky/common/external_interaction/ispyb/ispyb_utils.py +4 -4
- mx_bluesky/common/external_interaction/nexus/nexus_utils.py +2 -2
- mx_bluesky/common/parameters/components.py +22 -2
- mx_bluesky/common/parameters/constants.py +4 -16
- mx_bluesky/common/parameters/gridscan.py +36 -32
- mx_bluesky/common/plans/common_flyscan_xray_centre_plan.py +316 -0
- mx_bluesky/common/plans/inner_plans/__init__ .py +0 -0
- mx_bluesky/common/plans/read_hardware.py +3 -3
- mx_bluesky/common/utils/log.py +19 -15
- mx_bluesky/hyperion/__main__.py +6 -24
- mx_bluesky/hyperion/baton_handler.py +8 -3
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +4 -4
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +0 -33
- mx_bluesky/hyperion/device_setup_plans/smargon.py +2 -7
- mx_bluesky/hyperion/device_setup_plans/utils.py +6 -5
- mx_bluesky/hyperion/experiment_plans/__init__.py +1 -7
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +3 -13
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +80 -87
- mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +183 -0
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +50 -15
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +31 -7
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +4 -4
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +1 -1
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +13 -14
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +9 -8
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +30 -71
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +2 -2
- mx_bluesky/hyperion/external_interaction/agamemnon.py +78 -80
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +8 -6
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +3 -3
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +6 -3
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +2 -2
- mx_bluesky/hyperion/external_interaction/callbacks/snapshot_callback.py +183 -31
- mx_bluesky/hyperion/external_interaction/config_server.py +4 -1
- mx_bluesky/hyperion/parameters/cli.py +4 -19
- mx_bluesky/hyperion/parameters/constants.py +1 -5
- mx_bluesky/hyperion/parameters/device_composites.py +40 -5
- mx_bluesky/hyperion/parameters/gridscan.py +9 -58
- mx_bluesky/hyperion/parameters/load_centre_collect.py +4 -4
- mx_bluesky/hyperion/parameters/rotation.py +9 -12
- mx_bluesky/hyperion/utils/context.py +2 -2
- mx_bluesky/hyperion/utils/validation.py +15 -19
- {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/METADATA +7 -6
- {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/RECORD +86 -83
- {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/WHEEL +1 -1
- mx_bluesky/common/external_interaction/test_config_server.py +0 -38
- mx_bluesky/hyperion/device_setup_plans/check_beamstop.py +0 -27
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +0 -467
- /mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/{short1-laser.png → s1l.png} +0 -0
- /mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/{short2-laser.png → s2l.png} +0 -0
- /mx_bluesky/{hyperion → common}/device_setup_plans/position_detector.py +0 -0
- /mx_bluesky/common/plans/{do_fgs.py → inner_plans/do_fgs.py} +0 -0
- {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/licenses/LICENSE +0 -0
- {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from collections.abc import Callable, Sequence
|
|
3
3
|
from threading import Thread
|
|
4
|
+
from time import sleep # noqa
|
|
4
5
|
|
|
5
|
-
import bluesky.plan_stubs as bps
|
|
6
6
|
from bluesky.callbacks import CallbackBase
|
|
7
7
|
from bluesky.callbacks.zmq import Proxy, RemoteDispatcher
|
|
8
8
|
from dodal.log import LOGGER as dodal_logger
|
|
@@ -26,7 +26,7 @@ from mx_bluesky.common.external_interaction.callbacks.xray_centre.nexus_callback
|
|
|
26
26
|
from mx_bluesky.common.utils.log import (
|
|
27
27
|
ISPYB_ZOCALO_CALLBACK_LOGGER,
|
|
28
28
|
NEXUS_LOGGER,
|
|
29
|
-
|
|
29
|
+
_get_logging_dirs,
|
|
30
30
|
tag_filter,
|
|
31
31
|
)
|
|
32
32
|
from mx_bluesky.hyperion.external_interaction.callbacks.robot_load.ispyb_callback import (
|
|
@@ -93,14 +93,16 @@ def setup_logging(dev_mode: bool):
|
|
|
93
93
|
(ISPYB_ZOCALO_CALLBACK_LOGGER, "hyperion_ispyb_callback.log"),
|
|
94
94
|
(NEXUS_LOGGER, "hyperion_nexus_callback.log"),
|
|
95
95
|
]:
|
|
96
|
+
logging_path, debug_logging_path = _get_logging_dirs(dev_mode)
|
|
96
97
|
if logger.handlers == []:
|
|
97
98
|
handlers = set_up_all_logging_handlers(
|
|
98
99
|
logger,
|
|
99
|
-
|
|
100
|
+
logging_path,
|
|
100
101
|
filename,
|
|
101
102
|
dev_mode,
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
ERROR_LOG_BUFFER_LINES,
|
|
104
|
+
CONST.GRAYLOG_PORT,
|
|
105
|
+
debug_logging_path,
|
|
104
106
|
)
|
|
105
107
|
handlers["graylog_handler"].addFilter(tag_filter)
|
|
106
108
|
log_info(f"Loggers initialised with dev_mode={dev_mode}")
|
|
@@ -140,7 +142,7 @@ def wait_for_threads_forever(threads: Sequence[Thread]):
|
|
|
140
142
|
try:
|
|
141
143
|
log_debug("Trying to wait forever on callback and dispatcher threads")
|
|
142
144
|
while all(alive):
|
|
143
|
-
|
|
145
|
+
sleep(LIVENESS_POLL_SECONDS)
|
|
144
146
|
alive = [t.is_alive() for t in threads]
|
|
145
147
|
except KeyboardInterrupt:
|
|
146
148
|
log_info("Main thread received interrupt - exiting.")
|
|
@@ -25,7 +25,7 @@ from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_mapping i
|
|
|
25
25
|
populate_data_collection_info_for_rotation,
|
|
26
26
|
)
|
|
27
27
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
28
|
-
from mx_bluesky.hyperion.parameters.rotation import
|
|
28
|
+
from mx_bluesky.hyperion.parameters.rotation import SingleRotationScan
|
|
29
29
|
|
|
30
30
|
if TYPE_CHECKING:
|
|
31
31
|
from event_model.documents import Event, RunStart, RunStop
|
|
@@ -62,7 +62,7 @@ class RotationISPyBCallback(BaseISPyBCallback):
|
|
|
62
62
|
)
|
|
63
63
|
hyperion_params = doc.get("mx_bluesky_parameters")
|
|
64
64
|
assert isinstance(hyperion_params, str)
|
|
65
|
-
self.params =
|
|
65
|
+
self.params = SingleRotationScan.model_validate_json(hyperion_params)
|
|
66
66
|
dcgid = (
|
|
67
67
|
self.ispyb_ids.data_collection_group_id
|
|
68
68
|
if (self.params.sample_id == self.last_sample_id)
|
|
@@ -86,7 +86,7 @@ class RotationISPyBCallback(BaseISPyBCallback):
|
|
|
86
86
|
ISPYB_ZOCALO_CALLBACK_LOGGER.info("Beginning ispyb deposition")
|
|
87
87
|
data_collection_group_info = populate_data_collection_group(self.params)
|
|
88
88
|
data_collection_info = populate_data_collection_info_for_rotation(
|
|
89
|
-
cast(
|
|
89
|
+
cast(SingleRotationScan, self.params)
|
|
90
90
|
)
|
|
91
91
|
data_collection_info = populate_remaining_data_collection_info(
|
|
92
92
|
self.params.comment,
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from mx_bluesky.common.external_interaction.ispyb.data_model import DataCollectionInfo
|
|
4
|
-
from mx_bluesky.hyperion.parameters.rotation import
|
|
4
|
+
from mx_bluesky.hyperion.parameters.rotation import SingleRotationScan
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def populate_data_collection_info_for_rotation(params:
|
|
7
|
+
def populate_data_collection_info_for_rotation(params: SingleRotationScan):
|
|
8
8
|
info = DataCollectionInfo(
|
|
9
9
|
omega_start=params.omega_start_deg,
|
|
10
10
|
data_collection_number=params.detector_params.run_number, # type:ignore # the validator always makes this int
|
|
11
11
|
n_images=params.num_images,
|
|
12
12
|
axis_range=params.rotation_increment_deg,
|
|
13
13
|
axis_start=params.omega_start_deg,
|
|
14
|
-
axis_end=(
|
|
14
|
+
axis_end=(
|
|
15
|
+
params.omega_start_deg
|
|
16
|
+
+ params.scan_width_deg * params.rotation_direction.multiplier
|
|
17
|
+
),
|
|
15
18
|
kappa_start=params.kappa_start_deg,
|
|
16
19
|
)
|
|
17
20
|
return info
|
|
@@ -16,7 +16,7 @@ from mx_bluesky.common.external_interaction.nexus.nexus_utils import (
|
|
|
16
16
|
from mx_bluesky.common.external_interaction.nexus.write_nexus import NexusWriter
|
|
17
17
|
from mx_bluesky.common.utils.log import NEXUS_LOGGER
|
|
18
18
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
19
|
-
from mx_bluesky.hyperion.parameters.rotation import
|
|
19
|
+
from mx_bluesky.hyperion.parameters.rotation import SingleRotationScan
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
22
|
from event_model.documents import Event, EventDescriptor, RunStart
|
|
@@ -85,7 +85,7 @@ class RotationNexusFileCallback(PlanReactiveCallback):
|
|
|
85
85
|
NEXUS_LOGGER.info(
|
|
86
86
|
f"Nexus writer received start document with experiment parameters {hyperion_params}"
|
|
87
87
|
)
|
|
88
|
-
parameters =
|
|
88
|
+
parameters = SingleRotationScan.model_validate_json(hyperion_params)
|
|
89
89
|
NEXUS_LOGGER.info("Setting up nexus file...")
|
|
90
90
|
|
|
91
91
|
det_size = (
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import dataclasses
|
|
1
2
|
import re
|
|
3
|
+
from collections.abc import Iterator
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from math import cos, radians, sin
|
|
6
|
+
from pathlib import Path
|
|
2
7
|
|
|
3
8
|
from dodal.devices.oav.snapshots.snapshot_image_processing import (
|
|
4
9
|
compute_beam_centre_pixel_xy_for_mm_position,
|
|
@@ -10,17 +15,35 @@ from PIL import Image
|
|
|
10
15
|
from mx_bluesky.common.external_interaction.callbacks.common.plan_reactive_callback import (
|
|
11
16
|
PlanReactiveCallback,
|
|
12
17
|
)
|
|
13
|
-
from mx_bluesky.common.parameters.
|
|
18
|
+
from mx_bluesky.common.parameters.components import WithSnapshot
|
|
19
|
+
from mx_bluesky.common.parameters.constants import DocDescriptorNames, PlanNameConstants
|
|
14
20
|
from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER as CALLBACK_LOGGER
|
|
15
21
|
|
|
22
|
+
COMPRESSION_LEVEL = 6 # 6 is the default compression level for PIL if not specified
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclasses.dataclass
|
|
26
|
+
class _SnapshotInfo:
|
|
27
|
+
beam_centre: tuple[int, int]
|
|
28
|
+
microns_per_pixel: tuple[float, float]
|
|
29
|
+
snapshot_path: str
|
|
30
|
+
omega: int
|
|
31
|
+
sample_pos_mm: tuple[float, float, float]
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def snapshot_basename(self) -> str:
|
|
35
|
+
match = re.match("(.*)\\.png", self.snapshot_path)
|
|
36
|
+
assert match, f"Snapshot {self.snapshot_path} was not a .png file"
|
|
37
|
+
return match.groups()[0]
|
|
38
|
+
|
|
16
39
|
|
|
17
40
|
class BeamDrawingCallback(PlanReactiveCallback):
|
|
18
41
|
"""
|
|
19
42
|
Callback that monitors for OAV_ROTATION_SNAPSHOT_TRIGGERED events and
|
|
20
43
|
draws a crosshair at the beam centre, saving the snapshot to a file.
|
|
21
|
-
The callback assumes an OAV device "oav"
|
|
44
|
+
The callback assumes an OAV device "oav" and Smargon "smargon"
|
|
22
45
|
Examples:
|
|
23
|
-
Take a snapshot at the current location
|
|
46
|
+
Take a rotation snapshot at the current location
|
|
24
47
|
>>> from bluesky.run_engine import RunEngine
|
|
25
48
|
>>> import bluesky.preprocessors as bpp
|
|
26
49
|
>>> import bluesky.plan_stubs as bps
|
|
@@ -39,69 +62,198 @@ class BeamDrawingCallback(PlanReactiveCallback):
|
|
|
39
62
|
... yield from bps.create(DocDescriptorNames.OAV_ROTATION_SNAPSHOT_TRIGGERED)
|
|
40
63
|
... yield from bps.read(oav)
|
|
41
64
|
... yield from bps.save()
|
|
65
|
+
|
|
66
|
+
Generate rotation snapshots from a previously taken base gridscan snapshot.
|
|
67
|
+
WithSnapshot.snapshot_omegas_deg is ignored and snapshots are generated for the previously captured
|
|
68
|
+
0, 90 base images named "my_snapshot_prefix_0" and "my_snapshot_prefix_90"
|
|
69
|
+
>>> from dodal.devices.smargon import Smargon
|
|
70
|
+
>>> def take_snapshot(params: WithSnapshot, oav: OAV, smargon: Smargon, run_engine: RunEngine):
|
|
71
|
+
... run_engine.subscribe(BeamDrawingCallback())
|
|
72
|
+
... @bpp.run_decorator(md={
|
|
73
|
+
... "activate_callbacks": ["BeamDrawingCallback"],
|
|
74
|
+
... "with_snapshot": params.model_dump_json(),
|
|
75
|
+
... })
|
|
76
|
+
... def inner_plan():
|
|
77
|
+
... for omega in (0, 90,):
|
|
78
|
+
... yield from bps.abs_set(smargon.omega, omega, wait=True)
|
|
79
|
+
... yield from bps.abs_set(oav.grid_snapshot.directory, "/path/to/grid_snapshot_folder", wait=True)
|
|
80
|
+
... yield from bps.abs_set(oav.grid_snapshot.filename, f"my_grid_snapshot_prefix_{omega}", wait=True)
|
|
81
|
+
... yield from bps.trigger(oav.grid_snapshot, wait=True)
|
|
82
|
+
... yield from bps.create(DocDescriptorNames.OAV_GRID_SNAPSHOT_TRIGGERED)
|
|
83
|
+
... yield from bps.read(oav) # Capture base image path
|
|
84
|
+
... yield from bps.read(smargon) # Capture base image sample x, y, z, omega
|
|
85
|
+
... yield from bps.save()
|
|
86
|
+
... # Rest of gridscan here...
|
|
87
|
+
... # Later on...
|
|
88
|
+
... for omega in (0, 90,):
|
|
89
|
+
... yield from bps.abs_set(oav.snapshot.last_saved_path,
|
|
90
|
+
... f"/path/to/snapshot_folder/my_snapshot_prefix_{omega}.png", wait=True)
|
|
91
|
+
... yield from bps.create(DocDescriptorNames.OAV_ROTATION_SNAPSHOT_TRIGGERED)
|
|
92
|
+
... yield from bps.read(oav) # Capture path info for generated snapshot
|
|
93
|
+
... yield from bps.read(smargon) # Capture the current sample x, y, z
|
|
94
|
+
... yield from bps.save()
|
|
42
95
|
"""
|
|
43
96
|
|
|
44
97
|
def __init__(self, *args, **kwargs):
|
|
45
98
|
super().__init__(*args, log=CALLBACK_LOGGER, **kwargs)
|
|
46
|
-
self.
|
|
47
|
-
self._microns_per_pixel: tuple[float, float]
|
|
48
|
-
self._beam_centre: tuple[int, int]
|
|
99
|
+
self._base_snapshots: list[_SnapshotInfo] = []
|
|
49
100
|
self._rotation_snapshot_descriptor: str = ""
|
|
101
|
+
self._grid_snapshot_descriptor: str = ""
|
|
102
|
+
self._next_snapshot_info: Iterator | None = None
|
|
103
|
+
self._use_grid_snapshots: bool = False
|
|
104
|
+
|
|
105
|
+
def _reset(self):
|
|
106
|
+
self._base_snapshots = []
|
|
50
107
|
|
|
51
108
|
def activity_gated_start(self, doc: RunStart):
|
|
52
109
|
if self.activity_uid == doc.get("uid"):
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
110
|
+
self._reset()
|
|
111
|
+
with_snapshot = WithSnapshot.model_validate_json(doc.get("with_snapshot")) # type: ignore
|
|
112
|
+
self._use_grid_snapshots = with_snapshot.use_grid_snapshots
|
|
113
|
+
CALLBACK_LOGGER.info(f"Snapshot callback initialised with {with_snapshot}")
|
|
114
|
+
elif doc.get("subplan_name") == PlanNameConstants.ROTATION_MAIN:
|
|
115
|
+
self._next_snapshot_info = None
|
|
116
|
+
CALLBACK_LOGGER.info("Snapshot callback start rotation")
|
|
57
117
|
return doc
|
|
58
118
|
|
|
59
119
|
def activity_gated_descriptor(self, doc: EventDescriptor) -> EventDescriptor | None:
|
|
60
120
|
if doc.get("name") == DocDescriptorNames.OAV_ROTATION_SNAPSHOT_TRIGGERED:
|
|
61
121
|
self._rotation_snapshot_descriptor = doc["uid"]
|
|
122
|
+
elif doc.get("name") == DocDescriptorNames.OAV_GRID_SNAPSHOT_TRIGGERED:
|
|
123
|
+
self._grid_snapshot_descriptor = doc["uid"]
|
|
62
124
|
return doc
|
|
63
125
|
|
|
64
126
|
def activity_gated_event(self, doc: Event) -> Event:
|
|
65
127
|
if doc["descriptor"] == self._rotation_snapshot_descriptor:
|
|
66
128
|
self._handle_rotation_snapshot(doc)
|
|
129
|
+
elif doc["descriptor"] == self._grid_snapshot_descriptor:
|
|
130
|
+
self._handle_grid_snapshot(doc)
|
|
67
131
|
return doc
|
|
68
132
|
|
|
69
|
-
def _extract_base_snapshot_params(
|
|
133
|
+
def _extract_base_snapshot_params(
|
|
134
|
+
self, snapshot_device_prefix: str, doc: Event
|
|
135
|
+
) -> _SnapshotInfo:
|
|
70
136
|
data = doc["data"]
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
data["oav-
|
|
74
|
-
|
|
137
|
+
base_snapshot_path = data[f"oav-{snapshot_device_prefix}-last_saved_path"]
|
|
138
|
+
return _SnapshotInfo(
|
|
139
|
+
beam_centre=(data["oav-beam_centre_i"], data["oav-beam_centre_j"]),
|
|
140
|
+
microns_per_pixel=(
|
|
141
|
+
data["oav-microns_per_pixel_x"],
|
|
142
|
+
data["oav-microns_per_pixel_y"],
|
|
143
|
+
),
|
|
144
|
+
snapshot_path=base_snapshot_path,
|
|
145
|
+
sample_pos_mm=(
|
|
146
|
+
data.get("smargon-x", 0.0),
|
|
147
|
+
data.get("smargon-y", 0.0),
|
|
148
|
+
data.get("smargon-z", 0.0),
|
|
149
|
+
),
|
|
150
|
+
omega=round(data.get("smargon-omega", 0.0)),
|
|
75
151
|
)
|
|
76
|
-
self._beam_centre = (data["oav-beam_centre_i"], data["oav-beam_centre_j"])
|
|
77
152
|
|
|
78
|
-
def
|
|
79
|
-
self._extract_base_snapshot_params(doc)
|
|
153
|
+
def _handle_grid_snapshot(self, doc: Event):
|
|
154
|
+
snapshot_info = self._extract_base_snapshot_params("grid_snapshot", doc)
|
|
155
|
+
self._base_snapshots.append(snapshot_info)
|
|
156
|
+
|
|
157
|
+
def _handle_rotation_snapshot(self, doc: Event) -> Event:
|
|
80
158
|
data = doc["data"]
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
159
|
+
if self._use_grid_snapshots:
|
|
160
|
+
if not self._next_snapshot_info:
|
|
161
|
+
self._next_snapshot_info = iter(self._base_snapshots)
|
|
162
|
+
snapshot_info = next(self._next_snapshot_info, None)
|
|
163
|
+
assert snapshot_info, (
|
|
164
|
+
"Insufficient base gridscan snapshots to generate required rotation snapshots"
|
|
165
|
+
)
|
|
166
|
+
current_sample_pos_mm = (
|
|
167
|
+
data["smargon-x"],
|
|
168
|
+
data["smargon-y"],
|
|
169
|
+
data["smargon-z"],
|
|
170
|
+
)
|
|
171
|
+
CALLBACK_LOGGER.info(
|
|
172
|
+
f"Generating snapshot at {current_sample_pos_mm} from base snapshot {snapshot_info}"
|
|
173
|
+
)
|
|
174
|
+
output_snapshot_directory = data["oav-snapshot-directory"]
|
|
175
|
+
base_file_stem = Path(snapshot_info.snapshot_path).stem
|
|
176
|
+
output_snapshot_filename = _snapshot_filename(base_file_stem)
|
|
177
|
+
output_snapshot_path = (
|
|
178
|
+
f"{output_snapshot_directory}/{output_snapshot_filename}.png"
|
|
179
|
+
)
|
|
180
|
+
self._generate_snapshot_at(
|
|
181
|
+
snapshot_info,
|
|
182
|
+
output_snapshot_path,
|
|
183
|
+
*self._image_plane_offset_mm(snapshot_info, current_sample_pos_mm),
|
|
184
|
+
)
|
|
185
|
+
else:
|
|
186
|
+
snapshot_info = self._extract_base_snapshot_params("snapshot", doc)
|
|
187
|
+
output_snapshot_path = (
|
|
188
|
+
f"{snapshot_info.snapshot_basename}_with_beam_centre.png"
|
|
189
|
+
)
|
|
190
|
+
CALLBACK_LOGGER.info(
|
|
191
|
+
f"Annotating snapshot {output_snapshot_path} from base snapshot {snapshot_info}"
|
|
192
|
+
)
|
|
193
|
+
self._generate_snapshot_zero_offset(
|
|
194
|
+
snapshot_info,
|
|
195
|
+
output_snapshot_path,
|
|
196
|
+
)
|
|
87
197
|
data["oav-snapshot-last_saved_path"] = output_snapshot_path
|
|
88
198
|
return doc
|
|
89
199
|
|
|
200
|
+
def _image_plane_offset_mm(
|
|
201
|
+
self,
|
|
202
|
+
snapshot_info: _SnapshotInfo,
|
|
203
|
+
current_sample_pos_mm: tuple[float, float, float],
|
|
204
|
+
) -> tuple[float, float]:
|
|
205
|
+
return self._project_xyz_to_xy(
|
|
206
|
+
(
|
|
207
|
+
(current_sample_pos_mm[0] - snapshot_info.sample_pos_mm[0]),
|
|
208
|
+
(current_sample_pos_mm[1] - snapshot_info.sample_pos_mm[1]),
|
|
209
|
+
(current_sample_pos_mm[2] - snapshot_info.sample_pos_mm[2]),
|
|
210
|
+
),
|
|
211
|
+
snapshot_info.omega,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
def _project_xyz_to_xy(
|
|
215
|
+
self, xyz: tuple[float, float, float], omega_deg: float
|
|
216
|
+
) -> tuple[float, float]:
|
|
217
|
+
return (
|
|
218
|
+
xyz[0],
|
|
219
|
+
xyz[1] * cos(-radians(omega_deg)) + xyz[2] * sin(-radians(omega_deg)),
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def _generate_snapshot_zero_offset(
|
|
223
|
+
self,
|
|
224
|
+
base_snapshot_info: _SnapshotInfo,
|
|
225
|
+
output_snapshot_path: str,
|
|
226
|
+
):
|
|
227
|
+
self._generate_snapshot_at(base_snapshot_info, output_snapshot_path, 0, 0)
|
|
228
|
+
|
|
90
229
|
def _generate_snapshot_at(
|
|
91
|
-
self,
|
|
230
|
+
self,
|
|
231
|
+
base_snapshot_info: _SnapshotInfo,
|
|
232
|
+
output_snapshot_path: str,
|
|
233
|
+
image_plane_dx_mm: float,
|
|
234
|
+
image_plane_dy_mm: float,
|
|
92
235
|
):
|
|
93
236
|
"""
|
|
94
237
|
Save a snapshot to the specified path, with an annotated crosshair at the specified
|
|
95
238
|
position
|
|
96
239
|
Args:
|
|
97
|
-
|
|
240
|
+
base_snapshot_info: Metadata about the base snapshot image from which the annotated
|
|
241
|
+
image will be derived.
|
|
98
242
|
output_snapshot_path: The path to the image that will be annotated.
|
|
99
|
-
|
|
100
|
-
|
|
243
|
+
image_plane_dx_mm: Relative x location of the sample to the original image in the image plane (mm)
|
|
244
|
+
image_plane_dy_mm: Relative y location of the sample to the original image in the image plane (mm)
|
|
101
245
|
"""
|
|
102
|
-
image = Image.open(
|
|
246
|
+
image = Image.open(base_snapshot_info.snapshot_path)
|
|
103
247
|
x_px, y_px = compute_beam_centre_pixel_xy_for_mm_position(
|
|
104
|
-
(
|
|
248
|
+
(image_plane_dx_mm, image_plane_dy_mm),
|
|
249
|
+
base_snapshot_info.beam_centre,
|
|
250
|
+
base_snapshot_info.microns_per_pixel,
|
|
105
251
|
)
|
|
106
252
|
draw_crosshair(image, x_px, y_px)
|
|
107
|
-
image.save(output_snapshot_path, format="png")
|
|
253
|
+
image.save(output_snapshot_path, format="png", compress_level=COMPRESSION_LEVEL)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _snapshot_filename(grid_snapshot_name):
|
|
257
|
+
time_now = datetime.now()
|
|
258
|
+
filename = f"{time_now.strftime('%H%M%S%f')[:8]}_oav_snapshot_{grid_snapshot_name}"
|
|
259
|
+
return filename
|
|
@@ -21,7 +21,9 @@ class HyperionFeatureFlags(FeatureFlags):
|
|
|
21
21
|
set_stub_offsets: If True then set the stub offsets after moving to the crystal (ignored for
|
|
22
22
|
multi-centre)
|
|
23
23
|
omega_flip: If True then invert the smargon omega motor rotation commands with respect to
|
|
24
|
-
the hyperion request.
|
|
24
|
+
the hyperion request. See "Hyperion Coordinate Systems" in the documentation.
|
|
25
|
+
alternate_rotation_direction: If True then the for multi-sample pins the rotation direction of
|
|
26
|
+
successive rotation scans is alternated between positive and negative.
|
|
25
27
|
"""
|
|
26
28
|
|
|
27
29
|
@staticmethod
|
|
@@ -41,3 +43,4 @@ class HyperionFeatureFlags(FeatureFlags):
|
|
|
41
43
|
use_gpu_results: bool = CONST.I03.USE_GPU_RESULTS
|
|
42
44
|
set_stub_offsets: bool = CONST.I03.SET_STUB_OFFSETS
|
|
43
45
|
omega_flip: bool = CONST.I03.OMEGA_FLIP
|
|
46
|
+
alternate_rotation_direction: bool = CONST.I03.ALTERNATE_ROTATION_DIRECTION
|
|
@@ -8,8 +8,6 @@ from mx_bluesky._version import version
|
|
|
8
8
|
@dataclass
|
|
9
9
|
class HyperionArgs:
|
|
10
10
|
dev_mode: bool = False
|
|
11
|
-
verbose_event_logging: bool = False
|
|
12
|
-
skip_startup_connection: bool = False
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
def _add_callback_relevant_args(parser: argparse.ArgumentParser) -> None:
|
|
@@ -17,7 +15,7 @@ def _add_callback_relevant_args(parser: argparse.ArgumentParser) -> None:
|
|
|
17
15
|
parser.add_argument(
|
|
18
16
|
"--dev",
|
|
19
17
|
action="store_true",
|
|
20
|
-
help="Use dev options, such as local graylog instances
|
|
18
|
+
help="Use dev options, such as local graylog instances",
|
|
21
19
|
)
|
|
22
20
|
|
|
23
21
|
|
|
@@ -30,22 +28,11 @@ def parse_callback_dev_mode_arg() -> bool:
|
|
|
30
28
|
|
|
31
29
|
|
|
32
30
|
def parse_cli_args() -> HyperionArgs:
|
|
33
|
-
"""Parses all arguments relevant to hyperion.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
skip_startup_connection: bool)"""
|
|
31
|
+
"""Parses all arguments relevant to hyperion.
|
|
32
|
+
Returns:
|
|
33
|
+
an HyperionArgs dataclass with the fields: (dev_mode: bool)"""
|
|
37
34
|
parser = argparse.ArgumentParser()
|
|
38
35
|
_add_callback_relevant_args(parser)
|
|
39
|
-
parser.add_argument(
|
|
40
|
-
"--verbose-event-logging",
|
|
41
|
-
action="store_true",
|
|
42
|
-
help="Log all bluesky event documents to graylog",
|
|
43
|
-
)
|
|
44
|
-
parser.add_argument(
|
|
45
|
-
"--skip-startup-connection",
|
|
46
|
-
action="store_true",
|
|
47
|
-
help="Skip connecting to EPICS PVs on startup",
|
|
48
|
-
)
|
|
49
36
|
parser.add_argument(
|
|
50
37
|
"--version",
|
|
51
38
|
help="Print hyperion version string",
|
|
@@ -54,7 +41,5 @@ def parse_cli_args() -> HyperionArgs:
|
|
|
54
41
|
)
|
|
55
42
|
args = parser.parse_args()
|
|
56
43
|
return HyperionArgs(
|
|
57
|
-
verbose_event_logging=args.verbose_event_logging or False,
|
|
58
44
|
dev_mode=args.dev or False,
|
|
59
|
-
skip_startup_connection=args.skip_startup_connection or False,
|
|
60
45
|
)
|
|
@@ -12,8 +12,6 @@ from mx_bluesky.common.parameters.constants import (
|
|
|
12
12
|
OavConstants,
|
|
13
13
|
PlanGroupCheckpointConstants,
|
|
14
14
|
PlanNameConstants,
|
|
15
|
-
SimConstants,
|
|
16
|
-
TriggerConstants,
|
|
17
15
|
)
|
|
18
16
|
|
|
19
17
|
TEST_MODE = os.environ.get("HYPERION_TEST_MODE")
|
|
@@ -30,6 +28,7 @@ class I03Constants:
|
|
|
30
28
|
USE_PANDA_FOR_GRIDSCAN = False
|
|
31
29
|
SET_STUB_OFFSETS = False
|
|
32
30
|
OMEGA_FLIP = True
|
|
31
|
+
ALTERNATE_ROTATION_DIRECTION = True
|
|
33
32
|
|
|
34
33
|
# Turns on GPU processing for zocalo and logs a comparison between GPU and CPU-
|
|
35
34
|
# processed results.
|
|
@@ -42,15 +41,12 @@ class I03Constants:
|
|
|
42
41
|
@dataclass(frozen=True)
|
|
43
42
|
class HyperionConstants:
|
|
44
43
|
DESCRIPTORS = DocDescriptorNames()
|
|
45
|
-
TRIGGER = TriggerConstants()
|
|
46
44
|
ZOCALO_ENV = EnvironmentConstants.ZOCALO_ENV
|
|
47
45
|
HARDWARE = HardwareConstants()
|
|
48
46
|
I03 = I03Constants()
|
|
49
47
|
PARAM = ExperimentParamConstants()
|
|
50
48
|
PLAN = PlanNameConstants()
|
|
51
49
|
WAIT = PlanGroupCheckpointConstants()
|
|
52
|
-
SIM = SimConstants()
|
|
53
|
-
TRIGGER = TriggerConstants()
|
|
54
50
|
CALLBACK_0MQ_PROXY_PORTS = (5577, 5578)
|
|
55
51
|
DESCRIPTORS = DocDescriptorNames()
|
|
56
52
|
CONFIG_SERVER_URL = (
|
|
@@ -6,13 +6,17 @@ from dodal.devices.aperturescatterguard import (
|
|
|
6
6
|
)
|
|
7
7
|
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
8
8
|
from dodal.devices.backlight import Backlight
|
|
9
|
-
from dodal.devices.
|
|
9
|
+
from dodal.devices.common_dcm import BaseDCM
|
|
10
|
+
from dodal.devices.detector.detector_motion import DetectorMotion
|
|
10
11
|
from dodal.devices.eiger import EigerDetector
|
|
11
12
|
from dodal.devices.fast_grid_scan import (
|
|
12
13
|
PandAFastGridScan,
|
|
13
14
|
ZebraFastGridScan,
|
|
14
15
|
)
|
|
15
16
|
from dodal.devices.flux import Flux
|
|
17
|
+
from dodal.devices.i03 import Beamstop
|
|
18
|
+
from dodal.devices.oav.oav_detector import OAV
|
|
19
|
+
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
16
20
|
from dodal.devices.robot import BartRobot
|
|
17
21
|
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
18
22
|
from dodal.devices.smargon import Smargon
|
|
@@ -24,22 +28,53 @@ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
|
|
|
24
28
|
from dodal.devices.zocalo import ZocaloResults
|
|
25
29
|
from ophyd_async.fastcs.panda import HDFPanda
|
|
26
30
|
|
|
31
|
+
from mx_bluesky.common.plans.common_flyscan_xray_centre_plan import (
|
|
32
|
+
FlyScanEssentialDevices,
|
|
33
|
+
)
|
|
34
|
+
|
|
27
35
|
|
|
28
36
|
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
29
|
-
class HyperionFlyScanXRayCentreComposite:
|
|
37
|
+
class HyperionFlyScanXRayCentreComposite(FlyScanEssentialDevices):
|
|
30
38
|
"""All devices which are directly or indirectly required by this plan"""
|
|
31
39
|
|
|
32
40
|
aperture_scatterguard: ApertureScatterguard
|
|
33
41
|
attenuator: BinaryFilterAttenuator
|
|
42
|
+
dcm: BaseDCM
|
|
43
|
+
eiger: EigerDetector
|
|
44
|
+
flux: Flux
|
|
45
|
+
s4_slit_gaps: S4SlitGaps
|
|
46
|
+
undulator: Undulator
|
|
47
|
+
synchrotron: Synchrotron
|
|
48
|
+
zebra: Zebra
|
|
49
|
+
zocalo: ZocaloResults
|
|
50
|
+
panda: HDFPanda
|
|
51
|
+
panda_fast_grid_scan: PandAFastGridScan
|
|
52
|
+
robot: BartRobot
|
|
53
|
+
sample_shutter: ZebraShutter
|
|
34
54
|
backlight: Backlight
|
|
35
|
-
|
|
55
|
+
xbpm_feedback: XBPMFeedback
|
|
56
|
+
zebra_fast_grid_scan: ZebraFastGridScan
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
60
|
+
class GridDetectThenXRayCentreComposite:
|
|
61
|
+
"""All devices which are directly or indirectly required by this plan"""
|
|
62
|
+
|
|
63
|
+
aperture_scatterguard: ApertureScatterguard
|
|
64
|
+
attenuator: BinaryFilterAttenuator
|
|
65
|
+
backlight: Backlight
|
|
66
|
+
beamstop: Beamstop
|
|
67
|
+
dcm: BaseDCM
|
|
68
|
+
detector_motion: DetectorMotion
|
|
36
69
|
eiger: EigerDetector
|
|
37
70
|
zebra_fast_grid_scan: ZebraFastGridScan
|
|
38
71
|
flux: Flux
|
|
39
|
-
|
|
72
|
+
oav: OAV
|
|
73
|
+
pin_tip_detection: PinTipDetection
|
|
40
74
|
smargon: Smargon
|
|
41
|
-
undulator: Undulator
|
|
42
75
|
synchrotron: Synchrotron
|
|
76
|
+
s4_slit_gaps: S4SlitGaps
|
|
77
|
+
undulator: Undulator
|
|
43
78
|
xbpm_feedback: XBPMFeedback
|
|
44
79
|
zebra: Zebra
|
|
45
80
|
zocalo: ZocaloResults
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from dodal.devices.detector import (
|
|
4
|
-
DetectorParams,
|
|
5
|
-
)
|
|
6
3
|
from dodal.devices.fast_grid_scan import (
|
|
7
4
|
PandAGridScanParams,
|
|
8
5
|
ZebraGridScanParams,
|
|
@@ -13,7 +10,6 @@ from mx_bluesky.common.parameters.gridscan import (
|
|
|
13
10
|
SpecifiedThreeDGridScan,
|
|
14
11
|
)
|
|
15
12
|
from mx_bluesky.hyperion.parameters.components import WithHyperionUDCFeatures
|
|
16
|
-
from mx_bluesky.hyperion.parameters.constants import CONST, I03Constants
|
|
17
13
|
|
|
18
14
|
|
|
19
15
|
class GridCommonWithHyperionDetectorParams(GridCommon, WithHyperionUDCFeatures):
|
|
@@ -23,34 +19,11 @@ class GridCommonWithHyperionDetectorParams(GridCommon, WithHyperionUDCFeatures):
|
|
|
23
19
|
# https://github.com/DiamondLightSource/hyperion/issues/1395"""
|
|
24
20
|
@property
|
|
25
21
|
def detector_params(self):
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
or
|
|
29
|
-
)
|
|
30
|
-
optional_args = {}
|
|
31
|
-
if self.run_number:
|
|
32
|
-
optional_args["run_number"] = self.run_number
|
|
33
|
-
assert self.detector_distance_mm is not None, (
|
|
34
|
-
"Detector distance must be filled before generating DetectorParams"
|
|
35
|
-
)
|
|
36
|
-
return DetectorParams(
|
|
37
|
-
detector_size_constants=I03Constants.DETECTOR,
|
|
38
|
-
expected_energy_ev=self.demand_energy_ev,
|
|
39
|
-
exposure_time_s=self.exposure_time_s,
|
|
40
|
-
directory=self.storage_directory,
|
|
41
|
-
prefix=self.file_name,
|
|
42
|
-
detector_distance=self.detector_distance_mm,
|
|
43
|
-
omega_start=self.omega_start_deg or 0,
|
|
44
|
-
omega_increment=0,
|
|
45
|
-
num_images_per_trigger=1,
|
|
46
|
-
num_triggers=self.num_images,
|
|
47
|
-
use_roi_mode=self.use_roi_mode,
|
|
48
|
-
det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
|
|
49
|
-
trigger_mode=self.trigger_mode,
|
|
50
|
-
enable_dev_shm=self.features.compare_cpu_and_gpu_zocalo
|
|
51
|
-
or self.features.use_gpu_results,
|
|
52
|
-
**optional_args,
|
|
22
|
+
params = super().detector_params
|
|
23
|
+
params.enable_dev_shm = (
|
|
24
|
+
self.features.compare_cpu_and_gpu_zocalo or self.features.use_gpu_results
|
|
53
25
|
)
|
|
26
|
+
return params
|
|
54
27
|
|
|
55
28
|
|
|
56
29
|
class HyperionSpecifiedThreeDGridScan(WithHyperionUDCFeatures, SpecifiedThreeDGridScan):
|
|
@@ -58,36 +31,14 @@ class HyperionSpecifiedThreeDGridScan(WithHyperionUDCFeatures, SpecifiedThreeDGr
|
|
|
58
31
|
|
|
59
32
|
# These detector params only exist so that we can properly select enable_dev_shm. Remove in
|
|
60
33
|
# https://github.com/DiamondLightSource/hyperion/issues/1395"""
|
|
34
|
+
|
|
61
35
|
@property
|
|
62
36
|
def detector_params(self):
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
or
|
|
66
|
-
)
|
|
67
|
-
optional_args = {}
|
|
68
|
-
if self.run_number:
|
|
69
|
-
optional_args["run_number"] = self.run_number
|
|
70
|
-
assert self.detector_distance_mm is not None, (
|
|
71
|
-
"Detector distance must be filled before generating DetectorParams"
|
|
72
|
-
)
|
|
73
|
-
return DetectorParams(
|
|
74
|
-
detector_size_constants=I03Constants.DETECTOR,
|
|
75
|
-
expected_energy_ev=self.demand_energy_ev,
|
|
76
|
-
exposure_time_s=self.exposure_time_s,
|
|
77
|
-
directory=self.storage_directory,
|
|
78
|
-
prefix=self.file_name,
|
|
79
|
-
detector_distance=self.detector_distance_mm,
|
|
80
|
-
omega_start=self.omega_start_deg or 0,
|
|
81
|
-
omega_increment=0,
|
|
82
|
-
num_images_per_trigger=1,
|
|
83
|
-
num_triggers=self.num_images,
|
|
84
|
-
use_roi_mode=self.use_roi_mode,
|
|
85
|
-
det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
|
|
86
|
-
trigger_mode=self.trigger_mode,
|
|
87
|
-
enable_dev_shm=self.features.compare_cpu_and_gpu_zocalo
|
|
88
|
-
or self.features.use_gpu_results,
|
|
89
|
-
**optional_args,
|
|
37
|
+
params = super().detector_params
|
|
38
|
+
params.enable_dev_shm = (
|
|
39
|
+
self.features.compare_cpu_and_gpu_zocalo or self.features.use_gpu_results
|
|
90
40
|
)
|
|
41
|
+
return params
|
|
91
42
|
|
|
92
43
|
# Relative to common grid scan, stub offsets are defined by config server
|
|
93
44
|
@property
|