开机自动发送开始产测
authorxue <2144439972@qq.com>
Sat, 25 Apr 2026 03:26:53 +0000 (11:26 +0800)
committerxue <2144439972@qq.com>
Sat, 25 Apr 2026 03:26:53 +0000 (11:26 +0800)
mainwindow.cpp
mainwindow.h

index f436833..a97a573 100644 (file)
@@ -535,6 +535,32 @@ void MainWindow::readSerialData()
         if (line.startsWith("fac:")) {
             appendToLogFile(processLine);
         }
+
+        // 设备打印 InitBroadcastService start:未在产测则自动等同点击「开始产测」;已在等待开机则直接视为就绪并进入 PMADDVOTE
+        {
+            const QString kInitBc = QStringLiteral("InitBroadcastService start");
+            const bool hasInitBc = line.contains(kInitBc, Qt::CaseInsensitive) ||
+                processLine.contains(kInitBc, Qt::CaseInsensitive);
+            if (hasInitBc && serial && serial->isOpen()) {
+                if (!isProductionTestRunning) {
+                    qDebug() << "检测到 InitBroadcastService start,将自动开始产测流程";
+                    QTimer::singleShot(0, this, [this, kInitBc]() {
+                        if (!serial || !serial->isOpen() || isProductionTestRunning) {
+                            return;
+                        }
+                        ui->recvTextEdit->append(">>> 检测到 InitBroadcastService start,自动开始产测");
+                        appendToLogFile("=== 检测到 InitBroadcastService start,自动开始产测 ===\n");
+                        beginProductionTestSession();
+                        tryAdvanceProductionAfterBootKeywords(kInitBc);
+                    });
+                } else if (currentTestStep == TEST_WAIT_BOOT) {
+                    tryAdvanceProductionAfterBootKeywords(line);
+                    if (currentTestStep == TEST_WAIT_BOOT && processLine != line) {
+                        tryAdvanceProductionAfterBootKeywords(processLine);
+                    }
+                }
+            }
+        }
         
         // 开机检测(仅在未检测到开机时执行)
         // 同时检查原始行和去掉fac:前缀的行,确保能匹配到关键字
@@ -1930,30 +1956,42 @@ void MainWindow::tryAdvanceProductionAfterBootKeywords(const QString& data)
         return;
     }
 
+    const QString kInitBroadcast = QStringLiteral("InitBroadcastService start");
     const QStringList keywords = configManager.getBootDetectionKeywords();
-    if (keywords.isEmpty()) {
+
+    QString matchedReason;
+    if (data.contains(kInitBroadcast, Qt::CaseInsensitive)) {
+        matchedReason = kInitBroadcast;
+    } else {
+        if (keywords.isEmpty()) {
+            return;
+        }
+        for (const QString& keyword : keywords) {
+            if (data.contains(keyword, Qt::CaseInsensitive)) {
+                matchedReason = keyword;
+                break;
+            }
+        }
+    }
+
+    if (matchedReason.isEmpty()) {
         return;
     }
 
-    for (const QString& keyword : keywords) {
-        if (data.contains(keyword, Qt::CaseInsensitive)) {
-            qDebug() << "产测:检测到开机完成关键字:" << keyword << ",数据片段:" << data.left(120);
-            appendToLogFile(QString("=== 产测流程-检测到开机完成: %1 ===\n").arg(keyword));
+    qDebug() << "产测:检测到开机完成:" << matchedReason << ",数据片段:" << data.left(120);
+    appendToLogFile(QString("=== 产测流程-检测到开机完成: %1 ===\n").arg(matchedReason));
 
-            bootDetected = true;
-            currentTestStep = TEST_PMADDVOTE_INIT;
+    bootDetected = true;
+    currentTestStep = TEST_PMADDVOTE_INIT;
 
-            if (testTimeoutTimer->isActive()) {
-                testTimeoutTimer->stop();
-            }
+    if (testTimeoutTimer->isActive()) {
+        testTimeoutTimer->stop();
+    }
 
-            ui->statusbar->showMessage("设备已开机,正在发送 PMADDVOTE / ENTERTESTVIEW…", 3000);
-            ui->recvTextEdit->append(">>> 已检测到开机完成,进入 PMADDVOTE / ENTERTESTVIEW 初始化链(未发 AT^FACTORY=1)");
+    ui->statusbar->showMessage("设备已开机,正在发送 PMADDVOTE / ENTERTESTVIEW…", 3000);
+    ui->recvTextEdit->append(">>> 已检测到开机完成,进入 PMADDVOTE / ENTERTESTVIEW 初始化链(未发 AT^FACTORY=1)");
 
-            runPostFactoryProductionInit();
-            return;
-        }
-    }
+    runPostFactoryProductionInit();
 }
 
 void MainWindow::tryApplyProductionStartGreenAfterPmaddvoteOk(const QString& line)
