remove UNKNOWN, UNSTABLE and DISABLED from Readable.status
- re-add them where needed (epics, entangle ...) Change-Id: I2b8af9f5f86285f081d5418211f6940e80a1dbd7 Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/30718 Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de> Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de> Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
parent
11a3bed8b8
commit
361f9ac4fc
@ -58,3 +58,4 @@ FINALIZING = StatusType.FINALIZING
|
|||||||
ERROR = StatusType.ERROR
|
ERROR = StatusType.ERROR
|
||||||
ERROR_STANDBY = StatusType.ERROR_STANDBY
|
ERROR_STANDBY = StatusType.ERROR_STANDBY
|
||||||
ERROR_PREPARED = StatusType.ERROR_PREPARED
|
ERROR_PREPARED = StatusType.ERROR_PREPARED
|
||||||
|
UNKNOWN = StatusType.UNKNOWN # no SECoP standard (yet)
|
||||||
|
@ -834,10 +834,7 @@ class Readable(Module):
|
|||||||
Status = Enum('Status',
|
Status = Enum('Status',
|
||||||
IDLE=StatusType.IDLE,
|
IDLE=StatusType.IDLE,
|
||||||
WARN=StatusType.WARN,
|
WARN=StatusType.WARN,
|
||||||
UNSTABLE=270, # not SECoP standard. TODO: remove and adapt entangle
|
|
||||||
ERROR=StatusType.ERROR,
|
ERROR=StatusType.ERROR,
|
||||||
DISABLED=StatusType.DISABLED,
|
|
||||||
UNKNOWN=401, # not SECoP standard. TODO: remove and adapt entangle and epics
|
|
||||||
) #: status code Enum: extended automatically in inherited modules
|
) #: status code Enum: extended automatically in inherited modules
|
||||||
value = Parameter('current value of the module', FloatRange())
|
value = Parameter('current value of the module', FloatRange())
|
||||||
status = Parameter('current status of the module', StatusType(Status),
|
status = Parameter('current status of the module', StatusType(Status),
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
|
|
||||||
|
|
||||||
from frappy.datatypes import EnumType, FloatRange, StringType
|
from frappy.datatypes import EnumType, FloatRange, StringType, StatusType
|
||||||
from frappy.modules import Drivable, Parameter, Readable
|
from frappy.modules import Drivable, Parameter, Readable
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -71,7 +71,7 @@ class EpicsReadable(Readable):
|
|||||||
status_pv = Parameter('EPICS pv_name of status',
|
status_pv = Parameter('EPICS pv_name of status',
|
||||||
datatype=StringType(),
|
datatype=StringType(),
|
||||||
default="unset", export=False)
|
default="unset", export=False)
|
||||||
|
status = Parameter(datatype=StatusType(Readable, 'UNKNOWN'))
|
||||||
|
|
||||||
# Generic read and write functions
|
# Generic read and write functions
|
||||||
def _read_pv(self, pv_name):
|
def _read_pv(self, pv_name):
|
||||||
@ -132,6 +132,7 @@ class EpicsDrivable(Drivable):
|
|||||||
default="unset", export=False)
|
default="unset", export=False)
|
||||||
status_pv = Parameter('EPICS pv_name of status', datatype=StringType(),
|
status_pv = Parameter('EPICS pv_name of status', datatype=StringType(),
|
||||||
default="unset", export=False)
|
default="unset", export=False)
|
||||||
|
status = Parameter(datatype=StatusType(Drivable, 'UNKNOWN'))
|
||||||
|
|
||||||
|
|
||||||
# Generic read and write functions
|
# Generic read and write functions
|
||||||
|
@ -41,7 +41,7 @@ from frappy.datatypes import ArrayOf, EnumType, FloatRange, \
|
|||||||
from frappy.errors import CommunicationFailedError, \
|
from frappy.errors import CommunicationFailedError, \
|
||||||
ConfigError, HardwareError, ProgrammingError
|
ConfigError, HardwareError, ProgrammingError
|
||||||
from frappy.lib import lazy_property
|
from frappy.lib import lazy_property
|
||||||
from frappy.modules import Command, \
|
from frappy.modules import Command, StatusType, \
|
||||||
Drivable, Module, Parameter, Readable
|
Drivable, Module, Parameter, Readable
|
||||||
|
|
||||||
#####
|
#####
|
||||||
@ -173,11 +173,11 @@ class BasePyTangoDevice:
|
|||||||
)
|
)
|
||||||
|
|
||||||
tango_status_mapping = {
|
tango_status_mapping = {
|
||||||
PyTango.DevState.ON: Drivable.Status.IDLE,
|
PyTango.DevState.ON: StatusType.IDLE,
|
||||||
PyTango.DevState.ALARM: Drivable.Status.WARN,
|
PyTango.DevState.ALARM: StatusType.WARN,
|
||||||
PyTango.DevState.OFF: Drivable.Status.DISABLED,
|
PyTango.DevState.OFF: StatusType.DISABLED,
|
||||||
PyTango.DevState.FAULT: Drivable.Status.ERROR,
|
PyTango.DevState.FAULT: StatusType.ERROR,
|
||||||
PyTango.DevState.MOVING: Drivable.Status.BUSY,
|
PyTango.DevState.MOVING: StatusType.BUSY,
|
||||||
}
|
}
|
||||||
|
|
||||||
@lazy_property
|
@lazy_property
|
||||||
@ -364,13 +364,15 @@ class BasePyTangoDevice:
|
|||||||
class PyTangoDevice(BasePyTangoDevice):
|
class PyTangoDevice(BasePyTangoDevice):
|
||||||
"""Base for "normal" devices with status."""
|
"""Base for "normal" devices with status."""
|
||||||
|
|
||||||
|
status = Parameter(datatype=StatusType(Readable, 'UNKNOWN', 'DISABLED'))
|
||||||
|
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
# Query status code and string
|
# Query status code and string
|
||||||
tangoState = self._dev.State()
|
tangoState = self._dev.State()
|
||||||
tangoStatus = self._dev.Status()
|
tangoStatus = self._dev.Status()
|
||||||
|
|
||||||
# Map status
|
# Map status
|
||||||
myState = self.tango_status_mapping.get(tangoState, Drivable.Status.UNKNOWN)
|
myState = self.tango_status_mapping.get(tangoState, StatusType.UNKNOWN)
|
||||||
|
|
||||||
return (myState, tangoStatus)
|
return (myState, tangoStatus)
|
||||||
|
|
||||||
@ -456,6 +458,7 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
|||||||
default=60.0, readonly=False,
|
default=60.0, readonly=False,
|
||||||
datatype=FloatRange(0, 900, unit='s'), group='stability',
|
datatype=FloatRange(0, 900, unit='s'), group='stability',
|
||||||
)
|
)
|
||||||
|
status = Parameter(datatype=StatusType(PyTangoDevice, 'BUSY', 'UNSTABLE'))
|
||||||
|
|
||||||
_history = ()
|
_history = ()
|
||||||
_timeout = None
|
_timeout = None
|
||||||
@ -520,7 +523,7 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
|||||||
|
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
status = super().read_status()
|
status = super().read_status()
|
||||||
if status[0] in (Readable.Status.DISABLED, Readable.Status.ERROR):
|
if status[0] in (StatusType.DISABLED, StatusType.ERROR):
|
||||||
self.setFastPoll(False)
|
self.setFastPoll(False)
|
||||||
return status
|
return status
|
||||||
if self._isAtTarget():
|
if self._isAtTarget():
|
||||||
@ -857,7 +860,7 @@ class PartialDigitalInput(NamedDigitalInput):
|
|||||||
return value # mapping is done by datatype upon export()
|
return value # mapping is done by datatype upon export()
|
||||||
|
|
||||||
|
|
||||||
class DigitalOutput(PyTangoDevice, Drivable):
|
class DigitalOutput(PyTangoDevice):
|
||||||
"""A device that can set and read a digital value corresponding to a
|
"""A device that can set and read a digital value corresponding to a
|
||||||
bitfield.
|
bitfield.
|
||||||
"""
|
"""
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
"""drivers for CCU4, the cryostat control unit at SINQ"""
|
"""drivers for CCU4, the cryostat control unit at SINQ"""
|
||||||
# the most common Frappy classes can be imported from frappy.core
|
# the most common Frappy classes can be imported from frappy.core
|
||||||
from frappy.core import EnumType, FloatRange, \
|
from frappy.core import EnumType, FloatRange, \
|
||||||
HasIO, Parameter, Readable, StringIO
|
HasIO, Parameter, Readable, StringIO, StatusType
|
||||||
|
|
||||||
|
|
||||||
class CCU4IO(StringIO):
|
class CCU4IO(StringIO):
|
||||||
@ -51,16 +51,16 @@ class HeLevel(HasIO, Readable):
|
|||||||
readonly=False)
|
readonly=False)
|
||||||
sample_rate = Parameter('sample rate', EnumType(slow=0, fast=1), readonly=False)
|
sample_rate = Parameter('sample rate', EnumType(slow=0, fast=1), readonly=False)
|
||||||
|
|
||||||
Status = Readable.Status
|
status = Parameter(datatype=StatusType(Readable, 'DISABLED'))
|
||||||
|
|
||||||
# conversion of the code from the CCU4 parameter 'hsf'
|
# conversion of the code from the CCU4 parameter 'hsf'
|
||||||
STATUS_MAP = {
|
STATUS_MAP = {
|
||||||
0: (Status.IDLE, 'sensor ok'),
|
0: (StatusType.IDLE, 'sensor ok'),
|
||||||
1: (Status.ERROR, 'sensor warm'),
|
1: (StatusType.ERROR, 'sensor warm'),
|
||||||
2: (Status.ERROR, 'no sensor'),
|
2: (StatusType.ERROR, 'no sensor'),
|
||||||
3: (Status.ERROR, 'timeout'),
|
3: (StatusType.ERROR, 'timeout'),
|
||||||
4: (Status.ERROR, 'not yet read'),
|
4: (StatusType.ERROR, 'not yet read'),
|
||||||
5: (Status.DISABLED, 'disabled'),
|
5: (StatusType.DISABLED, 'disabled'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def query(self, cmd):
|
def query(self, cmd):
|
||||||
|
@ -31,7 +31,7 @@ import time
|
|||||||
from ast import literal_eval
|
from ast import literal_eval
|
||||||
|
|
||||||
import frappy.io
|
import frappy.io
|
||||||
from frappy.datatypes import BoolType, EnumType, FloatRange, IntRange
|
from frappy.datatypes import BoolType, EnumType, FloatRange, IntRange, StatusType
|
||||||
from frappy.lib import formatStatusBits
|
from frappy.lib import formatStatusBits
|
||||||
from frappy.core import Done, Drivable, Parameter, Property, CommonReadHandler, CommonWriteHandler
|
from frappy.core import Done, Drivable, Parameter, Property, CommonReadHandler, CommonWriteHandler
|
||||||
from frappy.io import HasIO
|
from frappy.io import HasIO
|
||||||
@ -158,6 +158,7 @@ class ResChannel(Channel):
|
|||||||
channel = Property('the Lakeshore channel', datatype=IntRange(1, 16), export=False)
|
channel = Property('the Lakeshore channel', datatype=IntRange(1, 16), export=False)
|
||||||
|
|
||||||
value = Parameter(datatype=FloatRange(unit='Ohm'))
|
value = Parameter(datatype=FloatRange(unit='Ohm'))
|
||||||
|
status = Parameter(datatype=StatusType(Drivable, 'DISABLED'))
|
||||||
pollinterval = Parameter(visibility=3, default=1)
|
pollinterval = Parameter(visibility=3, default=1)
|
||||||
range = Parameter('reading range', readonly=False,
|
range = Parameter('reading range', readonly=False,
|
||||||
datatype=EnumType(**RES_RANGE))
|
datatype=EnumType(**RES_RANGE))
|
||||||
|
@ -28,7 +28,7 @@ The PPMS hardware has some special requirements:
|
|||||||
needing a mechanism to treat a single parameter change correctly.
|
needing a mechanism to treat a single parameter change correctly.
|
||||||
|
|
||||||
Polling of value and status is done commonly for all modules. For each registered module
|
Polling of value and status is done commonly for all modules. For each registered module
|
||||||
<module>.update_value_status() is called in order to update their value and status.
|
<module>.update_value_status() is called in order to update their value and StatusType.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
@ -39,7 +39,6 @@ from frappy.datatypes import BoolType, EnumType, \
|
|||||||
FloatRange, IntRange, StatusType, StringType
|
FloatRange, IntRange, StatusType, StringType
|
||||||
from frappy.errors import HardwareError
|
from frappy.errors import HardwareError
|
||||||
from frappy.lib import clamp
|
from frappy.lib import clamp
|
||||||
from frappy.lib.enum import Enum
|
|
||||||
from frappy.modules import Communicator, \
|
from frappy.modules import Communicator, \
|
||||||
Drivable, Parameter, Property, Readable
|
Drivable, Parameter, Property, Readable
|
||||||
from frappy.io import HasIO
|
from frappy.io import HasIO
|
||||||
@ -117,7 +116,7 @@ class Main(Communicator):
|
|||||||
class PpmsBase(HasIO, Readable):
|
class PpmsBase(HasIO, Readable):
|
||||||
"""common base for all ppms modules"""
|
"""common base for all ppms modules"""
|
||||||
value = Parameter(needscfg=False)
|
value = Parameter(needscfg=False)
|
||||||
status = Parameter(needscfg=False)
|
status = Parameter(datatype=StatusType(Readable, 'DISABLED'), needscfg=False)
|
||||||
|
|
||||||
enabled = True # default, if no parameter enable is defined
|
enabled = True # default, if no parameter enable is defined
|
||||||
_last_settings = None # used by several modules
|
_last_settings = None # used by several modules
|
||||||
@ -140,13 +139,13 @@ class PpmsBase(HasIO, Readable):
|
|||||||
# update value and status
|
# update value and status
|
||||||
# to be reimplemented for modules looking at packed_status
|
# to be reimplemented for modules looking at packed_status
|
||||||
if not self.enabled:
|
if not self.enabled:
|
||||||
self.status = (self.Status.DISABLED, 'disabled')
|
self.status = (StatusType.DISABLED, 'disabled')
|
||||||
return
|
return
|
||||||
if value is None:
|
if value is None:
|
||||||
self.status = (self.Status.ERROR, 'invalid value')
|
self.status = (StatusType.ERROR, 'invalid value')
|
||||||
else:
|
else:
|
||||||
self.value = value
|
self.value = value
|
||||||
self.status = (self.Status.IDLE, '')
|
self.status = (StatusType.IDLE, '')
|
||||||
|
|
||||||
def comm_write(self, command):
|
def comm_write(self, command):
|
||||||
"""write command and check if result is OK"""
|
"""write command and check if result is OK"""
|
||||||
@ -293,19 +292,18 @@ class Chamber(PpmsDrivable):
|
|||||||
value is an Enum, which is redundant with the status text
|
value is an Enum, which is redundant with the status text
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Status = Drivable.Status
|
|
||||||
code_table = [
|
code_table = [
|
||||||
# valuecode, status, statusname, opcode, targetname
|
# valuecode, status, statusname, opcode, targetname
|
||||||
(0, Status.IDLE, 'unknown', 10, 'noop'),
|
(0, StatusType.IDLE, 'unknown', 10, 'noop'),
|
||||||
(1, Status.IDLE, 'purged_and_sealed', 1, 'purge_and_seal'),
|
(1, StatusType.IDLE, 'purged_and_sealed', 1, 'purge_and_seal'),
|
||||||
(2, Status.IDLE, 'vented_and_sealed', 2, 'vent_and_seal'),
|
(2, StatusType.IDLE, 'vented_and_sealed', 2, 'vent_and_seal'),
|
||||||
(3, Status.WARN, 'sealed_unknown', 0, 'seal_immediately'),
|
(3, StatusType.WARN, 'sealed_unknown', 0, 'seal_immediately'),
|
||||||
(4, Status.BUSY, 'purge_and_seal', None, None),
|
(4, StatusType.BUSY, 'purge_and_seal', None, None),
|
||||||
(5, Status.BUSY, 'vent_and_seal', None, None),
|
(5, StatusType.BUSY, 'vent_and_seal', None, None),
|
||||||
(6, Status.BUSY, 'pumping_down', None, None),
|
(6, StatusType.BUSY, 'pumping_down', None, None),
|
||||||
(8, Status.IDLE, 'pumping_continuously', 3, 'pump_continuously'),
|
(8, StatusType.IDLE, 'pumping_continuously', 3, 'pump_continuously'),
|
||||||
(9, Status.IDLE, 'venting_continuously', 4, 'vent_continuously'),
|
(9, StatusType.IDLE, 'venting_continuously', 4, 'vent_continuously'),
|
||||||
(15, Status.ERROR, 'general_failure', None, None),
|
(15, StatusType.ERROR, 'general_failure', None, None),
|
||||||
]
|
]
|
||||||
value_codes = {k: v for v, _, k, _, _ in code_table}
|
value_codes = {k: v for v, _, k, _, _ in code_table}
|
||||||
target_codes = {k: v for v, _, _, _, k in code_table if k}
|
target_codes = {k: v for v, _, _, _, k in code_table if k}
|
||||||
@ -324,7 +322,7 @@ class Chamber(PpmsDrivable):
|
|||||||
self.status = self.status_map[status_code]
|
self.status = self.status_map[status_code]
|
||||||
else:
|
else:
|
||||||
self.value = self.value_map['unknown']
|
self.value = self.value_map['unknown']
|
||||||
self.status = (self.Status.ERROR, 'unknown status code %d' % status_code)
|
self.status = (StatusType.ERROR, 'unknown status code %d' % status_code)
|
||||||
|
|
||||||
def read_target(self):
|
def read_target(self):
|
||||||
opcode = int(self.communicate('CHAMBER?'))
|
opcode = int(self.communicate('CHAMBER?'))
|
||||||
@ -341,13 +339,8 @@ class Chamber(PpmsDrivable):
|
|||||||
class Temp(PpmsDrivable):
|
class Temp(PpmsDrivable):
|
||||||
"""temperature"""
|
"""temperature"""
|
||||||
|
|
||||||
Status = Enum(
|
|
||||||
Drivable.Status,
|
|
||||||
RAMPING=370,
|
|
||||||
STABILIZING=380,
|
|
||||||
)
|
|
||||||
value = Parameter(datatype=FloatRange(unit='K'))
|
value = Parameter(datatype=FloatRange(unit='K'))
|
||||||
status = Parameter(datatype=StatusType(Status))
|
status = Parameter(datatype=StatusType(Drivable, 'RAMPING', 'STABILIZING'))
|
||||||
target = Parameter(datatype=FloatRange(1.7, 402.0, unit='K'), needscfg=False)
|
target = Parameter(datatype=FloatRange(1.7, 402.0, unit='K'), needscfg=False)
|
||||||
setpoint = Parameter('intermediate set point',
|
setpoint = Parameter('intermediate set point',
|
||||||
datatype=FloatRange(1.7, 402.0, unit='K'))
|
datatype=FloatRange(1.7, 402.0, unit='K'))
|
||||||
@ -362,15 +355,15 @@ class Temp(PpmsDrivable):
|
|||||||
general_stop = Property('respect general stop', datatype=BoolType(),
|
general_stop = Property('respect general stop', datatype=BoolType(),
|
||||||
default=True, value=False)
|
default=True, value=False)
|
||||||
STATUS_MAP = {
|
STATUS_MAP = {
|
||||||
1: (Status.IDLE, 'stable at target'),
|
1: (StatusType.IDLE, 'stable at target'),
|
||||||
2: (Status.RAMPING, 'ramping'),
|
2: (StatusType.RAMPING, 'ramping'),
|
||||||
5: (Status.STABILIZING, 'within tolerance'),
|
5: (StatusType.STABILIZING, 'within tolerance'),
|
||||||
6: (Status.STABILIZING, 'outside tolerance'),
|
6: (StatusType.STABILIZING, 'outside tolerance'),
|
||||||
7: (Status.STABILIZING, 'filling/emptying reservoir'),
|
7: (StatusType.STABILIZING, 'filling/emptying reservoir'),
|
||||||
10: (Status.WARN, 'standby'),
|
10: (StatusType.WARN, 'standby'),
|
||||||
13: (Status.WARN, 'control disabled'),
|
13: (StatusType.WARN, 'control disabled'),
|
||||||
14: (Status.ERROR, 'can not complete'),
|
14: (StatusType.ERROR, 'can not complete'),
|
||||||
15: (Status.ERROR, 'general failure'),
|
15: (StatusType.ERROR, 'general failure'),
|
||||||
}
|
}
|
||||||
|
|
||||||
channel = 'temp'
|
channel = 'temp'
|
||||||
@ -420,11 +413,11 @@ class Temp(PpmsDrivable):
|
|||||||
|
|
||||||
def update_value_status(self, value, packed_status):
|
def update_value_status(self, value, packed_status):
|
||||||
if value is None:
|
if value is None:
|
||||||
self.status = (self.Status.ERROR, 'invalid value')
|
self.status = (StatusType.ERROR, 'invalid value')
|
||||||
return
|
return
|
||||||
self.value = value
|
self.value = value
|
||||||
status_code = packed_status & 0xf
|
status_code = packed_status & 0xf
|
||||||
status = self.STATUS_MAP.get(status_code, (self.Status.ERROR, 'unknown status code %d' % status_code))
|
status = self.STATUS_MAP.get(status_code, (StatusType.ERROR, 'unknown status code %d' % status_code))
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if value > 11:
|
if value > 11:
|
||||||
# when starting from T > 50, this will be 15 min.
|
# when starting from T > 50, this will be 15 min.
|
||||||
@ -436,7 +429,7 @@ class Temp(PpmsDrivable):
|
|||||||
self._wait_at10 = False
|
self._wait_at10 = False
|
||||||
self._last_change = now
|
self._last_change = now
|
||||||
self._write_params(self.target, self.ramp, self.approachmode)
|
self._write_params(self.target, self.ramp, self.approachmode)
|
||||||
status = (self.Status.STABILIZING, 'waiting at 10 K')
|
status = (StatusType.STABILIZING, 'waiting at 10 K')
|
||||||
if self._last_change: # there was a change, which is not yet confirmed by hw
|
if self._last_change: # there was a change, which is not yet confirmed by hw
|
||||||
if now > self._last_change + 5:
|
if now > self._last_change + 5:
|
||||||
self._last_change = 0 # give up waiting for busy
|
self._last_change = 0 # give up waiting for busy
|
||||||
@ -444,14 +437,14 @@ class Temp(PpmsDrivable):
|
|||||||
self.log.debug('time needed to change to busy: %.3g', now - self._last_change)
|
self.log.debug('time needed to change to busy: %.3g', now - self._last_change)
|
||||||
self._last_change = 0
|
self._last_change = 0
|
||||||
else:
|
else:
|
||||||
status = (self.Status.BUSY, 'changed target')
|
status = (StatusType.BUSY, 'changed target')
|
||||||
if abs(self.value - self.target) < self.target * 0.01:
|
if abs(self.value - self.target) < self.target * 0.01:
|
||||||
self._last_target = self.target
|
self._last_target = self.target
|
||||||
elif self._last_target is None:
|
elif self._last_target is None:
|
||||||
self._last_target = self.value
|
self._last_target = self.value
|
||||||
if self._stopped:
|
if self._stopped:
|
||||||
# combine 'stopped' with current status text
|
# combine 'stopped' with current status text
|
||||||
if status[0] == self.Status.IDLE:
|
if status[0] == StatusType.IDLE:
|
||||||
status = (status[0], 'stopped')
|
status = (status[0], 'stopped')
|
||||||
else:
|
else:
|
||||||
status = (status[0], 'stopping (%s)' % status[1])
|
status = (status[0], 'stopping (%s)' % status[1])
|
||||||
@ -459,7 +452,7 @@ class Temp(PpmsDrivable):
|
|||||||
# handle timeout
|
# handle timeout
|
||||||
if self.isDriving(status):
|
if self.isDriving(status):
|
||||||
if now > self._expected_target_time + self.timeout:
|
if now > self._expected_target_time + self.timeout:
|
||||||
status = (self.Status.WARN, 'timeout while %s' % status[1])
|
status = (StatusType.WARN, 'timeout while %s' % status[1])
|
||||||
else:
|
else:
|
||||||
self._expected_target_time = 0
|
self._expected_target_time = 0
|
||||||
self.status = status
|
self.status = status
|
||||||
@ -469,7 +462,7 @@ class Temp(PpmsDrivable):
|
|||||||
if abs(self.target - self.value) <= 2e-5 * target and target == self.target:
|
if abs(self.target - self.value) <= 2e-5 * target and target == self.target:
|
||||||
return None
|
return None
|
||||||
self._status_before_change = self.status
|
self._status_before_change = self.status
|
||||||
self.status = (self.Status.BUSY, 'changed target')
|
self.status = (StatusType.BUSY, 'changed target')
|
||||||
self._last_change = time.time()
|
self._last_change = time.time()
|
||||||
self._write_params(target, self.ramp, self.approachmode)
|
self._write_params(target, self.ramp, self.approachmode)
|
||||||
self.log.debug('write_target %s' % repr((self.setpoint, target, self._wait_at10)))
|
self.log.debug('write_target %s' % repr((self.setpoint, target, self._wait_at10)))
|
||||||
@ -493,7 +486,7 @@ class Temp(PpmsDrivable):
|
|||||||
def stop(self):
|
def stop(self):
|
||||||
if not self.isDriving():
|
if not self.isDriving():
|
||||||
return
|
return
|
||||||
if self.status[0] != self.Status.STABILIZING:
|
if self.status[0] != StatusType.STABILIZING:
|
||||||
# we are not near target
|
# we are not near target
|
||||||
newtarget = clamp(self._last_target, self.value, self.target)
|
newtarget = clamp(self._last_target, self.value, self.target)
|
||||||
if newtarget != self.target:
|
if newtarget != self.target:
|
||||||
@ -506,16 +499,8 @@ class Temp(PpmsDrivable):
|
|||||||
class Field(PpmsDrivable):
|
class Field(PpmsDrivable):
|
||||||
"""magnetic field"""
|
"""magnetic field"""
|
||||||
|
|
||||||
Status = Enum(
|
|
||||||
Drivable.Status,
|
|
||||||
PREPARED=150,
|
|
||||||
PREPARING=340,
|
|
||||||
RAMPING=370,
|
|
||||||
STABILIZING=380,
|
|
||||||
FINALIZING=390,
|
|
||||||
)
|
|
||||||
value = Parameter(datatype=FloatRange(unit='T'))
|
value = Parameter(datatype=FloatRange(unit='T'))
|
||||||
status = Parameter(datatype=StatusType(Status))
|
status = Parameter(datatype=StatusType(Drivable, 'PREPARED', 'PREPARING', 'RAMPING', 'STABILIZING', 'FINALIZING'))
|
||||||
target = Parameter(datatype=FloatRange(-15, 15, unit='T')) # poll only one parameter
|
target = Parameter(datatype=FloatRange(-15, 15, unit='T')) # poll only one parameter
|
||||||
ramp = Parameter('ramping speed', readonly=False,
|
ramp = Parameter('ramping speed', readonly=False,
|
||||||
datatype=FloatRange(0.064, 1.19, unit='T/min'), default=0.19)
|
datatype=FloatRange(0.064, 1.19, unit='T/min'), default=0.19)
|
||||||
@ -525,16 +510,16 @@ class Field(PpmsDrivable):
|
|||||||
datatype=EnumType(persistent=0, driven=1), default=0)
|
datatype=EnumType(persistent=0, driven=1), default=0)
|
||||||
|
|
||||||
STATUS_MAP = {
|
STATUS_MAP = {
|
||||||
1: (Status.IDLE, 'persistent mode'),
|
1: (StatusType.IDLE, 'persistent mode'),
|
||||||
2: (Status.PREPARING, 'switch warming'),
|
2: (StatusType.PREPARING, 'switch warming'),
|
||||||
3: (Status.FINALIZING, 'switch cooling'),
|
3: (StatusType.FINALIZING, 'switch cooling'),
|
||||||
4: (Status.IDLE, 'driven stable'),
|
4: (StatusType.IDLE, 'driven stable'),
|
||||||
5: (Status.STABILIZING, 'driven final'),
|
5: (StatusType.STABILIZING, 'driven final'),
|
||||||
6: (Status.RAMPING, 'charging'),
|
6: (StatusType.RAMPING, 'charging'),
|
||||||
7: (Status.RAMPING, 'discharging'),
|
7: (StatusType.RAMPING, 'discharging'),
|
||||||
8: (Status.ERROR, 'current error'),
|
8: (StatusType.ERROR, 'current error'),
|
||||||
11: (Status.ERROR, 'probably quenched'),
|
11: (StatusType.ERROR, 'probably quenched'),
|
||||||
15: (Status.ERROR, 'general failure'),
|
15: (StatusType.ERROR, 'general failure'),
|
||||||
}
|
}
|
||||||
|
|
||||||
channel = 'field'
|
channel = 'field'
|
||||||
@ -563,33 +548,33 @@ class Field(PpmsDrivable):
|
|||||||
|
|
||||||
def update_value_status(self, value, packed_status):
|
def update_value_status(self, value, packed_status):
|
||||||
if value is None:
|
if value is None:
|
||||||
self.status = (self.Status.ERROR, 'invalid value')
|
self.status = (StatusType.ERROR, 'invalid value')
|
||||||
return
|
return
|
||||||
self.value = round(value * 1e-4, 7)
|
self.value = round(value * 1e-4, 7)
|
||||||
status_code = (packed_status >> 4) & 0xf
|
status_code = (packed_status >> 4) & 0xf
|
||||||
status = self.STATUS_MAP.get(status_code, (self.Status.ERROR, 'unknown status code %d' % status_code))
|
status = self.STATUS_MAP.get(status_code, (StatusType.ERROR, 'unknown status code %d' % status_code))
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if self._last_change: # there was a change, which is not yet confirmed by hw
|
if self._last_change: # there was a change, which is not yet confirmed by hw
|
||||||
if status_code == 1: # persistent mode
|
if status_code == 1: # persistent mode
|
||||||
# leads are ramping (ppms has no extra status code for this!)
|
# leads are ramping (ppms has no extra status code for this!)
|
||||||
if now < self._last_change + 30:
|
if now < self._last_change + 30:
|
||||||
status = (self.Status.PREPARING, 'ramping leads')
|
status = (StatusType.PREPARING, 'ramping leads')
|
||||||
else:
|
else:
|
||||||
status = (self.Status.WARN, 'timeout when ramping leads')
|
status = (StatusType.WARN, 'timeout when ramping leads')
|
||||||
elif now > self._last_change + 5:
|
elif now > self._last_change + 5:
|
||||||
self._last_change = 0 # give up waiting for driving
|
self._last_change = 0 # give up waiting for driving
|
||||||
elif self.isDriving(status) and status != self._status_before_change:
|
elif self.isDriving(status) and status != self._status_before_change:
|
||||||
self._last_change = 0
|
self._last_change = 0
|
||||||
self.log.debug('time needed to change to busy: %.3g', now - self._last_change)
|
self.log.debug('time needed to change to busy: %.3g', now - self._last_change)
|
||||||
else:
|
else:
|
||||||
status = (self.Status.BUSY, 'changed target')
|
status = (StatusType.BUSY, 'changed target')
|
||||||
if abs(self.target - self.value) <= 1e-4:
|
if abs(self.target - self.value) <= 1e-4:
|
||||||
self._last_target = self.target
|
self._last_target = self.target
|
||||||
elif self._last_target is None:
|
elif self._last_target is None:
|
||||||
self._last_target = self.value
|
self._last_target = self.value
|
||||||
if self._stopped:
|
if self._stopped:
|
||||||
# combine 'stopped' with current status text
|
# combine 'stopped' with current status text
|
||||||
if status[0] == self.Status.IDLE:
|
if status[0] == StatusType.IDLE:
|
||||||
status = (status[0], 'stopped')
|
status = (status[0], 'stopped')
|
||||||
else:
|
else:
|
||||||
status = (status[0], 'stopping (%s)' % status[1])
|
status = (status[0], 'stopping (%s)' % status[1])
|
||||||
@ -602,7 +587,7 @@ class Field(PpmsDrivable):
|
|||||||
self._status_before_change = self.status
|
self._status_before_change = self.status
|
||||||
self._stopped = False
|
self._stopped = False
|
||||||
self._last_change = time.time()
|
self._last_change = time.time()
|
||||||
self.status = (self.Status.BUSY, 'changed target')
|
self.status = (StatusType.BUSY, 'changed target')
|
||||||
self._write_params(target, self.ramp, self.approachmode, self.persistentmode)
|
self._write_params(target, self.ramp, self.approachmode, self.persistentmode)
|
||||||
return self.target
|
return self.target
|
||||||
|
|
||||||
@ -613,7 +598,7 @@ class Field(PpmsDrivable):
|
|||||||
self._last_change = time.time()
|
self._last_change = time.time()
|
||||||
self._status_before_change = self.status
|
self._status_before_change = self.status
|
||||||
self._stopped = False
|
self._stopped = False
|
||||||
self.status = (self.Status.BUSY, 'changed persistent mode')
|
self.status = (StatusType.BUSY, 'changed persistent mode')
|
||||||
self._write_params(self.target, self.ramp, self.approachmode, mode)
|
self._write_params(self.target, self.ramp, self.approachmode, mode)
|
||||||
return self.persistentmode
|
return self.persistentmode
|
||||||
|
|
||||||
@ -642,8 +627,6 @@ class Field(PpmsDrivable):
|
|||||||
class Position(PpmsDrivable):
|
class Position(PpmsDrivable):
|
||||||
"""rotator position"""
|
"""rotator position"""
|
||||||
|
|
||||||
Status = Drivable.Status
|
|
||||||
|
|
||||||
value = Parameter(datatype=FloatRange(unit='deg'))
|
value = Parameter(datatype=FloatRange(unit='deg'))
|
||||||
target = Parameter(datatype=FloatRange(-720., 720., unit='deg'))
|
target = Parameter(datatype=FloatRange(-720., 720., unit='deg'))
|
||||||
enabled = Parameter('is this channel used?', readonly=False,
|
enabled = Parameter('is this channel used?', readonly=False,
|
||||||
@ -651,11 +634,11 @@ class Position(PpmsDrivable):
|
|||||||
speed = Parameter('motor speed', readonly=False, default=12,
|
speed = Parameter('motor speed', readonly=False, default=12,
|
||||||
datatype=FloatRange(0.8, 12, unit='deg/sec'))
|
datatype=FloatRange(0.8, 12, unit='deg/sec'))
|
||||||
STATUS_MAP = {
|
STATUS_MAP = {
|
||||||
1: (Status.IDLE, 'at target'),
|
1: (StatusType.IDLE, 'at target'),
|
||||||
5: (Status.BUSY, 'moving'),
|
5: (StatusType.BUSY, 'moving'),
|
||||||
8: (Status.IDLE, 'at limit'),
|
8: (StatusType.IDLE, 'at limit'),
|
||||||
9: (Status.IDLE, 'at index'),
|
9: (StatusType.IDLE, 'at index'),
|
||||||
15: (Status.ERROR, 'general failure'),
|
15: (StatusType.ERROR, 'general failure'),
|
||||||
}
|
}
|
||||||
|
|
||||||
channel = 'position'
|
channel = 'position'
|
||||||
@ -683,14 +666,14 @@ class Position(PpmsDrivable):
|
|||||||
|
|
||||||
def update_value_status(self, value, packed_status):
|
def update_value_status(self, value, packed_status):
|
||||||
if not self.enabled:
|
if not self.enabled:
|
||||||
self.status = (self.Status.DISABLED, 'disabled')
|
self.status = (StatusType.DISABLED, 'disabled')
|
||||||
return
|
return
|
||||||
if value is None:
|
if value is None:
|
||||||
self.status = (self.Status.ERROR, 'invalid value')
|
self.status = (StatusType.ERROR, 'invalid value')
|
||||||
return
|
return
|
||||||
self.value = value
|
self.value = value
|
||||||
status_code = (packed_status >> 12) & 0xf
|
status_code = (packed_status >> 12) & 0xf
|
||||||
status = self.STATUS_MAP.get(status_code, (self.Status.ERROR, 'unknown status code %d' % status_code))
|
status = self.STATUS_MAP.get(status_code, (StatusType.ERROR, 'unknown status code %d' % status_code))
|
||||||
if self._last_change: # there was a change, which is not yet confirmed by hw
|
if self._last_change: # there was a change, which is not yet confirmed by hw
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if now > self._last_change + 5:
|
if now > self._last_change + 5:
|
||||||
@ -699,20 +682,20 @@ class Position(PpmsDrivable):
|
|||||||
self.log.debug('time needed to change to busy: %.3g', now - self._last_change)
|
self.log.debug('time needed to change to busy: %.3g', now - self._last_change)
|
||||||
self._last_change = 0
|
self._last_change = 0
|
||||||
else:
|
else:
|
||||||
status = (self.Status.BUSY, 'changed target')
|
status = (StatusType.BUSY, 'changed target')
|
||||||
# BUSY can not reliably be determined from the status code, we have to do it on our own
|
# BUSY can not reliably be determined from the status code, we have to do it on our own
|
||||||
if abs(value - self.target) < 0.1:
|
if abs(value - self.target) < 0.1:
|
||||||
self._last_target = self.target
|
self._last_target = self.target
|
||||||
if not self._within_target:
|
if not self._within_target:
|
||||||
self._within_target = time.time()
|
self._within_target = time.time()
|
||||||
if time.time() > self._within_target + 1:
|
if time.time() > self._within_target + 1:
|
||||||
if status[0] != self.Status.IDLE:
|
if status[0] != StatusType.IDLE:
|
||||||
status = (self.Status.IDLE, status[1])
|
status = (StatusType.IDLE, status[1])
|
||||||
elif status[0] != self.Status.BUSY:
|
elif status[0] != StatusType.BUSY:
|
||||||
status = (self.Status.BUSY, status[1])
|
status = (StatusType.BUSY, status[1])
|
||||||
if self._stopped:
|
if self._stopped:
|
||||||
# combine 'stopped' with current status text
|
# combine 'stopped' with current status text
|
||||||
if status[0] == self.Status.IDLE:
|
if status[0] == StatusType.IDLE:
|
||||||
status = (status[0], 'stopped')
|
status = (status[0], 'stopped')
|
||||||
else:
|
else:
|
||||||
status = (status[0], 'stopping (%s)' % status[1])
|
status = (status[0], 'stopping (%s)' % status[1])
|
||||||
@ -722,7 +705,7 @@ class Position(PpmsDrivable):
|
|||||||
self._stopped = False
|
self._stopped = False
|
||||||
self._last_change = 0
|
self._last_change = 0
|
||||||
self._status_before_change = self.status
|
self._status_before_change = self.status
|
||||||
self.status = (self.Status.BUSY, 'changed target')
|
self.status = (StatusType.BUSY, 'changed target')
|
||||||
self._write_params(target, self.speed)
|
self._write_params(target, self.speed)
|
||||||
return self.target
|
return self.target
|
||||||
|
|
||||||
|
75
test/test_status_members.py
Normal file
75
test/test_status_members.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Module authors:
|
||||||
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
from frappy.core import Readable, Drivable, StatusType
|
||||||
|
from frappy_ess.epics import EpicsReadable, EpicsDrivable
|
||||||
|
|
||||||
|
readable_codes = {m.name: m.value for m in Readable.Status.members}
|
||||||
|
drivable_codes = {m.name: m.value for m in Drivable.Status.members}
|
||||||
|
|
||||||
|
|
||||||
|
def test_entangle_status():
|
||||||
|
try:
|
||||||
|
# pylint: disable=import-outside-toplevel
|
||||||
|
# pylint: disable=unused-import
|
||||||
|
import PyTango
|
||||||
|
except ImportError:
|
||||||
|
return
|
||||||
|
|
||||||
|
# pylint: disable=import-outside-toplevel
|
||||||
|
from frappy_mlz.entangle import AnalogInput, AnalogOutput, TemperatureController
|
||||||
|
|
||||||
|
assert AnalogInput.status.datatype.members[0].export_datatype() == {
|
||||||
|
"type": "enum",
|
||||||
|
"members": {"UNKNOWN": StatusType.UNKNOWN,
|
||||||
|
"DISABLED": StatusType.DISABLED,
|
||||||
|
**readable_codes},
|
||||||
|
}
|
||||||
|
assert AnalogOutput.status.datatype.members[0].export_datatype() == {
|
||||||
|
"type": "enum",
|
||||||
|
"members": {"UNKNOWN": StatusType.UNKNOWN,
|
||||||
|
"DISABLED": StatusType.DISABLED,
|
||||||
|
"UNSTABLE": StatusType.UNSTABLE,
|
||||||
|
**drivable_codes},
|
||||||
|
}
|
||||||
|
assert TemperatureController.status.datatype.members[0].export_datatype() == {
|
||||||
|
"type": "enum",
|
||||||
|
"members": {"UNKNOWN": StatusType.UNKNOWN,
|
||||||
|
"DISABLED": StatusType.DISABLED,
|
||||||
|
"UNSTABLE": StatusType.UNSTABLE,
|
||||||
|
**drivable_codes},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_epics_status():
|
||||||
|
|
||||||
|
assert EpicsReadable.status.datatype.members[0].export_datatype() == {
|
||||||
|
"type": "enum",
|
||||||
|
"members": {"UNKNOWN": StatusType.UNKNOWN,
|
||||||
|
**readable_codes},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert EpicsDrivable.status.datatype.members[0].export_datatype() == {
|
||||||
|
"type": "enum",
|
||||||
|
"members": {"UNKNOWN": StatusType.UNKNOWN,
|
||||||
|
**drivable_codes},
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user