added 'export_as' for parameters and commands
the export argument of an accessible allows now to specify an other external name than the attribute used internally. export=True (default, use defined name, or prefix with '_' when name not in predefined list) export=False (do not export) export=<any string> (special cases only) Change-Id: I6c6669cd502d9d6fd3aa40091673e5554fd961bd Reviewed-on: https://forge.frm2.tum.de/review/19664 Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de> Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
parent
ef27fd1b54
commit
feba5a1400
@ -28,12 +28,12 @@ import time
|
|||||||
|
|
||||||
from secop.datatypes import (EnumType, FloatRange, StringType, TupleOf,
|
from secop.datatypes import (EnumType, FloatRange, StringType, TupleOf,
|
||||||
get_datatype)
|
get_datatype)
|
||||||
from secop.errors import ConfigError
|
from secop.errors import ConfigError, ProgrammingError
|
||||||
from secop.lib import (formatException, formatExtendedStack, mkthread,
|
from secop.lib import (formatException, formatExtendedStack, mkthread,
|
||||||
unset_value)
|
unset_value)
|
||||||
from secop.lib.enum import Enum
|
from secop.lib.enum import Enum
|
||||||
from secop.metaclass import ModuleMeta, add_metaclass
|
from secop.metaclass import ModuleMeta, add_metaclass
|
||||||
from secop.params import Command, Override, Parameter
|
from secop.params import Command, Override, Parameter, PREDEFINED_ACCESSIBLES
|
||||||
|
|
||||||
# XXX: connect with 'protocol'-Modules.
|
# XXX: connect with 'protocol'-Modules.
|
||||||
# Idea: every Module defined herein is also a 'protocol'-Module,
|
# Idea: every Module defined herein is also a 'protocol'-Module,
|
||||||
@ -41,8 +41,6 @@ from secop.params import Command, Override, Parameter
|
|||||||
# from these base classes (how to do this?)
|
# from these base classes (how to do this?)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@add_metaclass(ModuleMeta)
|
@add_metaclass(ModuleMeta)
|
||||||
class Module(object):
|
class Module(object):
|
||||||
"""Basic Module
|
"""Basic Module
|
||||||
@ -74,6 +72,7 @@ class Module(object):
|
|||||||
'visibility': None, # XXX: ????
|
'visibility': None, # XXX: ????
|
||||||
# what else?
|
# what else?
|
||||||
}
|
}
|
||||||
|
|
||||||
# properties, parameter and commands are auto-merged upon subclassing
|
# properties, parameter and commands are auto-merged upon subclassing
|
||||||
parameters = {}
|
parameters = {}
|
||||||
commands = {}
|
commands = {}
|
||||||
@ -126,11 +125,27 @@ class Module(object):
|
|||||||
# they need to be individual per instance since we use them also
|
# they need to be individual per instance since we use them also
|
||||||
# to cache the current value + qualifiers...
|
# to cache the current value + qualifiers...
|
||||||
accessibles = {}
|
accessibles = {}
|
||||||
for k, v in self.accessibles.items():
|
# conversion from exported names to internal attribute names
|
||||||
|
accessiblename2attr = {}
|
||||||
|
for aname, aobj in self.accessibles.items():
|
||||||
# make a copy of the Parameter/Command object
|
# make a copy of the Parameter/Command object
|
||||||
accessibles[k] = v.copy()
|
aobj = aobj.copy()
|
||||||
|
if aobj.export:
|
||||||
|
if aobj.export is True:
|
||||||
|
predefined_obj = PREDEFINED_ACCESSIBLES.get(aname, None)
|
||||||
|
if predefined_obj:
|
||||||
|
if isinstance(aobj, predefined_obj):
|
||||||
|
aobj.export = aname
|
||||||
|
else:
|
||||||
|
raise ProgrammingError("can not use '%s' as name of a %s" %
|
||||||
|
(aname, aobj.__class__.__name__))
|
||||||
|
else: # create custom parameter
|
||||||
|
aobj.export = '_' + aname
|
||||||
|
accessiblename2attr[aobj.export] = aname
|
||||||
|
accessibles[aname] = aobj
|
||||||
# do not re-use self.accessibles as this is the same for all instances
|
# do not re-use self.accessibles as this is the same for all instances
|
||||||
self.accessibles = accessibles
|
self.accessibles = accessibles
|
||||||
|
self.accessiblename2attr = accessiblename2attr
|
||||||
|
|
||||||
# 2) check and apply parameter_properties
|
# 2) check and apply parameter_properties
|
||||||
# specified as '<paramname>.<propertyname> = <propertyvalue>'
|
# specified as '<paramname>.<propertyname> = <propertyvalue>'
|
||||||
|
@ -29,6 +29,7 @@ from secop.lib import unset_value
|
|||||||
|
|
||||||
EVENT_ONLY_ON_CHANGED_VALUES = False
|
EVENT_ONLY_ON_CHANGED_VALUES = False
|
||||||
|
|
||||||
|
|
||||||
class CountedObj(object):
|
class CountedObj(object):
|
||||||
ctr = [0]
|
ctr = [0]
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -36,6 +37,7 @@ class CountedObj(object):
|
|||||||
cl[0] += 1
|
cl[0] += 1
|
||||||
self.ctr = cl[0]
|
self.ctr = cl[0]
|
||||||
|
|
||||||
|
|
||||||
class Accessible(CountedObj):
|
class Accessible(CountedObj):
|
||||||
'''abstract base class for Parameter and Command'''
|
'''abstract base class for Parameter and Command'''
|
||||||
|
|
||||||
@ -73,6 +75,7 @@ class Accessible(CountedObj):
|
|||||||
raise ProgrammingError('can not overrride property name %s' % name)
|
raise ProgrammingError('can not overrride property name %s' % name)
|
||||||
cls.valid_properties[name] = external
|
cls.valid_properties[name] = external
|
||||||
|
|
||||||
|
|
||||||
class Parameter(Accessible):
|
class Parameter(Accessible):
|
||||||
"""storage for Parameter settings + value + qualifiers
|
"""storage for Parameter settings + value + qualifiers
|
||||||
|
|
||||||
@ -219,3 +222,23 @@ class Command(Accessible):
|
|||||||
def for_export(self):
|
def for_export(self):
|
||||||
# used for serialisation only
|
# used for serialisation only
|
||||||
return self.exported_properties()
|
return self.exported_properties()
|
||||||
|
|
||||||
|
# list of predefined accessibles with their type
|
||||||
|
PREDEFINED_ACCESSIBLES = dict(
|
||||||
|
value = Parameter,
|
||||||
|
status = Parameter,
|
||||||
|
target = Parameter,
|
||||||
|
pollinterval = Parameter,
|
||||||
|
ramp = Parameter,
|
||||||
|
user_ramp = Parameter,
|
||||||
|
setpoint = Parameter,
|
||||||
|
time_to_target = Parameter,
|
||||||
|
unit = Parameter, # reserved name
|
||||||
|
loglevel = Parameter, # reserved name
|
||||||
|
mode = Parameter, # reserved name
|
||||||
|
stop = Command,
|
||||||
|
reset = Command,
|
||||||
|
go = Command,
|
||||||
|
abort = Command,
|
||||||
|
shutdown = Command,
|
||||||
|
)
|
||||||
|
@ -100,23 +100,19 @@ class Dispatcher(object):
|
|||||||
def announce_update(self, moduleobj, pname, pobj):
|
def announce_update(self, moduleobj, pname, pobj):
|
||||||
"""called by modules param setters to notify subscribers of new values
|
"""called by modules param setters to notify subscribers of new values
|
||||||
"""
|
"""
|
||||||
msg = (EVENTREPLY, u'%s:%s' % (moduleobj.name, pname), [pobj.export_value(), dict(t=pobj.timestamp)])
|
# argument pname is no longer used here - should we remove it?
|
||||||
|
msg = (EVENTREPLY, u'%s:%s' % (moduleobj.name, pobj.export),
|
||||||
|
[pobj.export_value(), dict(t=pobj.timestamp)])
|
||||||
self.broadcast_event(msg)
|
self.broadcast_event(msg)
|
||||||
|
|
||||||
def subscribe(self, conn, modulename, pname=None):
|
def subscribe(self, conn, eventname):
|
||||||
eventname = modulename
|
|
||||||
if pname:
|
|
||||||
eventname = u'%s:%s' % (modulename, pname)
|
|
||||||
self._subscriptions.setdefault(eventname, set()).add(conn)
|
self._subscriptions.setdefault(eventname, set()).add(conn)
|
||||||
|
|
||||||
def unsubscribe(self, conn, modulename, pname=None):
|
def unsubscribe(self, conn, eventname):
|
||||||
if pname:
|
if not ':' in eventname:
|
||||||
eventname = u'%s:%s' % (modulename, pname)
|
|
||||||
else:
|
|
||||||
eventname = modulename
|
|
||||||
# also remove 'more specific' subscriptions
|
# also remove 'more specific' subscriptions
|
||||||
for k, v in self._subscriptions.items():
|
for k, v in self._subscriptions.items():
|
||||||
if k.startswith(u'%s:' % modulename):
|
if k.startswith(u'%s:' % eventname):
|
||||||
v.discard(conn)
|
v.discard(conn)
|
||||||
if eventname in self._subscriptions:
|
if eventname in self._subscriptions:
|
||||||
self._subscriptions[eventname].discard(conn)
|
self._subscriptions[eventname].discard(conn)
|
||||||
@ -166,9 +162,9 @@ class Dispatcher(object):
|
|||||||
if modulename in self._export:
|
if modulename in self._export:
|
||||||
# omit export=False params!
|
# omit export=False params!
|
||||||
res = []
|
res = []
|
||||||
for aname, aobj in self.get_module(modulename).accessibles.items():
|
for aobj in self.get_module(modulename).accessibles.values():
|
||||||
if aobj.export:
|
if aobj.export:
|
||||||
res.append([aname, aobj.for_export()])
|
res.append([aobj.export, aobj.for_export()])
|
||||||
self.log.debug(u'list accessibles for module %s -> %r' %
|
self.log.debug(u'list accessibles for module %s -> %r' %
|
||||||
(modulename, res))
|
(modulename, res))
|
||||||
return res
|
return res
|
||||||
@ -218,11 +214,12 @@ class Dispatcher(object):
|
|||||||
# XXX: pipe through cmdspec.datatype.result ?
|
# XXX: pipe through cmdspec.datatype.result ?
|
||||||
return res, dict(t=currenttime())
|
return res, dict(t=currenttime())
|
||||||
|
|
||||||
def _setParameterValue(self, modulename, pname, value):
|
def _setParameterValue(self, modulename, exportedname, value):
|
||||||
moduleobj = self.get_module(modulename)
|
moduleobj = self.get_module(modulename)
|
||||||
if moduleobj is None:
|
if moduleobj is None:
|
||||||
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
|
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
|
||||||
|
|
||||||
|
pname = moduleobj.accessiblename2attr.get(exportedname, None)
|
||||||
pobj = moduleobj.accessibles.get(pname, None)
|
pobj = moduleobj.accessibles.get(pname, None)
|
||||||
if pobj is None or not isinstance(pobj, Parameter):
|
if pobj is None or not isinstance(pobj, Parameter):
|
||||||
raise NoSuchParameterError('Module has no such parameter on this SEC-Node!')
|
raise NoSuchParameterError('Module has no such parameter on this SEC-Node!')
|
||||||
@ -239,11 +236,12 @@ class Dispatcher(object):
|
|||||||
return pobj.export_value(), dict(t=pobj.timestamp)
|
return pobj.export_value(), dict(t=pobj.timestamp)
|
||||||
return pobj.export_value(), {}
|
return pobj.export_value(), {}
|
||||||
|
|
||||||
def _getParameterValue(self, modulename, pname):
|
def _getParameterValue(self, modulename, exportedname):
|
||||||
moduleobj = self.get_module(modulename)
|
moduleobj = self.get_module(modulename)
|
||||||
if moduleobj is None:
|
if moduleobj is None:
|
||||||
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
|
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
|
||||||
|
|
||||||
|
pname = moduleobj.accessiblename2attr.get(exportedname, None)
|
||||||
pobj = moduleobj.accessibles.get(pname, None)
|
pobj = moduleobj.accessibles.get(pname, None)
|
||||||
if pobj is None or not isinstance(pobj, Parameter):
|
if pobj is None or not isinstance(pobj, Parameter):
|
||||||
raise NoSuchParameterError('Module has no such parameter on this SEC-Node!')
|
raise NoSuchParameterError('Module has no such parameter on this SEC-Node!')
|
||||||
@ -324,16 +322,18 @@ class Dispatcher(object):
|
|||||||
if data:
|
if data:
|
||||||
raise ProtocolError('activate request don\'t take data!')
|
raise ProtocolError('activate request don\'t take data!')
|
||||||
if specifier:
|
if specifier:
|
||||||
modulename, pname = specifier, None
|
modulename, exportedname = specifier, None
|
||||||
if ':' in specifier:
|
if ':' in specifier:
|
||||||
modulename, pname = specifier.split(u':', 1)
|
modulename, exportedname = specifier.split(u':', 1)
|
||||||
if modulename not in self._export:
|
if modulename not in self._export:
|
||||||
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
|
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
|
||||||
if pname and pname not in self.get_module(modulename).accessibles:
|
moduleobj = self.get_module(modulename)
|
||||||
|
pname = moduleobj.accessiblename2attr.get(exportedname, True)
|
||||||
|
if pname and pname not in moduleobj.accessibles:
|
||||||
# what if we try to subscribe a command here ???
|
# what if we try to subscribe a command here ???
|
||||||
raise NoSuchParameterError('Module has no such parameter on this SEC-Node!')
|
raise NoSuchParameterError('Module has no such parameter on this SEC-Node!')
|
||||||
# activate only ONE module
|
# activate only ONE item (module or module:parameter)
|
||||||
self.subscribe(conn, modulename, pname)
|
self.subscribe(conn, specifier)
|
||||||
modules = [(modulename, pname)]
|
modules = [(modulename, pname)]
|
||||||
else:
|
else:
|
||||||
# activate all modules
|
# activate all modules
|
||||||
@ -346,17 +346,17 @@ class Dispatcher(object):
|
|||||||
moduleobj = self._modules.get(modulename, None)
|
moduleobj = self._modules.get(modulename, None)
|
||||||
if pname:
|
if pname:
|
||||||
pobj = moduleobj.accessibles[pname]
|
pobj = moduleobj.accessibles[pname]
|
||||||
updmsg = (EVENTREPLY, u'%s:%s' % (modulename, pname),
|
updmsg = (EVENTREPLY, u'%s:%s' % (modulename, pobj.export),
|
||||||
[pobj.export_value(), dict(t=pobj.timestamp)])
|
[pobj.export_value(), dict(t=pobj.timestamp)])
|
||||||
conn.queue_async_reply(updmsg)
|
conn.queue_async_reply(updmsg)
|
||||||
continue
|
continue
|
||||||
for pname, pobj in moduleobj.accessibles.items(): # pylint: disable=redefined-outer-name
|
for pobj in moduleobj.accessibles.values():
|
||||||
if not isinstance(pobj, Parameter):
|
if not isinstance(pobj, Parameter):
|
||||||
continue
|
continue
|
||||||
if not pobj.export: # XXX: handle export_as cases!
|
if not pobj.export:
|
||||||
continue
|
continue
|
||||||
# can not use announce_update here, as this will send to all clients
|
# can not use announce_update here, as this will send to all clients
|
||||||
updmsg = (EVENTREPLY, u'%s:%s' % (modulename, pname),
|
updmsg = (EVENTREPLY, u'%s:%s' % (modulename, pobj.export),
|
||||||
[pobj.export_value(), dict(t=pobj.timestamp)])
|
[pobj.export_value(), dict(t=pobj.timestamp)])
|
||||||
conn.queue_async_reply(updmsg)
|
conn.queue_async_reply(updmsg)
|
||||||
return (ENABLEEVENTSREPLY, specifier, None) if specifier else (ENABLEEVENTSREPLY, None, None)
|
return (ENABLEEVENTSREPLY, specifier, None) if specifier else (ENABLEEVENTSREPLY, None, None)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user