various fixes on mb11/dil5

This commit is contained in:
zolliker 2022-06-07 11:45:25 +02:00
parent 68e2e06905
commit 9c7b6aeb94
5 changed files with 146 additions and 57 deletions

View File

@ -21,11 +21,13 @@ description = IPS for magnet and levels
uri = mb11-ts:3003 uri = mb11-ts:3003
[T_stat] [T_stat]
class = secop_psi.mercury.TemperatureLoop class = secop_psi.mercury.TemperatureAutoFlow
description = static heat exchanger temperature description = static heat exchanger temperature
output_module = htr_stat output_module = htr_stat
needle_valve = p_stat
slot = DB6.T1,DB1.H1 slot = DB6.T1,DB1.H1
io = itc1 io = itc1
tolerance = 0.1
[htr_stat] [htr_stat]
class = secop_psi.mercury.HeaterOutput class = secop_psi.mercury.HeaterOutput
@ -37,8 +39,11 @@ io = itc1
class = secop_psi.mercury.PressureLoop class = secop_psi.mercury.PressureLoop
description = static needle valve pressure description = static needle valve pressure
output_module = pos_stat output_module = pos_stat
settling_time = 60
slot = DB5.P1,DB3.G1 slot = DB5.P1,DB3.G1
io = itc1 io = itc1
tolerance = 1
value.unit = mbar_flow
[pos_stat] [pos_stat]
class = secop_psi.mercury.ValvePos class = secop_psi.mercury.ValvePos
@ -47,11 +52,13 @@ slot = DB5.P1,DB3.G1
io = itc1 io = itc1
[T_dyn] [T_dyn]
class = secop_psi.mercury.TemperatureLoop class = secop_psi.mercury.TemperatureAutoFlow
description = dynamic heat exchanger temperature description = dynamic heat exchanger temperature
output_module = htr_dyn output_module = htr_dyn
needle_valve = p_dyn
slot = DB7.T1,DB2.H1 slot = DB7.T1,DB2.H1
io = itc1 io = itc1
tolerance = 0.1
[htr_dyn] [htr_dyn]
class = secop_psi.mercury.HeaterOutput class = secop_psi.mercury.HeaterOutput
@ -63,8 +70,11 @@ io = itc1
class = secop_psi.mercury.PressureLoop class = secop_psi.mercury.PressureLoop
description = dynamic needle valve pressure description = dynamic needle valve pressure
output_module = pos_dyn output_module = pos_dyn
settling_time = 60
slot = DB8.P1,DB4.G1 slot = DB8.P1,DB4.G1
io = itc1 io = itc1
tolerance = 1
value.unit = mbar_flow
[pos_dyn] [pos_dyn]
class = secop_psi.mercury.ValvePos class = secop_psi.mercury.ValvePos
@ -90,6 +100,7 @@ description = neck heater 1 temperature
output_module = htr_neck1 output_module = htr_neck1
slot = MB1.T1,MB0.H1 slot = MB1.T1,MB0.H1
io = itc2 io = itc2
tolerance = 1
[htr_neck1] [htr_neck1]
class = secop_psi.mercury.HeaterOutput class = secop_psi.mercury.HeaterOutput
@ -103,6 +114,7 @@ description = neck heater 2 temperature
output_module = htr_neck2 output_module = htr_neck2
slot = DB6.T1,DB1.H1 slot = DB6.T1,DB1.H1
io = itc2 io = itc2
tolerance = 1
[htr_neck2] [htr_neck2]
class = secop_psi.mercury.HeaterOutput class = secop_psi.mercury.HeaterOutput
@ -116,6 +128,7 @@ description = static needle valve temperature
output_module = htr_nvs output_module = htr_nvs
slot = DB7.T1,DB2.H1 slot = DB7.T1,DB2.H1
io = itc2 io = itc2
tolerance = 0.1
[htr_nvs] [htr_nvs]
class = secop_psi.mercury.HeaterOutput class = secop_psi.mercury.HeaterOutput
@ -129,6 +142,7 @@ description = dynamic needle valve heater temperature
output_module = htr_nvd output_module = htr_nvd
slot = DB8.T1,DB3.H1 slot = DB8.T1,DB3.H1
io = itc2 io = itc2
tolerance = 0.1
[htr_nvd] [htr_nvd]
class = secop_psi.mercury.HeaterOutput class = secop_psi.mercury.HeaterOutput
@ -149,15 +163,15 @@ slot = GRPZ
io = ips io = ips
target.max = 11 target.max = 11
#[om_io] [om_io]
#description = dom motor IO description = dom motor IO
#class = secop_psi.phytron.PhytronIO class = secop_psi.phytron.PhytronIO
#uri = mb11-ts.psi.ch:3004 uri = mb11-ts.psi.ch:3004
#[om] [om]
#description = stick rotation, typically used for omega description = stick rotation, typically used for omega
#class = secop_psi.phytron.Motor class = secop_psi.phytron.Motor
#io = om_io io = om_io
#sign = -1 sign = -1
#encoder_mode = NO encoder_mode = NO

