Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
evalott100 committed Jan 31, 2025
1 parent da625bf commit c403761
Show file tree
Hide file tree
Showing 25 changed files with 98 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/ophyd_async/core/_signal_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
# To be a 1D array shape should really be tuple[int], but np.array()
# currently produces tuple[int, ...] even when it has 1D input args
# https://github.com/numpy/numpy/issues/28077#issuecomment-2566485178

Array1D = np.ndarray[tuple[int, ...], np.dtype[DTypeScalar_co]]
"""A type alias for a 1D numpy array with a specific scalar data type."""

Primitive = bool | int | float | str
SignalDatatype = (
Primitive
Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/epics/adandor/_andor_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@


class Andor2Controller(adcore.ADBaseController[Andor2DriverIO]):
"""For controlling the Andor 2 detector."""

def __init__(
self,
driver: Andor2DriverIO,
Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/epics/adkinetix/_kinetix_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@


class KinetixController(adcore.ADBaseController[KinetixDriverIO]):
"""Controller for adkinetix detector."""

def __init__(
self,
driver: KinetixDriverIO,
Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/epics/adkinetix/_kinetix_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@


class KinetixTriggerMode(StrictEnum):
"""Trigger mode for ADKinetix detector."""

INTERNAL = "Internal"
EDGE = "Rising Edge"
GATE = "Exp. Gate"
Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/epics/adpilatus/_pilatus_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class PilatusReadoutTime(float, Enum):


class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
"""Controller for ADPilatus detector."""

_supported_trigger_types = {
DetectorTrigger.INTERNAL: PilatusTriggerMode.INTERNAL,
DetectorTrigger.CONSTANT_GATE: PilatusTriggerMode.EXT_ENABLE,
Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/epics/adpilatus/_pilatus_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@


class PilatusTriggerMode(StrictEnum):
"""Trigger modes for ADPilatus detector."""

INTERNAL = "Internal"
EXT_ENABLE = "Ext. Enable"
EXT_TRIGGER = "Ext. Trigger"
Expand Down
9 changes: 8 additions & 1 deletion src/ophyd_async/epics/adsimdetector/_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
from ophyd_async.epics import adcore


class SimDriverIO(adcore.ADBaseIO): ...
class SimDriverIO(adcore.ADBaseIO):
"""Base class for driving simulated Areadetector IO."""

pass


class SimController(adcore.ADBaseController[SimDriverIO]):
"""Controller for simulated Areadetector."""

def __init__(
self,
driver: SimDriverIO,
Expand All @@ -20,6 +25,8 @@ def get_deadtime(self, exposure: float | None) -> float:


class SimDetector(adcore.AreaDetector[SimController]):
"""Detector for simulated Areadetector."""

def __init__(
self,
prefix: str,
Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/epics/advimba/_vimba_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@


class VimbaController(adcore.ADBaseController[VimbaDriverIO]):
"""Controller for the Vimba detector."""

def __init__(
self,
driver: VimbaDriverIO,
Expand Down
6 changes: 6 additions & 0 deletions src/ophyd_async/epics/advimba/_vimba_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class VimbaConvertFormat(StrictEnum):


class VimbaTriggerSource(StrictEnum):
"""Mode for the source of triggers on the Vimbda."""

FREERUN = "Freerun"
LINE1 = "Line1"
LINE2 = "Line2"
Expand All @@ -35,11 +37,15 @@ class VimbaOverlap(StrictEnum):


class VimbaOnOff(StrictEnum):
"""On/Off modes on the Vimba detector."""

ON = "On"
OFF = "Off"


class VimbaExposeOutMode(StrictEnum):
"""Modes for exposure on the Vimba detector."""

TIMED = "Timed" # Use ExposureTime PV
TRIGGER_WIDTH = "TriggerWidth" # Expose for length of high signal

Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/epics/core/_aioca.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ def _use_pyepics_context_if_imported():


class CaSignalBackend(EpicsSignalBackend[SignalDatatypeT]):
"""Backend for a signal to interact with PVs over channel access."""

def __init__(
self,
datatype: type[SignalDatatypeT] | None,
Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/epics/core/_epics_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ def fill_backend_with_prefix(


class EpicsDeviceConnector(DeviceConnector):
"""Used for connecting signals to static EPICS pvs."""

def __init__(self, prefix: str) -> None:
self.prefix = prefix

Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/epics/core/_p4p.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ def _pva_request_string(fields: Sequence[str]) -> str:


class PvaSignalBackend(EpicsSignalBackend[SignalDatatypeT]):
"""Backend for a signal to interact with PVs over pva."""

def __init__(
self,
datatype: type[SignalDatatypeT] | None,
Expand Down
5 changes: 5 additions & 0 deletions src/ophyd_async/epics/core/_pvi_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ def _get_signal_details(entry: Entry) -> tuple[type[Signal], str, str]:


class PviDeviceConnector(DeviceConnector):
"""
Used for connecting to PVI devices, where signals are be dynamically
defined at introspection.
"""

def __init__(self, prefix: str = "", error_hint: str = "") -> None:
# TODO: what happens if we get a leading "pva://" here?
self.prefix = prefix
Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/epics/eiger/_eiger_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@


class EigerController(DetectorController):
"""Controller for the Eiger detector."""

def __init__(
self,
driver: EigerDriverIO,
Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/epics/eiger/_eiger_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class EigerTriggerMode(StrictEnum):


class EigerDriverIO(Device):
"""Contains signals for handling IO on the Eiger detector."""

def __init__(self, prefix: str, name: str = "") -> None:
self.bit_depth = epics_signal_r(int, f"{prefix}BitDepthReadout")
self.stale_parameters = epics_signal_r(bool, f"{prefix}StaleParameters")
Expand Down
4 changes: 4 additions & 0 deletions src/ophyd_async/epics/motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@


class MotorLimitsException(Exception):
"""Exception for invalid motor limits."""

pass


class InvalidFlyMotorException(Exception):
"""Exception for invalid motor."""

pass


Expand Down
10 changes: 10 additions & 0 deletions src/ophyd_async/epics/testing/_example_ioc.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@


class EpicsTestEnum(StrictEnum):
"""For testing strict enum values in test IOCs."""

A = "Aaa"
B = "Bbb"
C = "Ccc"


class EpicsTestSubsetEnum(SubsetEnum):
"""For testing subset enum values in test IOCs."""

A = "Aaa"
B = "Bbb"

Expand All @@ -34,6 +38,8 @@ class EpicsTestTable(Table):


class EpicsTestCaDevice(EpicsDevice):
"""Device for use in a channel access test IOC."""

my_int: A[SignalRW[int], PvSuffix("int")]
my_float: A[SignalRW[float], PvSuffix("float")]
float_prec_0: A[SignalRW[int], PvSuffix("float_prec_0")]
Expand All @@ -57,6 +63,8 @@ class EpicsTestCaDevice(EpicsDevice):


class EpicsTestPvaDevice(EpicsTestCaDevice):
"""Device for use in a pv access test IOC."""

# pva can support all signal types that ca can
int8a: A[SignalRW[Array1D[np.int8]], PvSuffix("int8a")]
uint16a: A[SignalRW[Array1D[np.uint16]], PvSuffix("uint16a")]
Expand All @@ -68,6 +76,8 @@ class EpicsTestPvaDevice(EpicsTestCaDevice):


class EpicsTestIocAndDevices:
"""Test IOC with ca and pva devices."""

def __init__(self):
self.prefix = generate_random_pv_prefix()
self.ioc = TestingIOC()
Expand Down
3 changes: 3 additions & 0 deletions src/ophyd_async/epics/testing/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@


def generate_random_pv_prefix() -> str:
"""For generating random PV names in test devices."""
return "".join(random.choice(string.ascii_lowercase) for _ in range(12)) + ":"


class TestingIOC:
"""For initialising an IOC in tests."""

def __init__(self):
self._db_macros: list[tuple[Path, dict[str, str]]] = []
self.output = ""
Expand Down
1 change: 1 addition & 0 deletions src/ophyd_async/fastcs/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@


def fastcs_connector(device: Device, uri: str, error_hint: str = "") -> DeviceConnector:
"""Used to create devices and connections on pvi device `Device`."""
# TODO: add Tango support based on uri scheme
connector = PviDeviceConnector(uri, error_hint)
connector.create_children_from_annotations(device)
Expand Down
20 changes: 20 additions & 0 deletions src/ophyd_async/fastcs/panda/_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@


class CaptureMode(StrictEnum):
"""Capture mode for the `DataBlock` on the PandA."""

FIRST_N = "FIRST_N"
LAST_N = "LAST_N"
FOREVER = "FOREVER"


class DataBlock(Device):
"""Data block for the PandA. Used for writing data through the IOC."""

# In future we may decide to make hdf_* optional
hdf_directory: SignalRW[str]
hdf_file_name: SignalRW[str]
Expand All @@ -31,22 +35,30 @@ class DataBlock(Device):


class PulseBlock(Device):
"""Used for configuring pulses in the PandA."""

delay: SignalRW[float]
width: SignalRW[float]


class PcompDirection(StrictEnum):
"""Direction options for position compare in the PandA."""

POSITIVE = "Positive"
NEGATIVE = "Negative"
EITHER = "Either"


class BitMux(SubsetEnum):
"""Bit input with configurable delay in the PandA."""

ZERO = "ZERO"
ONE = "ONE"


class PcompBlock(Device):
"""Position compare block in the PandA."""

active: SignalR[bool]
dir: SignalRW[PcompDirection]
enable: SignalRW[BitMux]
Expand All @@ -57,13 +69,17 @@ class PcompBlock(Device):


class TimeUnits(StrictEnum):
"""Options for units of time in the PandA."""

MIN = "min"
S = "s"
MS = "ms"
US = "us"


class SeqBlock(Device):
"""Sequencer block in the PandA."""

table: SignalRW[SeqTable]
active: SignalR[bool]
repeats: SignalRW[int]
Expand All @@ -73,11 +89,15 @@ class SeqBlock(Device):


class PcapBlock(Device):
"""Position capture block in the PandA."""

active: SignalR[bool]
arm: SignalRW[bool]


class CommonPandaBlocks(Device):
"""Pandablocks device with blocks which are common and required on introspection."""

pulse: DeviceVector[PulseBlock]
seq: DeviceVector[SeqBlock]
pcomp: DeviceVector[PcompBlock]
Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/fastcs/panda/_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@


class PandaPcapController(DetectorController):
"""For controlling a PCAP capture on the PandA."""

def __init__(self, pcap: PcapBlock) -> None:
self.pcap = pcap
self._arm_status: AsyncStatus | None = None
Expand Down
2 changes: 2 additions & 0 deletions src/ophyd_async/fastcs/panda/_hdf_panda.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
class HDFPanda(
CommonPandaBlocks, StandardDetector[PandaPcapController, PandaHDFWriter]
):
"""PandA with common blocks for standard HDF writing."""

def __init__(
self,
prefix: str,
Expand Down
6 changes: 6 additions & 0 deletions src/ophyd_async/fastcs/panda/_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@


class PandaHdf5DatasetType(StrictEnum):
"""Dataset options for HDF capture."""

FLOAT_64 = "float64"
UINT_32 = "uint32"

Expand All @@ -17,6 +19,8 @@ class DatasetTable(Table):


class SeqTrigger(StrictEnum):
"""Trigger options for the SeqTable."""

IMMEDIATE = "Immediate"
BITA_0 = "BITA=0"
BITA_1 = "BITA=1"
Expand All @@ -33,6 +37,8 @@ class SeqTrigger(StrictEnum):


class SeqTable(Table):
"""Data type for the panda seq table."""

repeats: Array1D[np.uint16]
trigger: Sequence[SeqTrigger]
position: Array1D[np.int32]
Expand Down
4 changes: 4 additions & 0 deletions src/ophyd_async/fastcs/panda/_trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class SeqTableInfo(BaseModel):


class StaticSeqTableTriggerLogic(FlyerController[SeqTableInfo]):
"""For controlling the PandA `SeqTable` when flyscanning."""

def __init__(self, seq: SeqBlock) -> None:
self.seq = seq

Expand Down Expand Up @@ -64,6 +66,8 @@ class PcompInfo(BaseModel):


class StaticPcompTriggerLogic(FlyerController[PcompInfo]):
"""For controlling the PandA `PcompBlock` when flyscanning."""

def __init__(self, pcomp: PcompBlock) -> None:
self.pcomp = pcomp

Expand Down
Loading

0 comments on commit c403761

Please sign in to comment.