Source code for ophyd_async.tango.testing._test_device
from typing import Annotated as A
from typing import Any
import numpy as np
from ophyd_async.core import (
Array1D,
Command,
SignalRW,
StandardReadable,
TriggerableCommand,
)
from ophyd_async.tango.core import DevStateEnum, TangoDevice, TangoPolling
from ._example_types import ExampleStrEnum
[docs]
class TangoTestDevice(TangoDevice, StandardReadable):
"""Declarative ophyd-async Device pairing 1:1 with `OneOfEverythingTangoDevice`.
Constructed with `auto_fill_signals=False`, so *only* these `Annotated` fields
are created — a purely declarative Device, unlike `TangoDevice(trl)` used on its
own (the default `auto_fill_signals=True` procedural/dynamic flavour, which
discovers every attribute/command on the live proxy with no annotations at all).
Both flavours connect to the exact same running `OneOfEverythingTangoDevice`
server instance and see identical values — see
`tests/system_tests/tango/core/test_tango_declarative_device.py`.
Field names must equal the server's real Tango attribute/command names (Tango
has no PvSuffix-equivalent indirection). A curated subset, not every datatype
`OneOfEverythingTangoDevice` serves (that full matrix is
`OneOfEverythingTangoDevice` itself plus the fully-dynamic `TangoDevice(trl)`
procedural flavour) — enough to demonstrate: scalar RW (`a_str`, `a_bool`), a
`StrictEnum` field (`strenum`), a `DevState`-backed enum (`my_state`),
a numeric scalar with change-detect polling params (`float64`), a spectrum/array
field (`int32_spectrum`), an image field (`float64_image`), a zero-arg
`TriggerableCommand` (`void_cmd`), and a typed `Command[P, T]` with mismatched
in/out types (`float_to_bool_cmd`).
`int32_spectrum` is typed `np.int_` (platform-native width, not `np.int32`) to
match what `get_python_type` (`ophyd_async.tango.core`) actually reports for
Tango SPECTRUM attributes - it maps every signed integer Tango type to plain
`int` regardless of width (unlike its own command return-type mapping, which
is width-specific), so a width-specific annotation here would fail the
connector's datatype check even though nothing is wrong. `np.int_`/`np.float64`
are used rather than bare `int`/`float` only because `Array1D`/`np.dtype` need
an `np.generic` subclass, not because the precision matters -
`np.dtype(np.int_) == np.dtype(int)` and `np.dtype(np.float64) == np.dtype(float)`
are both `True`.
`TangoPolling` is given on every field even though `OneOfEverythingTangoDevice`
itself pushes Tango change events for all its attributes, because
`TangoDevice`'s default `support_events=False` means the connector polls unless
told otherwise — this mirrors `ophyd_async.tango.demo.DemoMotor`'s convention so
copied-and-pasted reference code keeps working against servers that don't push
events.
"""
a_str: A[SignalRW[str], TangoPolling(0.1)]
a_bool: A[SignalRW[bool], TangoPolling(0.1)]
strenum: A[SignalRW[ExampleStrEnum], TangoPolling(0.1)]
my_state: A[SignalRW[DevStateEnum], TangoPolling(0.1)]
float64: A[SignalRW[float], TangoPolling(0.1, 0.001, 0.001)]
int32_spectrum: A[SignalRW[Array1D[np.int_]], TangoPolling(0.1)]
float64_image: A[SignalRW[np.ndarray[Any, np.dtype[np.float64]]], TangoPolling(0.1)]
void_cmd: TriggerableCommand
float_to_bool_cmd: Command[[float], bool]
def __init__(self, trl: str = "", name: str = "") -> None:
super().__init__(trl, name=name, auto_fill_signals=False)