|
| 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