rework EnumType to use better Enum's

unfortunately IntEnum can't be bent like we would need it (extensible).
So we had to write our own....

The members of the Enum still behave like ints, but also have
.name and .value attributes, should they be needed.

needed adoptions to correctly use (and test) the EnumType are included.

Change-Id: Ie019d2f449a244c4fab00554b6c6daaac8948b59
Reviewed-on: https://forge.frm2.tum.de/review/17843
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
Enrico Faulhaber
2018-04-26 16:29:09 +02:00
parent 927ca854a2
commit 574a66c65b
15 changed files with 644 additions and 298 deletions

View File

@ -25,7 +25,6 @@ import time
import random
from secop.modules import Drivable, Command, Param
from secop.protocol import status
from secop.datatypes import FloatRange, EnumType, TupleOf
from secop.lib import clamp, mkthread
@ -100,7 +99,7 @@ class Cryostat(CryoBase):
group='pid',
),
mode=Param("mode of regulation",
datatype=EnumType('ramp', 'pid', 'openloop'),
datatype=EnumType('mode', ramp=None, pid=None, openloop=None),
default='ramp',
readonly=False,
),
@ -153,7 +152,7 @@ class Cryostat(CryoBase):
return value
self.target = value
# next read_status will see this status, until the loop updates it
self.status = status.BUSY, 'new target set'
self.status = self.Status.BUSY, 'new target set'
return value
def read_maxpower(self, maxage=0):
@ -209,13 +208,13 @@ class Cryostat(CryoBase):
def thread(self):
self.sampletemp = self.T_start
self.regulationtemp = self.T_start
self.status = status.OK, ''
self.status = self.Status.IDLE, ''
while not self._stopflag:
try:
self.__sim()
except Exception as e:
self.log.exception(e)
self.status = status.ERROR, str(e)
self.status = self.Status.ERROR, str(e)
def __sim(self):
# complex thread handling:
@ -264,7 +263,7 @@ class Cryostat(CryoBase):
# b) see
# http://brettbeauregard.com/blog/2011/04/
# improving-the-beginners-pid-introduction/
if self.mode != 'openloop':
if self.mode != self.mode.openloop:
# fix artefacts due to too big timesteps
# actually i would prefer reducing looptime, but i have no
# good idea on when to increase it back again
@ -328,7 +327,7 @@ class Cryostat(CryoBase):
lastmode = self.mode
# c)
if self.setpoint != self.target:
if self.ramp == 0:
if self.ramp == 0 or self.mode == self.mode.enum.pid:
maxdelta = 10000
else:
maxdelta = self.ramp / 60. * h
@ -354,12 +353,12 @@ class Cryostat(CryoBase):
if abs(_T - self.target) > deviation:
deviation = abs(_T - self.target)
if (len(window) < 3) or deviation > self.tolerance:
self.status = status.BUSY, 'unstable'
self.status = self.Status.BUSY, 'unstable'
elif self.setpoint == self.target:
self.status = status.OK, 'at target'
self.status = self.Status.IDLE, 'at target'
damper -= (damper - 1) * 0.1 # max value for damper is 11
else:
self.status = status.BUSY, 'ramping setpoint'
self.status = self.Status.BUSY, 'ramping setpoint'
damper -= (damper - 1) * 0.05
self.regulationtemp = round(regulation, 3)
self.sampletemp = round(sample, 3)

View File

