frappy_psi.dilution_new: add file
This commit is contained in:
260
frappy_psi/dilution_new.py
Normal file
260
frappy_psi/dilution_new.py
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# 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:
|
||||||
|
# Andrea Plank <andrea.plank@psi.ch>
|
||||||
|
# Anik Stark <anik.stark@psi.ch>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
from frappy.core import Drivable, Parameter, Attached, FloatRange, \
|
||||||
|
IDLE, BUSY, WARN, ERROR
|
||||||
|
from frappy.datatypes import EnumType, BoolType, StructOf, StringType
|
||||||
|
from frappy.states import Retry, Finish, status_code, HasStates
|
||||||
|
from frappy.lib.enum import Enum
|
||||||
|
from frappy.errors import ImpossibleError
|
||||||
|
from frappy.lib.mathparser import MathParser
|
||||||
|
|
||||||
|
T = Enum( # target states
|
||||||
|
off = 0,
|
||||||
|
sorbpump = 2,
|
||||||
|
condense = 5,
|
||||||
|
remove = 7,
|
||||||
|
remove_and_sorbpump = 9,
|
||||||
|
remove_and_condense = 10,
|
||||||
|
manual = 11,
|
||||||
|
test = 12,
|
||||||
|
)
|
||||||
|
|
||||||
|
V = Enum(T, # value status inherits from target status
|
||||||
|
sorbpumping=1,
|
||||||
|
condensing=4,
|
||||||
|
circulating=6,
|
||||||
|
removing=8,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Dilution(HasStates, Drivable):
|
||||||
|
|
||||||
|
condenseline_pressure = Attached() # G1
|
||||||
|
condense_valve = Attached() # V1
|
||||||
|
dump_valve = Attached() # V9
|
||||||
|
forepump = Attached() # rotary_pump_He3 (24)
|
||||||
|
condenseline_valve = Attached() # V1
|
||||||
|
circuitshort_valve = Attached() # V3
|
||||||
|
still_valve = Attached() # V6
|
||||||
|
pumpout_valve = Attached() # V14
|
||||||
|
still_pressure = Attached() # P1
|
||||||
|
dump_pressure = Attached() # G3
|
||||||
|
oneK_temp = Attached()
|
||||||
|
still_temp = Attached()
|
||||||
|
mix_temp = Attached()
|
||||||
|
|
||||||
|
value = Parameter('current state', EnumType(V), default=0)
|
||||||
|
target = Parameter('target state', EnumType(T), default=0)
|
||||||
|
sorbpumped = Parameter('sorb pump done', BoolType(), default=False, readonly=False)
|
||||||
|
sorb_cond = Parameter('sorb condition', StringType(), default='oneK>4', readonly=False)
|
||||||
|
sorb_pump_time = Parameter('sorb pump time', FloatRange(), default=2400, readonly=False)
|
||||||
|
dump_target = Parameter('low dump pressure limit indicating end of condensation phase',
|
||||||
|
FloatRange(unit='mbar * min'), readonly=False, default=50)
|
||||||
|
pulse_factor = Parameter('factor for calculating pump out pulse length',
|
||||||
|
FloatRange(unit='mbar'), readonly=False, default=20)
|
||||||
|
end_condense_pressure = Parameter('low condense pressure indicating end of condensation phase',
|
||||||
|
FloatRange(unit='mbar'), readonly=False, default=50)
|
||||||
|
end_remove_pressure = Parameter('pressure reached before end of remove (before fore pump)',
|
||||||
|
FloatRange(unit='mbar'), readonly=False, default=0.02)
|
||||||
|
condensing_p_low = Parameter('when to start pumping dump', datatype=FloatRange(unit='mbar'), default=500, readonly=False)
|
||||||
|
st = StringType()
|
||||||
|
valve_set = StructOf(close=st, open=st, check_open=st, check_closed=st)
|
||||||
|
condense_valves = Parameter('valve to act when condensing', valve_set)
|
||||||
|
valves_after_remove = Parameter('valve to act after remove', valve_set)
|
||||||
|
check_after_remove = Parameter('check for manual valves after remove', valve_set)
|
||||||
|
init = True
|
||||||
|
_start_time = 0
|
||||||
|
_warn_manual_work = None
|
||||||
|
|
||||||
|
def write_target(self, target):
|
||||||
|
"""
|
||||||
|
if (target == Targetstates.SORBPUMP):
|
||||||
|
if self.value == target:
|
||||||
|
return self.target
|
||||||
|
self.start_machine(self.sorbpump)
|
||||||
|
self.value = Targetstates.SORBPUMP
|
||||||
|
return self.value
|
||||||
|
"""
|
||||||
|
self.log.info('start %s', target.name)
|
||||||
|
if self.value == target:
|
||||||
|
return target
|
||||||
|
try:
|
||||||
|
self.start_machine(getattr(self, target.name, None))
|
||||||
|
except Exception as e:
|
||||||
|
self.log.exception('error %s', e)
|
||||||
|
self.log.info('started %s', target.name)
|
||||||
|
return target
|
||||||
|
|
||||||
|
@status_code(BUSY, 'sorbpump state')
|
||||||
|
def sorbpump(self, state):
|
||||||
|
""" heat up to Tsorb and wait """
|
||||||
|
if state.init:
|
||||||
|
#self.ls372.write_target(40) # set Tsorb to 40K
|
||||||
|
self.start_time = state.now
|
||||||
|
self.handle_valves(**self.condense_valves)
|
||||||
|
return Retry
|
||||||
|
parser = MathParser(oneK=self.oneK_temp.value, still=self.still_temp.value, mix=self.mix_temp.value)
|
||||||
|
if parser.calculate(self.sorb_cond):
|
||||||
|
self.start_time = state.now
|
||||||
|
if state.now - self.start_time < self.sorb_pump_time:
|
||||||
|
return Retry
|
||||||
|
return self.condense
|
||||||
|
|
||||||
|
@status_code(BUSY)
|
||||||
|
def condense(self, state):
|
||||||
|
""" condense process """
|
||||||
|
if state.init:
|
||||||
|
# self.value = V.condensing
|
||||||
|
self.handle_valves(**self.condense_valves)
|
||||||
|
self.still_valve.write_target(100)
|
||||||
|
self._start_time = state.now
|
||||||
|
return Retry
|
||||||
|
pdump = self.dump_pressure.value
|
||||||
|
pcond = self.condenseline_pressure.read_value()
|
||||||
|
if pcond < self.condensing_p_low and state.now > self._start_time + 5:
|
||||||
|
pulse_time = 60 * self.pulse_factor / pdump
|
||||||
|
if pulse_time > 59:
|
||||||
|
pulse_time = 3600
|
||||||
|
self.pumpout_valve.delay = pulse_time
|
||||||
|
self.pumpout_valve.write_target(1)
|
||||||
|
if pdump > self.dump_target:
|
||||||
|
return Retry
|
||||||
|
return self.wait_for_condense_line_pressure
|
||||||
|
|
||||||
|
@status_code(BUSY)
|
||||||
|
def wait_for_condense_line_pressure(self, state):
|
||||||
|
if self.condenseline_pressure.read_value() > self.end_condense_pressure:
|
||||||
|
return Retry
|
||||||
|
self.condense_valve.write_target(0)
|
||||||
|
return self.circulate
|
||||||
|
|
||||||
|
@status_code(BUSY)
|
||||||
|
def circulate(self, state):
|
||||||
|
"""Zirkuliert die Mischung."""
|
||||||
|
if state.init:
|
||||||
|
self.handle_valves(**self.condense_valves)
|
||||||
|
if self.wait_valves():
|
||||||
|
return Retry
|
||||||
|
self.check_valve_result()
|
||||||
|
self.value = V.circulating
|
||||||
|
return Finish
|
||||||
|
|
||||||
|
@status_code(BUSY, 'remove (wait for turbo shut down)')
|
||||||
|
def remove(self, state):
|
||||||
|
"""Entfernt die Mischung."""
|
||||||
|
if state.init:
|
||||||
|
self.handle_valves(**self.remove_valves)
|
||||||
|
return Retry
|
||||||
|
self.circuitshort_valve.write_target(1)
|
||||||
|
return self.remove_endsequence
|
||||||
|
|
||||||
|
@status_code(BUSY)
|
||||||
|
def remove_endsequence(self, state):
|
||||||
|
if self.still_pressure.read_value() > self.end_remove_pressure:
|
||||||
|
return Retry
|
||||||
|
self.circuitshort_valve.write_target(0)
|
||||||
|
self.dump_valve.write_target(0)
|
||||||
|
return self.close_valves_after_remove
|
||||||
|
|
||||||
|
@status_code(BUSY)
|
||||||
|
def close_valves_after_remove(self, state):
|
||||||
|
if state.init:
|
||||||
|
self.handle_valves(**self.valves_after_remove)
|
||||||
|
self._warn_manual_work = True
|
||||||
|
return self.final_status(WARN, 'please check manual valves')
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
status = super().read_status()
|
||||||
|
if status[0] < ERROR and self._warn_manual_work:
|
||||||
|
try:
|
||||||
|
self.handle_valves(**self.check_after_remove)
|
||||||
|
self._warn_manual_work = False
|
||||||
|
except ImpossibleError:
|
||||||
|
return WARN, f'please close manual valves {",".join(self._valves_failed[False])}'
|
||||||
|
return status
|
||||||
|
|
||||||
|
def handle_valves(self, check_closed=(), check_open=(), close=(), open=()):
|
||||||
|
"""check ot set given valves
|
||||||
|
raises ImpossibleError, when checks fails """
|
||||||
|
self._valves_to_wait_for = {}
|
||||||
|
self._valves_failed = {True: [], False: []}
|
||||||
|
for flag, valves in enumerate([check_closed, check_open]):
|
||||||
|
for vname in valves.split():
|
||||||
|
if self.secNode.modules[vname].read_value() != flag:
|
||||||
|
self._valves_failed[flag].append(vname)
|
||||||
|
for flag, valves in enumerate([close, open]):
|
||||||
|
for vname in valves.split():
|
||||||
|
valve = self.secNode.modules[vname]
|
||||||
|
valve.write_target(flag)
|
||||||
|
if valve.isBusy():
|
||||||
|
self._valves_to_wait_for[vname] = (valve, flag)
|
||||||
|
elif valve.read_value() != flag:
|
||||||
|
self._valves_failed[flag].append(vname)
|
||||||
|
|
||||||
|
def wait_valves(self):
|
||||||
|
busy = False
|
||||||
|
for vname, (valve, flag) in dict(self._valves_to_wait_for.items()):
|
||||||
|
statuscode = valve.read_status()[0]
|
||||||
|
if statuscode == BUSY:
|
||||||
|
busy = True
|
||||||
|
continue
|
||||||
|
if valve.read_value() == flag and statuscode == IDLE:
|
||||||
|
self._valves_to_wait_for.pop(vname)
|
||||||
|
else:
|
||||||
|
self._valves_failed[flag].append(vname)
|
||||||
|
return busy
|
||||||
|
|
||||||
|
def check_valve_result(self):
|
||||||
|
result = []
|
||||||
|
for flag, valves in self._valves_failed.items():
|
||||||
|
if valves:
|
||||||
|
result.append(f"{','.join(valves)} not {'open' if flag else 'closed'}")
|
||||||
|
if result:
|
||||||
|
raise ImpossibleError(f"failed: {', '.join(result)}")
|
||||||
|
|
||||||
|
|
||||||
|
class DIL4(Dilution):
|
||||||
|
condense_valves = {
|
||||||
|
'close': 'V2 V3 V4 V7 V8 V10 V11A V12A V13A',
|
||||||
|
'check_closed': '',
|
||||||
|
'check_open': '',
|
||||||
|
'open': 'V1 V5 V9',
|
||||||
|
}
|
||||||
|
remove_valves = {
|
||||||
|
'close': '',
|
||||||
|
'check_closed': '',
|
||||||
|
'check_open': '',
|
||||||
|
'open': '',
|
||||||
|
}
|
||||||
|
valves_after_remove = {
|
||||||
|
'close': '',
|
||||||
|
'check_closed': '',
|
||||||
|
'open': '',
|
||||||
|
'check_open': '',
|
||||||
|
}
|
||||||
|
check_after_remove = {
|
||||||
|
'close': '',
|
||||||
|
'check_closed': '',
|
||||||
|
'open': '',
|
||||||
|
'check_open': '',
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user