removed old style syntax

- removed secop/metaclass.py
- moved code from ModuleMeta to modules.HasAccessibles.__init_subclass__
- reworked properties:
  assignment obj.property = value now always allowed
- reworked Parameters and Command to be true descriptors
- Command must now be solely used as decorator
- renamed 'usercommand' to 'Command'
- command methods no longer start with 'do_'
- reworked mechanism to determine accessible order:
  the attribute paramOrder, if given, determines order of accessibles
+ fixed some issues makeing the IDE more happy
+ simplified code for StatusType and added a test for it

Change-Id: I8045cf38ee6f4d4862428272df0b12a7c8abaca7
Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/25049
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
2021-02-12 18:37:04 +01:00
parent f9a2152883
commit 07b758c3dd
34 changed files with 1678 additions and 1978 deletions

View File

@ -27,18 +27,16 @@ from math import atan
from secop.datatypes import EnumType, FloatRange, TupleOf, StringType, BoolType
from secop.lib import clamp, mkthread
from secop.modules import Drivable, Override, Parameter
from secop.modules import Drivable, Parameter, Command
# test custom property (value.test can be changed in config file)
from secop.properties import Property
Parameter.properties['test'] = Property('A Property for testing purposes', StringType(), default='', export=True)
Parameter.propertyDict['test'] = Property('A Property for testing purposes', StringType(), default='', export=True)
class CryoBase(Drivable):
properties = {
'is_cryo': Property('private Flag if this is a cryostat', BoolType(), default=True, export=True),
}
is_cryo = Property('private Flag if this is a cryostat', BoolType(), default=True, export=True)
class Cryostat(CryoBase):
@ -49,93 +47,88 @@ class Cryostat(CryoBase):
- thermal transfer between regulation and samplen
"""
parameters = dict(
jitter=Parameter("amount of random noise on readout values",
datatype=FloatRange(0, 1), unit="K",
default=0.1, readonly=False, export=False,
),
T_start=Parameter("starting temperature for simulation",
datatype=FloatRange(0), default=10,
export=False,
),
looptime=Parameter("timestep for simulation",
datatype=FloatRange(0.01, 10), unit="s", default=1,
readonly=False, export=False,
jitter = Parameter("amount of random noise on readout values",
datatype=FloatRange(0, 1), unit="K",
default=0.1, readonly=False, export=False,
),
ramp=Parameter("ramping speed of the setpoint",
datatype=FloatRange(0, 1e3), unit="K/min", default=1,
readonly=False,
),
setpoint=Parameter("current setpoint during ramping else target",
datatype=FloatRange(), default=1, unit='K',
),
maxpower=Parameter("Maximum heater power",
datatype=FloatRange(0), default=1, unit="W",
readonly=False,
group='heater_settings',
),
heater=Parameter("current heater setting",
datatype=FloatRange(0, 100), default=0, unit="%",
group='heater_settings',
),
heaterpower=Parameter("current heater power",
datatype=FloatRange(0), default=0, unit="W",
group='heater_settings',
),
target=Override("target temperature",
datatype=FloatRange(0), default=0, unit="K",
T_start = Parameter("starting temperature for simulation",
datatype=FloatRange(0), default=10,
export=False,
),
looptime = Parameter("timestep for simulation",
datatype=FloatRange(0.01, 10), unit="s", default=1,
readonly=False, export=False,
),
ramp = Parameter("ramping speed of the setpoint",
datatype=FloatRange(0, 1e3), unit="K/min", default=1,
readonly=False,
),
value=Override("regulation temperature",
datatype=FloatRange(0), default=0, unit="K",
test='TEST',
setpoint = Parameter("current setpoint during ramping else target",
datatype=FloatRange(), default=1, unit='K',
),
maxpower = Parameter("Maximum heater power",
datatype=FloatRange(0), default=1, unit="W",
readonly=False,
group='heater_settings',
),
heater = Parameter("current heater setting",
datatype=FloatRange(0, 100), default=0, unit="%",
group='heater_settings',
),
heaterpower = Parameter("current heater power",
datatype=FloatRange(0), default=0, unit="W",
group='heater_settings',
),
target = Parameter("target temperature",
datatype=FloatRange(0), default=0, unit="K",
readonly=False,
),
value = Parameter("regulation temperature",
datatype=FloatRange(0), default=0, unit="K",
test='TEST',
),
pid = Parameter("regulation coefficients",
datatype=TupleOf(FloatRange(0), FloatRange(0, 100),
FloatRange(0, 100)),
default=(40, 10, 2), readonly=False,
group='pid',
),
pid=Parameter("regulation coefficients",
datatype=TupleOf(FloatRange(0), FloatRange(0, 100),
FloatRange(0, 100)),
default=(40, 10, 2), readonly=False,
# pylint: disable=invalid-name
p = Parameter("regulation coefficient 'p'",
datatype=FloatRange(0), default=40, unit="%/K", readonly=False,
group='pid',
),
p=Parameter("regulation coefficient 'p'",
datatype=FloatRange(0), default=40, unit="%/K", readonly=False,
group='pid',
),
i=Parameter("regulation coefficient 'i'",
datatype=FloatRange(0, 100), default=10, readonly=False,
group='pid',
),
d=Parameter("regulation coefficient 'd'",
datatype=FloatRange(0, 100), default=2, readonly=False,
group='pid',
),
mode=Parameter("mode of regulation",
datatype=EnumType('mode', ramp=None, pid=None, openloop=None),
default='ramp',
readonly=False,
),
pollinterval=Override("polling interval",
datatype=FloatRange(0), default=5,
),
tolerance=Parameter("temperature range for stability checking",
datatype=FloatRange(0, 100), default=0.1, unit='K',
i = Parameter("regulation coefficient 'i'",
datatype=FloatRange(0, 100), default=10, readonly=False,
group='pid',
),
d = Parameter("regulation coefficient 'd'",
datatype=FloatRange(0, 100), default=2, readonly=False,
group='pid',
),
mode = Parameter("mode of regulation",
datatype=EnumType('mode', ramp=None, pid=None, openloop=None),
default='ramp',
readonly=False,
),
pollinterval = Parameter("polling interval",
datatype=FloatRange(0), default=5,
),
tolerance = Parameter("temperature range for stability checking",
datatype=FloatRange(0, 100), default=0.1, unit='K',
readonly=False,
group='stability',
),
window = Parameter("time window for stability checking",
datatype=FloatRange(1, 900), default=30, unit='s',
readonly=False,
group='stability',
),
timeout = Parameter("max waiting time for stabilisation check",
datatype=FloatRange(1, 36000), default=900, unit='s',
readonly=False,
group='stability',
),
window=Parameter("time window for stability checking",
datatype=FloatRange(1, 900), default=30, unit='s',
readonly=False,
group='stability',
),
timeout=Parameter("max waiting time for stabilisation check",
datatype=FloatRange(1, 36000), default=900, unit='s',
readonly=False,
group='stability',
),
)
commands = dict(
stop=Override(
"Stop ramping the setpoint\n\nby setting the current setpoint as new target"),
)
def initModule(self):
self._stopflag = False
@ -180,8 +173,11 @@ class Cryostat(CryoBase):
def read_pid(self):
return (self.p, self.i, self.d)
def do_stop(self):
# stop the ramp by setting current setpoint as target
@Command()
def stop(self):
"""Stop ramping the setpoint
by setting the current setpoint as new target"""
# XXX: discussion: take setpoint or current value ???
self.write_target(self.setpoint)

View File

@ -28,42 +28,39 @@ import time
from secop.datatypes import ArrayOf, BoolType, EnumType, \
FloatRange, IntRange, StringType, StructOf, TupleOf
from secop.lib.enum import Enum
from secop.modules import Drivable, Override, Parameter as SECoP_Parameter, Readable
from secop.modules import Drivable, Parameter as SECoP_Parameter, Readable
from secop.properties import Property
class Parameter(SECoP_Parameter):
properties = {
'test' : Property('A property for testing purposes', StringType(), default='', mandatory=False, extname='test'),
}
test = Property('A property for testing purposes', StringType(), default='', mandatory=False, extname='test')
PERSIST = 101
class Switch(Drivable):
"""switch it on or off....
"""
parameters = {
'value': Override('current state (on or off)',
value = Parameter('current state (on or off)',
datatype=EnumType(on=1, off=0), default=0,
)
target = Parameter('wanted state (on or off)',
datatype=EnumType(on=1, off=0), default=0,
),
'target': Override('wanted state (on or off)',
datatype=EnumType(on=1, off=0), default=0,
readonly=False,
),
'switch_on_time': Parameter('seconds to wait after activating the switch',
readonly=False,
)
switch_on_time = Parameter('seconds to wait after activating the switch',
datatype=FloatRange(0, 60), unit='s',
default=10, export=False,
)
switch_off_time = Parameter('cool-down time in seconds',
datatype=FloatRange(0, 60), unit='s',
default=10, export=False,
),
'switch_off_time': Parameter('cool-down time in seconds',
datatype=FloatRange(0, 60), unit='s',
default=10, export=False,
),
}
)
properties = {
'description' : Property('The description of the Module', StringType(),
default='no description', mandatory=False, extname='description'),
}
description = Property('The description of the Module', StringType(),
default='no description', mandatory=False, extname='description')
def read_value(self):
# could ask HW
@ -109,30 +106,29 @@ class Switch(Drivable):
class MagneticField(Drivable):
"""a liquid magnet
"""
parameters = {
'value': Override('current field in T',
value = Parameter('current field in T',
unit='T', datatype=FloatRange(-15, 15), default=0,
)
target = Parameter('target field in T',
unit='T', datatype=FloatRange(-15, 15), default=0,
),
'target': Override('target field in T',
unit='T', datatype=FloatRange(-15, 15), default=0,
readonly=False,
),
'ramp': Parameter('ramping speed',
unit='T/min', datatype=FloatRange(0, 1), default=0.1,
readonly=False,
),
'mode': Parameter('what to do after changing field',
default=1, datatype=EnumType(persistent=1, hold=0),
readonly=False,
),
'heatswitch': Parameter('name of heat switch device',
datatype=StringType(), export=False,
),
}
readonly=False,
)
ramp = Parameter('ramping speed',
unit='T/min', datatype=FloatRange(0, 1), default=0.1,
readonly=False,
)
mode = Parameter('what to do after changing field',
default=1, datatype=EnumType(persistent=1, hold=0),
readonly=False,
)
heatswitch = Parameter('name of heat switch device',
datatype=StringType(), export=False,
)
Status = Enum(Drivable.Status, PERSIST=PERSIST, PREPARE=301, RAMPING=302, FINISH=303)
overrides = {
'status' : Override(datatype=TupleOf(EnumType(Status), StringType())),
}
status = Parameter(datatype=TupleOf(EnumType(Status), StringType()))
def initModule(self):
self._state = Enum('state', idle=1, switch_on=2, switch_off=3, ramp=4).idle
@ -202,21 +198,20 @@ class MagneticField(Drivable):
time.sleep(max(0.01, ts + loopdelay - time.time()))
self.log.error(self, 'main thread exited unexpectedly!')
def do_stop(self):
def stop(self):
self.write_target(self.read_value())
class CoilTemp(Readable):
"""a coil temperature
"""
parameters = {
'value': Override('Coil temperatur',
unit='K', datatype=FloatRange(), default=0,
),
'sensor': Parameter("Sensor number or calibration id",
datatype=StringType(), readonly=True,
),
}
value = Parameter('Coil temperatur',
unit='K', datatype=FloatRange(), default=0,
)
sensor = Parameter("Sensor number or calibration id",
datatype=StringType(), readonly=True,
)
def read_value(self):
return round(2.3 + random.random(), 3)
@ -225,18 +220,17 @@ class CoilTemp(Readable):
class SampleTemp(Drivable):
"""a sample temperature
"""
parameters = {
'value': Override('Sample temperature',
unit='K', datatype=FloatRange(), default=10,
),
'sensor': Parameter("Sensor number or calibration id",
datatype=StringType(), readonly=True,
),
'ramp': Parameter('moving speed in K/min',
datatype=FloatRange(0, 100), unit='K/min', default=0.1,
readonly=False,
),
}
value = Parameter('Sample temperature',
unit='K', datatype=FloatRange(), default=10,
)
sensor = Parameter("Sensor number or calibration id",
datatype=StringType(), readonly=True,
)
ramp = Parameter('moving speed in K/min',
datatype=FloatRange(0, 100), unit='K/min', default=0.1,
readonly=False,
)
def initModule(self):
_thread = threading.Thread(target=self._thread)
@ -272,20 +266,19 @@ class Label(Readable):
of several subdevices. used for demoing connections between
modules.
"""
parameters = {
'system': Parameter("Name of the magnet system",
datatype=StringType(), export=False,
),
'subdev_mf': Parameter("name of subdevice for magnet status",
datatype=StringType(), export=False,
),
'subdev_ts': Parameter("name of subdevice for sample temp",
datatype=StringType(), export=False,
),
'value': Override("final value of label string", default='',
datatype=StringType(),
),
}
system = Parameter("Name of the magnet system",
datatype=StringType(), export=False,
)
subdev_mf = Parameter("name of subdevice for magnet status",
datatype=StringType(), export=False,
)
subdev_ts = Parameter("name of subdevice for sample temp",
datatype=StringType(), export=False,
)
value = Parameter("final value of label string", default='',
datatype=StringType(),
)
def read_value(self):
strings = [self.system]
@ -317,29 +310,25 @@ class Label(Readable):
class DatatypesTest(Readable):
"""for demoing all datatypes
"""
parameters = {
'enum': Parameter('enum', datatype=EnumType(boo=None, faar=None, z=9),
readonly=False, default=1),
'tupleof': Parameter('tuple of int, float and str',
datatype=TupleOf(IntRange(), FloatRange(),
StringType()),
readonly=False, default=(1, 2.3, 'a')),
'arrayof': Parameter('array: 2..3 times bool',
datatype=ArrayOf(BoolType(), 2, 3),
readonly=False, default=[1, 0, 1]),
'intrange': Parameter('intrange', datatype=IntRange(2, 9),
readonly=False, default=4),
'floatrange': Parameter('floatrange', datatype=FloatRange(-1, 1),
readonly=False, default=0, ),
'struct': Parameter('struct(a=str, b=int, c=bool)',
datatype=StructOf(a=StringType(), b=IntRange(),
c=BoolType()),
),
}
enum = Parameter('enum', datatype=EnumType(boo=None, faar=None, z=9),
readonly=False, default=1)
tupleof = Parameter('tuple of int, float and str',
datatype=TupleOf(IntRange(), FloatRange(),
StringType()),
readonly=False, default=(1, 2.3, 'a'))
arrayof = Parameter('array: 2..3 times bool',
datatype=ArrayOf(BoolType(), 2, 3),
readonly=False, default=[1, 0, 1])
intrange = Parameter('intrange', datatype=IntRange(2, 9),
readonly=False, default=4)
floatrange = Parameter('floatrange', datatype=FloatRange(-1, 1),
readonly=False, default=0)
struct = Parameter('struct(a=str, b=int, c=bool)',
datatype=StructOf(a=StringType(), b=IntRange(),
c=BoolType()))
class ArrayTest(Readable):
parameters = {
"x": Parameter('value', datatype=ArrayOf(FloatRange(), 0, 100000),
default = 100000 * [0]),
}
x = Parameter('value', datatype=ArrayOf(FloatRange(), 0, 100000),
default=100000 * [0])

