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
|
@@ -4,7 +4,7 @@ import bluesky.plan_stubs as bps
|
|
|
4
4
|
import pydantic
|
|
5
5
|
from blueapi.core import BlueskyContext
|
|
6
6
|
from bluesky.utils import Msg
|
|
7
|
-
from dodal.devices.
|
|
7
|
+
from dodal.devices.motors import XYZOmegaStage
|
|
8
8
|
from dodal.devices.oav.oav_detector import OAV
|
|
9
9
|
from dodal.devices.oav.oav_parameters import OAV_CONFIG_JSON, OAVParameters
|
|
10
10
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection, Tip
|
|
@@ -14,27 +14,26 @@ from dodal.devices.oav.utils import (
|
|
|
14
14
|
get_move_required_so_that_beam_is_at_pixel,
|
|
15
15
|
wait_for_tip_to_be_found,
|
|
16
16
|
)
|
|
17
|
-
from dodal.devices.smargon import Smargon
|
|
18
17
|
|
|
18
|
+
from mx_bluesky.common.device_setup_plans.gonio import (
|
|
19
|
+
move_gonio_warn_on_out_of_range,
|
|
20
|
+
)
|
|
19
21
|
from mx_bluesky.common.device_setup_plans.setup_oav import pre_centring_setup_oav
|
|
22
|
+
from mx_bluesky.common.parameters.constants import HardwareConstants
|
|
20
23
|
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
21
|
-
from mx_bluesky.common.utils.exceptions import
|
|
24
|
+
from mx_bluesky.common.utils.exceptions import SampleError, catch_exception_and_warn
|
|
22
25
|
from mx_bluesky.common.utils.log import LOGGER
|
|
23
|
-
from mx_bluesky.hyperion.device_setup_plans.smargon import (
|
|
24
|
-
move_smargon_warn_on_out_of_range,
|
|
25
|
-
)
|
|
26
|
-
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
27
26
|
|
|
28
27
|
DEFAULT_STEP_SIZE = 0.5
|
|
28
|
+
CONST = HardwareConstants()
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
32
32
|
class PinTipCentringComposite:
|
|
33
33
|
"""All devices which are directly or indirectly required by this plan"""
|
|
34
34
|
|
|
35
|
-
backlight: Backlight
|
|
36
35
|
oav: OAV
|
|
37
|
-
|
|
36
|
+
gonio: XYZOmegaStage
|
|
38
37
|
pin_tip_detection: PinTipDetection
|
|
39
38
|
|
|
40
39
|
|
|
@@ -53,7 +52,7 @@ def trigger_and_return_pin_tip(
|
|
|
53
52
|
|
|
54
53
|
def move_pin_into_view(
|
|
55
54
|
pin_tip_device: PinTipDetection,
|
|
56
|
-
|
|
55
|
+
gonio: XYZOmegaStage,
|
|
57
56
|
step_magnitude_mm: float = DEFAULT_STEP_SIZE,
|
|
58
57
|
max_steps: int = 2,
|
|
59
58
|
) -> Generator[Msg, None, Pixel]:
|
|
@@ -63,13 +62,13 @@ def move_pin_into_view(
|
|
|
63
62
|
|
|
64
63
|
Args:
|
|
65
64
|
pin_tip_device (PinTipDetection): The device being used to detect the pin
|
|
66
|
-
|
|
65
|
+
gonio (XYZOmegaStage): The stage(gonio) to move the tip
|
|
67
66
|
step_magnitude_mm (float, optional): Distance to move the gonio (in mm) for each
|
|
68
67
|
step of the search. Defaults to 0.5.
|
|
69
68
|
max_steps (int, optional): The number of steps to search with. Defaults to 2.
|
|
70
69
|
|
|
71
70
|
Raises:
|
|
72
|
-
|
|
71
|
+
SampleError: Error if the pin tip is never found
|
|
73
72
|
|
|
74
73
|
Returns:
|
|
75
74
|
Tuple[int, int]: The location of the pin tip in pixels
|
|
@@ -88,25 +87,25 @@ def move_pin_into_view(
|
|
|
88
87
|
direction_multiple = -1 if tip_xy_px[0] == 0 else 1
|
|
89
88
|
step_vector_mm = step_magnitude_mm * direction_multiple
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
ideal_move_to_find_pin = float(
|
|
93
|
-
high_limit = yield from bps.rd(
|
|
94
|
-
low_limit = yield from bps.rd(
|
|
90
|
+
stage_x = yield from bps.rd(gonio.x.user_readback)
|
|
91
|
+
ideal_move_to_find_pin = float(stage_x) + step_vector_mm
|
|
92
|
+
high_limit = yield from bps.rd(gonio.x.high_limit_travel)
|
|
93
|
+
low_limit = yield from bps.rd(gonio.x.low_limit_travel)
|
|
95
94
|
move_within_limits = max(min(ideal_move_to_find_pin, high_limit), low_limit)
|
|
96
95
|
if move_within_limits != ideal_move_to_find_pin:
|
|
97
96
|
LOGGER.warning(
|
|
98
97
|
f"Pin tip is off screen, and moving {step_vector_mm}mm would cross limits, "
|
|
99
98
|
f"moving to {move_within_limits} instead"
|
|
100
99
|
)
|
|
101
|
-
yield from bps.mv(
|
|
100
|
+
yield from bps.mv(gonio.x, move_within_limits)
|
|
102
101
|
|
|
103
102
|
# Some time for the view to settle after the move
|
|
104
|
-
yield from bps.sleep(CONST.
|
|
103
|
+
yield from bps.sleep(CONST.OAV_REFRESH_DELAY)
|
|
105
104
|
|
|
106
105
|
tip_xy_px = yield from trigger_and_return_pin_tip(pin_tip_device)
|
|
107
106
|
|
|
108
107
|
if not pin_tip_valid(tip_xy_px):
|
|
109
|
-
raise
|
|
108
|
+
raise SampleError(
|
|
110
109
|
"Pin tip centring failed - pin too long/short/bent and out of range"
|
|
111
110
|
)
|
|
112
111
|
else:
|
|
@@ -127,7 +126,7 @@ def pin_tip_centre_plan(
|
|
|
127
126
|
to be.
|
|
128
127
|
"""
|
|
129
128
|
oav: OAV = composite.oav
|
|
130
|
-
|
|
129
|
+
gonio: XYZOmegaStage = composite.gonio
|
|
131
130
|
oav_params = OAVParameters("pinTipCentring", oav_config_file)
|
|
132
131
|
|
|
133
132
|
pin_tip_setup = composite.pin_tip_detection
|
|
@@ -139,10 +138,10 @@ def pin_tip_centre_plan(
|
|
|
139
138
|
def offset_and_move(tip: Pixel):
|
|
140
139
|
pixel_to_move_to = (tip[0] + tip_offset_px, tip[1])
|
|
141
140
|
position_mm = yield from get_move_required_so_that_beam_is_at_pixel(
|
|
142
|
-
|
|
141
|
+
gonio, pixel_to_move_to, oav
|
|
143
142
|
)
|
|
144
143
|
LOGGER.info(f"Tip centring moving to : {position_mm}")
|
|
145
|
-
yield from
|
|
144
|
+
yield from move_gonio_warn_on_out_of_range(gonio, position_mm)
|
|
146
145
|
|
|
147
146
|
LOGGER.info(f"Tip offset in pixels: {tip_offset_px}")
|
|
148
147
|
|
|
@@ -152,10 +151,10 @@ def pin_tip_centre_plan(
|
|
|
152
151
|
|
|
153
152
|
yield from pre_centring_setup_oav(oav, oav_params, pin_tip_setup)
|
|
154
153
|
|
|
155
|
-
tip = yield from move_pin_into_view(pin_tip_detect,
|
|
154
|
+
tip = yield from move_pin_into_view(pin_tip_detect, gonio)
|
|
156
155
|
yield from offset_and_move(tip)
|
|
157
156
|
|
|
158
|
-
yield from bps.mvr(
|
|
157
|
+
yield from bps.mvr(gonio.omega, -90)
|
|
159
158
|
|
|
160
159
|
# need to wait for the OAV image to update
|
|
161
160
|
# See #673 for improvements
|
|
@@ -83,12 +83,17 @@ class GridDetectionCallback(CallbackBase):
|
|
|
83
83
|
beam_x = data["oav-beam_centre_i"]
|
|
84
84
|
beam_y = data["oav-beam_centre_j"]
|
|
85
85
|
|
|
86
|
+
x_direction = data["oav-x_direction"]
|
|
87
|
+
y_direction = data["oav-y_direction"]
|
|
88
|
+
z_direction = data["oav-z_direction"]
|
|
89
|
+
|
|
86
90
|
position_grid_start_mm = calculate_x_y_z_of_pixel(
|
|
87
91
|
current_xyz,
|
|
88
92
|
smargon_omega,
|
|
89
93
|
centre_of_first_box,
|
|
90
94
|
(beam_x, beam_y),
|
|
91
95
|
(microns_per_pixel_x, microns_per_pixel_y),
|
|
96
|
+
(x_direction, y_direction, z_direction),
|
|
92
97
|
)
|
|
93
98
|
LOGGER.info(f"Calculated start position {position_grid_start_mm}")
|
|
94
99
|
|
|
@@ -5,14 +5,10 @@ from collections.abc import Callable, Sequence
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import TYPE_CHECKING, Any, TypeVar, cast
|
|
7
7
|
|
|
8
|
-
from dodal.beamline_specific_utils.i03 import beam_size_from_aperture
|
|
9
8
|
from dodal.devices.detector import DetectorParams
|
|
10
9
|
from dodal.devices.detector.det_resolution import resolution
|
|
11
10
|
from dodal.devices.synchrotron import SynchrotronMode
|
|
12
11
|
|
|
13
|
-
from mx_bluesky.common.external_interaction.callbacks.common.logging_callback import (
|
|
14
|
-
format_doc_for_log,
|
|
15
|
-
)
|
|
16
12
|
from mx_bluesky.common.external_interaction.callbacks.common.plan_reactive_callback import (
|
|
17
13
|
PlanReactiveCallback,
|
|
18
14
|
)
|
|
@@ -28,8 +24,12 @@ from mx_bluesky.common.external_interaction.ispyb.ispyb_store import (
|
|
|
28
24
|
from mx_bluesky.common.external_interaction.ispyb.ispyb_utils import get_ispyb_config
|
|
29
25
|
from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
|
|
30
26
|
from mx_bluesky.common.parameters.constants import DocDescriptorNames
|
|
31
|
-
from mx_bluesky.common.utils.log import
|
|
32
|
-
|
|
27
|
+
from mx_bluesky.common.utils.log import (
|
|
28
|
+
ISPYB_ZOCALO_CALLBACK_LOGGER,
|
|
29
|
+
format_doc_for_log,
|
|
30
|
+
set_dcgid_tag,
|
|
31
|
+
)
|
|
32
|
+
from mx_bluesky.common.utils.utils import convert_ev_to_angstrom
|
|
33
33
|
|
|
34
34
|
D = TypeVar("D")
|
|
35
35
|
if TYPE_CHECKING:
|
|
@@ -43,9 +43,9 @@ def _update_based_on_energy(
|
|
|
43
43
|
):
|
|
44
44
|
"""If energy has been read as part of this reading then add it into the data
|
|
45
45
|
collection info along with the other fields that depend on it."""
|
|
46
|
-
if energy_kev := doc["data"].get("dcm-
|
|
46
|
+
if energy_kev := doc["data"].get("dcm-energy_in_keV", None):
|
|
47
47
|
energy_ev = energy_kev * 1000
|
|
48
|
-
wavelength_angstroms =
|
|
48
|
+
wavelength_angstroms = convert_ev_to_angstrom(energy_ev)
|
|
49
49
|
data_collection_info.wavelength = wavelength_angstroms
|
|
50
50
|
data_collection_info.resolution = resolution(
|
|
51
51
|
detector_params,
|
|
@@ -147,18 +147,16 @@ class BaseISPyBCallback(PlanReactiveCallback):
|
|
|
147
147
|
)
|
|
148
148
|
return scan_data_infos
|
|
149
149
|
|
|
150
|
-
def _handle_ispyb_transmission_flux_read(
|
|
150
|
+
def _handle_ispyb_transmission_flux_read(
|
|
151
|
+
self, doc: Event
|
|
152
|
+
) -> Sequence[ScanDataInfo]:
|
|
151
153
|
assert self.params
|
|
152
154
|
aperture = doc["data"]["aperture_scatterguard-selected_aperture"]
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
beamsize_x_mm = beamsize.x_um / 1000 if beamsize.x_um else None
|
|
156
|
-
beamsize_y_mm = beamsize.y_um / 1000 if beamsize.y_um else None
|
|
155
|
+
beamsize_x_mm = doc["data"]["beamsize-x_um"] / 1000
|
|
156
|
+
beamsize_y_mm = doc["data"]["beamsize-y_um"] / 1000
|
|
157
157
|
hwscan_data_collection_info = DataCollectionInfo(
|
|
158
158
|
beamsize_at_samplex=beamsize_x_mm,
|
|
159
159
|
beamsize_at_sampley=beamsize_y_mm,
|
|
160
|
-
focal_spot_size_at_samplex=beamsize_x_mm,
|
|
161
|
-
focal_spot_size_at_sampley=beamsize_y_mm,
|
|
162
160
|
flux=doc["data"]["flux-flux_reading"],
|
|
163
161
|
)
|
|
164
162
|
if transmission := doc["data"]["attenuator-actual_transmission"]:
|
|
@@ -4,15 +4,14 @@ from mx_bluesky.common.external_interaction.ispyb.data_model import (
|
|
|
4
4
|
DataCollectionGroupInfo,
|
|
5
5
|
DataCollectionInfo,
|
|
6
6
|
)
|
|
7
|
-
from mx_bluesky.common.external_interaction.ispyb.ispyb_store import (
|
|
8
|
-
EIGER_FILE_SUFFIX,
|
|
9
|
-
I03_EIGER_DETECTOR,
|
|
10
|
-
)
|
|
11
7
|
from mx_bluesky.common.external_interaction.ispyb.ispyb_utils import (
|
|
12
8
|
get_current_time_string,
|
|
13
9
|
)
|
|
14
10
|
from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
|
|
15
11
|
|
|
12
|
+
I03_EIGER_DETECTOR = 78
|
|
13
|
+
EIGER_FILE_SUFFIX = "h5"
|
|
14
|
+
|
|
16
15
|
|
|
17
16
|
def populate_data_collection_group(params: DiffractionExperimentWithSample):
|
|
18
17
|
dcg_info = DataCollectionGroupInfo(
|
|
@@ -31,7 +30,6 @@ def populate_remaining_data_collection_info(
|
|
|
31
30
|
):
|
|
32
31
|
data_collection_info.visit_string = params.visit
|
|
33
32
|
data_collection_info.parent_id = data_collection_group_id
|
|
34
|
-
data_collection_info.sample_id = params.sample_id
|
|
35
33
|
data_collection_info.detector_id = I03_EIGER_DETECTOR
|
|
36
34
|
data_collection_info.comments = comment
|
|
37
35
|
data_collection_info.detector_distance = params.detector_params.detector_distance
|
|
@@ -40,7 +40,7 @@ class PlanReactiveCallback(CallbackBase):
|
|
|
40
40
|
self.log = log
|
|
41
41
|
|
|
42
42
|
def _run_activity_gated(self, name: str, func, doc, override=False):
|
|
43
|
-
# Runs `func` if self.active is True or
|
|
43
|
+
# Runs `func` if self.active is True or override is true. Override can be used
|
|
44
44
|
# to run the function even after setting self.active to False, i.e. in the last
|
|
45
45
|
# handler of a run.
|
|
46
46
|
|
|
@@ -9,7 +9,7 @@ from dodal.devices.zocalo import ZocaloStartInfo, ZocaloTrigger
|
|
|
9
9
|
from mx_bluesky.common.parameters.constants import (
|
|
10
10
|
DocDescriptorNames,
|
|
11
11
|
)
|
|
12
|
-
from mx_bluesky.common.utils.exceptions import
|
|
12
|
+
from mx_bluesky.common.utils.exceptions import ISPyBDepositionNotMadeError
|
|
13
13
|
from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
@@ -89,7 +89,7 @@ class ZocaloCallback(CallbackBase):
|
|
|
89
89
|
f"Zocalo handler received stop document, for run {doc.get('run_start')}."
|
|
90
90
|
)
|
|
91
91
|
if not self._started_zocalo_collections:
|
|
92
|
-
raise
|
|
92
|
+
raise ISPyBDepositionNotMadeError(
|
|
93
93
|
f"No ISPyB IDs received by the end of {self.triggering_plan=}"
|
|
94
94
|
)
|
|
95
95
|
for info in self._started_zocalo_collections:
|
mx_bluesky/common/external_interaction/callbacks/sample_handling/sample_handling_callback.py
CHANGED
|
@@ -12,7 +12,7 @@ from mx_bluesky.common.external_interaction.ispyb.exp_eye_store import (
|
|
|
12
12
|
BLSampleStatus,
|
|
13
13
|
ExpeyeInteraction,
|
|
14
14
|
)
|
|
15
|
-
from mx_bluesky.common.utils.exceptions import
|
|
15
|
+
from mx_bluesky.common.utils.exceptions import CrystalNotFoundError, SampleError
|
|
16
16
|
from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER
|
|
17
17
|
|
|
18
18
|
|
|
@@ -47,7 +47,7 @@ class SampleHandlingCallback(PlanReactiveCallback):
|
|
|
47
47
|
expeye = ExpeyeInteraction()
|
|
48
48
|
if doc["exit_status"] != "success":
|
|
49
49
|
reason = doc.get("reason", "")
|
|
50
|
-
exception_type, message =
|
|
50
|
+
exception_type, message = SampleError.type_and_message_from_reason(
|
|
51
51
|
reason
|
|
52
52
|
)
|
|
53
53
|
self.log.info(
|
|
@@ -83,7 +83,7 @@ class SampleHandlingCallback(PlanReactiveCallback):
|
|
|
83
83
|
|
|
84
84
|
def _decode_sample_status(self, exception_type: str) -> BLSampleStatus:
|
|
85
85
|
match exception_type:
|
|
86
|
-
case
|
|
86
|
+
case SampleError.__name__ | CrystalNotFoundError.__name__:
|
|
87
87
|
return BLSampleStatus.ERROR_SAMPLE
|
|
88
88
|
return BLSampleStatus.ERROR_BEAMLINE
|
|
89
89
|
|
|
@@ -40,8 +40,8 @@ from mx_bluesky.common.parameters.components import DiffractionExperimentWithSam
|
|
|
40
40
|
from mx_bluesky.common.parameters.constants import DocDescriptorNames, PlanNameConstants
|
|
41
41
|
from mx_bluesky.common.parameters.gridscan import GridCommon
|
|
42
42
|
from mx_bluesky.common.utils.exceptions import (
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
ISPyBDepositionNotMadeError,
|
|
44
|
+
SampleError,
|
|
45
45
|
)
|
|
46
46
|
from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER, set_dcgid_tag
|
|
47
47
|
from mx_bluesky.common.utils.utils import number_of_frames_from_scan_spec
|
|
@@ -87,7 +87,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
87
87
|
To use, subscribe the Bluesky RunEngine to an instance of this class.
|
|
88
88
|
E.g.:
|
|
89
89
|
ispyb_handler_callback = FGSISPyBCallback(parameters)
|
|
90
|
-
|
|
90
|
+
run_engine.subscribe(ispyb_handler_callback)
|
|
91
91
|
Or decorate a plan using bluesky.preprocessors.subs_decorator.
|
|
92
92
|
|
|
93
93
|
See: https://blueskyproject.io/bluesky/callbacks.html#ways-to-invoke-callbacks
|
|
@@ -240,7 +240,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
240
240
|
f"{y_steps} by {z_steps}."
|
|
241
241
|
)
|
|
242
242
|
|
|
243
|
-
self._populate_axis_info(data_collection_info,
|
|
243
|
+
self._populate_axis_info(data_collection_info, doc["data"])
|
|
244
244
|
|
|
245
245
|
scan_data_info = ScanDataInfo(
|
|
246
246
|
data_collection_info=data_collection_info,
|
|
@@ -254,15 +254,15 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
254
254
|
self._oav_snapshot_event_idx += 1
|
|
255
255
|
return [scan_data_info]
|
|
256
256
|
|
|
257
|
-
def _populate_axis_info(
|
|
258
|
-
|
|
259
|
-
):
|
|
260
|
-
if omega_start is not None:
|
|
257
|
+
def _populate_axis_info(self, data_collection_info: DataCollectionInfo, doc: dict):
|
|
258
|
+
if (omega_start := doc.get("smargon-omega")) is not None:
|
|
261
259
|
omega_in_gda_space = -omega_start
|
|
262
260
|
data_collection_info.omega_start = omega_in_gda_space
|
|
263
261
|
data_collection_info.axis_start = omega_in_gda_space
|
|
264
262
|
data_collection_info.axis_end = omega_in_gda_space
|
|
265
263
|
data_collection_info.axis_range = 0
|
|
264
|
+
if (chi_start := doc.get("smargon-chi")) is not None:
|
|
265
|
+
data_collection_info.chi_start = chi_start
|
|
266
266
|
|
|
267
267
|
def populate_info_for_update(
|
|
268
268
|
self,
|
|
@@ -303,8 +303,10 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
303
303
|
f"with uid: {self.uid_to_finalize_on}."
|
|
304
304
|
)
|
|
305
305
|
if self.ispyb_ids == IspybIds():
|
|
306
|
-
raise
|
|
307
|
-
|
|
306
|
+
raise ISPyBDepositionNotMadeError(
|
|
307
|
+
"ispyb was not initialised at run start"
|
|
308
|
+
)
|
|
309
|
+
exception_type, message = SampleError.type_and_message_from_reason(
|
|
308
310
|
doc.get("reason", "")
|
|
309
311
|
)
|
|
310
312
|
if exception_type:
|
|
@@ -33,7 +33,7 @@ class GridscanNexusFileCallback(PlanReactiveCallback):
|
|
|
33
33
|
To use, subscribe the Bluesky RunEngine to an instance of this class.
|
|
34
34
|
E.g.:
|
|
35
35
|
nexus_file_handler_callback = NexusFileCallback(parameters)
|
|
36
|
-
|
|
36
|
+
run_engine.subscribe(nexus_file_handler_callback)
|
|
37
37
|
Or decorate a plan using bluesky.preprocessors.subs_decorator.
|
|
38
38
|
|
|
39
39
|
See: https://blueskyproject.io/bluesky/callbacks.html#ways-to-invoke-callbacks
|
|
@@ -89,7 +89,7 @@ class GridscanNexusFileCallback(PlanReactiveCallback):
|
|
|
89
89
|
nexus_writer.beam,
|
|
90
90
|
nexus_writer.attenuator,
|
|
91
91
|
) = create_beam_and_attenuator_parameters(
|
|
92
|
-
data["dcm-
|
|
92
|
+
data["dcm-energy_in_keV"],
|
|
93
93
|
data["flux-flux_reading"],
|
|
94
94
|
data["attenuator-actual_transmission"],
|
|
95
95
|
)
|
|
@@ -9,8 +9,8 @@ from pydantic import TypeAdapter
|
|
|
9
9
|
|
|
10
10
|
from mx_bluesky.common.parameters.constants import (
|
|
11
11
|
GDA_DOMAIN_PROPERTIES_PATH,
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
FeatureSettings,
|
|
13
|
+
FeatureSettingSources,
|
|
14
14
|
OavConstants,
|
|
15
15
|
)
|
|
16
16
|
from mx_bluesky.common.utils.log import LOGGER
|
|
@@ -19,13 +19,13 @@ FEATURE_FLAG_CACHE_LENGTH_S = 60 * 5
|
|
|
19
19
|
# Used by the config server when refreshing its cache
|
|
20
20
|
_JSON_CONFIG_PATHS = [OavConstants.OAV_CONFIG_JSON]
|
|
21
21
|
|
|
22
|
-
T = TypeVar("T", bound=
|
|
22
|
+
T = TypeVar("T", bound=FeatureSettings)
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class MXConfigClient(ConfigServer, Generic[T]):
|
|
26
26
|
def __init__(
|
|
27
27
|
self,
|
|
28
|
-
feature_sources: type[
|
|
28
|
+
feature_sources: type[FeatureSettingSources],
|
|
29
29
|
feature_dc: type[T],
|
|
30
30
|
url: str = "https://daq-config.diamond.ac.uk",
|
|
31
31
|
):
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO replace these with autogenerated schema classes from the ExpEye API
|
|
3
|
+
https://github.com/DiamondLightSource/mx-bluesky/issues/1419
|
|
4
|
+
"""
|
|
5
|
+
|
|
1
6
|
from dataclasses import asdict, dataclass
|
|
2
7
|
from enum import Enum
|
|
3
8
|
|
|
@@ -18,6 +23,9 @@ class DataCollectionGroupInfo:
|
|
|
18
23
|
|
|
19
24
|
@dataclass(kw_only=True)
|
|
20
25
|
class DataCollectionInfo:
|
|
26
|
+
# This is used internally to keep track of the DataCollectionGroup ID
|
|
27
|
+
parent_id: int | None = None
|
|
28
|
+
|
|
21
29
|
omega_start: float | None = None
|
|
22
30
|
data_collection_number: int | None = None
|
|
23
31
|
xtal_snapshot1: str | None = None
|
|
@@ -28,15 +36,12 @@ class DataCollectionInfo:
|
|
|
28
36
|
n_images: int | None = None
|
|
29
37
|
axis_range: float | None = None
|
|
30
38
|
axis_end: float | None = None
|
|
39
|
+
chi_start: float | None = None
|
|
31
40
|
kappa_start: float | None = None
|
|
32
41
|
|
|
33
|
-
parent_id: int | None = None
|
|
34
42
|
visit_string: str | None = None
|
|
35
|
-
sample_id: int | None = None
|
|
36
43
|
detector_id: int | None = None
|
|
37
44
|
axis_start: float | None = None
|
|
38
|
-
focal_spot_size_at_samplex: float | None = None
|
|
39
|
-
focal_spot_size_at_sampley: float | None = None
|
|
40
45
|
slitgap_vertical: float | None = None
|
|
41
46
|
slitgap_horizontal: float | None = None
|
|
42
47
|
beamsize_at_samplex: float | None = None
|
|
@@ -60,6 +65,8 @@ class DataCollectionInfo:
|
|
|
60
65
|
synchrotron_mode: str | None = None
|
|
61
66
|
undulator_gap1: float | None = None
|
|
62
67
|
start_time: str | None = None
|
|
68
|
+
end_time: str | None = None
|
|
69
|
+
run_status: str | None = None
|
|
63
70
|
|
|
64
71
|
|
|
65
72
|
@dataclass
|
|
@@ -7,11 +7,17 @@ from event_model.documents import Event
|
|
|
7
7
|
from requests import JSONDecodeError, patch, post
|
|
8
8
|
from requests.auth import AuthBase
|
|
9
9
|
|
|
10
|
+
from mx_bluesky.common.external_interaction.ispyb.data_model import (
|
|
11
|
+
DataCollectionGridInfo,
|
|
12
|
+
DataCollectionGroupInfo,
|
|
13
|
+
DataCollectionInfo,
|
|
14
|
+
DataCollectionPositionInfo,
|
|
15
|
+
)
|
|
10
16
|
from mx_bluesky.common.external_interaction.ispyb.ispyb_utils import (
|
|
11
17
|
get_current_time_string,
|
|
12
18
|
get_ispyb_config,
|
|
13
19
|
)
|
|
14
|
-
from mx_bluesky.common.utils.exceptions import
|
|
20
|
+
from mx_bluesky.common.utils.exceptions import ISPyBDepositionNotMadeError
|
|
15
21
|
|
|
16
22
|
RobotActionID = int
|
|
17
23
|
|
|
@@ -33,14 +39,16 @@ def _get_base_url_and_token() -> tuple[str, str]:
|
|
|
33
39
|
return expeye_config["url"], expeye_config["token"]
|
|
34
40
|
|
|
35
41
|
|
|
36
|
-
def _send_and_get_response(auth, url, data, send_func) -> dict:
|
|
37
|
-
response = send_func(url, auth=auth, json=data)
|
|
42
|
+
def _send_and_get_response(auth, url, data, send_func, query_params=None) -> dict:
|
|
43
|
+
response = send_func(url, auth=auth, json=data, params=query_params)
|
|
38
44
|
if not response.ok:
|
|
39
45
|
try:
|
|
40
46
|
resp_txt = str(response.json())
|
|
41
47
|
except JSONDecodeError:
|
|
42
48
|
resp_txt = str(response)
|
|
43
|
-
raise
|
|
49
|
+
raise ISPyBDepositionNotMadeError(
|
|
50
|
+
f"Could not write {data} to {url}: {resp_txt}"
|
|
51
|
+
)
|
|
44
52
|
return response.json()
|
|
45
53
|
|
|
46
54
|
|
|
@@ -179,3 +187,154 @@ class ExpeyeInteraction:
|
|
|
179
187
|
bl_sample_status=response["blSampleStatus"],
|
|
180
188
|
container_id=response["containerId"],
|
|
181
189
|
)
|
|
190
|
+
|
|
191
|
+
def create_data_group(
|
|
192
|
+
self, proposal_reference: str, visit_number: int, data: DataCollectionGroupInfo
|
|
193
|
+
) -> int:
|
|
194
|
+
response = _send_and_get_response(
|
|
195
|
+
self._auth,
|
|
196
|
+
self._base_url + f"/proposals/{proposal_reference}/sessions/"
|
|
197
|
+
f"{visit_number}/data-groups",
|
|
198
|
+
_data_collection_group_info_to_json(data),
|
|
199
|
+
post,
|
|
200
|
+
)
|
|
201
|
+
return response["dataCollectionGroupId"]
|
|
202
|
+
|
|
203
|
+
def update_data_group(self, group_id: int, data: DataCollectionGroupInfo):
|
|
204
|
+
_send_and_get_response(
|
|
205
|
+
self._auth,
|
|
206
|
+
self._base_url + f"/data-groups/{group_id}",
|
|
207
|
+
_data_collection_group_info_to_json(data),
|
|
208
|
+
patch,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
def create_data_collection(self, group_id: int, data: DataCollectionInfo) -> int:
|
|
212
|
+
response = _send_and_get_response(
|
|
213
|
+
self._auth,
|
|
214
|
+
self._base_url + f"/data-groups/{group_id}/data-collections",
|
|
215
|
+
_data_collection_info_to_json(data),
|
|
216
|
+
post,
|
|
217
|
+
)
|
|
218
|
+
return response["dataCollectionId"]
|
|
219
|
+
|
|
220
|
+
def update_data_collection(
|
|
221
|
+
self,
|
|
222
|
+
data_collection_id: int,
|
|
223
|
+
data: DataCollectionInfo,
|
|
224
|
+
append_comment: bool = False,
|
|
225
|
+
):
|
|
226
|
+
_send_and_get_response(
|
|
227
|
+
self._auth,
|
|
228
|
+
self._base_url + f"/data-collections/{data_collection_id}",
|
|
229
|
+
_data_collection_info_to_json(data),
|
|
230
|
+
patch,
|
|
231
|
+
{"appendComment": "true"} if append_comment else None,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def create_position(
|
|
235
|
+
self, data_collection_id: int, data: DataCollectionPositionInfo
|
|
236
|
+
):
|
|
237
|
+
_send_and_get_response(
|
|
238
|
+
self._auth,
|
|
239
|
+
self._base_url + f"/data-collections/{data_collection_id}/position",
|
|
240
|
+
_position_info_to_json(data),
|
|
241
|
+
post,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
def create_grid(self, data_collection_id: int, data: DataCollectionGridInfo) -> int:
|
|
245
|
+
response = _send_and_get_response(
|
|
246
|
+
self._auth,
|
|
247
|
+
self._base_url + f"/data-collections/{data_collection_id}/grids",
|
|
248
|
+
_grid_info_to_json(data),
|
|
249
|
+
post,
|
|
250
|
+
)
|
|
251
|
+
return response["gridInfoId"]
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _none_to_absent(json: dict) -> dict:
|
|
255
|
+
for key in [key for key in json if json[key] is None]:
|
|
256
|
+
del json[key]
|
|
257
|
+
return json
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _data_collection_group_info_to_json(data: DataCollectionGroupInfo) -> dict:
|
|
261
|
+
return _none_to_absent(
|
|
262
|
+
{
|
|
263
|
+
"experimentType": data.experiment_type,
|
|
264
|
+
"sampleId": data.sample_id,
|
|
265
|
+
"actualSampleBarcode": data.sample_barcode,
|
|
266
|
+
"comments": data.comments,
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _data_collection_info_to_json(data: DataCollectionInfo) -> dict:
|
|
272
|
+
return _none_to_absent(
|
|
273
|
+
{
|
|
274
|
+
"omegaStart": data.omega_start,
|
|
275
|
+
"dataCollectionNumber": data.data_collection_number,
|
|
276
|
+
"xtalSnapshotFullPath1": data.xtal_snapshot1,
|
|
277
|
+
"xtalSnapshotFullPath2": data.xtal_snapshot2,
|
|
278
|
+
"xtalSnapshotFullPath3": data.xtal_snapshot3,
|
|
279
|
+
"xtalSnapshotFullPath4": data.xtal_snapshot4,
|
|
280
|
+
"numberOfImages": data.n_images,
|
|
281
|
+
"axisRange": data.axis_range,
|
|
282
|
+
"axisEnd": data.axis_end,
|
|
283
|
+
"chiStart": data.chi_start,
|
|
284
|
+
"kappaStart": data.kappa_start,
|
|
285
|
+
"detectorId": data.detector_id,
|
|
286
|
+
"axisStart": data.axis_start,
|
|
287
|
+
"slitGapVertical": data.slitgap_vertical,
|
|
288
|
+
"slitGapHorizontal": data.slitgap_horizontal,
|
|
289
|
+
"beamSizeAtSampleX": data.beamsize_at_samplex,
|
|
290
|
+
"beamSizeAtSampleY": data.beamsize_at_sampley,
|
|
291
|
+
"transmission": data.transmission,
|
|
292
|
+
"comments": data.comments,
|
|
293
|
+
"detectorDistance": data.detector_distance,
|
|
294
|
+
"exposureTime": data.exp_time,
|
|
295
|
+
"imageDirectory": data.imgdir,
|
|
296
|
+
"fileTemplate": data.file_template,
|
|
297
|
+
"imagePrefix": data.imgprefix,
|
|
298
|
+
"imageSuffix": data.imgsuffix,
|
|
299
|
+
"numberOfPasses": data.n_passes,
|
|
300
|
+
"overlap": data.overlap,
|
|
301
|
+
"flux": data.flux,
|
|
302
|
+
"startImageNumber": data.start_image_number,
|
|
303
|
+
"resolution": data.resolution,
|
|
304
|
+
"wavelength": data.wavelength,
|
|
305
|
+
"xBeam": data.xbeam,
|
|
306
|
+
"yBeam": data.ybeam,
|
|
307
|
+
"synchrotronMode": data.synchrotron_mode,
|
|
308
|
+
"undulatorGap1": data.undulator_gap1,
|
|
309
|
+
"startTime": data.start_time,
|
|
310
|
+
"endTime": data.end_time,
|
|
311
|
+
"runStatus": data.run_status,
|
|
312
|
+
}
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _position_info_to_json(data: DataCollectionPositionInfo) -> dict:
|
|
317
|
+
return _none_to_absent(
|
|
318
|
+
{
|
|
319
|
+
"posX": data.pos_x,
|
|
320
|
+
"posY": data.pos_y,
|
|
321
|
+
"posZ": data.pos_z,
|
|
322
|
+
}
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _grid_info_to_json(data: DataCollectionGridInfo) -> dict:
|
|
327
|
+
return _none_to_absent(
|
|
328
|
+
{
|
|
329
|
+
"snapshotOffsetXPixel": data.snapshot_offset_x_pixel,
|
|
330
|
+
"snapshotOffsetYPixel": data.snapshot_offset_y_pixel,
|
|
331
|
+
"dx": data.dx_in_mm,
|
|
332
|
+
"dy": data.dy_in_mm,
|
|
333
|
+
"stepsX": data.steps_x,
|
|
334
|
+
"stepsY": data.steps_y,
|
|
335
|
+
"orientation": data.orientation.value,
|
|
336
|
+
"pixelsPerMicronX": 1 / data.microns_per_pixel_x,
|
|
337
|
+
"pixelsPerMicronY": 1 / data.microns_per_pixel_y,
|
|
338
|
+
"snaked": data.snaked,
|
|
339
|
+
}
|
|
340
|
+
)
|