import serial
import time
import logging
class Program:
"""Aging chamber program step model"""
def __init__(self, temperature, humidity, hour, minute):
self.temperature = temperature
self.humidity = humidity
self.hour = hour
self.minute = minute
class GWSChamberCtrl(object):
# Serial command constants
COMMAND_GET_TEMP = "1, TEMP?"
COMMAND_GET_HUMI = "1, HUMI?"
COMMAND_GET_PRGM = "1, PRGM SET?"
COMMAND_STOP_DEV = "1, MODE, OFF"
COMMAND_EXECUTE_PROGRAM = "1, PRGM, RUN, RAM:1, STEP1"
COMMAND_SET_CONSTANT_TEMP = "1, TEMP"
COMMAND_SET_MODE_CONSTANT = "1, MODE, CONSTANT"
# Special response identifiers
NA_PREFIX = "NA:"
NOT_READY_CODE = "NA:CONT NOT READY-1"
# Success response identifier
OK_SIGN = "[b'OK:1"
def __init__(self, port, baudrate=19200, bytesize=8, stopbits=1, parity=None):
self.logger = logging.getLogger()
if not logging.root.handlers:
self.logger.setLevel(logging.INFO)
self.console_handler = logging.StreamHandler()
self.formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s", "%m-%d %H:%M:%S")
self.console_handler.setFormatter(self.formatter)
self.logger.addHandler(self.console_handler)
self.port = port
self.baudrate = baudrate
self.bytesize = bytesize
self.stopbits = stopbits
self.parity = parity
try:
self.ser = self.open_serial(port=self.port, baudrate=self.baudrate, bytesize=self.bytesize,
stopbits=self.stopbits, parity=self.parity)
self.logger.info("Serial Port Opened")
except Exception as e:
self.logger.error("Error Opening Serial Port: %s" % e)
def SInterval(self):
"""Command execution interval delay"""
time.sleep(0.5)
def open_serial(self, port, baudrate, bytesize, stopbits, parity):
"""Open serial connection"""
try:
if port is None:
port = self.port
else:
self.port = port
if baudrate is None:
baudrate = self.baudrate
else:
self.baudrate = baudrate
if bytesize is None:
bytesize = self.bytesize
else:
self.bytesize = bytesize
if stopbits is None:
stopbits = self.stopbits
else:
self.stopbits = stopbits
if parity is None:
parity = self.parity
else:
self.parity = parity
bytesize = serial.EIGHTBITS if bytesize == 8 else serial.SEVENBITS
parity = serial.PARITY_NONE if parity is None else parity
stopbits = serial.STOPBITS_ONE if stopbits == 1 else serial.STOPBITS_TWO
ser = serial.Serial(port=port, baudrate=baudrate, bytesize=bytesize, parity=parity,
stopbits=stopbits, timeout=1, xonxoff=False)
self.logger.info('%s has opened, baudrate is %s' % (port, baudrate))
return ser
except serial.SerialException as e:
self.logger.error('Can\'t open %s: %s' % (port, e))
raise
def send_command(self, command):
"""Send command to the serial port"""
try:
if self.ser is not None and self.ser.isOpen():
# Python 2: Directly send string (no need for encode)
self.ser.write(command)
self.logger.info('Send command:<%s>' % command)
time.sleep(0.5)
data = self.receive_data()
return data
else:
self.logger.error('Serial port is closed or does not exist')
except Exception as e:
self.logger.error('Error sending command <%s>: %s' % (command, e))
raise
def receive_data(self):
"""Get data from the serial port"""
try:
if self.ser is not None and self.ser.isOpen():
# Python 2: Directly read as string
data = []
while True:
line = self.ser.readline()
if not line:
break
data.append(line)
self.logger.info('Receive data:<%s>' % data)
return data
else:
self.logger.error('Serial port is closed or does not exist')
return None
except Exception as e:
self.logger.error('Error receiving data: %s' % e)
raise
def close_serial(self):
"""Close the serial connection"""
try:
if self.ser is not None and self.ser.isOpen():
self.ser.close()
self.logger.info('Serial port has been closed')
else:
self.logger.error('Serial port is already closed')
except Exception as e:
self.logger.error('Error closing serial port: %s' % e)
raise
def get_temperature(self):
"""
Get the chamber's temperature
:return: [monitored temperature, target temperature, highlimit temperature, lowlimit temperature]
"""
try:
# Send temperature query command
response = self.send_command(command=self.COMMAND_GET_TEMP)
# Validate response format
if not response or not isinstance(response, list):
raise ValueError("Invalid response format: %s" % response)
# Python 2: Directly use string (no need for decode)
data_str = response[0]
# Split string and convert to float
temp_values = data_str.strip().split(',')
# Validate split result count
if len(temp_values) != 4:
raise ValueError("Expected 4 temperature values, got %d" % len(temp_values))
# Convert each value to float
temperatures = [float(value.strip()) for value in temp_values]
return temperatures
except ValueError as ve:
self.logger.error("Temperature value error: %s" % ve)
raise
except Exception as e:
self.logger.error("Error getting temperature: %s" % e)
raise
def get_current_temperature(self, temperature_data):
"""
Get the first data (current temperature) from the temperature data list
Parameters:
temperature_data (list): List containing temperature data, format [monitored temperature, target temperature, high limit, low limit]
Returns:
float: Current temperature value
"""
try:
# Validate input type
if not isinstance(temperature_data, list):
raise TypeError("Parameter must be a list type")
# Validate list is not empty
if len(temperature_data) < 1:
raise IndexError("Temperature data list cannot be empty")
# Get the first element and assign to current_temp
current_temp = temperature_data[0]
# Validate data type is numeric
if not isinstance(current_temp, (int, float)):
raise TypeError("First temperature data must be a numeric type")
return current_temp
except Exception as e:
self.logger.error("Error getting current temperature: %s" % e)
raise
def get_humidity(self):
"""
Get the chamber's humidity
:return: [monitored humidity, target humidity, highlimit humidity, lowlimit humidity]
"""
try:
# Send humidity query command
response = self.send_command(command=self.COMMAND_GET_HUMI)
# Python 2: Directly use string
response_str = response[0] if response else ""
if response_str == self.NOT_READY_CODE:
self.logger.error("Unable to query humidity: Device does not support or humidity control is not enabled")
response = [0, 0, 0, 0]
return response
# Validate response format
if not response or not isinstance(response, list):
raise ValueError("Invalid response format: %s" % response)
# Get the first element (string) from the list
data_str = response[0]
# Split string and convert to float
humi_values = data_str.strip().split(',')
# Validate split result count
if len(humi_values) != 4:
raise ValueError("Expected 4 humidity values, got %d" % len(humi_values))
# Convert each value to float
humiditys = [float(value.strip()) for value in humi_values]
return humiditys
except ValueError as ve:
self.logger.error("Humidity value error: %s" % ve)
raise
except Exception as e:
self.logger.error("Error getting humidity: %s" % e)
raise
def get_current_humidity(self, humidity_data):
"""
Get the first data (current humidity) from the humidity data list
Parameters:
humidity_data (list): List containing humidity data, format [monitored humidity, target humidity, high limit, low limit]
Returns:
float: Current humidity value
"""
try:
# Validate input type
if not isinstance(humidity_data, list):
raise TypeError("Parameter must be a list type")
# Validate list is not empty
if len(humidity_data) < 1:
raise IndexError("Humidity data list cannot be empty")
# Get the first element and assign to current_humi
current_humi = humidity_data[0]
# Validate data type is numeric
if not isinstance(current_humi, (int, float)):
raise TypeError("First humidity data must be a numeric type")
return current_humi
except Exception as e:
self.logger.error("Error getting current humidity: %s" % e)
raise
def constant_mode_set(self, constant_target_temp, constant_target_humi):
"""
Set the target temperature and humidity in constant mode
Parameters:
constant_target_temp (float): Target temperature value to set
constant_target_humi (float): Target humidity value to set
Returns:
str: Complete instruction string sent to the aging chamber
Functionality:
Constructs "1, TEMP, SX.X" format instruction based on the input target temperature value
Constructs "1, HUMI, SX.X" format instruction based on the input target humidity value
"""
try:
# Temperature setting
# Validate input is numeric type
if not isinstance(constant_target_temp, (int, float)):
raise TypeError("Target temperature must be a numeric type")
# Format temperature value to a string with one decimal place
formatted_temp = "%.1f" % constant_target_temp
# Construct complete instruction string
constant_temp_command = "1, TEMP, S%s" % formatted_temp
# Humidity setting
# Validate input is numeric type
if not isinstance(constant_target_humi, (int, float)):
raise TypeError("Target humidity must be a numeric type")
# Format humidity value to a string with one decimal place
formatted_humi = "%.1f" % constant_target_humi
# Construct complete instruction string
constant_humi_command = "1, HUMI, S%s" % formatted_humi
# Send commands to the device
self.send_command(command=constant_temp_command)
time.sleep(1)
self.send_command(command=constant_humi_command)
time.sleep(1)
self.send_command(command=self.COMMAND_SET_MODE_CONSTANT)
time.sleep(1)
time.sleep(1)
# Check if settings are successful
temp_result_data = self.get_temperature()
time.sleep(1)
current_temp_set = temp_result_data[1]
self.logger.info("Current temp set: %s" % current_temp_set)
# Check if current set temperature matches expectations
if current_temp_set != constant_target_temp:
raise ValueError("Expected set temp to %s, current %s" % (
constant_target_temp, current_temp_set))
humi_result_data = self.get_humidity()
time.sleep(1)
current_humi_set = humi_result_data[1]
self.logger.info("Current humi set: %s" % current_humi_set)
# Check if current set humidity matches expectations
if current_humi_set != constant_target_humi:
raise ValueError("Expected set humi to %s, current %s" % (
constant_target_humi, current_humi_set))
except Exception as e:
self.logger.error("Setting failed: %s" % e)
raise
def WriteProgram(self, program_list):
"""Write the program to the device"""
# Send start editing command
try:
self.send_command("1, PRGM DATA WRITE, PGM1, EDIT START")
step = 1
for program in program_list:
commands = self.ConvertProgramToCommands(program, step)
for cmd in commands:
# Check command length limit
if len(cmd) > 50:
self.logger.info("Command too long, may be truncated: %s" % cmd)
if not self.send_command(cmd):
self.logger.info("Program write failed")
return False
step += 1
self.SInterval() # Step interval
# Send end editing command
self.send_command("1, PRGM DATA WRITE, PGM1, END, OFF")
self.send_command("1, PRGM DATA WRITE, PGM1, EDIT END")
self.logger.info("Program write successful")
return True
except Exception as e:
self.logger.error("Setting failed: %s" % e)
raise
def ConvertProgramToCommands(self, program, step):
"""
Convert program steps to commands
:param program: Program step
:param step: Step number
:return: Command list
"""
try:
commands = []
step_cmd = "STEP%d" % step
if program.temperature != 0:
temp_cmd = (
"1, PRGM DATA WRITE, PGM1, %s, " % step_cmd +
"TEMP%s, " % program.temperature +
"TIME%02d:%02d" % (program.hour, program.minute)
)
commands.append(temp_cmd)
if program.humidity != 0:
humi_cmd = (
"1, PRGM DATA WRITE, PGM1, %s, " % step_cmd +
"HUMI%s, " % program.humidity +
"TIME%02d:%02d" % (program.hour, program.minute)
)
commands.append(humi_cmd)
return commands
except Exception as e:
self.logger.error("Setting failed: %s" % e)
raise
def SetProgram(self, program_list):
"""Set the aging chamber program (with retry mechanism)"""
try:
# Three retry attempts
for tryout in range(1, 4):
if self.WriteProgram(program_list):
return True
self.logger.info("Attempt %d to write program failed, retrying in 1 second" % tryout)
time.sleep(1.0)
self.logger.error("Program write failed: Maximum retry attempts reached")
return False
except Exception as e:
self.logger.error("Setting failed: %s" % e)
raise
def RunProgram(self):
"""Execute the program (with retry mechanism)"""
try:
# Three retry attempts
for tryout in range(1, 4):
response = self.send_command(self.COMMAND_EXECUTE_PROGRAM)
# Check if response matches expectations
if response and any(self.OK_SIGN in r for r in response):
self.logger.info("Program started successfully")
return True
self.logger.info("Attempt %d to start program failed, response: %s" % (tryout, response))
time.sleep(1.0)
return False
except Exception as e:
self.logger.error("Setting failed: %s" % e)
raise
def StopDevice(self):
"""Stop the program (with retry mechanism)"""
try:
# Three retry attempts
for tryout in range(1, 4):
response = self.send_command(self.COMMAND_STOP_DEV)
if response and not any(self.NA_PREFIX in r for r in response):
self.logger.info("Device stopped")
self.close_serial()
return True
self.logger.info("Attempt %d to stop device failed" % tryout)
time.sleep(1.0)
self.logger.error("Device stop failed")
return False
except Exception as e:
self.logger.error("Setting failed: %s" % e)
raise
全部代码,保留每个函数的功能,但是整理