添加两个版本+添加一个重连机制的版本,待测试

This commit is contained in:
2025-10-31 16:56:02 +08:00
parent b7fc062f3f
commit 3e0c62ac01
8 changed files with 674 additions and 274 deletions

155
test.py
View File

@ -3,6 +3,7 @@
'''
# @Time : 2025/9/10 11:29
# @Author : reenrr
# @Description : 在main函数的基础上添加了重连机制为连接成功
# @File : test.py
'''
import sys
@ -26,13 +27,14 @@ tcp_server_port = 8888
# 数据保存目录
SAVE_DIR = "operation_records"
MAX_RECONNECT = 3 # 最大重连次数
RECONNECT_INTERVAL = 2000 # 重连间隔(毫秒)
class StatusMonitor(QWidget):
"""
中交三航精准布料浇筑要料系统 - 主界面类(深色主题)
使用TCP进行数据传输客户端模型与TCP服务器通信
"""
def __init__(self, parent=None):
"""构造函数初始化主界面的UI布局、控件和定时器"""
super().__init__(parent=parent)
@ -48,6 +50,12 @@ class StatusMonitor(QWidget):
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()
@ -72,7 +80,23 @@ class StatusMonitor(QWidget):
# 客户端自动后自动连接服务端
# ----------------
print(f"客户端启动,自动连接服务端{self.tcp_server_host}:{self.tcp_server_port}...")
self.tcp_socket.connectToHost(self.tcp_server_host, self.tcp_server_port)
self._connect_to_server()
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)
def _reconnect_to_server(self):
"""重连执行函数:仅在未连接且未达最大次数时触发"""
if not self.is_tcp_connected and self.reconnect_count < MAX_RECONNECT:
self.reconnect_count += 1
print(f"{self.reconnect_count}次重连(共{MAX_RECONNECT}次尝试)...")
self._connect_to_server()
elif self.reconnect_count >= MAX_RECONNECT:
self.reconnect_timer.stop() # 停止重连定时器
print(f"已达最大重连次数({MAX_RECONNECT}次),停止重连,请检查服务端状态")
def _init_save_dir(self):
"""初始化数据保存目录"""
@ -93,6 +117,9 @@ class StatusMonitor(QWidget):
# 错误信号(连接/通信出错时触发)
self.tcp_socket.errorOccurred.connect(self._on_tcp_error)
# ------------------
# 界面初始化函数
# ------------------
def _init_title_label(self):
"""初始化系统标题标签"""
titleLabel = QLabel("中交三航精准布料浇筑要料系统")
@ -131,7 +158,7 @@ class StatusMonitor(QWidget):
self.mainLayout.addWidget(self.bigContainer, 1)
def _init_status_groups(self, parent_layout):
"""初始化左右信息(各包含5个状态项)"""
"""初始化左右信息(各包含3个状态项)"""
statusWidget = QWidget()
statusLayout = QHBoxLayout(statusWidget)
statusLayout.setContentsMargins(0, 0, 0, 0)
@ -149,19 +176,15 @@ class StatusMonitor(QWidget):
# 左边5个状态项及对应初始值
leftStatusInfo = [
{"name": "任务单号", "value": "20250706-01", "api_field": "task_id"},
{"name": "工程名称", "value": "18号线二期工程", "api_field": "project_name"},
{"name": "区间段", "value": "停车场工作并上行", "api_field": "section"},
{"name": "坍落度", "value": "50~70 mm", "api_field": "slump"},
{"name": "配合比编号", "value": "P2022=001", "api_field": "mix_ratio_id"}
{"name": "任务单号", "value": "", "api_field": "task_id"},
{"name": "工程名称", "value": "", "api_field": "project_name"},
{"name": "配比号", "value": "", "api_field": "produce_mix_id"}
]
# 右边5个状态项及对应初始值
rightStatusInfo = [
{"name": "要料状态", "value": "请求中", "api_field": "request_status"},
{"name": "要料标号", "value": "C50P12", "api_field": "material_grade"},
{"name": "要料方量", "value": "2m³", "api_field": "volume"},
{"name": "要料时间", "value": "2分钟后", "api_field": "request_time"},
{"name": "小车状态", "value": "移动后", "api_field": "car_status"}
{"name": "要料状态", "value": "", "api_field": "flag"},
{"name": "砼强度", "value": "", "api_field": "beton_grade"},
{"name": "要料方量", "value": "", "api_field": "adjusted_volume"},
]
self.statusWidgets = []
@ -256,25 +279,17 @@ class StatusMonitor(QWidget):
buttonLayout.setSpacing(30)
# 按钮图标(需替换为实际图标路径)
start_icon_path = "img.png"
down_icon_path = "img.png"
error_icon_path = "img.png"
cancel_icon_path = "img.png"
self.startFeedButton = QPushButton("开始下料")
self.finishButton = QPushButton("下料完成")
self.errorButton = QPushButton("生产异常")
self.cancelButton = QPushButton("生产取消")
# 设置按钮图标
self.startFeedButton.setIcon(QIcon(start_icon_path))
self.finishButton.setIcon(QIcon(down_icon_path))
self.errorButton.setIcon(QIcon(error_icon_path))
self.cancelButton.setIcon(QIcon(cancel_icon_path))
# 设置图标大小
self.startFeedButton.setIconSize(QSize(20, 20))
self.finishButton.setIconSize(QSize(20, 20))
self.errorButton.setIconSize(QSize(20, 20))
self.cancelButton.setIconSize(QSize(20, 20))
@ -294,23 +309,6 @@ class StatusMonitor(QWidget):
opacity: 0.8;
}
"""
self.startFeedButton.setStyleSheet(button_base_style + """
QPushButton {
background-color: #2196F3;
color: white;
border-color: #1976D2;
}
""")
self.finishButton.setStyleSheet(button_base_style + """
QPushButton {
background-color: #00796B;
color: white;
border-color: #004D40;
}
""")
self.errorButton.setStyleSheet(button_base_style + """
QPushButton {
background-color: #E65100;
@ -328,25 +326,17 @@ class StatusMonitor(QWidget):
""")
button_font = QFont("Microsoft YaHei", 12, QFont.Bold)
self.startFeedButton.setFont(button_font)
self.finishButton.setFont(button_font)
self.errorButton.setFont(button_font)
self.cancelButton.setFont(button_font)
self.startFeedButton.clicked.connect(self.on_start_clicked)
self.finishButton.clicked.connect(self.on_finish_clicked)
self.errorButton.clicked.connect(self.on_error_clicked)
self.cancelButton.clicked.connect(self.on_cancel_clicked)
# 初始禁用“停止”和“异常”按钮(未连接时不可用)
self.startFeedButton.setDisabled(False)
self.finishButton.setDisabled(True)
self.errorButton.setDisabled(True)
self.cancelButton.setDisabled(True)
buttonLayout.addStretch(1)
buttonLayout.addWidget(self.startFeedButton)
buttonLayout.addWidget(self.finishButton)
buttonLayout.addWidget(self.errorButton)
buttonLayout.addWidget(self.cancelButton)
buttonLayout.addStretch(1)
@ -397,6 +387,9 @@ class StatusMonitor(QWidget):
# 绘制文本
painter.drawText(x, y + text_rect.height(), text)
# ------------------
# 数据保存
# ------------------
def _save_data_to_file(self, button_name):
"""
将服务端数据+按钮操作信息保存到JSON文件
@ -427,6 +420,23 @@ class StatusMonitor(QWidget):
except Exception as e:
print(f"💾 保存「{button_name}」操作记录失败:{str(e)}")
# --------------------
# 清空界面信息的通用方法
# --------------------
def _clear_ui_info(self):
"""清空所有状态项的显示内容,并并设置指示灯颜色"""
for widget in self.statusWidgets:
widget['valueLabel'].setText("")
# 指示灯设为初始灰色与_create_status_item中初始样式一致
widget['indicator'].setStyleSheet("""
QLabel {
background-color: #9E9E9E;
border-radius: 10px;
border: 2px solid #555555;
}
""")
print(" 界面信息已清空")
# ------------------
# TCP客户端核心功能
# ------------------
@ -434,14 +444,16 @@ class StatusMonitor(QWidget):
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._send_tcp_request("get_initial_data")
# 更新按钮状态:启用“下料完成”“生产异常”“生产取消”
self.finishButton.setDisabled(False)
# 更新按钮状态:启用“生产异常”“生产取消”
self.errorButton.setDisabled(False)
self.cancelButton.setDisabled(False)
@ -453,8 +465,6 @@ class StatusMonitor(QWidget):
print(f"TCP连接断开{self.tcp_server_host}:{self.tcp_server_port}")
# 启用/禁用按钮
self.startFeedButton.setDisabled(False)
self.finishButton.setDisabled(True)
self.errorButton.setDisabled(True)
self.cancelButton.setDisabled(True)
@ -487,16 +497,20 @@ class StatusMonitor(QWidget):
@Slot(QAbstractSocket.SocketError)
def _on_tcp_error(self, error):
"""TCP错误回调"""
error_str = self.tcp_socket.errorString()
print(f"TCP错误{error_str}")
self.is_tcp_connected = False
self.is_running = False
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.startFeedButton.setDisabled(False)
self.finishButton.setDisabled(True)
self.errorButton.setDisabled(True)
self.cancelButton.setDisabled(True)
# 启用/禁用按钮
self.errorButton.setDisabled(True)
self.cancelButton.setDisabled(True)
# 首次连接失败时,启动重连定时器
if not self.has_connected_once and self.reconnect_count == 0:
print(f"将在{RECONNECT_INTERVAL / 1000}秒后启动重连(最多{MAX_RECONNECT}次)")
self.reconnect_timer.start()
def _send_tcp_request(self, request_cmd="get_status"):
"""向TCP服务器发送请求指令"""
@ -551,37 +565,20 @@ class StatusMonitor(QWidget):
# ------------------
# 按钮点击事件
# ------------------
def on_start_clicked(self):
"""点击“开始下料”:向服务端发送开始指令"""
print("🔘 点击「开始下料」按钮")
if not self.is_tcp_connected:
print("TCP连接未建立尝试重新连接")
self.tcp_socket.connectToHost(self.tcp_server_host, self.tcp_server_port)
QTimer.singleShot(1000, lambda: self._send_tcp_request("start_feed")) # 等待连接成功后再发送请求
else:
print("TCP连接已建立")
self._send_tcp_request("start_feed")
def on_finish_clicked(self):
"""点击“下料完成”:向服务端发送完成指令"""
print("🔘 点击「下料完成」按钮")
self._send_tcp_request("finish_feed")
self._save_data_to_file("finish_feed")
def on_error_clicked(self):
"""点击“生产异常”:向服务端发送异常指令"""
print("🔘 点击「生产异常」按钮")
self._clear_ui_info()
self._send_tcp_request("production_error")
self._save_data_to_file("production_error")
def on_cancel_clicked(self):
"""点击“生产取消”:向服务端发送取消指令"""
print("🔘 点击「生产取消」按钮")
self._clear_ui_info()
self._send_tcp_request("cancel_feed")
self._save_data_to_file("cancel_feed")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = StatusMonitor()
window.show()
sys.exit(app.exec())
sys.exit(app.exec())