diff --git a/config/app_config.json b/config/app_config.json index f1b5f6c..f49e74a 100644 --- a/config/app_config.json +++ b/config/app_config.json @@ -7,7 +7,7 @@ "enable_keyboard_listener": false, "enable_camera": false }, - "base_url": "http://localhost:8085", + "base_url": "https://jsjtnew.tengzhicn.com", "mode": "api" }, "apis": { diff --git a/dao/inspection_dao.py b/dao/inspection_dao.py index 03d9e9b..89d27c2 100644 --- a/dao/inspection_dao.py +++ b/dao/inspection_dao.py @@ -180,7 +180,7 @@ class InspectionDAO: # 构建SQL sql = f""" - UPDATE inspection_config + UPDATE wsbz_inspection_config SET {', '.join(update_fields)} WHERE id = ? """ diff --git a/db/jtDB.db b/db/jtDB.db index ea75ffd..fcdd03a 100644 Binary files a/db/jtDB.db and b/db/jtDB.db differ diff --git a/from pymodbus.py b/from pymodbus.py index 37c95f2..340e121 100644 --- a/from pymodbus.py +++ b/from pymodbus.py @@ -2,7 +2,7 @@ from pymodbus.client import ModbusTcpClient import time client = ModbusTcpClient('localhost', port=5020) client.connect() -client.write_registers(address=11, values=[12900]) +client.write_registers(address=11, values=[25700]) # client.write_registers(address=3, values=[0]) # time.sleep(2) # client.write_registers(address=0, values=[0]) @@ -13,7 +13,7 @@ client.write_registers(address=11, values=[12900]) # 贴标完成 # client.write_registers(address=24, values=[1]) # client.write_registers(address=2, values=[0]) -client.write_registers(address=13, values=[0]) +client.write_registers(address=13, values=[1]) # time.sleep(2) # client.write_registers(address=20, values=[0]) # time.sleep(3) diff --git a/widgets/main_window.py b/widgets/main_window.py index 2a1c393..dbf6a46 100644 --- a/widgets/main_window.py +++ b/widgets/main_window.py @@ -77,6 +77,30 @@ class MainWindow(MainWindowUI): self.corp_name = corp_name self.corp_id = corp_id + # 初始化已处理的工程号集合,用于避免重复处理 + self._processed_gc_notes = set() + + # 初始化加载信息 + self._loading_info = {} + + # 初始化数据加载标志 + self._loading_data_in_progress = False + + # 初始化表格更新锁 + self._table_updating = False + + # 初始化当前处理行 + self._current_processing_row = None + + # 初始化当前订单号 + self._current_order_code = "" + + # 初始化当前箱号 + self._current_spack = "" + + # 初始化检验配置 + self.inspection_manager = InspectionConfigManager() + # 初始化焦点跟踪器 from utils.focus_tracker import FocusTracker self.focus_tracker = FocusTracker.get_instance() @@ -127,6 +151,9 @@ class MainWindow(MainWindowUI): self._weight_stable_threshold = 2 # 重量稳定阈值(秒) self._stability_check_timer = None # 用于检查重量稳定性的定时器 + # 初始化工程号对应强度数据 + self._order_strength_data = {} # 存储工程号对应的强度数据 + # 设置窗口标题 if user_name and corp_name: self.setWindowTitle(f"腾智微丝产线包装系统 ({corp_name})") @@ -951,6 +978,8 @@ class MainWindow(MainWindowUI): if response.get("status", False): gc_info = response.get("data", {}) self._current_gc_qd = gc_info.get("qd","") + # 保存工程号对应的强度数据 + self._order_strength_data[gc_note] = self._current_gc_qd self._current_gc_sc_gch = gc_info.get("sc_gch", "") # 保存sc_gch字段 # 先获取当前 info_table 已有的数据 order_info = {} @@ -1002,7 +1031,7 @@ class MainWindow(MainWindowUI): self.add_new_inspection_row(gc_note, self._current_order_code) else: # 弹框提示,获取接口中的 message - QMessageBox.warning(self, "提示", "获取工程号信息失败") + QMessageBox.warning(self, "提示", "未查询到工程信息!") else: # 直接添加新行 self.add_new_inspection_row(gc_note, self._current_order_code) @@ -1118,6 +1147,16 @@ class MainWindow(MainWindowUI): # 设置单元格属性以标识其关联的检验项 item.setData(Qt.UserRole, config.get('id')) self.process_table.setItem(data_start_row, col_index, item) + + # 如果是强度(qd)配置,并且有强度值,则设置强度值 + if config.get('name') == 'qd' and hasattr(self, '_current_gc_qd') and self._current_gc_qd: + item.setText(str(self._current_gc_qd)) + logging.info(f"已设置工程号 {gc_note} 的强度值: {self._current_gc_qd}") + # 如果是强度(qd)配置,并且有工程号对应的强度值,则设置强度值 + elif config.get('name') == 'qd' and hasattr(self, '_order_strength_data') and gc_note in self._order_strength_data: + qd_value = self._order_strength_data.get(gc_note, "") + item.setText(str(qd_value)) + logging.info(f"已从强度数据字典中设置工程号 {gc_note} 的强度值: {qd_value}") # 包装列设置为可编辑状态 packaging_start_col = 2 + len(enabled_configs) @@ -1145,8 +1184,35 @@ class MainWindow(MainWindowUI): # 只为没有自动填充值的配置创建空记录 for config in enabled_configs: config_name = config.get('name') + # 如果是强度(qd)配置,并且有强度值,则设置强度值 + if config_name == 'qd' and hasattr(self, '_current_gc_qd') and self._current_gc_qd: + data = [{ + 'position': config.get('position'), + 'config_id': config.get('id'), + 'value': str(self._current_gc_qd), + 'status': 'pass', # 设置为通过状态 + 'remark': '', + 'tray_id': tray_id, + 'package_id': self._current_spack or tray_id + }] + inspection_dao.save_inspection_data(self._current_order_code, gc_note, data) + logging.info(f"已保存工程号 {gc_note} 的强度值到数据库: {self._current_gc_qd}") + # 如果是强度(qd)配置,并且有工程号对应的强度值,则设置强度值 + elif config_name == 'qd' and hasattr(self, '_order_strength_data') and gc_note in self._order_strength_data: + qd_value = self._order_strength_data.get(gc_note, "") + data = [{ + 'position': config.get('position'), + 'config_id': config.get('id'), + 'value': str(qd_value), + 'status': 'pass', # 设置为通过状态 + 'remark': '', + 'tray_id': tray_id, + 'package_id': self._current_spack or tray_id + }] + inspection_dao.save_inspection_data(self._current_order_code, gc_note, data) + logging.info(f"已从强度数据字典中保存工程号 {gc_note} 的强度值到数据库: {qd_value}") # 如果order_info中没有对应的键,或者order_info为None - if not order_info or config_name not in order_info: + elif not order_info or config_name not in order_info: data = [{ 'position': config.get('position'), 'config_id': config.get('id'), @@ -1187,35 +1253,28 @@ class MainWindow(MainWindowUI): def handle_inspection_cell_changed(self, row, column): - """处理微丝包装单元格内容变更 - - Args: - row: 行索引 - column: 列索引 - """ - # 创建唯一键,包含行、列、工程号 + """处理检验表格单元格内容变更事件""" try: - # 获取工程号,用于创建更精确的唯一键 + # 获取工程号 order_item = self.process_table.item(row, 1) gc_note = order_item.text().strip() if order_item else "" - - # 创建包含行、列、工程号的唯一键 - cell_key = f"{row}_{column}_{gc_note}" - current_time = time.time() - - # 获取上次处理时间 - last_process_times = getattr(self, '_last_cell_process_times', {}) - last_process_time = last_process_times.get(cell_key, 0) - - # 如果同一单元格在1秒内被处理过,则忽略 - if current_time - last_process_time < 1.0: - logging.info(f"防抖:跳过重复处理单元格 [{row}, {column}], 工程号={gc_note},间隔小于1秒") + if not gc_note: return - # 更新处理时间 - if not hasattr(self, '_last_cell_process_times'): - self._last_cell_process_times = {} - self._last_cell_process_times[cell_key] = current_time + # 工程号级别的防抖 + current_time = time.time() + last_process_times = getattr(self, '_last_gc_process_times', {}) + last_process_time = last_process_times.get(gc_note, 0) + + # 如果同一工程号在1秒内被处理过,则忽略 + if current_time - last_process_time < 1.5: + logging.info(f"防抖:跳过重复处理工程号 {gc_note},间隔小于1秒") + return + + # 更新工程号的最后处理时间 + if not hasattr(self, '_last_gc_process_times'): + self._last_gc_process_times = {} + self._last_gc_process_times[gc_note] = current_time # 只处理数据行的检验列变更 if row < 2: # 忽略表头行 @@ -1225,10 +1284,6 @@ class MainWindow(MainWindowUI): if column < 2: return - # 获取工程号 - if not gc_note: - return - # 获取托盘号 tray_id = self.tray_edit.text() @@ -1341,9 +1396,9 @@ class MainWindow(MainWindowUI): last_save_time = getattr(self, '_last_save_time', 0) last_save_key = getattr(self, '_last_save_key', '') - # 如果是相同的数据且间隔小于0.5秒,则忽略此次保存 - if last_save_key == save_key and current_time - last_save_time < 0.5: - logging.info(f"防抖机制:跳过保存相同数据 {save_key},间隔小于0.5秒") + # 如果是相同的数据且间隔小于1秒,则忽略此次保存 + if last_save_key == save_key and current_time - last_save_time < 1: + logging.info(f"防抖机制:跳过保存相同数据 {save_key},间隔小于1秒") return # 更新最后保存的数据和时间 @@ -1666,6 +1721,12 @@ class MainWindow(MainWindowUI): # 检查表格中是否已有线径数据 xj_item = self.process_table.item(row_idx, xj_col) if not xj_item or not xj_item.text().strip(): + # 检查产品状态 + current_status = inspection_dao.get_product_status(self._current_order_code, gc_note, tray_id) + if current_status in ['labeled', 'weighed']: + logging.info(f"跳过加载线径数据:工程号 {gc_note} 已处于 {current_status} 状态") + continue + # 如果表格中没有线径数据,尝试从数据库加载 xj_data = inspection_dao.get_inspection_data_by_config( self._current_order_code, gc_note, tray_id, position=config.get('position'), config_id=config.get('id') @@ -1678,6 +1739,32 @@ class MainWindow(MainWindowUI): logging.info(f"从数据库加载线径数据: {xj_data.get('value')} 到工程号 {gc_note}") break + # 额外加载该工程号的强度(qd)数据 + for i, config in enumerate(enabled_configs): + if config.get('name') == 'qd': + qd_col = 2 + i + # 检查表格中是否已有强度数据 + qd_item = self.process_table.item(row_idx, qd_col) + if not qd_item or not qd_item.text().strip(): + # 如果表格中没有强度数据,尝试从数据库加载 + qd_data = inspection_dao.get_inspection_data_by_config( + self._current_order_code, gc_note, tray_id, position=config.get('position'), config_id=config.get('id') + ) + if qd_data and qd_data.get('value'): + # 设置强度数据到表格 + qd_item = QTableWidgetItem(str(qd_data.get('value'))) + qd_item.setTextAlignment(Qt.AlignCenter) + self.process_table.setItem(row_idx, qd_col, qd_item) + logging.info(f"从数据库加载强度数据: {qd_data.get('value')} 到工程号 {gc_note}") + # 如果数据库中没有强度数据,但有工程号对应的强度值,则设置强度值 + elif hasattr(self, '_order_strength_data') and gc_note in self._order_strength_data: + qd_value = self._order_strength_data.get(gc_note, "") + qd_item = QTableWidgetItem(str(qd_value)) + qd_item.setTextAlignment(Qt.AlignCenter) + self.process_table.setItem(row_idx, qd_col, qd_item) + logging.info(f"从强度数据字典中加载强度数据: {qd_value} 到工程号 {gc_note}") + break + row_idx += 1 # 设置表格为可编辑状态 @@ -1705,6 +1792,9 @@ class MainWindow(MainWindowUI): order_id: 工程号 tray_id: 托盘号 axios_num: 轴号,如果为None则使用数据库中的轴号 + + Returns: + bool: 是否成功加载记录(True表示加载成功,False表示记录已存在或加载失败) """ try: from dao.inspection_dao import InspectionDAO @@ -1713,14 +1803,14 @@ class MainWindow(MainWindowUI): # 首先检查该工程号的包装记录是否已存在 if inspection_dao.check_package_record_exists(order_id, gc_note, tray_id, package_id): logging.warning(f"工程号 {gc_note} 托盘号 {tray_id} 的包装记录已存在,跳过添加") - return + return False # 获取该工程号的所有检验数据 inspection_data = inspection_dao.get_inspection_data_by_order(order_id, gc_note, tray_id, package_id) if not inspection_data: logging.warning(f"未找到工程号 {gc_note} 托盘号 {tray_id} 的检验数据") - return + return False # 使用传入的轴号,如果没有传入则使用数据库中的轴号 if axios_num is not None: @@ -1742,7 +1832,7 @@ class MainWindow(MainWindowUI): # 只要贴标字段有值,就可以写入包装记录 if label_value is None or label_value == "": logging.warning(f"工程号 {order_id} 托盘号 {tray_id} 的贴标字段为空,不添加到包装记录") - return + return False # 获取当前时间作为完成时间 finish_time = datetime.now() @@ -1759,10 +1849,12 @@ class MainWindow(MainWindowUI): self._loading_data_in_progress = False logging.info(f"已将工程号 {order_id} 托盘号 {tray_id} 的检验数据添加到包装记录并回显") + return True except Exception as e: logging.error(f"加载已完成检验数据到包装记录失败: {str(e)}") QMessageBox.warning(self, "加载失败", f"加载已完成检验数据到包装记录失败: {str(e)}") + return False def show_pack_item(self, update_stats=True): """显示包装记录 @@ -2424,8 +2516,7 @@ class MainWindow(MainWindowUI): logging.info(f"开始处理稳定重量: {weight_kg}kg") # 这里不再写入D10=1,全部交由_process_stable_weight处理 self._process_stable_weight(weight_kg) - # 调用打印方法 - self._print_weight_label(weight_kg) + # 设置已处理标记和上次处理的重量 self._weight_processed = True self._last_processed_weight = weight_kg @@ -2459,7 +2550,6 @@ class MainWindow(MainWindowUI): try: # 忽略接近0的重量值,这可能表示产品已被移除 if weight_kg < 0.1: # 小于100g的重量视为无效 - logging.info(f"忽略接近零的重量值: {weight_kg}kg,可能表示产品已被移除") return # 获取数据行数 @@ -2626,6 +2716,11 @@ class MainWindow(MainWindowUI): net_weight_item = QTableWidgetItem(str(net_weight_kg)) net_weight_item.setTextAlignment(Qt.AlignCenter) self.process_table.setItem(data_row, net_weight_col, net_weight_item) + + # 更新产品状态为weighed + inspection_dao.update_product_status(self._current_order_code, gc_note, tray_id, 'weighed') + logging.info(f"工程号 {gc_note} 的称重已完成,状态更新为weighed") + # 获取轴号,优先使用当前行的轴号 axios_num = self.get_current_row_axios_num(data_row) # 如果开启 api 模式,则调用接口添加到包装记录 @@ -2713,10 +2808,11 @@ class MainWindow(MainWindowUI): info['zh'] = axios_num info['mzl'] = weight_kg info['printsl'] = 1 - logging.info(f"使用的xpack: {xpack}, spack: {info['spack']}") info['pono'] = info.get("pono") or order_info.get('ddmo') info["dycz"] = info.get("cz") - info['qd'] = self._current_gc_qd + # info['qd'] = self._current_gc_qd + # 找到对应工程的强度,并从字典中移除 + # info['qd'] = self._order_strength_data.pop(gc_note) info['sc_gch'] = gc_note order_others_info = inspection_dao.get_order_others_info(gc_note, self._current_order_code, tray_id) if order_others_info: @@ -2757,16 +2853,9 @@ class MainWindow(MainWindowUI): # 保存贴标数据到数据库 self.save_inspection_data(self._current_order_code, gc_note, tray_id, 11, 11, str(axios_num), "pass") - - # 更新产品状态为weighed - inspection_dao.update_product_status(self._current_order_code, gc_note, tray_id, 'weighed') - logging.info(f"工程号 {gc_note} 的称重已完成,状态更新为weighed") - # 重新连接信号 self.process_table.cellChanged.connect(self.handle_inspection_cell_changed) - - logging.info(f"已将稳定的称重数据 {weight_kg}kg 写入行 {data_row}, 列 {weight_col}") - + # 清除当前处理行的跟踪,因为称重完成后需要等待贴标 self._current_processing_row = None @@ -2781,18 +2870,6 @@ class MainWindow(MainWindowUI): # 释放处理锁 self._processing_weight_lock = False - def _print_weight_label(self, weight_kg): - """ - 打印重量标签 注意:目前打印是写入数据库打印,不需要再次调用 - Args: - weight_kg: 稳定的重量值(千克) - """ - try: - logging.info(f"开始打印重量标签,重量:{weight_kg}kg") - # TODO: 实现打印逻辑 - pass - except Exception as e: - logging.error(f"打印重量标签时发生错误: {str(e)}") @Slot(int, str) def handle_label_signal(self, signal, status): @@ -2855,7 +2932,6 @@ class MainWindow(MainWindowUI): if not gc_note: logging.warning("工程号为空") return - # 获取托盘号 tray_id = self.tray_edit.text() @@ -2898,8 +2974,15 @@ class MainWindow(MainWindowUI): logging.info(f"使用写入单元格的轴号: {axios_num_to_use}") # 调用加载到包装记录的方法,传入正确的轴号 - self.load_finished_record_to_package_record(self._current_order_code, gc_note, tray_id, axios_num_to_use, package_id) - logging.info(f"贴标完成,已将工程号 {gc_note} 的记录加载到包装记录,轴号: {axios_num_to_use}") + record_exists = inspection_dao.check_package_record_exists(self._current_order_code, gc_note, tray_id, package_id) + if not record_exists: + self.load_finished_record_to_package_record(self._current_order_code, gc_note, tray_id, axios_num_to_use, package_id) + logging.info(f"贴标完成,已将工程号 {gc_note} 的记录加载到包装记录,轴号: {axios_num_to_use}") + else: + logging.info(f"贴标完成,工程号 {gc_note} 的记录已存在于包装记录中,轴号: {axios_num_to_use}") + + # 将工程号添加到已处理集合中 + self._processed_gc_notes.add(gc_note) # 删除当前处理的行 self.process_table.removeRow(data_row) @@ -2907,7 +2990,7 @@ class MainWindow(MainWindowUI): # 清除当前处理行的跟踪 self._current_processing_row = None - + logging.info(f"已将_current_processing_row重置为空,当前 _current_processing_row 的值是 {self._current_processing_row}") # 复原寄存器 12 为 0 modbus = ModbusUtils() client = modbus.get_client() @@ -4000,6 +4083,11 @@ class MainWindow(MainWindowUI): self.init_seq[tray_id] = 1 logging.info(f"初始化托盘号 {tray_id} 的序号为 1") + # 清除已处理的工程号记录,避免切换托盘后出现误判 + if hasattr(self, '_processed_gc_notes'): + self._processed_gc_notes.clear() + logging.info(f"已清除已处理工程号记录,切换到托盘号: {tray_id}") + # 加载检验数据 self._safe_load_data() @@ -4921,137 +5009,6 @@ class MainWindow(MainWindowUI): logging.error(f"查找下一个处理行时发生错误: {str(e)}") return None - def handle_inspection_cell_changed(self, row, column): - """处理检验表格单元格内容变更事件""" - # 创建唯一键,包含行、列、工程号 - try: - # 获取工程号,用于创建更精确的唯一键 - order_item = self.process_table.item(row, 1) - gc_note = order_item.text().strip() if order_item else "" - - # 创建包含行、列、工程号的唯一键 - cell_key = f"{row}_{column}_{gc_note}" - current_time = time.time() - - # 获取上次处理时间 - last_process_times = getattr(self, '_last_cell_process_times', {}) - last_process_time = last_process_times.get(cell_key, 0) - - # 如果同一单元格在1秒内被处理过,则忽略 - if current_time - last_process_time < 1.0: - logging.info(f"防抖:跳过重复处理单元格 [{row}, {column}], 工程号={gc_note},间隔小于1秒") - return - - # 更新处理时间 - if not hasattr(self, '_last_cell_process_times'): - self._last_cell_process_times = {} - self._last_cell_process_times[cell_key] = current_time - - # 只处理数据行的检验列变更 - if row < 2: # 忽略表头行 - return - - # 忽略首尾两列(序号和工程号) - if column < 2: - return - - # 获取工程号 - if not gc_note: - return - - # 获取托盘号 - tray_id = self.tray_edit.text() - - # 获取启用的检验配置 - enabled_configs = self.inspection_manager.get_enabled_configs() - - # 判断是否是检验列(非包装列) - packaging_start_col = 2 + len(enabled_configs) - - # 获取单元格内容 - cell_item = self.process_table.item(row, column) - if not cell_item: - return - - value = cell_item.text().strip() - - # 默认设置为通过状态 - status = 'pass' - - # 记录当前正在处理的数据类型,用于日志输出 - data_type = "检验" - - if column >= 2 and column < packaging_start_col: - # 是检验列 - config_index = column - 2 - if config_index < len(enabled_configs): - config = enabled_configs[config_index] - data_type = config['display_name'] - - # 显示临时状态消息 - self.statusBar().showMessage(f"正在保存检验数据: {data_type}={value}", 1000) - - # 验证数据有效性 - # 设置单元格颜色为浅绿色,表示已填写 - cell_item.setBackground(QBrush(QColor("#c8e6c9"))) - - # 保持当前状态不变,由状态管理逻辑处理 - from dao.inspection_dao import InspectionDAO - inspection_dao = InspectionDAO() - status = inspection_dao.get_product_status(self._current_order_code, gc_note, tray_id) - self.save_inspection_data(self._current_order_code, gc_note, tray_id, config['position'], config['id'], value, status) - - # 判断是否是包装列 - 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(self._current_order_code, gc_note, tray_id, 11, 11, value, status) - - elif column == packaging_start_col + 1: - # 毛重列 - data_type = "毛重" - self.statusBar().showMessage(f"正在保存称重数据: {value}", 1000) - # 设置单元格颜色为通过 - cell_item.setBackground(QBrush(QColor("#c8e6c9"))) # 浅绿色 - # 保存毛重数据,position和config_id都是12 - self.save_inspection_data(self._current_order_code, gc_note, tray_id, 12, 12, value, status) - 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(self._current_order_code, gc_note, tray_id, 13, 13, value, status) - - # 记录详细日志 - logging.info(f"处理单元格变更: 行={row}, 列={column}, 类型={data_type}, 工程号={gc_note}, 值={value}, 状态={status}") - - # 检查是否完成检验并更新状态 - self.check_inspection_completed(row) - - except Exception as e: - logging.error(f"处理检验单元格变更失败: {str(e)}") - self.statusBar().showMessage(f"处理检验数据失败: {str(e)[:50]}...", 3000) - finally: - # 延迟一段时间后再触发查询,避免频繁刷新UI - # 但要避免在加载过程中触发新的加载 - if not self._loading_data_in_progress: - # 使用类变量保存定时器,避免创建多个定时器 - if hasattr(self, '_load_data_timer') and self._load_data_timer is not None: - self._load_data_timer.stop() - - self._load_data_timer = QTimer() - self._load_data_timer.setSingleShot(True) - self._load_data_timer.timeout.connect(self._safe_load_data) - self._load_data_timer.start(1000) - - - def check_inspection_completed(self, row): """检查行是否有至少一个检验项已完成,如果是则更新状态为inspected