mx-bluesky 1.4.4__py3-none-any.whl → 1.4.6__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 +9 -4
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +14 -3
- mx_bluesky/beamlines/i04/thawing_plan.py +9 -13
- mx_bluesky/beamlines/i24/serial/__init__.py +14 -0
- mx_bluesky/beamlines/i24/serial/dcid.py +3 -1
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +1 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +6 -3
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +11 -11
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +3 -3
- mx_bluesky/beamlines/i24/serial/log.py +0 -1
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +1 -1
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +3 -3
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +1 -1
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +2 -2
- mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +109 -0
- mx_bluesky/beamlines/i24/serial/write_nexus.py +2 -2
- mx_bluesky/common/device_setup_plans/setup_panda.py +9 -0
- mx_bluesky/common/external_interaction/callbacks/common/plan_reactive_callback.py +2 -2
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +11 -3
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_mapping.py +1 -1
- mx_bluesky/common/external_interaction/ispyb/exp_eye_store.py +6 -2
- mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +7 -0
- mx_bluesky/common/parameters/constants.py +16 -0
- mx_bluesky/common/parameters/gridscan.py +36 -1
- mx_bluesky/common/plans/do_fgs.py +4 -6
- mx_bluesky/common/plans/read_hardware.py +78 -0
- mx_bluesky/common/utils/context.py +68 -0
- mx_bluesky/common/utils/exceptions.py +2 -1
- mx_bluesky/{hyperion/experiment_plans/common → common}/xrc_result.py +16 -0
- mx_bluesky/definitions.py +4 -0
- mx_bluesky/hyperion/__main__.py +11 -42
- mx_bluesky/hyperion/device_setup_plans/setup_oav.py +5 -5
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +9 -8
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +2 -2
- mx_bluesky/hyperion/device_setup_plans/smargon.py +6 -6
- mx_bluesky/hyperion/device_setup_plans/utils.py +2 -2
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +14 -4
- mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +2 -6
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +0 -15
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +42 -93
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +14 -6
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +26 -21
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +11 -11
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +5 -9
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +1 -1
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +2 -4
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +10 -10
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +11 -18
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +2 -4
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +19 -10
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +2 -0
- mx_bluesky/hyperion/external_interaction/agamemnon.py +104 -0
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +19 -2
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +14 -9
- mx_bluesky/hyperion/external_interaction/config_server.py +13 -2
- mx_bluesky/hyperion/parameters/cli.py +1 -9
- mx_bluesky/hyperion/parameters/constants.py +6 -1
- mx_bluesky/hyperion/parameters/device_composites.py +49 -0
- mx_bluesky/hyperion/parameters/gridscan.py +5 -3
- mx_bluesky/hyperion/resources/panda/panda-gridscan.yaml +1006 -964
- mx_bluesky/hyperion/utils/__init__.py +1 -0
- mx_bluesky/hyperion/utils/context.py +0 -65
- mx_bluesky/hyperion/utils/validation.py +23 -20
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/METADATA +5 -4
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/RECORD +71 -66
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/WHEEL +1 -1
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/entry_points.txt +1 -0
- mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +0 -14
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +0 -54
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +0 -95
- /mx_bluesky/{hyperion/external_interaction/callbacks/common → beamlines/i24/serial/web_gui_plans}/__init__.py +0 -0
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dodal.devices.aperturescatterguard import ApertureValue
|
|
4
|
+
from dodal.devices.detector.det_dim_constants import EIGER2_X_9M_SIZE, EIGER2_X_16M_SIZE
|
|
5
|
+
from dodal.devices.detector.detector import DetectorParams
|
|
4
6
|
from dodal.devices.fast_grid_scan import (
|
|
5
7
|
ZebraGridScanParams,
|
|
6
8
|
)
|
|
9
|
+
from dodal.utils import get_beamline_name
|
|
7
10
|
from pydantic import Field, PrivateAttr
|
|
8
11
|
from scanspec.core import Path as ScanPath
|
|
9
12
|
from scanspec.specs import Line, Static
|
|
@@ -18,11 +21,12 @@ from mx_bluesky.common.parameters.components import (
|
|
|
18
21
|
XyzStarts,
|
|
19
22
|
)
|
|
20
23
|
from mx_bluesky.common.parameters.constants import (
|
|
24
|
+
DetectorParamConstants,
|
|
21
25
|
GridscanParamConstants,
|
|
22
26
|
HardwareConstants,
|
|
23
27
|
)
|
|
24
28
|
|
|
25
|
-
""
|
|
29
|
+
DETECTOR_SIZE_PER_BEAMLINE = {"i02-1": EIGER2_X_9M_SIZE, "dev": EIGER2_X_16M_SIZE}
|
|
26
30
|
|
|
27
31
|
|
|
28
32
|
class GridCommon(
|
|
@@ -146,3 +150,34 @@ class SpecifiedThreeDGridScan(
|
|
|
146
150
|
@property
|
|
147
151
|
def num_images(self) -> int:
|
|
148
152
|
return len(self.scan_points["sam_x"])
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def detector_params(self):
|
|
156
|
+
self.det_dist_to_beam_converter_path = (
|
|
157
|
+
self.det_dist_to_beam_converter_path
|
|
158
|
+
or DetectorParamConstants.BEAM_XY_LUT_PATH
|
|
159
|
+
)
|
|
160
|
+
optional_args = {}
|
|
161
|
+
if self.run_number:
|
|
162
|
+
optional_args["run_number"] = self.run_number
|
|
163
|
+
assert self.detector_distance_mm is not None, (
|
|
164
|
+
"Detector distance must be filled before generating DetectorParams"
|
|
165
|
+
)
|
|
166
|
+
return DetectorParams(
|
|
167
|
+
detector_size_constants=DETECTOR_SIZE_PER_BEAMLINE[
|
|
168
|
+
get_beamline_name("dev")
|
|
169
|
+
],
|
|
170
|
+
expected_energy_ev=self.demand_energy_ev,
|
|
171
|
+
exposure_time=self.exposure_time_s,
|
|
172
|
+
directory=self.storage_directory,
|
|
173
|
+
prefix=self.file_name,
|
|
174
|
+
detector_distance=self.detector_distance_mm,
|
|
175
|
+
omega_start=self.omega_start_deg or 0,
|
|
176
|
+
omega_increment=0,
|
|
177
|
+
num_images_per_trigger=1,
|
|
178
|
+
num_triggers=self.num_images,
|
|
179
|
+
use_roi_mode=self.use_roi_mode,
|
|
180
|
+
det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
|
|
181
|
+
trigger_mode=self.trigger_mode,
|
|
182
|
+
**optional_args,
|
|
183
|
+
)
|
|
@@ -14,12 +14,10 @@ from dodal.log import LOGGER
|
|
|
14
14
|
from dodal.plan_stubs.check_topup import check_topup_and_wait_if_necessary
|
|
15
15
|
from scanspec.core import AxesPoints, Axis
|
|
16
16
|
|
|
17
|
-
from mx_bluesky.common.device_setup_plans.read_hardware_for_setup import (
|
|
18
|
-
read_hardware_for_zocalo,
|
|
19
|
-
)
|
|
20
17
|
from mx_bluesky.common.parameters.constants import (
|
|
21
18
|
PlanNameConstants,
|
|
22
19
|
)
|
|
20
|
+
from mx_bluesky.common.plans.read_hardware import read_hardware_for_zocalo
|
|
23
21
|
from mx_bluesky.common.utils.tracing import TRACER
|
|
24
22
|
|
|
25
23
|
|
|
@@ -30,7 +28,7 @@ def _wait_for_zocalo_to_stage_then_do_fgs(
|
|
|
30
28
|
during_collection_plan: Callable[[], MsgGenerator] | None = None,
|
|
31
29
|
):
|
|
32
30
|
expected_images = yield from bps.rd(grid_scan_device.expected_images)
|
|
33
|
-
exposure_sec_per_image = yield from bps.rd(detector.cam.acquire_time) # type: ignore #
|
|
31
|
+
exposure_sec_per_image = yield from bps.rd(detector.cam.acquire_time) # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
|
|
34
32
|
LOGGER.info("waiting for topup if necessary...")
|
|
35
33
|
yield from check_topup_and_wait_if_necessary(
|
|
36
34
|
synchrotron,
|
|
@@ -101,8 +99,8 @@ def kickoff_and_complete_gridscan(
|
|
|
101
99
|
}
|
|
102
100
|
)
|
|
103
101
|
@bpp.contingency_decorator(
|
|
104
|
-
except_plan=lambda e: (yield from bps.stop(detector)), # type: ignore #
|
|
105
|
-
else_plan=lambda: (yield from bps.unstage(detector)),
|
|
102
|
+
except_plan=lambda e: (yield from bps.stop(detector)), # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
|
|
103
|
+
else_plan=lambda: (yield from bps.unstage(detector)),
|
|
106
104
|
)
|
|
107
105
|
def _decorated_do_fgs():
|
|
108
106
|
yield from _wait_for_zocalo_to_stage_then_do_fgs(
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import bluesky.plan_stubs as bps
|
|
4
|
+
from bluesky.protocols import Readable
|
|
5
|
+
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
6
|
+
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
7
|
+
from dodal.devices.dcm import DCM
|
|
8
|
+
from dodal.devices.eiger import EigerDetector
|
|
9
|
+
from dodal.devices.flux import Flux
|
|
10
|
+
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
11
|
+
from dodal.devices.smargon import Smargon
|
|
12
|
+
from dodal.devices.synchrotron import Synchrotron
|
|
13
|
+
from dodal.devices.undulator import Undulator
|
|
14
|
+
|
|
15
|
+
from mx_bluesky.common.parameters.constants import (
|
|
16
|
+
DocDescriptorNames,
|
|
17
|
+
)
|
|
18
|
+
from mx_bluesky.common.utils.log import LOGGER
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def read_hardware_plan(
|
|
22
|
+
signals: list[Readable],
|
|
23
|
+
event_name: str,
|
|
24
|
+
):
|
|
25
|
+
LOGGER.info(f"Reading status of beamline for event, {event_name}")
|
|
26
|
+
yield from bps.create(name=event_name)
|
|
27
|
+
for signal in signals:
|
|
28
|
+
yield from bps.read(signal)
|
|
29
|
+
yield from bps.save()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def read_hardware_for_zocalo(detector: EigerDetector):
|
|
33
|
+
""" "
|
|
34
|
+
If the RunEngine is subscribed to the ZocaloCallback, this plan will also trigger zocalo.
|
|
35
|
+
"""
|
|
36
|
+
yield from read_hardware_plan(
|
|
37
|
+
[detector.odin.file_writer.id], # type: ignore
|
|
38
|
+
DocDescriptorNames.ZOCALO_HW_READ,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def standard_read_hardware_pre_collection(
|
|
43
|
+
undulator: Undulator,
|
|
44
|
+
synchrotron: Synchrotron,
|
|
45
|
+
s4_slit_gaps: S4SlitGaps,
|
|
46
|
+
dcm: DCM,
|
|
47
|
+
smargon: Smargon,
|
|
48
|
+
):
|
|
49
|
+
LOGGER.info("Reading status of beamline for callbacks, pre collection.")
|
|
50
|
+
signals_to_read_pre_flyscan = [
|
|
51
|
+
undulator.current_gap,
|
|
52
|
+
synchrotron.synchrotron_mode,
|
|
53
|
+
s4_slit_gaps,
|
|
54
|
+
smargon,
|
|
55
|
+
dcm.energy_in_kev,
|
|
56
|
+
]
|
|
57
|
+
yield from read_hardware_plan(
|
|
58
|
+
signals_to_read_pre_flyscan, DocDescriptorNames.HARDWARE_READ_PRE
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def standard_read_hardware_during_collection(
|
|
63
|
+
aperture_scatterguard: ApertureScatterguard,
|
|
64
|
+
attenuator: BinaryFilterAttenuator,
|
|
65
|
+
flux: Flux,
|
|
66
|
+
dcm: DCM,
|
|
67
|
+
detector: EigerDetector,
|
|
68
|
+
):
|
|
69
|
+
signals_to_read_during_collection = [
|
|
70
|
+
aperture_scatterguard,
|
|
71
|
+
attenuator.actual_transmission,
|
|
72
|
+
flux.flux_reading,
|
|
73
|
+
dcm.energy_in_kev,
|
|
74
|
+
detector.bit_depth,
|
|
75
|
+
]
|
|
76
|
+
yield from read_hardware_plan(
|
|
77
|
+
signals_to_read_during_collection, DocDescriptorNames.HARDWARE_READ_DURING
|
|
78
|
+
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from typing import Any, ClassVar, Protocol, TypeVar, get_type_hints
|
|
3
|
+
|
|
4
|
+
from blueapi.core import BlueskyContext
|
|
5
|
+
from blueapi.core.bluesky_types import Device
|
|
6
|
+
|
|
7
|
+
from mx_bluesky.common.utils.log import LOGGER
|
|
8
|
+
|
|
9
|
+
T = TypeVar("T", bound=Device)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _IsDataclass(Protocol):
|
|
13
|
+
"""Protocol followed by any dataclass"""
|
|
14
|
+
|
|
15
|
+
__dataclass_fields__: ClassVar[dict]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
DT = TypeVar("DT", bound=_IsDataclass)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def find_device_in_context(
|
|
22
|
+
context: BlueskyContext,
|
|
23
|
+
name: str,
|
|
24
|
+
# Typing in here is wrong (see https://github.com/microsoft/pyright/issues/7228#issuecomment-1934500232)
|
|
25
|
+
# but this whole thing will go away when we do https://github.com/DiamondLightSource/hyperion/issues/868
|
|
26
|
+
expected_type: type[T] = Device, # type: ignore
|
|
27
|
+
) -> T:
|
|
28
|
+
LOGGER.debug(f"Looking for device {name} of type {expected_type} in context")
|
|
29
|
+
|
|
30
|
+
device = context.find_device(name)
|
|
31
|
+
if device is None:
|
|
32
|
+
raise ValueError(
|
|
33
|
+
f"Cannot find device named '{name}' in bluesky context {context.devices}."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if not isinstance(device, expected_type):
|
|
37
|
+
raise ValueError(
|
|
38
|
+
f"Found device named '{name}' and expected it to be a '{expected_type}' but it was a '{device.__class__.__name__}'"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
LOGGER.debug(f"Found matching device {device}")
|
|
42
|
+
return device
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def device_composite_from_context(context: BlueskyContext, dc: type[DT]) -> DT:
|
|
46
|
+
"""
|
|
47
|
+
Initializes all of the devices referenced in a given dataclass from a provided
|
|
48
|
+
context, checking that the types of devices returned by the context are compatible
|
|
49
|
+
with the type annotations of the dataclass.
|
|
50
|
+
|
|
51
|
+
Note that if the context was not created with `wait_for_connection=True` devices may
|
|
52
|
+
still be unconnected.
|
|
53
|
+
"""
|
|
54
|
+
LOGGER.debug(
|
|
55
|
+
f"Attempting to initialize devices referenced in dataclass {dc} from blueapi context"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
devices: dict[str, Any] = {}
|
|
59
|
+
dc_type_hints: dict[str, Any] = get_type_hints(dc)
|
|
60
|
+
|
|
61
|
+
for field in dataclasses.fields(dc):
|
|
62
|
+
device = find_device_in_context(
|
|
63
|
+
context, field.name, expected_type=dc_type_hints.get(field.name, Device)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
devices[field.name] = device
|
|
67
|
+
|
|
68
|
+
return dc(**devices)
|
|
@@ -39,7 +39,8 @@ T = TypeVar("T")
|
|
|
39
39
|
class CrystalNotFoundException(SampleException):
|
|
40
40
|
"""Raised if grid detection completed normally but no crystal was found."""
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
def __init__(self, *args):
|
|
43
|
+
super().__init__("Diffraction not found, skipping sample.")
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
def catch_exception_and_warn(
|
|
@@ -5,6 +5,8 @@ from collections.abc import Callable, Sequence
|
|
|
5
5
|
from functools import partial
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
|
+
from bluesky.callbacks import CallbackBase
|
|
9
|
+
from event_model import RunStart
|
|
8
10
|
|
|
9
11
|
from mx_bluesky.common.parameters.components import (
|
|
10
12
|
MultiXtalSelection,
|
|
@@ -12,6 +14,20 @@ from mx_bluesky.common.parameters.components import (
|
|
|
12
14
|
)
|
|
13
15
|
|
|
14
16
|
|
|
17
|
+
class XRayCentreEventHandler(CallbackBase):
|
|
18
|
+
def __init__(self):
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.xray_centre_results: Sequence[XRayCentreResult] | None = None
|
|
21
|
+
|
|
22
|
+
def start(self, doc: RunStart) -> RunStart | None:
|
|
23
|
+
if "xray_centre_results" in doc:
|
|
24
|
+
self.xray_centre_results = [
|
|
25
|
+
XRayCentreResult(**result_dict)
|
|
26
|
+
for result_dict in doc["xray_centre_results"] # type: ignore
|
|
27
|
+
]
|
|
28
|
+
return doc
|
|
29
|
+
|
|
30
|
+
|
|
15
31
|
@dataclasses.dataclass
|
|
16
32
|
class XRayCentreResult:
|
|
17
33
|
"""
|
mx_bluesky/hyperion/__main__.py
CHANGED
|
@@ -4,6 +4,7 @@ import threading
|
|
|
4
4
|
from collections.abc import Callable
|
|
5
5
|
from dataclasses import asdict
|
|
6
6
|
from queue import Queue
|
|
7
|
+
from sys import argv
|
|
7
8
|
from traceback import format_exception
|
|
8
9
|
from typing import Any
|
|
9
10
|
|
|
@@ -37,11 +38,8 @@ from mx_bluesky.hyperion.experiment_plans.experiment_registry import (
|
|
|
37
38
|
PLAN_REGISTRY,
|
|
38
39
|
PlanNotFound,
|
|
39
40
|
)
|
|
40
|
-
from mx_bluesky.hyperion.external_interaction.
|
|
41
|
-
|
|
42
|
-
)
|
|
43
|
-
from mx_bluesky.hyperion.external_interaction.callbacks.common.callback_util import (
|
|
44
|
-
CallbacksFactory,
|
|
41
|
+
from mx_bluesky.hyperion.external_interaction.agamemnon import (
|
|
42
|
+
update_params_from_agamemnon,
|
|
45
43
|
)
|
|
46
44
|
from mx_bluesky.hyperion.parameters.cli import parse_cli_args
|
|
47
45
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
@@ -56,7 +54,6 @@ class Command:
|
|
|
56
54
|
devices: Any | None = None
|
|
57
55
|
experiment: Callable[[Any, Any], MsgGenerator] | None = None
|
|
58
56
|
parameters: MxBlueskyParameters | None = None
|
|
59
|
-
callbacks: CallbacksFactory | None = None
|
|
60
57
|
|
|
61
58
|
|
|
62
59
|
@dataclass
|
|
@@ -88,7 +85,6 @@ class BlueskyRunner:
|
|
|
88
85
|
RE: RunEngine,
|
|
89
86
|
context: BlueskyContext,
|
|
90
87
|
skip_startup_connection=False,
|
|
91
|
-
use_external_callbacks: bool = False,
|
|
92
88
|
) -> None:
|
|
93
89
|
self.command_queue: Queue[Command] = Queue()
|
|
94
90
|
self.current_status: StatusAndMessage = StatusAndMessage(Status.IDLE)
|
|
@@ -99,15 +95,12 @@ class BlueskyRunner:
|
|
|
99
95
|
|
|
100
96
|
self.RE = RE
|
|
101
97
|
self.context = context
|
|
102
|
-
self.subscribed_per_plan_callbacks: list[int] = []
|
|
103
98
|
RE.subscribe(self.aperture_change_callback)
|
|
104
99
|
RE.subscribe(self.logging_uid_tag_callback)
|
|
105
100
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
self.publisher = Publisher(f"localhost:{CONST.CALLBACK_0MQ_PROXY_PORTS[0]}")
|
|
110
|
-
RE.subscribe(self.publisher)
|
|
101
|
+
LOGGER.info("Connecting to external callback ZMQ proxy...")
|
|
102
|
+
self.publisher = Publisher(f"localhost:{CONST.CALLBACK_0MQ_PROXY_PORTS[0]}")
|
|
103
|
+
RE.subscribe(self.publisher)
|
|
111
104
|
|
|
112
105
|
if VERBOSE_EVENT_LOGGING:
|
|
113
106
|
RE.subscribe(VerbosePlanExecutionLoggingCallback())
|
|
@@ -123,7 +116,6 @@ class BlueskyRunner:
|
|
|
123
116
|
experiment: Callable,
|
|
124
117
|
parameters: MxBlueskyParameters,
|
|
125
118
|
plan_name: str,
|
|
126
|
-
callbacks: CallbacksFactory | None,
|
|
127
119
|
) -> StatusAndMessage:
|
|
128
120
|
LOGGER.info(f"Started with parameters: {parameters.model_dump_json(indent=2)}")
|
|
129
121
|
|
|
@@ -142,7 +134,6 @@ class BlueskyRunner:
|
|
|
142
134
|
devices=devices,
|
|
143
135
|
experiment=experiment,
|
|
144
136
|
parameters=parameters,
|
|
145
|
-
callbacks=callbacks,
|
|
146
137
|
)
|
|
147
138
|
)
|
|
148
139
|
return StatusAndMessage(Status.SUCCESS)
|
|
@@ -181,17 +172,6 @@ class BlueskyRunner:
|
|
|
181
172
|
if command.experiment is None:
|
|
182
173
|
raise ValueError("No experiment provided for START")
|
|
183
174
|
try:
|
|
184
|
-
if (
|
|
185
|
-
not self.use_external_callbacks
|
|
186
|
-
and command.callbacks
|
|
187
|
-
and (cbs := command.callbacks())
|
|
188
|
-
):
|
|
189
|
-
LOGGER.info(
|
|
190
|
-
f"Using callbacks for this plan: {not self.use_external_callbacks} - {cbs}"
|
|
191
|
-
)
|
|
192
|
-
self.subscribed_per_plan_callbacks += [
|
|
193
|
-
self.RE.subscribe(cb) for cb in cbs
|
|
194
|
-
]
|
|
195
175
|
with TRACER.start_span("do_run"):
|
|
196
176
|
self.RE(command.experiment(command.devices, command.parameters))
|
|
197
177
|
|
|
@@ -212,11 +192,6 @@ class BlueskyRunner:
|
|
|
212
192
|
self.last_run_aborted = False
|
|
213
193
|
else:
|
|
214
194
|
self.current_status = make_error_status_and_message(exception)
|
|
215
|
-
finally:
|
|
216
|
-
[
|
|
217
|
-
self.RE.unsubscribe(cb)
|
|
218
|
-
for cb in self.subscribed_per_plan_callbacks
|
|
219
|
-
]
|
|
220
195
|
|
|
221
196
|
|
|
222
197
|
def compose_start_args(context: BlueskyContext, plan_name: str, action: Actions):
|
|
@@ -225,7 +200,6 @@ def compose_start_args(context: BlueskyContext, plan_name: str, action: Actions)
|
|
|
225
200
|
raise PlanNotFound(f"Experiment plan '{plan_name}' not found in registry.")
|
|
226
201
|
|
|
227
202
|
experiment_internal_param_type = experiment_registry_entry.get("param_type")
|
|
228
|
-
callback_type = experiment_registry_entry.get("callback_collection_type")
|
|
229
203
|
plan = context.plan_functions.get(plan_name)
|
|
230
204
|
if experiment_internal_param_type is None:
|
|
231
205
|
raise PlanNotFound(
|
|
@@ -237,13 +211,14 @@ def compose_start_args(context: BlueskyContext, plan_name: str, action: Actions)
|
|
|
237
211
|
)
|
|
238
212
|
try:
|
|
239
213
|
parameters = experiment_internal_param_type(**json.loads(request.data))
|
|
214
|
+
parameters = update_params_from_agamemnon(parameters)
|
|
240
215
|
if parameters.model_extra:
|
|
241
216
|
raise ValueError(f"Extra fields not allowed {parameters.model_extra}")
|
|
242
217
|
except Exception as e:
|
|
243
218
|
raise ValueError(
|
|
244
219
|
f"Supplied parameters don't match the plan for this endpoint {request.data}, for plan {plan_name}"
|
|
245
220
|
) from e
|
|
246
|
-
return plan, parameters, plan_name
|
|
221
|
+
return plan, parameters, plan_name
|
|
247
222
|
|
|
248
223
|
|
|
249
224
|
class RunExperiment(Resource):
|
|
@@ -256,12 +231,10 @@ class RunExperiment(Resource):
|
|
|
256
231
|
status_and_message = StatusAndMessage(Status.FAILED, f"{action} not understood")
|
|
257
232
|
if action == Actions.START.value:
|
|
258
233
|
try:
|
|
259
|
-
plan, params, plan_name
|
|
234
|
+
plan, params, plan_name = compose_start_args(
|
|
260
235
|
self.context, plan_name, action
|
|
261
236
|
)
|
|
262
|
-
status_and_message = self.runner.start(
|
|
263
|
-
plan, params, plan_name, callback_type
|
|
264
|
-
)
|
|
237
|
+
status_and_message = self.runner.start(plan, params, plan_name)
|
|
265
238
|
except Exception as e:
|
|
266
239
|
status_and_message = make_error_status_and_message(e)
|
|
267
240
|
LOGGER.error(format_exception(e))
|
|
@@ -312,7 +285,6 @@ def create_app(
|
|
|
312
285
|
test_config=None,
|
|
313
286
|
RE: RunEngine = RunEngine({}),
|
|
314
287
|
skip_startup_connection: bool = False,
|
|
315
|
-
use_external_callbacks: bool = False,
|
|
316
288
|
) -> tuple[Flask, BlueskyRunner]:
|
|
317
289
|
context = setup_context(
|
|
318
290
|
wait_for_connection=not skip_startup_connection,
|
|
@@ -320,7 +292,6 @@ def create_app(
|
|
|
320
292
|
runner = BlueskyRunner(
|
|
321
293
|
RE,
|
|
322
294
|
context=context,
|
|
323
|
-
use_external_callbacks=use_external_callbacks,
|
|
324
295
|
skip_startup_connection=skip_startup_connection,
|
|
325
296
|
)
|
|
326
297
|
app = Flask(__name__)
|
|
@@ -350,11 +321,9 @@ def create_targets():
|
|
|
350
321
|
do_default_logging_setup(
|
|
351
322
|
CONST.LOG_FILE_NAME, CONST.GRAYLOG_PORT, dev_mode=args.dev_mode
|
|
352
323
|
)
|
|
353
|
-
|
|
354
|
-
setup_callback_logging(args.dev_mode)
|
|
324
|
+
LOGGER.info(f"Hyperion launched with args:{argv}")
|
|
355
325
|
app, runner = create_app(
|
|
356
326
|
skip_startup_connection=args.skip_startup_connection,
|
|
357
|
-
use_external_callbacks=args.use_external_callbacks,
|
|
358
327
|
)
|
|
359
328
|
return app, runner, hyperion_port, args.dev_mode
|
|
360
329
|
|
|
@@ -55,14 +55,14 @@ def setup_pin_tip_detection_params(
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def setup_general_oav_params(oav: OAV, parameters: OAVParameters):
|
|
58
|
-
yield from set_using_group(oav.cam.color_mode, ColorMode.RGB1)
|
|
59
|
-
yield from set_using_group(oav.cam.acquire_period, parameters.acquire_period)
|
|
60
|
-
yield from set_using_group(oav.cam.acquire_time, parameters.exposure)
|
|
61
|
-
yield from set_using_group(oav.cam.gain, parameters.gain)
|
|
58
|
+
yield from set_using_group(oav.cam.color_mode, ColorMode.RGB1)
|
|
59
|
+
yield from set_using_group(oav.cam.acquire_period, parameters.acquire_period)
|
|
60
|
+
yield from set_using_group(oav.cam.acquire_time, parameters.exposure)
|
|
61
|
+
yield from set_using_group(oav.cam.gain, parameters.gain)
|
|
62
62
|
|
|
63
63
|
zoom_level_str = f"{float(parameters.zoom)}x"
|
|
64
64
|
yield from bps.abs_set(
|
|
65
|
-
oav.zoom_controller,
|
|
65
|
+
oav.zoom_controller,
|
|
66
66
|
zoom_level_str,
|
|
67
67
|
wait=True,
|
|
68
68
|
)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from importlib import resources
|
|
4
3
|
from pathlib import Path
|
|
5
4
|
|
|
6
5
|
import bluesky.plan_stubs as bps
|
|
@@ -8,15 +7,15 @@ from bluesky.utils import MsgGenerator
|
|
|
8
7
|
from dodal.common.beamlines.beamline_utils import get_path_provider
|
|
9
8
|
from dodal.devices.fast_grid_scan import PandAGridScanParams
|
|
10
9
|
from dodal.devices.smargon import Smargon
|
|
11
|
-
from ophyd_async.core import load_device
|
|
12
10
|
from ophyd_async.fastcs.panda import (
|
|
13
11
|
HDFPanda,
|
|
14
12
|
SeqTable,
|
|
15
13
|
SeqTrigger,
|
|
16
14
|
)
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
from mx_bluesky.common.device_setup_plans.setup_panda import load_panda_from_yaml
|
|
19
17
|
from mx_bluesky.common.utils.log import LOGGER
|
|
18
|
+
from mx_bluesky.hyperion.parameters.constants import DeviceSettingsConstants
|
|
20
19
|
|
|
21
20
|
MM_TO_ENCODER_COUNTS = 200000
|
|
22
21
|
GENERAL_TIMEOUT = 60
|
|
@@ -76,7 +75,8 @@ def _get_seq_table(
|
|
|
76
75
|
|
|
77
76
|
num_pulses = parameters.x_steps
|
|
78
77
|
|
|
79
|
-
|
|
78
|
+
# Integer precision here is 1e-6s, so casting is safe
|
|
79
|
+
delay_between_pulses = int(time_between_steps_ms * TICKS_PER_MS)
|
|
80
80
|
|
|
81
81
|
assert delay_between_pulses > PULSE_WIDTH_US
|
|
82
82
|
|
|
@@ -145,10 +145,11 @@ def setup_panda_for_flyscan(
|
|
|
145
145
|
|
|
146
146
|
yield from bps.stage(panda, group="panda-config")
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
yield from load_panda_from_yaml(
|
|
149
|
+
DeviceSettingsConstants.PANDA_FLYSCAN_SETTINGS_DIR,
|
|
150
|
+
DeviceSettingsConstants.PANDA_FLYSCAN_SETTINGS_FILENAME,
|
|
151
|
+
panda,
|
|
152
|
+
)
|
|
152
153
|
|
|
153
154
|
initial_x = yield from bps.rd(smargon.x.user_readback)
|
|
154
155
|
initial_y = yield from bps.rd(smargon.y.user_readback)
|
|
@@ -47,7 +47,7 @@ def bluesky_retry(func: Callable):
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
def arm_zebra(zebra: Zebra):
|
|
50
|
-
yield from bps.abs_set(zebra.pc.arm, ArmDemand.ARM, wait=True)
|
|
50
|
+
yield from bps.abs_set(zebra.pc.arm, ArmDemand.ARM, wait=True)
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
def tidy_up_zebra_after_rotation_scan(
|
|
@@ -56,7 +56,7 @@ def tidy_up_zebra_after_rotation_scan(
|
|
|
56
56
|
group="tidy_up_zebra_after_rotation",
|
|
57
57
|
wait=True,
|
|
58
58
|
):
|
|
59
|
-
yield from bps.abs_set(zebra.pc.arm, ArmDemand.DISARM, group=group)
|
|
59
|
+
yield from bps.abs_set(zebra.pc.arm, ArmDemand.DISARM, group=group)
|
|
60
60
|
yield from bps.abs_set(
|
|
61
61
|
zebra_shutter.control_mode, ZebraShutterControl.MANUAL, group=group
|
|
62
62
|
)
|
|
@@ -16,10 +16,10 @@ def move_smargon_warn_on_out_of_range(
|
|
|
16
16
|
"Pin tip centring failed - pin too long/short/bent and out of range"
|
|
17
17
|
)
|
|
18
18
|
yield from bps.mv(
|
|
19
|
-
smargon.x,
|
|
20
|
-
position[0],
|
|
21
|
-
smargon.y,
|
|
22
|
-
position[1],
|
|
23
|
-
smargon.z,
|
|
24
|
-
position[2],
|
|
19
|
+
smargon.x,
|
|
20
|
+
position[0],
|
|
21
|
+
smargon.y,
|
|
22
|
+
position[1],
|
|
23
|
+
smargon.z,
|
|
24
|
+
position[2],
|
|
25
25
|
)
|
|
@@ -44,7 +44,7 @@ def start_preparing_data_collection_then_do_plan(
|
|
|
44
44
|
"""
|
|
45
45
|
|
|
46
46
|
def wrapped_plan():
|
|
47
|
-
yield from bps.abs_set(eiger.do_arm, 1, group=group) # type: ignore #
|
|
47
|
+
yield from bps.abs_set(eiger.do_arm, 1, group=group) # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
|
|
48
48
|
if detector_distance_mm:
|
|
49
49
|
yield from set_detector_z_position(
|
|
50
50
|
detector_motion, detector_distance_mm, group
|
|
@@ -55,5 +55,5 @@ def start_preparing_data_collection_then_do_plan(
|
|
|
55
55
|
yield from check_beamstop(beamstop)
|
|
56
56
|
yield from bpp.contingency_wrapper(
|
|
57
57
|
wrapped_plan(),
|
|
58
|
-
except_plan=lambda e: (yield from bps.stop(eiger)), # type: ignore #
|
|
58
|
+
except_plan=lambda e: (yield from bps.stop(eiger)), # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
|
|
59
59
|
)
|
|
@@ -2,6 +2,8 @@ from bluesky import plan_stubs as bps
|
|
|
2
2
|
from bluesky.preprocessors import finalize_wrapper
|
|
3
3
|
from bluesky.utils import make_decorator
|
|
4
4
|
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
5
|
+
from dodal.devices.dcm import DCM
|
|
6
|
+
from dodal.devices.undulator import Undulator
|
|
5
7
|
from dodal.devices.xbpm_feedback import Pause, XBPMFeedback
|
|
6
8
|
|
|
7
9
|
from mx_bluesky.common.utils.log import LOGGER
|
|
@@ -23,14 +25,14 @@ def _check_and_pause_feedback(
|
|
|
23
25
|
turning XBPM feedback off.
|
|
24
26
|
|
|
25
27
|
"""
|
|
26
|
-
yield from bps.mv(attenuator, 1.0)
|
|
28
|
+
yield from bps.mv(attenuator, 1.0)
|
|
27
29
|
LOGGER.info("Waiting for XBPM feedback to be stable")
|
|
28
30
|
yield from bps.trigger(xbpm_feedback, wait=True)
|
|
29
31
|
LOGGER.info(
|
|
30
32
|
f"XPBM feedback in position, pausing and setting transmission to {desired_transmission_fraction}"
|
|
31
33
|
)
|
|
32
|
-
yield from bps.mv(xbpm_feedback.pause_feedback, Pause.PAUSE)
|
|
33
|
-
yield from bps.mv(attenuator, desired_transmission_fraction)
|
|
34
|
+
yield from bps.mv(xbpm_feedback.pause_feedback, Pause.PAUSE)
|
|
35
|
+
yield from bps.mv(attenuator, desired_transmission_fraction)
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
def _unpause_xbpm_feedback_and_set_transmission_to_1(
|
|
@@ -44,13 +46,15 @@ def _unpause_xbpm_feedback_and_set_transmission_to_1(
|
|
|
44
46
|
the beam in position
|
|
45
47
|
attenuator (BinaryFilterAttenuator): The attenuator used to set transmission
|
|
46
48
|
"""
|
|
47
|
-
yield from bps.mv(xbpm_feedback.pause_feedback, Pause.RUN, attenuator, 1.0)
|
|
49
|
+
yield from bps.mv(xbpm_feedback.pause_feedback, Pause.RUN, attenuator, 1.0)
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
def transmission_and_xbpm_feedback_for_collection_wrapper(
|
|
51
53
|
plan,
|
|
54
|
+
undulator: Undulator,
|
|
52
55
|
xbpm_feedback: XBPMFeedback,
|
|
53
56
|
attenuator: BinaryFilterAttenuator,
|
|
57
|
+
dcm: DCM,
|
|
54
58
|
desired_transmission_fraction: float,
|
|
55
59
|
):
|
|
56
60
|
"""Sets the transmission for the data collection, ensuring the xbpm feedback is valid
|
|
@@ -66,6 +70,9 @@ def transmission_and_xbpm_feedback_for_collection_wrapper(
|
|
|
66
70
|
mostly accounts for slow thermal drift so it is safe to assume that the beam is
|
|
67
71
|
stable during a collection.
|
|
68
72
|
|
|
73
|
+
In the case of a beam dump, undulator gap may not return. Therefore, we check here
|
|
74
|
+
that the undulator gap is correct after XBPM is stable, and before collection.
|
|
75
|
+
|
|
69
76
|
Args:
|
|
70
77
|
plan: The plan performing the data collection
|
|
71
78
|
xbpm_feedback (XBPMFeedback): The XBPM device that is responsible for keeping
|
|
@@ -78,6 +85,9 @@ def transmission_and_xbpm_feedback_for_collection_wrapper(
|
|
|
78
85
|
yield from _check_and_pause_feedback(
|
|
79
86
|
xbpm_feedback, attenuator, desired_transmission_fraction
|
|
80
87
|
)
|
|
88
|
+
# Verify Undulator gap is correct, as may not be after a beam dump
|
|
89
|
+
energy_in_kev = yield from bps.rd(dcm.energy_in_kev.user_readback)
|
|
90
|
+
yield from bps.abs_set(undulator, energy_in_kev, wait=True)
|
|
81
91
|
return (yield from plan)
|
|
82
92
|
|
|
83
93
|
return (
|
|
@@ -6,8 +6,8 @@ from dodal.devices.smargon import Smargon, StubPosition
|
|
|
6
6
|
|
|
7
7
|
from mx_bluesky.common.utils.log import LOGGER
|
|
8
8
|
from mx_bluesky.common.utils.tracing import TRACER
|
|
9
|
+
from mx_bluesky.common.xrc_result import XRayCentreResult
|
|
9
10
|
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import move_x_y_z
|
|
10
|
-
from mx_bluesky.hyperion.experiment_plans.common.xrc_result import XRayCentreResult
|
|
11
11
|
from mx_bluesky.hyperion.parameters.gridscan import HyperionSpecifiedThreeDGridScan
|
|
12
12
|
|
|
13
13
|
|
|
@@ -43,11 +43,7 @@ def change_aperture_then_move_to_xtal(
|
|
|
43
43
|
# https://github.com/DiamondLightSource/mx-bluesky/issues/552
|
|
44
44
|
if parameters and parameters.FGS_params.set_stub_offsets:
|
|
45
45
|
LOGGER.info("Recentring smargon co-ordinate system to this point.")
|
|
46
|
-
yield from bps.mv(
|
|
47
|
-
# See: https://github.com/bluesky/bluesky/issues/1809
|
|
48
|
-
smargon.stub_offsets, # type: ignore
|
|
49
|
-
StubPosition.CURRENT_AS_CENTER, # type: ignore
|
|
50
|
-
)
|
|
46
|
+
yield from bps.mv(smargon.stub_offsets, StubPosition.CURRENT_AS_CENTER)
|
|
51
47
|
|
|
52
48
|
|
|
53
49
|
def set_aperture_for_bbox_mm(
|