Files
Feeding_control_system/hardware/transmitter.py

285 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# hardware/transmitter.py
from pymodbus.exceptions import ModbusException
import socket
from config.ini_manager import ini_manager
from config.settings import app_set_config
import time
class TransmitterController:
def __init__(self, relay_controller):
self.relay_controller = relay_controller
self.test_upper_weight=5043
self.test_lower_weight=1256
self.is_start_upper=False
self.is_start_lower=False
self.start_time_upper=None
self.start_time_lower=None
self.upper_ip=ini_manager.upper_transmitter_ip
self.upper_port=ini_manager.upper_transmitter_port
self.lower_ip=ini_manager.lower_transmitter_ip
self.lower_port=ini_manager.lower_transmitter_port
# 变送器配置
self.config = {
1: { # 上料斗
'slave_id': 1,
'weight_register': 0x01,
'register_count': 2
},
2: { # 下料斗
'slave_id': 2,
'weight_register': 0x01,
'register_count': 2
}
}
# 备份 modbus 读取数据)
def read_data_bak(self, transmitter_id):
"""读取变送器数据"""
try:
if transmitter_id not in self.config:
print(f"无效变送器ID: {transmitter_id}")
return None
config = self.config[transmitter_id]
if not self.relay_controller.modbus_client.connect():
print("无法连接网络继电器Modbus服务")
return None
result = self.relay_controller.modbus_client.read_holding_registers(
address=config['weight_register'],
count=config['register_count'],
slave=config['slave_id']
)
if isinstance(result, Exception):
print(f"读取变送器 {transmitter_id} 失败: {result}")
return None
# 根据图片示例,正确解析数据
if config['register_count'] == 2:
# 获取原始字节数组
raw_data = result.registers
# 组合成32位整数
weight = (raw_data[0] << 16) + raw_data[1]
weight = weight / 1000.0 # 单位转换为千克
elif config['register_count'] == 1:
weight = float(result.registers[0])
else:
print(f"不支持的寄存器数量: {config['register_count']}")
return None
print(f"变送器 {transmitter_id} 读取重量: {weight}kg")
return weight
except ModbusException as e:
print(f"Modbus通信错误: {e}")
return None
except Exception as e:
print(f"数据解析错误: {e}")
return None
finally:
self.relay_controller.modbus_client.close()
# 直接读取 变送器返回的数据并解析
def read_data_sub(self, transmitter_id):
"""
Args: transmitter_id 为1 表示上料斗, 为2 表示下料斗
return: 读取成功返回重量 weight: int, 失败返回 None
"""
TIMEOUT = 2 # 超时时间为 2秒
BUFFER_SIZE= 1024
IP = None
PORT = None
weight = None
if transmitter_id == 1:
# 上料斗变送器的信息:
IP = self.upper_ip
PORT = self.upper_port
elif transmitter_id == 2:
# 下料斗变送器的信息:
IP = self.lower_ip
PORT = self.lower_port
if not IP or not PORT:
print(f"未配置变送器 {transmitter_id} 的IP或PORT")
return None
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try:
s.settimeout(TIMEOUT)
s.connect((IP, PORT))
# print(f"连接上料斗变送器 {IP}:{PORT} 成功")
# 接收数据变送器主动推送recv即可获取数据
data = s.recv(BUFFER_SIZE)
if data:
# print(f"收到原始数据:{data}")
# 提取出完整的一个数据包 (\r\n结尾)
packet = self.get_latest_valid_packet(data)
if not packet:
print("未获取到有效数据包!!")
return None
# 解析重量
weight = self.parse_weight(packet)
else:
print("未收到设备数据")
except ConnectionRefusedError:
print(f"变送器连接失败:{IP}:{PORT} 拒绝连接(设备离线/端口错误)")
except socket.timeout:
print(f"读取变送器数据超时:{TIMEOUT}秒内未收到数据")
except Exception as e:
print(f"读取异常:{e}")
# 成功返回重量int失败返回None
return weight
def read_data_sub_test(self, transmitter_id):
"""
测试用模拟读取变送器数据mock
Args: transmitter_id 为1 表示上料斗, 为2 表示下料斗
return: 读取成功返回重量 weight: int, 失败返回 None
"""
TIMEOUT = 2 # 超时时间为 2秒
BUFFER_SIZE= 1024
IP = None
PORT = None
weight = 0
if transmitter_id == 1:
# 上料斗变送器的信息:
IP = ini_manager.upper_transmitter_ip
PORT = ini_manager.upper_transmitter_port
if self.is_start_upper:
if self.start_time_upper is None:
self.start_time_upper=time.time()
weight=self.test_upper_weight-50*(time.time()-self.start_time_upper)
if weight<0:
weight=0
else:
weight=self.test_upper_weight
self.start_time_upper=None
return weight
elif transmitter_id == 2:
# 下料斗变送器的信息:
IP = ini_manager.lower_transmitter_ip
PORT = ini_manager.lower_transmitter_port
if self.is_start_lower:
if self.start_time_lower is None:
self.start_time_lower=time.time()
weight=self.test_lower_weight-50*(time.time()-self.start_time_lower)
else:
weight=self.test_lower_weight
self.start_time_lower=None
return weight
if not IP or not PORT:
print(f"未配置变送器 {transmitter_id} 的IP或PORT")
return 0
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try:
s.settimeout(TIMEOUT)
s.connect((IP, PORT))
# print(f"连接上料斗变送器 {IP}:{PORT} 成功")
# 接收数据变送器主动推送recv即可获取数据
data = s.recv(BUFFER_SIZE)
if data:
# print(f"收到原始数据:{data}")
# 提取出完整的一个数据包 (\r\n结尾)
packet = self.get_latest_valid_packet(data)
if not packet:
print("未获取到有效数据包!!")
return None
# 解析重量
weight = self.parse_weight(packet)
else:
print("未收到设备数据")
except ConnectionRefusedError:
print(f"变送器连接失败:{IP}:{PORT} 拒绝连接(设备离线/端口错误)")
except socket.timeout:
print(f"读取变送器数据超时:{TIMEOUT}秒内未收到数据")
except Exception as e:
print(f"读取异常:{e}")
# 成功返回重量int失败返回None
return weight
def get_latest_valid_packet(self, raw_data):
"""
解决TCP粘包:
从原始数据中,筛选所有有效包,返回最新的一个有效包
有效包标准: 1. 能UTF-8解码 2. 按逗号拆分≥3个字段 3. 第三个字段含数字(重量)
"""
DELIMITER = b'\r\n'
# 1. 按分隔符拆分,过滤空包
packets = [p for p in raw_data.split(DELIMITER) if p]
if not packets:
return None
valid_packets = []
for packet in packets:
try:
# 过滤无效ASCII字符只保留可见字符
valid_chars = [c for c in packet if 32 <= c <= 126]
filtered_packet = bytes(valid_chars)
# 2. 验证解码
data_str = filtered_packet.decode('utf-8').strip()
# 3. 验证字段数量
parts = data_str.split(',')
if len(parts) < 3:
continue
# 4. 验证重量字段含数字
weight_part = parts[2].strip()
if not any(char.isdigit() for char in weight_part):
continue
# 满足所有条件,加入有效包列表
valid_packets.append(packet)
except (UnicodeDecodeError, IndexError):
# 解码失败或字段异常,跳过该包
continue
# 返回最后一个有效包最新无有效包则返回None
return valid_packets[-1] if valid_packets else None
def parse_weight(self, packet_data):
"""解析重量函数:提取重量数值(如从 b'ST,NT,+0000175\r\n' 中提取 175)"""
try:
data_str = packet_data.decode('utf-8').strip()
parts = data_str.split(',')
# 确保有完整的数据包,三个字段
if len(parts) < 3:
print(f"parse_weight: 包格式错误(字段不足):{data_str}")
return None
weight_part = parts[2].strip()
return int(''.join(filter(str.isdigit, weight_part)))
except (IndexError, ValueError, UnicodeDecodeError) as e:
# print(f"数据解析失败:{e},原始数据包:{packet_data}")
return None
def read_data(self,transmitter_id):
"""获取重量函数根据变送器ID获取当前重量,三次"""
max_try_times=5
try_times=0
while try_times<max_try_times:
weight=self.read_data_sub(transmitter_id)
if weight is not None:
return weight
try_times+=1
print(f'-----获取重量异常-------------- transmitter_id: {transmitter_id}')
print(f'-----获取重量异常-------------- transmitter_id: {transmitter_id}')
return None