View File

@ -24,7 +24,7 @@
import random
from secop.datatypes import FloatRange, StringType
from secop.modules import Communicator, Drivable, Parameter, Readable, Override
from secop.modules import Communicator, Drivable, Parameter, Readable
from secop.params import Command
@ -45,11 +45,10 @@ class Heater(Drivable):
class name indicates it to be some heating element,
but the implementation may do anything
"""
parameters = {
'maxheaterpower': Parameter('maximum allowed heater power',
datatype=FloatRange(0, 100), unit='W',
),
}
maxheaterpower = Parameter('maximum allowed heater power',
datatype=FloatRange(0, 100), unit='W',
)
def read_value(self):
return round(100 * random.random(), 1)
@ -64,22 +63,21 @@ class Temp(Drivable):
class name indicates it to be some temperature controller,
but the implementation may do anything
"""
parameters = {
'sensor': Parameter(
"Sensor number or calibration id",
datatype=StringType(
8,
16),
readonly=True,
),
'target': Override(
"Target temperature",
default=300.0,
datatype=FloatRange(0),
readonly=False,
unit='K',
),
}
sensor = Parameter(
"Sensor number or calibration id",
datatype=StringType(
8,
16),
readonly=True,
)
target = Parameter(
"Target temperature",
default=300.0,
datatype=FloatRange(0),
readonly=False,
unit='K',
)
def read_value(self):
return round(100 * random.random(), 1)
@ -90,8 +88,8 @@ class Temp(Drivable):
class Lower(Communicator):
"""Communicator returning a lowercase version of the request"""
command = {
'communicate': Command('lowercase a string', argument=StringType(), result=StringType(), export='communicate'),
}
def do_communicate(self, request):
return str(request).lower()
@Command(argument=StringType(), result=StringType(), export='communicate')
def communicate(self, command):
"""lowercase a string"""
return str(command).lower()