mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-03-05 00:12:49 +01:00
393 lines
15 KiB
Python
393 lines
15 KiB
Python
from __future__ import annotations
|
|
|
|
import enum
|
|
|
|
from bec_lib.device import ComputedSignal, Device, Positioner, ReadoutPriority
|
|
from bec_lib.device import Signal as BECSignal
|
|
from bec_lib.logger import bec_logger
|
|
from qtpy.QtCore import Property, Signal, Slot
|
|
|
|
from bec_widgets.utils import ConnectionConfig
|
|
from bec_widgets.utils.bec_widget import BECWidget
|
|
from bec_widgets.utils.filter_io import FilterIO
|
|
from bec_widgets.utils.widget_io import WidgetIO
|
|
|
|
logger = bec_logger.logger
|
|
|
|
|
|
class BECDeviceFilter(enum.Enum):
|
|
"""Filter for the device classes."""
|
|
|
|
DEVICE = "Device"
|
|
POSITIONER = "Positioner"
|
|
SIGNAL = "Signal"
|
|
COMPUTED_SIGNAL = "ComputedSignal"
|
|
|
|
|
|
class DeviceInputConfig(ConnectionConfig):
|
|
device_filter: list[BECDeviceFilter] = []
|
|
readout_filter: list[ReadoutPriority] = []
|
|
devices: list[str] = []
|
|
default: str | None = None
|
|
arg_name: str | None = None
|
|
apply_filter: bool = True
|
|
|
|
|
|
class DeviceInputBase(BECWidget):
|
|
"""
|
|
Mixin base class for device input widgets.
|
|
It allows to filter devices from BEC based on
|
|
device class and readout priority.
|
|
"""
|
|
|
|
_device_handler = {
|
|
BECDeviceFilter.DEVICE: Device,
|
|
BECDeviceFilter.POSITIONER: Positioner,
|
|
BECDeviceFilter.SIGNAL: BECSignal,
|
|
BECDeviceFilter.COMPUTED_SIGNAL: ComputedSignal,
|
|
}
|
|
|
|
_filter_handler = {
|
|
BECDeviceFilter.DEVICE: "filter_to_device",
|
|
BECDeviceFilter.POSITIONER: "filter_to_positioner",
|
|
BECDeviceFilter.SIGNAL: "filter_to_signal",
|
|
BECDeviceFilter.COMPUTED_SIGNAL: "filter_to_computed_signal",
|
|
ReadoutPriority.MONITORED: "readout_monitored",
|
|
ReadoutPriority.BASELINE: "readout_baseline",
|
|
ReadoutPriority.ASYNC: "readout_async",
|
|
ReadoutPriority.CONTINUOUS: "readout_continuous",
|
|
ReadoutPriority.ON_REQUEST: "readout_on_request",
|
|
}
|
|
|
|
def __init__(self, client=None, config=None, gui_id: str = None):
|
|
|
|
if config is None:
|
|
config = DeviceInputConfig(widget_class=self.__class__.__name__)
|
|
else:
|
|
if isinstance(config, dict):
|
|
config = DeviceInputConfig(**config)
|
|
self.config = config
|
|
super().__init__(client=client, config=config, gui_id=gui_id, theme_update=True)
|
|
self.get_bec_shortcuts()
|
|
self._device_filter = []
|
|
self._readout_filter = []
|
|
self._devices = []
|
|
|
|
### QtSlots ###
|
|
|
|
@Slot(str)
|
|
def set_device(self, device: str):
|
|
"""
|
|
Set the device.
|
|
|
|
Args:
|
|
device (str): Default name.
|
|
"""
|
|
if self.validate_device(device) is True:
|
|
WidgetIO.set_value(widget=self, value=device)
|
|
self.config.default = device
|
|
else:
|
|
logger.warning(f"Device {device} is not in the filtered selection.")
|
|
|
|
@Slot()
|
|
def update_devices_from_filters(self):
|
|
"""Update the devices based on the current filter selection
|
|
in self.device_filter and self.readout_filter. If apply_filter is False,
|
|
it will not apply the filters, store the filter settings and return.
|
|
"""
|
|
current_device = WidgetIO.get_value(widget=self, as_string=True)
|
|
self.config.device_filter = self.device_filter
|
|
self.config.readout_filter = self.readout_filter
|
|
if self.apply_filter is False:
|
|
return
|
|
all_dev = self.dev.enabled_devices
|
|
# Filter based on device class
|
|
devs = [dev for dev in all_dev if self._check_device_filter(dev)]
|
|
# Filter based on readout priority
|
|
devs = [dev for dev in devs if self._check_readout_filter(dev)]
|
|
self.devices = [device.name for device in devs]
|
|
self.set_device(current_device)
|
|
|
|
@Slot(list)
|
|
def set_available_devices(self, devices: list[str]):
|
|
"""
|
|
Set the devices. If a device in the list is not valid, it will not be considered.
|
|
|
|
Args:
|
|
devices (list[str]): List of devices.
|
|
"""
|
|
self.apply_filter = False
|
|
self.devices = devices
|
|
|
|
### QtProperties ###
|
|
|
|
@Property(
|
|
"QStringList",
|
|
doc="List of devices. If updated, it will disable the apply filters property.",
|
|
)
|
|
def devices(self) -> list[str]:
|
|
"""
|
|
Get the list of devices for the applied filters.
|
|
|
|
Returns:
|
|
list[str]: List of devices.
|
|
"""
|
|
return self._devices
|
|
|
|
@devices.setter
|
|
def devices(self, value: list):
|
|
self._devices = value
|
|
self.config.devices = value
|
|
FilterIO.set_selection(widget=self, selection=value)
|
|
|
|
@Property(str)
|
|
def default(self):
|
|
"""Get the default device name. If set through this property, it will update only if the device is within the filtered selection."""
|
|
return self.config.default
|
|
|
|
@default.setter
|
|
def default(self, value: str):
|
|
if self.validate_device(value) is False:
|
|
return
|
|
self.config.default = value
|
|
WidgetIO.set_value(widget=self, value=value)
|
|
|
|
@Property(bool)
|
|
def apply_filter(self):
|
|
"""Apply the filters on the devices."""
|
|
return self.config.apply_filter
|
|
|
|
@apply_filter.setter
|
|
def apply_filter(self, value: bool):
|
|
self.config.apply_filter = value
|
|
self.update_devices_from_filters()
|
|
|
|
@Property(bool)
|
|
def filter_to_device(self):
|
|
"""Include devices in filters."""
|
|
return BECDeviceFilter.DEVICE in self.device_filter
|
|
|
|
@filter_to_device.setter
|
|
def filter_to_device(self, value: bool):
|
|
if value is True and BECDeviceFilter.DEVICE not in self.device_filter:
|
|
self._device_filter.append(BECDeviceFilter.DEVICE)
|
|
if value is False and BECDeviceFilter.DEVICE in self.device_filter:
|
|
self._device_filter.remove(BECDeviceFilter.DEVICE)
|
|
self.update_devices_from_filters()
|
|
|
|
@Property(bool)
|
|
def filter_to_positioner(self):
|
|
"""Include devices of type Positioner in filters."""
|
|
return BECDeviceFilter.POSITIONER in self.device_filter
|
|
|
|
@filter_to_positioner.setter
|
|
def filter_to_positioner(self, value: bool):
|
|
if value is True and BECDeviceFilter.POSITIONER not in self.device_filter:
|
|
self._device_filter.append(BECDeviceFilter.POSITIONER)
|
|
if value is False and BECDeviceFilter.POSITIONER in self.device_filter:
|
|
self._device_filter.remove(BECDeviceFilter.POSITIONER)
|
|
self.update_devices_from_filters()
|
|
|
|
@Property(bool)
|
|
def filter_to_signal(self):
|
|
"""Include devices of type Signal in filters."""
|
|
return BECDeviceFilter.SIGNAL in self.device_filter
|
|
|
|
@filter_to_signal.setter
|
|
def filter_to_signal(self, value: bool):
|
|
if value is True and BECDeviceFilter.SIGNAL not in self.device_filter:
|
|
self._device_filter.append(BECDeviceFilter.SIGNAL)
|
|
if value is False and BECDeviceFilter.SIGNAL in self.device_filter:
|
|
self._device_filter.remove(BECDeviceFilter.SIGNAL)
|
|
self.update_devices_from_filters()
|
|
|
|
@Property(bool)
|
|
def filter_to_computed_signal(self):
|
|
"""Include devices of type ComputedSignal in filters."""
|
|
return BECDeviceFilter.COMPUTED_SIGNAL in self.device_filter
|
|
|
|
@filter_to_computed_signal.setter
|
|
def filter_to_computed_signal(self, value: bool):
|
|
if value is True and BECDeviceFilter.COMPUTED_SIGNAL not in self.device_filter:
|
|
self._device_filter.append(BECDeviceFilter.COMPUTED_SIGNAL)
|
|
if value is False and BECDeviceFilter.COMPUTED_SIGNAL in self.device_filter:
|
|
self._device_filter.remove(BECDeviceFilter.COMPUTED_SIGNAL)
|
|
self.update_devices_from_filters()
|
|
|
|
@Property(bool)
|
|
def readout_monitored(self):
|
|
"""Include devices with readout priority Monitored in filters."""
|
|
return ReadoutPriority.MONITORED in self.readout_filter
|
|
|
|
@readout_monitored.setter
|
|
def readout_monitored(self, value: bool):
|
|
if value is True and ReadoutPriority.MONITORED not in self.readout_filter:
|
|
self._readout_filter.append(ReadoutPriority.MONITORED)
|
|
if value is False and ReadoutPriority.MONITORED in self.readout_filter:
|
|
self._readout_filter.remove(ReadoutPriority.MONITORED)
|
|
self.update_devices_from_filters()
|
|
|
|
@Property(bool)
|
|
def readout_baseline(self):
|
|
"""Include devices with readout priority Baseline in filters."""
|
|
return ReadoutPriority.BASELINE in self.readout_filter
|
|
|
|
@readout_baseline.setter
|
|
def readout_baseline(self, value: bool):
|
|
if value is True and ReadoutPriority.BASELINE not in self.readout_filter:
|
|
self._readout_filter.append(ReadoutPriority.BASELINE)
|
|
if value is False and ReadoutPriority.BASELINE in self.readout_filter:
|
|
self._readout_filter.remove(ReadoutPriority.BASELINE)
|
|
self.update_devices_from_filters()
|
|
|
|
@Property(bool)
|
|
def readout_async(self):
|
|
"""Include devices with readout priority Async in filters."""
|
|
return ReadoutPriority.ASYNC in self.readout_filter
|
|
|
|
@readout_async.setter
|
|
def readout_async(self, value: bool):
|
|
if value is True and ReadoutPriority.ASYNC not in self.readout_filter:
|
|
self._readout_filter.append(ReadoutPriority.ASYNC)
|
|
if value is False and ReadoutPriority.ASYNC in self.readout_filter:
|
|
self._readout_filter.remove(ReadoutPriority.ASYNC)
|
|
self.update_devices_from_filters()
|
|
|
|
@Property(bool)
|
|
def readout_continuous(self):
|
|
"""Include devices with readout priority continuous in filters."""
|
|
return ReadoutPriority.CONTINUOUS in self.readout_filter
|
|
|
|
@readout_continuous.setter
|
|
def readout_continuous(self, value: bool):
|
|
if value is True and ReadoutPriority.CONTINUOUS not in self.readout_filter:
|
|
self._readout_filter.append(ReadoutPriority.CONTINUOUS)
|
|
if value is False and ReadoutPriority.CONTINUOUS in self.readout_filter:
|
|
self._readout_filter.remove(ReadoutPriority.CONTINUOUS)
|
|
self.update_devices_from_filters()
|
|
|
|
@Property(bool)
|
|
def readout_on_request(self):
|
|
"""Include devices with readout priority OnRequest in filters."""
|
|
return ReadoutPriority.ON_REQUEST in self.readout_filter
|
|
|
|
@readout_on_request.setter
|
|
def readout_on_request(self, value: bool):
|
|
if value is True and ReadoutPriority.ON_REQUEST not in self.readout_filter:
|
|
self._readout_filter.append(ReadoutPriority.ON_REQUEST)
|
|
if value is False and ReadoutPriority.ON_REQUEST in self.readout_filter:
|
|
self._readout_filter.remove(ReadoutPriority.ON_REQUEST)
|
|
self.update_devices_from_filters()
|
|
|
|
### Python Methods and Properties ###
|
|
|
|
@property
|
|
def device_filter(self) -> list[object]:
|
|
"""Get the list of filters to apply on the devices."""
|
|
return self._device_filter
|
|
|
|
@property
|
|
def readout_filter(self) -> list[str]:
|
|
"""Get the list of filters to apply on the devices"""
|
|
return self._readout_filter
|
|
|
|
def get_available_filters(self) -> list:
|
|
"""Get the available filters."""
|
|
return [entry for entry in BECDeviceFilter]
|
|
|
|
def get_readout_priority_filters(self) -> list:
|
|
"""Get the available readout priority filters."""
|
|
return [entry for entry in ReadoutPriority]
|
|
|
|
def set_device_filter(
|
|
self, filter_selection: str | BECDeviceFilter | list[str] | list[BECDeviceFilter]
|
|
):
|
|
"""
|
|
Set the device filter. If None is passed, no filters are applied and all devices included.
|
|
|
|
Args:
|
|
filter_selection (str | list[str]): Device filters. It is recommended to make an enum for the filters.
|
|
"""
|
|
filters = None
|
|
if isinstance(filter_selection, list):
|
|
filters = [self._filter_handler.get(entry) for entry in filter_selection]
|
|
if isinstance(filter_selection, str) or isinstance(filter_selection, BECDeviceFilter):
|
|
filters = [self._filter_handler.get(filter_selection)]
|
|
if filters is None or any([entry is None for entry in filters]):
|
|
logger.warning(f"Device filter {filter_selection} is not in the device filter list.")
|
|
return
|
|
for entry in filters:
|
|
setattr(self, entry, True)
|
|
|
|
def set_readout_priority_filter(
|
|
self, filter_selection: str | ReadoutPriority | list[str] | list[ReadoutPriority]
|
|
):
|
|
"""
|
|
Set the readout priority filter. If None is passed, all filters are included.
|
|
|
|
Args:
|
|
filter_selection (str | list[str]): Readout priority filters.
|
|
"""
|
|
filters = None
|
|
if isinstance(filter_selection, list):
|
|
filters = [self._filter_handler.get(entry) for entry in filter_selection]
|
|
if isinstance(filter_selection, str) or isinstance(filter_selection, ReadoutPriority):
|
|
filters = [self._filter_handler.get(filter_selection)]
|
|
if filters is None or any([entry is None for entry in filters]):
|
|
logger.warning(
|
|
f"Readout priority filter {filter_selection} is not in the readout priority list."
|
|
)
|
|
return
|
|
for entry in filters:
|
|
setattr(self, entry, True)
|
|
|
|
def _check_device_filter(
|
|
self, device: Device | BECSignal | ComputedSignal | Positioner
|
|
) -> bool:
|
|
"""Check if filter for device type is applied or not.
|
|
|
|
Args:
|
|
device(Device | Signal | ComputedSignal | Positioner): Device object.
|
|
"""
|
|
return all(isinstance(device, self._device_handler[entry]) for entry in self.device_filter)
|
|
|
|
def _check_readout_filter(
|
|
self, device: Device | BECSignal | ComputedSignal | Positioner
|
|
) -> bool:
|
|
"""Check if filter for readout priority is applied or not.
|
|
|
|
Args:
|
|
device(Device | Signal | ComputedSignal | Positioner): Device object.
|
|
"""
|
|
return device.readout_priority in self.readout_filter
|
|
|
|
def get_device_object(self, device: str) -> object:
|
|
"""
|
|
Get the device object based on the device name.
|
|
|
|
Args:
|
|
device(str): Device name.
|
|
|
|
Returns:
|
|
object: Device object, can be device of type Device, Positioner, Signal or ComputedSignal.
|
|
"""
|
|
self.validate_device(device)
|
|
dev = getattr(self.dev, device.lower(), None)
|
|
if dev is None:
|
|
raise ValueError(
|
|
f"Device {device} is not found in the device manager {self.dev} as enabled device."
|
|
)
|
|
return dev
|
|
|
|
def validate_device(self, device: str) -> bool:
|
|
"""
|
|
Validate the device if it is present in the filtered device selection.
|
|
|
|
Args:
|
|
device(str): Device to validate.
|
|
"""
|
|
all_devs = [dev.name for dev in self.dev.enabled_devices]
|
|
if device in self.devices and device in all_devs:
|
|
return True
|
|
return False
|