diff --git a/examples/configs/signal-generator.yaml b/examples/configs/signal-generator.yaml deleted file mode 100644 index f7faeccfa..000000000 --- a/examples/configs/signal-generator.yaml +++ /dev/null @@ -1,15 +0,0 @@ -- type: tickit.devices.signal_generator.EpicsSignalGenerator - name: gen - inputs: {} -- type: tickit.devices.sink.Sink - name: sink - inputs: - input: - component: gen - port: value -- type: tickit.devices.sink.Sink - name: sink - inputs: - input: - component: gen - port: gate diff --git a/src/tickit/adapters/epics.py b/src/tickit/adapters/epics.py index bac9609dd..62e9180fd 100644 --- a/src/tickit/adapters/epics.py +++ b/src/tickit/adapters/epics.py @@ -3,8 +3,7 @@ import logging from abc import abstractmethod from dataclasses import dataclass -from enum import Enum -from typing import Any, Awaitable, Callable, Dict, Optional, Set, TypeVar +from typing import Any, Awaitable, Callable, Dict, Optional, Set from softioc import asyncio_dispatcher, builder, softioc @@ -42,6 +41,7 @@ def register_adapter() -> int: def register_background_task(task: Awaitable[None]) -> None: _REGISTERED_IOC_BACKGROUND_TASKS.add(task) + def notify_adapter_ready(adapter_id: int) -> None: """Notify the builder that a particular adapter has made all the records it needs. @@ -103,25 +103,45 @@ class OutputRecord: class EpicsAdapter: """An adapter interface for the EpicsIo.""" - interrupt_records: Dict[InputRecord, Callable[[], Any]] = {} + interrupt_records: Dict[InputRecord, Callable[[], Any]] interrupt: RaiseInterrupt + def __init__(self) -> None: + self.interrupt_records = {} + def float_rbv( self, name: str, getter: Callable[[], float], setter: Callable[[float], None], rbv_name: Optional[str] = None, + precision: int = 2, ): rbv_name = rbv_name or f"{name}_RBV" builder.aOut( name, initial_value=getter(), on_update=self.interrupting_callback(setter), + PREC=precision, + ) + rbv = builder.aIn( + rbv_name, + initial_value=getter(), + PREC=precision, ) - rbv = builder.aIn(rbv_name, initial_value=getter()) self.link_input_on_interrupt(rbv, getter) + def float_ro( + self, + name: str, + getter: Callable[[], float], + precision: int = 2, + ): + self.link_input_on_interrupt( + builder.aIn(name, PREC=precision), + getter, + ) + def int_rbv( self, name: str, @@ -154,22 +174,11 @@ def bool_rbv( rbv = builder.boolIn(rbv_name, initial_value=getter()) self.link_input_on_interrupt(rbv, getter) - def bool_rbv( - self, - name: str, - getter: Callable[[], bool], - setter: Callable[[bool], None], - rbv_name: Optional[str] = None, - ): - rbv_name = rbv_name or f"{name}_RBV" - builder.boolOut( - name, - initial_value=getter(), - on_update=self.interrupting_callback(setter), + def bool_ro(self, name: str, getter: Callable[[], float]): + self.link_input_on_interrupt( + builder.boolIn(name), + getter, ) - rbv = builder.boolIn(rbv_name, initial_value=getter()) - self.link_input_on_interrupt(rbv, getter) - def interrupting_callback( self, action: Callable[[Any], None] @@ -208,5 +217,5 @@ async def polling_task() -> None: while True: await asyncio.sleep(interval) await self.interrupt() - + register_background_task(polling_task()) diff --git a/src/tickit/devices/signal_generator.edl b/src/tickit/devices/signal_generator.edl deleted file mode 100644 index c3a3cf3d5..000000000 --- a/src/tickit/devices/signal_generator.edl +++ /dev/null @@ -1,595 +0,0 @@ -4 0 1 -beginScreenProperties -major 4 -minor 0 -release 1 -x 2660 -y 386 -w 620 -h 556 -font "arial-bold-r-14.0" -ctlFont "arial-bold-r-14.0" -btnFont "arial-bold-r-14.0" -fgColor index 14 -bgColor index 3 -textColor index 14 -ctlFgColor1 index 14 -ctlFgColor2 index 14 -ctlBgColor1 index 3 -ctlBgColor2 index 3 -topShadowColor index 1 -botShadowColor index 11 -title "Sine Generator" -showGrid -endScreenProperties - -# (Rectangle) -object activeRectangleClass -beginObjectProperties -major 4 -minor 0 -release 0 -x 317 -y 57 -w 140 -h 160 -lineColor index 14 -fill -fillColor index 5 -endObjectProperties - -# (X-Y Graph) -object xyGraphClass -beginObjectProperties -major 4 -minor 8 -release 0 -# Geometry -x 3 -y 222 -w 457 -h 227 -# Appearance -plotAreaBorder -autoScaleUpdateMs 5000 -autoScaleThreshPct 80 -fgColor index 13 -bgColor index 7 -gridColor index 33 -font "arial-bold-r-14.0" -# Operating Modes -plotMode "plotLastNPts" -nPts 3000 -updateTimerMs 100 -# X axis properties -showXAxis -xAxisStyle "time" -xAxisSrc "AutoScale" -xMax 1 -xShowLabelGrid -# Y axis properties -showYAxis -yAxisSrc "AutoScale" -yMin -5 -yMax 5 -yShowLabelGrid -yShowMajorGrid -# Y2 axis properties -y2AxisSrc "AutoScale" -y2Max 1 -# Trace Properties -numTraces 1 -yPv { - 0 "$(P):Signal_RBV" -} -plotColor { - 0 index 36 -} -endObjectProperties - -# (Rectangle) -object activeRectangleClass -beginObjectProperties -major 4 -minor 0 -release 0 -x 7 -y 57 -w 304 -h 160 -lineColor index 14 -fill -fillColor index 5 -endObjectProperties - -# (Rectangle) -object activeRectangleClass -beginObjectProperties -major 4 -minor 0 -release 0 -x 0 -y 0 -w 624 -h 40 -lineColor index 61 -fill -fillColor index 61 -endObjectProperties - -# (Static Text) -object activeXTextClass -beginObjectProperties -major 4 -minor 1 -release 1 -x 16 -y 11 -w 204 -h 21 -font "arial-bold-r-18.0" -fgColor index 14 -bgColor index 3 -useDisplayBg -value { - "$(P) - Signal Generator" -} -autoSize -endObjectProperties - -# (Exit Button) -object activeExitButtonClass -beginObjectProperties -major 4 -minor 1 -release 0 -x 367 -y 457 -w 90 -h 30 -fgColor index 46 -bgColor index 3 -topShadowColor index 1 -botShadowColor index 11 -label "EXIT" -font "arial-medium-r-18.0" -3d -endObjectProperties - -# (Static Text) -object activeXTextClass -beginObjectProperties -major 4 -minor 1 -release 1 -x 12 -y 47 -w 57 -h 16 -font "arial-bold-r-14.0" -fgColor index 14 -bgColor index 5 -value { - "Settings" -} -autoSize -border -endObjectProperties - -# (Static Text) -object activeXTextClass -beginObjectProperties -major 4 -minor 1 -release 1 -x 337 -y 127 -w 45 -h 16 -font "arial-bold-r-14.0" -fgColor index 14 -bgColor index 3 -useDisplayBg -value { - "Signal" -} -autoSize -endObjectProperties - -# (Static Text) -object activeXTextClass -beginObjectProperties -major 4 -minor 1 -release 1 -x 321 -y 46 -w 45 -h 16 -font "arial-bold-r-14.0" -fgColor index 14 -bgColor index 5 -value { - "Status" -} -autoSize -border -endObjectProperties - -# (Text Monitor) -object activeXTextDspClass:noedit -beginObjectProperties -major 4 -minor 6 -release 0 -x 337 -y 157 -w 110 -h 20 -controlPv "$(P):Signal_RBV" -format "float" -font "arial-bold-r-14.0" -fontAlign "center" -fgColor index 16 -fgAlarm -bgColor index 10 -precision 3 -nullColor index 14 -fastUpdate -useHexPrefix -showUnits -newPos -objType "monitors" -noExecuteClipMask -endObjectProperties - -# (Text Monitor) -object activeXTextDspClass:noedit -beginObjectProperties -major 4 -minor 6 -release 0 -x 225 -y 64 -w 70 -h 20 -controlPv "$(P):Amplitude_RBV" -format "float" -font "arial-bold-r-14.0" -fontAlign "center" -fgColor index 16 -fgAlarm -bgColor index 10 -precision 3 -nullColor index 14 -fastUpdate -useHexPrefix -showUnits -newPos -objType "monitors" -noExecuteClipMask -endObjectProperties - -# (Textentry) -object TextentryClass -beginObjectProperties -major 10 -minor 0 -release 0 -x 126 -y 62 -w 95 -h 23 -controlPv "$(P):Amplitude" -displayMode "decimal" -precision 3 -fgColor index 25 -fgAlarm -bgColor index 3 -fill -font "arial-bold-r-14.0" -endObjectProperties - -# (Static Text) -object activeXTextClass -beginObjectProperties -major 4 -minor 1 -release 1 -x 27 -y 67 -w 72 -h 16 -font "arial-bold-r-14.0" -fgColor index 14 -bgColor index 3 -useDisplayBg -value { - "Amplitude" -} -autoSize -endObjectProperties - -# (Text Monitor) -object activeXTextDspClass:noedit -beginObjectProperties -major 4 -minor 6 -release 0 -x 225 -y 94 -w 70 -h 20 -controlPv "$(P):Offset_RBV" -format "float" -font "arial-bold-r-14.0" -fontAlign "center" -fgColor index 16 -fgAlarm -bgColor index 10 -precision 3 -nullColor index 14 -fastUpdate -useHexPrefix -showUnits -newPos -objType "monitors" -noExecuteClipMask -endObjectProperties - -# (Textentry) -object TextentryClass -beginObjectProperties -major 10 -minor 0 -release 0 -x 126 -y 92 -w 95 -h 23 -controlPv "$(P):Offset" -displayMode "decimal" -precision 3 -fgColor index 25 -fgAlarm -bgColor index 3 -fill -font "arial-bold-r-14.0" -endObjectProperties - -# (Static Text) -object activeXTextClass -beginObjectProperties -major 4 -minor 1 -release 1 -x 57 -y 97 -w 43 -h 16 -font "arial-bold-r-14.0" -fgColor index 14 -bgColor index 3 -useDisplayBg -value { - "Offset" -} -autoSize -endObjectProperties - -# (Text Monitor) -object activeXTextDspClass:noedit -beginObjectProperties -major 4 -minor 6 -release 0 -x 225 -y 124 -w 70 -h 20 -controlPv "$(P):Frequency_RBV" -format "float" -font "arial-bold-r-14.0" -fontAlign "center" -fgColor index 16 -fgAlarm -bgColor index 10 -precision 3 -nullColor index 14 -fastUpdate -useHexPrefix -showUnits -newPos -objType "monitors" -noExecuteClipMask -endObjectProperties - -# (Textentry) -object TextentryClass -beginObjectProperties -major 10 -minor 0 -release 0 -x 126 -y 122 -w 95 -h 23 -controlPv "$(P):Frequency" -displayMode "decimal" -precision 3 -fgColor index 25 -fgAlarm -bgColor index 3 -fill -font "arial-bold-r-14.0" -endObjectProperties - -# (Static Text) -object activeXTextClass -beginObjectProperties -major 4 -minor 1 -release 1 -x 27 -y 127 -w 75 -h 16 -font "arial-bold-r-14.0" -fgColor index 14 -bgColor index 3 -useDisplayBg -value { - "Frequency" -} -autoSize -endObjectProperties - -# (Text Monitor) -object activeXTextDspClass:noedit -beginObjectProperties -major 4 -minor 6 -release 0 -x 225 -y 154 -w 70 -h 20 -controlPv "$(P):GateThreshold_RBV" -format "float" -font "arial-bold-r-14.0" -fontAlign "center" -fgColor index 16 -fgAlarm -bgColor index 10 -precision 3 -nullColor index 14 -fastUpdate -useHexPrefix -showUnits -newPos -objType "monitors" -noExecuteClipMask -endObjectProperties - -# (Textentry) -object TextentryClass -beginObjectProperties -major 10 -minor 0 -release 0 -x 126 -y 152 -w 95 -h 23 -controlPv "$(P):GateThreshold" -displayMode "decimal" -precision 3 -fgColor index 25 -fgAlarm -bgColor index 3 -fill -font "arial-bold-r-14.0" -endObjectProperties - -# (Static Text) -object activeXTextClass -beginObjectProperties -major 4 -minor 1 -release 1 -x 27 -y 157 -w 72 -h 16 -font "arial-bold-r-14.0" -fgColor index 14 -bgColor index 3 -useDisplayBg -value { - "Threshold" -} -autoSize -endObjectProperties - -# (Static Text) -object activeXTextClass -beginObjectProperties -major 4 -minor 1 -release 1 -x 347 -y 87 -w 33 -h 16 -font "arial-bold-r-14.0" -fgColor index 14 -bgColor index 3 -useDisplayBg -value { - "Gate" -} -autoSize -endObjectProperties - -# (Byte) -object ByteClass -beginObjectProperties -major 4 -minor 0 -release 0 -x 407 -y 87 -w 40 -h 20 -controlPv "$(P):Gate_RBV" -lineColor index 14 -onColor index 15 -offColor index 19 -endian "little" -numBits 1 -endObjectProperties - -# (Byte) -object ByteClass -beginObjectProperties -major 4 -minor 0 -release 0 -x 407 -y 67 -w 40 -h 20 -controlPv "$(P):Enabled_RBV" -lineColor index 14 -onColor index 15 -offColor index 19 -endian "little" -numBits 1 -endObjectProperties - -# (Button) -object activeButtonClass -beginObjectProperties -major 4 -minor 1 -release 0 -x 327 -y 67 -w 70 -h 20 -fgColor index 14 -onColor index 3 -offColor index 3 -inconsistentColor index 3 -topShadowColor index 1 -botShadowColor index 11 -controlPv "$(P):Enabled" -indicatorPv "$(P):Enabled" -onLabel "Disable" -offLabel "Enable" -labelType "literal" -3d -font "arial-bold-r-14.0" -objType "controls" -endObjectProperties - diff --git a/src/tickit/devices/signal_generator.py b/src/tickit/devices/signal_generator.py deleted file mode 100644 index feaeff69e..000000000 --- a/src/tickit/devices/signal_generator.py +++ /dev/null @@ -1,204 +0,0 @@ -import logging -import math -from enum import Enum -from typing import Any, TypedDict - -import pydantic.v1.dataclasses -from pydantic.v1 import Field -from softioc import builder - -from tickit.adapters.epics import EpicsAdapter -from tickit.adapters.io import EpicsIo -from tickit.core.adapter import AdapterContainer -from tickit.core.components.component import Component, ComponentConfig -from tickit.core.components.device_component import DeviceComponent -from tickit.core.device import Device, DeviceUpdate -from tickit.core.typedefs import SimTime - - -@pydantic.v1.dataclasses.dataclass -class WaveConfig: - amplitude: float = 1.0 - amplitude_offset: float = 1.0 - frequency: float = 1.0 - enabled: bool = True - - -class SignalGeneratorDevice(Device): - """A simple device which produces a pre-configured value.""" - - #: An empty typed mapping of device inputs - class Inputs(TypedDict): - ... - - #: A typed mapping containing the 'value' output value - class Outputs(TypedDict): - value: float - gate: bool - - _wave: WaveConfig - _gate_threshold: float - - _value: float - _gate: bool - - def __init__(self, wave: WaveConfig, gate_threshold: float = 0.5) -> None: - """A constructor of the source, which takes the pre-configured output value. - - Args: - value (Any): A pre-configured output value. - """ - self._wave = wave - self._gate_threshold = gate_threshold - self._value = 0.0 - self._gate = False - - def update(self, time: SimTime, inputs: Inputs) -> DeviceUpdate[Outputs]: - """The update method which produces the pre-configured output value. - - Args: - time (SimTime): The current simulation time (in nanoseconds). - inputs (State): A mapping of inputs to the device and their values. - - Returns: - DeviceUpdate[Outputs]: - The produced update event which contains the pre-configured value, and - never requests a callback. - """ - self._value = self._compute_wave(time) - self._gate = self._value > self._gate_threshold - return DeviceUpdate( - SignalGeneratorDevice.Outputs( - value=self._value, - gate=self._gate, - ), - None, - ) - - def get_amplitude(self) -> float: - return self._wave.amplitude - - def set_amplitude(self, amplitude: float) -> None: - self._wave.amplitude = amplitude - - def get_amplitude_offset(self) -> float: - return self._wave.amplitude - - def set_amplitude_offset(self, amplitude_offset: float) -> None: - self._wave.amplitude_offset = amplitude_offset - - def get_frequency(self) -> float: - return self._wave.amplitude - - def set_frequency(self, frequency: float) -> None: - self._wave.frequency = frequency - - def get_gate_threshold(self) -> float: - return self._gate_threshold - - def set_gate_threshold(self, gate_threshold: float) -> None: - self._gate_threshold = gate_threshold - - def is_enabled(self) -> bool: - return self._wave.enabled - - def set_enabled(self, enabled: bool) -> None: - self._wave.enabled = enabled - - def get_value(self) -> float: - return self._value - - def is_gate_open(self) -> bool: - return self._gate - - def _compute_wave(self, time: SimTime) -> float: - if self._wave.enabled: - return self._sine(time) - else: - return 0.0 - - def _sine(self, time: SimTime) -> float: - return self._wave.amplitude_offset + ( - self._wave.amplitude * self._sinosoid(time) - ) - - def _sinosoid(self, time: SimTime) -> float: - time_seconds = time * 1e-9 - return math.sin(2 * math.pi * self._wave.frequency * time_seconds) - - -class SignalGeneratorAdapter(EpicsAdapter): - """The adapter for the Femto device.""" - - device: SignalGeneratorDevice - - def __init__(self, device: SignalGeneratorDevice) -> None: - super().__init__() - self.device = device - - def on_db_load(self) -> None: - """Customises records that have been loaded in to suit the simulation.""" - self.float_rbv( - "Amplitude", - self.device.get_amplitude, - self.device.set_amplitude, - ) - self.float_rbv( - "Offset", - self.device.get_amplitude_offset, - self.device.set_amplitude_offset, - ) - self.float_rbv( - "Frequency", - self.device.get_frequency, - self.device.set_frequency, - ) - self.float_rbv( - "GateThreshold", - self.device.get_gate_threshold, - self.device.set_gate_threshold, - ) - self.bool_rbv( - "Enabled", - self.device.is_enabled, - self.device.set_enabled, - ) - - self.link_input_on_interrupt(builder.aIn("Signal_RBV"), self.device.get_value) - self.link_input_on_interrupt(builder.aIn("Gate_RBV"), self.device.is_gate_open) - - self.polling_interrupt(0.1) - - -@pydantic.v1.dataclasses.dataclass -class SignalGenerator(ComponentConfig): - """Source of a fixed value.""" - - wave: WaveConfig = Field(default_factory=WaveConfig) - - def __call__(self) -> Component: # noqa: D102 - return DeviceComponent( - name=self.name, device=SignalGeneratorDevice(wave=self.wave) - ) - - -@pydantic.v1.dataclasses.dataclass -class EpicsSignalGenerator(ComponentConfig): - """Source of a fixed value.""" - - wave: WaveConfig = Field(default_factory=WaveConfig) - ioc_name: str = "SIGNALGEN" - - def __call__(self) -> Component: # noqa: D102 - device = SignalGeneratorDevice(wave=self.wave) - adapters = [ - AdapterContainer( - SignalGeneratorAdapter(device), - EpicsIo(self.ioc_name), - ) - ] - return DeviceComponent( - name=self.name, - device=device, - adapters=adapters, - )