rework polling

Readable/Moveable can't be busy....

Change-Id: Icfe250e16dd30646cf3081f4850a8bacaa77a935
Reviewed-on: https://forge.frm2.tum.de/review/17859
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
Enrico Faulhaber
2018-04-26 16:25:02 +02:00
parent f0ea4bd9b8
commit 927ca854a2

View File

@ -21,6 +21,8 @@
# ***************************************************************************** # *****************************************************************************
"""Define Baseclasses for real Modules implemented in the server""" """Define Baseclasses for real Modules implemented in the server"""
from __future__ import print_function
# XXX: connect with 'protocol'-Modules. # XXX: connect with 'protocol'-Modules.
# Idea: every Module defined herein is also a 'protocol'-Module, # 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
@ -59,15 +61,23 @@ from secop.datatypes import DataType, EnumType, TupleOf, StringType, FloatRange,
EVENT_ONLY_ON_CHANGED_VALUES = False EVENT_ONLY_ON_CHANGED_VALUES = False
# storage for Parameter settings:
# if readonly is False, the value can be changed (by code, or remote)
# if no default is given, the parameter MUST be specified in the configfile
# during startup, value is initialized with the default value or
# from the config file if specified there
class Param(object): class Param(object):
"""storage for Parameter settings + value + qualifiers
if readonly is False, the value can be changed (by code, or remote)
if no default is given, the parameter MUST be specified in the configfile
during startup, value is initialized with the default value or
from the config file if specified there
poll can be:
- False (never poll this parameter)
- True (poll this ever pollinterval)
- positive int (poll every N(th) pollinterval)
- negative int (normally poll every N(th) pollinterval, if module is busy, poll every pollinterval)
note: Drivable (and derived classes) poll with 10 fold frequency if module is busy....
"""
def __init__(self, def __init__(self,
description, description,
datatype=None, datatype=None,
@ -432,6 +442,7 @@ class Module(object):
mkthread(self.late_init) mkthread(self.late_init)
def late_init(self): def late_init(self):
# this runs async somewhen after init
self.log.debug('late init()') self.log.debug('late init()')
@ -463,45 +474,37 @@ class Readable(Module):
self._pollthread = mkthread(self.__pollThread) self._pollthread = mkthread(self.__pollThread)
def __pollThread(self): def __pollThread(self):
try:
self.__pollThread_inner()
except Exception as e:
self.log.exception(e)
print(formatExtendedStack())
def __pollThread_inner(self):
"""super simple and super stupid per-module polling thread""" """super simple and super stupid per-module polling thread"""
i = 0 i = 0
fastpoll = True # first update should be quick
while True: while True:
i = 1 fastpoll = self.poll(i)
i += 1
try: try:
time.sleep(self.pollinterval * (0.1 if fastpoll else 1)) time.sleep(self.pollinterval * (0.1 if fastpoll else 1))
except TypeError: except TypeError:
time.sleep(min(self.pollinterval) time.sleep(min(self.pollinterval)
if fastpoll else max(self.pollinterval)) if fastpoll else max(self.pollinterval))
fastpoll = self.poll(i)
def poll(self, nr): def poll(self, nr=0):
# poll status first # Just poll all parameters regularly where polling is enabled
fastpoll = False
if 'status' in self.parameters:
stat = self.read_status(0)
# self.log.info('polling read_status -> %r' % (stat,))
fastpoll = stat[0] == status.BUSY
# if fastpoll:
# self.log.info('fastpoll!')
for pname, pobj in self.parameters.items(): for pname, pobj in self.parameters.items():
if not pobj.poll: if not pobj.poll:
continue continue
if pname == 'status': if nr % abs(int(pobj.poll)) == 0:
# status was already polled above # poll every 'pobj.poll' iteration
continue
if ((int(pobj.poll) < 0) and fastpoll) or (
nr % abs(int(pobj.poll))) == 0:
# poll always if pobj.poll is negative and fastpoll (i.e. Module is busy)
# otherwise poll every 'pobj.poll' iteration
rfunc = getattr(self, 'read_' + pname, None) rfunc = getattr(self, 'read_' + pname, None)
if rfunc: if rfunc:
try: try:
# self.log.info('polling read_%s -> %r' % (pname, rfunc()))
rfunc() rfunc()
except Exception: # really all! except Exception: # really all!
pass pass
return fastpoll
class Writable(Readable): class Writable(Readable):
@ -527,6 +530,29 @@ class Drivable(Writable):
Also status gets extended with a BUSY state indicating a running action. Also status gets extended with a BUSY state indicating a running action.
""" """
# improved polling: may poll faster if module is BUSY
def poll(self, nr=0):
# poll status first
stat = self.read_status(0)
fastpoll = stat[0] == status.BUSY
for pname, pobj in self.parameters.items():
if not pobj.poll:
continue
if pname == 'status':
# status was already polled above
continue
if ((int(pobj.poll) < 0) and fastpoll) or (
nr % abs(int(pobj.poll))) == 0:
# poll always if pobj.poll is negative and fastpoll (i.e. Module is busy)
# otherwise poll every 'pobj.poll' iteration
rfunc = getattr(self, 'read_' + pname, None)
if rfunc:
try:
rfunc()
except Exception: # really all!
pass
return fastpoll
def do_stop(self): def do_stop(self):
"""default implementation of the stop command """default implementation of the stop command