mx-bluesky 1.5.10__py3-none-any.whl → 1.5.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/aithre_lasershaping/experiment_plans/__init__.py +0 -0
- mx_bluesky/beamlines/aithre_lasershaping/experiment_plans/robot_load_plan.py +198 -0
- mx_bluesky/beamlines/aithre_lasershaping/goniometer_controls.py +2 -2
- mx_bluesky/beamlines/aithre_lasershaping/parameters/__init__.py +0 -0
- mx_bluesky/beamlines/aithre_lasershaping/parameters/constants.py +17 -0
- mx_bluesky/beamlines/aithre_lasershaping/parameters/robot_load_parameters.py +13 -0
- mx_bluesky/beamlines/aithre_lasershaping/pin_tip_centring.py +31 -0
- mx_bluesky/beamlines/aithre_lasershaping/robot_load.py +80 -0
- mx_bluesky/beamlines/i02_1/parameters/gridscan.py +1 -1
- mx_bluesky/beamlines/i04/__init__.py +6 -2
- mx_bluesky/beamlines/i04/callbacks/murko_callback.py +27 -12
- mx_bluesky/beamlines/i04/experiment_plans/i04_grid_detect_then_xray_centre_plan.py +94 -20
- mx_bluesky/beamlines/i04/external_interaction/__init__.py +0 -0
- mx_bluesky/beamlines/i04/external_interaction/config_server.py +15 -0
- mx_bluesky/beamlines/i04/oav_centering_plans/__init__.py +0 -0
- mx_bluesky/beamlines/i04/oav_centering_plans/oav_imaging.py +115 -0
- mx_bluesky/beamlines/i04/parameters/__init__.py +0 -0
- mx_bluesky/beamlines/i04/parameters/constants.py +21 -0
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +24 -1
- mx_bluesky/beamlines/i04/thawing_plan.py +149 -154
- mx_bluesky/beamlines/i24/jungfrau_commissioning/experiment_plans/do_darks.py +55 -10
- mx_bluesky/beamlines/i24/jungfrau_commissioning/plan_stubs/do_external_acquisition.py +1 -1
- mx_bluesky/beamlines/i24/jungfrau_commissioning/plan_stubs/plan_utils.py +1 -1
- mx_bluesky/beamlines/i24/serial/__init__.py +7 -5
- mx_bluesky/beamlines/i24/serial/dcid.py +6 -7
- mx_bluesky/beamlines/i24/serial/extruder/{i24ssx_Extruder_Collect_py3v2.py → i24ssx_extruder_collect_py3v2.py} +70 -37
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/CustomChip_py3v1.edl +11 -11
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DetStage.edl +3 -3
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +142 -142
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +135 -135
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/PMAC_Command.edl +8 -8
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +13 -13
- mx_bluesky/beamlines/i24/serial/fixed_target/{i24ssx_Chip_Collect_py3v1.py → i24ssx_chip_collect_py3v1.py} +12 -9
- mx_bluesky/beamlines/i24/serial/fixed_target/{i24ssx_Chip_Manager_py3v1.py → i24ssx_chip_manager_py3v1.py} +81 -78
- mx_bluesky/beamlines/i24/serial/fixed_target/{i24ssx_Chip_StartUp_py3v1.py → i24ssx_chip_startup_py3v1.py} +3 -3
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +33 -33
- mx_bluesky/beamlines/i24/serial/log.py +11 -11
- mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
- mx_bluesky/beamlines/i24/serial/parameters/utils.py +5 -5
- mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -12
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +122 -334
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv_abstract.py +5 -5
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +30 -251
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +3 -3
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +4 -4
- mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +103 -16
- mx_bluesky/beamlines/i24/serial/web_gui_plans/oav_plans.py +64 -0
- mx_bluesky/beamlines/i24/serial/write_nexus.py +4 -4
- mx_bluesky/common/device_setup_plans/gonio.py +28 -0
- mx_bluesky/common/device_setup_plans/manipulate_sample.py +8 -1
- mx_bluesky/common/device_setup_plans/robot_load_unload.py +1 -1
- mx_bluesky/common/device_setup_plans/setup_oav.py +8 -0
- mx_bluesky/common/device_setup_plans/setup_zebra_and_shutter.py +0 -5
- mx_bluesky/common/device_setup_plans/xbpm_feedback.py +8 -1
- mx_bluesky/common/experiment_plans/beamstop_check.py +229 -0
- mx_bluesky/common/experiment_plans/common_flyscan_xray_centre_plan.py +8 -6
- mx_bluesky/common/experiment_plans/common_grid_detect_then_xray_centre_plan.py +2 -2
- mx_bluesky/common/experiment_plans/inner_plans/do_fgs.py +1 -1
- mx_bluesky/common/experiment_plans/inner_plans/read_hardware.py +7 -4
- mx_bluesky/common/experiment_plans/inner_plans/write_sample_status.py +2 -2
- mx_bluesky/common/experiment_plans/oav_snapshot_plan.py +1 -2
- mx_bluesky/{hyperion → common}/experiment_plans/pin_tip_centring_plan.py +23 -24
- mx_bluesky/common/external_interaction/callbacks/common/grid_detection_callback.py +5 -0
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +13 -15
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_mapping.py +3 -5
- mx_bluesky/common/external_interaction/callbacks/common/plan_reactive_callback.py +1 -1
- mx_bluesky/common/external_interaction/callbacks/common/zocalo_callback.py +2 -2
- mx_bluesky/common/external_interaction/callbacks/sample_handling/sample_handling_callback.py +3 -3
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +12 -10
- mx_bluesky/common/external_interaction/callbacks/xray_centre/nexus_callback.py +2 -2
- mx_bluesky/common/external_interaction/config_server.py +4 -4
- mx_bluesky/common/external_interaction/ispyb/data_model.py +11 -4
- mx_bluesky/common/external_interaction/ispyb/exp_eye_store.py +163 -4
- mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +76 -167
- mx_bluesky/common/external_interaction/ispyb/ispyb_utils.py +0 -14
- mx_bluesky/common/external_interaction/nexus/nexus_utils.py +2 -2
- mx_bluesky/common/external_interaction/nexus/write_nexus.py +3 -3
- mx_bluesky/common/parameters/components.py +1 -0
- mx_bluesky/common/parameters/constants.py +4 -3
- mx_bluesky/common/parameters/device_composites.py +4 -2
- mx_bluesky/common/parameters/gridscan.py +2 -2
- mx_bluesky/common/utils/exceptions.py +24 -7
- mx_bluesky/common/utils/log.py +13 -4
- mx_bluesky/common/utils/tracing.py +5 -5
- mx_bluesky/common/utils/utils.py +56 -8
- mx_bluesky/hyperion/__main__.py +6 -16
- mx_bluesky/hyperion/baton_handler.py +38 -14
- mx_bluesky/hyperion/device_setup_plans/utils.py +1 -1
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +1 -1
- mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +15 -13
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +9 -9
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +7 -8
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +3 -10
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +4 -2
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +10 -4
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/udc_default_state.py +160 -0
- mx_bluesky/hyperion/external_interaction/agamemnon.py +3 -3
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +2 -2
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +3 -3
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +1 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +3 -6
- mx_bluesky/hyperion/external_interaction/config_server.py +5 -5
- mx_bluesky/hyperion/parameters/constants.py +11 -4
- mx_bluesky/hyperion/parameters/device_composites.py +2 -2
- mx_bluesky/hyperion/parameters/gridscan.py +4 -4
- mx_bluesky/hyperion/parameters/robot_load.py +1 -9
- mx_bluesky/hyperion/plan_runner.py +6 -6
- mx_bluesky/hyperion/runner.py +10 -8
- mx_bluesky/jupyter_example.ipynb +3 -3
- {mx_bluesky-1.5.10.dist-info → mx_bluesky-1.5.12.dist-info}/METADATA +9 -7
- {mx_bluesky-1.5.10.dist-info → mx_bluesky-1.5.12.dist-info}/RECORD +118 -104
- mx_bluesky/common/experiment_plans/inner_plans/udc_default_state.py +0 -65
- mx_bluesky/common/external_interaction/callbacks/common/logging_callback.py +0 -29
- mx_bluesky/hyperion/device_setup_plans/smargon.py +0 -25
- {mx_bluesky-1.5.10.dist-info → mx_bluesky-1.5.12.dist-info}/WHEEL +0 -0
- {mx_bluesky-1.5.10.dist-info → mx_bluesky-1.5.12.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.5.10.dist-info → mx_bluesky-1.5.12.dist-info}/licenses/LICENSE +0 -0
- {mx_bluesky-1.5.10.dist-info → mx_bluesky-1.5.12.dist-info}/top_level.txt +0 -0
mx_bluesky/common/utils/utils.py
CHANGED
|
@@ -4,23 +4,25 @@ from math import asin
|
|
|
4
4
|
from scanspec.core import AxesPoints, Axis
|
|
5
5
|
from scipy.constants import physical_constants
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
from mx_bluesky.common.utils.log import LOGGER
|
|
8
|
+
|
|
9
|
+
hc_in_ev_and_angstrom: float = (
|
|
8
10
|
physical_constants["speed of light in vacuum"][0]
|
|
9
11
|
* physical_constants["Planck constant in eV/Hz"][0]
|
|
10
12
|
* 1e10 # Angstroms per metre
|
|
11
13
|
)
|
|
12
14
|
|
|
13
15
|
|
|
14
|
-
def
|
|
15
|
-
return
|
|
16
|
+
def interconvert_ev_angstrom(wavelength_or_energy: float) -> float:
|
|
17
|
+
return hc_in_ev_and_angstrom / wavelength_or_energy
|
|
16
18
|
|
|
17
19
|
|
|
18
|
-
def
|
|
19
|
-
return
|
|
20
|
+
def convert_ev_to_angstrom(hv: float) -> float:
|
|
21
|
+
return interconvert_ev_angstrom(hv)
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
def
|
|
23
|
-
return
|
|
24
|
+
def convert_angstrom_to_ev(wavelength: float) -> float:
|
|
25
|
+
return interconvert_ev_angstrom(wavelength)
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
def number_of_frames_from_scan_spec(scan_points: AxesPoints[Axis]):
|
|
@@ -37,6 +39,52 @@ def energy_to_bragg_angle(energy_kev: float, d_a: float) -> float:
|
|
|
37
39
|
Returns:
|
|
38
40
|
The bragg angle in degrees
|
|
39
41
|
"""
|
|
40
|
-
wavelength_a =
|
|
42
|
+
wavelength_a = convert_ev_to_angstrom(energy_kev * 1000)
|
|
41
43
|
d = d_a
|
|
42
44
|
return asin(wavelength_a / (2 * d)) * 180 / math.pi
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def fix_transmission_and_exposure_time_for_current_wavelength(
|
|
48
|
+
current_wavelength_a: float,
|
|
49
|
+
assumed_wavelength_a_from_settings: float,
|
|
50
|
+
requested_trans_frac: float = 100,
|
|
51
|
+
requested_exposure_time_s: float = 0.004,
|
|
52
|
+
) -> tuple[float, float]:
|
|
53
|
+
"""
|
|
54
|
+
Calculates an exposure time and transmission fraction for XRC which will provide a good signal
|
|
55
|
+
on the detector by using a known good wavelength, comparing it to the beamlines current wavelength,
|
|
56
|
+
then scaling accordingly.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
current_wavelength_a: Current energy of the beamline in angstroms.
|
|
60
|
+
assumed_wavelength_a_from_settings: The known "good" wavelength. This should be read from
|
|
61
|
+
'gda.px.expttable.default.wavelength' in GDA's domain.properties, via the config server.
|
|
62
|
+
requested_trans_frac: Requested transmission fraction to use.
|
|
63
|
+
requested_exposure_time_s: Requested exposure time to use.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
The scaled transmission fraction and exposure time respectively, in a tuple.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
wavelength_scale = (assumed_wavelength_a_from_settings / current_wavelength_a) ** 2
|
|
70
|
+
|
|
71
|
+
# Transmission frac needed to get ideal signal
|
|
72
|
+
ideal_trans_frac = requested_trans_frac * wavelength_scale
|
|
73
|
+
if ideal_trans_frac <= 1:
|
|
74
|
+
new_trans_frac = ideal_trans_frac
|
|
75
|
+
new_exposure_time_s = requested_exposure_time_s
|
|
76
|
+
else:
|
|
77
|
+
# If the scaling would result in transmission fraction > 1,
|
|
78
|
+
# cap it to 1, find remaining scaling needed, and apply it
|
|
79
|
+
# to exposure time instead.
|
|
80
|
+
new_trans_frac = 1
|
|
81
|
+
scaling_applied_to_trans = new_trans_frac / requested_trans_frac
|
|
82
|
+
remaining_scaling_needed = wavelength_scale / scaling_applied_to_trans
|
|
83
|
+
new_exposure_time_s = requested_exposure_time_s * remaining_scaling_needed
|
|
84
|
+
|
|
85
|
+
LOGGER.info(
|
|
86
|
+
f"Fixing transmission fraction to {new_trans_frac} and exposure time to {new_exposure_time_s}s"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Exposure time in FGS IOC is in ms, and must be an integer, so round it here
|
|
90
|
+
return new_trans_frac, round(new_exposure_time_s, 3)
|
mx_bluesky/hyperion/__main__.py
CHANGED
|
@@ -22,7 +22,7 @@ from mx_bluesky.common.utils.log import (
|
|
|
22
22
|
from mx_bluesky.hyperion.baton_handler import run_forever
|
|
23
23
|
from mx_bluesky.hyperion.experiment_plans.experiment_registry import (
|
|
24
24
|
PLAN_REGISTRY,
|
|
25
|
-
|
|
25
|
+
PlanNotFoundError,
|
|
26
26
|
)
|
|
27
27
|
from mx_bluesky.hyperion.external_interaction.agamemnon import (
|
|
28
28
|
compare_params,
|
|
@@ -45,21 +45,13 @@ from mx_bluesky.hyperion.runner import (
|
|
|
45
45
|
from mx_bluesky.hyperion.utils.context import setup_context
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def compose_start_args(context: BlueskyContext, plan_name: str
|
|
48
|
+
def compose_start_args(context: BlueskyContext, plan_name: str):
|
|
49
49
|
experiment_registry_entry = PLAN_REGISTRY.get(plan_name)
|
|
50
50
|
if experiment_registry_entry is None:
|
|
51
|
-
raise
|
|
51
|
+
raise PlanNotFoundError(f"Experiment plan '{plan_name}' not found in registry.")
|
|
52
52
|
|
|
53
53
|
experiment_internal_param_type = experiment_registry_entry.get("param_type")
|
|
54
54
|
plan = context.plan_functions.get(plan_name)
|
|
55
|
-
if experiment_internal_param_type is None:
|
|
56
|
-
raise PlanNotFound(
|
|
57
|
-
f"Corresponding internal param type for '{plan_name}' not found in registry."
|
|
58
|
-
)
|
|
59
|
-
if plan is None:
|
|
60
|
-
raise PlanNotFound(
|
|
61
|
-
f"Experiment plan '{plan_name}' not found in context. Context has {context.plan_functions.keys()}"
|
|
62
|
-
)
|
|
63
55
|
try:
|
|
64
56
|
parameters = experiment_internal_param_type(**json.loads(request.data))
|
|
65
57
|
parameters = update_params_from_agamemnon(parameters)
|
|
@@ -80,13 +72,11 @@ class RunExperiment(Resource):
|
|
|
80
72
|
self.runner = runner
|
|
81
73
|
self.context = context
|
|
82
74
|
|
|
83
|
-
def put(self, plan_name: str, action:
|
|
75
|
+
def put(self, plan_name: str, action: str):
|
|
84
76
|
status_and_message = StatusAndMessage(Status.FAILED, f"{action} not understood")
|
|
85
77
|
if action == Actions.START.value:
|
|
86
78
|
try:
|
|
87
|
-
plan, params, plan_name = compose_start_args(
|
|
88
|
-
self.context, plan_name, action
|
|
89
|
-
)
|
|
79
|
+
plan, params, plan_name = compose_start_args(self.context, plan_name)
|
|
90
80
|
status_and_message = self.runner.start(plan, params, plan_name)
|
|
91
81
|
except Exception as e:
|
|
92
82
|
status_and_message = make_error_status_and_message(e)
|
|
@@ -115,7 +105,7 @@ class StopOrStatus(Resource):
|
|
|
115
105
|
status_and_message = StatusAndMessage(Status.FAILED, f"{action} not understood")
|
|
116
106
|
if action == Actions.STATUS.value:
|
|
117
107
|
LOGGER.debug(
|
|
118
|
-
f"Runner received status request - state of the runner object is: {self.runner.__dict__} - state of the
|
|
108
|
+
f"Runner received status request - state of the runner object is: {self.runner.__dict__} - state of the run_engine is: {self.runner.run_engine.__dict__}"
|
|
119
109
|
)
|
|
120
110
|
status_and_message = self.runner.current_status
|
|
121
111
|
return asdict(status_and_message)
|
|
@@ -9,15 +9,12 @@ from bluesky.utils import MsgGenerator, RunEngineInterrupted
|
|
|
9
9
|
from dodal.common.beamlines.commissioning_mode import set_commissioning_signal
|
|
10
10
|
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
11
11
|
from dodal.devices.baton import Baton
|
|
12
|
+
from dodal.devices.detector.detector_motion import DetectorMotion, ShutterState
|
|
12
13
|
from dodal.devices.motors import XYZStage
|
|
13
14
|
from dodal.devices.robot import BartRobot
|
|
14
15
|
from dodal.devices.smargon import Smargon
|
|
15
16
|
|
|
16
17
|
from mx_bluesky.common.device_setup_plans.robot_load_unload import robot_unload
|
|
17
|
-
from mx_bluesky.common.experiment_plans.inner_plans.udc_default_state import (
|
|
18
|
-
UDCDefaultDevices,
|
|
19
|
-
move_to_udc_default_state,
|
|
20
|
-
)
|
|
21
18
|
from mx_bluesky.common.external_interaction.alerting import (
|
|
22
19
|
AlertService,
|
|
23
20
|
get_alerting_service,
|
|
@@ -27,18 +24,23 @@ from mx_bluesky.common.utils.context import (
|
|
|
27
24
|
device_composite_from_context,
|
|
28
25
|
find_device_in_context,
|
|
29
26
|
)
|
|
27
|
+
from mx_bluesky.common.utils.exceptions import BeamlineCheckFailureError
|
|
30
28
|
from mx_bluesky.common.utils.log import LOGGER
|
|
31
29
|
from mx_bluesky.hyperion.experiment_plans.load_centre_collect_full_plan import (
|
|
32
30
|
create_devices,
|
|
33
31
|
load_centre_collect_full,
|
|
34
32
|
)
|
|
33
|
+
from mx_bluesky.hyperion.experiment_plans.udc_default_state import (
|
|
34
|
+
UDCDefaultDevices,
|
|
35
|
+
move_to_udc_default_state,
|
|
36
|
+
)
|
|
35
37
|
from mx_bluesky.hyperion.external_interaction.agamemnon import (
|
|
36
38
|
create_parameters_from_agamemnon,
|
|
37
39
|
)
|
|
38
40
|
from mx_bluesky.hyperion.external_interaction.alerting.constants import Subjects
|
|
39
41
|
from mx_bluesky.hyperion.parameters.components import Wait
|
|
40
42
|
from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
|
|
41
|
-
from mx_bluesky.hyperion.plan_runner import
|
|
43
|
+
from mx_bluesky.hyperion.plan_runner import PlanError, PlanRunner
|
|
42
44
|
from mx_bluesky.hyperion.utils.context import (
|
|
43
45
|
clear_all_device_caches,
|
|
44
46
|
setup_devices,
|
|
@@ -53,7 +55,7 @@ def run_forever(runner: PlanRunner):
|
|
|
53
55
|
while True:
|
|
54
56
|
try:
|
|
55
57
|
run_udc_when_requested(runner.context, runner)
|
|
56
|
-
except
|
|
58
|
+
except PlanError as e:
|
|
57
59
|
LOGGER.info(
|
|
58
60
|
"Caught exception during plan execution, stopped and waiting for baton.",
|
|
59
61
|
exc_info=e,
|
|
@@ -64,7 +66,7 @@ def run_forever(runner: PlanRunner):
|
|
|
64
66
|
# RunEngine.abort() will have been called and we will get RunEngineInterrupted
|
|
65
67
|
LOGGER.info(
|
|
66
68
|
f"RunEngine was interrupted. Runner state is {runner.current_status}, "
|
|
67
|
-
f"run engine is {runner.
|
|
69
|
+
f"run engine is {runner.run_engine.state}"
|
|
68
70
|
)
|
|
69
71
|
|
|
70
72
|
|
|
@@ -93,13 +95,17 @@ def run_udc_when_requested(context: BlueskyContext, runner: PlanRunner):
|
|
|
93
95
|
* A user requests the baton away from Hyperion
|
|
94
96
|
* Hyperion releases the baton when Agamemnon has no more instructions
|
|
95
97
|
* The RunEngine raises a RequestAbort exception, most likely due to a shutdown command
|
|
96
|
-
* A plan raises an exception not of type
|
|
98
|
+
* A plan raises an exception not of type WarningError (which is then wrapped as a PlanError)
|
|
97
99
|
Args:
|
|
98
100
|
baton: The baton device
|
|
99
101
|
runner: The runner
|
|
100
102
|
"""
|
|
101
103
|
_raise_udc_start_alert(get_alerting_service())
|
|
102
|
-
yield from
|
|
104
|
+
yield from bpp.contingency_wrapper(
|
|
105
|
+
_move_to_udc_default_state(context),
|
|
106
|
+
except_plan=trap_default_state_exception,
|
|
107
|
+
auto_raise=False,
|
|
108
|
+
)
|
|
103
109
|
|
|
104
110
|
# re-fetch the baton because the device has been reinstantiated
|
|
105
111
|
baton = _get_baton(context)
|
|
@@ -109,7 +115,7 @@ def run_udc_when_requested(context: BlueskyContext, runner: PlanRunner):
|
|
|
109
115
|
baton, runner, current_visit
|
|
110
116
|
)
|
|
111
117
|
if current_visit:
|
|
112
|
-
yield from
|
|
118
|
+
yield from _clean_up_udc(runner.context, current_visit)
|
|
113
119
|
|
|
114
120
|
def release_baton() -> MsgGenerator:
|
|
115
121
|
# If hyperion has given up the baton itself we need to also release requested
|
|
@@ -120,8 +126,20 @@ def run_udc_when_requested(context: BlueskyContext, runner: PlanRunner):
|
|
|
120
126
|
yield from bps.abs_set(baton.current_user, NO_USER, wait=True)
|
|
121
127
|
_raise_baton_released_alert(get_alerting_service(), previous_requested_user)
|
|
122
128
|
|
|
129
|
+
def trap_default_state_exception(e: Exception):
|
|
130
|
+
yield from bps.null()
|
|
131
|
+
if isinstance(e, BeamlineCheckFailureError):
|
|
132
|
+
LOGGER.warning("Caught default state check failure:", exc_info=e)
|
|
133
|
+
raise PlanError("Caught default state check failure") from e
|
|
134
|
+
else:
|
|
135
|
+
LOGGER.warning("Caught unexpected exception", exc_info=e)
|
|
136
|
+
raise PlanError("Unexpected exception from UDC Default State plan") from e
|
|
137
|
+
|
|
123
138
|
def collect_then_release() -> MsgGenerator:
|
|
124
|
-
yield from bpp.contingency_wrapper(
|
|
139
|
+
yield from bpp.contingency_wrapper(
|
|
140
|
+
collect(),
|
|
141
|
+
final_plan=release_baton,
|
|
142
|
+
)
|
|
125
143
|
|
|
126
144
|
context.run_engine(acquire_baton())
|
|
127
145
|
_initialise_udc(context, runner.is_dev_mode)
|
|
@@ -145,13 +163,13 @@ def _initialise_udc(context: BlueskyContext, dev_mode: bool):
|
|
|
145
163
|
|
|
146
164
|
def _wait_for_hyperion_requested(baton: Baton):
|
|
147
165
|
LOGGER.debug("Hyperion waiting for baton...")
|
|
148
|
-
|
|
166
|
+
sleep_per_check = 0.1
|
|
149
167
|
while True:
|
|
150
168
|
requested_user = yield from bps.rd(baton.requested_user)
|
|
151
169
|
if requested_user == HYPERION_USER:
|
|
152
170
|
LOGGER.debug("Baton requested for Hyperion")
|
|
153
171
|
break
|
|
154
|
-
yield from bps.sleep(
|
|
172
|
+
yield from bps.sleep(sleep_per_check)
|
|
155
173
|
|
|
156
174
|
|
|
157
175
|
def _fetch_and_process_agamemnon_instruction(
|
|
@@ -239,11 +257,17 @@ def _unrequest_baton(baton: Baton) -> MsgGenerator[str]:
|
|
|
239
257
|
return requested_user
|
|
240
258
|
|
|
241
259
|
|
|
242
|
-
def
|
|
260
|
+
def _clean_up_udc(context: BlueskyContext, visit: str) -> MsgGenerator:
|
|
261
|
+
cleanup_group = "cleanup"
|
|
243
262
|
robot = find_device_in_context(context, "robot", BartRobot)
|
|
244
263
|
smargon = find_device_in_context(context, "smargon", Smargon)
|
|
245
264
|
aperture_scatterguard = find_device_in_context(
|
|
246
265
|
context, "aperture_scatterguard", ApertureScatterguard
|
|
247
266
|
)
|
|
248
267
|
lower_gonio = find_device_in_context(context, "lower_gonio", XYZStage)
|
|
268
|
+
detector_motion = find_device_in_context(context, "detector_motion", DetectorMotion)
|
|
269
|
+
yield from bps.abs_set(
|
|
270
|
+
detector_motion.shutter, ShutterState.CLOSED, group=cleanup_group
|
|
271
|
+
)
|
|
249
272
|
yield from robot_unload(robot, smargon, aperture_scatterguard, lower_gonio, visit)
|
|
273
|
+
yield from bps.wait(cleanup_group)
|
|
@@ -7,6 +7,6 @@ from dodal.devices.i03.dcm import DCM
|
|
|
7
7
|
|
|
8
8
|
def fill_in_energy_if_not_supplied(dcm: DCM, detector_params: DetectorParams):
|
|
9
9
|
if not detector_params.expected_energy_ev:
|
|
10
|
-
actual_energy_ev = 1000 * (yield from bps.rd(dcm.
|
|
10
|
+
actual_energy_ev = 1000 * (yield from bps.rd(dcm.energy_in_keV))
|
|
11
11
|
detector_params.expected_energy_ev = actual_energy_ev
|
|
12
12
|
return detector_params
|
|
@@ -15,7 +15,7 @@ from mx_bluesky.common.device_setup_plans.setup_zebra_and_shutter import (
|
|
|
15
15
|
tidy_up_zebra_after_gridscan,
|
|
16
16
|
)
|
|
17
17
|
from mx_bluesky.common.experiment_plans.common_flyscan_xray_centre_plan import (
|
|
18
|
-
|
|
18
|
+
construct_beamline_specific_fast_gridscan_features,
|
|
19
19
|
)
|
|
20
20
|
from mx_bluesky.common.utils.log import LOGGER
|
|
21
21
|
from mx_bluesky.hyperion.device_setup_plans.setup_panda import (
|
|
@@ -35,7 +35,7 @@ from mx_bluesky.hyperion.parameters.device_composites import (
|
|
|
35
35
|
from mx_bluesky.hyperion.parameters.gridscan import HyperionSpecifiedThreeDGridScan
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class
|
|
38
|
+
class SmargonSpeedError(Exception):
|
|
39
39
|
pass
|
|
40
40
|
|
|
41
41
|
|
|
@@ -54,14 +54,14 @@ def construct_hyperion_specific_features(
|
|
|
54
54
|
xrc_composite.smargon.x,
|
|
55
55
|
xrc_composite.smargon.y,
|
|
56
56
|
xrc_composite.smargon.z,
|
|
57
|
-
xrc_composite.dcm.
|
|
57
|
+
xrc_composite.dcm.energy_in_keV,
|
|
58
58
|
]
|
|
59
59
|
|
|
60
60
|
signals_to_read_during_collection = [
|
|
61
61
|
xrc_composite.aperture_scatterguard,
|
|
62
62
|
xrc_composite.attenuator.actual_transmission,
|
|
63
63
|
xrc_composite.flux.flux_reading,
|
|
64
|
-
xrc_composite.dcm.
|
|
64
|
+
xrc_composite.dcm.energy_in_keV,
|
|
65
65
|
xrc_composite.eiger.bit_depth,
|
|
66
66
|
]
|
|
67
67
|
|
|
@@ -73,7 +73,7 @@ def construct_hyperion_specific_features(
|
|
|
73
73
|
set_flyscan_params_plan = partial(
|
|
74
74
|
set_fast_grid_scan_params,
|
|
75
75
|
xrc_composite.panda_fast_grid_scan,
|
|
76
|
-
xrc_parameters.
|
|
76
|
+
xrc_parameters.panda_fast_gridscan_params,
|
|
77
77
|
)
|
|
78
78
|
fgs_motors = xrc_composite.panda_fast_grid_scan
|
|
79
79
|
|
|
@@ -91,10 +91,10 @@ def construct_hyperion_specific_features(
|
|
|
91
91
|
set_flyscan_params_plan = partial(
|
|
92
92
|
set_fast_grid_scan_params,
|
|
93
93
|
xrc_composite.zebra_fast_grid_scan,
|
|
94
|
-
xrc_parameters.
|
|
94
|
+
xrc_parameters.fast_gridscan_params,
|
|
95
95
|
)
|
|
96
96
|
fgs_motors = xrc_composite.zebra_fast_grid_scan
|
|
97
|
-
return
|
|
97
|
+
return construct_beamline_specific_fast_gridscan_features(
|
|
98
98
|
setup_trigger_plan,
|
|
99
99
|
tidy_plan,
|
|
100
100
|
set_flyscan_params_plan,
|
|
@@ -126,21 +126,23 @@ def _panda_triggering_setup(
|
|
|
126
126
|
xrc_composite.panda_fast_grid_scan.run_up_distance_mm
|
|
127
127
|
)
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
detector_deadtime_s = 1e-4 # This value was empirically found to be safer than the documented deadtime in the Eiger manual
|
|
130
130
|
|
|
131
|
-
time_between_x_steps_ms = (
|
|
131
|
+
time_between_x_steps_ms = (detector_deadtime_s + parameters.exposure_time_s) * 1e3
|
|
132
132
|
|
|
133
133
|
smargon_speed_limit_mm_per_s = yield from bps.rd(
|
|
134
134
|
xrc_composite.smargon.x.max_velocity
|
|
135
135
|
)
|
|
136
136
|
|
|
137
137
|
sample_velocity_mm_per_s = (
|
|
138
|
-
parameters.
|
|
138
|
+
parameters.panda_fast_gridscan_params.x_step_size_mm
|
|
139
|
+
* 1e3
|
|
140
|
+
/ time_between_x_steps_ms
|
|
139
141
|
)
|
|
140
142
|
if sample_velocity_mm_per_s > smargon_speed_limit_mm_per_s:
|
|
141
|
-
raise
|
|
143
|
+
raise SmargonSpeedError(
|
|
142
144
|
f"Smargon speed was calculated from x step size\
|
|
143
|
-
{parameters.
|
|
145
|
+
{parameters.panda_fast_gridscan_params.x_step_size_mm}mm and\
|
|
144
146
|
time_between_x_steps_ms {time_between_x_steps_ms} as\
|
|
145
147
|
{sample_velocity_mm_per_s}mm/s. The smargon's speed limit is\
|
|
146
148
|
{smargon_speed_limit_mm_per_s}mm/s."
|
|
@@ -161,7 +163,7 @@ def _panda_triggering_setup(
|
|
|
161
163
|
|
|
162
164
|
yield from setup_panda_for_flyscan(
|
|
163
165
|
xrc_composite.panda,
|
|
164
|
-
parameters.
|
|
166
|
+
parameters.panda_fast_gridscan_params,
|
|
165
167
|
xrc_composite.smargon,
|
|
166
168
|
parameters.exposure_time_s,
|
|
167
169
|
time_between_x_steps_ms,
|
|
@@ -14,7 +14,7 @@ from dodal.devices.oav.oav_parameters import OAVParameters
|
|
|
14
14
|
import mx_bluesky.common.xrc_result as flyscan_result
|
|
15
15
|
from mx_bluesky.common.parameters.components import WithSnapshot
|
|
16
16
|
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
17
|
-
from mx_bluesky.common.utils.exceptions import
|
|
17
|
+
from mx_bluesky.common.utils.exceptions import CrystalNotFoundError
|
|
18
18
|
from mx_bluesky.common.utils.log import LOGGER
|
|
19
19
|
from mx_bluesky.common.xrc_result import XRayCentreEventHandler
|
|
20
20
|
from mx_bluesky.hyperion.experiment_plans.robot_load_then_centre_plan import (
|
|
@@ -93,7 +93,7 @@ def load_centre_collect_full(
|
|
|
93
93
|
),
|
|
94
94
|
flyscan_event_handler,
|
|
95
95
|
)
|
|
96
|
-
except
|
|
96
|
+
except CrystalNotFoundError:
|
|
97
97
|
if parameters.select_centres.ignore_xtal_not_found:
|
|
98
98
|
LOGGER.info("Ignoring crystal not found due to parameter settings.")
|
|
99
99
|
else:
|
|
@@ -13,7 +13,7 @@ from mx_bluesky.common.utils.context import device_composite_from_context
|
|
|
13
13
|
from mx_bluesky.common.utils.log import LOGGER
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
class
|
|
16
|
+
class AttenuationOptimisationFailedError(Exception):
|
|
17
17
|
pass
|
|
18
18
|
|
|
19
19
|
|
|
@@ -59,7 +59,7 @@ def check_parameters(
|
|
|
59
59
|
|
|
60
60
|
if upper_transmission < lower_transmission:
|
|
61
61
|
raise ValueError(
|
|
62
|
-
f"Upper transmission limit {upper_transmission} must be greater than lower
|
|
62
|
+
f"Upper transmission limit {upper_transmission} must be greater than lower transmission limit {lower_transmission}"
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
if not upper_transmission >= initial_transmission >= lower_transmission:
|
|
@@ -80,7 +80,7 @@ def calculate_new_direction(direction: Direction, deadtime, deadtime_threshold):
|
|
|
80
80
|
if deadtime > deadtime_threshold:
|
|
81
81
|
direction = Direction.NEGATIVE
|
|
82
82
|
LOGGER.info(
|
|
83
|
-
"Found
|
|
83
|
+
"Found transmission to go above deadtime threshold. Reducing transmission..."
|
|
84
84
|
)
|
|
85
85
|
return direction
|
|
86
86
|
|
|
@@ -111,7 +111,7 @@ def deadtime_calc_new_transmission(
|
|
|
111
111
|
Minimum expected transmission. Raise an error if transmission goes lower.
|
|
112
112
|
|
|
113
113
|
Raises:
|
|
114
|
-
|
|
114
|
+
AttenuationOptimisationFailedError:
|
|
115
115
|
This error is thrown if the transmission goes below the expected value or if the maximum cycles are reached
|
|
116
116
|
|
|
117
117
|
Returns:
|
|
@@ -124,7 +124,7 @@ def deadtime_calc_new_transmission(
|
|
|
124
124
|
else:
|
|
125
125
|
transmission /= increment
|
|
126
126
|
if transmission < lower_transmission_limit:
|
|
127
|
-
raise
|
|
127
|
+
raise AttenuationOptimisationFailedError(
|
|
128
128
|
"Calculated transmission is below expected limit"
|
|
129
129
|
)
|
|
130
130
|
return transmission
|
|
@@ -218,7 +218,7 @@ def deadtime_optimisation(
|
|
|
218
218
|
Minimum expected transmission. Raise an error if transmission goes lower.
|
|
219
219
|
|
|
220
220
|
Raises:
|
|
221
|
-
|
|
221
|
+
AttenuationOptimisationFailedError:
|
|
222
222
|
This error is thrown if the transmission goes below the expected value or the maximum cycles are reached
|
|
223
223
|
|
|
224
224
|
Returns:
|
|
@@ -262,7 +262,7 @@ def deadtime_optimisation(
|
|
|
262
262
|
break
|
|
263
263
|
|
|
264
264
|
if cycle == max_cycles - 1:
|
|
265
|
-
raise
|
|
265
|
+
raise AttenuationOptimisationFailedError(
|
|
266
266
|
f"Unable to optimise attenuation after maximum cycles.\
|
|
267
267
|
Deadtime did not get lower than threshold: {deadtime_threshold} in maximum cycles {max_cycles}"
|
|
268
268
|
)
|
|
@@ -367,12 +367,12 @@ def total_counts_optimisation(
|
|
|
367
367
|
if transmission > upper_transmission_limit:
|
|
368
368
|
transmission = upper_transmission_limit
|
|
369
369
|
elif transmission < lower_transmission_limit:
|
|
370
|
-
raise
|
|
370
|
+
raise AttenuationOptimisationFailedError(
|
|
371
371
|
f"Transmission has gone below lower threshold {lower_transmission_limit}"
|
|
372
372
|
)
|
|
373
373
|
|
|
374
374
|
if cycle == max_cycles - 1:
|
|
375
|
-
raise
|
|
375
|
+
raise AttenuationOptimisationFailedError(
|
|
376
376
|
f"Unable to optimise attenuation after maximum cycles.\
|
|
377
377
|
Total count is not within limits: {lower_count_limit} <= {total_count}\
|
|
378
378
|
<= {upper_count_limit}"
|
|
@@ -19,7 +19,11 @@ from mx_bluesky.common.experiment_plans.common_grid_detect_then_xray_centre_plan
|
|
|
19
19
|
detect_grid_and_do_gridscan,
|
|
20
20
|
)
|
|
21
21
|
from mx_bluesky.common.experiment_plans.oav_snapshot_plan import (
|
|
22
|
-
|
|
22
|
+
setup_beamline_for_oav,
|
|
23
|
+
)
|
|
24
|
+
from mx_bluesky.common.experiment_plans.pin_tip_centring_plan import (
|
|
25
|
+
PinTipCentringComposite,
|
|
26
|
+
pin_tip_centre_plan,
|
|
23
27
|
)
|
|
24
28
|
from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
|
|
25
29
|
ispyb_activation_wrapper,
|
|
@@ -31,10 +35,6 @@ from mx_bluesky.common.xrc_result import XRayCentreEventHandler
|
|
|
31
35
|
from mx_bluesky.hyperion.experiment_plans.hyperion_flyscan_xray_centre_plan import (
|
|
32
36
|
construct_hyperion_specific_features,
|
|
33
37
|
)
|
|
34
|
-
from mx_bluesky.hyperion.experiment_plans.pin_tip_centring_plan import (
|
|
35
|
-
PinTipCentringComposite,
|
|
36
|
-
pin_tip_centre_plan,
|
|
37
|
-
)
|
|
38
38
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
39
39
|
from mx_bluesky.hyperion.parameters.device_composites import (
|
|
40
40
|
HyperionGridDetectThenXRayCentreComposite,
|
|
@@ -78,13 +78,12 @@ def pin_centre_then_flyscan_plan(
|
|
|
78
78
|
|
|
79
79
|
pin_tip_centring_composite = PinTipCentringComposite(
|
|
80
80
|
oav=composite.oav,
|
|
81
|
-
|
|
82
|
-
backlight=composite.backlight,
|
|
81
|
+
gonio=composite.smargon,
|
|
83
82
|
pin_tip_detection=composite.pin_tip_detection,
|
|
84
83
|
)
|
|
85
84
|
|
|
86
85
|
def _pin_centre_then_flyscan_plan():
|
|
87
|
-
yield from
|
|
86
|
+
yield from setup_beamline_for_oav(
|
|
88
87
|
composite.smargon, composite.backlight, composite.aperture_scatterguard
|
|
89
88
|
)
|
|
90
89
|
|
|
@@ -20,7 +20,7 @@ from dodal.devices.motors import XYZStage
|
|
|
20
20
|
from dodal.devices.oav.oav_detector import OAV
|
|
21
21
|
from dodal.devices.robot import BartRobot, SampleLocation
|
|
22
22
|
from dodal.devices.smargon import Smargon
|
|
23
|
-
from dodal.devices.thawer import Thawer
|
|
23
|
+
from dodal.devices.thawer import OnOff, Thawer
|
|
24
24
|
from dodal.devices.webcam import Webcam
|
|
25
25
|
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
26
26
|
|
|
@@ -82,7 +82,6 @@ def do_robot_load(
|
|
|
82
82
|
sample_location: SampleLocation,
|
|
83
83
|
sample_id: int,
|
|
84
84
|
demand_energy_ev: float | None,
|
|
85
|
-
thawing_time: float,
|
|
86
85
|
):
|
|
87
86
|
yield from bps.abs_set(composite.robot.next_sample_id, sample_id, wait=True)
|
|
88
87
|
|
|
@@ -96,13 +95,10 @@ def do_robot_load(
|
|
|
96
95
|
|
|
97
96
|
yield from bps.wait("robot_load")
|
|
98
97
|
|
|
99
|
-
yield from bps.abs_set(
|
|
100
|
-
composite.thawer.thaw_for_time_s,
|
|
101
|
-
thawing_time,
|
|
102
|
-
group="thawing_finished",
|
|
103
|
-
)
|
|
104
98
|
yield from wait_for_smargon_not_disabled(composite.smargon)
|
|
105
99
|
|
|
100
|
+
yield from bps.mv(composite.thawer, OnOff.ON)
|
|
101
|
+
|
|
106
102
|
|
|
107
103
|
def pin_already_loaded(
|
|
108
104
|
robot: BartRobot, sample_location: SampleLocation
|
|
@@ -120,7 +116,6 @@ def robot_load_and_snapshots(
|
|
|
120
116
|
location: SampleLocation,
|
|
121
117
|
snapshot_directory: Path,
|
|
122
118
|
sample_id: int,
|
|
123
|
-
thawing_time: float,
|
|
124
119
|
demand_energy_ev: float | None,
|
|
125
120
|
):
|
|
126
121
|
yield from bps.abs_set(composite.backlight, InOut.IN, group="snapshot")
|
|
@@ -134,7 +129,6 @@ def robot_load_and_snapshots(
|
|
|
134
129
|
location,
|
|
135
130
|
sample_id,
|
|
136
131
|
demand_energy_ev,
|
|
137
|
-
thawing_time,
|
|
138
132
|
)
|
|
139
133
|
|
|
140
134
|
gonio_finished = yield from do_plan_while_lower_gonio_at_home(
|
|
@@ -173,7 +167,6 @@ def robot_load_and_change_energy_plan(
|
|
|
173
167
|
sample_location,
|
|
174
168
|
params.snapshot_directory,
|
|
175
169
|
params.sample_id,
|
|
176
|
-
params.thawing_time,
|
|
177
170
|
params.demand_energy_ev,
|
|
178
171
|
),
|
|
179
172
|
md={
|
|
@@ -10,6 +10,7 @@ from bluesky.utils import MsgGenerator
|
|
|
10
10
|
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
11
11
|
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
12
12
|
from dodal.devices.backlight import Backlight
|
|
13
|
+
from dodal.devices.beamsize.beamsize import BeamsizeBase
|
|
13
14
|
from dodal.devices.detector.detector_motion import DetectorMotion
|
|
14
15
|
from dodal.devices.eiger import EigerDetector
|
|
15
16
|
from dodal.devices.fast_grid_scan import PandAFastGridScan, ZebraFastGridScanThreeD
|
|
@@ -26,7 +27,7 @@ from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
|
26
27
|
from dodal.devices.smargon import Smargon
|
|
27
28
|
from dodal.devices.synchrotron import Synchrotron
|
|
28
29
|
from dodal.devices.thawer import Thawer
|
|
29
|
-
from dodal.devices.undulator import
|
|
30
|
+
from dodal.devices.undulator import UndulatorInKeV
|
|
30
31
|
from dodal.devices.webcam import Webcam
|
|
31
32
|
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
32
33
|
from dodal.devices.zebra.zebra import Zebra
|
|
@@ -70,6 +71,7 @@ class RobotLoadThenCentreComposite:
|
|
|
70
71
|
# HyperionGridDetectThenXRayCentreComposite fields
|
|
71
72
|
aperture_scatterguard: ApertureScatterguard
|
|
72
73
|
backlight: Backlight
|
|
74
|
+
beamsize: BeamsizeBase
|
|
73
75
|
detector_motion: DetectorMotion
|
|
74
76
|
eiger: EigerDetector
|
|
75
77
|
zebra_fast_grid_scan: ZebraFastGridScanThreeD
|
|
@@ -79,7 +81,7 @@ class RobotLoadThenCentreComposite:
|
|
|
79
81
|
smargon: Smargon
|
|
80
82
|
synchrotron: Synchrotron
|
|
81
83
|
s4_slit_gaps: S4SlitGaps
|
|
82
|
-
undulator:
|
|
84
|
+
undulator: UndulatorInKeV
|
|
83
85
|
zebra: Zebra
|
|
84
86
|
zocalo: ZocaloResults
|
|
85
87
|
panda: HDFPanda
|