添加TCP通讯,添加开始上料按钮,修改按钮逻辑
This commit is contained in:
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
18
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
18
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,18 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<value>
|
||||
<list size="5">
|
||||
<item index="0" class="java.lang.String" itemvalue="scipy" />
|
||||
<item index="1" class="java.lang.String" itemvalue="numpy" />
|
||||
<item index="2" class="java.lang.String" itemvalue="snap7" />
|
||||
<item index="3" class="java.lang.String" itemvalue="jsonchema" />
|
||||
<item index="4" class="java.lang.String" itemvalue="werkzeung" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="pyside6" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="pyside6" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/zjsh_ui_sysytem.iml" filepath="$PROJECT_DIR$/.idea/zjsh_ui_sysytem.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/zjsh_ui_sysytem.iml
generated
Normal file
8
.idea/zjsh_ui_sysytem.iml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="pyside6" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
12
config/config.json
Normal file
12
config/config.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"task_id": "20250706-01",
|
||||
"project_name": "18号线二期工程",
|
||||
"section": "停车场工作井上行",
|
||||
"slump": "50~70 mm",
|
||||
"mix_ratio_id": "P2022=001",
|
||||
"request_status": "请求中",
|
||||
"material_grade": "C50P12",
|
||||
"volume": "2m³",
|
||||
"request_time": "10分钟后",
|
||||
"car_status": "移动后"
|
||||
}
|
||||
191
config/tcp_server.py
Normal file
191
config/tcp_server.py
Normal file
@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
# @Time : 2025/9/19 09:48
|
||||
# @Author : reenrr
|
||||
# @File : mock_server.py
|
||||
'''
|
||||
import socket
|
||||
import json
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
class TCPServerSimulator:
|
||||
def __init__(self, host='127.0.0.1', port=8888, config_file='config.json'):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.server_socket = None
|
||||
self.is_running = False
|
||||
self.client_sockets = []
|
||||
self.config_file = config_file
|
||||
|
||||
# 初始状态为None
|
||||
self.data_template = None
|
||||
|
||||
# 从配置文件中加载固定数据
|
||||
self.load_config_data()
|
||||
|
||||
# 模拟数据模板
|
||||
if self.data_template is None:
|
||||
self.data_template = {
|
||||
"task_id": "None",
|
||||
"project_name": "None",
|
||||
"section": "None",
|
||||
"slump": "None",
|
||||
"mix_ratio_id": "None",
|
||||
"request_status": "None",
|
||||
"material_grade": "None",
|
||||
"volume": "None",
|
||||
"request_time": "None",
|
||||
"car_status": "None"
|
||||
}
|
||||
|
||||
def load_config_data(self):
|
||||
"""从配置文件中加载固定数据"""
|
||||
try:
|
||||
if os.path.exists(self.config_file):
|
||||
with open(self.config_file, 'r', encoding='utf-8') as f:
|
||||
self.data_template = json.load(f)
|
||||
print(f"成功从 {self.config_file} 加载配置数据")
|
||||
else:
|
||||
print(f"配置文件 {self.config_file} 不存在,将使用默认数据")
|
||||
# 创建默认配置文件
|
||||
self.create_default_config()
|
||||
except Exception as e:
|
||||
print(f"加载配置文件时发生错误:{e},将使用默认数据")
|
||||
self.data_template = None
|
||||
|
||||
def create_default_config(self):
|
||||
"""创建默认配置文件"""
|
||||
default_data = {
|
||||
"task_id": "None",
|
||||
"project_name": "None",
|
||||
"section": "None",
|
||||
"slump": "None",
|
||||
"mix_ratio_id": "None",
|
||||
"request_status": "None",
|
||||
"material_grade": "None",
|
||||
"volume": "None",
|
||||
"request_time": "None",
|
||||
"car_status": "None"
|
||||
}
|
||||
|
||||
try:
|
||||
with open(self.config_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(default_data, f, ensure_ascii=False, indent=4)
|
||||
print(f"已创建默认配置文件 {self.config_file}")
|
||||
self.data_template = default_data
|
||||
except Exception as e:
|
||||
print(f"创建默认配置文件时发生错误: {e}")
|
||||
self.data_template = None
|
||||
|
||||
def start(self):
|
||||
"""启动服务器"""
|
||||
self.is_running = True
|
||||
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.server_socket.bind((self.host, self.port))
|
||||
self.server_socket.listen(5)
|
||||
print(f"服务器已启动,监听 {self.host}:{self.port}...")
|
||||
|
||||
# 启动接受连接的线程
|
||||
accept_thread = threading.Thread(target=self.accept_connections, daemon=True)
|
||||
accept_thread.start()
|
||||
|
||||
try:
|
||||
while self.is_running:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n服务器正在关闭...")
|
||||
self.stop()
|
||||
|
||||
def accept_connections(self):
|
||||
"""接受客户端连接"""
|
||||
while self.is_running:
|
||||
try:
|
||||
client_socket, client_address = self.server_socket.accept()
|
||||
self.client_sockets.append(client_socket)
|
||||
print(f"客户端 {client_address} 已连接")
|
||||
|
||||
# 发送数据
|
||||
data = self.generate_simulated_data()
|
||||
self.send_data(client_socket, data)
|
||||
print(f"已向客户端 {client_address} 发送数据:{data}")
|
||||
|
||||
# 启动一个线程监听客户端发送的指令
|
||||
threading.Thread(
|
||||
target=self.listen_client_commands,
|
||||
args=(client_socket,client_address),
|
||||
daemon=True
|
||||
).start()
|
||||
|
||||
except Exception as e:
|
||||
if self.is_running:
|
||||
print(f"接受连接时发生错误: {e}")
|
||||
break
|
||||
|
||||
def listen_client_commands(self, client_socket, client_address):
|
||||
"""监听客户端发送的指令"""
|
||||
while self.is_running and client_socket in self.client_sockets:
|
||||
try:
|
||||
# 接收客户端发送的指令
|
||||
data = client_socket.recv(1024).decode('utf-8').strip()
|
||||
if data:
|
||||
print(f"客户端 {client_address} 发送指令: {data}")
|
||||
else:
|
||||
print(f"客户端 {client_address} 已断开连接")
|
||||
self.client_sockets.remove(client_socket)
|
||||
client_socket.close()
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"监听客户端 {client_address} 指令时发生错误: {e}")
|
||||
self.client_sockets.remove(client_socket)
|
||||
client_socket.close()
|
||||
break
|
||||
|
||||
def generate_simulated_data(self):
|
||||
"""生成模拟的状态数据"""
|
||||
if self.data_template is None:
|
||||
return None
|
||||
|
||||
data = self.data_template.copy()
|
||||
data["timestamp"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
return data
|
||||
|
||||
def send_data(self, client_socket, data):
|
||||
"""向客户端发送数据"""
|
||||
try:
|
||||
# 转换为JSON字符串并添加换行符作为结束标记
|
||||
if data is None:
|
||||
data_str = json.dumps(None) + "\n"
|
||||
else:
|
||||
data_str = json.dumps(data) + "\n"
|
||||
client_socket.sendall(data_str.encode('utf-8'))
|
||||
except Exception as e:
|
||||
print(f"向客户端 {client_socket.getpeername()} 发送数据时发生错误: {e}")
|
||||
|
||||
def stop(self):
|
||||
"""停止服务器"""
|
||||
self.is_running = False
|
||||
|
||||
# 关闭所有客户端连接
|
||||
for sock in self.client_sockets:
|
||||
try:
|
||||
sock.close()
|
||||
except Exception as e:
|
||||
print(f"关闭客户端连接时发生错误: {e}")
|
||||
|
||||
# 关闭服务器套接字
|
||||
if self.server_socket:
|
||||
try:
|
||||
self.server_socket.close()
|
||||
except Exception as e:
|
||||
print(f"关闭服务器套接字时发生错误: {e}")
|
||||
|
||||
print("服务器已关闭")
|
||||
|
||||
if __name__ == '__main__':
|
||||
server = TCPServerSimulator(host='127.0.0.1', port=8888)
|
||||
server.start()
|
||||
589
main.py
589
main.py
@ -10,14 +10,27 @@ from PySide6.QtWidgets import (
|
||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QLabel, QFrame, QSizePolicy, QPushButton
|
||||
)
|
||||
from PySide6.QtCore import Qt, QTimer, QPoint, QSize
|
||||
from PySide6.QtCore import Qt, QTimer, QPoint, QSize, Slot
|
||||
from PySide6.QtGui import QFont, QPainter, QColor, QPen, QIcon
|
||||
from datetime import datetime
|
||||
from PySide6.QtNetwork import QTcpSocket, QAbstractSocket
|
||||
import json
|
||||
import os
|
||||
|
||||
|
||||
# -----------
|
||||
# 参数配置
|
||||
# -----------
|
||||
tcp_server_host = "127.0.0.1"
|
||||
tcp_server_port = 8888
|
||||
|
||||
# 数据保存目录
|
||||
SAVE_DIR = "operation_records"
|
||||
|
||||
class StatusMonitor(QWidget):
|
||||
"""
|
||||
中交三航精准布料浇筑要料系统 - 主界面类(深色主题)
|
||||
使用TCP进行数据传输(客户端模型,与TCP服务器通信)
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
@ -25,6 +38,19 @@ class StatusMonitor(QWidget):
|
||||
super().__init__(parent=parent)
|
||||
self.is_running = False # 系统运行状态标记
|
||||
self.current_datetime = self.get_current_time() # 当前日期时间
|
||||
# 缓存服务端发送的最新JSON数据
|
||||
self.latest_json_data = {}
|
||||
|
||||
# ---------------
|
||||
# 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连接状态标记
|
||||
|
||||
# 绑定TCP信号与槽(事件驱动)
|
||||
self._bind_tcp_signals()
|
||||
|
||||
# 窗口基础设置
|
||||
self.setWindowTitle("中交三航精准布料浇筑要料系统")
|
||||
@ -40,6 +66,32 @@ class StatusMonitor(QWidget):
|
||||
self._init_title_label()
|
||||
self._init_status_container()
|
||||
self._init_timers()
|
||||
self._init_save_dir()
|
||||
|
||||
# ----------------
|
||||
# 客户端自动后自动连接服务端
|
||||
# ----------------
|
||||
print(f"客户端启动,自动连接服务端{self.tcp_server_host}:{self.tcp_server_port}...")
|
||||
self.tcp_socket.connectToHost(self.tcp_server_host, self.tcp_server_port)
|
||||
|
||||
def _init_save_dir(self):
|
||||
"""初始化数据保存目录"""
|
||||
if not os.path.exists(SAVE_DIR):
|
||||
os.makedirs(SAVE_DIR)
|
||||
print(f"已创建数据保存目录:{os.path.abspath(SAVE_DIR)}")
|
||||
else:
|
||||
print(f"数据保存目录已存在:{os.path.abspath(SAVE_DIR)}")
|
||||
|
||||
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)
|
||||
|
||||
def _init_title_label(self):
|
||||
"""初始化系统标题标签"""
|
||||
@ -97,152 +149,30 @@ class StatusMonitor(QWidget):
|
||||
|
||||
# 左边5个状态项及对应初始值
|
||||
leftStatusInfo = [
|
||||
{"name": "任务单号", "value": "20250706-01"},
|
||||
{"name": "工程名称", "value": "18号线二期工程"},
|
||||
{"name": "区间段", "value": "停车场工作并上行"},
|
||||
{"name": "坍落度", "value": "50~70 mm"},
|
||||
{"name": "配合比编号", "value": "P2022=001"}
|
||||
{"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"}
|
||||
]
|
||||
# 右边5个状态项及对应初始值
|
||||
rightStatusInfo = [
|
||||
{"name": "要料状态", "value": "请求中"},
|
||||
{"name": "要料标号", "value": "C50P12"},
|
||||
{"name": "要料方量", "value": "2m³"},
|
||||
{"name": "要料时间", "value": "2分钟后"},
|
||||
{"name": "小车状态", "value": "移动后"}
|
||||
{"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"}
|
||||
]
|
||||
self.statusWidgets = []
|
||||
|
||||
# 处理左边状态项
|
||||
for info in leftStatusInfo:
|
||||
statusItem = QFrame()
|
||||
statusItem.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #2D2D2D;
|
||||
border: 1px solid #444444;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
}
|
||||
""")
|
||||
statusItem.setFixedHeight(80)
|
||||
statusItem.setFixedWidth(320) # 统一加长状态项宽度(原无固定宽度)
|
||||
|
||||
itemLayout = QHBoxLayout(statusItem)
|
||||
itemLayout.setContentsMargins(10, 5, 10, 5)
|
||||
|
||||
# 状态指示灯
|
||||
indicator = QLabel()
|
||||
indicator.setFixedSize(20, 20)
|
||||
indicator.setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #9E9E9E;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #555555;
|
||||
}
|
||||
""")
|
||||
|
||||
# 状态名称标签
|
||||
nameLabel = QLabel(info["name"])
|
||||
nameLabel.setFixedWidth(100) # 加宽名称标签(原90)
|
||||
nameLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
nameLabel.setStyleSheet("font-size: 14px; color: #FFFFFF;")
|
||||
nameLabel.setFont(QFont("Microsoft YaHei", 12))
|
||||
|
||||
# 状态值标签
|
||||
valueLabel = QLabel(info["value"])
|
||||
valueLabel.setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
background-color: #2D2D2D;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
}
|
||||
""")
|
||||
valueLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
valueLabel.setMinimumWidth(150) # 加宽值标签(原80)
|
||||
|
||||
itemLayout.addWidget(indicator)
|
||||
itemLayout.addSpacing(10)
|
||||
itemLayout.addWidget(nameLabel)
|
||||
itemLayout.addStretch()
|
||||
itemLayout.addWidget(valueLabel)
|
||||
|
||||
self.statusWidgets.append({
|
||||
'indicator': indicator,
|
||||
'nameLabel': nameLabel,
|
||||
'valueLabel': valueLabel,
|
||||
'status': False,
|
||||
'initial_value': info["value"]
|
||||
})
|
||||
|
||||
statusItem = self._create_status_item(info)
|
||||
leftLayout.addWidget(statusItem)
|
||||
|
||||
# 处理右边状态项
|
||||
for info in rightStatusInfo:
|
||||
statusItem = QFrame()
|
||||
statusItem.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #2D2D2D;
|
||||
border: 1px solid #444444;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
}
|
||||
""")
|
||||
statusItem.setFixedHeight(80)
|
||||
statusItem.setFixedWidth(320) # 统一加长状态项宽度(与左侧一致)
|
||||
|
||||
itemLayout = QHBoxLayout(statusItem)
|
||||
itemLayout.setContentsMargins(10, 5, 10, 5)
|
||||
|
||||
# 状态指示灯
|
||||
indicator = QLabel()
|
||||
indicator.setFixedSize(20, 20)
|
||||
indicator.setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #9E9E9E;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #555555;
|
||||
}
|
||||
""")
|
||||
|
||||
# 状态名称标签
|
||||
nameLabel = QLabel(info["name"])
|
||||
nameLabel.setFixedWidth(100) # 加宽名称标签(与左侧一致)
|
||||
nameLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
nameLabel.setStyleSheet("font-size: 14px; color: #FFFFFF;")
|
||||
nameLabel.setFont(QFont("Microsoft YaHei", 12))
|
||||
|
||||
# 状态值标签
|
||||
valueLabel = QLabel(info["value"])
|
||||
valueLabel.setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
background-color: #2D2D2D;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
}
|
||||
""")
|
||||
valueLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
valueLabel.setMinimumWidth(150) # 加宽值标签(与左侧一致)
|
||||
|
||||
itemLayout.addWidget(indicator)
|
||||
itemLayout.addSpacing(10)
|
||||
itemLayout.addWidget(nameLabel)
|
||||
itemLayout.addStretch()
|
||||
itemLayout.addWidget(valueLabel)
|
||||
|
||||
self.statusWidgets.append({
|
||||
'indicator': indicator,
|
||||
'nameLabel': nameLabel,
|
||||
'valueLabel': valueLabel,
|
||||
'status': False,
|
||||
'initial_value': info["value"]
|
||||
})
|
||||
|
||||
statusItem = self._create_status_item(info)
|
||||
rightLayout.addWidget(statusItem)
|
||||
|
||||
statusLayout.addWidget(leftGroup)
|
||||
@ -251,6 +181,73 @@ class StatusMonitor(QWidget):
|
||||
|
||||
parent_layout.addWidget(statusWidget)
|
||||
|
||||
def _create_status_item(self, info):
|
||||
"""创建单个状态项"""
|
||||
statusItem = QFrame()
|
||||
statusItem.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #2D2D2D;
|
||||
border: 1px solid #444444;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
}
|
||||
""")
|
||||
statusItem.setFixedHeight(80)
|
||||
statusItem.setFixedWidth(320) # 统一加长状态项宽度(原无固定宽度)
|
||||
|
||||
itemLayout = QHBoxLayout(statusItem)
|
||||
itemLayout.setContentsMargins(10, 5, 10, 5)
|
||||
|
||||
# 状态指示灯
|
||||
indicator = QLabel()
|
||||
indicator.setFixedSize(20, 20)
|
||||
indicator.setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #9E9E9E;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #555555;
|
||||
}
|
||||
""")
|
||||
|
||||
# 状态名称标签
|
||||
nameLabel = QLabel(info["name"])
|
||||
nameLabel.setFixedWidth(100) # 加宽名称标签(原90)
|
||||
nameLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
nameLabel.setStyleSheet("font-size: 14px; color: #FFFFFF;")
|
||||
nameLabel.setFont(QFont("Microsoft YaHei", 12))
|
||||
|
||||
# 状态值标签
|
||||
valueLabel = QLabel(info["value"])
|
||||
valueLabel.setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
background-color: #2D2D2D;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
}
|
||||
""")
|
||||
valueLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
valueLabel.setMinimumWidth(150) # 加宽值标签(原80)
|
||||
|
||||
itemLayout.addWidget(indicator)
|
||||
itemLayout.addSpacing(10)
|
||||
itemLayout.addWidget(nameLabel)
|
||||
itemLayout.addStretch()
|
||||
itemLayout.addWidget(valueLabel)
|
||||
|
||||
self.statusWidgets.append({
|
||||
'indicator': indicator,
|
||||
'nameLabel': nameLabel,
|
||||
'valueLabel': valueLabel,
|
||||
'status': False,
|
||||
'initial_value': info["value"],
|
||||
'api_field': info["api_field"]
|
||||
})
|
||||
|
||||
return statusItem
|
||||
|
||||
def _init_operation_buttons(self, parent_layout):
|
||||
"""初始化操作按钮(下料完成/生产异常/生产取消)"""
|
||||
buttonContainer = QWidget()
|
||||
@ -259,23 +256,27 @@ 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.startButton = QPushButton("下料完成")
|
||||
self.restartButton = QPushButton("生产异常")
|
||||
self.stopButton = QPushButton("生产取消")
|
||||
self.startFeedButton = QPushButton("开始下料")
|
||||
self.finishButton = QPushButton("下料完成")
|
||||
self.errorButton = QPushButton("生产异常")
|
||||
self.cancelButton = QPushButton("生产取消")
|
||||
|
||||
# 设置按钮图标
|
||||
self.startButton.setIcon(QIcon(down_icon_path))
|
||||
self.restartButton.setIcon(QIcon(error_icon_path))
|
||||
self.stopButton.setIcon(QIcon(cancel_icon_path))
|
||||
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.startButton.setIconSize(QSize(20, 20))
|
||||
self.restartButton.setIconSize(QSize(20, 20))
|
||||
self.stopButton.setIconSize(QSize(20, 20))
|
||||
self.startFeedButton.setIconSize(QSize(20, 20))
|
||||
self.finishButton.setIconSize(QSize(20, 20))
|
||||
self.errorButton.setIconSize(QSize(20, 20))
|
||||
self.cancelButton.setIconSize(QSize(20, 20))
|
||||
|
||||
button_base_style = """
|
||||
QPushButton {
|
||||
@ -294,7 +295,15 @@ class StatusMonitor(QWidget):
|
||||
}
|
||||
"""
|
||||
|
||||
self.startButton.setStyleSheet(button_base_style + """
|
||||
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;
|
||||
@ -302,7 +311,7 @@ class StatusMonitor(QWidget):
|
||||
}
|
||||
""")
|
||||
|
||||
self.restartButton.setStyleSheet(button_base_style + """
|
||||
self.errorButton.setStyleSheet(button_base_style + """
|
||||
QPushButton {
|
||||
background-color: #E65100;
|
||||
color: white;
|
||||
@ -310,7 +319,7 @@ class StatusMonitor(QWidget):
|
||||
}
|
||||
""")
|
||||
|
||||
self.stopButton.setStyleSheet(button_base_style + """
|
||||
self.cancelButton.setStyleSheet(button_base_style + """
|
||||
QPushButton {
|
||||
background-color: #C62828;
|
||||
color: white;
|
||||
@ -319,18 +328,27 @@ class StatusMonitor(QWidget):
|
||||
""")
|
||||
|
||||
button_font = QFont("Microsoft YaHei", 12, QFont.Bold)
|
||||
self.startButton.setFont(button_font)
|
||||
self.restartButton.setFont(button_font)
|
||||
self.stopButton.setFont(button_font)
|
||||
self.startFeedButton.setFont(button_font)
|
||||
self.finishButton.setFont(button_font)
|
||||
self.errorButton.setFont(button_font)
|
||||
self.cancelButton.setFont(button_font)
|
||||
|
||||
self.startButton.clicked.connect(self.on_start_clicked)
|
||||
self.restartButton.clicked.connect(self.on_restart_clicked)
|
||||
self.stopButton.clicked.connect(self.on_stop_clicked)
|
||||
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.startButton)
|
||||
buttonLayout.addWidget(self.restartButton)
|
||||
buttonLayout.addWidget(self.stopButton)
|
||||
buttonLayout.addWidget(self.startFeedButton)
|
||||
buttonLayout.addWidget(self.finishButton)
|
||||
buttonLayout.addWidget(self.errorButton)
|
||||
buttonLayout.addWidget(self.cancelButton)
|
||||
buttonLayout.addStretch(1)
|
||||
|
||||
parent_layout.addWidget(buttonContainer)
|
||||
@ -342,15 +360,14 @@ class StatusMonitor(QWidget):
|
||||
self.time_timer.timeout.connect(self.update_time)
|
||||
self.time_timer.start(1000)
|
||||
|
||||
# 状态模拟定时器(每3秒更新一次)
|
||||
self.status_timer = QTimer()
|
||||
self.status_timer.timeout.connect(self.simulate_mcu_query)
|
||||
self.status_timer.start(3000)
|
||||
|
||||
def get_current_time(self):
|
||||
"""获取格式化的当前时间"""
|
||||
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
def get_timestamp(self):
|
||||
"""获取当前时间戳(秒级)"""
|
||||
return datetime.now().strftime('%Y%m%d_%H%M%S_%f')[:-3]
|
||||
|
||||
def update_time(self):
|
||||
"""更新时间显示并触发重绘"""
|
||||
self.current_datetime = self.get_current_time()
|
||||
@ -380,92 +397,69 @@ class StatusMonitor(QWidget):
|
||||
# 绘制文本
|
||||
painter.drawText(x, y + text_rect.height(), text)
|
||||
|
||||
def simulate_mcu_query(self):
|
||||
"""模拟单片机状态查询"""
|
||||
import random # 仅用于模拟,实际项目替换为真实通信逻辑
|
||||
def _save_data_to_file(self, button_name):
|
||||
"""
|
||||
将服务端数据+按钮操作信息保存到JSON文件
|
||||
|
||||
if self.is_running:
|
||||
for widget in self.statusWidgets:
|
||||
status = random.choice([True, False])
|
||||
widget['status'] = status
|
||||
参数:button_name:点击的按钮名称
|
||||
"""
|
||||
# 1、检查是否有服务端数据
|
||||
if not self.latest_server_data:
|
||||
print(f"⚠️ 未收到服务端数据,无法保存「{button_name}」操作记录")
|
||||
return
|
||||
|
||||
if status:
|
||||
# 状态为"是"时的样式 - 去除边框
|
||||
widget['valueLabel'].setText("是")
|
||||
widget['valueLabel'].setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #00E676;
|
||||
background-color: #2D2D2D; /* 与父容器相同背景色 */
|
||||
padding: 5px 10px;
|
||||
border: none; /* 明确去除边框 */
|
||||
min-width: 60px;
|
||||
}
|
||||
""")
|
||||
widget['indicator'].setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #00E676;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #00796B;
|
||||
}
|
||||
""")
|
||||
else:
|
||||
# 状态为"否"时的样式 - 去除边框
|
||||
widget['valueLabel'].setText("否")
|
||||
widget['valueLabel'].setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #FF5252;
|
||||
background-color: #2D2D2D; /* 与父容器相同背景色 */
|
||||
padding: 5px 10px;
|
||||
border: none; /* 明确去除边框 */
|
||||
min-width: 60px;
|
||||
}
|
||||
""")
|
||||
widget['indicator'].setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #FF5252;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #C62828;
|
||||
}
|
||||
""")
|
||||
# 2、构建完整数据(服务端数据+按钮操作信息+存档时间)
|
||||
save_data = {
|
||||
"opration_button": button_name,
|
||||
"save_time": self.get_current_time(),
|
||||
"server_data":self.latest_server_data
|
||||
}
|
||||
|
||||
def on_start_clicked(self):
|
||||
"""开始按钮点击事件"""
|
||||
if not self.is_running:
|
||||
self.is_running = True
|
||||
print("系统已启动,开始监控状态...")
|
||||
self.simulate_mcu_query() # 启动时立即更新一次状态
|
||||
# 3、生成唯一文件名(按时间戳命名)
|
||||
file_name = f"operation_record_{self.get_timestamp()}.json"
|
||||
file_path = os.path.join(SAVE_DIR, file_name)
|
||||
|
||||
def on_stop_clicked(self):
|
||||
"""停止按钮点击事件"""
|
||||
if self.is_running:
|
||||
self.is_running = False
|
||||
print("系统已停止,状态监控暂停...")
|
||||
# 4、写入JSON文件
|
||||
try:
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(save_data, f, ensure_ascii=False, indent=4)
|
||||
print(f"💾 保存「{button_name}」操作记录成功:{os.path.abspath(file_path)}")
|
||||
except Exception as e:
|
||||
print(f"💾 保存「{button_name}」操作记录失败:{str(e)}")
|
||||
|
||||
def on_restart_clicked(self):
|
||||
"""重启按钮点击事件"""
|
||||
print("系统正在重启...")
|
||||
# ------------------
|
||||
# TCP客户端核心功能
|
||||
# ------------------
|
||||
@Slot()
|
||||
def _on_tcp_connected(self):
|
||||
"""TCP连接成功回调"""
|
||||
self.is_tcp_connected = True
|
||||
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)
|
||||
|
||||
@Slot()
|
||||
def _on_tcp_disconnected(self):
|
||||
"""TCP连接断开回调"""
|
||||
self.is_tcp_connected = False
|
||||
self.is_running = False
|
||||
# 重置所有状态为初始值和样式
|
||||
for i, widget in enumerate(self.statusWidgets):
|
||||
widget['status'] = False
|
||||
# 恢复初始值
|
||||
widget['valueLabel'].setText(widget['initial_value'])
|
||||
# 恢复初始样式(无边框)
|
||||
widget['valueLabel'].setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
background-color: #2D2D2D;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
min-width: 80px;
|
||||
}
|
||||
""")
|
||||
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)
|
||||
|
||||
# 重置状态指示灯为“未连接”状态
|
||||
for widget in self.statusWidgets:
|
||||
widget['indicator'].setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #9E9E9E;
|
||||
@ -473,9 +467,118 @@ class StatusMonitor(QWidget):
|
||||
border: 2px solid #555555;
|
||||
}
|
||||
""")
|
||||
# 重启后自动开始
|
||||
QTimer.singleShot(1000, self.on_start_clicked)
|
||||
|
||||
@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_server_data = status_data
|
||||
self._update_ui_from_data(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错误回调"""
|
||||
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)
|
||||
|
||||
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 _update_ui_from_data(self, data):
|
||||
"""根据TCP获取的数据更新界面状态"""
|
||||
for widget in self.statusWidgets:
|
||||
api_field = widget['api_field']
|
||||
if api_field in data:
|
||||
new_value = str(data[api_field])
|
||||
widget['valueLabel'].setText(new_value)
|
||||
self.set_indicator_status(widget, new_value)
|
||||
|
||||
# ------------------
|
||||
# 状态指示灯逻辑
|
||||
# ------------------
|
||||
def set_indicator_status(self, widget, value):
|
||||
"""根据值设置状态指示灯颜色"""
|
||||
if value and value != "未知" and value != "" and value != "None":
|
||||
# 有效数据:绿色指示灯
|
||||
widget['indicator'].setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #00E676;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #00796B;
|
||||
}
|
||||
""")
|
||||
else:
|
||||
# 无效数据:红色指示灯
|
||||
widget['indicator'].setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #FF5252;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #C62828;
|
||||
}
|
||||
""")
|
||||
|
||||
# ------------------
|
||||
# 按钮点击事件
|
||||
# ------------------
|
||||
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._send_tcp_request("production_error")
|
||||
self._save_data_to_file("production_error")
|
||||
|
||||
def on_cancel_clicked(self):
|
||||
"""点击“生产取消”:向服务端发送取消指令"""
|
||||
print("🔘 点击「生产取消」按钮")
|
||||
self._send_tcp_request("cancel_feed")
|
||||
self._save_data_to_file("cancel_feed")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
17
operation_records/operation_record_20250919_204518_696.json
Normal file
17
operation_records/operation_record_20250919_204518_696.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"opration_button": "finish_feed",
|
||||
"save_time": "2025-09-19 20:45:18",
|
||||
"server_data": {
|
||||
"task_id": "20250706-01",
|
||||
"project_name": "18号线二期工程",
|
||||
"section": "停车场工作井上行",
|
||||
"slump": "50~70 mm",
|
||||
"mix_ratio_id": "P2022=001",
|
||||
"request_status": "请求中",
|
||||
"material_grade": "C50P12",
|
||||
"volume": "2m³",
|
||||
"request_time": "10分钟后",
|
||||
"car_status": "移动后",
|
||||
"timestamp": "2025-09-19 20:45:16"
|
||||
}
|
||||
}
|
||||
17
operation_records/operation_record_20250919_204539_327.json
Normal file
17
operation_records/operation_record_20250919_204539_327.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"opration_button": "production_error",
|
||||
"save_time": "2025-09-19 20:45:39",
|
||||
"server_data": {
|
||||
"task_id": "20250706-01",
|
||||
"project_name": "18号线二期工程",
|
||||
"section": "停车场工作井上行",
|
||||
"slump": "50~70 mm",
|
||||
"mix_ratio_id": "P2022=001",
|
||||
"request_status": "请求中",
|
||||
"material_grade": "C50P12",
|
||||
"volume": "2m³",
|
||||
"request_time": "10分钟后",
|
||||
"car_status": "移动后",
|
||||
"timestamp": "2025-09-19 20:45:16"
|
||||
}
|
||||
}
|
||||
17
operation_records/operation_record_20250919_204605_704.json
Normal file
17
operation_records/operation_record_20250919_204605_704.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"opration_button": "cancel_feed",
|
||||
"save_time": "2025-09-19 20:46:05",
|
||||
"server_data": {
|
||||
"task_id": "20250706-01",
|
||||
"project_name": "18号线二期工程",
|
||||
"section": "停车场工作井上行",
|
||||
"slump": "50~70 mm",
|
||||
"mix_ratio_id": "P2022=001",
|
||||
"request_status": "请求中",
|
||||
"material_grade": "C50P12",
|
||||
"volume": "2m³",
|
||||
"request_time": "10分钟后",
|
||||
"car_status": "移动后",
|
||||
"timestamp": "2025-09-19 20:45:16"
|
||||
}
|
||||
}
|
||||
17
operation_records/operation_record_20250919_204807_648.json
Normal file
17
operation_records/operation_record_20250919_204807_648.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"opration_button": "finish_feed",
|
||||
"save_time": "2025-09-19 20:48:07",
|
||||
"server_data": {
|
||||
"task_id": "20250706-01",
|
||||
"project_name": "18号线二期工程",
|
||||
"section": "停车场工作井上行",
|
||||
"slump": "50~70 mm",
|
||||
"mix_ratio_id": "P2022=001",
|
||||
"request_status": "请求中",
|
||||
"material_grade": "C50P12",
|
||||
"volume": "2m³",
|
||||
"request_time": "10分钟后",
|
||||
"car_status": "移动后",
|
||||
"timestamp": "2025-09-19 20:47:57"
|
||||
}
|
||||
}
|
||||
17
operation_records/operation_record_20250919_204858_912.json
Normal file
17
operation_records/operation_record_20250919_204858_912.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"opration_button": "production_error",
|
||||
"save_time": "2025-09-19 20:48:58",
|
||||
"server_data": {
|
||||
"task_id": "20250706-01",
|
||||
"project_name": "18号线二期工程",
|
||||
"section": "停车场工作井上行",
|
||||
"slump": "50~70 mm",
|
||||
"mix_ratio_id": "P2022=001",
|
||||
"request_status": "请求中",
|
||||
"material_grade": "C50P12",
|
||||
"volume": "2m³",
|
||||
"request_time": "10分钟后",
|
||||
"car_status": "移动后",
|
||||
"timestamp": "2025-09-19 20:48:53"
|
||||
}
|
||||
}
|
||||
589
test.py
589
test.py
@ -10,14 +10,27 @@ from PySide6.QtWidgets import (
|
||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QLabel, QFrame, QSizePolicy, QPushButton
|
||||
)
|
||||
from PySide6.QtCore import Qt, QTimer, QPoint, QSize
|
||||
from PySide6.QtCore import Qt, QTimer, QPoint, QSize, Slot
|
||||
from PySide6.QtGui import QFont, QPainter, QColor, QPen, QIcon
|
||||
from datetime import datetime
|
||||
from PySide6.QtNetwork import QTcpSocket, QAbstractSocket
|
||||
import json
|
||||
import os
|
||||
|
||||
|
||||
# -----------
|
||||
# 参数配置
|
||||
# -----------
|
||||
tcp_server_host = "127.0.0.1"
|
||||
tcp_server_port = 8888
|
||||
|
||||
# 数据保存目录
|
||||
SAVE_DIR = "operation_records"
|
||||
|
||||
class StatusMonitor(QWidget):
|
||||
"""
|
||||
中交三航精准布料浇筑要料系统 - 主界面类(深色主题)
|
||||
使用TCP进行数据传输(客户端模型,与TCP服务器通信)
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
@ -25,6 +38,19 @@ class StatusMonitor(QWidget):
|
||||
super().__init__(parent=parent)
|
||||
self.is_running = False # 系统运行状态标记
|
||||
self.current_datetime = self.get_current_time() # 当前日期时间
|
||||
# 缓存服务端发送的最新JSON数据
|
||||
self.latest_json_data = {}
|
||||
|
||||
# ---------------
|
||||
# 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连接状态标记
|
||||
|
||||
# 绑定TCP信号与槽(事件驱动)
|
||||
self._bind_tcp_signals()
|
||||
|
||||
# 窗口基础设置
|
||||
self.setWindowTitle("中交三航精准布料浇筑要料系统")
|
||||
@ -40,6 +66,32 @@ class StatusMonitor(QWidget):
|
||||
self._init_title_label()
|
||||
self._init_status_container()
|
||||
self._init_timers()
|
||||
self._init_save_dir()
|
||||
|
||||
# ----------------
|
||||
# 客户端自动后自动连接服务端
|
||||
# ----------------
|
||||
print(f"客户端启动,自动连接服务端{self.tcp_server_host}:{self.tcp_server_port}...")
|
||||
self.tcp_socket.connectToHost(self.tcp_server_host, self.tcp_server_port)
|
||||
|
||||
def _init_save_dir(self):
|
||||
"""初始化数据保存目录"""
|
||||
if not os.path.exists(SAVE_DIR):
|
||||
os.makedirs(SAVE_DIR)
|
||||
print(f"已创建数据保存目录:{os.path.abspath(SAVE_DIR)}")
|
||||
else:
|
||||
print(f"数据保存目录已存在:{os.path.abspath(SAVE_DIR)}")
|
||||
|
||||
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)
|
||||
|
||||
def _init_title_label(self):
|
||||
"""初始化系统标题标签"""
|
||||
@ -97,152 +149,30 @@ class StatusMonitor(QWidget):
|
||||
|
||||
# 左边5个状态项及对应初始值
|
||||
leftStatusInfo = [
|
||||
{"name": "任务单号", "value": "20250706-01"},
|
||||
{"name": "工程名称", "value": "18号线二期工程"},
|
||||
{"name": "区间段", "value": "停车场工作并上行"},
|
||||
{"name": "坍落度", "value": "50~70 mm"},
|
||||
{"name": "配合比编号", "value": "P2022=001"}
|
||||
{"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"}
|
||||
]
|
||||
# 右边5个状态项及对应初始值
|
||||
rightStatusInfo = [
|
||||
{"name": "要料状态", "value": "请求中"},
|
||||
{"name": "要料标号", "value": "C50P12"},
|
||||
{"name": "要料方量", "value": "2m³"},
|
||||
{"name": "要料时间", "value": "2分钟后"},
|
||||
{"name": "小车状态", "value": "移动后"}
|
||||
{"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"}
|
||||
]
|
||||
self.statusWidgets = []
|
||||
|
||||
# 处理左边状态项
|
||||
for info in leftStatusInfo:
|
||||
statusItem = QFrame()
|
||||
statusItem.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #2D2D2D;
|
||||
border: 1px solid #444444;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
}
|
||||
""")
|
||||
statusItem.setFixedHeight(80)
|
||||
statusItem.setFixedWidth(320) # 统一加长状态项宽度(原无固定宽度)
|
||||
|
||||
itemLayout = QHBoxLayout(statusItem)
|
||||
itemLayout.setContentsMargins(10, 5, 10, 5)
|
||||
|
||||
# 状态指示灯
|
||||
indicator = QLabel()
|
||||
indicator.setFixedSize(20, 20)
|
||||
indicator.setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #9E9E9E;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #555555;
|
||||
}
|
||||
""")
|
||||
|
||||
# 状态名称标签
|
||||
nameLabel = QLabel(info["name"])
|
||||
nameLabel.setFixedWidth(100) # 加宽名称标签(原90)
|
||||
nameLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
nameLabel.setStyleSheet("font-size: 14px; color: #FFFFFF;")
|
||||
nameLabel.setFont(QFont("Microsoft YaHei", 12))
|
||||
|
||||
# 状态值标签
|
||||
valueLabel = QLabel(info["value"])
|
||||
valueLabel.setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
background-color: #2D2D2D;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
}
|
||||
""")
|
||||
valueLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
valueLabel.setMinimumWidth(150) # 加宽值标签(原80)
|
||||
|
||||
itemLayout.addWidget(indicator)
|
||||
itemLayout.addSpacing(10)
|
||||
itemLayout.addWidget(nameLabel)
|
||||
itemLayout.addStretch()
|
||||
itemLayout.addWidget(valueLabel)
|
||||
|
||||
self.statusWidgets.append({
|
||||
'indicator': indicator,
|
||||
'nameLabel': nameLabel,
|
||||
'valueLabel': valueLabel,
|
||||
'status': False,
|
||||
'initial_value': info["value"]
|
||||
})
|
||||
|
||||
statusItem = self._create_status_item(info)
|
||||
leftLayout.addWidget(statusItem)
|
||||
|
||||
# 处理右边状态项
|
||||
for info in rightStatusInfo:
|
||||
statusItem = QFrame()
|
||||
statusItem.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #2D2D2D;
|
||||
border: 1px solid #444444;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
}
|
||||
""")
|
||||
statusItem.setFixedHeight(80)
|
||||
statusItem.setFixedWidth(320) # 统一加长状态项宽度(与左侧一致)
|
||||
|
||||
itemLayout = QHBoxLayout(statusItem)
|
||||
itemLayout.setContentsMargins(10, 5, 10, 5)
|
||||
|
||||
# 状态指示灯
|
||||
indicator = QLabel()
|
||||
indicator.setFixedSize(20, 20)
|
||||
indicator.setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #9E9E9E;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #555555;
|
||||
}
|
||||
""")
|
||||
|
||||
# 状态名称标签
|
||||
nameLabel = QLabel(info["name"])
|
||||
nameLabel.setFixedWidth(100) # 加宽名称标签(与左侧一致)
|
||||
nameLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
nameLabel.setStyleSheet("font-size: 14px; color: #FFFFFF;")
|
||||
nameLabel.setFont(QFont("Microsoft YaHei", 12))
|
||||
|
||||
# 状态值标签
|
||||
valueLabel = QLabel(info["value"])
|
||||
valueLabel.setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
background-color: #2D2D2D;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
}
|
||||
""")
|
||||
valueLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
valueLabel.setMinimumWidth(150) # 加宽值标签(与左侧一致)
|
||||
|
||||
itemLayout.addWidget(indicator)
|
||||
itemLayout.addSpacing(10)
|
||||
itemLayout.addWidget(nameLabel)
|
||||
itemLayout.addStretch()
|
||||
itemLayout.addWidget(valueLabel)
|
||||
|
||||
self.statusWidgets.append({
|
||||
'indicator': indicator,
|
||||
'nameLabel': nameLabel,
|
||||
'valueLabel': valueLabel,
|
||||
'status': False,
|
||||
'initial_value': info["value"]
|
||||
})
|
||||
|
||||
statusItem = self._create_status_item(info)
|
||||
rightLayout.addWidget(statusItem)
|
||||
|
||||
statusLayout.addWidget(leftGroup)
|
||||
@ -251,6 +181,73 @@ class StatusMonitor(QWidget):
|
||||
|
||||
parent_layout.addWidget(statusWidget)
|
||||
|
||||
def _create_status_item(self, info):
|
||||
"""创建单个状态项"""
|
||||
statusItem = QFrame()
|
||||
statusItem.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #2D2D2D;
|
||||
border: 1px solid #444444;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
}
|
||||
""")
|
||||
statusItem.setFixedHeight(80)
|
||||
statusItem.setFixedWidth(320) # 统一加长状态项宽度(原无固定宽度)
|
||||
|
||||
itemLayout = QHBoxLayout(statusItem)
|
||||
itemLayout.setContentsMargins(10, 5, 10, 5)
|
||||
|
||||
# 状态指示灯
|
||||
indicator = QLabel()
|
||||
indicator.setFixedSize(20, 20)
|
||||
indicator.setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #9E9E9E;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #555555;
|
||||
}
|
||||
""")
|
||||
|
||||
# 状态名称标签
|
||||
nameLabel = QLabel(info["name"])
|
||||
nameLabel.setFixedWidth(100) # 加宽名称标签(原90)
|
||||
nameLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
nameLabel.setStyleSheet("font-size: 14px; color: #FFFFFF;")
|
||||
nameLabel.setFont(QFont("Microsoft YaHei", 12))
|
||||
|
||||
# 状态值标签
|
||||
valueLabel = QLabel(info["value"])
|
||||
valueLabel.setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
background-color: #2D2D2D;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
}
|
||||
""")
|
||||
valueLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
valueLabel.setMinimumWidth(150) # 加宽值标签(原80)
|
||||
|
||||
itemLayout.addWidget(indicator)
|
||||
itemLayout.addSpacing(10)
|
||||
itemLayout.addWidget(nameLabel)
|
||||
itemLayout.addStretch()
|
||||
itemLayout.addWidget(valueLabel)
|
||||
|
||||
self.statusWidgets.append({
|
||||
'indicator': indicator,
|
||||
'nameLabel': nameLabel,
|
||||
'valueLabel': valueLabel,
|
||||
'status': False,
|
||||
'initial_value': info["value"],
|
||||
'api_field': info["api_field"]
|
||||
})
|
||||
|
||||
return statusItem
|
||||
|
||||
def _init_operation_buttons(self, parent_layout):
|
||||
"""初始化操作按钮(下料完成/生产异常/生产取消)"""
|
||||
buttonContainer = QWidget()
|
||||
@ -259,23 +256,27 @@ 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.startButton = QPushButton("下料完成")
|
||||
self.restartButton = QPushButton("生产异常")
|
||||
self.stopButton = QPushButton("生产取消")
|
||||
self.startFeedButton = QPushButton("开始下料")
|
||||
self.finishButton = QPushButton("下料完成")
|
||||
self.errorButton = QPushButton("生产异常")
|
||||
self.cancelButton = QPushButton("生产取消")
|
||||
|
||||
# 设置按钮图标
|
||||
self.startButton.setIcon(QIcon(down_icon_path))
|
||||
self.restartButton.setIcon(QIcon(error_icon_path))
|
||||
self.stopButton.setIcon(QIcon(cancel_icon_path))
|
||||
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.startButton.setIconSize(QSize(20, 20))
|
||||
self.restartButton.setIconSize(QSize(20, 20))
|
||||
self.stopButton.setIconSize(QSize(20, 20))
|
||||
self.startFeedButton.setIconSize(QSize(20, 20))
|
||||
self.finishButton.setIconSize(QSize(20, 20))
|
||||
self.errorButton.setIconSize(QSize(20, 20))
|
||||
self.cancelButton.setIconSize(QSize(20, 20))
|
||||
|
||||
button_base_style = """
|
||||
QPushButton {
|
||||
@ -294,7 +295,15 @@ class StatusMonitor(QWidget):
|
||||
}
|
||||
"""
|
||||
|
||||
self.startButton.setStyleSheet(button_base_style + """
|
||||
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;
|
||||
@ -302,7 +311,7 @@ class StatusMonitor(QWidget):
|
||||
}
|
||||
""")
|
||||
|
||||
self.restartButton.setStyleSheet(button_base_style + """
|
||||
self.errorButton.setStyleSheet(button_base_style + """
|
||||
QPushButton {
|
||||
background-color: #E65100;
|
||||
color: white;
|
||||
@ -310,7 +319,7 @@ class StatusMonitor(QWidget):
|
||||
}
|
||||
""")
|
||||
|
||||
self.stopButton.setStyleSheet(button_base_style + """
|
||||
self.cancelButton.setStyleSheet(button_base_style + """
|
||||
QPushButton {
|
||||
background-color: #C62828;
|
||||
color: white;
|
||||
@ -319,18 +328,27 @@ class StatusMonitor(QWidget):
|
||||
""")
|
||||
|
||||
button_font = QFont("Microsoft YaHei", 12, QFont.Bold)
|
||||
self.startButton.setFont(button_font)
|
||||
self.restartButton.setFont(button_font)
|
||||
self.stopButton.setFont(button_font)
|
||||
self.startFeedButton.setFont(button_font)
|
||||
self.finishButton.setFont(button_font)
|
||||
self.errorButton.setFont(button_font)
|
||||
self.cancelButton.setFont(button_font)
|
||||
|
||||
self.startButton.clicked.connect(self.on_start_clicked)
|
||||
self.restartButton.clicked.connect(self.on_restart_clicked)
|
||||
self.stopButton.clicked.connect(self.on_stop_clicked)
|
||||
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.startButton)
|
||||
buttonLayout.addWidget(self.restartButton)
|
||||
buttonLayout.addWidget(self.stopButton)
|
||||
buttonLayout.addWidget(self.startFeedButton)
|
||||
buttonLayout.addWidget(self.finishButton)
|
||||
buttonLayout.addWidget(self.errorButton)
|
||||
buttonLayout.addWidget(self.cancelButton)
|
||||
buttonLayout.addStretch(1)
|
||||
|
||||
parent_layout.addWidget(buttonContainer)
|
||||
@ -342,15 +360,14 @@ class StatusMonitor(QWidget):
|
||||
self.time_timer.timeout.connect(self.update_time)
|
||||
self.time_timer.start(1000)
|
||||
|
||||
# 状态模拟定时器(每3秒更新一次)
|
||||
self.status_timer = QTimer()
|
||||
self.status_timer.timeout.connect(self.simulate_mcu_query)
|
||||
self.status_timer.start(3000)
|
||||
|
||||
def get_current_time(self):
|
||||
"""获取格式化的当前时间"""
|
||||
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
def get_timestamp(self):
|
||||
"""获取当前时间戳(秒级)"""
|
||||
return datetime.now().strftime('%Y%m%d_%H%M%S_%f')[:-3]
|
||||
|
||||
def update_time(self):
|
||||
"""更新时间显示并触发重绘"""
|
||||
self.current_datetime = self.get_current_time()
|
||||
@ -380,92 +397,69 @@ class StatusMonitor(QWidget):
|
||||
# 绘制文本
|
||||
painter.drawText(x, y + text_rect.height(), text)
|
||||
|
||||
def simulate_mcu_query(self):
|
||||
"""模拟单片机状态查询"""
|
||||
import random # 仅用于模拟,实际项目替换为真实通信逻辑
|
||||
def _save_data_to_file(self, button_name):
|
||||
"""
|
||||
将服务端数据+按钮操作信息保存到JSON文件
|
||||
|
||||
if self.is_running:
|
||||
for widget in self.statusWidgets:
|
||||
status = random.choice([True, False])
|
||||
widget['status'] = status
|
||||
参数:button_name:点击的按钮名称
|
||||
"""
|
||||
# 1、检查是否有服务端数据
|
||||
if not self.latest_server_data:
|
||||
print(f"⚠️ 未收到服务端数据,无法保存「{button_name}」操作记录")
|
||||
return
|
||||
|
||||
if status:
|
||||
# 状态为"是"时的样式 - 去除边框
|
||||
widget['valueLabel'].setText("是")
|
||||
widget['valueLabel'].setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #00E676;
|
||||
background-color: #2D2D2D; /* 与父容器相同背景色 */
|
||||
padding: 5px 10px;
|
||||
border: none; /* 明确去除边框 */
|
||||
min-width: 60px;
|
||||
}
|
||||
""")
|
||||
widget['indicator'].setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #00E676;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #00796B;
|
||||
}
|
||||
""")
|
||||
else:
|
||||
# 状态为"否"时的样式 - 去除边框
|
||||
widget['valueLabel'].setText("否")
|
||||
widget['valueLabel'].setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #FF5252;
|
||||
background-color: #2D2D2D; /* 与父容器相同背景色 */
|
||||
padding: 5px 10px;
|
||||
border: none; /* 明确去除边框 */
|
||||
min-width: 60px;
|
||||
}
|
||||
""")
|
||||
widget['indicator'].setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #FF5252;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #C62828;
|
||||
}
|
||||
""")
|
||||
# 2、构建完整数据(服务端数据+按钮操作信息+存档时间)
|
||||
save_data = {
|
||||
"opration_button": button_name,
|
||||
"save_time": self.get_current_time(),
|
||||
"server_data":self.latest_server_data
|
||||
}
|
||||
|
||||
def on_start_clicked(self):
|
||||
"""开始按钮点击事件"""
|
||||
if not self.is_running:
|
||||
self.is_running = True
|
||||
print("系统已启动,开始监控状态...")
|
||||
self.simulate_mcu_query() # 启动时立即更新一次状态
|
||||
# 3、生成唯一文件名(按时间戳命名)
|
||||
file_name = f"operation_record_{self.get_timestamp()}.json"
|
||||
file_path = os.path.join(SAVE_DIR, file_name)
|
||||
|
||||
def on_stop_clicked(self):
|
||||
"""停止按钮点击事件"""
|
||||
if self.is_running:
|
||||
self.is_running = False
|
||||
print("系统已停止,状态监控暂停...")
|
||||
# 4、写入JSON文件
|
||||
try:
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(save_data, f, ensure_ascii=False, indent=4)
|
||||
print(f"💾 保存「{button_name}」操作记录成功:{os.path.abspath(file_path)}")
|
||||
except Exception as e:
|
||||
print(f"💾 保存「{button_name}」操作记录失败:{str(e)}")
|
||||
|
||||
def on_restart_clicked(self):
|
||||
"""重启按钮点击事件"""
|
||||
print("系统正在重启...")
|
||||
# ------------------
|
||||
# TCP客户端核心功能
|
||||
# ------------------
|
||||
@Slot()
|
||||
def _on_tcp_connected(self):
|
||||
"""TCP连接成功回调"""
|
||||
self.is_tcp_connected = True
|
||||
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)
|
||||
|
||||
@Slot()
|
||||
def _on_tcp_disconnected(self):
|
||||
"""TCP连接断开回调"""
|
||||
self.is_tcp_connected = False
|
||||
self.is_running = False
|
||||
# 重置所有状态为初始值和样式
|
||||
for i, widget in enumerate(self.statusWidgets):
|
||||
widget['status'] = False
|
||||
# 恢复初始值
|
||||
widget['valueLabel'].setText(widget['initial_value'])
|
||||
# 恢复初始样式(无边框)
|
||||
widget['valueLabel'].setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
background-color: #2D2D2D;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
min-width: 80px;
|
||||
}
|
||||
""")
|
||||
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)
|
||||
|
||||
# 重置状态指示灯为“未连接”状态
|
||||
for widget in self.statusWidgets:
|
||||
widget['indicator'].setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #9E9E9E;
|
||||
@ -473,9 +467,118 @@ class StatusMonitor(QWidget):
|
||||
border: 2px solid #555555;
|
||||
}
|
||||
""")
|
||||
# 重启后自动开始
|
||||
QTimer.singleShot(1000, self.on_start_clicked)
|
||||
|
||||
@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_server_data = status_data
|
||||
self._update_ui_from_data(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错误回调"""
|
||||
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)
|
||||
|
||||
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 _update_ui_from_data(self, data):
|
||||
"""根据TCP获取的数据更新界面状态"""
|
||||
for widget in self.statusWidgets:
|
||||
api_field = widget['api_field']
|
||||
if api_field in data:
|
||||
new_value = str(data[api_field])
|
||||
widget['valueLabel'].setText(new_value)
|
||||
self.set_indicator_status(widget, new_value)
|
||||
|
||||
# ------------------
|
||||
# 状态指示灯逻辑
|
||||
# ------------------
|
||||
def set_indicator_status(self, widget, value):
|
||||
"""根据值设置状态指示灯颜色"""
|
||||
if value and value != "未知" and value != "" and value != "None":
|
||||
# 有效数据:绿色指示灯
|
||||
widget['indicator'].setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #00E676;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #00796B;
|
||||
}
|
||||
""")
|
||||
else:
|
||||
# 无效数据:红色指示灯
|
||||
widget['indicator'].setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #FF5252;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #C62828;
|
||||
}
|
||||
""")
|
||||
|
||||
# ------------------
|
||||
# 按钮点击事件
|
||||
# ------------------
|
||||
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._send_tcp_request("production_error")
|
||||
self._save_data_to_file("production_error")
|
||||
|
||||
def on_cancel_clicked(self):
|
||||
"""点击“生产取消”:向服务端发送取消指令"""
|
||||
print("🔘 点击「生产取消」按钮")
|
||||
self._send_tcp_request("cancel_feed")
|
||||
self._save_data_to_file("cancel_feed")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
Reference in New Issue
Block a user