From d78ffcbbc8047a0a803c6faeebbd21ddec8dee7c Mon Sep 17 00:00:00 2001 From: Eva Lott Date: Thu, 4 Apr 2024 14:20:57 +0100 Subject: [PATCH] made a new class for the PandA hdf writer and adjusted `PandAController` Also made it possible to pre-intialise blocks so that the controller can have the same pcap device as the post init panda. --- src/ophyd_async/epics/pvi/__init__.py | 4 +- src/ophyd_async/epics/pvi/pvi.py | 25 +++++- src/ophyd_async/panda/__init__.py | 6 +- src/ophyd_async/panda/common_panda.py | 38 ++++++++ src/ophyd_async/panda/hdf_panda.py | 38 ++++++++ src/ophyd_async/panda/panda.py | 61 ------------- src/ophyd_async/panda/panda_controller.py | 10 +-- tests/epics/test_pvi.py | 46 +++++++++- tests/panda/test_hdf_panda.py | 88 +++++++++++++++++++ .../{test_panda.py => test_panda_connect.py} | 68 +++++++++----- tests/panda/test_panda_controller.py | 37 ++++++-- tests/panda/test_panda_utils.py | 17 +++- tests/panda/test_trigger.py | 19 ++-- tests/test_flyer_with_panda.py | 15 +++- 14 files changed, 358 insertions(+), 114 deletions(-) create mode 100644 src/ophyd_async/panda/common_panda.py create mode 100644 src/ophyd_async/panda/hdf_panda.py delete mode 100644 src/ophyd_async/panda/panda.py create mode 100644 tests/panda/test_hdf_panda.py rename tests/panda/{test_panda.py => test_panda_connect.py} (68%) diff --git a/src/ophyd_async/epics/pvi/__init__.py b/src/ophyd_async/epics/pvi/__init__.py index 307c3b35ef..522b96ae0a 100644 --- a/src/ophyd_async/epics/pvi/__init__.py +++ b/src/ophyd_async/epics/pvi/__init__.py @@ -1,3 +1,3 @@ -from .pvi import PVIEntry, fill_pvi_entries +from .pvi import PVIEntry, fill_pvi_entries, pre_initialize_blocks -__all__ = ["PVIEntry", "fill_pvi_entries"] +__all__ = ["PVIEntry", "fill_pvi_entries", "pre_initialize_blocks"] diff --git a/src/ophyd_async/epics/pvi/pvi.py b/src/ophyd_async/epics/pvi/pvi.py index dc211250db..dbebc78091 100644 --- a/src/ophyd_async/epics/pvi/pvi.py +++ b/src/ophyd_async/epics/pvi/pvi.py @@ -185,7 +185,7 @@ def _sim_common_blocks(device: Device, stripped_type: Optional[Type] = None): signal_type = args[0] if (args := get_args(sub_device_t)) else None sub_device = sub_device_t(SimSignalBackend(signal_type, sub_name)) else: - sub_device = sub_device_t() + sub_device = getattr(device, sub_name, sub_device_t()) if not is_signal: if is_device_vector: @@ -226,7 +226,10 @@ async def _get_pvi_entries(entry: PVIEntry, timeout=DEFAULT_TIMEOUT): if is_signal: device = _pvi_mapping[frozenset(pva_entries.keys())](signal_dtype, *pvs) else: - device = device_type() + if hasattr(entry.device, sub_name): + device = getattr(entry.device, sub_name) + else: + device = device_type() sub_entry = PVIEntry( device=device, common_device_type=device_type, sub_entries={} @@ -296,3 +299,21 @@ async def fill_pvi_entries( # We call set name now the parent field has been set in all of the # introspect-initialized devices. This will recursively set the names. device.set_name(device.name) + + +def pre_initialize_blocks(device: Device): + for name, device_type in get_type_hints(type(device)).items(): + if name in ("_name", "parent"): + continue + device_type = _strip_union(device_type) + is_device_vector, device_type = _strip_device_vector(device_type) + if ( + is_device_vector + or ((origin := get_origin(device_type)) and issubclass(origin, Signal)) + or (isclass(device_type) and issubclass(device_type, Signal)) + ): + continue + + sub_device = device_type() + setattr(device, name, sub_device) + pre_initialize_blocks(sub_device) diff --git a/src/ophyd_async/panda/__init__.py b/src/ophyd_async/panda/__init__.py index 4007412f87..c30268530e 100644 --- a/src/ophyd_async/panda/__init__.py +++ b/src/ophyd_async/panda/__init__.py @@ -1,4 +1,5 @@ -from .panda import PandA, PcapBlock, PulseBlock, SeqBlock, SeqTable, TimeUnits +from .common_panda import CommonPandaBlocks, PcapBlock, PulseBlock, SeqBlock, TimeUnits +from .hdf_panda import HDFPandA from .panda_controller import PandaPcapController from .table import ( SeqTable, @@ -10,7 +11,8 @@ from .utils import phase_sorter __all__ = [ - "PandA", + "CommonPandaBlocks", + "HDFPandA", "PcapBlock", "PulseBlock", "seq_table_from_arrays", diff --git a/src/ophyd_async/panda/common_panda.py b/src/ophyd_async/panda/common_panda.py new file mode 100644 index 0000000000..12ea1c087b --- /dev/null +++ b/src/ophyd_async/panda/common_panda.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +from enum import Enum + +from ophyd_async.core import Device, DeviceVector, SignalR, SignalRW +from ophyd_async.panda.table import SeqTable + + +class PulseBlock(Device): + delay: SignalRW[float] + width: SignalRW[float] + + +class TimeUnits(str, Enum): + min = "min" + s = "s" + ms = "ms" + us = "us" + + +class SeqBlock(Device): + table: SignalRW[SeqTable] + active: SignalRW[bool] + repeats: SignalRW[int] + prescale: SignalRW[float] + prescale_units: SignalRW[TimeUnits] + enable: SignalRW[str] + + +class PcapBlock(Device): + active: SignalR[bool] + arm: SignalRW[bool] + + +class CommonPandaBlocks(Device): + pulse: DeviceVector[PulseBlock] + seq: DeviceVector[SeqBlock] + pcap: PcapBlock diff --git a/src/ophyd_async/panda/hdf_panda.py b/src/ophyd_async/panda/hdf_panda.py new file mode 100644 index 0000000000..664205f59c --- /dev/null +++ b/src/ophyd_async/panda/hdf_panda.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +from typing import Sequence + +from ophyd_async.core import DEFAULT_TIMEOUT, SignalR, StandardDetector +from ophyd_async.epics.areadetector.writers import HDFWriter +from ophyd_async.epics.pvi import fill_pvi_entries, pre_initialize_blocks + +from .common_panda import CommonPandaBlocks +from .panda_controller import PandaPcapController + + +class HDFPandA(CommonPandaBlocks, StandardDetector): + def __init__( + self, + prefix: str, + config_sigs: Sequence[SignalR] = (), + name: str = "", + ): + self._prefix = prefix + pre_initialize_blocks(self) + super().__init__( + controller=PandaPcapController(pcap=self.pcap), + # TODO + writer=None, # type: ignore + config_sigs=config_sigs, + name=name, + writer_timeout=DEFAULT_TIMEOUT, + ) + + async def connect( + self, sim: bool = False, timeout: float = DEFAULT_TIMEOUT + ) -> None: + await fill_pvi_entries(self, self._prefix + "PVI", timeout=timeout, sim=sim) + await super().connect(sim=sim, timeout=timeout) + + async def stage(self): + pass diff --git a/src/ophyd_async/panda/panda.py b/src/ophyd_async/panda/panda.py deleted file mode 100644 index d77dd3ebb3..0000000000 --- a/src/ophyd_async/panda/panda.py +++ /dev/null @@ -1,61 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -from ophyd_async.core import DEFAULT_TIMEOUT, Device, DeviceVector, SignalR, SignalRW -from ophyd_async.epics.pvi import fill_pvi_entries -from ophyd_async.panda.table import SeqTable - - -class PulseBlock(Device): - delay: SignalRW[float] - width: SignalRW[float] - - -class TimeUnits(str, Enum): - min = "min" - s = "s" - ms = "ms" - us = "us" - - -class SeqBlock(Device): - table: SignalRW[SeqTable] - active: SignalRW[bool] - repeats: SignalRW[int] - prescale: SignalRW[float] - prescale_units: SignalRW[TimeUnits] - enable: SignalRW[str] - - -class PcapBlock(Device): - active: SignalR[bool] - arm: SignalRW[bool] - - -class CommonPandABlocks(Device): - pulse: DeviceVector[PulseBlock] - seq: DeviceVector[SeqBlock] - pcap: PcapBlock - - -class PandA(CommonPandABlocks): - def __init__(self, prefix: str, name: str = "") -> None: - self._prefix = prefix - super().__init__(name) - - async def connect( - self, sim: bool = False, timeout: float = DEFAULT_TIMEOUT - ) -> None: - """Initialises all blocks and connects them. - - First, checks for pvi information. If it exists, make all blocks from this. - Then, checks that all required blocks in the PandA have been made. - - If there's no pvi information, that's because we're in sim mode. In that case, - makes all required blocks. - """ - - await fill_pvi_entries(self, self._prefix + "PVI", timeout=timeout, sim=sim) - - await super().connect(sim) diff --git a/src/ophyd_async/panda/panda_controller.py b/src/ophyd_async/panda/panda_controller.py index 2c1100bfff..6000909576 100644 --- a/src/ophyd_async/panda/panda_controller.py +++ b/src/ophyd_async/panda/panda_controller.py @@ -7,15 +7,11 @@ DetectorTrigger, wait_for_value, ) - -from .panda import PcapBlock +from ophyd_async.panda import PcapBlock class PandaPcapController(DetectorControl): - def __init__( - self, - pcap: PcapBlock, - ) -> None: + def __init__(self, pcap: PcapBlock) -> None: self.pcap = pcap def get_deadtime(self, exposure: float) -> float: @@ -35,7 +31,7 @@ async def arm( await wait_for_value(self.pcap.active, True, timeout=1) return AsyncStatus(wait_for_value(self.pcap.active, False, timeout=None)) - async def disarm(self): + async def disarm(self) -> AsyncStatus: await asyncio.gather(self.pcap.arm.set(False)) await wait_for_value(self.pcap.active, False, timeout=1) return AsyncStatus(wait_for_value(self.pcap.active, False, timeout=None)) diff --git a/tests/epics/test_pvi.py b/tests/epics/test_pvi.py index 6f29441254..0993716f8d 100644 --- a/tests/epics/test_pvi.py +++ b/tests/epics/test_pvi.py @@ -10,7 +10,7 @@ SignalRW, SignalX, ) -from ophyd_async.epics.pvi import fill_pvi_entries +from ophyd_async.epics.pvi import fill_pvi_entries, pre_initialize_blocks class Block1(Device): @@ -94,3 +94,47 @@ async def test_fill_pvi_entries_sim_mode(pvi_test_device_t): # top level signals are typed assert test_device.signal_rw._backend.datatype is int + + +@pytest.fixture +def pvi_test_device_pre_initialize_blocks_t(): + """A fixture since pytest discourages init in test case classes""" + + class TestDevice(Block3, Device): + def __init__(self, prefix: str, name: str = ""): + self._prefix = prefix + super().__init__(name) + pre_initialize_blocks(self) + + async def connect( + self, sim: bool = False, timeout: float = DEFAULT_TIMEOUT + ) -> None: + await fill_pvi_entries(self, self._prefix + "PVI", timeout=timeout, sim=sim) + + await super().connect(sim) + + yield TestDevice + + +async def test_device_pre_initialize_blocks(pvi_test_device_pre_initialize_blocks_t): + device = pvi_test_device_pre_initialize_blocks_t("PREFIX:") + + block_2_device = device.device + block_1_device = device.device.device + top_block_1_device = device.signal_device + + # The pre_initialize_blocks has only made blocks, + # not signals or device vectors + assert isinstance(block_2_device, Block2) + assert isinstance(block_1_device, Block1) + assert isinstance(top_block_1_device, Block1) + assert not hasattr(device, "signal_x") + assert not hasattr(device, "signal_rw") + assert not hasattr(top_block_1_device, "signal_rw") + + await device.connect(sim=True) + + # The memory addresses have not changed + assert device.device is block_2_device + assert device.device.device is block_1_device + assert device.signal_device is top_block_1_device diff --git a/tests/panda/test_hdf_panda.py b/tests/panda/test_hdf_panda.py new file mode 100644 index 0000000000..3edd777d4b --- /dev/null +++ b/tests/panda/test_hdf_panda.py @@ -0,0 +1,88 @@ +import time +from typing import AsyncGenerator, AsyncIterator, Dict, Optional, Sequence + +import pytest +from bluesky.protocols import Descriptor, StreamAsset +from event_model import ComposeStreamResourceBundle, compose_stream_resource + +from ophyd_async.core import ( + DEFAULT_TIMEOUT, + DetectorWriter, + DeviceCollector, + SignalRW, + SimSignalBackend, + observe_value, +) +from ophyd_async.panda import HDFPandA + + +class DummyWriter(DetectorWriter): + def __init__(self, name: str, shape: Sequence[int]): + self.dummy_signal = SignalRW(backend=SimSignalBackend(int, source="test")) + self._shape = shape + self._name = name + self._file: Optional[ComposeStreamResourceBundle] = None + self._last_emitted = 0 + self.index = 0 + + async def open(self, multiplier: int = 1) -> Dict[str, Descriptor]: + return { + self._name: Descriptor( + source="sim://some-source", + shape=self._shape, + dtype="number", + external="STREAM:", + ) + } + + async def observe_indices_written( + self, timeout=DEFAULT_TIMEOUT + ) -> AsyncGenerator[int, None]: + num_captured: int + async for num_captured in observe_value(self.dummy_signal, timeout): + yield num_captured + + async def get_indices_written(self) -> int: + return self.index + + async def collect_stream_docs( + self, indices_written: int + ) -> AsyncIterator[StreamAsset]: + if indices_written: + if not self._file: + self._file = compose_stream_resource( + spec="AD_HDF5_SWMR_SLICE", + root="/", + data_key=self._name, + resource_path="", + resource_kwargs={ + "path": "", + "multiplier": 1, + "timestamps": "/entry/instrument/NDAttributes/NDArrayTimeStamp", + }, + ) + yield "stream_resource", self._file.stream_resource_doc + + if indices_written >= self._last_emitted: + indices = dict( + start=self._last_emitted, + stop=indices_written, + ) + self._last_emitted = indices_written + self._last_flush = time.monotonic() + yield "stream_datum", self._file.compose_stream_datum(indices) + + async def close(self) -> None: + self._file = None + + +@pytest.fixture +async def sim_hdf_panda(): + async with DeviceCollector(sim=True): + sim_hdf_panda = HDFPandA("HDFPANDA:") + yield sim_hdf_panda + + +async def test_hdf_panda_passes_blocks_to_controller(sim_hdf_panda: HDFPandA): + assert hasattr(sim_hdf_panda.controller, "pcap") + assert sim_hdf_panda.controller.pcap == sim_hdf_panda.pcap diff --git a/tests/panda/test_panda.py b/tests/panda/test_panda_connect.py similarity index 68% rename from tests/panda/test_panda.py rename to tests/panda/test_panda_connect.py index f27befd792..c908e456bd 100644 --- a/tests/panda/test_panda.py +++ b/tests/panda/test_panda_connect.py @@ -1,16 +1,16 @@ -"""Test file specifying how we want to eventually interact with the panda...""" +"""Used to test setting up signals for a PandA""" import copy from typing import Dict import numpy as np import pytest - -from ophyd_async.core import Device, DeviceCollector +from ophyd_async.core import DEFAULT_TIMEOUT, Device, DeviceCollector from ophyd_async.core.utils import NotConnected -from ophyd_async.epics.pvi import PVIEntry +from ophyd_async.epics.pvi import PVIEntry, fill_pvi_entries +from ophyd_async.epics.pvi.pvi import pre_initialize_blocks from ophyd_async.panda import ( - PandA, + CommonPandaBlocks, PcapBlock, PulseBlock, SeqBlock, @@ -44,25 +44,40 @@ def get(self, pv: str, timeout: float = 0.0): @pytest.fixture -async def sim_panda(): +async def panda_t(): + class Panda(CommonPandaBlocks): + def __init__(self, prefix: str, name: str = ""): + self._prefix = prefix + pre_initialize_blocks(self) + super().__init__(name) + + async def connect(self, sim: bool = False, timeout: float = DEFAULT_TIMEOUT): + await fill_pvi_entries(self, self._prefix + "PVI", timeout=timeout, sim=sim) + await super().connect(sim, timeout) + + yield Panda + + +@pytest.fixture +async def sim_panda(panda_t): async with DeviceCollector(sim=True): - sim_panda = PandA("PANDAQSRV:", "sim_panda") + sim_panda = panda_t("PANDAQSRV:", "sim_panda") assert sim_panda.name == "sim_panda" yield sim_panda -def test_panda_names_correct(sim_panda: PandA): +def test_panda_names_correct(sim_panda): assert sim_panda.seq[1].name == "sim_panda-seq-1" assert sim_panda.pulse[1].name == "sim_panda-pulse-1" -def test_panda_name_set(): - panda = PandA("", "panda") +def test_panda_name_set(panda_t): + panda = panda_t("", "panda") assert panda.name == "panda" -async def test_panda_children_connected(sim_panda: PandA): +async def test_panda_children_connected(sim_panda): # try to set and retrieve from simulated values... table = SeqTable( repeats=np.array([1, 1, 1, 32]).astype(np.uint16), @@ -98,8 +113,8 @@ async def test_panda_children_connected(sim_panda: PandA): assert readback_seq == table -async def test_panda_with_missing_blocks(panda_pva): - panda = PandA("PANDAQSRVI:") +async def test_panda_with_missing_blocks(panda_pva, panda_t): + panda = panda_t("PANDAQSRVI:") with pytest.raises(RuntimeError) as exc: await panda.connect() assert ( @@ -108,8 +123,8 @@ async def test_panda_with_missing_blocks(panda_pva): ) -async def test_panda_with_extra_blocks_and_signals(panda_pva): - panda = PandA("PANDAQSRV:") +async def test_panda_with_extra_blocks_and_signals(panda_pva, panda_t): + panda = panda_t("PANDAQSRV:") await panda.connect() assert panda.extra # type: ignore @@ -118,10 +133,21 @@ async def test_panda_with_extra_blocks_and_signals(panda_pva): assert panda.pcap.newsignal # type: ignore -async def test_panda_gets_types_from_common_class(panda_pva): - panda = PandA("PANDAQSRV:") +async def test_panda_gets_types_from_common_class(panda_pva, panda_t): + panda = panda_t("PANDAQSRV:") + + # Check that blocks are pre-initialized + assert not hasattr(panda, "seq") + assert not hasattr(panda, "pulse") + pcap = panda.pcap + assert not hasattr(pcap, "active") + assert not hasattr(pcap, "arm") + await panda.connect() + # The pre-initialized blocks are now filled + assert pcap is panda.pcap + # sub devices have the correct types assert isinstance(panda.pcap, PcapBlock) assert isinstance(panda.seq[1], SeqBlock) @@ -140,8 +166,8 @@ async def test_panda_gets_types_from_common_class(panda_pva): assert panda.pcap.newsignal._backend.datatype is None -async def test_panda_block_missing_signals(panda_pva): - panda = PandA("PANDAQSRVIB:") +async def test_panda_block_missing_signals(panda_pva, panda_t): + panda = panda_t("PANDAQSRVIB:") with pytest.raises(Exception) as exc: await panda.connect() @@ -152,8 +178,8 @@ async def test_panda_block_missing_signals(panda_pva): ) -async def test_panda_unable_to_connect_to_pvi(): - panda = PandA("NON-EXISTENT:") +async def test_panda_unable_to_connect_to_pvi(panda_t): + panda = panda_t("NON-EXISTENT:") with pytest.raises(NotConnected) as exc: await panda.connect(timeout=0.01) diff --git a/tests/panda/test_panda_controller.py b/tests/panda/test_panda_controller.py index d58c7e12ea..2ff6a4e069 100644 --- a/tests/panda/test_panda_controller.py +++ b/tests/panda/test_panda_controller.py @@ -3,26 +3,49 @@ from unittest.mock import patch import pytest - -from ophyd_async.core import DetectorTrigger, DeviceCollector -from ophyd_async.panda import PandA, PandaPcapController +from ophyd_async.core import DEFAULT_TIMEOUT, DetectorTrigger, Device, DeviceCollector +from ophyd_async.epics.pvi import fill_pvi_entries +from ophyd_async.epics.signal import epics_signal_rw +from ophyd_async.panda import CommonPandaBlocks, PandaPcapController @pytest.fixture async def sim_panda(): + class Panda(CommonPandaBlocks): + def __init__(self, prefix: str, name: str = ""): + self._prefix = prefix + super().__init__(name) + + async def connect(self, sim: bool = False, timeout: float = DEFAULT_TIMEOUT): + await fill_pvi_entries(self, self._prefix + "PVI", timeout=timeout, sim=sim) + await super().connect(sim, timeout) + async with DeviceCollector(sim=True): - sim_panda = PandA("PANDACONTROLLER:", "sim_panda") + sim_panda = Panda("PANDACONTROLLER:", "sim_panda") + sim_panda.phase_1_signal_units = epics_signal_rw(int, "") + assert sim_panda.name == "sim_panda" yield sim_panda +async def test_panda_controller_not_filled_blocks(): + class PcapBlock(Device): + pass # Not filled + + pandaController = PandaPcapController(pcap=PcapBlock()) + with patch("ophyd_async.panda.panda_controller.wait_for_value", return_value=None): + with pytest.raises(AttributeError) as exc: + await pandaController.arm(num=1, trigger=DetectorTrigger.constant_gate) + assert ("'PcapBlock' object has no attribute 'arm'") in str(exc.value) + + async def test_panda_controller_arm_disarm(sim_panda): - pandaController = PandaPcapController(pcap=sim_panda.pcap) + pandaController = PandaPcapController(sim_panda.pcap) with patch("ophyd_async.panda.panda_controller.wait_for_value", return_value=None): await pandaController.arm(num=1, trigger=DetectorTrigger.constant_gate) await pandaController.disarm() -async def test_panda_controller_wrong_trigger(sim_panda): - pandaController = PandaPcapController(pcap=sim_panda.pcap) +async def test_panda_controller_wrong_trigger(): + pandaController = PandaPcapController(None) with pytest.raises(AssertionError): await pandaController.arm(num=1, trigger=DetectorTrigger.internal) diff --git a/tests/panda/test_panda_utils.py b/tests/panda/test_panda_utils.py index 9dc21e6971..8a135fb72e 100644 --- a/tests/panda/test_panda_utils.py +++ b/tests/panda/test_panda_utils.py @@ -2,18 +2,29 @@ import pytest from bluesky import RunEngine - from ophyd_async.core import save_device from ophyd_async.core.device import DeviceCollector +from ophyd_async.core.utils import DEFAULT_TIMEOUT +from ophyd_async.epics.pvi import fill_pvi_entries from ophyd_async.epics.signal import epics_signal_rw -from ophyd_async.panda import PandA +from ophyd_async.panda import CommonPandaBlocks from ophyd_async.panda.utils import phase_sorter @pytest.fixture async def sim_panda(): + + class Panda(CommonPandaBlocks): + def __init__(self, prefix: str, name: str = ""): + self._prefix = prefix + super().__init__(name) + + async def connect(self, sim: bool = False, timeout: float = DEFAULT_TIMEOUT): + await fill_pvi_entries(self, self._prefix + "PVI", timeout=timeout, sim=sim) + await super().connect(sim, timeout) + async with DeviceCollector(sim=True): - sim_panda = PandA("PANDA") + sim_panda = Panda("PANDA") sim_panda.phase_1_signal_units = epics_signal_rw(int, "") assert sim_panda.name == "sim_panda" yield sim_panda diff --git a/tests/panda/test_trigger.py b/tests/panda/test_trigger.py index a4c3dc8a78..894dc28685 100644 --- a/tests/panda/test_trigger.py +++ b/tests/panda/test_trigger.py @@ -1,20 +1,29 @@ import pytest - -from ophyd_async.core.device import DeviceCollector -from ophyd_async.panda import PandA +from ophyd_async.core.device import DEFAULT_TIMEOUT, DeviceCollector +from ophyd_async.epics.pvi.pvi import fill_pvi_entries +from ophyd_async.panda import CommonPandaBlocks from ophyd_async.panda.trigger import StaticSeqTableTriggerLogic @pytest.fixture async def panda(): + class Panda(CommonPandaBlocks): + def __init__(self, prefix: str, name: str = ""): + self._prefix = prefix + super().__init__(name) + + async def connect(self, sim: bool = False, timeout: float = DEFAULT_TIMEOUT): + await fill_pvi_entries(self, self._prefix + "PVI", timeout=timeout, sim=sim) + await super().connect(sim, timeout) + async with DeviceCollector(sim=True): - sim_panda = PandA("PANDAQSRV:", "sim_panda") + sim_panda = Panda("PANDAQSRV:", "sim_panda") assert sim_panda.name == "sim_panda" yield sim_panda -def test_trigger_logic_has_given_methods(panda: PandA): +def test_trigger_logic_has_given_methods(panda): trigger_logic = StaticSeqTableTriggerLogic(panda.seq[1]) assert hasattr(trigger_logic, "prepare") assert hasattr(trigger_logic, "kickoff") diff --git a/tests/test_flyer_with_panda.py b/tests/test_flyer_with_panda.py index 573177e701..db4373c6e1 100644 --- a/tests/test_flyer_with_panda.py +++ b/tests/test_flyer_with_panda.py @@ -7,7 +7,6 @@ from bluesky.protocols import Descriptor, StreamAsset from bluesky.run_engine import RunEngine from event_model import ComposeStreamResourceBundle, compose_stream_resource - from ophyd_async.core import ( DEFAULT_TIMEOUT, DetectorControl, @@ -19,7 +18,8 @@ from ophyd_async.core.detector import StandardDetector from ophyd_async.core.device import DeviceCollector from ophyd_async.core.signal import observe_value, set_sim_value -from ophyd_async.panda import PandA +from ophyd_async.epics.pvi.pvi import fill_pvi_entries +from ophyd_async.panda import CommonPandaBlocks from ophyd_async.panda.trigger import StaticSeqTableTriggerLogic from ophyd_async.planstubs import ( prepare_static_seq_table_flyer_and_detectors_with_same_trigger, @@ -115,8 +115,17 @@ async def dummy_arm_2(self=None, trigger=None, num=0, exposure=None): @pytest.fixture async def panda(): + class Panda(CommonPandaBlocks): + def __init__(self, prefix: str, name: str = ""): + self._prefix = prefix + super().__init__(name) + + async def connect(self, sim: bool = False, timeout: float = DEFAULT_TIMEOUT): + await fill_pvi_entries(self, self._prefix + "PVI", timeout=timeout, sim=sim) + await super().connect(sim, timeout) + async with DeviceCollector(sim=True): - sim_panda = PandA("PANDAQSRV:", "sim_panda") + sim_panda = Panda("PANDAQSRV:", "sim_panda") assert sim_panda.name == "sim_panda" yield sim_panda