diff --git a/setup.cfg b/setup.cfg index 9361b9300..6c6872156 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,7 +35,7 @@ install_requires = xarray doct databroker - dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@a8034f3e170720cb7078a2d08871c5cb06b8cc54 + dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@32b5cbdb635c270491e8cad2274f238f67702ce5 pydantic<2.0 # See https://github.com/DiamondLightSource/hyperion/issues/774 scipy pyzmq<25 # See https://github.com/DiamondLightSource/hyperion/issues/1103 diff --git a/src/hyperion/device_setup_plans/check_topup.py b/src/hyperion/device_setup_plans/check_topup.py deleted file mode 100644 index aa75f76c5..000000000 --- a/src/hyperion/device_setup_plans/check_topup.py +++ /dev/null @@ -1,81 +0,0 @@ -import bluesky.plan_stubs as bps -from dodal.devices.synchrotron import Synchrotron, SynchrotronMode - -from hyperion.log import LOGGER - -ALLOWED_MODES = [SynchrotronMode.USER.value, SynchrotronMode.SPECIAL.value] -DECAY_MODE_COUNTDOWN = -1 # Value of the start_countdown PV when in decay mode -COUNTDOWN_DURING_TOPUP = 0 - - -def _in_decay_mode(time_to_topup): - if time_to_topup == DECAY_MODE_COUNTDOWN: - LOGGER.info("Machine in decay mode, gating disabled") - return True - return False - - -def _gating_permitted(machine_mode): - if machine_mode in ALLOWED_MODES: - LOGGER.info("Machine in allowed mode, gating top up enabled.") - return True - LOGGER.info("Machine not in allowed mode, gating disabled") - return False - - -def _delay_to_avoid_topup(total_run_time, time_to_topup): - if total_run_time > time_to_topup: - LOGGER.info( - """ - Total run time for this collection exceeds time to next top up. - Collection delayed until top up done. - """ - ) - return True - LOGGER.info( - """ - Total run time less than time to next topup. Proceeding with collection. - """ - ) - return False - - -def wait_for_topup_complete(synchrotron): - LOGGER.info("Waiting for topup to complete") - start = yield from bps.rd(synchrotron.top_up.start_countdown) - while start == COUNTDOWN_DURING_TOPUP: - yield from bps.sleep(0.1) - start = yield from bps.rd(synchrotron.top_up.start_countdown) - - -def check_topup_and_wait_if_necessary( - synchrotron: Synchrotron, - total_exposure_time: float, - ops_time: float, # Account for xray centering, rotation speed, etc -): # See https://github.com/DiamondLightSource/hyperion/issues/932 - """A small plan to check if topup gating is permitted and sleep until the topup\ - is over if it starts before the end of collection. - - Args: - synchrotron (Synchrotron): Synchrotron device. - total_exposure_time (float): Expected total exposure time for \ - collection, in seconds. - ops_time (float): Additional time to account for various operations,\ - eg. x-ray centering, in seconds. Defaults to 30.0. - """ - machine_mode = yield from bps.rd(synchrotron.machine_status.synchrotron_mode) - time_to_topup = yield from bps.rd(synchrotron.top_up.start_countdown) - if _in_decay_mode(time_to_topup) or not _gating_permitted(machine_mode): - yield from bps.null() - return - tot_run_time = total_exposure_time + ops_time - end_topup = yield from bps.rd(synchrotron.top_up.end_countdown) - time_to_wait = ( - end_topup if _delay_to_avoid_topup(tot_run_time, time_to_topup) else 0.0 - ) - - yield from bps.sleep(time_to_wait) - - check_start = yield from bps.rd(synchrotron.top_up.start_countdown) - if check_start == COUNTDOWN_DURING_TOPUP: - yield from wait_for_topup_complete(synchrotron) diff --git a/src/hyperion/device_setup_plans/read_hardware_for_setup.py b/src/hyperion/device_setup_plans/read_hardware_for_setup.py index bee40971f..9c509e02e 100644 --- a/src/hyperion/device_setup_plans/read_hardware_for_setup.py +++ b/src/hyperion/device_setup_plans/read_hardware_for_setup.py @@ -27,7 +27,7 @@ def read_hardware_for_ispyb_pre_collection( name=CONST.PLAN.ISPYB_HARDWARE_READ ) # gives name to event *descriptor* document yield from bps.read(undulator.current_gap) - yield from bps.read(synchrotron.machine_status.synchrotron_mode) + yield from bps.read(synchrotron.synchrotron_mode) yield from bps.read(s4_slit_gaps.xgap) yield from bps.read(s4_slit_gaps.ygap) yield from bps.read(aperture_scatterguard) diff --git a/src/hyperion/experiment_plans/flyscan_xray_centre_plan.py b/src/hyperion/experiment_plans/flyscan_xray_centre_plan.py index 42bf26307..10df97719 100755 --- a/src/hyperion/experiment_plans/flyscan_xray_centre_plan.py +++ b/src/hyperion/experiment_plans/flyscan_xray_centre_plan.py @@ -32,9 +32,9 @@ ZocaloResults, get_processing_result, ) +from dodal.plans.check_topup import check_topup_and_wait_if_necessary from ophyd_async.panda import PandA -from hyperion.device_setup_plans.check_topup import check_topup_and_wait_if_necessary from hyperion.device_setup_plans.manipulate_sample import move_x_y_z from hyperion.device_setup_plans.read_hardware_for_setup import ( read_hardware_for_ispyb_during_collection, diff --git a/src/hyperion/experiment_plans/rotation_scan_plan.py b/src/hyperion/experiment_plans/rotation_scan_plan.py index fa15cbf9e..3c5c0dda6 100644 --- a/src/hyperion/experiment_plans/rotation_scan_plan.py +++ b/src/hyperion/experiment_plans/rotation_scan_plan.py @@ -20,8 +20,8 @@ from dodal.devices.synchrotron import Synchrotron from dodal.devices.undulator import Undulator from dodal.devices.zebra import RotationDirection, Zebra +from dodal.plans.check_topup import check_topup_and_wait_if_necessary -from hyperion.device_setup_plans.check_topup import check_topup_and_wait_if_necessary from hyperion.device_setup_plans.manipulate_sample import ( cleanup_sample_environment, move_x_y_z, diff --git a/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py b/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py index 30e9eda1b..5f04681e3 100644 --- a/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py +++ b/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py @@ -82,7 +82,7 @@ def activity_gated_event(self, doc: Event) -> Event: "undulator_current_gap" ] self.params.hyperion_params.ispyb_params.synchrotron_mode = doc["data"][ - "synchrotron_machine_status_synchrotron_mode" + "synchrotron-synchrotron_mode" ] self.params.hyperion_params.ispyb_params.slit_gap_size_x = doc["data"][ "s4_slit_gaps_xgap" diff --git a/tests/conftest.py b/tests/conftest.py index ad456702d..16c474a92 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -25,7 +25,7 @@ from dodal.devices.robot import BartRobot from dodal.devices.s4_slit_gaps import S4SlitGaps from dodal.devices.smargon import Smargon -from dodal.devices.synchrotron import Synchrotron +from dodal.devices.synchrotron import Synchrotron, SynchrotronMode from dodal.devices.undulator import Undulator from dodal.devices.zebra import Zebra from dodal.log import LOGGER as dodal_logger @@ -270,7 +270,11 @@ def s4_slit_gaps(): @pytest.fixture def synchrotron(): - return i03.synchrotron(fake_with_ophyd_sim=True) + RunEngine() # A RE is needed to start the bluesky loop + synchrotron = i03.synchrotron(fake_with_ophyd_sim=True) + set_sim_value(synchrotron.synchrotron_mode, SynchrotronMode.USER) + set_sim_value(synchrotron.topup_start_countdown, 10) + return synchrotron @pytest.fixture diff --git a/tests/system_tests/experiment_plans/test_plan_system.py b/tests/system_tests/experiment_plans/test_plan_system.py index 86e7c4781..8a1d9aeeb 100644 --- a/tests/system_tests/experiment_plans/test_plan_system.py +++ b/tests/system_tests/experiment_plans/test_plan_system.py @@ -14,7 +14,8 @@ @pytest.mark.s03 -def test_getting_data_for_ispyb(): +@pytest.mark.asyncio +async def test_getting_data_for_ispyb(): undulator = Undulator( f"{CONST.SIM.INSERTION_PREFIX}-MO-SERVC-01:", name="undulator" ) @@ -28,7 +29,7 @@ def test_getting_data_for_ispyb(): ) undulator.wait_for_connection() - synchrotron.wait_for_connection() + await synchrotron.connect() slit_gaps.wait_for_connection() attenuator.wait_for_connection() flux.wait_for_connection() diff --git a/tests/unit_tests/device_setup_plans/test_topup_plan.py b/tests/unit_tests/device_setup_plans/test_topup_plan.py deleted file mode 100644 index 43923a9b0..000000000 --- a/tests/unit_tests/device_setup_plans/test_topup_plan.py +++ /dev/null @@ -1,93 +0,0 @@ -from unittest.mock import patch - -import bluesky.plan_stubs as bps -import pytest -from bluesky.run_engine import RunEngine -from dodal.beamlines import i03 -from dodal.devices.synchrotron import Synchrotron, SynchrotronMode - -from hyperion.device_setup_plans.check_topup import ( - check_topup_and_wait_if_necessary, - wait_for_topup_complete, -) - - -@pytest.fixture -def synchrotron() -> Synchrotron: - return i03.synchrotron(fake_with_ophyd_sim=True) - - -@patch("hyperion.device_setup_plans.check_topup.wait_for_topup_complete") -@patch("hyperion.device_setup_plans.check_topup.bps.sleep") -def test_when_topup_before_end_of_collection_wait( - fake_sleep, fake_wait, synchrotron: Synchrotron -): - synchrotron.machine_status.synchrotron_mode.sim_put(SynchrotronMode.USER.value) # type: ignore - synchrotron.top_up.start_countdown.sim_put(20.0) # type: ignore - synchrotron.top_up.end_countdown.sim_put(60.0) # type: ignore - - RE = RunEngine() - RE( - check_topup_and_wait_if_necessary( - synchrotron=synchrotron, - total_exposure_time=40.0, - ops_time=30.0, - ) - ) - fake_sleep.assert_called_once_with(60.0) - - -@patch("hyperion.device_setup_plans.check_topup.bps.rd") -@patch("hyperion.device_setup_plans.check_topup.bps.sleep") -def test_wait_for_topup_complete(fake_sleep, fake_rd, synchrotron): - def fake_generator(value): - yield from bps.null() - return value - - fake_rd.side_effect = [ - fake_generator(0.0), - fake_generator(0.0), - fake_generator(0.0), - fake_generator(10.0), - ] - - RE = RunEngine() - RE(wait_for_topup_complete(synchrotron)) - - assert fake_sleep.call_count == 3 - fake_sleep.assert_called_with(0.1) - - -@patch("hyperion.device_setup_plans.check_topup.bps.sleep") -@patch("hyperion.device_setup_plans.check_topup.bps.null") -def test_no_waiting_if_decay_mode(fake_null, fake_sleep, synchrotron: Synchrotron): - synchrotron.top_up.start_countdown.sim_put(-1) # type: ignore - - RE = RunEngine() - RE( - check_topup_and_wait_if_necessary( - synchrotron=synchrotron, - total_exposure_time=10.0, - ops_time=1.0, - ) - ) - fake_null.assert_called_once() - assert fake_sleep.call_count == 0 - - -@patch("hyperion.device_setup_plans.check_topup.bps.null") -def test_no_waiting_when_mode_does_not_allow_gating( - fake_null, synchrotron: Synchrotron -): - synchrotron.top_up.start_countdown.sim_put(1.0) # type: ignore - synchrotron.machine_status.synchrotron_mode.sim_put(SynchrotronMode.SHUTDOWN.value) # type: ignore - - RE = RunEngine() - RE( - check_topup_and_wait_if_necessary( - synchrotron=synchrotron, - total_exposure_time=10.0, - ops_time=1.0, - ) - ) - fake_null.assert_called_once() diff --git a/tests/unit_tests/experiment_plans/conftest.py b/tests/unit_tests/experiment_plans/conftest.py index a71e70ebc..862937883 100644 --- a/tests/unit_tests/experiment_plans/conftest.py +++ b/tests/unit_tests/experiment_plans/conftest.py @@ -44,7 +44,7 @@ def make_event_doc(data, descriptor="abc123") -> Event: BASIC_PRE_SETUP_DOC = { "undulator_current_gap": 0, "undulator_gap": 0, - "synchrotron_machine_status_synchrotron_mode": 0, + "synchrotron-synchrotron_mode": 0, "s4_slit_gaps_xgap": 0, "s4_slit_gaps_ygap": 0, "robot-barcode": "BARCODE", diff --git a/tests/unit_tests/experiment_plans/test_flyscan_xray_centre_plan.py b/tests/unit_tests/experiment_plans/test_flyscan_xray_centre_plan.py index d1c141473..0bbf01c8a 100644 --- a/tests/unit_tests/experiment_plans/test_flyscan_xray_centre_plan.py +++ b/tests/unit_tests/experiment_plans/test_flyscan_xray_centre_plan.py @@ -16,6 +16,7 @@ EIGER_TYPE_EIGER2_X_16M, ) from dodal.devices.fast_grid_scan import FastGridScan +from dodal.devices.synchrotron import SynchrotronMode from dodal.devices.zocalo import ZocaloStartInfo from ophyd.sim import make_fake_device from ophyd.status import Status @@ -177,9 +178,9 @@ def test_read_hardware_for_ispyb_updates_from_ophyd_devices( fake_fgs_composite.undulator.current_gap.sim_put(undulator_test_value) # type: ignore - synchrotron_test_value = "test" - fake_fgs_composite.synchrotron.machine_status.synchrotron_mode.sim_put( # type: ignore - synchrotron_test_value + synchrotron_test_value = SynchrotronMode.USER + set_sim_value( + fake_fgs_composite.synchrotron.synchrotron_mode, synchrotron_test_value ) transmission_test_value = 0.01 @@ -229,7 +230,7 @@ def test_read_hardware_for_ispyb_updates_from_ophyd_devices( assert hyperion_params.ispyb_params.undulator_gap == undulator_test_value # type: ignore assert ( hyperion_params.ispyb_params.synchrotron_mode # type: ignore - == synchrotron_test_value + == synchrotron_test_value.value ) assert hyperion_params.ispyb_params.slit_gap_size_x == xgap_test_value # type: ignore assert hyperion_params.ispyb_params.slit_gap_size_y == ygap_test_value # type: ignore diff --git a/tests/unit_tests/experiment_plans/test_panda_flyscan_xray_centre_plan.py b/tests/unit_tests/experiment_plans/test_panda_flyscan_xray_centre_plan.py index 36b583158..227c4cf08 100644 --- a/tests/unit_tests/experiment_plans/test_panda_flyscan_xray_centre_plan.py +++ b/tests/unit_tests/experiment_plans/test_panda_flyscan_xray_centre_plan.py @@ -13,6 +13,7 @@ EIGER_TYPE_EIGER2_X_16M, ) from dodal.devices.panda_fast_grid_scan import PandAFastGridScan +from dodal.devices.synchrotron import SynchrotronMode from ophyd.sim import make_fake_device from ophyd.status import Status from ophyd_async.core import set_sim_value @@ -140,9 +141,9 @@ def test_read_hardware_for_ispyb_updates_from_ophyd_devices( fake_fgs_composite.undulator.current_gap.sim_put(undulator_test_value) # type: ignore - synchrotron_test_value = "test" - fake_fgs_composite.synchrotron.machine_status.synchrotron_mode.sim_put( # type: ignore - synchrotron_test_value + synchrotron_test_value = SynchrotronMode.USER + set_sim_value( + fake_fgs_composite.synchrotron.synchrotron_mode, synchrotron_test_value ) transmission_test_value = 0.01 @@ -184,7 +185,7 @@ def test_read_hardware_for_ispyb_updates_from_ophyd_devices( assert params.hyperion_params.ispyb_params.undulator_gap == undulator_test_value # type: ignore assert ( params.hyperion_params.ispyb_params.synchrotron_mode # type: ignore - == synchrotron_test_value + == synchrotron_test_value.value ) assert params.hyperion_params.ispyb_params.slit_gap_size_x == xgap_test_value # type: ignore assert params.hyperion_params.ispyb_params.slit_gap_size_y == ygap_test_value # type: ignore diff --git a/tests/unit_tests/experiment_plans/test_pin_centre_then_xray_centre_plan.py b/tests/unit_tests/experiment_plans/test_pin_centre_then_xray_centre_plan.py index 82c116465..82f0bba64 100644 --- a/tests/unit_tests/experiment_plans/test_pin_centre_then_xray_centre_plan.py +++ b/tests/unit_tests/experiment_plans/test_pin_centre_then_xray_centre_plan.py @@ -5,6 +5,7 @@ from bluesky.utils import Msg from dodal.devices.detector.detector_motion import ShutterState from dodal.devices.fast_grid_scan import GridScanParams +from dodal.devices.synchrotron import SynchrotronMode from hyperion.experiment_plans.pin_centre_then_xray_centre_plan import ( create_parameters_for_grid_detection, @@ -122,6 +123,12 @@ def test_when_pin_centre_xray_centre_called_then_detector_positioned( add_simple_pin_tip_centre_handlers(sim_run_engine) add_simple_oav_mxsc_callback_handlers(sim_run_engine) + sim_run_engine.add_handler( + "read", + "synchrotron-synchrotron_mode", + lambda msg_: {"values": {"value": SynchrotronMode.SHUTDOWN}}, + ) + def add_handlers_to_simulate_detector_motion(msg: Msg): sim_run_engine.add_handler( "read", diff --git a/tests/unit_tests/external_interaction/callbacks/conftest.py b/tests/unit_tests/external_interaction/callbacks/conftest.py index 4dd6d155f..a8c01cb8d 100644 --- a/tests/unit_tests/external_interaction/callbacks/conftest.py +++ b/tests/unit_tests/external_interaction/callbacks/conftest.py @@ -67,7 +67,7 @@ class TestData: "data": { "s4_slit_gaps_xgap": 0.1234, "s4_slit_gaps_ygap": 0.2345, - "synchrotron_machine_status_synchrotron_mode": "test", + "synchrotron-synchrotron_mode": "test", "undulator_current_gap": 1.234, "robot-barcode": "BARCODE", }, @@ -138,7 +138,7 @@ class TestData: "data": { "s4_slit_gaps_xgap": 0.1234, "s4_slit_gaps_ygap": 0.2345, - "synchrotron_machine_status_synchrotron_mode": "test", + "synchrotron-synchrotron_mode": "test", "undulator_current_gap": 1.234, "robot-barcode": "BARCODE", },