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:
@ -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)
|
||||
|
@ -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):
|
||||
|
Reference in New Issue
Block a user