View File

@ -10,11 +10,18 @@ class = secop_psi.mercury.IO
description = connection to triton software description = connection to triton software
uri = tcp://linse-dil5:33576 uri = tcp://linse-dil5:33576
[action] #[ts]
class = secop_psi.triton.Action #class = secop_psi.switching_sensor.Sensor
description = higher level scripts #description = either T_mix or T_mix_wup, depending on T
#lower = T_mix
#upper = T_mix_wup
#switch_range = (1.5, 4)
[ts]
class = secop_psi.triton.TemperatureSensor
description = mix. chamber temperature
slot = T5
io = triton io = triton
slot = DR
[T_sorb] [T_sorb]
class = secop_psi.triton.TemperatureSensor class = secop_psi.triton.TemperatureSensor
@ -40,11 +47,11 @@ description = cold plate temperature
slot = T4 slot = T4
io = triton io = triton
[T_mix] [action]
class = secop_psi.triton.TemperatureSensor class = secop_psi.triton.Action
description = mix. chamber temperature description = higher level scripts
slot = T5
io = triton io = triton
slot = DR
[p_dump] [p_dump]
class = secop_psi.mercury.PressureSensor class = secop_psi.mercury.PressureSensor
@ -87,7 +94,7 @@ description = still warmup temperature
slot = MB1.T1 slot = MB1.T1
io = itc io = itc
[P_still_wup] [htr_still_wup]
class = secop_psi.mercury.HeaterOutput class = secop_psi.mercury.HeaterOutput
description = still warmup heater description = still warmup heater
slot = MB0.H1 slot = MB0.H1
@ -99,7 +106,7 @@ description = 1 K plate warmup temperature
slot = DB5.T1 slot = DB5.T1
io = itc io = itc
[P_one_K] [htr_one_K]
class = secop_psi.mercury.HeaterOutput class = secop_psi.mercury.HeaterOutput
description = 1 K plate warmup heater description = 1 K plate warmup heater
slot = DB3.H1 slot = DB3.H1
@ -111,7 +118,7 @@ description = mix. chamber warmup temperature
slot = DB6.T1 slot = DB6.T1
io = itc io = itc
[P_mix_wup] [htr_mix_wup]
class = secop_psi.mercury.HeaterOutput class = secop_psi.mercury.HeaterOutput
description = mix. chamber warmup heater description = mix. chamber warmup heater
slot = DB1.H1 slot = DB1.H1
@ -123,7 +130,7 @@ description = IVC warmup temperature
slot = DB7.T1 slot = DB7.T1
io = itc io = itc
[P_ivc_wup] [htr_ivc_wup]
class = secop_psi.mercury.HeaterOutput class = secop_psi.mercury.HeaterOutput
description = IVC warmup heater description = IVC warmup heater
slot = DB2.H1 slot = DB2.H1
@ -135,7 +142,7 @@ description = condenser temperature
slot = DB8.T1 slot = DB8.T1
io = itc io = itc
[P_cond] [htr_cond]
class = secop_psi.mercury.HeaterOutput class = secop_psi.mercury.HeaterOutput
description = condenser heater description = condenser heater
slot = DB3.H1 slot = DB3.H1

