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 <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
zolliker 2019-11-27 09:51:59 +01:00
parent ca8b07496f
commit 59fbd5cac0
7 changed files with 47 additions and 34 deletions

View File

@ -30,11 +30,12 @@ from secop.datatypes import EnumType, FloatRange, BoolType, IntRange, \
StringType, TupleOf, get_datatype, ArrayOf, TextType StringType, TupleOf, get_datatype, ArrayOf, TextType
from secop.errors import ConfigError, ProgrammingError from secop.errors import ConfigError, ProgrammingError
from secop.lib import formatException, \ from secop.lib import formatException, \
formatExtendedStack, mkthread, unset_value formatExtendedStack, mkthread
from secop.lib.enum import Enum from secop.lib.enum import Enum
from secop.metaclass import ModuleMeta from secop.metaclass import ModuleMeta
from secop.params import PREDEFINED_ACCESSIBLES, Command, Override, Parameter, Parameters, Commands from secop.params import PREDEFINED_ACCESSIBLES, Command, Override, Parameter, Parameters, Commands
from secop.properties import HasProperties, Property from secop.properties import HasProperties, Property
from secop.poller import Poller, BasicPoller
# XXX: connect with 'protocol'-Modules. # XXX: connect with 'protocol'-Modules.
@ -86,6 +87,8 @@ class Module(HasProperties, metaclass=ModuleMeta):
# reference to the dispatcher (used for sending async updates) # reference to the dispatcher (used for sending async updates)
DISPATCHER = None DISPATCHER = None
pollerClass = Poller
def __init__(self, name, logger, cfgdict, srv): def __init__(self, name, logger, cfgdict, srv):
# remember the dispatcher object (for the async callbacks) # remember the dispatcher object (for the async callbacks)
self.DISPATCHER = srv.dispatcher self.DISPATCHER = srv.dispatcher
@ -179,13 +182,14 @@ class Module(HasProperties, metaclass=ModuleMeta):
# is not specified in cfgdict # is not specified in cfgdict
for k, v in self.parameters.items(): for k, v in self.parameters.items():
if k not in cfgdict: if k not in cfgdict:
if v.default is unset_value and k != 'value': if v.default is None:
# unset_value is the one single value you can not specify.... if not v.poll:
raise ConfigError('Module %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 cfgdict[k] = v.datatype.default
cfgdict[k] = v.default else:
cfgdict[k] = v.default
# 5) 'apply' config: # 5) 'apply' config:
# pass values through the datatypes and store as attributes # pass values through the datatypes and store as attributes
@ -265,7 +269,7 @@ class Readable(Module):
parameters = { parameters = {
'value': Parameter('current value of the Module', readonly=True, 'value': Parameter('current value of the Module', readonly=True,
default=0., datatype=FloatRange(), default=0., datatype=FloatRange(),
unit='', poll=True, poll=True,
), ),
'pollinterval': Parameter('sleeptime between polls', default=5, 'pollinterval': Parameter('sleeptime between polls', default=5,
readonly=False, readonly=False,
@ -279,12 +283,12 @@ class Readable(Module):
} }
def startModule(self, started_callback): def startModule(self, started_callback):
'''start polling thread''' '''start basic polling thread'''
if hasattr(self, 'pollerClass'): # an other poller is used if issubclass(self.pollerClass, BasicPoller):
started_callback() # use basic poller for legacy code
else:
# basic poller kept for reference
mkthread(self.__pollThread, started_callback) mkthread(self.__pollThread, started_callback)
else:
started_callback()
def __pollThread(self, started_callback): def __pollThread(self, started_callback):
while True: while True:

View File

@ -58,17 +58,13 @@ class PollerBase:
poller as value. poller as value.
<name> is module.iodev or module.name, if iodev is not present <name> 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, # for modules with the same iodev, a common poller is used,
# modules without iodev all get their own poller # modules without iodev all get their own poller
name = getattr(module, 'iodev', module.name) name = getattr(module, 'iodev', module.name)
poller = table.get((pollerClass, name), None) poller = table.get((cls, name), None)
if poller is None: if poller is None:
poller = pollerClass(name) poller = cls(name)
table[(pollerClass, name)] = poller table[(cls, name)] = poller
poller.add_to_poller(module) poller.add_to_poller(module)
def start(self, started_callback): def start(self, started_callback):
@ -97,8 +93,6 @@ class PollerBase:
def __repr__(self): def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.name) return '%s(%r)' % (self.__class__.__name__, self.name)
__nonzero__ = __bool__ # Py2/3 compat
class Poller(PollerBase): class Poller(PollerBase):
'''a standard poller '''a standard poller
@ -246,4 +240,14 @@ class Poller(PollerBase):
'''is there any poll item?''' '''is there any poll item?'''
return any(self.queues.values()) 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

View File

@ -122,8 +122,9 @@ class TCPRequestHandler(socketserver.BaseRequestHandler):
try: try:
msg = decode_msg(origin) msg = decode_msg(origin)
except Exception as err: except Exception as err:
# we have to decode 'origin' here. utf-8 and ascii may lead to encoding errors # we have to decode 'origin' here
msg = origin.decode('latin-1').split(' ', 3) # 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), result = (ERRORPREFIX + msg[0], msg[1], ['InternalError', str(err),
{'exception': formatException(), {'exception': formatException(),
'traceback': formatExtendedStack()}]) 'traceback': formatExtendedStack()}])

