- all of them have to be checked! Change-Id: I89d55ca683d0b2710222f14c2c3cd42f8fbf3a1f
145 lines
5.4 KiB
Python
145 lines
5.4 KiB
Python
# -*- 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>
|
|
#
|
|
# *****************************************************************************
|
|
|
|
"""driver for cryotel stirling cryocooler"""
|
|
|
|
from frappy.core import Command, EnumType, FloatRange, HasIO, Parameter, Drivable, StringIO, StringType
|
|
from frappy.errors import CommunicationFailedError, HardwareError
|
|
|
|
|
|
class CryotelIO(StringIO):
|
|
end_of_line = ('\n', '\r')
|
|
# eol_write will be given explicitly
|
|
|
|
@Command(StringType(), result=StringType())
|
|
def communicate(self, command):
|
|
"""send a command and receive a reply
|
|
|
|
we receive an echo line first.
|
|
in case the command does not contain '=', the effective reply is in a second line
|
|
"""
|
|
with self._lock:
|
|
echo = super().communicate(command)
|
|
if echo.strip() != command.strip():
|
|
raise CommunicationFailedError('missing echo')
|
|
if '=' in command:
|
|
reply = echo
|
|
else:
|
|
reply = self._conn.readline().decode()
|
|
return reply
|
|
|
|
|
|
class Cryo(HasIO, Drivable):
|
|
value = Parameter('current temperature', FloatRange(unit='deg'))
|
|
target = Parameter('target temperature', FloatRange(unit='deg'), readonly=False)
|
|
mode = Parameter('control mode', EnumType('mode', off=0, power=1, temperature=2), readonly=False)
|
|
power = Parameter('power', FloatRange(unit='W'))
|
|
setpower = Parameter('requested power', FloatRange(unit='W'), default=0)
|
|
cool_power = Parameter('power for cooling', FloatRange(unit='W'), default=240, readonly=False)
|
|
hold_power = Parameter('power for holding T', FloatRange(unit='W'), default=120, readonly=False)
|
|
cool_threshold = Parameter('switch to cool_power once above this value', FloatRange(unit='K'), default=100, readonly=False)
|
|
hold_threshold = Parameter('switch to hold_power once below this value', FloatRange(unit='K'), default=95, readonly=False)
|
|
|
|
ioClass = CryotelIO
|
|
cnt_inside = 0
|
|
|
|
def get(self, cmd):
|
|
return float(self.communicate(cmd))
|
|
|
|
def set(self, cmd, value, check=False):
|
|
setcmd = '%s=%.2f' % (cmd, value)
|
|
self.communicate(setcmd)
|
|
reply = float(self.communicate(cmd))
|
|
if check:
|
|
if value != reply:
|
|
raise HardwareError('illegal reply from %s: %g' % (cmd, reply))
|
|
return reply
|
|
|
|
def read_value(self):
|
|
temp = self.get('TC')
|
|
status = self.status
|
|
if self.mode == 1: # P reg
|
|
setpower = self.setpower
|
|
if temp < self.hold_threshold:
|
|
setpower = self.hold_power
|
|
status = self.Status.IDLE, 'holding'
|
|
elif temp > self.cool_threshold:
|
|
setpower = self.cool_power
|
|
status = self.Status.BUSY, 'cooling'
|
|
if abs(setpower - self.setpower) > 0.009:
|
|
self.setpower = self.set('SET PWOUT', setpower)
|
|
elif self.mode == 2: # T reg
|
|
if self.status[0] == 'BUSY':
|
|
if abs(temp - self.target) < 1:
|
|
self.cnt_inside += 1
|
|
if self.cnt_inside >= 10:
|
|
status = self.Status.IDLE, ''
|
|
else:
|
|
status = self.Status.BUSY, 'settling'
|
|
else:
|
|
status = self.Status.BUSY, 'outside tolerance'
|
|
else:
|
|
status = self.Status.IDLE, 'off'
|
|
if status != self.status:
|
|
self.status = status
|
|
return temp
|
|
|
|
def read_target(self):
|
|
return self.get('SET TTARGET')
|
|
|
|
def write_target(self, value):
|
|
if abs(value - self.target) > 0.009:
|
|
self.status = self.Status.BUSY, 'changed target'
|
|
self.cnt_inside = 0
|
|
return self.set('SET TTARGET', value)
|
|
|
|
def read_power(self):
|
|
return self.get('P')
|
|
|
|
def read_setpower(self):
|
|
return self.get('SET PWOUT')
|
|
|
|
def read_mode(self):
|
|
is_stopped = self.get('SET SSTOP')
|
|
if is_stopped:
|
|
return 0 # off
|
|
pidmode = self.get('SET PID')
|
|
if pidmode == 2:
|
|
return 2 # T reg
|
|
return 1 # P reg
|
|
|
|
def write_mode(self, value):
|
|
if value == 0:
|
|
self.set('SET SSTOP', 1, check=True)
|
|
self.status = self.Status.IDLE, 'off'
|
|
return value
|
|
is_stopped = self.get('SET SSTOP')
|
|
if is_stopped:
|
|
self.set('SET SSTOP', 0, check=True)
|
|
if value == 1:
|
|
self.set('SET PID', 0, check=True)
|
|
self.read_value()
|
|
else:
|
|
self.set('SET PID', 2, check=True)
|
|
self.write_target(self.target)
|
|
return value
|