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

@@ -23,7 +23,7 @@
"""drivers for CCU4, the cryostat control unit at SINQ"""
# the most common Frappy classes can be imported from frappy.core
from frappy.core import EnumType, FloatRange, \
HasIO, Parameter, Readable, StringIO
HasIO, Parameter, Readable, StringIO, StatusType
class CCU4IO(StringIO):
@@ -51,16 +51,16 @@ class HeLevel(HasIO, Readable):
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'
STATUS_MAP = {
0: (Status.IDLE, 'sensor ok'),
1: (Status.ERROR, 'sensor warm'),
2: (Status.ERROR, 'no sensor'),
3: (Status.ERROR, 'timeout'),
4: (Status.ERROR, 'not yet read'),
5: (Status.DISABLED, 'disabled'),
0: (StatusType.IDLE, 'sensor ok'),
1: (StatusType.ERROR, 'sensor warm'),
2: (StatusType.ERROR, 'no sensor'),
3: (StatusType.ERROR, 'timeout'),
4: (StatusType.ERROR, 'not yet read'),
5: (StatusType.DISABLED, 'disabled'),
}
def query(self, cmd):

View File

@@ -31,7 +31,7 @@ import time
from ast import literal_eval
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.core import Done, Drivable, Parameter, Property, CommonReadHandler, CommonWriteHandler
from frappy.io import HasIO
@@ -158,6 +158,7 @@ class ResChannel(Channel):
channel = Property('the Lakeshore channel', datatype=IntRange(1, 16), export=False)
value = Parameter(datatype=FloatRange(unit='Ohm'))
status = Parameter(datatype=StatusType(Drivable, 'DISABLED'))
pollinterval = Parameter(visibility=3, default=1)
range = Parameter('reading range', readonly=False,
datatype=EnumType(**RES_RANGE))

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