@@ -2376,45 +2414,17 @@ void MainWindow::on_openRecordsFolderButton_clicked()
     }
 }
 
-void MainWindow::on_openButton_2_clicked()
+void MainWindow::beginProductionTestSession()
 {
-    // 检查串口是否已打开
-    if (!serial->isOpen()) {
-        QMessageBox::warning(this, "警告", "请先打开串口连接");
+    if (isProductionTestRunning) {
+        qDebug() << "beginProductionTestSession: 已在产测中,忽略";
         return;
     }
-
-    if (isProductionTestRunning) {
-        // 如果正在测试,则停止测试
-        lcdTestTimer->stop();
-        motorTestTimer->stop();
-        testTimeoutTimer->stop();
-        gSensorIntervalTimer->stop();
-        bluetoothTestTimer->stop();
-        isProductionTestRunning = false;
-        postFactoryVoteSequenceStarted = false;
-        waitingPmaddvoteOkForGreenButton = false;
-        currentTestStep = TEST_IDLE;
-        
-        // 关闭测试日志文件
-        if (testLogFile && testLogStream) {
-            *testLogStream << QString("=== 测试被用户停止: %1 ===\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
-            testLogStream->flush();
-        }
-        closeTestLogFile();
-        
-        ui->statusbar->showMessage("产测已停止", 2000);
-        qDebug() << "产测被用户停止";
-        
-        // 恢复产测按钮状态 - 变回蓝色并启用
-        ui->openButton_2->setStyleSheet("QPushButton { background-color: #4A90E2; color: white; padding: 5px 10px; border: none; border-radius: 3px; }");
-        ui->openButton_2->setEnabled(true);
-        qDebug() << "产测停止 - 按钮已恢复蓝色并启用";
-        
+    if (!serial || !serial->isOpen()) {
+        qDebug() << "beginProductionTestSession: 串口未打开,忽略";
         return;
     }
 
-    // 开始产测流程
     isProductionTestRunning = true;
     postFactoryVoteSequenceStarted = false;
     waitingPmaddvoteOkForGreenButton = false;
@@ -2424,27 +2434,27 @@ void MainWindow::on_openButton_2_clicked()
     testDurationSecMap.clear();
     currentTestStep = TEST_WAIT_BOOT;
     currentLcdColor = 1; // 从红色开始
-    
+
     // 在开始新测试前,尽量保存当前UI日志(如果有内容)
     saveRecvTextEditToLog();
     productionDeviceMac.clear();
 
     // 创建测试日志文件
     createTestLogFile();
-    
+
     // 清空所有UI内容
     ui->recvTextEdit->clear();           // 清空串口接收文本框
     ui->sendTextEdit->clear();           // 清空发送文本框
     ui->statusbar->clearMessage();       // 清空状态栏
-    
+
     // 重置开机检测状态
     bootDetected = false;
     factoryModeChecked = false;
     versionInfoFetched = false;
-    
+
     // 清空设备信息
     ui->VerlineEdit->clear();            // 清空版本信息
-    
+
     // 重置弹窗状态
     lcdTestPopupShown = false;
     motorTestPopupShown = false;
@@ -2453,7 +2463,7 @@ void MainWindow::on_openButton_2_clicked()
     deepsleepTestPopupShown = false;
     isWaitingForUserConfirm = false;
     keyTestStarted = false;
-    
+
     // 重置状态指示器
     updateTestStatus("lcd", STATUS_UNTESTED);
     updateTestStatus("tp", STATUS_UNTESTED);
@@ -2468,14 +2478,14 @@ void MainWindow::on_openButton_2_clicked()
     updateTestStatus("bluetooth", STATUS_UNTESTED);
     updateTestStatus("sechip", STATUS_UNTESTED);
     updateTestStatus("gps", STATUS_UNTESTED);
-    
+
     qDebug() << "已清空所有UI内容,准备开始新的产测";
-    
+
     ui->statusbar->showMessage("开始产测流程...", 2000);
     qDebug() << "=== 开始产测流程 ===";
     qDebug() << "流程: 等待开机完成 → 跳过 AT^FACTORY=1 → PMADDVOTE/ENTERTESTVIEW → 各测试项 → 完成后弹窗确认等";
     qDebug() << "LCD测试: 仅发送 AT^LCDTEST=1";
-    
+
     // 不再发送 AT^FACTORY=1:固件侧常会触发整机重启;在开机完成后再发 PMADDVOTE / ENTERTESTVIEW
     qDebug() << "Step 1: 等待串口开机关键字(bootDetectionKeywords),然后 PMADDVOTE / ENTERTESTVIEW";
 
@@ -2498,6 +2508,47 @@ void MainWindow::on_openButton_2_clicked()
     }
 }
 
+void MainWindow::on_openButton_2_clicked()
+{
+    // 检查串口是否已打开
+    if (!serial->isOpen()) {
+        QMessageBox::warning(this, "警告", "请先打开串口连接");
+        return;
+    }
+
+    if (isProductionTestRunning) {
+        // 如果正在测试,则停止测试
+        lcdTestTimer->stop();
+        motorTestTimer->stop();
+        testTimeoutTimer->stop();
+        gSensorIntervalTimer->stop();
+        bluetoothTestTimer->stop();
+        isProductionTestRunning = false;
+        postFactoryVoteSequenceStarted = false;
+        waitingPmaddvoteOkForGreenButton = false;
+        currentTestStep = TEST_IDLE;
+        
+        // 关闭测试日志文件
+        if (testLogFile && testLogStream) {
+            *testLogStream << QString("=== 测试被用户停止: %1 ===\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
+            testLogStream->flush();
+        }
+        closeTestLogFile();
+        
+        ui->statusbar->showMessage("产测已停止", 2000);
+        qDebug() << "产测被用户停止";
+        
+        // 恢复产测按钮状态 - 变回蓝色并启用
+        ui->openButton_2->setStyleSheet("QPushButton { background-color: #4A90E2; color: white; padding: 5px 10px; border: none; border-radius: 3px; }");
+        ui->openButton_2->setEnabled(true);
+        qDebug() << "产测停止 - 按钮已恢复蓝色并启用";
+        
+        return;
+    }
+
+    beginProductionTestSession();
+}
+
 // 执行录音测试(单轮;有等级标准则 mic level 与标准比较一次,无标准则只采样不判等级)
 void MainWindow::performAudioTest()
 {
@@ -2597,11 +2648,13 @@ void MainWindow::checkAudioTestResult(const QString& data)
         qDebug() << "发送AT^AUDIOTEST=6";
     }
 
-    // 固件在 =6 后异步停止 AI 语音/解码线程;稍候再判定/切下一项,减轻与后续 AT 的竞态。
+    // 顺序说明:上面已立刻发送 AT^AUDIOTEST=6,这一步才是「通知固件停止录音」。
+    // 固件收到 =6 后会异步停线程/拆音频(需数百 ms~1s+);此处延迟后再 skipToNext/发电池 AT,
+    // 避免与收尾并发导致异常。日志里在延迟期间仍可能出现 [AudioTest]/mic level,那是设备仍在打印,并非未先关再进下一项。
     static const int kMsAfterAudiotestStop = 2000;
-    ui->statusbar->showMessage(QStringLiteral("å½\95é\9f³æ\9c¬è½®å·²ç»\93æ\9d\9fï¼\8cç­\89å¾\85设å¤\87å®\8cæ\88\90é\9f³é¢\91收尾…"), 2000);
-    appendToLogFile(QString(">>> 等待 %1 ms 后再继续(避开 AUDIOTEST 异步停止)\n").arg(kMsAfterAudiotestStop));
-    qDebug() << "录音测试:已发 AUDIOTEST=6," << kMsAfterAudiotestStop << "ms 后再判定/切下一项";
+    ui->statusbar->showMessage(QStringLiteral("å·²å\8f\91é\80\81å\81\9cæ­¢å½\95é\9f³(AT^AUDIOTEST=6)ï¼\8cç­\89å¾\85设å¤\87收尾…"), 2000);
+    appendToLogFile(QString(">>> 已发送 AT^AUDIOTEST=6;延迟 %1 ms 待设备异步停止完毕,再进入下一项测试\n").arg(kMsAfterAudiotestStop));
+    qDebug() << "录音测试:已发 AUDIOTEST=6," << kMsAfterAudiotestStop << "ms 后再进入下一项";
 
     QTimer::singleShot(kMsAfterAudiotestStop, this, [this, micLevel]() {
         if (!isProductionTestRunning || currentTestStep != TEST_AUDIO) {
index c3275a0..9bb4747 100644 (file)
@@ -250,6 +250,7 @@ private:
     void finalizeGsensorTest();
     void stopGsensorCollectionAndFinalizeSoon(const QString& reasonSuffix);
 
+    void beginProductionTestSession();               // 开始产测(与「开始产测」按钮启动逻辑一致,串口须已打开)
     void finalizeAfterProductionResultDialog();      // 恢复产测按钮并 clearProductionTestState
 
     // G-sensor XYZ窗口采集状态