From 5a05bfe9a22926c4004571468e9994328089ccfd Mon Sep 17 00:00:00 2001 From: zhu-mengmeng <15588200382@163.com> Date: Thu, 17 Jul 2025 15:24:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=E7=BA=BF=E5=BE=84?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BC=9A=E8=AF=9D=E7=AE=A1=E7=90=86=E5=92=8C?= =?UTF-8?q?=E7=A8=B3=E5=AE=9A=E6=80=A7=E6=A3=80=E6=9F=A5=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- widgets/main_window.py | 335 +++++++++++++++++++++-------------------- 1 file changed, 171 insertions(+), 164 deletions(-) diff --git a/widgets/main_window.py b/widgets/main_window.py index d69fe13..e539617 100644 --- a/widgets/main_window.py +++ b/widgets/main_window.py @@ -225,7 +225,7 @@ class MainWindow(MainWindowUI): self.update_order_statistics() logging.info("主窗口初始化时已启动Modbus监控系统") - + def get_axios_num(self,tray_id): """获取托盘号对应的轴号""" from dao.inspection_dao import InspectionDAO @@ -1674,7 +1674,9 @@ class MainWindow(MainWindowUI): # 立即更新一次用电量数据 self.update_electricity_statistics() - + + # 连接称重数据变化信号 + self.machine_handlers.weight_changed.connect(self.handle_weight_data) # 立即更新一次订单数量和产量统计数据 self.update_order_statistics() @@ -2719,169 +2721,126 @@ class MainWindow(MainWindowUI): 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: - # 转换为浮点数,除以10000并保留三位小数 xj_value = round(float(value_str)/10000, 3) - - # 更新UI显示,实时回显最新测量值 self.statusBar().showMessage(f"线径数据: {xj_value:.3f}", 2000) - - # 初始化线径测量历史记录(如果不存在) + + # 查找线径对应的检验项配置和列 + xj_config = None + xj_column = None + enabled_configs = self.inspection_manager.get_enabled_configs() + for i, config in enumerate(enabled_configs): + if config.get('name') == 'xj' or config.get('display_name') == '线径': + xj_config = config + xj_column = 2 + i + break + if not xj_config or xj_column is None: + logging.warning("未找到线径对应的检验项配置或列索引") + return + + # 忽略接近0的值或异常值 + if xj_value < 0.001 or xj_value > 10: + logging.info(f"忽略异常线径值: {xj_value}") + return + + # 保存测量值到内部列表用于稳定性检测 + # 使用类属性存储最近的测量值,用于稳定性检测 if not hasattr(self, '_diameter_measurements'): self._diameter_measurements = [] - self._diameter_measurement_count = 0 + self._diameter_measurements.append(xj_value) + if len(self._diameter_measurements) > 5: + self._diameter_measurements.pop(0) - # 如果当前值不为0,添加到测量历史 - if xj_value > 0: - self._diameter_measurements.append(xj_value) - self._diameter_measurement_count += 1 - logging.info(f"添加线径测量值: {xj_value:.3}, 当前测量次数: {self._diameter_measurement_count}") - - # 保持最多5次测量记录 - if len(self._diameter_measurements) > 5: - self._diameter_measurements.pop(0) - - # 实时更新到微丝产线表格中(显示为临时值) - try: - # 查找线径对应的检验项配置 - xj_config = None - xj_column = None - enabled_configs = self.inspection_manager.get_enabled_configs() - - # 查找线径配置和对应的列索引 - for i, config in enumerate(enabled_configs): - if config.get('name') == 'xj' or config.get('display_name') == '线径': - xj_config = config - xj_column = 2 + i # 检验列从第3列开始 - break - - if xj_config and xj_column is not None: - # 找到当前选中的行或第一个有效行 - current_row = self.process_table.currentRow() - target_row = None - - # 如果当前选中了有效行,优先使用 - if current_row >= 2: - order_id_item = self.process_table.item(current_row, 1) - if order_id_item and order_id_item.text().strip(): - target_row = current_row - - # 如果没有选中行,查找第一个有效行 - if target_row is None: - for row in range(2, self.process_table.rowCount()): - order_id_item = self.process_table.item(row, 1) - if order_id_item and order_id_item.text().strip(): - target_row = row - break - - # 如果找到了有效行,更新线径值 - if target_row is not None: - # 暂时断开信号连接,避免触发cellChanged信号 - try: - self.process_table.cellChanged.disconnect(self.handle_inspection_cell_changed) - except: - pass - - # 创建显示项目并设置颜色 - formatted_value = f"{xj_value:.3f}" - item = QTableWidgetItem(formatted_value) - item.setTextAlignment(Qt.AlignCenter) - - # 设置单元格数据,包括配置ID - item.setData(Qt.UserRole, xj_config.get('id')) - - # 设置为临时值颜色(灰色) - item.setForeground(QBrush(QColor("#666666"))) - item.setToolTip(f"临时测量值 ({self._diameter_measurement_count}/5)") - - # 更新表格单元格 - self.process_table.setItem(target_row, xj_column, item) - - # 高亮显示更新的单元格 - self.process_table.setCurrentCell(target_row, xj_column) - - # 重新连接单元格变更信号 - self.process_table.cellChanged.connect(self.handle_inspection_cell_changed) - - logging.info(f"已将线径值 {xj_value:.3f} 实时更新到表格 [行 {target_row}, 列 {xj_column}]") - else: - logging.warning("未找到线径对应的检验项配置或列索引") - except Exception as e: - logging.error(f"更新线径值到表格失败: {str(e)}") + # 显示临时值到状态栏 + if len(self._diameter_measurements) < 5: + self.statusBar().showMessage(f"线径数据收集中: {xj_value:.3f} ({len(self._diameter_measurements)}/5)", 2000) + return - # 如果当前值为0,并且有足够的测量历史,检查是否可以保存最终值 - elif xj_value == 0 and len(self._diameter_measurements) >= 5: - logging.info(f"检测到线径值变为0,使用最后一次有效值 {self._diameter_measurements[-1]:.3f} 作为最终结果") + # 检查稳定性 + measurements = self._diameter_measurements[-5:] + min_value = min(measurements) + max_value = max(measurements) + avg_value = sum(measurements) / len(measurements) + error_range = avg_value * 0.04 # 允许4%误差 + + if max_value - min_value <= error_range: + # 数据稳定,可以保存 + final_value = avg_value # 使用平均值作为最终值 - # 计算测量值的稳定性 - measurements = self._diameter_measurements[-5:] # 取最后5次测量 - min_value = min(measurements) - max_value = max(measurements) - avg_value = sum(measurements) / len(measurements) + # 查找第一个没有线径数据的行 + data_row = None + for row in range(2, self.process_table.rowCount()): + cell_item = self.process_table.item(row, xj_column) + if not cell_item or not cell_item.text().strip() or cell_item.text().strip() == '0': + data_row = row + break - # 计算误差范围(4%) - error_range = avg_value * 0.04 - - # 检查是否在4%误差范围内 - if max_value - min_value <= error_range: - final_value = measurements[-1] - logging.info(f"连续5次测量稳定,误差范围: {min_value:.3f} - {max_value:.3f}, 平均: {avg_value:.3f}, 最终值: {final_value:.3f}") - - # 查找线径对应的检验项配置 - 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: - from dao.inspection_dao import InspectionDAO - inspection_dao = InspectionDAO() - bccd, tccd = inspection_dao.get_xj_range(self._current_order_code) - - if bccd is not None and tccd is not None: - if bccd - 0.5 <= final_value <= tccd + 0.5: # 允许±0.5的误差范围 - self.set_inspection_value('xj', xj_config, final_value) - else: - logging.warning(f"线径 {final_value:.3f} 不在公差范围内 ({bccd} - {tccd}),误差超过±0.5") - reply = QMessageBox.question( - self, - '确认保存', - f"线径 {final_value:.3f} 不在公差范围内 ({bccd} - {tccd}),\n是否继续保存?", - QMessageBox.Yes | QMessageBox.No, - QMessageBox.No - ) - if reply == QMessageBox.Yes: - self.set_inspection_value('xj', xj_config, final_value) - else: - logging.info(f"用户取消保存超出范围的线径值: {final_value:.3f}") - else: - logging.info(f"未找到订单 {self._current_order_code} 的线径公差范围,直接保存值 {final_value:.3f}") - self.set_inspection_value('xj', xj_config, final_value) - else: - logging.warning("未找到线径对应的检验项配置") + # 如果没找到空行,使用当前选中行或第一个数据行 + if data_row is None: + current_row = self.process_table.currentRow() + data_row = current_row if current_row >= 2 else 2 + logging.info(f"未找到没有线径数据的行,使用当前选中行或第一个数据行: {data_row}") else: - logging.warning(f"连续5次测量,误差范围: {min_value:.3f} - {max_value:.3f}, 平均: {avg_value:.3f}, 超出4%误差范围") + logging.info(f"找到没有线径数据的行: {data_row}") + + # 获取工程号 + gc_note_item = self.process_table.item(data_row, 1) + if not gc_note_item: + logging.warning("无法获取工程号") + return + + gc_note = gc_note_item.text().strip() + if not gc_note: + logging.warning("工程号为空") + return - # 重置测量历史 + # 获取托盘号 + tray_id = self.tray_edit.currentText() + + # 公差校验 + from dao.inspection_dao import InspectionDAO + inspection_dao = InspectionDAO() + bccd, tccd = inspection_dao.get_xj_range(self._current_order_code) + + if bccd is not None and tccd is not None: + if bccd - 0.5 <= final_value <= tccd + 0.5: + # 使用set_inspection_value保存数据 + self.set_inspection_value('xj', xj_config, final_value) + logging.info(f"已将稳定的线径值 {final_value:.3f} 保存到工程号 {gc_note} (行 {data_row})") + else: + reply = QMessageBox.question( + self, + '确认保存', + f"线径 {final_value:.3f} 不在公差范围内 ({bccd} - {tccd}),\n是否继续保存?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + if reply == QMessageBox.Yes: + self.set_inspection_value('xj', xj_config, final_value) + logging.info(f"已将超出公差范围的线径值 {final_value:.3f} 保存到工程号 {gc_note} (行 {data_row})") + else: + logging.info(f"用户取消保存超出范围的线径值: {final_value:.3f}") + else: + # 无公差范围,直接保存 + self.set_inspection_value('xj', xj_config, final_value) + logging.info(f"已将线径值 {final_value:.3f} 保存到工程号 {gc_note} (行 {data_row})") + + # 重置测量列表,准备下一次测量 self._diameter_measurements = [] - self._diameter_measurement_count = 0 - + self.statusBar().showMessage(f"线径数据已保存: {final_value:.3f}", 2000) + else: + # 数据不稳定,继续收集 + self.statusBar().showMessage(f"线径数据不稳定: {min_value:.3f} - {max_value:.3f}, 继续测量", 2000) + logging.warning(f"线径测量数据不稳定,范围: {min_value:.3f} - {max_value:.3f}, 平均值: {avg_value:.3f}") + except ValueError: logging.warning(f"线径数据格式错误: {value_str}") else: @@ -2889,6 +2848,34 @@ class MainWindow(MainWindowUI): except Exception as e: logging.error(f"处理线径数据失败: {str(e)}") + def _save_diameter_to_order(self, order_id, config, value): + """基于工程号保存线径值,确保即使行号变化也能保存到正确的产品""" + try: + # 查找工程号对应的行 + target_row = None + for row in range(2, self.process_table.rowCount()): + order_id_item = self.process_table.item(row, 1) + if order_id_item and order_id_item.text().strip() == order_id: + target_row = row + break + + if target_row is not None: + # 使用set_inspection_value保存数据 + self.set_inspection_value('xj', config, value) + logging.info(f"已将线径值 {value:.3f} 保存到工程号 {order_id} (行 {target_row})") + else: + logging.warning(f"找不到工程号 {order_id} 对应的行,无法保存线径值") + except Exception as e: + logging.error(f"保存线径值到工程号失败: {str(e)}") + + def _reset_diameter_session(self): + """重置线径测量会话状态""" + self._diameter_session_active = False + self._diameter_session_target_row = None + self._diameter_session_order_id = None + self._diameter_session_measurements = [] + self._diameter_session_start_time = 0 + def on_scanner_data_received(self, port_name, data): """扫码器数据接收回调函数 @@ -2977,21 +2964,41 @@ class MainWindow(MainWindowUI): logging.warning("无法添加新行,订单号为空") return - # 查找第一个没有该检测数据的行 - data_row = None - for row in range(2, self.process_table.rowCount()): - cell_item = self.process_table.item(row, col_index) - if not cell_item or not cell_item.text().strip() or (data_type == 'xj' and cell_item.text().strip() == '0'): - data_row = row - break - - # 如果没有找到没有该检测数据的行,使用当前选中行或第一个数据行 - if data_row is None: - current_row = self.process_table.currentRow() - data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行(索引为2) - logging.info(f"未找到没有{data_type}数据的行,使用当前选中行或第一个数据行: {data_row}") + # 对于线径数据,如果有活跃会话,优先使用会话锁定的工程号 + if data_type == 'xj' and hasattr(self, '_diameter_session_active') and self._diameter_session_active and self._diameter_session_order_id: + # 查找会话工程号对应的行 + data_row = None + for row in range(2, self.process_table.rowCount()): + order_id_item = self.process_table.item(row, 1) + if order_id_item and order_id_item.text().strip() == self._diameter_session_order_id: + data_row = row + break + + if data_row is not None: + logging.info(f"使用线径会话锁定的工程号 {self._diameter_session_order_id} 对应的行 {data_row}") + else: + logging.warning(f"找不到线径会话锁定的工程号 {self._diameter_session_order_id} 对应的行,将使用默认行选择逻辑") + # 继续使用默认逻辑 + data_row = None else: - logging.info(f"找到没有{data_type}数据的行: {data_row}") + # 默认行选择逻辑 + data_row = None + + # 如果没有找到特定行,使用默认逻辑查找第一个没有该检测数据的行 + if data_row is None: + for row in range(2, self.process_table.rowCount()): + cell_item = self.process_table.item(row, col_index) + if not cell_item or not cell_item.text().strip() or (data_type == 'xj' and cell_item.text().strip() == '0'): + data_row = row + break + + # 如果没有找到没有该检测数据的行,使用当前选中行或第一个数据行 + if data_row is None: + current_row = self.process_table.currentRow() + data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行(索引为2) + logging.info(f"未找到没有{data_type}数据的行,使用当前选中行或第一个数据行: {data_row}") + else: + logging.info(f"找到没有{data_type}数据的行: {data_row}") # 获取工程号 order_id_item = self.process_table.item(data_row, 1) @@ -3039,13 +3046,13 @@ class MainWindow(MainWindowUI): # 保存到数据库,但只在非加载状态下 if not self._loading_data_in_progress: tray_id = self.tray_edit.currentText() - self.save_inspection_data(order_id, tray_id, config_position, config_id, formatted_value, status) + self.save_inspection_data(self._current_order_code, order_id, tray_id, config_position, config_id, formatted_value, status) # 不需要在这里主动触发数据重新加载,因为handle_inspection_cell_changed会处理 # 重新连接信号 self.process_table.cellChanged.connect(self.handle_inspection_cell_changed) - logging.info(f"已将{data_type}数据 {formatted_value} 写入行 {data_row}, 列 {col_index}") + logging.info(f"已将{data_type}数据 {formatted_value} 写入工程号 {order_id} (行 {data_row}, 列 {col_index})") except Exception as e: logging.error(f"设置检验项值失败: {str(e)}")