diff --git a/src/test_rig_bluesky/plans.py b/src/test_rig_bluesky/plans.py index 4144ca6..59490a3 100644 --- a/src/test_rig_bluesky/plans.py +++ b/src/test_rig_bluesky/plans.py @@ -1,8 +1,10 @@ import math +from dataclasses import dataclass from typing import Any from bluesky import plan_stubs as bps from bluesky.plans import count +from bluesky.protocols import Movable from bluesky.utils import MsgGenerator from dodal.common import inject from dodal.devices.motors import XYZStage @@ -10,6 +12,9 @@ from dodal.plans import spec_scan from ophyd_async.core import TriggerInfo from ophyd_async.epics.adaravis import AravisDetector +from ophyd_async.epics.adcore import NDAttributePv, NDAttributePvDbrType +from ophyd_async.epics.adcore._core_io import NDROIStatNIO +from ophyd_async.plan_stubs import setup_ndattributes from scanspec.specs import Line, Spec imaging_detector = inject("imaging_detector") @@ -17,6 +22,15 @@ sample_stage = inject("sample_stage") +@dataclass +class ROI: + channel: int + name: str + start_x: int + start_y: int + size: int + + @attach_data_session_metadata_decorator() def snapshot( imaging_detector: AravisDetector = imaging_detector, @@ -30,7 +44,7 @@ def snapshot( def spectroscopy( spectroscopy_detector: AravisDetector = spectroscopy_detector, sample_stage: XYZStage = sample_stage, - spec: Spec | None = None, + spec: Spec[Movable] | None = None, exposure_time: float = 0.1, metadata: dict[str, Any] | None = None, ) -> MsgGenerator[None]: @@ -38,6 +52,52 @@ def spectroscopy( yield from bps.prepare( spectroscopy_detector, TriggerInfo(livetime=exposure_time), wait=True ) + # TODO: This would be nicer if NDArrayBaseIO had a PortName signal + yield from bps.abs_set( + spectroscopy_detector.fileio.nd_array_port, "D2.roistat", wait=True + ) + + rois = [ + ROI(2, "Green", 880, 605, 150), + ROI(3, "Blue", 1665, 600, 150), + ROI(1, "Red", 95, 610, 150), + ] + + params: list[NDAttributePv] = [] + for roi in rois: + roistatn = spectroscopy_detector.roistat.channels[roi.channel] # type: ignore + assert isinstance(roistatn, NDROIStatNIO) + + yield from bps.mv( + *(roistatn.name_, roi.name), + *(roistatn.min_x, roi.start_x), + *(roistatn.min_y, roi.start_y), + *(roistatn.size_x, roi.size), + *(roistatn.size_y, roi.size), + *(roistatn.use, True), + wait=True, + ) + + # TODO: We can't include all the channels as it makes the xml longer than 256 + # params.append( + params = [ + NDAttributePv( + name=f"{roi.name}Total", + signal=roistatn.total, + dbrtype=NDAttributePvDbrType.DBR_LONG, + description=f"Sum of {roi.name} channel", + ) + ] + # ) + + yield from setup_ndattributes(spectroscopy_detector.roistat, params) # type: ignore + + for motor in [sample_stage.x, sample_stage.y]: + yield from bps.mv( + *(motor.acceleration_time, 0.001), + *(motor.velocity, 100), + wait=True, + ) spec = spec or Line(sample_stage.x, 0, 5, 5) diff --git a/tests/system_tests/conftest.py b/tests/system_tests/conftest.py index 98345cf..3e1f6ae 100644 --- a/tests/system_tests/conftest.py +++ b/tests/system_tests/conftest.py @@ -1,4 +1,6 @@ import os +import socket +import textwrap from collections.abc import Generator from pathlib import Path @@ -12,6 +14,35 @@ PROJECT_ROOT = Path(__file__).parent.parent.parent +BEAMLINE_HOSTS = [ + "b01-1-ws001.diamond.ac.uk", + "b01-1-control.diamond.ac.uk", + "bl01c-ea-serv-01.diamond.ac.uk", + "bl01c-di-serv-01.diamond.ac.uk", +] + + +def pytest_configure(config: pytest.Config): + config.addinivalue_line( + "markers", "control_system: test requires direct access to the control system" + ) + + +def pytest_runtest_setup(item: pytest.Item): + if next(item.iter_markers(name="control_system"), None) is not None: + if not on_controllable_machine(): + pytest.skip( + reason=textwrap.dedent(f""" + This test needs direct access to the control system and can only + be run from one of the following machines: {BEAMLINE_HOSTS} + """) + ) + + +def on_controllable_machine() -> bool: + hostname = socket.gethostname() + return hostname in BEAMLINE_HOSTS + @pytest.fixture def instrument() -> str: diff --git a/tests/system_tests/test_plans_system.py b/tests/system_tests/test_plans_system.py index 905bace..1acd60a 100644 --- a/tests/system_tests/test_plans_system.py +++ b/tests/system_tests/test_plans_system.py @@ -1,5 +1,10 @@ +import pytest from blueapi.service.model import TaskRequest +from bluesky import RunEngine +from dodal.beamlines.b01_1 import sample_stage, spectroscopy_detector +from scanspec.specs import Line +from test_rig_bluesky.plans import spectroscopy from test_rig_bluesky.testing import BlueskyPlanRunner @@ -34,12 +39,25 @@ def test_spectroscopy( def test_demo_spectroscopy( bluesky_plan_runner: BlueskyPlanRunner, latest_commissioning_instrument_session: str ): + _sample_stage = sample_stage() + scan_spec = Line(_sample_stage.x, 2, 5, 30) * Line(_sample_stage.y, 0, 5, 50) events = bluesky_plan_runner.run( TaskRequest( name="demo_spectroscopy", - params={}, + params={"spec": scan_spec.serialize()}, instrument_session=latest_commissioning_instrument_session, ), timeout=10, ) assert events["FINISHED"][0]["scanDimensions"] == [5, 5] + + +@pytest.mark.control_system +def test_spectroscopy_re(): + RE = RunEngine() + _spectroscopy_detector = spectroscopy_detector(connect_immediately=True) + _sample_stage = sample_stage(connect_immediately=True) + + scan_spec = Line(_sample_stage.x, 2, 5, 30) * Line(_sample_stage.y, 0, 5, 50) + + RE(spectroscopy(_spectroscopy_detector, _sample_stage, scan_spec)) diff --git a/tests/unit_tests/test_plans.py b/tests/unit_tests/test_plans.py index 9b8245c..23af53f 100644 --- a/tests/unit_tests/test_plans.py +++ b/tests/unit_tests/test_plans.py @@ -82,8 +82,8 @@ async def test_spectroscopy(RE: RunEngine): docs, start=1, descriptor=1, - stream_resource=1, - stream_datum=30, + stream_resource=2, + stream_datum=2 * 30, event=30, stop=1, ) @@ -114,8 +114,8 @@ async def test_spectroscopy_defaults(RE: RunEngine): docs, start=1, descriptor=1, - stream_resource=1, - stream_datum=5, + stream_resource=2, + stream_datum=2 * 5, event=5, stop=1, )