From e63b0a0dd57aa124fcdbc1ecd3301fca3531b0fb Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Fri, 29 May 2026 14:07:07 +0100 Subject: [PATCH 1/2] Fix doc index.rst --- docs/developer/hyperion/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/developer/hyperion/index.rst b/docs/developer/hyperion/index.rst index d71d48ace..94859846e 100644 --- a/docs/developer/hyperion/index.rst +++ b/docs/developer/hyperion/index.rst @@ -28,6 +28,7 @@ Documentation specific for the Hyperion module within MX-Bluesky .. toctree:: :caption: How-to Guides :maxdepth: 1 + how-to/debug-k8s how-to/update-panda-ioc From 8a48be909239046526b30f7b306a93337e40e97d Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Fri, 29 May 2026 14:55:26 +0100 Subject: [PATCH 2/2] Make hyperion baton user name configurable --- helm/hyperion/templates/deployment.yaml | 3 ++- src/mx_bluesky/hyperion/__main__.py | 4 +++- src/mx_bluesky/hyperion/baton_handler.py | 15 ++++++++---- src/mx_bluesky/hyperion/parameters/cli.py | 7 ++++++ tests/conftest.py | 6 ++--- tests/system_tests/hyperion/test_main_udc.py | 6 ++--- .../unit_tests/hyperion/test_baton_handler.py | 24 +++++++++---------- tests/unit_tests/hyperion/test_main_system.py | 18 ++++++++++++-- 8 files changed, 56 insertions(+), 27 deletions(-) diff --git a/helm/hyperion/templates/deployment.yaml b/helm/hyperion/templates/deployment.yaml index 9ce64f3f4..2e45c9a53 100644 --- a/helm/hyperion/templates/deployment.yaml +++ b/helm/hyperion/templates/deployment.yaml @@ -59,7 +59,8 @@ spec: . /app/mx-bluesky/.venv/bin/activate hyperion --mode supervisor \ --client-config /etc/hyperion/client_config.yaml \ - --supervisor-config /etc/hyperion/supervisor_config.yaml + --supervisor-config /etc/hyperion/supervisor_config.yaml \ + --baton-name HyperionK8s \ {{- if .Values.supervisor.enableDebugging }} \ --debug-port {{ .Values.supervisor.debugPort }} {{- end }} diff --git a/src/mx_bluesky/hyperion/__main__.py b/src/mx_bluesky/hyperion/__main__.py index f20f049f7..a79a0bf73 100755 --- a/src/mx_bluesky/hyperion/__main__.py +++ b/src/mx_bluesky/hyperion/__main__.py @@ -14,7 +14,7 @@ LOGGER, do_default_logging_setup, ) -from mx_bluesky.hyperion.baton_handler import run_forever +from mx_bluesky.hyperion.baton_handler import run_forever, set_hyperion_baton_user from mx_bluesky.hyperion.in_process_runner import InProcessRunner from mx_bluesky.hyperion.parameters.cli import ( HyperionArgs, @@ -40,6 +40,8 @@ def initialise_globals(args: HyperionArgs): LOGGER.info(f"Hyperion launched with args:{argv}") if args.debug_port: enable_debugging(args.wait_for_debug_attach, args.debug_port) + # Apply a different name for our baton + set_hyperion_baton_user(args.baton_name) alerting.set_alerting_service(LoggingAlertService(CONST.GRAYLOG_STREAM_ID)) diff --git a/src/mx_bluesky/hyperion/baton_handler.py b/src/mx_bluesky/hyperion/baton_handler.py index b5d10e8ce..996cb1624 100644 --- a/src/mx_bluesky/hyperion/baton_handler.py +++ b/src/mx_bluesky/hyperion/baton_handler.py @@ -36,11 +36,16 @@ setup_devices, ) -HYPERION_USER = "Hyperion" +_hyperion_baton_user = "Hyperion" NO_USER = "None" COUNTDOWN_THRESHOLD_SECONDS = 600 +def set_hyperion_baton_user(new_user: str): + global _hyperion_baton_user + _hyperion_baton_user = new_user + + def run_forever(runner: PlanRunner): try: while True: @@ -77,7 +82,7 @@ def run_udc_when_requested(context: BlueskyContext, runner: PlanRunner): def acquire_baton() -> MsgGenerator: yield from _wait_for_hyperion_requested(baton) LOGGER.debug("Hyperion is now current baton holder.") - yield from bps.abs_set(baton.current_user, HYPERION_USER) + yield from bps.abs_set(baton.current_user, _hyperion_baton_user) def collect() -> MsgGenerator: """ @@ -161,7 +166,7 @@ def _wait_for_hyperion_requested(baton: Baton): sleep_per_check = 0.1 while True: requested_user = yield from bps.rd(baton.requested_user) - if requested_user == HYPERION_USER: + if requested_user == _hyperion_baton_user: LOGGER.debug("Baton requested for Hyperion") break yield from bps.sleep(sleep_per_check) @@ -205,7 +210,7 @@ def _raise_udc_completed_alert(alert_service: AlertService): def _is_requesting_baton(baton: Baton) -> MsgGenerator: requested_user = yield from bps.rd(baton.requested_user) - return requested_user == HYPERION_USER + return requested_user == _hyperion_baton_user def _get_baton(context: BlueskyContext) -> Baton: @@ -224,7 +229,7 @@ def _unrequest_baton(baton: Baton) -> MsgGenerator[str]: The previously requested user, or NO_USER if no user was already requested. """ requested_user = yield from bps.rd(baton.requested_user) - if requested_user == HYPERION_USER: + if requested_user == _hyperion_baton_user: LOGGER.debug("Hyperion no longer requesting baton") yield from bps.abs_set(baton.requested_user, NO_USER) return NO_USER diff --git a/src/mx_bluesky/hyperion/parameters/cli.py b/src/mx_bluesky/hyperion/parameters/cli.py index 04c7723ce..fc16ccc6e 100644 --- a/src/mx_bluesky/hyperion/parameters/cli.py +++ b/src/mx_bluesky/hyperion/parameters/cli.py @@ -25,6 +25,7 @@ class HyperionArgs(CommonArgs): mode: HyperionMode = HyperionMode.UDC client_config: str | None = None supervisor_config: str | None = None + baton_name: str = "Hyperion" @dataclass(kw_only=True) @@ -99,6 +100,11 @@ def parse_cli_args() -> HyperionArgs: "--supervisor-config", help="Specify the supervisor bluesky context configuration file.", ) + parser.add_argument( + "--baton-name", + default="Hyperion", + help="Specify the baton string that identifies this instance of hyperion", + ) args = parser.parse_args() return HyperionArgs( dev_mode=args.dev or False, @@ -107,4 +113,5 @@ def parse_cli_args() -> HyperionArgs: client_config=args.client_config, debug_port=args.debug_port, wait_for_debug_attach=args.wait_for_attach, + baton_name=args.baton_name, ) diff --git a/tests/conftest.py b/tests/conftest.py index 5b34e9b5c..b75af42f4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -94,7 +94,7 @@ _get_logging_dirs, do_default_logging_setup, ) -from mx_bluesky.hyperion.baton_handler import HYPERION_USER +from mx_bluesky.hyperion.baton_handler import _hyperion_baton_user from mx_bluesky.hyperion.parameters.device_composites import ( HyperionFlyScanXRayCentreComposite, ) @@ -411,8 +411,8 @@ def backlight(): @pytest.fixture def baton(): baton = i03.baton.build(connect_immediately=True, mock=True) - set_mock_value(baton.requested_user, HYPERION_USER) - set_mock_value(baton.current_user, HYPERION_USER) + set_mock_value(baton.requested_user, _hyperion_baton_user) + set_mock_value(baton.current_user, _hyperion_baton_user) return baton diff --git a/tests/system_tests/hyperion/test_main_udc.py b/tests/system_tests/hyperion/test_main_udc.py index c4b699c01..7c8f4cf88 100644 --- a/tests/system_tests/hyperion/test_main_udc.py +++ b/tests/system_tests/hyperion/test_main_udc.py @@ -7,7 +7,7 @@ from mx_bluesky.common.utils.context import find_device_in_context from mx_bluesky.hyperion.__main__ import main -from mx_bluesky.hyperion.baton_handler import HYPERION_USER +from mx_bluesky.hyperion.baton_handler import _hyperion_baton_user @pytest.fixture(autouse=True) @@ -17,7 +17,7 @@ def patch_setup_devices(): def patched_setup_devices(context: BlueskyContext, dev_mode: bool): setup_devices(context, True) # reapply requested user to the newly created fake baton - baton_with_requested_user(context, HYPERION_USER) + baton_with_requested_user(context, _hyperion_baton_user) # Patch setup_devices to patch the baton again when it is re-created with ( @@ -40,7 +40,7 @@ def patch_udc_default_state(): def baton_with_requested_user( - bluesky_context: BlueskyContext, user: str = HYPERION_USER + bluesky_context: BlueskyContext, user: str = _hyperion_baton_user ) -> Baton: baton = find_device_in_context(bluesky_context, "baton", Baton) set_mock_value(baton.requested_user, user) diff --git a/tests/unit_tests/hyperion/test_baton_handler.py b/tests/unit_tests/hyperion/test_baton_handler.py index b4c4878e0..d9453b17a 100644 --- a/tests/unit_tests/hyperion/test_baton_handler.py +++ b/tests/unit_tests/hyperion/test_baton_handler.py @@ -37,8 +37,8 @@ from mx_bluesky.common.utils.log import LOGGER from mx_bluesky.hyperion._plan_runner_params import RobotUnload, UDCCleanup, Wait from mx_bluesky.hyperion.baton_handler import ( - HYPERION_USER, NO_USER, + _hyperion_baton_user, _initialise_udc, run_forever, run_udc_when_requested, @@ -84,7 +84,7 @@ def patched_setup_devices(context: BlueskyContext, dev_mode: bool): except ValueError: # No baton, setup devices and configure it setup_devices(context, True) - baton_with_requested_user(context, HYPERION_USER) + baton_with_requested_user(context, _hyperion_baton_user) with ( patch( @@ -140,13 +140,13 @@ def mock_load_module(module, **kwargs): mock=True, ) synchrotron_with_countdown(context) - baton_with_requested_user(context, HYPERION_USER) + baton_with_requested_user(context, _hyperion_baton_user) yield context @pytest.fixture def bluesky_context_with_sim_run_engine(sim_run_engine: RunEngineSimulator): - baton_requested_user = HYPERION_USER + baton_requested_user = _hyperion_baton_user countdown = 1200 # Baton for sim run engine @@ -224,7 +224,7 @@ def single_collection_agamemnon_request_then_wait_forever( def baton_with_requested_user( - bluesky_context: BlueskyContext, user: str = HYPERION_USER + bluesky_context: BlueskyContext, user: str = _hyperion_baton_user ) -> Baton: baton = find_device_in_context(bluesky_context, "baton", Baton) set_mock_value(baton.requested_user, user) @@ -280,7 +280,7 @@ def test_loop_until_hyperion_requested( def set_hyperion_requested(*args): yield from bps.null() - set_mock_value(baton.requested_user, HYPERION_USER) + set_mock_value(baton.requested_user, _hyperion_baton_user) mock_calls: list[Any] = [MagicMock()] * (number_of_sleep_calls - 1) mock_calls.append(set_hyperion_requested()) @@ -303,7 +303,7 @@ def test_when_hyperion_requested_then_hyperion_set_to_current_user( run_udc_when_requested(bluesky_context, udc_runner) - assert get_mock_put(baton.current_user).mock_calls[0] == call(HYPERION_USER) + assert get_mock_put(baton.current_user).mock_calls[0] == call(_hyperion_baton_user) @patch("mx_bluesky.hyperion.in_process_runner.move_to_udc_default_state") @@ -325,7 +325,7 @@ def test_when_hyperion_requested_then_default_state_and_collection_run( async def _assert_baton_released(baton: Baton): - assert await baton.requested_user.get_value() != HYPERION_USER + assert await baton.requested_user.get_value() != _hyperion_baton_user assert get_mock_put(baton.current_user).mock_calls[-1] == call(NO_USER) @@ -608,7 +608,7 @@ async def take_requested_baton_away_then_wait_for_release_then_re_request(): await sleep(SLEEP_FAST_SPIN_WAIT_S) assert len(mock_create_parameters_from_agamemnon.mock_calls) == 1 # Re-request baton, wait until hyperion picks up baton - await baton.requested_user.set(HYPERION_USER) + await baton.requested_user.set(_hyperion_baton_user) while udc_runner.current_status != Status.BUSY: await sleep(SLEEP_FAST_SPIN_WAIT_S) finally: @@ -651,7 +651,7 @@ async def wait_for_baton_release_then_re_request(): while await baton.current_user.get_value() != NO_USER: await sleep(SLEEP_FAST_SPIN_WAIT_S) assert len(mock_create_parameters_from_agamemnon.mock_calls) == 2 - await baton.requested_user.set(HYPERION_USER) + await baton.requested_user.set(_hyperion_baton_user) while udc_runner.current_status != Status.BUSY: await sleep(SLEEP_FAST_SPIN_WAIT_S) finally: @@ -727,7 +727,7 @@ async def error_with_command_then_resume(): baton = find_device_in_context(udc_runner.context, "baton", Baton) while await baton.current_user.get_value() != NO_USER: await sleep(SLEEP_FAST_SPIN_WAIT_S) - await baton.requested_user.set(HYPERION_USER) + await baton.requested_user.set(_hyperion_baton_user) while udc_runner.current_status != Status.BUSY: # type: ignore await sleep(SLEEP_FAST_SPIN_WAIT_S) finally: @@ -773,7 +773,7 @@ async def release_baton_and_check_commissioning_signal_set(): try: mock_set_commissioning_signal.assert_not_called() LOGGER.debug("Set requested user") - set_mock_value(baton.requested_user, HYPERION_USER) + set_mock_value(baton.requested_user, _hyperion_baton_user) LOGGER.debug("Wait for create_parameters call") while len(parent.create_parameters_from_agamemnon.mock_calls) == 0: await sleep(SLEEP_FAST_SPIN_WAIT_S) diff --git a/tests/unit_tests/hyperion/test_main_system.py b/tests/unit_tests/hyperion/test_main_system.py index f859a91a1..af3a26793 100644 --- a/tests/unit_tests/hyperion/test_main_system.py +++ b/tests/unit_tests/hyperion/test_main_system.py @@ -24,7 +24,7 @@ initialise_globals, main, ) -from mx_bluesky.hyperion.baton_handler import HYPERION_USER +from mx_bluesky.hyperion.baton_handler import _hyperion_baton_user from mx_bluesky.hyperion.parameters.cli import ( HyperionArgs, HyperionMode, @@ -180,6 +180,20 @@ def test_hyperion_does_not_enable_debugging_if_not_specified( mock_enable_debugging.assert_not_called() +@patch("sys.argv", new=["hyperion", "--mode", "udc", "--baton-name", "HyperionK8s"]) +@patch("mx_bluesky.hyperion.__main__.do_default_logging_setup", MagicMock()) +@patch("mx_bluesky.hyperion.__main__.create_server_for_udc", MagicMock()) +@patch("mx_bluesky.hyperion.__main__.run_forever", MagicMock()) +@patch("mx_bluesky.hyperion.__main__.set_hyperion_baton_user") +def test_hyperion_sets_hyperion_baton_user_if_specified( + mock_set_baton_user: MagicMock, + mock_setup_context: MagicMock, +): + main() + + mock_set_baton_user.assert_called_once_with("HyperionK8s") + + @patch("sys.argv", new=["hyperion", "--mode", "udc"]) @patch("mx_bluesky.hyperion.__main__.do_default_logging_setup", MagicMock()) @patch("mx_bluesky.hyperion.__main__.run_forever", MagicMock()) @@ -308,7 +322,7 @@ def wait_for_udc_to_start_then_send_sigterm(): context = plan_runner.context baton = find_device_in_context(context, "baton", Baton) synchrotron = find_device_in_context(context, "synchrotron", Synchrotron) - set_mock_value(baton.requested_user, HYPERION_USER) + set_mock_value(baton.requested_user, _hyperion_baton_user) set_mock_value(synchrotron.machine_user_countdown, 1200) while len(mock_create_parameters_from_agamemnon.mock_calls) == 0: sleep(0.2)