from enum import Enum
from mxcubecore.HardwareObjects.abstract.AbstractNState import (
AbstractNState,
BaseValueEnum,
)
from mxcubecore.HardwareObjects.LNLS.EPICS.EPICSActuator import EPICSActuator
[docs]class EPICSNState(EPICSActuator, AbstractNState):
"""
This class manages devices that exist in a discrete number of states
and provides an interface between those states (configured in the yaml
files) and the EPICS PVs. It ensures that hardware states are treated as Enums.
YAML Example
------------
%YAML 1.2
---
class: LNLS.EPICS.EPICSNState.EPICSNState
epics:
"MNC:B:PB03:PV_ACTIVATE_BACKLIGHT":
channels:
rbv:
suffix: ':RBV'
polling_period: 200
val:
suffix: ':SET'
configuration:
default_limits: (0, 1)
values: {'LEVEL0': 1, 'LEVEL1': 0}
"""
[docs] def init(self):
super().init()
limits = self._nominal_limits
EPICSActuator.set_limits(self, limits)
self._initialise_values()
current_value = self.get_value()
self.update_value(current_value)
def _set_value(self, value):
if isinstance(value, Enum):
value = value.value
super()._set_value(value)
[docs] def get_value(self):
value = EPICSActuator.get_value(self)
if not isinstance(value, Enum):
value = self.value_to_enum(value)
return value
def _initialise_values(self):
values = self.get_property("values")
self.VALUES = Enum(
"ValueEnum",
dict(values, **{item.name: item.value for item in BaseValueEnum}),
)
[docs] def update_value(self, value=None) -> None:
if value is None:
return
if not isinstance(value, Enum):
value = self.value_to_enum(value)
super().update_value(value)
def hasnt_arrived(self, setpoint):
if not isinstance(setpoint, Enum):
setpoint = self.value_to_enum(setpoint)
readback = self.get_value()
if not readback:
return False
return setpoint != readback
[docs]class EPICSNStateInterval(EPICSNState):
"""
This class is a workaround for devices that exist in a discrete number of states
and do NOT have an on/off switch, but NEED to have a virtual on/off state in the
GUI. Example: our frontlight device only allows for intensity setting (between 0
and 20000), but does not have an on/off switch.
This class implements the following logic: if the state of the device is higher
than LEVEL1, it will be shown as on in the GUI. Otherwise, it will be shown as
off in the GUI.
Clicking on the icon when it is off will bring it to the LEVEL0 intensity value.
Clicking on the icon when it is on will bring it to the LEVEL1 intensity value.
YAML Example
------------
%YAML 1.2
---
class: LNLS.EPICS.EPICSNState.EPICSNStateInterval
epics:
"MNC:B:LUCIOLE01:LIGHT_CH2":
channels:
rbv:
suffix: ''
polling_period: 200
val:
suffix: ''
configuration:
default_limits: (0, 1)
values: {'LEVEL0': 15000, 'LEVEL1': 0}
"""
[docs] def update_value(self, value=None) -> None:
if value is None:
return
if isinstance(value, Enum):
value = value.value
if not isinstance(value, int):
return
values_list = list(self.VALUES)
if not (len(values_list) > 1):
return
enum_value = values_list[0] if value > values_list[1].value else values_list[1]
super().update_value(enum_value)
[docs]class EPICSToggle(EPICSNState):
"""
This class is a workaround for devices that exist in a discrete number of states
but take always the same input when switching between these states.
Example: a safety shutter with RBVs 0 for open and 1 for closed, but input value
must always be 1 for either opening/closing.
YAML Example
------------
%YAML 1.2
---
class: LNLS.EPICS.EPICSNState.EPICSToggle
epics:
"MNC:A:PPS01:":
channels:
rbv:
suffix: 'PG_STATUS'
polling_period: 200
val:
suffix: 'OEAOPENCLOSE'
configuration:
default_limits: (0, 1)
values: {'Open': 0, 'Closed': 1}
input_value: 1
"""
[docs] def init(self):
super().init()
self.input_value = self.get_property("input_value")
def _set_value(self, value): # noqa: ARG002
super()._set_value(self.input_value)