1.录音测试回退为一次
authorxue <2144439972@qq.com>
Fri, 24 Apr 2026 06:47:37 +0000 (14:47 +0800)
committerxue <2144439972@qq.com>
Fri, 24 Apr 2026 06:47:37 +0000 (14:47 +0800)
2.漏光测试失败则直接复测
3.取消关机指令

mainwindow.cpp
mainwindow.h

index 48329d6..f436833 100644 (file)
@@ -1,4 +1,4 @@
-#include "mainwindow.h"
+#include "mainwindow.h"
 #include "ui_mainwindow.h"
 #include "config_manager.h"
 #include <QMessageBox>
@@ -60,7 +60,7 @@ MainWindow::MainWindow(QWidget *parent)
     , ui(new Ui::MainWindow)
 {
     ui->setupUi(this);
-    setWindowTitle("FD272产测工具 V1.0");
+    setWindowTitle("FD272产测工具 V1.9");
     
     // 确保控制台输出使用UTF-8编码
     QTextCodec *utf8Codec = QTextCodec::codecForName("UTF-8");
@@ -106,8 +106,6 @@ MainWindow::MainWindow(QWidget *parent)
     lcdBacklightOn = false;            // 初始化LCD背光状态为关闭
     keyTestStarted = false;
     keyTestDialog = nullptr;
-    audioTestRoundIndex = 0;
-    audioTestAnyPass = false;
     audioTestAwaitingMicLevel = false;
     bluetoothTestTimer = new QTimer(this);
     gpsTestTimer = new QTimer(this);
@@ -128,6 +126,7 @@ MainWindow::MainWindow(QWidget *parent)
     leaklightGreenSeen = false;
     leaklightRedSeen = false;
     leaklightIrSeen = false;
+    leaklightAttemptIndex = 0;
     // 初始化日志文件相关变量
     testLogFile = nullptr;
     testLogStream = nullptr;
@@ -162,7 +161,6 @@ MainWindow::MainWindow(QWidget *parent)
     connect(bluetoothTestTimer, &QTimer::timeout, this, &MainWindow::onBluetoothTestTimer);
     connect(gpsTestTimer, &QTimer::timeout, this, &MainWindow::onGpsTestTimer);
     connect(gpsPollTimer, &QTimer::timeout, this, &MainWindow::onGpsPollTimer);
-
     // 初始化G-sensor采集状态
     gSensorCollecting = false;
     gSensorHasStart = false;
@@ -598,11 +596,11 @@ void MainWindow::readSerialData()
                 qDebug() << "产测模式: 已开启";
                 appendToLogFile(QString("=== 产测模式状态: 已开启 ===\n"));
                 
-                // 如果还未标记为已检测,标记并开始获取版本信息
-                if (!factoryModeChecked) {
+                // 仅在非产测流程中自动拉版本;产测时串口可能出现该行,避免与当前测试 AT 并发(如 G-sensor)
+                if (!isProductionTestRunning && !factoryModeChecked) {
                     factoryModeChecked = true;
                     QTimer::singleShot(1000, this, [this]() {
-                        if (!versionInfoFetched) {
+                        if (!isProductionTestRunning && !versionInfoFetched) {
                             startVersionInfoFetch();
                         }
                     });
@@ -614,11 +612,10 @@ void MainWindow::readSerialData()
                 qDebug() << "产测模式: 已关闭";
                 appendToLogFile(QString("=== 产测模式状态: 已关闭 ===\n"));
                 
-                // 如果还未标记为已检测,标记并开始获取版本信息
-                if (!factoryModeChecked) {
+                if (!isProductionTestRunning && !factoryModeChecked) {
                     factoryModeChecked = true;
                     QTimer::singleShot(1000, this, [this]() {
-                        if (!versionInfoFetched) {
+                        if (!isProductionTestRunning && !versionInfoFetched) {
                             startVersionInfoFetch();
                         }
                     });
@@ -2501,7 +2498,7 @@ void MainWindow::on_openButton_2_clicked()
     }
 }
 
-// 执行录音测试(配置了等级标准时共测 3 轮,任一轮「实际等级 >= 设置等级」则整体通过)
+// 执行录音测试(单轮;有等级标准则 mic level 与标准比较一次,无标准则只采样不判等级)
 void MainWindow::performAudioTest()
 {
     if (!isTestItemEnabled("mic")) {
@@ -2519,8 +2516,6 @@ void MainWindow::performAudioTest()
     qDebug() << "开始录音测试";
     appendToLogFile("\n========== 开始录音测试 ==========\n");
     updateTestStatus("mic", STATUS_TESTING);
-    audioTestRoundIndex = 0;
-    audioTestAnyPass = false;
     audioTestAwaitingMicLevel = false;
 
     QString standard = ui->audioLevelStandardLineEdit->text().trimmed();
@@ -2528,8 +2523,8 @@ void MainWindow::performAudioTest()
         appendToLogFile(">>> 未配置等级标准:单轮采样,不做等级校验\n");
         ui->statusbar->showMessage("录音测试中(未配标准,单轮),请对着麦克风说话...", 5000);
     } else {
-        appendToLogFile(QString(">>> å·²é\85\8dç½®ç­\89级æ \87å\87\86ã\80\8c%1ã\80\8dï¼\9aå\85± 3 è½®ï¼\8cä»»ä¸\80轮达æ \87å\8d³é\80\9aè¿\87\n").arg(standard));
-        ui->statusbar->showMessage("录音测试 1/3:请对着麦克风说话...", 5000);
+        appendToLogFile(QString(">>> å·²é\85\8dç½®ç­\89级æ \87å\87\86ã\80\8c%1ã\80\8dï¼\9aå\8d\95è½®å\88¤å®\9a\n").arg(standard));
+        ui->statusbar->showMessage("录音测试中,请对着麦克风说话...", 5000);
     }
     
     // 录音前关闭LCD(与V1.7一致)
@@ -2539,24 +2534,18 @@ void MainWindow::performAudioTest()
         qDebug() << "录音测试前发送AT^LCDOFF";
     }
 
-    beginAudioTestAttempt(0);
+    beginAudioTestAttempt();
 }
 
-void MainWindow::beginAudioTestAttempt(int roundIndex)
+void MainWindow::beginAudioTestAttempt()
 {
     if (!isProductionTestRunning || currentTestStep != TEST_AUDIO || !serial || !serial->isOpen()) {
         qDebug() << "录音测试:产测已停止或串口未打开";
         return;
     }
 
-    audioTestRoundIndex = roundIndex;
     audioTestAwaitingMicLevel = true;
 
-    QString standard = ui->audioLevelStandardLineEdit->text().trimmed();
-    if (!standard.isEmpty()) {
-        ui->statusbar->showMessage(QString("录音测试 %1/3:请对着麦克风说话...").arg(roundIndex + 1), 5000);
-    }
-
     QString volumeStr = ui->audioVolumeLineEdit->text().trimmed();
     if (volumeStr.isEmpty()) volumeStr = "2.0";
 
@@ -2598,8 +2587,8 @@ void MainWindow::checkAudioTestResult(const QString& data)
     testTimeoutTimer->stop();
     
     qDebug() << "录音测试解析到mic level:" << micLevel;
-    appendToLogFile(QString(">>> 录音测试 第%1轮: mic level=%2\n").arg(audioTestRoundIndex + 1).arg(micLevel));
-    ui->recvTextEdit->append(QString(">>> 录音测试 第%1轮: mic level=%2").arg(audioTestRoundIndex + 1).arg(micLevel));
+    appendToLogFile(QString(">>> 录音测试 mic level=%1\n").arg(micLevel));
+    ui->recvTextEdit->append(QString(">>> 录音测试 mic level=%1").arg(micLevel));
     
     // 结束本轮采集
     if (serial && serial->isOpen()) {
@@ -2607,64 +2596,58 @@ void MainWindow::checkAudioTestResult(const QString& data)
         appendToLogFile(">>> 发送指令: AT^AUDIOTEST=6 (结束本轮音频测试)\n");
         qDebug() << "发送AT^AUDIOTEST=6";
     }
-    
-    QString standard = ui->audioLevelStandardLineEdit->text().trimmed();
-    if (standard.isEmpty()) {
-        updateTestStatus("mic", STATUS_SUCCESS);
-        ui->statusbar->showMessage(QString("录音测试通过(等级:%1,未配置标准不校验)").arg(micLevel), 3000);
-        skipToNextEnabledTest();
-        return;
-    }
-    
-    // 等级排序:静音(0) < 极低(1) < 低(2) < 中(3) < 高(4);达标条件:micRank >= stdRank
-    auto levelRank = [](const QString& s) -> int {
-        if (s == QStringLiteral("静音")) return 0;
-        if (s == QStringLiteral("极低")) return 1;
-        if (s == QStringLiteral("低")) return 2;
-        if (s == QStringLiteral("中")) return 3;
-        if (s == QStringLiteral("高")) return 4;
-        return -1;
-    };
-    
-    int micRank = levelRank(micLevel);
-    int stdRank = levelRank(standard);
-    
-    if (micRank < 0 || stdRank < 0) {
-        updateTestStatus("mic", STATUS_FAILED);
-        ui->statusbar->showMessage(QString("录音测试失败:无法解析等级(实际:%1, 标准:%2)").arg(micLevel).arg(standard), 3000);
-        skipToNextEnabledTest();
-        return;
-    }
 
-    const bool roundPass = (micRank >= stdRank);
-    if (roundPass) {
-        audioTestAnyPass = true;
-        appendToLogFile(QString(">>> 第%1轮达标(%2 >= 标准%3)\n").arg(audioTestRoundIndex + 1).arg(micLevel).arg(standard));
-    } else {
-        appendToLogFile(QString(">>> 第%1轮未达标(%2 < 标准%3)\n").arg(audioTestRoundIndex + 1).arg(micLevel).arg(standard));
-    }
+    // 固件在 =6 后异步停止 AI 语音/解码线程;稍候再判定/切下一项,减轻与后续 AT 的竞态。
+    static const int kMsAfterAudiotestStop = 2000;
+    ui->statusbar->showMessage(QStringLiteral("录音本轮已结束,等待设备完成音频收尾…"), 2000);
+    appendToLogFile(QString(">>> 等待 %1 ms 后再继续(避开 AUDIOTEST 异步停止)\n").arg(kMsAfterAudiotestStop));
+    qDebug() << "录音测试:已发 AUDIOTEST=6," << kMsAfterAudiotestStop << "ms 后再判定/切下一项";
 
-    const int completedRound = audioTestRoundIndex;
-    const int nextRound = completedRound + 1;
+    QTimer::singleShot(kMsAfterAudiotestStop, this, [this, micLevel]() {
+        if (!isProductionTestRunning || currentTestStep != TEST_AUDIO) {
+            return;
+        }
 
-    if (nextRound < 3) {
-        QTimer::singleShot(400, this, [this, nextRound]() {
-            if (!isProductionTestRunning || currentTestStep != TEST_AUDIO) {
-                return;
-            }
-            beginAudioTestAttempt(nextRound);
-        });
-        return;
-    }
+        QString standard = ui->audioLevelStandardLineEdit->text().trimmed();
+        if (standard.isEmpty()) {
+            updateTestStatus("mic", STATUS_SUCCESS);
+            ui->statusbar->showMessage(QString("录音测试通过(等级:%1,未配置标准不校验)").arg(micLevel), 3000);
+            skipToNextEnabledTest();
+            return;
+        }
 
-    if (audioTestAnyPass) {
-        updateTestStatus("mic", STATUS_SUCCESS);
-        ui->statusbar->showMessage(QString("录音测试通过(3轮中至少一轮达标,标准:%1)").arg(standard), 3000);
-    } else {
-        updateTestStatus("mic", STATUS_FAILED);
-        ui->statusbar->showMessage(QString("录音测试失败:3轮均未达到标准%1").arg(standard), 3000);
-    }
-    skipToNextEnabledTest();
+        // 等级排序:静音(0) < 极低(1) < 低(2) < 中(3) < 高(4);达标条件:micRank >= stdRank
+        auto levelRank = [](const QString& s) -> int {
+            if (s == QStringLiteral("静音")) return 0;
+            if (s == QStringLiteral("极低")) return 1;
+            if (s == QStringLiteral("低")) return 2;
+            if (s == QStringLiteral("中")) return 3;
+            if (s == QStringLiteral("高")) return 4;
+            return -1;
+        };
+
+        int micRank = levelRank(micLevel);
+        int stdRank = levelRank(standard);
+
+        if (micRank < 0 || stdRank < 0) {
+            updateTestStatus("mic", STATUS_FAILED);
+            ui->statusbar->showMessage(QString("录音测试失败:无法解析等级(实际:%1, 标准:%2)").arg(micLevel).arg(standard), 3000);
+            skipToNextEnabledTest();
+            return;
+        }
+
+        const bool roundPass = (micRank >= stdRank);
+        if (roundPass) {
+            appendToLogFile(QString(">>> 单轮达标(%1 >= 标准%2)\n").arg(micLevel).arg(standard));
+            updateTestStatus("mic", STATUS_SUCCESS);
+            ui->statusbar->showMessage(QString("录音测试通过(%1 ≥ 标准%2)").arg(micLevel).arg(standard), 3000);
+        } else {
+            appendToLogFile(QString(">>> 单轮未达标(%1 < 标准%2)\n").arg(micLevel).arg(standard));
+            updateTestStatus("mic", STATUS_FAILED);
+            ui->statusbar->showMessage(QString("录音测试失败(%1 低于标准%2)").arg(micLevel).arg(standard), 3000);
+        }
+        skipToNextEnabledTest();
+    });
 }
 
 // 执行电池测试
@@ -2689,8 +2672,8 @@ void MainWindow::performBatteryTest()
     updateTestStatus("battery", STATUS_TESTING);
     ui->statusbar->showMessage("电池测试中,正在获取电池电压...", 3000);
     
-    // 延迟100ms后发送电池电量查询指令,确保串口已准备好
-    QTimer::singleShot(100, this, [this]() {
+    // 短暂延迟后再发 GETPOWER
+    QTimer::singleShot(200, this, [this]() {
         if (isProductionTestRunning && currentTestStep == TEST_BATTERY && serial && serial->isOpen()) {
             // 发送电池电量查询指令
             serial->write("AT^GETPOWER\r\n");
@@ -3226,6 +3209,7 @@ void MainWindow::performLeaklightTest()
     ui->recvTextEdit->append(">>> 发送指令: AT^LEAKLIGHT");
     ui->statusbar->showMessage("漏光测试中,等待green/red/ir数据...", 3000);
 
+    leaklightAttemptIndex = 0;
     leaklightGreen = 0;
     leaklightRed = 0;
     leaklightIr = 0;
@@ -3274,10 +3258,32 @@ void MainWindow::checkLeaklightTestResult(const QString& data)
         if (pass) {
             updateTestStatus("leaklight", STATUS_SUCCESS);
             ui->statusbar->showMessage("漏光测试通过(green/red/ir均非0)", 2500);
-        } else {
-            updateTestStatus("leaklight", STATUS_FAILED);
-            ui->statusbar->showMessage("漏光测试失败(存在0值)", 2500);
+            skipToNextEnabledTest();
+            return;
         }
+
+        // 首次不通过:自动再测一次;重测仍不通过则判失败
+        if (leaklightAttemptIndex == 0) {
+            leaklightAttemptIndex = 1;
+            leaklightGreen = 0;
+            leaklightRed = 0;
+            leaklightIr = 0;
+            leaklightGreenSeen = false;
+            leaklightRedSeen = false;
+            leaklightIrSeen = false;
+            ui->recvTextEdit->append(">>> 漏光首次未通过(存在0值),自动重测一次");
+            appendToLogFile(">>> 漏光首次未通过,自动重测一次\n");
+            ui->statusbar->showMessage("漏光首次未通过,正在自动重测…", 3000);
+            if (serial && serial->isOpen()) {
+                ui->recvTextEdit->append(">>> 发送指令: AT^LEAKLIGHT (重测)");
+                serial->write("AT^LEAKLIGHT\r\n");
+            }
+            testTimeoutTimer->start(10000);
+            return;
+        }
+
+        updateTestStatus("leaklight", STATUS_FAILED);
+        ui->statusbar->showMessage("漏光测试失败(重测后仍存在0值)", 2500);
         skipToNextEnabledTest();
     }
 }
@@ -4008,24 +4014,21 @@ void MainWindow::showFinalTestResult()
     qDebug() << "显示最终测试结果弹窗";
     resultDialog.exec();
     
-    // 用户点击确定后的处理:关机,不执行重启/RESTORE
-    qDebug() << "用户确认测试结果,发送关机指令";
-    
-    if (serial->isOpen()) {
-        ui->recvTextEdit->append(">>> 发送指令: AT^POWEROFF (产测结束关机)");
-        serial->write("AT^POWEROFF\r\n");
-        qDebug() << "发送关机指令: AT^POWEROFF";
-        ui->statusbar->showMessage("已发送关机指令", 3000);
-    }
-    
-    // 恢复产测按钮状态 - 变回蓝色并启用
+    // 用户点击确定后:不自动发送关机(避免影响后续操作;需关机请用界面关机按钮)
+    qDebug() << "用户确认测试结果,产测收尾(不发送 AT^POWEROFF)";
+    ui->recvTextEdit->append(">>> 产测结束:未自动发送关机指令");
+    ui->statusbar->showMessage("产测已结束,未自动关机", 3000);
+    finalizeAfterProductionResultDialog();
+}
+
+void MainWindow::finalizeAfterProductionResultDialog()
+{
     ui->openButton_2->setStyleSheet("QPushButton { background-color: #4A90E2; color: white; padding: 5px 10px; border: none; border-radius: 3px; }");
     ui->openButton_2->setEnabled(true);
     qDebug() << "产测完成 - 按钮已恢复蓝色并启用";
-    
-    // 清空整个产测状态
+
     clearProductionTestState();
-    
+
     qDebug() << "产测流程完全结束,状态已清空";
 }
 
@@ -4228,7 +4231,7 @@ void MainWindow::onTestTimeout()
 void MainWindow::clearProductionTestState()
 {
     qDebug() << "开始清空产测状态";
-    
+
     // 停止所有定时器
     if (lcdTestTimer->isActive()) {
         lcdTestTimer->stop();
index 6f049fd..c3275a0 100644 (file)
@@ -84,8 +84,8 @@ private slots:
     void showKeyTestConfirmDialog(bool allKeysTested); // 显示按键测试确认弹窗
     void performDeepsleepTest(); // 功耗:ENTERTESTVIEW -> TESTJUMP=DEEPSLEEP -> FACTORYDEEPSLEEPENTER
     void showDeepsleepTestConfirmDialog(); // 功耗测试确认弹窗
-    void performAudioTest(); // 执行录音测试(有标准时共3轮,任一轮达标即通过)
-    void beginAudioTestAttempt(int roundIndex); // 发送单轮 AT^AUDIOTEST
+    void performAudioTest(); // 执行录音测试(单轮;有标准则按等级判定一次)
+    void beginAudioTestAttempt(); // 发送 AT^AUDIOTEST=5/7 单轮采集
     void checkAudioTestResult(const QString& data); // 检查录音测试结果
     void performBatteryTest(); // 执行电池测试
     void checkBatteryTestResult(const QString& data); // 检查电池测试结果
@@ -172,15 +172,14 @@ private:
     int gpsMaxCn;                      // GPS测试期间最大CN值(仅日志)
     QMap<QString, QDateTime> testStartTimeMap;   // 各项测试开始时间
     QMap<QString, qint64> testDurationSecMap;   // 各项测试耗时(秒)
-    int audioTestRoundIndex;            // 录音测试当前轮次 0..2(有等级标准时共3轮)
-    bool audioTestAnyPass;             // 三轮中是否至少一轮满足等级标准
-    bool audioTestAwaitingMicLevel;     // 本轮是否仍在等待首条 mic level(防重复解析)
+    bool audioTestAwaitingMicLevel;     // 是否仍在等待首条 mic level(防重复解析)
     int leaklightGreen;
     int leaklightRed;
     int leaklightIr;
     bool leaklightGreenSeen;
     bool leaklightRedSeen;
     bool leaklightIrSeen;
+    int leaklightAttemptIndex;         // 0=首次;1=首次失败后自动重测(仅一轮重试)
     // 日志文件相关变量
     QFile *testLogFile;                 // 测试日志文件对象
     QTextStream *testLogStream;         // 测试日志文件流
@@ -251,6 +250,8 @@ private:
     void finalizeGsensorTest();
     void stopGsensorCollectionAndFinalizeSoon(const QString& reasonSuffix);
 
+    void finalizeAfterProductionResultDialog();      // 恢复产测按钮并 clearProductionTestState
+
     // G-sensor XYZ窗口采集状态
     bool gSensorCollecting;            // 是否正在收集XYZ数据
     bool gSensorHasStart;             // 是否已记录起始XYZ