mirror of
https://github.com/bec-project/ophyd_devices.git
synced 2025-06-07 04:10:39 +02:00
195 lines
5.4 KiB
Python
195 lines
5.4 KiB
Python
""" Utility class linked to BEC"""
|
|
|
|
import time
|
|
|
|
from bec_lib import bec_logger
|
|
from bec_lib.devicemanager import DeviceContainer
|
|
from bec_lib.tests.utils import ConnectorMock
|
|
from ophyd import Device, Kind, Signal
|
|
|
|
from ophyd_devices.utils.socket import data_shape, data_type
|
|
|
|
logger = bec_logger.logger
|
|
DEFAULT_EPICSSIGNAL_VALUE = object()
|
|
|
|
|
|
class DeviceMock:
|
|
"""Mock for Device"""
|
|
|
|
def __init__(self, name: str, value: float = 0.0):
|
|
self.name = name
|
|
self.read_buffer = value
|
|
self._config = {"deviceConfig": {"limits": [-50, 50]}, "userParameter": None}
|
|
self._read_only = False
|
|
self._enabled = True
|
|
|
|
def read(self):
|
|
"""Return the current value of the device"""
|
|
return {self.name: {"value": self.read_buffer}}
|
|
|
|
def readback(self):
|
|
"""Return the current value of the device"""
|
|
return self.read_buffer
|
|
|
|
@property
|
|
def read_only(self) -> bool:
|
|
"""Get the read only status of the device"""
|
|
return self._read_only
|
|
|
|
@read_only.setter
|
|
def read_only(self, val: bool):
|
|
self._read_only = val
|
|
|
|
@property
|
|
def enabled(self) -> bool:
|
|
"""Get the enabled status of the device"""
|
|
return self._enabled
|
|
|
|
@enabled.setter
|
|
def enabled(self, val: bool):
|
|
self._enabled = val
|
|
|
|
@property
|
|
def user_parameter(self):
|
|
"""Get the user parameter of the device"""
|
|
return self._config["userParameter"]
|
|
|
|
@property
|
|
def obj(self):
|
|
"""Get the device object"""
|
|
return self
|
|
|
|
|
|
class DMMock:
|
|
"""Mock for DeviceManager
|
|
|
|
The mocked DeviceManager creates a device containert and a connector.
|
|
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.devices = DeviceContainer()
|
|
self.connector = ConnectorMock()
|
|
|
|
def add_device(self, name: str, value: float = 0.0):
|
|
self.devices[name] = DeviceMock(name, value)
|
|
|
|
|
|
class ConfigSignal(Signal):
|
|
def __init__(
|
|
self,
|
|
*,
|
|
name,
|
|
value=0,
|
|
timestamp=None,
|
|
parent=None,
|
|
labels=None,
|
|
kind=Kind.hinted,
|
|
tolerance=None,
|
|
rtolerance=None,
|
|
metadata=None,
|
|
cl=None,
|
|
attr_name="",
|
|
config_storage_name: str = "config_storage",
|
|
):
|
|
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.storage_name = config_storage_name
|
|
|
|
def get(self):
|
|
self._readback = getattr(self.parent, self.storage_name)[self.name]
|
|
return self._readback
|
|
|
|
def put(self, value, connection_timeout=1, callback=None, timeout=1, **kwargs):
|
|
"""Using channel access, set the write PV to `value`.
|
|
|
|
Keyword arguments are passed on to callbacks
|
|
|
|
Parameters
|
|
----------
|
|
value : any
|
|
The value to set
|
|
connection_timeout : float, optional
|
|
If not already connected, allow up to `connection_timeout` seconds
|
|
for the connection to complete.
|
|
use_complete : bool, optional
|
|
Override put completion settings
|
|
callback : callable
|
|
Callback for when the put has completed
|
|
timeout : float, optional
|
|
Timeout before assuming that put has failed. (Only relevant if
|
|
put completion is used.)
|
|
"""
|
|
|
|
old_value = self.get()
|
|
timestamp = time.time()
|
|
getattr(self.parent, self.storage_name)[self.name] = value
|
|
super().put(value, timestamp=timestamp, force=True)
|
|
self._run_subs(
|
|
sub_type=self.SUB_VALUE, old_value=old_value, value=value, timestamp=timestamp
|
|
)
|
|
|
|
def describe(self):
|
|
"""Provide schema and meta-data for :meth:`~BlueskyInterface.read`
|
|
|
|
This keys in the `OrderedDict` this method returns must match the
|
|
keys in the `OrderedDict` return by :meth:`~BlueskyInterface.read`.
|
|
|
|
This provides schema related information, (ex shape, dtype), the
|
|
source (ex PV name), and if available, units, limits, precision etc.
|
|
|
|
Returns
|
|
-------
|
|
data_keys : OrderedDict
|
|
The keys must be strings and the values must be dict-like
|
|
with the ``event_model.event_descriptor.data_key`` schema.
|
|
"""
|
|
if self._readback is DEFAULT_EPICSSIGNAL_VALUE:
|
|
val = self.get()
|
|
else:
|
|
val = self._readback
|
|
return {
|
|
self.name: {
|
|
"source": f"{self.parent.prefix}:{self.name}",
|
|
"dtype": data_type(val),
|
|
"shape": data_shape(val),
|
|
}
|
|
}
|
|
|
|
|
|
class DeviceClassConnectionError(Device):
|
|
"""
|
|
Device that always raises a connection error when trying to connect.
|
|
It is used to test the wait_for_connection method in the DeviceServer.
|
|
"""
|
|
|
|
@property
|
|
def connected(self):
|
|
return False
|
|
|
|
def wait_for_connection(self, all_signals=False, timeout=2):
|
|
raise RuntimeError("Connection error")
|
|
|
|
|
|
class DeviceClassInitError(Device):
|
|
"""
|
|
Device that always raises an error when trying to construct the object.
|
|
It is used to test the error handling in the DeviceServer.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
raise RuntimeError("Init error")
|