frappy_psi: add jtccr

This commit is contained in:
2026-03-11 09:45:13 +01:00
parent 6927f3bd26
commit 0ca913bf19
+138
View File
@@ -0,0 +1,138 @@
# *****************************************************************************
# 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:
# Anik Stark <anik.stark@psi.ch>
# *****************************************************************************
from frappy.core import Parameter, Property, Writable, Attached, EnumType, FloatRange, \
IDLE, BUSY, WARN
STATES = {'manual': 0,
'high_pressure': 1,
'circulating': 2,
'warmup': 3,
}
class JTCCR(Writable):
compressor = Attached()
value = Parameter('current state', datatype=EnumType(STATES), default=0)
target = Parameter('target state', datatype=EnumType(STATES), default=0)
#p1min = Property('lower limit to switch to high pressure mode', dataype=FloatRange(unit='mbar'), default=1.8)
p1max = Property('limit to switch to circulating mode', datatype=FloatRange(unit='mbar'), default=2.2)
p2min = Property('lower limit to turn compressor off', datatype=FloatRange(unit='mbar'), default=0.12)
p2max = Property('upper limit to turn compressor on', datatype=FloatRange(unit='mbar'), default=0.8)
#p2lim = Property('do not start compressor if p2 is below this value', datatype=FloatRange(unit='mbar'), default=0.15)
pdifmax = Property('max pressure difference of compressor', datatype=FloatRange(unit='mbar'), default=5.0)
pdifmargin = Property('safety margin for pressure difference of compressor',
datatype=FloatRange(unit='mbar'), default=1.0)
p3margin = Property('start compressor when p3 is below pressreg setpoint plus this value',
datatype=FloatRange(unit='mbar'), default=0.01)
p3reg = Parameter('pressure regulation setpoint', datatype=FloatRange(unit='mbar'))
plow = Property('pressure below 5K', datatype=FloatRange(unit='mbar'), default=4.0)
valves_high_pressure = {
'close': 'V3 V4 V5 V6 V7 V8 V10',
'open': 'V1 V2 V9 Vm',
}
valves_circulating = {
'close': 'V3 V4 V5 V6 V7 V9 V10',
'open': 'V1 V2 V8 Vm',
}
valves_warmup = {
'close': 'V6 V7 V8 V9 V10',
'open': 'V1 V2 V3 V4 V5 Vm',
}
valves_security= {
'open': '',
'close': 'V1 V9'
}
valves_overpressure = {
'open': 'V10',
'close': ''
}
def write_target(self, target):
if self.value != target:
self.set_mode(STATES.get(target))
return target
def set_mode(self, state):
if state == 'high_pressure':
self.p3reg = 12
self.handle_valves(**self.valves_high_pressure)
elif state == 'circulating':
self.p3reg = self.plow
self.handle_valves(**self.valves_circulating)
elif state == 'warmup':
self.handle_valves(**self.valves_warmup)
self.value = state
def security_settings(self):
self.compressor.write_target(False)
self.handle_valves(**self.valves_security)
self.set_mode('manual')
def handle_valves(self, close=(), open=()):
"""set given valves. raises ImpossibleError, when checks fails"""
self._valves_to_wait_for = {}
self._valves_failed = {True: [], False: []}
for flag, valves in enumerate([close, open]):
for vname in valves.split():
valve = self.secNode.modules[vname]
valve.write_target(flag)
# TODO: do we need to wait for motor valve?
def doPoll(self):
p1 = self.secNode.module['P1'].read_value()
p2 = self.secNode.module['P2'].read_value()
p3 = self.secNode.module['P3'].read_value()
compressor_state = self.compressor.read_value()
if self.value == 'manual':
return self.set_mode('manual')
if p3 >= p2 + self.pdifmax + self.pdifmargin:
# overpressure protection
self.security_settings()
self.status = WARN, 'overpressure: He recovery output closed?'
return
if p2 < self.p2min and p3 < self.p3reg and self.value != 'high_pressure':
# underpressure protection
self.security_settings()
self.status = WARN, 'underpressure: not enough He'
return
if self.value == 'circulating':
if p3 < self.p3reg and not compressor_state:
self.compressor.write_target(True)
elif (p3 - p2) > self.pdifmax and compressor_state:
self.compressor.write_target(False)
# TODO: do we need to skip overpressure protection for one time?
if self.value == 'high_pressure' and p1 > self.p1max:
self.set_mode('circulating')
self.status = IDLE, ''
if p2 > self.p2max and not compressor_state:
self.compressor.write_target(True)
elif p2 < self.p2min and compressor_state:
self.compressor.write_target(False)
if (p3 - p2) >= self.pdifmax + 0.1:
self.handle_valves(**self.valves_overpressure)
self.status = BUSY, 'release to recovery'
elif self.secNode.modules['V10'].read_value():
self.secNode.modules['V10'].write_target(False)
self.status = IDLE, 'release finished'
self.status = IDLE, ''