View File

@ -225,12 +225,13 @@ class Magfield(HasLimits, Drivable):
def stabilize_field(self, state): def stabilize_field(self, state):
"""stabilize field""" """stabilize field"""
self.persistent_field = self.value
if state.now - state.stabilize_start < self.wait_stable_field: if state.now - state.stabilize_start < self.wait_stable_field:
if state.init: if state.init:
self.status = Status.STABILIZING, 'stabilizing field' self.status = Status.STABILIZING, 'stabilizing field'
self.persistent_field = self.value
return Retry() return Retry()
self.persistent_field = state.set_point if abs(self.value - state.set_point) < self.tolerance:
self.persistent_field = state.set_point
return self.check_switch_off return self.check_switch_off
def check_switch_off(self, state): def check_switch_off(self, state):

View File

@ -27,7 +27,7 @@ import time
from secop.core import Drivable, HasIO, Writable, \ from secop.core import Drivable, HasIO, Writable, \
Parameter, Property, Readable, StringIO, Attached, Done, IDLE, nopoll Parameter, Property, Readable, StringIO, Attached, Done, IDLE, nopoll
from secop.datatypes import EnumType, FloatRange, StringType, StructOf, BoolType from secop.datatypes import EnumType, FloatRange, StringType, StructOf, BoolType, TupleOf
from secop.errors import HardwareError from secop.errors import HardwareError
from secop_psi.convergence import HasConvergence from secop_psi.convergence import HasConvergence
from secop.lib.enum import Enum from secop.lib.enum import Enum
@ -172,26 +172,29 @@ class TemperatureSensor(MercuryChannel, Readable):
class HasInput(MercuryChannel): class HasInput(MercuryChannel):
controlled_by = Parameter('source of target value', EnumType(members={'self': SELF}), default=0) controlled_by = Parameter('source of target value', EnumType(members={'self': SELF}), default=0)
target = Parameter(readonly=False) # do not know why this? target = Parameter(readonly=False)
input_modules = () input_callbacks = ()
def add_input(self, modobj): def register_input(self, name, control_off):
if not self.input_modules: """register input
self.input_modules = []
self.input_modules.append(modobj) :param name: the name of the module (for controlled_by enum)
:param control_off: a method on the input module to switch off control
"""
if not self.input_callbacks:
self.input_callbacks = []
self.input_callbacks.append(control_off)
prev_enum = self.parameters['controlled_by'].datatype._enum prev_enum = self.parameters['controlled_by'].datatype._enum
# add enum member, using autoincrement feature of Enum # add enum member, using autoincrement feature of Enum
self.parameters['controlled_by'].datatype = EnumType(Enum(prev_enum, **{modobj.name: None})) self.parameters['controlled_by'].datatype = EnumType(Enum(prev_enum, **{name: None}))
def write_controlled_by(self, value): def write_controlled_by(self, value):
if self.controlled_by == value: if self.controlled_by == value:
return Done return Done
self.controlled_by = value self.controlled_by = value
if value == SELF: if value == SELF:
self.log.warning('switch to manual mode') for control_off in self.input_callbacks:
for input_module in self.input_modules: control_off()
if input_module.control_active:
input_module.write_control_active(False)
return Done return Done
@ -209,12 +212,17 @@ class Loop(HasConvergence, MercuryChannel, Drivable):
def initModule(self): def initModule(self):
super().initModule() super().initModule()
if self.output_module: if self.output_module:
self.output_module.add_input(self) self.output_module.register_input(self.name, self.control_off)
def control_off(self):
if self.control_active:
self.log.warning('switch to manual mode')
self.write_control_active(False)
def set_output(self, active): def set_output(self, active):
if active: if active:
if self.output_module and self.output_module.controlled_by != self.name: if self.output_module and self.output_module.controlled_by != self.name:
self.output_module.controlled_by = self.name self.output_module.write_controlled_by(self.name)
else: else:
if self.output_module and self.output_module.controlled_by != SELF: if self.output_module and self.output_module.controlled_by != SELF:
self.output_module.write_controlled_by(SELF) self.output_module.write_controlled_by(SELF)
@ -340,7 +348,6 @@ class TemperatureLoop(TemperatureSensor, Loop, Drivable):
ramp = Parameter('ramp rate', FloatRange(0, unit='K/min'), readonly=False) ramp = Parameter('ramp rate', FloatRange(0, unit='K/min'), readonly=False)
enable_ramp = Parameter('enable ramp rate', BoolType(), readonly=False) enable_ramp = Parameter('enable ramp rate', BoolType(), readonly=False)
setpoint = Parameter('working setpoint (differs from target when ramping)', FloatRange(0, unit='$')) setpoint = Parameter('working setpoint (differs from target when ramping)', FloatRange(0, unit='$'))
auto_flow = Parameter('enable auto flow', BoolType(), readonly=False)
tolerance = Parameter(default=0.1) tolerance = Parameter(default=0.1)
_last_setpoint_change = None _last_setpoint_change = None
@ -394,11 +401,16 @@ class TemperatureLoop(TemperatureSensor, Loop, Drivable):
def write_enable_ramp(self, value): def write_enable_ramp(self, value):
return self.change('TEMP:LOOP:RENA', value, off_on) return self.change('TEMP:LOOP:RENA', value, off_on)
def read_auto_flow(self): def set_output(self, active):
return self.query('TEMP:LOOP:FAUT', off_on) if active:
if self.output_module and self.output_module.controlled_by != self.name:
def write_auto_flow(self, value): self.output_module.write_controlled_by(self.name)
return self.change('TEMP:LOOP:FAUT', value, off_on) else:
if self.output_module and self.output_module.controlled_by != SELF:
self.output_module.write_controlled_by(SELF)
status = IDLE, 'control inactive'
if self.status != status:
self.status = status
def read_ramp(self): def read_ramp(self):
result = self.query('TEMP:LOOP:RSET') result = self.query('TEMP:LOOP:RSET')
@ -450,7 +462,7 @@ class ValvePos(HasInput, MercuryChannel, Drivable):
return self.change('PRES:LOOP:FSET', value) return self.change('PRES:LOOP:FSET', value)
class PressureLoop(PressureSensor, Loop, Drivable): class PressureLoop(HasInput, PressureSensor, Loop, Drivable):
channel_type = 'PRES,AUX' channel_type = 'PRES,AUX'
output_module = Attached(ValvePos, mandatory=False) output_module = Attached(ValvePos, mandatory=False)
tolerance = Parameter(default=0.1) tolerance = Parameter(default=0.1)
@ -467,12 +479,62 @@ class PressureLoop(PressureSensor, Loop, Drivable):
def read_target(self): def read_target(self):
return self.query('PRES:LOOP:PRST') return self.query('PRES:LOOP:PRST')
def set_target(self, value):
"""set the target without switching to manual
might be used by a software loop
"""
self.change('PRES:LOOP:PRST', value)
super().set_target(value)
def write_target(self, value): def write_target(self, value):
target = self.change('PRES:LOOP:PRST', value) self.write_controlled_by(SELF)
self.set_target(target) self.set_target(value)
return Done return Done
class HasAutoFlow:
needle_valve = Attached(PressureLoop, mandatory=False)
auto_flow = Parameter('enable auto flow', BoolType(), readonly=False, default=0)
flowpars = Parameter('Tdif(min, max), FlowSet(min, max)',
TupleOf(TupleOf(FloatRange(unit='K'), FloatRange(unit='K')),
TupleOf(FloatRange(unit='mbar'), FloatRange(unit='mbar'))),
readonly=False, default=((1,5), (4,20)))
def read_value(self):
value = super().read_value()
if self.auto_flow:
(dmin, dmax), (fmin, fmax) = self.flowpars
flowset = min(dmax - dmin, max(0, value - self.target - dmin)) / (dmax - dmin) * (fmax - fmin) + fmin
self.needle_valve.set_target(flowset)
return Done
def initModule(self):
super().initModule()
if self.needle_valve:
self.needle_valve.register_input(self.name, self.auto_flow_off)
def write_auto_flow(self, value):
if value:
if self.needle_valve and self.needle_valve.controlled_by != self.name:
self.needle_valve.write_controlled_by(self.name)
else:
if self.needle_valve and self.needle_valve.controlled_by != SELF:
self.needle_valve.write_controlled_by(SELF)
_, (fmin, _) = self.flowpars
self.needle_valve.write_target(fmin)
return value
def auto_flow_off(self):
if self.auto_flow:
self.log.warning('switch auto flow off')
self.write_auto_flow(False)
class TemperatureAutoFlow(HasAutoFlow, TemperatureLoop):
pass
class HeLevel(MercuryChannel, Readable): class HeLevel(MercuryChannel, Readable):
"""He level meter channel """He level meter channel

