[WIP] fi furnace improvements
- still under development Change-Id: I5fc22f041fb136b549016f510f06ea703122bee5
This commit is contained in:
parent
ccc66468d4
commit
dad9536eb5
@ -6,7 +6,7 @@ Node('fi.psi.ch',
|
||||
Mod('T_main',
|
||||
'frappy_psi.furnace.PRtransmitter',
|
||||
'sample temperature',
|
||||
addr='ai1',
|
||||
addr='ai2',
|
||||
valuerange=(0, 2300),
|
||||
value=Param(unit='degC'),
|
||||
)
|
||||
@ -14,7 +14,7 @@ Mod('T_main',
|
||||
Mod('T_extra',
|
||||
'frappy_psi.furnace.PRtransmitter',
|
||||
'extra temperature',
|
||||
addr='ai2',
|
||||
addr='ai1',
|
||||
valuerange=(0, 2300),
|
||||
value=Param(unit='degC'),
|
||||
)
|
||||
@ -49,8 +49,11 @@ Mod('T',
|
||||
'controlled Temperature',
|
||||
input_module='T_main',
|
||||
output_module='htr',
|
||||
value = Param(unit='degC'),
|
||||
output_min = 0,
|
||||
output_max = 100,
|
||||
# relais='relais',
|
||||
p=2,
|
||||
p=0.1,
|
||||
i=0.01,
|
||||
)
|
||||
|
||||
@ -81,16 +84,21 @@ Mod('flowswitch',
|
||||
true_level='low',
|
||||
)
|
||||
|
||||
# Mod('interlocks',
|
||||
# 'frappy_psi.furnace.Interlocks',
|
||||
# 'interlock parameters',
|
||||
# input='T_htr',
|
||||
# wall_T='T_wall',
|
||||
# vacuum='p',
|
||||
# control='T',
|
||||
# wall_limit=50,
|
||||
# vacuum_limit=0.1,
|
||||
# )
|
||||
Mod('interlocks',
|
||||
'frappy_psi.furnace.Interlocks',
|
||||
'interlock parameters',
|
||||
main_T='T_main',
|
||||
extra_T='T_extra',
|
||||
wall_T='T_wall',
|
||||
vacuum='p',
|
||||
control='T',
|
||||
htr='htr',
|
||||
flowswitch='flowswitch',
|
||||
wall_limit=50,
|
||||
main_T_limit = 1400,
|
||||
extra_T_limit = 1400,
|
||||
vacuum_limit=0.01,
|
||||
)
|
||||
|
||||
Mod('p',
|
||||
'frappy_psi.furnace.PKRgauge',
|
||||
@ -105,5 +113,5 @@ Mod('vso',
|
||||
'frappy_psi.ionopimax.VoltagePower',
|
||||
'voltage power output',
|
||||
target = 24,
|
||||
# export = False,
|
||||
export = False,
|
||||
)
|
||||
|
@ -19,9 +19,9 @@
|
||||
|
||||
"""interlocks for furnace"""
|
||||
|
||||
import time
|
||||
from frappy.core import Module, Writable, Attached, Parameter, FloatRange, Readable,\
|
||||
BoolType, ERROR, IDLE
|
||||
from frappy.errors import ImpossibleError
|
||||
from frappy.mixins import HasControlledBy
|
||||
from frappy_psi.picontrol import PImixin
|
||||
from frappy_psi.convergence import HasConvergence
|
||||
@ -30,15 +30,19 @@ import frappy_psi.tdkpower as tdkpower
|
||||
import frappy_psi.bkpower as bkpower
|
||||
|
||||
|
||||
class Interlocks(Module):
|
||||
input = Attached(Readable, 'the input module')
|
||||
vacuum = Attached(Readable, 'the vacuum pressure')
|
||||
wall_T = Attached(Readable, 'the wall temperature')
|
||||
htr_T = Attached(Readable, 'the heater temperature')
|
||||
class Interlocks(Writable):
|
||||
value = Parameter('interlock o.k.', BoolType(), default=True)
|
||||
target = Parameter('set to true to confirm', BoolType(), readonly=False)
|
||||
input = Attached(Readable, 'the input module', mandatory=False) # TODO: remove
|
||||
vacuum = Attached(Readable, 'the vacuum pressure', mandatory=False)
|
||||
wall_T = Attached(Readable, 'the wall temperature', mandatory=False)
|
||||
htr_T = Attached(Readable, 'the heater temperature', mandatory=False)
|
||||
main_T = Attached(Readable, 'the main temperature')
|
||||
extra_T = Attached(Readable, 'the extra temperature')
|
||||
control = Attached(Module, 'the control module')
|
||||
relais = Attached(Writable, 'the interlock relais')
|
||||
htr = Attached(Module, 'the heater module', mandatory=False)
|
||||
relais = Attached(Writable, 'the interlock relais', mandatory=False)
|
||||
flowswitch = Attached(Readable, 'the flow switch', mandatory=False)
|
||||
wall_limit = Parameter('maximum wall temperature', FloatRange(0, unit='degC'),
|
||||
default = 50, readonly = False)
|
||||
vacuum_limit = Parameter('maximum vacuum pressure', FloatRange(0, unit='mbar'),
|
||||
@ -46,9 +50,12 @@ class Interlocks(Module):
|
||||
htr_T_limit = Parameter('maximum htr temperature', FloatRange(0, unit='degC'),
|
||||
default = 530, readonly = False)
|
||||
main_T_limit = Parameter('maximum main temperature', FloatRange(0, unit='degC'),
|
||||
default = 530, readonly = False)
|
||||
default = 530, readonly = False)
|
||||
extra_T_limit = Parameter('maximum extra temperature', FloatRange(0, unit='degC'),
|
||||
default = 530, readonly = False)
|
||||
default = 530, readonly = False)
|
||||
|
||||
_off_reason = None # reason triggering interlock
|
||||
_conditions = '' # summary of reasons why locked now
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
@ -60,25 +67,50 @@ class Interlocks(Module):
|
||||
(self.vacuum, 'vacuum_limit'),
|
||||
]
|
||||
|
||||
def doPoll(self):
|
||||
# TODO: check channels are valid
|
||||
super().doPoll()
|
||||
newstatus = None
|
||||
if self.input.status[0] >= ERROR:
|
||||
newstatus = self.input.status
|
||||
else:
|
||||
for sensor, limitname in self._sensor_checks:
|
||||
if sensor.value > getattr(self, limitname):
|
||||
newstatus = f'above {sensor.name} limit'
|
||||
break
|
||||
if sensor.status[0] >= ERROR:
|
||||
newstatus = f'error at {sensor.name}: {sensor.status[1]}'
|
||||
return
|
||||
self.control.status = newstatus
|
||||
def write_target(self, value):
|
||||
if value:
|
||||
self.read_status()
|
||||
if self._conditions:
|
||||
raise ImpossibleError('not ready to start')
|
||||
self._off_reason = None
|
||||
self.value = True
|
||||
elif self.value:
|
||||
self.switch_off()
|
||||
self._off_reason = 'switched off'
|
||||
self.value = False
|
||||
self.read_status()
|
||||
|
||||
def switch_off(self):
|
||||
if self.value:
|
||||
self._off_reason = self._conditions
|
||||
self.value = False
|
||||
if self.control.control_active:
|
||||
self.log.error('switch control off %r', self.control.status)
|
||||
self.control.write_control_active(False)
|
||||
self.relais.write_target(False)
|
||||
self.control.write_control_active(False)
|
||||
self.control.status = ERROR, self._conditions
|
||||
if self.htr and self.htr.target:
|
||||
self.htr.write_target(0)
|
||||
if self.relais and (self.relais.value or self.relais.target):
|
||||
self.relais.write_target(False)
|
||||
|
||||
def read_status(self):
|
||||
conditions = []
|
||||
if self.flowswitch and self.flowswitch.value == 0:
|
||||
conditions.append('no cooling water')
|
||||
for sensor, limitname in self._sensor_checks:
|
||||
if sensor is None:
|
||||
continue
|
||||
if sensor.value > getattr(self, limitname):
|
||||
conditions.append(f'above {sensor.name} limit')
|
||||
if sensor.status[0] >= ERROR:
|
||||
conditions.append(f'error at {sensor.name}: {sensor.status[1]}')
|
||||
break
|
||||
self._conditions = ', '.join(conditions)
|
||||
if conditions and (self.control.control_active or self.htr.target):
|
||||
self.switch_off()
|
||||
if self.value:
|
||||
return IDLE, '; '.join(conditions)
|
||||
return ERROR, self._off_reason
|
||||
|
||||
|
||||
class PI(HasConvergence, PImixin):
|
||||
@ -111,3 +143,4 @@ class PKRgauge(LogVoltageInput):
|
||||
rawrange = (1.82, 8.6)
|
||||
valuerange = (5e-9, 1000)
|
||||
extendedrange = (0.5, 9.5)
|
||||
value = Parameter(unit='mbar')
|
||||
|
@ -63,7 +63,7 @@ import math
|
||||
from frappy.core import Readable, Writable, Parameter, Attached, IDLE, Property
|
||||
from frappy.lib import clamp
|
||||
from frappy.datatypes import LimitsType, EnumType, BoolType, FloatRange
|
||||
from frappy.mixins import HasOutputModule
|
||||
from frappy.newmixins import HasOutputModule
|
||||
from frappy_psi.convergence import HasConvergence
|
||||
|
||||
|
||||
@ -79,7 +79,8 @@ class PImixin(HasOutputModule, Writable):
|
||||
value = Parameter(unit='K')
|
||||
_lastdiff = None
|
||||
_lasttime = 0
|
||||
_clamp_limits = None
|
||||
_get_range = None # a function get output range from output_module
|
||||
_overflow = 0
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
@ -88,21 +89,9 @@ class PImixin(HasOutputModule, Writable):
|
||||
|
||||
def doPoll(self):
|
||||
super().doPoll()
|
||||
if self._clamp_limits is None:
|
||||
out = self.output_module
|
||||
if hasattr(out, 'max_target'):
|
||||
if hasattr(self, 'min_target'):
|
||||
self._clamp_limits = lambda v, o=out: clamp(v, o.read_min_target(), o.read_max_target())
|
||||
else:
|
||||
self._clamp_limits = lambda v, o=out: clamp(v, 0, o.read_max_target())
|
||||
elif hasattr(out, 'limit'): # mercury.HeaterOutput
|
||||
self._clamp_limits = lambda v, o=out: clamp(v, 0, o.read_limit())
|
||||
else:
|
||||
self._clamp_limits = lambda v: v
|
||||
if self.output_min == 0 and self.output_max == 0:
|
||||
self.output_max = self._clamp_limits(float('inf'))
|
||||
if not self.control_active:
|
||||
return
|
||||
out = self.output_module
|
||||
self.status = IDLE, 'controlling'
|
||||
now = time.time()
|
||||
deltat = clamp(0, now-self._lasttime, 10)
|
||||
@ -112,17 +101,51 @@ class PImixin(HasOutputModule, Writable):
|
||||
self._lastdiff = diff
|
||||
deltadiff = diff - self._lastdiff
|
||||
self._lastdiff = diff
|
||||
output, omin, omax = self._cvt2int(out.target)
|
||||
output += self._overflow + self.p * deltadiff + self.i * deltat * diff
|
||||
if output < omin:
|
||||
self._overflow = max(omin - omax, output - omin)
|
||||
output = omin
|
||||
elif output > omax:
|
||||
self._overflow = min(omax - omin, output - omax)
|
||||
output = omax
|
||||
else:
|
||||
self._overflow = 0
|
||||
out.update_target(self.name, self._cvt2ext(output))
|
||||
|
||||
def cvt2int_square(self, output):
|
||||
return (math.sqrt(max(0, clamp(x, *self._get_range()))) for x in (output, self.output_min, self.output_max))
|
||||
|
||||
def cvt2ext_square(self, output):
|
||||
return output ** 2
|
||||
|
||||
def cvt2int_lin(self, output):
|
||||
return (clamp(x, *self._get_range()) for x in (output, self.output_min, self.output_max))
|
||||
|
||||
def cvt2ext_lin(self, output):
|
||||
return output
|
||||
|
||||
def write_output_func(self, value):
|
||||
out = self.output_module
|
||||
output = out.target
|
||||
if self.output_func == 'square':
|
||||
output = math.sqrt(max(0, output))
|
||||
output += self.p * deltadiff + self.i * deltat * diff
|
||||
if self.output_func == 'square':
|
||||
output = output ** 2
|
||||
output = self._clamp_limits(output)
|
||||
out.update_target(self.name, clamp(output, self.output_min, self.output_max))
|
||||
if hasattr(out, 'max_target'):
|
||||
if hasattr(self, 'min_target'):
|
||||
self._get_range = lambda o=out: (o.read_min_target(), o.read_max_target())
|
||||
else:
|
||||
self._get_range = lambda o=out: (0, o.read_max_target())
|
||||
elif hasattr(out, 'limit'): # mercury.HeaterOutput
|
||||
self._get_range = lambda o=out: (0, o.read_limit())
|
||||
else:
|
||||
if self.output_min == self.output_max == 0:
|
||||
self.output_max = 1
|
||||
self._get_range = lambda o=self: (o.output_min, o.output_max)
|
||||
if self.output_min == self.output_max == 0:
|
||||
self.output_min, self.output_max = self._get_range()
|
||||
self.output_func = value
|
||||
self._cvt2int = getattr(self, f'cvt2int_{self.output_func.name}')
|
||||
self._cvt2ext = getattr(self, f'cvt2ext_{self.output_func.name}')
|
||||
|
||||
def write_control_active(self, value):
|
||||
super().write_control_active(value)
|
||||
if not value:
|
||||
self.output_module.write_target(0)
|
||||
|
||||
|
@ -31,13 +31,13 @@ class IO(StringIO):
|
||||
|
||||
class Power(HasIO, Readable):
|
||||
value = Parameter(datatype=FloatRange(0,3300,unit='W'))
|
||||
|
||||
voltage = Parameter('voltage', FloatRange(0,8, unit='V'))
|
||||
current = Parameter('current', FloatRange(0,400, unit='A'))
|
||||
|
||||
def read_value(self):
|
||||
reply_volt = self.communicate('MV?')
|
||||
reply_current = self.communicate('MC?')
|
||||
volt = float(reply_volt)
|
||||
current = float(reply_current)
|
||||
return volt*current
|
||||
self.voltage = float(self.communicate('MV?'))
|
||||
self.current = float(self.communicate('MC?'))
|
||||
return self.voltage * self.current
|
||||
|
||||
|
||||
class Output(HasIO, Writable):
|
||||
@ -59,7 +59,7 @@ class Output(HasIO, Writable):
|
||||
# take care of proper order
|
||||
if target == 0:
|
||||
self.write_output_enable(False)
|
||||
prev_curr = self.communicate(f'PC?')
|
||||
prev_curr = float(self.communicate(f'PC?'))
|
||||
volt = self.maxvolt if self.mode == 'current' else self.maxvolt * 0.01 * target
|
||||
curr = self.maxcurrent if self.mode == 'voltage' else self.maxcurrent * 0.01 * target
|
||||
if curr < prev_curr:
|
||||
|
Loading…
x
Reference in New Issue
Block a user