diff --git a/config/app_config.json b/config/app_config.json index 2786c98..3871033 100644 --- a/config/app_config.json +++ b/config/app_config.json @@ -90,6 +90,6 @@ } }, "electricity": { - "auto_start": true + "auto_start": false } } \ No newline at end of file diff --git a/db/jtDB.db b/db/jtDB.db index a9149d9..80bc8aa 100644 Binary files a/db/jtDB.db and b/db/jtDB.db differ diff --git a/ui/settings_ui.py b/ui/settings_ui.py index 451cd96..c946665 100644 --- a/ui/settings_ui.py +++ b/ui/settings_ui.py @@ -502,7 +502,7 @@ class SettingsUI(QWidget): # 添加到选项卡 self.tab_widget.addTab(self.electricity_tab, "电力监控") - + def create_pallet_type_tab(self): # 托盘类型设置选项卡 self.pallet_type_tab = QWidget() diff --git a/utils/serial_manager.py b/utils/serial_manager.py index 0f01dce..151431a 100644 --- a/utils/serial_manager.py +++ b/utils/serial_manager.py @@ -240,7 +240,7 @@ class SerialManager: parity_constant = serial.PARITY_EVEN else: parity_constant = serial.PARITY_NONE - + # 打开串口 self.serial_ports[port_name] = serial.Serial( port=port_name, @@ -276,12 +276,12 @@ class SerialManager: # 其他类型使用通用处理 thread = threading.Thread(target=self._read_thread, args=(port_name,)) - thread.daemon = True - thread.start() - self.read_threads[port_name] = thread + thread.daemon = True + thread.start() + self.read_threads[port_name] = thread return True - + except Exception as e: logging.error(f"打开串口失败: {port_name}, 错误: {e}") # 确保清理好资源 @@ -298,7 +298,7 @@ class SerialManager: self.read_threads.pop(port_name, None) return False - + def close_port(self, port_name: str) -> bool: """ 关闭串口 diff --git a/widgets/main_window.py b/widgets/main_window.py index 1834445..68138fc 100644 --- a/widgets/main_window.py +++ b/widgets/main_window.py @@ -58,12 +58,22 @@ class MainWindow(MainWindowUI): def __init__(self, user_id=None, user_name=None, corp_name=None, corp_id=None): """初始化主窗口""" - super().__init__(username=user_name) # 将 user_name 作为 username 参数传递给父类 + super().__init__(user_id) + + # 初始化用户信息 self.user_id = user_id self.user_name = user_name self.corp_name = corp_name self.corp_id = corp_id + # 初始化系统变量 + self._current_weight = 0.0 # 当前重量 + self._last_weight_time = 0 # 上次称重时间 + self._stability_check_timer = None # 稳定性检查定时器 + self._weight_stable_threshold = 2 # 重量稳定阈值(秒) + self._weight_processed = False # 新增:标记当前重量是否已处理,避免重复处理 + self._last_processed_weight = 0.0 # 新增:记录上次处理的重量 + # 初始化数据加载状态标志 self._loading_data_in_progress = False # 数据加载状态标志,防止循环调用 self._current_order_code = None # 存储当前订单号 @@ -421,7 +431,7 @@ class MainWindow(MainWindowUI): from widgets.loading_dialog_widget import LoadingDialog dialog = LoadingDialog(parent=self,user_id=self.user_id,user_name=self.user_name,corp_id=self.corp_id) - # 如果已有上料信息,则填充到对话框 + # 如果已有上料信息,作为参考显示在对话框中,但允许用户修改 if self._loading_info and self._current_stow_num > 0: dialog.order_input.setText(self._loading_info.get('order_code', '')) dialog.tray_input.setText(self._loading_info.get('tray_code', '')) @@ -429,11 +439,7 @@ class MainWindow(MainWindowUI): dialog.quantity_value.setText(self._loading_info.get('quantity_value', '--')) dialog.weight_value.setText(self._loading_info.get('weight_value', '--')) dialog.pallet_tier_value.setText(str(self._current_stow_num)) - # 只有当上料任务正在进行时才禁用输入框 - if self._is_loading_active: - # 禁用输入框,防止修改 - dialog.order_input.setEnabled(False) - dialog.tray_input.setEnabled(False) + # 不禁用输入框,允许用户修改 # 连接订单号信号 dialog.order_code_signal.connect(self.handle_order_code_received) @@ -454,18 +460,17 @@ class MainWindow(MainWindowUI): if stow_num == "--" or not stow_num: QMessageBox.warning(self, "错误", "未获取到托盘料信息,请重试") return - - # 如果是新的上料操作(没有存储的信息或层数为0) - if not self._loading_info or self._current_stow_num == 0: - self._current_stow_num = int(stow_num) - # 保存上料信息 - self._loading_info = { - 'order_code': dialog.order_input.text(), - 'tray_code': dialog.tray_input.text(), - 'axis_value': dialog.axis_value.text(), - 'quantity_value': dialog.quantity_value.text(), - 'weight_value': dialog.weight_value.text(), - } + + # 始终使用用户最新输入的信息 + self._current_stow_num = int(stow_num) + # 保存上料信息 + self._loading_info = { + 'order_code': dialog.order_input.text(), + 'tray_code': dialog.tray_input.text(), + 'axis_value': dialog.axis_value.text(), + 'quantity_value': dialog.quantity_value.text(), + 'weight_value': dialog.weight_value.text(), + } # 执行Modbus操作 modbus = ModbusUtils() @@ -495,34 +500,29 @@ class MainWindow(MainWindowUI): dialog = UnloadingDialog(self, self.user_id) - # 如果是同一下料任务(_current_unload_info不为空),则回显信息 + # 如果有之前的下料信息,作为参考显示在对话框中,但允许用户修改 if self._current_unload_info: - # 关键:确保回显到对话框的是固定的总层数 - self._current_unload_info['tier'] = str(self._total_unload_num) dialog.set_unloading_info(self._current_unload_info) - logging.info(f"回显下料信息:当前层数={self._current_unload_num}, 总层数={self._total_unload_num}") + logging.info(f"显示之前的下料信息作为参考") if dialog.exec_() == QDialog.Accepted: + # 获取用户最新输入的下料信息 unloading_info = dialog.get_unloading_info() - # 仅当开始一次全新的下料时(没有进行中的任务),才设置总层数 - is_new_task = not self._current_unload_info or self._current_unload_num == 0 - if is_new_task: - self._total_unload_num = int(unloading_info.get('tier', '3')) - self._current_unload_num = 1 - self._current_unload_info = unloading_info - logging.info(f"新的下料任务开始:总层数={self._total_unload_num}, 当前层数={self._current_unload_num}") + # 始终使用用户最新输入的信息 + self._total_unload_num = int(unloading_info.get('tier', '3')) + self._current_unload_num = 1 # 从第一层开始 + self._current_unload_info = unloading_info + logging.info(f"下料任务设置:总层数={self._total_unload_num}, 当前层数={self._current_unload_num}") - # 将初始层数(1)写入寄存器 - modbus = ModbusUtils() - client = modbus.get_client() - try: - modbus.write_register_until_success(client, 4, self._current_unload_num) - logging.info(f"下料初始化成功:层数 {self._current_unload_num} 已写入寄存器4") - finally: - modbus.close_client(client) - else: - logging.info(f"继续下料任务:总层数={self._total_unload_num}, 当前层数={self._current_unload_num}") + # 将初始层数(1)写入寄存器 + modbus = ModbusUtils() + client = modbus.get_client() + try: + modbus.write_register_until_success(client, 4, self._current_unload_num) + logging.info(f"下料初始化成功:层数 {self._current_unload_num} 已写入寄存器4") + finally: + modbus.close_client(client) # 统一更新UI显示 tray_code = self._current_unload_info.get('tray_code', '') @@ -536,42 +536,42 @@ class MainWindow(MainWindowUI): def restore_start_button_style(self): """恢复开始按钮的原始样式""" try: - self.start_button.setStyleSheet(""" + # 使用与main_window_ui.py中初始化时相同的样式,只恢复背景色 + button_style = """ QPushButton { - background-color: transparent; - color: black; - border: 1px solid #4caf50; + padding: 8px 16px; + font-weight: bold; border-radius: 4px; - padding: 2px 10px; - min-height: 29px; - max-height: 29px; + border: 1px solid #4caf50; } QPushButton:hover { - background-color: #f0f0f0; + background-color: #d7eeda; } - """) + """ + self.start_button.setStyleSheet(button_style) + logging.info("已恢复开始按钮原始样式") except Exception as e: - logging.error(f"{str(e)}") + logging.error(f"恢复开始按钮样式失败: {str(e)}") def fill_start_button_style(self): """填充开始按钮样式 - 绿色背景,白色字体""" try: - # 填充按钮样式 - 绿色背景,白色字体,高度与下料按钮一致 - self.start_button.setStyleSheet(""" + # 使用与main_window_ui.py中初始化时相同的样式,只改变背景色和文字颜色 + button_style = """ QPushButton { + padding: 8px 16px; + font-weight: bold; + border-radius: 4px; background-color: #4caf50; color: white; border: 1px solid #4caf50; - border-radius: 4px; - padding: 2px 10px; - min-height: 29px; - max-height: 29px; } QPushButton:hover { - background-color: #4caf50; + background-color: #45a049; color: white; } - """) + """ + self.start_button.setStyleSheet(button_style) logging.info("已填充开始按钮样式") except Exception as e: logging.error(f"填充开始按钮样式失败: {str(e)}") @@ -1723,6 +1723,11 @@ class MainWindow(MainWindowUI): logging.info(f"[显示] 称重数据: {weight_in_kg}kg (原始值: {weight_in_g}g)") self.weight_label.setText(f"重量: {weight_in_kg}kg") + # 检测重量从接近0到较大值的变化,判断为新产品 + if self._current_weight is not None and self._current_weight < 0.1 and weight_in_kg > 0.5: + logging.info(f"检测到新产品放上,重量从 {self._current_weight}kg 变为 {weight_in_kg}kg") + self._weight_processed = False # 重置处理标记,允许处理新产品 + # 更新当前重量和时间 self._current_weight = weight_in_kg self._last_weight_time = current_time @@ -1737,25 +1742,25 @@ class MainWindow(MainWindowUI): self._stability_check_timer.setSingleShot(True) # 单次触发 self._stability_check_timer.timeout.connect(lambda: self._check_weight_stability(weight_in_kg)) self._stability_check_timer.start(self._weight_stable_threshold * 1000) # 转换为毫秒 - # 获取当前选中的行或第一个数据行 + + # 尝试获取表格行数据,用于日志记录 current_row = self.process_table.currentRow() data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行(索引为2) - tray_id = self.tray_edit.currentText() - # 确保行存在 + # 记录表格行状态,仅用于日志记录,不影响后续处理 if data_row >= self.process_table.rowCount(): logging.warning(f"选中的行 {data_row} 超出了表格范围") - return - # 获取工程号 - gc_note = self.process_table.item(data_row, 1) - if not gc_note: - logging.warning("无法获取工程号") - return - - gc_note = gc_note.text().strip() - if not gc_note: - logging.warning("工程号为空") - return + else: + # 获取工程号,仅用于日志记录 + gc_note_item = self.process_table.item(data_row, 1) + if gc_note_item: + gc_note = gc_note_item.text().strip() + if gc_note: + logging.info(f"当前处理的工程号: {gc_note}, 行: {data_row}") + else: + logging.warning("工程号为空") + else: + logging.warning("无法获取工程号") except Exception as e: logging.error(f"处理称重数据时发生错误: {str(e)}") # 确保重新连接信号 @@ -1774,9 +1779,27 @@ class MainWindow(MainWindowUI): # 如果当前重量与定时器启动时的重量相同,说明这段时间内没有新的重量数据 if self._current_weight == original_weight_kg: logging.info(f"重量 {original_weight_kg}kg 在{self._weight_stable_threshold}秒内保持稳定") + + # 如果这个重量与上一次处理的重量接近(±0.1kg),且标记已处理,则跳过 + if self._weight_processed and abs(original_weight_kg - self._last_processed_weight) < 0.1: + logging.info(f"跳过处理:重量 {original_weight_kg}kg 与上次处理的重量 {self._last_processed_weight}kg 接近且已处理") + return + + # 称重稳定后,给寄存器 D10 为 1 表示已经称重完成 + modbus = ModbusUtils() + client = modbus.get_client() + modbus.write_register_until_success(client, 10, 1) + modbus.close_client(client) + + # 处理稳定重量 self._process_stable_weight(original_weight_kg) # 调用打印方法 self._print_weight_label(original_weight_kg) + + # 设置已处理标记和上次处理的重量 + self._weight_processed = True + self._last_processed_weight = original_weight_kg + logging.info(f"已标记重量 {original_weight_kg}kg 为已处理") else: logging.info(f"重量在{self._weight_stable_threshold}秒内发生变化,从 {original_weight_kg}kg 变为 {self._current_weight}kg") except Exception as e: @@ -1794,6 +1817,11 @@ class MainWindow(MainWindowUI): weight_kg: 稳定的重量值(千克) """ try: + # 忽略接近0的重量值,这可能表示产品已被移除 + if weight_kg < 0.1: # 小于100g的重量视为无效 + logging.info(f"忽略接近零的重量值: {weight_kg}kg,可能表示产品已被移除") + return + # 获取数据行数 if self.process_table.rowCount() <= 2: # 没有数据行 logging.warning("没有可用的数据行来写入称重数据") @@ -1807,15 +1835,22 @@ class MainWindow(MainWindowUI): # 计算净重列索引 - 净重位置在检验列之后的第三列(称重后面) net_weight_col = 2 + len(enabled_configs) + 2 - # 获取当前选中的行或第一个数据行 - current_row = self.process_table.currentRow() - data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行(索引为2) + # 查找第一个没有称重数据的行 + data_row = None + for row in range(2, self.process_table.rowCount()): + weight_item = self.process_table.item(row, weight_col) + if not weight_item or not weight_item.text().strip(): + 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_row}") + else: + logging.info(f"找到没有称重数据的行: {data_row}") - # 确保行存在 - if data_row >= self.process_table.rowCount(): - logging.warning(f"选中的行 {data_row} 超出了表格范围") - return - # 获取工程号 gc_note = self.process_table.item(data_row, 1) if not gc_note: @@ -1868,7 +1903,7 @@ class MainWindow(MainWindowUI): inspection_dao = InspectionDAO() # 调用接口 gc_api = GcApi() - axios_num = self.get_axios_num_by_order_id(self._current_order_code)+1 + axios_num = self.get_axios_num_by_order_id(self._current_order_code) + 1 # 获取订单信息和其他信息,两者都已经是字典格式 info = {} order_info = inspection_dao.get_order_info(self._current_order_code) @@ -1986,7 +2021,7 @@ class MainWindow(MainWindowUI): pass # 创建并设置贴标单元格 - label_item = QTableWidgetItem(axios_num) + label_item = QTableWidgetItem(str(axios_num)) label_item.setTextAlignment(Qt.AlignCenter) # 写入单元格 @@ -2221,11 +2256,18 @@ class MainWindow(MainWindowUI): self.error_1 = error_code self._update_error_status() - # 如果有故障,显示提示 - if error_code in (2, 3): + # 如果有故障,显示提示(对任何错误码都弹框) + if error_code > 0: QMessageBox.warning(self, "机器人视觉报警", f"机器人视觉报警: {detailed_desc}") - # 移除在下料区域显示异常信息的代码 + # 获取Modbus连接 + modbus = ModbusUtils() + client = modbus.get_client() + # 根据错误码可以添加不同的处理逻辑 + # 这里先简单处理,对所有错误都复位相关寄存器 + modbus.write_register_until_success(client, 2, 0) + modbus.write_register_until_success(client, 0, 0) + modbus.close_client(client) @Slot(int, str) def handle_error_2(self, error_code, error_desc): @@ -2238,9 +2280,19 @@ class MainWindow(MainWindowUI): self.error_2 = error_code self._update_error_status() - # 如果有故障,显示提示 - # 移除在下料区域显示异常信息的代码 - + # 如果有故障,显示提示(对任何错误码都弹框) + if error_code > 0: + QMessageBox.warning(self, "滚筒线报警", f"滚筒线报警: {detailed_desc}") + # 获取Modbus连接 + modbus = ModbusUtils() + client = modbus.get_client() + + # 根据错误码可以添加不同的处理逻辑 + # 这里先简单处理,对所有错误都复位相关寄存器 + modbus.write_register_until_success(client, 3, 0) + modbus.write_register_until_success(client, 4, 0) + modbus.close_client(client) + @Slot(int, str) def handle_error_3(self, error_code, error_desc): """拆码垛报警""" @@ -2263,7 +2315,7 @@ class MainWindow(MainWindowUI): elif error_code == 2: QMessageBox.warning(self, "异常", f"异常: {detailed_desc}") modbus.write_register_until_success(client, 3, 0) - modbus.write_register_until_success(client, 0, 0) + modbus.write_register_until_success(client, 4, 0) modbus.close_client(client) @Slot(int) @@ -2558,36 +2610,6 @@ class MainWindow(MainWindowUI): 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') @@ -2606,6 +2628,43 @@ class MainWindow(MainWindowUI): logging.warning(f"未找到{data_type}对应的列索引") return + # 检查表格是否有数据行 + 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 + + # 查找第一个没有该检测数据的行 + 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(): + 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) + if not order_id_item: + logging.warning("无法获取工程号") + return + + order_id = order_id_item.text().strip() + if not order_id: + logging.warning("工程号为空") + return + # 暂时断开信号连接,避免触发cellChanged信号 try: self.process_table.cellChanged.disconnect(self.handle_inspection_cell_changed) diff --git a/widgets/serial_settings_widget.py b/widgets/serial_settings_widget.py index 5e5f632..b4ee05d 100644 --- a/widgets/serial_settings_widget.py +++ b/widgets/serial_settings_widget.py @@ -103,7 +103,7 @@ class SerialSettingsWidget(SerialSettingsUI): else: # 如果之前没有选择,则设为"不使用" self.scanner_port_combo.setCurrentIndex(0) - + logging.info(f"已刷新串口列表,找到 {len(ports)} 个串口") except Exception as e: logging.error(f"刷新串口列表失败: {e}") @@ -361,7 +361,7 @@ class SerialSettingsWidget(SerialSettingsUI): QMessageBox.information(self, "测试成功", "串口打开成功") # 关闭串口 - self.serial_manager.close_port(port) + self.serial_manager.close_port(port) else: QMessageBox.critical(self, "测试失败", f"无法打开串口 {port}")