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:
2023-03-20 14:14:12 +01:00
parent 11a3bed8b8
commit 361f9ac4fc
8 changed files with 169 additions and 108 deletions

View File

@ -28,7 +28,7 @@ The PPMS hardware has some special requirements:
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
<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
@ -39,7 +39,6 @@ from frappy.datatypes import BoolType, EnumType, \
FloatRange, IntRange, StatusType, StringType
from frappy.errors import HardwareError
from frappy.lib import clamp
from frappy.lib.enum import Enum
from frappy.modules import Communicator, \
Drivable, Parameter, Property, Readable
from frappy.io import HasIO
@ -117,7 +116,7 @@ class Main(Communicator):
class PpmsBase(HasIO, Readable):
"""common base for all ppms modules"""
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
_last_settings = None # used by several modules
@ -140,13 +139,13 @@ class PpmsBase(HasIO, Readable):
# update value and status
# to be reimplemented for modules looking at packed_status
if not self.enabled:
self.status = (self.Status.DISABLED, 'disabled')
self.status = (StatusType.DISABLED, 'disabled')
return
if value is None:
self.status = (self.Status.ERROR, 'invalid value')
self.status = (StatusType.ERROR, 'invalid value')
else:
self.value = value
self.status = (self.Status.IDLE, '')
self.status = (StatusType.IDLE, '')
def comm_write(self, command):
"""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
"""
Status = Drivable.Status
code_table = [
# valuecode, status, statusname, opcode, targetname
(0, Status.IDLE, 'unknown', 10, 'noop'),
(1, Status.IDLE, 'purged_and_sealed', 1, 'purge_and_seal'),
(2, Status.IDLE, 'vented_and_sealed', 2, 'vent_and_seal'),
(3, Status.WARN, 'sealed_unknown', 0, 'seal_immediately'),
(4, Status.BUSY, 'purge_and_seal', None, None),
(5, Status.BUSY, 'vent_and_seal', None, None),
(6, Status.BUSY, 'pumping_down', None, None),
(8, Status.IDLE, 'pumping_continuously', 3, 'pump_continuously'),
(9, Status.IDLE, 'venting_continuously', 4, 'vent_continuously'),
(15, Status.ERROR, 'general_failure', None, None),
(0, StatusType.IDLE, 'unknown', 10, 'noop'),
(1, StatusType.IDLE, 'purged_and_sealed', 1, 'purge_and_seal'),
(2, StatusType.IDLE, 'vented_and_sealed', 2, 'vent_and_seal'),
(3, StatusType.WARN, 'sealed_unknown', 0, 'seal_immediately'),
(4, StatusType.BUSY, 'purge_and_seal', None, None),
(5, StatusType.BUSY, 'vent_and_seal', None, None),
(6, StatusType.BUSY, 'pumping_down', None, None),
(8, StatusType.IDLE, 'pumping_continuously', 3, 'pump_continuously'),
(9, StatusType.IDLE, 'venting_continuously', 4, 'vent_continuously'),
(15, StatusType.ERROR, 'general_failure', None, None),
]
value_codes = {k: v for v, _, k, _, _ in code_table}
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]
else:
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):
opcode = int(self.communicate('CHAMBER?'))
@ -341,13 +339,8 @@ class Chamber(PpmsDrivable):
class Temp(PpmsDrivable):
"""temperature"""
Status = Enum(
Drivable.Status,
RAMPING=370,
STABILIZING=380,
)
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)
setpoint = Parameter('intermediate set point',
datatype=FloatRange(1.7, 402.0, unit='K'))
@ -362,15 +355,15 @@ class Temp(PpmsDrivable):
general_stop = Property('respect general stop', datatype=BoolType(),
default=True, value=False)
STATUS_MAP = {
1: (Status.IDLE, 'stable at target'),
2: (Status.RAMPING, 'ramping'),
5: (Status.STABILIZING, 'within tolerance'),
6: (Status.STABILIZING, 'outside tolerance'),
7: (Status.STABILIZING, 'filling/emptying reservoir'),
10: (Status.WARN, 'standby'),
13: (Status.WARN, 'control disabled'),
14: (Status.ERROR, 'can not complete'),
15: (Status.ERROR, 'general failure'),
1: (StatusType.IDLE, 'stable at target'),
2: (StatusType.RAMPING, 'ramping'),
5: (StatusType.STABILIZING, 'within tolerance'),
6: (StatusType.STABILIZING, 'outside tolerance'),
7: (StatusType.STABILIZING, 'filling/emptying reservoir'),
10: (StatusType.WARN, 'standby'),
13: (StatusType.WARN, 'control disabled'),
14: (StatusType.ERROR, 'can not complete'),
15: (StatusType.ERROR, 'general failure'),
}
channel = 'temp'
@ -420,11 +413,11 @@ class Temp(PpmsDrivable):
def update_value_status(self, value, packed_status):
if value is None:
self.status = (self.Status.ERROR, 'invalid value')
self.status = (StatusType.ERROR, 'invalid value')
return
self.value = value
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()
if value > 11:
# when starting from T > 50, this will be 15 min.
@ -436,7 +429,7 @@ class Temp(PpmsDrivable):
self._wait_at10 = False
self._last_change = now
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 now > self._last_change + 5:
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._last_change = 0
else:
status = (self.Status.BUSY, 'changed target')
status = (StatusType.BUSY, 'changed target')
if abs(self.value - self.target) < self.target * 0.01:
self._last_target = self.target
elif self._last_target is None:
self._last_target = self.value
if self._stopped:
# combine 'stopped' with current status text
if status[0] == self.Status.IDLE:
if status[0] == StatusType.IDLE:
status = (status[0], 'stopped')
else:
status = (status[0], 'stopping (%s)' % status[1])
@ -459,7 +452,7 @@ class Temp(PpmsDrivable):
# handle timeout
if self.isDriving(status):
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:
self._expected_target_time = 0
self.status = status
@ -469,7 +462,7 @@ class Temp(PpmsDrivable):
if abs(self.target - self.value) <= 2e-5 * target and target == self.target:
return None
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._write_params(target, self.ramp, self.approachmode)
self.log.debug('write_target %s' % repr((self.setpoint, target, self._wait_at10)))
@ -493,7 +486,7 @@ class Temp(PpmsDrivable):
def stop(self):
if not self.isDriving():
return
if self.status[0] != self.Status.STABILIZING:
if self.status[0] != StatusType.STABILIZING:
# we are not near target
newtarget = clamp(self._last_target, self.value, self.target)
if newtarget != self.target:
@ -506,16 +499,8 @@ class Temp(PpmsDrivable):
class Field(PpmsDrivable):
"""magnetic field"""
Status = Enum(
Drivable.Status,
PREPARED=150,
PREPARING=340,
RAMPING=370,
STABILIZING=380,
FINALIZING=390,
)
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
ramp = Parameter('ramping speed', readonly=False,
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)
STATUS_MAP = {
1: (Status.IDLE, 'persistent mode'),
2: (Status.PREPARING, 'switch warming'),
3: (Status.FINALIZING, 'switch cooling'),
4: (Status.IDLE, 'driven stable'),
5: (Status.STABILIZING, 'driven final'),
6: (Status.RAMPING, 'charging'),
7: (Status.RAMPING, 'discharging'),
8: (Status.ERROR, 'current error'),
11: (Status.ERROR, 'probably quenched'),
15: (Status.ERROR, 'general failure'),
1: (StatusType.IDLE, 'persistent mode'),
2: (StatusType.PREPARING, 'switch warming'),
3: (StatusType.FINALIZING, 'switch cooling'),
4: (StatusType.IDLE, 'driven stable'),
5: (StatusType.STABILIZING, 'driven final'),
6: (StatusType.RAMPING, 'charging'),
7: (StatusType.RAMPING, 'discharging'),
8: (StatusType.ERROR, 'current error'),
11: (StatusType.ERROR, 'probably quenched'),
15: (StatusType.ERROR, 'general failure'),
}
channel = 'field'
@ -563,33 +548,33 @@ class Field(PpmsDrivable):
def update_value_status(self, value, packed_status):
if value is None:
self.status = (self.Status.ERROR, 'invalid value')
self.status = (StatusType.ERROR, 'invalid value')
return
self.value = round(value * 1e-4, 7)
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()
if self._last_change: # there was a change, which is not yet confirmed by hw
if status_code == 1: # persistent mode
# leads are ramping (ppms has no extra status code for this!)
if now < self._last_change + 30:
status = (self.Status.PREPARING, 'ramping leads')
status = (StatusType.PREPARING, 'ramping leads')
else:
status = (self.Status.WARN, 'timeout when ramping leads')
status = (StatusType.WARN, 'timeout when ramping leads')
elif now > self._last_change + 5:
self._last_change = 0 # give up waiting for driving
elif self.isDriving(status) and status != self._status_before_change:
self._last_change = 0
self.log.debug('time needed to change to busy: %.3g', now - self._last_change)
else:
status = (self.Status.BUSY, 'changed target')
status = (StatusType.BUSY, 'changed target')
if abs(self.target - self.value) <= 1e-4:
self._last_target = self.target
elif self._last_target is None:
self._last_target = self.value
if self._stopped:
# combine 'stopped' with current status text
if status[0] == self.Status.IDLE:
if status[0] == StatusType.IDLE:
status = (status[0], 'stopped')
else:
status = (status[0], 'stopping (%s)' % status[1])
@ -602,7 +587,7 @@ class Field(PpmsDrivable):
self._status_before_change = self.status
self._stopped = False
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)
return self.target
@ -613,7 +598,7 @@ class Field(PpmsDrivable):
self._last_change = time.time()
self._status_before_change = self.status
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)
return self.persistentmode
@ -642,8 +627,6 @@ class Field(PpmsDrivable):
class Position(PpmsDrivable):
"""rotator position"""
Status = Drivable.Status
value = Parameter(datatype=FloatRange(unit='deg'))
target = Parameter(datatype=FloatRange(-720., 720., unit='deg'))
enabled = Parameter('is this channel used?', readonly=False,
@ -651,11 +634,11 @@ class Position(PpmsDrivable):
speed = Parameter('motor speed', readonly=False, default=12,
datatype=FloatRange(0.8, 12, unit='deg/sec'))
STATUS_MAP = {
1: (Status.IDLE, 'at target'),
5: (Status.BUSY, 'moving'),
8: (Status.IDLE, 'at limit'),
9: (Status.IDLE, 'at index'),
15: (Status.ERROR, 'general failure'),
1: (StatusType.IDLE, 'at target'),
5: (StatusType.BUSY, 'moving'),
8: (StatusType.IDLE, 'at limit'),
9: (StatusType.IDLE, 'at index'),
15: (StatusType.ERROR, 'general failure'),
}
channel = 'position'
@ -683,14 +666,14 @@ class Position(PpmsDrivable):
def update_value_status(self, value, packed_status):
if not self.enabled:
self.status = (self.Status.DISABLED, 'disabled')
self.status = (StatusType.DISABLED, 'disabled')
return
if value is None:
self.status = (self.Status.ERROR, 'invalid value')
self.status = (StatusType.ERROR, 'invalid value')
return
self.value = value
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
now = time.time()
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._last_change = 0
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
if abs(value - self.target) < 0.1:
self._last_target = self.target
if not self._within_target:
self._within_target = time.time()
if time.time() > self._within_target + 1:
if status[0] != self.Status.IDLE:
status = (self.Status.IDLE, status[1])
elif status[0] != self.Status.BUSY:
status = (self.Status.BUSY, status[1])
if status[0] != StatusType.IDLE:
status = (StatusType.IDLE, status[1])
elif status[0] != StatusType.BUSY:
status = (StatusType.BUSY, status[1])
if self._stopped:
# combine 'stopped' with current status text
if status[0] == self.Status.IDLE:
if status[0] == StatusType.IDLE:
status = (status[0], 'stopped')
else:
status = (status[0], 'stopping (%s)' % status[1])
@ -722,7 +705,7 @@ class Position(PpmsDrivable):
self._stopped = False
self._last_change = 0
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)
return self.target