more merges from gerrit
Change-Id: I13441cd8889dd39f74a2dd1a85e75a1b76bb93c8
This commit is contained in:
@ -31,7 +31,7 @@ import math
|
||||
from secop.datatypes import ArrayOf, FloatRange, StringType, StructOf, TupleOf
|
||||
from secop.errors import ConfigError, DisabledError
|
||||
from secop.lib.sequence import SequencerMixin, Step
|
||||
from secop.modules import BasicPoller, Drivable, Parameter
|
||||
from secop.modules import Drivable, Parameter
|
||||
|
||||
|
||||
class GarfieldMagnet(SequencerMixin, Drivable):
|
||||
@ -47,9 +47,6 @@ class GarfieldMagnet(SequencerMixin, Drivable):
|
||||
the symmetry setting selects which.
|
||||
"""
|
||||
|
||||
pollerClass = BasicPoller
|
||||
|
||||
|
||||
# parameters
|
||||
subdev_currentsource = Parameter('(bipolar) Powersupply', datatype=StringType(), readonly=True, export=False)
|
||||
subdev_enable = Parameter('Switch to set for on/off', datatype=StringType(), readonly=True, export=False)
|
||||
@ -57,10 +54,10 @@ class GarfieldMagnet(SequencerMixin, Drivable):
|
||||
subdev_symmetry = Parameter('Switch to read for symmetry', datatype=StringType(), readonly=True, export=False)
|
||||
userlimits = Parameter('User defined limits of device value',
|
||||
datatype=TupleOf(FloatRange(unit='$'), FloatRange(unit='$')),
|
||||
default=(float('-Inf'), float('+Inf')), readonly=False, poll=10)
|
||||
default=(float('-Inf'), float('+Inf')), readonly=False)
|
||||
abslimits = Parameter('Absolute limits of device value',
|
||||
datatype=TupleOf(FloatRange(unit='$'), FloatRange(unit='$')),
|
||||
default=(-0.5, 0.5), poll=True,
|
||||
default=(-0.5, 0.5),
|
||||
)
|
||||
precision = Parameter('Precision of the device value (allowed deviation '
|
||||
'of stable values from target)',
|
||||
@ -71,7 +68,7 @@ class GarfieldMagnet(SequencerMixin, Drivable):
|
||||
calibration = Parameter('Coefficients for calibration '
|
||||
'function: [c0, c1, c2, c3, c4] calculates '
|
||||
'B(I) = c0*I + c1*erf(c2*I) + c3*atan(c4*I)'
|
||||
' in T', poll=1,
|
||||
' in T',
|
||||
datatype=ArrayOf(FloatRange(), 5, 5),
|
||||
default=(1.0, 0.0, 0.0, 0.0, 0.0))
|
||||
calibrationtable = Parameter('Map of Coefficients for calibration per symmetry setting',
|
||||
@ -137,7 +134,7 @@ class GarfieldMagnet(SequencerMixin, Drivable):
|
||||
'_current2field polynome not monotonic!')
|
||||
|
||||
def initModule(self):
|
||||
super(GarfieldMagnet, self).initModule()
|
||||
super().initModule()
|
||||
self._enable = self.DISPATCHER.get_module(self.subdev_enable)
|
||||
self._symmetry = self.DISPATCHER.get_module(self.subdev_symmetry)
|
||||
self._polswitch = self.DISPATCHER.get_module(self.subdev_polswitch)
|
||||
@ -220,7 +217,7 @@ class GarfieldMagnet(SequencerMixin, Drivable):
|
||||
self._currentsource.read_value() *
|
||||
self._get_field_polarity())
|
||||
|
||||
def read_hw_status(self):
|
||||
def readHwStatus(self):
|
||||
# called from SequencerMixin.read_status if no sequence is running
|
||||
if self._enable.value == 'Off':
|
||||
return self.Status.WARN, 'Disabled'
|
||||
|
@ -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