improve HasRamp
Change-Id: I0ddabb8b2681712637a18c392da5424d30a05b4b
This commit is contained in:
parent
459a80b4d2
commit
75f156beef
@ -33,54 +33,64 @@ class HasRamp:
|
|||||||
# make sure it is a drivable
|
# make sure it is a drivable
|
||||||
status = Parameter()
|
status = Parameter()
|
||||||
target = Parameter()
|
target = Parameter()
|
||||||
ramp = Parameter('ramp ratge', FloatRange(0), default=0, readonly=False)
|
ramp = Parameter('ramp rate', FloatRange(0, unit='$/min'), default=0, readonly=False)
|
||||||
ramp_used = Parameter('False: infinite ramp', BoolType(), default=False, readonly=False)
|
ramp_used = Parameter('False: infinite ramp', BoolType(), default=False, readonly=False)
|
||||||
setpoint = Parameter('ramping setpoint', FloatRange())
|
setpoint = Parameter('ramping setpoint', FloatRange(unit='$'), readonly=False)
|
||||||
maxlag = Parameter('max lag between setpoint and value',
|
maxdif = Parameter('''max. difference between setpoint and value
|
||||||
FloatRange(0, unit='s'), default=60, readonly=False)
|
|
||||||
|
stop ramp then value lags behind setpoint by more than maxdif
|
||||||
|
maxdif=0: use 'ramp' value (value lags 1 minute behind setpoint)
|
||||||
|
''',
|
||||||
|
FloatRange(0, unit='$'), default=0, readonly=False)
|
||||||
rampinterval = Parameter('interval for changing the setpoint', FloatRange(0, unit='s'),
|
rampinterval = Parameter('interval for changing the setpoint', FloatRange(0, unit='s'),
|
||||||
default=1, readonly=False)
|
default=1, readonly=False)
|
||||||
|
workingramp = Parameter('effective ramp', FloatRange(unit='$/min'))
|
||||||
|
|
||||||
_ramp_status = None
|
_ramp_status = None
|
||||||
_last_time = None
|
_last_time = None
|
||||||
|
_buffer = 0
|
||||||
def checkProperties(self):
|
|
||||||
unit = self.parameters['value'].datatype.unit
|
|
||||||
self.parameters['setpoint'].setProperty('unit', unit)
|
|
||||||
self.writeDict['setpoint'] = self.parameters['setpoint'].default
|
|
||||||
super().checkProperties()
|
|
||||||
|
|
||||||
def doPoll(self):
|
def doPoll(self):
|
||||||
super().doPoll() # suppose that this is reading value and status
|
super().doPoll() # suppose that this is reading value and status
|
||||||
self.ramp_step(self.target)
|
self.ramp_step(self.target)
|
||||||
|
|
||||||
def write_setpoint(self, value):
|
|
||||||
# written only once at startup
|
|
||||||
self._last_time = time.time()
|
|
||||||
self.setpoint = self.read_value()
|
|
||||||
|
|
||||||
def ramp_step(self, target):
|
def ramp_step(self, target):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
setpoint = self.setpoint
|
setpoint = self.setpoint
|
||||||
if self._ramp_status is not None:
|
if self._ramp_status is not None:
|
||||||
if setpoint == target:
|
if setpoint == target:
|
||||||
self._ramp_status = None # at target
|
self._ramp_status = None # at target
|
||||||
|
self.workingramp = 0
|
||||||
else:
|
else:
|
||||||
sign = copysign(1, target - setpoint)
|
sign = copysign(1, target - setpoint)
|
||||||
delay = now - self._last_time
|
prev_t, prev_v = self._last_point
|
||||||
|
if self.value == prev_v:
|
||||||
|
return # no reads happened
|
||||||
|
delay = (now - prev_t) / 60.0 # minutes !
|
||||||
|
slope = (self.value - prev_v) / max(1e-5, delay)
|
||||||
dif = (setpoint - self.value) * sign
|
dif = (setpoint - self.value) * sign
|
||||||
ramp_sec = self.ramp / 60.0
|
maxdif = self.maxdif or self.ramp
|
||||||
if dif < self.maxlag * ramp_sec:
|
if dif < maxdif:
|
||||||
setpoint += delay * sign * ramp_sec
|
# reduce ramp when slope is bigger than ramp
|
||||||
|
ramp = max(2 * self.ramp - sign * slope, 0) + self._buffer
|
||||||
|
if ramp > self.ramp:
|
||||||
|
self._buffer = min(ramp - self.ramp, self.ramp)
|
||||||
|
ramp = self.ramp
|
||||||
|
else:
|
||||||
|
self._buffer = 0
|
||||||
|
setpoint += sign * delay * ramp
|
||||||
if sign * (setpoint - target) >= 0:
|
if sign * (setpoint - target) >= 0:
|
||||||
self.setpoint = target
|
self.write_setpoint(setpoint)
|
||||||
|
self.workingramp = 0
|
||||||
self._ramp_status = None # at target
|
self._ramp_status = None # at target
|
||||||
else:
|
else:
|
||||||
self.setpoint = setpoint
|
if ramp != self.workingramp:
|
||||||
|
self.workingramp = sign * ramp
|
||||||
|
self.write_setpoint(setpoint)
|
||||||
self._ramp_status = 'ramping'
|
self._ramp_status = 'ramping'
|
||||||
super().write_target(self.setpoint)
|
|
||||||
else:
|
else:
|
||||||
self._ramp_status = 'holding'
|
self._ramp_status = 'holding'
|
||||||
self._last_time = now
|
self._last_point = now, self.value
|
||||||
self.read_status()
|
self.read_status()
|
||||||
|
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
@ -106,22 +116,24 @@ class HasRamp:
|
|||||||
if self._ramp_status:
|
if self._ramp_status:
|
||||||
self.write_target(self.target)
|
self.write_target(self.target)
|
||||||
|
|
||||||
|
def write_setpoint(self, setpoint):
|
||||||
|
super().write_target(setpoint)
|
||||||
|
return setpoint
|
||||||
|
|
||||||
def read_target(self):
|
def read_target(self):
|
||||||
if not self._ramp_status:
|
if not self._ramp_status:
|
||||||
return super().read_target()
|
return super().read_target()
|
||||||
return self.target
|
return self.target
|
||||||
|
|
||||||
def write_target(self, target):
|
def write_target(self, target):
|
||||||
if not self.ramp_used:
|
if self.ramp_used:
|
||||||
self._ramp_status = None
|
if self.parameters['setpoint'].readerror:
|
||||||
self.setpoint = target
|
self.write_setpoint(self.read_value())
|
||||||
super().write_target(target)
|
self._ramp_status = 'changed target'
|
||||||
|
self._last_time = time.time()
|
||||||
|
self.setFastPoll(True, self.rampinterval)
|
||||||
|
self.ramp_step(target)
|
||||||
return target
|
return target
|
||||||
self._ramp_status = 'changed target'
|
self._ramp_status = None
|
||||||
v = self.read_value()
|
self.write_setpoint(target)
|
||||||
maxdif = self.maxlag * self.ramp / 60
|
|
||||||
# setpoint must not differ too much from value
|
|
||||||
self.setpoint = clamp(v - maxdif, self.setpoint, v + maxdif)
|
|
||||||
self.setFastPoll(True, self.rampinterval)
|
|
||||||
self.ramp_step(target)
|
|
||||||
return target
|
return target
|
||||||
|
@ -22,13 +22,16 @@
|
|||||||
|
|
||||||
"""modules to access parameters"""
|
"""modules to access parameters"""
|
||||||
|
|
||||||
from frappy.core import Drivable, IDLE, Attached, StringType, Property, Proxy
|
from frappy.core import Drivable, IDLE, Attached, StringType, Property, \
|
||||||
from frappy.mixins import HasRamp
|
Parameter, FloatRange
|
||||||
from frappy.errors import ConfigError
|
from frappy.errors import ConfigError
|
||||||
from frappy_psi.convergence import HasConvergence
|
from frappy_psi.convergence import HasConvergence
|
||||||
|
from frappy_psi.mixins import HasRamp
|
||||||
|
|
||||||
|
|
||||||
class Driv(Drivable):
|
class Driv(Drivable):
|
||||||
|
value = Parameter(datatype=FloatRange(unit='$'))
|
||||||
|
target = Parameter(datatype=FloatRange(unit='$'))
|
||||||
read = Attached(description='<module>.<parameter> for read')
|
read = Attached(description='<module>.<parameter> for read')
|
||||||
write = Attached(description='<module>.<parameter> for read')
|
write = Attached(description='<module>.<parameter> for read')
|
||||||
unit = Property('main unit', StringType())
|
unit = Property('main unit', StringType())
|
||||||
@ -40,8 +43,7 @@ class Driv(Drivable):
|
|||||||
super().setProperty(key, value)
|
super().setProperty(key, value)
|
||||||
|
|
||||||
def checkProperties(self):
|
def checkProperties(self):
|
||||||
self.parameters['value'].setProperty('unit', self.unit)
|
self.applyMainUnit(self.unit)
|
||||||
self.parameters['target'].setProperty('unit', self.unit)
|
|
||||||
if self.read == self.name or self.write == self.name:
|
if self.read == self.name or self.write == self.name:
|
||||||
raise ConfigError('illegal recursive read/write module')
|
raise ConfigError('illegal recursive read/write module')
|
||||||
super().checkProperties()
|
super().checkProperties()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user