mx-bluesky 0.0.2__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/__main__.py +1 -2
- mx_bluesky/_version.py +14 -2
- mx_bluesky/beamlines/i04/__init__.py +3 -0
- mx_bluesky/beamlines/i04/callbacks/murko_callback.py +45 -0
- mx_bluesky/beamlines/i04/thawing_plan.py +85 -0
- mx_bluesky/beamlines/i24/serial/__init__.py +49 -0
- mx_bluesky/beamlines/i24/serial/blueapi_config.yaml +12 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/dcid.py +53 -41
- mx_bluesky/{I24 → beamlines/i24}/serial/extruder/EX-gui-edm/DetStage.edl +3 -4
- mx_bluesky/{I24 → beamlines/i24}/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +28 -32
- mx_bluesky/{I24 → beamlines/i24}/serial/extruder/EX-gui-edm/microdrop_alignment.edl +0 -1
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +516 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/CustomChip_py3v1.edl +3 -4
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DetStage.edl +3 -4
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +273 -223
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/ME14E-GeneralPurpose.edl +0 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +12 -13
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/PMAC_Command.edl +0 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/Shutter_Control.edl +0 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/microdrop_alignment.edl +0 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/nudgechip.edl +0 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +273 -143
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/short1-laser.png +0 -0
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/short2-laser.png +0 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/ft_utils.py +24 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +808 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +377 -416
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +34 -40
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +328 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/i24ssx_moveonclick.py +66 -48
- mx_bluesky/{I24 → beamlines/i24}/serial/log.py +66 -19
- mx_bluesky/beamlines/i24/serial/parameters/__init__.py +15 -0
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +47 -0
- mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +103 -0
- mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +9 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/parameters/fixed_target/cs/motor_direction.txt +1 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/minichip-oxford.pvar +1 -1
- mx_bluesky/beamlines/i24/serial/parameters/utils.py +42 -0
- mx_bluesky/beamlines/i24/serial/run_extruder.sh +19 -0
- mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +22 -0
- mx_bluesky/beamlines/i24/serial/run_serial.py +36 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/set_visit_directory.sh +6 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/setup_beamline/pv.py +1 -62
- mx_bluesky/{I24 → beamlines/i24}/serial/setup_beamline/pv_abstract.py +6 -7
- mx_bluesky/{I24 → beamlines/i24}/serial/setup_beamline/setup_beamline.py +90 -269
- mx_bluesky/{I24 → beamlines/i24}/serial/setup_beamline/setup_detector.py +47 -40
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +459 -0
- mx_bluesky/beamlines/i24/serial/start_blueapi.sh +28 -0
- mx_bluesky/beamlines/i24/serial/write_nexus.py +105 -0
- mx_bluesky/example.py +4 -4
- 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/hyperion/parameters/components.py +253 -0
- 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.0.2.dist-info → mx_bluesky-1.1.0.dist-info}/METADATA +53 -32
- mx_bluesky-1.1.0.dist-info/RECORD +136 -0
- {mx_bluesky-0.0.2.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/I24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +0 -476
- mx_bluesky/I24/serial/fixed_target/FT-gui-edm/ME14E-motors.edl +0 -1874
- mx_bluesky/I24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +0 -706
- mx_bluesky/I24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +0 -463
- mx_bluesky/I24/serial/parameters/__init__.py +0 -5
- mx_bluesky/I24/serial/parameters/constants.py +0 -39
- mx_bluesky/I24/serial/parameters/fixed_target/cs/cs_maker.json +0 -9
- mx_bluesky/I24/serial/parameters/fixed_target/cs/fiducial_1.txt +0 -4
- mx_bluesky/I24/serial/parameters/fixed_target/cs/fiducial_2.txt +0 -4
- mx_bluesky/I24/serial/parameters/fixed_target/litemaps/currentchip.map +0 -81
- mx_bluesky/I24/serial/parameters/fixed_target/parameters.txt +0 -13
- mx_bluesky/I24/serial/run_serial.py +0 -52
- mx_bluesky/I24/serial/write_nexus.py +0 -113
- mx_bluesky-0.0.2.dist-info/RECORD +0 -58
- mx_bluesky-0.0.2.dist-info/entry_points.txt +0 -4
- /mx_bluesky/{I24 → beamlines}/__init__.py +0 -0
- /mx_bluesky/{I24/serial → beamlines/i24}/__init__.py +0 -0
- /mx_bluesky/{I24 → beamlines/i24}/serial/extruder/__init__.py +0 -0
- /mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/__init__.py +0 -0
- /mx_bluesky/{I24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/oxford.pvar +0 -0
- /mx_bluesky/{I24 → beamlines/i24}/serial/run_ssx.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-0.0.2.dist-info → mx_bluesky-1.1.0.dist-info}/LICENSE +0 -0
- {mx_bluesky-0.0.2.dist-info → mx_bluesky-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -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()
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import bluesky.plan_stubs as bps
|
|
4
|
+
from dodal.devices.aperturescatterguard import (
|
|
5
|
+
ApertureScatterguard,
|
|
6
|
+
ApertureValue,
|
|
7
|
+
)
|
|
8
|
+
from dodal.devices.backlight import Backlight, BacklightPosition
|
|
9
|
+
from dodal.devices.detector.detector_motion import DetectorMotion
|
|
10
|
+
from dodal.devices.smargon import Smargon
|
|
11
|
+
|
|
12
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
13
|
+
|
|
14
|
+
LOWER_DETECTOR_SHUTTER_AFTER_SCAN = True
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def setup_sample_environment(
|
|
18
|
+
aperture_scatterguard: ApertureScatterguard,
|
|
19
|
+
aperture_position_gda_name: str | None,
|
|
20
|
+
backlight: Backlight,
|
|
21
|
+
group="setup_senv",
|
|
22
|
+
):
|
|
23
|
+
"""Move the aperture into required position, move out the backlight."""
|
|
24
|
+
aperture_value = (
|
|
25
|
+
None
|
|
26
|
+
if not aperture_position_gda_name
|
|
27
|
+
else ApertureValue(aperture_position_gda_name)
|
|
28
|
+
)
|
|
29
|
+
yield from move_aperture_if_required(
|
|
30
|
+
aperture_scatterguard, aperture_value, group=group
|
|
31
|
+
)
|
|
32
|
+
yield from bps.abs_set(backlight, BacklightPosition.OUT, group=group)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def move_aperture_if_required(
|
|
36
|
+
aperture_scatterguard: ApertureScatterguard,
|
|
37
|
+
aperture_value: ApertureValue | None,
|
|
38
|
+
group="move_aperture",
|
|
39
|
+
):
|
|
40
|
+
if not aperture_value:
|
|
41
|
+
previous_aperture_position = yield from bps.rd(aperture_scatterguard)
|
|
42
|
+
assert isinstance(previous_aperture_position, ApertureValue)
|
|
43
|
+
LOGGER.info(
|
|
44
|
+
f"Using previously set aperture position {previous_aperture_position}"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
else:
|
|
48
|
+
LOGGER.info(f"Setting aperture position to {aperture_value}")
|
|
49
|
+
yield from bps.abs_set(
|
|
50
|
+
aperture_scatterguard,
|
|
51
|
+
aperture_value,
|
|
52
|
+
group=group,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def cleanup_sample_environment(
|
|
57
|
+
detector_motion: DetectorMotion,
|
|
58
|
+
group="cleanup_senv",
|
|
59
|
+
):
|
|
60
|
+
"""Put the detector shutter back down"""
|
|
61
|
+
|
|
62
|
+
yield from bps.abs_set(
|
|
63
|
+
detector_motion.shutter,
|
|
64
|
+
int(not LOWER_DETECTOR_SHUTTER_AFTER_SCAN),
|
|
65
|
+
group=group,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def move_x_y_z(
|
|
70
|
+
smargon: Smargon,
|
|
71
|
+
x_mm: float | None = None,
|
|
72
|
+
y_mm: float | None = None,
|
|
73
|
+
z_mm: float | None = None,
|
|
74
|
+
wait=False,
|
|
75
|
+
group="move_x_y_z",
|
|
76
|
+
):
|
|
77
|
+
"""Move the x, y, and z axes of the given smargon to the specified position. All
|
|
78
|
+
axes are optional."""
|
|
79
|
+
|
|
80
|
+
LOGGER.info(f"Moving smargon to x, y, z: {(x_mm, y_mm, z_mm)}")
|
|
81
|
+
if x_mm:
|
|
82
|
+
yield from bps.abs_set(smargon.x, x_mm, group=group)
|
|
83
|
+
if y_mm:
|
|
84
|
+
yield from bps.abs_set(smargon.y, y_mm, group=group)
|
|
85
|
+
if z_mm:
|
|
86
|
+
yield from bps.abs_set(smargon.z, z_mm, group=group)
|
|
87
|
+
if wait:
|
|
88
|
+
yield from bps.wait(group)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def move_phi_chi_omega(
|
|
92
|
+
smargon: Smargon,
|
|
93
|
+
phi: float | None = None,
|
|
94
|
+
chi: float | None = None,
|
|
95
|
+
omega: float | None = None,
|
|
96
|
+
wait=False,
|
|
97
|
+
group="move_phi_chi_omega",
|
|
98
|
+
):
|
|
99
|
+
"""Move the x, y, and z axes of the given smargon to the specified position. All
|
|
100
|
+
axes are optional."""
|
|
101
|
+
|
|
102
|
+
LOGGER.info(f"Moving smargon to phi, chi, omega: {(phi, chi, omega)}")
|
|
103
|
+
if phi:
|
|
104
|
+
yield from bps.abs_set(smargon.phi, phi, group=group)
|
|
105
|
+
if chi:
|
|
106
|
+
yield from bps.abs_set(smargon.chi, chi, group=group)
|
|
107
|
+
if omega:
|
|
108
|
+
yield from bps.abs_set(smargon.omega, omega, group=group)
|
|
109
|
+
if wait:
|
|
110
|
+
yield from bps.wait(group)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from bluesky import plan_stubs as bps
|
|
2
|
+
from dodal.devices.detector.detector_motion import DetectorMotion, ShutterState
|
|
3
|
+
|
|
4
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def set_detector_z_position(
|
|
8
|
+
detector_motion: DetectorMotion, detector_position: float, group=None
|
|
9
|
+
):
|
|
10
|
+
LOGGER.info(f"Moving detector to {detector_position} ({group})")
|
|
11
|
+
yield from bps.abs_set(detector_motion.z, detector_position, group=group)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def set_shutter(detector_motion: DetectorMotion, state: ShutterState, group=None):
|
|
15
|
+
LOGGER.info(f"Setting shutter to {state} ({group})")
|
|
16
|
+
yield from bps.abs_set(detector_motion.shutter, state, group=group)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import bluesky.plan_stubs as bps
|
|
4
|
+
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
5
|
+
from dodal.devices.attenuator import Attenuator
|
|
6
|
+
from dodal.devices.dcm import DCM
|
|
7
|
+
from dodal.devices.eiger import EigerDetector
|
|
8
|
+
from dodal.devices.flux import Flux
|
|
9
|
+
from dodal.devices.robot import BartRobot
|
|
10
|
+
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
11
|
+
from dodal.devices.smargon import Smargon
|
|
12
|
+
from dodal.devices.synchrotron import Synchrotron
|
|
13
|
+
from dodal.devices.undulator import Undulator
|
|
14
|
+
|
|
15
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
16
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def read_hardware_pre_collection(
|
|
20
|
+
undulator: Undulator,
|
|
21
|
+
synchrotron: Synchrotron,
|
|
22
|
+
s4_slit_gaps: S4SlitGaps,
|
|
23
|
+
robot: BartRobot,
|
|
24
|
+
smargon: Smargon,
|
|
25
|
+
):
|
|
26
|
+
LOGGER.info("Reading status of beamline for callbacks, pre collection.")
|
|
27
|
+
yield from bps.create(
|
|
28
|
+
name=CONST.DESCRIPTORS.HARDWARE_READ_PRE
|
|
29
|
+
) # gives name to event *descriptor* document
|
|
30
|
+
yield from bps.read(undulator.current_gap)
|
|
31
|
+
yield from bps.read(synchrotron.synchrotron_mode)
|
|
32
|
+
yield from bps.read(s4_slit_gaps.xgap)
|
|
33
|
+
yield from bps.read(s4_slit_gaps.ygap)
|
|
34
|
+
yield from bps.read(smargon.x)
|
|
35
|
+
yield from bps.read(smargon.y)
|
|
36
|
+
yield from bps.read(smargon.z)
|
|
37
|
+
yield from bps.save()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def read_hardware_during_collection(
|
|
41
|
+
aperture_scatterguard: ApertureScatterguard,
|
|
42
|
+
attenuator: Attenuator,
|
|
43
|
+
flux: Flux,
|
|
44
|
+
dcm: DCM,
|
|
45
|
+
detector: EigerDetector,
|
|
46
|
+
):
|
|
47
|
+
LOGGER.info("Reading status of beamline for callbacks, during collection.")
|
|
48
|
+
yield from bps.create(name=CONST.DESCRIPTORS.HARDWARE_READ_DURING)
|
|
49
|
+
yield from bps.read(aperture_scatterguard)
|
|
50
|
+
yield from bps.read(attenuator.actual_transmission)
|
|
51
|
+
yield from bps.read(flux.flux_reading)
|
|
52
|
+
yield from bps.read(dcm.energy_in_kev)
|
|
53
|
+
yield from bps.read(detector.bit_depth)
|
|
54
|
+
yield from bps.save()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def read_hardware_for_zocalo(detector: EigerDetector):
|
|
58
|
+
yield from bps.create(name=CONST.DESCRIPTORS.ZOCALO_HW_READ)
|
|
59
|
+
yield from bps.read(detector.odin.file_writer.id)
|
|
60
|
+
yield from bps.save()
|