feat: 增强线径数据处理逻辑,新增会话管理和稳定性检查功能,优化数据保存流程

This commit is contained in:
zhu-mengmeng 2025-07-17 15:24:52 +08:00
parent 383e6d3aaa
commit 5a05bfe9a2

View File

@ -225,7 +225,7 @@ class MainWindow(MainWindowUI):
self.update_order_statistics() self.update_order_statistics()
logging.info("主窗口初始化时已启动Modbus监控系统") logging.info("主窗口初始化时已启动Modbus监控系统")
def get_axios_num(self,tray_id): def get_axios_num(self,tray_id):
"""获取托盘号对应的轴号""" """获取托盘号对应的轴号"""
from dao.inspection_dao import InspectionDAO from dao.inspection_dao import InspectionDAO
@ -1674,7 +1674,9 @@ class MainWindow(MainWindowUI):
# 立即更新一次用电量数据 # 立即更新一次用电量数据
self.update_electricity_statistics() self.update_electricity_statistics()
# 连接称重数据变化信号
self.machine_handlers.weight_changed.connect(self.handle_weight_data)
# 立即更新一次订单数量和产量统计数据 # 立即更新一次订单数量和产量统计数据
self.update_order_statistics() self.update_order_statistics()
@ -2719,169 +2721,126 @@ class MainWindow(MainWindowUI):
logging.error(f"处理米电阻数据失败: {str(e)}") logging.error(f"处理米电阻数据失败: {str(e)}")
def on_diameter_data_received(self, port_name, data): def on_diameter_data_received(self, port_name, data):
"""线径数据接收回调函数 """线径数据接收回调函数 - 采用类似称重的逻辑,不使用会话机制"""
Args:
port_name: 串口名称
data: 接收到的数据
"""
try: try:
# 解析数据
data_str = data.decode('utf-8') if isinstance(data, bytes) else str(data) data_str = data.decode('utf-8') if isinstance(data, bytes) else str(data)
logging.info(f"收到线径数据: {data_str} 来自 {port_name}") logging.info(f"收到线径数据: {data_str} 来自 {port_name}")
# 提取线径值,格式为"线径数据: xxx" # 提取线径值
if "线径数据:" in data_str: if "线径数据:" in data_str:
value_str = data_str.split("线径数据:")[1].strip() value_str = data_str.split("线径数据:")[1].strip()
try: try:
# 转换为浮点数除以10000并保留三位小数
xj_value = round(float(value_str)/10000, 3) xj_value = round(float(value_str)/10000, 3)
# 更新UI显示实时回显最新测量值
self.statusBar().showMessage(f"线径数据: {xj_value:.3f}", 2000) 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'): if not hasattr(self, '_diameter_measurements'):
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: if len(self._diameter_measurements) < 5:
self._diameter_measurements.append(xj_value) self.statusBar().showMessage(f"线径数据收集中: {xj_value:.3f} ({len(self._diameter_measurements)}/5)", 2000)
self._diameter_measurement_count += 1 return
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)}")
# 如果当前值为0并且有足够的测量历史检查是否可以保存最终值 # 检查稳定性
elif xj_value == 0 and len(self._diameter_measurements) >= 5: measurements = self._diameter_measurements[-5:]
logging.info(f"检测到线径值变为0使用最后一次有效值 {self._diameter_measurements[-1]:.3f} 作为最终结果") 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次测量 data_row = None
min_value = min(measurements) for row in range(2, self.process_table.rowCount()):
max_value = max(measurements) cell_item = self.process_table.item(row, xj_column)
avg_value = sum(measurements) / len(measurements) 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 if data_row is None:
current_row = self.process_table.currentRow()
# 检查是否在4%误差范围内 data_row = current_row if current_row >= 2 else 2
if max_value - min_value <= error_range: logging.info(f"未找到没有线径数据的行,使用当前选中行或第一个数据行: {data_row}")
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("未找到线径对应的检验项配置")
else: 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_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: except ValueError:
logging.warning(f"线径数据格式错误: {value_str}") logging.warning(f"线径数据格式错误: {value_str}")
else: else:
@ -2889,6 +2848,34 @@ class MainWindow(MainWindowUI):
except Exception as e: except Exception as e:
logging.error(f"处理线径数据失败: {str(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): def on_scanner_data_received(self, port_name, data):
"""扫码器数据接收回调函数 """扫码器数据接收回调函数
@ -2977,21 +2964,41 @@ class MainWindow(MainWindowUI):
logging.warning("无法添加新行,订单号为空") logging.warning("无法添加新行,订单号为空")
return return
# 查找第一个没有该检测数据的行 # 对于线径数据,如果有活跃会话,优先使用会话锁定的工程号
data_row = None if data_type == 'xj' and hasattr(self, '_diameter_session_active') and self._diameter_session_active and self._diameter_session_order_id:
for row in range(2, self.process_table.rowCount()): # 查找会话工程号对应的行
cell_item = self.process_table.item(row, col_index) data_row = None
if not cell_item or not cell_item.text().strip() or (data_type == 'xj' and cell_item.text().strip() == '0'): for row in range(2, self.process_table.rowCount()):
data_row = row order_id_item = self.process_table.item(row, 1)
break if order_id_item and order_id_item.text().strip() == self._diameter_session_order_id:
data_row = row
# 如果没有找到没有该检测数据的行,使用当前选中行或第一个数据行 break
if data_row is None:
current_row = self.process_table.currentRow() if data_row is not None:
data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行索引为2 logging.info(f"使用线径会话锁定的工程号 {self._diameter_session_order_id} 对应的行 {data_row}")
logging.info(f"未找到没有{data_type}数据的行,使用当前选中行或第一个数据行: {data_row}") else:
logging.warning(f"找不到线径会话锁定的工程号 {self._diameter_session_order_id} 对应的行,将使用默认行选择逻辑")
# 继续使用默认逻辑
data_row = None
else: 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) order_id_item = self.process_table.item(data_row, 1)
@ -3039,13 +3046,13 @@ class MainWindow(MainWindowUI):
# 保存到数据库,但只在非加载状态下 # 保存到数据库,但只在非加载状态下
if not self._loading_data_in_progress: if not self._loading_data_in_progress:
tray_id = self.tray_edit.currentText() 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会处理 # 不需要在这里主动触发数据重新加载因为handle_inspection_cell_changed会处理
# 重新连接信号 # 重新连接信号
self.process_table.cellChanged.connect(self.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: except Exception as e:
logging.error(f"设置检验项值失败: {str(e)}") logging.error(f"设置检验项值失败: {str(e)}")