diff --git a/docs/conf.py b/docs/conf.py index 1838703..5e18c10 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -64,6 +64,8 @@ ("py:class", "htss_rig_bluesky.devices.SampleStage"), ("py:class", "SampleStage"), ("py:class", "htss_rig_bluesky.plans.detector.Roi"), + ("py:class", "ophyd_async.epics.areadetector.aravis.AravisDetector"), + ("py:class", "AravisDetector"), ("py:class", "NoneType"), ("py:class", "'str'"), ("py:class", "'float'"), diff --git a/pyproject.toml b/pyproject.toml index bd9e845..c51960f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ dependencies = [ "pandablocks", "scipy", "tiled==0.1.0a91", + "ophyd_async>=0.3.4", ] dynamic = ["version"] license.file = "LICENSE" diff --git a/src/htss_rig_bluesky/devices.py b/src/htss_rig_bluesky/devices.py index 5bb4a7f..f40b232 100644 --- a/src/htss_rig_bluesky/devices.py +++ b/src/htss_rig_bluesky/devices.py @@ -1,62 +1,82 @@ +from enum import Enum +from pathlib import Path + import epics -from bluesky.protocols import Status -from dodal.devices.areadetector import AdAravisDetector -from ophyd import Component, Device, EpicsMotor, EpicsSignalWithRBV, MotorBundle +from ophyd_async.core import ( + AsyncStatus, + AutoIncrementFilenameProvider, + Device, + StaticPathProvider, +) +from ophyd_async.epics.areadetector.aravis import AravisDetector +from ophyd_async.epics.motion import Motor +from ophyd_async.epics.signal import epics_signal_rw from .names import pv_prefix -class SampleStage(MotorBundle): - x: EpicsMotor = Component(EpicsMotor, "X") - theta: EpicsMotor = Component(EpicsMotor, "A") +class SampleStage(Device): + def __init__(self, prefix: str, name: str): + self.x = Motor(prefix + "X") + self.theta = Motor(prefix + "A") + super().__init__(name) -class Backlight(Device): - on: EpicsSignalWithRBV = Component(EpicsSignalWithRBV, "State") +class BacklightPower(str, Enum): + ON = "On" + OFF = "Off" - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.on.put_complete = True - def set(self, value) -> Status: - # Shortcut so that bps.abs_set(beam.on, True) is the same - # as bps.abs_set(beam, True) - return self.on.set(value) +class Backlight(Device): + def __init__(self, prefix: str, name: str = ""): + self.power = epics_signal_rw(BacklightPower, prefix + "State") + super().__init__(name) + + @AsyncStatus.wrap + async def set(self, position: BacklightPower): + """This setter will turn the backlight on when we move it in to the beam and off + when we move it out.""" + await self.power.set(position) def sample(name: str = "sample_stage") -> SampleStage: """ - Create sample stage Ophyd device + Create sample stage ophyd-async device Args: name: Name for this device for reference in events. Defaults to "sample_stage". Returns: - SampleStage: A new Ophyd Device + SampleStage: A new ophyd-async Device """ return SampleStage(name=name, prefix=f"{pv_prefix()}-MO-MAP-01:STAGE:") -def det(name: str = "det") -> AdAravisDetector: +def det(name: str = "det") -> AravisDetector: """ - Create detector stage Ophyd device + Create detector stage ophyd-async device Args: name: Name for this device for reference in events. Defaults to "det". Returns: - SampleStage: A new Ophyd Device + AravisDetector: A new ophyd-async Device """ - det = AdAravisDetector(name=name, prefix=f"{pv_prefix()}-EA-DET-01:") - det.read_attrs += ["cam"] - det.cam.read_attrs += ["acquire_time", "acquire_period"] - det.hdf.reg_root = "/exports/mybeamline/data" - det.hdf.write_path_template = "%Y" - return det + dir_prov = StaticPathProvider( + AutoIncrementFilenameProvider(), + Path("/exports/mybeamline/data"), + ) + return AravisDetector( + name=name, + prefix=f"{pv_prefix()}-EA-DET-01:", + path_provider=dir_prov, + hdf_suffix="HDF5:", + drv_suffix="DET:", + ) def beam(name: str = "beam") -> Backlight: @@ -68,7 +88,7 @@ def beam(name: str = "beam") -> Backlight: Defaults to "beam". Backlight: - SampleStage: A new Ophyd Device + Backlight: A new ophyd-async Device """ return Backlight(name=name, prefix=f"{pv_prefix()}-EA-BEAM-01:") diff --git a/src/htss_rig_bluesky/plans/calibration.py b/src/htss_rig_bluesky/plans/calibration.py index 1a6de45..8d57388 100644 --- a/src/htss_rig_bluesky/plans/calibration.py +++ b/src/htss_rig_bluesky/plans/calibration.py @@ -3,11 +3,11 @@ import bluesky.plan_stubs as bps import bluesky.plans as bp -from htss_rig_bluesky.devices import AdAravisDetector, SampleStage +from htss_rig_bluesky.devices import AravisDetector, SampleStage def scan_center( - det: AdAravisDetector, + det: AravisDetector, sample: SampleStage, min_x: float | None = None, max_x: float | None = None, @@ -42,15 +42,15 @@ def scan_center( Plan """ - x_range = abs(sample.x.high_limit - sample.x.low_limit) + x_range = abs(sample.x.high_limit_travel - sample.x.low_limit_travel) limit_margin = x_range * 0.01 - min_x = min_x or sample.x.low_limit + limit_margin - max_x = max_x or sample.x.high_limit - limit_margin + min_x = min_x or sample.x.low_limit_travel + limit_margin + max_x = max_x or sample.x.high_limit_travel - limit_margin yield from bps.mv( - det.cam.num_images, + det.drv.num_images, images_per_side, - det.cam.acquire_time, + det.drv.acquire_time, exposure_time, ) yield from bp.grid_scan( @@ -68,7 +68,7 @@ def scan_center( def scan_exposure( - det: AdAravisDetector, + det: AravisDetector, min_exposure: float = 0.01, max_exposure: float = 0.2, exposure_steps: int = 10, @@ -85,8 +85,8 @@ def scan_exposure( Yields: Plan """ - exposure_time = det.cam.acquire_time - yield from bps.abs_set(det.cam.acquire_period, max_exposure + 0.1) + exposure_time = det.drv.acquire_time + yield from bps.abs_set(det.drv.acquire_period, max_exposure + 0.1) yield from bp.scan( [det], exposure_time, diff --git a/src/htss_rig_bluesky/plans/detector.py b/src/htss_rig_bluesky/plans/detector.py index 5ff8467..f7f10ff 100644 --- a/src/htss_rig_bluesky/plans/detector.py +++ b/src/htss_rig_bluesky/plans/detector.py @@ -2,8 +2,7 @@ from dataclasses import dataclass import bluesky.plan_stubs as bps - -from htss_rig_bluesky.devices import AdAravisDetector +from ophyd_async.epics.areadetector.aravis import AravisDetector @dataclass @@ -30,7 +29,7 @@ def max_y(self) -> int: return self.min_y + (self.size_y or 0) -def ensure_detector_ready(det: AdAravisDetector) -> Generator: +def ensure_detector_ready(det: AravisDetector) -> Generator: """ Setup detector for exercises @@ -40,20 +39,20 @@ def ensure_detector_ready(det: AdAravisDetector) -> Generator: Yields: Plan """ - + # TODO: need num exposures too? yield from bps.mv( - det.cam.num_exposures, + det.drv.num_images, 1, - det.cam.num_images, - 1, - det.cam.acquire_period, + det.drv.acquire_period, 0.1, - det.cam.acquire_time, + det.drv.acquire_time, 0.15, + det.hdf.nd_array_port, + "DET.CAM", ) -def set_roi(det: AdAravisDetector, roi: Roi) -> Generator: +def set_roi(det: AravisDetector, roi: Roi) -> Generator: """ Setup detector ROI and frame size @@ -64,16 +63,10 @@ def set_roi(det: AdAravisDetector, roi: Roi) -> Generator: Yields: Plan """ + # Ophyd Async AravisDetector doesn't appear to have signal for max sizes + # eg DET:MaxSizeX_RBVDET:MaxSizeX_RBV - max_x = yield from bps.rd(det.cam.max_size.max_size_x) - max_y = yield from bps.rd(det.cam.max_size.max_size_y) - - sets = { - det.cam.min_x: roi.min_x, - det.cam.min_y: roi.min_y, - det.cam.size.size_x: roi.size_x or max_x, - det.cam.size.size_y: roi.size_y or max_y, - } + 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) diff --git a/src/htss_rig_bluesky/plans/exercise.py b/src/htss_rig_bluesky/plans/exercise.py index 6f3907e..996c34a 100644 --- a/src/htss_rig_bluesky/plans/exercise.py +++ b/src/htss_rig_bluesky/plans/exercise.py @@ -9,12 +9,12 @@ import bluesky.plans as bp from ophyd import PositionerBase -from htss_rig_bluesky.devices import AdAravisDetector, SampleStage +from htss_rig_bluesky.devices import AravisDetector, SampleStage from .detector import ensure_detector_ready -def exercise_beamline(det: AdAravisDetector, sample: SampleStage) -> Generator: +def exercise_beamline(det: AravisDetector, sample: SampleStage) -> Generator: """ Perform all beamline exercise plans sequentially. @@ -48,7 +48,7 @@ def exercise_motors(sample: SampleStage) -> Generator: ) -def exercise_detector(det: AdAravisDetector) -> Generator: +def exercise_detector(det: AravisDetector) -> Generator: """ exercise the detector by taking a frame. @@ -64,12 +64,12 @@ def exercise_detector(det: AdAravisDetector) -> Generator: yield from bp.count([det]) -def exercise_scan(det: AdAravisDetector, sample: SampleStage) -> Generator: +def exercise_scan(det: AravisDetector, sample: SampleStage) -> Generator: """ Perform a short scan to exercise the test rig. Args: - det (AdAravisDetector): Detector + det (AravisDetector): Detector sample (SampleStage): Sample stage Yields: diff --git a/src/htss_rig_bluesky/startup.py b/src/htss_rig_bluesky/startup.py index 5fd33ac..b113a06 100644 --- a/src/htss_rig_bluesky/startup.py +++ b/src/htss_rig_bluesky/startup.py @@ -6,11 +6,13 @@ import matplotlib import matplotlib.pyplot as plt # noqa: F401 import numpy as np # noqa: F401 -from bluesky import RunEngine from bluesky.callbacks.best_effort import BestEffortCallback +from bluesky.run_engine import RunEngine from dodal.utils import make_all_devices +from ophyd_async.core import DeviceCollector # noqa: F401 import htss_rig_bluesky.devices as devices +from htss_rig_bluesky.devices import beam, det, sample # noqa: F401 from htss_rig_bluesky.plans.calibration import scan_center, scan_exposure # noqa: F401 from htss_rig_bluesky.plans.detector import ( # noqa: F401 Roi, @@ -46,6 +48,8 @@ matplotlib.use("QtAgg") +RE = RunEngine() + successful_devices, errors = make_all_devices(devices) if len(errors) > 0: print(f"The following devices failed to connect{errors}") @@ -54,7 +58,6 @@ bec = BestEffortCallback() -RE = RunEngine() RE.subscribe(bec) if os.environ.get("MINIMAL", False):