diff --git a/src/i19_bluesky/parameters/components.py b/src/i19_bluesky/parameters/components.py index d13bfc61..bf1bb794 100644 --- a/src/i19_bluesky/parameters/components.py +++ b/src/i19_bluesky/parameters/components.py @@ -22,7 +22,6 @@ class DetectorType(StrEnum): class VisitParameters(BaseModel): - hutch: HutchName visit: Path dataset: str filename_prefix: str diff --git a/src/i19_bluesky/parameters/devices_composites.py b/src/i19_bluesky/parameters/devices_composites.py index dd746d81..cc43a4d3 100644 --- a/src/i19_bluesky/parameters/devices_composites.py +++ b/src/i19_bluesky/parameters/devices_composites.py @@ -1,4 +1,10 @@ import pydantic +from dodal.devices.beamlines.i19.access_controlled.energy_device import ( + AccessControlledEnergyComposite, +) +from dodal.devices.beamlines.i19.access_controlled.shutter import ( + AccessControlledShutter, +) from dodal.devices.beamlines.i19.backlight import BacklightPosition from dodal.devices.beamlines.i19.diffractometer import ( FourCircleDiffractometer, @@ -20,6 +26,8 @@ class SerialCollectionEh2PandaComposite: panda: HDFPanda eiger: EigerDetector serial_stages: XYZPhiStage + shutter: AccessControlledShutter + energy_device: AccessControlledEnergyComposite @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True}) diff --git a/src/i19_bluesky/parameters/serial_parameters.py b/src/i19_bluesky/parameters/serial_parameters.py index 43d66528..7ee19149 100644 --- a/src/i19_bluesky/parameters/serial_parameters.py +++ b/src/i19_bluesky/parameters/serial_parameters.py @@ -7,6 +7,7 @@ from i19_bluesky.parameters.components import ( DetectorType, + HutchName, PandaRotationParams, RotationAxis, VisitParameters, @@ -57,6 +58,7 @@ def split_wells_per_run(self) -> list[dict]: class SerialExperimentEh2(SerialExperiment): aperture_request: PinColRequest detector_type: DetectorType + hutch: HutchName = HutchName.EH2 @property def zebra_rotation_params(self) -> ZebraRotationParams: diff --git a/src/i19_bluesky/serial/device_setup_plans/eiger_metadata.py b/src/i19_bluesky/serial/device_setup_plans/eiger_metadata.py index 2b183454..e3400445 100644 --- a/src/i19_bluesky/serial/device_setup_plans/eiger_metadata.py +++ b/src/i19_bluesky/serial/device_setup_plans/eiger_metadata.py @@ -66,7 +66,7 @@ def write_eiger_params( energy: float, wavelength: float, eiger: EigerDetector, - wait: bool, + wait: bool = True, group: str = "eiger_metadata", ): beam_centre = calculate_beam_centre_from_lut( diff --git a/src/i19_bluesky/serial/example_zebra_plans/example_trigger_plan_zebra_vs_panda.py b/src/i19_bluesky/serial/example_zebra_plans/example_trigger_plan_zebra_vs_panda.py index 8a50eb02..a0ad0053 100644 --- a/src/i19_bluesky/serial/example_zebra_plans/example_trigger_plan_zebra_vs_panda.py +++ b/src/i19_bluesky/serial/example_zebra_plans/example_trigger_plan_zebra_vs_panda.py @@ -15,13 +15,11 @@ from i19_bluesky.parameters.serial_parameters import SerialExperimentEh2 from i19_bluesky.serial.device_setup_plans.diffractometer_plans import ( move_sample_stage_back, + setup_sample_stage, ) from i19_bluesky.serial.example_zebra_plans.zebra_collection_setup_plan import ( setup_zebra_for_collection, ) -from i19_bluesky.serial.run_panda_plans.panda_serial_collection import ( - setup_sample_stage, -) RAMP = 0.5 diff --git a/src/i19_bluesky/serial/panda_setup_plans/__init__.py b/src/i19_bluesky/serial/panda_plans/__init__.py similarity index 100% rename from src/i19_bluesky/serial/panda_setup_plans/__init__.py rename to src/i19_bluesky/serial/panda_plans/__init__.py diff --git a/src/i19_bluesky/serial/panda_setup_plans/panda_setup_plans.py b/src/i19_bluesky/serial/panda_plans/panda_setup_plans.py similarity index 97% rename from src/i19_bluesky/serial/panda_setup_plans/panda_setup_plans.py rename to src/i19_bluesky/serial/panda_plans/panda_setup_plans.py index 446da5fa..45124e31 100644 --- a/src/i19_bluesky/serial/panda_setup_plans/panda_setup_plans.py +++ b/src/i19_bluesky/serial/panda_plans/panda_setup_plans.py @@ -9,7 +9,7 @@ from i19_bluesky.log import LOGGER from i19_bluesky.parameters.components import PandaRotationParams -from i19_bluesky.serial.panda_setup_plans.panda_stubs import ( +from i19_bluesky.serial.panda_plans.panda_stubs import ( DeviceSettingsConstants, arm_panda, generate_panda_seq_table, diff --git a/src/i19_bluesky/serial/panda_setup_plans/panda_stubs.py b/src/i19_bluesky/serial/panda_plans/panda_stubs.py similarity index 100% rename from src/i19_bluesky/serial/panda_setup_plans/panda_stubs.py rename to src/i19_bluesky/serial/panda_plans/panda_stubs.py diff --git a/src/i19_bluesky/serial/run_panda_plans/panda_serial_collection.py b/src/i19_bluesky/serial/run_panda_plans/panda_serial_collection.py index af0d8e44..2bd03503 100644 --- a/src/i19_bluesky/serial/run_panda_plans/panda_serial_collection.py +++ b/src/i19_bluesky/serial/run_panda_plans/panda_serial_collection.py @@ -1,28 +1,19 @@ import bluesky.plan_stubs as bps from bluesky.utils import MsgGenerator -from dodal.devices.beamlines.i19.diffractometer import ( - FourCircleDiffractometer, -) -from dodal.devices.motors import XYZPhiStage -from ophyd_async.fastcs.eiger import EigerDetector -from ophyd_async.fastcs.panda import HDFPanda from i19_bluesky.log import LOGGER from i19_bluesky.parameters.devices_composites import SerialCollectionEh2PandaComposite from i19_bluesky.parameters.serial_parameters import SerialExperimentEh2 from i19_bluesky.serial.device_setup_plans.diffractometer_plans import ( - move_sample_stage_back, move_stage_x_and_z, - setup_sample_stage, ) -from i19_bluesky.serial.panda_setup_plans.panda_setup_plans import ( - reset_panda, +from i19_bluesky.serial.panda_plans.panda_setup_plans import ( setup_panda_for_rotation, ) -from i19_bluesky.serial.panda_setup_plans.panda_stubs import arm_panda, disarm_panda +from i19_bluesky.serial.panda_plans.panda_stubs import arm_panda -def trigger_panda( +def trigger_panda_collection( parameters: SerialExperimentEh2, devices: SerialCollectionEh2PandaComposite, ) -> MsgGenerator: @@ -41,10 +32,6 @@ def trigger_panda( panda (HDFPanda): The fastcs PandA ophyd device. eiger (EigerDetector): The eiger detector device """ - yield from setup_sample_stage( - parameters.panda_rotation_params, - devices.serial_stages, - ) yield from setup_panda_for_rotation( parameters.panda_rotation_params, devices.panda, @@ -76,28 +63,3 @@ def trigger_panda( parameters.panda_rotation_params.scan_start_deg, wait=True, ) - - -def end_run( - rot_axis_start: float, - panda: HDFPanda, - eiger: EigerDetector, - serial_stages: XYZPhiStage, -): - LOGGER.info("Disarm eiger") - yield from bps.trigger(eiger.detector.disarm) - LOGGER.info("Disarm panda") - yield from disarm_panda(panda) - yield from reset_panda(panda) - yield from move_sample_stage_back(serial_stages, rot_axis_start) - - -def run_on_collection_abort( - panda: HDFPanda, - eiger: EigerDetector, - diffractometer: FourCircleDiffractometer, -) -> MsgGenerator: - LOGGER.warning("ABORT") - yield from bps.abs_set(diffractometer.phi.motor_stop, 1, wait=True) - yield from bps.trigger(eiger.detector.disarm) - yield from disarm_panda(panda) diff --git a/src/i19_bluesky/serial/run_panda_plans/run_serial_with_panda.py b/src/i19_bluesky/serial/run_panda_plans/run_serial_with_panda.py index 0bba6093..1b23c7e6 100644 --- a/src/i19_bluesky/serial/run_panda_plans/run_serial_with_panda.py +++ b/src/i19_bluesky/serial/run_panda_plans/run_serial_with_panda.py @@ -1,42 +1,70 @@ +import bluesky.plan_stubs as bps import bluesky.preprocessors as bpp from bluesky.utils import MsgGenerator from dodal.common import inject +from dodal.devices.beamlines.i19.access_controlled.shutter import ( + AccessControlledShutter, +) +from dodal.devices.beamlines.i19.diffractometer import ( + FourCircleDiffractometer, +) +from dodal.devices.motors import XYZPhiStage +from ophyd_async.fastcs.eiger import EigerDetector +from ophyd_async.fastcs.panda import HDFPanda +from i19_bluesky.log import LOGGER from i19_bluesky.parameters.devices_composites import SerialCollectionEh2PandaComposite from i19_bluesky.parameters.serial_parameters import SerialExperimentEh2 +from i19_bluesky.plans.optics_hutch_control_plans import close_experiment_shutter +from i19_bluesky.serial.device_setup_plans.diffractometer_plans import ( + move_sample_stage_back, +) +from i19_bluesky.serial.panda_plans.panda_setup_plans import reset_panda +from i19_bluesky.serial.panda_plans.panda_stubs import disarm_panda from i19_bluesky.serial.run_panda_plans.panda_serial_collection import ( - end_run, - run_on_collection_abort, - trigger_panda, + trigger_panda_collection, ) -from i19_bluesky.serial.setup_beamline_plans.setup_beamline_pre_collection import ( - setup_beamline_before_collection, +from i19_bluesky.serial.setup_beamline_plans.setup_beamline import ( + setup_eh2_serial_collection, ) -def setup_then_trigger_panda( +def main_collection_plan( parameters: SerialExperimentEh2, devices: SerialCollectionEh2PandaComposite, ) -> MsgGenerator: - """Run primary setup processes then trigger PandA to collect data from experiment. - Has contingencies to abort if any stage produces errors, before moving the - diffractometer to its starting position. Designed to be called with BlueAPI. - - Args: - parameters (SerialExperimentEh2): SerialExperimentEh2 object - devices (SerialCollectionEh2PandaComposite): SerialCollectionEh2PandaComposite - object - """ - - yield from setup_beamline_before_collection( - parameters.aperture_request, - parameters.detector_distance_mm, - parameters.two_theta_deg, - devices.backlight, - devices.pincol, - devices.diffractometer, - ) - yield from trigger_panda(parameters, devices) + """Run a small rotative serial crystallography collection using the PandA to trigger + the detector.""" + yield from setup_eh2_serial_collection(parameters, devices) + yield from trigger_panda_collection(parameters, devices) + + +def run_on_collection_end( + rot_axis_start: float, + panda: HDFPanda, + eiger: EigerDetector, + serial_stages: XYZPhiStage, + shutter: AccessControlledShutter, +): + LOGGER.info("Disarm eiger") + yield from bps.trigger(eiger.detector.disarm) + LOGGER.info("Disarm panda") + yield from disarm_panda(panda) + yield from reset_panda(panda) + yield from move_sample_stage_back(serial_stages, rot_axis_start) + LOGGER.info("Close experiment shutter") + yield from close_experiment_shutter(shutter) + + +def run_on_collection_abort( + panda: HDFPanda, + eiger: EigerDetector, + diffractometer: FourCircleDiffractometer, +) -> MsgGenerator: + LOGGER.warning("ABORT") + yield from bps.abs_set(diffractometer.phi.motor_stop, 1, wait=True) + yield from bps.trigger(eiger.detector.disarm) + yield from disarm_panda(panda) @bpp.run_decorator() @@ -45,18 +73,19 @@ def run_serial_with_panda( devices: SerialCollectionEh2PandaComposite = inject(), ) -> MsgGenerator: yield from bpp.contingency_wrapper( - setup_then_trigger_panda(parameters, devices), + main_collection_plan(parameters, devices), except_plan=lambda: ( yield from run_on_collection_abort( devices.panda, devices.eiger, devices.diffractometer ) ), final_plan=lambda: ( - yield from end_run( + yield from run_on_collection_end( parameters.rot_axis_start, devices.panda, devices.eiger, devices.serial_stages, + devices.shutter, ) ), auto_raise=False, diff --git a/src/i19_bluesky/serial/setup_beamline_plans/setup_beamline_pre_collection.py b/src/i19_bluesky/serial/setup_beamline_plans/setup_beamline.py similarity index 60% rename from src/i19_bluesky/serial/setup_beamline_plans/setup_beamline_pre_collection.py rename to src/i19_bluesky/serial/setup_beamline_plans/setup_beamline.py index 26876825..e10e9c4d 100644 --- a/src/i19_bluesky/serial/setup_beamline_plans/setup_beamline_pre_collection.py +++ b/src/i19_bluesky/serial/setup_beamline_plans/setup_beamline.py @@ -1,3 +1,4 @@ +import bluesky.plan_stubs as bps from bluesky.utils import MsgGenerator from dodal.devices.beamlines.i19.backlight import BacklightPosition from dodal.devices.beamlines.i19.diffractometer import ( @@ -11,12 +12,45 @@ from i19_bluesky.eh2.backlight_plan import move_backlight_out from i19_bluesky.eh2.pincol_control_plans import move_pin_col_to_requested_in_position from i19_bluesky.log import LOGGER +from i19_bluesky.parameters.devices_composites import SerialCollectionEh2PandaComposite +from i19_bluesky.parameters.serial_parameters import SerialExperimentEh2 +from i19_bluesky.plans.optics_hutch_control_plans import open_experiment_shutter from i19_bluesky.serial.device_setup_plans.diffractometer_plans import ( move_detector_stage, + setup_sample_stage, ) +from i19_bluesky.serial.device_setup_plans.eiger_metadata import write_eiger_params -def setup_beamline_before_collection( +def setup_eh2_serial_collection( + parameters: SerialExperimentEh2, + devices: SerialCollectionEh2PandaComposite, +) -> MsgGenerator: + # Open shutter + yield from open_experiment_shutter(devices.shutter) + # Set up eiger - TO BE ADDED - IN SEPARATE BRANCH + # Read energy and wavelength from dcm to then set up eiger + energ_in_kev = yield from bps.rd(devices.energy_device.energy_in_kev) + wavelength_in_a = yield from bps.rd(devices.energy_device.wavelength_in_a) + yield from write_eiger_params( + parameters, energ_in_kev, wavelength_in_a, devices.eiger + ) + # Set up beamline for collection + yield from setup_beamline_for_collection( + parameters.aperture_request, + parameters.detector_distance_mm, + parameters.two_theta_deg, + devices.backlight, + devices.pincol, + devices.diffractometer, + ) + # Set up sample stage + yield from setup_sample_stage( + parameters.panda_rotation_params, devices.serial_stages + ) + + +def setup_beamline_for_collection( aperture_request: PinColRequest, detector_distance_mm: float, two_theta_deg: float, diff --git a/tests/unit_tests/parameters/test_components.py b/tests/unit_tests/parameters/test_components.py index f1bf486a..bb21a40b 100644 --- a/tests/unit_tests/parameters/test_components.py +++ b/tests/unit_tests/parameters/test_components.py @@ -3,7 +3,6 @@ import pytest from i19_bluesky.parameters.components import ( - HutchName, PandaRotationParams, RotationParams, VisitParameters, @@ -24,13 +23,12 @@ def test_validate_visit_parameters_model(hutch): def test_visit_parameters(): model = { - "hutch": HutchName.EH2, "visit": "/tmp/foo", "dataset": "bar", "filename_prefix": "some_file", } - params = VisitParameters(**model) + params = VisitParameters(**model) # type: ignore assert isinstance(params.visit, Path) assert params.collection_directory == Path("/tmp/foo/bar") diff --git a/tests/unit_tests/serial/conftest.py b/tests/unit_tests/serial/conftest.py index 8be8fa7b..6ee46ccc 100644 --- a/tests/unit_tests/serial/conftest.py +++ b/tests/unit_tests/serial/conftest.py @@ -1,11 +1,19 @@ import os from typing import Any +from unittest.mock import MagicMock import pytest from bluesky.run_engine import RunEngine from dodal.beamlines import i19_2 from dodal.common.beamlines.beamline_utils import get_path_provider, set_path_provider from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider +from dodal.devices.beamlines.i19.access_controlled.energy_device import ( + AccessControlledEnergyComposite, +) +from dodal.devices.beamlines.i19.access_controlled.shutter import ( + AccessControlledShutter, + HutchState, +) from dodal.devices.beamlines.i19.backlight import BacklightPosition from dodal.devices.beamlines.i19.diffractometer import ( FourCircleDiffractometer, @@ -20,7 +28,7 @@ from ophyd_async.fastcs.eiger import EigerDetector from ophyd_async.fastcs.panda import HDFPanda -from i19_bluesky.parameters.components import HutchName, Path +from i19_bluesky.parameters.components import Path from i19_bluesky.parameters.devices_composites import ( SerialCollectionEh2PandaComposite, SerialCollectionEh2ZebraComposite, @@ -161,9 +169,37 @@ async def create_mock_signals(devices_and_signals: dict[Device, dict[str, Any]]) return eiger +@pytest.fixture +async def eh2_shutter(RE: RunEngine) -> AccessControlledShutter: + shutter = AccessControlledShutter("", HutchState.EH2, name="mock_shutter") + await shutter.connect(mock=True) + + shutter.url = "http://test-blueapi.url" + return shutter + + +@pytest.fixture +async def eh2_energy_device(RE: RunEngine) -> AccessControlledEnergyComposite: + energy_device = AccessControlledEnergyComposite( + "", HutchState.EH2, "/path/to/config/", MagicMock(), "", "mock_energy_composite" + ) + await energy_device.connect(mock=True) + energy_device.url = "http://test-blueapi.url" + set_mock_value(energy_device.energy_in_kev, 17.9) + set_mock_value(energy_device.wavelength_in_a, 0.6) + return energy_device + + @pytest.fixture async def devices( - mock_panda, eh2_eiger, eh2_backlight, eh2_diffractometer, serial_stages, pincol + mock_panda, + eh2_eiger, + eh2_backlight, + eh2_diffractometer, + serial_stages, + pincol, + eh2_shutter, + eh2_energy_device, ) -> SerialCollectionEh2PandaComposite: devices = SerialCollectionEh2PandaComposite( diffractometer=eh2_diffractometer, @@ -172,6 +208,8 @@ async def devices( panda=mock_panda, eiger=eh2_eiger, serial_stages=serial_stages, + shutter=eh2_shutter, + energy_device=eh2_energy_device, ) return devices @@ -196,7 +234,6 @@ async def devices_zebra( @pytest.fixture def parameters(): return SerialExperimentEh2( - hutch=HutchName.EH2, visit=Path("/tmp/i19-2/cm12345-1"), dataset="foo", filename_prefix="bar_01", diff --git a/tests/unit_tests/serial/panda_setup_plans/test_panda_setup_plans.py b/tests/unit_tests/serial/panda_setup_plans/test_panda_setup_plans.py index b2cb778a..ea16374b 100644 --- a/tests/unit_tests/serial/panda_setup_plans/test_panda_setup_plans.py +++ b/tests/unit_tests/serial/panda_setup_plans/test_panda_setup_plans.py @@ -6,7 +6,7 @@ from i19_bluesky.parameters.devices_composites import SerialCollectionEh2PandaComposite from i19_bluesky.parameters.serial_parameters import SerialExperimentEh2 -from i19_bluesky.serial.panda_setup_plans.panda_setup_plans import ( +from i19_bluesky.serial.panda_plans.panda_setup_plans import ( reset_panda, setup_panda_for_rotation, ) @@ -16,11 +16,9 @@ async def test_wait_between_setting_table_and_arming( mock_panda: HDFPanda, RE: RunEngine, parameters: SerialExperimentEh2 ): with ( + patch("i19_bluesky.serial.panda_plans.panda_setup_plans.load_panda_from_yaml"), patch( - "i19_bluesky.serial.panda_setup_plans.panda_setup_plans.load_panda_from_yaml" - ), - patch( - "i19_bluesky.serial.panda_setup_plans.panda_setup_plans.bps.wait" + "i19_bluesky.serial.panda_plans.panda_setup_plans.bps.wait" ) as patch_wait, ): RE(setup_panda_for_rotation(parameters.panda_rotation_params, mock_panda)) @@ -33,7 +31,7 @@ async def test_setup_panda_for_rotation( RE: RunEngine, ): with patch( - "i19_bluesky.serial.panda_setup_plans.panda_setup_plans.load_panda_from_yaml" + "i19_bluesky.serial.panda_plans.panda_setup_plans.load_panda_from_yaml" ) as patch_load: parameters.rot_axis_start = 4 parameters.images_per_well = 25 @@ -70,7 +68,7 @@ async def test_setup_panda_for_rotation( async def test_reset_panda(mock_panda: HDFPanda, RE: RunEngine): with patch( - "i19_bluesky.serial.panda_setup_plans.panda_setup_plans.load_panda_from_yaml" + "i19_bluesky.serial.panda_plans.panda_setup_plans.load_panda_from_yaml" ) as patch_load: RE(reset_panda(mock_panda, group="reset panda")) patch_load.assert_called_once() diff --git a/tests/unit_tests/serial/panda_setup_plans/test_panda_stubs.py b/tests/unit_tests/serial/panda_setup_plans/test_panda_stubs.py index 50f6608e..32dd210c 100644 --- a/tests/unit_tests/serial/panda_setup_plans/test_panda_stubs.py +++ b/tests/unit_tests/serial/panda_setup_plans/test_panda_stubs.py @@ -3,7 +3,7 @@ from ophyd_async.core import set_mock_value from ophyd_async.fastcs.panda import HDFPanda, PandaBitMux, SeqTrigger -from i19_bluesky.serial.panda_setup_plans.panda_stubs import ( +from i19_bluesky.serial.panda_plans.panda_stubs import ( arm_panda, disarm_panda, generate_panda_seq_table, diff --git a/tests/unit_tests/serial/run_panda_plans/test_panda_serial_collection.py b/tests/unit_tests/serial/run_panda_plans/test_panda_serial_collection.py index cfb20605..5f9d096b 100644 --- a/tests/unit_tests/serial/run_panda_plans/test_panda_serial_collection.py +++ b/tests/unit_tests/serial/run_panda_plans/test_panda_serial_collection.py @@ -1,50 +1,24 @@ from unittest.mock import MagicMock, call, patch from bluesky.run_engine import RunEngine -from ophyd_async.core import get_mock_put from i19_bluesky.parameters.devices_composites import SerialCollectionEh2PandaComposite from i19_bluesky.parameters.serial_parameters import SerialExperimentEh2 -from i19_bluesky.serial.device_setup_plans.diffractometer_plans import ( - setup_sample_stage, -) from i19_bluesky.serial.run_panda_plans.panda_serial_collection import ( - end_run, - run_on_collection_abort, - trigger_panda, + trigger_panda_collection, ) -async def test_setup_sample_stage( - devices: SerialCollectionEh2PandaComposite, - RE: RunEngine, - parameters: SerialExperimentEh2, -): - - RE(setup_sample_stage(parameters.panda_rotation_params, devices.serial_stages)) - mock_phi = get_mock_put(devices.serial_stages.phi.user_setpoint) - mock_phi.assert_called_once_with(0.0) - - mock_phi_velocity = get_mock_put(devices.serial_stages.phi.velocity) - mock_phi_velocity.assert_called_once_with(1.0) - - @patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.bps.trigger") @patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.bps.abs_set") @patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.move_stage_x_and_z") -@patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.reset_panda") -@patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.disarm_panda") @patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.arm_panda") @patch( "i19_bluesky.serial.run_panda_plans.panda_serial_collection.setup_panda_for_rotation" ) -@patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.setup_sample_stage") def test_trigger_panda_call_order( - mock_setup_sample_stage: MagicMock, mock_setup_panda_for_rotation: MagicMock, mock_arm_panda: MagicMock, - mock_disarm_panda: MagicMock, - mock_reset_panda: MagicMock, mock_move_stage_x_and_z: MagicMock, mock_set_value_for_params: MagicMock, mock_arm_or_disarm: MagicMock, @@ -57,19 +31,13 @@ def test_trigger_panda_call_order( parent_mock = MagicMock() parent_mock.attach_mock(mock_set_value_for_params, "mock_set_value_for_params") parent_mock.attach_mock(mock_move_stage_x_and_z, "mock_move_stage_x_and_z") - parent_mock.attach_mock(mock_setup_sample_stage, "mock_setup_sample_stage") parent_mock.attach_mock( mock_setup_panda_for_rotation, "mock_setup_panda_for_rotation" ) parent_mock.attach_mock(mock_arm_panda, "mock_arm_panda") - parent_mock.attach_mock(mock_disarm_panda, "mock_disarm_panda") - parent_mock.attach_mock(mock_reset_panda, "mock_reset_panda") parent_mock.attach_mock(mock_arm_or_disarm, "mock_arm_or_disarm") - RE(trigger_panda(parameters, devices)) + RE(trigger_panda_collection(parameters, devices)) expected_calls = [ - call.mock_setup_sample_stage( - parameters.panda_rotation_params, devices.serial_stages - ), call.mock_setup_panda_for_rotation( parameters.panda_rotation_params, devices.panda ), @@ -81,46 +49,3 @@ def test_trigger_panda_call_order( call.mock_set_value_for_params(devices.diffractometer.phi, 5.0, wait=True), ] parent_mock.assert_has_calls(expected_calls, any_order=True) - - -@patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.reset_panda") -@patch( - "i19_bluesky.serial.run_panda_plans.panda_serial_collection.move_sample_stage_back" -) -@patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.bps.trigger") -@patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.disarm_panda") -async def test_end_run( - mock_disarm_panda: MagicMock, - mock_disarm_eiger: MagicMock, - mock_move_sample_stage_back: MagicMock, - mock_reset_panda: MagicMock, - parameters: SerialExperimentEh2, - devices: SerialCollectionEh2PandaComposite, - RE: RunEngine, -): - RE( - end_run( - parameters.rot_axis_start, - devices.panda, - devices.eiger, - devices.serial_stages, - ) - ) - mock_disarm_eiger.assert_called_once_with(devices.eiger.detector.disarm) - mock_move_sample_stage_back.assert_called_once_with(devices.serial_stages, 0) - mock_disarm_panda.assert_called_once_with(devices.panda) - mock_reset_panda.assert_called_once_with(devices.panda) - - -@patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.bps.trigger") -@patch("i19_bluesky.serial.run_panda_plans.panda_serial_collection.disarm_panda") -async def test_run_on_collection_abort( - mock_disarm_panda: MagicMock, - mock_disarm_eiger: MagicMock, - RE: RunEngine, - devices: SerialCollectionEh2PandaComposite, -): - RE(run_on_collection_abort(devices.panda, devices.eiger, devices.diffractometer)) - get_mock_put(devices.diffractometer.phi.motor_stop).assert_called_once_with(1) - mock_disarm_eiger.assert_called_once_with(devices.eiger.detector.disarm) - mock_disarm_panda.assert_called_once_with(devices.panda) diff --git a/tests/unit_tests/serial/run_panda_plans/test_run_serial_with_panda.py b/tests/unit_tests/serial/run_panda_plans/test_run_serial_with_panda.py index 6fcd3887..32bb0ce5 100644 --- a/tests/unit_tests/serial/run_panda_plans/test_run_serial_with_panda.py +++ b/tests/unit_tests/serial/run_panda_plans/test_run_serial_with_panda.py @@ -1,55 +1,101 @@ from unittest.mock import MagicMock, patch from bluesky.run_engine import RunEngine +from ophyd_async.core import get_mock_put from i19_bluesky.parameters.devices_composites import SerialCollectionEh2PandaComposite from i19_bluesky.parameters.serial_parameters import SerialExperimentEh2 from i19_bluesky.serial.run_panda_plans.run_serial_with_panda import ( + main_collection_plan, + run_on_collection_abort, + run_on_collection_end, run_serial_with_panda, - setup_then_trigger_panda, ) -@patch("i19_bluesky.serial.run_panda_plans.run_serial_with_panda.end_run") -@patch( - "i19_bluesky.serial.run_panda_plans.run_serial_with_panda.setup_then_trigger_panda" -) +@patch("i19_bluesky.serial.run_panda_plans.run_serial_with_panda.run_on_collection_end") +@patch("i19_bluesky.serial.run_panda_plans.run_serial_with_panda.main_collection_plan") async def test_run_serial_with_panda( - mock_setup_then_trigger_panda: MagicMock, + mock_main_plan: MagicMock, mock_end_run: MagicMock, RE: RunEngine, parameters: SerialExperimentEh2, devices: SerialCollectionEh2PandaComposite, ): RE(run_serial_with_panda(parameters, devices)) - mock_setup_then_trigger_panda.assert_called_once() + mock_main_plan.assert_called_once() mock_end_run.assert_called_once_with( parameters.rot_axis_start, devices.panda, devices.eiger, devices.serial_stages, + devices.shutter, ) @patch( - "i19_bluesky.serial.run_panda_plans.run_serial_with_panda.setup_beamline_before_collection" + "i19_bluesky.serial.run_panda_plans.run_serial_with_panda.setup_eh2_serial_collection" +) +@patch( + "i19_bluesky.serial.run_panda_plans.run_serial_with_panda.trigger_panda_collection" ) -@patch("i19_bluesky.serial.run_panda_plans.run_serial_with_panda.trigger_panda") -async def test_setup_then_trigger_panda( +async def test_main_collection_plan( mock_trigger_panda: MagicMock, - mock_setup_beamline_before_collection: MagicMock, + mock_setup_collection: MagicMock, parameters: SerialExperimentEh2, devices: SerialCollectionEh2PandaComposite, RE: RunEngine, ): - RE(setup_then_trigger_panda(parameters, devices)) - mock_setup_beamline_before_collection.assert_called_once_with( - parameters.aperture_request, - parameters.detector_distance_mm, - parameters.two_theta_deg, - devices.backlight, - devices.pincol, - devices.diffractometer, - ) + RE(main_collection_plan(parameters, devices)) + mock_setup_collection.assert_called_once_with(parameters, devices) mock_trigger_panda.assert_called_once_with(parameters, devices) + + +@patch("i19_bluesky.serial.run_panda_plans.run_serial_with_panda.bps.trigger") +@patch("i19_bluesky.serial.run_panda_plans.run_serial_with_panda.disarm_panda") +async def test_run_on_collection_abort( + mock_disarm_panda: MagicMock, + mock_disarm_eiger: MagicMock, + RE: RunEngine, + devices: SerialCollectionEh2PandaComposite, +): + RE(run_on_collection_abort(devices.panda, devices.eiger, devices.diffractometer)) + get_mock_put(devices.diffractometer.phi.motor_stop).assert_called_once_with(1) + mock_disarm_eiger.assert_called_once_with(devices.eiger.detector.disarm) + mock_disarm_panda.assert_called_once_with(devices.panda) + + +@patch( + "i19_bluesky.serial.run_panda_plans.run_serial_with_panda.close_experiment_shutter" +) +@patch("i19_bluesky.serial.run_panda_plans.run_serial_with_panda.reset_panda") +@patch( + "i19_bluesky.serial.run_panda_plans.run_serial_with_panda.move_sample_stage_back" +) +@patch("i19_bluesky.serial.run_panda_plans.run_serial_with_panda.bps.trigger") +@patch("i19_bluesky.serial.run_panda_plans.run_serial_with_panda.disarm_panda") +async def test_end_run( + mock_disarm_panda: MagicMock, + mock_disarm_eiger: MagicMock, + mock_move_sample_stage_back: MagicMock, + mock_reset_panda: MagicMock, + mock_close_shutter: MagicMock, + parameters: SerialExperimentEh2, + devices: SerialCollectionEh2PandaComposite, + RE: RunEngine, +): + RE( + run_on_collection_end( + parameters.rot_axis_start, + devices.panda, + devices.eiger, + devices.serial_stages, + devices.shutter, + ) + ) + mock_disarm_eiger.assert_called_once_with(devices.eiger.detector.disarm) + mock_move_sample_stage_back.assert_called_once_with(devices.serial_stages, 0) + mock_disarm_panda.assert_called_once_with(devices.panda) + mock_reset_panda.assert_called_once_with(devices.panda) + mock_close_shutter.assert_called_once_with(devices.shutter) diff --git a/tests/unit_tests/serial/setup_beamline_plans/test_setup_beamline_before_collection.py b/tests/unit_tests/serial/setup_beamline_plans/test_setup_beamline.py similarity index 60% rename from tests/unit_tests/serial/setup_beamline_plans/test_setup_beamline_before_collection.py rename to tests/unit_tests/serial/setup_beamline_plans/test_setup_beamline.py index 26f95ea1..5037bb38 100644 --- a/tests/unit_tests/serial/setup_beamline_plans/test_setup_beamline_before_collection.py +++ b/tests/unit_tests/serial/setup_beamline_plans/test_setup_beamline.py @@ -1,3 +1,5 @@ +from unittest.mock import MagicMock, patch + import pytest from bluesky.run_engine import RunEngine from dodal.common.enums import InOutUpper @@ -8,8 +10,9 @@ from i19_bluesky.parameters.devices_composites import SerialCollectionEh2PandaComposite from i19_bluesky.parameters.serial_parameters import SerialExperimentEh2 -from i19_bluesky.serial.setup_beamline_plans.setup_beamline_pre_collection import ( - setup_beamline_before_collection, +from i19_bluesky.serial.setup_beamline_plans.setup_beamline import ( + setup_beamline_for_collection, + setup_eh2_serial_collection, ) @@ -20,7 +23,7 @@ (80, 90, PinColRequest.PCOL100, [23.4, 22.1, 12, 18.7]), ], ) -async def test_setup_beamline_before_collection( +async def test_setup_beamline_for_collection( detector_z: float, detector_two_theta: float, eh2_aperture: PinColRequest, @@ -39,7 +42,7 @@ async def test_setup_beamline_before_collection( set_mock_value(devices.pincol.mapt.col_x.in_positions[size], in_positions[2]) set_mock_value(devices.pincol.mapt.col_y.in_positions[size], in_positions[3]) RE( - setup_beamline_before_collection( + setup_beamline_for_collection( parameters.aperture_request, parameters.detector_distance_mm, parameters.two_theta_deg, @@ -67,3 +70,34 @@ async def test_setup_beamline_before_collection( assert ( await devices.pincol._collimator.y.user_readback.get_value() == in_positions[3] ) + + +@patch("i19_bluesky.serial.setup_beamline_plans.setup_beamline.write_eiger_params") +@patch("i19_bluesky.serial.setup_beamline_plans.setup_beamline.setup_sample_stage") +@patch( + "i19_bluesky.serial.setup_beamline_plans.setup_beamline.setup_beamline_for_collection" +) +@patch("i19_bluesky.serial.setup_beamline_plans.setup_beamline.open_experiment_shutter") +def test_setup_eh2_serial_collection( + mock_open_shutter: MagicMock, + mock_setup: MagicMock, + mock_stage: MagicMock, + mock_write_eiger_params: MagicMock, + parameters: SerialExperimentEh2, + devices: SerialCollectionEh2PandaComposite, + RE: RunEngine, +): + set_mock_value(devices.energy_device.energy_in_kev, 17.9) + set_mock_value(devices.energy_device.wavelength_in_a, 0.6) + RE(setup_eh2_serial_collection(parameters, devices)) + + mock_open_shutter.assert_called_once_with(devices.shutter) + mock_write_eiger_params.assert_called_once_with( + parameters, 17.9, 0.6, devices.eiger + ) + mock_setup.assert_called_once_with( + "100um", 320, 0, devices.backlight, devices.pincol, devices.diffractometer + ) + mock_stage.assert_called_once_with( + parameters.panda_rotation_params, devices.serial_stages + )