diff --git a/src/htss_rig_bluesky/plans/detector.py b/src/htss_rig_bluesky/plans/detector.py deleted file mode 100644 index b21e9af..0000000 --- a/src/htss_rig_bluesky/plans/detector.py +++ /dev/null @@ -1,74 +0,0 @@ -from collections.abc import Generator -from dataclasses import dataclass - -import bluesky.plan_stubs as bps -from ophyd_async.epics.adaravis import AravisDetector - - -@dataclass -class Roi: - min_x: int - min_y: int - size_x: int | None = None - size_y: int | None = None - - @classmethod - def full_frame(cls) -> "Roi": - return cls(0, 0) - - @classmethod - def strip(cls) -> "Roi": - return cls(0, 600, size_y=20) - - @property - def max_x(self) -> int: - return self.min_x + (self.size_x or 0) - - @property - def max_y(self) -> int: - return self.min_y + (self.size_y or 0) - - -def ensure_detector_ready(det: AravisDetector) -> Generator: - """ - Setup detector for exercises - - Args: - det: Detector - - Yields: - Plan - """ - # TODO: need num exposures too? - yield from bps.mv( - det.drv.num_images, - 1, - det.drv.acquire_period, - 0.1, - det.drv.acquire_time, - 0.15, - det.hdf.nd_array_port, - "DET.CAM", - ) - - -def set_roi(det: AravisDetector, roi: Roi) -> Generator: - """ - Setup detector ROI and frame size - - Args: - det: Detector - roi: ROI to take - - Yields: - Plan - """ - # Ophyd Async AravisDetector doesn't appear to have signal for max sizes - # eg DET:MaxSizeX_RBVDET:MaxSizeX_RBV - - sets = {det.drv.array_size_x: roi.size_x, det.drv.array_size_y: roi.size_y} - - for signal, value in sets.items(): - yield from bps.abs_set(signal, value) - - # TODO: Must prime plugins before acquisition diff --git a/src/htss_rig_bluesky/plans/diagnostic.py b/src/htss_rig_bluesky/plans/diagnostic.py index c88cd6c..61ac3f4 100644 --- a/src/htss_rig_bluesky/plans/diagnostic.py +++ b/src/htss_rig_bluesky/plans/diagnostic.py @@ -8,6 +8,14 @@ from scanspec.specs import Line import logging +from ophyd_async.fastcs.panda import HDFPanda +from pathlib import Path +from dodal.beamlines.training_rig import TrainingRigSampleStage + +def step_scan_diagnostic(detector: AravisDetector, panda: HDFPanda, sample_stage: TrainingRigSampleStage) -> MsgGenerator: + scanspec = Line(sample_stage.x, 0, 10, 10) * ~Line(sample_stage.theta, 0, 360, 4) + yield from spec_scan([detector], scanspec) + def detector_diagnostic(detector: AravisDetector) -> MsgGenerator: diagnostic_fields = {component for _, component in detector.drv.children()}.union( {component for _, component in detector.hdf.children()} diff --git a/src/htss_rig_bluesky/plans/exercise.py b/src/htss_rig_bluesky/plans/exercise.py deleted file mode 100644 index dd4f310..0000000 --- a/src/htss_rig_bluesky/plans/exercise.py +++ /dev/null @@ -1,165 +0,0 @@ -# -# The following set of plans may be used as system tests to "exercise" the rigs -# - - -from collections.abc import Generator - -import bluesky.plan_stubs as bps -from dodal.beamlines.training_rig import TrainingRigSampleStage as SampleStage -from dodal.plans import count, spec_scan -from ophyd_async.epics.adaravis import AravisDetector -from ophyd_async.epics.motor import Motor -from scanspec.specs import Line - - -def exercise_beamline(det: AravisDetector, sample: SampleStage) -> Generator: - """ - Perform all beamline exercise plans sequentially. - - Args: - det: Detector - sample: Sample stage - - Yields: - Plan - """ - - yield from exercise_motors(sample) - yield from exercise_detector(det) - yield from exercise_scan(det, sample) - - -def exercise_motors(sample: SampleStage) -> Generator: - """ - exercise the motors on the sample stage. - - Args: - sample: Sample stage - - Yields: - Plan - """ - - yield from exercise_motor(sample.x, -24.9, 14.0, tolerance=0.1) - yield from exercise_motor( - sample.theta, -1000.0, 1000.0, tolerance=0.1, check_limits=False - ) - - -def exercise_detector(det: AravisDetector) -> Generator: - """ - exercise the detector by taking a frame. - - Args: - det: Detector - - Yields: - Plan - """ - - exposure_time = 0.05 - yield from bps.mv( - det.drv.acquire_time, - exposure_time, - det.drv.acquire_period, - det.controller.get_deadtime(exposure_time) + exposure_time, - ) - yield from count([det], num=20) - - -def exercise_scan(det: AravisDetector, sample: SampleStage) -> Generator: - """ - Perform a short scan to exercise the test rig. - - Args: - det (AravisDetector): Detector - sample (SampleStage): Sample stage - - Yields: - Plan - """ - - yield from spec_scan( - [det], - Line(sample.x, -5, 5, 4) * Line(sample.theta, -180, 180, 4), - ) - - -def exercise_motor( - motor: Motor, - low_limit: float, - high_limit: float, - tolerance: float = 0.0, - check_limits: bool = True, -) -> Generator: - """ - exercise a motor by making sure it can traverse between a low point - and a high point. - - Args: - motor: The motor - low_limit: Place to start - high_limit: Place to end - tolerance: Tolerance for checking motor position. Defaults to 0.0. - check_limits: Check whether the motor's limits fall within the bounds, - disable for limitless motors. Defaults to True. - - Yields: - Plan - """ - - if check_limits: - yield from assert_limits_within(motor, low_limit, high_limit) - yield from bps.abs_set(motor, low_limit, wait=True) - yield from assert_motor_at(motor, low_limit, tolerance) - yield from bps.abs_set(motor, high_limit, wait=True) - yield from assert_motor_at(motor, high_limit, tolerance) - - -def assert_limits_within( - motor: Motor, low_limit: float, high_limit: float -) -> Generator: - """ - Check a motors limits fall within the bounds supplied. - Note this is not an exact check, just whether the real limits exceed - the "limit limits" supplied. - - Args: - motor: The motor with limits - low_limit: The lower bound - high_limit: The upper bound - """ - - name = motor.name - motor_high_limit: float = yield from bps.rd(motor.high_limit_travel) - motor_low_limit: float = yield from bps.rd(motor.low_limit_travel) - assert motor_high_limit >= high_limit, ( - f"{name}'s upper limit is {motor.high_limit_travel}, should be >= {high_limit}" - ) - assert motor_low_limit <= low_limit, ( - f"{name}'s lower limit is {motor_low_limit}, should be <= {low_limit}" - ) - - -def assert_motor_at(motor: Motor, pos: float, tolerance: float = 0.0) -> Generator: - """ - Check a motor has reached a required position - - Args: - motor: The motor to check - pos: The required position - tolerance: Plus or minus tolerance, useful for - less precise motors. Defaults to 0.0. - - Yields: - Plan - """ - - actual_pos = yield from bps.rd(motor) - upper_bound = pos + (tolerance / 2.0) - lower_bound = pos - (tolerance / 2.0) - assert upper_bound >= actual_pos >= lower_bound, ( - f"{motor.name} is at {actual_pos}, " - ) - f"should be between {lower_bound} and {upper_bound}" diff --git a/src/htss_rig_bluesky/plans/system_test.py b/src/htss_rig_bluesky/plans/system_test.py deleted file mode 100644 index e7449b8..0000000 --- a/src/htss_rig_bluesky/plans/system_test.py +++ /dev/null @@ -1,61 +0,0 @@ -import bluesky.plans as bp -import bluesky.preprocessors as bpp -from bluesky.utils import MsgGenerator -from dodal.plan_stubs.data_session import attach_data_session_metadata_decorator -from ophyd_async.core import ( - StandardDetector, - StandardFlyer, -) -from ophyd_async.epics.motor import Motor -from ophyd_async.fastcs.panda import ( - HDFPanda, - StaticSeqTableTriggerLogic, -) -from ophyd_async.plan_stubs import ensure_connected, fly_and_collect -from ophyd_async.plan_stubs._fly import ( - prepare_static_seq_table_flyer_and_detectors_with_same_trigger, -) - -# Plans utilized for system testing -# This module contains a collection of Bluesky plans designed for system testing. -# These plans are used to perform various types of scans and data collection routines -# using the Bluesky framework and ophyd_async devices. Each plan is decorated with -# metadata to attach data session information, ensuring that all collected data -# is properly annotated and organized. - - -@attach_data_session_metadata_decorator() -def fly_and_collect_plan(panda: HDFPanda, diff: StandardDetector) -> MsgGenerator: - trigger_logic = StaticSeqTableTriggerLogic(panda.seq[1]) - - flyer = StandardFlyer( - trigger_logic, - name="flyer", - ) - yield from ensure_connected(diff, panda, flyer) - - @bpp.stage_decorator(devices=[diff, panda, flyer]) - @bpp.run_decorator() - def inner(): - yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( - flyer, [diff], number_of_frames=15, exposure=0.1, shutter_time=0.05 - ) - yield from fly_and_collect( - stream_name="primary", - flyer=flyer, - detectors=[diff], - ) - - yield from inner() - - -@attach_data_session_metadata_decorator() -def step_scan_plan(detectors: StandardDetector, motor: Motor) -> MsgGenerator: - yield from bp.scan([detectors], motor, -15, 15, num=10) - - -@attach_data_session_metadata_decorator() -def log_scan_plan(detectors: StandardDetector, motor: Motor) -> MsgGenerator: - yield from bp.log_scan( - detectors=[detectors], motor=motor, start=1, stop=100, num=10 - ) diff --git a/tests/system_tests/conftest.py b/tests/system_tests/conftest.py index cf2ebd6..badeb55 100644 --- a/tests/system_tests/conftest.py +++ b/tests/system_tests/conftest.py @@ -6,25 +6,6 @@ from htss_rig_bluesky.names import BEAMLINE - -@pytest.fixture -def task_definition() -> dict[str, Task]: - return { - "step_scan_plan": Task( - name="step_scan_plan", - params={"detectors": "det", "motor": "sample_stage.theta"}, - ), - "fly_and_collect_plan": Task( - name="fly_and_collect_plan", - params={"panda": "panda", "diff": "det"}, - ), - "log_scan_plan": Task( - name="log_scan_plan", - params={"detectors": "det", "motor": "sample_stage.x"}, - ), - } - - @pytest.fixture def config() -> ApplicationConfig: if BEAMLINE == "p46": diff --git a/tests/system_tests/test_plans.py b/tests/system_tests/test_plans.py index 36c2619..1866c27 100644 --- a/tests/system_tests/test_plans.py +++ b/tests/system_tests/test_plans.py @@ -4,9 +4,20 @@ from blueapi.core.bluesky_types import DataEvent from blueapi.worker.event import TaskStatus, WorkerEvent, WorkerState from blueapi.worker.task import Task - +from htss_rig_bluesky.plans.diagnostic import motor_diagnostic, detector_diagnostic, step_scan_diagnostic # Please export BEAMLINE=pXX before running the tests or add it in pyproject.toml +TASKS = [ + Task(name=motor_diagnostic.__name__, params={ + "motor": "sample_stage.x" + }), + Task(name=motor_diagnostic.__name__, params={ + "motor": "sample_stage.theta" + }), + Task(name=detector_diagnostic.__name__, params={ + "detector": "det" + }), +] def _check_all_events(all_events: list[AnyEvent]): assert ( @@ -54,21 +65,20 @@ def test_device_present(client: BlueapiClient, device: str): @pytest.mark.parametrize( - "plan", ["step_scan_plan", "fly_and_collect_plan", "log_scan_plan"] + "task", TASKS ) -def test_spec_scan_task( +def test_plan_runs( client: BlueapiClient, - task_definition: dict[str, Task], - plan: str, + task: Task, ): - assert client.get_plan(plan), f"In {plan} is available" + assert client.get_plan(task.plan), f"In {task.plan} is not available" all_events: list[AnyEvent] = [] def on_event(event: AnyEvent): all_events.append(event) - client.run_task(task_definition[plan], on_event=on_event) + client.run_task(task, on_event=on_event) _check_all_events(all_events)