diff --git a/frappy_psi/mixins.py b/frappy_psi/mixins.py index 0fcfc1e..b44df3e 100644 --- a/frappy_psi/mixins.py +++ b/frappy_psi/mixins.py @@ -33,54 +33,64 @@ class HasRamp: # make sure it is a drivable status = 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) - setpoint = Parameter('ramping setpoint', FloatRange()) - maxlag = Parameter('max lag between setpoint and value', - FloatRange(0, unit='s'), default=60, readonly=False) + setpoint = Parameter('ramping setpoint', FloatRange(unit='$'), readonly=False) + maxdif = Parameter('''max. difference between setpoint and value + + 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'), default=1, readonly=False) + workingramp = Parameter('effective ramp', FloatRange(unit='$/min')) + _ramp_status = None _last_time = None - - def checkProperties(self): - unit = self.parameters['value'].datatype.unit - self.parameters['setpoint'].setProperty('unit', unit) - self.writeDict['setpoint'] = self.parameters['setpoint'].default - super().checkProperties() + _buffer = 0 def doPoll(self): super().doPoll() # suppose that this is reading value and status 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): now = time.time() setpoint = self.setpoint if self._ramp_status is not None: if setpoint == target: self._ramp_status = None # at target + self.workingramp = 0 else: 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 - ramp_sec = self.ramp / 60.0 - if dif < self.maxlag * ramp_sec: - setpoint += delay * sign * ramp_sec + maxdif = self.maxdif or self.ramp + if dif < maxdif: + # 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: - self.setpoint = target + self.write_setpoint(setpoint) + self.workingramp = 0 self._ramp_status = None # at target else: - self.setpoint = setpoint + if ramp != self.workingramp: + self.workingramp = sign * ramp + self.write_setpoint(setpoint) self._ramp_status = 'ramping' - super().write_target(self.setpoint) else: self._ramp_status = 'holding' - self._last_time = now + self._last_point = now, self.value self.read_status() def read_status(self): @@ -106,22 +116,24 @@ class HasRamp: if self._ramp_status: self.write_target(self.target) + def write_setpoint(self, setpoint): + super().write_target(setpoint) + return setpoint + def read_target(self): if not self._ramp_status: return super().read_target() return self.target def write_target(self, target): - if not self.ramp_used: - self._ramp_status = None - self.setpoint = target - super().write_target(target) + if self.ramp_used: + if self.parameters['setpoint'].readerror: + self.write_setpoint(self.read_value()) + self._ramp_status = 'changed target' + self._last_time = time.time() + self.setFastPoll(True, self.rampinterval) + self.ramp_step(target) return target - self._ramp_status = 'changed target' - v = self.read_value() - 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) + self._ramp_status = None + self.write_setpoint(target) return target diff --git a/frappy_psi/parmod.py b/frappy_psi/parmod.py index 54a8506..58c3b3f 100644 --- a/frappy_psi/parmod.py +++ b/frappy_psi/parmod.py @@ -22,13 +22,16 @@ """modules to access parameters""" -from frappy.core import Drivable, IDLE, Attached, StringType, Property, Proxy -from frappy.mixins import HasRamp +from frappy.core import Drivable, IDLE, Attached, StringType, Property, \ + Parameter, FloatRange from frappy.errors import ConfigError from frappy_psi.convergence import HasConvergence +from frappy_psi.mixins import HasRamp class Driv(Drivable): + value = Parameter(datatype=FloatRange(unit='$')) + target = Parameter(datatype=FloatRange(unit='$')) read = Attached(description='. for read') write = Attached(description='. for read') unit = Property('main unit', StringType()) @@ -40,8 +43,7 @@ class Driv(Drivable): super().setProperty(key, value) def checkProperties(self): - self.parameters['value'].setProperty('unit', self.unit) - self.parameters['target'].setProperty('unit', self.unit) + self.applyMainUnit(self.unit) if self.read == self.name or self.write == self.name: raise ConfigError('illegal recursive read/write module') super().checkProperties()