From 2b7daa27894b7c7d945c9b48aed4a5ef62e959da Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Tue, 14 Oct 2025 17:16:07 +0100 Subject: [PATCH 1/9] Add load_centre_collect, robot_unload and udc_default_state blueapi plans --- run_hyperion.sh | 26 ++++++--- src/mx_bluesky/hyperion/blueapi_config.yaml | 19 ++++++ .../hyperion/blueapi_dev_config.yaml | 17 ++++++ .../hyperion/blueapi_plans/__init__.py | 58 +++++++++++++++++++ .../move_to_udc_default_state.json | 5 ++ .../hyperion_blueapi/robot_unload.json | 7 +++ .../test_load_centre_collect.json | 56 ++++++++++++++++++ 7 files changed, 181 insertions(+), 7 deletions(-) create mode 100644 src/mx_bluesky/hyperion/blueapi_config.yaml create mode 100644 src/mx_bluesky/hyperion/blueapi_dev_config.yaml create mode 100644 src/mx_bluesky/hyperion/blueapi_plans/__init__.py create mode 100644 tests/test_data/hyperion_blueapi/move_to_udc_default_state.json create mode 100644 tests/test_data/hyperion_blueapi/robot_unload.json create mode 100644 tests/test_data/hyperion_blueapi/test_load_centre_collect.json diff --git a/run_hyperion.sh b/run_hyperion.sh index 95f8c13066..80ce1b9b03 100755 --- a/run_hyperion.sh +++ b/run_hyperion.sh @@ -7,6 +7,9 @@ START=1 IN_DEV=false MODE=gda +CONFIG_DIR=`dirname $0`/src/mx_bluesky/hyperion +BLUEAPI_CONFIG=$CONFIG_DIR/blueapi_config.yaml + for option in "$@"; do case $option in -b=*|--beamline=*) @@ -22,10 +25,14 @@ for option in "$@"; do ;; --dev) IN_DEV=true + BLUEAPI_CONFIG=$CONFIG_DIR/blueapi_dev_config.yaml ;; --udc) MODE=udc ;; + --blueapi) + MODE=blueapi + ;; --help|--info|--h) source .venv/bin/activate echo "`basename $0` [options]" @@ -38,10 +45,9 @@ Options: --stop Used to stop a currently running instance of Hyperion. Will override any other operations options. --no-start Used to specify that the script should be run without starting the server. - --skip-startup-connection - Do not connect to devices at startup --dev Enable dev mode to run from a local workspace on a development machine. --udc Start hyperion in UDC mode instead of taking commands from GDA + --blueapi Start hyperion in blueapi mode instead of taking commands from GDA --help This help By default this script will start an Hyperion server unless the --no-start flag is specified. @@ -59,6 +65,7 @@ kill_active_apps () { echo "Killing active instances of hyperion and hyperion-callbacks..." pkill -e -f "python.*hyperion" pkill -e -f "SCREEN.*hyperion" + blueapi controller stop 2>/dev/null echo "done." } @@ -120,8 +127,6 @@ if [[ $START == 1 ]]; then source .venv/bin/activate - #Add future arguments here - declare -A h_and_cb_args=( ["IN_DEV"]="$IN_DEV" ) declare -A h_and_cb_arg_strings=( ["IN_DEV"]="--dev" ) @@ -136,8 +141,15 @@ if [[ $START == 1 ]]; then done unset PYEPICS_LIBCA - echo "Starting hyperion with hyperion $h_commands, start_log is $start_log_path" - hyperion `echo $h_commands;`>$start_log_path 2>&1 & + if [ $MODE = "blueapi" ]; then + echo "Starting hyperion in blueapi mode, start log is $start_log_path" + blueapi --config $BLUEAPI_CONFIG serve > $start_log_path 2>&1 & + HEALTHCHECK_ENDPOINT="healthz" + else + echo "Starting hyperion with hyperion $h_commands, start_log is $start_log_path" + hyperion `echo $h_commands;`>$start_log_path 2>&1 & + HEALTHCHECK_ENDPOINT="status" + fi echo "Starting hyperion-callbacks with hyperion-callbacks $cb_commands, start_log is $callback_start_log_path" hyperion-callbacks `echo $cb_commands;`>$callback_start_log_path 2>&1 & echo "$(date) Waiting for Hyperion to start" @@ -145,7 +157,7 @@ if [[ $START == 1 ]]; then for i in {1..30} do echo "$(date)" - curl --head -X GET http://localhost:5005/status >/dev/null + curl --head -X GET http://localhost:5005/$HEALTHCHECK_ENDPOINT >/dev/null ret_value=$? if [ $ret_value -ne 0 ]; then sleep 1 diff --git a/src/mx_bluesky/hyperion/blueapi_config.yaml b/src/mx_bluesky/hyperion/blueapi_config.yaml new file mode 100644 index 0000000000..16fc8a1327 --- /dev/null +++ b/src/mx_bluesky/hyperion/blueapi_config.yaml @@ -0,0 +1,19 @@ +env: + sources: + - kind: dodal + module: dodal.beamlines.i03 + - kind: planFunctions + module: mx_bluesky.hyperion.experiment_plans.load_centre_collect_full_plan + events: + broadcast_status_events: false +api: + url: http://localhost:5005 + cors: + allow_credentials: True + origins: + - "*" +logging: + graylog: + url: "tcp://graylog-log-target.diamond.ac.uk:12232" + enabled: true + diff --git a/src/mx_bluesky/hyperion/blueapi_dev_config.yaml b/src/mx_bluesky/hyperion/blueapi_dev_config.yaml new file mode 100644 index 0000000000..3a1d460f62 --- /dev/null +++ b/src/mx_bluesky/hyperion/blueapi_dev_config.yaml @@ -0,0 +1,17 @@ +env: + sources: + - kind: dodal + module: dodal.beamlines.i03 + mock: true + - kind: planFunctions + module: mx_bluesky.hyperion.blueapi_plans + events: + broadcast_status_events: false +api: + url: http://localhost:5005 + cors: + allow_credentials: True + origins: + - "*" +logging: + level: DEBUG \ No newline at end of file diff --git a/src/mx_bluesky/hyperion/blueapi_plans/__init__.py b/src/mx_bluesky/hyperion/blueapi_plans/__init__.py new file mode 100644 index 0000000000..a2fcbfb188 --- /dev/null +++ b/src/mx_bluesky/hyperion/blueapi_plans/__init__.py @@ -0,0 +1,58 @@ +""" +This module contains the bluesky plan entry points for use with hyperion-blueapi. +""" + +from bluesky.utils import MsgGenerator +from dodal.common import inject +from dodal.devices.aperturescatterguard import ApertureScatterguard +from dodal.devices.motors import XYZStage +from dodal.devices.robot import BartRobot +from dodal.devices.smargon import Smargon + +from mx_bluesky.common.device_setup_plans.robot_load_unload import ( + robot_unload as _robot_unload, +) +from mx_bluesky.common.experiment_plans.inner_plans.udc_default_state import ( + UDCDefaultDevices, +) +from mx_bluesky.common.experiment_plans.inner_plans.udc_default_state import ( + move_to_udc_default_state as _move_to_udc_default_state, +) +from mx_bluesky.hyperion.experiment_plans.load_centre_collect_full_plan import ( + LoadCentreCollectComposite, +) +from mx_bluesky.hyperion.experiment_plans.load_centre_collect_full_plan import ( + load_centre_collect_full as _load_centre_collect_full, +) +from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect + +__all__ = [ + "LoadCentreCollectComposite", + "LoadCentreCollect", + "UDCDefaultDevices", + "load_centre_collect", + "move_to_udc_default_state", + "robot_unload", +] + + +def load_centre_collect( + parameters: LoadCentreCollect, composite: LoadCentreCollectComposite = inject() +) -> MsgGenerator: + yield from _load_centre_collect_full(composite, parameters) + + +def robot_unload( + visit: str, + robot: BartRobot = inject("robot"), + smargon: Smargon = inject("smargon"), + aperture_scatterguard: ApertureScatterguard = inject("aperture_scatterguard"), + lower_gonio: XYZStage = inject("lower_gonio"), +) -> MsgGenerator: + yield from _robot_unload(robot, smargon, aperture_scatterguard, lower_gonio, visit) + + +def move_to_udc_default_state( + composite: UDCDefaultDevices = inject(), +) -> MsgGenerator: + yield from _move_to_udc_default_state(composite) diff --git a/tests/test_data/hyperion_blueapi/move_to_udc_default_state.json b/tests/test_data/hyperion_blueapi/move_to_udc_default_state.json new file mode 100644 index 0000000000..7a5e3cebfd --- /dev/null +++ b/tests/test_data/hyperion_blueapi/move_to_udc_default_state.json @@ -0,0 +1,5 @@ +{ + "name": "move_to_udc_default_state", + "params": {}, + "instrument_session": "cm12345-1" +} diff --git a/tests/test_data/hyperion_blueapi/robot_unload.json b/tests/test_data/hyperion_blueapi/robot_unload.json new file mode 100644 index 0000000000..f1e26039ae --- /dev/null +++ b/tests/test_data/hyperion_blueapi/robot_unload.json @@ -0,0 +1,7 @@ +{ + "name": "robot_unload", + "params": { + "visit": "cm12345-1" + }, + "instrument_session": "cm12345-1" +} diff --git a/tests/test_data/hyperion_blueapi/test_load_centre_collect.json b/tests/test_data/hyperion_blueapi/test_load_centre_collect.json new file mode 100644 index 0000000000..376eeace9f --- /dev/null +++ b/tests/test_data/hyperion_blueapi/test_load_centre_collect.json @@ -0,0 +1,56 @@ +{ + "name": "load_centre_collect", + "params": { + "parameters": { + "parameter_model_version": "5.0.0", + "beamline": "BL03S", + "det_dist_to_beam_converter_path": "tests/test_data/test_lookup_table.txt", + "insertion_prefix": "SR03S", + "visit": "cm31105-4", + "detector_distance_mm": 255, + "sample_id": 12345, + "sample_puck": 40, + "sample_pin": 3, + "robot_load_then_centre": { + "storage_directory": "{tmp_data}/123458/xraycentring", + "file_name": "robot_load_centring_file", + "comment": "Robot load and centre", + "exposure_time_s": 0.004, + "use_roi_mode": false, + "demand_energy_ev": 11100, + "run_number": 0, + "panda_runup_distance_mm": 0.17, + "box_size_um": 20 + }, + "multi_rotation_scan": { + "comment": "Rotation", + "storage_directory": "{tmp_data}/123458/", + "file_name": "file_name", + "exposure_time_s": 0.004, + "selected_aperture": "SMALL_APERTURE", + "transmission_frac": 1.0, + "demand_energy_ev": 11100, + "rotation_increment_deg": 0.1, + "shutter_opening_time_s": 0.6, + "snapshot_omegas_deg": [ + 0, + 90, + 180, + 270 + ], + "run_number": 1, + "rotation_scans": [ + { + "rotation_axis": "omega", + "rotation_direction": "Negative", + "scan_width_deg": 180.0, + "omega_start_deg": 0, + "phi_start_deg": 0.47, + "chi_start_deg": 23.85 + } + ] + } + } + }, + "instrument_session": "cm12345-1" +} From 88687d64c646709463a1b5ddddd1aa5da0944303 Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Mon, 20 Oct 2025 17:47:03 +0100 Subject: [PATCH 2/9] Add some documentation for hyperion-blueapi --- docs/developer/hyperion/hyperion-blueapi.rst | 35 +++++++++++++++++++ .../hyperion/blueapi_plans/__init__.py | 19 ++++++++++ 2 files changed, 54 insertions(+) create mode 100644 docs/developer/hyperion/hyperion-blueapi.rst diff --git a/docs/developer/hyperion/hyperion-blueapi.rst b/docs/developer/hyperion/hyperion-blueapi.rst new file mode 100644 index 0000000000..0c4bee4350 --- /dev/null +++ b/docs/developer/hyperion/hyperion-blueapi.rst @@ -0,0 +1,35 @@ +Hyperion on BlueAPI +=================== + +This document describes the migration of Hyperion from a monolithic service that contains its own application server +and is only partially dependent on BlueAPI, +to a standard BlueAPI application deployment. + +Architecture +------------ + +Hyperion on BlueAPI consists of two components: + +* hyperion-blueapi: This is intended to ultimately be a standard blueapi installation, consisting of a beamline + module and a dodal plan module. In the interim, deployment may vary from the standard method until such time as + monolithic operation can be desupported. ``hyperion-blueapi`` exposes a minimal set of bluesky plans for UDC data + collection. + +* hyperion-supervisor: This will be a separate service that is responsible for fetching instructions from +Agamemnon, decoding them and sending corresponding requests to ``hyperion-blueapi`` for execution. The supervisor +also monitors the state of ``hyperion-blueapi``, manages the Hyperion baton and provides endpoints for status +monitoring. + +Deployment +---------- + +``hyperion-blueapi`` is automatically available in a standard Hyperion deployment. + +Launching +--------- + +``hyperion-blueapi`` can be launched in using the ``run_hyperion.sh`` script, using the ``--blueapi`` option: + +:: + + ./run_hyperion.sh --beamline=i03 --dev --blueapi diff --git a/src/mx_bluesky/hyperion/blueapi_plans/__init__.py b/src/mx_bluesky/hyperion/blueapi_plans/__init__.py index a2fcbfb188..dd15f95f87 100644 --- a/src/mx_bluesky/hyperion/blueapi_plans/__init__.py +++ b/src/mx_bluesky/hyperion/blueapi_plans/__init__.py @@ -1,5 +1,8 @@ """ This module contains the bluesky plan entry points for use with hyperion-blueapi. +The json schema and documentation therein generated by the blueapi /plans endpoint +from this file constitutes the hyperion-blueapi interface to the hyperion supervisor +process. """ from bluesky.utils import MsgGenerator @@ -39,6 +42,15 @@ def load_centre_collect( parameters: LoadCentreCollect, composite: LoadCentreCollectComposite = inject() ) -> MsgGenerator: + """ + Attempt a complete data collection experiment, consisting of the following: + * Load the sample if necessary + * Move to the specified goniometer start angles + * Perform optical centring, then X-ray centring + * If X-ray centring finds one or more diffracting centres then for each centre + that satisfies the chosen selection function, + move to that centre and do a collection with the specified parameters. + """ yield from _load_centre_collect_full(composite, parameters) @@ -49,10 +61,17 @@ def robot_unload( aperture_scatterguard: ApertureScatterguard = inject("aperture_scatterguard"), lower_gonio: XYZStage = inject("lower_gonio"), ) -> MsgGenerator: + """ + Unload the currently mounted pin into the location that it was loaded from. + This is to be invoked as the final step upon successful completion of the UDC queue. + """ yield from _robot_unload(robot, smargon, aperture_scatterguard, lower_gonio, visit) def move_to_udc_default_state( composite: UDCDefaultDevices = inject(), ) -> MsgGenerator: + """ + Move beamline hardware to known positions prior to UDC start. + """ yield from _move_to_udc_default_state(composite) From 353efb919b1a465327a815149594199a8d72e817 Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Thu, 4 Dec 2025 17:13:28 +0000 Subject: [PATCH 3/9] Fixup imports --- src/mx_bluesky/hyperion/blueapi_plans/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mx_bluesky/hyperion/blueapi_plans/__init__.py b/src/mx_bluesky/hyperion/blueapi_plans/__init__.py index dd15f95f87..cef1bab1d0 100644 --- a/src/mx_bluesky/hyperion/blueapi_plans/__init__.py +++ b/src/mx_bluesky/hyperion/blueapi_plans/__init__.py @@ -15,18 +15,18 @@ from mx_bluesky.common.device_setup_plans.robot_load_unload import ( robot_unload as _robot_unload, ) -from mx_bluesky.common.experiment_plans.inner_plans.udc_default_state import ( - UDCDefaultDevices, -) -from mx_bluesky.common.experiment_plans.inner_plans.udc_default_state import ( - move_to_udc_default_state as _move_to_udc_default_state, -) from mx_bluesky.hyperion.experiment_plans.load_centre_collect_full_plan import ( LoadCentreCollectComposite, ) from mx_bluesky.hyperion.experiment_plans.load_centre_collect_full_plan import ( load_centre_collect_full as _load_centre_collect_full, ) +from mx_bluesky.hyperion.experiment_plans.udc_default_state import ( + UDCDefaultDevices, +) +from mx_bluesky.hyperion.experiment_plans.udc_default_state import ( + move_to_udc_default_state as _move_to_udc_default_state, +) from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect __all__ = [ From 148b9667d06ec10b504956a1f86ea7928b7399a4 Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Fri, 5 Dec 2025 12:26:09 +0000 Subject: [PATCH 4/9] Updates to work with DeviceManager, BartRobot changes --- .../common/device_setup_plans/robot_load_unload.py | 4 ++-- src/mx_bluesky/hyperion/blueapi_config.yaml | 3 +-- src/mx_bluesky/hyperion/blueapi_dev_config.yaml | 4 ++-- .../device_setup_plans/test_robot_load_unload.py | 10 +++++++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/mx_bluesky/common/device_setup_plans/robot_load_unload.py b/src/mx_bluesky/common/device_setup_plans/robot_load_unload.py index ca01a5779b..868fed66ab 100644 --- a/src/mx_bluesky/common/device_setup_plans/robot_load_unload.py +++ b/src/mx_bluesky/common/device_setup_plans/robot_load_unload.py @@ -5,7 +5,7 @@ from bluesky.utils import MsgGenerator from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue from dodal.devices.motors import XYZStage -from dodal.devices.robot import BartRobot +from dodal.devices.robot import SAMPLE_LOCATION_EMPTY, BartRobot from dodal.devices.smargon import CombinedMove, Smargon, StubPosition from dodal.plan_stubs.motor_utils import MoveTooLargeError, home_and_reset_wrapper @@ -117,7 +117,7 @@ def do_robot_unload_and_send_to_ispyb(): yield from bps.save() def _unload(): - yield from bps.abs_set(robot, None, wait=True) + yield from bps.abs_set(robot, SAMPLE_LOCATION_EMPTY, wait=True) yield from wait_for_smargon_not_disabled(smargon) gonio_finished = yield from do_plan_while_lower_gonio_at_home( diff --git a/src/mx_bluesky/hyperion/blueapi_config.yaml b/src/mx_bluesky/hyperion/blueapi_config.yaml index 16fc8a1327..399082b485 100644 --- a/src/mx_bluesky/hyperion/blueapi_config.yaml +++ b/src/mx_bluesky/hyperion/blueapi_config.yaml @@ -1,6 +1,6 @@ env: sources: - - kind: dodal + - kind: deviceManager module: dodal.beamlines.i03 - kind: planFunctions module: mx_bluesky.hyperion.experiment_plans.load_centre_collect_full_plan @@ -16,4 +16,3 @@ logging: graylog: url: "tcp://graylog-log-target.diamond.ac.uk:12232" enabled: true - diff --git a/src/mx_bluesky/hyperion/blueapi_dev_config.yaml b/src/mx_bluesky/hyperion/blueapi_dev_config.yaml index 3a1d460f62..16ac4e3eb5 100644 --- a/src/mx_bluesky/hyperion/blueapi_dev_config.yaml +++ b/src/mx_bluesky/hyperion/blueapi_dev_config.yaml @@ -1,6 +1,6 @@ env: sources: - - kind: dodal + - kind: deviceManager module: dodal.beamlines.i03 mock: true - kind: planFunctions @@ -14,4 +14,4 @@ api: origins: - "*" logging: - level: DEBUG \ No newline at end of file + level: DEBUG diff --git a/tests/unit_tests/common/device_setup_plans/test_robot_load_unload.py b/tests/unit_tests/common/device_setup_plans/test_robot_load_unload.py index 1c5289f82f..f4de244c98 100644 --- a/tests/unit_tests/common/device_setup_plans/test_robot_load_unload.py +++ b/tests/unit_tests/common/device_setup_plans/test_robot_load_unload.py @@ -7,7 +7,7 @@ from bluesky.utils import Msg from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue from dodal.devices.motors import XYZStage -from dodal.devices.robot import BartRobot +from dodal.devices.robot import SAMPLE_LOCATION_EMPTY, BartRobot from dodal.devices.smargon import CombinedMove, Smargon, StubPosition from ophyd_async.core import get_mock_put, set_mock_value @@ -87,7 +87,9 @@ async def test_when_robot_unload_called_then_sample_area_prepared_before_load( assert_message_and_return_remaining( msgs, - lambda msg: msg.command == "set" and msg.obj is robot and msg.args[0] is None, + lambda msg: msg.command == "set" + and msg.obj is robot + and msg.args[0] == SAMPLE_LOCATION_EMPTY, ) assert_message_and_return_remaining( msgs, @@ -134,7 +136,9 @@ def locate_gonio(_): msgs = assert_message_and_return_remaining( msgs, - lambda msg: msg.command == "set" and msg.obj is robot and msg.args[0] is None, + lambda msg: msg.command == "set" + and msg.obj is robot + and msg.args[0] == SAMPLE_LOCATION_EMPTY, ) msgs = assert_message_and_return_remaining( From a6024692a114b87cb1cc4e5c30dd946e679efedf Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Fri, 5 Dec 2025 13:16:36 +0000 Subject: [PATCH 5/9] Pin dodal --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f22c5df38b..1f6a976739 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ dependencies = [ "ophyd >= 1.10.5", "ophyd-async >= 0.14.0", "bluesky >= 1.14.6", - "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@main", + "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@a946a46ffd418ea25fbae9134954659cfa223d6f", ] From cf5b103975cb8f9f0429a3a9cabbabed22d7da44 Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Fri, 5 Dec 2025 17:04:21 +0000 Subject: [PATCH 6/9] Make docs build happy --- docs/developer/hyperion/hyperion-blueapi.rst | 6 +++--- docs/developer/hyperion/index.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/developer/hyperion/hyperion-blueapi.rst b/docs/developer/hyperion/hyperion-blueapi.rst index 0c4bee4350..b4fe1e082a 100644 --- a/docs/developer/hyperion/hyperion-blueapi.rst +++ b/docs/developer/hyperion/hyperion-blueapi.rst @@ -16,9 +16,9 @@ Hyperion on BlueAPI consists of two components: collection. * hyperion-supervisor: This will be a separate service that is responsible for fetching instructions from -Agamemnon, decoding them and sending corresponding requests to ``hyperion-blueapi`` for execution. The supervisor -also monitors the state of ``hyperion-blueapi``, manages the Hyperion baton and provides endpoints for status -monitoring. + Agamemnon, decoding them and sending corresponding requests to ``hyperion-blueapi`` for execution. The supervisor + also monitors the state of ``hyperion-blueapi``, manages the Hyperion baton and provides endpoints for status + monitoring. Deployment ---------- diff --git a/docs/developer/hyperion/index.rst b/docs/developer/hyperion/index.rst index 81c1f9507e..a6f542affe 100644 --- a/docs/developer/hyperion/index.rst +++ b/docs/developer/hyperion/index.rst @@ -17,6 +17,7 @@ Documentation specific for the Hyperion module within MX-Bluesky reference/param-hierarchy reference/coordinate-systems system-tests + hyperion-blueapi +++ From 44c615094e372a7d3c6f5923bdb7d0b489080d58 Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Mon, 8 Dec 2025 14:20:15 +0000 Subject: [PATCH 7/9] Additional unit tests for blueapi plans --- .../unit_tests/hyperion/test_blueapi_plans.py | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 tests/unit_tests/hyperion/test_blueapi_plans.py diff --git a/tests/unit_tests/hyperion/test_blueapi_plans.py b/tests/unit_tests/hyperion/test_blueapi_plans.py new file mode 100644 index 0000000000..3e226c32c0 --- /dev/null +++ b/tests/unit_tests/hyperion/test_blueapi_plans.py @@ -0,0 +1,73 @@ +from pathlib import Path +from unittest.mock import patch + +import pytest +from blueapi.config import ApplicationConfig +from blueapi.core import BlueskyContext +from blueapi.worker import Task +from bluesky import RunEngine + +from .conftest import raw_params_from_file + + +@pytest.fixture +def bluesky_context(run_engine: RunEngine): + config = ApplicationConfig( + **{ + "env": { + "sources": [ + { + "kind": "deviceManager", + "module": "dodal.beamlines.i03", + "mock": True, + }, + { + "kind": "planFunctions", + "module": "mx_bluesky.hyperion.blueapi_plans", + }, + ] + } + } + ) + yield BlueskyContext(run_engine=run_engine, configuration=config) + + +def test_load_centre_collect(bluesky_context: BlueskyContext, tmp_path: Path): + params = raw_params_from_file( + "tests/test_data/parameter_json_files/good_test_load_centre_collect_params.json", + tmp_path, + ) + _call_blueapi_plan( + bluesky_context, + "load_centre_collect", + "_load_centre_collect_full", + {"parameters": params}, + ) + + +def test_robot_unload(bluesky_context: BlueskyContext, tmp_path: Path): + _call_blueapi_plan( + bluesky_context, "robot_unload", "_robot_unload", {"visit": "cm12345-67"} + ) + + +def test_move_to_udc_default_state(bluesky_context: BlueskyContext): + _call_blueapi_plan( + bluesky_context, "move_to_udc_default_state", "_move_to_udc_default_state", {} + ) + + +def _call_blueapi_plan( + bluesky_context: BlueskyContext, + plan_name: str, + internal_name: str, + parameters: dict, +): + with patch( + f"mx_bluesky.hyperion.blueapi_plans.{internal_name}", + return_value=iter([]), + create=False, + ) as mock_plan: + Task(name=plan_name, params=parameters).do_task(bluesky_context) + + mock_plan.assert_called_once() From 575614eeb04cd4d81020b213b3c0a47488b57431 Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Tue, 9 Dec 2025 09:39:33 +0000 Subject: [PATCH 8/9] Fix incorrect blueapi config for production --- src/mx_bluesky/hyperion/blueapi_config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mx_bluesky/hyperion/blueapi_config.yaml b/src/mx_bluesky/hyperion/blueapi_config.yaml index 399082b485..da8bfcf231 100644 --- a/src/mx_bluesky/hyperion/blueapi_config.yaml +++ b/src/mx_bluesky/hyperion/blueapi_config.yaml @@ -3,7 +3,7 @@ env: - kind: deviceManager module: dodal.beamlines.i03 - kind: planFunctions - module: mx_bluesky.hyperion.experiment_plans.load_centre_collect_full_plan + module: mx_bluesky.hyperion.blueapi_plans events: broadcast_status_events: false api: From f8aacea06b1f5ca6736a95dd53de91cce2b15d1b Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Thu, 8 Jan 2026 11:50:06 +0000 Subject: [PATCH 9/9] Unpin main --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1f6a976739..f22c5df38b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ dependencies = [ "ophyd >= 1.10.5", "ophyd-async >= 0.14.0", "bluesky >= 1.14.6", - "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@a946a46ffd418ea25fbae9134954659cfa223d6f", + "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@main", ]