diff --git a/secop/modules.py b/secop/modules.py index 122a215..fc1929c 100644 --- a/secop/modules.py +++ b/secop/modules.py @@ -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() diff --git a/secop_mlz/entangle.py b/secop_mlz/entangle.py index f6b289b..ba97125 100644 --- a/secop_mlz/entangle.py +++ b/secop_mlz/entangle.py @@ -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):