fixes in mercury an triton
- Valve is now a drivable, as it will check success, and retry, which might take around 1 second + some more
This commit is contained in:
parent
54b58f2188
commit
dfbc1c757a
@ -9,6 +9,7 @@ uri = tcp://5000
|
|||||||
class = secop_psi.mercury.IO
|
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
|
||||||
|
timeout = 5
|
||||||
|
|
||||||
[ts]
|
[ts]
|
||||||
class = secop_psi.triton.TemperatureLoop
|
class = secop_psi.triton.TemperatureLoop
|
||||||
@ -20,7 +21,7 @@ io = triton
|
|||||||
[htr_mix]
|
[htr_mix]
|
||||||
class = secop_psi.triton.HeaterOutput
|
class = secop_psi.triton.HeaterOutput
|
||||||
description = mix. chamber heater
|
description = mix. chamber heater
|
||||||
slot = H1
|
slot = H1,T5
|
||||||
io = triton
|
io = triton
|
||||||
|
|
||||||
[T_sorb]
|
[T_sorb]
|
||||||
|
@ -318,8 +318,8 @@ class HeaterOutput(HasInput, MercuryChannel, Writable):
|
|||||||
return volt * current
|
return volt * current
|
||||||
|
|
||||||
def read_target(self):
|
def read_target(self):
|
||||||
if self.controlled_by != 0 and self.target:
|
if self.controlled_by != 0:
|
||||||
return 0
|
return Done
|
||||||
if self._last_target is not None:
|
if self._last_target is not None:
|
||||||
return Done
|
return Done
|
||||||
self._volt_target = self.query('HTR:SIG:VOLT')
|
self._volt_target = self.query('HTR:SIG:VOLT')
|
||||||
@ -344,25 +344,28 @@ class HeaterOutput(HasInput, MercuryChannel, Writable):
|
|||||||
|
|
||||||
class TemperatureLoop(TemperatureSensor, Loop, Drivable):
|
class TemperatureLoop(TemperatureSensor, Loop, Drivable):
|
||||||
channel_type = 'TEMP'
|
channel_type = 'TEMP'
|
||||||
output_module = Attached(HeaterOutput, mandatory=False)
|
output_module = Attached(HasInput, mandatory=False)
|
||||||
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='$'))
|
||||||
tolerance = Parameter(default=0.1)
|
tolerance = Parameter(default=0.1)
|
||||||
_last_setpoint_change = None
|
_last_setpoint_change = None
|
||||||
|
ENABLE = 'TEMP:LOOP:ENAB'
|
||||||
|
ENABLE_RAMP = 'TEMP:LOOP:RENA'
|
||||||
|
RAMP_RATE = 'TEMP:LOOP:RSET'
|
||||||
|
|
||||||
def doPoll(self):
|
def doPoll(self):
|
||||||
super().doPoll()
|
super().doPoll()
|
||||||
self.read_setpoint()
|
self.read_setpoint()
|
||||||
|
|
||||||
def read_control_active(self):
|
def read_control_active(self):
|
||||||
active = self.query('TEMP:LOOP:ENAB', off_on)
|
active = self.query(self.ENABLE, off_on)
|
||||||
self.set_output(active)
|
self.set_output(active)
|
||||||
return active
|
return active
|
||||||
|
|
||||||
def write_control_active(self, value):
|
def write_control_active(self, value):
|
||||||
self.set_output(value)
|
self.set_output(value)
|
||||||
return self.change('TEMP:LOOP:ENAB', value, off_on)
|
return self.change(self.ENABLE, value, off_on)
|
||||||
|
|
||||||
@nopoll # polled by read_setpoint
|
@nopoll # polled by read_setpoint
|
||||||
def read_target(self):
|
def read_target(self):
|
||||||
@ -396,10 +399,10 @@ class TemperatureLoop(TemperatureSensor, Loop, Drivable):
|
|||||||
return Done
|
return Done
|
||||||
|
|
||||||
def read_enable_ramp(self):
|
def read_enable_ramp(self):
|
||||||
return self.query('TEMP:LOOP:RENA', off_on)
|
return self.query(self.ENABLE_RAMP, off_on)
|
||||||
|
|
||||||
def write_enable_ramp(self, value):
|
def write_enable_ramp(self, value):
|
||||||
return self.change('TEMP:LOOP:RENA', value, off_on)
|
return self.change(self.ENABLE_RAMP, value, off_on)
|
||||||
|
|
||||||
def set_output(self, active):
|
def set_output(self, active):
|
||||||
if active:
|
if active:
|
||||||
@ -413,7 +416,7 @@ class TemperatureLoop(TemperatureSensor, Loop, Drivable):
|
|||||||
self.status = status
|
self.status = status
|
||||||
|
|
||||||
def read_ramp(self):
|
def read_ramp(self):
|
||||||
result = self.query('TEMP:LOOP:RSET')
|
result = self.query(self.RAMP_RATE)
|
||||||
return min(9e99, result)
|
return min(9e99, result)
|
||||||
|
|
||||||
def write_ramp(self, value):
|
def write_ramp(self, value):
|
||||||
@ -422,11 +425,11 @@ class TemperatureLoop(TemperatureSensor, Loop, Drivable):
|
|||||||
self.write_enable_ramp(0)
|
self.write_enable_ramp(0)
|
||||||
return 0
|
return 0
|
||||||
if value >= 9e99:
|
if value >= 9e99:
|
||||||
self.change('TEMP:LOOP:RSET', 'inf', as_string)
|
self.change(self.RAMP_RATE, 'inf', as_string)
|
||||||
self.write_enable_ramp(0)
|
self.write_enable_ramp(0)
|
||||||
return 9e99
|
return 9e99
|
||||||
self.write_enable_ramp(1)
|
self.write_enable_ramp(1)
|
||||||
return self.change('TEMP:LOOP:RSET', max(1e-4, value))
|
return self.change(self.RAMP_RATE, max(1e-4, value))
|
||||||
|
|
||||||
|
|
||||||
class PressureSensor(MercuryChannel, Readable):
|
class PressureSensor(MercuryChannel, Readable):
|
||||||
@ -542,6 +545,7 @@ class HeLevel(MercuryChannel, Readable):
|
|||||||
(when filling) and slow (when consuming). We have to handle this by software.
|
(when filling) and slow (when consuming). We have to handle this by software.
|
||||||
"""
|
"""
|
||||||
channel_type = 'LVL'
|
channel_type = 'LVL'
|
||||||
|
value = Parameter(unit='%')
|
||||||
sample_rate = Parameter('_', EnumType(slow=0, fast=1), readonly=False)
|
sample_rate = Parameter('_', EnumType(slow=0, fast=1), readonly=False)
|
||||||
hysteresis = Parameter('hysteresis for detection of increase', FloatRange(0, 100, unit='%'),
|
hysteresis = Parameter('hysteresis for detection of increase', FloatRange(0, 100, unit='%'),
|
||||||
default=5, readonly=False)
|
default=5, readonly=False)
|
||||||
@ -595,9 +599,7 @@ class HeLevel(MercuryChannel, Readable):
|
|||||||
|
|
||||||
class N2Level(MercuryChannel, Readable):
|
class N2Level(MercuryChannel, Readable):
|
||||||
channel_type = 'LVL'
|
channel_type = 'LVL'
|
||||||
|
value = Parameter(unit='%')
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
return self.query('LVL:SIG:NIT:LEV')
|
return self.query('LVL:SIG:NIT:LEV')
|
||||||
|
|
||||||
|
|
||||||
# TODO: magnet power supply
|
|
||||||
|
@ -20,15 +20,16 @@
|
|||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
"""oxford instruments triton (kelvinoxjt dil)"""
|
"""oxford instruments triton (kelvinoxjt dil)"""
|
||||||
|
|
||||||
from secop.core import Drivable, HasIO, Writable, \
|
from math import sqrt
|
||||||
Parameter, Property, Readable, StringIO, Attached, Done, IDLE, WARN, nopoll
|
from secop.core import Writable, Parameter, Readable, Drivable, IDLE, WARN, BUSY, Done
|
||||||
from secop.datatypes import EnumType, FloatRange, StringType, StructOf, BoolType
|
from secop.errors import HardwareError
|
||||||
|
from secop.datatypes import EnumType, FloatRange
|
||||||
from secop.lib.enum import Enum
|
from secop.lib.enum import Enum
|
||||||
from secop_psi.mercury import MercuryChannel, Mapped, off_on
|
from secop_psi.mercury import MercuryChannel, Mapped, off_on, HasInput, SELF
|
||||||
import secop_psi.mercury as mercury
|
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=0, OPEN=1)
|
||||||
actions_map = Mapped(STOP=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
|
actions_map.mapping['NONE'] = actions.none # when writing, STOP is used instead of NONE
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ actions_map.mapping['NONE'] = actions.none # when writing, STOP is used instead
|
|||||||
class Action(MercuryChannel, Writable):
|
class Action(MercuryChannel, Writable):
|
||||||
channel_type = 'ACTN'
|
channel_type = 'ACTN'
|
||||||
value = Parameter('running action', EnumType(actions))
|
value = Parameter('running action', EnumType(actions))
|
||||||
target = Parameter('valve target', EnumType(none=0, condense=1, collect=3), readonly=False)
|
target = Parameter('action to do', EnumType(none=0, condense=1, collect=3), readonly=False)
|
||||||
_target = 0
|
_target = 0
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
@ -64,16 +65,50 @@ class Action(MercuryChannel, Writable):
|
|||||||
# EPCL (empty pre-coll automation)
|
# EPCL (empty pre-coll automation)
|
||||||
|
|
||||||
|
|
||||||
class Valve(MercuryChannel, Writable):
|
class Valve(MercuryChannel, Drivable):
|
||||||
channel_type = 'VALV'
|
channel_type = 'VALV'
|
||||||
value = Parameter('valve state', EnumType(closed=0, opened=1))
|
value = Parameter('valve state', EnumType(closed=0, opened=1))
|
||||||
target = Parameter('valve target', EnumType(close=0, open=1))
|
target = Parameter('valve target', EnumType(close=0, open=1))
|
||||||
|
|
||||||
|
_try_count = None
|
||||||
|
|
||||||
|
def doPoll(self):
|
||||||
|
self.read_status()
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
return self.query('VALV:SIG:STATE', open_close)
|
return self.query('VALV:SIG:STATE', open_close)
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
pos = self.read_value()
|
||||||
|
if self._try_count is None:
|
||||||
|
return IDLE, ''
|
||||||
|
if pos == self.target:
|
||||||
|
if self._try_count:
|
||||||
|
# make sure last sent command was not opposite
|
||||||
|
self.change('VALV:SIG:STATE', self.target, open_close)
|
||||||
|
self._try_count = None
|
||||||
|
self.setFastPoll(False)
|
||||||
|
return IDLE, ''
|
||||||
|
self._try_count += 1
|
||||||
|
if self._try_count % 4 == 0:
|
||||||
|
# send opposite position in order to unblock
|
||||||
|
self.change('VALV:SIG:STATE', pos, open_close)
|
||||||
|
return BUSY, 'unblock'
|
||||||
|
if self._try_count > 9:
|
||||||
|
# make sure system does not toggle later
|
||||||
|
self.change('VALV:SIG:STATE', pos, open_close)
|
||||||
|
return ERROR, 'can not %s valve' % self.target.name
|
||||||
|
self.change('VALV:SIG:STATE', self.target, open_close)
|
||||||
|
self._try_count += 1
|
||||||
|
return BUSY, 'waiting'
|
||||||
|
|
||||||
def write_target(self, value):
|
def write_target(self, value):
|
||||||
return self.change('VALV:SIG:STATE', value, open_close)
|
if value != self.read_value():
|
||||||
|
self._try_count = 0
|
||||||
|
self.setFastPoll(True, 0.25)
|
||||||
|
self.change('VALV:SIG:STATE', value, open_close)
|
||||||
|
self.status = BUSY, self.target.name
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class Pump(MercuryChannel, Writable):
|
class Pump(MercuryChannel, Writable):
|
||||||
@ -208,8 +243,52 @@ class TemperatureSensor(ScannerChannel, mercury.TemperatureSensor):
|
|||||||
|
|
||||||
|
|
||||||
class TemperatureLoop(ScannerChannel, mercury.TemperatureLoop):
|
class TemperatureLoop(ScannerChannel, mercury.TemperatureLoop):
|
||||||
pass
|
ENABLE = 'TEMP:LOOP:MODE'
|
||||||
|
ENABLE_RAMP = 'TEMP:LOOP:RAMP:ENAB'
|
||||||
|
RAMP_RATE = 'TEMP:LOOP:RAMP:RATE'
|
||||||
|
enable_pid_table = None # remove, does not work on triton
|
||||||
|
|
||||||
|
def write_control_active(self, value):
|
||||||
|
self.change('SYS:DR:CHAN:MC', 'T5', str)
|
||||||
|
if value:
|
||||||
|
self.change('TEMP:LOOP:FILT:ENAB', 'ON', str)
|
||||||
|
if self.output_module:
|
||||||
|
limit = self.output_module.read_limit() or None # None: max. limit
|
||||||
|
self.output_module.write_limit(limit)
|
||||||
|
return super().write_control_active(value)
|
||||||
|
|
||||||
|
|
||||||
class HeaterOutput(mercury.HeaterOutput):
|
class HeaterOutput(HasInput, MercuryChannel, Readable):
|
||||||
pass # not sure if we need special handling of triton heater output: to be checked!
|
"""heater output"""
|
||||||
|
channel_type = 'HTR,TEMP'
|
||||||
|
value = Parameter('heater output', FloatRange(unit='W'))
|
||||||
|
target = Parameter('heater output', FloatRange(0, unit='$'), readonly=False)
|
||||||
|
limit = Parameter('max. heater power', FloatRange(unit='W'), readonly=False)
|
||||||
|
resistivity = Parameter('heater resistivity', FloatRange(unit='Ohm'))
|
||||||
|
|
||||||
|
def read_resistivity(self):
|
||||||
|
return self.query('HTR:RES')
|
||||||
|
|
||||||
|
def read_limit(self):
|
||||||
|
maxcur = self.query('TEMP:LOOP:RANGE') * 0.001 # mA -> A
|
||||||
|
return self.read_resistivity() * maxcur ** 2
|
||||||
|
|
||||||
|
def write_limit(self, value):
|
||||||
|
if value is None:
|
||||||
|
maxcur = 0.1 # max. allowed current 100mA
|
||||||
|
else:
|
||||||
|
maxcur = sqrt(value / self.read_resistivity())
|
||||||
|
self.change('TEMP:LOOP:RANGE', maxcur * 1000)
|
||||||
|
return self.read_limit()
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
return self.query('HTR:SIG:POWR') * 1e-6
|
||||||
|
|
||||||
|
def read_target(self):
|
||||||
|
if self.controlled_by != 0:
|
||||||
|
return Done
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
def write_target(self, value):
|
||||||
|
self.write_controlled_by(SELF)
|
||||||
|
return self.change('HTR:SIG:POWR', value * 1e6)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user