Only send async updates for exported params

Change-Id: I466d99bf0234529ed58fa8045af83a0da44f96f7
This commit is contained in:
Enrico Faulhaber 2017-09-11 15:22:13 +02:00
parent b26b64032e
commit 9a402857f3
2 changed files with 29 additions and 26 deletions

View File

@ -19,10 +19,10 @@
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de> # Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
# #
# ***************************************************************************** # *****************************************************************************
"""Define Baseclasses for real devices implemented in the server""" """Define Baseclasses for real Modules implemented in the server"""
# XXX: connect with 'protocol'-Devices. # XXX: connect with 'protocol'-Modules.
# Idea: every Device defined herein is also a 'protocol'-device, # Idea: every Module defined herein is also a 'protocol'-Module,
# all others MUST derive from those, the 'interface'-class is still derived # all others MUST derive from those, the 'interface'-class is still derived
# from these base classes (how to do this?) # from these base classes (how to do this?)
@ -165,7 +165,7 @@ class CMD(object):
# Meta class # Meta class
# warning: MAGIC! # warning: MAGIC!
class DeviceMeta(type): class ModuleMeta(type):
def __new__(mcs, name, bases, attrs): def __new__(mcs, name, bases, attrs):
newtype = type.__new__(mcs, name, bases, attrs) newtype = type.__new__(mcs, name, bases, attrs)
@ -260,6 +260,7 @@ class DeviceMeta(type):
if not EVENT_ONLY_ON_CHANGED_VALUES or (value != pobj.value): if not EVENT_ONLY_ON_CHANGED_VALUES or (value != pobj.value):
pobj.value = value pobj.value = value
# also send notification # also send notification
if self.PARAMS[pname].export:
self.log.debug('%s is now %r' % (pname, value)) self.log.debug('%s is now %r' % (pname, value))
self.DISPATCHER.announce_update(self, pname, pobj) self.DISPATCHER.announce_update(self, pname, pobj)
@ -283,9 +284,9 @@ class DeviceMeta(type):
return newtype return newtype
# Basic device class # Basic module class
# #
# within devices, parameters should only be addressed as self.<pname> # within Modules, parameters should only be addressed as self.<pname>
# i.e. self.value, self.target etc... # i.e. self.value, self.target etc...
# these are accesses to the cached version. # these are accesses to the cached version.
# they can also be written to # they can also be written to
@ -293,9 +294,9 @@ class DeviceMeta(type):
# if you want to 'update from the hardware', call self.read_<pname> # if you want to 'update from the hardware', call self.read_<pname>
# the return value of this method will be used as the new cached value and # the return value of this method will be used as the new cached value and
# be returned. # be returned.
class Device(object): class Module(object):
"""Basic Device, doesn't do much""" """Basic Module, doesn't do much"""
__metaclass__ = DeviceMeta __metaclass__ = ModuleMeta
# static PROPERTIES, definitions in derived classes should overwrite earlier ones. # static PROPERTIES, definitions in derived classes should overwrite earlier ones.
# how to configure some stuff which makes sense to take from configfile??? # how to configure some stuff which makes sense to take from configfile???
PROPERTIES = { PROPERTIES = {
@ -342,10 +343,10 @@ class Device(object):
# derive automatic properties # derive automatic properties
mycls = self.__class__ mycls = self.__class__
myclassname = '%s.%s' % (mycls.__module__, mycls.__name__) myclassname = '%s.%s' % (mycls.__module__, mycls.__name__)
self.PROPERTIES['implementation'] = myclassname self.PROPERTIES['_implementation'] = myclassname
self.PROPERTIES['interfaces'] = [ self.PROPERTIES['interface_class'] = [
b.__name__ for b in mycls.__mro__ if b.__module__.startswith('secop.modules')] b.__name__ for b in mycls.__mro__ if b.__module__.startswith('secop.modules')]
self.PROPERTIES['interface'] = self.PROPERTIES['interfaces'][0] #self.PROPERTIES['interface'] = self.PROPERTIES['interfaces'][0]
# remove unset (default) module properties # remove unset (default) module properties
for k, v in self.PROPERTIES.items(): for k, v in self.PROPERTIES.items():
@ -370,7 +371,7 @@ class Device(object):
for k, v in cfgdict.items(): for k, v in cfgdict.items():
if k not in self.PARAMS: if k not in self.PARAMS:
raise ConfigError( raise ConfigError(
'Device %s:config Parameter %r ' 'Module %s:config Parameter %r '
'not unterstood! (use on of %r)' % 'not unterstood! (use on of %r)' %
(self.name, k, self.PARAMS.keys())) (self.name, k, self.PARAMS.keys()))
# complain if a PARAM entry has no default value and # complain if a PARAM entry has no default value and
@ -379,7 +380,7 @@ class Device(object):
if k not in cfgdict: if k not in cfgdict:
if v.default is Ellipsis and k != 'value': if v.default is Ellipsis and k != 'value':
# Ellipsis is the one single value you can not specify.... # Ellipsis is the one single value you can not specify....
raise ConfigError('Device %s: Parameter %r has no default ' raise ConfigError('Module %s: Parameter %r has no default '
'value and was not given in config!' % 'value and was not given in config!' %
(self.name, k)) (self.name, k))
# assume default value was given # assume default value was given
@ -402,7 +403,7 @@ class Device(object):
except (ValueError, TypeError) as e: except (ValueError, TypeError) as e:
self.log.exception(formatExtendedStack()) self.log.exception(formatExtendedStack())
raise raise
raise ConfigError('Device %s: config parameter %r:\n%r' % raise ConfigError('Module %s: config parameter %r:\n%r' %
(self.name, k, e)) (self.name, k, e))
setattr(self, k, v) setattr(self, k, v)
self._requestLock = threading.RLock() self._requestLock = threading.RLock()
@ -416,17 +417,17 @@ class Device(object):
self.log.debug('late init()') self.log.debug('late init()')
class Readable(Device): class Readable(Module):
"""Basic readable device """Basic readable Module
providing the readonly parameter 'value' and 'status' providing the readonly parameter 'value' and 'status'
""" """
PARAMS = { PARAMS = {
'value': PARAM('current value of the device', readonly=True, default=0., 'value': PARAM('current value of the Module', readonly=True, default=0.,
datatype=FloatRange(), unit='', poll=True), datatype=FloatRange(), unit='', poll=True),
'pollinterval': PARAM('sleeptime between polls', default=5, 'pollinterval': PARAM('sleeptime between polls', default=5,
readonly=False, datatype=FloatRange(0.1, 120), ), readonly=False, datatype=FloatRange(0.1, 120), ),
'status': PARAM('current status of the device', default=(status.OK, ''), 'status': PARAM('current status of the Module', default=(status.OK, ''),
datatype=TupleOf( datatype=TupleOf(
EnumType(**{ EnumType(**{
'IDLE': status.OK, 'IDLE': status.OK,
@ -440,7 +441,7 @@ class Readable(Device):
} }
def init(self): def init(self):
Device.init(self) Module.init(self)
self._pollthread = threading.Thread(target=self.__pollThread) self._pollthread = threading.Thread(target=self.__pollThread)
self._pollthread.daemon = True self._pollthread.daemon = True
self._pollthread.start() self._pollthread.start()
@ -475,7 +476,7 @@ class Readable(Device):
continue continue
if ((int(pobj.poll) < 0) and fastpoll) or ( if ((int(pobj.poll) < 0) and fastpoll) or (
0 == nr % abs(int(pobj.poll))): 0 == nr % abs(int(pobj.poll))):
# poll always if pobj.poll is negative and fastpoll (i.e. device is busy) # poll always if pobj.poll is negative and fastpoll (i.e. Module is busy)
# otherwise poll every 'pobj.poll' iteration # otherwise poll every 'pobj.poll' iteration
rfunc = getattr(self, 'read_' + pname, None) rfunc = getattr(self, 'read_' + pname, None)
if rfunc: if rfunc:
@ -487,14 +488,14 @@ class Readable(Device):
return fastpoll return fastpoll
class Driveable(Readable): class Drivable(Readable):
"""Basic Driveable device """Basic Drivable Module
providing a settable 'target' parameter to those of a Readable providing a settable 'target' parameter to those of a Readable
""" """
PARAMS = { PARAMS = {
'target': PARAM( 'target': PARAM(
'target value of the device', 'target value of the Module',
default=0., default=0.,
readonly=False, readonly=False,
datatype=FloatRange(), datatype=FloatRange(),

View File

@ -390,6 +390,8 @@ class Dispatcher(object):
# easy approach: poll all values... # easy approach: poll all values...
for modulename, moduleobj in self._modules.items(): for modulename, moduleobj in self._modules.items():
for pname, pobj in moduleobj.PARAMS.items(): for pname, pobj in moduleobj.PARAMS.items():
if not pobj.export:
continue
# WARNING: THIS READS ALL PARAMS FROM HW! # WARNING: THIS READS ALL PARAMS FROM HW!
# XXX: should we send the cached values instead? (pbj.value) # XXX: should we send the cached values instead? (pbj.value)
# also: ignore errors here. # also: ignore errors here.