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.
Files changed (94) hide show
  1. mx_bluesky/__init__.py +8 -3
  2. mx_bluesky/__main__.py +12 -7
  3. mx_bluesky/_version.py +2 -2
  4. mx_bluesky/beamlines/i04/callbacks/murko_callback.py +14 -4
  5. mx_bluesky/beamlines/i04/thawing_plan.py +49 -11
  6. mx_bluesky/beamlines/i24/serial/__init__.py +3 -0
  7. mx_bluesky/beamlines/i24/serial/dcid.py +19 -21
  8. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +69 -91
  9. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +2 -5
  10. mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
  11. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +111 -143
  12. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +141 -222
  13. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -216
  14. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +18 -17
  15. mx_bluesky/beamlines/i24/serial/log.py +58 -49
  16. mx_bluesky/beamlines/i24/serial/parameters/constants.py +0 -1
  17. mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
  18. mx_bluesky/beamlines/i24/serial/run_extruder.sh +30 -5
  19. mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +30 -5
  20. mx_bluesky/beamlines/i24/serial/run_serial.py +24 -8
  21. mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -2
  22. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +79 -81
  23. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +9 -20
  24. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +26 -28
  25. mx_bluesky/beamlines/i24/serial/write_nexus.py +11 -11
  26. mx_bluesky/common/__init__.py +0 -0
  27. mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +14 -0
  28. mx_bluesky/common/external_interaction/config_server.py +46 -0
  29. mx_bluesky/common/parameters/components.py +258 -0
  30. mx_bluesky/common/parameters/constants.py +138 -0
  31. mx_bluesky/common/parameters/gridscan.py +94 -0
  32. mx_bluesky/common/parameters/robot_load.py +16 -0
  33. mx_bluesky/common/plans/__init__.py +1 -0
  34. mx_bluesky/common/plans/do_fgs.py +121 -0
  35. mx_bluesky/common/utils/log.py +118 -0
  36. mx_bluesky/{hyperion → common/utils}/tracing.py +2 -2
  37. mx_bluesky/hyperion/__main__.py +13 -10
  38. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +31 -26
  39. mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +6 -12
  40. mx_bluesky/hyperion/device_setup_plans/setup_oav.py +6 -12
  41. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -6
  42. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +49 -18
  43. mx_bluesky/hyperion/device_setup_plans/smargon.py +6 -6
  44. mx_bluesky/hyperion/device_setup_plans/utils.py +2 -2
  45. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +4 -4
  46. mx_bluesky/hyperion/experiment_plans/__init__.py +4 -0
  47. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +83 -0
  48. mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +47 -0
  49. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
  50. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +145 -161
  51. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +56 -22
  52. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +52 -10
  53. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +21 -20
  54. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +11 -14
  55. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
  56. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +40 -21
  57. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +19 -19
  58. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +21 -21
  59. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +51 -13
  60. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +24 -7
  61. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +5 -6
  62. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +1 -2
  63. mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
  64. mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
  65. mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +30 -25
  66. mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +29 -12
  67. mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +1 -1
  68. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +1 -1
  69. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +7 -4
  70. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +5 -3
  71. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +28 -20
  72. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
  73. mx_bluesky/hyperion/external_interaction/config_server.py +11 -28
  74. mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +1 -1
  75. mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +1 -1
  76. mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +2 -2
  77. mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +1 -1
  78. mx_bluesky/hyperion/log.py +0 -84
  79. mx_bluesky/hyperion/parameters/components.py +4 -251
  80. mx_bluesky/hyperion/parameters/constants.py +22 -119
  81. mx_bluesky/hyperion/parameters/gridscan.py +35 -74
  82. mx_bluesky/hyperion/parameters/load_centre_collect.py +16 -11
  83. mx_bluesky/hyperion/parameters/rotation.py +23 -10
  84. mx_bluesky/hyperion/utils/utils.py +17 -0
  85. mx_bluesky/hyperion/utils/validation.py +5 -6
  86. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/METADATA +36 -33
  87. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/RECORD +91 -81
  88. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/WHEEL +1 -1
  89. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +0 -161
  90. mx_bluesky/example.py +0 -19
  91. mx_bluesky/hyperion/parameters/robot_load.py +0 -16
  92. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/LICENSE +0 -0
  93. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/entry_points.txt +0 -0
  94. {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: "Hyperion"})
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(
@@ -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, MsgGenerator
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.components import HyperionParameters
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: HyperionParameters | None = None
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: HyperionParameters,
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(dev_mode=args.dev_mode)
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(