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