From 59fbd5cac06845c07dab5dfeac3b695eb3f2e278 Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Wed, 27 Nov 2019 09:51:59 +0100 Subject: [PATCH] make secop.poller.Poller default modules using the old poller (now called secop.poller.BasicPoller) need to explcitly declare it (pollerClass = BasicPoller) BasicPoller is still used in: secop_mlz/amagnet.py secop_mlz/entangle.py secop/simulation.py Remark: before removing BasicPoller we may need a replacement for Readable.pollParams Change-Id: If1ae8b68e02f13e601334656b818337c882e06cc Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/21910 Tested-by: JenkinsCodeReview Reviewed-by: Markus Zolliker --- secop/modules.py | 32 ++++++++++++++++++-------------- secop/poller.py | 24 ++++++++++++++---------- secop/protocol/interface/tcp.py | 5 +++-- secop/server.py | 5 +---- secop/simulation.py | 7 +++++-- secop_mlz/amagnet.py | 4 +++- secop_mlz/entangle.py | 4 +++- 7 files changed, 47 insertions(+), 34 deletions(-) diff --git a/secop/modules.py b/secop/modules.py index 3153d6f..f706580 100644 --- a/secop/modules.py +++ b/secop/modules.py @@ -30,11 +30,12 @@ from secop.datatypes import EnumType, FloatRange, BoolType, IntRange, \ StringType, TupleOf, get_datatype, ArrayOf, TextType from secop.errors import ConfigError, ProgrammingError from secop.lib import formatException, \ - formatExtendedStack, mkthread, unset_value + formatExtendedStack, mkthread from secop.lib.enum import Enum from secop.metaclass import ModuleMeta from secop.params import PREDEFINED_ACCESSIBLES, Command, Override, Parameter, Parameters, Commands from secop.properties import HasProperties, Property +from secop.poller import Poller, BasicPoller # XXX: connect with 'protocol'-Modules. @@ -86,6 +87,8 @@ class Module(HasProperties, metaclass=ModuleMeta): # reference to the dispatcher (used for sending async updates) DISPATCHER = None + pollerClass = Poller + def __init__(self, name, logger, cfgdict, srv): # remember the dispatcher object (for the async callbacks) self.DISPATCHER = srv.dispatcher @@ -179,13 +182,14 @@ class Module(HasProperties, metaclass=ModuleMeta): # is not specified in cfgdict for k, v in self.parameters.items(): 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.... - raise ConfigError('Module %s: Parameter %r has no default ' - 'value and was not given in config!' % - (self.name, k)) - # assume default value was given - cfgdict[k] = v.default + if v.default is None: + if not v.poll: + raise ConfigError('Module %s: Parameter %r has no default ' + 'value and was not given in config!' % + (self.name, k)) + cfgdict[k] = v.datatype.default + else: + cfgdict[k] = v.default # 5) 'apply' config: # pass values through the datatypes and store as attributes @@ -265,7 +269,7 @@ class Readable(Module): parameters = { 'value': Parameter('current value of the Module', readonly=True, default=0., datatype=FloatRange(), - unit='', poll=True, + poll=True, ), 'pollinterval': Parameter('sleeptime between polls', default=5, readonly=False, @@ -279,12 +283,12 @@ class Readable(Module): } def startModule(self, started_callback): - '''start polling thread''' - if hasattr(self, 'pollerClass'): # an other poller is used - started_callback() - else: - # basic poller kept for reference + '''start basic polling thread''' + if issubclass(self.pollerClass, BasicPoller): + # use basic poller for legacy code mkthread(self.__pollThread, started_callback) + else: + started_callback() def __pollThread(self, started_callback): while True: diff --git a/secop/poller.py b/secop/poller.py index 7372423..05f3a54 100644 --- a/secop/poller.py +++ b/secop/poller.py @@ -58,17 +58,13 @@ class PollerBase: poller as value. is module.iodev or module.name, if iodev is not present ''' - try: - pollerClass = module.pollerClass - except AttributeError: - return # no pollerClass -> fall back to simple poller # for modules with the same iodev, a common poller is used, # modules without iodev all get their own poller name = getattr(module, 'iodev', module.name) - poller = table.get((pollerClass, name), None) + poller = table.get((cls, name), None) if poller is None: - poller = pollerClass(name) - table[(pollerClass, name)] = poller + poller = cls(name) + table[(cls, name)] = poller poller.add_to_poller(module) def start(self, started_callback): @@ -97,8 +93,6 @@ class PollerBase: def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.name) - __nonzero__ = __bool__ # Py2/3 compat - class Poller(PollerBase): '''a standard poller @@ -246,4 +240,14 @@ class Poller(PollerBase): '''is there any poll item?''' return any(self.queues.values()) - __nonzero__ = __bool__ # Py2/3 compat + +class BasicPoller(PollerBase): + """basic poller + + this is just a dummy, the poller thread is started in Readable.startModule + """ + # pylint: disable=abstract-method + + @classmethod + def add_to_table(cls, table, module): + pass diff --git a/secop/protocol/interface/tcp.py b/secop/protocol/interface/tcp.py index 6b61a7d..0772d4f 100644 --- a/secop/protocol/interface/tcp.py +++ b/secop/protocol/interface/tcp.py @@ -122,8 +122,9 @@ class TCPRequestHandler(socketserver.BaseRequestHandler): try: msg = decode_msg(origin) except Exception as err: - # we have to decode 'origin' here. utf-8 and ascii may lead to encoding errors - msg = origin.decode('latin-1').split(' ', 3) + # we have to decode 'origin' here + # use latin-1, as utf-8 or ascii may lead to encoding errors + msg = origin.decode('latin-1').split(' ', 3) + (None,) # make sure len(msg) > 1 result = (ERRORPREFIX + msg[0], msg[1], ['InternalError', str(err), {'exception': formatException(), 'traceback': formatExtendedStack()}]) diff --git a/secop/server.py b/secop/server.py index e7d3acb..fbc1236 100644 --- a/secop/server.py +++ b/secop/server.py @@ -185,10 +185,7 @@ class Server: for modname, modobj in self.modules.items(): self.log.info('registering module %r' % modname) self.dispatcher.register_module(modobj, modname, modobj.properties['export']) - try: - modobj.pollerClass.add_to_table(pollTable, modobj) - except AttributeError: - pass + modobj.pollerClass.add_to_table(pollTable, modobj) # also call earlyInit on the modules modobj.earlyInit() diff --git a/secop/simulation.py b/secop/simulation.py index 272965c..34a63b9 100644 --- a/secop/simulation.py +++ b/secop/simulation.py @@ -27,10 +27,12 @@ from time import sleep from secop.datatypes import FloatRange from secop.lib import mkthread -from secop.modules import Drivable, Module, Parameter, Readable, Writable +from secop.modules import Drivable, Module, Parameter, Readable, Writable, BasicPoller class SimBase: + pollerClass = BasicPoller + def __init__(self, cfgdict): # spice up parameters if requested by extra property # hint: us a comma-separated list if mor than one extra_param @@ -68,7 +70,7 @@ class SimBase: self.log.info('sim thread ended') def sim(self): - return True + return True # nothing to do, stop thread def read_value(self): if 'jitter' in self.accessibles: @@ -138,6 +140,7 @@ class SimDrivable(SimBase, Drivable): except Exception: pass self.status = self.Status.IDLE, '' + return False # keep thread running def _hw_wait(self): while self.status[0] == self.Status.BUSY: diff --git a/secop_mlz/amagnet.py b/secop_mlz/amagnet.py index 95a1658..94b509f 100644 --- a/secop_mlz/amagnet.py +++ b/secop_mlz/amagnet.py @@ -31,7 +31,7 @@ import math from secop.datatypes import ArrayOf, FloatRange, StringType, StructOf, TupleOf from secop.errors import ConfigError, DisabledError from secop.lib.sequence import SequencerMixin, Step -from secop.modules import Drivable, Parameter +from secop.modules import Drivable, Parameter, BasicPoller class GarfieldMagnet(SequencerMixin, Drivable): @@ -47,6 +47,8 @@ class GarfieldMagnet(SequencerMixin, Drivable): the symmetry setting selects which. """ + pollerClass = BasicPoller + parameters = { 'subdev_currentsource': Parameter('(bipolar) Powersupply', datatype=StringType(), readonly=True, export=False), 'subdev_enable': Parameter('Switch to set for on/off', datatype=StringType(), readonly=True, export=False), diff --git a/secop_mlz/entangle.py b/secop_mlz/entangle.py index c720fa3..8485feb 100644 --- a/secop_mlz/entangle.py +++ b/secop_mlz/entangle.py @@ -41,7 +41,7 @@ from secop.errors import CommunicationFailedError, \ ConfigError, HardwareError, ProgrammingError from secop.lib import lazy_property from secop.modules import Command, Drivable, \ - Module, Override, Parameter, Readable + Module, Override, Parameter, Readable, BasicPoller ##### @@ -158,6 +158,8 @@ class PyTangoDevice(Module): execution and attribute operations with logging and exception mapping. """ + pollerClass = BasicPoller + parameters = { 'comtries': Parameter('Maximum retries for communication', datatype=IntRange(1, 100), default=3, readonly=False,