Skip to content

Commit b56f429

Browse files
committed
Jiffy script for feeding data into XRC service
1 parent 3c691ac commit b56f429

1 file changed

Lines changed: 186 additions & 0 deletions

File tree

contrib/feed_xrc.py

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
Directly feed PIA results into X-Ray Centering and fetch the result
5+
6+
Wanted to have an easy way to feed the captured results from the PIA
7+
service directly into the DLSXRayCentering service and get the results
8+
back, without having to set uup a fixed test or inject test data into
9+
the live Zocalo system.
10+
11+
Args:
12+
GRID_WIDTH How wide the grid scan was taken at. This is used to
13+
derive the other dimension for each scan.
14+
PIA_FILES JSON-lines files, with one entry on each line of a
15+
message with the following structure:
16+
17+
{"file_number": N, "file_seen_at": 123456, "n_spots_total": 43}
18+
"""
19+
20+
from __future__ import annotations
21+
22+
import json
23+
import logging
24+
import types
25+
from argparse import ArgumentParser, RawDescriptionHelpFormatter
26+
from pathlib import Path
27+
from unittest import mock
28+
29+
from workflows.recipe.wrapper import RecipeWrapper
30+
31+
import dlstbx.services.xray_centering
32+
33+
try:
34+
from rich import print
35+
except ModuleNotFoundError:
36+
pass
37+
38+
39+
class FakeTransport:
40+
messages = []
41+
42+
def subscribe(self, *args, **kwargs):
43+
pass
44+
45+
def connect(self):
46+
return True
47+
48+
def disconnect(self):
49+
pass
50+
51+
def subscription_callback_set_intercept(self, *args):
52+
pass
53+
54+
def transaction_begin(self, *args, **kwargs):
55+
return mock.sentinel.transaction
56+
57+
def ack(self, *args, **kwargs):
58+
pass
59+
60+
def transaction_commit(self, *args, **kwargs):
61+
return mock.sentinel.transaction
62+
63+
64+
def generate_recipe_message(parameters, gridinfo):
65+
"""Helper function taken from XRC tests."""
66+
message = {
67+
"recipe": {
68+
"1": {
69+
"service": "DLS X-Ray Centering",
70+
"queue": "reduce.xray_centering",
71+
"parameters": parameters,
72+
"gridinfo": gridinfo,
73+
},
74+
"start": [(1, [])],
75+
},
76+
"recipe-pointer": 1,
77+
"recipe-path": [],
78+
"environment": {
79+
"ID": mock.sentinel.GUID,
80+
"source": mock.sentinel.source,
81+
"timestamp": mock.sentinel.timestamp,
82+
},
83+
"payload": mock.sentinel.payload,
84+
}
85+
return message
86+
87+
88+
class Collection:
89+
def __init__(self, dcid: int, pia_file: Path):
90+
self.dcid = dcid
91+
self.pia_file = pia_file
92+
self.pia = read_pia_file(pia_file)
93+
94+
95+
def read_pia_file(file: Path) -> list[dict]:
96+
pia = []
97+
for line in file.read_text().splitlines():
98+
if not line:
99+
continue
100+
data = json.loads(line)
101+
# Integerize this to avoid potential pydantic problem
102+
if "file-seen-at" in data:
103+
data["file-seen-at"] = int(data["file-seen-at"])
104+
pia.append(data)
105+
106+
return pia
107+
108+
109+
parser = ArgumentParser(epilog=__doc__, formatter_class=RawDescriptionHelpFormatter)
110+
parser.add_argument(
111+
"grid_width",
112+
type=int,
113+
help="Width of image grid. Used for constructing the grid layout.",
114+
metavar="GRID_WIDTH",
115+
)
116+
parser.add_argument(
117+
"pia_files",
118+
nargs="+",
119+
type=Path,
120+
metavar="PIA_FILES",
121+
help="JSONLines data files containing PIA results, one file per DCID",
122+
)
123+
parser.add_argument(
124+
"--no-snaked",
125+
action="store_false",
126+
dest="snaked",
127+
help="The supplied data file isn't snaked",
128+
)
129+
args = parser.parse_args()
130+
131+
logging.basicConfig(level=logging.DEBUG, format="%(message)s")
132+
133+
xrc = dlstbx.services.xray_centering.DLSXRayCentering()
134+
xrc.transport = FakeTransport()
135+
xrc.start()
136+
xrc.log = logging.getLogger("xrc")
137+
# Initializing this screw up logging.. reset it here
138+
logging.getLogger().setLevel(logging.DEBUG)
139+
for h in logging.getLogger().handlers:
140+
h.setLevel(logging.DEBUG)
141+
142+
dcs = [Collection(i + 1, p) for i, p in enumerate(args.pia_files)]
143+
144+
messages_out = {"success": []}
145+
146+
147+
def _rw_send_result(self, channel, message, **kwargs):
148+
messages_out[channel].append(message)
149+
150+
151+
for i, dc in enumerate(dcs):
152+
print(f"Handling {dc.dcid} ({dc.pia_file})")
153+
154+
assert len(dc.pia) % args.grid_width == 0
155+
gridinfo = {
156+
"dx_mm": 0.02,
157+
"dy_mm": 0.02,
158+
"gridInfoId": 1000 + i,
159+
"orientation": "horizontal",
160+
"micronsPerPixelX": 0.72,
161+
"micronsPerPixelY": 0.72,
162+
"snaked": 1 if args.snaked else 0,
163+
"snapshot_offsetXPixel": 0,
164+
"snapshot_offsetYPixel": 0,
165+
"steps_x": args.grid_width,
166+
"steps_y": len(dc.pia) // args.grid_width,
167+
}
168+
parameters = {
169+
"dcid": f"{dc.dcid}",
170+
"dcg_dcids": [x.dcid for x in dcs[:i]],
171+
"experiment_type": "Mesh3D",
172+
"beamline": "i03",
173+
}
174+
175+
rw = RecipeWrapper(
176+
message=generate_recipe_message(parameters, gridinfo), transport=xrc.transport
177+
)
178+
rw.send_to = types.MethodType(_rw_send_result, rw)
179+
for pia in dc.pia:
180+
header = {
181+
"message-id": mock.sentinel.message_id,
182+
"subscription": mock.sentinel.subscription,
183+
}
184+
xrc.add_pia_result(rw, header, pia)
185+
186+
print(messages_out)

0 commit comments

Comments
 (0)