174 lines
6.8 KiB
Python
174 lines
6.8 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
'''
|
||
# @Time : 2025/11/13 11:05
|
||
# @Author : reenrr
|
||
# @File : tcp_client.py
|
||
'''
|
||
import json
|
||
from PySide6.QtCore import QTimer, Slot, Signal, QObject
|
||
from PySide6.QtNetwork import QTcpSocket, QAbstractSocket
|
||
from datetime import datetime
|
||
|
||
# -----------
|
||
# 参数配置
|
||
# -----------
|
||
tcp_server_host = "127.0.0.1"
|
||
tcp_server_port = 8888
|
||
|
||
RECONNECT_INTERVAL = 2000 # 重连间隔(毫秒)
|
||
|
||
class TcpClient(QObject):
|
||
# 信号,用于向主窗口/重量弹窗发送最新重量数据
|
||
weight_updated = Signal(dict) # 重量更新信号
|
||
connection_status_changed = Signal(bool) # 连接状态更新信号
|
||
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
|
||
self.is_running = False # 系统运行状态标记
|
||
self.latest_json_data = {} # 缓存服务端发送的最新JSON数据
|
||
|
||
self.statusWidgets = [] # 存储api_field + 对应的value标签
|
||
|
||
# ---------------
|
||
# TCP客户端核心配置
|
||
# ---------------
|
||
self.tcp_socket = QTcpSocket(self) # TCP socket实例
|
||
self.tcp_server_host = tcp_server_host
|
||
self.tcp_server_port = tcp_server_port
|
||
self.is_tcp_connected = False # TCP连接状态标记
|
||
self.has_connected_once = False # 连接至服务器至少一次标记(区别首次连接和断开后重连)
|
||
self.reconnect_count = 0 # 重连次数计数器
|
||
|
||
# 重连定时器,每隔RECONNECT_INTERVAL毫秒重连一次
|
||
self.reconnect_timer = QTimer(self)
|
||
self.reconnect_timer.setInterval(RECONNECT_INTERVAL)
|
||
self.reconnect_timer.timeout.connect(self._reconnect_to_server) # 绑定重连函数
|
||
|
||
# 绑定TCP信号与槽(事件驱动)
|
||
self._bind_tcp_signals()
|
||
|
||
# ---------------------
|
||
# TCP连接相关函数
|
||
# ---------------------
|
||
def _connect_to_server(self):
|
||
"""主动发起连接(仅在未连接状态下有效"""
|
||
if not self.is_tcp_connected:
|
||
self.tcp_socket.abort() # 终止现有连接
|
||
self.tcp_socket.connectToHost(self.tcp_server_host, self.tcp_server_port)
|
||
print(f"发起连接请求:{self.tcp_server_host}:{self.tcp_server_port}")
|
||
|
||
def _reconnect_to_server(self):
|
||
"""重连执行函数:仅在未连接且未达最大次数时触发"""
|
||
if not self.is_tcp_connected:
|
||
self.reconnect_count += 1
|
||
# 日志优化:每10次重连提示一次,避免日志刷屏
|
||
if self.reconnect_count % 10 == 0:
|
||
print(f"已连续重连{self.reconnect_count}次,仍未连接服务端,请检查服务端状态...")
|
||
else:
|
||
print(f"第{self.reconnect_count}次重连请求:{self.tcp_server_host}:{self.tcp_server_port}")
|
||
self._connect_to_server()
|
||
|
||
def _bind_tcp_signals(self):
|
||
"""绑定TCP socket的核心信号(连接、断开、接收数据、错误)"""
|
||
# 连接成功信号
|
||
self.tcp_socket.connected.connect(self._on_tcp_connected)
|
||
# 断开连接信号
|
||
self.tcp_socket.disconnected.connect(self._on_tcp_disconnected)
|
||
# 接收数据信号(有新数据时触发)
|
||
self.tcp_socket.readyRead.connect(self._on_tcp_data_received)
|
||
# 错误信号(连接/通信出错时触发)
|
||
self.tcp_socket.errorOccurred.connect(self._on_tcp_error)
|
||
|
||
# ------------------
|
||
# TCP客户端核心功能
|
||
# ------------------
|
||
@Slot()
|
||
def _on_tcp_connected(self):
|
||
"""TCP连接成功回调"""
|
||
self.is_tcp_connected = True
|
||
self.has_connected_once = True
|
||
self.reconnect_timer.stop() # 停止重连定时器
|
||
self.reconnect_count = 0 # 重连计数器清零
|
||
self.is_running = True
|
||
print(f"TCP连接成功:{self.tcp_server_host}:{self.tcp_server_port}")
|
||
|
||
# 发送连接状态信号
|
||
self.connection_status_changed.emit(True)
|
||
# 连接成功后,向服务器发送“请求初始数据”指令
|
||
self._send_tcp_request("get_initial_data")
|
||
|
||
@Slot()
|
||
def _on_tcp_disconnected(self):
|
||
"""TCP连接断开回调"""
|
||
self.is_tcp_connected = False
|
||
self.is_running = False
|
||
print(f"TCP连接断开:{self.tcp_server_host}:{self.tcp_server_port}")
|
||
|
||
# 发送连接状态信号
|
||
self.connection_status_changed.emit(False)
|
||
|
||
# 发送重量清零信号
|
||
self.weight_updated.emit({"hopper_up_weight": 0, "hopper_down_weight": 0})
|
||
|
||
if not self.reconnect_timer.isActive(): # 未启动重连定时器时才启动
|
||
print(f"连接断开,将在{RECONNECT_INTERVAL / 1000}秒后启动重连...")
|
||
self.reconnect_timer.start()
|
||
|
||
@Slot()
|
||
def _on_tcp_data_received(self):
|
||
"""TCP数据接收回调(服务器发送数据时触发)"""
|
||
tcp_data = self.tcp_socket.readAll().data().decode("utf-8").strip()
|
||
print(f"TCP数据接收:{tcp_data}")
|
||
|
||
# 解析数据
|
||
try:
|
||
status_data = json.loads(tcp_data)
|
||
self.latest_json_data = status_data
|
||
self.weight_updated.emit(status_data)
|
||
except json.JSONDecodeError as e:
|
||
print(f"TCP数据解析失败(非JSON格式):{e}, 原始数据:{tcp_data}")
|
||
except Exception as e:
|
||
print(f"TCP数据处理异常:{e}")
|
||
|
||
@Slot(QAbstractSocket.SocketError)
|
||
def _on_tcp_error(self, error):
|
||
"""TCP错误回调"""
|
||
if not self.is_tcp_connected:
|
||
error_str = self.tcp_socket.errorString()
|
||
print(f"TCP错误:{error_str}")
|
||
self.is_tcp_connected = False
|
||
self.is_running = False
|
||
|
||
# 发送连接断开信号
|
||
self.connection_status_changed.emit(False)
|
||
|
||
# 首次连接失败时,启动重连定时器
|
||
if not self.reconnect_timer.isActive():
|
||
print(f"将在{RECONNECT_INTERVAL / 1000}秒后启动重连")
|
||
self.reconnect_timer.start()
|
||
|
||
def _send_tcp_request(self, request_cmd="get_status"):
|
||
"""向TCP服务器发送请求指令"""
|
||
if not self.is_tcp_connected:
|
||
print("TCP连接未建立,无法发送请求")
|
||
return
|
||
|
||
# 构造请求数据
|
||
request_data = json.dumps({
|
||
"cmd": request_cmd,
|
||
"timestamp": self.get_current_time(),
|
||
"client_info": "布料系统客户端"
|
||
}) + "\n" # 增加换行符作为数据结束标识
|
||
|
||
# 发送请求数据
|
||
self.tcp_socket.write(request_data.encode("utf-8"))
|
||
print(f"TCP请求发送:{request_data.strip()}")
|
||
|
||
# ------------------
|
||
# 时间相关的通用方法
|
||
# ------------------
|
||
def get_current_time(self):
|
||
"""获取格式化的当前时间"""
|
||
return datetime.now().strftime('%Y-%m-%d %H:%M:%S') |