diff --git a/setup.cfg b/setup.cfg index 67a626699..a5f5831ac 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,7 +35,7 @@ install_requires = xarray doct databroker - dodal @ git+https://github.com/DiamondLightSource/python-dodal.git@787580a676b769628665f732f97cadd78a69d1da + dodal @ git+https://github.com/DiamondLightSource/python-dodal.git@9f82cf522fddbdb415d36ead204ead380ab0648a [options.extras_require] dev = diff --git a/src/artemis/conftest.py b/src/artemis/conftest.py index a938a7354..d675e0b25 100644 --- a/src/artemis/conftest.py +++ b/src/artemis/conftest.py @@ -1,5 +1,12 @@ +import sys from os import environ, getenv + +def pytest_runtest_teardown(): + if "dodal.i03" in sys.modules: + sys.modules["dodal.i03"].clear_devices() + + s03_epics_server_port = getenv("S03_EPICS_CA_SERVER_PORT") s03_epics_repeater_port = getenv("S03_EPICS_CA_REPEATER_PORT") diff --git a/src/artemis/experiment_plans/fast_grid_scan_plan.py b/src/artemis/experiment_plans/fast_grid_scan_plan.py index e764011cf..8009a93c3 100644 --- a/src/artemis/experiment_plans/fast_grid_scan_plan.py +++ b/src/artemis/experiment_plans/fast_grid_scan_plan.py @@ -7,13 +7,21 @@ import bluesky.preprocessors as bpp from bluesky import RunEngine from bluesky.utils import ProgressBarManager -from dodal.devices.aperturescatterguard import AperturePositions, ApertureScatterguard -from dodal.devices.eiger import EigerDetector -from dodal.devices.fast_grid_scan import FastGridScan, set_fast_grid_scan_params -from dodal.devices.fast_grid_scan_composite import FGSComposite -from dodal.devices.s4_slit_gaps import S4SlitGaps -from dodal.devices.synchrotron import Synchrotron -from dodal.devices.undulator import Undulator +from dodal import i03 +from dodal.devices.aperturescatterguard import AperturePositions +from dodal.devices.eiger import DetectorParams +from dodal.devices.fast_grid_scan import set_fast_grid_scan_params +from dodal.i03 import ( + ApertureScatterguard, + Backlight, + EigerDetector, + FastGridScan, + S4SlitGaps, + Smargon, + Synchrotron, + Undulator, + Zebra, +) import artemis.log from artemis.device_setup_plans.setup_zebra_for_fgs import ( @@ -34,15 +42,47 @@ from artemis.utils import Point3D if TYPE_CHECKING: - from dodal.devices.fast_grid_scan_composite import FGSComposite - from artemis.external_interaction.callbacks.fgs.fgs_callback_collection import ( FGSCallbackCollection, ) from artemis.parameters.internal_parameters import InternalParameters -fast_grid_scan_composite: FGSComposite = None -eiger: EigerDetector = None + +class FGSComposite: + """A device consisting of all the Devices required for a fast gridscan.""" + + aperture_scatterguard: ApertureScatterguard + backlight: Backlight + eiger: EigerDetector + fast_grid_scan: FastGridScan + s4_slit_gaps: S4SlitGaps + sample_motors: Smargon + synchrotron: Synchrotron + undulator: Undulator + zebra: Zebra + + def __init__( + self, + aperture_positions: AperturePositions = None, + detector_params: DetectorParams = None, + fake: bool = False, + ): + self.aperture_scatterguard = i03.aperture_scatterguard( + fake_with_ophyd_sim=fake, aperture_positions=aperture_positions + ) + self.backlight = i03.backlight(fake_with_ophyd_sim=fake) + self.eiger = i03.eiger( + wait_for_connection=False, fake_with_ophyd_sim=fake, params=detector_params + ) + self.fast_grid_scan = i03.fast_grid_scan(fake_with_ophyd_sim=fake) + self.s4_slit_gaps = i03.s4_slit_gaps(fake_with_ophyd_sim=fake) + self.sample_motors = i03.smargon(fake_with_ophyd_sim=fake) + self.undulator = i03.undulator(fake_with_ophyd_sim=fake) + self.synchrotron = i03.synchrotron(fake_with_ophyd_sim=fake) + self.zebra = i03.zebra(fake_with_ophyd_sim=fake) + + +fast_grid_scan_composite: FGSComposite | None = None def get_beamline_parameters(): @@ -51,7 +91,7 @@ def get_beamline_parameters(): def create_devices(): """Creates the devices required for the plan and connect to them""" - global fast_grid_scan_composite, eiger + global fast_grid_scan_composite prefixes = get_beamline_prefixes() artemis.log.LOGGER.info( f"Creating devices for {prefixes.beamline_prefix} and {prefixes.insertion_prefix}" @@ -59,21 +99,8 @@ def create_devices(): aperture_positions = AperturePositions.from_gda_beamline_params( get_beamline_parameters() ) - fast_grid_scan_composite = FGSComposite( - insertion_prefix=prefixes.insertion_prefix, - name="fgs", - prefix=prefixes.beamline_prefix, - aperture_positions=aperture_positions, - ) - - # Note, eiger cannot be currently waited on, see #166 - eiger = EigerDetector( - name="eiger", - prefix=f"{prefixes.beamline_prefix}-EA-EIGER-01:", - ) - artemis.log.LOGGER.info("Connecting to EPICS devices...") - fast_grid_scan_composite.wait_for_connection() + fast_grid_scan_composite = FGSComposite(aperture_positions=aperture_positions) artemis.log.LOGGER.info("Connected.") @@ -123,6 +150,7 @@ def move_xyz( ): """Move 'sample motors' to a specific motor position (e.g. a position obtained from gridscan processing results)""" + artemis.log.LOGGER.info(f"Moving Smargon x, y, z to: {xray_centre_motor_position}") yield from bps.mv( sample_motors.x, xray_centre_motor_position.x, @@ -150,6 +178,7 @@ def wait_for_fgs_valid(fgs_motors: FastGridScan, timeout=0.5): def tidy_up_plans(fgs_composite: FGSComposite): + artemis.log.LOGGER.info("Tidying up Zebra") yield from set_zebra_shutter_to_manual(fgs_composite.zebra) @@ -157,7 +186,6 @@ def tidy_up_plans(fgs_composite: FGSComposite): @bpp.run_decorator(md={"subplan_name": "run_gridscan"}) def run_gridscan( fgs_composite: FGSComposite, - eiger: EigerDetector, parameters: InternalParameters, md={ "plan_name": "run_gridscan", @@ -187,7 +215,7 @@ def run_gridscan( @bpp.set_run_key_decorator("do_fgs") @bpp.run_decorator(md={"subplan_name": "do_fgs"}) - @bpp.stage_decorator([eiger]) + @bpp.stage_decorator([fgs_composite.eiger]) def do_fgs(): yield from bps.wait() # Wait for all moves to complete yield from bps.kickoff(fgs_motors) @@ -204,7 +232,6 @@ def do_fgs(): @bpp.run_decorator(md={"subplan_name": "run_gridscan_and_move"}) def run_gridscan_and_move( fgs_composite: FGSComposite, - eiger: EigerDetector, parameters: InternalParameters, subscriptions: FGSCallbackCollection, ): @@ -222,11 +249,11 @@ def run_gridscan_and_move( # While the gridscan is happening we want to write out nexus files and trigger zocalo @bpp.subs_decorator([subscriptions.nexus_handler, subscriptions.zocalo_handler]) - def gridscan_with_subscriptions(fgs_composite, detector, params): - yield from run_gridscan(fgs_composite, detector, params) + def gridscan_with_subscriptions(fgs_composite, params): + artemis.log.LOGGER.info("Starting grid scan") + yield from run_gridscan(fgs_composite, params) - artemis.log.LOGGER.info("Starting grid scan") - yield from gridscan_with_subscriptions(fgs_composite, eiger, parameters) + yield from gridscan_with_subscriptions(fgs_composite, parameters) # the data were submitted to zocalo by the zocalo callback during the gridscan, # but results may not be ready, and need to be collected regardless. @@ -263,15 +290,18 @@ def get_plan( Returns: Generator: The plan for the gridscan """ - eiger.set_detector_parameters(parameters.artemis_params.detector_params) + assert fast_grid_scan_composite is not None + fast_grid_scan_composite.eiger.set_detector_parameters( + parameters.artemis_params.detector_params + ) @bpp.finalize_decorator(lambda: tidy_up_plans(fast_grid_scan_composite)) @bpp.subs_decorator(subscriptions.ispyb_handler) - def run_gridscan_and_move_and_tidy(fgs_composite, detector, params, comms): - yield from run_gridscan_and_move(fgs_composite, detector, params, comms) + def run_gridscan_and_move_and_tidy(fgs_composite, params, comms): + yield from run_gridscan_and_move(fgs_composite, params, comms) return run_gridscan_and_move_and_tidy( - fast_grid_scan_composite, eiger, parameters, subscriptions + fast_grid_scan_composite, parameters, subscriptions ) diff --git a/src/artemis/external_interaction/callbacks/fgs/ispyb_callback.py b/src/artemis/external_interaction/callbacks/fgs/ispyb_callback.py index 95342a161..7ea7f5664 100644 --- a/src/artemis/external_interaction/callbacks/fgs/ispyb_callback.py +++ b/src/artemis/external_interaction/callbacks/fgs/ispyb_callback.py @@ -70,16 +70,16 @@ def event(self, doc: dict): if event_descriptor.get("name") == ISPYB_PLAN_NAME: self.params.artemis_params.ispyb_params.undulator_gap = doc["data"][ - "fgs_undulator_gap" + "undulator_gap" ] self.params.artemis_params.ispyb_params.synchrotron_mode = doc["data"][ - "fgs_synchrotron_machine_status_synchrotron_mode" + "synchrotron_machine_status_synchrotron_mode" ] self.params.artemis_params.ispyb_params.slit_gap_size_x = doc["data"][ - "fgs_s4_slit_gaps_xgap" + "s4_slit_gaps_xgap" ] self.params.artemis_params.ispyb_params.slit_gap_size_y = doc["data"][ - "fgs_s4_slit_gaps_ygap" + "s4_slit_gaps_ygap" ] LOGGER.info("Creating ispyb entry.") diff --git a/src/artemis/external_interaction/callbacks/fgs/tests/conftest.py b/src/artemis/external_interaction/callbacks/fgs/tests/conftest.py index 167ff5d4d..cad9600dd 100644 --- a/src/artemis/external_interaction/callbacks/fgs/tests/conftest.py +++ b/src/artemis/external_interaction/callbacks/fgs/tests/conftest.py @@ -92,10 +92,10 @@ class TestData: "descriptor": "bd45c2e5-2b85-4280-95d7-a9a15800a78b", "time": 1666604299.828203, "data": { - "fgs_s4_slit_gaps_xgap": 0.1234, - "fgs_s4_slit_gaps_ygap": 0.2345, - "fgs_synchrotron_machine_status_synchrotron_mode": "test", - "fgs_undulator_gap": 1.234, + "s4_slit_gaps_xgap": 0.1234, + "s4_slit_gaps_ygap": 0.2345, + "synchrotron_machine_status_synchrotron_mode": "test", + "undulator_gap": 1.234, }, "timestamps": {"det1": 1666604299.8220396, "det2": 1666604299.8235943}, "seq_num": 1, diff --git a/src/artemis/external_interaction/callbacks/fgs/tests/test_fgs_callback_collection.py b/src/artemis/external_interaction/callbacks/fgs/tests/test_fgs_callback_collection.py index be7168444..040fd0da3 100644 --- a/src/artemis/external_interaction/callbacks/fgs/tests/test_fgs_callback_collection.py +++ b/src/artemis/external_interaction/callbacks/fgs/tests/test_fgs_callback_collection.py @@ -3,13 +3,15 @@ import pytest from bluesky.run_engine import RunEngine from dodal.devices.eiger import DetectorParams, EigerDetector -from dodal.devices.fast_grid_scan_composite import FGSComposite -from artemis.experiment_plans.fast_grid_scan_plan import run_gridscan_and_move +from artemis.experiment_plans.fast_grid_scan_plan import ( + FGSComposite, + run_gridscan_and_move, +) from artemis.external_interaction.callbacks.fgs.fgs_callback_collection import ( FGSCallbackCollection, ) -from artemis.parameters.constants import SIM_BEAMLINE, SIM_INSERTION_PREFIX +from artemis.parameters.constants import SIM_BEAMLINE from artemis.parameters.internal_parameters import InternalParameters from artemis.utils import Point3D @@ -89,13 +91,10 @@ def test_communicator_in_composite_run( callbacks.zocalo_handler._run_start = MagicMock() callbacks.zocalo_handler.xray_centre_motor_position = Point3D(1, 2, 3) - fast_grid_scan_composite = FGSComposite( - insertion_prefix=SIM_INSERTION_PREFIX, name="fgs", prefix=SIM_BEAMLINE - ) + fast_grid_scan_composite = FGSComposite() # this is where it's currently getting stuck: # fast_grid_scan_composite.fast_grid_scan.is_invalid = lambda: False # but this is not a solution - fast_grid_scan_composite.wait_for_connection() # Would be better to use get_plan instead but eiger doesn't work well in S03 RE(run_gridscan_and_move(fast_grid_scan_composite, eiger, params, callbacks)) diff --git a/src/artemis/external_interaction/callbacks/fgs/zocalo_callback.py b/src/artemis/external_interaction/callbacks/fgs/zocalo_callback.py index 6aa035b3f..2e047216e 100644 --- a/src/artemis/external_interaction/callbacks/fgs/zocalo_callback.py +++ b/src/artemis/external_interaction/callbacks/fgs/zocalo_callback.py @@ -139,7 +139,7 @@ def wait_for_results(self, fallback_xyz: Point3D) -> Point3D: self.ispyb.append_to_comment("Found no diffraction.") xray_centre = fallback_xyz bbox_size = None - LOGGER.warn(log_msg) + LOGGER.warning(log_msg) self.processing_time = time.time() - self.processing_start_time self.ispyb.append_to_comment( diff --git a/src/artemis/system_tests/test_fgs_plan.py b/src/artemis/system_tests/test_fgs_plan.py index a123d86e7..a5349a6c1 100644 --- a/src/artemis/system_tests/test_fgs_plan.py +++ b/src/artemis/system_tests/test_fgs_plan.py @@ -7,12 +7,12 @@ from bluesky.run_engine import RunEngine from dodal.devices.aperturescatterguard import AperturePositions from dodal.devices.detector import DetectorParams -from dodal.devices.eiger import DetectorParams, EigerDetector -from dodal.devices.fast_grid_scan_composite import FGSComposite +from dodal.devices.eiger import DetectorParams import artemis.experiment_plans.fast_grid_scan_plan as fgs_plan from artemis.exceptions import WarningException from artemis.experiment_plans.fast_grid_scan_plan import ( + FGSComposite, get_plan, read_hardware_for_ispyb, run_gridscan, @@ -28,46 +28,9 @@ ISPYB_CONFIG, ) from artemis.parameters.beamline_parameters import GDABeamlineParameters -from artemis.parameters.constants import ( - I03_BEAMLINE_PARAMETER_PATH, - SIM_BEAMLINE, - SIM_INSERTION_PREFIX, -) +from artemis.parameters.constants import I03_BEAMLINE_PARAMETER_PATH, SIM_BEAMLINE from artemis.parameters.internal_parameters import InternalParameters - -@pytest.fixture() -def eiger() -> EigerDetector: - detector_params: DetectorParams = DetectorParams( - current_energy=100, - exposure_time=0.1, - directory="/tmp", - prefix="file_name", - detector_distance=100.0, - omega_start=0.0, - omega_increment=0.1, - num_images_per_trigger=1, - num_triggers=50, - use_roi_mode=False, - run_number=0, - det_dist_to_beam_converter_path="src/artemis/unit_tests/test_lookup_table.txt", - ) - eiger = EigerDetector.with_params( - params=detector_params, name="eiger", prefix="BL03S-EA-EIGER-01:" - ) - - # Otherwise odin moves too fast to be tested - eiger.cam.manual_trigger.put("Yes") - - # S03 currently does not have StaleParameters_RBV - eiger.wait_for_stale_parameters = lambda: None - eiger.odin.check_odin_initialised = lambda: (True, "") - - fgs_plan.eiger = eiger - - yield eiger - - params = InternalParameters() params.artemis_params.beamline = SIM_BEAMLINE @@ -80,11 +43,21 @@ def RE(): @pytest.fixture def fgs_composite(): fast_grid_scan_composite = FGSComposite( - insertion_prefix=SIM_INSERTION_PREFIX, - name="fgs", - prefix=SIM_BEAMLINE, + detector_params=DetectorParams( + current_energy=100, + exposure_time=0.1, + directory="/tmp", + prefix="file_name", + detector_distance=100.0, + omega_start=0.0, + omega_increment=0.1, + num_images_per_trigger=1, + num_triggers=50, + use_roi_mode=False, + run_number=0, + det_dist_to_beam_converter_path="src/artemis/unit_tests/test_lookup_table.txt", + ) ) - fast_grid_scan_composite.wait_for_connection() fgs_plan.fast_grid_scan_composite = fast_grid_scan_composite gda_beamline_parameters = GDABeamlineParameters.from_file( I03_BEAMLINE_PARAMETER_PATH @@ -98,6 +71,12 @@ def fgs_composite(): fast_grid_scan_composite.aperture_scatterguard.aperture.z.move( aperture_positions.LARGE[2], wait=True ) + fast_grid_scan_composite.eiger.cam.manual_trigger.put("Yes") + + # S03 currently does not have StaleParameters_RBV + fast_grid_scan_composite.eiger.wait_for_stale_parameters = lambda: None + fast_grid_scan_composite.eiger.odin.check_odin_initialised = lambda: (True, "") + fast_grid_scan_composite.aperture_scatterguard.scatterguard.x.set_lim(-4.8, 5.7) return fast_grid_scan_composite @@ -113,19 +92,16 @@ def test_run_gridscan( complete: MagicMock, kickoff: MagicMock, wait: MagicMock, - eiger: EigerDetector, RE: RunEngine, fgs_composite: FGSComposite, ): - eiger.unstage = lambda: True - fgs_composite.wait_for_connection() + fgs_composite.eiger.unstage = lambda: True # Would be better to use get_plan instead but eiger doesn't work well in S03 - RE(run_gridscan(fgs_composite, eiger, params)) + RE(run_gridscan(fgs_composite, params)) @pytest.mark.s03 def test_read_hardware_for_ispyb( - eiger: EigerDetector, RE: RunEngine, fgs_composite: FGSComposite, ): @@ -137,13 +113,11 @@ def test_read_hardware_for_ispyb( def read_run(u, s, g): yield from read_hardware_for_ispyb(u, s, g) - fgs_composite.wait_for_connection() RE(read_run(undulator, synchrotron, slit_gaps)) @pytest.mark.s03 @patch("artemis.experiment_plans.fast_grid_scan_plan.fast_grid_scan_composite") -@patch("artemis.experiment_plans.fast_grid_scan_plan.eiger") @patch("bluesky.plan_stubs.wait") @patch("bluesky.plan_stubs.kickoff") @patch("bluesky.plan_stubs.complete") @@ -155,7 +129,6 @@ def test_full_plan_tidies_at_end( complete: MagicMock, kickoff: MagicMock, wait: MagicMock, - eiger: EigerDetector, fgs_composite: FGSComposite, RE: RunEngine, ): @@ -166,7 +139,6 @@ def test_full_plan_tidies_at_end( @pytest.mark.s03 @patch("artemis.experiment_plans.fast_grid_scan_plan.fast_grid_scan_composite") -@patch("artemis.experiment_plans.fast_grid_scan_plan.eiger") @patch("bluesky.plan_stubs.wait") @patch("bluesky.plan_stubs.kickoff") @patch("bluesky.plan_stubs.complete") @@ -178,7 +150,6 @@ def test_full_plan_tidies_at_end_when_plan_fails( complete: MagicMock, kickoff: MagicMock, wait: MagicMock, - eiger: EigerDetector, fgs_composite: FGSComposite, RE: RunEngine, ): @@ -187,12 +158,10 @@ def test_full_plan_tidies_at_end_when_plan_fails( with pytest.raises(Exception): RE(get_plan(params, callbacks)) set_shutter_to_manual.assert_called_once() - # tidy_plans.assert_called_once() @pytest.mark.s03 def test_GIVEN_scan_invalid_WHEN_plan_run_THEN_ispyb_entry_made_but_no_zocalo_entry( - eiger: EigerDetector, RE: RunEngine, fgs_composite: FGSComposite, fetch_comment: Callable, @@ -227,7 +196,6 @@ def test_GIVEN_scan_invalid_WHEN_plan_run_THEN_ispyb_entry_made_but_no_zocalo_en def test_WHEN_plan_run_THEN_move_to_centre_returned_from_zocalo_expected_centre( complete: MagicMock, kickoff: MagicMock, - eiger: EigerDetector, RE: RunEngine, fgs_composite: FGSComposite, zocalo_env: None, @@ -243,8 +211,8 @@ def test_WHEN_plan_run_THEN_move_to_centre_returned_from_zocalo_expected_centre( # Currently s03 calls anything with z_steps > 1 invalid parameters.experiment_params.z_steps = 1 - eiger.stage = MagicMock() - eiger.unstage = MagicMock() + fgs_composite.eiger.stage = MagicMock() + fgs_composite.eiger.unstage = MagicMock() callbacks = FGSCallbackCollection.from_params(parameters) callbacks.ispyb_handler.ispyb.ISPYB_CONFIG_PATH = ISPYB_CONFIG diff --git a/src/artemis/system_tests/test_main_system.py b/src/artemis/system_tests/test_main_system.py index 09c010050..cb4a1d91b 100644 --- a/src/artemis/system_tests/test_main_system.py +++ b/src/artemis/system_tests/test_main_system.py @@ -46,12 +46,16 @@ class ClientAndRunEngine: mock_run_engine: MockRunEngine +def mock_dict_values(d: dict): + return {k: MagicMock() for k, _ in d.items()} + + @pytest.fixture def test_env(): mock_run_engine = MockRunEngine() with patch.dict( "artemis.__main__.PLAN_REGISTRY", - {(k, MagicMock()) for k, _ in PLAN_REGISTRY.items()}, + {k: mock_dict_values(v) for k, v in PLAN_REGISTRY.items()}, ): app, runner = create_app({"TESTING": True}, mock_run_engine) runner_thread = threading.Thread(target=runner.wait_on_queue) @@ -59,7 +63,7 @@ def test_env(): with app.test_client() as client: with patch.dict( "artemis.__main__.PLAN_REGISTRY", - {(k, MagicMock()) for k, _ in PLAN_REGISTRY.items()}, + {k: mock_dict_values(v) for k, v in PLAN_REGISTRY.items()}, ): yield ClientAndRunEngine(client, mock_run_engine) @@ -208,14 +212,38 @@ def test_cli_args_parse(): assert test_args == ("DEBUG", True, True, True) -@patch("artemis.experiment_plans.fast_grid_scan_plan.EigerDetector") -@patch("artemis.experiment_plans.fast_grid_scan_plan.FGSComposite") +@patch("dodal.i03.ApertureScatterguard") +@patch("dodal.i03.Backlight") +@patch("dodal.i03.EigerDetector") +@patch("dodal.i03.FastGridScan") +@patch("dodal.i03.S4SlitGaps") +@patch("dodal.i03.Smargon") +@patch("dodal.i03.Synchrotron") +@patch("dodal.i03.Undulator") +@patch("dodal.i03.Zebra") @patch("artemis.experiment_plans.fast_grid_scan_plan.get_beamline_parameters") def test_when_blueskyrunner_initiated_then_plans_are_setup_and_devices_connected( - mock_get_beamline_params, mock_fgs, mock_eiger + mock_get_beamline_params, + zebra, + undulator, + synchrotron, + smargon, + s4_slits, + fast_grid_scan, + eiger, + backlight, + aperture_scatterguard, ): BlueskyRunner(MagicMock(), skip_startup_connection=False) - mock_fgs.return_value.wait_for_connection.assert_called_once() + zebra.return_value.wait_for_connection.assert_called_once() + undulator.return_value.wait_for_connection.assert_called_once() + synchrotron.return_value.wait_for_connection.assert_called_once() + smargon.return_value.wait_for_connection.assert_called_once() + s4_slits.return_value.wait_for_connection.assert_called_once() + fast_grid_scan.return_value.wait_for_connection.assert_called_once() + eiger.return_value.wait_for_connection.assert_not_called() # can't wait on eiger + backlight.return_value.wait_for_connection.assert_called_once() + aperture_scatterguard.return_value.wait_for_connection.assert_called_once() @patch("artemis.experiment_plans.fast_grid_scan_plan.EigerDetector") @@ -233,23 +261,53 @@ def test_when_blueskyrunner_initiated_and_skip_flag_is_set_then_plans_are_setup_ @patch("artemis.experiment_plans.fast_grid_scan_plan.get_beamline_parameters") @patch("artemis.experiment_plans.fast_grid_scan_plan.create_devices") def test_when_blueskyrunner_initiated_and_skip_flag_is_set_then_setup_called_upon_start( - mock_get_beamline_params, mock_fgs, mock_eiger, mock_setup + mock_setup, mock_get_beamline_params, mock_fgs, mock_eiger ): - runner = BlueskyRunner(MagicMock(), skip_startup_connection=True) - mock_setup.assert_not_called() - runner.start(MagicMock(), MagicMock(), "fast_grid_scan") - mock_setup.assert_called_once() + mock_setup = MagicMock() + with patch.dict( + "artemis.__main__.PLAN_REGISTRY", + { + "fast_grid_scan": { + "setup": mock_setup, + "run": MagicMock(), + "param_type": MagicMock(), + }, + }, + ): + runner = BlueskyRunner(MagicMock(), skip_startup_connection=True) + mock_setup.assert_not_called() + runner.start(MagicMock(), MagicMock(), "fast_grid_scan") + mock_setup.assert_called_once() @patch("artemis.experiment_plans.fast_grid_scan_plan.EigerDetector") @patch("artemis.experiment_plans.fast_grid_scan_plan.FGSComposite") @patch("artemis.experiment_plans.fast_grid_scan_plan.get_beamline_parameters") -@patch("artemis.experiment_plans.fast_grid_scan_plan.create_devices") def test_when_blueskyrunner_initiated_and_skip_flag_is_not_set_then_all_plans_setup( mock_get_beamline_params, mock_fgs, mock_eiger, - mock_setup, ): - BlueskyRunner(MagicMock(), skip_startup_connection=False) - mock_setup.assert_called() + mock_setup = MagicMock() + with patch.dict( + "artemis.__main__.PLAN_REGISTRY", + { + "fast_grid_scan": { + "setup": mock_setup, + "run": MagicMock(), + "param_type": MagicMock(), + }, + "other_plan": { + "setup": mock_setup, + "run": MagicMock(), + "param_type": MagicMock(), + }, + "yet_another_plan": { + "setup": mock_setup, + "run": MagicMock(), + "param_type": MagicMock(), + }, + }, + ): + BlueskyRunner(MagicMock(), skip_startup_connection=False) + assert mock_setup.call_count == 3 diff --git a/src/artemis/unit_tests/test_fast_grid_scan_plan.py b/src/artemis/unit_tests/test_fast_grid_scan_plan.py index 74612cbc0..97fda58f8 100644 --- a/src/artemis/unit_tests/test_fast_grid_scan_plan.py +++ b/src/artemis/unit_tests/test_fast_grid_scan_plan.py @@ -10,14 +10,13 @@ EIGER_TYPE_EIGER2_X_4M, EIGER_TYPE_EIGER2_X_16M, ) -from dodal.devices.eiger import EigerDetector from dodal.devices.fast_grid_scan import FastGridScan -from dodal.devices.fast_grid_scan_composite import FGSComposite from ophyd.sim import make_fake_device from ophyd.status import Status from artemis.exceptions import WarningException from artemis.experiment_plans.fast_grid_scan_plan import ( + FGSComposite, read_hardware_for_ispyb, run_gridscan, run_gridscan_and_move, @@ -49,9 +48,17 @@ def test_params(): @pytest.fixture -def fake_fgs_composite(): - FakeComposite = make_fake_device(FGSComposite) - fake_composite: FGSComposite = FakeComposite("test", name="fgs") +def fake_fgs_composite(test_params: InternalParameters): + fake_composite = FGSComposite( + aperture_positions=AperturePositions( + LARGE=(1, 2, 3, 4, 5), + MEDIUM=(2, 3, 3, 5, 6), + SMALL=(3, 4, 3, 6, 7), + ROBOT_LOAD=(0, 0, 3, 0, 0), + ), + detector_params=test_params.artemis_params.detector_params, + fake=True, + ) fake_composite.aperture_scatterguard.aperture.x.user_setpoint._use_limits = False fake_composite.aperture_scatterguard.aperture.y.user_setpoint._use_limits = False fake_composite.aperture_scatterguard.aperture.z.user_setpoint._use_limits = False @@ -61,17 +68,10 @@ def fake_fgs_composite(): fake_composite.aperture_scatterguard.scatterguard.y.user_setpoint._use_limits = ( False ) - fake_composite.aperture_scatterguard.load_aperture_positions( - AperturePositions( - LARGE=(1, 2, 3, 4, 5), - MEDIUM=(2, 3, 3, 5, 6), - SMALL=(3, 4, 3, 6, 7), - ROBOT_LOAD=(0, 0, 3, 0, 0), - ) - ) fake_composite.fast_grid_scan.scan_invalid.sim_put(False) fake_composite.fast_grid_scan.position_counter.sim_put(0) + return fake_composite @@ -94,15 +94,6 @@ def mock_subscriptions(test_params): return subscriptions -@pytest.fixture -def fake_eiger(test_params: InternalParameters): - FakeEiger: EigerDetector = make_fake_device(EigerDetector) - fake_eiger = FakeEiger.with_params( - params=test_params.artemis_params.detector_params, name="test" - ) - return fake_eiger - - def test_given_full_parameters_dict_when_detector_name_used_and_converted_then_detector_constants_correct(): params = InternalParameters(RawParameters()) assert ( @@ -181,7 +172,6 @@ def test_results_adjusted_and_passed_to_move_xyz( move_aperture: MagicMock, fake_fgs_composite: FGSComposite, mock_subscriptions: FGSCallbackCollection, - fake_eiger: EigerDetector, test_params: InternalParameters, ): RE = RunEngine({}) @@ -194,7 +184,6 @@ def test_results_adjusted_and_passed_to_move_xyz( RE( run_gridscan_and_move( fake_fgs_composite, - fake_eiger, test_params, mock_subscriptions, ) @@ -205,7 +194,6 @@ def test_results_adjusted_and_passed_to_move_xyz( RE( run_gridscan_and_move( fake_fgs_composite, - fake_eiger, test_params, mock_subscriptions, ) @@ -216,7 +204,6 @@ def test_results_adjusted_and_passed_to_move_xyz( RE( run_gridscan_and_move( fake_fgs_composite, - fake_eiger, test_params, mock_subscriptions, ) @@ -239,7 +226,9 @@ def test_results_adjusted_and_passed_to_move_xyz( @patch("bluesky.plan_stubs.mv") def test_results_passed_to_move_motors( - bps_mv: MagicMock, test_params: InternalParameters + bps_mv: MagicMock, + test_params: InternalParameters, + fake_fgs_composite: FGSComposite, ): from artemis.experiment_plans.fast_grid_scan_plan import move_xyz @@ -249,8 +238,7 @@ def test_results_passed_to_move_motors( motor_position = test_params.experiment_params.grid_position_to_motor_position( Point3D(1, 2, 3) ) - FakeComposite: FGSComposite = make_fake_device(FGSComposite) - RE(move_xyz(FakeComposite("test", name="fgs").sample_motors, motor_position)) + RE(move_xyz(fake_fgs_composite.sample_motors, motor_position)) bps_mv.assert_called_once_with( ANY, motor_position.x, ANY, motor_position.y, ANY, motor_position.z ) @@ -262,14 +250,15 @@ def test_results_passed_to_move_motors( @patch("artemis.experiment_plans.fast_grid_scan_plan.run_gridscan.do_fgs") @patch("artemis.experiment_plans.fast_grid_scan_plan.run_gridscan") @patch("artemis.experiment_plans.fast_grid_scan_plan.move_xyz") +@patch("bluesky.plan_stubs.rd") def test_individual_plans_triggered_once_and_only_once_in_composite_run( + rd: MagicMock, move_xyz: MagicMock, run_gridscan: MagicMock, do_fgs: MagicMock, move_aperture: MagicMock, mock_subscriptions: FGSCallbackCollection, fake_fgs_composite: FGSComposite, - fake_eiger: EigerDetector, test_params: FGSComposite, ): RE = RunEngine({}) @@ -280,13 +269,12 @@ def test_individual_plans_triggered_once_and_only_once_in_composite_run( RE( run_gridscan_and_move( fake_fgs_composite, - fake_eiger, test_params, mock_subscriptions, ) ) - run_gridscan.assert_called_once_with(fake_fgs_composite, fake_eiger, params) + run_gridscan.assert_called_once_with(fake_fgs_composite, params) move_xyz.assert_called_once_with(ANY, Point3D(0.05, 0.15000000000000002, 0.25)) @@ -296,14 +284,15 @@ def test_individual_plans_triggered_once_and_only_once_in_composite_run( @patch("artemis.experiment_plans.fast_grid_scan_plan.run_gridscan.do_fgs") @patch("artemis.experiment_plans.fast_grid_scan_plan.run_gridscan") @patch("artemis.experiment_plans.fast_grid_scan_plan.move_xyz") +@patch("bluesky.plan_stubs.rd") def test_logging_within_plan( + rd: MagicMock, move_xyz: MagicMock, run_gridscan: MagicMock, do_fgs: MagicMock, move_aperture: MagicMock, mock_subscriptions: FGSCallbackCollection, fake_fgs_composite: FGSComposite, - fake_eiger: EigerDetector, test_params: InternalParameters, ): RE = RunEngine({}) @@ -313,13 +302,12 @@ def test_logging_within_plan( RE( run_gridscan_and_move( fake_fgs_composite, - fake_eiger, test_params, mock_subscriptions, ) ) - run_gridscan.assert_called_once_with(fake_fgs_composite, fake_eiger, test_params) + run_gridscan.assert_called_once_with(fake_fgs_composite, test_params) move_xyz.assert_called_once_with(ANY, Point3D(0.05, 0.15000000000000002, 0.25)) @@ -359,13 +347,14 @@ def test_GIVEN_scan_not_valid_THEN_wait_for_FGS_raises_and_sleeps_called( @patch("artemis.experiment_plans.fast_grid_scan_plan.bps.kickoff") @patch("artemis.experiment_plans.fast_grid_scan_plan.bps.complete") @patch("artemis.experiment_plans.fast_grid_scan_plan.bps.mv") +@patch("artemis.experiment_plans.fast_grid_scan_plan.wait_for_fgs_valid") def test_when_grid_scan_ran_then_eiger_disarmed_before_zocalo_end( + wait_for_valid, mock_mv, mock_complete, mock_kickoff, mock_abs_set, fake_fgs_composite: FGSComposite, - fake_eiger: EigerDetector, test_params: InternalParameters, mock_subscriptions: FGSCallbackCollection, ): @@ -374,19 +363,18 @@ def test_when_grid_scan_ran_then_eiger_disarmed_before_zocalo_end( # Put both mocks in a parent to easily capture order mock_parent = MagicMock() - fake_eiger.disarm_detector = mock_parent.disarm + fake_fgs_composite.eiger.disarm_detector = mock_parent.disarm - fake_eiger.filewriters_finished = Status() - fake_eiger.filewriters_finished.set_finished() - fake_eiger.odin.check_odin_state = MagicMock(return_value=True) - fake_eiger.stage = MagicMock() + fake_fgs_composite.eiger.filewriters_finished = Status() + fake_fgs_composite.eiger.filewriters_finished.set_finished() + fake_fgs_composite.eiger.odin.check_odin_state = MagicMock(return_value=True) + fake_fgs_composite.eiger.stage = MagicMock() mock_subscriptions.zocalo_handler.zocalo_interactor.run_end = mock_parent.run_end RE( run_gridscan_and_move( fake_fgs_composite, - fake_eiger, test_params, mock_subscriptions, )