View File

@ -185,10 +185,7 @@ class Server:
for modname, modobj in self.modules.items(): for modname, modobj in self.modules.items():
self.log.info('registering module %r' % modname) self.log.info('registering module %r' % modname)
self.dispatcher.register_module(modobj, modname, modobj.properties['export']) self.dispatcher.register_module(modobj, modname, modobj.properties['export'])
try: modobj.pollerClass.add_to_table(pollTable, modobj)
modobj.pollerClass.add_to_table(pollTable, modobj)
except AttributeError:
pass
# also call earlyInit on the modules # also call earlyInit on the modules
modobj.earlyInit() modobj.earlyInit()

View File

@ -27,10 +27,12 @@ from time import sleep
from secop.datatypes import FloatRange from secop.datatypes import FloatRange
from secop.lib import mkthread 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: class SimBase:
pollerClass = BasicPoller
def __init__(self, cfgdict): def __init__(self, cfgdict):
# spice up parameters if requested by extra property # spice up parameters if requested by extra property
# hint: us a comma-separated list if mor than one extra_param # hint: us a comma-separated list if mor than one extra_param
@ -68,7 +70,7 @@ class SimBase:
self.log.info('sim thread ended') self.log.info('sim thread ended')
def sim(self): def sim(self):
return True return True # nothing to do, stop thread
def read_value(self): def read_value(self):
if 'jitter' in self.accessibles: if 'jitter' in self.accessibles:
@ -138,6 +140,7 @@ class SimDrivable(SimBase, Drivable):
except Exception: except Exception:
pass pass
self.status = self.Status.IDLE, '' self.status = self.Status.IDLE, ''
return False # keep thread running
def _hw_wait(self): def _hw_wait(self):
while self.status[0] == self.Status.BUSY: while self.status[0] == self.Status.BUSY:

View File

@ -31,7 +31,7 @@ import math
from secop.datatypes import ArrayOf, FloatRange, StringType, StructOf, TupleOf from secop.datatypes import ArrayOf, FloatRange, StringType, StructOf, TupleOf
from secop.errors import ConfigError, DisabledError from secop.errors import ConfigError, DisabledError
from secop.lib.sequence import SequencerMixin, Step from secop.lib.sequence import SequencerMixin, Step
from secop.modules import Drivable, Parameter from secop.modules import Drivable, Parameter, BasicPoller
class GarfieldMagnet(SequencerMixin, Drivable): class GarfieldMagnet(SequencerMixin, Drivable):
@ -47,6 +47,8 @@ class GarfieldMagnet(SequencerMixin, Drivable):
the symmetry setting selects which. the symmetry setting selects which.
""" """
pollerClass = BasicPoller
parameters = { parameters = {
'subdev_currentsource': Parameter('(bipolar) Powersupply', datatype=StringType(), readonly=True, export=False), '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), 'subdev_enable': Parameter('Switch to set for on/off', datatype=StringType(), readonly=True, export=False),

View File

@ -41,7 +41,7 @@ from secop.errors import CommunicationFailedError, \
ConfigError, HardwareError, ProgrammingError ConfigError, HardwareError, ProgrammingError
from secop.lib import lazy_property from secop.lib import lazy_property
from secop.modules import Command, Drivable, \ 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. execution and attribute operations with logging and exception mapping.
""" """
pollerClass = BasicPoller
parameters = { parameters = {
'comtries': Parameter('Maximum retries for communication', 'comtries': Parameter('Maximum retries for communication',
datatype=IntRange(1, 100), default=3, readonly=False, datatype=IntRange(1, 100), default=3, readonly=False,