diff --git a/frappy_psi/dilution_new.py b/frappy_psi/dilution_new.py new file mode 100644 index 00000000..7a1d6ab8 --- /dev/null +++ b/frappy_psi/dilution_new.py @@ -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 +# Anik Stark +# +# ***************************************************************************** + +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': '', + }