Commit c4be57ed authored by Anthony Mauro's avatar Anthony Mauro

Refactoring 335 model

parent 82b3bcd5
Pipeline #12134 failed with stages
in 31 minutes and 24 seconds
......@@ -27,6 +27,7 @@ yml configuration example:
inputs:
- name: ls335_A
channel: A
# possible set-point units: Kelvin, Celsius, Sensor_unit
unit: Kelvin
#tango_server: ls_335
- name: ls335_A_c # input temperature in Celsius
......@@ -38,6 +39,7 @@ yml configuration example:
- name: ls335_B
channel: B
# possible set-point units: Kelvin, Celsius, Sensor_unit
unit: Kelvin
#tango_server: ls_335
- name: ls335_B_c # input temperature in Celsius
......@@ -69,6 +71,7 @@ import time
import enum
from bliss.common import mapping
from bliss.comm import serial
from bliss.comm import gpib
from bliss.comm.util import get_interface, get_comm
from bliss.common.logtools import LogMixin
from bliss.controllers.temperature.lakeshore.lakeshore import LakeshoreBase
......@@ -124,19 +127,16 @@ class LakeShore335(LogMixin):
# ------------
def _initialize_input(self, input):
self._logger.info("_initialize_input")
self._add_custom_method_input(input)
# - Output object
# -------------
def _initialize_output(self, output):
self._logger.info("_initialize_output")
# self._add_custom_method_output(output)
# - Loop object
# -----------
def _initialize_loop(self, loop):
self._logger.info("_initialize_loop")
self._add_custom_method_loop(loop)
# Get input object channel
ipc = loop.input.config["channel"]
# Get output object unit
......@@ -185,76 +185,20 @@ class LakeShore335(LogMixin):
"Temperature OverRange in Sensor_unit on input %s" % channel
)
# Adding CUSTOM INPUT-object related method(s)
# --------------------------------------------
def _add_custom_method_input(self, input):
self._logger.info("_add_custom_method_input")
def alarm_status():
""" Shows high and low alarm state for given input
Args:
None
Returns:
high and low alarm state (str, str): "On/Off"
"""
self._logger.info("alarm_status")
return self._alarm_status(input.config.get("channel"))
input.alarm_status = alarm_status
def alarm_reset():
""" Clears both the high and low status of all alarms
Args:
None (though this command does not need the input channel,
we put it here, since alarms are related to the state
on input like for ex. measured temperature above
alarm high-limit etc.)
Returns:
None
"""
self.log.info("alarm_reset")
return self._alarm_reset()
input.alarm_reset = alarm_reset
# Next 3 (model, show, loglevel) are also in custom methods
# for output and loop objects
def model():
""" Get the model number
Returns:
model (int): model number
"""
self.log.info("model")
return self._model()
input.model = model
def show():
""" Display all main parameters and values for the
temperature controller
Returns:
model, PID, heater range, loop status, sensors
configuration, inputs temperature
"""
self.log.info("show")
return self._show()
input.show = show
def loglevel(value=None):
""" Set/Read the log level ("NOTSET","DEBUG","INFO","WARNING",
"WARN","ERROR","CRITICAL","FATAL")
Args:
value (str): The value of the log-level if set
None if read
Returns:
None if set
value (str): The value of the log-level if read
"""
self.log.info("loglevel")
return self._log_level(value=value)
input.loglevel = loglevel
def _sensor_type(self, channel, type=None, compensation=None):
""" Read or set input type parameters
Args: According to the model, use the appropriate args
type (int): 0 to ?
compensation (int): 0=off and 1 =on
example: input.sensor_type(type=3,compensation=1)
Returns:
<type>, <compensation>
"""
self._logger.info("_sensor_type")
if type is None:
return self.send_cmd("INTYPE?", channel=channel)
else:
self.send_cmd("INTYPE", type, compensation, channel=channel)
# Standard OUTPUT-object related method(s)
# ----------------------------------------
......@@ -268,12 +212,11 @@ class LakeShore335(LogMixin):
None if set
value (float): The value of the setpoint if read
"""
self.log.info("setpoint")
self._channel = channel
self._logger.info("setpoint")
if value is None:
return float(self.send_cmd("SETP?"))
# send the setpoint
self.send_cmd("SETP", value)
return float(self.send_cmd("SETP?", channel=channel))
else:
self.send_cmd("SETP", value, channel=channel)
def ramp_rate(self, channel, value=None):
""" Set/read the control setpoint ramp rate.
......@@ -286,18 +229,16 @@ class LakeShore335(LogMixin):
None if set
value (float): The value of the ramp rate if read.
"""
self.log.info("ramp_rate")
self._channel = channel
self._logger.info("ramp_rate")
if value is None:
rate_value = self.send_cmd("RAMP?").split(",")[1]
return float(rate_value)
r = self.send_cmd("RAMP?", channel=channel).split(",")
state = "ON" if int(r[0]) == 1 else "OFF"
rate_value = float(r[1])
return {"state": state, "rate": rate_value}
# send the ramp rate if OK
if value < 0.1 or value > 100:
raise ValueError("Ramp value %s is out of bounds [0.1,100]" % value)
# send the ramp rate
self.send_cmd("RAMP", 0, value)
self.send_cmd("RAMP", 0, value, channel=channel)
def ramp(self, channel, sp, rate):
"""Change temperature to a set value at a controlled ramp rate
......@@ -308,127 +249,50 @@ class LakeShore335(LogMixin):
Returns:
None
"""
self.log.info("ramp")
self.log.debug("ramp(): SP=%r, RR=%r" % (sp, rate))
self._channel = channel
self._logger.info("ramp")
self._logger.debug("ramp(): SP=%r, RR=%r" % (sp, rate))
self.setpoint(channel, sp)
if rate < 0.1 or rate > 100:
raise ValueError("Ramp value %s is out of bounds [0.1,100]" % rate)
self.send_cmd("RAMP", 1, rate)
# Adding CUSTOM OUTPUT-object related method(s)
# ---------------------------------------------
def _add_custom_method_output(self, output):
self.log.info("_add_custom_method_output")
def ramp_status():
"""Check ramp status (if running or not)
Args:
None
Returns:
Ramp status (1 = running, 0 = not running)
"""
self.log.info("ramp_status")
return self._rampstatus(output.config.get("channel"))
self.send_cmd("RAMP", 1, rate, channel=channel)
output.ramp_status = ramp_status
def heater_range(value=None):
""" Set/Read the heater range (0 to 3) from 0 to 50W in 50Ohms
Args:
value (int): The value of the range if set. The valid range:
for chan. 1 and 2: 0=Off,1=Low,2=Medium,3=High
for channels 3 and 4: 0=Off,1=On
None if read
Returns:
None if set
value (int): The value of the range if read
"""
self.log.info("heater_range")
return self._heater_range(output.config.get("channel"), value=value)
output.heater_range = heater_range
def outmode(mode=None, input=None):
""" Read/Set Output Control Mode and Control Input
Args:
mode (int): control mode. Valide entires: 0=Off,
1=Closed Loop PID, 2=Zone, 3=Open Loop,
4=Monitor Out, 5=Warmup Supply
input (str): which input to control from.
Valid entries: None, A, B
Returns:
None if set
mode (str): control mode
input (str): which input controls the loop.
"""
self.log.info("outmode")
return self._outmode(output.config.get("channel"), mode=mode, input=input)
output.outmode = outmode
# Next 3 (model, show, loglevel) are also in custom methods
# for input and loop objects
def model():
""" Get the model number
Returns:
model (int): model number
"""
self.log.info("model")
return self._model()
output.model = model
def show():
""" Display all main parameters and values for the
temperature controller
Returns:
model, PID, heater range, loop status, sensors
configuration, inputs temperature
"""
self.log.info("show")
return self._show()
output.show = show
def loglevel(value=None):
""" Set/Read the log level ("NOTSET","DEBUG","INFO","WARNING",
"WARN","ERROR","CRITICAL","FATAL")
Args:
value (str): The value of the log-level if set
None if read
Returns:
None if set
value (str): The value of the log-level if read
"""
self.log.info("loglevel")
return self._log_level(value=value)
output.loglevel = loglevel
def ramp_status(self, channel):
""" Check ramp status (if running or not)
Args:
channel (int): output channel. Valid entries: 1 or 2
Returns:
Ramp status (1 = running, 0 = not running)
"""
# TODO: in case rampstatus found is 0 (= no ramping active)
# could add sending command *STB? and checking bit 7,
# which indicates (when set to 1) that ramp is done.
self._logger.info("ramp_status")
self._logger.debug("ramp_status(): channel = %r" % channel)
ramp_stat = self.send_cmd("RAMPST?", channel=channel)
self._logger.debug("ramp_status(): ramp_status = %r" % ramp_stat)
return int(ramp_stat)
# Standard LOOP-object related method(s)
# --------------------------------------
def pid(self, channel, **kwargs):
""" Read/Set Control Loop PID Values (P, I, D)
Args:
Args:
channel (int): loop channel. Valid entries: 1 or 2
P (float): Proportional gain (0.1 to 1000)
I (float): Integral reset (0.1 to 1000) [value/s]
D (float): Derivative rate (0 to 200) [%]
None if read
Returns:
P (float): Proportional gain (0.1 to 1000), None if read
I (float): Integral reset (0.1 to 1000) [value/s], None if read
D (float): Derivative rate (0 to 200) [%], None if read
Returns:
None if set
p (float): P
i (float): I
d (float): D
"""
self.log.info("pid")
self._channel = channel
self._logger.info("pid")
kp = kwargs.get("P")
ki = kwargs.get("I")
kd = kwargs.get("D")
if len(kwargs):
kpc, kic, kdc = self.send_cmd("PID?").split(",")
kpc, kic, kdc = self.send_cmd("PID?", channel=channel).split(",")
if kp is None:
kp = kpc
if ki is None:
......@@ -443,73 +307,11 @@ class LakeShore335(LogMixin):
raise ValueError("Integral reset %s is out of bounds [0.1,1000]" % ki)
if float(kd) < 0 or float(kd) > 200:
raise ValueError("Derivative rate %s is out of bounds [0,200]" % kd)
self.send_cmd("PID", kp, ki, kd)
self.send_cmd("PID", kp, ki, kd, channel=channel)
else:
kp, ki, kd = self.send_cmd("PID?").split(",")
kp, ki, kd = self.send_cmd("PID?", channel=channel).split(",")
return float(kp), float(ki), float(kd)
# Adding CUSTOM LOOP-object related method(s)
# ---------------------------------------------
def _add_custom_method_loop(self, loop):
self.log.info("_add_custom_method_loop")
def outmode(mode=None, input=None):
""" Read/Set Output Control Mode and Control Input
Args:
mode (int): control mode. Valid entries: 0=Off,
1=Closed Loop PID, 2=Zone, 3=Open Loop,
4=Monitor Out, 5=Warmup Supply
input (str): which input to control from.
Valid entries: None, A or B.
Returns:
None if set
mode (str): control mode
input (str): which input control the loop.
"""
self.log.info("outmode")
return self._outmode(loop.config.get("channel"), mode=mode, input=input)
loop.outmode = outmode
# Next 3 (model, show, loglevel) are also in custom methods
# for input and output objects
def model():
""" Get the model number
Returns:
model (int): model number
"""
self.log.info("model")
return self._model()
loop.model = model
def show():
""" Display all main parameters and values for the
temperature controller
Returns:
model, PID, heater range, loop status,
sensors configuration, inputs temperature
"""
self.log.info("show")
return self._show()
loop.show = show
def loglevel(value=None):
""" Set/Read the log level ("NOTSET","DEBUG","INFO","WARNING",
"WARN","ERROR","CRITICAL","FATAL")
Args:
value (str): The value of the log-level if set
None if read
Returns:
None if set
value (str): The value of the log-level if read
"""
self.log.info("loglevel")
return self._log_level(value=value)
loop.loglevel = loglevel
# General CUSTOM methods [valid for any type of object:
# input, output, loop]
# -----------------------------------------------------
......@@ -518,543 +320,12 @@ class LakeShore335(LogMixin):
Returns:
model (int): model number
"""
self.log.info("_model")
self._logger.info("_model")
model = self.send_cmd("*IDN?").split(",")[1]
return int(model[5:8])
def _show(self):
""" Display all main parameters and values for the
temperature controller
Returns:
device ID, PID, heater range, loop status,
sensors configuration, inputs temperature etc.
"""
self.log.info("_show")
self.log.debug("channel = %r" % self._channel)
# Get full identification string
full_id = self.send_cmd("*IDN?")
print("\nLakeshore identification %s" % (full_id))
# Sensor A
# --------
print("\nSensor A:")
print("=========")
# Specify channel to be used in send_cmd for the commands
# needing it: INCRV?, INTYPE?, RDGST?, KRDG?, CRDG?, SRDG?
self._channel = "A"
# Get temperature calibration curve for input A
asw = self.send_cmd("INCRV?")
print("Uses calibration curve number %d" % int(asw))
asw = self.send_cmd("CRVHDR? %s" % asw)
asw = asw.split(",")
print(
"Curve type = %s, SerNum = %s, Format = %s"
% (asw[0].strip(), asw[1].strip(), self.CURVEFORMAT335[int(asw[2])])
)
print(
"Temp.limit = %s K , Temp.coeff. = %s"
% (asw[3], self.CURVETEMPCOEF335[int(asw[4])])
)
# Get input A sensor preferred units (Kelvin,Celsius,Sensor-unit)
# The same preferred units are used for the control set-point
asw = self.send_cmd("INTYPE?")
asw = asw.split(",")
ipsu_A = asw[4]
print("Input A sensor preferred units = %s" % self.REVSPUNITS335[int(ipsu_A)])
# Query Input Status before reading temperature
# If status is OK, then read the temperature in
# Kelvin, Celsius and sensor units (volt or ohm).
asw = int(self.send_cmd("RDGST?"))
if asw == 0:
# Read input A temperature now since input status OK
tempK_A = float(self.send_cmd("KRDG?")) # in Kelvin
tempC_A = float(self.send_cmd("CRDG?")) # in Celsius
resorvol_A = float(self.send_cmd("SRDG?")) # in sensor units
print("Temperature on input A = %.3f K (%.3f C)" % (tempK_A, tempC_A))
print("Input A reading in Sensor Units = %.3f" % resorvol_A)
else:
print("Invalid status on input %s" % self._channel)
if asw & 16:
self.log.warning("Temperature UnderRange on input %s" % self._channel)
if asw & 32:
self.log.warning("Temperature OverRange on input %s" % self._channel)
if asw & 64:
self.log.warning("0 value in sensor units on input %s" % self._channel)
if asw & 128:
self.log.warning(
"Overrange of value in sensor units on input %s" % self._channel
)
# Sensor B
# --------
print("\nSensor B:")
print("=========")
# Specify channel to be used in send_cmd for the commands
# needing it: INCRV?, INTYPE?, KRDG?, CRDG?, SRDG?
self._channel = "B"
# Get temperature calibration curve for input B
asw = self.send_cmd("INCRV?")
print("Uses calibration curve number %d" % int(asw))
asw = self.send_cmd("CRVHDR? %s" % asw)
asw = asw.split(",")
print(
"Curve type = %s, SerNum = %s, Format = %s"
% (asw[0].strip(), asw[1].strip(), self.CURVEFORMAT335[int(asw[2])])
)
print(
"Temp.limit = %s K, Temp.coeff. = %s"
% (asw[3], self.CURVETEMPCOEF335[int(asw[4])])
)
# Get input B sensor preferred units (Kelvin,Celsius,Sensor-unit)
# The same preferred units are used for the control set-point
asw = self.send_cmd("INTYPE?")
asw = asw.split(",")
ipsu_B = asw[4]
print("Input B sensor preferred units = %s" % self.REVSPUNITS335[int(ipsu_B)])
# Query Input Status before reading temperature
# If status is OK, then read the temperature in
# Kelvin, Celsius and sensor units (volt or ohm).
asw = int(self.send_cmd("RDGST?"))
if asw == 0:
# Read input B temperature now since input status OK
tempK_B = float(self.send_cmd("KRDG?")) # in Kelvin
tempC_B = float(self.send_cmd("CRDG?")) # in Celsius
resorvol_B = float(self.send_cmd("SRDG?")) # in sensor units
print("Temperature on input B = %.3f K (%.3f C)" % (tempK_B, tempC_B))
print("Input B reading in Sensor Units = %.3f" % resorvol_B)
else:
print("Invalid status on input %s" % self._channel)
if asw & 16:
self.log.warning("Temperature UnderRange on input %s" % self._channel)
if asw & 32:
self.log.warning("Temperature OverRange on input %s" % self._channel)
if asw & 64:
self.log.warning("0 value in sensor units on input %s" % self._channel)
if asw & 128:
self.log.warning(
"Overrange of value in sensor units on input %s" % self._channel
)
# Loop 1
# ------
print("\nLoop 1:")
print("=======")
# Specify channel to be used in send_cmd for the commands
# needing it: OUTMODE?, RAMP?, SETP?, RAMPST?, PID?
self._channel = "1"
# Get control loop parameters
asw = self.send_cmd("OUTMODE?").split(",")
mode = asw[0]
sensor = asw[1]
if sensor == "1":
units = ipsu_A
if sensor == "2":
units = ipsu_B
units = self.REVSPUNITS335[int(units)]
print("Controlled by sensor %s in %s " % (sensor, units))
print("Temp Control is set to %s" % self.MODE335[int(mode)])
# Read ramp enable/disable status and ramp rate
rp_1 = self.send_cmd("RAMP?").split(",")
ronoff_1 = "ON" if int(rp_1[0]) == 1 else "OFF"
rrate_1 = float(rp_1[1])
# Read setpoint
sp_1 = float(self.send_cmd("SETP?"))
print(
"Ramp enable is %s with set-point %.3f %s and ramp-rate = %.3f K/min."
% (ronoff_1, sp_1, units, rrate_1)
)
# Read ramp status (only if ramp is enabled)
if ronoff_1 == "ON":
asw = self.send_cmd("RAMPST?")
rs_1 = "RAMPING" if int(asw) == 1 else "NOT RAMPING"
print("Ramp status is %s." % rs_1)
kp, ki, kd = self.send_cmd("PID?").split(",")
print("PID parameters: ")
print(" P = %.1f" % float(kp))
print(" I = %.1f" % float(ki))
print(" D = %.1f" % float(kd))
# Loop 2
# ------
print("\nLoop 2:")
print("=======")
# Specify channel to be used in send_cmd for the commands
# needing it: OUTMODE?, RAMP?, SETP?, RAMPST?, PID?
self._channel = "2"
# Get control loop parameters
asw = self.send_cmd("OUTMODE?").split(",")
mode = asw[0]
sensor = asw[1]
if sensor == "1":
units = ipsu_A
if sensor == "2":
units = ipsu_B
units = self.REVSPUNITS335[int(units)]
print("Controlled by sensor %s in %s " % (sensor, units))
print("Temp Control is set to %s" % self.MODE335[int(mode)])
# Read ramp enable/disable status and ramp rate
rp_2 = self.send_cmd("RAMP?").split(",")
ronoff_2 = "ON" if int(rp_2[0]) == 1 else "OFF"
rrate_2 = float(rp_2[1])
# Read setpoint
sp_2 = float(self.send_cmd("SETP?"))
print(
"Ramp enable is %s with set-point %.3f %s and ramp-rate = %.3f K/min. "
% (ronoff_2, sp_2, units, rrate_2)
)
# Read ramp status (only if ramp is enabled)
if ronoff_2 == "ON":
asw = self.send_cmd("RAMPST?")
rs_2 = "RAMPING" if int(asw) == 1 else "NOT RAMPING"
print("Ramp status is %s." % rs_2)
# Get PID parameters for loop 2
kp, ki, kd = self.send_cmd("PID?").split(",")
print("PID parameters: ")
print(" P = %.1f" % float(kp))
print(" I = %.1f" % float(ki))
print(" D = %.1f" % float(kd))
# Output Heater
# -------------
print("\nHeater 1:")
print("=========")
# Get heater range value
self._channel = "1"
htr_range = int(self.send_cmd("RANGE?"))
if htr_range == 0:
print("Heater is OFF")
else:
print("Heater is on range = %d" % htr_range)
# Get heater power
htr_power = float(self.send_cmd("HTR?"))
print("Heater power = %.1f %%" % htr_power)
# Get heater status
htr_status = int(self.send_cmd("HTRST?"))
print("Heater status = %s" % self.HTRSTATUS335[int(htr_status)])
print("\nHeater 2:")
print("=========")
# Get heater range value
self._channel = "2"
htr_range = int(self.send_cmd("RANGE?"))
if htr_range == 0:
print("Heater is OFF")
else:
print("Heater is on range = %d" % htr_range)
# Get heater power
htr_power = float(self.send_cmd("HTR?"))
print("Heater power = %.1f %%" % htr_power)
# Get heater status
htr_status = int(self.send_cmd("HTRST?"))
print("Heater status = %s" % self.HTRSTATUS335[int(htr_status)])
def _log_level(self, value=None):
""" Set/Read the log level ("NOTSET","DEBUG","INFO","WARNING",
"WARN","ERROR","CRITICAL","FATAL")
Args:
value (str): The value of the log-level if set
None if read
Returns:
None if set
value (str): The value of the log-level if read
"""
self.log.info("_low_level")
# Get the log-level
if value is None:
level_as_num = self.log.level
return logging._levelToName[level_as_num]
# Set the log-level
value = value.upper()
if value not in [
"NOTSET",
"DEBUG",
"INFO",
"WARNING",
"WARN",
"ERROR",
"CRITICAL",
"FATAL",
]:
raise ValueError("Log-Level %s is invalid" % value)
self.log.setLevel(logging._nameToLevel[value])
# CUSTOM INPUT-object related method(s)