2025-06-07 10:45:09 +08:00
|
|
|
|
import os
|
|
|
|
|
|
import sys
|
|
|
|
|
|
import logging
|
2025-06-07 14:10:32 +08:00
|
|
|
|
import json
|
2025-06-07 10:45:09 +08:00
|
|
|
|
from datetime import datetime
|
2025-06-08 01:24:27 +08:00
|
|
|
|
from pathlib import Path
|
2025-06-10 16:13:22 +08:00
|
|
|
|
from utils.modbus_utils import ModbusUtils
|
2025-06-11 15:21:26 +08:00
|
|
|
|
from utils.modbus_monitor import get_instance as get_modbus_monitor
|
|
|
|
|
|
from utils.register_handlers import (
|
2025-06-12 10:25:33 +08:00
|
|
|
|
NGHandler,
|
2025-06-11 15:21:26 +08:00
|
|
|
|
WeightDataHandler,
|
|
|
|
|
|
LabelSignalHandler,
|
|
|
|
|
|
MachineStatusHandlers,
|
|
|
|
|
|
LoadingFeedbackHandler,
|
|
|
|
|
|
UnloadingFeedbackHandler,
|
|
|
|
|
|
Error1Handler,
|
|
|
|
|
|
Error2Handler,
|
2025-06-12 10:25:33 +08:00
|
|
|
|
Error3Handler,
|
|
|
|
|
|
UnloadingLevelHandler,
|
|
|
|
|
|
UnloadingPositionHandler
|
2025-06-11 15:21:26 +08:00
|
|
|
|
)
|
2025-06-07 10:45:09 +08:00
|
|
|
|
# 导入PySide6
|
2025-06-08 01:24:27 +08:00
|
|
|
|
from PySide6.QtWidgets import (
|
|
|
|
|
|
QWidget, QMessageBox, QTableWidgetItem, QStackedWidget, QLabel, QMainWindow,
|
2025-06-12 10:25:33 +08:00
|
|
|
|
QTableWidget, QMenu, QComboBox, QFormLayout, QDialog, QVBoxLayout, QHBoxLayout, QPushButton,
|
|
|
|
|
|
QStatusBar, QSplitter, QFrame
|
2025-06-08 01:24:27 +08:00
|
|
|
|
)
|
2025-06-11 15:21:26 +08:00
|
|
|
|
from PySide6.QtCore import Qt, QTimer, Slot
|
2025-06-07 10:45:09 +08:00
|
|
|
|
from PySide6.QtGui import QBrush, QColor
|
|
|
|
|
|
|
|
|
|
|
|
# 导入UI
|
|
|
|
|
|
from ui.main_window_ui import MainWindowUI
|
|
|
|
|
|
# 导入相机显示组件和设置组件
|
|
|
|
|
|
from widgets.camera_display_widget import CameraDisplayWidget
|
|
|
|
|
|
from widgets.camera_settings_widget import CameraSettingsWidget
|
|
|
|
|
|
|
2025-06-07 16:44:27 +08:00
|
|
|
|
# 导入检验配置管理器
|
|
|
|
|
|
from utils.inspection_config_manager import InspectionConfigManager
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 导入托盘类型管理器
|
|
|
|
|
|
from utils.pallet_type_manager import PalletTypeManager
|
2025-06-12 17:29:35 +08:00
|
|
|
|
# 导入串口管理
|
|
|
|
|
|
from utils.serial_manager import SerialManager
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
|
|
|
|
|
class MainWindow(MainWindowUI):
|
|
|
|
|
|
"""主窗口"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, user_id=None, user_name=None, corp_name=None, corp_id=None, position_id=None):
|
|
|
|
|
|
super().__init__()
|
2025-06-13 09:01:41 +08:00
|
|
|
|
|
2025-06-07 10:45:09 +08:00
|
|
|
|
self.user_id = user_id
|
|
|
|
|
|
self.user_name = user_name
|
|
|
|
|
|
self.corp_name = corp_name
|
|
|
|
|
|
self.corp_id = corp_id
|
|
|
|
|
|
self.position_id = position_id
|
2025-06-11 17:00:05 +08:00
|
|
|
|
self.init_seq = {} # 初始化轴包装的序号
|
2025-06-13 09:01:41 +08:00
|
|
|
|
|
2025-06-07 10:45:09 +08:00
|
|
|
|
# 设置窗口标题
|
|
|
|
|
|
if user_name and corp_name:
|
|
|
|
|
|
self.setWindowTitle(f"腾智微丝产线包装系统 - {user_name} ({corp_name})")
|
2025-06-13 09:01:41 +08:00
|
|
|
|
|
2025-06-07 14:10:32 +08:00
|
|
|
|
# 加载配置文件
|
|
|
|
|
|
self.config = self.load_config()
|
|
|
|
|
|
self.camera_enabled = self.config.get('camera', {}).get('enabled', False)
|
|
|
|
|
|
|
2025-06-07 16:44:27 +08:00
|
|
|
|
# 初始化检验配置管理器
|
|
|
|
|
|
self.inspection_manager = InspectionConfigManager.get_instance()
|
|
|
|
|
|
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 初始化托盘类型管理器
|
|
|
|
|
|
self.pallet_type_manager = PalletTypeManager.get_instance()
|
|
|
|
|
|
|
|
|
|
|
|
# 创建表单布局,用于添加托盘类型选择控件
|
|
|
|
|
|
self.material_form_layout = QFormLayout()
|
|
|
|
|
|
self.material_content_layout.addLayout(self.material_form_layout)
|
|
|
|
|
|
|
|
|
|
|
|
self.output_form_layout = QFormLayout()
|
|
|
|
|
|
self.output_content_layout.addLayout(self.output_form_layout)
|
|
|
|
|
|
|
2025-06-07 14:10:32 +08:00
|
|
|
|
# 只有在相机启用时创建相机显示组件
|
|
|
|
|
|
if self.camera_enabled:
|
|
|
|
|
|
# 创建相机显示组件并添加到上料区
|
|
|
|
|
|
self.camera_display = CameraDisplayWidget()
|
|
|
|
|
|
self.material_content_layout.addWidget(self.camera_display)
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 在上料区添加占位标签
|
|
|
|
|
|
self.material_placeholder = QLabel("相机功能已禁用")
|
|
|
|
|
|
self.material_placeholder.setAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.material_placeholder.setStyleSheet("color: #888888; background-color: #f0f0f0;")
|
|
|
|
|
|
self.material_content_layout.addWidget(self.material_placeholder)
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 为下料区添加占位标签,确保它保持为空
|
2025-06-12 10:25:33 +08:00
|
|
|
|
self.output_placeholder = QWidget()
|
|
|
|
|
|
self.output_placeholder.setStyleSheet("background-color: #f0f0f0;")
|
|
|
|
|
|
placeholder_layout = QVBoxLayout(self.output_placeholder)
|
|
|
|
|
|
placeholder_layout.setAlignment(Qt.AlignCenter)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加标题标签
|
|
|
|
|
|
title_label = QLabel("下料区")
|
|
|
|
|
|
title_label.setAlignment(Qt.AlignCenter)
|
|
|
|
|
|
title_label.setStyleSheet("color: #888888;")
|
|
|
|
|
|
title_label.setFont(self.second_title_font)
|
|
|
|
|
|
placeholder_layout.addWidget(title_label)
|
|
|
|
|
|
|
2025-06-07 10:45:09 +08:00
|
|
|
|
self.output_content_layout.addWidget(self.output_placeholder)
|
|
|
|
|
|
|
2025-06-12 10:25:33 +08:00
|
|
|
|
# 添加下料信息标签
|
|
|
|
|
|
self.unloading_level_label = QLabel("下料层数:--")
|
|
|
|
|
|
self.unloading_position_label = QLabel("下料位置:--")
|
|
|
|
|
|
placeholder_layout.addWidget(self.unloading_level_label)
|
|
|
|
|
|
placeholder_layout.addWidget(self.unloading_position_label)
|
|
|
|
|
|
self.unloading_level_label.setStyleSheet("color: #888888; font-weight: bold;")
|
|
|
|
|
|
self.unloading_position_label.setStyleSheet("color: #888888; font-weight: bold;")
|
|
|
|
|
|
self.unloading_level_label.setFont(self.normal_font)
|
|
|
|
|
|
self.unloading_position_label.setFont(self.normal_font)
|
2025-06-13 09:01:41 +08:00
|
|
|
|
|
2025-06-07 10:45:09 +08:00
|
|
|
|
# 创建堆叠部件
|
|
|
|
|
|
self.stacked_widget = QStackedWidget()
|
|
|
|
|
|
self.stacked_widget.addWidget(self.central_widget) # 主页面
|
|
|
|
|
|
|
|
|
|
|
|
# 不在这里直接初始化相机设置组件
|
|
|
|
|
|
# 延迟创建,保证创建的时候SettingsUI的所有控件都已经准备好
|
|
|
|
|
|
self.camera_settings = None
|
|
|
|
|
|
|
|
|
|
|
|
# 设置中央部件为堆叠部件
|
|
|
|
|
|
self.setCentralWidget(self.stacked_widget)
|
|
|
|
|
|
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 添加托盘类型选择下拉框
|
|
|
|
|
|
self.add_pallet_type_selectors()
|
|
|
|
|
|
|
2025-06-07 10:45:09 +08:00
|
|
|
|
# 连接信号和槽
|
2025-06-07 14:10:32 +08:00
|
|
|
|
self.connect_signals()
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 默认显示主页面
|
|
|
|
|
|
self.stacked_widget.setCurrentIndex(0)
|
|
|
|
|
|
|
2025-06-07 16:44:27 +08:00
|
|
|
|
# 配置检验列 - 使用检验配置管理器获取启用的列数和标题
|
|
|
|
|
|
self.update_inspection_columns()
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
2025-06-08 01:24:27 +08:00
|
|
|
|
# 设置表格上下文菜单
|
|
|
|
|
|
self.process_table.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
|
|
|
|
|
|
|
|
|
|
# 加载未完成的检验数据
|
2025-06-10 13:37:43 +08:00
|
|
|
|
self.load_finished_inspection_data()
|
|
|
|
|
|
|
|
|
|
|
|
# 加载已完成检验数据
|
|
|
|
|
|
self.show_pack_item()
|
2025-06-11 17:00:05 +08:00
|
|
|
|
# 创建状态处理器实例
|
|
|
|
|
|
self.machine_handlers = MachineStatusHandlers()
|
|
|
|
|
|
|
|
|
|
|
|
# 添加状态显示到状态栏
|
|
|
|
|
|
self.modbus_status_label = QLabel("Modbus: 未连接")
|
|
|
|
|
|
self.weight_label = QLabel("重量: --")
|
|
|
|
|
|
self.label_status_label = QLabel("贴标: 无贴标")
|
|
|
|
|
|
self.error_status_label = QLabel("故障: 无")
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
2025-06-11 17:00:05 +08:00
|
|
|
|
# 设置样式
|
|
|
|
|
|
self.error_status_label.setStyleSheet("color: green; font-weight: bold;")
|
|
|
|
|
|
|
|
|
|
|
|
# 添加到状态栏
|
|
|
|
|
|
self.statusBar().addPermanentWidget(self.modbus_status_label)
|
|
|
|
|
|
self.statusBar().addPermanentWidget(self.weight_label)
|
|
|
|
|
|
self.statusBar().addPermanentWidget(self.label_status_label)
|
|
|
|
|
|
self.statusBar().addPermanentWidget(self.error_status_label)
|
|
|
|
|
|
self.statusBar().addPermanentWidget(QLabel(" "))
|
2025-06-07 10:45:09 +08:00
|
|
|
|
logging.info(f"主窗口已创建,用户: {user_name}")
|
2025-06-12 17:29:35 +08:00
|
|
|
|
|
|
|
|
|
|
# 初始化串口管理器
|
|
|
|
|
|
self.serial_manager = SerialManager()
|
2025-06-13 09:01:41 +08:00
|
|
|
|
|
|
|
|
|
|
# 注册串口数据回调函数
|
|
|
|
|
|
self.register_serial_callbacks()
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
2025-06-10 16:13:22 +08:00
|
|
|
|
def add_pallet_type_selectors(self):
|
|
|
|
|
|
"""添加托盘类型选择下拉框"""
|
|
|
|
|
|
# 创建上料托盘类型选择下拉框
|
|
|
|
|
|
self.input_pallet_type_label = QLabel("上料托盘类型:")
|
|
|
|
|
|
self.input_pallet_type_label.setFont(self.normal_font)
|
|
|
|
|
|
self.input_pallet_type_label.setVisible(False)
|
|
|
|
|
|
self.material_form_layout.addRow(self.input_pallet_type_label)
|
|
|
|
|
|
|
|
|
|
|
|
self.input_pallet_type_combo = QComboBox()
|
|
|
|
|
|
self.input_pallet_type_combo.setFont(self.normal_font)
|
|
|
|
|
|
self.input_pallet_type_combo.setVisible(False)
|
|
|
|
|
|
self.material_form_layout.addRow("", self.input_pallet_type_combo)
|
|
|
|
|
|
|
|
|
|
|
|
# 创建下料托盘类型选择下拉框
|
|
|
|
|
|
self.output_pallet_type_label = QLabel("下料托盘类型:")
|
|
|
|
|
|
self.output_pallet_type_label.setFont(self.normal_font)
|
|
|
|
|
|
self.output_pallet_type_label.setVisible(False)
|
|
|
|
|
|
self.output_form_layout.addRow(self.output_pallet_type_label)
|
|
|
|
|
|
|
|
|
|
|
|
self.output_pallet_type_combo = QComboBox()
|
|
|
|
|
|
self.output_pallet_type_combo.setFont(self.normal_font)
|
|
|
|
|
|
self.output_pallet_type_combo.setVisible(False)
|
|
|
|
|
|
self.output_form_layout.addRow("", self.output_pallet_type_combo)
|
|
|
|
|
|
|
|
|
|
|
|
# 加载托盘类型数据
|
|
|
|
|
|
self.update_pallet_types()
|
|
|
|
|
|
|
|
|
|
|
|
def update_pallet_types(self):
|
|
|
|
|
|
"""更新托盘类型下拉框"""
|
|
|
|
|
|
# 重新加载托盘类型数据
|
|
|
|
|
|
self.pallet_type_manager.reload_pallet_types()
|
|
|
|
|
|
|
|
|
|
|
|
# 更新上料托盘类型
|
|
|
|
|
|
input_types = self.pallet_type_manager.get_pallet_types_by_operation("input")
|
|
|
|
|
|
self.input_pallet_type_combo.clear()
|
|
|
|
|
|
for pallet_type in input_types:
|
|
|
|
|
|
self.input_pallet_type_combo.addItem(pallet_type['type_name'], pallet_type['id'])
|
|
|
|
|
|
|
|
|
|
|
|
# 更新下料托盘类型
|
|
|
|
|
|
output_types = self.pallet_type_manager.get_pallet_types_by_operation("output")
|
|
|
|
|
|
self.output_pallet_type_combo.clear()
|
|
|
|
|
|
for pallet_type in output_types:
|
|
|
|
|
|
self.output_pallet_type_combo.addItem(pallet_type['type_name'], pallet_type['id'])
|
|
|
|
|
|
|
2025-06-07 14:10:32 +08:00
|
|
|
|
def load_config(self):
|
|
|
|
|
|
"""加载配置文件"""
|
|
|
|
|
|
config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config", "app_config.json")
|
|
|
|
|
|
try:
|
|
|
|
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
|
|
|
|
config = json.load(f)
|
|
|
|
|
|
logging.info(f"已加载配置文件: {config_path}")
|
|
|
|
|
|
return config
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"加载配置文件失败: {e}")
|
|
|
|
|
|
return {}
|
|
|
|
|
|
|
2025-06-07 10:45:09 +08:00
|
|
|
|
def connect_signals(self):
|
2025-06-13 09:01:41 +08:00
|
|
|
|
"""连接信号槽"""
|
|
|
|
|
|
# 连接微丝产线表格单元格变更信号槽
|
|
|
|
|
|
self.process_table.cellChanged.connect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
|
2025-06-07 10:45:09 +08:00
|
|
|
|
# 连接菜单动作
|
|
|
|
|
|
self.main_action.triggered.connect(self.show_main_page)
|
|
|
|
|
|
self.settings_action.triggered.connect(self.show_settings_page)
|
|
|
|
|
|
|
2025-06-07 16:44:27 +08:00
|
|
|
|
# 工程号输入框回车事件
|
|
|
|
|
|
self.order_edit.returnPressed.connect(self.handle_order_enter)
|
|
|
|
|
|
|
2025-06-09 01:16:49 +08:00
|
|
|
|
# 托盘号输入框回车和切换事件,触发未加载数据查询
|
|
|
|
|
|
# QComboBox没有returnPressed信号,只有currentTextChanged和activated信号
|
2025-06-10 13:37:43 +08:00
|
|
|
|
self.tray_edit.currentTextChanged.connect(self.load_finished_inspection_data)
|
|
|
|
|
|
self.tray_edit.activated.connect(self.load_finished_inspection_data) # 当用户选择一项时触发
|
2025-06-09 01:16:49 +08:00
|
|
|
|
|
2025-06-07 10:45:09 +08:00
|
|
|
|
# 连接按钮事件
|
2025-06-13 15:57:26 +08:00
|
|
|
|
self.input_button.clicked.connect(self.handle_input)
|
|
|
|
|
|
self.output_button.clicked.connect(self.handle_output)
|
2025-06-07 10:45:09 +08:00
|
|
|
|
self.start_button.clicked.connect(self.handle_start)
|
|
|
|
|
|
self.stop_button.clicked.connect(self.handle_stop)
|
|
|
|
|
|
|
2025-06-10 13:37:43 +08:00
|
|
|
|
# 设置表格上下文菜单
|
|
|
|
|
|
self.process_table.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
|
|
|
|
self.process_table.customContextMenuRequested.connect(self.show_table_context_menu)
|
|
|
|
|
|
|
2025-06-07 14:10:32 +08:00
|
|
|
|
# 只有在相机启用时连接相机信号
|
|
|
|
|
|
if self.camera_enabled and hasattr(self, 'camera_display'):
|
|
|
|
|
|
self.camera_display.signal_camera_status.connect(self.handle_camera_status)
|
2025-06-10 13:37:43 +08:00
|
|
|
|
|
2025-06-11 17:00:05 +08:00
|
|
|
|
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
2025-06-07 16:44:27 +08:00
|
|
|
|
def update_inspection_columns(self):
|
|
|
|
|
|
"""更新检验列配置 - 使用检验配置管理器获取启用的列数和标题"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 获取已启用的检验配置
|
|
|
|
|
|
enabled_configs = self.inspection_manager.get_enabled_configs()
|
|
|
|
|
|
|
|
|
|
|
|
# 获取启用的列数
|
|
|
|
|
|
column_count = len(enabled_configs)
|
|
|
|
|
|
if column_count == 0:
|
|
|
|
|
|
# 如果没有启用的列,至少显示一列
|
|
|
|
|
|
column_count = 1
|
|
|
|
|
|
headers = ["检验项"]
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 如果有启用的列,使用配置的标题
|
|
|
|
|
|
headers = [config['display_name'] for config in enabled_configs]
|
|
|
|
|
|
|
|
|
|
|
|
# 设置检验列
|
|
|
|
|
|
self.set_inspection_columns(column_count, headers)
|
|
|
|
|
|
logging.info(f"已更新检验列配置:{column_count}列, 标题: {headers}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"更新检验列配置失败: {str(e)}")
|
|
|
|
|
|
# 如果更新失败,使用默认配置
|
|
|
|
|
|
self.set_inspection_columns(1, ["检验项"])
|
|
|
|
|
|
|
2025-06-07 10:45:09 +08:00
|
|
|
|
def show_main_page(self):
|
|
|
|
|
|
self.stacked_widget.setCurrentWidget(self.central_widget)
|
|
|
|
|
|
|
2025-06-07 16:44:27 +08:00
|
|
|
|
# 更新检验列配置
|
|
|
|
|
|
self.update_inspection_columns()
|
|
|
|
|
|
|
2025-06-08 01:24:27 +08:00
|
|
|
|
# 加载未完成的检验数据
|
2025-06-10 13:37:43 +08:00
|
|
|
|
self.load_finished_inspection_data()
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
2025-06-07 14:10:32 +08:00
|
|
|
|
# 只有在相机启用时处理相机显示
|
|
|
|
|
|
if self.camera_enabled and hasattr(self, 'camera_display'):
|
|
|
|
|
|
# 如果相机已连接,直接开始显示相机画面
|
|
|
|
|
|
if self.camera_display.camera_manager.isOpen:
|
|
|
|
|
|
if not self.camera_display.camera_manager.isGrabbing:
|
|
|
|
|
|
self.camera_display.start_display()
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
|
|
|
|
|
logging.info("显示主页面")
|
|
|
|
|
|
|
|
|
|
|
|
def show_settings_page(self):
|
2025-06-07 16:44:27 +08:00
|
|
|
|
"""显示设置页面"""
|
2025-06-12 17:29:35 +08:00
|
|
|
|
# 创建设置窗口
|
|
|
|
|
|
if not hasattr(self, 'settings_window'):
|
|
|
|
|
|
from widgets.settings_window import SettingsWindow
|
|
|
|
|
|
self.settings_window = SettingsWindow(self)
|
|
|
|
|
|
# 连接设置改变信号
|
|
|
|
|
|
self.settings_window.settings_changed.connect(self.on_settings_changed)
|
|
|
|
|
|
|
|
|
|
|
|
# 显示设置窗口
|
|
|
|
|
|
self.settings_window.show()
|
|
|
|
|
|
logging.info("显示设置窗口")
|
|
|
|
|
|
|
|
|
|
|
|
def on_settings_changed(self):
|
|
|
|
|
|
"""设置改变时触发"""
|
|
|
|
|
|
# 重新加载配置
|
|
|
|
|
|
from utils.config_loader import ConfigLoader
|
|
|
|
|
|
config_loader = ConfigLoader.get_instance()
|
|
|
|
|
|
config_loader.load_config()
|
|
|
|
|
|
self.config = self.load_config() # 重新加载配置到 self.config
|
|
|
|
|
|
|
|
|
|
|
|
# 更新串口管理器配置
|
|
|
|
|
|
self.serial_manager.reload_config()
|
|
|
|
|
|
|
|
|
|
|
|
logging.info("设置已更新,重新加载配置")
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
|
|
|
|
|
def handle_input(self):
|
|
|
|
|
|
"""处理上料按钮点击事件"""
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 创建对话框
|
|
|
|
|
|
dialog = QDialog(self)
|
|
|
|
|
|
dialog.setWindowTitle("上料操作")
|
2025-06-11 08:39:18 +08:00
|
|
|
|
dialog.setFixedSize(300, 200)
|
2025-06-10 16:13:22 +08:00
|
|
|
|
|
|
|
|
|
|
# 对话框布局
|
|
|
|
|
|
layout = QVBoxLayout(dialog)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加提示信息
|
2025-06-11 15:21:26 +08:00
|
|
|
|
info_label = QLabel("请选择拆垛层数:")
|
2025-06-10 16:13:22 +08:00
|
|
|
|
info_label.setFont(self.normal_font)
|
|
|
|
|
|
layout.addWidget(info_label)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加托盘类型选择
|
|
|
|
|
|
pallet_combo = QComboBox()
|
|
|
|
|
|
pallet_combo.setFont(self.normal_font)
|
|
|
|
|
|
# 复制当前托盘类型选择器的内容
|
|
|
|
|
|
for i in range(self.input_pallet_type_combo.count()):
|
|
|
|
|
|
pallet_combo.addItem(self.input_pallet_type_combo.itemText(i))
|
|
|
|
|
|
layout.addWidget(pallet_combo)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加按钮
|
|
|
|
|
|
button_layout = QHBoxLayout()
|
|
|
|
|
|
confirm_button = QPushButton("确认")
|
|
|
|
|
|
confirm_button.setFont(self.normal_font)
|
|
|
|
|
|
confirm_button.setStyleSheet("background-color: #e3f2fd; border: 1px solid #2196f3; padding: 8px 16px; font-weight: bold; border-radius: 4px;")
|
|
|
|
|
|
|
|
|
|
|
|
cancel_button = QPushButton("取消")
|
|
|
|
|
|
cancel_button.setFont(self.normal_font)
|
|
|
|
|
|
cancel_button.setStyleSheet("padding: 8px 16px; font-weight: bold; border-radius: 4px;")
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
2025-06-10 16:13:22 +08:00
|
|
|
|
button_layout.addStretch()
|
|
|
|
|
|
button_layout.addWidget(confirm_button)
|
|
|
|
|
|
button_layout.addWidget(cancel_button)
|
|
|
|
|
|
layout.addLayout(button_layout)
|
|
|
|
|
|
|
|
|
|
|
|
# 连接按钮信号
|
|
|
|
|
|
confirm_button.clicked.connect(dialog.accept)
|
|
|
|
|
|
cancel_button.clicked.connect(dialog.reject)
|
|
|
|
|
|
|
|
|
|
|
|
# 显示对话框
|
|
|
|
|
|
result = dialog.exec()
|
|
|
|
|
|
|
|
|
|
|
|
# 如果用户确认,则执行上料操作
|
|
|
|
|
|
if result == QDialog.Accepted:
|
|
|
|
|
|
selected_type = pallet_combo.currentText()
|
|
|
|
|
|
|
|
|
|
|
|
# 执行Modbus操作
|
|
|
|
|
|
modbus = ModbusUtils()
|
|
|
|
|
|
client = modbus.get_client()
|
|
|
|
|
|
try:
|
2025-06-11 08:39:18 +08:00
|
|
|
|
# 上料 D2 寄存器写入 1 ,D0 寄存器写入托盘类型
|
|
|
|
|
|
if modbus.write_register_until_success(client, 2, 1) and modbus.write_register_until_success(client, 0, selected_type):
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 创建状态标签并显示在右上角
|
2025-06-11 08:39:18 +08:00
|
|
|
|
self.show_operation_status("上料托盘", "input", selected_type)
|
2025-06-10 16:13:22 +08:00
|
|
|
|
else:
|
|
|
|
|
|
QMessageBox.information(self, "操作提示", "上料失败")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"上料操作失败: {str(e)}")
|
|
|
|
|
|
QMessageBox.critical(self, "错误", f"上料操作失败: {str(e)}")
|
|
|
|
|
|
finally:
|
|
|
|
|
|
modbus.close_client(client)
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
|
|
|
|
|
def handle_output(self):
|
|
|
|
|
|
"""处理下料按钮点击事件"""
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 创建对话框
|
|
|
|
|
|
dialog = QDialog(self)
|
|
|
|
|
|
dialog.setWindowTitle("下料操作")
|
2025-06-11 08:39:18 +08:00
|
|
|
|
dialog.setFixedSize(300, 200)
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 对话框布局
|
|
|
|
|
|
layout = QVBoxLayout(dialog)
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 添加提示信息
|
|
|
|
|
|
info_label = QLabel("请选择下料托盘类型:")
|
|
|
|
|
|
info_label.setFont(self.normal_font)
|
|
|
|
|
|
layout.addWidget(info_label)
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 添加托盘类型选择
|
|
|
|
|
|
pallet_combo = QComboBox()
|
|
|
|
|
|
pallet_combo.setFont(self.normal_font)
|
|
|
|
|
|
# 复制当前托盘类型选择器的内容
|
|
|
|
|
|
for i in range(self.output_pallet_type_combo.count()):
|
|
|
|
|
|
pallet_combo.addItem(self.output_pallet_type_combo.itemText(i))
|
|
|
|
|
|
layout.addWidget(pallet_combo)
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 添加按钮
|
|
|
|
|
|
button_layout = QHBoxLayout()
|
|
|
|
|
|
confirm_button = QPushButton("确认")
|
|
|
|
|
|
confirm_button.setFont(self.normal_font)
|
|
|
|
|
|
confirm_button.setStyleSheet("background-color: #fff8e1; border: 1px solid #ffc107; padding: 8px 16px; font-weight: bold; border-radius: 4px;")
|
|
|
|
|
|
|
|
|
|
|
|
cancel_button = QPushButton("取消")
|
|
|
|
|
|
cancel_button.setFont(self.normal_font)
|
|
|
|
|
|
cancel_button.setStyleSheet("padding: 8px 16px; font-weight: bold; border-radius: 4px;")
|
|
|
|
|
|
|
|
|
|
|
|
button_layout.addStretch()
|
|
|
|
|
|
button_layout.addWidget(confirm_button)
|
|
|
|
|
|
button_layout.addWidget(cancel_button)
|
|
|
|
|
|
layout.addLayout(button_layout)
|
|
|
|
|
|
|
|
|
|
|
|
# 连接按钮信号
|
|
|
|
|
|
confirm_button.clicked.connect(dialog.accept)
|
|
|
|
|
|
cancel_button.clicked.connect(dialog.reject)
|
|
|
|
|
|
|
|
|
|
|
|
# 显示对话框
|
|
|
|
|
|
result = dialog.exec()
|
2025-06-11 08:39:18 +08:00
|
|
|
|
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 如果用户确认,则执行下料操作
|
|
|
|
|
|
if result == QDialog.Accepted:
|
|
|
|
|
|
selected_type = pallet_combo.currentText()
|
2025-06-11 08:39:18 +08:00
|
|
|
|
# 获取托盘的排序,该顺序影响着下料寄存器的写入值 切记,需要和 PLC 确认沟通完成后才能修改排序值
|
|
|
|
|
|
pallets_dict = self.pallet_type_manager.get_pallet_type_by_type(selected_type)
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 执行Modbus操作
|
|
|
|
|
|
modbus = ModbusUtils()
|
|
|
|
|
|
client = modbus.get_client()
|
|
|
|
|
|
try:
|
2025-06-11 08:39:18 +08:00
|
|
|
|
#TODO: 下料 D3 寄存器写入 1 D1 寄存器写入托盘类型
|
|
|
|
|
|
if modbus.write_register_until_success(client, 3, 1) and modbus.write_register_until_success(client, 1, pallets_dict.get(selected_type)):
|
2025-06-10 16:13:22 +08:00
|
|
|
|
# 创建状态标签并显示在右上角
|
2025-06-11 08:39:18 +08:00
|
|
|
|
self.show_operation_status("下料托盘", "output", selected_type)
|
2025-06-10 16:13:22 +08:00
|
|
|
|
else:
|
|
|
|
|
|
QMessageBox.information(self, "操作提示", "下料失败")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"下料操作失败: {str(e)}")
|
|
|
|
|
|
QMessageBox.critical(self, "错误", f"下料操作失败: {str(e)}")
|
|
|
|
|
|
finally:
|
|
|
|
|
|
modbus.close_client(client)
|
|
|
|
|
|
|
|
|
|
|
|
def handle_start(self):
|
2025-06-11 13:19:57 +08:00
|
|
|
|
"""
|
2025-06-11 15:21:26 +08:00
|
|
|
|
处理开始按钮点击事件,并启动 modbus 监控
|
2025-06-11 13:19:57 +08:00
|
|
|
|
"""
|
|
|
|
|
|
# 创建对话框
|
|
|
|
|
|
dialog = QDialog(self)
|
|
|
|
|
|
dialog.setWindowTitle("上料操作")
|
|
|
|
|
|
dialog.setFixedSize(300, 200)
|
|
|
|
|
|
|
|
|
|
|
|
# 对话框布局
|
|
|
|
|
|
layout = QVBoxLayout(dialog)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加提示信息
|
2025-06-11 15:21:26 +08:00
|
|
|
|
info_label = QLabel("请选择拆垛层数:")
|
2025-06-11 13:19:57 +08:00
|
|
|
|
info_label.setFont(self.normal_font)
|
|
|
|
|
|
layout.addWidget(info_label)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加托盘类型选择
|
|
|
|
|
|
pallet_combo = QComboBox()
|
|
|
|
|
|
pallet_combo.setFont(self.normal_font)
|
|
|
|
|
|
# 复制当前托盘类型选择器的内容
|
|
|
|
|
|
for i in range(1,4):
|
|
|
|
|
|
pallet_combo.addItem(str(i))
|
|
|
|
|
|
layout.addWidget(pallet_combo)
|
|
|
|
|
|
|
2025-06-13 09:01:41 +08:00
|
|
|
|
# 添加提示信息
|
|
|
|
|
|
info_label = QLabel("请选择码垛层数:")
|
|
|
|
|
|
info_label.setFont(self.normal_font)
|
|
|
|
|
|
layout.addWidget(info_label)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加托盘类型选择
|
|
|
|
|
|
pallet_combo2 = QComboBox()
|
|
|
|
|
|
pallet_combo2.setFont(self.normal_font)
|
|
|
|
|
|
# 复制当前托盘类型选择器的内容
|
|
|
|
|
|
for i in range(1,4):
|
|
|
|
|
|
pallet_combo2.addItem(str(i))
|
|
|
|
|
|
layout.addWidget(pallet_combo2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-06-11 13:19:57 +08:00
|
|
|
|
# 添加按钮
|
|
|
|
|
|
button_layout = QHBoxLayout()
|
|
|
|
|
|
confirm_button = QPushButton("确认")
|
|
|
|
|
|
confirm_button.setFont(self.normal_font)
|
|
|
|
|
|
confirm_button.setStyleSheet("background-color: #e3f2fd; border: 1px solid #2196f3; padding: 8px 16px; font-weight: bold; border-radius: 4px;")
|
|
|
|
|
|
|
|
|
|
|
|
cancel_button = QPushButton("取消")
|
|
|
|
|
|
cancel_button.setFont(self.normal_font)
|
|
|
|
|
|
cancel_button.setStyleSheet("padding: 8px 16px; font-weight: bold; border-radius: 4px;")
|
|
|
|
|
|
|
|
|
|
|
|
button_layout.addStretch()
|
|
|
|
|
|
button_layout.addWidget(confirm_button)
|
|
|
|
|
|
button_layout.addWidget(cancel_button)
|
|
|
|
|
|
layout.addLayout(button_layout)
|
|
|
|
|
|
|
|
|
|
|
|
# 连接按钮信号
|
|
|
|
|
|
confirm_button.clicked.connect(dialog.accept)
|
|
|
|
|
|
cancel_button.clicked.connect(dialog.reject)
|
|
|
|
|
|
|
|
|
|
|
|
# 显示对话框
|
|
|
|
|
|
result = dialog.exec()
|
|
|
|
|
|
|
|
|
|
|
|
# 如果用户确认,则执行上料操作
|
|
|
|
|
|
if result == QDialog.Accepted:
|
|
|
|
|
|
stow_num = pallet_combo.currentText()
|
2025-06-13 09:01:41 +08:00
|
|
|
|
stow_num2 = pallet_combo2.currentText()
|
2025-06-11 13:19:57 +08:00
|
|
|
|
|
|
|
|
|
|
# 获取托盘号对应的托盘类型
|
|
|
|
|
|
tray_id = self.tray_edit.currentText()
|
|
|
|
|
|
pallet_type = self.pallet_type_manager.get_pallet_type_by_pallet_id(tray_id)
|
2025-06-11 17:00:05 +08:00
|
|
|
|
# 初始化托盘号对应的序号
|
|
|
|
|
|
if tray_id not in self.init_seq:
|
|
|
|
|
|
self.init_seq[tray_id] = 1
|
|
|
|
|
|
|
2025-06-11 13:19:57 +08:00
|
|
|
|
if not pallet_type:
|
|
|
|
|
|
QMessageBox.warning(self, "错误", "未查到对应下料托盘类型")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 执行Modbus操作
|
|
|
|
|
|
modbus = ModbusUtils()
|
|
|
|
|
|
client = modbus.get_client()
|
|
|
|
|
|
try:
|
2025-06-11 15:21:26 +08:00
|
|
|
|
# 启动Modbus监控
|
|
|
|
|
|
self.setup_modbus_monitor()
|
|
|
|
|
|
|
2025-06-12 17:29:35 +08:00
|
|
|
|
# 启动串口监听
|
|
|
|
|
|
self.serial_manager.auto_open_configured_ports()
|
|
|
|
|
|
|
2025-06-13 09:24:58 +08:00
|
|
|
|
# 显式启动键盘监听器
|
|
|
|
|
|
self.serial_manager.start_keyboard_listener()
|
|
|
|
|
|
logging.info("已在开始操作时启动键盘监听器")
|
|
|
|
|
|
|
2025-06-11 13:19:57 +08:00
|
|
|
|
success0 = modbus.write_register_until_success(client, 0, int(stow_num))
|
|
|
|
|
|
success1 = modbus.write_register_until_success(client, 1, int(pallet_type))
|
|
|
|
|
|
success2 = modbus.write_register_until_success(client, 2, 1)
|
|
|
|
|
|
success3 = modbus.write_register_until_success(client, 3, 1)
|
2025-06-13 09:01:41 +08:00
|
|
|
|
success4 = modbus.write_register_until_success(client, 4, int(stow_num2))
|
2025-06-11 13:19:57 +08:00
|
|
|
|
# 上料 D2 寄存器写入 1 ,D0 寄存器写入托盘类型
|
2025-06-13 09:01:41 +08:00
|
|
|
|
if success0 and success2 and success1 and success3 and success4:
|
2025-06-11 13:19:57 +08:00
|
|
|
|
# 创建状态标签并显示在右上角
|
2025-06-13 09:01:41 +08:00
|
|
|
|
self.show_operation_status("拆垛层数", "input", stow_num)
|
2025-06-11 13:19:57 +08:00
|
|
|
|
else:
|
|
|
|
|
|
QMessageBox.information(self, "操作提示", "上料失败")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"上料操作失败: {str(e)}")
|
|
|
|
|
|
QMessageBox.critical(self, "错误", f"上料操作失败: {str(e)}")
|
|
|
|
|
|
finally:
|
|
|
|
|
|
modbus.close_client(client)
|
|
|
|
|
|
|
2025-06-12 17:29:35 +08:00
|
|
|
|
|
2025-06-10 16:13:22 +08:00
|
|
|
|
def handle_stop(self):
|
2025-06-11 15:21:26 +08:00
|
|
|
|
"""处理停止按钮点击事件,并关闭 modbus 监控"""
|
2025-06-10 16:13:22 +08:00
|
|
|
|
modbus = ModbusUtils()
|
|
|
|
|
|
client = modbus.get_client()
|
|
|
|
|
|
try:
|
2025-06-11 13:19:57 +08:00
|
|
|
|
success2 = modbus.write_register_until_success(client, 2, 0)
|
|
|
|
|
|
success3 = modbus.write_register_until_success(client, 3, 0)
|
|
|
|
|
|
# 停止 D1 寄存器2、3写入 0
|
|
|
|
|
|
if not success2 and not success3:
|
2025-06-10 16:13:22 +08:00
|
|
|
|
QMessageBox.information(self, "操作提示", "停止失败")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"停止操作失败: {str(e)}")
|
|
|
|
|
|
QMessageBox.critical(self, "错误", f"停止操作失败: {str(e)}")
|
|
|
|
|
|
finally:
|
|
|
|
|
|
modbus.close_client(client)
|
2025-06-11 15:21:26 +08:00
|
|
|
|
# 停止Modbus监控
|
|
|
|
|
|
if hasattr(self, 'modbus_monitor'):
|
|
|
|
|
|
logging.info("停止Modbus监控")
|
|
|
|
|
|
self.modbus_monitor.stop()
|
2025-06-12 17:29:35 +08:00
|
|
|
|
# 停止串口监听
|
|
|
|
|
|
self.serial_manager.stop_keyboard_listener()
|
|
|
|
|
|
self.serial_manager.close_all_ports()
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
|
|
|
|
|
def handle_camera_status(self, is_connected, message):
|
|
|
|
|
|
"""处理相机状态变化"""
|
|
|
|
|
|
if is_connected:
|
|
|
|
|
|
logging.info("相机已连接并显示")
|
|
|
|
|
|
else:
|
|
|
|
|
|
logging.warning(f"相机显示问题: {message}")
|
|
|
|
|
|
|
|
|
|
|
|
def handle_camera_connection(self, is_connected, message):
|
|
|
|
|
|
"""处理相机连接状态变化"""
|
|
|
|
|
|
if is_connected:
|
|
|
|
|
|
logging.info("相机已连接")
|
|
|
|
|
|
# 如果当前在主页面,直接开始显示相机画面
|
|
|
|
|
|
if self.stacked_widget.currentWidget() == self.central_widget:
|
|
|
|
|
|
self.camera_display.start_display()
|
|
|
|
|
|
else:
|
|
|
|
|
|
if message:
|
|
|
|
|
|
logging.warning(f"相机连接失败: {message}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
logging.info("相机已断开")
|
|
|
|
|
|
# 如果相机断开,确保停止显示
|
|
|
|
|
|
self.camera_display.stop_display()
|
|
|
|
|
|
|
|
|
|
|
|
def handle_camera_params_changed(self, exposure_time, gain, frame_rate):
|
|
|
|
|
|
"""处理相机参数变化"""
|
|
|
|
|
|
logging.info(f"相机参数已更新: 曝光={exposure_time:.1f}μs, 增益={gain:.1f}dB, 帧率={frame_rate:.1f}fps")
|
|
|
|
|
|
# 这里可以添加对相机参数变化的处理逻辑
|
|
|
|
|
|
|
|
|
|
|
|
def handle_camera_error(self, error_msg):
|
|
|
|
|
|
"""处理相机错误"""
|
|
|
|
|
|
logging.error(f"相机错误: {error_msg}")
|
|
|
|
|
|
QMessageBox.warning(self, "相机错误", error_msg)
|
|
|
|
|
|
|
|
|
|
|
|
def closeEvent(self, event):
|
|
|
|
|
|
"""窗口关闭事件"""
|
2025-06-11 15:21:26 +08:00
|
|
|
|
# 停止Modbus监控
|
|
|
|
|
|
if hasattr(self, 'modbus_monitor'):
|
|
|
|
|
|
logging.info("停止Modbus监控")
|
|
|
|
|
|
self.modbus_monitor.stop()
|
|
|
|
|
|
|
2025-06-07 14:10:32 +08:00
|
|
|
|
# 只有在相机启用时处理相机关闭
|
|
|
|
|
|
if self.camera_enabled and hasattr(self, 'camera_display'):
|
|
|
|
|
|
# 停止相机显示
|
|
|
|
|
|
self.camera_display.stop_display()
|
2025-06-07 10:45:09 +08:00
|
|
|
|
|
2025-06-12 17:29:35 +08:00
|
|
|
|
# 停止串口监听
|
|
|
|
|
|
self.serial_manager.stop_keyboard_listener()
|
|
|
|
|
|
self.serial_manager.close_all_ports()
|
|
|
|
|
|
|
2025-06-07 10:45:09 +08:00
|
|
|
|
# 接受关闭事件
|
2025-06-11 15:21:26 +08:00
|
|
|
|
event.accept()
|
2025-06-12 17:29:35 +08:00
|
|
|
|
|
2025-06-07 16:44:27 +08:00
|
|
|
|
|
|
|
|
|
|
def handle_order_enter(self):
|
|
|
|
|
|
"""处理工程号输入框按下回车事件"""
|
|
|
|
|
|
logging.info("工程号输入框按下回车事件")
|
|
|
|
|
|
# 获取当前输入的工程号
|
|
|
|
|
|
order_text = self.order_edit.text().strip()
|
|
|
|
|
|
|
|
|
|
|
|
if order_text:
|
|
|
|
|
|
logging.info(f"输入的工程号: {order_text}")
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
# 在微丝产线表格中添加一条新记录
|
|
|
|
|
|
self.add_new_inspection_row(order_text)
|
|
|
|
|
|
|
2025-06-07 16:44:27 +08:00
|
|
|
|
else:
|
|
|
|
|
|
logging.warning("工程号为空")
|
|
|
|
|
|
QMessageBox.warning(self, "输入提示", "请输入有效的工程号")
|
|
|
|
|
|
|
|
|
|
|
|
# 处理完后可以清除焦点,让输入框失去焦点
|
2025-06-08 01:24:27 +08:00
|
|
|
|
self.central_widget.setFocus()
|
|
|
|
|
|
|
|
|
|
|
|
def add_new_inspection_row(self, order_id):
|
2025-06-11 15:21:26 +08:00
|
|
|
|
"""在微丝产线表格中添加一条新记录,添加到表格末尾
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
order_id: 工程号
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 获取启用的检验配置
|
|
|
|
|
|
enabled_configs = self.inspection_manager.get_enabled_configs()
|
|
|
|
|
|
|
2025-06-10 13:37:43 +08:00
|
|
|
|
# 断开单元格变更信号,避免加载过程中触发保存
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.process_table.cellChanged.disconnect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
2025-06-11 15:21:26 +08:00
|
|
|
|
# 计算新行的行索引(添加到末尾)
|
|
|
|
|
|
data_start_row = self.process_table.rowCount()
|
|
|
|
|
|
|
|
|
|
|
|
# 在末尾添加新行
|
2025-06-08 01:24:27 +08:00
|
|
|
|
self.process_table.insertRow(data_start_row)
|
|
|
|
|
|
|
2025-06-11 15:21:26 +08:00
|
|
|
|
# 计算新行的序号(最后一个序号+1)
|
|
|
|
|
|
new_seq = 1 # 默认为1
|
|
|
|
|
|
if data_start_row > 2: # 如果有其他数据行
|
|
|
|
|
|
prev_seq_item = self.process_table.item(data_start_row - 1, 0)
|
|
|
|
|
|
if prev_seq_item:
|
2025-06-08 01:24:27 +08:00
|
|
|
|
try:
|
2025-06-11 15:21:26 +08:00
|
|
|
|
prev_seq = int(prev_seq_item.text())
|
|
|
|
|
|
new_seq = prev_seq + 1
|
2025-06-08 01:24:27 +08:00
|
|
|
|
except ValueError:
|
2025-06-11 15:21:26 +08:00
|
|
|
|
new_seq = data_start_row - 1 # 备选方案:使用行索引作为序号
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
# 添加工程号到表格的第二列
|
|
|
|
|
|
item = QTableWidgetItem(order_id)
|
|
|
|
|
|
item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.process_table.setItem(data_start_row, 1, item)
|
|
|
|
|
|
|
2025-06-11 15:21:26 +08:00
|
|
|
|
# 添加序号到表格的第一列
|
|
|
|
|
|
item = QTableWidgetItem(str(new_seq))
|
2025-06-08 01:24:27 +08:00
|
|
|
|
item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.process_table.setItem(data_start_row, 0, item)
|
|
|
|
|
|
|
|
|
|
|
|
# 检验列设置为可编辑状态
|
|
|
|
|
|
for i, config in enumerate(enabled_configs):
|
|
|
|
|
|
col_index = 2 + i # 检验列从第3列开始
|
|
|
|
|
|
|
|
|
|
|
|
# 创建空的可编辑单元格
|
|
|
|
|
|
item = QTableWidgetItem("")
|
|
|
|
|
|
item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
# 设置单元格属性以标识其关联的检验项
|
|
|
|
|
|
item.setData(Qt.UserRole, config.get('id'))
|
|
|
|
|
|
self.process_table.setItem(data_start_row, col_index, item)
|
|
|
|
|
|
|
|
|
|
|
|
# 包装列设置为可编辑状态
|
|
|
|
|
|
packaging_start_col = 2 + len(enabled_configs)
|
|
|
|
|
|
for i in range(2): # 贴标和称重
|
|
|
|
|
|
col_index = packaging_start_col + i
|
|
|
|
|
|
item = QTableWidgetItem("")
|
|
|
|
|
|
item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.process_table.setItem(data_start_row, col_index, item)
|
|
|
|
|
|
|
|
|
|
|
|
# 设置表格为可编辑状态
|
|
|
|
|
|
self.process_table.setEditTriggers(QTableWidget.DoubleClicked | QTableWidget.EditKeyPressed)
|
|
|
|
|
|
|
2025-06-10 13:37:43 +08:00
|
|
|
|
# 重新连接单元格内容变更信号
|
2025-06-08 01:24:27 +08:00
|
|
|
|
self.process_table.cellChanged.connect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
|
|
|
|
|
|
# 选中新添加的行
|
|
|
|
|
|
self.process_table.selectRow(data_start_row)
|
|
|
|
|
|
|
|
|
|
|
|
# 限制最大行数
|
|
|
|
|
|
self.limit_table_rows(10) # 最多保留10行数据
|
|
|
|
|
|
|
2025-06-10 13:37:43 +08:00
|
|
|
|
# 将工程号和托盘号保存到数据库,确保能够正确关联
|
|
|
|
|
|
from dao.inspection_dao import InspectionDAO
|
|
|
|
|
|
inspection_dao = InspectionDAO()
|
|
|
|
|
|
tray_id = self.tray_edit.currentText()
|
|
|
|
|
|
|
|
|
|
|
|
# 为每个检验位置创建一个空记录,确保工程号在数据库中存在
|
|
|
|
|
|
for config in enabled_configs:
|
|
|
|
|
|
data = [{
|
|
|
|
|
|
'position': config.get('position'),
|
|
|
|
|
|
'config_id': config.get('id'),
|
|
|
|
|
|
'value': '',
|
|
|
|
|
|
'status': '', # 默认设置为通过状态
|
|
|
|
|
|
'remark': '',
|
|
|
|
|
|
'tray_id': tray_id
|
|
|
|
|
|
}]
|
|
|
|
|
|
inspection_dao.save_inspection_data(order_id, data)
|
|
|
|
|
|
|
|
|
|
|
|
# 为贴标和称重也创建空记录
|
2025-06-14 00:34:40 +08:00
|
|
|
|
for position in [11, 12, 13]: # 11是贴标,12是毛重,13是净重
|
2025-06-10 13:37:43 +08:00
|
|
|
|
data = [{
|
|
|
|
|
|
'position': position,
|
|
|
|
|
|
'config_id': position,
|
|
|
|
|
|
'value': '',
|
|
|
|
|
|
'status': 'pass', # 默认设置为通过状态
|
|
|
|
|
|
'remark': '',
|
|
|
|
|
|
'tray_id': tray_id
|
|
|
|
|
|
}]
|
|
|
|
|
|
inspection_dao.save_inspection_data(order_id, data)
|
|
|
|
|
|
|
2025-06-11 15:21:26 +08:00
|
|
|
|
logging.info(f"已添加工程号 {order_id} 的新记录,显示在第{new_seq}条")
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"添加新记录失败: {str(e)}")
|
|
|
|
|
|
QMessageBox.warning(self, "添加失败", f"添加新记录失败: {str(e)}")
|
2025-06-10 13:37:43 +08:00
|
|
|
|
finally:
|
|
|
|
|
|
# 重新加载数据,确保UI显示正确
|
|
|
|
|
|
self.load_finished_inspection_data()
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
def limit_table_rows(self, max_rows):
|
|
|
|
|
|
"""限制表格最大行数
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
max_rows: 最大行数(不包括表头行)
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 计算数据总行数
|
|
|
|
|
|
data_rows = self.process_table.rowCount() - 2 # 减去表头行
|
|
|
|
|
|
|
|
|
|
|
|
# 如果超过最大行数,删除多余的行
|
|
|
|
|
|
if data_rows > max_rows:
|
|
|
|
|
|
# 要删除的行数
|
|
|
|
|
|
rows_to_remove = data_rows - max_rows
|
|
|
|
|
|
|
|
|
|
|
|
# 从最后一行开始删除
|
|
|
|
|
|
for i in range(rows_to_remove):
|
|
|
|
|
|
self.process_table.removeRow(self.process_table.rowCount() - 1)
|
|
|
|
|
|
|
|
|
|
|
|
logging.info(f"已限制表格最大行数为 {max_rows} 行数据,删除了 {rows_to_remove} 行")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"限制表格行数失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
def handle_inspection_cell_changed(self, row, column):
|
2025-06-11 15:21:26 +08:00
|
|
|
|
"""处理微丝包装单元格内容变更
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
row: 行索引
|
|
|
|
|
|
column: 列索引
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 只处理数据行的检验列变更
|
|
|
|
|
|
if row < 2: # 忽略表头行
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 忽略首尾两列(序号和工程号)
|
|
|
|
|
|
if column < 2:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 获取工程号
|
|
|
|
|
|
order_id_item = self.process_table.item(row, 1)
|
|
|
|
|
|
if not order_id_item:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
order_id = order_id_item.text().strip()
|
|
|
|
|
|
if not order_id:
|
|
|
|
|
|
return
|
2025-06-09 01:16:49 +08:00
|
|
|
|
|
|
|
|
|
|
# 获取托盘号
|
|
|
|
|
|
tray_id = self.tray_edit.currentText()
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
# 获取启用的检验配置
|
|
|
|
|
|
enabled_configs = self.inspection_manager.get_enabled_configs()
|
|
|
|
|
|
|
|
|
|
|
|
# 判断是否是检验列(非包装列)
|
|
|
|
|
|
packaging_start_col = 2 + len(enabled_configs)
|
2025-06-10 13:37:43 +08:00
|
|
|
|
|
|
|
|
|
|
# 获取单元格内容
|
|
|
|
|
|
cell_item = self.process_table.item(row, column)
|
|
|
|
|
|
if not cell_item:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
value = cell_item.text().strip()
|
|
|
|
|
|
|
|
|
|
|
|
# 默认设置为通过状态
|
|
|
|
|
|
status = 'pass'
|
|
|
|
|
|
|
|
|
|
|
|
# 记录当前正在处理的数据类型,用于日志输出
|
|
|
|
|
|
data_type = "检验"
|
|
|
|
|
|
|
2025-06-08 01:24:27 +08:00
|
|
|
|
if column >= 2 and column < packaging_start_col:
|
|
|
|
|
|
# 是检验列
|
|
|
|
|
|
config_index = column - 2
|
|
|
|
|
|
if config_index < len(enabled_configs):
|
|
|
|
|
|
config = enabled_configs[config_index]
|
2025-06-10 13:37:43 +08:00
|
|
|
|
data_type = config['display_name']
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
# 显示临时状态消息
|
2025-06-10 13:37:43 +08:00
|
|
|
|
self.statusBar().showMessage(f"正在保存检验数据: {data_type}={value}", 1000)
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
# 验证数据有效性
|
|
|
|
|
|
if self.validate_inspection_value(config, value):
|
|
|
|
|
|
# 设置单元格颜色为通过
|
|
|
|
|
|
cell_item.setBackground(QBrush(QColor("#c8e6c9"))) # 浅绿色
|
|
|
|
|
|
status = 'pass'
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 设置单元格颜色为警告
|
|
|
|
|
|
cell_item.setBackground(QBrush(QColor("#fff9c4"))) # 浅黄色
|
|
|
|
|
|
status = 'warning'
|
|
|
|
|
|
|
|
|
|
|
|
# 保存到数据库
|
2025-06-09 01:16:49 +08:00
|
|
|
|
self.save_inspection_data(order_id, tray_id, config['position'], config['id'], value, status)
|
2025-06-10 13:37:43 +08:00
|
|
|
|
|
|
|
|
|
|
# 判断是否是包装列
|
|
|
|
|
|
elif column == packaging_start_col:
|
|
|
|
|
|
# 贴标列
|
|
|
|
|
|
data_type = "贴标"
|
|
|
|
|
|
self.statusBar().showMessage(f"正在保存贴标数据: {value}", 1000)
|
|
|
|
|
|
# 设置单元格颜色为通过
|
|
|
|
|
|
cell_item.setBackground(QBrush(QColor("#c8e6c9"))) # 浅绿色
|
|
|
|
|
|
# 保存贴标数据,position和config_id都是11
|
|
|
|
|
|
self.save_inspection_data(order_id, tray_id, 11, 11, value, status)
|
|
|
|
|
|
|
|
|
|
|
|
elif column == packaging_start_col + 1:
|
2025-06-14 00:34:40 +08:00
|
|
|
|
# 毛重列
|
|
|
|
|
|
data_type = "毛重"
|
2025-06-10 13:37:43 +08:00
|
|
|
|
self.statusBar().showMessage(f"正在保存称重数据: {value}", 1000)
|
|
|
|
|
|
# 设置单元格颜色为通过
|
|
|
|
|
|
cell_item.setBackground(QBrush(QColor("#c8e6c9"))) # 浅绿色
|
2025-06-14 00:34:40 +08:00
|
|
|
|
# 保存毛重数据,position和config_id都是12
|
2025-06-10 13:37:43 +08:00
|
|
|
|
self.save_inspection_data(order_id, tray_id, 12, 12, value, status)
|
2025-06-14 00:34:40 +08:00
|
|
|
|
elif column == packaging_start_col + 2:
|
|
|
|
|
|
# 净重列
|
|
|
|
|
|
data_type = "净重"
|
|
|
|
|
|
self.statusBar().showMessage(f"正在保存净重数据: {value}", 1000)
|
|
|
|
|
|
# 设置单元格颜色为通过
|
|
|
|
|
|
cell_item.setBackground(QBrush(QColor("#c8e6c9"))) # 浅绿色
|
|
|
|
|
|
# 保存净重数据,position和config_id都是13
|
|
|
|
|
|
self.save_inspection_data(order_id, tray_id, 13, 13, value, status)
|
2025-06-10 13:37:43 +08:00
|
|
|
|
|
|
|
|
|
|
# 记录详细日志
|
|
|
|
|
|
logging.info(f"处理单元格变更: 行={row}, 列={column}, 类型={data_type}, 工程号={order_id}, 值={value}, 状态={status}")
|
|
|
|
|
|
|
2025-06-08 01:24:27 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"处理检验单元格变更失败: {str(e)}")
|
|
|
|
|
|
self.statusBar().showMessage(f"处理检验数据失败: {str(e)[:50]}...", 3000)
|
2025-06-10 13:37:43 +08:00
|
|
|
|
finally:
|
|
|
|
|
|
# 延迟一段时间后再触发查询,避免频繁刷新UI
|
|
|
|
|
|
QTimer.singleShot(1000, self.load_finished_inspection_data)
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
def validate_inspection_value(self, config, value):
|
|
|
|
|
|
"""验证检验值是否有效
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
config: 检验配置
|
|
|
|
|
|
value: 检验值
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
bool: 是否有效
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
2025-06-10 13:37:43 +08:00
|
|
|
|
# 特殊处理贴标和称重数据 - 这些数据默认都是有效的
|
|
|
|
|
|
if config.get('position') in [11, 12]: # 11是贴标,12是称重
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
2025-06-08 01:24:27 +08:00
|
|
|
|
# 检查值是否为空
|
|
|
|
|
|
if not value and config.get('required', False):
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
# 根据数据类型验证
|
|
|
|
|
|
data_type = config.get('data_type')
|
|
|
|
|
|
|
|
|
|
|
|
if data_type == 'number':
|
|
|
|
|
|
# 数值类型验证
|
|
|
|
|
|
try:
|
2025-06-10 13:37:43 +08:00
|
|
|
|
# 如果值为空且不是必填,则视为有效
|
|
|
|
|
|
if not value and not config.get('required', False):
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
2025-06-08 01:24:27 +08:00
|
|
|
|
num_value = float(value)
|
|
|
|
|
|
min_value = config.get('min_value')
|
|
|
|
|
|
max_value = config.get('max_value')
|
|
|
|
|
|
|
|
|
|
|
|
if min_value is not None and num_value < min_value:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
if max_value is not None and num_value > max_value:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
elif data_type == 'enum':
|
|
|
|
|
|
# 枚举类型验证
|
|
|
|
|
|
enum_values = config.get('enum_values')
|
|
|
|
|
|
if enum_values and isinstance(enum_values, list):
|
2025-06-10 13:37:43 +08:00
|
|
|
|
# 如果值为空且不是必填,则视为有效
|
|
|
|
|
|
if not value and not config.get('required', False):
|
|
|
|
|
|
return True
|
2025-06-08 01:24:27 +08:00
|
|
|
|
return value in enum_values
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
# 文本类型不做特殊验证
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"验证检验值失败: {str(e)}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
2025-06-09 01:16:49 +08:00
|
|
|
|
def save_inspection_data(self, order_id, tray_id, position, config_id, value, status):
|
2025-06-08 01:24:27 +08:00
|
|
|
|
"""保存检验数据到数据库
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
order_id: 工程号
|
|
|
|
|
|
position: 位置序号
|
|
|
|
|
|
config_id: 配置ID
|
|
|
|
|
|
value: 检验值
|
|
|
|
|
|
status: 状态
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
from dao.inspection_dao import InspectionDAO
|
|
|
|
|
|
inspection_dao = InspectionDAO()
|
2025-06-11 15:21:26 +08:00
|
|
|
|
modbus = ModbusUtils()
|
|
|
|
|
|
client = modbus.get_client()
|
2025-06-08 01:24:27 +08:00
|
|
|
|
# 记录保存前的详细日志
|
2025-06-09 01:16:49 +08:00
|
|
|
|
logging.info(f"正在保存检验数据: 工程号={order_id}, 托盘号={tray_id}, 位置={position}, 配置ID={config_id}, 值={value}, 状态={status}")
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
# 构建数据
|
|
|
|
|
|
data = [{
|
|
|
|
|
|
'position': position,
|
|
|
|
|
|
'config_id': config_id,
|
|
|
|
|
|
'value': value,
|
|
|
|
|
|
'status': status,
|
2025-06-09 01:16:49 +08:00
|
|
|
|
'remark': '',
|
|
|
|
|
|
'tray_id': tray_id
|
2025-06-08 01:24:27 +08:00
|
|
|
|
}]
|
|
|
|
|
|
|
|
|
|
|
|
# 保存到数据库
|
2025-06-11 15:21:26 +08:00
|
|
|
|
inspection_dao.save_inspection_data(order_id, data)
|
2025-06-11 17:00:05 +08:00
|
|
|
|
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"保存检验数据失败: {str(e)}")
|
|
|
|
|
|
# 显示错误消息
|
|
|
|
|
|
self.statusBar().showMessage(f"保存检验数据错误: {str(e)[:50]}...", 3000)
|
|
|
|
|
|
|
2025-06-10 13:37:43 +08:00
|
|
|
|
def load_finished_inspection_data(self):
|
|
|
|
|
|
"""加载未完成的检验数据并显示在表格中"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 使用InspectionDAO获取未完成的检验数据
|
|
|
|
|
|
from dao.inspection_dao import InspectionDAO
|
|
|
|
|
|
inspection_dao = InspectionDAO()
|
|
|
|
|
|
|
|
|
|
|
|
# 获取托盘号
|
|
|
|
|
|
tray_id = self.tray_edit.currentText()
|
|
|
|
|
|
|
|
|
|
|
|
# 使用get_inspection_data_unfinished获取未完成的数据
|
|
|
|
|
|
unfinished_data = inspection_dao.get_inspection_data_unfinished(tray_id)
|
|
|
|
|
|
|
|
|
|
|
|
if not unfinished_data:
|
|
|
|
|
|
logging.info("没有未完成的检验数据")
|
|
|
|
|
|
# 清空表格现有数据行,但保留表头
|
|
|
|
|
|
while self.process_table.rowCount() > 2:
|
|
|
|
|
|
self.process_table.removeRow(2)
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
logging.info(f"已加载未完成的检验数据,共 {len(unfinished_data)} 条记录")
|
|
|
|
|
|
|
|
|
|
|
|
# 获取启用的检验配置
|
|
|
|
|
|
enabled_configs = self.inspection_manager.get_enabled_configs()
|
|
|
|
|
|
|
|
|
|
|
|
# 按工程号分组
|
|
|
|
|
|
orders_data = {}
|
|
|
|
|
|
for data in unfinished_data:
|
|
|
|
|
|
order_id = data['order_id']
|
|
|
|
|
|
if order_id not in orders_data:
|
|
|
|
|
|
orders_data[order_id] = []
|
|
|
|
|
|
orders_data[order_id].append(data)
|
|
|
|
|
|
|
|
|
|
|
|
# 断开单元格变更信号,避免加载过程中触发保存
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.process_table.cellChanged.disconnect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# 清空表格现有数据行,但保留表头
|
|
|
|
|
|
while self.process_table.rowCount() > 2:
|
|
|
|
|
|
self.process_table.removeRow(2)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加数据到表格 - 从第3行开始添加数据
|
|
|
|
|
|
row_idx = 2
|
|
|
|
|
|
|
|
|
|
|
|
# 确保按工程号倒序排列,最新的工程号在最前面
|
2025-06-11 15:21:26 +08:00
|
|
|
|
sorted_order_ids = sorted(orders_data.keys(), reverse=False)
|
2025-06-10 13:37:43 +08:00
|
|
|
|
|
|
|
|
|
|
for order_id in sorted_order_ids:
|
|
|
|
|
|
items = orders_data[order_id]
|
|
|
|
|
|
|
|
|
|
|
|
# 添加新行
|
|
|
|
|
|
self.process_table.insertRow(row_idx)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加序号到第一列
|
|
|
|
|
|
seq_item = QTableWidgetItem(str(row_idx - 1))
|
|
|
|
|
|
seq_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.process_table.setItem(row_idx, 0, seq_item)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加工程号到第二列
|
|
|
|
|
|
order_item = QTableWidgetItem(order_id)
|
|
|
|
|
|
order_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.process_table.setItem(row_idx, 1, order_item)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加检验数据
|
|
|
|
|
|
for item in items:
|
|
|
|
|
|
position = item['position']
|
|
|
|
|
|
value = item['value'] if item['value'] else ""
|
|
|
|
|
|
status = item['status']
|
|
|
|
|
|
config_id = item['config_id']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 找到对应的列索引
|
|
|
|
|
|
col_index = None
|
|
|
|
|
|
for i, config in enumerate(enabled_configs):
|
|
|
|
|
|
if config.get('position') == position:
|
|
|
|
|
|
col_index = 2 + i # 检验列从第3列开始
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
if col_index is not None:
|
|
|
|
|
|
# 创建单元格并设置值
|
|
|
|
|
|
cell_item = QTableWidgetItem(str(value))
|
|
|
|
|
|
cell_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
# 存储配置ID,用于保存时确定是哪个检验项
|
2025-06-11 15:21:26 +08:00
|
|
|
|
cell_item.setData(Qt.UserRole, config_id)
|
2025-06-10 13:37:43 +08:00
|
|
|
|
# 设置单元格
|
|
|
|
|
|
self.process_table.setItem(row_idx, col_index, cell_item)
|
|
|
|
|
|
# 添加贴标(11)和称重数据(12)
|
|
|
|
|
|
if position == 11: # 贴标
|
|
|
|
|
|
# 贴标列索引 = 2(序号和工程号) + 检验列数
|
|
|
|
|
|
label_col = 2 + len(enabled_configs)
|
|
|
|
|
|
self.process_table.setItem(row_idx, label_col, QTableWidgetItem(str(value)))
|
|
|
|
|
|
elif position == 12: # 称重
|
|
|
|
|
|
# 称重列索引 = 2(序号和工程号) + 检验列数 + 1(贴标)
|
|
|
|
|
|
weight_col = 2 + len(enabled_configs) + 1
|
|
|
|
|
|
self.process_table.setItem(row_idx, weight_col, QTableWidgetItem(str(value)))
|
2025-06-14 00:34:40 +08:00
|
|
|
|
elif position == 13: # 净重
|
|
|
|
|
|
# 净重列索引 = 2(序号和工程号) + 检验列数 + 2(贴标和称重)
|
|
|
|
|
|
net_weight_col = 2 + len(enabled_configs) + 2
|
|
|
|
|
|
self.process_table.setItem(row_idx, net_weight_col, QTableWidgetItem(str(value)))
|
2025-06-10 13:37:43 +08:00
|
|
|
|
row_idx += 1
|
|
|
|
|
|
|
|
|
|
|
|
# 设置表格为可编辑状态
|
|
|
|
|
|
self.process_table.setEditTriggers(QTableWidget.DoubleClicked | QTableWidget.EditKeyPressed)
|
|
|
|
|
|
|
|
|
|
|
|
# 重新连接单元格变更信号
|
|
|
|
|
|
self.process_table.cellChanged.connect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"加载未完成的检验数据失败: {str(e)}")
|
|
|
|
|
|
QMessageBox.warning(self, "加载失败", f"加载未完成的检验数据失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
finally:
|
|
|
|
|
|
# 加载包装记录
|
|
|
|
|
|
self.show_pack_item()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_finished_record_to_package_record(self, order_id, tray_id):
|
|
|
|
|
|
"""加载已完成检验数据到包装记录
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
order_id: 工程号
|
|
|
|
|
|
tray_id: 托盘号
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
from dao.inspection_dao import InspectionDAO
|
|
|
|
|
|
inspection_dao = InspectionDAO()
|
|
|
|
|
|
|
|
|
|
|
|
# 获取该工程号的所有检验数据
|
|
|
|
|
|
inspection_data = inspection_dao.get_inspection_data_by_order(order_id, tray_id)
|
|
|
|
|
|
|
|
|
|
|
|
if not inspection_data:
|
|
|
|
|
|
logging.warning(f"未找到工程号 {order_id} 托盘号 {tray_id} 的检验数据")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 从检验数据中获取贴标和称重数据
|
|
|
|
|
|
label_value = ""
|
|
|
|
|
|
weight_value = ""
|
2025-06-14 00:34:40 +08:00
|
|
|
|
net_weight_value = ""
|
2025-06-10 13:37:43 +08:00
|
|
|
|
for item in inspection_data:
|
|
|
|
|
|
if item['position'] == 11: # 贴标
|
|
|
|
|
|
label_value = item['value']
|
|
|
|
|
|
elif item['position'] == 12: # 称重
|
|
|
|
|
|
weight_value = item['value']
|
2025-06-14 00:34:40 +08:00
|
|
|
|
elif item['position'] == 13: # 净重
|
|
|
|
|
|
net_weight_value = item['value']
|
2025-06-10 13:37:43 +08:00
|
|
|
|
|
|
|
|
|
|
# 只要贴标字段有值,就可以写入包装记录
|
|
|
|
|
|
if not label_value:
|
|
|
|
|
|
logging.warning(f"工程号 {order_id} 托盘号 {tray_id} 的贴标字段为空,不添加到包装记录")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 获取当前包装记录,检查是否已经存在相同的记录
|
|
|
|
|
|
existing_records = inspection_dao.get_package_record(tray_id)
|
|
|
|
|
|
for record in existing_records:
|
|
|
|
|
|
if record[0] == order_id and record[4] == label_value:
|
|
|
|
|
|
logging.info(f"工程号 {order_id} 托盘号 {tray_id} 贴标值 {label_value} 的包装记录已存在,不重复添加")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 获取当前时间作为完成时间
|
|
|
|
|
|
finish_time = datetime.now()
|
|
|
|
|
|
|
|
|
|
|
|
# 将数据写入到数据库表 inspection_pack_data
|
2025-06-14 00:34:40 +08:00
|
|
|
|
inspection_dao.save_package_record(order_id, tray_id, label_value, weight_value,net_weight_value, finish_time)
|
2025-06-10 13:37:43 +08:00
|
|
|
|
|
|
|
|
|
|
# 回显数据
|
|
|
|
|
|
self.show_pack_item()
|
|
|
|
|
|
|
|
|
|
|
|
logging.info(f"已将工程号 {order_id} 托盘号 {tray_id} 的检验数据添加到包装记录并回显")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"加载已完成检验数据到包装记录失败: {str(e)}")
|
|
|
|
|
|
QMessageBox.warning(self, "加载失败", f"加载已完成检验数据到包装记录失败: {str(e)}")
|
|
|
|
|
|
def show_pack_item(self):
|
|
|
|
|
|
from dao.inspection_dao import InspectionDAO
|
|
|
|
|
|
inspection_dao = InspectionDAO()
|
|
|
|
|
|
|
|
|
|
|
|
# 获取托盘号
|
|
|
|
|
|
tray_id = self.tray_edit.currentText()
|
|
|
|
|
|
# 读取已包装的记录信息,然后回显到 UI
|
|
|
|
|
|
package_record = inspection_dao.get_package_record(tray_id)
|
|
|
|
|
|
|
|
|
|
|
|
# 清空包装记录表格,只保留表头
|
|
|
|
|
|
while self.record_table.rowCount() > 1:
|
|
|
|
|
|
self.record_table.removeRow(1)
|
|
|
|
|
|
|
|
|
|
|
|
# 断开包装记录表的信号连接(如果有)
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.record_table.cellChanged.disconnect()
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# 添加所有包装记录到表格
|
|
|
|
|
|
for index, item in enumerate(package_record):
|
|
|
|
|
|
# 在包装记录表中添加新行
|
|
|
|
|
|
row_index = index + 1 # 从第2行开始(索引为1)
|
|
|
|
|
|
self.record_table.insertRow(row_index)
|
|
|
|
|
|
|
|
|
|
|
|
# 设置包装记录数据
|
|
|
|
|
|
# 序号 - 第1列
|
|
|
|
|
|
seq_item = QTableWidgetItem(str(row_index))
|
|
|
|
|
|
seq_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.record_table.setItem(row_index, 0, seq_item)
|
|
|
|
|
|
|
|
|
|
|
|
# 工程号 - 第2列
|
|
|
|
|
|
order_item = QTableWidgetItem(item[0])
|
|
|
|
|
|
order_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.record_table.setItem(row_index, 1, order_item)
|
|
|
|
|
|
|
|
|
|
|
|
# 材质 - 第3列
|
|
|
|
|
|
material_item = QTableWidgetItem(item[1])
|
|
|
|
|
|
material_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.record_table.setItem(row_index, 2, material_item)
|
|
|
|
|
|
|
|
|
|
|
|
# 规格 - 第4列
|
|
|
|
|
|
spec_item = QTableWidgetItem(item[2])
|
|
|
|
|
|
spec_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.record_table.setItem(row_index, 3, spec_item)
|
|
|
|
|
|
|
|
|
|
|
|
# 托盘号 - 第5列
|
|
|
|
|
|
tray_item = QTableWidgetItem(item[3])
|
|
|
|
|
|
tray_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.record_table.setItem(row_index, 4, tray_item)
|
|
|
|
|
|
|
|
|
|
|
|
# 轴包装号(贴标)- 第6列
|
|
|
|
|
|
label_item = QTableWidgetItem(item[4])
|
|
|
|
|
|
label_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.record_table.setItem(row_index, 5, label_item)
|
|
|
|
|
|
|
|
|
|
|
|
# 重量 - 第7列
|
|
|
|
|
|
weight_item = QTableWidgetItem(str(item[5]))
|
|
|
|
|
|
weight_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.record_table.setItem(row_index, 6, weight_item)
|
|
|
|
|
|
|
2025-06-14 00:34:40 +08:00
|
|
|
|
# 净重 - 第8列
|
|
|
|
|
|
net_weight_item = QTableWidgetItem(str(item[6]))
|
|
|
|
|
|
net_weight_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.record_table.setItem(row_index, 7, net_weight_item)
|
|
|
|
|
|
|
2025-06-10 13:37:43 +08:00
|
|
|
|
# 包装时间
|
2025-06-14 00:34:40 +08:00
|
|
|
|
pack_time = QTableWidgetItem(str(item[7]))
|
|
|
|
|
|
pack_time.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.record_table.setItem(row_index, 8, pack_time)
|
2025-06-10 13:37:43 +08:00
|
|
|
|
# 更新包装记录统计数据
|
|
|
|
|
|
self.update_package_statistics()
|
|
|
|
|
|
def update_package_statistics(self):
|
|
|
|
|
|
"""更新包装记录统计数据"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 获取包装记录表的行数(不包括表头)
|
|
|
|
|
|
package_count = self.record_table.rowCount() - 1
|
|
|
|
|
|
|
|
|
|
|
|
# 更新任务表格中的已完成数量
|
|
|
|
|
|
completed_item = QTableWidgetItem(str(package_count))
|
|
|
|
|
|
completed_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.task_table.setItem(2, 2, completed_item)
|
|
|
|
|
|
|
|
|
|
|
|
# 计算已完成公斤数(如果称重列有数值)
|
|
|
|
|
|
completed_kg = 0
|
|
|
|
|
|
for row in range(1, self.record_table.rowCount()):
|
|
|
|
|
|
weight_item = self.record_table.item(row, 6) # 称重列
|
|
|
|
|
|
if weight_item and weight_item.text():
|
|
|
|
|
|
try:
|
|
|
|
|
|
completed_kg += float(weight_item.text())
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# 更新任务表格中的已完成公斤
|
|
|
|
|
|
completed_kg_item = QTableWidgetItem(str(completed_kg))
|
|
|
|
|
|
completed_kg_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.task_table.setItem(2, 3, completed_kg_item)
|
|
|
|
|
|
|
|
|
|
|
|
logging.info(f"已更新包装记录统计数据: 完成数量={package_count}, 完成公斤={completed_kg}")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"更新包装记录统计数据失败: {str(e)}")
|
2025-06-11 17:00:05 +08:00
|
|
|
|
|
2025-06-08 01:24:27 +08:00
|
|
|
|
def show_table_context_menu(self, pos):
|
|
|
|
|
|
"""显示表格上下文菜单
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
pos: 鼠标位置
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 获取当前单元格
|
|
|
|
|
|
cell_index = self.process_table.indexAt(pos)
|
|
|
|
|
|
if not cell_index.isValid():
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
row = cell_index.row()
|
|
|
|
|
|
column = cell_index.column()
|
|
|
|
|
|
|
|
|
|
|
|
# 只对数据行和检验列显示上下文菜单
|
|
|
|
|
|
if row < 2: # 忽略表头行
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 获取工程号
|
|
|
|
|
|
order_id_item = self.process_table.item(row, 1)
|
|
|
|
|
|
if not order_id_item:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
order_id = order_id_item.text().strip()
|
|
|
|
|
|
if not order_id:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2025-06-09 01:16:49 +08:00
|
|
|
|
# 获取托盘号
|
|
|
|
|
|
tray_id = self.tray_edit.currentText()
|
|
|
|
|
|
|
2025-06-08 01:24:27 +08:00
|
|
|
|
# 创建上下文菜单
|
|
|
|
|
|
menu = QMenu(self)
|
|
|
|
|
|
|
|
|
|
|
|
# 获取启用的检验配置
|
|
|
|
|
|
enabled_configs = self.inspection_manager.get_enabled_configs()
|
|
|
|
|
|
|
|
|
|
|
|
# 判断是否是检验列(非包装列)
|
|
|
|
|
|
packaging_start_col = 2 + len(enabled_configs)
|
|
|
|
|
|
if column >= 2 and column < packaging_start_col:
|
|
|
|
|
|
# 是检验列
|
|
|
|
|
|
config_index = column - 2
|
|
|
|
|
|
if config_index < len(enabled_configs):
|
|
|
|
|
|
config = enabled_configs[config_index]
|
|
|
|
|
|
position = config.get('position')
|
|
|
|
|
|
|
|
|
|
|
|
# 添加查询数据库菜单项
|
|
|
|
|
|
check_action = menu.addAction("检查数据库记录")
|
2025-06-09 01:16:49 +08:00
|
|
|
|
check_action.triggered.connect(lambda: self.check_database_record(order_id, position, tray_id))
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
# 显示菜单
|
|
|
|
|
|
menu.exec_(self.process_table.viewport().mapToGlobal(pos))
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"显示表格上下文菜单失败: {str(e)}")
|
2025-06-10 13:37:43 +08:00
|
|
|
|
|
2025-06-09 01:16:49 +08:00
|
|
|
|
def check_database_record(self, order_id, position, tray_id):
|
2025-06-08 01:24:27 +08:00
|
|
|
|
"""检查数据库记录
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
order_id: 工程号
|
|
|
|
|
|
position: 位置序号
|
2025-06-10 13:37:43 +08:00
|
|
|
|
tray_id: 托盘号
|
2025-06-08 01:24:27 +08:00
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
from dao.inspection_dao import InspectionDAO
|
|
|
|
|
|
inspection_dao = InspectionDAO()
|
|
|
|
|
|
|
|
|
|
|
|
# 获取检验数据
|
2025-06-09 01:16:49 +08:00
|
|
|
|
inspection_data = inspection_dao.get_inspection_data_by_order(order_id, tray_id)
|
2025-06-08 01:24:27 +08:00
|
|
|
|
|
|
|
|
|
|
# 查找对应位置的数据
|
|
|
|
|
|
matching_data = None
|
|
|
|
|
|
for data in inspection_data:
|
|
|
|
|
|
if data.get('position') == position:
|
|
|
|
|
|
matching_data = data
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
# 显示结果
|
|
|
|
|
|
if matching_data:
|
|
|
|
|
|
value = matching_data.get('value')
|
|
|
|
|
|
status = matching_data.get('status')
|
|
|
|
|
|
|
|
|
|
|
|
message = f"数据库记录:\n\n"
|
|
|
|
|
|
message += f"工程号: {order_id}\n"
|
|
|
|
|
|
message += f"位置: {position}\n"
|
|
|
|
|
|
message += f"值: {value}\n"
|
|
|
|
|
|
message += f"状态: {status}\n"
|
|
|
|
|
|
|
|
|
|
|
|
QMessageBox.information(self, "数据库记录", message)
|
|
|
|
|
|
else:
|
|
|
|
|
|
QMessageBox.warning(self, "数据库记录", f"未找到工程号 {order_id} 位置 {position} 的数据")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"检查数据库记录失败: {str(e)}")
|
2025-06-10 16:13:22 +08:00
|
|
|
|
QMessageBox.warning(self, "查询失败", f"检查数据库记录失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
def show_operation_status(self, status, operation_type, pallet_type):
|
|
|
|
|
|
"""在右上角显示操作状态
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
status: 状态文本
|
|
|
|
|
|
operation_type: 操作类型 (input/output)
|
|
|
|
|
|
pallet_type: 托盘类型
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 确定要添加标签的容器
|
|
|
|
|
|
if operation_type == "input":
|
|
|
|
|
|
container = self.material_content
|
|
|
|
|
|
else:
|
|
|
|
|
|
container = self.output_content
|
|
|
|
|
|
|
|
|
|
|
|
# 如果已存在状态标签,则移除它
|
|
|
|
|
|
status_label_name = f"{operation_type}_status_label"
|
|
|
|
|
|
if hasattr(self, status_label_name):
|
|
|
|
|
|
old_label = getattr(self, status_label_name)
|
|
|
|
|
|
old_label.deleteLater()
|
|
|
|
|
|
|
|
|
|
|
|
# 创建新的状态标签
|
|
|
|
|
|
status_label = QLabel(f"{status}: {pallet_type}", container)
|
|
|
|
|
|
status_label.setFont(self.normal_font)
|
|
|
|
|
|
status_label.setStyleSheet("color: red; background-color: transparent;")
|
|
|
|
|
|
status_label.setAlignment(Qt.AlignRight | Qt.AlignTop)
|
|
|
|
|
|
|
|
|
|
|
|
# 使用绝对定位,放置在右上角
|
|
|
|
|
|
status_label.setGeometry(container.width() - 200, 5, 190, 30)
|
|
|
|
|
|
|
|
|
|
|
|
# 确保标签始终保持在顶层显示
|
|
|
|
|
|
status_label.raise_()
|
|
|
|
|
|
status_label.show()
|
|
|
|
|
|
|
|
|
|
|
|
# 保存标签引用
|
|
|
|
|
|
setattr(self, status_label_name, status_label)
|
|
|
|
|
|
|
|
|
|
|
|
# 保存原始的resize事件处理函数
|
|
|
|
|
|
if not hasattr(container, "_original_resize_event"):
|
|
|
|
|
|
container._original_resize_event = container.resizeEvent
|
|
|
|
|
|
|
|
|
|
|
|
# 添加窗口大小变化事件处理,确保标签位置随窗口调整
|
|
|
|
|
|
container.resizeEvent = lambda event: self.adjust_status_label_position(event, container, status_label)
|
|
|
|
|
|
|
|
|
|
|
|
def adjust_status_label_position(self, event, container, label):
|
|
|
|
|
|
"""调整状态标签位置,确保始终在右上角
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
event: 窗口大小变化事件
|
|
|
|
|
|
container: 标签所在的容器
|
|
|
|
|
|
label: 状态标签
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 更新标签位置,保持在右上角
|
|
|
|
|
|
label.setGeometry(container.width() - 200, 5, 190, 30)
|
|
|
|
|
|
|
|
|
|
|
|
# 调用原始的resizeEvent(如果有的话)
|
|
|
|
|
|
original_resize = getattr(container, "_original_resize_event", None)
|
|
|
|
|
|
if original_resize:
|
2025-06-11 15:21:26 +08:00
|
|
|
|
original_resize(event)
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== Modbus监控系统相关方法 ====================
|
|
|
|
|
|
|
|
|
|
|
|
def setup_modbus_monitor(self):
|
|
|
|
|
|
"""设置Modbus监控系统"""
|
|
|
|
|
|
# 获取Modbus监控器实例
|
|
|
|
|
|
self.modbus_monitor = get_modbus_monitor()
|
|
|
|
|
|
|
|
|
|
|
|
# 注册寄存器处理器
|
|
|
|
|
|
self._register_modbus_handlers()
|
|
|
|
|
|
|
|
|
|
|
|
# 连接信号槽
|
|
|
|
|
|
self._connect_modbus_signals()
|
|
|
|
|
|
|
|
|
|
|
|
# 启动监控
|
|
|
|
|
|
self.modbus_monitor.start()
|
|
|
|
|
|
|
|
|
|
|
|
logging.info("Modbus监控系统已设置")
|
|
|
|
|
|
|
|
|
|
|
|
def _register_modbus_handlers(self):
|
|
|
|
|
|
"""注册寄存器处理器"""
|
2025-06-12 10:25:33 +08:00
|
|
|
|
|
|
|
|
|
|
# 注册D6处理器,处理NG信号
|
|
|
|
|
|
self.modbus_monitor.register_handler(6, NGHandler(self.machine_handlers.handle_ng))
|
|
|
|
|
|
|
2025-06-11 15:21:26 +08:00
|
|
|
|
# 注册D11处理器,处理称重数据
|
2025-06-12 10:25:33 +08:00
|
|
|
|
self.modbus_monitor.register_handler(11, WeightDataHandler(self.machine_handlers.handle_weight_data))
|
2025-06-11 15:21:26 +08:00
|
|
|
|
|
|
|
|
|
|
# 注册D13处理器,处理贴标信号
|
2025-06-12 10:25:33 +08:00
|
|
|
|
self.modbus_monitor.register_handler(13, LabelSignalHandler(self.machine_handlers.handle_label_signal))
|
2025-06-11 15:21:26 +08:00
|
|
|
|
|
|
|
|
|
|
# 注册D20-D24处理器,处理各种状态信息
|
2025-06-12 10:25:33 +08:00
|
|
|
|
self.modbus_monitor.register_handler(20, LoadingFeedbackHandler(self.handle_loading_feedback))
|
|
|
|
|
|
self.modbus_monitor.register_handler(21, UnloadingFeedbackHandler(self.handle_unloading_feedback))
|
|
|
|
|
|
self.modbus_monitor.register_handler(22, Error1Handler(self.machine_handlers.handle_error_1))
|
|
|
|
|
|
self.modbus_monitor.register_handler(23, Error2Handler(self.machine_handlers.handle_error_2))
|
|
|
|
|
|
self.modbus_monitor.register_handler(24, Error3Handler(self.machine_handlers.handle_error_3))
|
|
|
|
|
|
|
|
|
|
|
|
# 注册下料层数和位置处理器
|
|
|
|
|
|
self.modbus_monitor.register_handler(4, UnloadingLevelHandler(self.handle_unloading_level))
|
|
|
|
|
|
self.modbus_monitor.register_handler(5, UnloadingPositionHandler(self.handle_unloading_position))
|
2025-06-11 15:21:26 +08:00
|
|
|
|
|
|
|
|
|
|
def _connect_modbus_signals(self):
|
|
|
|
|
|
"""连接Modbus信号槽"""
|
|
|
|
|
|
# 连接监控器状态信号
|
2025-06-12 10:25:33 +08:00
|
|
|
|
self.modbus_monitor.monitor_status_changed.connect(self.handle_modbus_status_change)
|
2025-06-11 15:21:26 +08:00
|
|
|
|
self.modbus_monitor.register_error.connect(self.handle_register_error)
|
2025-06-12 10:25:33 +08:00
|
|
|
|
self.machine_handlers.ng_changed.connect(self.handle_ng)
|
2025-06-11 15:21:26 +08:00
|
|
|
|
# 直接连接寄存器变化信号
|
|
|
|
|
|
self.modbus_monitor.register_changed.connect(self.handle_register_change)
|
|
|
|
|
|
|
|
|
|
|
|
# 连接机器状态信号
|
|
|
|
|
|
self.machine_handlers.loading_feedback_changed.connect(self.handle_loading_feedback)
|
|
|
|
|
|
self.machine_handlers.unloading_feedback_changed.connect(self.handle_unloading_feedback)
|
|
|
|
|
|
self.machine_handlers.error_1_changed.connect(self.handle_error_1)
|
|
|
|
|
|
self.machine_handlers.error_2_changed.connect(self.handle_error_2)
|
|
|
|
|
|
self.machine_handlers.error_3_changed.connect(self.handle_error_3)
|
|
|
|
|
|
|
2025-06-12 10:25:33 +08:00
|
|
|
|
# 连接称重数据和贴标信号
|
|
|
|
|
|
self.machine_handlers.weight_changed.connect(self.handle_weight_data)
|
|
|
|
|
|
self.machine_handlers.label_signal_changed.connect(self.handle_label_signal)
|
|
|
|
|
|
|
2025-06-11 15:21:26 +08:00
|
|
|
|
@Slot(int)
|
|
|
|
|
|
def handle_weight_data(self, weight):
|
|
|
|
|
|
"""处理称重数据变化"""
|
|
|
|
|
|
logging.info(f"[处理] 称重数据: {weight}g")
|
|
|
|
|
|
# 更新UI显示
|
|
|
|
|
|
self.weight_label.setText(f"重量: {weight}g")
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 获取数据行数
|
|
|
|
|
|
if self.process_table.rowCount() <= 2: # 没有数据行
|
|
|
|
|
|
logging.warning("没有可用的数据行来写入称重数据")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 获取启用的检验配置
|
|
|
|
|
|
enabled_configs = self.inspection_manager.get_enabled_configs()
|
|
|
|
|
|
|
|
|
|
|
|
# 计算称重列索引 - 称重位置在检验列之后的第二列(贴标后面)
|
|
|
|
|
|
weight_col = 2 + len(enabled_configs) + 1
|
2025-06-14 00:34:40 +08:00
|
|
|
|
# 计算净重列索引 - 净重位置在检验列之后的第三列(称重后面)
|
|
|
|
|
|
net_weight_col = 2 + len(enabled_configs) + 2
|
2025-06-11 15:21:26 +08:00
|
|
|
|
|
|
|
|
|
|
# 获取当前选中的行或第一个数据行
|
|
|
|
|
|
current_row = self.process_table.currentRow()
|
|
|
|
|
|
data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行(索引为2)
|
|
|
|
|
|
|
|
|
|
|
|
# 确保行存在
|
|
|
|
|
|
if data_row >= self.process_table.rowCount():
|
|
|
|
|
|
logging.warning(f"选中的行 {data_row} 超出了表格范围")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 获取工程号
|
|
|
|
|
|
order_id_item = self.process_table.item(data_row, 1)
|
|
|
|
|
|
if not order_id_item:
|
|
|
|
|
|
logging.warning("无法获取工程号")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
order_id = order_id_item.text().strip()
|
|
|
|
|
|
if not order_id:
|
|
|
|
|
|
logging.warning("工程号为空")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2025-06-12 10:25:33 +08:00
|
|
|
|
# 暂时断开信号连接,避免触发cellChanged信号
|
2025-06-11 15:21:26 +08:00
|
|
|
|
try:
|
|
|
|
|
|
self.process_table.cellChanged.disconnect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
except:
|
2025-06-12 10:25:33 +08:00
|
|
|
|
pass
|
2025-06-11 15:21:26 +08:00
|
|
|
|
|
2025-06-12 10:25:33 +08:00
|
|
|
|
# 设置称重值单元格
|
2025-06-11 15:21:26 +08:00
|
|
|
|
weight_item = QTableWidgetItem(str(weight))
|
|
|
|
|
|
weight_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.process_table.setItem(data_row, weight_col, weight_item)
|
2025-06-11 17:00:05 +08:00
|
|
|
|
|
|
|
|
|
|
# 保存到数据库
|
2025-06-12 10:25:33 +08:00
|
|
|
|
tray_id = self.tray_edit.currentText()
|
|
|
|
|
|
self.save_inspection_data(order_id, tray_id, 12, 12, str(weight), "pass")
|
2025-06-11 17:00:05 +08:00
|
|
|
|
|
2025-06-14 00:34:40 +08:00
|
|
|
|
# 保存净重到数据库(毛重-工字轮重量,TODO :先默认工字轮重量为10g后续从接口获取)
|
|
|
|
|
|
net_weight = weight - 10
|
|
|
|
|
|
self.save_inspection_data(order_id, tray_id, 13, 13, str(net_weight), "pass")
|
|
|
|
|
|
|
|
|
|
|
|
# 设置净重单元格
|
|
|
|
|
|
net_weight_item = QTableWidgetItem(str(net_weight))
|
|
|
|
|
|
net_weight_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.process_table.setItem(data_row, net_weight_col, net_weight_item)
|
|
|
|
|
|
|
2025-06-12 10:25:33 +08:00
|
|
|
|
# 重新连接信号
|
2025-06-11 15:21:26 +08:00
|
|
|
|
self.process_table.cellChanged.connect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
|
2025-06-12 10:25:33 +08:00
|
|
|
|
logging.info(f"已将称重数据 {weight}g 写入行 {data_row}, 列 {weight_col}")
|
|
|
|
|
|
|
|
|
|
|
|
# TODO:调用称重打印,进行下一步打印
|
2025-06-11 15:21:26 +08:00
|
|
|
|
except Exception as e:
|
2025-06-12 10:25:33 +08:00
|
|
|
|
logging.error(f"处理称重数据时发生错误: {str(e)}")
|
|
|
|
|
|
# 确保重新连接信号
|
2025-06-11 15:21:26 +08:00
|
|
|
|
try:
|
|
|
|
|
|
self.process_table.cellChanged.connect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|
2025-06-11 17:00:05 +08:00
|
|
|
|
|
2025-06-11 15:21:26 +08:00
|
|
|
|
@Slot(int, str)
|
|
|
|
|
|
def handle_label_signal(self, signal, status):
|
2025-06-12 10:25:33 +08:00
|
|
|
|
"""处理贴标信号"""
|
|
|
|
|
|
logging.info(f"[处理] 贴标信号: {status} (值={signal})")
|
|
|
|
|
|
|
2025-06-11 15:21:26 +08:00
|
|
|
|
# 更新UI显示
|
|
|
|
|
|
self.label_status_label.setText(f"贴标: {status}")
|
2025-06-11 17:00:05 +08:00
|
|
|
|
|
2025-06-12 10:25:33 +08:00
|
|
|
|
# 只有当信号为贴标完成(1)时才进行处理
|
2025-06-11 17:00:05 +08:00
|
|
|
|
if signal == 1:
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 获取数据行数
|
|
|
|
|
|
if self.process_table.rowCount() <= 2: # 没有数据行
|
2025-06-12 10:25:33 +08:00
|
|
|
|
logging.warning("没有可用的数据行来写入贴标数据")
|
2025-06-11 17:00:05 +08:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 获取当前选中的行或第一个数据行
|
|
|
|
|
|
current_row = self.process_table.currentRow()
|
|
|
|
|
|
data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行(索引为2)
|
|
|
|
|
|
|
|
|
|
|
|
# 确保行存在
|
|
|
|
|
|
if data_row >= self.process_table.rowCount():
|
|
|
|
|
|
logging.warning(f"选中的行 {data_row} 超出了表格范围")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 获取工程号
|
|
|
|
|
|
order_id_item = self.process_table.item(data_row, 1)
|
|
|
|
|
|
if not order_id_item:
|
|
|
|
|
|
logging.warning("无法获取工程号")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
order_id = order_id_item.text().strip()
|
|
|
|
|
|
if not order_id:
|
|
|
|
|
|
logging.warning("工程号为空")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 获取托盘号
|
|
|
|
|
|
tray_id = self.tray_edit.currentText()
|
|
|
|
|
|
|
|
|
|
|
|
# 获取启用的检验配置
|
|
|
|
|
|
enabled_configs = self.inspection_manager.get_enabled_configs()
|
|
|
|
|
|
|
|
|
|
|
|
# 计算贴标列索引 - 贴标位置在检验列之后的第一列
|
|
|
|
|
|
label_col = 2 + len(enabled_configs)
|
|
|
|
|
|
|
|
|
|
|
|
# 生成贴标号(托盘号+序号)
|
|
|
|
|
|
label_value = f"{tray_id}-{self.init_seq[tray_id]}"
|
|
|
|
|
|
|
|
|
|
|
|
# 断开单元格变更信号,避免程序自动写入时触发
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.process_table.cellChanged.disconnect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
except:
|
2025-06-12 10:25:33 +08:00
|
|
|
|
pass
|
2025-06-11 17:00:05 +08:00
|
|
|
|
|
|
|
|
|
|
# 创建并设置贴标单元格
|
|
|
|
|
|
label_item = QTableWidgetItem(label_value)
|
|
|
|
|
|
label_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
|
|
|
|
|
|
# 写入单元格
|
|
|
|
|
|
self.process_table.setItem(data_row, label_col, label_item)
|
|
|
|
|
|
logging.info(f"已将贴标数据 {label_value} 写入表格单元格 [{data_row}, {label_col}]")
|
|
|
|
|
|
|
|
|
|
|
|
# 保存贴标数据到数据库
|
2025-06-12 10:25:33 +08:00
|
|
|
|
self.save_inspection_data(order_id, tray_id, 11, 11, label_value, "pass")
|
2025-06-11 17:00:05 +08:00
|
|
|
|
|
|
|
|
|
|
# 调用加载到包装记录的方法
|
|
|
|
|
|
self.load_finished_record_to_package_record(order_id, tray_id)
|
|
|
|
|
|
logging.info(f"贴标完成,已将工程号 {order_id} 的记录加载到包装记录")
|
|
|
|
|
|
|
|
|
|
|
|
# 删除当前处理的行
|
|
|
|
|
|
self.process_table.removeRow(data_row)
|
|
|
|
|
|
logging.info(f"已删除处理完成的行 {data_row}")
|
|
|
|
|
|
|
|
|
|
|
|
# 重新连接单元格变更信号
|
|
|
|
|
|
self.process_table.cellChanged.connect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
|
|
|
|
|
|
# 更新托盘号对应的序号
|
|
|
|
|
|
self.init_seq[tray_id] += 1
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"处理贴标完成信号失败: {str(e)}")
|
|
|
|
|
|
# 确保信号重新连接
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.process_table.cellChanged.connect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|
2025-06-11 15:21:26 +08:00
|
|
|
|
|
|
|
|
|
|
@Slot(bool, str)
|
2025-06-12 10:25:33 +08:00
|
|
|
|
def handle_modbus_status_change(self, is_connected, message):
|
2025-06-11 15:21:26 +08:00
|
|
|
|
"""处理Modbus连接状态变化"""
|
|
|
|
|
|
if is_connected:
|
|
|
|
|
|
self.modbus_status_label.setText("Modbus: 已连接")
|
|
|
|
|
|
self.modbus_status_label.setStyleSheet("color: green;")
|
|
|
|
|
|
logging.info(f"Modbus已连接: {message}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
self.modbus_status_label.setText("Modbus: 未连接")
|
|
|
|
|
|
self.modbus_status_label.setToolTip(message)
|
|
|
|
|
|
self.modbus_status_label.setStyleSheet("color: red;")
|
|
|
|
|
|
logging.warning(f"Modbus连接断开: {message}")
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(int, str)
|
|
|
|
|
|
def handle_register_error(self, address, error_msg):
|
|
|
|
|
|
"""处理寄存器读取错误"""
|
|
|
|
|
|
logging.warning(f"[处理] 寄存器D{address}错误: {error_msg}")
|
|
|
|
|
|
# 在这里可以添加错误处理逻辑
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(int, int)
|
|
|
|
|
|
def handle_register_change(self, address, value):
|
|
|
|
|
|
"""处理寄存器变化"""
|
|
|
|
|
|
logging.info(f"[处理] 寄存器D{address}变化: {value}")
|
|
|
|
|
|
# 在这里可以添加通用寄存器变化处理逻辑
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(int, str)
|
|
|
|
|
|
def handle_loading_feedback(self, status, desc):
|
|
|
|
|
|
"""处理上料信息反馈"""
|
|
|
|
|
|
logging.info(f"[处理] 上料信息: {desc}")
|
2025-06-11 17:00:05 +08:00
|
|
|
|
|
2025-06-12 10:25:33 +08:00
|
|
|
|
# 如果上料完成(status=1),显示状态信息,把上料寄存器置为 0
|
|
|
|
|
|
if status == 1:
|
|
|
|
|
|
modbus = ModbusUtils()
|
|
|
|
|
|
client = modbus.get_client()
|
|
|
|
|
|
modbus.write_register_until_success(client, 2, 0)
|
|
|
|
|
|
modbus.close_client(client)
|
2025-06-11 17:00:05 +08:00
|
|
|
|
self.show_operation_status("上料完成", "input", desc)
|
|
|
|
|
|
QMessageBox.information(self, "上料操作", f"上料操作已完成: {desc}")
|
2025-06-12 10:25:33 +08:00
|
|
|
|
|
2025-06-11 15:21:26 +08:00
|
|
|
|
@Slot(int, str)
|
|
|
|
|
|
def handle_unloading_feedback(self, status, desc):
|
|
|
|
|
|
"""处理下料信息反馈"""
|
|
|
|
|
|
logging.info(f"[处理] 下料信息: {desc}")
|
2025-06-12 10:25:33 +08:00
|
|
|
|
# 如果下料完成(status=1),显示状态信息,把下料寄存器置为 0
|
|
|
|
|
|
if status == 1:
|
|
|
|
|
|
modbus = ModbusUtils()
|
|
|
|
|
|
client = modbus.get_client()
|
|
|
|
|
|
modbus.write_register_until_success(client, 3, 0)
|
|
|
|
|
|
modbus.close_client(client)
|
|
|
|
|
|
self.show_operation_status("下料", "output", desc)
|
2025-06-11 17:00:05 +08:00
|
|
|
|
QMessageBox.information(self, "下料操作", f"下料操作已完成: {desc}")
|
2025-06-11 15:21:26 +08:00
|
|
|
|
|
|
|
|
|
|
def _update_error_status(self):
|
|
|
|
|
|
"""更新故障状态显示"""
|
|
|
|
|
|
# 收集所有故障信息
|
|
|
|
|
|
error_codes = [
|
|
|
|
|
|
getattr(self, 'error_1', 0),
|
|
|
|
|
|
getattr(self, 'error_2', 0),
|
|
|
|
|
|
getattr(self, 'error_3', 0)
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# 检查是否有故障
|
|
|
|
|
|
has_error = any(code > 0 for code in error_codes)
|
|
|
|
|
|
|
|
|
|
|
|
if has_error:
|
|
|
|
|
|
# 收集所有错误信息
|
|
|
|
|
|
errors = []
|
|
|
|
|
|
error_map = self.machine_handlers.error_map
|
|
|
|
|
|
|
|
|
|
|
|
if getattr(self, 'error_1', 0) > 0:
|
|
|
|
|
|
errors.append(f"故障1: {error_map.get(self.error_1, '未知')}")
|
|
|
|
|
|
if getattr(self, 'error_2', 0) > 0:
|
|
|
|
|
|
errors.append(f"故障2: {error_map.get(self.error_2, '未知')}")
|
|
|
|
|
|
if getattr(self, 'error_3', 0) > 0:
|
|
|
|
|
|
errors.append(f"故障3: {error_map.get(self.error_3, '未知')}")
|
|
|
|
|
|
|
|
|
|
|
|
self.error_status_label.setText("故障: 有")
|
|
|
|
|
|
self.error_status_label.setToolTip("\n".join(errors))
|
|
|
|
|
|
self.error_status_label.setStyleSheet("color: red; font-weight: bold;")
|
|
|
|
|
|
else:
|
|
|
|
|
|
self.error_status_label.setText("故障: 无")
|
|
|
|
|
|
self.error_status_label.setToolTip("")
|
|
|
|
|
|
self.error_status_label.setStyleSheet("color: green; font-weight: bold;")
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(int, str)
|
|
|
|
|
|
def handle_error_1(self, error_code, error_desc):
|
|
|
|
|
|
"""处理故障信息1"""
|
|
|
|
|
|
logging.info(f"[处理] 故障信息1: {error_desc}")
|
|
|
|
|
|
# 保存故障码
|
|
|
|
|
|
self.error_1 = error_code
|
|
|
|
|
|
self._update_error_status()
|
|
|
|
|
|
|
|
|
|
|
|
# 如果有故障,显示提示
|
|
|
|
|
|
if error_code > 0:
|
|
|
|
|
|
QMessageBox.warning(self, "设备故障", f"故障1: {error_desc}")
|
2025-06-12 10:25:33 +08:00
|
|
|
|
# error_1 属于上料故障,需要把上料寄存器置为 0
|
|
|
|
|
|
modbus = ModbusUtils()
|
|
|
|
|
|
client = modbus.get_client()
|
|
|
|
|
|
modbus.write_register_until_success(client, 2, 0)
|
|
|
|
|
|
modbus.close_client(client)
|
|
|
|
|
|
self.show_operation_status("上料", "input", error_desc)
|
|
|
|
|
|
|
2025-06-11 15:21:26 +08:00
|
|
|
|
|
|
|
|
|
|
@Slot(int, str)
|
|
|
|
|
|
def handle_error_2(self, error_code, error_desc):
|
|
|
|
|
|
"""处理故障信息2"""
|
|
|
|
|
|
logging.info(f"[处理] 故障信息2: {error_desc}")
|
|
|
|
|
|
# 保存故障码
|
|
|
|
|
|
self.error_2 = error_code
|
|
|
|
|
|
self._update_error_status()
|
|
|
|
|
|
|
|
|
|
|
|
# 如果有故障,显示提示
|
|
|
|
|
|
if error_code > 0:
|
|
|
|
|
|
QMessageBox.warning(self, "设备故障", f"故障2: {error_desc}")
|
2025-06-12 10:25:33 +08:00
|
|
|
|
# error_2 属于下料故障,需要把下料寄存器置为 0
|
|
|
|
|
|
modbus = ModbusUtils()
|
|
|
|
|
|
client = modbus.get_client()
|
|
|
|
|
|
modbus.write_register_until_success(client, 3, 0)
|
|
|
|
|
|
modbus.close_client(client)
|
|
|
|
|
|
self.show_operation_status("下料", "output", error_desc)
|
2025-06-11 15:21:26 +08:00
|
|
|
|
|
|
|
|
|
|
@Slot(int, str)
|
|
|
|
|
|
def handle_error_3(self, error_code, error_desc):
|
|
|
|
|
|
"""处理故障信息3"""
|
|
|
|
|
|
logging.info(f"[处理] 故障信息3: {error_desc}")
|
|
|
|
|
|
# 保存故障码
|
|
|
|
|
|
self.error_3 = error_code
|
|
|
|
|
|
self._update_error_status()
|
|
|
|
|
|
|
|
|
|
|
|
# 如果有故障,显示提示
|
|
|
|
|
|
if error_code > 0:
|
|
|
|
|
|
QMessageBox.warning(self, "设备故障", f"故障3: {error_desc}")
|
2025-06-12 10:25:33 +08:00
|
|
|
|
# error_3 属于全局故障,需要把上料和下料寄存器置为 0
|
|
|
|
|
|
modbus = ModbusUtils()
|
|
|
|
|
|
client = modbus.get_client()
|
|
|
|
|
|
modbus.write_register_until_success(client, 2, 0)
|
|
|
|
|
|
modbus.write_register_until_success(client, 3, 0)
|
|
|
|
|
|
modbus.close_client(client)
|
|
|
|
|
|
self.show_operation_status("上料", "input", error_desc)
|
|
|
|
|
|
self.show_operation_status("下料", "output", error_desc)
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(int)
|
|
|
|
|
|
def handle_unloading_level(self, level):
|
|
|
|
|
|
"""处理下料层数信息"""
|
|
|
|
|
|
self.unloading_level_label.setText(f"下料层数:{level}")
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(int)
|
|
|
|
|
|
def handle_unloading_position(self, position):
|
|
|
|
|
|
"""处理下料位置信息"""
|
|
|
|
|
|
self.unloading_position_label.setText(f"下料位置:{position}")
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(int)
|
|
|
|
|
|
def handle_ng(self, ng):
|
|
|
|
|
|
"""处理NG信号, 删除当前在处理的数据(也就是第一条数据)"""
|
|
|
|
|
|
if ng == 1:
|
|
|
|
|
|
# 获取当前选中的行或第一个数据行,并删除
|
2025-06-12 17:29:35 +08:00
|
|
|
|
try:
|
|
|
|
|
|
order_id = self.process_table.item(2, 1).text().strip()
|
|
|
|
|
|
tray_id = self.tray_edit.currentText()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"处理NG信号时发生错误: {str(e)}")
|
|
|
|
|
|
order_id = ""
|
|
|
|
|
|
tray_id = ""
|
|
|
|
|
|
|
2025-06-12 10:25:33 +08:00
|
|
|
|
self.inspection_manager.delete_inspection_data(order_id, tray_id)
|
|
|
|
|
|
# 触发重新查询,更新数据
|
|
|
|
|
|
self.load_finished_inspection_data()
|
|
|
|
|
|
logging.info(f"已删除当前在处理的数据: order_id: {order_id}, tray_id: {tray_id}")
|
|
|
|
|
|
# 复原NG信号
|
|
|
|
|
|
modbus = ModbusUtils()
|
|
|
|
|
|
client = modbus.get_client()
|
|
|
|
|
|
modbus.write_register_until_success(client, 6, 0)
|
2025-06-13 09:01:41 +08:00
|
|
|
|
modbus.close_client(client)
|
|
|
|
|
|
|
|
|
|
|
|
def register_serial_callbacks(self):
|
|
|
|
|
|
"""注册串口数据回调函数"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 注册米电阻数据回调
|
|
|
|
|
|
self.serial_manager.callbacks['mdz_data'] = self.on_mdz_data_received
|
|
|
|
|
|
|
|
|
|
|
|
# 注册线径数据回调
|
|
|
|
|
|
self.serial_manager.callbacks['xj_data'] = self.on_diameter_data_received
|
|
|
|
|
|
|
|
|
|
|
|
# 自动打开已配置的串口
|
|
|
|
|
|
self.serial_manager.auto_open_configured_ports()
|
|
|
|
|
|
|
|
|
|
|
|
logging.info("已注册串口数据回调函数")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"注册串口数据回调函数失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
def on_mdz_data_received(self, port_name, data):
|
|
|
|
|
|
"""米电阻数据接收回调函数
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
port_name: 串口名称
|
|
|
|
|
|
data: 接收到的数据
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 解析数据
|
|
|
|
|
|
data_str = data.decode('utf-8') if isinstance(data, bytes) else str(data)
|
|
|
|
|
|
logging.info(f"收到米电阻数据: {data_str} 来自 {port_name}")
|
|
|
|
|
|
|
|
|
|
|
|
# 提取米电阻值,格式为"米电阻数据: xxx"
|
|
|
|
|
|
if "米电阻数据:" in data_str:
|
|
|
|
|
|
value_str = data_str.split("米电阻数据:")[1].strip()
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 转换为浮点数
|
|
|
|
|
|
mdz_value = float(value_str)
|
|
|
|
|
|
|
|
|
|
|
|
# 查找米电阻对应的检验项配置
|
|
|
|
|
|
mdz_config = None
|
|
|
|
|
|
enabled_configs = self.inspection_manager.get_enabled_configs()
|
|
|
|
|
|
for config in enabled_configs:
|
|
|
|
|
|
if config.get('name') == 'mdz' or config.get('display_name') == '米电阻':
|
|
|
|
|
|
mdz_config = config
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
if mdz_config:
|
|
|
|
|
|
# 找到对应的检验项,将数据写入对应的单元格
|
|
|
|
|
|
self.set_inspection_value('mdz', mdz_config, mdz_value)
|
|
|
|
|
|
else:
|
|
|
|
|
|
logging.warning("未找到米电阻对应的检验项配置")
|
|
|
|
|
|
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
logging.warning(f"米电阻数据格式错误: {value_str}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
logging.warning(f"收到的数据不包含米电阻数据标记: {data_str}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"处理米电阻数据失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
def on_diameter_data_received(self, port_name, data):
|
|
|
|
|
|
"""线径数据接收回调函数
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
port_name: 串口名称
|
|
|
|
|
|
data: 接收到的数据
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 解析数据
|
|
|
|
|
|
data_str = data.decode('utf-8') if isinstance(data, bytes) else str(data)
|
|
|
|
|
|
logging.info(f"收到线径数据: {data_str} 来自 {port_name}")
|
|
|
|
|
|
|
|
|
|
|
|
# 提取线径值,格式为"线径数据: xxx"
|
|
|
|
|
|
if "线径数据:" in data_str:
|
|
|
|
|
|
value_str = data_str.split("线径数据:")[1].strip()
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 转换为浮点数
|
|
|
|
|
|
xj_value = float(value_str)
|
|
|
|
|
|
|
|
|
|
|
|
# 查找线径对应的检验项配置
|
|
|
|
|
|
xj_config = None
|
|
|
|
|
|
enabled_configs = self.inspection_manager.get_enabled_configs()
|
|
|
|
|
|
for config in enabled_configs:
|
|
|
|
|
|
if config.get('name') == 'xj' or config.get('display_name') == '线径':
|
|
|
|
|
|
xj_config = config
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
if xj_config:
|
|
|
|
|
|
# 找到对应的检验项,将数据写入对应的单元格
|
|
|
|
|
|
self.set_inspection_value('xj', xj_config, xj_value)
|
|
|
|
|
|
else:
|
|
|
|
|
|
logging.warning("未找到线径对应的检验项配置")
|
|
|
|
|
|
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
logging.warning(f"线径数据格式错误: {value_str}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
logging.warning(f"收到的数据不包含线径数据标记: {data_str}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"处理线径数据失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
def set_inspection_value(self, data_type, config, value):
|
|
|
|
|
|
"""设置检验项目值到表格中
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
data_type: 数据类型,'mdz'表示米电阻,'xj'表示线径
|
|
|
|
|
|
config: 检验项配置
|
|
|
|
|
|
value: 检验值
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 获取当前选中的行或第一个数据行
|
|
|
|
|
|
current_row = self.process_table.currentRow()
|
|
|
|
|
|
data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行(索引为2)
|
|
|
|
|
|
|
|
|
|
|
|
# 确保行存在
|
|
|
|
|
|
if data_row >= self.process_table.rowCount():
|
|
|
|
|
|
logging.warning(f"选中的行 {data_row} 超出了表格范围")
|
|
|
|
|
|
# 可能需要添加新行
|
|
|
|
|
|
if self.process_table.rowCount() <= 2: # 只有表头行
|
|
|
|
|
|
order_id = self.order_edit.text().strip()
|
|
|
|
|
|
if order_id:
|
|
|
|
|
|
self.add_new_inspection_row(order_id)
|
|
|
|
|
|
data_row = 2 # 新添加的行
|
|
|
|
|
|
else:
|
|
|
|
|
|
logging.warning("无法添加新行,订单号为空")
|
|
|
|
|
|
return
|
|
|
|
|
|
else:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 获取工程号
|
|
|
|
|
|
order_id_item = self.process_table.item(data_row, 1)
|
|
|
|
|
|
if not order_id_item:
|
|
|
|
|
|
logging.warning("无法获取工程号")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
order_id = order_id_item.text().strip()
|
|
|
|
|
|
if not order_id:
|
|
|
|
|
|
logging.warning("工程号为空")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 获取检验项的列索引
|
|
|
|
|
|
config_id = config.get('id')
|
|
|
|
|
|
config_position = config.get('position')
|
|
|
|
|
|
col_index = None
|
|
|
|
|
|
|
|
|
|
|
|
# 获取启用的检验配置
|
|
|
|
|
|
enabled_configs = self.inspection_manager.get_enabled_configs()
|
|
|
|
|
|
|
|
|
|
|
|
# 根据检验项配置查找对应的列索引
|
|
|
|
|
|
for i, cfg in enumerate(enabled_configs):
|
|
|
|
|
|
if cfg.get('id') == config_id:
|
|
|
|
|
|
col_index = 2 + i # 检验列从第3列开始
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
if col_index is None:
|
|
|
|
|
|
logging.warning(f"未找到{data_type}对应的列索引")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 暂时断开信号连接,避免触发cellChanged信号
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.process_table.cellChanged.disconnect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# 格式化值并设置单元格
|
|
|
|
|
|
formatted_value = str(value)
|
|
|
|
|
|
if config.get('data_type') == 'number':
|
|
|
|
|
|
# 格式化数字,保留2位小数
|
|
|
|
|
|
formatted_value = f"{value:.2f}"
|
|
|
|
|
|
|
|
|
|
|
|
# 设置单元格值
|
|
|
|
|
|
item = QTableWidgetItem(formatted_value)
|
|
|
|
|
|
item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
|
|
item.setData(Qt.UserRole, config_id) # 保存配置ID,用于识别检验项
|
|
|
|
|
|
self.process_table.setItem(data_row, col_index, item)
|
|
|
|
|
|
|
|
|
|
|
|
# 验证数据是否在有效范围内
|
|
|
|
|
|
status = "pass"
|
|
|
|
|
|
if config.get('data_type') == 'number':
|
|
|
|
|
|
min_value = config.get('min_value')
|
|
|
|
|
|
max_value = config.get('max_value')
|
|
|
|
|
|
if (min_value is not None and value < min_value) or (max_value is not None and value > max_value):
|
|
|
|
|
|
status = "fail"
|
|
|
|
|
|
item.setBackground(QBrush(QColor("#ffcdd2"))) # 浅红色
|
|
|
|
|
|
else:
|
|
|
|
|
|
item.setBackground(QBrush(QColor("#c8e6c9"))) # 浅绿色
|
|
|
|
|
|
|
|
|
|
|
|
# 保存到数据库
|
|
|
|
|
|
tray_id = self.tray_edit.currentText()
|
|
|
|
|
|
self.save_inspection_data(order_id, tray_id, config_position, config_id, formatted_value, status)
|
|
|
|
|
|
|
|
|
|
|
|
# 重新连接信号
|
|
|
|
|
|
self.process_table.cellChanged.connect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
|
|
|
|
|
|
logging.info(f"已将{data_type}数据 {formatted_value} 写入行 {data_row}, 列 {col_index}")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.error(f"设置检验项值失败: {str(e)}")
|
|
|
|
|
|
# 确保重新连接信号
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.process_table.cellChanged.connect(self.handle_inspection_cell_changed)
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|