"""Support for the ADVimba areaDetector driver.
https://github.com/areaDetector/ADVimba.
"""
import asyncio
from collections.abc import Sequence
from typing import Annotated as A
from ophyd_async.core import (
DetectorTriggerLogic,
OnOff,
PathProvider,
SignalDict,
SignalR,
SignalRW,
StrictEnum,
)
from ophyd_async.epics.core import PvSuffix
from .adcore import (
ADArmLogic,
ADBaseIO,
ADWriterType,
AreaDetector,
NDPluginBaseIO,
prepare_exposures,
)
from .adgenicam import get_camera_deadtime
__all__ = [
"VimbaDetector",
"VimbaDriverIO",
"VimbaTriggerLogic",
"VimbaConvertFormat",
"VimbaTriggerSource",
"VimbaOverlap",
"VimbaExposeOutMode",
]
[docs]
class VimbaTriggerSource(StrictEnum):
"""Mode for the source of triggers on the Vimba."""
FREERUN = "Freerun"
LINE1 = "Line1"
LINE2 = "Line2"
FIXED_RATE = "FixedRate"
SOFTWARE = "Software"
ACTION0 = "Action0"
ACTION1 = "Action1"
[docs]
class VimbaOverlap(StrictEnum):
"""Overlap modes for the Vimba detector."""
OFF = OnOff.OFF.value
PREV_FRAME = "PreviousFrame"
[docs]
class VimbaExposeOutMode(StrictEnum):
"""Exposure control modes for Vimba detectors."""
TIMED = "Timed" # Use ExposureTime PV
TRIGGER_WIDTH = "TriggerWidth" # Expose for length of high signal
[docs]
class VimbaDriverIO(ADBaseIO):
"""Mirrors the interface provided by ADVimba/db/vimba.template."""
convert_pixel_format: A[
SignalRW[VimbaConvertFormat], PvSuffix.rbv("ConvertPixelFormat")
]
trigger_source: A[SignalRW[VimbaTriggerSource], PvSuffix.rbv("TriggerSource")]
trigger_mode: A[SignalRW[OnOff], PvSuffix.rbv("TriggerMode")]
trigger_overlap: A[SignalRW[VimbaOverlap], PvSuffix.rbv("TriggerOverlap")]
exposure_mode: A[SignalRW[VimbaExposeOutMode], PvSuffix.rbv("ExposureMode")]
[docs]
class VimbaTriggerLogic(DetectorTriggerLogic):
"""Trigger logic for ADVimba detectors."""
def __init__(self, driver: VimbaDriverIO, override_deadtime: float | None = None):
self.driver = driver
self.override_deadtime = override_deadtime
[docs]
def config_sigs(self) -> set[SignalR]:
return {self.driver.model}
[docs]
def get_deadtime(self, config_values: SignalDict) -> float:
return get_camera_deadtime(
model=config_values[self.driver.model],
override_deadtime=self.override_deadtime,
)
[docs]
async def prepare_internal(self, num: int, livetime: float, deadtime: float):
await asyncio.gather(
self.driver.trigger_mode.set(OnOff.OFF),
self.driver.exposure_mode.set(VimbaExposeOutMode.TIMED),
self.driver.trigger_source.set(VimbaTriggerSource.FREERUN),
)
await prepare_exposures(self.driver, num, livetime, deadtime)
[docs]
async def prepare_edge(self, num: int, livetime: float):
await asyncio.gather(
self.driver.trigger_mode.set(OnOff.ON),
self.driver.exposure_mode.set(VimbaExposeOutMode.TIMED),
self.driver.trigger_source.set(VimbaTriggerSource.LINE1),
)
await prepare_exposures(self.driver, num, livetime)
[docs]
async def prepare_level(self, num: int):
await asyncio.gather(
self.driver.trigger_mode.set(OnOff.ON),
self.driver.exposure_mode.set(VimbaExposeOutMode.TRIGGER_WIDTH),
self.driver.trigger_source.set(VimbaTriggerSource.LINE1),
)
await prepare_exposures(self.driver, num)
[docs]
class VimbaDetector(AreaDetector[VimbaDriverIO]):
"""Create an ADVimba AreaDetector instance.
:param prefix: EPICS PV prefix for the detector
:param path_provider: Provider for file paths during acquisition
:param driver_suffix: Suffix for the driver PV, defaults to "cam1:"
:param override_deadtime:
If provided, this value is used for deadtime instead of looking up
based on camera model.
:param writer_type: Type of file writer (HDF or TIFF)
:param writer_suffix: Suffix for the writer PV
:param plugins: Additional areaDetector plugins to include
:param config_sigs: Additional signals to include in configuration
:param name: Name for the detector device
"""
def __init__(
self,
prefix: str,
path_provider: PathProvider | None = None,
driver_suffix="cam1:",
override_deadtime: float | None = None,
writer_type: ADWriterType | None = ADWriterType.HDF,
writer_suffix: str | None = None,
plugins: dict[str, NDPluginBaseIO] | None = None,
config_sigs: Sequence[SignalR] = (),
name: str = "",
) -> None:
driver = VimbaDriverIO(prefix + driver_suffix)
super().__init__(
prefix=prefix,
driver=driver,
arm_logic=ADArmLogic(driver),
trigger_logic=VimbaTriggerLogic(driver, override_deadtime),
path_provider=path_provider,
writer_type=writer_type,
writer_suffix=writer_suffix,
plugins=plugins,
config_sigs=config_sigs,
name=name,
)