further work on needle valve, pump and lakeshore
This commit is contained in:
@ -23,20 +23,21 @@
|
||||
import time
|
||||
import math
|
||||
from frappy.lib.enum import Enum
|
||||
from frappy.lib import clamp
|
||||
# the most common Frappy classes can be imported from frappy.core
|
||||
from frappy.core import HasIO, Parameter, Command, Readable, Writable, Drivable, \
|
||||
Property, StringIO, BUSY, IDLE, WARN, ERROR, DISABLED, Attached
|
||||
Property, StringIO, BUSY, IDLE, WARN, ERROR, DISABLED, Attached, nopoll
|
||||
from frappy.datatypes import BoolType, EnumType, FloatRange, StructOf, \
|
||||
StatusType, IntRange, StringType, TupleOf
|
||||
from frappy.errors import CommunicationFailedError
|
||||
from frappy.states import HasStates, status_code, Retry
|
||||
|
||||
|
||||
M = Enum(idle=0, opening=1, closing=2, opened=3, closed=5, no_motor=6)
|
||||
M = Enum(idle=0, opening=1, closing=2, opened=3, closed=4, no_motor=5)
|
||||
A = Enum(disabled=0, manual=1, auto=2)
|
||||
|
||||
|
||||
class CCU4IO(StringIO):
|
||||
class IO(StringIO):
|
||||
"""communication with CCU4"""
|
||||
# for completeness: (not needed, as it is the default)
|
||||
end_of_line = '\n'
|
||||
@ -44,8 +45,8 @@ class CCU4IO(StringIO):
|
||||
identification = [('cid', r'cid=CCU4.*')]
|
||||
|
||||
|
||||
class CCU4Base(HasIO):
|
||||
ioClass = CCU4IO
|
||||
class Base(HasIO):
|
||||
ioClass = IO
|
||||
|
||||
def command(self, **kwds):
|
||||
"""send a command and get the response
|
||||
@ -79,7 +80,7 @@ class CCU4Base(HasIO):
|
||||
return result
|
||||
|
||||
|
||||
class HeLevel(CCU4Base, Readable):
|
||||
class HeLevel(Base, Readable):
|
||||
"""He Level channel of CCU4"""
|
||||
|
||||
value = Parameter(unit='%')
|
||||
@ -121,10 +122,10 @@ class HeLevel(CCU4Base, Readable):
|
||||
return self.command(hfu=value)
|
||||
|
||||
|
||||
class Valve(CCU4Base, Writable):
|
||||
class Valve(Base, Writable):
|
||||
value = Parameter('relay state', BoolType())
|
||||
target = Parameter('relay target', BoolType())
|
||||
ioClass = CCU4IO
|
||||
ioClass = IO
|
||||
STATE_MAP = {0: (0, (IDLE, 'off')),
|
||||
1: (1, (IDLE, 'on')),
|
||||
2: (0, (ERROR, 'no valve')),
|
||||
@ -173,7 +174,7 @@ class N2TempSensor(Readable):
|
||||
value = Parameter('LN2 T sensor', FloatRange(unit='K'), default=0)
|
||||
|
||||
|
||||
class N2Level(CCU4Base, Readable):
|
||||
class N2Level(Base, Readable):
|
||||
valve = Attached(Writable, mandatory=False)
|
||||
lower = Attached(Readable, mandatory=False)
|
||||
upper = Attached(Readable, mandatory=False)
|
||||
@ -285,138 +286,320 @@ class N2Level(CCU4Base, Readable):
|
||||
self.command(nc=0)
|
||||
|
||||
|
||||
class FlowPressure(CCU4Base, Readable):
|
||||
class HasFilter:
|
||||
__value1 = None
|
||||
__value = None
|
||||
__last = None
|
||||
|
||||
def filter(self, filter_time, value):
|
||||
now = time.time()
|
||||
if self.__value is None:
|
||||
self.__last = now
|
||||
self.__value1 = value
|
||||
self.__value = value
|
||||
weight = (now - self.__last) / filter_time
|
||||
self.__value1 += weight * (value - self.__value)
|
||||
self.__value += weight * (self.__value1 - self.__value)
|
||||
self.__last = now
|
||||
return self.__value
|
||||
|
||||
|
||||
class Pressure(HasFilter, Base, Readable):
|
||||
value = Parameter(unit='mbar')
|
||||
mbar_offset = Parameter(unit='mbar', default=0.8, readonly=False)
|
||||
mbar_offset = Parameter('offset in mbar', FloatRange(unit='mbar'), default=0.8, readonly=False)
|
||||
filter_time = Parameter('filter time', FloatRange(unit='sec'), readonly=False, default=3)
|
||||
pollinterval = Parameter(default=0.25)
|
||||
|
||||
def read_value(self):
|
||||
return self.filter(self.command(f=float)) - self.mbar_offset
|
||||
return self.filter(self.filter_time, self.command(f=float)) - self.mbar_offset
|
||||
|
||||
|
||||
class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
flow = Attached(Readable, mandatory=False)
|
||||
flow_pressure = Attached(Readable, mandatory=False)
|
||||
class NeedleValveFlow(HasStates, Base, Drivable):
|
||||
flow_sensor = Attached(Readable, mandatory=False)
|
||||
pressure = Attached(Pressure, mandatory=False)
|
||||
use_pressure = Parameter('flag (use pressure instead of flow meter)', BoolType(),
|
||||
readonly=False, default=False)
|
||||
lnm_per_mbar = Parameter('scale factor', FloatRange(unit='lnm/mbar'), readonly=False, default=0.6)
|
||||
|
||||
value = Parameter(unit='ln/min')
|
||||
target = Parameter(unit='ln/min')
|
||||
|
||||
lnm_per_mbar = Parameter(unit='ln/min/mbar', default=0.6, readonly=False)
|
||||
use_pressure = Parameter('use flow from pressure', BoolType(),
|
||||
default=False, readonly=False)
|
||||
motor_state = Parameter('motor_state', EnumType(M))
|
||||
motor_state = Parameter('motor_state', EnumType(M), default=0)
|
||||
tolerance = Parameter('tolerance', FloatRange(0), value=0.25, readonly=False)
|
||||
tolerance2 = Parameter('tolerance limit above 2 lnm', FloatRange(0), value=0.5, readonly=False)
|
||||
prop = Parameter('proportional term', FloatRange(unit='s/lnm'), readonly=False)
|
||||
prop = Parameter('proportional term', FloatRange(unit='s/lnm'), readonly=False, default=0.001)
|
||||
deriv = Parameter('min progress time constant', FloatRange(unit='s'),
|
||||
default=30, readonly=False)
|
||||
settle = Parameter('time within tolerance before getting quiet', FloatRange(unit='s'),
|
||||
default=30, readonly=False)
|
||||
step_factor = Parameter('factor (no progress time) / (min step size)', FloatRange(), default=300)
|
||||
control_active = Parameter('control active flag', BoolType(), readonly=False)
|
||||
pollinterval = Parameter(default=1)
|
||||
step_factor = Parameter('factor (no progress time) / (min step size)', FloatRange(), default=300, readonly=False)
|
||||
control_active = Parameter('control active flag', BoolType(), readonly=False, default=1)
|
||||
min_open_step = Parameter('minimal open step', FloatRange(unit='s'), readonly=False, default=0.06)
|
||||
min_close_step = Parameter('minimal close step', FloatRange(unit='s'), readonly=False, default=0.05)
|
||||
raw_open_step = Parameter('step after direction change', FloatRange(unit='s'), readonly=False, default=0.12)
|
||||
raw_close_step = Parameter('step after direction change', FloatRange(unit='s'), readonly=False, default=0.04)
|
||||
pollinterval = Parameter(default=5)
|
||||
_ref_time = 0
|
||||
_ref_dif = 0
|
||||
_last_cycle = 0
|
||||
_last_progress = 0
|
||||
_dir = 0
|
||||
_rawdir = 0
|
||||
_step = 0
|
||||
|
||||
def initModule(self):
|
||||
if self.pressure:
|
||||
self.pressure.addCallback('value', self.update_from_pressure)
|
||||
if self.flow_sensor:
|
||||
self.flow_sensor.addCallback('value', self.update_from_flow)
|
||||
super().initModule()
|
||||
if self.flow_pressure:
|
||||
self.flow_pressure.addCallback('value', self.update_flow_pressure)
|
||||
if self.flow:
|
||||
self.flow.addCallback('value', self.update_flow)
|
||||
self.write_tolerance(self.tolerance)
|
||||
|
||||
def update_from_flow(self, value):
|
||||
if not self.use_pressure:
|
||||
self.value = value
|
||||
# self.cycle_machine()
|
||||
|
||||
def update_from_pressure(self, value):
|
||||
if self.use_pressure:
|
||||
self.value = value * self.lnm_per_mbar
|
||||
# self.cycle_machine()
|
||||
|
||||
# def doPoll(self):
|
||||
# """only the updates should trigger the machine"""
|
||||
|
||||
def read_value(self):
|
||||
p = self.pressure.read_value() * self.lnm_per_mbar
|
||||
f = self.flow_sensor.read_value()
|
||||
# self.log.info('p %g f %g +- %.2g', p, f, self.flow_sensor.stddev)
|
||||
self.read_motor_state()
|
||||
return p if self.use_pressure else f
|
||||
|
||||
def write_tolerance(self, tolerance):
|
||||
if hasattr(self.flow_pressure, 'tolerance'):
|
||||
self.flow_pressure.tolerance = tolerance / self.lnm_per_mbar
|
||||
if hasattr(self.flow, 'tolerance'):
|
||||
self.flow.tolerance = tolerance
|
||||
if hasattr(self.pressure, 'tolerance'):
|
||||
self.pressure.tolerance = tolerance / self.lnm_per_mbar
|
||||
if hasattr(self.flow_sensor, 'tolerance'):
|
||||
self.flow_sensor.tolerance = tolerance
|
||||
|
||||
def read_use_pressure(self):
|
||||
if self.flow_pressure:
|
||||
if self.flow:
|
||||
if self.pressure:
|
||||
if self.flow_sensor:
|
||||
return self.use_pressure
|
||||
return True
|
||||
return False
|
||||
|
||||
def update_flow(self, value):
|
||||
if not self.use_pressure:
|
||||
self.value = value
|
||||
self.cycle_machine()
|
||||
|
||||
def update_flow_pressure(self, value):
|
||||
if self.use_pressure:
|
||||
self.value = value * self.lnm_per_mbar
|
||||
self.cycle_machine()
|
||||
|
||||
def write_target(self, value):
|
||||
self.start_machine(self.controlling, in_tol_time=0,
|
||||
ref_time=0, ref_dif=0, prev_dif=0)
|
||||
self.start_machine(self.change_target, fast_poll=1)
|
||||
|
||||
@status_code(BUSY)
|
||||
def unblock_from_open(self, state):
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.motor_state == 'opened':
|
||||
self.command(mp=-60)
|
||||
return Retry
|
||||
if self.motor_state == 'closing':
|
||||
return Retry
|
||||
if self.motor_state == 'closed':
|
||||
if self.value > max(1, self.target):
|
||||
return Retry
|
||||
state.flow_before = self.value
|
||||
state.wiggle = 1
|
||||
state.start_wiggle = state.now
|
||||
self.command(mp=60)
|
||||
return self.unblock_open
|
||||
return self.approaching
|
||||
def change_target(self, state):
|
||||
state.in_tol_time = 0
|
||||
state.last_minstep = {}
|
||||
state.last_progress = state.now
|
||||
state.ref_time = 0
|
||||
state.ref_dif = 0
|
||||
state.prev_dif = 0 # used?
|
||||
state.last_close_time = 0
|
||||
state.last_pulse_time = 0
|
||||
state.raw_fact = 1
|
||||
state.raw_step = 0
|
||||
if abs(self.target - self.value) < self._tolerance():
|
||||
return self.at_target
|
||||
return self.raw_control
|
||||
|
||||
def start_direction(self, state):
|
||||
if self.target > self.value:
|
||||
self._dir = 1
|
||||
state.minstep = self.min_open_step
|
||||
else:
|
||||
self._dir = -1
|
||||
state.minstep = self.min_close_step
|
||||
state.prev = []
|
||||
|
||||
def perform_pulse(self, state):
|
||||
tol = self._tolerance()
|
||||
dif = self.target - self.value
|
||||
difdir = dif * self._dir
|
||||
state.last_pulse_time = state.now
|
||||
if difdir > tol:
|
||||
step = state.minstep + (difdir - tol) * self.prop
|
||||
elif difdir > 0:
|
||||
step = state.minstep * difdir / tol
|
||||
else:
|
||||
return
|
||||
self.log.info('MP %g dif=%g tol=%g', step * self._dir, dif, tol)
|
||||
self.command(mp=clamp(-1, step * self._dir, 1))
|
||||
|
||||
@status_code(BUSY)
|
||||
def unblock_open(self, state):
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.value < state.flow_before:
|
||||
state.flow_before_open = self.value
|
||||
elif self.value > state.flow_before + 1:
|
||||
state.wiggle = -state.wiggle / 2
|
||||
self.command(mp=state.wiggle)
|
||||
state.start_wiggle = state.now
|
||||
return self.unblock_close
|
||||
if self.motor_state == 'opening':
|
||||
def raw_control(self, state):
|
||||
tol = self._tolerance()
|
||||
if state.init:
|
||||
self.start_direction(state)
|
||||
state.raw_step = self.raw_open_step if self._dir > 0 else -self.raw_close_step
|
||||
state.raw_fact = 1
|
||||
# if self.read_motor_state() == M.closed:
|
||||
# # TODO: also check for flow near lower limit ? but only once after change_target
|
||||
# self.log.info('start with fast opening')
|
||||
# state.raw_step = 1
|
||||
# self._dir = 1
|
||||
difdir = (self.target - self.value) * self._dir
|
||||
state.prev.append(difdir)
|
||||
state.diflim = max(0, difdir - tol * 1)
|
||||
state.success = 0
|
||||
self.command(mp=state.raw_step)
|
||||
self.log.info('first rawstep %g', state.raw_step)
|
||||
state.last_pulse_time = state.now
|
||||
state.raw_pulse_cnt = 0
|
||||
state.cycle_cnt = 0
|
||||
return Retry
|
||||
if self.motor_state == 'idle':
|
||||
self.command(mp=state.wiggle)
|
||||
difdir = (self.target - self.value) * self._dir
|
||||
state.cycle_cnt += 1
|
||||
state.prev.append(difdir)
|
||||
del state.prev[:-5]
|
||||
if state.prev[-1] > max(state.prev[:-1]):
|
||||
# TODO: use the amount of overshoot to reduce the raw_step
|
||||
state.cycle_cnt = 0
|
||||
self.log.info('difference is increasing %s', ' '.join(f'{v:g}' for v in state.prev))
|
||||
return Retry
|
||||
if self.motor_state == 'opened':
|
||||
if state.now < state.start_wiggle + 20:
|
||||
return Retry
|
||||
return self.final_status(ERROR, 'can not open')
|
||||
if state.cycle_cnt >= 5:
|
||||
state.cycle_cnt = 0
|
||||
state.diflim = max(tol, min(state.prev) - tol * 0.5)
|
||||
state.raw_pulse_cnt += 1
|
||||
self.command(mp=state.raw_step * state.raw_fact)
|
||||
self.log.info('rawstep %g', state.raw_step)
|
||||
if state.raw_pulse_cnt % 5 == 0 and state.raw_pulse_cnt > 5:
|
||||
state.raw_fact *= 1.25
|
||||
return Retry
|
||||
if difdir >= state.diflim:
|
||||
state.success = max(0, state.success - 1)
|
||||
return Retry
|
||||
state.success += 1
|
||||
if state.success <= 3:
|
||||
return Retry
|
||||
if state.raw_pulse_cnt < 3:
|
||||
state.raw_fact = 1 - (3 - state.raw_pulse_cnt) ** 2 * 0.05
|
||||
if state.raw_fact != 1:
|
||||
if self._dir > 0:
|
||||
self.raw_open_step *= state.raw_fact
|
||||
self.log.info('raw_open_step %g f=%g', self.raw_open_step, state.raw_fact)
|
||||
self.min_open_pulse = min(self.min_open_pulse, self.raw_open_step)
|
||||
else:
|
||||
self.raw_close_step *= state.raw_fact
|
||||
self.log.info('raw_close_step %g f=%g', self.raw_close_step, state.raw_fact)
|
||||
self.min_close_pulse = min(self.min_close_pulse, self.raw_close_step)
|
||||
return self.controlling
|
||||
|
||||
# @status_code(BUSY)
|
||||
# def raw_control(self, state):
|
||||
# tol = self._tolerance()
|
||||
# if state.init:
|
||||
# self.start_direction(state)
|
||||
# if self._dir != self._rawdir:
|
||||
# self._rawdir = self._dir
|
||||
# state.first_step = self.first_open_step if self._dir > 0 else -self.first_close_step
|
||||
# else:
|
||||
# state.first_step = 0
|
||||
# state.first_fact = 1
|
||||
# # if self.read_motor_state() == M.closed:
|
||||
# # # TODO: also check for flow near lower limit ? but only once after change_target
|
||||
# # self.log.info('start with fast opening')
|
||||
# # state.first_step = 1
|
||||
# # self._dir = 1
|
||||
# difdir = (self.target - self.value) * self._dir
|
||||
# state.prev = [difdir]
|
||||
# state.diflim = max(0, difdir - tol * 0.5)
|
||||
# state.success = 0
|
||||
# if state.first_step:
|
||||
# self.command(mp=state.first_step)
|
||||
# else:
|
||||
# self.perform_pulse(state)
|
||||
# self.log.info('firststep %g', state.first_step)
|
||||
# state.last_pulse_time = state.now
|
||||
# state.raw_pulse_cnt = 0
|
||||
# return Retry
|
||||
# difdir = (self.target - self.value) * self._dir
|
||||
# if state.delta(5):
|
||||
# state.diflim = max(0, min(state.prev) - tol * 0.1)
|
||||
# state.prev = [difdir]
|
||||
# state.raw_pulse_cnt += 1
|
||||
# if state.first_step and state.raw_pulse_cnt % 10 == 0:
|
||||
# self.command(mp=state.first_step * state.first_fact)
|
||||
# self.log.info('repeat firststep %g', state.first_step * state.first_fact)
|
||||
# state.first_fact *= 1.25
|
||||
# else:
|
||||
# self.perform_pulse(state)
|
||||
# return Retry
|
||||
# state.prev.append(difdir)
|
||||
# if difdir >= state.diflim:
|
||||
# state.success = max(0, state.success - 1)
|
||||
# return Retry
|
||||
# state.success += 1
|
||||
# if state.success <= 5:
|
||||
# return Retry
|
||||
# if state.first_step:
|
||||
# if state.raw_pulse_cnt < 3:
|
||||
# state.first_fact = 1 - (3 - state.raw_pulse_cnt) ** 2 * 0.04
|
||||
# if state.first_fact != 1:
|
||||
# if self._dir > 0:
|
||||
# self.first_open_step *= state.first_fact
|
||||
# self.log.info('first_open_step %g f=%g', self.first_open_step, state.first_fact)
|
||||
# else:
|
||||
# self.first_close_step *= state.first_fact
|
||||
# self.log.info('first_close_step %g f=%g', self.first_close_step, state.first_fact)
|
||||
# return self.controlling
|
||||
|
||||
@status_code(BUSY)
|
||||
def unblock_close(self, state):
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.value > state.flow_before:
|
||||
state.flow_before_open = self.value
|
||||
elif self.value < state.flow_before - 1:
|
||||
if state.wiggle < self.prop * 2:
|
||||
return self.final_status(IDLE, '')
|
||||
state.wiggle = -state.wiggle / 2
|
||||
self.command(mp=state.wiggle)
|
||||
state.start_wiggle = state.now
|
||||
return self.unblock_open
|
||||
if self.motor_state == 'closing':
|
||||
def controlling(self, state):
|
||||
dif = self.target - self.value
|
||||
if state.init:
|
||||
self.start_direction(state)
|
||||
state.ref_dif = abs(dif)
|
||||
state.ref_time = state.now
|
||||
state.in_tol_time = 0
|
||||
difdir = dif * self._dir # negative when overshoot happend
|
||||
# difdif = dif - state.prev_dif
|
||||
# state.prev_dif = dif
|
||||
expected_dif = state.ref_dif * math.exp((state.ref_time - state.now) / self.deriv)
|
||||
|
||||
tol = self._tolerance()
|
||||
if difdir < tol:
|
||||
# prev_minstep = state.last_minstep.pop(self._dir, None)
|
||||
# attr = 'min_open_step' if self._dir > 0 else 'min_close_step'
|
||||
# if prev_minstep is not None:
|
||||
# # increase minstep
|
||||
# minstep = getattr(self, attr)
|
||||
# setattr(self, attr, minstep * 1.1)
|
||||
# self.log.info('increase %s to %g', attr, minstep)
|
||||
if difdir > -tol: # within tolerance
|
||||
delta = state.delta()
|
||||
state.in_tol_time += delta
|
||||
if state.in_tol_time > self.settle:
|
||||
# state.last_minstep.pop(self._dir, None)
|
||||
self.log.info('at target %g %g', dif, tol)
|
||||
return self.at_target
|
||||
if difdir < 0:
|
||||
return Retry
|
||||
# self.log.info('minstep=0 dif=%g', dif)
|
||||
else: # overshoot
|
||||
self.log.info('overshoot %g', dif)
|
||||
return self.raw_control
|
||||
# # overshoot
|
||||
# prev_minstep = state.last_minstep.pop(self._dir, None)
|
||||
# if prev_minstep is None:
|
||||
# minstep = getattr(self, attr) * 0.9
|
||||
# self.log.info('decrease %s to %g', attr, minstep)
|
||||
# setattr(self, attr, minstep)
|
||||
# self.start_step(state, self.target)
|
||||
# still approaching
|
||||
if difdir <= expected_dif:
|
||||
if difdir < expected_dif / 1.25 - tol:
|
||||
state.ref_time = state.now
|
||||
state.ref_dif = (difdir + tol) * 1.25
|
||||
# self.log.info('new ref %g', state.ref_dif)
|
||||
state.last_progress = state.now
|
||||
return Retry # progressing: no pulse needed
|
||||
if state.now < state.last_pulse_time + 2.5:
|
||||
return Retry
|
||||
if self.motor_state == 'idle':
|
||||
self.command(mp=state.wiggle)
|
||||
return Retry
|
||||
if self.motor_state == 'closed':
|
||||
if state.now < state.start_wiggle + 20:
|
||||
return Retry
|
||||
return self.final_status(ERROR, 'can not close')
|
||||
return self.final_status(WARN, 'unblock interrupted')
|
||||
# TODO: check motor state for closed / opened ?
|
||||
self.perform_pulse(state)
|
||||
return Retry
|
||||
|
||||
def _tolerance(self):
|
||||
return min(self.tolerance * min(1, self.value / 2), self.tolerance2)
|
||||
@ -426,48 +609,67 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
dif = self.target - self.value
|
||||
if abs(dif) > self._tolerance():
|
||||
state.in_tol_time = 0
|
||||
self.log.info('unstable %g', dif)
|
||||
return self.unstable
|
||||
return Retry
|
||||
|
||||
@status_code(IDLE, 'unstable')
|
||||
def unstable(self, state):
|
||||
difdir = (self.target - self.value) * self._dir
|
||||
if difdir < 0 or self._dir == 0:
|
||||
return self.raw_control
|
||||
return self.controlling(state)
|
||||
|
||||
@status_code(BUSY)
|
||||
def controlling(self, state):
|
||||
delta = state.delta(0)
|
||||
dif = self.target - self.value
|
||||
difdif = dif - state.prev_dif
|
||||
state.prev_dif = dif
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.motor_state == 'closed':
|
||||
if dif < 0 or difdif < 0:
|
||||
return Retry
|
||||
return self.unblock_from_open
|
||||
elif self.motor_state == 'opened': # trigger also when flow too high?
|
||||
if dif > 0 or difdif > 0:
|
||||
return Retry
|
||||
self.command(mp=-60)
|
||||
return self.unblock_from_open
|
||||
def read_motor_state(self):
|
||||
return self.command(fm=int)
|
||||
|
||||
tolerance = self._tolerance()
|
||||
if abs(dif) < tolerance:
|
||||
state.in_tol_time += delta
|
||||
if state.in_tol_time > self.settle:
|
||||
return self.at_target
|
||||
@Command
|
||||
def close(self):
|
||||
"""close valve fully"""
|
||||
self.command(mp=-60)
|
||||
self.motor_state = self.command(fm=int)
|
||||
self.start_machine(self.closing)
|
||||
|
||||
@status_code(BUSY)
|
||||
def closing(self, state):
|
||||
if state.init:
|
||||
state.start_time = state.now
|
||||
self.read_motor_state()
|
||||
if self.motor_state == M.closing:
|
||||
return Retry
|
||||
expected_dif = state.ref_dif * math.exp((state.now - state.ref_time) / self.deriv)
|
||||
if abs(dif) < expected_dif:
|
||||
if abs(dif) < expected_dif / 1.25:
|
||||
state.ref_time = state.now
|
||||
state.ref_dif = abs(dif) * 1.25
|
||||
state.last_progress = state.now
|
||||
return Retry # progress is fast enough
|
||||
state.ref_time = state.now
|
||||
state.ref_dif = abs(dif)
|
||||
state.step += dif * delta * self.prop
|
||||
if abs(state.step) < (state.now - state.last_progress) / self.step_factor:
|
||||
# wait until step size is big enough
|
||||
if self.motor_state == M.closed:
|
||||
return self.final_status(IDLE, 'closed')
|
||||
if state.now < state.start_time + 1:
|
||||
return Retry
|
||||
self.command(mp=state.step)
|
||||
return Retry
|
||||
return self.final_status(IDLE, 'fixed')
|
||||
|
||||
@Command
|
||||
def open(self):
|
||||
"""open valve fully"""
|
||||
self.command(mp=60)
|
||||
self.read_motor_state()
|
||||
self.start_machine(self.opening)
|
||||
|
||||
@status_code(BUSY)
|
||||
def opening(self, state):
|
||||
if state.init:
|
||||
state.start_time = state.now
|
||||
self.read_motor_state()
|
||||
if self.motor_state == M.opening:
|
||||
return Retry
|
||||
if self.motor_state == M.opened:
|
||||
return self.final_status(IDLE, 'opened')
|
||||
if state.now < state.start_time + 1:
|
||||
return Retry
|
||||
return self.final_status(IDLE, 'fixed')
|
||||
|
||||
@Command(FloatRange())
|
||||
def pulse(self, value):
|
||||
"""perform a motor pulse"""
|
||||
self.log.info('pulse %g', value)
|
||||
self.command(mp=value)
|
||||
if value > 0:
|
||||
self.motor_state = M.opening
|
||||
return self.opening
|
||||
self.motor_state = M.closing
|
||||
return self.closing
|
||||
|
Reference in New Issue
Block a user