mx-bluesky 1.2.0__py3-none-any.whl → 1.4.1a0__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/__init__.py +8 -3
- mx_bluesky/__main__.py +12 -7
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/i04/callbacks/murko_callback.py +14 -4
- mx_bluesky/beamlines/i04/thawing_plan.py +49 -11
- mx_bluesky/beamlines/i24/serial/__init__.py +3 -0
- mx_bluesky/beamlines/i24/serial/dcid.py +19 -21
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +69 -91
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +2 -5
- mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +111 -143
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +141 -222
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -216
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +18 -17
- mx_bluesky/beamlines/i24/serial/log.py +58 -49
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +0 -1
- mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
- mx_bluesky/beamlines/i24/serial/run_extruder.sh +30 -5
- mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +30 -5
- mx_bluesky/beamlines/i24/serial/run_serial.py +24 -8
- mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +79 -81
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +9 -20
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +26 -28
- mx_bluesky/beamlines/i24/serial/write_nexus.py +11 -11
- mx_bluesky/common/__init__.py +0 -0
- mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +14 -0
- mx_bluesky/common/external_interaction/config_server.py +46 -0
- mx_bluesky/common/parameters/components.py +258 -0
- mx_bluesky/common/parameters/constants.py +138 -0
- mx_bluesky/common/parameters/gridscan.py +94 -0
- mx_bluesky/common/parameters/robot_load.py +16 -0
- mx_bluesky/common/plans/__init__.py +1 -0
- mx_bluesky/common/plans/do_fgs.py +121 -0
- mx_bluesky/common/utils/log.py +118 -0
- mx_bluesky/{hyperion → common/utils}/tracing.py +2 -2
- mx_bluesky/hyperion/__main__.py +13 -10
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +31 -26
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +6 -12
- mx_bluesky/hyperion/device_setup_plans/setup_oav.py +6 -12
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -6
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +49 -18
- 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 +4 -4
- mx_bluesky/hyperion/experiment_plans/__init__.py +4 -0
- mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +83 -0
- mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +47 -0
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +145 -161
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +56 -22
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +52 -10
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +21 -20
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +11 -14
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +40 -21
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +19 -19
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +21 -21
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +51 -13
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +24 -7
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +5 -6
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +1 -2
- mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +30 -25
- mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +29 -12
- mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +7 -4
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +5 -3
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +28 -20
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
- mx_bluesky/hyperion/external_interaction/config_server.py +11 -28
- mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +1 -1
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +1 -1
- mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +2 -2
- mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +1 -1
- mx_bluesky/hyperion/log.py +0 -84
- mx_bluesky/hyperion/parameters/components.py +4 -251
- mx_bluesky/hyperion/parameters/constants.py +22 -119
- mx_bluesky/hyperion/parameters/gridscan.py +35 -74
- mx_bluesky/hyperion/parameters/load_centre_collect.py +16 -11
- mx_bluesky/hyperion/parameters/rotation.py +23 -10
- mx_bluesky/hyperion/utils/utils.py +17 -0
- mx_bluesky/hyperion/utils/validation.py +5 -6
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/METADATA +36 -33
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/RECORD +91 -81
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/WHEEL +1 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +0 -161
- mx_bluesky/example.py +0 -19
- mx_bluesky/hyperion/parameters/robot_load.py +0 -16
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from dodal.devices.aperturescatterguard import ApertureValue
|
|
4
|
+
from dodal.devices.detector import EIGER2_X_16M_SIZE
|
|
5
|
+
from dodal.devices.zocalo.zocalo_constants import ZOCALO_ENV as ZOCALO_ENV_FROM_DODAL
|
|
6
|
+
from dodal.utils import get_beamline_name
|
|
7
|
+
from pydantic.dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
BEAMLINE = get_beamline_name("test")
|
|
10
|
+
TEST_MODE = BEAMLINE == "test"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class DocDescriptorNames:
|
|
15
|
+
# Robot load event descriptor
|
|
16
|
+
ROBOT_LOAD = "robot_load"
|
|
17
|
+
# For callbacks to use
|
|
18
|
+
OAV_ROTATION_SNAPSHOT_TRIGGERED = "rotation_snapshot_triggered"
|
|
19
|
+
OAV_GRID_SNAPSHOT_TRIGGERED = "snapshot_to_ispyb"
|
|
20
|
+
HARDWARE_READ_PRE = "read_hardware_for_callbacks_pre_collection"
|
|
21
|
+
HARDWARE_READ_DURING = "read_hardware_for_callbacks_during_collection"
|
|
22
|
+
ZOCALO_HW_READ = "zocalo_read_hardware_plan"
|
|
23
|
+
FLYSCAN_RESULTS = "flyscan_results_obtained"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class OavConstants:
|
|
28
|
+
OAV_CONFIG_JSON = (
|
|
29
|
+
"tests/test_data/test_OAVCentring.json"
|
|
30
|
+
if TEST_MODE
|
|
31
|
+
else (
|
|
32
|
+
f"/dls_sw/{BEAMLINE}/software/daq_configuration/json/OAVCentring_hyperion.json"
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True)
|
|
38
|
+
class PlanNameConstants:
|
|
39
|
+
# Robot load subplan
|
|
40
|
+
ROBOT_LOAD = "robot_load"
|
|
41
|
+
# Gridscan
|
|
42
|
+
GRID_DETECT_AND_DO_GRIDSCAN = "grid_detect_and_do_gridscan"
|
|
43
|
+
GRID_DETECT_INNER = "grid_detect"
|
|
44
|
+
GRIDSCAN_OUTER = "run_gridscan_move_and_tidy"
|
|
45
|
+
GRIDSCAN_AND_MOVE = "run_gridscan_and_move"
|
|
46
|
+
GRIDSCAN_MAIN = "run_gridscan"
|
|
47
|
+
DO_FGS = "do_fgs"
|
|
48
|
+
# Rotation scan
|
|
49
|
+
ROTATION_MULTI = "multi_rotation_wrapper"
|
|
50
|
+
ROTATION_OUTER = "rotation_scan_with_cleanup"
|
|
51
|
+
ROTATION_MAIN = "rotation_scan_main"
|
|
52
|
+
FLYSCAN_RESULTS = "xray_centre_results"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True)
|
|
56
|
+
class EnvironmentConstants:
|
|
57
|
+
ZOCALO_ENV = ZOCALO_ENV_FROM_DODAL
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True)
|
|
61
|
+
class TriggerConstants:
|
|
62
|
+
ZOCALO = "trigger_zocalo_on"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class HardwareConstants:
|
|
67
|
+
OAV_REFRESH_DELAY = 0.3
|
|
68
|
+
PANDA_FGS_RUN_UP_DEFAULT = 0.17
|
|
69
|
+
CRYOJET_MARGIN_MM = 0.2
|
|
70
|
+
THAWING_TIME = 20
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass(frozen=True)
|
|
74
|
+
class GridscanParamConstants:
|
|
75
|
+
WIDTH_UM = 600.0
|
|
76
|
+
EXPOSURE_TIME_S = 0.004
|
|
77
|
+
USE_ROI = True
|
|
78
|
+
BOX_WIDTH_UM = 20.0
|
|
79
|
+
OMEGA_1 = 0.0
|
|
80
|
+
OMEGA_2 = 90.0
|
|
81
|
+
PANDA_RUN_UP_DISTANCE_MM = 0.2
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(frozen=True)
|
|
85
|
+
class RotationParamConstants:
|
|
86
|
+
DEFAULT_APERTURE_POSITION = ApertureValue.LARGE
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass(frozen=True)
|
|
90
|
+
class DetectorParamConstants:
|
|
91
|
+
BEAM_XY_LUT_PATH = (
|
|
92
|
+
"tests/test_data/test_det_dist_converter.txt"
|
|
93
|
+
if TEST_MODE
|
|
94
|
+
else f"/dls_sw/{BEAMLINE}/software/daq_configuration/lookup/DetDistToBeamXYConverter.txt"
|
|
95
|
+
)
|
|
96
|
+
DETECTOR = EIGER2_X_16M_SIZE
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass(frozen=True)
|
|
100
|
+
class ExperimentParamConstants:
|
|
101
|
+
DETECTOR = DetectorParamConstants()
|
|
102
|
+
GRIDSCAN = GridscanParamConstants()
|
|
103
|
+
ROTATION = RotationParamConstants()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass(frozen=True)
|
|
107
|
+
class PlanGroupCheckpointConstants:
|
|
108
|
+
# For places to synchronise / stop and wait in plans, use as bluesky group names
|
|
109
|
+
GRID_READY_FOR_DC = "grid_ready_for_data_collection"
|
|
110
|
+
ROTATION_READY_FOR_DC = "rotation_ready_for_data_collection"
|
|
111
|
+
MOVE_GONIO_TO_START = "move_gonio_to_start"
|
|
112
|
+
READY_FOR_OAV = "ready_for_oav"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass(frozen=True)
|
|
116
|
+
class SimConstants:
|
|
117
|
+
BEAMLINE = "BL03S"
|
|
118
|
+
INSERTION_PREFIX = "SR03S"
|
|
119
|
+
# this one is for unit tests
|
|
120
|
+
ISPYB_CONFIG = "tests/test_data/test_config.cfg"
|
|
121
|
+
# this one is for system tests
|
|
122
|
+
DEV_ISPYB_DATABASE_CFG = "/dls_sw/dasc/mariadb/credentials/ispyb-hyperion-dev.cfg"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class Actions(Enum):
|
|
126
|
+
START = "start"
|
|
127
|
+
STOP = "stop"
|
|
128
|
+
SHUTDOWN = "shutdown"
|
|
129
|
+
STATUS = "status"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class Status(Enum):
|
|
133
|
+
WARN = "Warn"
|
|
134
|
+
FAILED = "Failed"
|
|
135
|
+
SUCCESS = "Success"
|
|
136
|
+
BUSY = "Busy"
|
|
137
|
+
ABORTING = "Aborting"
|
|
138
|
+
IDLE = "Idle"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from dodal.devices.aperturescatterguard import ApertureValue
|
|
6
|
+
from dodal.devices.detector import (
|
|
7
|
+
DetectorParams,
|
|
8
|
+
)
|
|
9
|
+
from pydantic import Field
|
|
10
|
+
|
|
11
|
+
from mx_bluesky.common.parameters.components import (
|
|
12
|
+
DiffractionExperimentWithSample,
|
|
13
|
+
IspybExperimentType,
|
|
14
|
+
OptionalGonioAngleStarts,
|
|
15
|
+
WithScan,
|
|
16
|
+
XyzStarts,
|
|
17
|
+
)
|
|
18
|
+
from mx_bluesky.common.parameters.constants import (
|
|
19
|
+
DetectorParamConstants,
|
|
20
|
+
GridscanParamConstants,
|
|
21
|
+
HardwareConstants,
|
|
22
|
+
)
|
|
23
|
+
from mx_bluesky.common.parameters.robot_load import RobotLoadAndEnergyChange
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class GridCommon(
|
|
27
|
+
DiffractionExperimentWithSample,
|
|
28
|
+
OptionalGonioAngleStarts,
|
|
29
|
+
):
|
|
30
|
+
grid_width_um: float = Field(default=GridscanParamConstants.WIDTH_UM)
|
|
31
|
+
exposure_time_s: float = Field(default=GridscanParamConstants.EXPOSURE_TIME_S)
|
|
32
|
+
use_roi_mode: bool = Field(default=GridscanParamConstants.USE_ROI)
|
|
33
|
+
|
|
34
|
+
ispyb_experiment_type: IspybExperimentType = Field(
|
|
35
|
+
default=IspybExperimentType.GRIDSCAN_3D
|
|
36
|
+
)
|
|
37
|
+
selected_aperture: ApertureValue | None = Field(default=ApertureValue.SMALL)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def detector_params(self):
|
|
41
|
+
self.det_dist_to_beam_converter_path = (
|
|
42
|
+
self.det_dist_to_beam_converter_path
|
|
43
|
+
or DetectorParamConstants.BEAM_XY_LUT_PATH
|
|
44
|
+
)
|
|
45
|
+
optional_args = {}
|
|
46
|
+
if self.run_number:
|
|
47
|
+
optional_args["run_number"] = self.run_number
|
|
48
|
+
assert (
|
|
49
|
+
self.detector_distance_mm is not None
|
|
50
|
+
), "Detector distance must be filled before generating DetectorParams"
|
|
51
|
+
os.makedirs(self.storage_directory, exist_ok=True)
|
|
52
|
+
return DetectorParams(
|
|
53
|
+
detector_size_constants=DetectorParamConstants.DETECTOR,
|
|
54
|
+
expected_energy_ev=self.demand_energy_ev,
|
|
55
|
+
exposure_time=self.exposure_time_s,
|
|
56
|
+
directory=self.storage_directory,
|
|
57
|
+
prefix=self.file_name,
|
|
58
|
+
detector_distance=self.detector_distance_mm,
|
|
59
|
+
omega_start=self.omega_start_deg or 0,
|
|
60
|
+
omega_increment=0,
|
|
61
|
+
num_images_per_trigger=1,
|
|
62
|
+
num_triggers=self.num_images,
|
|
63
|
+
use_roi_mode=self.use_roi_mode,
|
|
64
|
+
det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
|
|
65
|
+
trigger_mode=self.trigger_mode,
|
|
66
|
+
**optional_args,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class RobotLoadThenCentre(GridCommon):
|
|
71
|
+
thawing_time: float = Field(default=HardwareConstants.THAWING_TIME)
|
|
72
|
+
|
|
73
|
+
def robot_load_params(self):
|
|
74
|
+
my_params = self.model_dump()
|
|
75
|
+
return RobotLoadAndEnergyChange(**my_params)
|
|
76
|
+
|
|
77
|
+
def pin_centre_then_xray_centre_params(self):
|
|
78
|
+
my_params = self.model_dump()
|
|
79
|
+
del my_params["thawing_time"]
|
|
80
|
+
return PinTipCentreThenXrayCentre(**my_params)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class GridScanWithEdgeDetect(GridCommon):
|
|
84
|
+
box_size_um: float = Field(default=GridscanParamConstants.BOX_WIDTH_UM)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class PinTipCentreThenXrayCentre(GridCommon):
|
|
88
|
+
tip_offset_um: float = 0
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class SpecifiedGrid(XyzStarts, WithScan):
|
|
92
|
+
"""A specified grid is one which has defined values for the start position,
|
|
93
|
+
grid and box sizes, etc., as opposed to parameters for a plan which will create
|
|
94
|
+
those parameters at some point (e.g. through optical pin detection)."""
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
|
|
3
|
+
from mx_bluesky.common.parameters.components import (
|
|
4
|
+
MxBlueskyParameters,
|
|
5
|
+
WithOptionalEnergyChange,
|
|
6
|
+
WithSample,
|
|
7
|
+
WithSnapshot,
|
|
8
|
+
WithVisit,
|
|
9
|
+
)
|
|
10
|
+
from mx_bluesky.common.parameters.constants import HardwareConstants
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RobotLoadAndEnergyChange(
|
|
14
|
+
MxBlueskyParameters, WithSample, WithSnapshot, WithOptionalEnergyChange, WithVisit
|
|
15
|
+
):
|
|
16
|
+
thawing_time: float = Field(default=HardwareConstants.THAWING_TIME)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Common MX plans which includes open and close data collection runs. These be used in isolation or as part of a larger plan"""
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from time import time
|
|
3
|
+
|
|
4
|
+
import bluesky.plan_stubs as bps
|
|
5
|
+
import bluesky.preprocessors as bpp
|
|
6
|
+
from bluesky.utils import MsgGenerator
|
|
7
|
+
from dodal.devices.eiger import EigerDetector
|
|
8
|
+
from dodal.devices.fast_grid_scan import FastGridScanCommon
|
|
9
|
+
from dodal.devices.synchrotron import Synchrotron
|
|
10
|
+
from dodal.devices.zocalo.zocalo_results import (
|
|
11
|
+
ZOCALO_STAGE_GROUP,
|
|
12
|
+
)
|
|
13
|
+
from dodal.log import LOGGER
|
|
14
|
+
from dodal.plan_stubs.check_topup import check_topup_and_wait_if_necessary
|
|
15
|
+
from scanspec.core import AxesPoints, Axis
|
|
16
|
+
|
|
17
|
+
from mx_bluesky.common.device_setup_plans.read_hardware_for_setup import (
|
|
18
|
+
read_hardware_for_zocalo,
|
|
19
|
+
)
|
|
20
|
+
from mx_bluesky.common.parameters.constants import (
|
|
21
|
+
EnvironmentConstants,
|
|
22
|
+
PlanNameConstants,
|
|
23
|
+
TriggerConstants,
|
|
24
|
+
)
|
|
25
|
+
from mx_bluesky.common.utils.tracing import TRACER
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _wait_for_zocalo_to_stage_then_do_fgs(
|
|
29
|
+
grid_scan_device: FastGridScanCommon,
|
|
30
|
+
detector: EigerDetector,
|
|
31
|
+
synchrotron: Synchrotron,
|
|
32
|
+
during_collection_plan: Callable[[], MsgGenerator] | None = None,
|
|
33
|
+
):
|
|
34
|
+
expected_images = yield from bps.rd(grid_scan_device.expected_images)
|
|
35
|
+
exposure_sec_per_image = yield from bps.rd(detector.cam.acquire_time) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
36
|
+
LOGGER.info("waiting for topup if necessary...")
|
|
37
|
+
yield from check_topup_and_wait_if_necessary(
|
|
38
|
+
synchrotron,
|
|
39
|
+
expected_images * exposure_sec_per_image,
|
|
40
|
+
30.0,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Make sure ZocaloResults queue is clear and ready to accept our new data. Zocalo MUST
|
|
44
|
+
# have been staged using ZOCALO_STAGE_GROUP prior to this
|
|
45
|
+
LOGGER.info("Waiting for Zocalo device queue to have been cleared...")
|
|
46
|
+
yield from bps.wait(ZOCALO_STAGE_GROUP)
|
|
47
|
+
|
|
48
|
+
# Triggers Zocalo if RE is subscribed to ZocaloCallback
|
|
49
|
+
yield from read_hardware_for_zocalo(detector)
|
|
50
|
+
LOGGER.info("Wait for all moves with no assigned group")
|
|
51
|
+
yield from bps.wait()
|
|
52
|
+
|
|
53
|
+
LOGGER.info("kicking off FGS")
|
|
54
|
+
yield from bps.kickoff(grid_scan_device, wait=True)
|
|
55
|
+
gridscan_start_time = time()
|
|
56
|
+
if during_collection_plan:
|
|
57
|
+
yield from during_collection_plan()
|
|
58
|
+
LOGGER.info("completing FGS")
|
|
59
|
+
yield from bps.complete(grid_scan_device, wait=True)
|
|
60
|
+
# Remove this logging statement once metrics have been added
|
|
61
|
+
LOGGER.info(
|
|
62
|
+
f"Grid scan motion program took {round(time()-gridscan_start_time,2)} to complete"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def kickoff_and_complete_gridscan(
|
|
67
|
+
gridscan: FastGridScanCommon,
|
|
68
|
+
detector: EigerDetector, # Once Eiger inherits from StandardDetector, use that type instead
|
|
69
|
+
synchrotron: Synchrotron,
|
|
70
|
+
scan_points: list[AxesPoints[Axis]],
|
|
71
|
+
scan_start_indices: list[int],
|
|
72
|
+
plan_during_collection: Callable[[], MsgGenerator] | None = None,
|
|
73
|
+
zocalo_environment: str = EnvironmentConstants.ZOCALO_ENV,
|
|
74
|
+
):
|
|
75
|
+
"""Triggers a grid scan motion program and waits for completion, accounting for synchrotron topup.
|
|
76
|
+
If the RunEngine is subscribed to ZocaloCallback, this plan will also trigger Zocalo.
|
|
77
|
+
|
|
78
|
+
Can be used for multiple successive grid scans, see Hyperion's usage
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
gridscan (FastGridScanCommon): Device which can trigger a fast grid scan and wait for completion
|
|
82
|
+
detector (EigerDetector) Detector device
|
|
83
|
+
synchrotron (Synchrotron): Synchrotron device
|
|
84
|
+
scan_points (list[AxesPoints[Axis]]): Each element in the list contains all the grid points for that grid scan.
|
|
85
|
+
Two elements in this list indicates that two grid scans will be done, eg for Hyperion's 3D grid scans.
|
|
86
|
+
scan_start_indices (list[int]): Contains the first index of each grid scan
|
|
87
|
+
plan_during_collection (Optional, MsgGenerator): Generic plan called in between kickoff and completion,
|
|
88
|
+
eg waiting on zocalo.
|
|
89
|
+
zocalo_environment (Optional, str) Used for zocalo connection
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
assert len(scan_points) == len(
|
|
93
|
+
scan_start_indices
|
|
94
|
+
), "scan_points and scan_start_indices must be lists of the same length!"
|
|
95
|
+
|
|
96
|
+
plan_name = PlanNameConstants.DO_FGS
|
|
97
|
+
|
|
98
|
+
@TRACER.start_as_current_span(plan_name)
|
|
99
|
+
@bpp.set_run_key_decorator(plan_name)
|
|
100
|
+
@bpp.run_decorator(
|
|
101
|
+
md={
|
|
102
|
+
"subplan_name": plan_name,
|
|
103
|
+
TriggerConstants.ZOCALO: plan_name,
|
|
104
|
+
"scan_points": scan_points,
|
|
105
|
+
"scan_start_indices": scan_start_indices,
|
|
106
|
+
"zocalo_environment": zocalo_environment,
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
@bpp.contingency_decorator(
|
|
110
|
+
except_plan=lambda e: (yield from bps.stop(detector)), # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
111
|
+
else_plan=lambda: (yield from bps.unstage(detector)), # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
112
|
+
)
|
|
113
|
+
def _decorated_do_fgs():
|
|
114
|
+
yield from _wait_for_zocalo_to_stage_then_do_fgs(
|
|
115
|
+
gridscan,
|
|
116
|
+
detector,
|
|
117
|
+
synchrotron,
|
|
118
|
+
during_collection_plan=plan_during_collection,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
yield from _decorated_do_fgs()
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from logging.handlers import TimedRotatingFileHandler
|
|
3
|
+
from os import environ
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from dodal.log import (
|
|
7
|
+
ERROR_LOG_BUFFER_LINES,
|
|
8
|
+
CircularMemoryHandler,
|
|
9
|
+
DodalLogHandlers,
|
|
10
|
+
integrate_bluesky_and_ophyd_logging,
|
|
11
|
+
set_up_all_logging_handlers,
|
|
12
|
+
)
|
|
13
|
+
from dodal.log import LOGGER as dodal_logger
|
|
14
|
+
|
|
15
|
+
LOGGER = logging.getLogger("mx-bluesky")
|
|
16
|
+
|
|
17
|
+
__logger_handlers: DodalLogHandlers | None = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ExperimentMetadataTagFilter(logging.Filter):
|
|
21
|
+
"""When an instance of this custom filter is added to a logging handler, dc_group_id
|
|
22
|
+
and run_id will be tagged in that handlers' log messages."""
|
|
23
|
+
|
|
24
|
+
dc_group_id: str | None = None
|
|
25
|
+
run_uid: str | None = None
|
|
26
|
+
|
|
27
|
+
def filter(self, record):
|
|
28
|
+
if self.dc_group_id:
|
|
29
|
+
record.dc_group_id = self.dc_group_id
|
|
30
|
+
if self.run_uid:
|
|
31
|
+
record.run_uid = self.run_uid
|
|
32
|
+
return True
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
tag_filter = ExperimentMetadataTagFilter()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def set_dcgid_tag(dcgid):
|
|
39
|
+
"""Set the datacollection group id as a tag on all subsequent log messages.
|
|
40
|
+
Setting to None will remove the tag."""
|
|
41
|
+
tag_filter.dc_group_id = dcgid
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def set_uid_tag(uid):
|
|
45
|
+
"""Set the unique id as a tag on all subsequent log messages.
|
|
46
|
+
Setting to None will remove the tag."""
|
|
47
|
+
tag_filter.run_uid = uid
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def do_default_logging_setup(
|
|
51
|
+
file_name: str,
|
|
52
|
+
graylog_port: int,
|
|
53
|
+
dev_mode: bool = False,
|
|
54
|
+
integrate_all_logs: bool = True,
|
|
55
|
+
):
|
|
56
|
+
"""Configures dodal logger so that separate debug and info log files are created,
|
|
57
|
+
info logs are sent to Graylog, info logs are streamed to sys.sterr, and logs from ophyd
|
|
58
|
+
and bluesky and ophyd-async are optionally included."""
|
|
59
|
+
|
|
60
|
+
handlers = set_up_all_logging_handlers(
|
|
61
|
+
dodal_logger,
|
|
62
|
+
_get_logging_dir(),
|
|
63
|
+
file_name,
|
|
64
|
+
dev_mode,
|
|
65
|
+
ERROR_LOG_BUFFER_LINES,
|
|
66
|
+
graylog_port,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if integrate_all_logs:
|
|
70
|
+
integrate_bluesky_and_ophyd_logging(dodal_logger)
|
|
71
|
+
|
|
72
|
+
handlers["graylog_handler"].addFilter(tag_filter)
|
|
73
|
+
|
|
74
|
+
global __logger_handlers
|
|
75
|
+
__logger_handlers = handlers
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _get_debug_handler() -> CircularMemoryHandler:
|
|
79
|
+
assert (
|
|
80
|
+
__logger_handlers is not None
|
|
81
|
+
), "You can only use this after running the default logging setup"
|
|
82
|
+
return __logger_handlers["debug_memory_handler"]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def flush_debug_handler() -> str:
|
|
86
|
+
"""Writes the contents of the circular debug log buffer to disk and returns the written filename"""
|
|
87
|
+
handler = _get_debug_handler()
|
|
88
|
+
assert isinstance(
|
|
89
|
+
handler.target, TimedRotatingFileHandler
|
|
90
|
+
), "Circular memory handler doesn't have an appropriate fileHandler target"
|
|
91
|
+
handler.flush()
|
|
92
|
+
return handler.target.baseFilename
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _get_logging_dir() -> Path:
|
|
96
|
+
"""Get the path to write the mx_bluesky log files to.
|
|
97
|
+
|
|
98
|
+
Log location can be specified in the LOG_DIR environment variable, otherwise MX bluesky logs are written to 'dls_sw/ixx/logs/bluesky'.
|
|
99
|
+
This directory will be created if it is not found
|
|
100
|
+
|
|
101
|
+
Logs are written to ./tmp/logs/bluesky if BEAMLINE environment variable is not found
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
logging_path (Path): Path to the log file for the file handler to write to.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
logging_str = environ.get("LOG_DIR")
|
|
108
|
+
if logging_str:
|
|
109
|
+
logging_path = Path(logging_str)
|
|
110
|
+
else:
|
|
111
|
+
beamline = environ.get("BEAMLINE")
|
|
112
|
+
logging_path = (
|
|
113
|
+
Path(f"/dls_sw/{beamline}/logs/bluesky/")
|
|
114
|
+
if beamline
|
|
115
|
+
else Path("/tmp/logs/bluesky")
|
|
116
|
+
)
|
|
117
|
+
Path.mkdir(logging_path, exist_ok=True, parents=True)
|
|
118
|
+
return logging_path
|
|
@@ -8,8 +8,8 @@ from opentelemetry.sdk.trace import TracerProvider
|
|
|
8
8
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def setup_tracing():
|
|
12
|
-
resource = Resource(attributes={SERVICE_NAME:
|
|
11
|
+
def setup_tracing(service_name: str = "Hyperion"):
|
|
12
|
+
resource = Resource(attributes={SERVICE_NAME: service_name})
|
|
13
13
|
|
|
14
14
|
traceProvider = TracerProvider(resource=resource)
|
|
15
15
|
processor = BatchSpanProcessor(
|
mx_bluesky/hyperion/__main__.py
CHANGED
|
@@ -7,13 +7,18 @@ from queue import Queue
|
|
|
7
7
|
from traceback import format_exception
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
|
-
from blueapi.core import BlueskyContext
|
|
10
|
+
from blueapi.core import BlueskyContext
|
|
11
11
|
from bluesky.callbacks.zmq import Publisher
|
|
12
12
|
from bluesky.run_engine import RunEngine
|
|
13
|
+
from bluesky.utils import MsgGenerator
|
|
13
14
|
from flask import Flask, request
|
|
14
15
|
from flask_restful import Api, Resource
|
|
15
16
|
from pydantic.dataclasses import dataclass
|
|
16
17
|
|
|
18
|
+
from mx_bluesky.common.parameters.components import MxBlueskyParameters
|
|
19
|
+
from mx_bluesky.common.parameters.constants import Actions, Status
|
|
20
|
+
from mx_bluesky.common.utils.log import do_default_logging_setup, flush_debug_handler
|
|
21
|
+
from mx_bluesky.common.utils.tracing import TRACER
|
|
17
22
|
from mx_bluesky.hyperion.exceptions import WarningException
|
|
18
23
|
from mx_bluesky.hyperion.experiment_plans.experiment_registry import (
|
|
19
24
|
PLAN_REGISTRY,
|
|
@@ -36,13 +41,9 @@ from mx_bluesky.hyperion.external_interaction.callbacks.logging_callback import
|
|
|
36
41
|
)
|
|
37
42
|
from mx_bluesky.hyperion.log import (
|
|
38
43
|
LOGGER,
|
|
39
|
-
do_default_logging_setup,
|
|
40
|
-
flush_debug_handler,
|
|
41
44
|
)
|
|
42
45
|
from mx_bluesky.hyperion.parameters.cli import parse_cli_args
|
|
43
|
-
from mx_bluesky.hyperion.parameters.
|
|
44
|
-
from mx_bluesky.hyperion.parameters.constants import CONST, Actions, Status
|
|
45
|
-
from mx_bluesky.hyperion.tracing import TRACER
|
|
46
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
46
47
|
from mx_bluesky.hyperion.utils.context import setup_context
|
|
47
48
|
|
|
48
49
|
VERBOSE_EVENT_LOGGING: bool | None = None
|
|
@@ -53,7 +54,7 @@ class Command:
|
|
|
53
54
|
action: Actions
|
|
54
55
|
devices: Any | None = None
|
|
55
56
|
experiment: Callable[[Any, Any], MsgGenerator] | None = None
|
|
56
|
-
parameters:
|
|
57
|
+
parameters: MxBlueskyParameters | None = None
|
|
57
58
|
callbacks: CallbacksFactory | None = None
|
|
58
59
|
|
|
59
60
|
|
|
@@ -119,7 +120,7 @@ class BlueskyRunner:
|
|
|
119
120
|
def start(
|
|
120
121
|
self,
|
|
121
122
|
experiment: Callable,
|
|
122
|
-
parameters:
|
|
123
|
+
parameters: MxBlueskyParameters,
|
|
123
124
|
plan_name: str,
|
|
124
125
|
callbacks: CallbacksFactory | None,
|
|
125
126
|
) -> StatusAndMessage:
|
|
@@ -239,7 +240,7 @@ def compose_start_args(context: BlueskyContext, plan_name: str, action: Actions)
|
|
|
239
240
|
raise ValueError(f"Extra fields not allowed {parameters.model_extra}")
|
|
240
241
|
except Exception as e:
|
|
241
242
|
raise ValueError(
|
|
242
|
-
f"Supplied parameters don't match the plan for this endpoint {request.data}"
|
|
243
|
+
f"Supplied parameters don't match the plan for this endpoint {request.data}, for plan {plan_name}"
|
|
243
244
|
) from e
|
|
244
245
|
return plan, parameters, plan_name, callback_type
|
|
245
246
|
|
|
@@ -345,7 +346,9 @@ def create_app(
|
|
|
345
346
|
def create_targets():
|
|
346
347
|
hyperion_port = 5005
|
|
347
348
|
args = parse_cli_args()
|
|
348
|
-
do_default_logging_setup(
|
|
349
|
+
do_default_logging_setup(
|
|
350
|
+
CONST.LOG_FILE_NAME, CONST.GRAYLOG_PORT, dev_mode=args.dev_mode
|
|
351
|
+
)
|
|
349
352
|
if not args.use_external_callbacks:
|
|
350
353
|
setup_callback_logging(args.dev_mode)
|
|
351
354
|
app, runner = create_app(
|