From 58206364604006a36b46ed7e20e0ee4c560023b9 Mon Sep 17 00:00:00 2001 From: "Ware, Joseph (DLSLtd,RAL,LSCI)" Date: Fri, 30 Aug 2024 15:39:14 +0100 Subject: [PATCH] Update to Pydantic2 and ophyd_async 0.5.x --- pyproject.toml | 6 +- src/dodal/beamlines/i03.py | 12 ++-- src/dodal/beamlines/i13_1.py | 14 ++-- src/dodal/beamlines/i22.py | 33 ++++----- src/dodal/beamlines/p38.py | 28 ++++---- src/dodal/beamlines/p45.py | 22 +++--- src/dodal/beamlines/training_rig.py | 21 ++++-- src/dodal/common/beamlines/beamline_utils.py | 18 ++--- src/dodal/common/types.py | 6 +- src/dodal/common/udc_directory_provider.py | 49 +++++++------ src/dodal/common/visit.py | 72 +++++++++---------- src/dodal/devices/CTAB.py | 2 +- src/dodal/devices/aperture.py | 2 +- src/dodal/devices/aperturescatterguard.py | 36 ++++++---- src/dodal/devices/dcm.py | 2 +- src/dodal/devices/detector/detector.py | 41 ++++++----- src/dodal/devices/detector/detector_motion.py | 2 +- src/dodal/devices/fast_grid_scan.py | 43 ++++++----- src/dodal/devices/focusing_mirror.py | 4 +- src/dodal/devices/i22/dcm.py | 2 +- src/dodal/devices/i22/fswitch.py | 8 ++- src/dodal/devices/i22/nxsas.py | 42 ++++++++--- src/dodal/devices/i24/aperture.py | 2 +- src/dodal/devices/i24/beamstop.py | 2 +- src/dodal/devices/i24/dcm.py | 2 +- src/dodal/devices/i24/i24_detector_motion.py | 2 +- src/dodal/devices/i24/pmac.py | 20 ++++-- src/dodal/devices/linkam3.py | 2 +- src/dodal/devices/motors.py | 2 +- .../devices/oav/oav_to_redis_forwarder.py | 3 +- src/dodal/devices/robot.py | 3 +- src/dodal/devices/scatterguard.py | 2 +- src/dodal/devices/scintillator.py | 2 +- src/dodal/devices/slits.py | 2 +- src/dodal/devices/smargon.py | 2 +- src/dodal/devices/tetramm.py | 27 +++---- .../devices/training_rig/sample_stage.py | 2 +- src/dodal/devices/turbo_slit.py | 2 +- src/dodal/devices/undulator.py | 2 +- src/dodal/devices/util/adjuster_plans.py | 2 +- src/dodal/devices/util/save_panda.py | 2 +- src/dodal/devices/util/test_utils.py | 2 +- src/dodal/devices/xbpm_feedback.py | 3 +- src/dodal/devices/xspress3/xspress3.py | 2 +- src/dodal/devices/zocalo/zocalo_results.py | 8 ++- src/dodal/log.py | 42 ++++++++--- src/dodal/plans/data_session_metadata.py | 17 ++--- src/dodal/plans/motor_util_plans.py | 2 +- tests/common/test_udc_directory_provider.py | 41 +++++------ tests/devices/i22/test_metadataholder.py | 10 ++- tests/devices/unit_tests/conftest.py | 29 +++++--- tests/devices/unit_tests/test_tetramm.py | 37 +++++----- .../devices/unit_tests/test_zocalo_results.py | 2 +- .../unit_tests/util/test_save_panda.py | 2 +- ...ke_beamline_all_devices_raise_exception.py | 2 +- tests/fake_beamline_some_devices_working.py | 2 +- tests/plans/test_motor_util_plans.py | 11 ++- .../preprocessors/test_filesystem_metadata.py | 46 ++++++------ 58 files changed, 448 insertions(+), 358 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c4d31959ce..396fa32b69 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,15 +15,15 @@ description = "Ophyd devices and other utils that could be used across DLS beaml dependencies = [ "click", "ophyd", - "ophyd-async<0.4.0", # Need to pin to <0.4.0 as this requires pydantic>2.0 see https://github.com/DiamondLightSource/dodal/issues/679 + "ophyd-async>=0.5.1", "bluesky", "pyepics", "dataclasses-json", "pillow", - "zocalo>=0.32.0,<1.0.0", # TODO remove pin against <1.0.0, see #679 + "zocalo>=1.0.0", "requests", "graypy", - "pydantic", + "pydantic>=2.0", "opencv-python-headless", # For pin-tip detection. "aioca", # Required for CA support with ophyd-async. "p4p", # Required for PVA support with ophyd-async. diff --git a/src/dodal/beamlines/i03.py b/src/dodal/beamlines/i03.py index 00fecdaccf..4beac5b1f0 100644 --- a/src/dodal/beamlines/i03.py +++ b/src/dodal/beamlines/i03.py @@ -1,13 +1,13 @@ -from ophyd_async.panda import HDFPanda +from ophyd_async.fastcs.panda import HDFPanda from dodal.common.beamlines.beamline_parameters import get_beamline_parameters from dodal.common.beamlines.beamline_utils import ( device_instantiation, - get_directory_provider, - set_directory_provider, + get_path_provider, + set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.common.udc_directory_provider import PandASubdirectoryProvider +from dodal.common.udc_directory_provider import PandASubpathProvider from dodal.devices.aperturescatterguard import ( ApertureScatterguard, load_positions_from_beamline_parameters, @@ -52,7 +52,7 @@ set_log_beamline(BL) set_utils_beamline(BL) -set_directory_provider(PandASubdirectoryProvider()) +set_path_provider(PandASubpathProvider()) def aperture_scatterguard( @@ -378,7 +378,7 @@ def panda( "-EA-PANDA-01:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) diff --git a/src/dodal/beamlines/i13_1.py b/src/dodal/beamlines/i13_1.py index 05f3779676..fcd354a721 100644 --- a/src/dodal/beamlines/i13_1.py +++ b/src/dodal/beamlines/i13_1.py @@ -1,14 +1,14 @@ from pathlib import Path -from ophyd_async.epics.areadetector.aravis import AravisDetector +from ophyd_async.epics.adaravis import AravisDetector from dodal.common.beamlines.beamline_utils import ( device_instantiation, - get_directory_provider, - set_directory_provider, + get_path_provider, + set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.common.visit import StaticVisitDirectoryProvider +from dodal.common.visit import StaticVisitPathProvider from dodal.devices.motors import XYZPositioner from dodal.log import set_beamline as set_log_beamline from dodal.utils import get_beamline_name @@ -16,8 +16,8 @@ BL = get_beamline_name("i13-1") set_log_beamline(BL) set_utils_beamline(BL) -set_directory_provider( - StaticVisitDirectoryProvider( +set_path_provider( + StaticVisitPathProvider( BL, Path("/data/2024/cm37257-4/"), # latest commissioning visit ) @@ -60,7 +60,7 @@ def side_camera( bl_prefix=False, drv_suffix="CAM:", hdf_suffix="HDF5:", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), wait=wait_for_connection, fake=fake_with_ophyd_sim, ) diff --git a/src/dodal/beamlines/i22.py b/src/dodal/beamlines/i22.py index 5984dcba87..7251600dc5 100644 --- a/src/dodal/beamlines/i22.py +++ b/src/dodal/beamlines/i22.py @@ -1,16 +1,17 @@ from pathlib import Path -from ophyd_async.epics.areadetector import AravisDetector, PilatusDetector -from ophyd_async.panda import HDFPanda +from ophyd_async.epics.adaravis import AravisDetector +from ophyd_async.epics.adpilatus import PilatusDetector +from ophyd_async.fastcs.panda import HDFPanda from dodal.common.beamlines.beamline_utils import ( device_instantiation, - get_directory_provider, - set_directory_provider, + get_path_provider, + set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.common.beamlines.device_helpers import numbered_slits -from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider +from dodal.common.visit import DirectoryServiceClient, StaticVisitPathProvider from dodal.devices.focusing_mirror import FocusingMirror from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator from dodal.devices.i22.fswitch import FSwitch @@ -32,8 +33,8 @@ # Communication with GDA is also WIP so for now we determine an arbitrary scan number # locally and write the commissioning directory. The scan number is not guaranteed to # be unique and the data is at risk - this configuration is for testing only. -set_directory_provider( - StaticVisitDirectoryProvider( +set_path_provider( + StaticVisitPathProvider( BL, Path("/dls/i22/data/2024/cm37271-2/bluesky"), client=DirectoryServiceClient("http://i22-control:8088/api"), @@ -61,7 +62,7 @@ def saxs( sensor_thickness=(0.45, "mm"), distance=(4711.833684146172, "mm"), ), - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -97,7 +98,7 @@ def waxs( sensor_thickness=(0.45, "mm"), distance=(175.4199417092314, "mm"), ), - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -112,7 +113,7 @@ def i0( wait_for_connection, fake_with_ophyd_sim, type="Cividec Diamond XBPM", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -127,7 +128,7 @@ def it( wait_for_connection, fake_with_ophyd_sim, type="PIN Diode", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -295,7 +296,7 @@ def panda1( "-EA-PANDA-01:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -310,7 +311,7 @@ def panda2( "-EA-PANDA-02:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -325,7 +326,7 @@ def panda3( "-EA-PANDA-03:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -340,7 +341,7 @@ def panda4( "-EA-PANDA-04:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -361,7 +362,7 @@ def oav( description="AVT Mako G-507B", distance=(-1.0, "m"), ), - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) diff --git a/src/dodal/beamlines/p38.py b/src/dodal/beamlines/p38.py index bca78b1e79..11da31b0da 100644 --- a/src/dodal/beamlines/p38.py +++ b/src/dodal/beamlines/p38.py @@ -1,16 +1,16 @@ from pathlib import Path -from ophyd_async.epics.areadetector import AravisDetector -from ophyd_async.panda import HDFPanda +from ophyd_async.epics.adaravis import AravisDetector +from ophyd_async.fastcs.panda import HDFPanda from dodal.common.beamlines.beamline_utils import ( device_instantiation, - get_directory_provider, - set_directory_provider, + get_path_provider, + set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.common.beamlines.device_helpers import numbered_slits -from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitDirectoryProvider +from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider from dodal.devices.focusing_mirror import FocusingMirror from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator from dodal.devices.i22.fswitch import FSwitch @@ -30,8 +30,8 @@ # Communication with GDA is also WIP so for now we determine an arbitrary scan number # locally and write the commissioning directory. The scan number is not guaranteed to # be unique and the data is at risk - this configuration is for testing only. -set_directory_provider( - StaticVisitDirectoryProvider( +set_path_provider( + StaticVisitPathProvider( BL, Path("/dls/p38/data/2024/cm37282-2/bluesky"), client=LocalDirectoryServiceClient(), @@ -50,7 +50,7 @@ def d3( fake_with_ophyd_sim, drv_suffix="DET:", hdf_suffix="HDF5:", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -67,7 +67,7 @@ def d11( fake_with_ophyd_sim, drv_suffix="DET:", hdf_suffix="HDF5:", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -82,7 +82,7 @@ def d12( fake_with_ophyd_sim, drv_suffix="DET:", hdf_suffix="HDF5:", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -96,7 +96,7 @@ def i0( "-EA-XBPM-01:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -273,7 +273,7 @@ def panda1( "-EA-PANDA-01:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -288,7 +288,7 @@ def panda2( "-EA-PANDA-02:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -303,7 +303,7 @@ def panda3( "-EA-PANDA-03:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) diff --git a/src/dodal/beamlines/p45.py b/src/dodal/beamlines/p45.py index 3b105ccd9b..4a55060f38 100644 --- a/src/dodal/beamlines/p45.py +++ b/src/dodal/beamlines/p45.py @@ -1,15 +1,15 @@ from pathlib import Path -from ophyd_async.epics.areadetector import AravisDetector -from ophyd_async.panda import HDFPanda +from ophyd_async.epics.adaravis import AravisDetector +from ophyd_async.fastcs.panda import HDFPanda from dodal.common.beamlines.beamline_utils import ( device_instantiation, - get_directory_provider, - set_directory_provider, + get_path_provider, + set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.common.visit import StaticVisitDirectoryProvider +from dodal.common.visit import StaticVisitPathProvider from dodal.devices.p45 import Choppers, TomoStageWithStretchAndSkew from dodal.log import set_beamline as set_log_beamline from dodal.utils import get_beamline_name, skip_device @@ -17,8 +17,8 @@ BL = get_beamline_name("p45") set_log_beamline(BL) set_utils_beamline(BL) -set_directory_provider( - StaticVisitDirectoryProvider( +set_path_provider( + StaticVisitPathProvider( BL, Path("/data/2024/cm37283-2/"), # latest commissioning visit ) @@ -62,7 +62,7 @@ def det( fake_with_ophyd_sim, drv_suffix="DET:", hdf_suffix="HDF5:", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -79,7 +79,7 @@ def diff( fake_with_ophyd_sim, drv_suffix="DET:", hdf_suffix="HDF5:", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -97,7 +97,7 @@ def panda1( "-MO-PANDA-01:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -112,5 +112,5 @@ def panda2( "-MO-PANDA-02:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) diff --git a/src/dodal/beamlines/training_rig.py b/src/dodal/beamlines/training_rig.py index 3d31794444..3c026c6576 100644 --- a/src/dodal/beamlines/training_rig.py +++ b/src/dodal/beamlines/training_rig.py @@ -1,10 +1,14 @@ from pathlib import Path -from ophyd_async.core import StaticDirectoryProvider -from ophyd_async.epics.areadetector.aravis import AravisDetector +from ophyd_async.epics.adaravis import AravisDetector -from dodal.common.beamlines.beamline_utils import device_instantiation +from dodal.common.beamlines.beamline_utils import ( + device_instantiation, + get_path_provider, + set_path_provider, +) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider from dodal.devices.training_rig.sample_stage import TrainingRigSampleStage from dodal.log import set_beamline as set_log_beamline from dodal.utils import get_beamline_name @@ -24,6 +28,14 @@ set_log_beamline(BL) set_utils_beamline(BL) +set_path_provider( + StaticVisitPathProvider( + BL, + Path("/exports/mybeamline/data"), + client=LocalDirectoryServiceClient(), + ) +) + def sample_stage( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False @@ -40,7 +52,6 @@ def sample_stage( def det( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> AravisDetector: - directory_provider = StaticDirectoryProvider(Path("/exports/mybeamline/data")) return device_instantiation( AravisDetector, "det", @@ -49,5 +60,5 @@ def det( fake_with_ophyd_sim, drv_suffix="DET:", hdf_suffix="HDF5:", - directory_provider=directory_provider, + path_provider=get_path_provider(), ) diff --git a/src/dodal/common/beamlines/beamline_utils.py b/src/dodal/common/beamlines/beamline_utils.py index 4ff68ca5d9..ac1418e44d 100644 --- a/src/dodal/common/beamlines/beamline_utils.py +++ b/src/dodal/common/beamlines/beamline_utils.py @@ -8,14 +8,14 @@ from ophyd_async.core import Device as OphydV2Device from ophyd_async.core import wait_for_connection as v2_device_wait_for_connection -from dodal.common.types import UpdatingDirectoryProvider +from dodal.common.types import UpdatingPathProvider from dodal.utils import AnyDevice, BeamlinePrefix, skip_device DEFAULT_CONNECTION_TIMEOUT: Final[float] = 5.0 ACTIVE_DEVICES: dict[str, AnyDevice] = {} BL = "" -DIRECTORY_PROVIDER: UpdatingDirectoryProvider | None = None +PATH_PROVIDER: UpdatingPathProvider | None = None def set_beamline(beamline: str): @@ -125,15 +125,15 @@ def device_instantiation( return device_instance -def set_directory_provider(provider: UpdatingDirectoryProvider): - global DIRECTORY_PROVIDER +def set_path_provider(provider: UpdatingPathProvider): + global PATH_PROVIDER - DIRECTORY_PROVIDER = provider + PATH_PROVIDER = provider -def get_directory_provider() -> UpdatingDirectoryProvider: - if DIRECTORY_PROVIDER is None: +def get_path_provider() -> UpdatingPathProvider: + if PATH_PROVIDER is None: raise ValueError( - "DirectoryProvider has not been set! Ophyd-async StandardDetectors will not be able to write!" + "PathProvider has not been set! Ophyd-async StandardDetectors will not be able to write!" ) - return DIRECTORY_PROVIDER + return PATH_PROVIDER diff --git a/src/dodal/common/types.py b/src/dodal/common/types.py index 05c62ff37a..22c546c655 100644 --- a/src/dodal/common/types.py +++ b/src/dodal/common/types.py @@ -5,7 +5,7 @@ ) from bluesky.utils import Msg -from ophyd_async.core import DirectoryProvider +from ophyd_async.core import PathProvider # String identifier used by 'wait' or stubs that await Group = str @@ -15,6 +15,8 @@ PlanGenerator = Callable[..., MsgGenerator] -class UpdatingDirectoryProvider(DirectoryProvider, ABC): +class UpdatingPathProvider(PathProvider, ABC): + @abstractmethod + async def data_session(self) -> str: ... @abstractmethod async def update(self, **kwargs) -> None: ... diff --git a/src/dodal/common/udc_directory_provider.py b/src/dodal/common/udc_directory_provider.py index 3e8f79a4bb..6839358a6c 100644 --- a/src/dodal/common/udc_directory_provider.py +++ b/src/dodal/common/udc_directory_provider.py @@ -1,27 +1,35 @@ from pathlib import Path -from ophyd_async.core import DirectoryInfo +from ophyd_async.core import FilenameProvider, PathInfo -from dodal.common.types import UpdatingDirectoryProvider +from dodal.common.types import UpdatingPathProvider from dodal.log import LOGGER -class PandASubdirectoryProvider(UpdatingDirectoryProvider): +class PandAFilenameProvider(FilenameProvider): + def __init__(self, suffix: str | None = None): + self.suffix = suffix + + def __call__(self, device_name: str | None = None): + return f"{device_name}-{self.suffix}" + + +class PandASubpathProvider(UpdatingPathProvider): """Directory provider for the HDFPanda. Points to a panda subdirectory within the directory path provided""" resource_dir = Path("panda") - def __init__(self, directory: Path | None = None): - if directory is None: + def __init__(self, directory: Path | None = None, suffix: str = ""): + self._output_directory: Path | None = directory + self._filename_provider = PandAFilenameProvider(suffix=suffix) + if self._output_directory is None: LOGGER.debug( f"{self.__class__.__name__} instantiated with no root path, update() must be called before writing data!" ) - self._directory_info = ( - DirectoryInfo(root=directory, resource_dir=self.resource_dir) - if directory - else None - ) + + async def data_session(self) -> str: + return self._filename_provider.suffix or "" async def update(self, *, directory: Path, suffix: str = "", **kwargs): """Update the root directory into which panda pcap files are written. This will result in the panda @@ -32,16 +40,13 @@ async def update(self, *, directory: Path, suffix: str = "", **kwargs): suffix: Optional str that will be appended to the panda device name along with the file type extension to construct the output filename """ - output_directory = directory / self.resource_dir - output_directory.mkdir(exist_ok=True) - - self._directory_info = DirectoryInfo( - root=directory, resource_dir=self.resource_dir, suffix=suffix + self._output_directory = directory / self.resource_dir + self._filename_provider.suffix = suffix + + def __call__(self, device_name: str | None = None) -> PathInfo: + assert self._output_directory + return PathInfo( + directory_path=self._output_directory, + filename=self._filename_provider(device_name), + create_dir_depth=-1, # allows PandA HDFWriter to make any number of dirs ) - - def __call__(self) -> DirectoryInfo: - if self._directory_info is None: - raise ValueError( - "Directory unknown for PandA to write into, update() needs to be called at least once" - ) - return self._directory_info diff --git a/src/dodal/common/visit.py b/src/dodal/common/visit.py index ed97e1ef4f..c8cec4385d 100644 --- a/src/dodal/common/visit.py +++ b/src/dodal/common/visit.py @@ -2,10 +2,10 @@ from pathlib import Path from aiohttp import ClientSession -from ophyd_async.core import DirectoryInfo +from ophyd_async.core import FilenameProvider, PathInfo from pydantic import BaseModel -from dodal.common.types import UpdatingDirectoryProvider +from dodal.common.types import UpdatingPathProvider from dodal.log import LOGGER """ @@ -36,6 +36,18 @@ async def get_current_collection(self) -> DataCollectionIdentifier: """Get current collection""" +class DiamondFilenameProvider(FilenameProvider): + def __init__(self, beamline: str, client: DirectoryServiceClientBase): + self._beamline = beamline + self._client = client + self.collectionId: DataCollectionIdentifier | None = None + + def __call__(self, device_name: str | None = None): + assert device_name, "Diamond filename requires device_name to be passed" + assert self.collectionId is not None + return f"{self._beamline}-{self.collectionId.collectionNumber}-{device_name}" + + class DirectoryServiceClient(DirectoryServiceClientBase): """Client for the VisitService REST API Currently exposed by the GDA Server to co-ordinate unique filenames. @@ -52,7 +64,7 @@ async def create_new_collection(self) -> DataCollectionIdentifier: async with session.post(f"{self._url}/numtracker") as response: response.raise_for_status() json = await response.json() - new_collection = DataCollectionIdentifier.parse_obj(json) + new_collection = DataCollectionIdentifier.model_validate_json(json) LOGGER.debug("New DataCollection: %s", new_collection) return new_collection @@ -61,7 +73,7 @@ async def get_current_collection(self) -> DataCollectionIdentifier: async with session.get(f"{self._url}/numtracker") as response: response.raise_for_status() json = await response.json() - current_collection = DataCollectionIdentifier.parse_obj(json) + current_collection = DataCollectionIdentifier.model_validate_json(json) LOGGER.debug("Current DataCollection: %s", current_collection) return current_collection @@ -84,22 +96,16 @@ async def get_current_collection(self) -> DataCollectionIdentifier: return DataCollectionIdentifier(collectionNumber=self._count) -class StaticVisitDirectoryProvider(UpdatingDirectoryProvider): +class StaticVisitPathProvider(UpdatingPathProvider): """ - Static (single visit) implementation of DirectoryProvider whilst awaiting auth infrastructure to generate necessary information per-scan. + Static (single visit) implementation of PathProvider whilst awaiting auth infrastructure to generate necessary information per-scan. Allows setting a singular visit into which all run files will be saved. update() queries a visit service to get the next DataCollectionIdentifier to increment the suffix of all file writers' next files. Requires that all detectors are running with a mutual view on the filesystem. Supports a single Visit which should be passed as a Path relative to the root of the Detector IOC mounting. - i.e. to write to visit /dls/ixx/data/YYYY/cm12345-1, assuming all detectors are mounted with /data -> /dls/ixx/data, root=/data/YYYY/cm12345-1/ + i.e. to write to visit /dls/ixx/data/YYYY/cm12345-1 """ - _beamline: str - _root: Path - _client: DirectoryServiceClientBase - _current_collection: DirectoryInfo | None - _session: ClientSession | None - def __init__( self, beamline: str, @@ -108,9 +114,10 @@ def __init__( ): self._beamline = beamline self._client = client or DirectoryServiceClient(f"{beamline}-control:8088/api") + self._filename_provider = DiamondFilenameProvider(self._beamline, self._client) self._root = root - self._current_collection = None - self._session = None + self.current_collection: PathInfo | None + self._session: ClientSession | None async def update(self, **kwargs) -> None: """ @@ -121,33 +128,22 @@ async def update(self, **kwargs) -> None: # TODO: DAQ-4827: Pass AuthN information as part of request try: - collection_id_info = await self._client.create_new_collection() - self._current_collection = self._generate_directory_info(collection_id_info) + self._filename_provider.collectionId = ( + await self._client.create_new_collection() + ) except Exception: LOGGER.error( "Exception while updating data collection, preventing overwriting data by setting current_collection to None" ) - self._current_collection = None + self._collection_id_info = None raise - def _generate_directory_info( - self, - collection_id_info: DataCollectionIdentifier, - ) -> DirectoryInfo: - return DirectoryInfo( - # See DocString of DirectoryInfo. At DLS, root = visit directory, resource_dir is relative to it. - root=self._root, - # https://github.com/DiamondLightSource/dodal/issues/452 - # Currently all h5 files written to visit/ directory, as no guarantee that visit/dataCollection/ directory will have been produced. If it is as part of #452, append the resource_dir - resource_dir=Path("."), - # Diamond standard file naming - prefix=f"{self._beamline}-{collection_id_info.collectionNumber}-", - ) + async def data_session(self) -> str: + collection = await self._client.get_current_collection() + return f"{self._beamline}-{collection.collectionNumber}" - def __call__(self) -> DirectoryInfo: - if self._current_collection is not None: - return self._current_collection - else: - raise ValueError( - "No current collection, update() needs to be called at least once" - ) + def __call__(self, device_name: str | None = None) -> PathInfo: + assert device_name, "Must call PathProvider with device_name" + return PathInfo( + directory_path=self._root, filename=self._filename_provider(device_name) + ) diff --git a/src/dodal/devices/CTAB.py b/src/dodal/devices/CTAB.py index 6e2a3511f0..f8dacc0897 100644 --- a/src/dodal/devices/CTAB.py +++ b/src/dodal/devices/CTAB.py @@ -1,5 +1,5 @@ from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import epics_signal_r diff --git a/src/dodal/devices/aperture.py b/src/dodal/devices/aperture.py index 4388c3777d..b007c402f9 100644 --- a/src/dodal/devices/aperture.py +++ b/src/dodal/devices/aperture.py @@ -1,5 +1,5 @@ from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import epics_signal_r diff --git a/src/dodal/devices/aperturescatterguard.py b/src/dodal/devices/aperturescatterguard.py index e85cf727dd..7e20a08f5a 100644 --- a/src/dodal/devices/aperturescatterguard.py +++ b/src/dodal/devices/aperturescatterguard.py @@ -5,8 +5,13 @@ from bluesky.protocols import Movable, Reading from event_model.documents.event_descriptor import DataKey -from ophyd_async.core import AsyncStatus, HintedSignal, SignalR, StandardReadable -from ophyd_async.core.soft_signal_backend import SoftConverter, SoftSignalBackend +from ophyd_async.core import ( + AsyncStatus, + HintedSignal, + SignalR, + SoftSignalBackend, + StandardReadable, +) from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters from dodal.devices.aperture import Aperture @@ -115,6 +120,20 @@ def load_positions_from_beamline_parameters( } +def _reading( + self, value: SingleAperturePosition, timestamp: float, severity: int +) -> Reading: + return Reading( + value=asdict(value), + timestamp=timestamp, + alarm_severity=-1 if severity > 2 else severity, + ) + + +def _ApertureConverter(soft_converter): + soft_converter.reading = _reading # type: ignore + + class ApertureScatterguard(StandardReadable, Movable): def __init__( self, @@ -130,22 +149,11 @@ def __init__( aperture_backend = SoftSignalBackend( SingleAperturePosition, self._loaded_positions[AperturePosition.ROBOT_LOAD] ) - aperture_backend.converter = self.ApertureConverter() + aperture_backend.converter = _ApertureConverter(aperture_backend.converter) # type: ignore self.selected_aperture = self.SelectedAperture(backend=aperture_backend) self.add_readables([self.selected_aperture], wrapper=HintedSignal) super().__init__(name) - class ApertureConverter(SoftConverter): - # Ophyd-async #311 should add a default converter for dataclasses to do this - def reading( - self, value: SingleAperturePosition, timestamp: float, severity: int - ) -> Reading: - return Reading( - value=asdict(value), - timestamp=timestamp, - alarm_severity=-1 if severity > 2 else severity, - ) - class SelectedAperture(SignalR): async def read(self, *args, **kwargs): assert isinstance(self.parent, ApertureScatterguard) diff --git a/src/dodal/devices/dcm.py b/src/dodal/devices/dcm.py index 9b6c3bed71..cb303ce617 100644 --- a/src/dodal/devices/dcm.py +++ b/src/dodal/devices/dcm.py @@ -1,5 +1,5 @@ from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import epics_signal_r diff --git a/src/dodal/devices/detector/detector.py b/src/dodal/devices/detector/detector.py index 7b4019b89c..d7f5d0f094 100644 --- a/src/dodal/devices/detector/detector.py +++ b/src/dodal/devices/detector/detector.py @@ -1,7 +1,13 @@ from enum import Enum, auto from typing import Any -from pydantic import BaseModel, root_validator, validator +from pydantic import ( + BaseModel, + ConfigDict, + field_serializer, + field_validator, + model_validator, +) from dodal.devices.detector.det_dim_constants import ( EIGER2_X_16M_SIZE, @@ -42,20 +48,23 @@ class DetectorParams(BaseModel): trigger_mode: TriggerMode = TriggerMode.SET_FRAMES detector_size_constants: DetectorSizeConstants = EIGER2_X_16M_SIZE beam_xy_converter: DetectorDistanceToBeamXYConverter = None # type: ignore # Filled in by validator - run_number: int = None # type: ignore # Filled in by validator + run_number: int | None = None # type: ignore # Filled in by validator enable_dev_shm: bool = ( False # Remove in https://github.com/DiamondLightSource/hyperion/issues/1395 ) + model_config = ConfigDict( + arbitrary_types_allowed=True, + ) + + @field_serializer("beam_xy_converter") + def serialize_beam_xy_converter(self, _: DetectorDistanceToBeamXYConverter): + return None - class Config: - arbitrary_types_allowed = True - json_encoders = { - DetectorDistanceToBeamXYConverter: lambda _: None, - DetectorSizeConstants: lambda d: d.det_type_string, - } + @field_serializer("detector_size_constants") + def serialize_detector_size_constants(self, size: DetectorSizeConstants): + return size.det_type_string - # should be replaced with model_validator once move to pydantic 2 is complete - @root_validator(pre=True) + @model_validator(mode="before") def create_beamxy_and_runnumber(cls, values: dict[str, Any]) -> dict[str, Any]: values["beam_xy_converter"] = DetectorDistanceToBeamXYConverter( values["det_dist_to_beam_converter_path"] @@ -64,18 +73,18 @@ def create_beamxy_and_runnumber(cls, values: dict[str, Any]) -> dict[str, Any]: values["run_number"] = get_run_number(values["directory"], values["prefix"]) return values - @validator("detector_size_constants", pre=True) - def _parse_detector_size_constants( - cls, det_type: str, values: dict[str, Any] - ) -> DetectorSizeConstants: + @field_validator("detector_size_constants", mode="before") + @classmethod + def _parse_detector_size_constants(cls, det_type: str) -> DetectorSizeConstants: return ( det_type if isinstance(det_type, DetectorSizeConstants) else constants_from_type(det_type) ) - @validator("directory", pre=True) - def _parse_directory(cls, directory: str, values: dict[str, Any]) -> str: + @field_validator("directory", mode="before") + @classmethod + def _parse_directory(cls, directory: str) -> str: if not directory.endswith("/"): directory += "/" return directory diff --git a/src/dodal/devices/detector/detector_motion.py b/src/dodal/devices/detector/detector_motion.py index 05306e504d..c200a6ab8f 100644 --- a/src/dodal/devices/detector/detector_motion.py +++ b/src/dodal/devices/detector/detector_motion.py @@ -1,7 +1,7 @@ from enum import Enum from ophyd_async.core import Device -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw diff --git a/src/dodal/devices/fast_grid_scan.py b/src/dodal/devices/fast_grid_scan.py index a7706e0535..8f3d7c2d14 100644 --- a/src/dodal/devices/fast_grid_scan.py +++ b/src/dodal/devices/fast_grid_scan.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Any, Generic, TypeVar +from typing import Generic, TypeVar import numpy as np from bluesky.plan_stubs import mv @@ -20,7 +20,7 @@ epics_signal_rw_rbv, epics_signal_x, ) -from pydantic import validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator from pydantic.dataclasses import dataclass from dodal.log import LOGGER @@ -69,9 +69,10 @@ class GridScanParamsCommon(AbstractExperimentWithBeamParams): y2_start: float = 0.1 z1_start: float = 0.1 z2_start: float = 0.1 - x_axis: GridAxis = GridAxis(0, 0, 0) - y_axis: GridAxis = GridAxis(0, 0, 0) - z_axis: GridAxis = GridAxis(0, 0, 0) + x_axis: GridAxis = Field(default=GridAxis(0, 0, 0), exclude=True) + y_axis: GridAxis = Field(default=GridAxis(0, 0, 0), exclude=True) + z_axis: GridAxis = Field(default=GridAxis(0, 0, 0), exclude=True) + model_config = ConfigDict(arbitrary_types_allowed=True) # Whether to set the stub offsets after centering set_stub_offsets: bool = False @@ -91,25 +92,20 @@ def get_param_positions(self) -> dict: "z2_start": self.z2_start, } - class Config: - arbitrary_types_allowed = True - fields = { - "x_axis": {"exclude": True}, - "y_axis": {"exclude": True}, - "z_axis": {"exclude": True}, - } - - @validator("x_axis", always=True) - def _get_x_axis(cls, x_axis: GridAxis, values: dict[str, Any]) -> GridAxis: - return GridAxis(values["x_start"], values["x_step_size"], values["x_steps"]) + @field_validator("x_axis") + @classmethod + def _get_x_axis(cls, _: GridAxis, v: ValidationInfo) -> GridAxis: + return GridAxis(v.data["x_start"], v.data["x_step_size"], v.data["x_steps"]) - @validator("y_axis", always=True) - def _get_y_axis(cls, y_axis: GridAxis, values: dict[str, Any]) -> GridAxis: - return GridAxis(values["y1_start"], values["y_step_size"], values["y_steps"]) + @field_validator("y_axis") + @classmethod + def _get_y_axis(cls, _: GridAxis, v: ValidationInfo) -> GridAxis: + return GridAxis(v.data["y1_start"], v.data["y_step_size"], v.data["y_steps"]) - @validator("z_axis", always=True) - def _get_z_axis(cls, z_axis: GridAxis, values: dict[str, Any]) -> GridAxis: - return GridAxis(values["z2_start"], values["z_step_size"], values["z_steps"]) + @field_validator("z_axis") + @classmethod + def _get_z_axis(cls, _: GridAxis, v: ValidationInfo) -> GridAxis: + return GridAxis(v.data["z2_start"], v.data["z_step_size"], v.data["z_steps"]) def get_num_images(self): return self.x_steps * self.y_steps + self.x_steps * self.z_steps @@ -155,7 +151,8 @@ def get_param_positions(self): param_positions["dwell_time_ms"] = self.dwell_time_ms return param_positions - @validator("dwell_time_ms", always=True, check_fields=True) + @field_validator("dwell_time_ms") + @classmethod def non_integer_dwell_time(cls, dwell_time_ms: float) -> float: dwell_time_floor_rounded = np.floor(dwell_time_ms) dwell_time_is_close = np.isclose( diff --git a/src/dodal/devices/focusing_mirror.py b/src/dodal/devices/focusing_mirror.py index 0d693be039..c8fe794e98 100644 --- a/src/dodal/devices/focusing_mirror.py +++ b/src/dodal/devices/focusing_mirror.py @@ -8,9 +8,9 @@ HintedSignal, StandardReadable, observe_value, + soft_signal_r_and_setter, ) -from ophyd_async.core.signal import soft_signal_r_and_setter -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import ( epics_signal_r, epics_signal_rw, diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index 6ff5f8430e..6424dfc604 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -6,7 +6,7 @@ from bluesky.protocols import Reading from event_model.documents.event_descriptor import DataKey from ophyd_async.core import ConfigSignal, StandardReadable, soft_signal_r_and_setter -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import epics_signal_r # Conversion constant for energy and wavelength, taken from the X-Ray data booklet diff --git a/src/dodal/devices/i22/fswitch.py b/src/dodal/devices/i22/fswitch.py index b4c7ff763b..1bde0ffbda 100644 --- a/src/dodal/devices/i22/fswitch.py +++ b/src/dodal/devices/i22/fswitch.py @@ -4,8 +4,12 @@ from bluesky.protocols import Reading from event_model import DataKey -from ophyd_async.core import ConfigSignal, StandardReadable, soft_signal_r_and_setter -from ophyd_async.core.device import DeviceVector +from ophyd_async.core import ( + ConfigSignal, + DeviceVector, + StandardReadable, + soft_signal_r_and_setter, +) from ophyd_async.epics.signal import epics_signal_r diff --git a/src/dodal/devices/i22/nxsas.py b/src/dodal/devices/i22/nxsas.py index 2ed1565a83..b7b7299596 100644 --- a/src/dodal/devices/i22/nxsas.py +++ b/src/dodal/devices/i22/nxsas.py @@ -1,12 +1,32 @@ +import asyncio +from collections.abc import Awaitable, Iterable from dataclasses import dataclass, fields +from typing import TypeVar from bluesky.protocols import Reading from event_model.documents.event_descriptor import DataKey -from ophyd_async.core import DirectoryProvider, merge_gathered_dicts -from ophyd_async.epics.areadetector import AravisDetector, PilatusDetector -from ophyd_async.epics.areadetector.aravis import AravisController +from ophyd_async.core import PathProvider +from ophyd_async.epics.adaravis import AravisController, AravisDetector +from ophyd_async.epics.adpilatus import PilatusDetector ValueAndUnits = tuple[float, str] +T = TypeVar("T") + + +# Temporarily duplicated non-public method from ophyd_async +async def _merge_gathered_dicts( + coros: Iterable[Awaitable[dict[str, T]]], +) -> dict[str, T]: + """Merge dictionaries produced by a sequence of coroutines. + + Can be used for merging ``read()`` or ``describe``. For instance:: + + combined_read = await merge_gathered_dicts(s.read() for s in signals) + """ + ret: dict[str, T] = {} + for result in await asyncio.gather(*coros): + ret.update(result) + return ret @dataclass @@ -80,7 +100,7 @@ class NXSasPilatus(PilatusDetector): def __init__( self, prefix: str, - directory_provider: DirectoryProvider, + path_provider: PathProvider, drv_suffix: str, hdf_suffix: str, metadata_holder: NXSasMetadataHolder, @@ -93,7 +113,7 @@ def __init__( Writes hdf5 files.""" super().__init__( prefix, - directory_provider, + path_provider, drv_suffix=drv_suffix, hdf_suffix=hdf_suffix, name=name, @@ -101,7 +121,7 @@ def __init__( self._metadata_holder = metadata_holder async def read_configuration(self) -> dict[str, Reading]: - return await merge_gathered_dicts( + return await _merge_gathered_dicts( r for r in ( super().read_configuration(), @@ -110,7 +130,7 @@ async def read_configuration(self) -> dict[str, Reading]: ) async def describe_configuration(self) -> dict[str, DataKey]: - return await merge_gathered_dicts( + return await _merge_gathered_dicts( r for r in ( super().describe_configuration(), @@ -123,7 +143,7 @@ class NXSasOAV(AravisDetector): def __init__( self, prefix: str, - directory_provider: DirectoryProvider, + path_provider: PathProvider, drv_suffix: str, hdf_suffix: str, metadata_holder: NXSasMetadataHolder, @@ -137,7 +157,7 @@ def __init__( Writes hdf5 files.""" super().__init__( prefix, - directory_provider, + path_provider, drv_suffix=drv_suffix, hdf_suffix=hdf_suffix, name=name, @@ -146,7 +166,7 @@ def __init__( self._metadata_holder = metadata_holder async def read_configuration(self) -> dict[str, Reading]: - return await merge_gathered_dicts( + return await _merge_gathered_dicts( r for r in ( super().read_configuration(), @@ -155,7 +175,7 @@ async def read_configuration(self) -> dict[str, Reading]: ) async def describe_configuration(self) -> dict[str, DataKey]: - return await merge_gathered_dicts( + return await _merge_gathered_dicts( r for r in ( super().describe_configuration(), diff --git a/src/dodal/devices/i24/aperture.py b/src/dodal/devices/i24/aperture.py index 5a855e344b..55df736f72 100644 --- a/src/dodal/devices/i24/aperture.py +++ b/src/dodal/devices/i24/aperture.py @@ -1,7 +1,7 @@ from enum import Enum from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import epics_signal_rw diff --git a/src/dodal/devices/i24/beamstop.py b/src/dodal/devices/i24/beamstop.py index 1d8cc4c337..a5c2ec52b6 100644 --- a/src/dodal/devices/i24/beamstop.py +++ b/src/dodal/devices/i24/beamstop.py @@ -1,7 +1,7 @@ from enum import Enum from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import epics_signal_rw diff --git a/src/dodal/devices/i24/dcm.py b/src/dodal/devices/i24/dcm.py index 45182ea1dc..e40357f5e3 100644 --- a/src/dodal/devices/i24/dcm.py +++ b/src/dodal/devices/i24/dcm.py @@ -1,5 +1,5 @@ from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import epics_signal_r diff --git a/src/dodal/devices/i24/i24_detector_motion.py b/src/dodal/devices/i24/i24_detector_motion.py index 9dc0ff0fe6..b0a3fb9350 100644 --- a/src/dodal/devices/i24/i24_detector_motion.py +++ b/src/dodal/devices/i24/i24_detector_motion.py @@ -1,5 +1,5 @@ from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor class DetectorMotion(StandardReadable): diff --git a/src/dodal/devices/i24/pmac.py b/src/dodal/devices/i24/pmac.py index 706d4c5fd2..12832ab8d8 100644 --- a/src/dodal/devices/i24/pmac.py +++ b/src/dodal/devices/i24/pmac.py @@ -2,12 +2,18 @@ from typing import SupportsFloat from bluesky.protocols import Triggerable -from ophyd_async.core import AsyncStatus, StandardReadable, wait_for_value -from ophyd_async.core.signal import CalculateTimeout, SignalR, SignalRW -from ophyd_async.core.signal_backend import SignalBackend -from ophyd_async.core.soft_signal_backend import SoftSignalBackend -from ophyd_async.core.utils import DEFAULT_TIMEOUT -from ophyd_async.epics.motion import Motor +from ophyd_async.core import ( + DEFAULT_TIMEOUT, + AsyncStatus, + CalculateTimeout, + SignalBackend, + SignalR, + SignalRW, + SoftSignalBackend, + StandardReadable, + wait_for_value, +) +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw HOME_STR = r"\#1hmz\#2hmz\#3hmz" # Command to home the PMAC motors @@ -121,7 +127,7 @@ def __init__( super().__init__(backend, timeout, name) @AsyncStatus.wrap - async def set(self, value: int, wait=True, timeout=None): + async def set(self, value: int, wait: bool = True, timeout: float | None = None): prog_str = f"&2b{value}r" assert isinstance(timeout, SupportsFloat) or ( timeout is None diff --git a/src/dodal/devices/linkam3.py b/src/dodal/devices/linkam3.py index 31621b9747..7f1c0b77c9 100644 --- a/src/dodal/devices/linkam3.py +++ b/src/dodal/devices/linkam3.py @@ -8,9 +8,9 @@ HintedSignal, StandardReadable, WatchableAsyncStatus, + WatcherUpdate, observe_value, ) -from ophyd_async.core.utils import WatcherUpdate from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw diff --git a/src/dodal/devices/motors.py b/src/dodal/devices/motors.py index 5d7de2b388..4237082598 100644 --- a/src/dodal/devices/motors.py +++ b/src/dodal/devices/motors.py @@ -1,5 +1,5 @@ from ophyd_async.core import Device -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor class XYZPositioner(Device): diff --git a/src/dodal/devices/oav/oav_to_redis_forwarder.py b/src/dodal/devices/oav/oav_to_redis_forwarder.py index 4d80a37613..1af27d2972 100644 --- a/src/dodal/devices/oav/oav_to_redis_forwarder.py +++ b/src/dodal/devices/oav/oav_to_redis_forwarder.py @@ -6,8 +6,7 @@ import numpy as np from aiohttp import ClientResponse, ClientSession from bluesky.protocols import Flyable -from ophyd_async.core import AsyncStatus, StandardReadable -from ophyd_async.core.signal import soft_signal_r_and_setter +from ophyd_async.core import AsyncStatus, StandardReadable, soft_signal_r_and_setter from ophyd_async.epics.signal import epics_signal_r from PIL import Image from redis.asyncio import StrictRedis diff --git a/src/dodal/devices/robot.py b/src/dodal/devices/robot.py index f941306239..e871624807 100644 --- a/src/dodal/devices/robot.py +++ b/src/dodal/devices/robot.py @@ -10,8 +10,7 @@ set_and_wait_for_value, wait_for_value, ) -from ophyd_async.epics.signal import epics_signal_r, epics_signal_x -from ophyd_async.epics.signal.signal import epics_signal_rw_rbv +from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw_rbv, epics_signal_x from dodal.log import LOGGER diff --git a/src/dodal/devices/scatterguard.py b/src/dodal/devices/scatterguard.py index 3df94b9ba4..3d77ee5fb9 100644 --- a/src/dodal/devices/scatterguard.py +++ b/src/dodal/devices/scatterguard.py @@ -1,5 +1,5 @@ from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor class Scatterguard(StandardReadable): diff --git a/src/dodal/devices/scintillator.py b/src/dodal/devices/scintillator.py index 75a6b0b902..f595306fd2 100644 --- a/src/dodal/devices/scintillator.py +++ b/src/dodal/devices/scintillator.py @@ -1,5 +1,5 @@ from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor class Scintillator(StandardReadable): diff --git a/src/dodal/devices/slits.py b/src/dodal/devices/slits.py index 2733b9379c..830c6dbd8c 100644 --- a/src/dodal/devices/slits.py +++ b/src/dodal/devices/slits.py @@ -1,5 +1,5 @@ from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor class Slits(StandardReadable): diff --git a/src/dodal/devices/smargon.py b/src/dodal/devices/smargon.py index 27ea6f2a7a..50ddc944cf 100644 --- a/src/dodal/devices/smargon.py +++ b/src/dodal/devices/smargon.py @@ -7,7 +7,7 @@ from bluesky import plan_stubs as bps from bluesky.utils import Msg from ophyd_async.core import AsyncStatus, Device, StandardReadable, wait_for_value -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import epics_signal_r from dodal.devices.util.epics_util import SetWhenEnabled diff --git a/src/dodal/devices/tetramm.py b/src/dodal/devices/tetramm.py index 501f450f79..c058b3a64c 100644 --- a/src/dodal/devices/tetramm.py +++ b/src/dodal/devices/tetramm.py @@ -1,5 +1,4 @@ import asyncio -from collections.abc import Sequence from enum import Enum from bluesky.protocols import Hints @@ -8,16 +7,18 @@ DetectorControl, DetectorTrigger, Device, - DirectoryProvider, + PathProvider, ShapeProvider, StandardDetector, set_and_wait_for_value, soft_signal_r_and_setter, ) -from ophyd_async.epics.areadetector.utils import stop_busy_record -from ophyd_async.epics.areadetector.writers import HDFWriter, NDFileHDF -from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw -from ophyd_async.epics.signal.signal import epics_signal_rw_rbv +from ophyd_async.epics.adcore import ADHDFWriter, NDFileHDFIO, stop_busy_record +from ophyd_async.epics.signal import ( + epics_signal_r, + epics_signal_rw, + epics_signal_rw_rbv, +) class TetrammRange(str, Enum): @@ -108,7 +109,7 @@ def __init__( self.minimum_values_per_reading = minimum_values_per_reading self.readings_per_frame = readings_per_frame - def get_deadtime(self, exposure: float) -> float: + def get_deadtime(self, exposure: float | None) -> float: # 2 internal clock cycles. Best effort approximation return 2 / self.base_sample_rate @@ -210,8 +211,8 @@ class TetrammShapeProvider(ShapeProvider): def __init__(self, controller: TetrammController) -> None: self.controller = controller - async def __call__(self) -> Sequence[int]: - return [self.max_channels, self.controller.readings_per_frame] + async def __call__(self) -> tuple[int, int]: + return (self.max_channels, self.controller.readings_per_frame) # TODO: Support MeanValue signals https://github.com/DiamondLightSource/dodal/issues/337 @@ -219,13 +220,13 @@ class TetrammDetector(StandardDetector): def __init__( self, prefix: str, - directory_provider: DirectoryProvider, + path_provider: PathProvider, name: str, type: str | None = None, **scalar_sigs: str, ) -> None: self.drv = TetrammDriver(prefix + "DRV:") - self.hdf = NDFileHDF(prefix + "HDF5:") + self.hdf = NDFileHDFIO(prefix + "HDF5:") controller = TetrammController(self.drv) config_signals = [ self.drv.values_per_reading, @@ -239,9 +240,9 @@ def __init__( self.type = None super().__init__( controller, - HDFWriter( + ADHDFWriter( self.hdf, - directory_provider, + path_provider, lambda: self.name, TetrammShapeProvider(controller), **scalar_sigs, diff --git a/src/dodal/devices/training_rig/sample_stage.py b/src/dodal/devices/training_rig/sample_stage.py index bb5faddb7f..31230caa40 100644 --- a/src/dodal/devices/training_rig/sample_stage.py +++ b/src/dodal/devices/training_rig/sample_stage.py @@ -1,5 +1,5 @@ from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor class TrainingRigSampleStage(StandardReadable): diff --git a/src/dodal/devices/turbo_slit.py b/src/dodal/devices/turbo_slit.py index c0e6358c44..7bb8b28f48 100644 --- a/src/dodal/devices/turbo_slit.py +++ b/src/dodal/devices/turbo_slit.py @@ -1,5 +1,5 @@ from ophyd_async.core import Device -from ophyd_async.epics.motion.motor import Motor +from ophyd_async.epics.motor import Motor class TurboSlit(Device): diff --git a/src/dodal/devices/undulator.py b/src/dodal/devices/undulator.py index d43e98a4ea..0e085d4d33 100644 --- a/src/dodal/devices/undulator.py +++ b/src/dodal/devices/undulator.py @@ -1,7 +1,7 @@ from enum import Enum from ophyd_async.core import ConfigSignal, StandardReadable, soft_signal_r_and_setter -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.epics.signal import epics_signal_r # The acceptable difference, in mm, between the undulator gap and the DCM diff --git a/src/dodal/devices/util/adjuster_plans.py b/src/dodal/devices/util/adjuster_plans.py index 99b7e7b5e3..f136f827f2 100644 --- a/src/dodal/devices/util/adjuster_plans.py +++ b/src/dodal/devices/util/adjuster_plans.py @@ -8,7 +8,7 @@ from bluesky import plan_stubs as bps from bluesky.utils import Msg from ophyd.epics_motor import EpicsMotor -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from dodal.log import LOGGER diff --git a/src/dodal/devices/util/save_panda.py b/src/dodal/devices/util/save_panda.py index 31fd018821..af0ffaa8df 100644 --- a/src/dodal/devices/util/save_panda.py +++ b/src/dodal/devices/util/save_panda.py @@ -7,7 +7,7 @@ from bluesky.run_engine import RunEngine from ophyd_async.core import Device, save_device -from ophyd_async.panda import phase_sorter +from ophyd_async.fastcs.panda import phase_sorter from dodal.beamlines import module_name_for_beamline from dodal.utils import make_device diff --git a/src/dodal/devices/util/test_utils.py b/src/dodal/devices/util/test_utils.py index 45df1a23ac..2947718072 100644 --- a/src/dodal/devices/util/test_utils.py +++ b/src/dodal/devices/util/test_utils.py @@ -2,7 +2,7 @@ callback_on_mock_put, set_mock_value, ) -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor def patch_motor(motor: Motor, initial_position=0): diff --git a/src/dodal/devices/xbpm_feedback.py b/src/dodal/devices/xbpm_feedback.py index 738c3c0d5f..a61804704a 100644 --- a/src/dodal/devices/xbpm_feedback.py +++ b/src/dodal/devices/xbpm_feedback.py @@ -1,8 +1,7 @@ from enum import Enum from bluesky.protocols import Triggerable -from ophyd_async.core import Device, observe_value -from ophyd_async.core.async_status import AsyncStatus +from ophyd_async.core import AsyncStatus, Device, observe_value from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw diff --git a/src/dodal/devices/xspress3/xspress3.py b/src/dodal/devices/xspress3/xspress3.py index f8ae574f0a..3f2f855607 100644 --- a/src/dodal/devices/xspress3/xspress3.py +++ b/src/dodal/devices/xspress3/xspress3.py @@ -9,7 +9,7 @@ DeviceVector, wait_for_value, ) -from ophyd_async.epics.signal.signal import ( +from ophyd_async.epics.signal import ( epics_signal_r, epics_signal_rw, epics_signal_rw_rbv, diff --git a/src/dodal/devices/zocalo/zocalo_results.py b/src/dodal/devices/zocalo/zocalo_results.py index 1aa74c7432..8d029f1714 100644 --- a/src/dodal/devices/zocalo/zocalo_results.py +++ b/src/dodal/devices/zocalo/zocalo_results.py @@ -11,8 +11,12 @@ import workflows.transport from bluesky.protocols import Descriptor, Triggerable from numpy.typing import NDArray -from ophyd_async.core import HintedSignal, StandardReadable, soft_signal_r_and_setter -from ophyd_async.core.async_status import AsyncStatus +from ophyd_async.core import ( + AsyncStatus, + HintedSignal, + StandardReadable, + soft_signal_r_and_setter, +) from workflows.transport.common_transport import CommonTransport from dodal.devices.zocalo.zocalo_interaction import _get_zocalo_connection diff --git a/src/dodal/log.py b/src/dodal/log.py index 64745c9f8a..1d67f25f2b 100644 --- a/src/dodal/log.py +++ b/src/dodal/log.py @@ -8,28 +8,50 @@ from pathlib import Path from typing import TypedDict +import colorlog from bluesky.log import logger as bluesky_logger from graypy import GELFTCPHandler from ophyd.log import logger as ophyd_logger -from ophyd_async.log import ( - DEFAULT_DATE_FORMAT, - DEFAULT_FORMAT, - DEFAULT_LOG_COLORS, - ColoredFormatterWithDeviceName, -) -from ophyd_async.log import logger as ophyd_async_logger LOGGER = logging.getLogger("Dodal") +# Temporarily duplicated https://github.com/bluesky/ophyd-async/issues/550 +ophyd_async_logger = logging.getLogger("ophyd_async") LOGGER.setLevel(logging.DEBUG) -DEFAULT_FORMATTER = ColoredFormatterWithDeviceName( - fmt=DEFAULT_FORMAT, datefmt=DEFAULT_DATE_FORMAT, log_colors=DEFAULT_LOG_COLORS -) ERROR_LOG_BUFFER_LINES = 20000 INFO_LOG_DAYS = 30 DEBUG_LOG_FILES_TO_KEEP = 7 DEFAULT_GRAYLOG_PORT = 12231 +# Temporarily duplicated https://github.com/bluesky/ophyd-async/issues/550 +DEFAULT_FORMAT = ( + "%(log_color)s[%(levelname)1.1s %(asctime)s.%(msecs)03d " + "%(module)s:%(lineno)d] %(message)s" +) + +DEFAULT_DATE_FORMAT = "%y%m%d %H:%M:%S" + +DEFAULT_LOG_COLORS = { + "DEBUG": "cyan", + "INFO": "green", + "WARNING": "yellow", + "ERROR": "red", + "CRITICAL": "red,bg_white", +} + + +class ColoredFormatterWithDeviceName(colorlog.ColoredFormatter): + def format(self, record): + message = super().format(record) + if device_name := getattr(record, "ophyd_async_device_name", None): + message = f"[{device_name}]{message}" + return message + + +DEFAULT_FORMATTER = ColoredFormatterWithDeviceName( + fmt=DEFAULT_FORMAT, datefmt=DEFAULT_DATE_FORMAT, log_colors=DEFAULT_LOG_COLORS +) + class CircularMemoryHandler(logging.Handler): """Loosely based on the MemoryHandler, which keeps a buffer and writes it when full diff --git a/src/dodal/plans/data_session_metadata.py b/src/dodal/plans/data_session_metadata.py index 3b93ffe235..4bbbe66090 100644 --- a/src/dodal/plans/data_session_metadata.py +++ b/src/dodal/plans/data_session_metadata.py @@ -1,29 +1,28 @@ from bluesky import plan_stubs as bps from bluesky import preprocessors as bpp from bluesky.utils import make_decorator -from ophyd_async.core import DirectoryInfo from dodal.common.beamlines import beamline_utils -from dodal.common.types import MsgGenerator, UpdatingDirectoryProvider +from dodal.common.types import MsgGenerator, UpdatingPathProvider DATA_SESSION = "data_session" DATA_GROUPS = "data_groups" def attach_data_session_metadata_wrapper( - plan: MsgGenerator, provider: UpdatingDirectoryProvider | None = None + plan: MsgGenerator, provider: UpdatingPathProvider | None = None ) -> MsgGenerator: """ Attach data session metadata to the runs within a plan and make it correlate - with an ophyd-async DirectoryProvider. + with an ophyd-async PathProvider. - This updates the directory provider (which in turn makes a call to to a service + This updates the path provider (which in turn makes a call to to a service to figure out which scan number we are using for such a scan), and ensures the start document contains the correct data session. Args: plan: The plan to preprocess - provider: The directory provider that participating detectors are aware of. + provider: The path provider that participating detectors are aware of. Returns: MsgGenerator: A plan @@ -32,13 +31,11 @@ def attach_data_session_metadata_wrapper( Iterator[Msg]: Plan messages """ if provider is None: - provider = beamline_utils.get_directory_provider() + provider = beamline_utils.get_path_provider() yield from bps.wait_for([provider.update]) - directory_info: DirectoryInfo = provider() + data_session = (yield from bps.wait_for([provider.data_session]))[0].result() # https://github.com/DiamondLightSource/dodal/issues/452 # As part of 452, write each dataCollection into their own folder, then can use resource_dir directly - assert directory_info.prefix is not None - data_session = directory_info.prefix.removesuffix("-") yield from bpp.inject_md_wrapper(plan, md={DATA_SESSION: data_session}) diff --git a/src/dodal/plans/motor_util_plans.py b/src/dodal/plans/motor_util_plans.py index 54cda5f79c..80d0e93611 100644 --- a/src/dodal/plans/motor_util_plans.py +++ b/src/dodal/plans/motor_util_plans.py @@ -6,7 +6,7 @@ from bluesky.preprocessors import finalize_wrapper, pchain from bluesky.utils import Msg, make_decorator from ophyd_async.core import Device -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from dodal.common import MsgGenerator diff --git a/tests/common/test_udc_directory_provider.py b/tests/common/test_udc_directory_provider.py index 5e47eaac6c..f6f9f077c6 100644 --- a/tests/common/test_udc_directory_provider.py +++ b/tests/common/test_udc_directory_provider.py @@ -3,7 +3,7 @@ import pytest -from dodal.common.udc_directory_provider import PandASubdirectoryProvider +from dodal.common.udc_directory_provider import PandASubpathProvider @pytest.mark.parametrize( @@ -16,15 +16,14 @@ ], ], ) -def test_udc_directory_provider_get_and_set(root, expected): - provider = PandASubdirectoryProvider(root) +def test_udc_path_provider_get_and_set(root, expected): + provider = PandASubpathProvider(root) directory_info = provider() - assert directory_info.root == root - assert directory_info.root.joinpath(directory_info.resource_dir) == expected + assert directory_info.directory_path == expected -def test_udc_directory_provider_excepts_before_update(): - provider = PandASubdirectoryProvider() +def test_udc_path_provider_excepts_before_update(): + provider = PandASubpathProvider() with pytest.raises( ValueError, match=re.escape( @@ -38,42 +37,38 @@ def test_udc_directory_provider_excepts_before_update(): "initial", [Path("."), None], ) -async def test_udc_directory_provider_after_update(initial, tmp_path): - provider = PandASubdirectoryProvider(initial) +async def test_udc_path_provider_after_update(initial, tmp_path): + provider = PandASubpathProvider(initial) await provider.update(directory=tmp_path) directory_info = provider() - assert directory_info.root == tmp_path - assert directory_info.resource_dir == Path("panda") + assert directory_info.directory_path == tmp_path / "panda" -async def test_udc_directory_provider_no_suffix(tmp_path): +async def test_udc_path_provider_no_suffix(tmp_path): initial = Path("initial") - provider = PandASubdirectoryProvider(initial) + provider = PandASubpathProvider(initial) root_path = tmp_path / "my_data" root_path.mkdir() await provider.update(directory=root_path) directory_info = provider() - assert directory_info.root == root_path - assert directory_info.resource_dir == Path("panda") - assert directory_info.suffix == "" + assert directory_info.directory_path == root_path / "panda" -async def test_udc_directory_provider_with_suffix(tmp_path): +async def test_udc_path_provider_with_suffix(tmp_path): initial = Path("initial") - provider = PandASubdirectoryProvider(initial) + provider = PandASubpathProvider(initial) root_path = tmp_path / "my_data" root_path.mkdir() await provider.update(directory=root_path, suffix="_123") directory_info = provider() - assert directory_info.root == root_path - assert directory_info.resource_dir == Path("panda") - assert directory_info.suffix == "_123" + assert directory_info.directory_path == root_path / "panda" + assert directory_info.filename.endswith("_123") -async def test_udc_directory_provider_creates_subdirectory_if_not_exists(tmp_path): +async def test_udc_path_provider_creates_subdirectory_if_not_exists(tmp_path): root = tmp_path subdir = root / Path("panda") assert not subdir.exists() - provider = PandASubdirectoryProvider(Path("initial")) + provider = PandASubpathProvider(Path("initial")) await provider.update(directory=root) assert subdir.exists() diff --git a/tests/devices/i22/test_metadataholder.py b/tests/devices/i22/test_metadataholder.py index 7d083396d4..74fcf87ebb 100644 --- a/tests/devices/i22/test_metadataholder.py +++ b/tests/devices/i22/test_metadataholder.py @@ -1,14 +1,12 @@ -from pathlib import Path - import pytest -from ophyd_async.core import DeviceCollector, StaticDirectoryProvider -from ophyd_async.epics.areadetector import PilatusDetector +from ophyd_async.core import DeviceCollector, PathProvider +from ophyd_async.epics.adpilatus import PilatusDetector from dodal.devices.i22.nxsas import NXSasMetadataHolder, NXSasPilatus @pytest.fixture -def saxs(tmp_path: Path, RE) -> PilatusDetector: +def saxs(static_path_provider: PathProvider, RE) -> PilatusDetector: with DeviceCollector(mock=True): saxs = NXSasPilatus( prefix="-EA-PILAT-01:", @@ -22,7 +20,7 @@ def saxs(tmp_path: Path, RE) -> PilatusDetector: "m", ), # To get from configuration data after visit begins ), - directory_provider=StaticDirectoryProvider(tmp_path), + path_provider=static_path_provider, ) return saxs diff --git a/tests/devices/unit_tests/conftest.py b/tests/devices/unit_tests/conftest.py index ee8e39144f..5ade0352fe 100644 --- a/tests/devices/unit_tests/conftest.py +++ b/tests/devices/unit_tests/conftest.py @@ -3,24 +3,37 @@ import pytest from bluesky.run_engine import RunEngine from ophyd_async.core import ( - DirectoryInfo, - DirectoryProvider, - StaticDirectoryProvider, + PathInfo, + PathProvider, ) from dodal.beamlines import i03 from dodal.common.beamlines.beamline_utils import clear_devices +from dodal.common.visit import ( + DirectoryServiceClientBase, + LocalDirectoryServiceClient, + StaticVisitPathProvider, +) from dodal.devices.util.test_utils import patch_motor -DIRECTORY_INFO_FOR_TESTING: DirectoryInfo = DirectoryInfo( - root=Path("/does/not/exist"), - resource_dir=Path("/on/this/filesystem"), +PATH_INFO_FOR_TESTING: PathInfo = PathInfo( + directory_path=Path("/does/not/exist"), + filename="on_this_filesystem", ) @pytest.fixture -def static_directory_provider(tmp_path: Path) -> DirectoryProvider: - return StaticDirectoryProvider(tmp_path) +def dummy_visit_client() -> DirectoryServiceClientBase: + return LocalDirectoryServiceClient() + + +@pytest.fixture +def static_path_provider( + tmp_path: Path, dummy_visit_client: DirectoryServiceClientBase +) -> PathProvider: + return StaticVisitPathProvider( + beamline="ixx", root=tmp_path, client=dummy_visit_client + ) @pytest.fixture diff --git a/tests/devices/unit_tests/test_tetramm.py b/tests/devices/unit_tests/test_tetramm.py index 94f2e77d1d..fb7ce3e9b1 100644 --- a/tests/devices/unit_tests/test_tetramm.py +++ b/tests/devices/unit_tests/test_tetramm.py @@ -3,11 +3,11 @@ from ophyd_async.core import ( DetectorTrigger, DeviceCollector, - DirectoryProvider, + PathProvider, + TriggerInfo, set_mock_value, ) -from ophyd_async.core.detector import TriggerInfo -from ophyd_async.epics.areadetector import FileWriteMode +from ophyd_async.epics.adcore import FileWriteMode from dodal.devices.tetramm import ( TetrammController, @@ -41,11 +41,11 @@ async def tetramm_controller( @pytest.fixture -async def tetramm(static_directory_provider: DirectoryProvider) -> TetrammDetector: +async def tetramm(static_path_provider: PathProvider) -> TetrammDetector: async with DeviceCollector(mock=True): tetramm = TetrammDetector( "MY-TETRAMM:", - static_directory_provider, + static_path_provider, name=TEST_TETRAMM_NAME, ) @@ -166,10 +166,11 @@ async def test_sample_rate_scales_with_exposure_time( ): await tetramm.prepare( TriggerInfo( - 100, - DetectorTrigger.edge_trigger, - 2e-5, - exposure, + number=100, + trigger=DetectorTrigger.edge_trigger, + deadtime=2e-5, + livetime=exposure, + frame_timeout=None, ) ) values_per_reading = await tetramm.drv.values_per_reading.get_value() @@ -252,10 +253,11 @@ async def test_prepare_with_too_low_a_deadtime_raises_error( ): await tetramm.prepare( TriggerInfo( - 5, - DetectorTrigger.edge_trigger, - 1.0 / 100_000.0, - VALID_TEST_EXPOSURE_TIME, + number=5, + trigger=DetectorTrigger.edge_trigger, + deadtime=1.0 / 100_000.0, + livetime=VALID_TEST_EXPOSURE_TIME, + frame_timeout=None, ) ) @@ -265,10 +267,11 @@ async def test_prepare_arms_tetramm( ): await tetramm.prepare( TriggerInfo( - 5, - DetectorTrigger.edge_trigger, - 0.1, - VALID_TEST_EXPOSURE_TIME, + number=5, + trigger=DetectorTrigger.edge_trigger, + deadtime=0.1, + livetime=VALID_TEST_EXPOSURE_TIME, + frame_timeout=None, ) ) await assert_armed(tetramm.drv) diff --git a/tests/devices/unit_tests/test_zocalo_results.py b/tests/devices/unit_tests/test_zocalo_results.py index 180b84d077..dd519babc1 100644 --- a/tests/devices/unit_tests/test_zocalo_results.py +++ b/tests/devices/unit_tests/test_zocalo_results.py @@ -6,7 +6,7 @@ import pytest from bluesky.run_engine import RunEngine from bluesky.utils import FailedStatus -from ophyd_async.core.async_status import AsyncStatus +from ophyd_async.core import AsyncStatus from workflows.recipe import RecipeWrapper from dodal.devices.zocalo.zocalo_results import ( diff --git a/tests/devices/unit_tests/util/test_save_panda.py b/tests/devices/unit_tests/util/test_save_panda.py index 2774ba9b0f..3d11cb8399 100644 --- a/tests/devices/unit_tests/util/test_save_panda.py +++ b/tests/devices/unit_tests/util/test_save_panda.py @@ -3,7 +3,7 @@ import pytest from bluesky.simulators import RunEngineSimulator -from ophyd_async.panda import phase_sorter +from ophyd_async.fastcs.panda import phase_sorter from dodal.devices.util.save_panda import _save_panda, main diff --git a/tests/fake_beamline_all_devices_raise_exception.py b/tests/fake_beamline_all_devices_raise_exception.py index 9204c1725d..4538be727a 100644 --- a/tests/fake_beamline_all_devices_raise_exception.py +++ b/tests/fake_beamline_all_devices_raise_exception.py @@ -1,7 +1,7 @@ import asyncio from bluesky.protocols import Readable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from dodal.devices.cryostream import CryoStream diff --git a/tests/fake_beamline_some_devices_working.py b/tests/fake_beamline_some_devices_working.py index 67cca3c586..bd0d1f1408 100644 --- a/tests/fake_beamline_some_devices_working.py +++ b/tests/fake_beamline_some_devices_working.py @@ -1,7 +1,7 @@ from unittest.mock import MagicMock from bluesky.protocols import Readable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor from dodal.devices.undulator import Undulator diff --git a/tests/plans/test_motor_util_plans.py b/tests/plans/test_motor_util_plans.py index 03b8d7b62f..dc4a761ca2 100644 --- a/tests/plans/test_motor_util_plans.py +++ b/tests/plans/test_motor_util_plans.py @@ -4,9 +4,14 @@ from bluesky import plan_stubs as bps from bluesky.run_engine import RunEngine from bluesky.utils import FailedStatus -from ophyd_async.core import Device, DeviceCollector, get_mock_put, set_mock_value -from ophyd_async.core.signal import soft_signal_rw -from ophyd_async.epics.motion import Motor +from ophyd_async.core import ( + Device, + DeviceCollector, + get_mock_put, + set_mock_value, + soft_signal_rw, +) +from ophyd_async.epics.motor import Motor from dodal.devices.util.test_utils import patch_motor from dodal.plans.motor_util_plans import ( diff --git a/tests/preprocessors/test_filesystem_metadata.py b/tests/preprocessors/test_filesystem_metadata.py index 8ca4551e8f..1b045753f0 100644 --- a/tests/preprocessors/test_filesystem_metadata.py +++ b/tests/preprocessors/test_filesystem_metadata.py @@ -16,15 +16,15 @@ from bluesky.protocols import HasName, Readable, Reading, Triggerable from bluesky.run_engine import RunEngine from event_model.documents.event_descriptor import DataKey -from ophyd_async.core import AsyncStatus, DeviceCollector, DirectoryProvider +from ophyd_async.core import AsyncStatus, DeviceCollector, PathProvider from pydantic import BaseModel -from dodal.common.types import MsgGenerator, UpdatingDirectoryProvider +from dodal.common.types import MsgGenerator, UpdatingPathProvider from dodal.common.visit import ( DataCollectionIdentifier, DirectoryServiceClientBase, LocalDirectoryServiceClient, - StaticVisitDirectoryProvider, + StaticVisitPathProvider, ) from dodal.plans.data_session_metadata import ( DATA_SESSION, @@ -34,12 +34,12 @@ class FakeDetector(Readable, HasName, Triggerable): _name: str - _provider: DirectoryProvider + _provider: PathProvider def __init__( self, name: str, - provider: DirectoryProvider, + provider: PathProvider, ) -> None: self._name = name self._provider = provider @@ -53,12 +53,8 @@ async def read(self) -> dict[str, Reading]: } async def describe(self) -> dict[str, DataKey]: - directory_info = self._provider() - source = str( - directory_info.root - / directory_info.resource_dir - / f"{directory_info.prefix}{self.name}{directory_info.suffix}.h5" - ) + directory_info = self._provider(self.name) + source = str(directory_info.directory_path / f"{directory_info.filename}.h5") return { f"{self.name}_data": { "dtype": "string", @@ -111,12 +107,12 @@ def client() -> DirectoryServiceClientBase: @pytest.fixture def provider( client: DirectoryServiceClientBase, tmp_path: Path -) -> UpdatingDirectoryProvider: - return StaticVisitDirectoryProvider("example", tmp_path, client=client) +) -> UpdatingPathProvider: + return StaticVisitPathProvider("example", tmp_path, client=client) @pytest.fixture(params=[1, 2]) -def detectors(request, provider: UpdatingDirectoryProvider) -> list[Readable]: +def detectors(request, provider: UpdatingPathProvider) -> list[Readable]: number_of_detectors = request.param with DeviceCollector(mock=True): dets: list[Readable] = [ @@ -185,7 +181,7 @@ def nested_run_without_metadata( def test_simple_run_gets_scan_number( RE: RunEngine, detectors: list[Readable], - provider: UpdatingDirectoryProvider, + provider: UpdatingPathProvider, tmp_path: Path, ) -> None: docs = collect_docs( @@ -203,7 +199,7 @@ def test_multi_run_gets_scan_numbers( RE: RunEngine, detectors: list[Readable], plan: Callable[[list[Readable]], MsgGenerator], - provider: UpdatingDirectoryProvider, + provider: UpdatingPathProvider, tmp_path: Path, ) -> None: """Test is here to demonstrate that multi run plans will overwrite files.""" @@ -222,7 +218,7 @@ def test_multi_run_gets_scan_numbers( def test_multi_run_single_stage( RE: RunEngine, detectors: list[Readable], - provider: UpdatingDirectoryProvider, + provider: UpdatingPathProvider, tmp_path: Path, ) -> None: docs = collect_docs( @@ -248,7 +244,7 @@ def test_multi_run_single_stage( def test_multi_run_single_stage_multi_group( RE: RunEngine, detectors: list[Readable], - provider: UpdatingDirectoryProvider, + provider: UpdatingPathProvider, tmp_path: Path, ) -> None: docs = collect_docs( @@ -273,7 +269,7 @@ def test_multi_run_single_stage_multi_group( def test_nested_run_with_metadata( RE: RunEngine, detectors: list[Readable], - provider: UpdatingDirectoryProvider, + provider: UpdatingPathProvider, tmp_path: Path, ) -> None: """Test is here to demonstrate that nested runs will be treated as a single run. @@ -296,7 +292,7 @@ def test_nested_run_with_metadata( def test_nested_run_without_metadata( RE: RunEngine, detectors: list[Readable], - provider: UpdatingDirectoryProvider, + provider: UpdatingPathProvider, tmp_path: Path, ) -> None: """Test is here to demonstrate that nested runs will be treated as a single run. @@ -316,10 +312,10 @@ def test_nested_run_without_metadata( assert_all_detectors_used_collection_numbers(tmp_path, docs, detectors, ["1", "1"]) -def test_visit_directory_provider_fails( +def test_visit_path_provider_fails( RE: RunEngine, detectors: list[Readable], - provider: UpdatingDirectoryProvider, + provider: UpdatingPathProvider, client: MockDirectoryServiceClient, ) -> None: client.fail = True @@ -331,10 +327,10 @@ def test_visit_directory_provider_fails( ) -def test_visit_directory_provider_fails_after_one_sucess( +def test_visit_path_provider_fails_after_one_sucess( RE: RunEngine, detectors: list[Readable], - provider: UpdatingDirectoryProvider, + provider: UpdatingPathProvider, client: MockDirectoryServiceClient, ) -> None: collect_docs( @@ -354,7 +350,7 @@ def test_visit_directory_provider_fails_after_one_sucess( def collect_docs( RE: RunEngine, plan: MsgGenerator, - provider: UpdatingDirectoryProvider, + provider: UpdatingPathProvider, ) -> list[DataEvent]: events = []