[WIP] frappy_psi:ccu4: add HeLevel, N2

Change-Id: Ib31ec440ecc51b01035d783065cb805b942b61b1
This commit is contained in:
zolliker 2024-06-19 17:18:18 +02:00
parent 4da6aa95d7
commit c6056ad1de

View File

@ -20,9 +20,12 @@
# *****************************************************************************
"""drivers for CCU4, the cryostat control unit at SINQ"""
import time
# the most common Frappy classes can be imported from frappy.core
from frappy.core import EnumType, FloatRange, \
HasIO, Parameter, Readable, StringIO, StatusType
from frappy.core import EnumType, FloatRange, TupleOf, \
HasIO, Parameter, Command, Readable, StringIO, StatusType, \
BUSY, IDLE, ERROR, DISABLED
from frappy.states import HasStates, status_code, Retry
class CCU4IO(StringIO):
@ -54,12 +57,12 @@ class HeLevel(HasIO, Readable):
# conversion of the code from the CCU4 parameter 'hsf'
STATUS_MAP = {
0: (StatusType.IDLE, 'sensor ok'),
1: (StatusType.ERROR, 'sensor warm'),
2: (StatusType.ERROR, 'no sensor'),
3: (StatusType.ERROR, 'timeout'),
4: (StatusType.ERROR, 'not yet read'),
5: (StatusType.DISABLED, 'disabled'),
0: (IDLE, 'sensor ok'),
1: (ERROR, 'sensor warm'),
2: (ERROR, 'no sensor'),
3: (ERROR, 'timeout'),
4: (ERROR, 'not yet read'),
5: (DISABLED, 'disabled'),
}
def query(self, cmd):
@ -96,3 +99,116 @@ class HeLevel(HasIO, Readable):
def write_sample_rate(self, value):
return self.query(f'hf={int(value)}')
class HeLevelAuto(HasStates, HeLevel):
fill_level = Parameter('low threshold triggering start filling',
FloatRange(unit='%'), readonly=False)
full_level = Parameter('high threshold triggering stop filling',
FloatRange(unit='%'), readonly=False)
raw = Parameter('unsmoothed level', FloatRange(unit='%'))
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)
status = Parameter(datatype=StatusType(HeLevel, 'BUSY'))
_filling = False
_fillstart = 0
_last_read = 0
def doPoll(self):
super().doPoll()
if self._filling:
if self._filling == 1 and
if self.value
self.query('hcd=1')
def read_value(self):
self.raw = super().read_value()
if not self._state_machine.is_active:
return self.raw
return self.value
def read_status(self):
status = HeLevel.read_status(self)
if status[0] == IDLE:
return HasStates.read_status(self)
self.stop_machine(status)
return status
@status_code(BUSY)
def watching(self, state):
delta = state.delta(10)
if self.raw > self.value:
self.value -= delta / (3600 * self.fill_hours_range[1])
elif self.raw < self.value:
self.value -= delta / (3600 * self.fill_hours_range[0])
else:
self.value = self.raw
if self.value < self.fill_level:
self.query('hcd=1 hf=1')
state.fillstart = state.now
return self.precooling
self.query('hcd=1 hf=1')
return Retry
@status_code(BUSY)
def precooling(self, state):
delta = state.delta(1)
if self.raw > self.value:
self.value += delta / (60 * self.fill_minutes_range[0])
elif self.raw < self.value:
self.value -= delta / (60 * self.fill_minutes_range[0])
else:
self.value = self.raw
if self.value > self.full_level:
self.query('hcd=0 hf=0')
return self.watching
self.query('hcd=1 hf=1')
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)
if self.raw > self.value:
self.value += delta / (60 * self.fill_minutes_range[0])
elif self.raw < self.value:
self.value += delta / (60 * self.fill_minutes_range[1])
else:
self.value = self.raw
if self.value > self.full_level:
self.query('hcd=0 hf=0')
return self.watching
self.query('hcd=1 hf=1')
return Retry
@Command()
def fill(self):
self.start_machine(self.precooling, fillstart=time.time())
self.query('hcd=1 hf=1')
@Command()
def stop(self):
pass
class N2Sensor(HasIO, Readable):
# conversion of the code from the CCU4 parameter 'ns'
STATUS_MAP = {
0: (IDLE, 'sensor ok'),
1: (ERROR, 'no sensor'),
2: (ERROR, 'short circuit'),
3: (ERROR, 'upside down'),
4: (ERROR, 'sensor warm'),
5: (ERROR, 'empty'),
}