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,15 @@
|
|
|
1
|
+
from mx_bluesky.beamlines.i24.serial.parameters.constants import SSXType
|
|
2
|
+
from mx_bluesky.beamlines.i24.serial.parameters.experiment_parameters import (
|
|
3
|
+
ChipDescription,
|
|
4
|
+
ExtruderParameters,
|
|
5
|
+
FixedTargetParameters,
|
|
6
|
+
)
|
|
7
|
+
from mx_bluesky.beamlines.i24.serial.parameters.utils import get_chip_format
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"SSXType",
|
|
11
|
+
"ExtruderParameters",
|
|
12
|
+
"ChipDescription",
|
|
13
|
+
"FixedTargetParameters",
|
|
14
|
+
"get_chip_format",
|
|
15
|
+
]
|
|
@@ -2,9 +2,9 @@ import json
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import Literal
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel,
|
|
5
|
+
from pydantic import BaseModel, field_validator
|
|
6
6
|
|
|
7
|
-
from mx_bluesky.i24.serial.fixed_target.ft_utils import (
|
|
7
|
+
from mx_bluesky.beamlines.i24.serial.fixed_target.ft_utils import (
|
|
8
8
|
ChipType,
|
|
9
9
|
MappingType,
|
|
10
10
|
PumpProbeSetting,
|
|
@@ -21,7 +21,8 @@ class SerialExperiment(BaseModel):
|
|
|
21
21
|
detector_distance_mm: float
|
|
22
22
|
detector_name: Literal["eiger", "pilatus"]
|
|
23
23
|
|
|
24
|
-
@
|
|
24
|
+
@field_validator("visit", mode="before")
|
|
25
|
+
@classmethod
|
|
25
26
|
def _parse_visit(cls, visit: str | Path):
|
|
26
27
|
if isinstance(visit, str):
|
|
27
28
|
return Path(visit)
|
|
@@ -56,8 +57,6 @@ class ExtruderParameters(SerialExperiment, LaserExperiment):
|
|
|
56
57
|
class ChipDescription(BaseModel):
|
|
57
58
|
"""Parameters defining the chip in use for FT collection."""
|
|
58
59
|
|
|
59
|
-
model_config = ConfigDict(use_enum_values=True)
|
|
60
|
-
|
|
61
60
|
chip_type: ChipType
|
|
62
61
|
x_num_steps: int
|
|
63
62
|
y_num_steps: int
|
|
@@ -68,13 +67,6 @@ class ChipDescription(BaseModel):
|
|
|
68
67
|
b2b_horz: float
|
|
69
68
|
b2b_vert: float
|
|
70
69
|
|
|
71
|
-
@validator("chip_type", pre=True)
|
|
72
|
-
def _parse_chip(cls, chip_type: str | int):
|
|
73
|
-
if isinstance(chip_type, str):
|
|
74
|
-
return ChipType[chip_type]
|
|
75
|
-
else:
|
|
76
|
-
return ChipType(chip_type)
|
|
77
|
-
|
|
78
70
|
@property
|
|
79
71
|
def chip_format(self) -> list[int]:
|
|
80
72
|
return [self.x_blocks, self.y_blocks, self.x_num_steps, self.y_num_steps]
|
|
@@ -97,8 +89,6 @@ class ChipDescription(BaseModel):
|
|
|
97
89
|
class FixedTargetParameters(SerialExperiment, LaserExperiment):
|
|
98
90
|
"""Fixed target parameter model."""
|
|
99
91
|
|
|
100
|
-
model_config = ConfigDict(use_enum_values=True)
|
|
101
|
-
|
|
102
92
|
num_exposures: int
|
|
103
93
|
chip: ChipDescription
|
|
104
94
|
map_type: MappingType
|
|
@@ -106,17 +96,6 @@ class FixedTargetParameters(SerialExperiment, LaserExperiment):
|
|
|
106
96
|
checker_pattern: bool = False
|
|
107
97
|
total_num_images: int = 0 # Calculated in the code for now
|
|
108
98
|
|
|
109
|
-
@validator("map_type", pre=True)
|
|
110
|
-
def _parse_map(cls, map_type: str | int):
|
|
111
|
-
if isinstance(map_type, str):
|
|
112
|
-
return MappingType[map_type]
|
|
113
|
-
else:
|
|
114
|
-
return MappingType(map_type)
|
|
115
|
-
|
|
116
|
-
@validator("pump_repeat", pre=True)
|
|
117
|
-
def _parse_pump(cls, pump_repeat: int):
|
|
118
|
-
return PumpProbeSetting(pump_repeat)
|
|
119
|
-
|
|
120
99
|
@classmethod
|
|
121
100
|
def from_file(cls, filename: str | Path):
|
|
122
101
|
with open(filename) as fh:
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
|
-
from mx_bluesky.i24.serial.fixed_target.ft_utils import ChipType
|
|
4
|
-
from mx_bluesky.i24.serial.parameters.experiment_parameters import
|
|
5
|
-
|
|
3
|
+
from mx_bluesky.beamlines.i24.serial.fixed_target.ft_utils import ChipType
|
|
4
|
+
from mx_bluesky.beamlines.i24.serial.parameters.experiment_parameters import (
|
|
5
|
+
ChipDescription,
|
|
6
|
+
)
|
|
7
|
+
from mx_bluesky.beamlines.i24.serial.setup_beamline import caget, pv
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
def get_chip_format(chip_type: ChipType) -> ChipDescription:
|
|
@@ -7,8 +7,8 @@ from dodal.devices.i24.beamstop import Beamstop, BeamstopPositions
|
|
|
7
7
|
from dodal.devices.i24.dual_backlight import BacklightPositions, DualBacklight
|
|
8
8
|
from dodal.devices.i24.i24_detector_motion import DetectorMotion
|
|
9
9
|
|
|
10
|
-
from mx_bluesky.i24.serial.setup_beamline import pv
|
|
11
|
-
from mx_bluesky.i24.serial.setup_beamline.ca import caget, caput
|
|
10
|
+
from mx_bluesky.beamlines.i24.serial.setup_beamline import pv
|
|
11
|
+
from mx_bluesky.beamlines.i24.serial.setup_beamline.ca import caget, caput
|
|
12
12
|
|
|
13
13
|
logger = logging.getLogger("I24ssx.sup")
|
|
14
14
|
|
|
@@ -13,11 +13,11 @@ from bluesky.utils import Msg
|
|
|
13
13
|
from dodal.common import inject
|
|
14
14
|
from dodal.devices.i24.i24_detector_motion import DetectorMotion
|
|
15
15
|
|
|
16
|
-
from mx_bluesky.i24.serial import log
|
|
17
|
-
from mx_bluesky.i24.serial.parameters import SSXType
|
|
18
|
-
from mx_bluesky.i24.serial.setup_beamline import pv
|
|
19
|
-
from mx_bluesky.i24.serial.setup_beamline.ca import caget
|
|
20
|
-
from mx_bluesky.i24.serial.setup_beamline.pv_abstract import (
|
|
16
|
+
from mx_bluesky.beamlines.i24.serial import log
|
|
17
|
+
from mx_bluesky.beamlines.i24.serial.parameters import SSXType
|
|
18
|
+
from mx_bluesky.beamlines.i24.serial.setup_beamline import pv
|
|
19
|
+
from mx_bluesky.beamlines.i24.serial.setup_beamline.ca import caget
|
|
20
|
+
from mx_bluesky.beamlines.i24.serial.setup_beamline.pv_abstract import (
|
|
21
21
|
Detector,
|
|
22
22
|
Eiger,
|
|
23
23
|
Pilatus,
|
|
@@ -8,9 +8,12 @@ from typing import Literal
|
|
|
8
8
|
|
|
9
9
|
import requests
|
|
10
10
|
|
|
11
|
-
from mx_bluesky.i24.serial.fixed_target.ft_utils import ChipType, MappingType
|
|
12
|
-
from mx_bluesky.i24.serial.parameters import
|
|
13
|
-
|
|
11
|
+
from mx_bluesky.beamlines.i24.serial.fixed_target.ft_utils import ChipType, MappingType
|
|
12
|
+
from mx_bluesky.beamlines.i24.serial.parameters import (
|
|
13
|
+
ExtruderParameters,
|
|
14
|
+
FixedTargetParameters,
|
|
15
|
+
)
|
|
16
|
+
from mx_bluesky.beamlines.i24.serial.setup_beamline import Eiger, caget, cagetstring
|
|
14
17
|
|
|
15
18
|
logger = logging.getLogger("I24ssx.nexus_writer")
|
|
16
19
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# placeholder file to start layout
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import json
|
|
3
|
+
import threading
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from dataclasses import asdict
|
|
6
|
+
from queue import Queue
|
|
7
|
+
from traceback import format_exception
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from blueapi.core import BlueskyContext, MsgGenerator
|
|
11
|
+
from bluesky.callbacks.zmq import Publisher
|
|
12
|
+
from bluesky.run_engine import RunEngine
|
|
13
|
+
from flask import Flask, request
|
|
14
|
+
from flask_restful import Api, Resource
|
|
15
|
+
from pydantic.dataclasses import dataclass
|
|
16
|
+
|
|
17
|
+
from mx_bluesky.hyperion.exceptions import WarningException
|
|
18
|
+
from mx_bluesky.hyperion.experiment_plans.experiment_registry import (
|
|
19
|
+
PLAN_REGISTRY,
|
|
20
|
+
PlanNotFound,
|
|
21
|
+
)
|
|
22
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.__main__ import (
|
|
23
|
+
setup_logging as setup_callback_logging,
|
|
24
|
+
)
|
|
25
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.aperture_change_callback import (
|
|
26
|
+
ApertureChangeCallback,
|
|
27
|
+
)
|
|
28
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.common.callback_util import (
|
|
29
|
+
CallbacksFactory,
|
|
30
|
+
)
|
|
31
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.log_uid_tag_callback import (
|
|
32
|
+
LogUidTaggingCallback,
|
|
33
|
+
)
|
|
34
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.logging_callback import (
|
|
35
|
+
VerbosePlanExecutionLoggingCallback,
|
|
36
|
+
)
|
|
37
|
+
from mx_bluesky.hyperion.log import (
|
|
38
|
+
LOGGER,
|
|
39
|
+
do_default_logging_setup,
|
|
40
|
+
flush_debug_handler,
|
|
41
|
+
)
|
|
42
|
+
from mx_bluesky.hyperion.parameters.cli import parse_cli_args
|
|
43
|
+
from mx_bluesky.hyperion.parameters.components import HyperionParameters
|
|
44
|
+
from mx_bluesky.hyperion.parameters.constants import CONST, Actions, Status
|
|
45
|
+
from mx_bluesky.hyperion.tracing import TRACER
|
|
46
|
+
from mx_bluesky.hyperion.utils.context import setup_context
|
|
47
|
+
|
|
48
|
+
VERBOSE_EVENT_LOGGING: bool | None = None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class Command:
|
|
53
|
+
action: Actions
|
|
54
|
+
devices: Any | None = None
|
|
55
|
+
experiment: Callable[[Any, Any], MsgGenerator] | None = None
|
|
56
|
+
parameters: HyperionParameters | None = None
|
|
57
|
+
callbacks: CallbacksFactory | None = None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class StatusAndMessage:
|
|
62
|
+
status: str
|
|
63
|
+
message: str = ""
|
|
64
|
+
|
|
65
|
+
def __init__(self, status: Status, message: str = "") -> None:
|
|
66
|
+
self.status = status.value
|
|
67
|
+
self.message = message
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class ErrorStatusAndMessage(StatusAndMessage):
|
|
72
|
+
exception_type: str = ""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def make_error_status_and_message(exception: Exception):
|
|
76
|
+
return ErrorStatusAndMessage(
|
|
77
|
+
status=Status.FAILED.value,
|
|
78
|
+
message=repr(exception),
|
|
79
|
+
exception_type=type(exception).__name__,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class BlueskyRunner:
|
|
84
|
+
def __init__(
|
|
85
|
+
self,
|
|
86
|
+
RE: RunEngine,
|
|
87
|
+
context: BlueskyContext,
|
|
88
|
+
skip_startup_connection=False,
|
|
89
|
+
use_external_callbacks: bool = False,
|
|
90
|
+
) -> None:
|
|
91
|
+
self.command_queue: Queue[Command] = Queue()
|
|
92
|
+
self.current_status: StatusAndMessage = StatusAndMessage(Status.IDLE)
|
|
93
|
+
self.last_run_aborted: bool = False
|
|
94
|
+
self.aperture_change_callback = ApertureChangeCallback()
|
|
95
|
+
self.logging_uid_tag_callback = LogUidTaggingCallback()
|
|
96
|
+
self.context: BlueskyContext
|
|
97
|
+
|
|
98
|
+
self.RE = RE
|
|
99
|
+
self.context = context
|
|
100
|
+
self.subscribed_per_plan_callbacks: list[int] = []
|
|
101
|
+
RE.subscribe(self.aperture_change_callback)
|
|
102
|
+
RE.subscribe(self.logging_uid_tag_callback)
|
|
103
|
+
|
|
104
|
+
self.use_external_callbacks = use_external_callbacks
|
|
105
|
+
if self.use_external_callbacks:
|
|
106
|
+
LOGGER.info("Connecting to external callback ZMQ proxy...")
|
|
107
|
+
self.publisher = Publisher(f"localhost:{CONST.CALLBACK_0MQ_PROXY_PORTS[0]}")
|
|
108
|
+
RE.subscribe(self.publisher)
|
|
109
|
+
|
|
110
|
+
if VERBOSE_EVENT_LOGGING:
|
|
111
|
+
RE.subscribe(VerbosePlanExecutionLoggingCallback())
|
|
112
|
+
|
|
113
|
+
self.skip_startup_connection = skip_startup_connection
|
|
114
|
+
if not self.skip_startup_connection:
|
|
115
|
+
LOGGER.info("Initialising dodal devices...")
|
|
116
|
+
for plan_name in PLAN_REGISTRY:
|
|
117
|
+
PLAN_REGISTRY[plan_name]["setup"](context)
|
|
118
|
+
|
|
119
|
+
def start(
|
|
120
|
+
self,
|
|
121
|
+
experiment: Callable,
|
|
122
|
+
parameters: HyperionParameters,
|
|
123
|
+
plan_name: str,
|
|
124
|
+
callbacks: CallbacksFactory | None,
|
|
125
|
+
) -> StatusAndMessage:
|
|
126
|
+
LOGGER.info(f"Started with parameters: {parameters.model_dump_json(indent=2)}")
|
|
127
|
+
|
|
128
|
+
devices: Any = PLAN_REGISTRY[plan_name]["setup"](self.context)
|
|
129
|
+
|
|
130
|
+
if (
|
|
131
|
+
self.current_status.status == Status.BUSY.value
|
|
132
|
+
or self.current_status.status == Status.ABORTING.value
|
|
133
|
+
):
|
|
134
|
+
return StatusAndMessage(Status.FAILED, "Bluesky already running")
|
|
135
|
+
else:
|
|
136
|
+
self.current_status = StatusAndMessage(Status.BUSY)
|
|
137
|
+
self.command_queue.put(
|
|
138
|
+
Command(
|
|
139
|
+
action=Actions.START,
|
|
140
|
+
devices=devices,
|
|
141
|
+
experiment=experiment,
|
|
142
|
+
parameters=parameters,
|
|
143
|
+
callbacks=callbacks,
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
return StatusAndMessage(Status.SUCCESS)
|
|
147
|
+
|
|
148
|
+
def stopping_thread(self):
|
|
149
|
+
try:
|
|
150
|
+
self.RE.abort()
|
|
151
|
+
self.current_status = StatusAndMessage(Status.IDLE)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
self.current_status = make_error_status_and_message(e)
|
|
154
|
+
|
|
155
|
+
def stop(self) -> StatusAndMessage:
|
|
156
|
+
if self.current_status.status == Status.IDLE.value:
|
|
157
|
+
return StatusAndMessage(Status.FAILED, "Bluesky not running")
|
|
158
|
+
elif self.current_status.status == Status.ABORTING.value:
|
|
159
|
+
return StatusAndMessage(Status.FAILED, "Bluesky already stopping")
|
|
160
|
+
else:
|
|
161
|
+
self.current_status = StatusAndMessage(Status.ABORTING)
|
|
162
|
+
stopping_thread = threading.Thread(target=self.stopping_thread)
|
|
163
|
+
stopping_thread.start()
|
|
164
|
+
self.last_run_aborted = True
|
|
165
|
+
return StatusAndMessage(Status.ABORTING)
|
|
166
|
+
|
|
167
|
+
def shutdown(self):
|
|
168
|
+
"""Stops the run engine and the loop waiting for messages."""
|
|
169
|
+
print("Shutting down: Stopping the run engine gracefully")
|
|
170
|
+
self.stop()
|
|
171
|
+
self.command_queue.put(Command(action=Actions.SHUTDOWN))
|
|
172
|
+
|
|
173
|
+
def wait_on_queue(self):
|
|
174
|
+
while True:
|
|
175
|
+
command = self.command_queue.get()
|
|
176
|
+
if command.action == Actions.SHUTDOWN:
|
|
177
|
+
return
|
|
178
|
+
elif command.action == Actions.START:
|
|
179
|
+
if command.experiment is None:
|
|
180
|
+
raise ValueError("No experiment provided for START")
|
|
181
|
+
try:
|
|
182
|
+
if (
|
|
183
|
+
not self.use_external_callbacks
|
|
184
|
+
and command.callbacks
|
|
185
|
+
and (cbs := command.callbacks())
|
|
186
|
+
):
|
|
187
|
+
LOGGER.info(
|
|
188
|
+
f"Using callbacks for this plan: {not self.use_external_callbacks} - {cbs}"
|
|
189
|
+
)
|
|
190
|
+
self.subscribed_per_plan_callbacks += [
|
|
191
|
+
self.RE.subscribe(cb) for cb in cbs
|
|
192
|
+
]
|
|
193
|
+
with TRACER.start_span("do_run"):
|
|
194
|
+
self.RE(command.experiment(command.devices, command.parameters))
|
|
195
|
+
|
|
196
|
+
self.current_status = StatusAndMessage(
|
|
197
|
+
Status.IDLE,
|
|
198
|
+
self.aperture_change_callback.last_selected_aperture,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
self.last_run_aborted = False
|
|
202
|
+
except WarningException as exception:
|
|
203
|
+
LOGGER.warning("Warning Exception", exc_info=True)
|
|
204
|
+
self.current_status = make_error_status_and_message(exception)
|
|
205
|
+
except Exception as exception:
|
|
206
|
+
LOGGER.error("Exception on running plan", exc_info=True)
|
|
207
|
+
|
|
208
|
+
if self.last_run_aborted:
|
|
209
|
+
# Aborting will cause an exception here that we want to swallow
|
|
210
|
+
self.last_run_aborted = False
|
|
211
|
+
else:
|
|
212
|
+
self.current_status = make_error_status_and_message(exception)
|
|
213
|
+
finally:
|
|
214
|
+
[
|
|
215
|
+
self.RE.unsubscribe(cb)
|
|
216
|
+
for cb in self.subscribed_per_plan_callbacks
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def compose_start_args(context: BlueskyContext, plan_name: str, action: Actions):
|
|
221
|
+
experiment_registry_entry = PLAN_REGISTRY.get(plan_name)
|
|
222
|
+
if experiment_registry_entry is None:
|
|
223
|
+
raise PlanNotFound(f"Experiment plan '{plan_name}' not found in registry.")
|
|
224
|
+
|
|
225
|
+
experiment_internal_param_type = experiment_registry_entry.get("param_type")
|
|
226
|
+
callback_type = experiment_registry_entry.get("callback_collection_type")
|
|
227
|
+
plan = context.plan_functions.get(plan_name)
|
|
228
|
+
if experiment_internal_param_type is None:
|
|
229
|
+
raise PlanNotFound(
|
|
230
|
+
f"Corresponding internal param type for '{plan_name}' not found in registry."
|
|
231
|
+
)
|
|
232
|
+
if plan is None:
|
|
233
|
+
raise PlanNotFound(
|
|
234
|
+
f"Experiment plan '{plan_name}' not found in context. Context has {context.plan_functions.keys()}"
|
|
235
|
+
)
|
|
236
|
+
try:
|
|
237
|
+
parameters = experiment_internal_param_type(**json.loads(request.data))
|
|
238
|
+
if parameters.model_extra:
|
|
239
|
+
raise ValueError(f"Extra fields not allowed {parameters.model_extra}")
|
|
240
|
+
except Exception as e:
|
|
241
|
+
raise ValueError(
|
|
242
|
+
f"Supplied parameters don't match the plan for this endpoint {request.data}"
|
|
243
|
+
) from e
|
|
244
|
+
return plan, parameters, plan_name, callback_type
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class RunExperiment(Resource):
|
|
248
|
+
def __init__(self, runner: BlueskyRunner, context: BlueskyContext) -> None:
|
|
249
|
+
super().__init__()
|
|
250
|
+
self.runner = runner
|
|
251
|
+
self.context = context
|
|
252
|
+
|
|
253
|
+
def put(self, plan_name: str, action: Actions):
|
|
254
|
+
status_and_message = StatusAndMessage(Status.FAILED, f"{action} not understood")
|
|
255
|
+
if action == Actions.START.value:
|
|
256
|
+
try:
|
|
257
|
+
plan, params, plan_name, callback_type = compose_start_args(
|
|
258
|
+
self.context, plan_name, action
|
|
259
|
+
)
|
|
260
|
+
status_and_message = self.runner.start(
|
|
261
|
+
plan, params, plan_name, callback_type
|
|
262
|
+
)
|
|
263
|
+
except Exception as e:
|
|
264
|
+
status_and_message = make_error_status_and_message(e)
|
|
265
|
+
LOGGER.error(format_exception(e))
|
|
266
|
+
|
|
267
|
+
elif action == Actions.STOP.value:
|
|
268
|
+
status_and_message = self.runner.stop()
|
|
269
|
+
# no idea why mypy gives an attribute error here but nowhere else for this
|
|
270
|
+
# exact same situation...
|
|
271
|
+
return asdict(status_and_message) # type: ignore
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class StopOrStatus(Resource):
|
|
275
|
+
def __init__(self, runner: BlueskyRunner) -> None:
|
|
276
|
+
super().__init__()
|
|
277
|
+
self.runner: BlueskyRunner = runner
|
|
278
|
+
|
|
279
|
+
def put(self, action):
|
|
280
|
+
status_and_message = StatusAndMessage(Status.FAILED, f"{action} not understood")
|
|
281
|
+
if action == Actions.STOP.value:
|
|
282
|
+
status_and_message = self.runner.stop()
|
|
283
|
+
return asdict(status_and_message)
|
|
284
|
+
|
|
285
|
+
def get(self, **kwargs):
|
|
286
|
+
action = kwargs.get("action")
|
|
287
|
+
status_and_message = StatusAndMessage(Status.FAILED, f"{action} not understood")
|
|
288
|
+
if action == Actions.STATUS.value:
|
|
289
|
+
LOGGER.debug(
|
|
290
|
+
f"Runner received status request - state of the runner object is: {self.runner.__dict__} - state of the RE is: {self.runner.RE.__dict__}"
|
|
291
|
+
)
|
|
292
|
+
status_and_message = self.runner.current_status
|
|
293
|
+
return asdict(status_and_message)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class FlushLogs(Resource):
|
|
297
|
+
def put(self, **kwargs):
|
|
298
|
+
try:
|
|
299
|
+
status_and_message = StatusAndMessage(
|
|
300
|
+
Status.SUCCESS, f"Flushed debug log to {flush_debug_handler()}"
|
|
301
|
+
)
|
|
302
|
+
except Exception as e:
|
|
303
|
+
status_and_message = StatusAndMessage(
|
|
304
|
+
Status.FAILED, f"Failed to flush debug log: {e}"
|
|
305
|
+
)
|
|
306
|
+
return asdict(status_and_message)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def create_app(
|
|
310
|
+
test_config=None,
|
|
311
|
+
RE: RunEngine = RunEngine({}),
|
|
312
|
+
skip_startup_connection: bool = False,
|
|
313
|
+
use_external_callbacks: bool = False,
|
|
314
|
+
) -> tuple[Flask, BlueskyRunner]:
|
|
315
|
+
context = setup_context(
|
|
316
|
+
wait_for_connection=not skip_startup_connection,
|
|
317
|
+
)
|
|
318
|
+
runner = BlueskyRunner(
|
|
319
|
+
RE,
|
|
320
|
+
context=context,
|
|
321
|
+
use_external_callbacks=use_external_callbacks,
|
|
322
|
+
skip_startup_connection=skip_startup_connection,
|
|
323
|
+
)
|
|
324
|
+
app = Flask(__name__)
|
|
325
|
+
if test_config:
|
|
326
|
+
app.config.update(test_config)
|
|
327
|
+
api = Api(app)
|
|
328
|
+
api.add_resource(
|
|
329
|
+
RunExperiment,
|
|
330
|
+
"/<string:plan_name>/<string:action>",
|
|
331
|
+
resource_class_args=[runner, context],
|
|
332
|
+
)
|
|
333
|
+
api.add_resource(
|
|
334
|
+
FlushLogs,
|
|
335
|
+
"/flush_debug_log",
|
|
336
|
+
)
|
|
337
|
+
api.add_resource(
|
|
338
|
+
StopOrStatus,
|
|
339
|
+
"/<string:action>",
|
|
340
|
+
resource_class_args=[runner],
|
|
341
|
+
)
|
|
342
|
+
return app, runner
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def create_targets():
|
|
346
|
+
hyperion_port = 5005
|
|
347
|
+
args = parse_cli_args()
|
|
348
|
+
do_default_logging_setup(dev_mode=args.dev_mode)
|
|
349
|
+
if not args.use_external_callbacks:
|
|
350
|
+
setup_callback_logging(args.dev_mode)
|
|
351
|
+
app, runner = create_app(
|
|
352
|
+
skip_startup_connection=args.skip_startup_connection,
|
|
353
|
+
use_external_callbacks=args.use_external_callbacks,
|
|
354
|
+
)
|
|
355
|
+
return app, runner, hyperion_port, args.dev_mode
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def main():
|
|
359
|
+
app, runner, port, dev_mode = create_targets()
|
|
360
|
+
atexit.register(runner.shutdown)
|
|
361
|
+
flask_thread = threading.Thread(
|
|
362
|
+
target=lambda: app.run(
|
|
363
|
+
host="0.0.0.0", port=port, debug=True, use_reloader=False
|
|
364
|
+
),
|
|
365
|
+
daemon=True,
|
|
366
|
+
)
|
|
367
|
+
flask_thread.start()
|
|
368
|
+
LOGGER.info(f"Hyperion now listening on {port} ({'IN DEV' if dev_mode else ''})")
|
|
369
|
+
runner.wait_on_queue()
|
|
370
|
+
flask_thread.join()
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
if __name__ == "__main__":
|
|
374
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import bluesky.plan_stubs as bps
|
|
4
|
+
from dodal.devices.focusing_mirror import (
|
|
5
|
+
FocusingMirrorWithStripes,
|
|
6
|
+
MirrorStripe,
|
|
7
|
+
VFMMirrorVoltages,
|
|
8
|
+
)
|
|
9
|
+
from dodal.devices.undulator_dcm import UndulatorDCM
|
|
10
|
+
from dodal.devices.util.adjuster_plans import lookup_table_adjuster
|
|
11
|
+
from dodal.devices.util.lookup_tables import (
|
|
12
|
+
linear_interpolation_lut,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
16
|
+
|
|
17
|
+
MIRROR_VOLTAGE_GROUP = "MIRROR_VOLTAGE_GROUP"
|
|
18
|
+
DCM_GROUP = "DCM_GROUP"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _apply_and_wait_for_voltages_to_settle(
|
|
22
|
+
stripe: MirrorStripe,
|
|
23
|
+
mirror: FocusingMirrorWithStripes,
|
|
24
|
+
mirror_voltages: VFMMirrorVoltages,
|
|
25
|
+
):
|
|
26
|
+
with open(mirror_voltages.voltage_lookup_table_path) as lut_file:
|
|
27
|
+
json_obj = json.load(lut_file)
|
|
28
|
+
|
|
29
|
+
# sample mode is the only mode supported
|
|
30
|
+
sample_data = json_obj["sample"]
|
|
31
|
+
mirror_key = mirror.name.lower()
|
|
32
|
+
if stripe == MirrorStripe.BARE:
|
|
33
|
+
stripe_key = "bare"
|
|
34
|
+
elif stripe == MirrorStripe.RHODIUM:
|
|
35
|
+
stripe_key = "rh"
|
|
36
|
+
elif stripe == MirrorStripe.PLATINUM:
|
|
37
|
+
stripe_key = "pt"
|
|
38
|
+
else:
|
|
39
|
+
raise ValueError(f"Unsupported stripe '{stripe}'")
|
|
40
|
+
|
|
41
|
+
required_voltages = sample_data[stripe_key][mirror_key]
|
|
42
|
+
for voltage_channel, required_voltage in zip(
|
|
43
|
+
mirror_voltages.voltage_channels.values(), required_voltages, strict=False
|
|
44
|
+
):
|
|
45
|
+
LOGGER.debug(
|
|
46
|
+
f"Applying and waiting for voltage {voltage_channel.name} = {required_voltage}"
|
|
47
|
+
)
|
|
48
|
+
yield from bps.abs_set(
|
|
49
|
+
voltage_channel, required_voltage, group=MIRROR_VOLTAGE_GROUP
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
yield from bps.wait(group=MIRROR_VOLTAGE_GROUP)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def adjust_mirror_stripe(
|
|
56
|
+
energy_kev, mirror: FocusingMirrorWithStripes, mirror_voltages: VFMMirrorVoltages
|
|
57
|
+
):
|
|
58
|
+
"""Feedback should be OFF prior to entry, in order to prevent
|
|
59
|
+
feedback from making unnecessary corrections while beam is being adjusted."""
|
|
60
|
+
stripe = mirror.energy_to_stripe(energy_kev)
|
|
61
|
+
|
|
62
|
+
LOGGER.info(
|
|
63
|
+
f"Adjusting mirror stripe for {energy_kev}keV selecting {stripe} stripe"
|
|
64
|
+
)
|
|
65
|
+
yield from bps.abs_set(mirror.stripe, stripe, wait=True)
|
|
66
|
+
yield from bps.trigger(mirror.apply_stripe)
|
|
67
|
+
|
|
68
|
+
LOGGER.info("Adjusting mirror voltages...")
|
|
69
|
+
yield from _apply_and_wait_for_voltages_to_settle(stripe, mirror, mirror_voltages)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def adjust_dcm_pitch_roll_vfm_from_lut(
|
|
73
|
+
undulator_dcm: UndulatorDCM,
|
|
74
|
+
vfm: FocusingMirrorWithStripes,
|
|
75
|
+
vfm_mirror_voltages: VFMMirrorVoltages,
|
|
76
|
+
energy_kev,
|
|
77
|
+
):
|
|
78
|
+
"""Beamline energy-change post-adjustments : Adjust DCM and VFM directly from lookup tables.
|
|
79
|
+
Lookups are performed against the Bragg angle which will have been automatically set by EPICS as a side-effect of the
|
|
80
|
+
energy change prior to calling this function.
|
|
81
|
+
Feedback should be OFF prior to entry, in order to prevent
|
|
82
|
+
feedback from making unnecessary corrections while beam is being adjusted."""
|
|
83
|
+
|
|
84
|
+
# DCM Pitch
|
|
85
|
+
dcm = undulator_dcm.dcm
|
|
86
|
+
LOGGER.info(f"Adjusting DCM and VFM for {energy_kev} keV")
|
|
87
|
+
bragg_deg = yield from bps.rd(dcm.bragg_in_degrees.user_readback)
|
|
88
|
+
LOGGER.info(f"Read Bragg angle = {bragg_deg} degrees")
|
|
89
|
+
dcm_pitch_adjuster = lookup_table_adjuster(
|
|
90
|
+
linear_interpolation_lut(undulator_dcm.pitch_energy_table_path),
|
|
91
|
+
dcm.pitch_in_mrad,
|
|
92
|
+
bragg_deg,
|
|
93
|
+
)
|
|
94
|
+
yield from dcm_pitch_adjuster(DCM_GROUP)
|
|
95
|
+
# It's possible we can remove these waits but we need to check
|
|
96
|
+
LOGGER.info("Waiting for DCM pitch adjust to complete...")
|
|
97
|
+
|
|
98
|
+
# DCM Roll
|
|
99
|
+
dcm_roll_adjuster = lookup_table_adjuster(
|
|
100
|
+
linear_interpolation_lut(undulator_dcm.roll_energy_table_path),
|
|
101
|
+
dcm.roll_in_mrad,
|
|
102
|
+
bragg_deg,
|
|
103
|
+
)
|
|
104
|
+
yield from dcm_roll_adjuster(DCM_GROUP)
|
|
105
|
+
LOGGER.info("Waiting for DCM roll adjust to complete...")
|
|
106
|
+
|
|
107
|
+
# DCM Perp pitch
|
|
108
|
+
offset_mm = undulator_dcm.dcm_fixed_offset_mm
|
|
109
|
+
LOGGER.info(f"Adjusting DCM offset to {offset_mm} mm")
|
|
110
|
+
yield from bps.abs_set(dcm.offset_in_mm, offset_mm, group=DCM_GROUP)
|
|
111
|
+
|
|
112
|
+
#
|
|
113
|
+
# Adjust mirrors
|
|
114
|
+
#
|
|
115
|
+
|
|
116
|
+
# No need to change HFM
|
|
117
|
+
|
|
118
|
+
# Assumption is focus mode is already set to "sample"
|
|
119
|
+
# not sure how we check this
|
|
120
|
+
|
|
121
|
+
# VFM Stripe selection
|
|
122
|
+
yield from adjust_mirror_stripe(energy_kev, vfm, vfm_mirror_voltages)
|
|
123
|
+
yield from bps.wait(DCM_GROUP)
|
|
124
|
+
|
|
125
|
+
# VFM Adjust - for I03 this table always returns the same value
|
|
126
|
+
vfm_lut = vfm.bragg_to_lat_lookup_table_path
|
|
127
|
+
assert vfm_lut is not None
|
|
128
|
+
vfm_x_adjuster = lookup_table_adjuster(
|
|
129
|
+
linear_interpolation_lut(vfm_lut),
|
|
130
|
+
vfm.x_mm,
|
|
131
|
+
bragg_deg,
|
|
132
|
+
)
|
|
133
|
+
LOGGER.info("Waiting for VFM Lat (Horizontal Translation) to complete...")
|
|
134
|
+
yield from vfm_x_adjuster()
|