mx-bluesky 1.4.1a0__py3-none-any.whl → 1.4.3__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/redis_to_murko_forwarder.py +178 -0
- mx_bluesky/beamlines/i24/serial/__init__.py +0 -6
- mx_bluesky/beamlines/i24/serial/dcid.py +125 -151
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +1 -1
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +88 -43
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +1 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +2 -46
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +85 -122
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +58 -66
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +1 -19
- mx_bluesky/beamlines/i24/serial/parameters/__init__.py +11 -2
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +16 -2
- mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +94 -19
- mx_bluesky/beamlines/i24/serial/parameters/utils.py +19 -0
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +61 -8
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +81 -40
- mx_bluesky/beamlines/i24/serial/write_nexus.py +66 -67
- mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/aperture_change_callback.py +1 -1
- mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/grid_detection_callback.py +19 -1
- mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/ispyb_callback_base.py +40 -34
- mx_bluesky/{hyperion → common}/external_interaction/callbacks/common/ispyb_mapping.py +4 -4
- mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/logging_callback.py +1 -1
- mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/zocalo_callback.py +14 -9
- mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/ispyb_callback.py +46 -38
- mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/ispyb_mapping.py +2 -2
- mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/nexus_callback.py +20 -15
- mx_bluesky/common/external_interaction/config_server.py +11 -0
- mx_bluesky/common/external_interaction/ispyb/__init__.py +0 -0
- mx_bluesky/{hyperion → common}/external_interaction/ispyb/data_model.py +2 -0
- mx_bluesky/{hyperion → common}/external_interaction/ispyb/exp_eye_store.py +67 -17
- mx_bluesky/{hyperion → common}/external_interaction/ispyb/ispyb_store.py +20 -18
- mx_bluesky/{hyperion → common}/external_interaction/ispyb/ispyb_utils.py +2 -2
- mx_bluesky/common/external_interaction/nexus/__init__.py +0 -0
- mx_bluesky/{hyperion → common}/external_interaction/nexus/nexus_utils.py +21 -6
- mx_bluesky/{hyperion → common}/external_interaction/nexus/write_nexus.py +5 -5
- mx_bluesky/common/external_interaction/test_config_server.py +38 -0
- mx_bluesky/common/parameters/components.py +10 -8
- mx_bluesky/common/parameters/constants.py +6 -0
- mx_bluesky/common/parameters/gridscan.py +102 -53
- mx_bluesky/common/plans/do_fgs.py +4 -4
- mx_bluesky/{hyperion → common/utils}/exceptions.py +27 -1
- mx_bluesky/common/utils/log.py +17 -7
- mx_bluesky/hyperion/__main__.py +15 -14
- mx_bluesky/hyperion/device_setup_plans/check_beamstop.py +27 -0
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +34 -37
- mx_bluesky/hyperion/device_setup_plans/manipulate_sample.py +7 -7
- mx_bluesky/hyperion/device_setup_plans/position_detector.py +1 -1
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +3 -3
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +21 -4
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +62 -36
- mx_bluesky/hyperion/device_setup_plans/smargon.py +3 -3
- mx_bluesky/hyperion/device_setup_plans/utils.py +4 -0
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +8 -8
- mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +28 -17
- mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +10 -1
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +54 -58
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +22 -31
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +57 -40
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +3 -3
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +8 -2
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +6 -14
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +12 -11
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +4 -4
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +39 -30
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +36 -18
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +33 -21
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +10 -9
- mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +31 -20
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +46 -30
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +39 -24
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +25 -24
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +13 -9
- mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +50 -0
- mx_bluesky/hyperion/external_interaction/config_server.py +15 -1
- mx_bluesky/hyperion/parameters/components.py +3 -2
- mx_bluesky/hyperion/parameters/constants.py +1 -0
- mx_bluesky/hyperion/parameters/gridscan.py +56 -89
- mx_bluesky/hyperion/parameters/load_centre_collect.py +51 -6
- mx_bluesky/hyperion/parameters/robot_load.py +40 -0
- mx_bluesky/hyperion/parameters/rotation.py +28 -3
- mx_bluesky/hyperion/utils/context.py +1 -1
- mx_bluesky/hyperion/utils/validation.py +5 -3
- {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/METADATA +6 -6
- mx_bluesky-1.4.3.dist-info/RECORD +155 -0
- {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/WHEEL +1 -1
- mx_bluesky/common/parameters/robot_load.py +0 -16
- mx_bluesky/hyperion/external_interaction/exceptions.py +0 -13
- mx_bluesky/hyperion/log.py +0 -15
- mx_bluesky-1.4.1a0.dist-info/RECORD +0 -150
- /mx_bluesky/{hyperion/external_interaction/callbacks/xray_centre → common/external_interaction}/__init__.py +0 -0
- /mx_bluesky/{hyperion/external_interaction/ispyb → common/external_interaction/callbacks/common}/__init__.py +0 -0
- /mx_bluesky/{hyperion → common}/external_interaction/callbacks/common/abstract_event.py +0 -0
- /mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/log_uid_tag_callback.py +0 -0
- /mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/plan_reactive_callback.py +0 -0
- /mx_bluesky/{hyperion/external_interaction/nexus → common/external_interaction/callbacks/xray_centre}/__init__.py +0 -0
- /mx_bluesky/{hyperion → common}/utils/utils.py +0 -0
- {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/top_level.txt +0 -0
mx_bluesky/_version.py
CHANGED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import json
|
|
3
|
+
import pickle
|
|
4
|
+
from typing import TypedDict
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import zmq
|
|
8
|
+
from dodal.beamlines.i04 import MURKO_REDIS_DB, REDIS_HOST, REDIS_PASSWORD
|
|
9
|
+
from numpy.typing import NDArray
|
|
10
|
+
from PIL import Image
|
|
11
|
+
from redis import StrictRedis
|
|
12
|
+
|
|
13
|
+
from mx_bluesky.common.utils.log import LOGGER
|
|
14
|
+
|
|
15
|
+
MURKO_ADDRESS = "tcp://i04-murko-prod.diamond.ac.uk:8008"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MurkoRequest(TypedDict):
|
|
19
|
+
to_predict: NDArray
|
|
20
|
+
model_img_size: tuple[int, int]
|
|
21
|
+
save: bool
|
|
22
|
+
min_size: int
|
|
23
|
+
description: list
|
|
24
|
+
prefix: list[str]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_image_size(image: NDArray) -> tuple[int, int]:
|
|
28
|
+
"""Returns the width and height of a numpy image"""
|
|
29
|
+
return image.shape[1], image.shape[0]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def send_to_murko_and_get_results(request: MurkoRequest) -> dict:
|
|
33
|
+
LOGGER.info(f"Sending {request['prefix']} to murko")
|
|
34
|
+
context = zmq.Context()
|
|
35
|
+
socket = context.socket(zmq.REQ)
|
|
36
|
+
socket.connect(MURKO_ADDRESS)
|
|
37
|
+
socket.send(pickle.dumps(request))
|
|
38
|
+
raw_results = socket.recv()
|
|
39
|
+
results = pickle.loads(raw_results)
|
|
40
|
+
LOGGER.info(f"Got {len(results['descriptions'])} results")
|
|
41
|
+
return results
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def correlate_results_to_uuids(request: MurkoRequest, murko_results: dict) -> list:
|
|
45
|
+
results = []
|
|
46
|
+
uuids = request["prefix"]
|
|
47
|
+
|
|
48
|
+
width, height = get_image_size(request["to_predict"][0])
|
|
49
|
+
|
|
50
|
+
for uuid, prediction in zip(uuids, murko_results["descriptions"], strict=False):
|
|
51
|
+
coords = prediction["most_likely_click"]
|
|
52
|
+
y_coord = coords[0] * height
|
|
53
|
+
x_coord = coords[1] * width
|
|
54
|
+
results.append(
|
|
55
|
+
{"uuid": uuid, "x_pixel_coord": x_coord, "y_pixel_coord": y_coord}
|
|
56
|
+
)
|
|
57
|
+
return results
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class BatchMurkoForwarder:
|
|
61
|
+
def __init__(self, redis_client: StrictRedis, batch_size: int):
|
|
62
|
+
"""
|
|
63
|
+
Holds image data streamed from redis and forwards it to murko when:
|
|
64
|
+
* A set number have been received
|
|
65
|
+
* The shape of the images changes
|
|
66
|
+
* When `flush` is called
|
|
67
|
+
|
|
68
|
+
Once data has been forwarded this will then wait on the results and put them
|
|
69
|
+
back in redis.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
redis_client: The client to send murko results back to redis.
|
|
73
|
+
batch_size: How many results to accumulate until they are flushed to redis.
|
|
74
|
+
"""
|
|
75
|
+
self.redis_client = redis_client
|
|
76
|
+
self.batch_size = batch_size
|
|
77
|
+
self._uuids_and_images: dict[str, NDArray] = {}
|
|
78
|
+
self._last_image_size: tuple[int, int] | None = None
|
|
79
|
+
self._last_sample_id = ""
|
|
80
|
+
|
|
81
|
+
def _handle_batch_of_images(self, sample_id, images, uuids):
|
|
82
|
+
request_arguments: MurkoRequest = {
|
|
83
|
+
"model_img_size": (256, 320),
|
|
84
|
+
"to_predict": np.array(images),
|
|
85
|
+
"save": False,
|
|
86
|
+
"min_size": 64,
|
|
87
|
+
"description": [
|
|
88
|
+
"foreground",
|
|
89
|
+
"crystal",
|
|
90
|
+
"loop_inside",
|
|
91
|
+
"loop",
|
|
92
|
+
["crystal", "loop"],
|
|
93
|
+
["crystal", "loop", "stem"],
|
|
94
|
+
],
|
|
95
|
+
"prefix": uuids,
|
|
96
|
+
}
|
|
97
|
+
predictions = send_to_murko_and_get_results(request_arguments)
|
|
98
|
+
results = correlate_results_to_uuids(request_arguments, predictions)
|
|
99
|
+
self._send_murko_results_to_redis(sample_id, results)
|
|
100
|
+
|
|
101
|
+
def _send_murko_results_to_redis(self, sample_id: str, results: list):
|
|
102
|
+
for result in results:
|
|
103
|
+
self.redis_client.hset(
|
|
104
|
+
f"murko:{sample_id}:results", result["uuid"], json.dumps(result)
|
|
105
|
+
)
|
|
106
|
+
self.redis_client.publish("murko-results", json.dumps(results))
|
|
107
|
+
|
|
108
|
+
def add(self, sample_id: str, uuid: str, image: NDArray):
|
|
109
|
+
"""Add an image to the batch to send to murko."""
|
|
110
|
+
image_size = get_image_size(image)
|
|
111
|
+
self._last_sample_id = sample_id
|
|
112
|
+
if self._last_image_size and self._last_image_size != image_size:
|
|
113
|
+
self.flush()
|
|
114
|
+
self._uuids_and_images[uuid] = image
|
|
115
|
+
self._last_image_size = image_size
|
|
116
|
+
if len(self._uuids_and_images.keys()) >= self.batch_size:
|
|
117
|
+
self.flush()
|
|
118
|
+
|
|
119
|
+
def flush(self):
|
|
120
|
+
"""Flush the batch to murko."""
|
|
121
|
+
if self._uuids_and_images:
|
|
122
|
+
self._handle_batch_of_images(
|
|
123
|
+
self._last_sample_id,
|
|
124
|
+
list(self._uuids_and_images.values()),
|
|
125
|
+
list(self._uuids_and_images.keys()),
|
|
126
|
+
)
|
|
127
|
+
self._uuids_and_images = {}
|
|
128
|
+
self._last_image_size = None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class RedisListener:
|
|
132
|
+
TIMEOUT_S = 2
|
|
133
|
+
|
|
134
|
+
def __init__(
|
|
135
|
+
self,
|
|
136
|
+
redis_host=REDIS_HOST,
|
|
137
|
+
redis_password=REDIS_PASSWORD,
|
|
138
|
+
db=MURKO_REDIS_DB,
|
|
139
|
+
redis_channel="murko",
|
|
140
|
+
):
|
|
141
|
+
self.redis_client = StrictRedis(
|
|
142
|
+
host=redis_host,
|
|
143
|
+
password=redis_password,
|
|
144
|
+
db=db,
|
|
145
|
+
)
|
|
146
|
+
self.pubsub = self.redis_client.pubsub()
|
|
147
|
+
self.channel = redis_channel
|
|
148
|
+
self.forwarder = BatchMurkoForwarder(self.redis_client, 10)
|
|
149
|
+
|
|
150
|
+
def _get_and_handle_message(self):
|
|
151
|
+
message = self.pubsub.get_message(timeout=self.TIMEOUT_S)
|
|
152
|
+
if message and message["type"] == "message":
|
|
153
|
+
data = json.loads(message["data"])
|
|
154
|
+
LOGGER.info(f"Received from redis: {data}")
|
|
155
|
+
uuid = data["uuid"]
|
|
156
|
+
sample_id = data["sample_id"]
|
|
157
|
+
|
|
158
|
+
# Images are put in redis as raw jpeg bytes, murko needs numpy arrays
|
|
159
|
+
raw_image = self.redis_client.hget(f"murko:{sample_id}:raw", uuid)
|
|
160
|
+
assert isinstance(raw_image, bytes)
|
|
161
|
+
image = Image.open(io.BytesIO(raw_image))
|
|
162
|
+
image = np.asarray(image)
|
|
163
|
+
|
|
164
|
+
self.forwarder.add(sample_id, uuid, image)
|
|
165
|
+
|
|
166
|
+
elif not message:
|
|
167
|
+
self.forwarder.flush()
|
|
168
|
+
|
|
169
|
+
def listen_for_image_data_forever(self):
|
|
170
|
+
self.pubsub.subscribe(self.channel)
|
|
171
|
+
|
|
172
|
+
while True:
|
|
173
|
+
self._get_and_handle_message()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
if __name__ == "__main__":
|
|
177
|
+
client = RedisListener()
|
|
178
|
+
client.listen_for_image_data_forever()
|
|
@@ -18,9 +18,6 @@ from .fixed_target.i24ssx_Chip_Manager_py3v1 import (
|
|
|
18
18
|
moveto,
|
|
19
19
|
moveto_preset,
|
|
20
20
|
pumpprobe_calc,
|
|
21
|
-
save_screen_map,
|
|
22
|
-
upload_parameters,
|
|
23
|
-
write_parameter_file,
|
|
24
21
|
)
|
|
25
22
|
from .log import clean_up_log_config_at_end, setup_collection_logs
|
|
26
23
|
from .setup_beamline.setup_detector import setup_detector_stage
|
|
@@ -44,9 +41,6 @@ __all__ = [
|
|
|
44
41
|
"load_lite_map",
|
|
45
42
|
"load_stock_map",
|
|
46
43
|
"pumpprobe_calc",
|
|
47
|
-
"save_screen_map",
|
|
48
|
-
"upload_parameters",
|
|
49
|
-
"write_parameter_file",
|
|
50
44
|
"setup_collection_logs",
|
|
51
45
|
"clean_up_log_config_at_end",
|
|
52
46
|
]
|
|
@@ -2,29 +2,26 @@ import datetime
|
|
|
2
2
|
import json
|
|
3
3
|
import math
|
|
4
4
|
import os
|
|
5
|
-
import re
|
|
6
5
|
import subprocess
|
|
7
|
-
import warnings
|
|
8
6
|
from functools import lru_cache
|
|
9
7
|
|
|
8
|
+
import bluesky.plan_stubs as bps
|
|
10
9
|
import requests
|
|
10
|
+
from dodal.beamlines import i24
|
|
11
|
+
from dodal.devices.i24.beam_center import DetectorBeamCenter
|
|
12
|
+
from dodal.devices.i24.dcm import DCM
|
|
13
|
+
from dodal.devices.i24.focus_mirrors import FocusMirrorsMode
|
|
14
|
+
from dodal.devices.i24.pilatus_metadata import PilatusMetadata
|
|
11
15
|
|
|
16
|
+
from mx_bluesky.beamlines.i24.serial.fixed_target.ft_utils import PumpProbeSetting
|
|
12
17
|
from mx_bluesky.beamlines.i24.serial.log import SSX_LOGGER
|
|
13
|
-
from mx_bluesky.beamlines.i24.serial.parameters import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
caget,
|
|
19
|
-
cagetstring,
|
|
20
|
-
pv,
|
|
18
|
+
from mx_bluesky.beamlines.i24.serial.parameters import (
|
|
19
|
+
BeamSettings,
|
|
20
|
+
DetectorName,
|
|
21
|
+
ExtruderParameters,
|
|
22
|
+
FixedTargetParameters,
|
|
21
23
|
)
|
|
22
|
-
|
|
23
|
-
try:
|
|
24
|
-
from typing import Literal
|
|
25
|
-
except ImportError:
|
|
26
|
-
pass
|
|
27
|
-
|
|
24
|
+
from mx_bluesky.beamlines.i24.serial.setup_beamline import Detector, Eiger, Pilatus
|
|
28
25
|
|
|
29
26
|
# Collection start/end script to kick off analysis
|
|
30
27
|
COLLECTION_START_SCRIPT = "/dls_sw/i24/scripts/RunAtStartOfCollect-i24-ssx.sh"
|
|
@@ -49,19 +46,54 @@ def get_auth_header() -> dict:
|
|
|
49
46
|
return {"Authorization": "Bearer " + token}
|
|
50
47
|
|
|
51
48
|
|
|
52
|
-
|
|
49
|
+
def read_beam_info_from_hardware(
|
|
50
|
+
dcm: DCM,
|
|
51
|
+
mirrors: FocusMirrorsMode,
|
|
52
|
+
beam_center: DetectorBeamCenter,
|
|
53
|
+
detector_name: DetectorName,
|
|
54
|
+
):
|
|
55
|
+
""" Read the beam information from hardware.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
dcm (DCM): The decm device.
|
|
59
|
+
mirrors (FocusMirrorMode): The device describing the focus mirror mode settings.
|
|
60
|
+
beam_center (DetectorBeamCenter): A device to set and read the beam center on \
|
|
61
|
+
the detector.
|
|
62
|
+
detector_name (DetectorName): The detector currently in use.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
BeamSettings parameter model.
|
|
53
66
|
"""
|
|
54
|
-
|
|
67
|
+
wavelength = yield from bps.rd(dcm.wavelength_in_a)
|
|
68
|
+
beamsize_x = yield from bps.rd(mirrors.beam_size_x)
|
|
69
|
+
beamsize_y = yield from bps.rd(mirrors.beam_size_y)
|
|
70
|
+
pixel_size = (
|
|
71
|
+
Eiger().pixel_size_mm if detector_name == "eiger" else Pilatus().pixel_size_mm
|
|
72
|
+
)
|
|
73
|
+
beam_center_x = yield from bps.rd(beam_center.beam_x)
|
|
74
|
+
beam_center_y = yield from bps.rd(beam_center.beam_y)
|
|
75
|
+
return BeamSettings(
|
|
76
|
+
wavelength_in_a=wavelength,
|
|
77
|
+
beam_size_in_um=(beamsize_x, beamsize_y),
|
|
78
|
+
beam_center_in_mm=(
|
|
79
|
+
beam_center_x * pixel_size[0],
|
|
80
|
+
beam_center_y * pixel_size[1],
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class DCID:
|
|
86
|
+
""" Interfaces with ISPyB to allow ssx DCID/synchweb interaction.
|
|
55
87
|
|
|
56
88
|
Args:
|
|
57
|
-
server: The URL for the bridge server, if not the default.
|
|
58
|
-
emit_errors:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
89
|
+
server (str, optional): The URL for the bridge server, if not the default.
|
|
90
|
+
emit_errors (bool, optional): If False, errors while interacting with the DCID \
|
|
91
|
+
server will not be propagated to the caller. This decides if you want to \
|
|
92
|
+
stop collection if you can't get a DCID. Defaults to True.
|
|
93
|
+
timeout (float, optional): Length of time in s to wait for the DB server before \
|
|
94
|
+
giving up. Defaults to 10 s.
|
|
95
|
+
expt_parameters (ExtruderParameters | FixedTargetParameters): Collection \
|
|
96
|
+
parameters input by user.
|
|
65
97
|
|
|
66
98
|
|
|
67
99
|
Attributes:
|
|
@@ -75,47 +107,44 @@ class DCID:
|
|
|
75
107
|
server: str | None = None,
|
|
76
108
|
emit_errors: bool = True,
|
|
77
109
|
timeout: float = 10,
|
|
78
|
-
|
|
79
|
-
detector: Detector | Literal["eiger", "pilatus"] | None = None,
|
|
110
|
+
expt_params: ExtruderParameters | FixedTargetParameters,
|
|
80
111
|
):
|
|
112
|
+
self.parameters = expt_params
|
|
81
113
|
self.detector: Detector
|
|
82
114
|
# Handle case of string literal
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
self.detector = Pilatus()
|
|
89
|
-
warnings.warn(
|
|
90
|
-
"Please pass detector= to DCID. Pilatus assumed, this will be removed in the future.",
|
|
91
|
-
UserWarning,
|
|
92
|
-
stacklevel=5,
|
|
93
|
-
)
|
|
115
|
+
match expt_params.detector_name:
|
|
116
|
+
case "eiger":
|
|
117
|
+
self.detector = Eiger()
|
|
118
|
+
case "pilatus":
|
|
119
|
+
self.detector = Pilatus()
|
|
94
120
|
|
|
95
121
|
self.server = server or DEFAULT_ISPYB_SERVER
|
|
96
122
|
self.emit_errors = emit_errors
|
|
97
123
|
self.error = False
|
|
98
124
|
self.timeout = timeout
|
|
99
|
-
self.ssx_type = SSXType(ssx_type)
|
|
100
125
|
self.dcid = None
|
|
101
126
|
|
|
102
127
|
def generate_dcid(
|
|
103
128
|
self,
|
|
104
|
-
|
|
129
|
+
beam_settings: BeamSettings,
|
|
105
130
|
image_dir: str,
|
|
131
|
+
file_template: str,
|
|
106
132
|
num_images: int,
|
|
107
|
-
exposure_time: float,
|
|
108
|
-
start_time: datetime.datetime | None = None,
|
|
109
133
|
shots_per_position: int = 1,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
pump_status: int = 0,
|
|
134
|
+
start_time: datetime.datetime | None = None,
|
|
135
|
+
pump_probe: bool = False,
|
|
113
136
|
):
|
|
114
137
|
"""Generate an ispyb DCID.
|
|
115
138
|
|
|
116
139
|
Args:
|
|
117
|
-
|
|
118
|
-
image_dir: The location the images will be written
|
|
140
|
+
beam_settings (BeamSettings): Information about the beam read from hardware.
|
|
141
|
+
image_dir (str): The location the images will be written to.
|
|
142
|
+
num_images (int): Total number of images to be collected.
|
|
143
|
+
shots_per_position (int, optional): Number of exposures per position in a \
|
|
144
|
+
chip. Defaults to 1, which works for extruder.
|
|
145
|
+
start_time(datetime, optional): Collection start time. Defaults to None.
|
|
146
|
+
pump_probe (bool, optional): If True, a pump probe collection is running. \
|
|
147
|
+
Defaults to False.
|
|
119
148
|
"""
|
|
120
149
|
try:
|
|
121
150
|
if not start_time:
|
|
@@ -123,22 +152,18 @@ class DCID:
|
|
|
123
152
|
elif not start_time.timetz:
|
|
124
153
|
start_time = start_time.astimezone()
|
|
125
154
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
155
|
+
resolution = get_resolution(
|
|
156
|
+
self.detector,
|
|
157
|
+
self.parameters.detector_distance_mm,
|
|
158
|
+
beam_settings.wavelength_in_a,
|
|
159
|
+
)
|
|
160
|
+
beamsize_x, beamsize_y = beam_settings.beam_size_in_um
|
|
161
|
+
transmission = self.parameters.transmission * 100
|
|
162
|
+
xbeam, ybeam = beam_settings.beam_center_in_mm
|
|
133
163
|
|
|
134
164
|
if isinstance(self.detector, Pilatus):
|
|
135
|
-
# Mirror the construction that the PPU does
|
|
136
|
-
fileTemplate = get_pilatus_filename_template_from_pvs()
|
|
137
165
|
startImageNumber = 0
|
|
138
166
|
elif isinstance(self.detector, Eiger):
|
|
139
|
-
# Eiger base filename is directly written to the PV
|
|
140
|
-
# Nexgen then uses this to write the .nxs file
|
|
141
|
-
fileTemplate = str(cagetstring(self.detector.pv.file_name)) + ".nxs"
|
|
142
167
|
startImageNumber = 1
|
|
143
168
|
else:
|
|
144
169
|
raise ValueError("Unknown detector:", self.detector)
|
|
@@ -147,48 +172,48 @@ class DCID:
|
|
|
147
172
|
{
|
|
148
173
|
"name": "Xray probe",
|
|
149
174
|
"offset": 0,
|
|
150
|
-
"duration":
|
|
151
|
-
"period":
|
|
175
|
+
"duration": self.parameters.exposure_time_s,
|
|
176
|
+
"period": self.parameters.exposure_time_s,
|
|
152
177
|
"repetition": shots_per_position,
|
|
153
178
|
"eventType": "XrayDetection",
|
|
154
179
|
}
|
|
155
180
|
]
|
|
156
|
-
if
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
181
|
+
if pump_probe:
|
|
182
|
+
match self.parameters:
|
|
183
|
+
case FixedTargetParameters():
|
|
184
|
+
# pump then probe - pump_delay corresponds to time *before* first image
|
|
185
|
+
pump_delay = (
|
|
186
|
+
-self.parameters.laser_delay_s
|
|
187
|
+
if self.parameters.pump_repeat
|
|
188
|
+
is not PumpProbeSetting.Short2
|
|
189
|
+
else self.parameters.laser_delay_s
|
|
190
|
+
)
|
|
191
|
+
case ExtruderParameters():
|
|
192
|
+
pump_delay = self.parameters.laser_delay_s
|
|
167
193
|
events.append(
|
|
168
194
|
{
|
|
169
195
|
"name": "Laser probe",
|
|
170
196
|
"offset": pump_delay,
|
|
171
|
-
"duration":
|
|
172
|
-
# "period": None,
|
|
197
|
+
"duration": self.parameters.laser_dwell_s,
|
|
173
198
|
"repetition": 1,
|
|
174
199
|
"eventType": "LaserExcitation",
|
|
175
200
|
},
|
|
176
201
|
)
|
|
177
202
|
|
|
178
203
|
data = {
|
|
179
|
-
"detectorDistance":
|
|
204
|
+
"detectorDistance": self.parameters.detector_distance_mm,
|
|
180
205
|
"detectorId": self.detector.id,
|
|
181
|
-
"exposureTime":
|
|
182
|
-
"fileTemplate":
|
|
183
|
-
"imageDirectory":
|
|
184
|
-
"numberOfImages":
|
|
185
|
-
"resolution":
|
|
206
|
+
"exposureTime": self.parameters.exposure_time_s,
|
|
207
|
+
"fileTemplate": file_template,
|
|
208
|
+
"imageDirectory": image_dir,
|
|
209
|
+
"numberOfImages": num_images,
|
|
210
|
+
"resolution": resolution,
|
|
186
211
|
"startImageNumber": startImageNumber,
|
|
187
212
|
"startTime": start_time.isoformat(),
|
|
188
|
-
"transmission":
|
|
189
|
-
"visit": visit,
|
|
190
|
-
"wavelength":
|
|
191
|
-
"group": {"experimentType": self.
|
|
213
|
+
"transmission": transmission,
|
|
214
|
+
"visit": self.parameters.visit.name,
|
|
215
|
+
"wavelength": beam_settings.wavelength_in_a,
|
|
216
|
+
"group": {"experimentType": self.parameters.ispyb_experiment_type},
|
|
192
217
|
"xBeam": xbeam,
|
|
193
218
|
"yBeam": ybeam,
|
|
194
219
|
"ssx": {
|
|
@@ -334,84 +359,33 @@ class DCID:
|
|
|
334
359
|
SSX_LOGGER.warning("Error completing DCID: %s (%s)", e, resp_str)
|
|
335
360
|
|
|
336
361
|
|
|
337
|
-
def
|
|
338
|
-
"""
|
|
339
|
-
Get the template file path by querying the detector PVs.
|
|
340
|
-
|
|
341
|
-
Returns: A template string, with the image numbers replaced with '#'
|
|
342
|
-
"""
|
|
343
|
-
|
|
344
|
-
filename = cagetstring(pv.pilat_filename)
|
|
345
|
-
filename_template = cagetstring(pv.pilat_filetemplate)
|
|
346
|
-
file_number = int(caget(pv.pilat_filenumber))
|
|
347
|
-
# Exploit fact that passing negative numbers will put the - before the 0's
|
|
348
|
-
expected_filename = str(filename_template % (filename, f"{file_number:05d}_", -9))
|
|
349
|
-
# Now, find the -09 part of this
|
|
350
|
-
numberpart = re.search(r"(-0+9)", expected_filename)
|
|
351
|
-
# Make sure this was the only one
|
|
352
|
-
if numberpart is not None:
|
|
353
|
-
assert re.search(r"(-0+9)", expected_filename[numberpart.end() :]) is None
|
|
354
|
-
template_fill = "#" * len(numberpart.group(0))
|
|
355
|
-
return (
|
|
356
|
-
expected_filename[: numberpart.start()]
|
|
357
|
-
+ template_fill
|
|
358
|
-
+ expected_filename[numberpart.end() :]
|
|
359
|
-
)
|
|
360
|
-
else:
|
|
361
|
-
raise ValueError(f"{filename=} did not contain the numbers for templating")
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
def get_beamsize() -> tuple[float | None, float | None]:
|
|
362
|
+
def get_pilatus_filename_template_from_device():
|
|
365
363
|
"""
|
|
366
|
-
|
|
364
|
+
Get the template file path by querying the detector PVs, mirror the construction \
|
|
365
|
+
that the PPU does.
|
|
367
366
|
|
|
368
367
|
Returns:
|
|
369
|
-
A
|
|
370
|
-
if the focus mode was unrecognised.
|
|
368
|
+
A template string, with the image numbers replaced with '#'
|
|
371
369
|
"""
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
"focus30d": ("30x30", 30, 30),
|
|
377
|
-
"focus50d": ("50x50", 50, 50),
|
|
378
|
-
"focus1050d": ("10x50", 10, 50),
|
|
379
|
-
"focus5010d": ("50x10", 50, 10),
|
|
380
|
-
"focus3010d": ("30x10", 30, 10),
|
|
381
|
-
}
|
|
382
|
-
v_mode = caget("BL24I-OP-MFM-01:G0:TARGETAPPLY")
|
|
383
|
-
h_mode = caget("BL24I-OP-MFM-01:G1:TARGETAPPLY")
|
|
384
|
-
# Validate these and note an error otherwise
|
|
385
|
-
if not v_mode.startswith("VMFM") or v_mode[4:] not in focus_modes:
|
|
386
|
-
SSX_LOGGER.error("Unrecognised vertical beam mode %s", v_mode)
|
|
387
|
-
if not h_mode.startswith("HMFM") or h_mode[4:] not in focus_modes:
|
|
388
|
-
SSX_LOGGER.error("Unrecognised horizontal beam mode %s", h_mode)
|
|
389
|
-
_, h, _ = focus_modes.get(h_mode[4:], (None, None, None))
|
|
390
|
-
_, _, v = focus_modes.get(v_mode[4:], (None, None, None))
|
|
391
|
-
|
|
392
|
-
return (h, v)
|
|
370
|
+
pilatus_metadata: PilatusMetadata = i24.pilatus_metadata()
|
|
371
|
+
|
|
372
|
+
filename_template = yield from bps.rd(pilatus_metadata.filename_template)
|
|
373
|
+
return filename_template
|
|
393
374
|
|
|
394
375
|
|
|
395
376
|
def get_resolution(detector: Detector, distance: float, wavelength: float) -> float:
|
|
396
|
-
"""
|
|
397
|
-
Calculate the inscribed resolution for detector.
|
|
377
|
+
""" Calculate the inscribed resolution for detector.
|
|
398
378
|
|
|
399
|
-
This assumes perfectly centered beam as I don't know where to
|
|
400
|
-
|
|
379
|
+
This assumes perfectly centered beam as I don't know where to extract the beam \
|
|
380
|
+
position parameters yet.
|
|
401
381
|
|
|
402
382
|
Args:
|
|
403
|
-
|
|
404
|
-
|
|
383
|
+
detector (Detector): Detector instance, Eiger() or Pilatus().
|
|
384
|
+
distance (float): Distance to detector, in mm.
|
|
385
|
+
wavelength (float): Beam wavelength, in Å.
|
|
405
386
|
|
|
406
387
|
Returns:
|
|
407
|
-
Maximum resolution
|
|
388
|
+
Maximum resolution, in Å.
|
|
408
389
|
"""
|
|
409
390
|
width = detector.image_size_mm[0]
|
|
410
391
|
return round(wavelength / (2 * math.sin(math.atan(width / (2 * distance)) / 2)), 2)
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
def get_beam_center(detector: Detector) -> tuple[float, float]:
|
|
414
|
-
"""Get the detector beam center, in mm"""
|
|
415
|
-
beamX = float(caget(detector.pv.beamx)) * detector.pixel_size_mm[0]
|
|
416
|
-
beamY = float(caget(detector.pv.beamy)) * detector.pixel_size_mm[1]
|
|
417
|
-
return (beamX, beamY)
|
|
@@ -370,7 +370,7 @@ font "arial-medium-r-24.0"
|
|
|
370
370
|
buttonLabel "Start"
|
|
371
371
|
numCmds 1
|
|
372
372
|
command {
|
|
373
|
-
0 "blueapi -c CONFIG_LOCATION controller run run_extruder_plan '\{\"zebra\":\"zebra\",\"aperture\":\"aperture\",\"backlight\":\"backlight\",\"beamstop\":\"beamstop\",\"detector_stage\":\"detector_motion\",\"shutter\":\"shutter\",\"dcm\":\"dcm\"\}'"
|
|
373
|
+
0 "blueapi -c CONFIG_LOCATION controller run run_extruder_plan '\{\"zebra\":\"zebra\",\"aperture\":\"aperture\",\"backlight\":\"backlight\",\"beamstop\":\"beamstop\",\"detector_stage\":\"detector_motion\",\"shutter\":\"shutter\",\"dcm\":\"dcm\",\"mirrors\":\"focus_mirrors\",\"attenuator\":\"attenuator\"\}'"
|
|
374
374
|
}
|
|
375
375
|
endObjectProperties
|
|
376
376
|
|