取消开始产测重启操作
authorxue <2144439972@qq.com>
Thu, 23 Apr 2026 02:31:32 +0000 (10:31 +0800)
committerxue <2144439972@qq.com>
Thu, 23 Apr 2026 02:31:32 +0000 (10:31 +0800)
mainwindow.cpp
mainwindow.h

index 9ea6236..48329d6 100644 (file)
@@ -94,6 +94,8 @@ MainWindow::MainWindow(QWidget *parent)
     gSensorIntervalTimer = new QTimer(this);
     currentLcdColor = 0;
     isProductionTestRunning = false;
+    postFactoryVoteSequenceStarted = false;
+    waitingPmaddvoteOkForGreenButton = false;
     currentTestStep = TEST_IDLE;
     lcdTestPopupShown = false;
     motorTestPopupShown = false;
@@ -550,6 +552,14 @@ void MainWindow::readSerialData()
         if (bootDetected && !factoryModeChecked && !isProductionTestRunning) {
             checkFactoryMode(line);
         }
+
+        // 产测已开始:等待开机完成关键字后再进入 PMADDVOTE(与空闲时的 checkBootDetection 使用同一关键字列表)
+        if (isProductionTestRunning && currentTestStep == TEST_WAIT_BOOT) {
+            tryAdvanceProductionAfterBootKeywords(line);
+            if (currentTestStep == TEST_WAIT_BOOT && processLine != line) {
+                tryAdvanceProductionAfterBootKeywords(processLine);
+            }
+        }
         
         // 版本信息获取:当正在获取版本信息时,接收到数据后继续发送下一条指令
         if (versionInfoFetched && currentVersionCommandIndex >= 0 && 
@@ -618,10 +628,13 @@ void MainWindow::readSerialData()
         
         // 4G/PPP 相关解析已移除
         
-        // 检测工厂模式进入确认(用于产测)
-        if (line.contains("fac:enter or exit facotry mode")) {
+        // 检测工厂模式进入确认(用于产测;兼容 facotry/factory 拼写,大小写不敏感)
+        if (line.contains("enter or exit facotry mode", Qt::CaseInsensitive) ||
+            line.contains("enter or exit factory mode", Qt::CaseInsensitive)) {
             checkFactoryModeResult(line);
         }
+
+        tryApplyProductionStartGreenAfterPmaddvoteOk(line);
         
         // 4G/PPP 相关解析已移除
         
@@ -740,6 +753,8 @@ void MainWindow::on_ProductionTestButton_clicked()
         lcdTestTimer->stop();
         motorTestTimer->stop();
         isProductionTestRunning = false;
+        postFactoryVoteSequenceStarted = false;
+        waitingPmaddvoteOkForGreenButton = false;
         currentTestStep = TEST_IDLE;
         ui->statusbar->showMessage("产测已停止", 2000);
         
@@ -753,6 +768,8 @@ void MainWindow::on_ProductionTestButton_clicked()
 
     // 开始产测流程
     isProductionTestRunning = true;
+    postFactoryVoteSequenceStarted = false;
+    waitingPmaddvoteOkForGreenButton = false;
     if (isTestItemEnabled("lcd")) {
         currentTestStep = TEST_LCD;
         currentLcdColor = 1; // 从红色开始
@@ -910,88 +927,67 @@ void MainWindow::checkFactoryModeResult(const QString& data)
     if (!isProductionTestRunning || currentTestStep != TEST_RESET) {
         return;
     }
-    
+
+    const bool hasFacConfirm = data.contains("enter or exit facotry mode", Qt::CaseInsensitive)
+        || data.contains("enter or exit factory mode", Qt::CaseInsensitive);
+    if (!hasFacConfirm) {
+        return;
+    }
+
     qDebug() << "工厂模式确认收到数据:" << data.trimmed();
-    
-    // 解析新格式:fac:enter or exit facotry mode :1 或 fac:enter or exit facotry mode :0
-    if (data.contains("fac:enter or exit facotry mode")) {
-        // 使用正则表达式提取cmd值
-        QRegExp factoryModePattern("fac:enter or exit facotry mode\\s*:\\s*(\\d+)");
-        if (factoryModePattern.indexIn(data) != -1) {
-            QString cmdStr = factoryModePattern.cap(1);
-            int cmd = cmdStr.toInt();
-            
-            qDebug() << "解析到工厂模式命令:" << cmd << "(1=进入产测, 0=退出产测)";
-            
-            if (cmd == 1) {
-                // cmd=1 表示成功进入产测模式
-                // 停止超时检测,因为已经收到确认
-                if (testTimeoutTimer->isActive()) {
-                    testTimeoutTimer->stop();
-                    qDebug() << "工厂模式进入确认收到,停止超时检测";
-                }
-                
-                qDebug() << "工厂模式进入成功确认 (cmd=1)";
-                ui->statusbar->showMessage("工厂模式进入成功,准备重启设备...", 2000);
-                
-                // 产测开始确认 - 按钮变绿色并禁用
-                ui->openButton_2->setStyleSheet("QPushButton { background-color: #28A745; color: white; padding: 5px 10px; border: none; border-radius: 3px; }");
-                ui->openButton_2->setEnabled(false);
-                qDebug() << "产测开始确认 - 按钮已变绿色并禁用";
-                
-                // 延迟2秒后发送重启指令
-                QTimer::singleShot(2000, this, [this]() {
-                    if (isProductionTestRunning && currentTestStep == TEST_RESET) {
-                        ui->statusbar->showMessage("产测模式已开启 - 重启设备中...", 3000);
-                        qDebug() << "工厂模式确认后发送设备重启指令";
-                        ui->recvTextEdit->append(">>> 发送指令: AT^RESET (恢复出厂设置/重启设备)");
-                        serial->write("AT^RESET\r\n");
-                        qDebug() << "发送设备重启指令: AT^RESET";
-                        
-                        // 切换到等待BuildVer状态
-                        currentTestStep = TEST_WAIT_BUILDVER;
-                        qDebug() << "等待设备重启完成,寻找BuildVer标志...";
-                    }
-                });
-                
-            } else if (cmd == 0) {
-                // cmd=0 表示退出产测模式
-                qDebug() << "工厂模式退出确认 (cmd=0)";
-                ui->statusbar->showMessage("工厂模式已退出", 2000);
-                
-            } else {
-                // 未知的cmd值
-                qDebug() << "未知的工厂模式命令值:" << cmd;
-                ui->statusbar->showMessage("收到未知的工厂模式命令", 2000);
-            }
-        } else {
-            // 无法解析cmd值,使用旧的逻辑作为备用
-            qDebug() << "无法解析工厂模式命令值,使用旧逻辑";
-            
-            // 停止超时检测,因为已经收到确认
+
+    int cmd = -1;
+    QRegExp reFacotry("(?:fac:)?enter or exit facotry mode\\s*:\\s*(\\d+)", Qt::CaseInsensitive);
+    QRegExp reFactory("(?:fac:)?enter or exit factory mode\\s*:\\s*(\\d+)", Qt::CaseInsensitive);
+    if (reFacotry.indexIn(data) != -1) {
+        cmd = reFacotry.cap(1).toInt();
+    } else if (reFactory.indexIn(data) != -1) {
+        cmd = reFactory.cap(1).toInt();
+    }
+
+    if (cmd >= 0) {
+        qDebug() << "解析到工厂模式命令:" << cmd << "(1=进入产测, 0=退出产测)";
+
+        if (cmd == 1) {
             if (testTimeoutTimer->isActive()) {
                 testTimeoutTimer->stop();
-                qDebug() << "工厂模式确认收到,停止超时检测";
+                qDebug() << "工厂模式进入确认收到,停止超时检测";
             }
-            
-            qDebug() << "工厂模式进入成功确认 (备用逻辑)";
-            ui->statusbar->showMessage("工厂模式进入成功,准备重启设备...", 2000);
-            
-            // 延迟2秒后发送重启指令
-            QTimer::singleShot(2000, this, [this]() {
-                if (isProductionTestRunning && currentTestStep == TEST_RESET) {
-                    ui->statusbar->showMessage("产测模式已开启 - 重启设备中...", 3000);
-                    qDebug() << "工厂模式确认后发送设备重启指令";
-                    ui->recvTextEdit->append(">>> 发送指令: AT^RESET (恢复出厂设置/重启设备)");
-                    serial->write("AT^RESET\r\n");
-                    qDebug() << "发送设备重启指令: AT^RESET";
-                    
-                    // 切换到等待BuildVer状态
-                    currentTestStep = TEST_WAIT_BUILDVER;
-                    qDebug() << "等待设备重启完成,寻找BuildVer标志...";
-                }
-            });
+
+            qDebug() << "工厂模式进入成功确认 (cmd=1)";
+            ui->statusbar->showMessage("工厂模式进入成功,等待 PMADDVOTE OK...", 2000);
+
+            ui->openButton_2->setStyleSheet("QPushButton { background-color: #4A90E2; color: white; padding: 5px 10px; border: none; border-radius: 3px; }");
+            qDebug() << "工厂模式进入成功,已发 PMADDVOTE 链后将根据 OK 把产测按钮变绿";
+
+            currentTestStep = TEST_PMADDVOTE_INIT;
+            qDebug() << "不执行 AT^RESET,直接进入 PMADDVOTE / ENTERTESTVIEW";
+            runPostFactoryProductionInit();
+
+        } else if (cmd == 0) {
+            qDebug() << "工厂模式退出确认 (cmd=0)";
+            ui->statusbar->showMessage("工厂模式已退出", 2000);
+
+        } else {
+            qDebug() << "未知的工厂模式命令值:" << cmd;
+            ui->statusbar->showMessage("收到未知的工厂模式命令", 2000);
+        }
+    } else {
+        qDebug() << "无法解析工厂模式命令值,使用备用逻辑";
+
+        if (testTimeoutTimer->isActive()) {
+            testTimeoutTimer->stop();
+            qDebug() << "工厂模式确认收到,停止超时检测";
         }
+
+        qDebug() << "工厂模式进入成功确认 (备用逻辑)";
+        ui->statusbar->showMessage("工厂模式进入成功,等待 PMADDVOTE OK...", 2000);
+
+        ui->openButton_2->setStyleSheet("QPushButton { background-color: #4A90E2; color: white; padding: 5px 10px; border: none; border-radius: 3px; }");
+
+        currentTestStep = TEST_PMADDVOTE_INIT;
+        qDebug() << "不执行 AT^RESET,直接进入 PMADDVOTE / ENTERTESTVIEW (备用解析路径)";
+        runPostFactoryProductionInit();
     }
 }
 
@@ -1878,59 +1874,119 @@ void MainWindow::showKeyTestConfirmDialog(bool allKeysTested)
     }
 }
 
-// 检查BuildVer启动完成
+// 工厂确认或重启后:PMADDVOTE → ENTERTESTVIEW → LCD
+void MainWindow::runPostFactoryProductionInit()
+{
+    if (!isProductionTestRunning || !serial || !serial->isOpen()) {
+        qDebug() << "runPostFactoryProductionInit: 产测未运行或串口未打开,跳过";
+        return;
+    }
+    if (postFactoryVoteSequenceStarted) {
+        qDebug() << "runPostFactoryProductionInit: 初始化链已在执行,忽略重复调用";
+        return;
+    }
+    postFactoryVoteSequenceStarted = true;
+    waitingPmaddvoteOkForGreenButton = true;
+
+    serial->write("AT^PMADDVOTE\r\n");
+    qDebug() << "发送AT^PMADDVOTE指令(第1次): AT^PMADDVOTE";
+
+    QTimer::singleShot(500, this, [this]() {
+        if (isProductionTestRunning && serial->isOpen()) {
+            serial->write("AT^PMADDVOTE\r\n");
+            qDebug() << "发送AT^PMADDVOTE指令(第2次): AT^PMADDVOTE";
+
+            if (serial->isOpen()) {
+                ui->recvTextEdit->append(">>> 发送指令: AT^ENTERTESTVIEW (进入产测界面)");
+                serial->write("AT^ENTERTESTVIEW\r\n");
+                qDebug() << "发送AT^ENTERTESTVIEW (进入产测界面)";
+            }
+
+            QTimer::singleShot(1000, this, [this]() {
+                if (!isProductionTestRunning) {
+                    return;
+                }
+                if (isTestItemEnabled("lcd")) {
+                    qDebug() << "AT^PMADDVOTE指令发送完成,开始LCD测试";
+                    ui->statusbar->showMessage("初始化完成,开始LCD测试", 2000);
+                    currentTestStep = TEST_LCD;
+                    QTimer::singleShot(1000, this, [this]() {
+                        if (isProductionTestRunning && currentTestStep == TEST_LCD) {
+                            qDebug() << "开始LCD测试流程";
+                            performLcdTest();
+                        }
+                    });
+                } else {
+                    qDebug() << "AT^PMADDVOTE指令发送完成,LCD测试未启用,跳过";
+                    ui->statusbar->showMessage("初始化完成,LCD测试未启用,跳过", 2000);
+                    currentTestStep = TEST_LCD;
+                    skipToNextEnabledTest();
+                }
+            });
+        }
+    });
+}
+
+void MainWindow::tryAdvanceProductionAfterBootKeywords(const QString& data)
+{
+    if (!isProductionTestRunning || currentTestStep != TEST_WAIT_BOOT) {
+        return;
+    }
+
+    const QStringList keywords = configManager.getBootDetectionKeywords();
+    if (keywords.isEmpty()) {
+        return;
+    }
+
+    for (const QString& keyword : keywords) {
+        if (data.contains(keyword, Qt::CaseInsensitive)) {
+            qDebug() << "产测:检测到开机完成关键字:" << keyword << ",数据片段:" << data.left(120);
+            appendToLogFile(QString("=== 产测流程-检测到开机完成: %1 ===\n").arg(keyword));
+
+            bootDetected = true;
+            currentTestStep = TEST_PMADDVOTE_INIT;
+
+            if (testTimeoutTimer->isActive()) {
+                testTimeoutTimer->stop();
+            }
+
+            ui->statusbar->showMessage("设备已开机,正在发送 PMADDVOTE / ENTERTESTVIEW…", 3000);
+            ui->recvTextEdit->append(">>> 已检测到开机完成,进入 PMADDVOTE / ENTERTESTVIEW 初始化链(未发 AT^FACTORY=1)");
+
+            runPostFactoryProductionInit();
+            return;
+        }
+    }
+}
+
+void MainWindow::tryApplyProductionStartGreenAfterPmaddvoteOk(const QString& line)
+{
+    if (!waitingPmaddvoteOkForGreenButton || !isProductionTestRunning ||
+        currentTestStep != TEST_PMADDVOTE_INIT) {
+        return;
+    }
+    const QString t = line.trimmed();
+    if (t.compare(QStringLiteral("OK"), Qt::CaseInsensitive) != 0) {
+        return;
+    }
+    waitingPmaddvoteOkForGreenButton = false;
+    ui->openButton_2->setStyleSheet("QPushButton { background-color: #28A745; color: white; padding: 5px 10px; border: none; border-radius: 3px; }");
+    ui->openButton_2->setEnabled(false);
+    qDebug() << "收到 AT^PMADDVOTE 的 OK 响应,产测按钮已变绿并禁用";
+    ui->statusbar->showMessage("PMADDVOTE 成功,产测进行中", 2000);
+}
+
+// 检查BuildVer启动完成(仅当仍使用「先重启再等 BuildVer」时使用)
 void MainWindow::checkBuildVerResult(const QString& data)
 {
-    // 检测设备启动完成标志,只在等待BuildVer阶段检测(大小写不敏感)
-    if (data.contains("BuildVer", Qt::CaseInsensitive) && currentTestStep == TEST_WAIT_BUILDVER && 
+    if (data.contains("BuildVer", Qt::CaseInsensitive) && currentTestStep == TEST_WAIT_BUILDVER &&
         isProductionTestRunning) {
-        
+
         qDebug() << "检测到BuildVer启动完成标志:" << data.trimmed();
-        qDebug() << "设备重启完成,发送AT^PMADDVOTE指令";
-        
-        ui->statusbar->showMessage("设备重启完成,发送初始化指令...", 2000);
-        
-        // 发送第一次AT^PMADDVOTE指令
-        serial->write("AT^PMADDVOTE\r\n");
-        qDebug() << "发送AT^PMADDVOTE指令(第1次): AT^PMADDVOTE";
-        
-        // 延迟0.5秒后发送第二次AT^PMADDVOTE指令
-        QTimer::singleShot(500, this, [this]() {
-            if (isProductionTestRunning && serial->isOpen()) {
-                serial->write("AT^PMADDVOTE\r\n");
-                qDebug() << "发送AT^PMADDVOTE指令(第2次): AT^PMADDVOTE";
-                
-                // 开机初始化完成后,进入产测界面(满足AT^ENTERTESTVIEW要求)
-                if (serial->isOpen()) {
-                    ui->recvTextEdit->append(">>> 发送指令: AT^ENTERTESTVIEW (进入产测界面)");
-                    serial->write("AT^ENTERTESTVIEW\r\n");
-                    qDebug() << "发送AT^ENTERTESTVIEW (进入产测界面)";
-                }
-                
-                // 初始化完成后,切换到 LCD(若启用);功耗测试在 GPS 之后
-                QTimer::singleShot(1000, this, [this]() {
-                    if (!isProductionTestRunning) {
-                        return;
-                    }
-                    if (isTestItemEnabled("lcd")) {
-                        qDebug() << "AT^PMADDVOTE指令发送完成,开始LCD测试";
-                        ui->statusbar->showMessage("初始化完成,开始LCD测试", 2000);
-                        currentTestStep = TEST_LCD;
-                        QTimer::singleShot(1000, this, [this]() {
-                            if (isProductionTestRunning && currentTestStep == TEST_LCD) {
-                                qDebug() << "开始LCD测试流程";
-                                performLcdTest();
-                            }
-                        });
-                    } else {
-                        qDebug() << "AT^PMADDVOTE指令发送完成,LCD测试未启用,跳过";
-                        ui->statusbar->showMessage("初始化完成,LCD测试未启用,跳过", 2000);
-                        currentTestStep = TEST_LCD;
-                        skipToNextEnabledTest();
-                    }
-                });
-            }
-        });
+        ui->statusbar->showMessage("设备启动完成,发送初始化指令...", 2000);
+
+        currentTestStep = TEST_PMADDVOTE_INIT;
+        runPostFactoryProductionInit();
     }
 }
 
@@ -2339,6 +2395,8 @@ void MainWindow::on_openButton_2_clicked()
         gSensorIntervalTimer->stop();
         bluetoothTestTimer->stop();
         isProductionTestRunning = false;
+        postFactoryVoteSequenceStarted = false;
+        waitingPmaddvoteOkForGreenButton = false;
         currentTestStep = TEST_IDLE;
         
         // 关闭测试日志文件
@@ -2361,10 +2419,13 @@ void MainWindow::on_openButton_2_clicked()
 
     // 开始产测流程
     isProductionTestRunning = true;
+    postFactoryVoteSequenceStarted = false;
+    waitingPmaddvoteOkForGreenButton = false;
+    recvBuffer.clear();
     testStartTime = QDateTime::currentDateTime(); // 记录测试开始时间
     testStartTimeMap.clear();
     testDurationSecMap.clear();
-    currentTestStep = TEST_RESET;
+    currentTestStep = TEST_WAIT_BOOT;
     currentLcdColor = 1; // 从红色开始
     
     // 在开始新测试前,尽量保存当前UI日志(如果有内容)
@@ -2415,27 +2476,29 @@ void MainWindow::on_openButton_2_clicked()
     
     ui->statusbar->showMessage("开始产测流程...", 2000);
     qDebug() << "=== 开始产测流程 ===";
-    qDebug() << "流程: 产测开启(AT^FACTORY=1) → 设备重启 → … → 全部完成后弹窗确认 → AT^RESTORE(恢复出厂) → AT^POWEROFF(关机)";
+    qDebug() << "流程: 等待开机完成 → 跳过 AT^FACTORY=1 → PMADDVOTE/ENTERTESTVIEW → 各测试项 → 完成后弹窗确认等";
     qDebug() << "LCD测试: 仅发送 AT^LCDTEST=1";
     
-    // 发送产测开启指令,等待工厂模式确认
-    qDebug() << "Step 1: 发送产测开启指令";
-    ui->statusbar->showMessage("发送产测开启指令,等待设备确认...", 3000);
-    ui->recvTextEdit->append(">>> 发送指令: AT^FACTORY=1 (开始产测)");
-    serial->write("AT^FACTORY=1\r\n");
-    qDebug() << "发送产测开启指令(第1次): AT^FACTORY=1,等待接收 'fac:enter or exit facotry mode' 确认";
-    
-    // 延迟0.5秒后再发送一次,确保设备接收到指令
-    QTimer::singleShot(500, this, [this]() {
-        if (isProductionTestRunning && serial->isOpen()) {
-            ui->recvTextEdit->append(">>> 发送指令: AT^FACTORY=1 (开始产测 - 第2次)");
-            serial->write("AT^FACTORY=1\r\n");
-            qDebug() << "发送产测开启指令(第2次): AT^FACTORY=1";
-        }
-    });
-    
-    // 启动10秒超时检测,如果没有收到工厂模式确认则提示
-    testTimeoutTimer->start(10000);
+    // 不再发送 AT^FACTORY=1:固件侧常会触发整机重启;在开机完成后再发 PMADDVOTE / ENTERTESTVIEW
+    qDebug() << "Step 1: 等待串口开机关键字(bootDetectionKeywords),然后 PMADDVOTE / ENTERTESTVIEW";
+
+    ui->openButton_2->setStyleSheet("QPushButton { background-color: #4A90E2; color: white; padding: 5px 10px; border: none; border-radius: 3px; }");
+    ui->openButton_2->setEnabled(true);
+    qDebug() << "产测开始 - 先等开机完成;之后等待 AT^PMADDVOTE 回复 OK 后按钮变绿(期间可再点此按钮停止)";
+
+    const QStringList bootKws = configManager.getBootDetectionKeywords();
+    if (bootKws.isEmpty()) {
+        qDebug() << "bootDetectionKeywords 为空,跳过等待开机";
+        ui->recvTextEdit->append(">>> 未配置开机关键字,跳过等待,直接发送 PMADDVOTE / ENTERTESTVIEW(未发 AT^FACTORY=1)");
+        ui->statusbar->showMessage("未配置开机关键字,直接初始化…", 3000);
+        currentTestStep = TEST_PMADDVOTE_INIT;
+        runPostFactoryProductionInit();
+    } else {
+        ui->recvTextEdit->append(">>> 等待设备开机完成(与 settings 中 bootDetectionKeywords 匹配后,再发 PMADDVOTE / ENTERTESTVIEW)");
+        ui->statusbar->showMessage("等待设备开机完成… 请上电或重启设备", 0);
+        testTimeoutTimer->stop();
+        testTimeoutTimer->start(120000);
+    }
 }
 
 // 执行录音测试(配置了等级标准时共测 3 轮,任一轮「实际等级 >= 设置等级」则整体通过)
@@ -3981,6 +4044,20 @@ void MainWindow::onTestTimeout()
     
     // 根据当前测试步骤确定测试名称
     switch (currentTestStep) {
+        case TEST_WAIT_BOOT:
+            qDebug() << "等待设备开机超时,未匹配到 bootDetectionKeywords";
+            ui->statusbar->showMessage("等待开机超时,请检查设备或关键字配置", 5000);
+            QMessageBox::warning(this, "等待开机超时",
+                QStringLiteral("在 120 秒内未在串口日志中检测到开机关键字(bootDetectionKeywords)。\n"
+                               "请确认设备已上电/重启,或在 settings.json 中配置与固件日志一致的关键字。"));
+            isProductionTestRunning = false;
+            waitingPmaddvoteOkForGreenButton = false;
+            postFactoryVoteSequenceStarted = false;
+            currentTestStep = TEST_IDLE;
+            ui->openButton_2->setStyleSheet("QPushButton { background-color: #4A90E2; color: white; padding: 5px 10px; border: none; border-radius: 3px; }");
+            ui->openButton_2->setEnabled(true);
+            return;
+
         case TEST_RESET:
             // 工厂模式进入超时
             qDebug() << "工厂模式进入超时,未收到确认消息";
@@ -4187,6 +4264,8 @@ void MainWindow::clearProductionTestState()
     
     // 重置产测状态变量
     isProductionTestRunning = false;
+    postFactoryVoteSequenceStarted = false;
+    waitingPmaddvoteOkForGreenButton = false;
     currentTestStep = TEST_IDLE;
     currentLcdColor = 0;
     
index 3215a5a..6f049fd 100644 (file)
@@ -69,7 +69,10 @@ private slots:
     void showBatteryChargeFailDialog(); // 显示电池充电失败弹窗
     void clearProductionTestState(); // 清空产测状态
     void checkLcdTestResult(const QString& data); // 检查LCD测试结果
-    void checkBuildVerResult(const QString& data); // 检查BuildVer启动完成
+    void checkBuildVerResult(const QString& data); // 检查BuildVer启动完成(仅重启流程备用)
+    void runPostFactoryProductionInit(); // 工厂确认后:PMADDVOTE / ENTERTESTVIEW / LCD(不重启)
+    void tryAdvanceProductionAfterBootKeywords(const QString& data); // 产测已开始且处于等待开机:匹配到关键字则进入 PMADDVOTE 链
+    void tryApplyProductionStartGreenAfterPmaddvoteOk(const QString& line); // 收到 PMADDVOTE 后的 OK 再将产测按钮变绿
     void showLcdTestConfirmDialog(); // 显示LCD测试确认弹窗
     void showMotorTestConfirmDialog(); // 显示马达测试确认弹窗
     void performTouchTest(); // 触控测试(AT^TESTJUMP=TP)
@@ -142,6 +145,8 @@ private:
     QTimer *gpsPollTimer;              // GPS信息轮询定时器(每2秒发AT^GPSINFO)
     int currentLcdColor;               // 当前LCD测试颜色 (1=红, 2=绿, 3=蓝)
     bool isProductionTestRunning;      // 产测是否正在运行
+    bool postFactoryVoteSequenceStarted; // PMADDVOTE 初始化链是否已启动(防重复)
+    bool waitingPmaddvoteOkForGreenButton; // 已发 PMADDVOTE,等待 OK 后再把「开始产测」按钮变绿
     QDateTime testStartTime;           // 测试开始时间
     bool lcdTestPopupShown;            // LCD测试弹窗是否已显示
     bool motorTestPopupShown;          // 马达测试弹窗是否已显示
@@ -197,8 +202,10 @@ private:
     
     enum ProductionTestStep {
         TEST_IDLE,
+        TEST_WAIT_BOOT,   // 已点开始产测,等待串口出现开机完成关键字(与 settings 中 bootDetectionKeywords 一致)
         TEST_RESET,
         TEST_WAIT_BUILDVER,
+        TEST_PMADDVOTE_INIT,
         TEST_LCD,
         TEST_TOUCH,
         TEST_BRIGHT,