From f7576cf541657f61603354abf82b6cc7e7b520b1 Mon Sep 17 00:00:00 2001 From: l_samenv Date: Fri, 25 Sep 2020 11:19:17 +0200 Subject: [PATCH] fix initwrite behaviour with handlers, a parameter from the cfg file which is not the first of parameters with the same handler were not written. fix: write_ method is called for all parameters in .writeDict even if there is no poll entry. with this fix, the write_ is called even when a parameter is not polled. --- secop/modules.py | 54 ++++++++++++++++++++++----------------------- secop/params.py | 10 ++++----- secop/poller.py | 6 ++++- test/test_poller.py | 3 ++- 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/secop/modules.py b/secop/modules.py index 0940ba5..980e056 100644 --- a/secop/modules.py +++ b/secop/modules.py @@ -346,16 +346,6 @@ class Module(HasProperties, metaclass=ModuleMeta): def initModule(self): self.log.debug('empty %s.initModule()' % self.__class__.__name__) - def startModule(self, started_callback): - """runs after init of all modules - - started_callback to be called when thread spawned by late_init - or, if not implemented, immediately - might return a timeout value, if different from default - """ - self.log.debug('empty %s.startModule()' % self.__class__.__name__) - started_callback() - def pollOneParam(self, pname): """poll parameter with proper error handling""" try: @@ -367,23 +357,34 @@ class Module(HasProperties, metaclass=ModuleMeta): except Exception: self.log.error(formatException()) - def writeOrPoll(self, pname): - """write configured value for a parameter, if any, else poll + def writeInitParams(self, started_callback=None): + """write values for parameters with configured values + this must be called at the beginning of the poller thread with proper error handling """ - try: - if pname in self.writeDict: - self.log.debug('write parameter %s', pname) - getattr(self, 'write_' + pname)(self.writeDict.pop(pname)) - else: - getattr(self, 'read_' + pname)() - except SilentError: - pass - except SECoPError as e: - self.log.error(str(e)) - except Exception: - self.log.error(formatException()) + for pname in list(self.writeDict): + if pname in self.writeDict: # this might not be true with handlers + try: + self.log.debug('initialize parameter %s', pname) + getattr(self, 'write_' + pname)(self.writeDict.pop(pname)) + except SilentError: + pass + except SECoPError as e: + self.log.error(str(e)) + except Exception: + self.log.error(formatException()) + if started_callback: + started_callback() + + def startModule(self, started_callback): + """runs after init of all modules + + started_callback to be called when the thread spawned by startModule + has finished its initial work + might return a timeout value, if different from default + """ + mkthread(self.writeInitParams, started_callback) class Readable(Module): @@ -424,7 +425,7 @@ class Readable(Module): # use basic poller for legacy code mkthread(self.__pollThread, started_callback) else: - started_callback() + super().startModule(self, started_callback) def __pollThread(self, started_callback): while True: @@ -439,8 +440,7 @@ class Readable(Module): def __pollThread_inner(self, started_callback): """super simple and super stupid per-module polling thread""" - for pname in list(self.writeDict): - self.writeOrPoll(pname) + self.writeInitParams() i = 0 fastpoll = self.pollParams(i) started_callback() diff --git a/secop/params.py b/secop/params.py index 40904a6..af26423 100644 --- a/secop/params.py +++ b/secop/params.py @@ -115,7 +115,8 @@ class Parameter(Accessible): settable=False, default=False), 'handler': Property('[internal] overload the standard read and write functions', ValueType(), export=False, default=None, mandatory=False, settable=False), - 'initwrite': Property('[internal] write this parameter on initialization (default None: write if given in config)', + 'initwrite': Property('[internal] write this parameter on initialization' + ' (default None: write if given in config)', NoneOr(BoolType()), export=False, default=None, mandatory=False, settable=False), } @@ -139,11 +140,8 @@ class Parameter(Accessible): datatype.setProperty('unit', unit) super(Parameter, self).__init__(**kwds) - if self.initwrite: - if self.readonly: - raise ProgrammingError('can not have both readonly and initwrite!') - if not self.poll: - raise ProgrammingError('only polled parameters can have initwrite!') + if self.initwrite and self.readonly: + raise ProgrammingError('can not have both readonly and initwrite!') if self.constant is not None: self.properties['readonly'] = True diff --git a/secop/poller.py b/secop/poller.py index 9c29d37..852803c 100644 --- a/secop/poller.py +++ b/secop/poller.py @@ -118,8 +118,10 @@ class Poller(PollerBase): self._stopped = False self.maxwait = 3600 self.name = name + self.modules = [] # used for writeInitParams only def add_to_poller(self, module): + self.modules.append(module) factors = self.DEFAULT_FACTORS.copy() try: factors[DYNAMIC] = module.fast_pollfactor @@ -227,11 +229,13 @@ class Poller(PollerBase): # nothing to do (else we might call time.sleep(float('inf')) below started_callback() return + for module in self.modules: + module.writeInitParams() # do all polls once and, at the same time, insert due info for _, queue in sorted(self.queues.items()): # do SLOW polls first for idx, (_, _, (_, module, pobj, pname, factor)) in enumerate(queue): lastdue = time.time() - module.writeOrPoll(pname) + module.pollOneParam(pname) due = lastdue + min(self.maxwait, module.pollinterval * factor) # in python 3 comparing tuples need some care, as not all objects # are comparable. Inserting a unique idx solves the problem. diff --git a/test/test_poller.py b/test/test_poller.py index e23a0b7..d36eb67 100644 --- a/test/test_poller.py +++ b/test/test_poller.py @@ -163,7 +163,8 @@ class Module: def pollOneParam(self, pname): getattr(self, 'read_' + pname)() - writeOrPoll = pollOneParam + def writeInitParams(self): + pass def __repr__(self): rdict = self.__dict__.copy()