@ -24,9 +24,9 @@ import time
import random
import threading
from secop.lib.enum import Enum
from secop.modules import Readable, Drivable, Param
from secop.datatypes import EnumType, FloatRange, IntRange, ArrayOf, StringType, TupleOf, StructOf, BoolType
from secop.protocol import status
class Switch(Drivable):
@ -50,9 +50,6 @@ class Switch(Drivable):
),
}
def init(self):
self._started = 0
def read_value(self, maxage=0):
# could ask HW
# we just return the value of the target here.
@ -65,7 +62,7 @@ class Switch(Drivable):
def write_target(self, value):
# could tell HW
pass
setattr(self, 'status', (self.Status.BUSY, 'switching %s' % value.name.upper()))
# note: setting self.target to the new value is done after this....
# note: we may also return the read-back value from the hw here
@ -73,8 +70,8 @@ class Switch(Drivable):
self.log.info("read status")
info = self._update()
if self.target == self.value:
return status.OK, ''
return status.BUSY, info
return self.Status.IDLE, ''
return self.Status.BUSY, info
def _update(self):
started = self.parameters['target'].timestamp
@ -90,7 +87,7 @@ class Switch(Drivable):
info = 'is switched OFF'
self.value = self.target
if info:
self.log.debug(info)
self.log.info(info)
return info
@ -119,7 +116,7 @@ class MagneticField(Drivable):
}
def init(self):
self._state = 'idle'
self._state = Enum('state', idle=1, switch_on=2, switch_off=3, ramp=4).idle
self._heatswitch = self.DISPATCHER.get_module(self.heatswitch)
_thread = threading.Thread(target=self._thread)
_thread.daemon = True
@ -135,44 +132,45 @@ class MagneticField(Drivable):
# note: we may also return the read-back value from the hw here
def read_status(self, maxage=0):
return (status.OK, '') if self._state == 'idle' else (status.BUSY,
self._state)
if self._state == self._state.enum.idle:
return (self.Status.IDLE, '')
return (self.Status.BUSY, self._state.name)
def _thread(self):
loopdelay = 1
while True:
ts = time.time()
if self._state == 'idle':
if self._state == self._state.enum.idle:
if self.target != self.value:
self.log.debug('got new target -> switching heater on')
self._state = 'switch_on'
self._state = self._state.enum.switch_on
self._heatswitch.write_target('on')
if self._state == 'switch_on':
if self._state == self._state.enum.switch_on:
# wait until switch is on
if self._heatswitch.read_value() == 'on':
self.log.debug('heatswitch is on -> ramp to %.3f' %
self.target)
self._state = 'ramp'
if self._state == 'ramp':
self._state = self._state.enum.ramp
if self._state == self._state.enum.ramp:
if self.target == self.value:
self.log.debug('at field! mode is %r' % self.mode)
if self.mode:
self.log.debug('at field -> switching heater off')
self._state = 'switch_off'
self._state = self._state.enum.switch_off
self._heatswitch.write_target('off')
else:
self.log.debug('at field -> hold')
self._state = 'idle'
self.status = self.read_status() # push async
self._state = self._state.enum.idle
self.read_status() # push async
else:
step = self.ramp * loopdelay / 60.
step = max(min(self.target - self.value, step), -step)
self.value += step
if self._state == 'switch_off':
if self._state == self._state.enum.switch_off:
# wait until switch is off
if self._heatswitch.read_value() == 'off':
self.log.debug('heatswitch is off at %.3f' % self.value)
self._state = 'idle'
self._state = self._state.enum.idle
self.read_status() # update async
time.sleep(max(0.01, ts + loopdelay - time.time()))
self.log.error(self, 'main thread exited unexpectedly!')
@ -229,10 +227,10 @@ class SampleTemp(Drivable):
while True:
ts = time.time()
if self.value == self.target:
if self.status != status.OK:
self.status = status.OK, ''
if self.status[0] != self.Status.IDLE:
self.status = self.Status.IDLE, ''
else:
self.status = status.BUSY, 'ramping'
self.status = self.Status.BUSY, 'ramping'
step = self.ramp * loopdelay / 60.
step = max(min(self.target - self.value, step), -step)
self.value += step
@ -278,7 +276,7 @@ class Label(Readable):
mf_mode = dev_mf.mode
mf_val = dev_mf.value
mf_unit = dev_mf.parameters['value'].unit
if mf_stat[0] == status.OK:
if mf_stat[0] == self.Status.IDLE:
state = 'Persistent' if mf_mode else 'Non-persistent'
else:
state = mf_stat[1] or 'ramping'
@ -293,21 +291,24 @@ class DatatypesTest(Readable):
"""for demoing all datatypes
"""
parameters = {
'enum': Param(
'enum', datatype=EnumType(
'boo', 'faar', z=9), readonly=False, default=1), 'tupleof': Param(
'tuple of int, float and str', datatype=TupleOf(
IntRange(), FloatRange(), StringType()), readonly=False, default=(
1, 2.3, 'a')), 'arrayof': Param(
'array: 2..3 times bool', datatype=ArrayOf(
BoolType(), 2, 3), readonly=False, default=[
1, 0, 1]), 'intrange': Param(
'intrange', datatype=IntRange(
2, 9), readonly=False, default=4), 'floatrange': Param(
'floatrange', datatype=FloatRange(
-1, 1), readonly=False, default=0, ), 'struct': Param(
'struct(a=str, b=int, c=bool)', datatype=StructOf(
a=StringType(), b=IntRange(), c=BoolType()), ), }
'enum': Param('enum', datatype=EnumType(boo=None, faar=None, z=9),
readonly=False, default=1),
'tupleof': Param('tuple of int, float and str',
datatype=TupleOf(IntRange(), FloatRange(),
StringType()),
readonly=False, default=(1, 2.3, 'a')),
'arrayof': Param('array: 2..3 times bool',
datatype=ArrayOf(BoolType(), 2, 3),
readonly=False, default=[1, 0, 1]),
'intrange': Param('intrange', datatype=IntRange(2, 9),
readonly=False, default=4),
'floatrange': Param('floatrange', datatype=FloatRange(-1, 1),
readonly=False, default=0, ),
'struct': Param('struct(a=str, b=int, c=bool)',
datatype=StructOf(a=StringType(), b=IntRange(),
c=BoolType()),
),
}
class ArrayTest(Readable):