diff --git a/frappy_psi/thermofisher.py b/frappy_psi/thermofisher.py index 589dfe0..f160e06 100644 --- a/frappy_psi/thermofisher.py +++ b/frappy_psi/thermofisher.py @@ -1,5 +1,3 @@ -#!/usr/bin/env 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 @@ -17,64 +15,13 @@ # # Module authors: # Oksana Shliakhtun +# Markus Zolliker # ***************************************************************************** -""" RUFS Command: Description of Bits +"""bath thermostat Thermo Scientificâ„¢ ARCTIC A10 Refrigerated Circulators""" - ====== ======================================================== ============================================== - Value Description - ====== ======================================================== ============================================== - V1 - B6: warning, rtd1 (internal temp. sensor) is shorted - B0 --> 1 - B7: warning, rtd1 is open - B1 --> 2 - V2 - B0: error, HTC (high temperature cutout) fault B2 --> 4 - - B1: error, high RA (refrigeration) temperature fault B3 --> 8 - - V3 B4 --> 16 - B0: warning, low level in the bath - B5 --> 32 - B1: warning, low temperature - B6 --> 64 - B2: warning, high temperature - B7 --> 128 - B3: error, low level in the bath - - B4: error, low temperature fault - - B5: error, high temperature fault - - B6: error, low temperature fixed* fault - - B7: error, high temperature fixed** fault - - V4 - B3: idle, circulator** is running - - B5: error, circulator** fault - - V5 - B0: error, pump speed fault - - B1: error, motor overloaded - - B2: error, high pressure cutout - - B3: idle, maximum cooling - - B4: idle, cooling - - B5: idle, maximum heating - - B6: idle, heating - ====== ======================================================== ============================================== - -""" - -from frappy.core import StringIO, Parameter, HasIO, \ +from frappy.core import Command, StringIO, Parameter, HasIO, \ Drivable, FloatRange, IDLE, BUSY, ERROR, WARN, BoolType +from frappy.structparam import StructParam from frappy_psi.convergence import HasConvergence @@ -85,17 +32,17 @@ class ThermFishIO(StringIO): class TemperatureLoopA10(HasConvergence, HasIO, Drivable): ioClass = ThermFishIO - value = Parameter('internal temperature', unit='degC') value = Parameter('temperature', unit='degC') target = Parameter('setpoint/target', datatype=FloatRange, unit='degC', default=0) - circ_on = Parameter('is circulation running', BoolType(), readonly=False, default=False) - # pids - p_heat = Parameter('proportional heat parameter', FloatRange(), readonly=False) - i_heat = Parameter('integral heat parameter', FloatRange(), readonly=False) - d_heat = Parameter('derivative heat parameter', FloatRange(), readonly=False) - p_cool = Parameter('proportional cool parameter', FloatRange(), readonly=False) - i_cool = Parameter('integral cool parameter', FloatRange(), readonly=False) - d_cool = Parameter('derivative cool parameter', FloatRange(), readonly=False) + control_active = Parameter('circilation and control is on', BoolType(), default=False) + ctrlpars = StructParam('control parameters struct', dict( + p_heat = Parameter('proportional heat parameter', FloatRange()), + i_heat = Parameter('integral heat parameter', FloatRange()), + d_heat = Parameter('derivative heat parameter', FloatRange()), + p_cool = Parameter('proportional cool parameter', FloatRange()), + i_cool = Parameter('integral cool parameter', FloatRange()), + d_cool = Parameter('derivative cool parameter', FloatRange()), + ), readonly=False) status_messages = [ (ERROR, 'high tempr. cutout fault', 2, 0), @@ -122,20 +69,22 @@ class TemperatureLoopA10(HasConvergence, HasIO, Drivable): ] def get_par(self, cmd): - """ - All the reading commands starts with 'R', in the source code all the commands are written without 'R' (except - 'RUFS').The result of a reading command is a value in the format '20C', without spaces. + """get parameter and convert to float - :param cmd: any hardware command + :param cmd: hardware command without the leading 'R' - :return: 'R'+cmd + :return: result converted to float """ new_cmd = 'R' + cmd - reply = self.communicate(new_cmd) - if any(unit.isalpha() for unit in reply): - reply = ''.join(unit for unit in reply if not unit.isalpha()) + reply = self.communicate(new_cmd).strip() + while reply[-1].isalpha(): + reply = reply[:-1] return float(reply) + def set_par(self, cmd, value): + self.communicate(f'S{cmd} {value}') + return self.get_par(cmd) + def read_value(self): """ Reading internal temperature sensor value. @@ -143,6 +92,34 @@ class TemperatureLoopA10(HasConvergence, HasIO, Drivable): return self.get_par('T') def read_status(self): + """ convert from RUFS Command: Description of Bits + + ====== ======================================================== =============== + Value Description + ====== ======================================================== =============== + V1 B6: warning, rtd1 (internal temp. sensor) is shorted B0 --> 1 + B7: warning, rtd1 is open B1 --> 2 + V2 B0: error, HTC (high temperature cutout) fault B2 --> 4 + B1: error, high RA (refrigeration) temperature fault B3 --> 8 + V3 B0: warning, low level in the bath B5 --> 32 + B1: warning, low temperature B6 --> 64 + B2: warning, high temperature B7 --> 128 + B3: error, low level in the bath + B4: error, low temperature fault + B5: error, high temperature fault + B6: error, low temperature fixed* fault + B7: error, high temperature fixed** fault + V4 B3: idle, circulator** is running + B5: error, circulator** fault + V5 B0: error, pump speed fault + B1: error, motor overloaded + B2: error, high pressure cutout + B3: idle, maximum cooling + B4: idle, cooling + B5: idle, maximum heating + B6: idle, heating + ====== ======================================================== =============== + """ result_str = self.communicate('RUFS') # read unit fault status values_str = result_str.strip().split() values_int = [int(val) for val in values_str] @@ -157,72 +134,55 @@ class TemperatureLoopA10(HasConvergence, HasIO, Drivable): return status_type, status_msg return WARN, 'circulation off' - def read_circ_on(self): - return self.communicate('RO') + def read_control_active(self): + return int(self.get_par('O')) - def write_circ_on(self, circ_on): - circ_on_str = '1' if circ_on else '0' - self.communicate(f'SO {circ_on_str}') - return self.read_circ_on() + @Command + def control_off(self): + """switch control and circulation off""" + self.control_active = self.set_par('O', 0) def read_target(self): return self.get_par('S') def write_target(self, target): - """ - :param target: here, it serves as an equivalent to a setpoint. - """ - self.write_circ_on('1') + self.control_active = self.set_par('O', 1) self.communicate(f'SS {target}') self.convergence_start() return target - ## heat PID def read_p_heat(self): - p_heat = self.get_par('PH') - return float(p_heat) + return self.get_par('PH') - def write_p_heat(self, p_heat): - self.communicate(f'SPH {p_heat}') - return p_heat + def write_p_heat(self, value): + return self.set_par('PH', value) def read_i_heat(self): - i_heat = self.get_par('IH') - return float(i_heat) + return self.get_par('IH') - def write_i_heat(self, i_heat): - self.communicate(f'SIH {i_heat}') - return i_heat + def write_i_heat(self, value): + return self.set_par('IH', value) def read_d_heat(self): - d_heat = self.get_par('DH') - return float(d_heat) + return self.get_par('DH') - def write_d_heat(self, d_heat): - self.communicate(f'SDH {d_heat}') - return d_heat + def write_d_heat(self, value): + return self.set_par('DH', value) - ## cool PID def read_p_cool(self): - p_cool = self.get_par('PC') - return float(p_cool) + return self.get_par('PC') - def write_p_cool(self, p_cool): - self.communicate(f'SPC {p_cool}') - return p_cool + def write_p_cool(self, value): + return self.set_par('PC', value) def read_i_cool(self): - i_cool = self.get_par('IC') - return float(i_cool) + return self.get_par('IC') - def write_i_cool(self, i_cool): - self.communicate(f'SIC {i_cool}') - return i_cool + def write_i_cool(self, value): + return self.set_par('IC', value) def read_d_cool(self): - d_cool = self.get_par('DC') - return float(d_cool) + return self.get_par('DC') - def write_d_cool(self, d_cool): - self.communicate(f'SDC {d_cool}') - return d_cool + def write_d_cool(self, value): + return self.set_par('DC', value)