MLZ: fix tango WindowTimeout
Change-Id: I4e1f7e9ab519fe1ae5b6bcc4b9898b44f017c45f Reviewed-on: https://forge.frm2.tum.de/review/17742 Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de> Tested-by: Jens Krueger <jens.krueger@frm2.tum.de> Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
parent
851da9696c
commit
0d25dc35e0
@ -37,10 +37,11 @@ import PyTango
|
|||||||
|
|
||||||
from secop.lib import lazy_property
|
from secop.lib import lazy_property
|
||||||
from secop.protocol import status
|
from secop.protocol import status
|
||||||
from secop.parse import Parser
|
|
||||||
from secop.datatypes import IntRange, FloatRange, StringType, TupleOf, \
|
from secop.datatypes import IntRange, FloatRange, StringType, TupleOf, \
|
||||||
ArrayOf, EnumType
|
ArrayOf, EnumType
|
||||||
from secop.errors import ConfigError, ProgrammingError, CommunicationError, HardwareError
|
from secop.errors import ConfigError, ProgrammingError, CommunicationError, \
|
||||||
|
HardwareError
|
||||||
from secop.modules import Param, Command, Override, Module, Readable, Drivable
|
from secop.modules import Param, Command, Override, Module, Readable, Drivable
|
||||||
|
|
||||||
#####
|
#####
|
||||||
@ -160,9 +161,11 @@ class PyTangoDevice(Module):
|
|||||||
|
|
||||||
parameters = {
|
parameters = {
|
||||||
'comtries': Param('Maximum retries for communication',
|
'comtries': Param('Maximum retries for communication',
|
||||||
datatype=IntRange(1, 100), default=3, readonly=False, group='communication'),
|
datatype=IntRange(1, 100), default=3, readonly=False,
|
||||||
'comdelay': Param('Delay between retries', datatype=FloatRange(0), unit='s', default=0.1,
|
group='communication'),
|
||||||
readonly=False, group='communication'),
|
'comdelay': Param('Delay between retries', datatype=FloatRange(0),
|
||||||
|
unit='s', default=0.1, readonly=False,
|
||||||
|
group='communication'),
|
||||||
|
|
||||||
'tangodevice': Param('Tango device name',
|
'tangodevice': Param('Tango device name',
|
||||||
datatype=StringType(), readonly=True,
|
datatype=StringType(), readonly=True,
|
||||||
@ -432,18 +435,26 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
|||||||
'precision': Param('Precision of the device value (allowed deviation '
|
'precision': Param('Precision of the device value (allowed deviation '
|
||||||
'of stable values from target)',
|
'of stable values from target)',
|
||||||
unit='main', datatype=FloatRange(1e-38),
|
unit='main', datatype=FloatRange(1e-38),
|
||||||
readonly=False,
|
readonly=False, group='stability',
|
||||||
),
|
),
|
||||||
'window': Param('Time window for checking stabilization if > 0',
|
'window': Param('Time window for checking stabilization if > 0',
|
||||||
unit='s', default=60.0, readonly=False,
|
unit='s', default=60.0, readonly=False,
|
||||||
datatype=FloatRange(0, 900),
|
datatype=FloatRange(0, 900), group='stability',
|
||||||
),
|
),
|
||||||
|
'timeout': Param('Timeout for waiting for a stable value (if > 0)',
|
||||||
|
unit='s', default=60.0, readonly=False,
|
||||||
|
datatype=FloatRange(0, 900), group='stability',
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
_history = ()
|
||||||
|
_timeout = None
|
||||||
|
_moving = False
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
super(AnalogOutput, self).init()
|
super(AnalogOutput, self).init()
|
||||||
# init history
|
# init history
|
||||||
self._history = [] # will keep (timestamp, value) tuple
|
self._history = [] # will keep (timestamp, value) tuple
|
||||||
|
self._timeout = None # keeps the time at which we will timeout, or None
|
||||||
|
|
||||||
def late_init(self):
|
def late_init(self):
|
||||||
super(AnalogOutput, self).late_init()
|
super(AnalogOutput, self).late_init()
|
||||||
@ -458,7 +469,7 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
|||||||
super(AnalogOutput, self).poll(nr)
|
super(AnalogOutput, self).poll(nr)
|
||||||
while len(self._history) > 2:
|
while len(self._history) > 2:
|
||||||
# if history would be too short, break
|
# if history would be too short, break
|
||||||
if self._history[-1][0] - self._history[1][0] < self.window:
|
if self._history[-1][0] - self._history[1][0] <= self.window:
|
||||||
break
|
break
|
||||||
# else: remove a stale point
|
# else: remove a stale point
|
||||||
self._history.pop(0)
|
self._history.pop(0)
|
||||||
@ -475,15 +486,31 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
|||||||
def _isAtTarget(self):
|
def _isAtTarget(self):
|
||||||
if self.target is None:
|
if self.target is None:
|
||||||
return True # avoid bootstrapping problems
|
return True # avoid bootstrapping problems
|
||||||
|
if not self._history:
|
||||||
|
return False # no history -> no knowledge
|
||||||
# check subset of _history which is in window
|
# check subset of _history which is in window
|
||||||
# also check if there is at least one value before window
|
# also check if there is at least one value before window
|
||||||
# to know we have enough datapoints
|
# to know we have enough datapoints
|
||||||
hist = self._history[:]
|
hist = self._history[:]
|
||||||
window_start = currenttime() - self.window
|
window_start = currenttime() - self.window
|
||||||
hist_in_window = [v for (t, v) in hist if t >= window_start]
|
hist_in_window = [v for (t, v) in hist if t >= window_start]
|
||||||
stable = all(abs(v - self.target) <= self.precision
|
|
||||||
for v in hist_in_window)
|
max_in_hist = max(hist_in_window)
|
||||||
return 0 < len(hist_in_window) < len(hist) and stable
|
min_in_hist = min(hist_in_window)
|
||||||
|
stable = max_in_hist - min_in_hist <= 2*self.precision
|
||||||
|
at_target = min_in_hist <= self.target <= max_in_hist
|
||||||
|
|
||||||
|
return stable and at_target
|
||||||
|
|
||||||
|
def read_status(self, maxage=0):
|
||||||
|
if self._isAtTarget():
|
||||||
|
self._timeout = None
|
||||||
|
self._moving = False
|
||||||
|
return super(AnalogOutput, self).read_status()
|
||||||
|
if self._timeout:
|
||||||
|
if self._timeout < currenttime():
|
||||||
|
return status.UNSTABLE, 'timeout after waiting for stable value'
|
||||||
|
return (status.BUSY, 'Moving') if self._moving else (status.OK, 'stable')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def absmin(self):
|
def absmin(self):
|
||||||
@ -534,10 +561,22 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
|||||||
self.do_stop()
|
self.do_stop()
|
||||||
self._hw_wait()
|
self._hw_wait()
|
||||||
self._dev.value = value
|
self._dev.value = value
|
||||||
|
# set meaningful timeout
|
||||||
|
self._timeout = currenttime() + self.window + self.timeout
|
||||||
|
if hasattr(self, 'ramp'):
|
||||||
|
self._timeout += abs((self.target or self.value) - self.value) / \
|
||||||
|
((self.ramp or 1e-8) * 60)
|
||||||
|
elif hasattr(self, 'speed'):
|
||||||
|
self._timeout += abs((self.target or self.value) - self.value) / \
|
||||||
|
(self.speed or 1e-8)
|
||||||
|
if not self.timeout:
|
||||||
|
self._timeout = None
|
||||||
|
self._moving = True
|
||||||
|
self._history = [] # clear history
|
||||||
self.read_status(0) # poll our status to keep it updated
|
self.read_status(0) # poll our status to keep it updated
|
||||||
|
|
||||||
def _hw_wait(self):
|
def _hw_wait(self):
|
||||||
while self.read_status(0)[0] == status.BUSY:
|
while super(AnalogOutput, self).read_status()[0] == status.BUSY:
|
||||||
sleep(0.3)
|
sleep(0.3)
|
||||||
|
|
||||||
def do_stop(self):
|
def do_stop(self):
|
||||||
@ -751,11 +790,8 @@ class NamedDigitalInput(DigitalInput):
|
|||||||
def init(self):
|
def init(self):
|
||||||
super(NamedDigitalInput, self).init()
|
super(NamedDigitalInput, self).init()
|
||||||
try:
|
try:
|
||||||
mapping, rem = Parser().parse(self.mapping)
|
# pylint: disable=eval-used
|
||||||
if rem:
|
self.parameters['value'].datatype = EnumType(**eval(self.mapping))
|
||||||
raise ValueError('Illegal Value for mapping, '
|
|
||||||
'trailing garbage: %r' % rem)
|
|
||||||
self.parameters['value'].datatype = EnumType(**mapping)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError('Illegal Value for mapping: %r' % e)
|
raise ValueError('Illegal Value for mapping: %r' % e)
|
||||||
|
|
||||||
@ -779,7 +815,7 @@ class PartialDigitalInput(NamedDigitalInput):
|
|||||||
def init(self):
|
def init(self):
|
||||||
super(PartialDigitalInput, self).init()
|
super(PartialDigitalInput, self).init()
|
||||||
self._mask = (1 << self.bitwidth) - 1
|
self._mask = (1 << self.bitwidth) - 1
|
||||||
#self.parameters['value'].datatype = IntRange(0, self._mask)
|
# self.parameters['value'].datatype = IntRange(0, self._mask)
|
||||||
|
|
||||||
def read_value(self, maxage=0):
|
def read_value(self, maxage=0):
|
||||||
raw_value = self._dev.value
|
raw_value = self._dev.value
|
||||||
@ -850,8 +886,8 @@ class PartialDigitalOutput(NamedDigitalOutput):
|
|||||||
def init(self):
|
def init(self):
|
||||||
super(PartialDigitalOutput, self).init()
|
super(PartialDigitalOutput, self).init()
|
||||||
self._mask = (1 << self.bitwidth) - 1
|
self._mask = (1 << self.bitwidth) - 1
|
||||||
#self.parameters['value'].datatype = IntRange(0, self._mask)
|
# self.parameters['value'].datatype = IntRange(0, self._mask)
|
||||||
#self.parameters['target'].datatype = IntRange(0, self._mask)
|
# self.parameters['target'].datatype = IntRange(0, self._mask)
|
||||||
|
|
||||||
def read_value(self, maxage=0):
|
def read_value(self, maxage=0):
|
||||||
raw_value = self._dev.value
|
raw_value = self._dev.value
|
||||||
|
Loading…
x
Reference in New Issue
Block a user