Files
cristallina/crq_exp/dilsc.py

182 lines
7.2 KiB
Python
Executable File

""" DilSc prototype
"""
from slic.core.adjustable import Adjustable, PVAdjustable
from slic.core.device import Device, SimpleDevice
from frappy.client import SecopClient
from frappy import states
from frappy.datatypes import StatusType
class Dilution(Device):
def __init__(self, **kwargs):
self.name = 'DilSc'
ID = self.name
super().__init__(ID, **kwargs)
self.address = 'dilsc.psi.ch:5000'
self.dilsc = SecopClient(self.address)
self.dilsc.connect()
self.x = MagnetCoil("X", self.dilsc, 'x', limit_low=-0.6, limit_high=0.6)
self.y = MagnetCoil("Y", self.dilsc, 'y', limit_low=-0.6, limit_high=0.6)
self.z = MagnetCoil("Z", self.dilsc, 'z', limit_low=-5.2, limit_high=5.2)
self.T_plato = Thermometer('T_plato', self.dilsc, limit_low=0, limit_high=300)
self.T_chip = Thermometer('T_chip', self.dilsc, limit_low=0, limit_high=300)
self.T_reg = Thermometer('T_reg', self.dilsc, limit_low=0, limit_high=300)
class Thermometer(Adjustable):
def __init__(self, name, dilsc_connection, limit_low=-0.0001, limit_high=0.0001):
super().__init__(name, limit_low=limit_low, limit_high=limit_high)
self.dilsc = dilsc_connection
# Heater to regulate channel A on the Galgen LakeShore372. Give value in Ohm.
self.heater_resistance = 82
# Defines heater ranges and PID values for various control temperature regions.
self.heater_settings = [
{'range': 'off', 'pid': (800, 1, 0), 'temp_start': -1, 'temp_end': 0.00099},
{'range': '300uA', 'pid': (800, 1, 0), 'temp_start': 0.001, 'temp_end': 0.037},
{'range': '1mA', 'pid': (40, 14, 0), 'temp_start': 0.038, 'temp_end': 0.108},
{'range': '3mA', 'pid': (4, 15, 0), 'temp_start': 0.110, 'temp_end': 0.350},
{'range': '10mA', 'pid': (2, 12, 0), 'temp_start': 0.352, 'temp_end': 1.100},
{'range': '30mA', 'pid': (2, 60, 0), 'temp_start': 1.100, 'temp_end': 2.300}
]
def _check_connection(func):
def checker(self, *args, **kwargs):
if not self.dilsc.online:
raise ConnectionError(f'No connection to dilsc at {self.address}')
else:
return func(self, *args, **kwargs)
return checker
@_check_connection
def set_heater_range(self, range_value):
""" Sets the heater range for the control channel and sets the PID parameters for the associated control loop.
TODO: Consider redoing so that it only works for the T_reg channel.
TODO: the pid values are now sent with stringio and not saved as parameters in slic, this should be fixed.
-
"""
heater_range_map = {
'off': 0, '30uA': 1, '100uA': 2, '300uA': 3, '1mA': 4, '3mA': 5, '10mA': 6, '30mA': 7, '100mA': 8}
if range_value not in heater_range_map:
raise ValueError("Invalid range value. Allowed are: ['off','30uA','100uA','300uA','1mA','3mA','10mA','30mA','100mA']")
enum_value = heater_range_map[range_value]
self.dilsc.setParameter(self.name, 'htrrng', enum_value)
for setting in self.heater_settings:
if setting['range'] == range_value:
self.set_PID_parameters(*setting['pid'])
break
return
@_check_connection
def get_current_value(self):
cacheitem = self.dilsc.getParameter(f'{self.name}', 'value', trycache=False)
return cacheitem.value
@_check_connection
def set_target_value(self, value, adjust_heater_range=True):
if adjust_heater_range:
range_to_set = None
for setting in self.heater_settings:
if setting['temp_start'] <= value and setting['temp_end'] >= value:
range_to_set = setting['range']
if range_to_set != None:
self.set_heater_range(range_to_set)
else:
raise ValueError(f"Heater range for the target value {value} could not be set")
self.dilsc.setParameter(f'{self.name}', 'target', value)
@_check_connection
def get_target_value(self):
cacheitem = self.dilsc.getParameter(f'{self.name}', 'target', trycache=False)
return cacheitem.value
@_check_connection
def is_moving(self):
response = self.dilsc.getParameter(f'{self.name}','status', trycache=False)
return response[0][0] > StatusType.PREPARED
@_check_connection
def get_PID_parameters(self):
""" Returns the current PID parameters associated with the control loop for
this thermometer.
"""
# response = self.dilsc.getParameter(f'{self.name}','ctrlpars', trycache=False)
response = self.dilsc.execCommand('lscio', 'communicate', 'PID?')
return response
@_check_connection
def set_PID_parameters(self, p, i, d):
""" Sets the PID parameters for the associated control loop.
TODO:
- This still returns a timeout error but sets the correct values.
- The range is limited to less than the Lakeshore range allows, this needs
to be fixed in frappy.
"""
# self.dilsc.setParameter(f'{self.name}', 'ctrlpars', {'p': p, 'i': i, 'd': d})
command_string = f'PID {0},{p},{i},{d}; PID?'
self.dilsc.execCommand('lscio', 'communicate', command_string)
class MagnetCoil(Adjustable):
def __init__(self, name, dilsc_connection, direction, limit_low=-0.0001, limit_high=0.0001):
super().__init__(name, limit_low=-0.0001, limit_high=0.0001)
self.direction = direction.lower()
if self.direction not in ["x", "y", "z"]:
raise ValueError("Direction must be either x, y or z.")
self.dilsc = dilsc_connection
def _check_connection(func):
def checker(self, *args, **kwargs):
if not self.dilsc.online:
raise ConnectionError(f'No connection to dilsc at {self.address}')
else:
return func(self, *args, **kwargs)
return checker
@_check_connection
def get_current_value(self):
cacheitem = self.dilsc.getParameter(f'mf{self.direction}', 'value', trycache=False)
return cacheitem.value
@_check_connection
def set_target_value(self, value):
self.dilsc.setParameter(f'mf{self.direction}', 'target', value)
@_check_connection
def is_moving(self):
response = self.dilsc.getParameter(f'mf{self.direction}','status', trycache=False)
return response[0][0] > StatusType.PREPARED
@_check_connection
def set_ramp_speed(self, direction, value):
""" Sets ramp speed for a given direction (x,y or z) and value in T/min.
"""
if value > 0.5:
raise ValueError('Do not exceed 0.5 T/min unless you want a quench party')
if value <= 0:
raise ValueError('Only positive values are allowed')
if self.direction not in ["x", "y", "z"]:
raise ValueError("Direction must be either x, y or z.")
else:
self.dilsc.setParameter(direction, 'ramp', value)
return