more merges from gerrit
Change-Id: I13441cd8889dd39f74a2dd1a85e75a1b76bb93c8
This commit is contained in:
@ -39,7 +39,7 @@ from secop.datatypes import ArrayOf, EnumType, FloatRange, \
|
||||
from secop.errors import CommunicationFailedError, \
|
||||
ConfigError, HardwareError, ProgrammingError
|
||||
from secop.lib import lazy_property
|
||||
from secop.modules import BasicPoller, Command, \
|
||||
from secop.modules import Command, \
|
||||
Drivable, Module, Parameter, Readable
|
||||
|
||||
#####
|
||||
@ -157,8 +157,6 @@ 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,
|
||||
@ -210,7 +208,7 @@ class PyTangoDevice(Module):
|
||||
# exception mapping is enabled).
|
||||
self._createPyTangoDevice = self._applyGuardToFunc(
|
||||
self._createPyTangoDevice, 'constructor')
|
||||
super(PyTangoDevice, self).earlyInit()
|
||||
super().earlyInit()
|
||||
|
||||
@lazy_property
|
||||
def _dev(self):
|
||||
@ -249,10 +247,10 @@ class PyTangoDevice(Module):
|
||||
# otherwise would lead to attribute errors later
|
||||
try:
|
||||
device.State
|
||||
except AttributeError:
|
||||
except AttributeError as e:
|
||||
raise CommunicationFailedError(
|
||||
self, 'connection to Tango server failed, '
|
||||
'is the server running?')
|
||||
'is the server running?') from e
|
||||
return self._applyGuardsToPyTangoDevice(device)
|
||||
|
||||
def _applyGuardsToPyTangoDevice(self, dev):
|
||||
@ -376,14 +374,17 @@ class AnalogInput(PyTangoDevice, Readable):
|
||||
The AnalogInput handles all devices only delivering an analogue value.
|
||||
"""
|
||||
|
||||
def startModule(self, started_callback):
|
||||
super(AnalogInput, self).startModule(started_callback)
|
||||
# query unit from tango and update value property
|
||||
attrInfo = self._dev.attribute_query('value')
|
||||
# prefer configured unit if nothing is set on the Tango device, else
|
||||
# update
|
||||
if attrInfo.unit != 'No unit':
|
||||
self.accessibles['value'].datatype.setProperty('unit', attrInfo.unit)
|
||||
def startModule(self, start_events):
|
||||
super().startModule(start_events)
|
||||
try:
|
||||
# query unit from tango and update value property
|
||||
attrInfo = self._dev.attribute_query('value')
|
||||
# prefer configured unit if nothing is set on the Tango device, else
|
||||
# update
|
||||
if attrInfo.unit != 'No unit':
|
||||
self.accessibles['value'].datatype.setProperty('unit', attrInfo.unit)
|
||||
except Exception as e:
|
||||
self.log.error(e)
|
||||
|
||||
def read_value(self):
|
||||
return self._dev.value
|
||||
@ -422,7 +423,7 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
||||
userlimits = Parameter('User defined limits of device value',
|
||||
datatype=LimitsType(FloatRange(unit='$')),
|
||||
default=(float('-Inf'), float('+Inf')),
|
||||
readonly=False, poll=10,
|
||||
readonly=False,
|
||||
)
|
||||
abslimits = Parameter('Absolute limits of device value',
|
||||
datatype=LimitsType(FloatRange(unit='$')),
|
||||
@ -446,13 +447,13 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
||||
_moving = False
|
||||
|
||||
def initModule(self):
|
||||
super(AnalogOutput, self).initModule()
|
||||
super().initModule()
|
||||
# init history
|
||||
self._history = [] # will keep (timestamp, value) tuple
|
||||
self._timeout = None # keeps the time at which we will timeout, or None
|
||||
|
||||
def startModule(self, started_callback):
|
||||
super(AnalogOutput, self).startModule(started_callback)
|
||||
def startModule(self, start_events):
|
||||
super().startModule(start_events)
|
||||
# query unit from tango and update value property
|
||||
attrInfo = self._dev.attribute_query('value')
|
||||
# prefer configured unit if nothing is set on the Tango device, else
|
||||
@ -460,8 +461,8 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
||||
if attrInfo.unit != 'No unit':
|
||||
self.accessibles['value'].datatype.setProperty('unit', attrInfo.unit)
|
||||
|
||||
def pollParams(self, nr=0):
|
||||
super(AnalogOutput, self).pollParams(nr)
|
||||
def doPoll(self):
|
||||
super().doPoll()
|
||||
while len(self._history) > 2:
|
||||
# if history would be too short, break
|
||||
if self._history[-1][0] - self._history[1][0] <= self.window:
|
||||
@ -489,8 +490,11 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
||||
hist = self._history[:]
|
||||
window_start = currenttime() - self.window
|
||||
hist_in_window = [v for (t, v) in hist if t >= window_start]
|
||||
if len(hist) == len(hist_in_window):
|
||||
return False # no data point before window
|
||||
if not hist_in_window:
|
||||
return False # no relevant history -> no knowledge
|
||||
# window is too small -> use last point only
|
||||
hist_in_window = [self.value]
|
||||
|
||||
max_in_hist = max(hist_in_window)
|
||||
min_in_hist = min(hist_in_window)
|
||||
@ -503,13 +507,14 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
||||
if self._isAtTarget():
|
||||
self._timeout = None
|
||||
self._moving = False
|
||||
return super(AnalogOutput, self).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):
|
||||
@ -571,11 +576,14 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
||||
if not self.timeout:
|
||||
self._timeout = None
|
||||
self._moving = True
|
||||
self._history = [] # clear history
|
||||
self.read_status() # poll our status to keep it updated
|
||||
# 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 (this will also set fast poll)
|
||||
return self.read_target()
|
||||
|
||||
def _hw_wait(self):
|
||||
while super(AnalogOutput, self).read_status()[0] == self.Status.BUSY:
|
||||
while super().read_status()[0] == self.Status.BUSY:
|
||||
sleep(0.3)
|
||||
|
||||
def stop(self):
|
||||
@ -597,8 +605,7 @@ class Actuator(AnalogOutput):
|
||||
readonly=False, datatype=FloatRange(0, unit='$/s'),
|
||||
)
|
||||
ramp = Parameter('The speed of changing the value',
|
||||
readonly=False, datatype=FloatRange(0, unit='$/s'),
|
||||
poll=30,
|
||||
readonly=False, datatype=FloatRange(0, unit='$/min'),
|
||||
)
|
||||
|
||||
def read_speed(self):
|
||||
@ -677,17 +684,22 @@ class TemperatureController(Actuator):
|
||||
)
|
||||
pid = Parameter('pid control Parameters',
|
||||
datatype=TupleOf(FloatRange(), FloatRange(), FloatRange()),
|
||||
readonly=False, group='pid', poll=30,
|
||||
readonly=False, group='pid',
|
||||
)
|
||||
setpoint = Parameter('Current setpoint', datatype=FloatRange(unit='$'), poll=1,
|
||||
setpoint = Parameter('Current setpoint', datatype=FloatRange(unit='$'),
|
||||
)
|
||||
heateroutput = Parameter('Heater output', datatype=FloatRange(), poll=1,
|
||||
heateroutput = Parameter('Heater output', datatype=FloatRange(),
|
||||
)
|
||||
|
||||
# overrides
|
||||
precision = Parameter(default=0.1)
|
||||
ramp = Parameter(description='Temperature ramp')
|
||||
|
||||
def doPoll(self):
|
||||
super().doPoll()
|
||||
self.read_setpoint()
|
||||
self.read_heateroutput()
|
||||
|
||||
def read_ramp(self):
|
||||
return self._dev.ramp
|
||||
|
||||
@ -730,6 +742,10 @@ class TemperatureController(Actuator):
|
||||
def read_heateroutput(self):
|
||||
return self._dev.heaterOutput
|
||||
|
||||
# remove UserCommand setposition from Actuator
|
||||
# (makes no sense for a TemperatureController)
|
||||
setposition = None
|
||||
|
||||
|
||||
class PowerSupply(Actuator):
|
||||
"""A power supply (voltage and current) device.
|
||||
@ -737,13 +753,19 @@ class PowerSupply(Actuator):
|
||||
|
||||
# parameters
|
||||
voltage = Parameter('Actual voltage',
|
||||
datatype=FloatRange(unit='V'), poll=-5)
|
||||
datatype=FloatRange(unit='V'))
|
||||
current = Parameter('Actual current',
|
||||
datatype=FloatRange(unit='A'), poll=-5)
|
||||
datatype=FloatRange(unit='A'))
|
||||
|
||||
# overrides
|
||||
ramp = Parameter(description='Current/voltage ramp')
|
||||
|
||||
def doPoll(self):
|
||||
super().doPoll()
|
||||
# TODO: poll voltage and current faster when busy
|
||||
self.read_voltage()
|
||||
self.read_current()
|
||||
|
||||
def read_ramp(self):
|
||||
return self._dev.ramp
|
||||
|
||||
@ -777,16 +799,18 @@ class NamedDigitalInput(DigitalInput):
|
||||
datatype=StringType(), export=False) # XXX:!!!
|
||||
|
||||
def initModule(self):
|
||||
super(NamedDigitalInput, self).initModule()
|
||||
super().initModule()
|
||||
try:
|
||||
# pylint: disable=eval-used
|
||||
mapping = eval(self.mapping.replace('\n', ' '))
|
||||
mapping = self.mapping
|
||||
if isinstance(mapping, str):
|
||||
# pylint: disable=eval-used
|
||||
mapping = eval(self.mapping.replace('\n', ' '))
|
||||
if isinstance(mapping, str):
|
||||
# pylint: disable=eval-used
|
||||
mapping = eval(mapping)
|
||||
self.accessibles['value'].setProperty('datatype', EnumType('value', **mapping))
|
||||
except Exception as e:
|
||||
raise ValueError('Illegal Value for mapping: %r' % e)
|
||||
raise ValueError('Illegal Value for mapping: %r' % self.mapping) from e
|
||||
|
||||
def read_value(self):
|
||||
value = self._dev.value
|
||||
@ -805,7 +829,7 @@ class PartialDigitalInput(NamedDigitalInput):
|
||||
datatype=IntRange(0), default=1)
|
||||
|
||||
def initModule(self):
|
||||
super(PartialDigitalInput, self).initModule()
|
||||
super().initModule()
|
||||
self._mask = (1 << self.bitwidth) - 1
|
||||
# self.accessibles['value'].datatype = IntRange(0, self._mask)
|
||||
|
||||
@ -827,9 +851,16 @@ 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):
|
||||
attrObj = self._dev.read_attribute('value')
|
||||
@ -845,22 +876,25 @@ class NamedDigitalOutput(DigitalOutput):
|
||||
datatype=StringType(), export=False)
|
||||
|
||||
def initModule(self):
|
||||
super(NamedDigitalOutput, self).initModule()
|
||||
super().initModule()
|
||||
try:
|
||||
# pylint: disable=eval-used
|
||||
mapping = eval(self.mapping.replace('\n', ' '))
|
||||
mapping = self.mapping
|
||||
if isinstance(mapping, str):
|
||||
# pylint: disable=eval-used
|
||||
mapping = eval(self.mapping.replace('\n', ' '))
|
||||
if isinstance(mapping, str):
|
||||
# pylint: disable=eval-used
|
||||
mapping = eval(mapping)
|
||||
self.accessibles['value'].setProperty('datatype', EnumType('value', **mapping))
|
||||
self.accessibles['target'].setProperty('datatype', EnumType('target', **mapping))
|
||||
except Exception as e:
|
||||
raise ValueError('Illegal Value for mapping: %r' % e)
|
||||
raise ValueError('Illegal Value for mapping: %r' % self.mapping) from e
|
||||
|
||||
def write_target(self, value):
|
||||
# map from enum-str to integer value
|
||||
self._dev.value = int(value)
|
||||
self.read_value()
|
||||
return self.read_target()
|
||||
|
||||
|
||||
class PartialDigitalOutput(NamedDigitalOutput):
|
||||
@ -875,7 +909,7 @@ class PartialDigitalOutput(NamedDigitalOutput):
|
||||
datatype=IntRange(0), default=1)
|
||||
|
||||
def initModule(self):
|
||||
super(PartialDigitalOutput, self).initModule()
|
||||
super().initModule()
|
||||
self._mask = (1 << self.bitwidth) - 1
|
||||
# self.accessibles['value'].datatype = IntRange(0, self._mask)
|
||||
# self.accessibles['target'].datatype = IntRange(0, self._mask)
|
||||
@ -891,6 +925,7 @@ class PartialDigitalOutput(NamedDigitalOutput):
|
||||
(value << self.startbit)
|
||||
self._dev.value = newvalue
|
||||
self.read_value()
|
||||
return self.read_target()
|
||||
|
||||
|
||||
class StringIO(PyTangoDevice, Module):
|
||||
|
Reference in New Issue
Block a user