mirror of
https://github.com/bec-project/ophyd_devices.git
synced 2025-06-23 03:17:58 +02:00
feat: added computed signal
This commit is contained in:
@ -7,18 +7,19 @@ from .galil.sgalil_ophyd import SGalilMotor
|
|||||||
from .npoint.npoint import NPointAxis
|
from .npoint.npoint import NPointAxis
|
||||||
from .rt_lamni import RtFlomniMotor, RtLamniMotor
|
from .rt_lamni import RtFlomniMotor, RtLamniMotor
|
||||||
from .sim.sim import SimCamera
|
from .sim.sim import SimCamera
|
||||||
from .sim.sim import SimMonitor
|
|
||||||
from .sim.sim import SimFlyer
|
from .sim.sim import SimFlyer
|
||||||
from .sim.sim import SimFlyer as SynFlyer
|
from .sim.sim import SimFlyer as SynFlyer
|
||||||
|
from .sim.sim import SimMonitor
|
||||||
from .sim.sim import SimMonitor as SynAxisMonitor
|
from .sim.sim import SimMonitor as SynAxisMonitor
|
||||||
from .sim.sim import SimMonitor as SynGaussBEC
|
from .sim.sim import SimMonitor as SynGaussBEC
|
||||||
from .sim.sim import SimPositioner
|
from .sim.sim import SimPositioner
|
||||||
from .sim.sim import SimPositioner as SynAxisOPAAS
|
from .sim.sim import SimPositioner as SynAxisOPAAS
|
||||||
from .sim.sim import SynDeviceOPAAS
|
from .sim.sim import SynDeviceOPAAS
|
||||||
|
from .sim.sim_frameworks import DeviceProxy, H5ImageReplayProxy, SlitProxy
|
||||||
from .sim.sim_signals import ReadOnlySignal
|
from .sim.sim_signals import ReadOnlySignal
|
||||||
from .sim.sim_frameworks import DeviceProxy, SlitProxy, H5ImageReplayProxy
|
|
||||||
from .sim.sim_signals import ReadOnlySignal as SynSignalRO
|
from .sim.sim_signals import ReadOnlySignal as SynSignalRO
|
||||||
from .sls_devices.sls_devices import SLSInfo, SLSOperatorMessages
|
from .sls_devices.sls_devices import SLSInfo, SLSOperatorMessages
|
||||||
from .smaract.smaract_ophyd import SmaractMotor
|
from .smaract.smaract_ophyd import SmaractMotor
|
||||||
from .utils.bec_device_base import BECDeviceBase
|
from .utils.bec_device_base import BECDeviceBase
|
||||||
|
from .utils.dynamic_pseudo import ComputedSignal
|
||||||
from .utils.static_device_test import launch
|
from .utils.static_device_test import launch
|
||||||
|
122
ophyd_devices/utils/dynamic_pseudo.py
Normal file
122
ophyd_devices/utils/dynamic_pseudo.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
"""
|
||||||
|
This module provides a class for creating a pseudo signal that is computed from other signals.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from functools import reduce
|
||||||
|
|
||||||
|
from ophyd import SignalRO
|
||||||
|
from ophyd.ophydobj import Kind
|
||||||
|
|
||||||
|
|
||||||
|
def rgetattr(obj, attr, *args):
|
||||||
|
"""See https://stackoverflow.com/questions/31174295/getattr-and-setattr-on-nested-objects"""
|
||||||
|
|
||||||
|
def _getattr(obj, attr):
|
||||||
|
return getattr(obj, attr, *args)
|
||||||
|
|
||||||
|
return reduce(_getattr, [obj] + attr.split("."))
|
||||||
|
|
||||||
|
|
||||||
|
class ComputedSignal(SignalRO):
|
||||||
|
"""
|
||||||
|
A read-only signal that is computed from other signals. The compute method should be a string
|
||||||
|
representation of a function that takes the input signals as arguments and returns the computed
|
||||||
|
value. The input signals should be provided as a list of strings that represent the path to the
|
||||||
|
signal in the device manager.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
name,
|
||||||
|
value=0,
|
||||||
|
timestamp=None,
|
||||||
|
device_manager=None,
|
||||||
|
parent=None,
|
||||||
|
labels=None,
|
||||||
|
kind=Kind.hinted,
|
||||||
|
tolerance=None,
|
||||||
|
rtolerance=None,
|
||||||
|
metadata=None,
|
||||||
|
cl=None,
|
||||||
|
attr_name=""
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
name=name,
|
||||||
|
value=value,
|
||||||
|
timestamp=timestamp,
|
||||||
|
parent=parent,
|
||||||
|
labels=labels,
|
||||||
|
kind=kind,
|
||||||
|
tolerance=tolerance,
|
||||||
|
rtolerance=rtolerance,
|
||||||
|
metadata=metadata,
|
||||||
|
cl=cl,
|
||||||
|
attr_name=attr_name,
|
||||||
|
)
|
||||||
|
self._device_manager = device_manager
|
||||||
|
self._input_signals = []
|
||||||
|
self._signal_subs = []
|
||||||
|
self._compute_method = None
|
||||||
|
|
||||||
|
def _signal_callback(self, *args, **kwargs):
|
||||||
|
self._run_subs(sub_type=self.SUB_VALUE, old_value=None, value=self.get())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def compute_method(self):
|
||||||
|
"""
|
||||||
|
Set the compute method for the pseudo signal
|
||||||
|
|
||||||
|
Args:
|
||||||
|
compute_method (str): The compute method to be used. This should be a string
|
||||||
|
representation of a function that takes the input signals as arguments
|
||||||
|
and returns the computed value.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self._compute_method
|
||||||
|
|
||||||
|
@compute_method.setter
|
||||||
|
def compute_method(self, method: str):
|
||||||
|
if not method.startswith("def"):
|
||||||
|
raise ValueError("The compute method should be a string representation of a function")
|
||||||
|
|
||||||
|
# get the function name
|
||||||
|
function_name = method.split("(")[0].split(" ")[1]
|
||||||
|
# pylint: disable=exec-used
|
||||||
|
exec(method)
|
||||||
|
self._compute_method = locals()[function_name]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def input_signals(self):
|
||||||
|
"""
|
||||||
|
Set the input signals for the pseudo signal
|
||||||
|
|
||||||
|
Args:
|
||||||
|
*input_vars: The input signals to be used for the computation
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self._input_signals
|
||||||
|
|
||||||
|
@input_signals.setter
|
||||||
|
def input_signals(self, input_vars):
|
||||||
|
if self._signal_subs:
|
||||||
|
for signal, sub_id in self._signal_subs:
|
||||||
|
signal.unsubscribe(sub_id)
|
||||||
|
signals = []
|
||||||
|
for signal in input_vars:
|
||||||
|
if isinstance(signal, str):
|
||||||
|
target = signal.replace("_", ".")
|
||||||
|
parts = target.split(".")
|
||||||
|
target = ".".join([parts[0], "obj"] + parts[1:])
|
||||||
|
obj = rgetattr(self._device_manager.devices, target)
|
||||||
|
sub_id = obj.subscribe(self._signal_callback)
|
||||||
|
self._signal_subs.append((obj, sub_id))
|
||||||
|
signals.append(obj)
|
||||||
|
else:
|
||||||
|
signals.append(signal)
|
||||||
|
self._input_signals = signals
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
if self._compute_method:
|
||||||
|
return self._compute_method(*self._input_signals)
|
||||||
|
return None
|
Reference in New Issue
Block a user