support for fast poll when busy

Module.setFastPoll may be called depending on status in order to
change the poll interval dependent whether the module is busy or
not. It is assured that the new interval is applied immediately.

Change-Id: I2bd8f68440dc4a93b39e5083a579fc1c123fe578
Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/27896
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
zolliker 2022-03-04 14:57:33 +01:00
parent b423235c5d
commit e0fe7e46d1
2 changed files with 40 additions and 22 deletions

View File

@ -24,7 +24,7 @@
import time
import threading
from queue import Queue, Empty
from collections import OrderedDict
from functools import wraps
@ -293,7 +293,7 @@ class Module(HasAccessibles):
self.initModuleDone = False
self.startModuleDone = False
self.remoteLogHandler = None
self.nextPollEvent = threading.Event()
self.changePollinterval = Queue() # used for waiting between polls and transmit info to the thread
errors = []
# handle module properties
@ -587,9 +587,17 @@ class Module(HasAccessibles):
all other parameters are polled automatically
"""
def triggerPollEvent(self, *args): # args needed for valueCallback
"""interrupts waiting between polls"""
self.nextPollEvent.set() # trigger poll loop
def setFastPoll(self, pollinterval):
"""change poll interval
:param pollinterval: a new (typically lower) pollinterval
special values: True: set to 0.25 (default fast poll interval)
False: set to self.pollinterval (value for idle)
"""
if pollinterval is False:
self.changePollinterval.put(self.pollinterval)
return
self.changePollinterval.put(0.25 if pollinterval is True else pollinterval)
def callPollFunc(self, rfunc):
"""call read method with proper error handling"""
@ -614,25 +622,27 @@ class Module(HasAccessibles):
polled_parameters.append((rfunc, pobj))
self.callPollFunc(rfunc)
started_callback()
pollinterval = self.pollinterval
last_slow = last_main = 0
last_error = None
error_count = 0
to_poll = ()
while True:
now = time.time()
wait_main = last_main + self.pollinterval - now
wait_main = last_main + pollinterval - now
wait_slow = last_slow + self.slowinterval - now
wait_time = min(wait_main, wait_slow)
if wait_time > 0:
self.nextPollEvent.wait(wait_time)
self.nextPollEvent.clear()
# remark: if there would be a need to trigger polling all parameters,
# we might replace nextPollEvent by a Queue and act depending on the
# queued item
try:
result = self.changePollinterval.get(timeout=wait_time)
except Empty:
result = None
if result is not None:
pollinterval = result
continue
# call doPoll, if due
if wait_main <= 0:
last_main = (now // self.pollinterval) * self.pollinterval
last_main = (now // pollinterval) * pollinterval
try:
self.doPoll()
if last_error and error_count > 1:
@ -712,8 +722,9 @@ class Readable(Module):
def earlyInit(self):
super().earlyInit()
# in case pollinterval is reduced a lot, we do not want to wait
self.valueCallbacks['pollinterval'].append(self.triggerPollEvent)
# trigger a poll interval change when self.pollinterval changes.
# self.setFastPoll with a float argument does the job here
self.valueCallbacks['pollinterval'].append(self.setFastPoll)
def doPoll(self):
self.read_value()

View File

@ -507,13 +507,14 @@ class AnalogOutput(PyTangoDevice, Drivable):
if self._isAtTarget():
self._timeout = None
self._moving = False
return super().read_status()
if self._timeout:
if self._timeout < currenttime():
return self.Status.UNSTABLE, 'timeout after waiting for stable value'
if self._moving:
return (self.Status.BUSY, 'moving')
return (self.Status.IDLE, 'stable')
status = super().read_status()
else:
if self._timeout and self._timeout < currenttime():
status = self.Status.UNSTABLE, 'timeout after waiting for stable value'
else:
status = (self.Status.BUSY, 'moving') if self._moving else (self.Status.IDLE, 'stable')
self.setFastPoll(self.isBusy(status))
return status
@property
def absmin(self):
@ -578,7 +579,7 @@ class AnalogOutput(PyTangoDevice, Drivable):
# do not clear the history here:
# - if the target is not changed by more than precision, there is no need to wait
# self._history = []
self.read_status() # poll our status to keep it updated
self.read_status() # poll our status to keep it updated (this will also set fast poll)
return self.read_target()
def _hw_wait(self):
@ -850,9 +851,15 @@ class DigitalOutput(PyTangoDevice, Drivable):
def read_value(self):
return self._dev.value # mapping is done by datatype upon export()
def read_status(self):
status = self.read_status()
self.setFastPoll(self.isBusy(status))
return status
def write_target(self, value):
self._dev.value = value
self.read_value()
self.read_status() # this will also set fast poll
return self.read_target()
def read_target(self):