diff --git a/dao/inspection_dao.py b/dao/inspection_dao.py index 10e51ae..b800046 100644 --- a/dao/inspection_dao.py +++ b/dao/inspection_dao.py @@ -314,7 +314,7 @@ class InspectionDAO: LEFT JOIN inspection_config c ON d.config_id = c.id WHERE d.is_deleted = FALSE AND d.tray_id = ? AND d.order_id IN ({placeholders}) - ORDER BY d.order_id, d.position + ORDER BY d.create_time, d.order_id, d.position """ params = [tray_id] + order_ids @@ -358,7 +358,7 @@ class InspectionDAO: FROM inspection_data d LEFT JOIN inspection_config c ON d.config_id = c.id WHERE d.order_id = ? AND d.is_deleted = FALSE AND d.tray_id = ? - ORDER BY d.position + ORDER BY d.create_time, d.order_id, d.position """ params = (order_id, tray_id) @@ -386,31 +386,6 @@ class InspectionDAO: logging.error(f"获取检验数据失败: {str(e)}") return [] - def get_inspection_unfinished_count(self, order_id, tray_id): - """根据托盘号和工程号获取未完成检验的数量,如果存在一个未完成则都算未完成 - - Args: - order_id: 工程号 - tray_id: 托盘号 - Returns: - tuple: 工程号, 托盘号, 未完成检验的数量 - """ - try: - sql =""" - select order_id, tray_id, count(case when status != 'pass' then null else status end) as unfinished_count - from inspection_data - where is_deleted = 0 and order_id = ? and tray_id = ? - group by order_id, tray_id - """ - params = (order_id, tray_id) - self.db.cursor.execute(sql, params) - result = self.db.cursor.fetchone() - if result: - return result[0], result[1], result[2] - return order_id, tray_id, 0 - except Exception as e: - logging.error(f"获取未完成检验数据失败: {str(e)}") - return order_id, tray_id, 0 def get_package_record(self, tray_id): """根据托盘号获取包装记录 diff --git a/db/jtDB.db b/db/jtDB.db index 75949fa..e7baf46 100644 Binary files a/db/jtDB.db and b/db/jtDB.db differ diff --git a/from pymodbus.py b/from pymodbus.py new file mode 100644 index 0000000..2d0fcb0 --- /dev/null +++ b/from pymodbus.py @@ -0,0 +1,9 @@ +from pymodbus.client import ModbusTcpClient + +client = ModbusTcpClient('localhost', port=5020) +client.connect() +client.write_registers(address=11, values=[15]) + +result = client.read_holding_registers(address=11, count=1) +print(result,"123===") +client.close() \ No newline at end of file diff --git a/ui/main_window_ui.py b/ui/main_window_ui.py index 7ec17ff..66cf74b 100644 --- a/ui/main_window_ui.py +++ b/ui/main_window_ui.py @@ -501,13 +501,13 @@ class MainWindowUI(QMainWindow): self.record_layout.addWidget(self.record_title) # 创建表格 - self.record_table = QTableWidget(14, 8) # 14行7列:序号、订单、材质、规格、托号、轴包装号、重量 + self.record_table = QTableWidget(14, 8) # 14行7列:序号、订单、品名、规格、托号、轴包装号、重量 # 应用通用表格设置 self.setup_table_common(self.record_table) # 设置列标题 - record_headers = ["序号", "订单", "材质", "规格", "托号", "轴包装号", "重量", "完成时间"] + record_headers = ["序号", "订单", "品名", "规格", "托号", "轴包装号", "重量", "完成时间"] for col, header in enumerate(record_headers): self.record_table.setItem(0, col, self.create_header_item(header)) diff --git a/utils/modbus_monitor.py b/utils/modbus_monitor.py new file mode 100644 index 0000000..6da34e0 --- /dev/null +++ b/utils/modbus_monitor.py @@ -0,0 +1,258 @@ +import time +import logging +from threading import Thread, Event +from PySide6.QtCore import QObject, Signal +from .modbus_utils import ModbusUtils + +class RegisterValue: + """寄存器值对象,用于存储寄存器的值和状态""" + def __init__(self, address, value=None): + self.address = address + self.value = value + self.last_value = None + self.error_count = 0 + self.last_read_time = None + self.last_change_time = None + + def update(self, new_value): + """更新寄存器值,并返回值是否发生变化""" + self.last_read_time = time.time() + if self.value != new_value: + self.last_value = self.value + self.value = new_value + self.last_change_time = self.last_read_time + return True + return False + + def record_error(self): + """记录错误次数""" + self.error_count += 1 + return self.error_count + + def reset_error(self): + """重置错误计数""" + self.error_count = 0 + + +class RegisterHandler: + """寄存器处理器基类""" + def handle_change(self, value): + """处理寄存器值变化的方法,由子类实现具体逻辑""" + pass + + +class ModbusMonitor(QObject): + """Modbus寄存器监控器""" + # 定义信号:寄存器地址、新值 + register_changed = Signal(int, int) + register_error = Signal(int, str) + monitor_status_changed = Signal(bool, str) + + def __init__(self, polling_interval=1.0, max_errors=3, retry_interval=5.0): + """ + 初始化Modbus监控器 + + Args: + polling_interval: 轮询间隔,单位秒 + max_errors: 最大错误次数,超过此次数将暂停特定寄存器的监控 + retry_interval: 重试间隔,单位秒 + """ + super().__init__() + self.polling_interval = polling_interval + self.max_errors = max_errors + self.retry_interval = retry_interval + + # 初始化存储 + self.registers = {} # 存储寄存器值的字典 + self.handlers = {} # 存储寄存器处理器的字典 + self.stop_event = Event() # 用于停止监控线程的事件 + self.monitor_thread = None + self.modbus = ModbusUtils() + self.client = None + self.running = False + + # 初始化要监控的寄存器列表 + self._initialize_registers() + + def _initialize_registers(self): + """初始化要监控的寄存器列表""" + # 默认监控的寄存器地址 + register_addresses = [11, 13, 20, 21, 22, 23, 24] + for address in register_addresses: + self.registers[address] = RegisterValue(address) + + def register_handler(self, address, handler): + """注册寄存器处理器 + + Args: + address: 寄存器地址 + handler: RegisterHandler的实例 + """ + if address not in self.registers: + self.registers[address] = RegisterValue(address) + + if address not in self.handlers: + self.handlers[address] = [] + + self.handlers[address].append(handler) + logging.info(f"已注册寄存器D{address}的处理器: {handler.__class__.__name__}") + + def start(self): + """启动监控线程""" + if self.monitor_thread and self.monitor_thread.is_alive(): + logging.warning("监控器已经在运行中") + return + + self.stop_event.clear() + self.monitor_thread = Thread(target=self._monitor_loop, daemon=True) + self.monitor_thread.start() + self.running = True + self.monitor_status_changed.emit(True, "监控器已启动") + logging.info("Modbus监控器已启动") + + def stop(self): + """停止监控线程""" + if not self.monitor_thread or not self.monitor_thread.is_alive(): + return + + self.stop_event.set() + if self.monitor_thread: + self.monitor_thread.join(timeout=5.0) # 给线程5秒时间完成 + + if self.client: + self.modbus.close_client(self.client) + self.client = None + + self.running = False + self.monitor_status_changed.emit(False, "监控器已停止") + logging.info("Modbus监控器已停止") + + def _monitor_loop(self): + """监控循环,在独立线程中运行""" + try: + while not self.stop_event.is_set(): + # 检查连接并根据需要重新连接 + if not self.client: + self._reconnect() + + if self.client: + # 读取并处理所有注册的寄存器 + self._read_registers() + + # 等待下一次轮询,或者直到收到停止信号 + self.stop_event.wait(self.polling_interval) + except Exception as e: + logging.error(f"监控线程发生异常: {str(e)}", exc_info=True) + self.monitor_status_changed.emit(False, f"监控器异常: {str(e)}") + finally: + if self.client: + self.modbus.close_client(self.client) + self.client = None + + def _reconnect(self): + """重新连接到Modbus服务器""" + try: + if self.client: + self.modbus.close_client(self.client) + + self.client = self.modbus.get_client() + if self.client: + logging.info("Modbus监控器成功连接到服务器") + self.monitor_status_changed.emit(True, "已连接到Modbus服务器") + # 重置所有寄存器的错误计数 + for reg in self.registers.values(): + reg.reset_error() + else: + logging.warning("Modbus监控器无法连接到服务器") + self.monitor_status_changed.emit(False, "无法连接到Modbus服务器") + # 等待重试间隔 + self.stop_event.wait(self.retry_interval) + except Exception as e: + logging.error(f"重新连接Modbus服务器时发生错误: {str(e)}") + self.monitor_status_changed.emit(False, f"连接错误: {str(e)}") + # 等待重试间隔 + self.stop_event.wait(self.retry_interval) + + def _read_registers(self): + """读取所有注册的寄存器""" + for address, reg_value in self.registers.items(): + if self.stop_event.is_set(): + break + + # 如果错误次数超过阈值且未到重试时间,则跳过此寄存器 + if reg_value.error_count >= self.max_errors: + current_time = time.time() + last_read = reg_value.last_read_time or 0 + if current_time - last_read < self.retry_interval: + continue + + try: + # 读取寄存器值 + result = self.modbus.read_holding_register(self.client, address) + if result is None or len(result) == 0: + error_count = reg_value.record_error() + error_msg = f"读取寄存器D{address}失败,这是第{error_count}次失败" + logging.warning(error_msg) + self.register_error.emit(address, error_msg) + + # 如果连续失败次数达到阈值,尝试重连 + if error_count >= self.max_errors: + logging.error(f"寄存器D{address}连续{error_count}次读取失败,将在{self.retry_interval}秒后重试") + # 下次将在retry_interval后尝试读取此寄存器 + continue + + # 成功读取,重置错误计数 + reg_value.reset_error() + + # 更新值并检查是否发生变化 + if reg_value.update(result[0]): + logging.info(f"寄存器D{address}值变化: {reg_value.last_value} -> {reg_value.value}") + # 发出信号 + self.register_changed.emit(address, reg_value.value) + # 调用注册的处理器 + self._notify_handlers(address, reg_value.value) + except Exception as e: + error_count = reg_value.record_error() + error_msg = f"读取寄存器D{address}时发生异常: {str(e)}" + logging.error(error_msg) + self.register_error.emit(address, error_msg) + + if error_count >= self.max_errors: + logging.error(f"寄存器D{address}连续{error_count}次读取异常,将在{self.retry_interval}秒后重试") + + def _notify_handlers(self, address, value): + """通知所有注册的处理器""" + if address in self.handlers: + for handler in self.handlers[address]: + try: + handler.handle_change(value) + except Exception as e: + logging.error(f"调用寄存器D{address}的处理器时发生异常: {str(e)}", exc_info=True) + + def get_register_value(self, address): + """获取寄存器的当前值 + + Args: + address: 寄存器地址 + + Returns: + 当前值,如果未读取过则返回None + """ + if address in self.registers: + return self.registers[address].value + return None + + def is_running(self): + """返回监控器是否正在运行""" + return self.running and self.monitor_thread and self.monitor_thread.is_alive() + + +# 单例模式 +_instance = None + +def get_instance(): + """获取ModbusMonitor单例""" + global _instance + if _instance is None: + _instance = ModbusMonitor() + return _instance \ No newline at end of file diff --git a/utils/register_handlers.py b/utils/register_handlers.py new file mode 100644 index 0000000..aa7dc99 --- /dev/null +++ b/utils/register_handlers.py @@ -0,0 +1,135 @@ +import logging +from PySide6.QtCore import QObject, Signal +from .modbus_monitor import RegisterHandler + +class WeightDataHandler(RegisterHandler): + """寄存器D11处理器,处理称重数据值""" + def __init__(self, callback=None): + super().__init__() + self.callback = callback + self.last_weight = None + + def handle_change(self, value): + weight = value # 可能需要转换单位或格式,这里简单处理 + logging.info(f"称重数据变化: {self.last_weight} -> {weight}") + self.last_weight = weight + + # 如果有回调函数,则调用 + if self.callback: + self.callback(weight) + + +class LabelSignalHandler(RegisterHandler): + """寄存器D13处理器,处理贴标信号""" + def __init__(self, callback=None): + super().__init__() + self.callback = callback + self.status_map = { + 0: "无贴标", + 1: "贴标中", + 2: "贴标完成", + 3: "贴标错误" + } + + def handle_change(self, value): + status = self.status_map.get(value, f"未知状态({value})") + logging.info(f"贴标信号: {status}") + + # 如果有回调函数,则调用 + if self.callback: + self.callback(value, status) + + +class MachineStatusHandlers(QObject): + """机器状态相关寄存器(D20-D24)处理器集合""" + + # 定义信号 + loading_feedback_changed = Signal(int, str) # 上料信息反馈 + unloading_feedback_changed = Signal(int, str) # 下料信息反馈 + error_1_changed = Signal(int, str) # 故障信息1 + error_2_changed = Signal(int, str) # 故障信息2 + error_3_changed = Signal(int, str) # 故障信息3 + + def __init__(self): + super().__init__() + + # 上料信息反馈状态映射 + self.loading_feedback_map = { + 0: "无操作", + 1: "上料中", + 2: "上料完成", + 3: "上料错误" + } + + # 下料信息反馈状态映射 + self.unloading_feedback_map = { + 0: "无操作", + 1: "下料中", + 2: "下料完成", + 3: "下料错误" + } + + # 故障信息映射 - 可以根据实际设备故障码进行扩展 + self.error_map = { + 0: "正常", + 1: "急停触发", + 2: "通信错误", + 3: "伺服报警", + 4: "气压不足", + 5: "材料不足" + } + + +class LoadingFeedbackHandler(RegisterHandler): + """寄存器D20处理器,处理上料信息反馈""" + def __init__(self, handlers): + self.handlers = handlers + + def handle_change(self, value): + feedback = self.handlers.loading_feedback_map.get(value, f"未知状态({value})") + logging.info(f"上料信息反馈: {feedback}") + self.handlers.loading_feedback_changed.emit(value, feedback) + + +class UnloadingFeedbackHandler(RegisterHandler): + """寄存器D21处理器,处理下料信息反馈""" + def __init__(self, handlers): + self.handlers = handlers + + def handle_change(self, value): + feedback = self.handlers.unloading_feedback_map.get(value, f"未知状态({value})") + logging.info(f"下料信息反馈: {feedback}") + self.handlers.unloading_feedback_changed.emit(value, feedback) + + +class Error1Handler(RegisterHandler): + """寄存器D22处理器,处理故障信息1""" + def __init__(self, handlers): + self.handlers = handlers + + def handle_change(self, value): + error_desc = self.handlers.error_map.get(value, f"未知错误({value})") + logging.info(f"故障信息1: {error_desc}") + self.handlers.error_1_changed.emit(value, error_desc) + + +class Error2Handler(RegisterHandler): + """寄存器D23处理器,处理故障信息2""" + def __init__(self, handlers): + self.handlers = handlers + + def handle_change(self, value): + error_desc = self.handlers.error_map.get(value, f"未知错误({value})") + logging.info(f"故障信息2: {error_desc}") + self.handlers.error_2_changed.emit(value, error_desc) + + +class Error3Handler(RegisterHandler): + """寄存器D24处理器,处理故障信息3""" + def __init__(self, handlers): + self.handlers = handlers + + def handle_change(self, value): + error_desc = self.handlers.error_map.get(value, f"未知错误({value})") + logging.info(f"故障信息3: {error_desc}") + self.handlers.error_3_changed.emit(value, error_desc) \ No newline at end of file diff --git a/widgets/main_window.py b/widgets/main_window.py index 6d75deb..9173cf3 100644 --- a/widgets/main_window.py +++ b/widgets/main_window.py @@ -5,12 +5,23 @@ import json from datetime import datetime from pathlib import Path from utils.modbus_utils import ModbusUtils +from utils.modbus_monitor import get_instance as get_modbus_monitor +from utils.register_handlers import ( + WeightDataHandler, + LabelSignalHandler, + MachineStatusHandlers, + LoadingFeedbackHandler, + UnloadingFeedbackHandler, + Error1Handler, + Error2Handler, + Error3Handler +) # 导入PySide6 from PySide6.QtWidgets import ( QWidget, QMessageBox, QTableWidgetItem, QStackedWidget, QLabel, QMainWindow, QTableWidget, QMenu, QComboBox, QFormLayout, QDialog, QVBoxLayout, QHBoxLayout, QPushButton ) -from PySide6.QtCore import Qt, QTimer +from PySide6.QtCore import Qt, QTimer, Slot from PySide6.QtGui import QBrush, QColor # 导入UI @@ -260,7 +271,7 @@ class MainWindow(MainWindowUI): layout = QVBoxLayout(dialog) # 添加提示信息 - info_label = QLabel("请选择上料托盘类型:") + info_label = QLabel("请选择拆垛层数:") info_label.setFont(self.normal_font) layout.addWidget(info_label) @@ -382,7 +393,7 @@ class MainWindow(MainWindowUI): def handle_start(self): """ - 处理开始按钮点击事件 + 处理开始按钮点击事件,并启动 modbus 监控 """ # 创建对话框 dialog = QDialog(self) @@ -393,7 +404,7 @@ class MainWindow(MainWindowUI): layout = QVBoxLayout(dialog) # 添加提示信息 - info_label = QLabel("请选择上料托盘类型:") + info_label = QLabel("请选择拆垛层数:") info_label.setFont(self.normal_font) layout.addWidget(info_label) @@ -442,6 +453,9 @@ class MainWindow(MainWindowUI): modbus = ModbusUtils() client = modbus.get_client() try: + # 启动Modbus监控 + self.setup_modbus_monitor() + 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) @@ -460,7 +474,7 @@ class MainWindow(MainWindowUI): modbus.close_client(client) def handle_stop(self): - """处理停止按钮点击事件""" + """处理停止按钮点击事件,并关闭 modbus 监控""" modbus = ModbusUtils() client = modbus.get_client() try: @@ -474,6 +488,10 @@ class MainWindow(MainWindowUI): QMessageBox.critical(self, "错误", f"停止操作失败: {str(e)}") finally: modbus.close_client(client) + # 停止Modbus监控 + if hasattr(self, 'modbus_monitor'): + logging.info("停止Modbus监控") + self.modbus_monitor.stop() def handle_camera_status(self, is_connected, message): """处理相机状态变化""" @@ -509,13 +527,18 @@ class MainWindow(MainWindowUI): def closeEvent(self, event): """窗口关闭事件""" + # 停止Modbus监控 + if hasattr(self, 'modbus_monitor'): + logging.info("停止Modbus监控") + self.modbus_monitor.stop() + # 只有在相机启用时处理相机关闭 if self.camera_enabled and hasattr(self, 'camera_display'): # 停止相机显示 self.camera_display.stop_display() # 接受关闭事件 - event.accept() + event.accept() def handle_order_enter(self): """处理工程号输入框按下回车事件""" @@ -537,7 +560,7 @@ class MainWindow(MainWindowUI): self.central_widget.setFocus() def add_new_inspection_row(self, order_id): - """在微丝产线表格中添加一条新记录 + """在微丝产线表格中添加一条新记录,添加到表格末尾 Args: order_id: 工程号 @@ -546,35 +569,36 @@ class MainWindow(MainWindowUI): # 获取启用的检验配置 enabled_configs = self.inspection_manager.get_enabled_configs() - # 固定的数据起始行索引 - data_start_row = 2 # 数据从第3行开始 - # 断开单元格变更信号,避免加载过程中触发保存 try: self.process_table.cellChanged.disconnect(self.handle_inspection_cell_changed) except: pass - # 在指定行索引插入新行 - 总是插入到第一个数据行 + # 计算新行的行索引(添加到末尾) + data_start_row = self.process_table.rowCount() + + # 在末尾添加新行 self.process_table.insertRow(data_start_row) - # 更新序号 - 所有现有行序号+1 - for row in range(data_start_row + 1, self.process_table.rowCount()): - seq_item = self.process_table.item(row, 0) - if seq_item: + # 计算新行的序号(最后一个序号+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: try: - current_seq = int(seq_item.text()) - seq_item.setText(str(current_seq + 1)) + prev_seq = int(prev_seq_item.text()) + new_seq = prev_seq + 1 except ValueError: - pass + new_seq = data_start_row - 1 # 备选方案:使用行索引作为序号 # 添加工程号到表格的第二列 item = QTableWidgetItem(order_id) item.setTextAlignment(Qt.AlignCenter) self.process_table.setItem(data_start_row, 1, item) - # 添加序号到表格的第一列 - 新行始终是第1条 - item = QTableWidgetItem("1") + # 添加序号到表格的第一列 + item = QTableWidgetItem(str(new_seq)) item.setTextAlignment(Qt.AlignCenter) self.process_table.setItem(data_start_row, 0, item) @@ -638,7 +662,7 @@ class MainWindow(MainWindowUI): }] inspection_dao.save_inspection_data(order_id, data) - logging.info(f"已添加工程号 {order_id} 的新记录,显示在第1条") + logging.info(f"已添加工程号 {order_id} 的新记录,显示在第{new_seq}条") except Exception as e: logging.error(f"添加新记录失败: {str(e)}") @@ -672,7 +696,7 @@ class MainWindow(MainWindowUI): logging.error(f"限制表格行数失败: {str(e)}") def handle_inspection_cell_changed(self, row, column): - """处理检验单元格内容变更 + """处理微丝包装单元格内容变更 Args: row: 行索引 @@ -843,7 +867,8 @@ class MainWindow(MainWindowUI): try: from dao.inspection_dao import InspectionDAO inspection_dao = InspectionDAO() - + modbus = ModbusUtils() + client = modbus.get_client() # 记录保存前的详细日志 logging.info(f"正在保存检验数据: 工程号={order_id}, 托盘号={tray_id}, 位置={position}, 配置ID={config_id}, 值={value}, 状态={status}") @@ -858,24 +883,24 @@ class MainWindow(MainWindowUI): }] # 保存到数据库 - result = inspection_dao.save_inspection_data(order_id, data) - if result: - logging.info(f"已成功保存工程号 {order_id} 的检验数据,位置: {position}, 值: {value}") - # 显示临时状态消息 - self.statusBar().showMessage(f"已保存检验数据:{value}", 3000) + inspection_dao.save_inspection_data(order_id, data) + # if result: + # logging.info(f"已成功保存工程号 {order_id} 的检验数据,位置: {position}, 值: {value}") + # # 显示临时状态消息 + # self.statusBar().showMessage(f"已保存检验数据:{value}", 3000) - # 如果是贴标字段且有值,直接写入包装记录 - if position == 11 and value: - # 直接调用加载到包装记录的方法 - self.load_finished_record_to_package_record(order_id, tray_id) - logging.info(f"检测到贴标字段有值,已自动写入包装记录") - else: - # 使用延迟调用,避免频繁刷新UI - QTimer.singleShot(500, self.check_and_process_finished_records) - else: - logging.warning(f"保存工程号 {order_id} 的检验数据失败") - # 显示错误消息 - self.statusBar().showMessage(f"保存检验数据失败", 3000) + # #TODO: 如果是称重字段,生成贴标号,然后给打印机信号,进行贴标 + # if position == 12 and value: + # # 直接调用加载到包装记录的方法 + # self.load_finished_record_to_package_record(order_id, tray_id) + # logging.info(f"检测到称重字段有值,已自动写入包装记录") + # else: + # # 使用延迟调用,避免频繁刷新UI + # QTimer.singleShot(500, self.check_and_process_finished_records) + # else: + # logging.warning(f"保存工程号 {order_id} 的检验数据失败") + # # 显示错误消息 + # self.statusBar().showMessage(f"保存检验数据失败", 3000) except Exception as e: logging.error(f"保存检验数据失败: {str(e)}") @@ -929,7 +954,7 @@ class MainWindow(MainWindowUI): row_idx = 2 # 确保按工程号倒序排列,最新的工程号在最前面 - sorted_order_ids = sorted(orders_data.keys(), reverse=True) + sorted_order_ids = sorted(orders_data.keys(), reverse=False) for order_id in sorted_order_ids: items = orders_data[order_id] @@ -967,16 +992,7 @@ class MainWindow(MainWindowUI): cell_item = QTableWidgetItem(str(value)) cell_item.setTextAlignment(Qt.AlignCenter) # 存储配置ID,用于保存时确定是哪个检验项 - cell_item.setData(Qt.UserRole, config_id) - - # 根据状态设置单元格颜色 - if status == 'fail': - cell_item.setBackground(QBrush(QColor("#ffcdd2"))) # 浅红色 - elif status == 'warning': - cell_item.setBackground(QBrush(QColor("#fff9c4"))) # 浅黄色 - elif status == 'pass': - cell_item.setBackground(QBrush(QColor("#c8e6c9"))) # 浅绿色 - + cell_item.setData(Qt.UserRole, config_id) # 设置单元格 self.process_table.setItem(row_idx, col_index, cell_item) # 添加贴标(11)和称重数据(12) @@ -1358,4 +1374,262 @@ class MainWindow(MainWindowUI): # 调用原始的resizeEvent(如果有的话) original_resize = getattr(container, "_original_resize_event", None) if original_resize: - original_resize(event) \ No newline at end of file + original_resize(event) + + # ==================== Modbus监控系统相关方法 ==================== + + def setup_modbus_monitor(self): + """设置Modbus监控系统""" + # 获取Modbus监控器实例 + self.modbus_monitor = get_modbus_monitor() + + # 创建机器状态处理器实例 + self.machine_handlers = MachineStatusHandlers() + + # 添加状态显示到状态栏 + self.modbus_status_label = QLabel("Modbus: 未连接") + self.weight_label = QLabel("重量: --") + self.label_status_label = QLabel("贴标: 无贴标") + self.error_status_label = QLabel("故障: 无") + + # 设置样式 + 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._register_modbus_handlers() + + # 连接信号槽 + self._connect_modbus_signals() + + # 启动监控 + self.modbus_monitor.start() + + logging.info("Modbus监控系统已设置") + + def _register_modbus_handlers(self): + """注册寄存器处理器""" + # 注册D11处理器,处理称重数据 + self.modbus_monitor.register_handler(11, WeightDataHandler(self.handle_weight_data)) + + # 注册D13处理器,处理贴标信号 + self.modbus_monitor.register_handler(13, LabelSignalHandler(self.handle_label_signal)) + + # 注册D20-D24处理器,处理各种状态信息 + self.modbus_monitor.register_handler(20, LoadingFeedbackHandler(self.machine_handlers)) + self.modbus_monitor.register_handler(21, UnloadingFeedbackHandler(self.machine_handlers)) + self.modbus_monitor.register_handler(22, Error1Handler(self.machine_handlers)) + self.modbus_monitor.register_handler(23, Error2Handler(self.machine_handlers)) + self.modbus_monitor.register_handler(24, Error3Handler(self.machine_handlers)) + + def _connect_modbus_signals(self): + """连接Modbus信号槽""" + # 连接监控器状态信号 + self.modbus_monitor.monitor_status_changed.connect(self.handle_modbus_status) + self.modbus_monitor.register_error.connect(self.handle_register_error) + + # 直接连接寄存器变化信号 + 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) + + @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 + + # 获取当前选中的行或第一个数据行 + 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 + + # 断开单元格变更信号,避免程序自动写入时触发 + try: + self.process_table.cellChanged.disconnect(self.handle_inspection_cell_changed) + except: + logging.warning("断开单元格变更信号失败,可能信号未连接") + + # 创建并设置称重单元格 + weight_item = QTableWidgetItem(str(weight)) + weight_item.setTextAlignment(Qt.AlignCenter) + + # 写入单元格 + self.process_table.setItem(data_row, weight_col, weight_item) + logging.info(f"已将称重数据 {weight}g 写入表格单元格 [{data_row}, {weight_col}]") + + # 重新连接单元格变更信号 + self.process_table.cellChanged.connect(self.handle_inspection_cell_changed) + + # 主动触发单元格变更事件 + self.handle_inspection_cell_changed(data_row, weight_col) + + except Exception as e: + logging.error(f"写入称重数据到表格失败: {str(e)}") + # 确保信号重新连接 + try: + self.process_table.cellChanged.connect(self.handle_inspection_cell_changed) + except: + pass + + @Slot(int, str) + def handle_label_signal(self, signal, status): + """处理贴标信号变化""" + logging.info(f"[处理] 贴标状态: {status}") + # 更新UI显示 + self.label_status_label.setText(f"贴标: {status}") + # 在这里可以添加更多业务逻辑 + pass + + @Slot(bool, str) + def handle_modbus_status(self, is_connected, message): + """处理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}") + # 在这里添加业务逻辑 + pass + + @Slot(int, str) + def handle_unloading_feedback(self, status, desc): + """处理下料信息反馈""" + logging.info(f"[处理] 下料信息: {desc}") + # 在这里添加业务逻辑 + pass + + 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}") + # 在这里添加其他故障处理逻辑 + pass + + @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}") + # 在这里添加其他故障处理逻辑 + pass + + @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}") + # 在这里添加其他故障处理逻辑 + pass \ No newline at end of file