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
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:

View File

@ -58,17 +58,13 @@ class PollerBase:
poller as value.
<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,
# 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

View File

@ -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()}])

View File

@ -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()

View File

@ -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:

View File

@ -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),

View File

@ -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,