fix inheritance problem with mixin
- a mixin should not inherit from module then it has Parameters - Parameters in mixins must be complete, not just overrides - check precedence of read_<param> or handler Change-Id: I72d9355a1982770d1a99d9552a20330103c97edb
This commit is contained in:
parent
6538500881
commit
48230334af
@ -495,9 +495,12 @@ class SecopClient(ProxyClient):
|
|||||||
def _set_state(self, online, state=None):
|
def _set_state(self, online, state=None):
|
||||||
# treat reconnecting as online!
|
# treat reconnecting as online!
|
||||||
state = state or self.state
|
state = state or self.state
|
||||||
|
try:
|
||||||
self.callback(None, 'nodeStateChange', online, state)
|
self.callback(None, 'nodeStateChange', online, state)
|
||||||
for mname in self.modules:
|
for mname in self.modules:
|
||||||
self.callback(mname, 'nodeStateChange', online, state)
|
self.callback(mname, 'nodeStateChange', online, state)
|
||||||
|
except Exception as e:
|
||||||
|
self.log.error('ERROR in nodeStateCallback %s', e)
|
||||||
# set online attribute after callbacks -> callback may check for old state
|
# set online attribute after callbacks -> callback may check for old state
|
||||||
self.online = online
|
self.online = online
|
||||||
self.state = state
|
self.state = state
|
||||||
|
@ -98,14 +98,17 @@ def clamp(_min, value, _max):
|
|||||||
|
|
||||||
|
|
||||||
def get_class(spec):
|
def get_class(spec):
|
||||||
"""loads a class given by string in dotted notaion (as python would do)"""
|
"""loads a class given by string in dotted notation (as python would do)"""
|
||||||
modname, classname = spec.rsplit('.', 1)
|
modname, classname = spec.rsplit('.', 1)
|
||||||
if modname.startswith('secop'):
|
if modname.startswith('secop'):
|
||||||
module = importlib.import_module(modname)
|
module = importlib.import_module(modname)
|
||||||
else:
|
else:
|
||||||
# rarely needed by now....
|
# rarely needed by now....
|
||||||
module = importlib.import_module('secop.' + modname)
|
module = importlib.import_module('secop.' + modname)
|
||||||
|
try:
|
||||||
return getattr(module, classname)
|
return getattr(module, classname)
|
||||||
|
except AttributeError:
|
||||||
|
raise AttributeError('no such class') from None
|
||||||
|
|
||||||
|
|
||||||
def mkthread(func, *args, **kwds):
|
def mkthread(func, *args, **kwds):
|
||||||
|
@ -93,9 +93,12 @@ class HasAccessibles(HasProperties):
|
|||||||
rfunc_handler = pobj.handler.get_read_func(cls, pname) if pobj.handler else None
|
rfunc_handler = pobj.handler.get_read_func(cls, pname) if pobj.handler else None
|
||||||
wrapped = hasattr(rfunc, '__wrapped__')
|
wrapped = hasattr(rfunc, '__wrapped__')
|
||||||
if rfunc_handler:
|
if rfunc_handler:
|
||||||
if rfunc and not wrapped:
|
if 'read_' + pname in cls.__dict__:
|
||||||
|
if pname in cls.__dict__:
|
||||||
raise ProgrammingError("parameter '%s' can not have a handler "
|
raise ProgrammingError("parameter '%s' can not have a handler "
|
||||||
"and read_%s" % (pname, pname))
|
"and read_%s" % (pname, pname))
|
||||||
|
# read_<pname> overwrites inherited handler
|
||||||
|
else:
|
||||||
rfunc = rfunc_handler
|
rfunc = rfunc_handler
|
||||||
wrapped = False
|
wrapped = False
|
||||||
|
|
||||||
|
@ -101,10 +101,10 @@ class Parameter(Accessible):
|
|||||||
|
|
||||||
description = Property(
|
description = Property(
|
||||||
'mandatory description of the parameter', TextType(),
|
'mandatory description of the parameter', TextType(),
|
||||||
extname='description', mandatory=True)
|
extname='description', mandatory=True, export='always')
|
||||||
datatype = Property(
|
datatype = Property(
|
||||||
'datatype of the Parameter (SECoP datainfo)', DataTypeType(),
|
'datatype of the Parameter (SECoP datainfo)', DataTypeType(),
|
||||||
extname='datainfo', mandatory=True)
|
extname='datainfo', mandatory=True, export='always')
|
||||||
readonly = Property(
|
readonly = Property(
|
||||||
'not changeable via SECoP (default True)', BoolType(),
|
'not changeable via SECoP (default True)', BoolType(),
|
||||||
extname='readonly', default=True, export='always')
|
extname='readonly', default=True, export='always')
|
||||||
@ -283,7 +283,7 @@ class Command(Accessible):
|
|||||||
|
|
||||||
description = Property(
|
description = Property(
|
||||||
'description of the Command', TextType(),
|
'description of the Command', TextType(),
|
||||||
extname='description', export=True, mandatory=True)
|
extname='description', export='always', mandatory=True)
|
||||||
group = Property(
|
group = Property(
|
||||||
'optional command group of the command.', StringType(),
|
'optional command group of the command.', StringType(),
|
||||||
extname='group', export=True, default='')
|
extname='group', export=True, default='')
|
||||||
|
@ -228,11 +228,15 @@ class Server:
|
|||||||
errors.append(self.unknown_options(cls, opts))
|
errors.append(self.unknown_options(cls, opts))
|
||||||
self.modules = OrderedDict()
|
self.modules = OrderedDict()
|
||||||
badclass = None
|
badclass = None
|
||||||
|
failed = set() # python modules failed to load
|
||||||
self.lastError = None
|
self.lastError = None
|
||||||
for modname, options in self.module_cfg.items():
|
for modname, options in self.module_cfg.items():
|
||||||
opts = dict(options)
|
opts = dict(options)
|
||||||
try:
|
try:
|
||||||
classname = opts.pop('class')
|
classname = opts.pop('class')
|
||||||
|
pymodule = classname.rpartition('.')[0]
|
||||||
|
if pymodule in failed:
|
||||||
|
continue
|
||||||
cls = get_class(classname)
|
cls = get_class(classname)
|
||||||
modobj = cls(modname, self.log.getChild(modname), opts, self)
|
modobj = cls(modname, self.log.getChild(modname), opts, self)
|
||||||
# all used args should be popped from opts!
|
# all used args should be popped from opts!
|
||||||
@ -242,8 +246,12 @@ class Server:
|
|||||||
except ConfigError as e:
|
except ConfigError as e:
|
||||||
errors.append(str(e))
|
errors.append(str(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
if str(e) == 'no such class':
|
||||||
|
errors.append('%s not found' % classname)
|
||||||
|
else:
|
||||||
|
failed.add(pymodule)
|
||||||
badclass = classname
|
badclass = classname
|
||||||
errors.append('error while loading %s' % badclass)
|
errors.append('error importing %s' % pymodule)
|
||||||
|
|
||||||
poll_table = dict()
|
poll_table = dict()
|
||||||
# all objs created, now start them up and interconnect
|
# all objs created, now start them up and interconnect
|
||||||
|
@ -43,9 +43,17 @@ def make_cvt_list(dt, tail=''):
|
|||||||
else:
|
else:
|
||||||
return [] # ArrayType, BlobType and TextType are ignored: too much data, probably not used
|
return [] # ArrayType, BlobType and TextType are ignored: too much data, probably not used
|
||||||
result = []
|
result = []
|
||||||
|
print('START', dt)
|
||||||
for subkey, elmtype in items:
|
for subkey, elmtype in items:
|
||||||
|
print('MAKE_CVT_LIST', subkey, elmtype)
|
||||||
for fun, tail_, opts in make_cvt_list(elmtype, '%s.%s' % (tail, subkey)):
|
for fun, tail_, opts in make_cvt_list(elmtype, '%s.%s' % (tail, subkey)):
|
||||||
result.append((lambda v, k=subkey, f=fun: f(v[k]), tail_, opts))
|
def conv(value, key=subkey, func=fun):
|
||||||
|
try:
|
||||||
|
return value[key]
|
||||||
|
except KeyError: # can not use value.get() because value might be a list
|
||||||
|
return None
|
||||||
|
result.append((conv, tail_, opts))
|
||||||
|
print('END', result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@ -128,8 +128,9 @@ class Main(Communicator):
|
|||||||
return data # return data as string
|
return data # return data as string
|
||||||
|
|
||||||
|
|
||||||
class PpmsBase(HasIodev, Readable):
|
class PpmsMixin:
|
||||||
"""common base for all ppms modules"""
|
"""common base for all ppms modules"""
|
||||||
|
|
||||||
iodev = Attached()
|
iodev = Attached()
|
||||||
|
|
||||||
pollerClass = Poller
|
pollerClass = Poller
|
||||||
@ -139,7 +140,7 @@ class PpmsBase(HasIodev, Readable):
|
|||||||
|
|
||||||
# as this pollinterval affects only the polling of settings
|
# as this pollinterval affects only the polling of settings
|
||||||
# it would be confusing to export it.
|
# it would be confusing to export it.
|
||||||
pollinterval = Parameter(export=False)
|
pollinterval = Parameter('', FloatRange(), needscfg=False, export=False)
|
||||||
|
|
||||||
def initModule(self):
|
def initModule(self):
|
||||||
self._iodev.register(self)
|
self._iodev.register(self)
|
||||||
@ -172,7 +173,7 @@ class PpmsBase(HasIodev, Readable):
|
|||||||
self.status = (self.Status.IDLE, '')
|
self.status = (self.Status.IDLE, '')
|
||||||
|
|
||||||
|
|
||||||
class Channel(PpmsBase):
|
class Channel(PpmsMixin, HasIodev, Readable):
|
||||||
"""channel base class"""
|
"""channel base class"""
|
||||||
|
|
||||||
value = Parameter('main value of channels', poll=True)
|
value = Parameter('main value of channels', poll=True)
|
||||||
@ -270,7 +271,7 @@ class BridgeChannel(Channel):
|
|||||||
return self.no, 0, 0, change.dcflag, change.readingmode, 0
|
return self.no, 0, 0, change.dcflag, change.readingmode, 0
|
||||||
|
|
||||||
|
|
||||||
class Level(PpmsBase):
|
class Level(PpmsMixin, HasIodev, Readable):
|
||||||
"""helium level"""
|
"""helium level"""
|
||||||
|
|
||||||
level = IOHandler('level', 'LEVEL?', '%g,%d')
|
level = IOHandler('level', 'LEVEL?', '%g,%d')
|
||||||
@ -293,7 +294,7 @@ class Level(PpmsBase):
|
|||||||
return dict(value=level, status=(self.Status.IDLE, ''))
|
return dict(value=level, status=(self.Status.IDLE, ''))
|
||||||
|
|
||||||
|
|
||||||
class Chamber(PpmsBase, Drivable):
|
class Chamber(PpmsMixin, HasIodev, Drivable):
|
||||||
"""sample chamber handling
|
"""sample chamber handling
|
||||||
|
|
||||||
value is an Enum, which is redundant with the status text
|
value is an Enum, which is redundant with the status text
|
||||||
@ -368,7 +369,7 @@ class Chamber(PpmsBase, Drivable):
|
|||||||
return (change.target,)
|
return (change.target,)
|
||||||
|
|
||||||
|
|
||||||
class Temp(PpmsBase, Drivable):
|
class Temp(PpmsMixin, HasIodev, Drivable):
|
||||||
"""temperature"""
|
"""temperature"""
|
||||||
|
|
||||||
temp = IOHandler('temp', 'TEMP?', '%g,%g,%d')
|
temp = IOHandler('temp', 'TEMP?', '%g,%g,%d')
|
||||||
@ -553,7 +554,7 @@ class Temp(PpmsBase, Drivable):
|
|||||||
self._stopped = True
|
self._stopped = True
|
||||||
|
|
||||||
|
|
||||||
class Field(PpmsBase, Drivable):
|
class Field(PpmsMixin, HasIodev, Drivable):
|
||||||
"""magnetic field"""
|
"""magnetic field"""
|
||||||
|
|
||||||
field = IOHandler('field', 'FIELD?', '%g,%g,%d,%d')
|
field = IOHandler('field', 'FIELD?', '%g,%g,%d,%d')
|
||||||
@ -562,6 +563,7 @@ class Field(PpmsBase, Drivable):
|
|||||||
PREPARED=150,
|
PREPARED=150,
|
||||||
PREPARING=340,
|
PREPARING=340,
|
||||||
RAMPING=370,
|
RAMPING=370,
|
||||||
|
STABILIZING=380,
|
||||||
FINALIZING=390,
|
FINALIZING=390,
|
||||||
)
|
)
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
@ -584,7 +586,7 @@ class Field(PpmsBase, Drivable):
|
|||||||
2: (Status.PREPARING, 'switch warming'),
|
2: (Status.PREPARING, 'switch warming'),
|
||||||
3: (Status.FINALIZING, 'switch cooling'),
|
3: (Status.FINALIZING, 'switch cooling'),
|
||||||
4: (Status.IDLE, 'driven stable'),
|
4: (Status.IDLE, 'driven stable'),
|
||||||
5: (Status.FINALIZING, 'driven final'),
|
5: (Status.STABILIZING, 'driven final'),
|
||||||
6: (Status.RAMPING, 'charging'),
|
6: (Status.RAMPING, 'charging'),
|
||||||
7: (Status.RAMPING, 'discharging'),
|
7: (Status.RAMPING, 'discharging'),
|
||||||
8: (Status.ERROR, 'current error'),
|
8: (Status.ERROR, 'current error'),
|
||||||
@ -690,7 +692,7 @@ class Field(PpmsBase, Drivable):
|
|||||||
self._stopped = True
|
self._stopped = True
|
||||||
|
|
||||||
|
|
||||||
class Position(PpmsBase, Drivable):
|
class Position(PpmsMixin, HasIodev, Drivable):
|
||||||
"""rotator position"""
|
"""rotator position"""
|
||||||
|
|
||||||
move = IOHandler('move', 'MOVE?', '%g,%g,%g')
|
move = IOHandler('move', 'MOVE?', '%g,%g,%g')
|
||||||
|
@ -359,6 +359,8 @@ class SeaModule(Module):
|
|||||||
else:
|
else:
|
||||||
pobj = Parameter(**kwds)
|
pobj = Parameter(**kwds)
|
||||||
datatype = pobj.datatype
|
datatype = pobj.datatype
|
||||||
|
if name == 'cc' and key == 'value':
|
||||||
|
print('cc.value: %r %r' % (kwds, pobj))
|
||||||
attributes[key] = pobj
|
attributes[key] = pobj
|
||||||
if not hasattr(cls, 'read_' + key):
|
if not hasattr(cls, 'read_' + key):
|
||||||
def rfunc(self, cmd='hval /sics/%s/%s' % (sea_object, path)):
|
def rfunc(self, cmd='hval /sics/%s/%s' % (sea_object, path)):
|
||||||
|
@ -38,12 +38,14 @@ class TestCmd(Module):
|
|||||||
result=StringType())
|
result=StringType())
|
||||||
def arg(self, *arg):
|
def arg(self, *arg):
|
||||||
"""5 args"""
|
"""5 args"""
|
||||||
|
self.tuple = arg
|
||||||
return repr(arg)
|
return repr(arg)
|
||||||
|
|
||||||
@Command(argument=StructOf(a=StringType(), b=FloatRange(), c=BoolType(), optional=['b']),
|
@Command(argument=StructOf(a=StringType(), b=FloatRange(), c=BoolType(), optional=['b']),
|
||||||
result=StringType())
|
result=StringType())
|
||||||
def keyed(self, **arg):
|
def keyed(self, **arg):
|
||||||
"""keyworded arg"""
|
"""keyworded arg"""
|
||||||
|
self.struct = arg
|
||||||
return repr(arg)
|
return repr(arg)
|
||||||
|
|
||||||
@Command(argument=FloatRange(), result=StringType())
|
@Command(argument=FloatRange(), result=StringType())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user