132 lines
4.7 KiB
Python
132 lines
4.7 KiB
Python
# *****************************************************************************
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free Software
|
|
# Foundation; either version 2 of the License, or (at your option) any later
|
|
# version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along with
|
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
# Module authors:
|
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
|
#
|
|
# *****************************************************************************
|
|
|
|
import time
|
|
from frappy.core import Attached, Readable, Writable, Parameter, Command, \
|
|
IDLE, BUSY, DISABLED, ERROR
|
|
from frappy.datatypes import FloatRange, StatusType, TupleOf, EnumType
|
|
from frappy.states import HasStates, Retry, status_code
|
|
|
|
|
|
class AutoFill(HasStates, Readable):
|
|
level = Attached(Readable)
|
|
valve = Attached(Writable)
|
|
status = Parameter(datatype=StatusType(Readable, 'BUSY'))
|
|
mode = Parameter('auto mode', EnumType(disabled=0, auto=30), readonly=False)
|
|
|
|
fill_level = Parameter('low threshold triggering start filling',
|
|
FloatRange(unit='%'), readonly=False)
|
|
full_level = Parameter('high threshold triggering stop filling',
|
|
FloatRange(unit='%'), readonly=False)
|
|
fill_minutes_range = Parameter('range of possible fill rate',
|
|
TupleOf(FloatRange(unit='min'), FloatRange(unit='min')),
|
|
readonly=False)
|
|
hold_hours_range = Parameter('range of possible consumption rate',
|
|
TupleOf(FloatRange(unit='h'), FloatRange(unit='h')),
|
|
readonly=False)
|
|
fill_delay = Parameter('delay for cooling the transfer line',
|
|
FloatRange(unit='min'), readonly=False)
|
|
|
|
def read_status(self):
|
|
if self.mode == 'DISABLED':
|
|
return DISABLED, ''
|
|
vstatus = self.valve.status
|
|
if vstatus[0] // 100 != IDLE // 100:
|
|
self.stop_machine(vstatus)
|
|
return vstatus
|
|
status = self.level.read_status(self)
|
|
if status[0] // 100 == IDLE // 100:
|
|
return HasStates.read_status(self)
|
|
self.stop_machine(status)
|
|
return status
|
|
|
|
def write_mode(self, mode):
|
|
if mode == 'DISABLED':
|
|
self.stop_machine((DISABLED, ''))
|
|
elif mode == 'AUTO':
|
|
self.start_machine(self.watching)
|
|
return mode
|
|
|
|
@status_code(BUSY)
|
|
def watching(self, state):
|
|
if state.init:
|
|
self.valve.write_target(0)
|
|
delta = state.delta(10)
|
|
raw = self.level.value
|
|
if raw > self.value:
|
|
self.value -= delta / (3600 * self.hold_hours_range[1])
|
|
elif raw < self.value:
|
|
self.value -= delta / (3600 * self.hold_hours_range[0])
|
|
else:
|
|
self.value = raw
|
|
if self.value < self.fill_level:
|
|
return self.precooling
|
|
return Retry
|
|
|
|
@status_code(BUSY)
|
|
def precooling(self, state):
|
|
if state.init:
|
|
state.fillstart = state.now
|
|
self.valve.write_target(1)
|
|
delta = state.delta(1)
|
|
raw = self.level.value
|
|
if raw > self.value:
|
|
self.value += delta / (60 * self.fill_minutes_range[0])
|
|
elif raw < self.value:
|
|
self.value -= delta / (60 * self.fill_minutes_range[0])
|
|
else:
|
|
self.value = raw
|
|
if self.value > self.full_level:
|
|
return self.watching
|
|
if state.now > state.fillstart + self.fill_delay * 60:
|
|
return self.filling
|
|
return Retry
|
|
|
|
@status_code(BUSY)
|
|
def filling(self, state):
|
|
delta = state.delta(1)
|
|
raw = self.level.value
|
|
if raw > self.value:
|
|
self.value += delta / (60 * self.fill_minutes_range[0])
|
|
elif raw < self.value:
|
|
self.value += delta / (60 * self.fill_minutes_range[1])
|
|
else:
|
|
self.value = raw
|
|
if self.value > self.full_level:
|
|
return self.watching
|
|
return Retry
|
|
|
|
def on_cleanup(self, state):
|
|
try:
|
|
self.valve.write_target(0)
|
|
except Exception:
|
|
pass
|
|
super().on_cleanup()
|
|
|
|
@Command()
|
|
def fill(self):
|
|
self.mode = 'AUTO'
|
|
self.start_machine(self.precooling, fillstart=time.time())
|
|
|
|
@Command()
|
|
def stop(self):
|
|
self.start_machine(self.watching)
|