View File

@ -29,7 +29,8 @@ import secop_psi.mercury as mercury
actions = Enum(none=0, condense=1, circulate=2, collect=3) actions = Enum(none=0, condense=1, circulate=2, collect=3)
open_close = Mapped(CLOSE=False, OPEN=True) open_close = Mapped(CLOSE=False, OPEN=True)
actions_map = Mapped(NONE=actions.none, COND=actions.condense, COLL=actions.collect) actions_map = Mapped(STOP=actions.none, COND=actions.condense, COLL=actions.collect)
actions_map.mapping['NONE'] = actions.none # when writing, STOP is used instead of NONE
class Action(MercuryChannel, Writable): class Action(MercuryChannel, Writable):
@ -49,15 +50,18 @@ class Action(MercuryChannel, Writable):
return self.change('SYS:DR:ACTN', value, actions_map) return self.change('SYS:DR:ACTN', value, actions_map)
# actions: # actions:
# NONE (no action)
# COND (condense mixture)
# COLL (collect mixture)
# STOP (go to NONE)
#
# not yet used (would need a subclass of Action):
# CLDN (cool down) # CLDN (cool down)
# PCL (precool automation) # PCL (precool automation)
# COND (condense mixture) # PCOND (pause pre-cool (not condense?) automation)
# PCOND (pause pre-cool (not condense?) automation # RCOND (resume pre-cool (not condense?) automation)
# RCOND (resume pre-cool (not condense?) automation
# WARM (warm-up) # WARM (warm-up)
# COLL (collect mixture)
# EPCL (empty pre-coll automation) # EPCL (empty pre-coll automation)
# STOP
class Valve(MercuryChannel, Writable): class Valve(MercuryChannel, Writable):
@ -173,6 +177,7 @@ class FlowMeter(MercuryChannel, Readable):
class TemperatureSensor(mercury.TemperatureSensor): class TemperatureSensor(mercury.TemperatureSensor):
# TODO: excitation, enable # TODO: excitation, enable
# TODO: switch on/off filter, check
filter_time = Parameter('filter time', FloatRange(1, 200, unit='sec'), readonly=False) filter_time = Parameter('filter time', FloatRange(1, 200, unit='sec'), readonly=False)
dwell_time = Parameter('dwell time', FloatRange(1, 200, unit='sec'), readonly=False) dwell_time = Parameter('dwell time', FloatRange(1, 200, unit='sec'), readonly=False)
pause_time = Parameter('pause time', FloatRange(3, 200, unit='sec'), readonly=False) pause_time = Parameter('pause time', FloatRange(3, 200, unit='sec'), readonly=False)
@ -199,4 +204,4 @@ class TemperatureSensor(mercury.TemperatureSensor):
class TemperatureLoop(mercury.TemperatureLoop): class TemperatureLoop(mercury.TemperatureLoop):
pass # TODO: switch on/off filter, check pass