diff --git a/src/crystallography_bluesky/i15_1/callbacks/analysis_callback.py b/src/crystallography_bluesky/i15_1/callbacks/analysis_callback.py index a81e405..cf309c5 100644 --- a/src/crystallography_bluesky/i15_1/callbacks/analysis_callback.py +++ b/src/crystallography_bluesky/i15_1/callbacks/analysis_callback.py @@ -3,7 +3,7 @@ from bluesky.callbacks import CallbackBase from dodal.log import LOGGER from event_model.documents import RunStart, RunStop -from heliotrapi.client import AnalysisClient +from heliotrapi.client import AnalysisClient, AnalysisResult I15_1_ANALYSIS_URL = "https://i15-1-analysis.diamond.ac.uk" @@ -47,9 +47,9 @@ def stop(self, doc: RunStop) -> RunStop | None: ) LOGGER.info(f"Submitted analysis request {self.request_id}") - def wait_on_and_retrieve_result(self): + def wait_on_and_retrieve_result(self) -> AnalysisResult: if not self.request_id: raise ValueError("Results requested but analysis has not been triggered") result = self._client.get_request_id_result(self.request_id) LOGGER.info(f"Received result from analysis {result} for {self.request_id}") - return result.result + return result diff --git a/src/crystallography_bluesky/i15_1/plans/centre_sample.py b/src/crystallography_bluesky/i15_1/plans/centre_sample.py index 256b9de..b705e8a 100644 --- a/src/crystallography_bluesky/i15_1/plans/centre_sample.py +++ b/src/crystallography_bluesky/i15_1/plans/centre_sample.py @@ -91,7 +91,7 @@ def per_step(): ) analysis_result = analysis_callback.wait_on_and_retrieve_result() - centre = analysis_result["centre"] + centre = analysis_result.result["centre"] assert (start_z < centre < end_z) or (start_z > centre > end_z), ( f"Analysis result {centre} is not within the bounds of the scan: " + f"({start_z, end_z})" @@ -99,3 +99,5 @@ def per_step(): LOGGER.info(f"Got {centre} from analysis, moving there now") yield from bps.mv(hexapod.z, centre) + + return analysis_result diff --git a/src/crystallography_bluesky/i15_1/plans/generic_collection.py b/src/crystallography_bluesky/i15_1/plans/generic_collection.py index b525ecd..a7bd074 100644 --- a/src/crystallography_bluesky/i15_1/plans/generic_collection.py +++ b/src/crystallography_bluesky/i15_1/plans/generic_collection.py @@ -9,6 +9,7 @@ from dodal.common import inject from dodal.common.beamlines.beamline_utils import get_config_client from dodal.devices.beamlines.i15_1.attenuators import FastAttenuator, SlowAttenuator +from dodal.devices.beamlines.i15_1.hexapod import Hexapod from dodal.devices.beamlines.i15_1.laue import LaueMonochrometer from dodal.devices.beamlines.i15_1.robot import Robot from dodal.devices.synchrotron import Synchrotron @@ -63,6 +64,7 @@ class GenericCollectionDevices: fast_attenuator: FastAttenuator slow_attenuator: SlowAttenuator synchrotron: Synchrotron + hexapod: Hexapod def get_default_baseline_devices(all_devices: GenericCollectionDevices): @@ -72,6 +74,7 @@ def get_default_baseline_devices(all_devices: GenericCollectionDevices): all_devices.tth, all_devices.fast_attenuator, all_devices.slow_attenuator, + all_devices.hexapod, ] diff --git a/tests/unit_tests/i15_1/callbacks/test_analysis_callback.py b/tests/unit_tests/i15_1/callbacks/test_analysis_callback.py index 763d15e..d4daa9e 100644 --- a/tests/unit_tests/i15_1/callbacks/test_analysis_callback.py +++ b/tests/unit_tests/i15_1/callbacks/test_analysis_callback.py @@ -105,7 +105,7 @@ def test_wait_on_and_retrieve_result(mock_client_cls): callback = TriggerAnalysisCallback("url", "analysis") callback.request_id = MagicMock() - assert callback.wait_on_and_retrieve_result() == 42 + assert callback.wait_on_and_retrieve_result().result == 42 mock_client.get_request_id_result.assert_called_once() diff --git a/tests/unit_tests/i15_1/conftest.py b/tests/unit_tests/i15_1/conftest.py index daec664..d575ae3 100644 --- a/tests/unit_tests/i15_1/conftest.py +++ b/tests/unit_tests/i15_1/conftest.py @@ -14,10 +14,10 @@ from dodal.devices.beamlines.i15_1.attenuators import FastAttenuator, SlowAttenuator from dodal.devices.beamlines.i15_1.beam_health import BeamHealth from dodal.devices.beamlines.i15_1.blower import Blower +from dodal.devices.beamlines.i15_1.hexapod import Hexapod from dodal.devices.beamlines.i15_1.cobra import Cobra from dodal.devices.beamlines.i15_1.laue import LaueMonochrometer from dodal.devices.beamlines.i15_1.robot import Robot -from dodal.devices.motors import XYZStage from dodal.devices.synchrotron import Synchrotron from dodal.devices.tetramm import SummingTetrammDetector from dodal.devices.zebra.zebra import Zebra, ZebraMapping @@ -115,9 +115,9 @@ async def fast_shutter() -> ZebraFastShutter: @pytest.fixture -async def hexapod() -> XYZStage: +async def hexapod() -> Hexapod: async with init_devices(mock=True): - hexapod = XYZStage("") + hexapod = Hexapod("", "") return hexapod @@ -154,6 +154,7 @@ async def common_collection_devices( slow_attenuator: SlowAttenuator, fast_attenuator: FastAttenuator, synchrotron: Synchrotron, + hexapod: Hexapod, ) -> GenericCollectionDevices: return GenericCollectionDevices( eiger, @@ -166,6 +167,7 @@ async def common_collection_devices( fast_attenuator, slow_attenuator, synchrotron, + hexapod, ) diff --git a/tests/unit_tests/i15_1/test_centre_sample.py b/tests/unit_tests/i15_1/test_centre_sample.py index 85a34f1..95e7695 100644 --- a/tests/unit_tests/i15_1/test_centre_sample.py +++ b/tests/unit_tests/i15_1/test_centre_sample.py @@ -5,7 +5,7 @@ import bluesky.plan_stubs as bps import bluesky.preprocessors as bpp import pytest -from bluesky.run_engine import RunEngine +from bluesky.run_engine import RunEngine, RunEngineResult from bluesky.simulators import RunEngineSimulator, assert_message_and_return_remaining from dodal.devices.motors import XYZStage from heliotrapi.models import AnalysisResult @@ -24,7 +24,7 @@ @pytest.fixture def blueapi_run_engine(): # This adds the info the blueapi will add in prod - RE = RunEngine() + RE = RunEngine(call_returns_result=True) RE.md["data_session_directory"] = "/dls/i15-1/data/2026/cm44163-3" RE.md["scan_file"] = "i15-1-95557" return RE @@ -36,7 +36,9 @@ def mock_analysis_callback(): "crystallography_bluesky.i15_1.plans.centre_sample.TriggerAnalysisCallback" ) as patch_analysis_callback: instance = patch_analysis_callback.return_value - instance.wait_on_and_retrieve_result.return_value = {"centre": 15} + instance.wait_on_and_retrieve_result.return_value = AnalysisResult( + result={"centre": 15}, status="completed", analysis_name="centring" + ) yield patch_analysis_callback @@ -50,7 +52,7 @@ def mock_analysis_client(): analysis_name="fake_sample_alignment_i15_1", result={"centre": 17}, status="completed", - created_at=datetime.now(), + created_at=datetime(2026, 10, 20, 14, 35, 46, 96041), ) yield mock_client @@ -261,7 +263,7 @@ def test_centre_sample_moved_to_start_before_stage( @patch("crystallography_bluesky.i15_1.plans.centre_sample.generic_per_step_collection") -def test_centre_sample_calls_analysis_and_retrieves_result( +def test_centre_sample_calls_analysis_and_retrieves_and_returns_result( mock_generic_collection: MagicMock, mock_analysis_client: MagicMock, hexapod: XYZStage, @@ -276,12 +278,22 @@ def my_plan(*_, **__): yield from bps.null() mock_generic_collection.side_effect = my_plan - blueapi_run_engine( + result = blueapi_run_engine( centre_sample(10, 20, 10, 0.01, generic_collection_devices, hexapod) ) mock_analysis_client.submit.assert_called_once() mock_analysis_client.get_request_id_result.assert_called_once() + assert isinstance(result, RunEngineResult) and result.plan_result == AnalysisResult( + request_id=None, + status="completed", + analysis_name="fake_sample_alignment_i15_1", + inputs=None, + result={"centre": 17}, + created_at=datetime(2026, 10, 20, 14, 35, 46, 96041), + finished_at=None, + version="0.3.3", + ) @patch("crystallography_bluesky.i15_1.plans.centre_sample.generic_per_step_collection") diff --git a/tests/unit_tests/i15_1/test_single_temperature_collection.py b/tests/unit_tests/i15_1/test_single_temperature_collection.py index 6f5958d..5f45bdd 100644 --- a/tests/unit_tests/i15_1/test_single_temperature_collection.py +++ b/tests/unit_tests/i15_1/test_single_temperature_collection.py @@ -96,6 +96,7 @@ def fake_setup(*_, **__): common_collection_devices.xtal, common_collection_devices.fast_attenuator, common_collection_devices.slow_attenuator, + common_collection_devices.hexapod, ] + baseline_devices )