mx-bluesky 0.3.1__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/i04/__init__.py +3 -0
- mx_bluesky/{i04 → beamlines/i04}/thawing_plan.py +5 -4
- mx_bluesky/{i24 → beamlines/i24}/serial/blueapi_config.yaml +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/dcid.py +2 -2
- mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/DetStage.edl +3 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +7 -7
- mx_bluesky/{i24 → beamlines/i24}/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +12 -9
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/CustomChip_py3v1.edl +3 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DetStage.edl +3 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +245 -200
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +4 -4
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +8 -8
- mx_bluesky/beamlines/i24/serial/fixed_target/__init__.py +0 -0
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +80 -70
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +20 -21
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +5 -5
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -4
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_moveonclick.py +59 -39
- mx_bluesky/{i24 → beamlines/i24}/serial/log.py +1 -9
- mx_bluesky/beamlines/i24/serial/parameters/__init__.py +15 -0
- mx_bluesky/{i24 → beamlines/i24}/serial/parameters/constants.py +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/parameters/experiment_parameters.py +4 -25
- mx_bluesky/{i24 → beamlines/i24}/serial/parameters/utils.py +5 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/run_serial.py +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/pv_abstract.py +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_beamline.py +2 -2
- mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_detector.py +5 -5
- mx_bluesky/{i24 → beamlines/i24}/serial/write_nexus.py +6 -3
- mx_bluesky/hyperion/__init__.py +1 -0
- mx_bluesky/hyperion/__main__.py +374 -0
- mx_bluesky/hyperion/device_setup_plans/__init__.py +0 -0
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +134 -0
- mx_bluesky/hyperion/device_setup_plans/manipulate_sample.py +110 -0
- mx_bluesky/hyperion/device_setup_plans/position_detector.py +16 -0
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +60 -0
- mx_bluesky/hyperion/device_setup_plans/setup_oav.py +87 -0
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +210 -0
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +214 -0
- mx_bluesky/hyperion/device_setup_plans/smargon.py +25 -0
- mx_bluesky/hyperion/device_setup_plans/utils.py +44 -0
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +93 -0
- mx_bluesky/hyperion/exceptions.py +47 -0
- mx_bluesky/hyperion/experiment_plans/__init__.py +30 -0
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +84 -0
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +528 -0
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +209 -0
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +173 -0
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +81 -0
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +463 -0
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +119 -0
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +164 -0
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +322 -0
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +436 -0
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +68 -0
- mx_bluesky/hyperion/external_interaction/__init__.py +9 -0
- mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +10 -0
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +148 -0
- mx_bluesky/hyperion/external_interaction/callbacks/aperture_change_callback.py +22 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +46 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +70 -0
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +88 -0
- mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +203 -0
- mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +20 -0
- mx_bluesky/hyperion/external_interaction/callbacks/logging_callback.py +29 -0
- mx_bluesky/hyperion/external_interaction/callbacks/plan_reactive_callback.py +101 -0
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +88 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +174 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +17 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +102 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +269 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_mapping.py +53 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +95 -0
- mx_bluesky/hyperion/external_interaction/callbacks/zocalo_callback.py +92 -0
- mx_bluesky/hyperion/external_interaction/config_server.py +35 -0
- mx_bluesky/hyperion/external_interaction/exceptions.py +13 -0
- mx_bluesky/hyperion/external_interaction/ispyb/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/ispyb/data_model.py +95 -0
- mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +125 -0
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +276 -0
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_utils.py +29 -0
- mx_bluesky/hyperion/external_interaction/nexus/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +148 -0
- mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +114 -0
- mx_bluesky/hyperion/log.py +99 -0
- mx_bluesky/hyperion/parameters/__init__.py +2 -0
- mx_bluesky/hyperion/parameters/cli.py +68 -0
- mx_bluesky/{parameters → hyperion/parameters}/components.py +77 -24
- mx_bluesky/hyperion/parameters/constants.py +158 -0
- mx_bluesky/hyperion/parameters/gridscan.py +216 -0
- mx_bluesky/hyperion/parameters/rotation.py +160 -0
- mx_bluesky/hyperion/resources/panda/panda-gridscan.yaml +964 -0
- mx_bluesky/hyperion/tracing.py +28 -0
- mx_bluesky/hyperion/utils/context.py +84 -0
- mx_bluesky/hyperion/utils/utils.py +25 -0
- mx_bluesky/hyperion/utils/validation.py +196 -0
- mx_bluesky/jupyter_example.ipynb +3 -2
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/METADATA +26 -11
- mx_bluesky-1.1.0.dist-info/RECORD +136 -0
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/WHEEL +1 -1
- mx_bluesky-1.1.0.dist-info/entry_points.txt +8 -0
- mx_bluesky/i04/__init__.py +0 -3
- mx_bluesky/i24/serial/parameters/__init__.py +0 -15
- mx_bluesky/parameters/__init__.py +0 -31
- mx_bluesky-0.3.1.dist-info/RECORD +0 -67
- mx_bluesky-0.3.1.dist-info/entry_points.txt +0 -4
- /mx_bluesky/{i24 → beamlines}/__init__.py +0 -0
- /mx_bluesky/{i04 → beamlines/i04}/callbacks/murko_callback.py +0 -0
- /mx_bluesky/{i24/serial/extruder → beamlines/i24}/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/microdrop_alignment.edl +0 -0
- /mx_bluesky/{i24/serial/fixed_target → beamlines/i24/serial/extruder}/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/ME14E-GeneralPurpose.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/PMAC_Command.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/Shutter_Control.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/microdrop_alignment.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/nudgechip.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/short1-laser.png +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/short2-laser.png +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/ft_utils.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/cs/cs_maker.json +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/cs/motor_direction.txt +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/minichip-oxford.pvar +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/oxford.pvar +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/run_extruder.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/run_fixed_target.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/run_ssx.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/set_visit_directory.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/ca.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/pv.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_zebra_plans.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/start_blueapi.sh +0 -0
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/LICENSE +0 -0
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.plan_reactive_callback import (
|
|
6
|
+
PlanReactiveCallback,
|
|
7
|
+
)
|
|
8
|
+
from mx_bluesky.hyperion.external_interaction.nexus.nexus_utils import (
|
|
9
|
+
create_beam_and_attenuator_parameters,
|
|
10
|
+
vds_type_based_on_bit_depth,
|
|
11
|
+
)
|
|
12
|
+
from mx_bluesky.hyperion.external_interaction.nexus.write_nexus import NexusWriter
|
|
13
|
+
from mx_bluesky.hyperion.log import NEXUS_LOGGER
|
|
14
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
15
|
+
from mx_bluesky.hyperion.parameters.gridscan import ThreeDGridScan
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from event_model.documents import Event, EventDescriptor, RunStart
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class GridscanNexusFileCallback(PlanReactiveCallback):
|
|
22
|
+
"""Callback class to handle the creation of Nexus files based on experiment \
|
|
23
|
+
parameters. Initialises on recieving a 'start' document for the \
|
|
24
|
+
'run_gridscan_move_and_tidy' sub plan, which must also contain the run parameters, \
|
|
25
|
+
as metadata under the 'hyperion_internal_parameters' key. Actually writes the \
|
|
26
|
+
nexus files on updates the timestamps on recieving the 'ispyb_reading_hardware' event \
|
|
27
|
+
document, and finalises the files on getting a 'stop' document for the whole run.
|
|
28
|
+
|
|
29
|
+
To use, subscribe the Bluesky RunEngine to an instance of this class.
|
|
30
|
+
E.g.:
|
|
31
|
+
nexus_file_handler_callback = NexusFileCallback(parameters)
|
|
32
|
+
RE.subscribe(nexus_file_handler_callback)
|
|
33
|
+
Or decorate a plan using bluesky.preprocessors.subs_decorator.
|
|
34
|
+
|
|
35
|
+
See: https://blueskyproject.io/bluesky/callbacks.html#ways-to-invoke-callbacks
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self) -> None:
|
|
39
|
+
super().__init__(NEXUS_LOGGER)
|
|
40
|
+
self.run_start_uid: str | None = None
|
|
41
|
+
self.nexus_writer_1: NexusWriter | None = None
|
|
42
|
+
self.nexus_writer_2: NexusWriter | None = None
|
|
43
|
+
self.descriptors: dict[str, EventDescriptor] = {}
|
|
44
|
+
self.log = NEXUS_LOGGER
|
|
45
|
+
|
|
46
|
+
def activity_gated_start(self, doc: RunStart):
|
|
47
|
+
if doc.get("subplan_name") == CONST.PLAN.GRIDSCAN_OUTER:
|
|
48
|
+
json_params = doc.get("hyperion_parameters")
|
|
49
|
+
NEXUS_LOGGER.info(
|
|
50
|
+
f"Nexus writer received start document with experiment parameters {json_params}"
|
|
51
|
+
)
|
|
52
|
+
parameters = ThreeDGridScan.from_json(json_params)
|
|
53
|
+
d_size = parameters.detector_params.detector_size_constants.det_size_pixels
|
|
54
|
+
grid_n_img_1 = parameters.scan_indices[1]
|
|
55
|
+
grid_n_img_2 = parameters.num_images - grid_n_img_1
|
|
56
|
+
data_shape_1 = (grid_n_img_1, d_size.width, d_size.height)
|
|
57
|
+
data_shape_2 = (grid_n_img_2, d_size.width, d_size.height)
|
|
58
|
+
run_number_2 = parameters.detector_params.run_number + 1
|
|
59
|
+
self.nexus_writer_1 = NexusWriter(
|
|
60
|
+
parameters, data_shape_1, parameters.scan_points_first_grid
|
|
61
|
+
)
|
|
62
|
+
self.nexus_writer_2 = NexusWriter(
|
|
63
|
+
parameters,
|
|
64
|
+
data_shape_2,
|
|
65
|
+
parameters.scan_points_second_grid,
|
|
66
|
+
run_number=run_number_2,
|
|
67
|
+
vds_start_index=parameters.scan_indices[1],
|
|
68
|
+
omega_start_deg=90,
|
|
69
|
+
)
|
|
70
|
+
self.run_start_uid = doc.get("uid")
|
|
71
|
+
|
|
72
|
+
def activity_gated_descriptor(self, doc: EventDescriptor):
|
|
73
|
+
self.descriptors[doc["uid"]] = doc
|
|
74
|
+
|
|
75
|
+
def activity_gated_event(self, doc: Event) -> Event | None:
|
|
76
|
+
assert (event_descriptor := self.descriptors.get(doc["descriptor"])) is not None
|
|
77
|
+
if event_descriptor.get("name") == CONST.DESCRIPTORS.HARDWARE_READ_DURING:
|
|
78
|
+
data = doc["data"]
|
|
79
|
+
for nexus_writer in [self.nexus_writer_1, self.nexus_writer_2]:
|
|
80
|
+
assert nexus_writer, "Nexus callback did not receive start doc"
|
|
81
|
+
(
|
|
82
|
+
nexus_writer.beam,
|
|
83
|
+
nexus_writer.attenuator,
|
|
84
|
+
) = create_beam_and_attenuator_parameters(
|
|
85
|
+
data["dcm-energy_in_kev"],
|
|
86
|
+
data["flux_flux_reading"],
|
|
87
|
+
data["attenuator-actual_transmission"],
|
|
88
|
+
)
|
|
89
|
+
vds_data_type = vds_type_based_on_bit_depth(
|
|
90
|
+
doc["data"]["eiger_bit_depth"]
|
|
91
|
+
)
|
|
92
|
+
nexus_writer.create_nexus_file(vds_data_type)
|
|
93
|
+
NEXUS_LOGGER.info(f"Nexus file created at {nexus_writer.data_filename}")
|
|
94
|
+
|
|
95
|
+
return super().activity_gated_event(doc)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from bluesky.callbacks import CallbackBase
|
|
6
|
+
from dodal.devices.zocalo import ZocaloStartInfo, ZocaloTrigger
|
|
7
|
+
|
|
8
|
+
from mx_bluesky.hyperion.external_interaction.exceptions import ISPyBDepositionNotMade
|
|
9
|
+
from mx_bluesky.hyperion.log import ISPYB_LOGGER
|
|
10
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
11
|
+
from mx_bluesky.hyperion.utils.utils import number_of_frames_from_scan_spec
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from event_model.documents import Event, EventDescriptor, RunStart, RunStop
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ZocaloCallback(CallbackBase):
|
|
18
|
+
"""Callback class to handle the triggering of Zocalo processing.
|
|
19
|
+
Sends zocalo a run_start signal on receiving a start document for the specified
|
|
20
|
+
sub-plan, and sends a run_end signal on receiving a stop document for the same plan.
|
|
21
|
+
|
|
22
|
+
The metadata of the sub-plan this starts on must include a zocalo_environment.
|
|
23
|
+
|
|
24
|
+
Shouldn't be subscribed directly to the RunEngine, instead should be passed to the
|
|
25
|
+
`emit` argument of an ISPyB callback which appends DCIDs to the relevant start doc.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def _reset_state(self):
|
|
29
|
+
self.run_uid: str | None = None
|
|
30
|
+
self.triggering_plan: str | None = None
|
|
31
|
+
self.zocalo_interactor: ZocaloTrigger | None = None
|
|
32
|
+
self.zocalo_info: list[ZocaloStartInfo] = []
|
|
33
|
+
self.descriptors: dict[str, EventDescriptor] = {}
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
):
|
|
38
|
+
super().__init__()
|
|
39
|
+
self._reset_state()
|
|
40
|
+
|
|
41
|
+
def start(self, doc: RunStart):
|
|
42
|
+
ISPYB_LOGGER.info("Zocalo handler received start document.")
|
|
43
|
+
if triggering_plan := doc.get(CONST.TRIGGER.ZOCALO):
|
|
44
|
+
self.triggering_plan = triggering_plan
|
|
45
|
+
assert isinstance(zocalo_environment := doc.get("zocalo_environment"), str)
|
|
46
|
+
ISPYB_LOGGER.info(f"Zocalo environment set to {zocalo_environment}.")
|
|
47
|
+
self.zocalo_interactor = ZocaloTrigger(zocalo_environment)
|
|
48
|
+
|
|
49
|
+
if self.triggering_plan and doc.get("subplan_name") == self.triggering_plan:
|
|
50
|
+
self.run_uid = doc.get("uid")
|
|
51
|
+
assert isinstance(scan_points := doc.get("scan_points"), list)
|
|
52
|
+
if (
|
|
53
|
+
isinstance(ispyb_ids := doc.get("ispyb_dcids"), tuple)
|
|
54
|
+
and len(ispyb_ids) > 0
|
|
55
|
+
):
|
|
56
|
+
ids_and_shape = list(zip(ispyb_ids, scan_points, strict=False))
|
|
57
|
+
start_frame = 0
|
|
58
|
+
self.zocalo_info = []
|
|
59
|
+
for idx, id_and_shape in enumerate(ids_and_shape):
|
|
60
|
+
id, shape = id_and_shape
|
|
61
|
+
num_frames = number_of_frames_from_scan_spec(shape)
|
|
62
|
+
self.zocalo_info.append(
|
|
63
|
+
ZocaloStartInfo(id, None, start_frame, num_frames, idx)
|
|
64
|
+
)
|
|
65
|
+
start_frame += num_frames
|
|
66
|
+
else:
|
|
67
|
+
raise ISPyBDepositionNotMade(
|
|
68
|
+
f"No ISPyB IDs received by the start of {self.triggering_plan=}"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def descriptor(self, doc: EventDescriptor):
|
|
72
|
+
self.descriptors[doc["uid"]] = doc
|
|
73
|
+
|
|
74
|
+
def event(self, doc: Event) -> Event:
|
|
75
|
+
event_descriptor = self.descriptors[doc["descriptor"]]
|
|
76
|
+
if event_descriptor.get("name") == CONST.DESCRIPTORS.ZOCALO_HW_READ:
|
|
77
|
+
filename = doc["data"]["eiger_odin_file_writer_id"]
|
|
78
|
+
for start_info in self.zocalo_info:
|
|
79
|
+
start_info.filename = filename
|
|
80
|
+
assert self.zocalo_interactor is not None
|
|
81
|
+
self.zocalo_interactor.run_start(start_info)
|
|
82
|
+
return doc
|
|
83
|
+
|
|
84
|
+
def stop(self, doc: RunStop):
|
|
85
|
+
if doc.get("run_start") == self.run_uid:
|
|
86
|
+
ISPYB_LOGGER.info(
|
|
87
|
+
f"Zocalo handler received stop document, for run {doc.get('run_start')}."
|
|
88
|
+
)
|
|
89
|
+
assert self.zocalo_interactor is not None
|
|
90
|
+
for info in self.zocalo_info:
|
|
91
|
+
self.zocalo_interactor.run_end(info.ispyb_dcid)
|
|
92
|
+
self._reset_state()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from daq_config_server.client import ConfigServer
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
|
|
4
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
5
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
6
|
+
|
|
7
|
+
_CONFIG_SERVER: ConfigServer | None = None
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def config_server() -> ConfigServer:
|
|
11
|
+
global _CONFIG_SERVER
|
|
12
|
+
if _CONFIG_SERVER is None:
|
|
13
|
+
_CONFIG_SERVER = ConfigServer(CONST.CONFIG_SERVER_URL, LOGGER)
|
|
14
|
+
return _CONFIG_SERVER
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FeatureFlags(BaseModel):
|
|
18
|
+
# The default value will be used as the fallback when doing a best-effort fetch
|
|
19
|
+
# from the service
|
|
20
|
+
use_panda_for_gridscan: bool = False
|
|
21
|
+
use_gpu_for_gridscan: bool = False
|
|
22
|
+
set_stub_offsets: bool = False
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def _get_flags(cls):
|
|
26
|
+
flags = config_server().best_effort_get_all_feature_flags()
|
|
27
|
+
return {f: flags[f] for f in flags if f in cls.__fields__.keys()}
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def best_effort(cls):
|
|
31
|
+
return cls(**cls._get_flags())
|
|
32
|
+
|
|
33
|
+
def update_self_from_server(self):
|
|
34
|
+
for flag, value in self._get_flags().items():
|
|
35
|
+
setattr(self, flag, value)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from mx_bluesky.hyperion.exceptions import WarningException
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ISPyBDepositionNotMade(Exception):
|
|
5
|
+
"""Raised when the ISPyB or Zocalo callbacks can't access ISPyB deposition numbers."""
|
|
6
|
+
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NoCentreFoundException(WarningException):
|
|
11
|
+
"""Error for if zocalo is unable to find the centre during a gridscan."""
|
|
12
|
+
|
|
13
|
+
pass
|
|
File without changes
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from dataclasses import asdict, dataclass
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Orientation(Enum):
|
|
6
|
+
HORIZONTAL = "horizontal"
|
|
7
|
+
VERTICAL = "vertical"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass()
|
|
11
|
+
class DataCollectionGroupInfo:
|
|
12
|
+
visit_string: str
|
|
13
|
+
experiment_type: str
|
|
14
|
+
sample_id: int | None
|
|
15
|
+
sample_barcode: str | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(kw_only=True)
|
|
19
|
+
class DataCollectionInfo:
|
|
20
|
+
omega_start: float | None = None
|
|
21
|
+
data_collection_number: int | None = None
|
|
22
|
+
xtal_snapshot1: str | None = None
|
|
23
|
+
xtal_snapshot2: str | None = None
|
|
24
|
+
xtal_snapshot3: str | None = None
|
|
25
|
+
xtal_snapshot4: str | None = None
|
|
26
|
+
|
|
27
|
+
n_images: int | None = None
|
|
28
|
+
axis_range: float | None = None
|
|
29
|
+
axis_end: float | None = None
|
|
30
|
+
kappa_start: float | None = None
|
|
31
|
+
|
|
32
|
+
parent_id: int | None = None
|
|
33
|
+
visit_string: str | None = None
|
|
34
|
+
sample_id: int | None = None
|
|
35
|
+
detector_id: int | None = None
|
|
36
|
+
axis_start: float | None = None
|
|
37
|
+
focal_spot_size_at_samplex: float | None = None
|
|
38
|
+
focal_spot_size_at_sampley: float | None = None
|
|
39
|
+
slitgap_vertical: float | None = None
|
|
40
|
+
slitgap_horizontal: float | None = None
|
|
41
|
+
beamsize_at_samplex: float | None = None
|
|
42
|
+
beamsize_at_sampley: float | None = None
|
|
43
|
+
transmission: float | None = None
|
|
44
|
+
comments: str | None = None
|
|
45
|
+
detector_distance: float | None = None
|
|
46
|
+
exp_time: float | None = None
|
|
47
|
+
imgdir: str | None = None
|
|
48
|
+
file_template: str | None = None
|
|
49
|
+
imgprefix: str | None = None
|
|
50
|
+
imgsuffix: str | None = None
|
|
51
|
+
n_passes: int | None = None
|
|
52
|
+
overlap: int | None = None
|
|
53
|
+
flux: float | None = None
|
|
54
|
+
start_image_number: int | None = None
|
|
55
|
+
resolution: float | None = None
|
|
56
|
+
wavelength: float | None = None
|
|
57
|
+
xbeam: float | None = None
|
|
58
|
+
ybeam: float | None = None
|
|
59
|
+
synchrotron_mode: str | None = None
|
|
60
|
+
undulator_gap1: float | None = None
|
|
61
|
+
start_time: str | None = None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class DataCollectionPositionInfo:
|
|
66
|
+
pos_x: float
|
|
67
|
+
pos_y: float
|
|
68
|
+
pos_z: float
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class DataCollectionGridInfo:
|
|
73
|
+
dx_in_mm: float
|
|
74
|
+
dy_in_mm: float
|
|
75
|
+
steps_x: int
|
|
76
|
+
steps_y: int
|
|
77
|
+
microns_per_pixel_x: float
|
|
78
|
+
microns_per_pixel_y: float
|
|
79
|
+
snapshot_offset_x_pixel: int
|
|
80
|
+
snapshot_offset_y_pixel: int
|
|
81
|
+
orientation: Orientation
|
|
82
|
+
snaked: bool
|
|
83
|
+
|
|
84
|
+
def as_dict(self):
|
|
85
|
+
d = asdict(self)
|
|
86
|
+
d["orientation"] = self.orientation.value
|
|
87
|
+
return d
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass(kw_only=True)
|
|
91
|
+
class ScanDataInfo:
|
|
92
|
+
data_collection_info: DataCollectionInfo
|
|
93
|
+
data_collection_id: int | None = None
|
|
94
|
+
data_collection_position_info: DataCollectionPositionInfo | None = None
|
|
95
|
+
data_collection_grid_info: DataCollectionGridInfo | None = None
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import configparser
|
|
2
|
+
|
|
3
|
+
from requests import patch, post
|
|
4
|
+
from requests.auth import AuthBase
|
|
5
|
+
|
|
6
|
+
from mx_bluesky.hyperion.external_interaction.exceptions import ISPyBDepositionNotMade
|
|
7
|
+
from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_utils import (
|
|
8
|
+
get_current_time_string,
|
|
9
|
+
get_ispyb_config,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
RobotActionID = int
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BearerAuth(AuthBase):
|
|
16
|
+
def __init__(self, token):
|
|
17
|
+
self.token = token
|
|
18
|
+
|
|
19
|
+
def __call__(self, r):
|
|
20
|
+
r.headers["authorization"] = "Bearer " + self.token
|
|
21
|
+
return r
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _get_base_url_and_token() -> tuple[str, str]:
|
|
25
|
+
config = configparser.ConfigParser()
|
|
26
|
+
conf = get_ispyb_config()
|
|
27
|
+
config.read(conf)
|
|
28
|
+
expeye_config = config["expeye"]
|
|
29
|
+
return expeye_config["url"], expeye_config["token"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ExpeyeInteraction:
|
|
33
|
+
CREATE_ROBOT_ACTION = "/proposals/{proposal}/sessions/{visit_number}/robot-actions"
|
|
34
|
+
UPDATE_ROBOT_ACTION = "/robot-actions/{action_id}"
|
|
35
|
+
|
|
36
|
+
def __init__(self) -> None:
|
|
37
|
+
url, token = _get_base_url_and_token()
|
|
38
|
+
self.base_url = url + "/core"
|
|
39
|
+
self.auth = BearerAuth(token)
|
|
40
|
+
|
|
41
|
+
def _send_and_get_response(self, url, data, send_func) -> dict:
|
|
42
|
+
response = send_func(url, auth=self.auth, json=data)
|
|
43
|
+
if not response.ok:
|
|
44
|
+
raise ISPyBDepositionNotMade(f"Could not write {data} to {url}: {response}")
|
|
45
|
+
return response.json()
|
|
46
|
+
|
|
47
|
+
def start_load(
|
|
48
|
+
self,
|
|
49
|
+
proposal_reference: str,
|
|
50
|
+
visit_number: int,
|
|
51
|
+
sample_id: int,
|
|
52
|
+
dewar_location: int,
|
|
53
|
+
container_location: int,
|
|
54
|
+
) -> RobotActionID:
|
|
55
|
+
"""Create a robot load entry in ispyb.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
proposal_reference (str): The proposal of the experiment e.g. cm37235
|
|
59
|
+
visit_number (int): The visit number for the proposal, usually this can be
|
|
60
|
+
found added to the end of the proposal e.g. the data for
|
|
61
|
+
visit number 2 of proposal cm37235 is in cm37235-2
|
|
62
|
+
sample_id (int): The id of the sample in the database
|
|
63
|
+
dewar_location (int): Which puck in the dewar the sample is in
|
|
64
|
+
container_location (int): Which pin in that puck has the sample
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
RobotActionID: The id of the robot load action that is created
|
|
68
|
+
"""
|
|
69
|
+
url = self.base_url + self.CREATE_ROBOT_ACTION.format(
|
|
70
|
+
proposal=proposal_reference, visit_number=visit_number
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
data = {
|
|
74
|
+
"startTimestamp": get_current_time_string(),
|
|
75
|
+
"sampleId": sample_id,
|
|
76
|
+
"actionType": "LOAD",
|
|
77
|
+
"containerLocation": container_location,
|
|
78
|
+
"dewarLocation": dewar_location,
|
|
79
|
+
}
|
|
80
|
+
response = self._send_and_get_response(url, data, post)
|
|
81
|
+
return response["robotActionId"]
|
|
82
|
+
|
|
83
|
+
def update_barcode_and_snapshots(
|
|
84
|
+
self,
|
|
85
|
+
action_id: RobotActionID,
|
|
86
|
+
barcode: str,
|
|
87
|
+
snapshot_before_path: str,
|
|
88
|
+
snapshot_after_path: str,
|
|
89
|
+
):
|
|
90
|
+
"""Update the barcode and snapshots of an existing robot action.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
action_id (RobotActionID): The id of the action to update
|
|
94
|
+
barcode (str): The barcode to give the action
|
|
95
|
+
snapshot_before_path (str): Path to the snapshot before robot load
|
|
96
|
+
snapshot_after_path (str): Path to the snapshot after robot load
|
|
97
|
+
"""
|
|
98
|
+
url = self.base_url + self.UPDATE_ROBOT_ACTION.format(action_id=action_id)
|
|
99
|
+
|
|
100
|
+
data = {
|
|
101
|
+
"sampleBarcode": barcode,
|
|
102
|
+
"xtalSnapshotBefore": snapshot_before_path,
|
|
103
|
+
"xtalSnapshotAfter": snapshot_after_path,
|
|
104
|
+
}
|
|
105
|
+
self._send_and_get_response(url, data, patch)
|
|
106
|
+
|
|
107
|
+
def end_load(self, action_id: RobotActionID, status: str, reason: str):
|
|
108
|
+
"""Finish an existing robot action, providing final information about how it went
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
action_id (RobotActionID): The action to finish.
|
|
112
|
+
status (str): The status of the action at the end, "success" for success,
|
|
113
|
+
otherwise error
|
|
114
|
+
reason (str): If the status is in error than the reason for that error
|
|
115
|
+
"""
|
|
116
|
+
url = self.base_url + self.UPDATE_ROBOT_ACTION.format(action_id=action_id)
|
|
117
|
+
|
|
118
|
+
run_status = "SUCCESS" if status == "success" else "ERROR"
|
|
119
|
+
|
|
120
|
+
data = {
|
|
121
|
+
"endTimestamp": get_current_time_string(),
|
|
122
|
+
"status": run_status,
|
|
123
|
+
"message": reason,
|
|
124
|
+
}
|
|
125
|
+
self._send_and_get_response(url, data, patch)
|