add flamemag / flamedil config
This commit is contained in:
parent
ef9d89993c
commit
3ab7eb99ab
166
cfg/main/flamemag.cfg
Normal file
166
cfg/main/flamemag.cfg
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
[cio]
|
||||||
|
class = secop_psi.cryoltd.IO
|
||||||
|
description = 'IO to cryo ltd software'
|
||||||
|
uri = tcp://flamedil:3128
|
||||||
|
|
||||||
|
[main]
|
||||||
|
class = secop_psi.cryoltd.Main
|
||||||
|
description = master module
|
||||||
|
io= cio
|
||||||
|
|
||||||
|
[B]
|
||||||
|
description = 'magnetic field'
|
||||||
|
class = secop_psi.cryoltd.MainMagfield
|
||||||
|
channel = Main
|
||||||
|
constraint = 80000
|
||||||
|
target.max = 35000
|
||||||
|
hw_units = T
|
||||||
|
# A_to_G is needed for ramp rate
|
||||||
|
A_to_G = 285.73
|
||||||
|
ramp.max = 412
|
||||||
|
overshoot = {'o': 1, 't': 180}
|
||||||
|
degauss = {'s': 500, 'd': 30, 'f': 5, 't': 120}
|
||||||
|
tolerance = 5
|
||||||
|
|
||||||
|
[Bx]
|
||||||
|
description = 'magnetic field x component'
|
||||||
|
class = secop_psi.cryoltd.ComponentField
|
||||||
|
channel = VMX
|
||||||
|
check_against = B
|
||||||
|
target.max = 200
|
||||||
|
hw_units = A
|
||||||
|
A_to_G = 4.134
|
||||||
|
ramp.max = 23
|
||||||
|
tolerance = 1
|
||||||
|
|
||||||
|
[By]
|
||||||
|
description = 'magnetic field y component'
|
||||||
|
class = secop_psi.cryoltd.ComponentField
|
||||||
|
channel = VMY
|
||||||
|
check_against = B
|
||||||
|
target.max = 100
|
||||||
|
hw_units = A
|
||||||
|
A_to_G = 4.1117
|
||||||
|
ramp.max = 22.9
|
||||||
|
tolerance = 1
|
||||||
|
|
||||||
|
[Bz]
|
||||||
|
description = 'magnetic field z component'
|
||||||
|
class = secop_psi.cryoltd.ComponentField
|
||||||
|
channel = VMZ
|
||||||
|
check_against = B
|
||||||
|
target.max = 100
|
||||||
|
hw_units = A
|
||||||
|
A_to_G = 5.74
|
||||||
|
ramp.max = 33.6
|
||||||
|
tolerance = 1
|
||||||
|
|
||||||
|
[compressorA]
|
||||||
|
description = 'compressor A'
|
||||||
|
class = secop_psi.cryoltd.Compressor
|
||||||
|
channel = A
|
||||||
|
|
||||||
|
[compressorB]
|
||||||
|
description = 'compressor B'
|
||||||
|
class = secop_psi.cryoltd.Compressor
|
||||||
|
channel = B
|
||||||
|
|
||||||
|
[T_stage1_A]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = 1st Stage A
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_stage2_A]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = 2nd Stage A
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_stage1_B]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = 1st Stage B
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_stage2_B]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = 2nd Stage B
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_top_A]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Inner Magnet A (Top)
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_bottom_A]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Inner Magnet A (Bottom)
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_top_B]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Inner Magnet B (Top)
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_bottom_B]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Inner Magnet B (Bottom)
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_Z_shim]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Z Shim Former
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_XY_shim]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = XY Shim Former
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_XY_vector]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = XY Vector Former
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_radiation_shield]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Radiation Shield
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_persistent_joints]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Persistent Joints
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_outer_A]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Outer Magnet A
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_outer_B]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Outer Magnet B
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_shim_B]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Z Shim Former B
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_bore_shield]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Bore Radiation Shield
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_XYZ_shim]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = XYZ Shim Plate
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_Z_shim_switch]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Z Shim Switch
|
||||||
|
main = main
|
||||||
|
|
||||||
|
[T_main_switch]
|
||||||
|
class = secop_psi.cryoltd.Temperature
|
||||||
|
channel = Main Coil Switch
|
||||||
|
main = main
|
146
cfg/stick/flamedil.cfg
Normal file
146
cfg/stick/flamedil.cfg
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
[NODE]
|
||||||
|
id = triton.psi.ch
|
||||||
|
description = triton test
|
||||||
|
|
||||||
|
[INTERFACE]
|
||||||
|
uri = tcp://5000
|
||||||
|
|
||||||
|
[T_mix]
|
||||||
|
class = secop_psi.triton.TemperatureLoop
|
||||||
|
description = mix. chamber temperature
|
||||||
|
slot = T5
|
||||||
|
output_module = htr_mix
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[T_sorb]
|
||||||
|
class = secop_psi.triton.TemperatureSensor
|
||||||
|
description = sorb temperature
|
||||||
|
slot = T1
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[T_still]
|
||||||
|
class = secop_psi.triton.TemperatureSensor
|
||||||
|
description = still temperature
|
||||||
|
slot = T4
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[T_hx]
|
||||||
|
class = secop_psi.triton.TemperatureSensor
|
||||||
|
description = heat exchanger T
|
||||||
|
slot = T2
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[T_jtstage]
|
||||||
|
class = secop_psi.triton.TemperatureSensor
|
||||||
|
description = jt stage temperature
|
||||||
|
slot = T3
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[htr_mix]
|
||||||
|
class = secop_psi.triton.HeaterOutputWithRange
|
||||||
|
description = mix. chamber heater
|
||||||
|
slot = H1,T5
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[htr_sorb]
|
||||||
|
class = secop_psi.triton.HeaterOutput
|
||||||
|
description = sorb heater
|
||||||
|
slot = H3
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[htr_still]
|
||||||
|
class = secop_psi.triton.HeaterOutput
|
||||||
|
description = still heater
|
||||||
|
slot = H2
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[action]
|
||||||
|
class = secop_psi.triton.Action
|
||||||
|
description = higher level scripts
|
||||||
|
io = triton
|
||||||
|
slot = DR
|
||||||
|
|
||||||
|
[p_dump]
|
||||||
|
class = secop_psi.mercury.PressureSensor
|
||||||
|
description = dump pressure
|
||||||
|
slot = P1
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[p_cond]
|
||||||
|
class = secop_psi.mercury.PressureSensor
|
||||||
|
description = condenser pressure
|
||||||
|
slot = P2
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[p_still]
|
||||||
|
class = secop_psi.mercury.PressureSensor
|
||||||
|
description = still pressure
|
||||||
|
slot = P3
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
#[p_fore]
|
||||||
|
#class = secop_psi.mercury.PressureSensor
|
||||||
|
#description = pressure on the pump side
|
||||||
|
#slot = P4
|
||||||
|
#io = triton
|
||||||
|
|
||||||
|
[p_back]
|
||||||
|
class = secop_psi.mercury.PressureSensor
|
||||||
|
description = pressure on the back side of the pump
|
||||||
|
slot = P5
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[V1]
|
||||||
|
class = secop_psi.triton.Valve
|
||||||
|
description = valve V1
|
||||||
|
slot = V1
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[V2]
|
||||||
|
class = secop_psi.triton.Valve
|
||||||
|
description = valve V2
|
||||||
|
slot = V2
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[V4]
|
||||||
|
class = secop_psi.triton.Valve
|
||||||
|
description = valve V4
|
||||||
|
slot = V4
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[V5]
|
||||||
|
class = secop_psi.triton.Valve
|
||||||
|
description = valve V5
|
||||||
|
slot = V5
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[V9]
|
||||||
|
class = secop_psi.triton.Valve
|
||||||
|
description = valve V9
|
||||||
|
slot = V9
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
#[turbo]
|
||||||
|
#class = secop_psi.triton.TurboPump
|
||||||
|
#description = still turbo pump
|
||||||
|
#slot = TURB1
|
||||||
|
#io = triton
|
||||||
|
|
||||||
|
[fp]
|
||||||
|
class = secop_psi.triton.Pump
|
||||||
|
description = still fore pump
|
||||||
|
slot = FP
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[compressor]
|
||||||
|
class = secop_psi.triton.Pump
|
||||||
|
description = compressor
|
||||||
|
slot = COMP
|
||||||
|
io = triton
|
||||||
|
|
||||||
|
[triton]
|
||||||
|
class = secop_psi.mercury.IO
|
||||||
|
description = connection to triton software
|
||||||
|
uri = tcp://flamedil:33576
|
||||||
|
timeout = 9
|
||||||
|
|
512
secop_psi/cryoltd.py
Normal file
512
secop_psi/cryoltd.py
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Module authors:
|
||||||
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
"""flame magnet using cryogenic limited software suite
|
||||||
|
|
||||||
|
Remarks: for reading, we use the GetAll command, which is quite fast
|
||||||
|
(3 ms response time). However, as in many such sloppy programmed software
|
||||||
|
the timing is badly handled, as after changing parameters, the readback values
|
||||||
|
are not yet up to date. We use the array block_until for this purpose: any value
|
||||||
|
changed from the client is fixed for at least 10 seconds.
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
from secop.core import HasIO, StringIO, Readable, Drivable, Parameter, Command, \
|
||||||
|
Module, Property, Attached, Enum, IDLE, BUSY, ERROR
|
||||||
|
from secop.features import HasLimits
|
||||||
|
from secop.errors import ConfigError, BadValueError, HardwareError
|
||||||
|
from secop.datatypes import FloatRange, StringType, EnumType, StructOf, OrType
|
||||||
|
|
||||||
|
# floating point value followed with unit
|
||||||
|
VALUE_UNIT = re.compile(r'([-0-9.E]*\d|inf)([A-Za-z/%]*)$')
|
||||||
|
|
||||||
|
|
||||||
|
def as_float(value):
|
||||||
|
"""converts string (with unit) to float"""
|
||||||
|
return float(VALUE_UNIT.match(value).group(1))
|
||||||
|
|
||||||
|
|
||||||
|
BOOL_MAP = {'TRUE': True, 'FALSE': False}
|
||||||
|
|
||||||
|
|
||||||
|
def as_bool(value):
|
||||||
|
return BOOL_MAP[value]
|
||||||
|
|
||||||
|
|
||||||
|
def as_string(value):
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class Mapped:
|
||||||
|
def __init__(self, **kwds):
|
||||||
|
self.mapping = kwds
|
||||||
|
self.mapping.update({v: k for k, v in kwds.items()})
|
||||||
|
|
||||||
|
def __call__(self, value):
|
||||||
|
return self.mapping[value]
|
||||||
|
|
||||||
|
|
||||||
|
class IO(StringIO):
|
||||||
|
identification = [('Get:Remote:Remote_Ready', 'Remote_Ready RECEIVED: TRUE')]
|
||||||
|
end_of_line = (b'\n', b'\r\n')
|
||||||
|
timeout = 15
|
||||||
|
|
||||||
|
|
||||||
|
class Main(HasIO, Module):
|
||||||
|
pollinterval = Parameter('main poll interval', FloatRange(0.001), default=1)
|
||||||
|
export = False
|
||||||
|
params_map = None
|
||||||
|
triggers = None
|
||||||
|
initpolls = None
|
||||||
|
ioClass = IO
|
||||||
|
|
||||||
|
def register_module(self, obj, **params):
|
||||||
|
"""register a channel
|
||||||
|
|
||||||
|
:param obj: the remote object
|
||||||
|
:param **params:
|
||||||
|
pname=<Key in GetAll> or pname=(<Key in Getall>, convert function)
|
||||||
|
the convert function is as_float by default
|
||||||
|
"""
|
||||||
|
if self.params_map is None:
|
||||||
|
self.params_map = {}
|
||||||
|
self.triggers = set()
|
||||||
|
obj.block_until = {}
|
||||||
|
if hasattr(obj, 'trigger_update'):
|
||||||
|
self.triggers.add(obj.trigger_update)
|
||||||
|
for pname, value in params.items():
|
||||||
|
if isinstance(value, str):
|
||||||
|
key, cvt = value, as_float
|
||||||
|
else:
|
||||||
|
key, cvt = value
|
||||||
|
self.params_map[key.replace('<CH>', obj.channel)] = obj, pname, cvt
|
||||||
|
|
||||||
|
def doPoll(self):
|
||||||
|
# format of reply:
|
||||||
|
# "key1:value1;key2:value2;"
|
||||||
|
reply = self.communicate('GetAll').split('RECEIVED ', 1)[1].split(';')
|
||||||
|
missing = None, None, None
|
||||||
|
for line in reply:
|
||||||
|
try:
|
||||||
|
key, value = line.split(':', 1)
|
||||||
|
except ValueError: # missing ':'
|
||||||
|
if line:
|
||||||
|
pass
|
||||||
|
# ignore multiline values
|
||||||
|
# if needed, we may collect here and treat with a special key
|
||||||
|
continue
|
||||||
|
obj, pname, cvt = self.params_map.get(key, missing)
|
||||||
|
if obj:
|
||||||
|
if not hasattr(obj, pname):
|
||||||
|
raise ConfigError('%s has no attribute %s' % (obj.name, pname))
|
||||||
|
if time.time() > obj.block_until.get(pname, 0):
|
||||||
|
setattr(obj, pname, cvt(value))
|
||||||
|
for trigger in self.triggers: # do trigger after updates
|
||||||
|
trigger()
|
||||||
|
|
||||||
|
def communicate(self, cmd):
|
||||||
|
return self.io.communicate(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
class Channel:
|
||||||
|
main = Attached(Main, default='main')
|
||||||
|
channel = Property('channel name', StringType())
|
||||||
|
pollinterval = Parameter(export=False)
|
||||||
|
block_until = None
|
||||||
|
|
||||||
|
def sendcmd(self, cmd):
|
||||||
|
cmd = cmd.replace('<CH>', self.channel)
|
||||||
|
reply = self.main.communicate(cmd)
|
||||||
|
if not reply.startswith(cmd):
|
||||||
|
print('MISMATCH', cmd, reply)
|
||||||
|
|
||||||
|
def block(self, pname, value=None):
|
||||||
|
self.block_until[pname] = time.time() + 10
|
||||||
|
if value is not None:
|
||||||
|
setattr(self, pname, value)
|
||||||
|
|
||||||
|
def doPoll(self):
|
||||||
|
"""poll is done by main module"""
|
||||||
|
|
||||||
|
|
||||||
|
class Temperature(Channel, Readable):
|
||||||
|
channel = Property('channel name as in reply to GetAll', StringType())
|
||||||
|
value = Parameter('T sensor value', FloatRange(0, unit='K'))
|
||||||
|
description = '' # by default take description from channel name
|
||||||
|
|
||||||
|
KEYS = {
|
||||||
|
'1st Stage A',
|
||||||
|
'2nd Stage A',
|
||||||
|
'1st Stage B',
|
||||||
|
'2nd Stage B',
|
||||||
|
'Inner Magnet A (Top)',
|
||||||
|
'Inner Magnet A (Bottom)',
|
||||||
|
'Z Shim Former',
|
||||||
|
'XY Shim Former',
|
||||||
|
'XY Vector Former',
|
||||||
|
'Radiation Shield',
|
||||||
|
'Persistent Joints',
|
||||||
|
'Outer Magnet A',
|
||||||
|
'Inner Magnet B (Top)',
|
||||||
|
'Inner Magnet B (Bottom)',
|
||||||
|
'Z Shim Former B',
|
||||||
|
'Outer Magnet B',
|
||||||
|
'Bore Radiation Shield',
|
||||||
|
'XYZ Shim Plate',
|
||||||
|
'Z Shim Switch',
|
||||||
|
'Main Coil Switch',
|
||||||
|
}
|
||||||
|
|
||||||
|
def initModule(self):
|
||||||
|
super().initModule()
|
||||||
|
if not self.description:
|
||||||
|
self.description = self.channel
|
||||||
|
if self.channel not in self.KEYS:
|
||||||
|
raise ConfigError('channel (%s) must be one of %r' % (self.channel, self.KEYS))
|
||||||
|
self.main.register_module(self, value=self.channel)
|
||||||
|
|
||||||
|
|
||||||
|
class Magfield(HasLimits, Channel, Drivable):
|
||||||
|
_status_text = ''
|
||||||
|
_ready_text = ''
|
||||||
|
_error_text = ''
|
||||||
|
_last_error = ''
|
||||||
|
_rate_units = ''
|
||||||
|
_next_target = None
|
||||||
|
_last_target = None
|
||||||
|
hw_units = Property('hardware units: A or T', EnumType(A=0, T=1))
|
||||||
|
A_to_G = Property('A_to_G = "Gauss Value / Amps Value"', FloatRange(0))
|
||||||
|
tolerance = Parameter('tolerance', FloatRange(0), readonly=False, default=1)
|
||||||
|
|
||||||
|
def earlyInit(self):
|
||||||
|
super().earlyInit()
|
||||||
|
dt = self.parameters['target'].datatype
|
||||||
|
if dt.min < 1e-99: # make limits symmetric
|
||||||
|
dt.min = - dt.max
|
||||||
|
min_, max_ = self.target_limits
|
||||||
|
self.target_limits = [max(min_, dt.min), max_]
|
||||||
|
dt = self.parameters['ramp'].datatype
|
||||||
|
if self.ramp == 0: # unconfigured: take max.
|
||||||
|
self.ramp = dt.max
|
||||||
|
|
||||||
|
def to_gauss(self, value):
|
||||||
|
value, unit = VALUE_UNIT.match(value).groups()
|
||||||
|
if unit == 'T':
|
||||||
|
return float(value) * 10000
|
||||||
|
if unit == 'A':
|
||||||
|
return float(value) * self.A_to_G
|
||||||
|
raise HardwareError('received unknown unit: %s' % unit)
|
||||||
|
|
||||||
|
def to_gauss_min(self, value):
|
||||||
|
value, unit = VALUE_UNIT.match(value).groups()
|
||||||
|
if unit == 'A/s':
|
||||||
|
return float(value) * self.A_to_G * 60.0
|
||||||
|
if unit == 'T/s':
|
||||||
|
return float(value) * 10000 * 60
|
||||||
|
if unit == 'T/min':
|
||||||
|
return float(value) * 10000
|
||||||
|
if unit == 'T/h':
|
||||||
|
return float(value) * 10000 / 60.0
|
||||||
|
raise HardwareError('received unknown unit: %s' % unit)
|
||||||
|
|
||||||
|
def initModule(self):
|
||||||
|
super().initModule()
|
||||||
|
self.main.register_module(
|
||||||
|
self,
|
||||||
|
value=('<CH>_Field', self.to_gauss),
|
||||||
|
_status_text=('<CH>_Status', str),
|
||||||
|
_ready_text=('<CH>_Ready', str),
|
||||||
|
_error_text=('<CH>_Error', self.cvt_error),
|
||||||
|
_rate_units=('<CH>_Rate Units', str),
|
||||||
|
current=('<CH>_PSU Output', self.to_gauss),
|
||||||
|
voltage='<CH>_Voltage',
|
||||||
|
working_ramp=('<CH>_Ramp Rate', self.to_gauss_min),
|
||||||
|
setpoint=('<CH>_Setpoint', self.to_gauss),
|
||||||
|
switch_heater=('<CH>_Heater', self.cvt_switch_heater),
|
||||||
|
mode=('<CH>_Persistent Mode', self.cvt_mode),
|
||||||
|
approach_mode=('<CH>_Approach', self.cvt_approach_mode),
|
||||||
|
)
|
||||||
|
|
||||||
|
def cvt_error(self, text):
|
||||||
|
if text != self._last_error:
|
||||||
|
self._last_error = text
|
||||||
|
return text
|
||||||
|
return self._error_text
|
||||||
|
|
||||||
|
def trigger_update(self):
|
||||||
|
# called after treating result of GetAll message
|
||||||
|
if self._error_text:
|
||||||
|
status = ERROR, '%s while %s' % (self._error_text, self._status_text)
|
||||||
|
elif self._ready_text == 'TRUE':
|
||||||
|
with self.accessLock: # must not be in parallel with write_target
|
||||||
|
target = self._next_target
|
||||||
|
if target is not None: # target change pending
|
||||||
|
if target == self._last_target and abs(self.value - target) <= self.tolerance:
|
||||||
|
# we are already there
|
||||||
|
self._last_target = None
|
||||||
|
status = IDLE, self._status_text
|
||||||
|
else:
|
||||||
|
if self.hw_units == 'A':
|
||||||
|
self.sendcmd('Set:<CH>:Sweep %gA' % (target / self.A_to_G))
|
||||||
|
else:
|
||||||
|
self.sendcmd('Set:<CH>:Sweep %gT' % (target / 10000))
|
||||||
|
self._next_target = None
|
||||||
|
self._last_target = target
|
||||||
|
status = BUSY, 'changed target'
|
||||||
|
else:
|
||||||
|
status = IDLE, self._status_text
|
||||||
|
elif self._next_target is None:
|
||||||
|
txt = self._status_text
|
||||||
|
if txt.startswith('Ramping Magnet'):
|
||||||
|
if txt.endswith(': ') or ' 1 seconds' in txt:
|
||||||
|
txt = 'stabilizing'
|
||||||
|
else:
|
||||||
|
txt = 'ramping'
|
||||||
|
status = BUSY, txt
|
||||||
|
else:
|
||||||
|
return # do not change status when aborting
|
||||||
|
self.status = status
|
||||||
|
|
||||||
|
value = Parameter('magnetic field in the coil', FloatRange(unit='G'))
|
||||||
|
|
||||||
|
setpoint = Parameter('setpoint', FloatRange(unit='G'), default=0)
|
||||||
|
ramp = Parameter('ramp rate', FloatRange(0, unit='$/min'), default=0, readonly=False)
|
||||||
|
|
||||||
|
def write_ramp(self, ramp):
|
||||||
|
if self._rate_units != 'A/s':
|
||||||
|
self.sendcmd('Set:<CH>:ChangeRateUnits A/s')
|
||||||
|
self.sendcmd('Set:<CH>:SetRate %g' % (ramp / self.A_to_G / 60.0))
|
||||||
|
return ramp
|
||||||
|
|
||||||
|
def write_target(self, target):
|
||||||
|
self.reset_error()
|
||||||
|
self.check_limits(target)
|
||||||
|
self.write_ramp(self.ramp)
|
||||||
|
if self.approach_mode == self.approach_mode.OVERSHOOT:
|
||||||
|
o = self.overshoot['o']
|
||||||
|
if (target - self.value) * o < 0:
|
||||||
|
self.write_overshoot(dict(self.overshoot, o=-o))
|
||||||
|
self.block('_error_text', '')
|
||||||
|
if self._ready_text == 'FALSE':
|
||||||
|
if target != self._last_target or abs(self.value - target) > self.tolerance:
|
||||||
|
self.status = BUSY, 'aborting'
|
||||||
|
self.sendcmd('Set:<CH>:Abort')
|
||||||
|
self._next_target = target
|
||||||
|
else:
|
||||||
|
self._next_target = target
|
||||||
|
self.trigger_update() # update status
|
||||||
|
return target
|
||||||
|
|
||||||
|
working_ramp = Parameter('actual ramp rate', FloatRange(0, unit='$/min'))
|
||||||
|
|
||||||
|
Mode = Enum(
|
||||||
|
# DISABLED=0,
|
||||||
|
PERSISTENT=30,
|
||||||
|
SEMIPERSISTENT=31,
|
||||||
|
DRIVEN=50,
|
||||||
|
)
|
||||||
|
mode = Parameter('persistent mode', EnumType(Mode), readonly=False, default=30)
|
||||||
|
mode_map = Mapped(DRIVEN=0, PERSISTENT=1, SEMIPERSISTENT=2)
|
||||||
|
|
||||||
|
def write_mode(self, value):
|
||||||
|
code = self.mode_map(value)
|
||||||
|
self.sendcmd('Set:<CH>:SetPM %d' % code)
|
||||||
|
self.block('mode')
|
||||||
|
return value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def cvt_mode(text):
|
||||||
|
text = text.lower()
|
||||||
|
if 'off' in text:
|
||||||
|
if '0' in text:
|
||||||
|
return 30
|
||||||
|
return 31
|
||||||
|
return 50
|
||||||
|
|
||||||
|
ApproachMode = Enum(
|
||||||
|
DIRECT=0,
|
||||||
|
OVERSHOOT=1,
|
||||||
|
CYCLING=2,
|
||||||
|
DEGAUSSING=3,
|
||||||
|
)
|
||||||
|
approach_mode = Parameter('approach mode', EnumType(ApproachMode), readonly=False,
|
||||||
|
group='approach_settings', default=0)
|
||||||
|
|
||||||
|
def write_approach_mode(self, value):
|
||||||
|
self.sendcmd('Set:<CH>:SetApproach %d' % value)
|
||||||
|
self.block('approach_mode')
|
||||||
|
return value
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def cvt_approach_mode(cls, text):
|
||||||
|
return cls.ApproachMode(text.upper())
|
||||||
|
|
||||||
|
overshoot = Parameter('overshoot [%] and hold time [s]',
|
||||||
|
StructOf(o=FloatRange(-100, 100, unit='%'), t=FloatRange(0, unit='s')),
|
||||||
|
readonly=False, default=dict(o=0, t=0),
|
||||||
|
group='approach_settings')
|
||||||
|
|
||||||
|
def write_overshoot(self, value):
|
||||||
|
self.sendcmd('Set:<CH>:SetOvershoot %g,%g' % (value['o'], value['t']))
|
||||||
|
return value
|
||||||
|
|
||||||
|
cycle = Parameter('start value, damping factor, final value, hold time',
|
||||||
|
StructOf(s=FloatRange(-100, 100, unit='%'),
|
||||||
|
d=FloatRange(0, 100, unit='%'),
|
||||||
|
a=FloatRange(0, 100, unit='G'),
|
||||||
|
t=FloatRange(0, unit='s')),
|
||||||
|
readonly=False, default=dict(s=0, d=0, a=0, t=0),
|
||||||
|
group='approach_settings')
|
||||||
|
|
||||||
|
def write_cycle(self, value):
|
||||||
|
self.sendcmd('Set:<CH>:SetCycling %g,%g,%g,%g' %
|
||||||
|
(value['s'], value['d'], value['a'] * 1e-4, value['t']))
|
||||||
|
return value
|
||||||
|
|
||||||
|
degauss = Parameter('start value [G], damping factor [%], accuracy [G], hold time [s]',
|
||||||
|
StructOf(s=FloatRange(-10000, 10000, unit='G'),
|
||||||
|
d=FloatRange(0, 100, unit='%'),
|
||||||
|
f=FloatRange(0, 10000, unit='G'),
|
||||||
|
t=FloatRange(0, unit='s')),
|
||||||
|
readonly=False, default=dict(s=0, d=0, f=0, t=0),
|
||||||
|
group='approach_settings')
|
||||||
|
|
||||||
|
def write_degauss(self, value):
|
||||||
|
self.sendcmd('Set:<CH>:SetDegaussing %g,%g,%g,%g' %
|
||||||
|
(value['s'] * 1e-4, value['d'], value['f'] * 1e-4, value['t']))
|
||||||
|
return value
|
||||||
|
|
||||||
|
current = Parameter(
|
||||||
|
'leads current (in units of field)', FloatRange(unit='$'))
|
||||||
|
voltage = Parameter(
|
||||||
|
'voltage over leads', FloatRange(unit='V'))
|
||||||
|
switch_heater = Parameter(
|
||||||
|
'voltage over leads', EnumType(OFF=0, ON=1))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def cvt_switch_heater(text):
|
||||||
|
return 'ON' in text
|
||||||
|
|
||||||
|
@Command()
|
||||||
|
def stop(self):
|
||||||
|
self.sendcmd('Set:<CH>:Abort')
|
||||||
|
|
||||||
|
@Command()
|
||||||
|
def reset_quench(self):
|
||||||
|
"""reset quench condition"""
|
||||||
|
self.sendcmd('Set:<CH>:ResetQuench')
|
||||||
|
|
||||||
|
@Command()
|
||||||
|
def reset_error(self):
|
||||||
|
"""reset error"""
|
||||||
|
self._error_text = ''
|
||||||
|
|
||||||
|
|
||||||
|
class MainMagfield(Magfield):
|
||||||
|
checked_modules = None
|
||||||
|
|
||||||
|
def earlyInit(self):
|
||||||
|
super().earlyInit()
|
||||||
|
self.checked_modules = []
|
||||||
|
|
||||||
|
# TODO: turn into a property
|
||||||
|
constraint = Parameter('product check', FloatRange(unit='G^2'), default=80000)
|
||||||
|
|
||||||
|
def check_combined(self, obj, value, main_target):
|
||||||
|
sumvalue2 = sum((max(o.value ** 2, value ** 2 if o == obj else 0)
|
||||||
|
for o in self.checked_modules))
|
||||||
|
if sumvalue2 * max(self.value ** 2, main_target) > self.constraint ** 2:
|
||||||
|
raise BadValueError('outside constraint (B * Bxyz > %g G^2' * self.constraint)
|
||||||
|
|
||||||
|
def check_limits(self, value):
|
||||||
|
super().check_limits(value)
|
||||||
|
self.check_combined(None, 0, value)
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentField(Magfield):
|
||||||
|
check_against = Attached(MainMagfield)
|
||||||
|
|
||||||
|
def initModule(self):
|
||||||
|
super().initModule()
|
||||||
|
self.check_against.checked_modules.append(self)
|
||||||
|
|
||||||
|
def check_limits(self, value):
|
||||||
|
super().check_limits(value)
|
||||||
|
self.check_against.check_combined(self, value, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class Compressor(Channel, Drivable):
|
||||||
|
def initModule(self):
|
||||||
|
super().initModule()
|
||||||
|
self.main.register_module(
|
||||||
|
self,
|
||||||
|
value=('Compressor<CH>_Status', self.cvt_value),
|
||||||
|
_ready_text=('Compressor<CH>_Ready', str),
|
||||||
|
_error_text=('Compressor<CH>_Error', str),
|
||||||
|
run_time='Compressor<CH>_RunTime',
|
||||||
|
)
|
||||||
|
# TODO: what is Compressor_Error? (without A or B)
|
||||||
|
|
||||||
|
value = Parameter('compressor switch', EnumType(OFF=0, ON=1))
|
||||||
|
run_time = Parameter('run time', FloatRange(0, unit='h'))
|
||||||
|
|
||||||
|
_status_text = ''
|
||||||
|
_ready_text = ''
|
||||||
|
_error_text = ''
|
||||||
|
|
||||||
|
def cvt_value(self, text):
|
||||||
|
self._status_text = text
|
||||||
|
value = text == 'Running'
|
||||||
|
if time.time() > self.block_until.get('target', 0):
|
||||||
|
self.target = value
|
||||||
|
return value
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
return self._status_text == 'Running'
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
if self.target != self.value:
|
||||||
|
return BUSY, 'switching %s' % self.target.name
|
||||||
|
# TODO: find out possible status texts
|
||||||
|
if self._ready_text == 'TRUE':
|
||||||
|
return IDLE, 'ready'
|
||||||
|
if self._error_text:
|
||||||
|
return ERROR, self._error_text
|
||||||
|
return IDLE, self._status_text
|
||||||
|
|
||||||
|
target = Parameter('compressor switch', EnumType(OFF=0, ON=1))
|
||||||
|
|
||||||
|
def write_target(self, value):
|
||||||
|
if value:
|
||||||
|
self.sendcmd('SetCompressor:Start <CH>')
|
||||||
|
else:
|
||||||
|
self.sendcmd('SetCompressor:Stop <CH>')
|
||||||
|
self.block('target')
|
||||||
|
self.read_status()
|
||||||
|
return value
|
||||||
|
|
||||||
|
@Command()
|
||||||
|
def reset_error(self):
|
||||||
|
"""reset error"""
|
||||||
|
self.sendcmd('Set:Compressor:Reset <CH>')
|
||||||
|
self._error_text = ''
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user