fix several regressions from switching to accessibles

also include some basic tests now

Change-Id: Ia07892c03f4d72f5da307a79a9827f926940881d
Reviewed-on: https://forge.frm2.tum.de/review/18539
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Tested-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
Enrico Faulhaber
2018-07-26 17:53:00 +02:00
parent 5b273e36a7
commit b1f1653ebd
10 changed files with 283 additions and 80 deletions

View File

@ -81,27 +81,17 @@ class Module(object):
self.DISPATCHER = dispatcher
self.log = logger
self.name = modname
# make local copies of parameter objects
# they need to be individual per instance since we use them also
# to cache the current value + qualifiers...
params = {}
for k, v in list(self.parameters.items()):
entry = v.copy()
if '$' in entry.unit:
entry.unit = entry.unit.replace('$', self.parameters['value'].unit)
params[k] = entry
# do not re-use self.parameters as this is the same for all instances
self.parameters = params
# make local copies of properties
# handle module properties
# 1) make local copies of properties
# XXX: self.properties = self.properties.copy() ???
props = {}
for k, v in list(self.properties.items()):
props[k] = v
self.properties = props
# check and apply properties specified in cfgdict
# moduleproperties are to be specified as
# '.<propertyname>=<propertyvalue>'
# 2) check and apply properties specified in cfgdict
# specified as '.<propertyname> = <propertyvalue>'
for k, v in list(cfgdict.items()): # keep list() as dict may change during iter
if k[0] == '.':
if k[1:] in self.properties:
@ -109,42 +99,61 @@ class Module(object):
else:
raise ConfigError('Module %r has no property %r' %
(self.name, k[1:]))
# remove unset (default) module properties
# 3) remove unset (default) module properties
for k, v in list(self.properties.items()): # keep list() as dict may change during iter
if v is None:
del self.properties[k]
# MAGIC: derive automatic properties
# 4) set automatic properties
mycls = self.__class__
myclassname = '%s.%s' % (mycls.__module__, mycls.__name__)
self.properties['_implementation'] = myclassname
self.properties['interface_class'] = [
b.__name__ for b in mycls.__mro__ if b.__module__.startswith('secop.modules')]
# check and apply parameter_properties
# specified as '<paramname>.<propertyname> = <propertyvalue>'
# handle Features
# XXX: todo
# handle accessibles
# 1) make local copies of parameter objects
# they need to be individual per instance since we use them also
# to cache the current value + qualifiers...
accessibles = {}
for k, v in self.accessibles.items():
# make a copy of the Parameter/Command object
accessibles[k] = v.copy()
# do not re-use self.accessibles as this is the same for all instances
self.accessibles = accessibles
# 2) check and apply parameter_properties
# specified as '<paramname>.<propertyname> = <propertyvalue>'
for k, v in list(cfgdict.items()): # keep list() as dict may change during iter
if '.' in k[1:]:
paramname, propname = k.split('.', 1)
if paramname in self.parameters:
paramobj = self.parameters[paramname]
paramobj = self.accessibles.get(paramname, None)
if paramobj:
if propname == 'datatype':
paramobj.datatype = get_datatype(cfgdict.pop(k))
elif hasattr(paramobj, propname):
setattr(paramobj, propname, cfgdict.pop(k))
else:
raise ConfigError('Module %s: Parameter %r has not property %r!' %
(self.name, paramname, propname))
# check config for problems
# only accept remaining config items specified in parameters
# 3) check config for problems:
# only accept remaining config items specified in parameters
for k, v in cfgdict.items():
if k not in self.parameters:
if k not in self.accessibles:
raise ConfigError(
'Module %s:config Parameter %r '
'not unterstood! (use one of %s)' %
(self.name, k, ', '.join(self.parameters)))
(self.name, k, ', '.join(n for n,o in self.accessibles if isinstance(o, Parameter))))
# complain if a Parameter entry has no default value and
# is not specified in cfgdict
for k, v in self.parameters.items():
# 4) complain if a Parameter entry has no default value and
# is not specified in cfgdict
for k, v in self.accessibles.items():
if not isinstance(v, Parameter):
continue
if k not in cfgdict:
if v.default is unset_value and k != 'value':
# unset_value is the one single value you can not specify....
@ -154,16 +163,13 @@ class Module(object):
# assume default value was given
cfgdict[k] = v.default
# replace CLASS level Parameter objects with INSTANCE level ones
# self.parameters[k] = self.parameters[k].copy() # already done above...
# now 'apply' config:
# pass values through the datatypes and store as attributes
# 5) 'apply' config:
# pass values through the datatypes and store as attributes
for k, v in cfgdict.items():
if k == 'value':
continue
# apply datatype, complain if type does not fit
datatype = self.parameters[k].datatype
datatype = self.accessibles[k].datatype
try:
v = datatype.validate(v)
except (ValueError, TypeError):
@ -175,6 +181,14 @@ class Module(object):
# write to the hardware, if possible!
setattr(self, k, v)
# Adopt units AFTER applying the cfgdict
for k, v in self.accessibles.items():
if not isinstance(v, Parameter):
continue
if '$' in v.unit:
v.unit = v.unit.replace('$', self.accessibles['value'].unit)
def init(self):
# may be overriden in derived classes to init stuff
self.log.debug('empty init()')
@ -258,7 +272,9 @@ class Readable(Module):
def poll(self, nr=0):
# Just poll all parameters regularly where polling is enabled
for pname, pobj in self.parameters.items():
for pname, pobj in self.accessibles.items():
if not isinstance(pobj, Parameter):
continue
if not pobj.poll:
continue
if nr % abs(int(pobj.poll)) == 0:
@ -309,7 +325,9 @@ class Drivable(Writable):
# poll status first
stat = self.read_status(0)
fastpoll = stat[0] == self.Status.BUSY
for pname, pobj in self.parameters.items():
for pname, pobj in self.accessibles.items():
if not isinstance(pobj, Parameter):
continue
if not pobj.poll:
continue
if pname == 'status':