1
authorxue <2144439972@qq.com>
Fri, 10 Apr 2026 11:30:29 +0000 (19:30 +0800)
committerxue <2144439972@qq.com>
Fri, 10 Apr 2026 11:30:29 +0000 (19:30 +0800)
config_manager.cpp
factory_tool.pro
mainwindow.cpp
mainwindow.h
mainwindow.ui
qrcode_barcode_generator.cpp [deleted file]
qrcode_barcode_generator.h [deleted file]
qrcodegen.cpp [deleted file]
qrcodegen.hpp [deleted file]

index 7216de6..37697e2 100644 (file)
@@ -1,4 +1,4 @@
-#include "config_manager.h"
+#include "config_manager.h"
 #include <QFile>
 #include <QFileInfo>
 #include <QJsonDocument>
@@ -55,8 +55,6 @@ void ConfigManager::setDefaultConfig()
     testConfig["portName"] = "";
     testConfig["audioVolume"] = "2.0";
     testConfig["audioLevelStandard"] = "中";
-    testConfig["expectedModemVer"] = "";
-    testConfig["expectedPYVer"] = "";
     testConfig["expectedVer"] = "";
     testConfig["bluetoothAddress"] = "";
     testConfig["bluetoothRSSI"] = "";
@@ -68,10 +66,15 @@ void ConfigManager::setDefaultConfig()
     testCheckboxes["vib"] = true;
     testCheckboxes["btn"] = true;
     testCheckboxes["mic"] = true;
-    testCheckboxes["charge"] = true;
-    testCheckboxes["ntc"] = true;
+    testCheckboxes["tp"] = true;
+    testCheckboxes["bright"] = true;
+    testCheckboxes["battery"] = true;
     testCheckboxes["gsensor"] = true;
+    testCheckboxes["leaklight"] = true;
     testCheckboxes["bt"] = true;
+    testCheckboxes["sechip"] = true;
+    testCheckboxes["gps"] = true;
+    testCheckboxes["deepsleep"] = true;
     testCheckboxes["4g"] = true;
     defaultConfig["testCheckboxes"] = testCheckboxes;
     
index 047d1cf..5b5038e 100644 (file)
@@ -15,16 +15,12 @@ CODECFORSRC = UTF-8
 SOURCES += \
     main.cpp \
     mainwindow.cpp \
-    config_manager.cpp \
-    qrcode_barcode_generator.cpp \
-    qrcodegen.cpp
+    config_manager.cpp
 
 # 头文件列表
 HEADERS += \
     mainwindow.h \
-    config_manager.h \
-    qrcode_barcode_generator.h \
-    qrcodegen.hpp
+    config_manager.h
 
 # UI 文件列表(关键:必须包含 mainwindow.ui)
 FORMS += \
index d3894f6..4239790 100644 (file)
@@ -1,4 +1,4 @@
-#include "mainwindow.h"
+#include "mainwindow.h"
 #include "ui_mainwindow.h"
 #include "config_manager.h"
 #include <QMessageBox>
@@ -36,7 +36,7 @@ MainWindow::MainWindow(QWidget *parent)
     , ui(new Ui::MainWindow)
 {
     ui->setupUi(this);
-    setWindowTitle("雷鸟产测工具 V1.7");
+    setWindowTitle("FD272产测工具 V1.0");
     
     // 确保控制台输出使用UTF-8编码
     QTextCodec *utf8Codec = QTextCodec::codecForName("UTF-8");
@@ -62,71 +62,47 @@ MainWindow::MainWindow(QWidget *parent)
     // 设置产测按钮默认蓝色样式
     ui->openButton_2->setStyleSheet("QPushButton { background-color: #4A90E2; color: white; padding: 5px 10px; border: none; border-radius: 3px; }");
     
-    // 调整二维码/条形码区域的布局比例:上方二维码占 2/3,下方条形码占 1/3
-    if (ui->verticalLayout_4) {
-        ui->verticalLayout_4->setStretch(0, 0); // checkbox
-        ui->verticalLayout_4->setStretch(1, 2); // qr view
-        ui->verticalLayout_4->setStretch(2, 1); // barcode view
-    }
-    if (ui->verticalLayout_2) {
-        ui->verticalLayout_2->setStretch(0, 0); // checkbox
-        ui->verticalLayout_2->setStretch(1, 2); // qr view
-        ui->verticalLayout_2->setStretch(2, 1); // barcode view
-    }
     
     // 初始化产测相关变量
     lcdTestTimer = new QTimer(this);
     motorTestTimer = new QTimer(this);
-    test4GTimer = new QTimer(this);
-    signal4GTimer = new QTimer(this);
-    cat1SignalTimer = new QTimer(this);
     testTimeoutTimer = new QTimer(this);
+    gSensorIntervalTimer = new QTimer(this);
     currentLcdColor = 0;
-    initRetry4GCount = 0;
-    signalTest4GCount = 0;
-    signalReceived4GCount = 0;
-    signalGood4GCount = 0;
-    pppCommandCount = 0;
-    hasReceivedSignalSim = false;
-    hasReceivedFirstSignal = false;
-    cat1SignalTestCount = 0;
-    cat1SignalReceivedCount = 0;
-    cat1SignalGoodCount = 0;
-    cat1Signal99Count = 0;
-    cat1SignalTotalValue = 0;
-    hasReceivedPppInfo = false;
     isProductionTestRunning = false;
     currentTestStep = TEST_IDLE;
     lcdTestPopupShown = false;
     motorTestPopupShown = false;
+    touchTestPopupShown = false;
+    brightTestPopupShown = false;
+    deepsleepTestPopupShown = false;
     isWaitingForUserConfirm = false;
     lcdBacklightOn = false;            // 初始化LCD背光状态为关闭
     keyTestStarted = false;
     keyTestDialog = nullptr;
-    audioTestPopupShown = false;
-    audioPlaybackPopupShown = false;
-    audioRecordTimer = new QTimer(this);
-    audioTestDialog = nullptr;
-    audioDialogLabel = nullptr;
-    audioDialogInfo = nullptr;
-    audioStartButton = nullptr;
-    audioPassButton = nullptr;
-    audioFailButton = nullptr;
-    audioRetestButton = nullptr;
-    audioMicMiddleConsecutiveCount = 0;
-    audioMicTotalPassCount = 0;
-    audioTestFinished = false;
+    keyRotateDetected = false;
+    audioTestRoundIndex = 0;
+    audioTestAnyPass = false;
+    audioTestAwaitingMicLevel = false;
     bluetoothTestTimer = new QTimer(this);
+    gpsTestTimer = new QTimer(this);
+    gpsPollTimer = new QTimer(this);
     bluetoothAddresses.clear();
     bluetoothTestFinished = false;
+    gpsTestFinished = false;
+    gpsInfoSeenIn10Sec = false;
+    gpsMaxNumSats = 0;
+    gpsMaxTrace = 0;
+    gpsMaxCn = 0;
     detectedBluetoothAddress = "";
-    detectedBluetoothRSSI = 0;
-    pingTestTimer = new QTimer(this);
-    pingTestFinished = false;
-    pingSuccessCount = 0;
-    currentAudioStep = AUDIO_PLAYING_1KHZ;
-    currentModemVersion = "";
-    currentSimStatus = -1;
+    detectedBluetoothRSSI = -128;
+    bluetoothMaxRssi = -128;
+    leaklightGreen = 0;
+    leaklightRed = 0;
+    leaklightIr = 0;
+    leaklightGreenSeen = false;
+    leaklightRedSeen = false;
+    leaklightIrSeen = false;
     // 初始化日志文件相关变量
     testLogFile = nullptr;
     testLogStream = nullptr;
@@ -155,39 +131,25 @@ MainWindow::MainWindow(QWidget *parent)
     // 连接定时器信号
     connect(lcdTestTimer, &QTimer::timeout, this, &MainWindow::onLcdTestTimer);
     connect(motorTestTimer, &QTimer::timeout, this, &MainWindow::onMotorTestTimer);
-    connect(test4GTimer, &QTimer::timeout, this, &MainWindow::on4GTestTimeout);
-    connect(signal4GTimer, &QTimer::timeout, this, &MainWindow::on4GSignalTimer);
-    connect(cat1SignalTimer, &QTimer::timeout, this, &MainWindow::onCat1SignalTimer);
     connect(testTimeoutTimer, &QTimer::timeout, this, &MainWindow::onTestTimeout);
-    connect(audioRecordTimer, &QTimer::timeout, this, &MainWindow::onAudioRecordTimer);
+    connect(gSensorIntervalTimer, &QTimer::timeout, this, &MainWindow::onGsensorIntervalTimeout);
     connect(bluetoothTestTimer, &QTimer::timeout, this, &MainWindow::onBluetoothTestTimer);
-    connect(pingTestTimer, &QTimer::timeout, this, &MainWindow::onPingTestTimeout);
-    
-    // 连接ICCID和IMEI输入框的信号
-    connect(ui->ICCIDlineEdit, &QLineEdit::textChanged, this, &MainWindow::onICCIDLineEditTextChanged);
-    connect(ui->IMEIlineEdit, &QLineEdit::textChanged, this, &MainWindow::onIMEILineEditTextChanged);
-    
-    // 连接ICCID和IMEI复选框的信号
-    connect(ui->checkBox_iccid, &QCheckBox::stateChanged, this, &MainWindow::onICCIDCheckBoxStateChanged);
-    connect(ui->checkBox_imei, &QCheckBox::stateChanged, this, &MainWindow::onIMEICheckBoxStateChanged);
-    
-    // 初始化GraphicsView的场景
-    ui->graphicsView_iccid_qr->setScene(new QGraphicsScene(this));
-    ui->graphicsView_iccid_barcode->setScene(new QGraphicsScene(this));
-    ui->graphicsView_imei_qr->setScene(new QGraphicsScene(this));
-    ui->graphicsView_imei_barcode->setScene(new QGraphicsScene(this));
-    
-    // 设置GraphicsView的背景和缩放
-    ui->graphicsView_iccid_qr->setBackgroundBrush(QBrush(Qt::white));
-    ui->graphicsView_iccid_barcode->setBackgroundBrush(QBrush(Qt::white));
-    ui->graphicsView_imei_qr->setBackgroundBrush(QBrush(Qt::white));
-    ui->graphicsView_imei_barcode->setBackgroundBrush(QBrush(Qt::white));
-    
-    // 设置GraphicsView的缩放模式,使其能够自适应
-    ui->graphicsView_iccid_qr->setRenderHint(QPainter::Antialiasing, false);
-    ui->graphicsView_iccid_barcode->setRenderHint(QPainter::Antialiasing, false);
-    ui->graphicsView_imei_qr->setRenderHint(QPainter::Antialiasing, false);
-    ui->graphicsView_imei_barcode->setRenderHint(QPainter::Antialiasing, false);
+    connect(gpsTestTimer, &QTimer::timeout, this, &MainWindow::onGpsTestTimer);
+    connect(gpsPollTimer, &QTimer::timeout, this, &MainWindow::onGpsPollTimer);
+
+    // 初始化G-sensor采集状态
+    gSensorCollecting = false;
+    gSensorHasStart = false;
+    gSensorHasLast = false;
+    gSensorErrorDetected = false;
+    gSensorAnySample = false;
+    gSensorAllSamplesInRange = true;
+    gSensorStartX = 0.0;
+    gSensorStartY = 0.0;
+    gSensorStartZ = 0.0;
+    gSensorLastX = 0.0;
+    gSensorLastY = 0.0;
+    gSensorLastZ = 0.0;
 }
 
 MainWindow::~MainWindow()
@@ -510,534 +472,10 @@ void MainWindow::CheckVibratorFunc(const QString& data)
     }
 }
 
-// 提取IMEI信息(支持多种格式)
-void MainWindow::ShowPppInfoIMEI(const QString& data)
-{
-    QString imeiInfo;
-    
-    qDebug() << "开始提取IMEI,原始数据:" << data.trimmed();
-    
-    // 优先处理直接以数字开头的格式(最常见的新格式)
-    QRegExp imeiPattern("^(\\d{15})");
-    if (imeiPattern.indexIn(data.trimmed()) == 0) {
-        imeiInfo = imeiPattern.cap(1);
-        qDebug() << "使用数字开头格式提取IMEI:" << imeiInfo;
-    }
-    // 方法2:查找"IMEI:"标识的格式(传统格式)
-    else {
-        int start = data.indexOf("IMEI:", 0, Qt::CaseInsensitive);
-        if (start != -1) {
-            // 计算IMEI号码的起始位置(跳过"IMEI: ")
-            const QString key = "IMEI:";
-            start += key.length();
-            
-            // 跳过可能的空格
-            while (start < data.length() && data.at(start) == ' ') {
-                start++;
-            }
-
-            // 查找行结束标志(换行符或字符串结束)
-            int end = data.indexOf('\n', start);
-            if (end == -1) {
-                end = data.indexOf('\r', start);
-                if (end == -1) {
-                    end = data.length();
-                }
-            }
-
-            // 提取IMEI信息
-            imeiInfo = data.mid(start, end - start).trimmed();
-            qDebug() << "使用IMEI:格式提取IMEI:" << imeiInfo;
-        }
-        // 方法3:查找"imei:"标识的格式
-        else {
-            start = data.indexOf("imei:", 0, Qt::CaseInsensitive);
-            if (start != -1) {
-                // 计算IMEI号码的起始位置(跳过"imei:")
-                const QString key = "imei:";
-                start += key.length();
-                
-                // 查找@符号作为结束标志
-                int end = data.indexOf('@', start);
-                if (end == -1) {
-                    // 如果没有@符号,查找行结束标志
-                    end = data.indexOf('\n', start);
-                    if (end == -1) {
-                        end = data.indexOf('\r', start);
-                        if (end == -1) {
-                            end = data.length();
-                        }
-                    }
-                }
-                
-                // 提取IMEI信息
-                imeiInfo = data.mid(start, end - start).trimmed();
-                qDebug() << "使用imei:格式提取IMEI:" << imeiInfo;
-        } else {
-                qDebug() << "未找到任何IMEI格式,数据:" << data.trimmed();
-                return;
-            }
-        }
-    }
-
-    // 验证IMEI格式是否有效
-    if (!imeiInfo.isEmpty() && isValidIMEI(imeiInfo)) {
-        // 显示到IMEI文本框
-        ui->IMEIlineEdit->setText(imeiInfo);
-        qDebug() << "最终提取的IMEI:" << imeiInfo;
-        // 尝试重命名日志文件(如果ICCID也已获取)
-        renameLogFileWithDeviceInfo();
-    } else {
-        if (imeiInfo.isEmpty()) {
-            qDebug() << "IMEI信息为空,数据:" << data.trimmed();
-        } else {
-            qDebug() << "IMEI格式不正确,不显示到界面,IMEI:" << imeiInfo;
-            ui->statusbar->showMessage("IMEI格式不正确: " + imeiInfo, 3000);
-        }
-    }
-}
-
-// 提取ICCID信息(支持多种格式)
-void MainWindow::ShowPppInfoICCID(const QString& data)
-{
-    QString iccidInfo;
-    
-    // 方法1:使用正则表达式提取@iccid:后面的数字字母组合
-    QRegExp iccidRegex("@?iccid:([0-9A-Fa-f]{19,20})(?:@|\\s|$)", Qt::CaseInsensitive);
-    if (iccidRegex.indexIn(data) != -1) {
-        iccidInfo = iccidRegex.cap(1);
-        qDebug() << "使用正则表达式提取ICCID:" << iccidInfo;
-    } else {
-        // 方法2:传统方式查找"ICCID:"
-        int start = data.indexOf("ICCID:", 0, Qt::CaseInsensitive);
-        if (start == -1) {
-            qDebug() << "未找到ICCID:标识";
-            return;
-        }
-
-        // 计算ICCID号码的起始位置(跳过"ICCID:")
-        start += 6; // "ICCID:"的长度
-        
-        // 跳过可能的空格
-        while (start < data.length() && data.at(start) == ' ') {
-            start++;
-        }
-
-        // 查找ICCID结束标志(@符号、换行符或字符串结束)
-        int end = data.indexOf('@', start);
-        if (end == -1) {
-            end = data.indexOf('\n', start);
-            if (end == -1) {
-                end = data.indexOf('\r', start);
-                if (end == -1) {
-                    end = data.length();
-                }
-            }
-        }
-
-        // 提取ICCID信息
-        iccidInfo = data.mid(start, end - start).trimmed();
-        qDebug() << "使用传统方式提取ICCID:" << iccidInfo;
-    }
-
-    // 验证ICCID格式是否有效
-    if (isValidICCID(iccidInfo)) {
-        // 显示到ICCID文本框
-        ui->ICCIDlineEdit->setText(iccidInfo);
-        qDebug() << "提取的ICCID:" << iccidInfo;
-        // 尝试重命名日志文件(如果IMEI也已获取)
-        renameLogFileWithDeviceInfo();
-    } else {
-        qDebug() << "ICCID格式不正确,不显示到界面,ICCID:" << iccidInfo;
-        ui->statusbar->showMessage("ICCID格式不正确: " + iccidInfo, 3000);
-    }
-}
-
-// 提取Modem Version信息(支持多种格式)
-void MainWindow::ShowPppInfoModemVer(const QString& data)
-{
-    QString modemVerInfo;
-    int start;
-    
-    // 方法1:查找"Modem Version:"标识的格式(传统格式)
-    start = data.indexOf("Modem Version:", 0, Qt::CaseInsensitive);
-    if (start != -1) {
-        // 计算版本信息的起始位置(跳过"Modem Version: ")
-        const QString key = "Modem Version:";
-        start += key.length();
-        
-        // 跳过可能的空格
-        while (start < data.length() && data.at(start) == ' ') {
-            start++;
-        }
-
-        // 查找行结束标志(换行符或字符串结束)
-        int end = data.indexOf('\n', start);
-        if (end == -1) {
-            end = data.indexOf('\r', start);
-            if (end == -1) {
-                end = data.length();
-            }
-        }
-
-        // 提取Modem版本信息
-        modemVerInfo = data.mid(start, end - start).trimmed();
-    }
-    // 方法2:查找"@modemVer:"标识的格式(新格式)
-    else {
-        start = data.indexOf("@modemVer:", 0, Qt::CaseInsensitive);
-        if (start != -1) {
-            // 计算版本信息的起始位置(跳过"@modemVer:")
-            const QString key = "@modemVer:";
-            start += key.length();
-            
-            // 查找行结束标志(换行符或字符串结束)
-            int end = data.indexOf('\n', start);
-            if (end == -1) {
-                end = data.indexOf('\r', start);
-                if (end == -1) {
-                    end = data.length();
-                }
-            }
-            
-            // 提取Modem版本信息
-            modemVerInfo = data.mid(start, end - start).trimmed();
-            qDebug() << "使用@modemVer:格式提取Modem Version:" << modemVerInfo;
-        }
-        // 方法3:查找"modemver(...)"括号格式
-        else {
-            // 使用正则表达式提取括号内的内容
-            QRegExp modemPattern("modemver\\(([^)]+)\\)", Qt::CaseInsensitive);
-            if (modemPattern.indexIn(data) != -1) {
-                modemVerInfo = modemPattern.cap(1);
-                qDebug() << "使用括号格式提取Modem Version:" << modemVerInfo;
-                } else {
-                qDebug() << "未找到Modem Version信息,数据格式:" << data.trimmed();
-                    return;
-                }
-            }
-    }
-
-    // 验证Modem Version是否有效(不为空即可)
-    if (!modemVerInfo.isEmpty()) {
-        // 保存到成员变量
-        currentModemVersion = modemVerInfo;
-        // 显示到ModemVer文本框
-        ui->ModemVerlineEdit->setText(modemVerInfo);
-        qDebug() << "提取的Modem Version:" << modemVerInfo;
-        qDebug() << "MODEM类型判断: " << (isEGModem() ? "EG系列" : "非EG系列");
-
-        // 清理版本字符串:移除特殊字符,去除首尾空格
-        modemVerInfo = modemVerInfo.replace("!", "").trimmed();
-        currentModemVersion = modemVerInfo;
-        ui->ModemVerlineEdit->setText(modemVerInfo);
-
-        // 校验期望MODEM_VER
-        QString expectedModem = ui->expectedModemVerLineEdit ? ui->expectedModemVerLineEdit->text().trimmed() : QString();
-        // 对期望值也进行相同的清理处理
-        if (!expectedModem.isEmpty()) {
-            expectedModem = expectedModem.replace("!", "").trimmed();
-        }
-        
-        qDebug() << "提取的Modem Version:" << modemVerInfo;
-        qDebug() << "期望的Modem Version:" << expectedModem;
-        
-        // 使用大小写不敏感对比,更宽松
-        if (!expectedModem.isEmpty() && modemVerInfo.compare(expectedModem, Qt::CaseInsensitive) != 0) {
-            ui->statusbar->showMessage("MODEM版本不一致: 实际=" + modemVerInfo + " 期望=" + expectedModem, 5000);
-            QMessageBox::critical(this, "版本校验失败", "MODEM_VER不一致,测试失败!\n实际: " + modemVerInfo + "\n期望: " + expectedModem);
-            isProductionTestRunning = false;
-            currentTestStep = TEST_COMPLETED;
-            showFinalTestResult();
-            return;
-        }
-    } else {
-        qDebug() << "Modem Version信息为空,数据:" << data.trimmed();
-    }
-}
-
-// 提取PYVER Version信息(支持多种格式)
-void MainWindow::ShowPppInfoPYVer(const QString& data)
-{
-    QString PYVerInfo;
-    int start;
-    
-    // 方法1:查找"Modem Version:"标识的格式(传统格式)
-    start = data.indexOf("PYVER Version:", 0, Qt::CaseInsensitive);
-    if (start != -1) {
-        // 计算版本信息的起始位置(跳过"Modem Version: ")
-        const QString key = "PYVER Version:";
-        start += key.length();
-        
-        // 跳过可能的空格
-        while (start < data.length() && data.at(start) == ' ') {
-            start++;
-        }
-
-        // 查找结束位置:优先查找@modemVer:(如果PYVer和ModemVer在同一行)
-        int end = data.indexOf("@modemVer:", start, Qt::CaseInsensitive);
-        if (end == -1) {
-            // 如果没有@modemVer:,查找行结束标志(换行符或字符串结束)
-            end = data.indexOf('\n', start);
-            if (end == -1) {
-                end = data.indexOf('\r', start);
-                if (end == -1) {
-                    end = data.length();
-                }
-            }
-        }
-
-        // 提取PYVer版本信息(到@modemVer:之前或行尾)
-        PYVerInfo = data.mid(start, end - start).trimmed();
-    }
-    // 方法2:查找"@pyver:"标识的格式(新格式)
-    else {
-        start = data.indexOf("@pyver:", 0, Qt::CaseInsensitive);
-        if (start != -1) {
-            // 计算版本信息的起始位置(跳过"@pyver:")
-            const QString key = "@pyver:";
-            start += key.length();
-            
-            // 查找结束位置:优先查找@modemVer:(如果PYVer和ModemVer在同一行)
-            int end = data.indexOf("@modemVer:", start, Qt::CaseInsensitive);
-            if (end == -1) {
-                // 如果没有@modemVer:,查找行结束标志(换行符或字符串结束)
-                end = data.indexOf('\n', start);
-                if (end == -1) {
-                    end = data.indexOf('\r', start);
-                    if (end == -1) {
-                        end = data.length();
-                    }
-                }
-            }
-            
-            // 提取PYVer版本信息(到@modemVer:之前或行尾)
-            PYVerInfo = data.mid(start, end - start).trimmed();
-            qDebug() << "使用@pyver:格式提取pyver:" << PYVerInfo;
-        }
-        // 方法3:查找"modemver(...)"括号格式
-        else {
-            // 使用正则表达式提取括号内的内容
-            QRegExp PYPattern("pyver\\(([^)]+)\\)", Qt::CaseInsensitive);
-            if (PYPattern.indexIn(data) != -1) {
-                PYVerInfo = PYPattern.cap(1);
-                qDebug() << "使用括号格式提取pvver :" << PYVerInfo;
-            } else {
-                qDebug() << "使用括号格式提取pvver :" << data.trimmed();
-                return;
-            }
-        }
-    }
-
-    // 验证PYVER Version是否有效(不为空即可)
-    if (!PYVerInfo.isEmpty()) {
-        // 清理版本字符串:移除特殊字符,去除首尾空格
-        PYVerInfo = PYVerInfo.replace("!", "").trimmed();
-        
-        // 保存到成员变量
-        currentPYVerVersion = PYVerInfo;
-        // 显示到PYVer文本框
-        ui->PYVerlineEdit->setText(PYVerInfo);
-        qDebug() << "提取的PYVER Version:" << PYVerInfo;
-        qDebug() << "MODEM类型判断: " << (isEGModem() ? "EG系列" : "非EG系列");
-        
-        // 校验期望PYVER
-        QString expectedPYVER = ui->expectedPYVerLineEdit ? ui->expectedPYVerLineEdit->text().trimmed() : QString();
-        // 对期望值也进行相同的清理处理
-        if (!expectedPYVER.isEmpty()) {
-            expectedPYVER = expectedPYVER.replace("!", "").trimmed();
-        }
-        
-        qDebug() << "提取的PYVER Version:" << PYVerInfo;
-        qDebug() << "期望的PYVER Version:" << expectedPYVER;
-        
-        // 使用大小写不敏感对比,更宽松
-        if (!expectedPYVER.isEmpty() && PYVerInfo.compare(expectedPYVER, Qt::CaseInsensitive) != 0) {
-            ui->statusbar->showMessage("PYVER版本不一致: 实际=" + PYVerInfo + " 期望=" + expectedPYVER, 5000);
-            QMessageBox::critical(this, "版本校验失败", "PYVER不一致,测试失败!\n实际: " + PYVerInfo + "\n期望: " + expectedPYVER);
-            isProductionTestRunning = false;
-            currentTestStep = TEST_COMPLETED;
-            showFinalTestResult();
-            return;
-        }
-    } else {
-        qDebug() << "PYVER Version信息为空,数据:" << data.trimmed();
-    }
-}
-
-// IMEI格式验证函数 - IMEI应为15位数字
-bool MainWindow::isValidIMEI(const QString& imei)
-{
-    // 检查是否为空
-    if (imei.isEmpty()) {
-        return false;
-    }
-    
-    // 检查长度是否为15位
-    if (imei.length() != 15) {
-        qDebug() << "IMEI长度不正确,应为15位,实际为:" << imei.length() << "位,IMEI:" << imei;
-        return false;
-    }
-    
-    // 检查是否全为数字
-    for (int i = 0; i < imei.length(); i++) {
-        if (!imei.at(i).isDigit()) {
-            qDebug() << "IMEI包含非数字字符,IMEI:" << imei;
-            return false;
-        }
-    }
-    
-    qDebug() << "IMEI格式验证通过:" << imei;
-    return true;
-}
-
-// ICCID格式验证函数 - ICCID应为19-20位字母数字组合
-bool MainWindow::isValidICCID(const QString& iccid)
-{
-    // 检查是否为空
-    if (iccid.isEmpty()) {
-        return false;
-    }
-    
-    // 检查长度是否为19-20位
-    if (iccid.length() < 19 || iccid.length() > 20) {
-        qDebug() << "ICCID长度不正确,应为19-20位,实际为:" << iccid.length() << "位,ICCID:" << iccid;
-        return false;
-    }
-    
-    // 检查是否为字母数字组合(允许数字和字母)
-    for (int i = 0; i < iccid.length(); i++) {
-        QChar ch = iccid.at(i);
-        if (!ch.isDigit() && !ch.isLetter()) {
-            qDebug() << "ICCID包含非法字符(只允许数字和字母),ICCID:" << iccid;
-            return false;
-        }
-    }
-    
-    qDebug() << "ICCID格式验证通过:" << iccid;
-    return true;
-}
-
-// MODEM类型判断函数 - 判断是否为EG系列MODEM
-bool MainWindow::isEGModem() const
-{
-    bool result = currentModemVersion.startsWith("EG", Qt::CaseInsensitive);
-
-    if(!ui->PYVerlineEdit->text().isEmpty())
-    {
-        qDebug() << "存在PYVER版本,判断为EG版本" << currentPYVerVersion;
-        return true;
-    }
-    //  else {
-    //     qDebug() << "不存在PYVER版本,判断为非EG版本" << PYVerInfo;
-    //     return false;
-    // }
-
-    qDebug() << "isEGModem() 调用 - MODEM版本:" << currentModemVersion << ",结果:" << (result ? "EG系列" : "非EG系列");
-    return result;
-}
-
-// 解析MESSAGE_4G_NETWORK_STAT_CHANGED格式数据
+// 解析MESSAGE_4G_NETWORK_STAT_CHANGED格式数据(4G功能已移除,保留空实现以兼容旧日志)
 void MainWindow::parseNetworkStatChanged(const QString& data)
 {
-    qDebug() << "开始解析MESSAGE_4G_NETWORK_STAT_CHANGED数据:" << data.trimmed();
-    
-    // 使用正则表达式解析各个字段
-    // 格式:netstat(0),signal(99),sim(1),network(GSM),imei(860132064650112),iccid(898604B7192590313291),operator(-1)
-    
-    // 解析netstat
-    QRegExp netstatRegex("netstat\\((\\d+)\\)");
-    if (netstatRegex.indexIn(data) != -1) {
-        int netstat = netstatRegex.cap(1).toInt();
-        qDebug() << "解析到netstat值:" << netstat;
-    }
-    
-    // 解析signal值
-    QRegExp signalRegex("signal\\((\\d+)\\)");
-    if (signalRegex.indexIn(data) != -1) {
-        int signalValue = signalRegex.cap(1).toInt();
-        qDebug() << "解析到signal值:" << signalValue;
-        
-        // 将signal值显示到界面
-        appendToLogFile(QString("Network Stat Signal值: %1\n").arg(signalValue));
-        ui->statusbar->showMessage(QString("检测到Network Stat Signal值: %1").arg(signalValue), 2000);
-        
-        // 如果正在进行4G测试,处理signal值
-        if (isProductionTestRunning && (currentTestStep == TEST_PPP_INIT || currentTestStep == TEST_PPP_SIGNAL)) {
-            // 构造类似@sim:@signal:格式的数据供现有逻辑处理
-            QString simulatedData = QString("@sim:%1@signal:%2@").arg(1).arg(signalValue); // 假设sim为1
-            
-            // 解析sim值(从数据中提取实际值)
-            QRegExp simRegex("sim\\((\\d+)\\)");
-            if (simRegex.indexIn(data) != -1) {
-                int simValue = simRegex.cap(1).toInt();
-                simulatedData = QString("@sim:%1@signal:%2@").arg(simValue).arg(signalValue);
-                qDebug() << "解析到sim值:" << simValue;
-                
-                // 保存SIM卡状态
-                currentSimStatus = simValue;
-                qDebug() << "更新SIM卡状态:" << (currentSimStatus == 1 ? "已插入" : "未插入");
-            }
-            
-            qDebug() << "构造模拟数据供现有逻辑处理:" << simulatedData;
-            checkSignalAndSimValues(simulatedData);
-        }
-    }
-    
-    // 解析sim值
-    QRegExp simRegex("sim\\((\\d+)\\)");
-    if (simRegex.indexIn(data) != -1) {
-        int simValue = simRegex.cap(1).toInt();
-        qDebug() << "解析到sim值:" << simValue;
-        
-        // 保存SIM卡状态
-        currentSimStatus = simValue;
-        qDebug() << "更新SIM卡状态:" << (currentSimStatus == 1 ? "已插入" : "未插入");
-    }
-    
-    // 解析network类型
-    QRegExp networkRegex("network\\(([^)]+)\\)");
-    if (networkRegex.indexIn(data) != -1) {
-        QString networkType = networkRegex.cap(1);
-        qDebug() << "解析到network类型:" << networkType;
-    }
-    
-    // 解析IMEI
-    QRegExp imeiRegex("imei\\((\\d{15})\\)");
-    if (imeiRegex.indexIn(data) != -1) {
-        QString imei = imeiRegex.cap(1);
-        qDebug() << "解析到IMEI:" << imei;
-        
-        // 验证IMEI格式
-        if (isValidIMEI(imei)) {
-            ui->IMEIlineEdit->setText(imei);
-            qDebug() << "IMEI格式验证通过,已更新到界面:" << imei;
-        } else {
-            qDebug() << "IMEI格式验证失败:" << imei;
-        }
-    }
-    
-    // 解析ICCID
-    QRegExp iccidRegex("iccid\\(([0-9A-Fa-f]{19,20})\\)");
-    if (iccidRegex.indexIn(data) != -1) {
-        QString iccid = iccidRegex.cap(1);
-        qDebug() << "解析到ICCID:" << iccid;
-        
-        // 验证ICCID格式
-        if (isValidICCID(iccid)) {
-            ui->ICCIDlineEdit->setText(iccid);
-            qDebug() << "ICCID格式验证通过,已更新到界面:" << iccid;
-        } else {
-            qDebug() << "ICCID格式验证失败:" << iccid;
-        }
-    }
-    
-    // 解析operator
-    QRegExp operatorRegex("operator\\(([^)]+)\\)");
-    if (operatorRegex.indexIn(data) != -1) {
-        QString operatorValue = operatorRegex.cap(1);
-        qDebug() << "解析到operator值:" << operatorValue;
-    }
+    Q_UNUSED(data);
 }
 
 // 串口数据接收处理函数
@@ -1155,115 +593,14 @@ void MainWindow::readSerialData()
             }
         }
         
-        // 检测设备信息数据(去掉[PPP_INFO]限制,支持多种格式)(大小写不敏感)
-        // 检测包含@sim:的数据(可能是IMEI数据和4G信号数据)
-        if (line.contains("@sim:") || processLine.contains("@sim:")) {
-            qDebug() << "检测到包含@sim:的数据:";
-            qDebug() << "  原始line:" << line.trimmed();
-            qDebug() << "  processLine:" << processLine.trimmed();
-            
-            // 优先处理processLine
-            QString dataToProcess = processLine.contains("@sim:") ? processLine : line;
-            
-            // 提取IMEI(15位数字)
-            QRegExp imeiRegex("(\\d{15})");
-            if (imeiRegex.indexIn(dataToProcess) != -1) {
-                QString extractedImei = imeiRegex.cap(1);
-                qDebug() << "从@sim:数据中提取到IMEI:" << extractedImei;
-                // 验证IMEI格式
-                if (isValidIMEI(extractedImei)) {
-                    ui->IMEIlineEdit->setText(extractedImei);
-                } else {
-                    qDebug() << "从@sim:数据中提取的IMEI格式不正确:" << extractedImei;
-                    ui->statusbar->showMessage("IMEI格式不正确: " + extractedImei, 3000);
-                }
-            } else {
-                qDebug() << "在@sim:数据中未找到15位数字";
-                ShowPppInfoIMEI(dataToProcess);
-            }
-            
-            // 检查是否正在进行4G测试,如果是则解析signal和sim值进行判断
-            if (isProductionTestRunning && (currentTestStep == TEST_PPP_INIT || currentTestStep == TEST_PPP_SIGNAL)) {
-                checkSignalAndSimValues(dataToProcess);
-            }
-        }
-        // 检测传统格式的IMEI数据:IMEI:开头的格式
-        else if (line.indexOf("IMEI:", 0, Qt::CaseInsensitive) != -1 || processLine.indexOf("IMEI:", 0, Qt::CaseInsensitive) != -1) {
-            qDebug() << "接收到IMEI数据:" << line.trimmed();
-            qDebug() << "原始数据长度:" << line.length() << "内容:" << line.toUtf8().toHex();
-            if (processLine.indexOf("IMEI:", 0, Qt::CaseInsensitive) != -1) {
-                ShowPppInfoIMEI(processLine);
-            } else {
-                ShowPppInfoIMEI(line);
-            }
-        }
-        // 检测新格式的IMEI数据:imei:开头的格式
-        else if (line.indexOf("imei:", 0, Qt::CaseInsensitive) != -1 || processLine.indexOf("imei:", 0, Qt::CaseInsensitive) != -1) {
-            qDebug() << "接收到新格式IMEI数据:" << line.trimmed();
-            if (processLine.indexOf("imei:", 0, Qt::CaseInsensitive) != -1) {
-                ShowPppInfoIMEI(processLine);
-            } else {
-                ShowPppInfoIMEI(line);
-            }
-        }
-
-        if (line.indexOf("ICCID:", 0, Qt::CaseInsensitive) != -1) {
-            qDebug() << "接收到ICCID数据:" << line.trimmed();
-            ShowPppInfoICCID(line);
-        }
-
-        if (line.indexOf("Modem Version:", 0, Qt::CaseInsensitive) != -1) {
-            qDebug() << "接收到Modem Version数据:" << line.trimmed();
-            ShowPppInfoModemVer(line);
-        }
-        // 检测新格式的Modem Version数据:@modemVer:开头的格式
-        else if (line.indexOf("@modemVer:", 0, Qt::CaseInsensitive) != -1) {
-            qDebug() << "接收到新格式Modem Version数据:" << line.trimmed();
-            ShowPppInfoModemVer(line);
-        }
-        // 检测括号格式的Modem Version数据:modemver(...)格式
-        else if (line.indexOf("modemver(", 0, Qt::CaseInsensitive) != -1) {
-            qDebug() << "接收到括号格式Modem Version数据:" << line.trimmed();
-            ShowPppInfoModemVer(line);
-        }
-
-        if (line.indexOf("pyver:", 0, Qt::CaseInsensitive) != -1) {
-            qDebug() << "接收到pyver数据:" << line.trimmed();
-            ShowPppInfoPYVer(line);
-        }
-        // 检测新格式的Modem Version数据:@modemVer:开头的格式
-        else if (line.indexOf("@pyVer:", 0, Qt::CaseInsensitive) != -1) {
-            qDebug() << "接收到新格式接收到括号格式pyver数据数据:" << line.trimmed();
-            ShowPppInfoPYVer(line);
-        }
-        // 检测括号格式的Modem Version数据:modemver(...)格式
-        else if (line.indexOf("pyver(", 0, Qt::CaseInsensitive) != -1) {
-            qDebug() << "接收到括号格式pyver数据:" << line.trimmed();
-            ShowPppInfoPYVer(line);
-        }
-        
-        // 检测MESSAGE_4G_NETWORK_STAT_CHANGED格式数据
-        if (line.contains("MESSAGE_4G_NETWORK_STAT_CHANGED")) {
-            qDebug() << "接收到MESSAGE_4G_NETWORK_STAT_CHANGED数据:" << line.trimmed();
-            parseNetworkStatChanged(line);
-        }
+        // 4G/PPP 相关解析已移除
         
         // 检测工厂模式进入确认(用于产测)
         if (line.contains("fac:enter or exit facotry mode")) {
             checkFactoryModeResult(line);
         }
         
-        // 检测PPP初始化结果(用于产测)
-        if (line.contains("PPP environment initialized successfully") || 
-            line.contains("PPP init failed")) {
-            check4GInitResult(line);
-        }
-        
-        // 检测4G信号质量结果(用于产测)
-        if (line.contains("Overall LTE Quality") || line.contains("LTE Signal Quality") || 
-            line.contains("RSRQ:") || line.contains("RSRP:") || line.contains("总体评级")) {
-            check4GSignalResult(line);
-        }
+        // 4G/PPP 相关解析已移除
         
         // 检测LCD测试结果(用于产测)
         if (line.contains("LCD test")) {
@@ -1276,15 +613,13 @@ void MainWindow::readSerialData()
         }
         
         // 检测按键测试结果(用于产测)
-        if (line.contains("fac:id")) {
+        if (line.contains("fac:id") || line.contains("testPresenter::OnRotate")) {
             checkKeyTestResult(line);
         }
         
         // 检测录音测试结果(用于产测)
-        if (line.contains("MEDIAAUDIORECORD") || line.contains("MEDIAMUSICPLAY") ||
-            line.contains("Start Audio Capturer SUCCESS") ||
-            line.contains("fac:mic level:")) {
-            qDebug() << "录音测试相关数据匹配:" << line.trimmed();
+        if ((line.contains("AudioTest", Qt::CaseInsensitive) || line.contains("mic level", Qt::CaseInsensitive)) &&
+            isProductionTestRunning && currentTestStep == TEST_AUDIO) {
             checkAudioTestResult(line);
         }
         
@@ -1294,48 +629,41 @@ void MainWindow::readSerialData()
         }
         
         // 检测G-sensor测试结果(用于产测)
-        if (line.contains("gsensor") || line.contains("GSENSOR") || line.contains("fac:gsensor")) {
-            qDebug() << "检测到gsensor相关数据,调用checkGsensorTestResult:" << line.trimmed();
+        // 设备输出格式可能为:XYZ: X=-1.28,Y=8.22,Z=-4.60
+        if (line.contains("XYZ:") || line.contains("gsensor") || line.contains("GSENSOR") || line.contains("fac:gsensor")) {
+            qDebug() << "检测到gsensor/XYZ相关数据,调用checkGsensorTestResult:" << line.trimmed();
             checkGsensorTestResult(line);
         }
+
+        // 检测漏光测试结果
+        if (line.contains("leaklight:", Qt::CaseInsensitive) || line.contains("green:", Qt::CaseInsensitive) ||
+            line.contains("red:", Qt::CaseInsensitive) || line.contains("ir:", Qt::CaseInsensitive)) {
+            checkLeaklightTestResult(line);
+        }
         
         // 检测蓝牙测试结果(用于产测)
-        if (line.contains("fac:Device:") || line.contains("[BtScan]") || line.contains("BTSCANTEST")) {
+        // 扩大触发条件:fac:Device、[BtScan]、BTSCANTEST、RSSI+Device、RSSI+MAC、或RSSI+MAC地址格式(xx:xx:xx:xx:xx:xx)
+        bool btRelated = line.contains("fac:Device:") || line.contains("[BtScan]") || line.contains("BTSCANTEST") ||
+                         (line.contains("RSSI", Qt::CaseInsensitive) && (line.contains("Device", Qt::CaseInsensitive) || line.contains("MAC", Qt::CaseInsensitive) || line.count(':') >= 5));
+        if (btRelated && isProductionTestRunning && currentTestStep == TEST_BLUETOOTH) {
             qDebug() << "检测到蓝牙测试相关数据,调用checkBluetoothTestResult:" << line.trimmed();
             checkBluetoothTestResult(line);
         }
         
-        // 检测CAT1信号测试结果 - 只在CAT1测试期间且包含特定格式时处理
-        if (hasReceivedFirstSignal && isProductionTestRunning && line.contains("signal:")) {
-            // 根据MODEM类型过滤特定格式的数据
-            bool shouldProcess = false;
-            if (isEGModem()) {
-                // EG系列:只处理rx_buffer格式,跳过cmd格式
-                shouldProcess = line.contains("rx_buffer:") && !line.contains("cmd ") && !line.contains("bufferlen");
-            } else {
-                // N7系列:处理PPPSIGNALINFO相关格式
-                shouldProcess = line.contains("RSRP:") || line.contains("signal:");
-            }
-            
-            if (shouldProcess) {
-                qDebug() << "检测到CAT1信号相关数据:" << line.trimmed();
-                checkCat1SignalResult(line);
-            } else {
-                qDebug() << "跳过CAT1信号数据(格式不匹配):" << line.left(50) + "...";
-            }
+        // 检测SE芯片测试结果(cywee is active / cywee is not active)
+        if (line.contains("cywee", Qt::CaseInsensitive)) {
+            checkSeChipTestResult(line);
         }
         
-        // 检测Ping测试结果
-        if (isProductionTestRunning && currentTestStep == TEST_PPP_PING && 
-            (line.contains("PING", Qt::CaseInsensitive) || 
-             line.contains("bytes from", Qt::CaseInsensitive) ||
-             line.contains("Reply from", Qt::CaseInsensitive) ||
-             line.contains("timeout", Qt::CaseInsensitive) ||
-             line.contains("unreachable", Qt::CaseInsensitive))) {
-            qDebug() << "检测到Ping测试相关数据:" << line.trimmed();
-            checkPingTestResult(line);
+        // 检测GPS测试结果(GPSINFO / trace 等)
+        if ((line.contains("GPSINFO", Qt::CaseInsensitive) || line.contains("num_sats", Qt::CaseInsensitive) ||
+             line.contains("trace", Qt::CaseInsensitive)) &&
+            isProductionTestRunning && currentTestStep == TEST_GPS) {
+            checkGpsTestResult(line);
         }
         
+        // 4G/PPP/Ping/CAT1 相关解析已移除
+        
         // 检测LCD背光状态
         if (line.contains("LCD backlight turned on")) {
             lcdBacklightOn = true;
@@ -1406,7 +734,6 @@ void MainWindow::on_ProductionTestButton_clicked()
     if (isTestItemEnabled("lcd")) {
         currentTestStep = TEST_LCD;
         currentLcdColor = 1; // 从红色开始
-        initRetry4GCount = 0;
         
         ui->statusbar->showMessage("开始产测流程...", 2000);
         qDebug() << "开始产测流程";
@@ -1419,7 +746,6 @@ void MainWindow::on_ProductionTestButton_clicked()
         // LCD未启用,跳过到下一个启用的测试
         currentTestStep = TEST_LCD; // 先设置为LCD,然后skipToNextEnabledTest会跳过
         currentLcdColor = 1; // 初始化LCD颜色
-        initRetry4GCount = 0;
         
         ui->statusbar->showMessage("开始产测流程...", 2000);
         qDebug() << "开始产测流程,LCD测试未启用,跳过";
@@ -1654,23 +980,16 @@ void MainWindow::performMotorTestContinue()
     motorTestPopupShown = false;
     
     updateTestStatus("motor", STATUS_TESTING);
-    ui->statusbar->showMessage("马达测试中 - 开启2秒", 2000);
+    ui->statusbar->showMessage("马达测试中 - 开启2秒后确认", 2000);
     
-    // 发送马达开启指令
     serial->write("AT^VIBRATORTIMER=1\r\n");
     qDebug() << "发送马达开启指令: AT^VIBRATORTIMER=1";
     
-    // 先停止可能正在运行的定时器
     if (motorTestTimer->isActive()) {
         motorTestTimer->stop();
-        qDebug() << "警告:马达定时器已在运行,先停止";
     }
-    
-    // 设置2秒定时器
-    qDebug() << "启动马达定时器,2秒后关闭马达";
     motorTestTimer->start(2000);
     
-    // 启动10秒超时检测
     testTimeoutTimer->start(10000);
 }
 
@@ -1683,14 +1002,10 @@ void MainWindow::onMotorTestTimer()
         return;
     }
     
-    qDebug() << "马达测试时间到,关闭马达";
-    ui->statusbar->showMessage("马达测试中 - 关闭", 1000);
-    
-    // 发送马达关闭指令
-    serial->write("AT^VIBRATORTIMER=0\r\n");
-    qDebug() << "发送马达关闭指令: AT^VIBRATORTIMER=0";
+    qDebug() << "马达测试时间到,显示确认弹窗";
+    ui->statusbar->showMessage("马达测试 - 请确认", 1000);
     
-    // 马达测试完成,显示确认弹窗
+    // 马达测试完成,显示确认弹窗(不再发送 VIBRATORTIMER=0)
     qDebug() << "马达测试完成,显示确认弹窗";
     
     // 如果弹窗还没有显示过,则显示确认弹窗
@@ -1703,82 +1018,7 @@ void MainWindow::onMotorTestTimer()
     }
 }
 
-// 4G测试函数
-void MainWindow::perform4GTest()
-{
-    // 检查4G测试是否启用
-    if (!isTestItemEnabled("ppp")) {
-        qDebug() << "4G测试未启用,跳过";
-        updateTestStatus("ppp", STATUS_UNTESTED);
-        // 4G测试是最后一个测试,如果未启用,直接完成测试
-        if (isProductionTestRunning) {
-            currentTestStep = TEST_COMPLETED;
-            isProductionTestRunning = false;
-            QString fileName = saveTestRecord();
-            showFinalTestResult();
-        }
-        return;
-    }
-    
-    if (!isProductionTestRunning) {
-        qDebug() << "4G测试跳过 - 产测未运行";
-        return;
-    }
-    
-    initRetry4GCount++;
-    qDebug() << "4G测试 - 初始化尝试次数:" << initRetry4GCount;
-    
-    if (initRetry4GCount == 1) {
-        ui->recvTextEdit->append("========== 开始4G测试 ==========");
-    } else {
-        ui->recvTextEdit->append(QString("========== 4G测试重试 (第%1次) ==========").arg(initRetry4GCount));
-    }
-    updateTestStatus("ppp", STATUS_TESTING);
-    
-    // 4G电源已在初始化阶段开关,这里直接使用CAT1GETSIGNAL获取信号
-    qDebug() << "4G电源已在初始化阶段开关,直接开始4G信号质量检测";
-    ui->statusbar->showMessage("4G测试中 - 开始信号强度检测...", 3000);
-    
-    // 重置标志
-    hasReceivedSignalSim = false;
-    hasReceivedPppInfo = true; // 标记为已收到,跳过PPP等待
-    hasReceivedFirstSignal = false; // 重置,确保能重新启动CAT1测试
-    pppCommandCount = 0;
-    
-    // 初始化CAT1信号测试计数器
-    cat1SignalTestCount = 0;
-    cat1SignalReceivedCount = 0;
-    cat1SignalGoodCount = 0;
-    cat1Signal99Count = 0;
-    cat1SignalTotalValue = 0;
-    
-    // 停止可能正在运行的定时器
-    if (cat1SignalTimer->isActive()) {
-        cat1SignalTimer->stop();
-        qDebug() << "停止CAT1信号测试定时器";
-    }
-    if (signal4GTimer->isActive()) {
-        signal4GTimer->stop();
-        qDebug() << "停止4G信号检测定时器";
-    }
-    
-    // 直接开始CAT1信号测试,使用CAT1GETSIGNAL指令
-    currentTestStep = TEST_PPP_SIGNAL;
-    qDebug() << "开始CAT1信号测试,发送AT^CAT1GETSIGNAL指令";
-    ui->recvTextEdit->append("========== 开始4G信号质量测试 ==========");
-    ui->statusbar->showMessage("开始CAT1信号测试,每2秒检测一次...", 3000);
-    
-    // 启动CAT1信号测试定时器,每2秒发送一次
-    cat1SignalTimer->start(2000);
-    
-    // 立即发送第一次CAT1信号检测指令
-    serial->write("AT^CAT1GETSIGNAL\r\n");
-    cat1SignalTestCount++;
-    qDebug() << "发送第" << cat1SignalTestCount << "次CAT1信号检测指令: AT^CAT1GETSIGNAL";
-    ui->recvTextEdit->append(">>> 发送指令: AT^CAT1GETSIGNAL");
-}
-
-// 检查4G初始化结果
+// 检查4G初始化结果(4G功能已移除)
 // 检查工厂模式进入结果
 void MainWindow::checkFactoryModeResult(const QString& data)
 {
@@ -1870,164 +1110,36 @@ void MainWindow::checkFactoryModeResult(const QString& data)
     }
 }
 
-void MainWindow::check4GInitResult(const QString& data)
+// 检查LCD测试结果
+void MainWindow::checkLcdTestResult(const QString& data)
 {
-    if (!isProductionTestRunning || currentTestStep != TEST_PPP_INIT) {
-        return;
-    }
-
-    // 收到回复,停止超时定时器
-    if (test4GTimer->isActive()) {
-        test4GTimer->stop();
-        qDebug() << "收到4G回复,停止超时定时器";
-    }
-
-    if (data.contains("PPP environment initialized successfully")) {
-        qDebug() << "4G初始化通过(已废弃,不再使用PPP相关指令)";
-        // PPP相关指令已失效,不再处理
+    // 检测LCD测试回复信息,并且只在LCD测试阶段且未显示过弹窗时弹出
+    // 修改:在最后一个颜色(蓝色)显示时才弹窗,让用户观察完整过程
+    if (data.contains("LCD test") && currentTestStep == TEST_LCD && 
+        !lcdTestPopupShown && isProductionTestRunning && currentLcdColor >= 3) {
         
-    } else if (data.contains("PPP init failed")) {
-        qDebug() << "4G初始化失败,重试中...";
-        ui->statusbar->showMessage("4G初始化失败,重试中...", 2000);
+        qDebug() << "检测到LCD测试回复:" << data.trimmed();
+        qDebug() << "准备显示LCD测试确认弹窗";
         
-        // 如果重试次数过多,可以设置上限
-        if (initRetry4GCount >= 10) {
-            qDebug() << "4G初始化重试次数过多,停止测试";
-            ui->statusbar->showMessage("4G初始化失败次数过多,测试停止", 5000);
-            updateTestStatus("ppp", STATUS_FAILED);
-            isProductionTestRunning = false;
-            currentTestStep = TEST_IDLE;
-            
-        // 保存测试记录到Excel(失败记录)
-            QString fileName = saveTestRecord();
-            
-            // 显示最终测试结果弹窗
-            showFinalTestResult();
-            // ui->ProductionTestButton->setText("开始产测");
-            return;
-        }
+        // 标记弹窗已显示,避免重复弹出
+        lcdTestPopupShown = true;
+        isWaitingForUserConfirm = true;
         
-        // 先发送停止指令,然后延迟重试
-        qDebug() << "4G失败重试前发送停止指令";
-        serial->write("AT^VIBRATORTIMER=0\r\n");  // 停止马达
-        serial->write("AT^LCDTEST=2\r\n");        // 退出LCD测试
+        // 注意:不暂停LCD定时器,让颜色切换继续进行
+        // LCD颜色切换应该独立于用户确认过程
+        qDebug() << "显示LCD确认弹窗,但颜色切换继续进行";
         
-        QTimer::singleShot(1000, this, [this]() {
-            qDebug() << "停止指令已发送,开始4G失败重试";
-            perform4GTest();
-        });
+        // LCD测试回复已检测到,弹窗处理由performLcdTest中的定时器处理
+        
+        // 不需要在这里处理弹窗,由performLcdTest中的定时器处理
     }
 }
 
-// 4G测试超时处理
-void MainWindow::on4GTestTimeout()
+// 显示LCD测试确认弹窗
+void MainWindow::showLcdTestConfirmDialog()
 {
-    test4GTimer->stop();
-    
-    if (!isProductionTestRunning || currentTestStep != TEST_PPP_INIT) {
-        qDebug() << "4G超时但测试状态已改变,忽略";
-        return;
-    }
-    
-    // 检查当前是在等待PPP回复还是等待signal和sim数据
-    if (pppCommandCount <= 3) {
-        // 前3次,等待PPP数据超时
-        if (hasReceivedPppInfo) {
-            qDebug() << "PPP数据等待超时,但已收到PPPINFO,继续正常流程";
-            return;
-        }
-        
-        qDebug() << "PPP数据第" << pppCommandCount << "次等待超时,准备重试";
-        
-        // 重新等待PPP数据
-        QTimer::singleShot(1000, this, [this]() {
-            if (isProductionTestRunning) {
-                sendPppCommand();
-            }
-        });
-        return;
-    } else {
-        // 超过3次后,等待signal和sim数据超时
-        if (hasReceivedSignalSim) {
-            qDebug() << "4G测试超时,但已收到signal和sim数据,测试已完成";
-            return; // 已经在checkSignalAndSimValues中处理了结果
-        }
-        
-        qDebug() << "4G测试超时,5秒内未收到signal和sim数据,判断为失败";
-        ui->statusbar->showMessage("4G测试失败 - 5秒内未收到signal和sim数据", 3000);
-    }
-    
-    // 直接判断为失败,不再重试
-    updateTestStatus("ppp", STATUS_FAILED);
-    isProductionTestRunning = false;
-    currentTestStep = TEST_COMPLETED;
-    
-    // 保存测试记录
-    QString fileName = saveTestRecord();
-    
-    // 显示最终测试结果弹窗
-    showFinalTestResult();
-    return;
-    
-    // 检查重试次数(这部分代码现在不会执行到)
-    if (initRetry4GCount >= 10) {
-        qDebug() << "4G初始化重试次数过多,停止测试";
-        ui->statusbar->showMessage("4G初始化失败次数过多,测试停止", 5000);
-        updateTestStatus("ppp", STATUS_FAILED);
-        isProductionTestRunning = false;
-        currentTestStep = TEST_IDLE;
-        
-        // 保存测试记录到Excel(失败记录)
-        QString fileName = saveTestRecord();
-        
-        // 显示最终测试结果弹窗
-        showFinalTestResult();
-        return;
-    }
-    
-    // 先发送停止指令,然后延迟重试
-    qDebug() << "PPP重试前发送停止指令";
-    serial->write("AT^VIBRATORTIMER=0\r\n");  // 停止马达
-    // serial->write("AT^LCDTEST=2\r\n");        // 退出LCD测试 - 已屏蔽
-    
-    QTimer::singleShot(1000, this, [this]() {
-        if (isProductionTestRunning && currentTestStep == TEST_PPP_INIT) {
-            qDebug() << "停止指令已发送,开始PPP重试";
-            perform4GTest();
-        }
-    });
-}
-
-// 检查LCD测试结果
-void MainWindow::checkLcdTestResult(const QString& data)
-{
-    // 检测LCD测试回复信息,并且只在LCD测试阶段且未显示过弹窗时弹出
-    // 修改:在最后一个颜色(蓝色)显示时才弹窗,让用户观察完整过程
-    if (data.contains("LCD test") && currentTestStep == TEST_LCD && 
-        !lcdTestPopupShown && isProductionTestRunning && currentLcdColor >= 3) {
-        
-        qDebug() << "检测到LCD测试回复:" << data.trimmed();
-        qDebug() << "准备显示LCD测试确认弹窗";
-        
-        // 标记弹窗已显示,避免重复弹出
-        lcdTestPopupShown = true;
-        isWaitingForUserConfirm = true;
-        
-        // 注意:不暂停LCD定时器,让颜色切换继续进行
-        // LCD颜色切换应该独立于用户确认过程
-        qDebug() << "显示LCD确认弹窗,但颜色切换继续进行";
-        
-        // LCD测试回复已检测到,弹窗处理由performLcdTest中的定时器处理
-        
-        // 不需要在这里处理弹窗,由performLcdTest中的定时器处理
-    }
-}
-
-// 显示LCD测试确认弹窗
-void MainWindow::showLcdTestConfirmDialog()
-{
-    if (lcdTestPopupShown) {
-        qDebug() << "LCD确认弹窗已显示过,跳过";
+    if (lcdTestPopupShown) {
+        qDebug() << "LCD确认弹窗已显示过,跳过";
         return;
     }
 
@@ -2040,7 +1152,7 @@ void MainWindow::showLcdTestConfirmDialog()
         // 弹出确认对话框让测试人员确认LCD显示是否正常
         QMessageBox msgBox(this);
         msgBox.setWindowTitle("LCD测试确认");
-    msgBox.setText("LCD测试正在进行,请观察LCD是否正常显示红、绿、蓝色切换");
+    msgBox.setText("LCD测试正在进行,请观察LCD是否正常显示红、绿、蓝、白色切换");
     msgBox.setInformativeText("观察完成后请选择LCD显示结果:");
         msgBox.setIcon(QMessageBox::Question);
     
@@ -2349,12 +1461,22 @@ void MainWindow::showMotorTestConfirmDialog()
     if (msgBox.clickedButton() == successButton) {
         // 测试人员确认马达震动正常
         qDebug() << "测试人员确认:马达震动正常";
+        if (serial && serial->isOpen()) {
+            serial->write("AT^VIBRATORTIMER=0\r\n");
+            qDebug() << "马达测试通过:发送停止马达指令 AT^VIBRATORTIMER=0";
+            ui->recvTextEdit->append(">>> 发送指令: AT^VIBRATORTIMER=0 (测试通过,关闭马达)");
+        }
         ui->statusbar->showMessage("马达测试通过 - 震动正常!", 3000);
         updateTestStatus("motor", STATUS_SUCCESS);
         
     } else if (msgBox.clickedButton() == failButton) {
         // 测试人员确认马达震动异常
         qDebug() << "测试人员确认:马达震动异常";
+        if (serial && serial->isOpen()) {
+            serial->write("AT^VIBRATORTIMER=0\r\n");
+            qDebug() << "马达测试失败:发送停止马达指令 AT^VIBRATORTIMER=0";
+            ui->recvTextEdit->append(">>> 发送指令: AT^VIBRATORTIMER=0 (测试失败,关闭马达)");
+        }
         ui->statusbar->showMessage("马达测试失败 - 震动异常!", 3000);
         updateTestStatus("motor", STATUS_FAILED);
         
@@ -2401,6 +1523,302 @@ void MainWindow::showMotorTestConfirmDialog()
     }
 }
 
+// 执行触控测试(AT^TESTJUMP=TP)
+void MainWindow::performTouchTest()
+{
+    if (!isProductionTestRunning || currentTestStep != TEST_TOUCH) {
+        return;
+    }
+    if (!isTestItemEnabled("tp")) {
+        qDebug() << "触控测试未启用,跳过";
+        updateTestStatus("tp", STATUS_UNTESTED);
+        skipToNextEnabledTest();
+        return;
+    }
+
+    if (testTimeoutTimer->isActive()) {
+        testTimeoutTimer->stop();
+    }
+
+    ui->recvTextEdit->append("========== 开始触控测试(TESTJUMP=TP) ==========");
+    updateTestStatus("tp", STATUS_TESTING);
+    ui->statusbar->showMessage("触控测试中:将进入触控测试界面,请等待判断弹窗", 3000);
+
+    // 1) 每次跳转前先回到产测界面
+    if (serial && serial->isOpen()) {
+        ui->recvTextEdit->append(">>> 发送指令: AT^ENTERTESTVIEW (进入产测界面)");
+        serial->write("AT^ENTERTESTVIEW\r\n");
+        qDebug() << "发送指令: AT^ENTERTESTVIEW (触控测试前)";
+    }
+
+    // 2) 延迟后进入对应测试跳转界面
+    QTimer::singleShot(300, this, [this]() {
+        if (!isProductionTestRunning || currentTestStep != TEST_TOUCH) return;
+        if (!serial || !serial->isOpen()) return;
+
+        ui->recvTextEdit->append(">>> 发送指令: AT^TESTJUMP=TP");
+        serial->write("AT^TESTJUMP=TP\r\n");
+        qDebug() << "发送指令: AT^TESTJUMP=TP";
+
+        // 3) 进入界面后弹窗让用户判断
+        QTimer::singleShot(500, this, [this]() {
+            if (isProductionTestRunning && currentTestStep == TEST_TOUCH) {
+                showTouchTestConfirmDialog();
+            }
+        });
+    });
+}
+
+// 触控测试确认弹窗
+void MainWindow::showTouchTestConfirmDialog()
+{
+    if (touchTestPopupShown) {
+        return;
+    }
+
+    touchTestPopupShown = true;
+    isWaitingForUserConfirm = true;
+
+    QMessageBox msgBox(this);
+    msgBox.setWindowTitle("触控测试确认");
+    msgBox.setIcon(QMessageBox::Question);
+    msgBox.setText("触控测试已进入,请测试人员判断触控是否正常。");
+    msgBox.setInformativeText("请选择触控测试结果:");
+    msgBox.resize(420, 200);
+
+    QPushButton *successButton = msgBox.addButton("✓ 测试通过", QMessageBox::AcceptRole);
+    QPushButton *failButton = msgBox.addButton("✗ 测试失败", QMessageBox::RejectRole);
+    QPushButton *retestButton = msgBox.addButton("🔄 重新测试", QMessageBox::ResetRole);
+
+    successButton->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; padding: 12px 20px; border-radius: 4px; font-weight: bold; font-size: 16px; }");
+    failButton->setStyleSheet("QPushButton { background-color: #f44336; color: white; padding: 12px 20px; border-radius: 4px; font-weight: bold; font-size: 16px; }");
+    retestButton->setStyleSheet("QPushButton { background-color: #FF9800; color: white; padding: 12px 20px; border-radius: 4px; font-weight: bold; font-size: 16px; }");
+
+    msgBox.exec();
+
+    isWaitingForUserConfirm = false;
+
+    if (msgBox.clickedButton() == successButton) {
+        ui->statusbar->showMessage("触控测试通过", 2000);
+        updateTestStatus("tp", STATUS_SUCCESS);
+    } else if (msgBox.clickedButton() == failButton) {
+        ui->statusbar->showMessage("触控测试失败", 2000);
+        updateTestStatus("tp", STATUS_FAILED);
+    } else if (msgBox.clickedButton() == retestButton) {
+        // 重新测试
+        touchTestPopupShown = false;
+        isWaitingForUserConfirm = false;
+        QTimer::singleShot(300, this, [this]() {
+            if (isProductionTestRunning && currentTestStep == TEST_TOUCH) {
+                performTouchTest();
+            }
+        });
+        return;
+    }
+
+    touchTestPopupShown = false;
+    if (isProductionTestRunning && currentTestStep == TEST_TOUCH) {
+        skipToNextEnabledTest();
+    }
+}
+
+// 执行亮度测试(AT^BRIGHTRAMP)
+void MainWindow::performBrightTest()
+{
+    if (!isProductionTestRunning || currentTestStep != TEST_BRIGHT) {
+        return;
+    }
+    if (!isTestItemEnabled("bright")) {
+        qDebug() << "亮度测试未启用,跳过";
+        updateTestStatus("bright", STATUS_UNTESTED);
+        skipToNextEnabledTest();
+        return;
+    }
+
+    if (testTimeoutTimer->isActive()) {
+        testTimeoutTimer->stop();
+    }
+
+    ui->recvTextEdit->append("========== 开始亮度测试(AT^BRIGHTRAMP) ==========");
+    updateTestStatus("bright", STATUS_TESTING);
+    ui->statusbar->showMessage("亮度测试中:自动调节亮度,请观察后确认", 3000);
+
+    if (!serial || !serial->isOpen()) {
+        return;
+    }
+    ui->recvTextEdit->append(">>> 发送指令: AT^BRIGHTRAMP");
+    serial->write("AT^BRIGHTRAMP\r\n");
+    qDebug() << "发送指令: AT^BRIGHTRAMP";
+
+    QTimer::singleShot(500, this, [this]() {
+        if (isProductionTestRunning && currentTestStep == TEST_BRIGHT) {
+            showBrightTestConfirmDialog();
+        }
+    });
+}
+
+void MainWindow::showBrightTestConfirmDialog()
+{
+    if (brightTestPopupShown) {
+        return;
+    }
+
+    brightTestPopupShown = true;
+    isWaitingForUserConfirm = true;
+
+    QMessageBox msgBox(this);
+    msgBox.setWindowTitle("亮度测试确认");
+    msgBox.setIcon(QMessageBox::Question);
+    msgBox.setText("亮度测试已进入,请测试人员判断亮度是否正常。");
+    msgBox.setInformativeText("请选择亮度测试结果:");
+    msgBox.resize(420, 200);
+
+    QPushButton *successButton = msgBox.addButton("✓ 测试通过", QMessageBox::AcceptRole);
+    QPushButton *failButton = msgBox.addButton("✗ 测试失败", QMessageBox::RejectRole);
+    QPushButton *retestButton = msgBox.addButton("🔄 重新测试", QMessageBox::ResetRole);
+
+    successButton->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; padding: 12px 20px; border-radius: 4px; font-weight: bold; font-size: 16px; }");
+    failButton->setStyleSheet("QPushButton { background-color: #f44336; color: white; padding: 12px 20px; border-radius: 4px; font-weight: bold; font-size: 16px; }");
+    retestButton->setStyleSheet("QPushButton { background-color: #FF9800; color: white; padding: 12px 20px; border-radius: 4px; font-weight: bold; font-size: 16px; }");
+
+    msgBox.exec();
+
+    isWaitingForUserConfirm = false;
+
+    if (msgBox.clickedButton() == successButton) {
+        ui->statusbar->showMessage("亮度测试通过", 2000);
+        updateTestStatus("bright", STATUS_SUCCESS);
+    } else if (msgBox.clickedButton() == failButton) {
+        ui->statusbar->showMessage("亮度测试失败", 2000);
+        updateTestStatus("bright", STATUS_FAILED);
+    } else if (msgBox.clickedButton() == retestButton) {
+        brightTestPopupShown = false;
+        isWaitingForUserConfirm = false;
+        QTimer::singleShot(300, this, [this]() {
+            if (isProductionTestRunning && currentTestStep == TEST_BRIGHT) {
+                performBrightTest();
+            }
+        });
+        return;
+    }
+
+    brightTestPopupShown = false;
+    if (isProductionTestRunning && currentTestStep == TEST_BRIGHT) {
+        skipToNextEnabledTest();
+    }
+}
+
+// 执行功耗测试(AT^TESTJUMP=DEEPSLEEP,进入后由人工确认)
+void MainWindow::performDeepsleepTest()
+{
+    if (!isProductionTestRunning || currentTestStep != TEST_DEEPSLEEP) {
+        return;
+    }
+    if (!isTestItemEnabled("deepsleep")) {
+        qDebug() << "功耗测试未启用,跳过";
+        updateTestStatus("deepsleep", STATUS_UNTESTED);
+        skipToNextEnabledTest();
+        return;
+    }
+
+    if (testTimeoutTimer->isActive()) {
+        testTimeoutTimer->stop();
+    }
+
+    ui->recvTextEdit->append("========== 开始功耗测试(TESTJUMP=DEEPSLEEP) ==========");
+    updateTestStatus("deepsleep", STATUS_TESTING);
+    ui->statusbar->showMessage("功耗测试:将进入深度睡眠测试界面,请等待确认弹窗", 3000);
+
+    if (serial && serial->isOpen()) {
+        ui->recvTextEdit->append(">>> 发送指令: AT^ENTERTESTVIEW (进入产测界面)");
+        serial->write("AT^ENTERTESTVIEW\r\n");
+        qDebug() << "发送指令: AT^ENTERTESTVIEW (功耗测试前)";
+    }
+
+    QTimer::singleShot(300, this, [this]() {
+        if (!isProductionTestRunning || currentTestStep != TEST_DEEPSLEEP) {
+            return;
+        }
+        if (!serial || !serial->isOpen()) {
+            return;
+        }
+        ui->recvTextEdit->append(">>> 发送指令: AT^TESTJUMP=DEEPSLEEP");
+        serial->write("AT^TESTJUMP=DEEPSLEEP\r\n");
+        qDebug() << "发送指令: AT^TESTJUMP=DEEPSLEEP";
+
+        // 仅进入界面不会真正进深睡,需再发 FACTORYDEEPSLEEPENTER
+        QTimer::singleShot(400, this, [this]() {
+            if (!isProductionTestRunning || currentTestStep != TEST_DEEPSLEEP) {
+                return;
+            }
+            if (!serial || !serial->isOpen()) {
+                return;
+            }
+            ui->recvTextEdit->append(">>> 发送指令: AT^FACTORYDEEPSLEEPENTER");
+            serial->write("AT^FACTORYDEEPSLEEPENTER\r\n");
+            qDebug() << "发送指令: AT^FACTORYDEEPSLEEPENTER";
+            appendToLogFile(">>> 发送指令: AT^FACTORYDEEPSLEEPENTER\n");
+
+            QTimer::singleShot(500, this, [this]() {
+                if (isProductionTestRunning && currentTestStep == TEST_DEEPSLEEP) {
+                    showDeepsleepTestConfirmDialog();
+                }
+            });
+        });
+    });
+}
+
+void MainWindow::showDeepsleepTestConfirmDialog()
+{
+    if (deepsleepTestPopupShown) {
+        return;
+    }
+
+    deepsleepTestPopupShown = true;
+    isWaitingForUserConfirm = true;
+
+    QMessageBox msgBox(this);
+    msgBox.setWindowTitle("功耗测试确认");
+    msgBox.setIcon(QMessageBox::Question);
+    msgBox.setText("设备已进入深度睡眠(功耗)测试界面,请测试人员根据功耗/唤醒表现判断是否正常。");
+    msgBox.setInformativeText("请选择功耗测试结果:");
+    msgBox.resize(420, 200);
+
+    QPushButton *successButton = msgBox.addButton("✓ 测试通过", QMessageBox::AcceptRole);
+    QPushButton *failButton = msgBox.addButton("✗ 测试失败", QMessageBox::RejectRole);
+    QPushButton *retestButton = msgBox.addButton("🔄 重新测试", QMessageBox::ResetRole);
+
+    successButton->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; padding: 12px 20px; border-radius: 4px; font-weight: bold; font-size: 16px; }");
+    failButton->setStyleSheet("QPushButton { background-color: #f44336; color: white; padding: 12px 20px; border-radius: 4px; font-weight: bold; font-size: 16px; }");
+    retestButton->setStyleSheet("QPushButton { background-color: #FF9800; color: white; padding: 12px 20px; border-radius: 4px; font-weight: bold; font-size: 16px; }");
+
+    msgBox.exec();
+
+    isWaitingForUserConfirm = false;
+
+    if (msgBox.clickedButton() == successButton) {
+        ui->statusbar->showMessage("功耗测试通过", 2000);
+        updateTestStatus("deepsleep", STATUS_SUCCESS);
+    } else if (msgBox.clickedButton() == failButton) {
+        ui->statusbar->showMessage("功耗测试失败", 2000);
+        updateTestStatus("deepsleep", STATUS_FAILED);
+    } else if (msgBox.clickedButton() == retestButton) {
+        deepsleepTestPopupShown = false;
+        isWaitingForUserConfirm = false;
+        QTimer::singleShot(300, this, [this]() {
+            if (isProductionTestRunning && currentTestStep == TEST_DEEPSLEEP) {
+                performDeepsleepTest();
+            }
+        });
+        return;
+    }
+
+    deepsleepTestPopupShown = false;
+    if (isProductionTestRunning && currentTestStep == TEST_DEEPSLEEP) {
+        skipToNextEnabledTest();
+    }
+}
+
 // 执行按键测试
 void MainWindow::performKeyTest()
 {
@@ -2434,48 +1852,58 @@ void MainWindow::performKeyTest()
     }
 
     qDebug() << "开始按键测试";
-    ui->recvTextEdit->append("========== 开始按键测试 ==========");
+    ui->recvTextEdit->append("========== 开始按键测试(TESTJUMP=KEY) ==========");
     updateTestStatus("key", STATUS_TESTING);
-    ui->statusbar->showMessage("按键测试中,请按下设备上的按键", 10000);
+    ui->statusbar->showMessage("按键测试中,请按键0并执行旋转动作", 10000);
     
-    // 重置按键测试状态
+    // 重置按键测试状态(仅按键0 + 旋转)
     keyTestStarted = true;
     for (int i = 0; i < 4; i++) {
         keyTestStatus[i] = 0;
     }
+    keyRotateDetected = false;
+
+    // 先跳转产测主界面,再跳KEY测试界面
+    if (serial && serial->isOpen()) {
+        ui->recvTextEdit->append(">>> 发送指令: AT^ENTERTESTVIEW (进入产测界面)");
+        serial->write("AT^ENTERTESTVIEW\r\n");
+    }
+    QTimer::singleShot(300, this, [this]() {
+        if (!isProductionTestRunning || currentTestStep != TEST_KEY) {
+            return;
+        }
+        if (serial && serial->isOpen()) {
+            ui->recvTextEdit->append(">>> 发送指令: AT^TESTJUMP=KEY");
+            serial->write("AT^TESTJUMP=KEY\r\n");
+        }
+    });
     
     // 创建按键测试弹窗
     keyTestDialog = new QDialog(this);
     keyTestDialog->setWindowTitle("按键测试");
-    keyTestDialog->setFixedSize(450, 280);  // 调大尺寸
+    keyTestDialog->setFixedSize(460, 250);
     keyTestDialog->setModal(true);
     
     // 设置布局
     QVBoxLayout *layout = new QVBoxLayout(keyTestDialog);
-    QLabel *instructionLabel = new QLabel("请æ\8c\89ä¸\8b设å¤\87ä¸\8aç\9a\84æ\8c\89é\94®ï¼\8cæ\8c\89é\92®ä¼\9aå\8f\98è\89²è¡¨ç¤ºæ£\80æµ\8bå\88°");
+    QLabel *instructionLabel = new QLabel("请æ£\80æµ\8bæ\8c\89é\94®0ä¸\8eæ\97\8b转äº\8bä»¶ï¼\9bæ£\80æµ\8bå\88°å\90\8eæ\8c\89é\92®ä¼\9aå\8f\98绿");
     layout->addWidget(instructionLabel);
-    
-    // 创建按键按钮网格
-    QGridLayout *gridLayout = new QGridLayout();
-    
-    // 创建4个按键按钮
-    for (int i = 0; i < 4; i++) {
-        keyButtons[i] = new QPushButton(QString("按键 %1").arg(i));
-        keyButtons[i]->setFixedSize(60, 40);
-        keyButtons[i]->setStyleSheet(
-            "QPushButton { "
-            "background-color: #f0f0f0; "
-            "border: 2px solid #888888; "
-            "border-radius: 5px; "
-            "font-weight: bold; "
-            "}"
-        );
-        
-        // 将按钮添加到网格布局(2x2)
-        gridLayout->addWidget(keyButtons[i], i / 2, i % 2);
-    }
-    
-    layout->addLayout(gridLayout);
+
+    // 仅保留按键0和旋转两个检测项
+    QHBoxLayout *indicators = new QHBoxLayout();
+    keyButtons[0] = new QPushButton("按键 0");
+    keyButtons[0]->setFixedSize(120, 44);
+    keyButtons[0]->setStyleSheet("QPushButton { background-color: #f0f0f0; border: 2px solid #888888; border-radius: 5px; font-weight: bold; }");
+
+    keyButtons[1] = new QPushButton("旋转");
+    keyButtons[1]->setFixedSize(120, 44);
+    keyButtons[1]->setStyleSheet("QPushButton { background-color: #f0f0f0; border: 2px solid #888888; border-radius: 5px; font-weight: bold; }");
+    keyButtons[2] = nullptr;
+    keyButtons[3] = nullptr;
+
+    indicators->addWidget(keyButtons[0]);
+    indicators->addWidget(keyButtons[1]);
+    layout->addLayout(indicators);
     
     // 添加测试结果按钮
     QHBoxLayout *buttonLayout = new QHBoxLayout();
@@ -2580,59 +2008,53 @@ void MainWindow::checkKeyTestResult(const QString& data)
     QRegExp keyPattern("fac:id\\s*:\\s*(\\d+)");
     if (keyPattern.indexIn(data) != -1) {
         int keyId = keyPattern.cap(1).toInt();
-        
-        if (keyId >= 0 && keyId <= 3) {
+        if (keyId == 0) {
             qDebug() << "检测到按键按下: ID =" << keyId;
-            
-            // 标记该按键已测试
-            keyTestStatus[keyId] = 1;
-            
-            // 更新按钮颜色
-            if (keyButtons[keyId]) {
-                keyButtons[keyId]->setStyleSheet(
+            keyTestStatus[0] = 1;
+            if (keyButtons[0]) {
+                keyButtons[0]->setStyleSheet(
                     "QPushButton { "
-                    "background-color: #4CAF50; "  // 绿色表示已测试
+                    "background-color: #4CAF50; "
                     "color: white; "
                     "border: 2px solid #45a049; "
                     "border-radius: 5px; "
                     "font-weight: bold; "
                     "}"
                 );
-                keyButtons[keyId]->setText(QString("按键 %1 ✓").arg(keyId));
+                keyButtons[0]->setText("按键 0 ✓");
             }
-            
-            ui->statusbar->showMessage(QString("检测到按键 %1").arg(keyId), 1000);
-            
-            // 检查是否所有按键都已测试
-            bool allKeysTested = true;
-            for (int i = 0; i < 4; i++) {
-                if (keyTestStatus[i] == 0) {
-                    allKeysTested = false;
-        break;
-                }
-            }
-            
-            if (allKeysTested) {
-                qDebug() << "所有按键都已检测到,自动通过按键测试";
-                ui->statusbar->showMessage("所有按键检测完成,自动通过测试", 2000);
-                
-                // 延迟1秒后自动关闭弹窗并标记为通过
-                QTimer::singleShot(1000, [this]() {
-                    if (keyTestDialog && isProductionTestRunning && currentTestStep == TEST_KEY) {
-                        keyTestDialog->accept();
-                        qDebug() << "按键测试自动通过";
-                        updateTestStatus("key", STATUS_SUCCESS);
-                        
-                        // 继续下一个测试
-                        qDebug() << "=== 按键测试自动完成,推进到下一个测试 ===";
-                        keyTestStarted = false;
-                        skipToNextEnabledTest();
-                    }
-                });
-            } else {
-                qDebug() << "按键 " << keyId << " 检测完成,等待其他按键或用户选择";
+            ui->statusbar->showMessage("检测到按键 0", 1000);
+        }
+    }
+
+    // 旋转事件:testPresenter::OnRotate
+    if (data.contains("testPresenter::OnRotate")) {
+        keyRotateDetected = true;
+        if (keyButtons[1]) {
+            keyButtons[1]->setStyleSheet(
+                "QPushButton { "
+                "background-color: #4CAF50; "
+                "color: white; "
+                "border: 2px solid #45a049; "
+                "border-radius: 5px; "
+                "font-weight: bold; "
+                "}"
+            );
+            keyButtons[1]->setText("旋转 ✓");
+        }
+        ui->statusbar->showMessage("检测到旋转事件 OnRotate", 1000);
+    }
+
+    // 条件满足自动通过:按键0 + 旋转
+    if (keyTestStatus[0] == 1 && keyRotateDetected) {
+        QTimer::singleShot(500, [this]() {
+            if (keyTestDialog && isProductionTestRunning && currentTestStep == TEST_KEY) {
+                keyTestDialog->accept();
+                updateTestStatus("key", STATUS_SUCCESS);
+                keyTestStarted = false;
+                skipToNextEnabledTest();
             }
-        }
+        });
     }
 }
 
@@ -2734,87 +2156,34 @@ void MainWindow::checkBuildVerResult(const QString& data)
                 serial->write("AT^PMADDVOTE\r\n");
                 qDebug() << "发送AT^PMADDVOTE指令(第2次): AT^PMADDVOTE";
                 
-                // å¦\82æ\9e\9c4Gæµ\8bè¯\95å·²å\90¯ç\94¨ï¼\8cå\85\88å\85³é\97­å\92\8cå¼\80å\90¯4Gç\94µæº\90
-                if (isTestItemEnabled("ppp")) {
-                    qDebug() << "4G测试已启用,在初始化阶段先关闭和开启4G电源";
-                    // 先发送AT^CAT1POWER=0关闭CAT1电源
-                    serial->write("AT^CAT1POWER=0\r\n");
-                    qDebug() << "发送CAT1电源关闭指令: AT^CAT1POWER=0 (初始化阶段)";
-                    
-                    // 延迟1秒后发送AT^CAT1POWER=1开启CAT1电源
-                    QTimer::singleShot(1000, this, [this]() {
-                        if (isProductionTestRunning && serial->isOpen()) {
-                            serial->write("AT^CAT1POWER=1\r\n");
-                            qDebug() << "发送CAT1电源开启指令: AT^CAT1POWER=1 (初始化阶段)";
-                            
-                            // 延迟1秒后发送AT^CAT1LOGLEVEL=4指令
+                // å¼\80æ\9cºå\88\9då§\8bå\8c\96å®\8cæ\88\90å\90\8eï¼\8cè¿\9bå\85¥äº§æµ\8bç\95\8cé\9d¢ï¼\88满足AT^ENTERTESTVIEWè¦\81æ±\82ï¼\89
+                if (serial->isOpen()) {
+                    ui->recvTextEdit->append(">>> 发送指令: AT^ENTERTESTVIEW (进入产测界面)");
+                    serial->write("AT^ENTERTESTVIEW\r\n");
+                    qDebug() << "发送AT^ENTERTESTVIEW (进入产测界面)";
+                }
+                
+                // 初始化完成后,切换到LCD测试阶段(如果启用)
+                QTimer::singleShot(1000, this, [this]() {
+                    if (isProductionTestRunning) {
+                        if (isTestItemEnabled("lcd")) {
+                            qDebug() << "AT^PMADDVOTE指令发送完成,开始LCD测试";
+                            ui->statusbar->showMessage("初始化完成,开始LCD测试", 2000);
+                            currentTestStep = TEST_LCD;
                             QTimer::singleShot(1000, this, [this]() {
-                                if (isProductionTestRunning && serial->isOpen()) {
-                                    serial->write("AT^CAT1LOGLEVEL=4\r\n");
-                                    qDebug() << "发送CAT1日志级别设置指令: AT^CAT1LOGLEVEL=4 (初始化阶段)";
-                                    
-                                    // 延迟1秒后切换到LCD测试阶段(如果启用)
-                                    QTimer::singleShot(1000, this, [this]() {
-                                        if (isProductionTestRunning) {
-                                            // 检查LCD测试是否启用
-                                            if (isTestItemEnabled("lcd")) {
-                                                qDebug() << "4G电源初始化完成,开始LCD测试";
-                                                ui->statusbar->showMessage("初始化完成,开始LCD测试", 2000);
-                                                
-                                                // 切换到LCD测试阶段
-                                                currentTestStep = TEST_LCD;
-                                                
-                                                // 延迟1秒后开始LCD测试
-                                                QTimer::singleShot(1000, this, [this]() {
-                                                    if (isProductionTestRunning && currentTestStep == TEST_LCD) {
-                                                        qDebug() << "开始LCD测试流程";
-                                                        performLcdTest();
-                                                    }
-                                                });
-                                            } else {
-                                                qDebug() << "4G电源初始化完成,LCD测试未启用,跳过";
-                                                ui->statusbar->showMessage("初始化完成,LCD测试未启用,跳过", 2000);
-                                                
-                                                // LCD未启用,跳过到下一个启用的测试
-                                                currentTestStep = TEST_LCD;
-                                                skipToNextEnabledTest();
-                                            }
-                                        }
-                                    });
+                                if (isProductionTestRunning && currentTestStep == TEST_LCD) {
+                                    qDebug() << "开始LCD测试流程";
+                                    performLcdTest();
                                 }
                             });
+                        } else {
+                            qDebug() << "AT^PMADDVOTE指令发送完成,LCD测试未启用,跳过";
+                            ui->statusbar->showMessage("初始化完成,LCD测试未启用,跳过", 2000);
+                            currentTestStep = TEST_LCD;
+                            skipToNextEnabledTest();
                         }
-                    });
-                } else {
-                    // 4G测试未启用,直接切换到LCD测试阶段
-                    QTimer::singleShot(1000, this, [this]() {
-                        if (isProductionTestRunning) {
-                            // 检查LCD测试是否启用
-                            if (isTestItemEnabled("lcd")) {
-                                qDebug() << "AT^PMADDVOTE指令发送完成,开始LCD测试";
-                                ui->statusbar->showMessage("初始化完成,开始LCD测试", 2000);
-                                
-                                // 切换到LCD测试阶段
-                                currentTestStep = TEST_LCD;
-                                
-                                // 延迟1秒后开始LCD测试
-                                QTimer::singleShot(1000, this, [this]() {
-                                    if (isProductionTestRunning && currentTestStep == TEST_LCD) {
-                                        qDebug() << "开始LCD测试流程";
-                                        performLcdTest();
-                                    }
-                                });
-                            } else {
-                                qDebug() << "AT^PMADDVOTE指令发送完成,LCD测试未启用,跳过";
-                                ui->statusbar->showMessage("初始化完成,LCD测试未启用,跳过", 2000);
-                                
-                                // LCD未启用,跳过到下一个启用的测试
-                                currentTestStep = TEST_LCD;
-                                skipToNextEnabledTest();
-                            }
-                        }
-                    });
-                }
+                    }
+                });
             }
         });
     }
@@ -2825,6 +2194,9 @@ void MainWindow::updateTestStatus(const QString& testName, TestStatus status)
 {
     QString iconText, statusText, iconColor, textColor;
     
+    // 用于时间记录的统一名称(charge/ntc不计入,由battery代表)
+    QString timeKey = (testName == "audio") ? "mic" : ((testName == "charge" || testName == "ntc") ? QString() : testName);
+    
     switch (status) {
         case STATUS_UNTESTED:
             iconText = "○";
@@ -2837,64 +2209,95 @@ void MainWindow::updateTestStatus(const QString& testName, TestStatus status)
             statusText = "测试中";
             iconColor = "#FFA500";  // 橙色
             textColor = "#FFA500";
+            if (!timeKey.isEmpty()) {
+                testStartTimeMap[timeKey] = QDateTime::currentDateTime();
+                testDurationSecMap.remove(timeKey);
+            }
             break;
         case STATUS_SUCCESS:
             iconText = "✓";
             statusText = "通过";
             iconColor = "#4CAF50";  // 绿色
             textColor = "#4CAF50";
+            if (!timeKey.isEmpty() && testStartTimeMap.contains(timeKey)) {
+                qint64 secs = testStartTimeMap[timeKey].secsTo(QDateTime::currentDateTime());
+                testDurationSecMap[timeKey] = secs;
+                testStartTimeMap.remove(timeKey);
+                int m = secs / 60, s = secs % 60;
+                statusText = QString("通过 (%1:%2)").arg(m).arg(s, 2, 10, QChar('0'));
+            }
             break;
         case STATUS_FAILED:
             iconText = "✗";
             statusText = "失败";
             iconColor = "#F44336";  // 红色
             textColor = "#F44336";
+            if (!timeKey.isEmpty() && testStartTimeMap.contains(timeKey)) {
+                qint64 secs = testStartTimeMap[timeKey].secsTo(QDateTime::currentDateTime());
+                testDurationSecMap[timeKey] = secs;
+                testStartTimeMap.remove(timeKey);
+                int m = secs / 60, s = secs % 60;
+                statusText = QString("失败 (%1:%2)").arg(m).arg(s, 2, 10, QChar('0'));
+            }
             break;
     }
     
-    QString iconStyle = QString("color: %1; font-size: 18px; font-weight: bold;").arg(iconColor);
-    QString textStyle = QString("color: %1;").arg(textColor);
-    
+    QString rowBg;
+    if (status == STATUS_SUCCESS) {
+        rowBg = "#dff5df";
+    } else if (status == STATUS_FAILED) {
+        rowBg = "#ffe0e0";
+    }
+
+    // 从第一个状态标签获取基准字体,确保状态切换时字体不变
+    QFont baseFont = ui->lcdStatusText->font();
+    int ptSize = baseFont.pointSize() > 0 ? baseFont.pointSize() : 10;
+    QString fontCss = QString("font-family: \"%1\"; font-size: %2pt; font-weight: %3;")
+        .arg(baseFont.family()).arg(ptSize).arg(baseFont.bold() ? 700 : 400);
+    QString iconFontCss = "font-size: 18px; font-weight: bold;";
+
+    const QString iconStyle = rowBg.isEmpty()
+        ? QString("color: %1; %2").arg(iconColor, iconFontCss)
+        : QString("color: %1; %2 background-color: %3;").arg(iconColor, iconFontCss, rowBg);
+    const QString textStyle = rowBg.isEmpty()
+        ? QString("color: %1; %2").arg(textColor, fontCss)
+        : QString("color: %1; %2 background-color: %3;").arg(textColor, fontCss, rowBg);
+    const QString labelStyle = rowBg.isEmpty() ? "" : QString("background-color: %1; %2").arg(rowBg, fontCss);
+
+    auto applyRow = [&](QLabel* nameLabel, QLabel* iconLabel, QLabel* statusLabel) {
+        if (nameLabel) nameLabel->setStyleSheet(labelStyle);
+        iconLabel->setText(iconText);
+        iconLabel->setStyleSheet(iconStyle);
+        statusLabel->setText(statusText);
+        statusLabel->setStyleSheet(textStyle);
+    };
+
     if (testName == "lcd") {
-        ui->lcdStatusIcon->setText(iconText);
-        ui->lcdStatusIcon->setStyleSheet(iconStyle);
-        ui->lcdStatusText->setText(statusText);
-        ui->lcdStatusText->setStyleSheet(textStyle);
+        applyRow(ui->lcdTestLabel, ui->lcdStatusIcon, ui->lcdStatusText);
+    } else if (testName == "tp") {
+        applyRow(ui->tpTestLabel, ui->tpStatusIcon, ui->tpStatusText);
+    } else if (testName == "bright") {
+        applyRow(ui->brightTestLabel, ui->brightStatusIcon, ui->brightStatusText);
     } else if (testName == "motor") {
-        ui->motorStatusIcon->setText(iconText);
-        ui->motorStatusIcon->setStyleSheet(iconStyle);
-        ui->motorStatusText->setText(statusText);
-        ui->motorStatusText->setStyleSheet(textStyle);
+        applyRow(ui->motorTestLabel, ui->motorStatusIcon, ui->motorStatusText);
     } else if (testName == "key") {
-        ui->keyStatusIcon->setText(iconText);
-        ui->keyStatusIcon->setStyleSheet(iconStyle);
-        ui->keyStatusText->setText(statusText);
-        ui->keyStatusText->setStyleSheet(textStyle);
-    } else if (testName == "audio") {
-        ui->audioStatusIcon->setText(iconText);
-        ui->audioStatusIcon->setStyleSheet(iconStyle);
-        ui->audioStatusText->setText(statusText);
-        ui->audioStatusText->setStyleSheet(textStyle);
+        applyRow(ui->keyTestLabel, ui->keyStatusIcon, ui->keyStatusText);
+    } else if (testName == "deepsleep") {
+        applyRow(ui->deepsleepTestLabel, ui->deepsleepStatusIcon, ui->deepsleepStatusText);
+    } else if (testName == "mic" || testName == "audio") {
+        applyRow(ui->audioTestLabel, ui->audioStatusIcon, ui->audioStatusText);
     } else if (testName == "battery") {
-        ui->batteryStatusIcon->setText(iconText);
-        ui->batteryStatusIcon->setStyleSheet(iconStyle);
-        ui->batteryStatusText->setText(statusText);
-        ui->batteryStatusText->setStyleSheet(textStyle);
+        applyRow(ui->batteryTestLabel, ui->batteryStatusIcon, ui->batteryStatusText);
     } else if (testName == "gsensor") {
-        ui->gsensorStatusIcon->setText(iconText);
-        ui->gsensorStatusIcon->setStyleSheet(iconStyle);
-        ui->gsensorStatusText->setText(statusText);
-        ui->gsensorStatusText->setStyleSheet(textStyle);
+        applyRow(ui->gsensorTestLabel, ui->gsensorStatusIcon, ui->gsensorStatusText);
     } else if (testName == "bluetooth") {
-        ui->bluetoothStatusIcon->setText(iconText);
-        ui->bluetoothStatusIcon->setStyleSheet(iconStyle);
-        ui->bluetoothStatusText->setText(statusText);
-        ui->bluetoothStatusText->setStyleSheet(textStyle);
-    } else if (testName == "ppp") {
-        ui->pppStatusIcon->setText(iconText);
-        ui->pppStatusIcon->setStyleSheet(iconStyle);
-        ui->pppStatusText->setText(statusText);
-        ui->pppStatusText->setStyleSheet(textStyle);
+        applyRow(ui->bluetoothTestLabel, ui->bluetoothStatusIcon, ui->bluetoothStatusText);
+    } else if (testName == "sechip") {
+        applyRow(ui->sechipTestLabel, ui->sechipStatusIcon, ui->sechipStatusText);
+    } else if (testName == "gps") {
+        applyRow(ui->gpsTestLabel, ui->gpsStatusIcon, ui->gpsStatusText);
+    } else if (testName == "leaklight") {
+        applyRow(ui->leaklightTestLabel, ui->leaklightStatusIcon, ui->leaklightStatusText);
     } else if (testName == "charge" || testName == "ntc") {
         // charge和ntc是电池测试的子项,它们的状态用于判定,但不单独显示在UI中
         // 它们的状态会影响battery的整体状态,但这里不更新UI
@@ -2911,65 +2314,81 @@ void MainWindow::updateOverallStatus()
     // 检查各项测试状态(只检查成功状态,并根据checkbox决定是否参与判定)
     bool allEnabledTestsPassed = true;
     
+    auto isPassed = [](const QString& t) { return t.startsWith("通过"); };
     // 检查启用的测试项
     if (isTestItemEnabled("lcd")) {
-        bool lcdSuccess = ui->lcdStatusText->text() == "通过";
-        if (!lcdSuccess) allEnabledTestsPassed = false;
+        if (!isPassed(ui->lcdStatusText->text())) allEnabledTestsPassed = false;
+    }
+    if (isTestItemEnabled("tp")) {
+        if (!isPassed(ui->tpStatusText->text())) allEnabledTestsPassed = false;
+    }
+    if (isTestItemEnabled("bright")) {
+        if (!isPassed(ui->brightStatusText->text())) allEnabledTestsPassed = false;
     }
     if (isTestItemEnabled("motor")) {
-        bool motorSuccess = ui->motorStatusText->text() == "通过";
-        if (!motorSuccess) allEnabledTestsPassed = false;
+        if (!isPassed(ui->motorStatusText->text())) allEnabledTestsPassed = false;
     }
     if (isTestItemEnabled("key")) {
-        bool keySuccess = ui->keyStatusText->text() == "通过";
-        if (!keySuccess) allEnabledTestsPassed = false;
+        if (!isPassed(ui->keyStatusText->text())) allEnabledTestsPassed = false;
     }
-    if (isTestItemEnabled("audio")) {
-        bool audioSuccess = ui->audioStatusText->text() == "通过";
-        if (!audioSuccess) allEnabledTestsPassed = false;
+    if (isTestItemEnabled("deepsleep")) {
+        if (!isPassed(ui->deepsleepStatusText->text())) allEnabledTestsPassed = false;
     }
-    // 电池测试:检查充电和NTC(如果启用)
-    // 注意:charge和ntc的状态通过updateTestStatus("charge", ...)和updateTestStatus("ntc", ...)更新
-    // 但由于UI中可能没有独立的charge和ntc状态显示,这里使用battery状态作为综合判定
-    // 如果charge或ntc任一启用,都需要检查battery状态
-    if (isTestItemEnabled("charge") || isTestItemEnabled("ntc")) {
-        bool batterySuccess = ui->batteryStatusText->text() == "通过";
-        if (!batterySuccess) allEnabledTestsPassed = false;
+    if (isTestItemEnabled("mic")) {
+        if (!isPassed(ui->audioStatusText->text())) allEnabledTestsPassed = false;
+    }
+    if (isTestItemEnabled("battery")) {
+        if (!isPassed(ui->batteryStatusText->text())) allEnabledTestsPassed = false;
     }
     if (isTestItemEnabled("gsensor")) {
-        bool gsensorSuccess = ui->gsensorStatusText->text() == "通过";
-        if (!gsensorSuccess) allEnabledTestsPassed = false;
+        if (!isPassed(ui->gsensorStatusText->text())) allEnabledTestsPassed = false;
     }
     if (isTestItemEnabled("bluetooth")) {
-        bool bluetoothSuccess = ui->bluetoothStatusText->text() == "通过";
-        if (!bluetoothSuccess) allEnabledTestsPassed = false;
+        if (!isPassed(ui->bluetoothStatusText->text())) allEnabledTestsPassed = false;
     }
-    if (isTestItemEnabled("ppp")) {
-        bool pppSuccess = ui->pppStatusText->text() == "通过";
-        if (!pppSuccess) allEnabledTestsPassed = false;
+    if (isTestItemEnabled("leaklight")) {
+        if (!isPassed(ui->leaklightStatusText->text())) allEnabledTestsPassed = false;
+    }
+    if (isTestItemEnabled("sechip")) {
+        if (!isPassed(ui->sechipStatusText->text())) allEnabledTestsPassed = false;
+    }
+    if (isTestItemEnabled("gps")) {
+        if (!isPassed(ui->gpsStatusText->text())) allEnabledTestsPassed = false;
+    }
+    // 计算各测试项总耗时
+    qint64 totalSecs = 0;
+    for (auto it = testDurationSecMap.constBegin(); it != testDurationSecMap.constEnd(); ++it) {
+        totalSecs += it.value();
+    }
+    QString totalTimeStr;
+    if (totalSecs > 0) {
+        int tm = totalSecs / 60, ts = totalSecs % 60;
+        totalTimeStr = QString("%1分%2秒").arg(tm).arg(ts);
     }
-    
     QString overallIcon, overallText, overallColor;
     
     if (allEnabledTestsPassed) {
-        // 所有启用的测试都通过
         overallIcon = "✓";
-        overallText = "通过";
+        overallText = totalTimeStr.isEmpty() ? "通过" : QString("通过 · 总耗时 %1").arg(totalTimeStr);
         overallColor = "#4CAF50";
     } else {
-        // 任何其他情况(失败、测试中、等待开始等)都显示为未通过
         overallIcon = "✗";
-        overallText = "未通过";
+        overallText = totalTimeStr.isEmpty() ? "未通过" : QString("未通过 · 总耗时 %1").arg(totalTimeStr);
         overallColor = "#F44336";
     }
     
     QString overallStyle = QString("color: %1; font-size: 18px; font-weight: bold;").arg(overallColor);
     QString textStyle = QString("color: %1;").arg(overallColor);
     
+    // 整体状态同样保持字体不变
+    int overallPt = ui->overallStatusText->font().pointSize();
+    if (overallPt <= 0) overallPt = 10;
+    QString overallFontCss = QString("font-family: \"%1\"; font-size: %2pt;")
+        .arg(ui->overallStatusText->font().family()).arg(overallPt);
     ui->overallStatusIcon->setText(overallIcon);
     ui->overallStatusIcon->setStyleSheet(overallStyle);
     ui->overallStatusText->setText(overallText);
-    ui->overallStatusText->setStyleSheet(textStyle);
+    ui->overallStatusText->setStyleSheet(textStyle + overallFontCss);
 }
 
 // 保存测试记录到CSV文件(可用Excel打开)
@@ -2978,189 +2397,85 @@ QString MainWindow::saveTestRecord()
     QDateTime endTime = QDateTime::currentDateTime();
     QString startTimeStr = testStartTime.toString("yyyy-MM-dd hh:mm:ss");
     QString endTimeStr = endTime.toString("yyyy-MM-dd hh:mm:ss");
-    
+
     // 计算测试总耗时
     qint64 totalSeconds = testStartTime.secsTo(endTime);
     int minutes = totalSeconds / 60;
     int seconds = totalSeconds % 60;
     QString durationStr = QString("%1分%2秒").arg(minutes).arg(seconds);
-    
-    QString date = endTime.toString("yyyy-MM-dd");
-    
+
     // 获取当前测试数据
     QString ver = ui->VerlineEdit->text().trimmed();
-    QString imei = ui->IMEIlineEdit->text().trimmed();
-    QString iccid = ui->ICCIDlineEdit->text().trimmed();
-    QString modem = ui->ModemVerlineEdit->text().trimmed();
-    QString pyver = ui->PYVerlineEdit->text().trimmed();
-    
-    // 检查ICCID是否有效,如果无效则不写入记录
-    if (iccid.isEmpty() || iccid == "未获取") {
-        qDebug() << "ICCID为空或未获取,跳过保存测试记录";
-        ui->statusbar->showMessage("ICCID未获取,测试记录未保存", 3000);
-        return QString(); // 返回空字符串表示未保存
-    }
-    
-    // 检查ICCID格式是否正确
-    if (!isValidICCID(iccid)) {
-        qDebug() << "ICCID格式不正确,跳过保存测试记录,ICCID:" << iccid;
-        ui->statusbar->showMessage("ICCID格式不正确,测试记录未保存: " + iccid, 3000);
-        return QString(); // 返回空字符串表示未保存
-    }
-    
-    // 检查IMEI格式是否正确(如果IMEI不为空)
-    if (!imei.isEmpty() && !isValidIMEI(imei)) {
-        qDebug() << "IMEI格式不正确,跳过保存测试记录,IMEI:" << imei;
-        ui->statusbar->showMessage("IMEI格式不正确,测试记录未保存: " + imei, 3000);
-        return QString(); // 返回空字符串表示未保存
-    }
-    
-    // 调试信息:输出获取到的值
-    qDebug() << "保存时获取的数据:";
-    qDebug() << "  Ver:" << (ver.isEmpty() ? "空" : ver);
-    qDebug() << "  IMEI:" << (imei.isEmpty() ? "空" : imei);
-    qDebug() << "  ICCID:" << iccid;
-    qDebug() << "  Modem:" << (modem.isEmpty() ? "空" : modem);
-    qDebug() << "  PYVER:" << (pyver.isEmpty() ? "空" : pyver);
-    
+
     // 获取各项测试结果
     QString lcdResult = ui->lcdStatusText->text();
+    QString tpResult = ui->tpStatusText->text();
+    QString brightResult = ui->brightStatusText->text();
     QString motorResult = ui->motorStatusText->text();
     QString keyResult = ui->keyStatusText->text();
+    QString deepsleepResult = ui->deepsleepStatusText->text();
     QString audioResult = ui->audioStatusText->text();
     QString batteryResult = ui->batteryStatusText->text();
     QString gsensorResult = ui->gsensorStatusText->text();
+    QString leaklightResult = ui->leaklightStatusText->text();
     QString bluetoothResult = ui->bluetoothStatusText->text();
-    QString pppResult = ui->pppStatusText->text();
+    QString sechipResult = ui->sechipStatusText->text();
+    QString gpsResult = ui->gpsStatusText->text();
     QString overallResult = ui->overallStatusText->text();
-    
-    // 创建保存目录(在源码同一路径下)
+
+    // 创建保存目录
     QString appPath = QCoreApplication::applicationDirPath();
     QString saveDir = appPath + "/产测记录";
     QDir dir;
     if (!dir.exists(saveDir)) {
         dir.mkpath(saveDir);
     }
-    
-    // CSV文件路径(统一命名)
+
     QString csvFileName = "产测记录.csv";
     QString csvFilePath = saveDir + "/" + csvFileName;
-    
-    // 检查是否是新文件(需要写入表头)
+
     bool isNewFile = !QFile::exists(csvFilePath);
-    
-    // 构建新的测试数据行
-    QString newDataLine = QString("%1,%2,%3,%4,%5,%6,%7,%8,%9,%10,%11,%12,%13,%14,%15,%16\n")
-              .arg(startTimeStr)
-              .arg(endTimeStr)
-              .arg(durationStr)
-              .arg(ver.isEmpty() ? "未获取" : ver)
-              .arg(imei.isEmpty() ? "未获取" : imei)
-              .arg(iccid.isEmpty() ? "未获取" : iccid)
-              .arg(modem.isEmpty() ? "未获取" : modem)
-              .arg(pyver.isEmpty() ? "未获取" : pyver)
-              .arg(lcdResult)
-              .arg(motorResult)
-              .arg(keyResult)
-              .arg(audioResult)
-              .arg(batteryResult)
-              .arg(gsensorResult)
-              .arg(bluetoothResult)
-              .arg(pppResult)
-              .arg(overallResult);
-    
-    QString finalContent;
-    
-    if (isNewFile) {
-        // 新文件,写入表头和数据
-        finalContent += "开始时间,结束时间,测试耗时,软件版本,IMEI,ICCID,Modem版本,PYVER版本,LCD测试,马达测试,按键测试,录音测试,电池测试,G-sensor测试,蓝牙测试,4G测试,整体结果\n";
-        finalContent += newDataLine;
-        qDebug() << "创建新的CSV文件:" << csvFilePath;
-            } else {
-        // 现有文件,需要检查ICCID是否重复
-        QFile existingFile(csvFilePath);
-        if (!existingFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
-            QMessageBox::warning(this, "读取失败", 
-                QString("无法读取现有测试记录文件:\n%1").arg(csvFilePath));
-            return QString();
-        }
-        
-        // 读取所有现有数据
-        QStringList existingLines;
-        QTextStream readStream(&existingFile);
-        while (!readStream.atEnd()) {
-            existingLines << readStream.readLine();
-        }
-        existingFile.close();
-        
-        // 构建最终内容
-        if (!existingLines.isEmpty()) {
-            finalContent = existingLines[0] + "\n"; // 表头
-            
-            bool iccidFound = false;
-            
-            // 检查每一行数据,查找相同ICCID
-            for (int i = 1; i < existingLines.size(); i++) {
-                QString line = existingLines[i];
-                if (line.trimmed().isEmpty()) continue;
-                
-                // 分割CSV行,获取ICCID字段(第6列,索引为5)
-                QStringList fields = line.split(",");
-                if (fields.size() >= 6) {
-                    QString existingIccid = fields[5].trimmed();
-                    
-                    if (existingIccid == iccid) {
-                        // 找到相同ICCID,替换为新记录
-                        finalContent += newDataLine;
-                        iccidFound = true;
-                        qDebug() << "找到相同ICCID,替换记录:" << iccid;
-        } else {
-                        // 保留不同ICCID的记录
-                        finalContent += line + "\n";
-                    }
-                } else {
-                    // 格式不正确的行,保留
-                    finalContent += line + "\n";
-                }
-            }
-            
-            // 如果没有找到相同ICCID,追加新记录
-            if (!iccidFound) {
-                finalContent += newDataLine;
-                qDebug() << "未找到相同ICCID,追加新记录:" << iccid;
-            }
-        } else {
-            // 文件为空,重建
-            finalContent = "开始时间,结束时间,测试耗时,软件版本,IMEI,ICCID,Modem版本,PYVER版本,LCD测试,马达测试,按键测试,录音测试,电池测试,G-sensor测试,蓝牙测试,4G测试,整体结果\n";
-            finalContent += newDataLine;
-        }
-    }
-    
-    // 写入最终内容到文件
+
+    const QString header =
+        "开始时间,结束时间,测试耗时,软件版本,LCD测试,触控测试,亮度测试,马达测试,按键测试,功耗测试,录音测试,电池测试,G-sensor测试,漏光测试,蓝牙测试,SE芯片测试,GPS测试,整体结果\n";
+
+    QString newDataLine = QString("%1,%2,%3,%4,%5,%6,%7,%8,%9,%10,%11,%12,%13,%14,%15,%16,%17,%18\n")
+                              .arg(startTimeStr)
+                              .arg(endTimeStr)
+                              .arg(durationStr)
+                              .arg(ver.isEmpty() ? "未获取" : ver)
+                              .arg(lcdResult)
+                              .arg(tpResult)
+                              .arg(brightResult)
+                              .arg(motorResult)
+                              .arg(keyResult)
+                              .arg(deepsleepResult)
+                              .arg(audioResult)
+                              .arg(batteryResult)
+                              .arg(gsensorResult)
+                              .arg(leaklightResult)
+                              .arg(bluetoothResult)
+                              .arg(sechipResult)
+                              .arg(gpsResult)
+                              .arg(overallResult);
+
     QFile csvFile(csvFilePath);
-    if (!csvFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
-        QMessageBox::warning(this, "保存失败", 
-            QString("无法创建测试记录文件:\n%1\n错误:%2")
-            .arg(csvFilePath)
-            .arg(csvFile.errorString()));
-        return QString(); // 返回空字符串表示保存失败
+    if (!csvFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
+        QMessageBox::warning(this, "保存失败",
+                             QString("无法写入测试记录文件:\n%1\n错误:%2")
+                                 .arg(csvFilePath)
+                                 .arg(csvFile.errorString()));
+        return QString();
     }
-    
-    // 使用系统本地编码写入数据(Windows下通常是GBK)
-    QByteArray localData = finalContent.toLocal8Bit();
-    csvFile.write(localData);
-    qDebug() << "使用本地编码写入数据,大小:" << localData.size() << "字节";
-    
+
+    if (isNewFile) {
+        csvFile.write(header.toLocal8Bit());
+    }
+    csvFile.write(newDataLine.toLocal8Bit());
     csvFile.close();
-    
-    qDebug() << "测试记录已保存到:" << csvFilePath;
-    qDebug() << "记录内容: 开始时间=" << startTimeStr << ", 结束时间=" << endTimeStr << ", 耗时=" << durationStr
-             << ", Ver=" << ver << ", IMEI=" << imei << ", ICCID=" << iccid << ", Modem=" << modem
-             << ", PYVER=" << pyver << ", LCD=" << lcdResult << ", 马达=" << motorResult 
-             << ", 按键=" << keyResult << ", PPP=" << pppResult 
-             << ", 整体=" << overallResult;
-    
-    // 显示保存成功消息
+
+    qDebug() << "测试记录已追加保存到:" << csvFilePath;
+
     ui->statusbar->showMessage(QString("测试记录已保存: %1").arg(csvFileName), 3000);
     
     // 关闭测试日志文件(测试完成)
@@ -3192,146 +2507,6 @@ QString MainWindow::saveTestRecord()
 }
 
 
-// 检查signal和sim值进行4G测试判断
-void MainWindow::checkSignalAndSimValues(const QString& data)
-{
-    qDebug() << "开始检查signal和sim值,数据:" << data.trimmed();
-    
-    // 标记已收到signal和sim数据
-    hasReceivedSignalSim = true;
-    
-    // 停止超时检测,因为已经收到数据
-    if (test4GTimer->isActive()) {
-        test4GTimer->stop();
-        qDebug() << "收到signal和sim数据,停止4G测试超时检测";
-    }
-    
-    // 解析sim值:@sim:1@
-    QRegExp simRegex("@sim:(\\d+)@");
-    int simValue = -1;
-    if (simRegex.indexIn(data) != -1) {
-        simValue = simRegex.cap(1).toInt();
-        qDebug() << "解析到sim值:" << simValue;
-        
-        // 保存SIM卡状态
-        currentSimStatus = simValue;
-        qDebug() << "更新SIM卡状态:" << (currentSimStatus == 1 ? "已插入" : "未插入");
-    } else {
-        qDebug() << "未找到sim值";
-        return;
-    }
-    
-    // 解析signal值:@signal:31@
-    QRegExp signalRegex("@signal:(\\d+)@");
-    int signalValue = -1;
-    if (signalRegex.indexIn(data) != -1) {
-        signalValue = signalRegex.cap(1).toInt();
-        qDebug() << "解析到signal值:" << signalValue;
-        
-        // 将signal值显示到界面
-        appendToLogFile(QString("Signal值: %1\n").arg(signalValue));
-        ui->statusbar->showMessage(QString("检测到Signal值: %1").arg(signalValue), 2000);
-            } else {
-        qDebug() << "未找到signal值";
-        return;
-    }
-    
-    // 移除signal=99的立即失败逻辑,改为在CAT1测试中统计处理
-    
-    // 如果是第一次收到signal值,启动CAT1信号测试
-    if (!hasReceivedFirstSignal) {
-        hasReceivedFirstSignal = true;
-        qDebug() << "收到第一次signal值:" << signalValue << ",启动CAT1信号测试";
-        
-        // 停止signal4GTimer,避免与CAT1测试冲突导致重复弹窗
-        if (signal4GTimer->isActive()) {
-            signal4GTimer->stop();
-            qDebug() << "停止signal4GTimer,改用CAT1信号测试";
-        }
-        
-        // 初始化CAT1信号测试计数器
-        cat1SignalTestCount = 0;
-        cat1SignalReceivedCount = 0;
-        cat1SignalGoodCount = 0;
-        cat1Signal99Count = 0;
-        cat1SignalTotalValue = 0;
-        // 移除了cat1ProcessedSignals的清理,改用时间戳方式
-        
-        // 启动CAT1信号测试定时器,每2秒发送一次
-        cat1SignalTimer->start(2000);
-        ui->statusbar->showMessage("开始CAT1信号测试,每2秒检测一次...", 3000);
-        
-        // 立即发送第一次CAT1信号检测指令
-        serial->write("AT^CAT1GETSIGNAL\r\n");
-        cat1SignalTestCount++;
-        qDebug() << "发送第" << cat1SignalTestCount << "次CAT1信号检测指令: AT^CAT1GETSIGNAL";
-        return;
-    }
-    
-    // 如果已经启动了CAT1信号测试,不再执行旧的判断逻辑
-    if (hasReceivedFirstSignal) {
-        qDebug() << "已启动CAT1信号测试,跳过旧的signal判断逻辑";
-        return;
-    }
-    
-    // 判断4G测试是否通过(这段代码理论上不会执行到)
-    bool simOk = (simValue == 1);
-    bool signalOk = (signalValue > 25);
-    bool testPassed = simOk && signalOk;
-    
-    qDebug() << "4G测试判断结果:";
-    qDebug() << "  sim值:" << simValue << "(期望: 1)" << "检查结果:" << (simOk ? "通过" : "失败");
-    qDebug() << "  signal值:" << signalValue << "(期望: >25)" << "检查结果:" << (signalOk ? "通过" : "失败");
-    qDebug() << "  最终测试结果:" << (testPassed ? "通过" : "失败");
-    qDebug() << "  警告:此代码不应该执行到,因为已启动CAT1测试";
-    
-    if (testPassed) {
-        // 测试通过
-        ui->statusbar->showMessage(QString("4G测试通过 - SIM: %1, Signal: %2").arg(simValue).arg(signalValue), 3000);
-        updateTestStatus("ppp", STATUS_SUCCESS);
-        
-        // 完成产测
-        if (isProductionTestRunning) {
-            currentTestStep = TEST_COMPLETED;
-            isProductionTestRunning = false;
-            qDebug() << "4G测试通过,产测流程完成";
-            
-            // 保存测试记录
-            QString fileName = saveTestRecord();
-            
-            // 显示最终测试结果弹窗
-            showFinalTestResult();
-            }
-        } else {
-        // 测试失败
-        QString failReason;
-        if (simValue != 1) {
-            failReason += QString("SIM卡状态异常(值:%1,期望:1) ").arg(simValue);
-        }
-        if (signalValue == 99) {
-            failReason += QString("无效信号值(值:%1)").arg(signalValue);
-        } else if (signalValue <= 25) {
-            failReason += QString("信号强度不足(值:%1,期望:>25)").arg(signalValue);
-        }
-        
-        ui->statusbar->showMessage(QString("4G测试失败 - %1").arg(failReason), 3000);
-        updateTestStatus("ppp", STATUS_FAILED);
-        
-        // 完成产测(失败)
-        if (isProductionTestRunning) {
-            currentTestStep = TEST_COMPLETED;
-            isProductionTestRunning = false;
-            qDebug() << "4G测试失败,产测流程完成";
-            
-            // 保存测试记录
-            QString fileName = saveTestRecord();
-            
-            // 显示最终测试结果弹窗
-            showFinalTestResult();
-        }
-    }
-}
-
 // 解析wstp_print格式的BTMAC和CyweeChipInfo数据
 void MainWindow::parseWstpPrintData(const QString& data)
 {
@@ -3388,37 +2563,6 @@ void MainWindow::parseWstpPrintData(const QString& data)
     }
 }
 
-// 发送PPP指令
-void MainWindow::sendPppCommand()
-{
-    if (!isProductionTestRunning || currentTestStep != TEST_PPP_INIT) {
-        qDebug() << "PPP数据等待跳过 - 测试状态不正确";
-        return;
-    }
-    
-    pppCommandCount++;
-    qDebug() << "等待PPP数据,第" << pppCommandCount << "次";
-    
-    if (pppCommandCount <= 3) {
-        // 前3次等待PPP相关数据
-        ui->statusbar->showMessage(QString("4G测试中 - 等待PPP数据(%1/3)").arg(pppCommandCount), 3000);
-        
-        // 不再发送PPPENVINIT指令,直接等待设备自动发送PPPINFO
-        
-        // 启动5秒超时定时器,等待PPPINFO回复
-        test4GTimer->start(5000);
-        qDebug() << "启动5秒超时定时器,等待PPPINFO回复";
-                } else {
-        // 超过3次后,改为等待signal和sim数据
-        qDebug() << "PPP数据已等待3次,改为等待signal和sim数据";
-        ui->statusbar->showMessage("4G测试中 - 等待signal和sim数据...", 5000);
-        
-        // 启动5秒超时检测,等待signal和sim数据
-        test4GTimer->start(5000);
-        qDebug() << "启动5秒超时检测,等待signal和sim数据";
-    }
-}
-
 // 打开记录文件夹
 void MainWindow::on_openRecordsFolderButton_clicked()
 {
@@ -3457,12 +2601,9 @@ void MainWindow::on_openButton_2_clicked()
         // 如果正在测试,则停止测试
         lcdTestTimer->stop();
         motorTestTimer->stop();
-        test4GTimer->stop();
-        signal4GTimer->stop();
         testTimeoutTimer->stop();
-        cat1SignalTimer->stop();
+        gSensorIntervalTimer->stop();
         bluetoothTestTimer->stop();
-        pingTestTimer->stop();
         isProductionTestRunning = false;
         currentTestStep = TEST_IDLE;
         
@@ -3487,30 +2628,13 @@ void MainWindow::on_openButton_2_clicked()
     // 开始产测流程
     isProductionTestRunning = true;
     testStartTime = QDateTime::currentDateTime(); // 记录测试开始时间
+    testStartTimeMap.clear();
+    testDurationSecMap.clear();
     currentTestStep = TEST_RESET;
     currentLcdColor = 1; // 从红色开始
-    initRetry4GCount = 0;
-    signalTest4GCount = 0;
-    signalReceived4GCount = 0;
-    signalGood4GCount = 0;
-    pppCommandCount = 0;
-    hasReceivedSignalSim = false;
-    hasReceivedPppInfo = false;
-    
-    // 在开始新测试前,检查是否需要保存当前UI日志
-    QString currentLogContent = ui->recvTextEdit->toPlainText();
-    QString currentImei = ui->IMEIlineEdit->text().trimmed();
-    QString currentIccid = ui->ICCIDlineEdit->text().trimmed();
-    
-    // 如果UI有日志内容且IMEI/ICCID已知,则保存日志
-    if (!currentLogContent.trimmed().isEmpty() && 
-        !currentImei.isEmpty() && currentImei != "未获取" &&
-        !currentIccid.isEmpty() && currentIccid != "未获取") {
-        qDebug() << "开始新测试前,保存当前UI日志";
-        saveRecvTextEditToLog();
-        } else {
-        qDebug() << "UI日志为空或IMEI/ICCID未知,放弃保存,直接清空UI";
-    }
+    
+    // 在开始新测试前,尽量保存当前UI日志(如果有内容)
+    saveRecvTextEditToLog();
     
     // 创建测试日志文件
     createTestLogFile();
@@ -3527,34 +2651,36 @@ void MainWindow::on_openButton_2_clicked()
     
     // 清空设备信息
     ui->VerlineEdit->clear();            // 清空版本信息
-    ui->IMEIlineEdit->clear();           // 清空IMEI
-    ui->ICCIDlineEdit->clear();          // 清空ICCID
-    ui->ModemVerlineEdit->clear();       // 清空调制解调器版本
-    ui->PYVerlineEdit->clear();       // 清空python版本
     
     // 重置弹窗状态
     lcdTestPopupShown = false;
     motorTestPopupShown = false;
+    touchTestPopupShown = false;
+    brightTestPopupShown = false;
+    deepsleepTestPopupShown = false;
     isWaitingForUserConfirm = false;
     keyTestStarted = false;
-    audioTestPopupShown = false;
-    audioPlaybackPopupShown = false;
     
     // 重置状态指示器
     updateTestStatus("lcd", STATUS_UNTESTED);
+    updateTestStatus("tp", STATUS_UNTESTED);
+    updateTestStatus("bright", STATUS_UNTESTED);
     updateTestStatus("motor", STATUS_UNTESTED);
     updateTestStatus("key", STATUS_UNTESTED);
-    updateTestStatus("audio", STATUS_UNTESTED);
+    updateTestStatus("deepsleep", STATUS_UNTESTED);
+    updateTestStatus("mic", STATUS_UNTESTED);
     updateTestStatus("battery", STATUS_UNTESTED);
     updateTestStatus("gsensor", STATUS_UNTESTED);
+    updateTestStatus("leaklight", STATUS_UNTESTED);
     updateTestStatus("bluetooth", STATUS_UNTESTED);
-    updateTestStatus("ppp", STATUS_UNTESTED);
+    updateTestStatus("sechip", STATUS_UNTESTED);
+    updateTestStatus("gps", STATUS_UNTESTED);
     
     qDebug() << "已清空所有UI内容,准备开始新的产测";
     
     ui->statusbar->showMessage("开始产测流程...", 2000);
     qDebug() << "=== 开始产测流程 ===";
-    qDebug() << "流程: 产测开启(AT^FACTORY=1) → 设备重启 → 等待BuildVer → LCD测试(红绿蓝各1s) → 马达测试(开启2s) → 按键测试 → 录音测试(录音3s+播放) → 电池测试(电压检测) → G-sensor测试 → 4G测试(初始化+信号检测) → 退出产测(AT^FACTORY=0)";
+    qDebug() << "流程: 产测开启(AT^FACTORY=1) → 设备重启 → … → 全部完成后弹窗确认 → AT^POWEROFF(关机)";
     qDebug() << "LCD指令映射: 进入=AT^LCDTEST=1, 红色=AT^LCDTEST=6, 绿色=AT^LCDTEST=3, 蓝色=AT^LCDTEST=4, 退出=AT^LCDTEST=2";
     
     // 发送产测开启指令,等待工厂模式确认
@@ -3577,571 +2703,193 @@ void MainWindow::on_openButton_2_clicked()
     testTimeoutTimer->start(10000);
 }
 
-// 执行录音测试
+// 执行录音测试(配置了等级标准时共测 3 轮,任一轮「实际等级 >= 设置等级」则整体通过)
 void MainWindow::performAudioTest()
 {
-    qDebug() << "*** performAudioTest 被调用 ***";
-    qDebug() << "当前状态检查 - isProductionTestRunning:" << isProductionTestRunning;
-    qDebug() << "当前状态检查 - currentTestStep:" << currentTestStep;
-    qDebug() << "当前状态检查 - TEST_AUDIO:" << TEST_AUDIO;
-    
-    // 检查录音测试是否启用
-    if (!isTestItemEnabled("audio")) {
+    if (!isTestItemEnabled("mic")) {
         qDebug() << "录音测试未启用,跳过";
-        updateTestStatus("audio", STATUS_UNTESTED);
+        updateTestStatus("mic", STATUS_UNTESTED);
         skipToNextEnabledTest();
         return;
     }
     
     if (!isProductionTestRunning || currentTestStep != TEST_AUDIO) {
-        qDebug() << "*** performAudioTest 条件不满足,退出 ***";
+        qDebug() << "录音测试条件不满足,退出";
         return;
     }
-
-    qDebug() << "开始录音测试(自动模式:AT^AUDIOTEST)";
-    ui->recvTextEdit->append("========== 开始录音测试 ==========");
     
-    // 录音测试前,无论LCD状态如何,都强制关闭LCD
-    // 因为按键测试时按下按键可能会自动开启LCD,但状态可能没有及时更新
-    // 为了确保LCD关闭,无论状态如何都发送关闭命令
-    qDebug() << "录音测试前强制关闭LCD(无论状态如何)";
-    qDebug() << "当前LCD状态 - LCD测试启用:" << isTestItemEnabled("lcd") << "LCD背光状态:" << lcdBacklightOn;
-    serial->write("AT^LCDOFF\r\n");
-    qDebug() << "发送指令: AT^LCDOFF (关闭LCD)";
-    ui->recvTextEdit->append(">>> 发送指令: AT^LCDOFF (关闭LCD,录音测试前强制关闭)");
-    
-    // 更新LCD状态为关闭(因为发送了关闭命令)
-    lcdBacklightOn = false;
+    qDebug() << "开始录音测试";
+    appendToLogFile("\n========== 开始录音测试 ==========\n");
+    updateTestStatus("mic", STATUS_TESTING);
+    audioTestRoundIndex = 0;
+    audioTestAnyPass = false;
+    audioTestAwaitingMicLevel = false;
+
+    QString standard = ui->audioLevelStandardLineEdit->text().trimmed();
+    if (standard.isEmpty()) {
+        appendToLogFile(">>> 未配置等级标准:单轮采样,不做等级校验\n");
+        ui->statusbar->showMessage("录音测试中(未配标准,单轮),请对着麦克风说话...", 5000);
+    } else {
+        appendToLogFile(QString(">>> 已配置等级标准「%1」:共 3 轮,任一轮达标即通过\n").arg(standard));
+        ui->statusbar->showMessage("录音测试 1/3:请对着麦克风说话...", 5000);
+    }
     
-    // LCD关闭后延迟1秒再进行下一步
-    QTimer::singleShot(1000, this, [this]() {
-        if (isProductionTestRunning && currentTestStep == TEST_AUDIO) {
-            performAudioTestContinue();
-        }
-    });
+    // 录音前关闭LCD(与V1.7一致)
+    if (isTestItemEnabled("lcd") && lcdBacklightOn) {
+        serial->write("AT^LCDOFF\r\n");
+        appendToLogFile(">>> 发送指令: AT^LCDOFF (录音测试前关闭LCD)\n");
+        qDebug() << "录音测试前发送AT^LCDOFF";
+    }
+
+    beginAudioTestAttempt(0);
 }
 
-// 继续录音测试(LCD关闭后或无需关闭LCD时调用)
-void MainWindow::performAudioTestContinue()
+void MainWindow::beginAudioTestAttempt(int roundIndex)
 {
-    if (!isProductionTestRunning || currentTestStep != TEST_AUDIO) {
+    if (!isProductionTestRunning || currentTestStep != TEST_AUDIO || !serial || !serial->isOpen()) {
+        qDebug() << "录音测试:产测已停止或串口未打开";
         return;
     }
-    
-    updateTestStatus("audio", STATUS_TESTING);
-    ui->statusbar->showMessage("录音测试中:正在发送AUDIOTEST指令...", 3000);
 
-    // 重置录音测试相关状态
-    audioMicMiddleConsecutiveCount = 0;
-    audioMicTotalPassCount = 0;
-    audioTestFinished = false;
-    
-    // 获取音频测试通过标准(从UI中读取)
-    audioLevelStandard = "中"; // 默认值
-    if (ui->recordVolumelineEdit) {
-        QString levelText = ui->recordVolumelineEdit->text().trimmed();
-        if (!levelText.isEmpty()) {
-            audioLevelStandard = levelText;
-        }
+    audioTestRoundIndex = roundIndex;
+    audioTestAwaitingMicLevel = true;
+
+    QString standard = ui->audioLevelStandardLineEdit->text().trimmed();
+    if (!standard.isEmpty()) {
+        ui->statusbar->showMessage(QString("录音测试 %1/3:请对着麦克风说话...").arg(roundIndex + 1), 5000);
     }
-    qDebug() << "音频测试通过标准设置为:" << audioLevelStandard;
 
-    // 发送录音测试指令:先 AT^AUDIOTEST=5 再 AT^AUDIOTEST=7 <音量>
-    QTimer::singleShot(0, this, [this]() {
-        if (!isProductionTestRunning || currentTestStep != TEST_AUDIO) {
-            qDebug() << "录音测试已结束或步骤变化,取消发送AUDIOTEST指令";
+    QString volumeStr = ui->audioVolumeLineEdit->text().trimmed();
+    if (volumeStr.isEmpty()) volumeStr = "2.0";
+
+    QTimer::singleShot(200, this, [this, volumeStr]() {
+        if (!isProductionTestRunning || currentTestStep != TEST_AUDIO || !serial || !serial->isOpen()) {
+            qDebug() << "录音测试:产测已停止或串口未打开";
             return;
         }
-        
-        QString audioCmd1 = "AT^AUDIOTEST=5\r\n";
-        serial->write(audioCmd1.toUtf8());
-        qDebug() << "发送录音测试指令1:" << audioCmd1.trimmed();
-        ui->recvTextEdit->append(">>> 发送指令: AT^AUDIOTEST=5");
-        
-        // 稍微延迟后发送第二条指令(带音量参数)
-        QTimer::singleShot(200, this, [this]() {
-        if (!isProductionTestRunning || currentTestStep != TEST_AUDIO) {
-            qDebug() << "录音测试已结束或步骤变化,取消发送第二条AUDIOTEST指令";
+        serial->write("AT^AUDIOTEST=5\r\n");
+        appendToLogFile(">>> 发送指令: AT^AUDIOTEST=5\n");
+        qDebug() << "发送AT^AUDIOTEST=5";
+    });
+
+    QTimer::singleShot(500, this, [this, volumeStr]() {
+        if (!isProductionTestRunning || currentTestStep != TEST_AUDIO || !serial || !serial->isOpen())
             return;
-        }
+        QString cmd = QString("AT^AUDIOTEST=7 %1\r\n").arg(volumeStr);
+        serial->write(cmd.toUtf8());
+        appendToLogFile(QString(">>> 发送指令: %1\n").arg(cmd.trimmed()));
+        qDebug() << "发送" << cmd.trimmed();
+        testTimeoutTimer->start(15000);  // 15秒超时
+    });
+}
 
-        // 从界面读取音量文本框的值
-        QString volumeStr;
-        if (ui->audioVolumeLineEdit) {
-            volumeStr = ui->audioVolumeLineEdit->text().trimmed();
-        }
+void MainWindow::checkAudioTestResult(const QString& data)
+{
+    if (!isProductionTestRunning || currentTestStep != TEST_AUDIO)
+        return;
+    if (!audioTestAwaitingMicLevel)
+        return;
+    
+    // 解析 mic level: 静音/极低/低/中/高
+    QRegExp micPattern("mic\\s*level\\s*[::]\\s*(\\S+)", Qt::CaseInsensitive);
+    if (micPattern.indexIn(data) == -1)
+        return;
+    
+    QString micLevel = micPattern.cap(1).trimmed();
+    audioTestAwaitingMicLevel = false;
+    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));
+    
+    // 结束本轮采集
+    if (serial && serial->isOpen()) {
+        serial->write("AT^AUDIOTEST=6\r\n");
+        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;
+    }
 
-        float volume = 2; // 默认音量为2
-        bool ok = false;
-        if (!volumeStr.isEmpty()) {
-            float v = volumeStr.toFloat(&ok);
-            if (ok && v >= 0) {
-                volume = v;
-        } else {
-                qDebug() << "音量文本框内容非法,使用默认音量 2,原始值:" << volumeStr;
-            }
-        } else {
-            qDebug() << "音量文本框为空,使用默认音量 2";
-        }
+    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));
+    }
+
+    const int completedRound = audioTestRoundIndex;
+    const int nextRound = completedRound + 1;
 
-        QString audioCmd2 = QString("AT^AUDIOTEST=7 %1\r\n").arg(volume);
-        serial->write(audioCmd2.toUtf8());
-        qDebug() << "发送录音测试指令2:" << audioCmd2.trimmed();
-        ui->recvTextEdit->append(QString(">>> 发送指令: AT^AUDIOTEST=7 %1").arg(volume));
+    if (nextRound < 3) {
+        QTimer::singleShot(400, this, [this, nextRound]() {
+            if (!isProductionTestRunning || currentTestStep != TEST_AUDIO) {
+                return;
+            }
+            beginAudioTestAttempt(nextRound);
         });
-    });
+        return;
+    }
 
-    // 启动 8 秒定时器,在此期间统计"fac:mic level:中"
-    if (audioRecordTimer->isActive()) {
-        audioRecordTimer->stop();
+    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);
     }
-    audioRecordTimer->start(8000);
+    skipToNextEnabledTest();
 }
 
-// æ\98¾ç¤ºå½\95é\9f³æµ\8bè¯\95å¼¹çª\97
-void MainWindow::showAudioTestDialog()
+// æ\89§è¡\8cç\94µæ± æµ\8bè¯\95
+void MainWindow::performBatteryTest()
 {
-    if (audioTestPopupShown || !isProductionTestRunning || currentTestStep != TEST_AUDIO) {
-        qDebug() << "录音测试弹窗已显示或条件不满足,跳过显示";
+    // 检查电池测试是否启用(充电或NTC任一启用即可)
+    if (!isTestItemEnabled("battery")) {
+        qDebug() << "电池测试未启用(充电和NTC都未启用),跳过";
+        updateTestStatus("battery", STATUS_UNTESTED);
+        updateTestStatus("charge", STATUS_UNTESTED);
+        updateTestStatus("ntc", STATUS_UNTESTED);
+        skipToNextEnabledTest();
         return;
     }
     
-    // 如果已经有录音测试弹窗存在,先关闭它
-    if (audioTestDialog) {
-        qDebug() << "检测到已存在的录音测试弹窗,先关闭";
-        audioTestDialog->close();
-        audioTestDialog->deleteLater();
-        audioTestDialog = nullptr;
+    if (!isProductionTestRunning || currentTestStep != TEST_BATTERY) {
+        return;
     }
-    
-    // 创建录音测试对话框
-    audioTestDialog = new QDialog(this);
-    audioTestDialog->setWindowTitle("录音测试");
-    audioTestDialog->setFixedSize(520, 350);
-    audioTestDialog->setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
-    
-    // 设置对话框样式
-    audioTestDialog->setStyleSheet(
-        "QDialog { "
-        "background-color: #f0f0f0; "
-        "border-radius: 8px; "
-        "}"
-    );
-    
-    // 使用垂直布局管理器
-    QVBoxLayout *mainLayout = new QVBoxLayout(audioTestDialog);
-    mainLayout->setSpacing(15);
-    mainLayout->setContentsMargins(20, 20, 20, 20);
-    
-    // 创建主标签
-    audioDialogLabel = new QLabel("🎤 录音测试");
-    audioDialogLabel->setAlignment(Qt::AlignCenter);
-    audioDialogLabel->setStyleSheet(
-        "QLabel { "
-        "font-size: 18px; "
-        "font-weight: bold; "
-        "background-color: #4CAF50; "
-        "color: white; "
-        "border-radius: 4px; "
-        "padding: 12px; "
-        "}"
-    );
-    mainLayout->addWidget(audioDialogLabel);
-    
-    // 创建信息标签
-    audioDialogInfo = new QLabel("🎤 请点击下方按钮开始录音测试\n流程:开始录音→1s后播放1kHz音频→2s后停止播放和录音→1s后播放录音→3s后停止");
-    audioDialogInfo->setAlignment(Qt::AlignCenter);
-    audioDialogInfo->setWordWrap(true);
-    audioDialogInfo->setMinimumHeight(80);
-    audioDialogInfo->setStyleSheet(
-        "QLabel { "
-        "font-size: 14px; "
-        "background-color: white; "
-        "border: 1px solid #ddd; "
-        "border-radius: 4px; "
-        "padding: 15px; "
-        "color: #333; "
-        "}"
-    );
-    mainLayout->addWidget(audioDialogInfo);
-    
-    // 创建开始录音按钮布局
-    QHBoxLayout *startLayout = new QHBoxLayout();
-    startLayout->setSpacing(10);
-    
-    audioStartButton = new QPushButton("🎤 开始录音测试");
-    audioStartButton->setMinimumHeight(50);
-    audioStartButton->setStyleSheet(
-        "QPushButton { "
-        "background-color: #2196F3; "
-        "color: white; "
-        "padding: 12px 20px; "
-        "border-radius: 4px; "
-        "font-weight: bold; "
-        "font-size: 16px; "
-        "}"
-    );
-    startLayout->addWidget(audioStartButton);
-    mainLayout->addLayout(startLayout);
-    
-    // 创建测试结果按钮布局
-    QHBoxLayout *resultLayout = new QHBoxLayout();
-    resultLayout->setSpacing(10);
-    
-    // 创建按钮
-    audioPassButton = new QPushButton("✓ 录音正常");
-    audioPassButton->setMinimumHeight(45);
-    audioPassButton->setStyleSheet(
-        "QPushButton { "
-        "background-color: #4CAF50; "
-        "color: white; "
-        "padding: 10px 15px; "
-        "border-radius: 4px; "
-        "font-weight: bold; "
-        "font-size: 14px; "
-        "} "
-        "QPushButton:disabled { "
-        "background-color: #cccccc; "
-        "color: #666666; "
-        "}"
-    );
-    audioPassButton->setEnabled(true); // 允许直接点击通过
-    
-    audioFailButton = new QPushButton("✗ 录音异常");
-    audioFailButton->setMinimumHeight(45);
-    audioFailButton->setStyleSheet(
-        "QPushButton { "
-        "background-color: #f44336; "
-        "color: white; "
-        "padding: 10px 15px; "
-        "border-radius: 4px; "
-        "font-weight: bold; "
-        "font-size: 14px; "
-        "}"
-    );
-    audioFailButton->setEnabled(false); // 初始禁用
-    
-    audioRetestButton = new QPushButton("🔄 重新测试");
-    audioRetestButton->setMinimumHeight(45);
-    audioRetestButton->setStyleSheet(
-        "QPushButton { "
-        "background-color: #FF9800; "
-        "color: white; "
-        "padding: 10px 15px; "
-        "border-radius: 4px; "
-        "font-weight: bold; "
-        "font-size: 14px; "
-        "}"
-    );
-    audioRetestButton->setEnabled(false); // 初始禁用
-    
-    resultLayout->addWidget(audioPassButton);
-    resultLayout->addWidget(audioFailButton);
-    resultLayout->addWidget(audioRetestButton);
-    
-    // 添加按钮布局到主布局
-    mainLayout->addLayout(resultLayout);
-    
-    // 连接开始录音按钮事件
-    connect(audioStartButton, &QPushButton::clicked, [this]() {
-        qDebug() << "用户点击开始录音测试";
-        
-        // 禁用开始按钮,更新界面状态
-        audioStartButton->setEnabled(false);
-        audioDialogInfo->setText("🎤 正在开始录音...\n请保持安静");
-        
-        // 发送开始录音指令
-        QString recordCommand = "AT^MEDIAAUDIORECORD=xx,mp3,16000,1,16,/user/res/music/1.mp3\r\n";
-        serial->write(recordCommand.toUtf8());
-        qDebug() << "发送录音开始指令:" << recordCommand.trimmed();
-        
-        // 延迟1秒后发送播放1kHz音频指令
-        QTimer::singleShot(1000, this, [this]() {
-            if (audioTestDialog && isProductionTestRunning) {
-                audioDialogInfo->setText("📢 正在播放1kHz测试音频(2秒)\n同时进行录音,请注意听声音...");
-                
-                // 发送播放1kHz音频指令
-                QString play1khzCommand = "AT^MEDIAMUSICPLAY=xx,/user/res/music/1khz.mp3,1,0\r\n";
-                serial->write(play1khzCommand.toUtf8());
-                qDebug() << "发送播放1kHz音频指令:" << play1khzCommand.trimmed();
-                
-                // 设置当前步骤为播放1kHz音频,启动2秒定时器
-                currentAudioStep = AUDIO_PLAYING_1KHZ;
-                audioRecordTimer->start(2000);
-            }
-        });
-    });
-    
-    // 连接测试结果按钮事件
-    connect(audioPassButton, &QPushButton::clicked, [this]() {
-        if (audioTestDialog) {
-            audioTestDialog->accept();
-            qDebug() << "用户确认:录音测试通过";
-        ui->statusbar->showMessage("录音测试通过", 2000);
-        updateTestStatus("audio", STATUS_SUCCESS);
-        
-            // 继续下一个测试
-            skipToNextEnabledTest();
-        }
-    });
-    
-    connect(audioFailButton, &QPushButton::clicked, [this]() {
-        if (audioTestDialog) {
-            audioTestDialog->accept();
-            qDebug() << "用户确认:录音测试失败";
-        ui->statusbar->showMessage("录音测试失败", 2000);
-        updateTestStatus("audio", STATUS_FAILED);
-        
-            // 继续下一个测试
-            skipToNextEnabledTest();
-        }
-    });
-    
-    connect(audioRetestButton, &QPushButton::clicked, [this]() {
-        qDebug() << "用户选择重新测试录音";
-        
-        // 发送停止指令
-        serial->write("AT^MEDIAMUSICSTOP\r\n");
-        serial->write("AT^MEDIAAUDIORECORDSTOP\r\n");
-        
-        // 重置界面状态
-        audioStartButton->setEnabled(true);
-        audioPassButton->setEnabled(false);
-        audioFailButton->setEnabled(false);
-        audioRetestButton->setEnabled(false);
-        audioDialogInfo->setText("🎤 请点击下方按钮开始录音测试\n流程:开始录音→1s后播放1kHz音频→2s后停止播放和录音→1s后播放录音→3s后停止");
-        
-        // 重置录音测试状态
-        currentAudioStep = AUDIO_STARTING_RECORD;
-        audioRecordTimer->stop();
-        
-        qDebug() << "录音测试已重置,等待用户重新开始";
-    });
-    
-    // 显示对话框
-    audioTestPopupShown = true;
-    audioTestDialog->show();
-    qDebug() << "录音测试弹窗已显示";
-}
-
-// 录音测试定时器 - 8秒超时检查“fac:mic level:中”
-void MainWindow::onAudioRecordTimer()
-{
-    audioRecordTimer->stop();
-    
-    if (!isProductionTestRunning || currentTestStep != TEST_AUDIO) {
-        return;
-    }
-
-    if (audioTestFinished) {
-        qDebug() << "录音测试已在8秒内完成,不再处理超时";
-        return;
-    }
-
-    // 8秒内未检测到连续3个或累计6个满足要求的Level,判定录音测试失败
-    qDebug() << QString("录音测试8秒超时,连续计数: %1,累计计数: %2,未达到通过标准(连续3次或累计6次),测试失败")
-                .arg(audioMicMiddleConsecutiveCount)
-                .arg(audioMicTotalPassCount);
-    ui->statusbar->showMessage(QString("录音测试失败:8秒内未达到通过标准(连续3次或累计6次满足要求 >= %1)").arg(audioLevelStandard), 3000);
-    ui->recvTextEdit->append(QString(">>> 音频测试超时失败: 连续 %1 次,累计 %2 次 (要求: 连续3次或累计6次 >= %3)")
-                             .arg(audioMicMiddleConsecutiveCount)
-                             .arg(audioMicTotalPassCount)
-                             .arg(audioLevelStandard));
-    updateTestStatus("audio", STATUS_FAILED);
-
-    audioTestFinished = true;
-
-    // 发送结束音频测试指令
-    QString audioEndCmd = "AT^AUDIOTEST=6\r\n";
-    serial->write(audioEndCmd.toUtf8());
-    qDebug() << "发送结束音频测试指令:" << audioEndCmd.trimmed();
-
-    // 继续后续测试
-    skipToNextEnabledTest();
-}
-
-// 解析音频等级(从文字或rms_db数值)
-QString MainWindow::parseAudioLevel(const QString& data)
-{
-    // 尝试从 "fac:mic level:" 后面提取值
-    QRegExp levelPattern("fac:mic level:(.+)");
-    if (levelPattern.indexIn(data) != -1) {
-        QString levelStr = levelPattern.cap(1).trimmed();
-        
-        // 如果是数字(rms_db值),转换为等级文字
-        bool ok;
-        double rms_db = levelStr.toDouble(&ok);
-        if (ok) {
-            // 根据rms_db值转换为等级
-            if (rms_db < -60.0) {
-                return "静音";
-            } else if (rms_db < -40.0) {
-                return "极低";
-            } else if (rms_db < -20.0) {
-                return "低";
-            } else if (rms_db < -6.0) {
-                return "中";
-            } else if (rms_db < 0.0) {
-                return "高";
-            } else {
-                return "最大";
-            }
-        } else {
-            // 已经是文字,直接返回(去除可能的空格和换行)
-            return levelStr;
-        }
-    }
-    
-    return "";
-}
-
-// 获取音频等级数值(用于比较)
-// 返回值:静音=0, 极低=1, 低=2, 中=3, 高=4, 最大=5
-int MainWindow::getAudioLevelValue(const QString& level)
-{
-    if (level == "静音") {
-        return 0;
-    } else if (level == "极低") {
-        return 1;
-    } else if (level == "低") {
-        return 2;
-    } else if (level == "中") {
-        return 3;
-    } else if (level == "高") {
-        return 4;
-    } else if (level == "最大") {
-        return 5;
-    }
-    return -1; // 未知等级
-}
-
-// 判断检测到的等级是否满足要求
-// 如果设置"低",那么"低"、"中"、"高"、"最大"都可以通过,但"极低"和"静音"不能通过
-bool MainWindow::isAudioLevelPass(const QString& detectedLevel, const QString& requiredLevel)
-{
-    int detectedValue = getAudioLevelValue(detectedLevel);
-    int requiredValue = getAudioLevelValue(requiredLevel);
-    
-    if (detectedValue == -1 || requiredValue == -1) {
-        return false; // 未知等级,不通过
-    }
-    
-    // 检测到的等级值 >= 要求的等级值,则通过
-    return detectedValue >= requiredValue;
-}
-
-// 检查录音测试结果
-void MainWindow::checkAudioTestResult(const QString& data)
-{
-    if (!isProductionTestRunning || currentTestStep != TEST_AUDIO) {
-        return;
-    }
-    
-    qDebug() << "录音测试收到数据:" << data.trimmed();
-
-    // 只在录音测试过程中统计"fac:mic level:"的值
-    if (data.contains("fac:mic level:")) {
-        // 解析检测到的音频等级
-        QString detectedLevel = parseAudioLevel(data);
-        
-        if (detectedLevel.isEmpty()) {
-            qDebug() << "无法解析音频等级,跳过";
-            return;
-        }
-        
-        qDebug() << QString("解析到音频等级: %1 (要求: >= %2)").arg(detectedLevel).arg(audioLevelStandard);
-        
-        // 判断是否满足要求
-        bool isPass = isAudioLevelPass(detectedLevel, audioLevelStandard);
-        
-        if (isPass) {
-            // 满足要求,连续计数和累计计数都增加
-            audioMicMiddleConsecutiveCount++;
-            audioMicTotalPassCount++;
-            
-            qDebug() << QString("检测到满足要求的等级 \"%1\" (>= %2),连续计数: %3,累计计数: %4")
-                        .arg(detectedLevel)
-                        .arg(audioLevelStandard)
-                        .arg(audioMicMiddleConsecutiveCount)
-                        .arg(audioMicTotalPassCount);
-            
-            ui->recvTextEdit->append(QString(">>> 音频测试: 检测到等级 %1 (>= %2),连续 %3 次,累计 %4 次")
-                                     .arg(detectedLevel)
-                                     .arg(audioLevelStandard)
-                                     .arg(audioMicMiddleConsecutiveCount)
-                                     .arg(audioMicTotalPassCount));
-        } else {
-            // 不满足要求,连续计数清零,但累计计数不变
-            qDebug() << QString("检测到不满足要求的等级 \"%1\" (< %2),重置连续计数")
-                        .arg(detectedLevel)
-                        .arg(audioLevelStandard);
-            audioMicMiddleConsecutiveCount = 0;
-            
-            ui->recvTextEdit->append(QString(">>> 音频测试: 检测到等级 %1 (< %2,不满足要求),连续计数已重置")
-                                     .arg(detectedLevel)
-                                     .arg(audioLevelStandard));
-        }
-
-        // 判断是否通过:
-        // 1. 连续3次满足要求,或者
-        // 2. 累计6次满足要求(即使不是连续的)
-        bool testPassed = false;
-        QString passReason;
-        
-        if (audioMicMiddleConsecutiveCount >= 3) {
-            testPassed = true;
-            passReason = QString("连续 %1 次满足要求").arg(audioMicMiddleConsecutiveCount);
-        } else if (audioMicTotalPassCount >= 6) {
-            testPassed = true;
-            passReason = QString("累计 %1 次满足要求").arg(audioMicTotalPassCount);
-        }
-        
-        if (!audioTestFinished && testPassed) {
-            qDebug() << QString("录音测试通过:%1 (要求: >= %2)").arg(passReason).arg(audioLevelStandard);
-            ui->statusbar->showMessage(QString("录音测试通过:%1").arg(passReason), 3000);
-            ui->recvTextEdit->append(QString(">>> 音频测试通过: %1").arg(passReason));
-            updateTestStatus("audio", STATUS_SUCCESS);
-
-            audioTestFinished = true;
-
-            // 停止8秒定时器,避免再触发超时失败
-            if (audioRecordTimer->isActive()) {
-                audioRecordTimer->stop();
-            }
-
-            // 发送结束音频测试指令
-            QString audioEndCmd = "AT^AUDIOTEST=6\r\n";
-            serial->write(audioEndCmd.toUtf8());
-            ui->recvTextEdit->append(">>> 发送指令: AT^AUDIOTEST=6 (结束音频测试)");
-            qDebug() << "发送结束音频测试指令:" << audioEndCmd.trimmed();
-
-            // 继续后续测试
-            skipToNextEnabledTest();
-        }
-    }
-}
-
-// 执行电池测试
-void MainWindow::performBatteryTest()
-{
-    // 检查电池测试是否启用(充电或NTC任一启用即可)
-    if (!isTestItemEnabled("charge") && !isTestItemEnabled("ntc")) {
-        qDebug() << "电池测试未启用(充电和NTC都未启用),跳过";
-        updateTestStatus("battery", STATUS_UNTESTED);
-        updateTestStatus("charge", STATUS_UNTESTED);
-        updateTestStatus("ntc", STATUS_UNTESTED);
-        skipToNextEnabledTest();
-        return;
-    }
-    
-    if (!isProductionTestRunning || currentTestStep != TEST_BATTERY) {
-        return;
-    }
-
-    qDebug() << "开始电池测试";
-    ui->recvTextEdit->append("========== 开始电池测试 ==========");
-    updateTestStatus("battery", STATUS_TESTING);
-    ui->statusbar->showMessage("电池测试中,正在获取电池电压...", 3000);
+
+    qDebug() << "开始电池测试";
+    ui->recvTextEdit->append("========== 开始电池测试 ==========");
+    updateTestStatus("battery", STATUS_TESTING);
+    ui->statusbar->showMessage("电池测试中,正在获取电池电压...", 3000);
     
     // 延迟100ms后发送电池电量查询指令,确保串口已准备好
     QTimer::singleShot(100, this, [this]() {
@@ -4288,8 +3036,8 @@ void MainWindow::checkBatteryTestResult(const QString& data)
 
         // 充电状态检查(根据checkbox决定是否判定)
         // 注意:充电状态每次都解析,但只有charge checkbox选中时才用充电状态来判断是否通过
-        bool chargeCheckEnabled = isTestItemEnabled("charge");
-        bool ntcCheckEnabled = isTestItemEnabled("ntc");
+        bool chargeCheckEnabled = isTestItemEnabled("battery");
+        bool ntcCheckEnabled = isTestItemEnabled("battery");
         
         // 拼接检测结果日志(包含所有已获取的字段)
         // 注意:充电状态和温度数据每次都解析并记录,无论checkbox是否选中
@@ -4345,10 +3093,9 @@ void MainWindow::checkBatteryTestResult(const QString& data)
                 ui->statusbar->showMessage("充电检查失败 - 设备未充电", 2000);
                 ui->recvTextEdit->append(">>> 充电检查失败:设备未充电");
                 
-                // 显示充电失败对话框(每次失败都显示,让用户可以选择重试)
+                // 显示充电失败对话框(用户点确认会在此函数内调用skipToNextEnabledTest并返回)
                 showBatteryChargeFailDialog();
-                
-                // 注意:不直接返回,继续检查NTC(如果启用),然后根据所有检查结果来决定是否通过
+                return;  // 充电失败后不再继续后续NTC等逻辑,已由弹窗处理
             } else if (chargeFound && charge != -1 && charge != 0 && charge != 1) {
                 // 检测到charge字段但值不是0或1,可能是异常值
                 qDebug() << QString("充电检查:检测到异常charge值 %1,期望0或1").arg(charge);
@@ -4410,7 +3157,8 @@ void MainWindow::checkBatteryTestResult(const QString& data)
 
         // NTC温度检查(根据checkbox决定是否判定)
         // 注意:温度数据每次都解析,但只有NTC checkbox选中时才用温度>0来判断是否通过
-        bool isVoltageNormal = (voltage >= 3.3 && voltage <= 4.4);
+        // 电压范围:3.3V-4.6V(支持充满电时4.5V+的测量值)
+        bool isVoltageNormal = (voltage >= 3.3 && voltage <= 4.6);
         bool isTempNormal = true;  // 默认温度正常(未检测到温度时不影响结果)
         
         // 温度数据每次都解析(已经在上面解析了),这里只判断是否满足条件
@@ -4484,7 +3232,7 @@ void MainWindow::checkBatteryTestResult(const QString& data)
                 failReasons << "NTC温度检查失败";
             }
             if (!isVoltageNormal) {
-                failReasons << QString("电压异常: %1V(正常范围3.3V-4.4V)").arg(voltage);
+                failReasons << QString("电压异常: %1V(正常范围3.3V-4.6V)").arg(voltage);
             }
             failMsg += failReasons.join(",");
 
@@ -4501,7 +3249,7 @@ void MainWindow::checkBatteryTestResult(const QString& data)
     }
 }
 
-// 执行G-sensor测试
+// 执行G-sensor测试(10秒内计算XYZ变化)
 void MainWindow::performGsensorTest()
 {
     // 检查G-sensor测试是否启用
@@ -4511,132 +3259,303 @@ void MainWindow::performGsensorTest()
         skipToNextEnabledTest();
         return;
     }
-    
+
     if (!isProductionTestRunning || currentTestStep != TEST_GSENSOR) {
         return;
     }
 
-    qDebug() << "开始G-sensor测试";
+    qDebug() << "开始G-sensor测试(10秒XYZ变化计算)";
     ui->recvTextEdit->append("========== 开始G-sensor测试 ==========");
     updateTestStatus("gsensor", STATUS_TESTING);
-    ui->statusbar->showMessage("G-sensor测试中,正在获取sensor ID...", 3000);
-    
-    // 发送G-sensor查询指令
-    serial->write("AT^GETCYWEEID\r\n");
-    qDebug() << "发送G-sensor测试指令: AT^GETCYWEEID";
-    
-    // 启动10秒超时检测
-    testTimeoutTimer->start(10000);
+    ui->statusbar->showMessage("G-sensor测试中:AT^GSENSORTEST=1(10秒)", 3000);
+
+    // 重置采集状态
+    gSensorCollecting = false;
+    gSensorHasStart = false;
+    gSensorHasLast = false;
+    gSensorErrorDetected = false;
+    gSensorAnySample = false;
+    gSensorAllSamplesInRange = true;
+    gSensorStartX = 0.0;
+    gSensorStartY = 0.0;
+    gSensorStartZ = 0.0;
+    gSensorLastX = 0.0;
+    gSensorLastY = 0.0;
+    gSensorLastZ = 0.0;
+
+    // 防止通用超时干扰
+    if (testTimeoutTimer->isActive()) {
+        testTimeoutTimer->stop();
+    }
+    if (gSensorIntervalTimer->isActive()) {
+        gSensorIntervalTimer->stop();
+    }
+
+    // 1) 延迟一点后开始打印XYZ
+    QTimer::singleShot(300, this, [this]() {
+        if (!isProductionTestRunning || currentTestStep != TEST_GSENSOR) {
+            return;
+        }
+        if (!serial || !serial->isOpen()) {
+            return;
+        }
+
+        ui->recvTextEdit->append(">>> 发送指令: AT^GSENSORTEST=1 (开始检测与打印)");
+        serial->write("AT^GSENSORTEST=1\r\n");
+        qDebug() << "发送指令: AT^GSENSORTEST=1";
+
+        gSensorCollecting = true;
+        ui->statusbar->showMessage("G-sensor检测与打印中...(10秒)", 2000);
+
+        // 10秒后停止检测并计算
+        gSensorIntervalTimer->start(10000);
+    });
 }
 
-// 检查G-sensor测试结果
+// 检查G-sensor测试结果(解析XYZ)
 void MainWindow::checkGsensorTestResult(const QString& data)
 {
-    qDebug() << "=== checkGsensorTestResult 被调用 ===";
-    qDebug() << "isProductionTestRunning:" << isProductionTestRunning;
-    qDebug() << "currentTestStep:" << currentTestStep << "(TEST_GSENSOR=" << TEST_GSENSOR << ")";
-    
     if (!isProductionTestRunning || currentTestStep != TEST_GSENSOR) {
-        qDebug() << "G-sensor测试条件不满足,跳过处理";
         return;
     }
-    
-    qDebug() << "G-sensor测试收到数据:" << data.trimmed();
-    
-    // 检测G-sensor ID,格式可能为:fac:gsensor id : 0x13 或类似
-    if (data.contains("gsensor") && data.contains("id")) {
-        QRegExp gsensorPattern("(?:fac:)?gsensor\\s+id\\s*[:]\\s*(0?[xX]?[0-9A-Fa-f]+)");
-        qDebug() << "尝试匹配G-sensor模式,输入数据:" << data.trimmed();
-        qDebug() << "正则表达式模式:" << gsensorPattern.pattern();
-        if (gsensorPattern.indexIn(data) != -1) {
-            // 停止超时检测,因为已经收到响应
-            if (testTimeoutTimer->isActive()) {
-                testTimeoutTimer->stop();
-                qDebug() << "G-sensor测试收到响应,停止超时检测";
-            }
-            
-            qDebug() << "G-sensor正则表达式匹配通过!";
-            QString gsensorId = gsensorPattern.cap(1).toUpper(); // 转换为大写便于比较
-            qDebug() << "检测到G-sensor ID:" << gsensorId;
-            
-            // 检查G-sensor ID是否为有效值(期望值为0X13)
-            // 支持多种格式: 0X13, 0x13, 13, 19 (十进制)
-            bool isValidId = false;
-            QString expectedId = "0X13";
-            
-            // 统一转换为大写便于比较
-            QString upperGsensorId = gsensorId.toUpper();
-            
-            if (upperGsensorId == "0X13" || upperGsensorId == "13") {
-                isValidId = true;
-            } else {
-                // 尝试解析数值进行比较
-                bool ok;
-                int decimalValue = 0;
-                
-                // 如果以0X或0x开头,按十六进制解析
-                if (upperGsensorId.startsWith("0X")) {
-                    decimalValue = upperGsensorId.mid(2).toInt(&ok, 16);
-                } else {
-                    // 尝试直接按十六进制解析
-                    decimalValue = upperGsensorId.toInt(&ok, 16);
-                    // 如果十六进制解析失败,尝试十进制解析
-                if (!ok) {
-                        decimalValue = upperGsensorId.toInt(&ok, 10);
-                    }
-                }
-                
-                if (ok && decimalValue == 0x13) {
-                    isValidId = true;
+
+    const QString trimmed = data.trimmed();
+
+    if (trimmed.contains("ERROR", Qt::CaseInsensitive) || trimmed.contains("FAIL", Qt::CaseInsensitive)) {
+        gSensorErrorDetected = true;
+        return;
+    }
+
+    if (!gSensorCollecting) {
+        return; // 非采集窗口不做解析
+    }
+
+    // 解析格式:XYZ: X=-1.28,Y=8.22,Z=-4.60
+    if (trimmed.contains("XYZ:", Qt::CaseInsensitive)) {
+        QRegExp xyzPattern("XYZ\\s*:\\s*X\\s*=\\s*([-]?[0-9]+\\.?[0-9]*),\\s*Y\\s*=\\s*([-]?[0-9]+\\.?[0-9]*),\\s*Z\\s*=\\s*([-]?[0-9]+\\.?[0-9]*)",
+                            Qt::CaseInsensitive);
+        if (xyzPattern.indexIn(trimmed) != -1) {
+            bool okX = false, okY = false, okZ = false;
+            double x = xyzPattern.cap(1).toDouble(&okX);
+            double y = xyzPattern.cap(2).toDouble(&okY);
+            double z = xyzPattern.cap(3).toDouble(&okZ);
+
+            if (okX && okY && okZ) {
+                ui->recvTextEdit->append(QString(">>> G-sensor采样: XYZ: X=%1,Y=%2,Z=%3")
+                                         .arg(x, 0, 'f', 2).arg(y, 0, 'f', 2).arg(z, 0, 'f', 2));
+                gSensorAnySample = true;
+                // 起始XYZ:第一次解析到的点
+                if (!gSensorHasStart) {
+                    gSensorHasStart = true;
+                    gSensorStartX = x;
+                    gSensorStartY = y;
+                    gSensorStartZ = z;
+                    ui->statusbar->showMessage(QString("G-sensor起始XYZ已记录:X=%1 Y=%2 Z=%3").arg(x).arg(y).arg(z), 1500);
                 }
-            }
-            
-            if (isValidId) {
-                qDebug() << "G-sensor ID有效,测试通过";
-                ui->statusbar->showMessage(QString("G-sensor测试通过 - ID: %1").arg(gsensorId), 2000);
-                updateTestStatus("gsensor", STATUS_SUCCESS);
-            } else {
-                qDebug() << "G-sensor ID不匹配期望值,测试失败";
-                ui->statusbar->showMessage(QString("G-sensor测试失败 - ID: %1 (期望: 0X13)").arg(gsensorId), 2000);
-                updateTestStatus("gsensor", STATUS_FAILED);
-            }
-            
-            // G-sensor测试完成,推进到下一个测试
-            if (isProductionTestRunning && currentTestStep == TEST_GSENSOR) {
-                qDebug() << "G-sensor测试完成,推进到下一个测试";
-                skipToNextEnabledTest();
-            }
-        } else {
-            qDebug() << "G-sensor正则表达式匹配失败,数据:" << data.trimmed();
-            
-            // 手动测试特定格式:fac:gsensor id : 0x13
-            if (data.contains("gsensor id") && data.contains("0x13")) {
-                qDebug() << "手动检测到 fac:gsensor id : 0x13 格式,强制通过测试";
-                ui->statusbar->showMessage("G-sensor测试通过 - ID: 0x13 (手动检测)", 2000);
-                updateTestStatus("gsensor", STATUS_SUCCESS);
-                
-                // G-sensor测试完成,推进到下一个测试
-                if (isProductionTestRunning && currentTestStep == TEST_GSENSOR) {
-                    qDebug() << "G-sensor测试完成(手动),推进到下一个测试";
-                    skipToNextEnabledTest();
+
+                // 末尾XYZ:10秒窗口内最后一次解析到的点
+                gSensorHasLast = true;
+                gSensorLastX = x;
+                gSensorLastY = y;
+                gSensorLastZ = z;
+
+                // 每条采样都要满足阈值范围
+                auto parseDoubleOrInvalid = [](const QString& s, bool& ok) -> double {
+                    QString t = s.trimmed();
+                    if (t.isEmpty()) {
+                        ok = false;
+                        return 0.0;
+                    }
+                    return t.toDouble(&ok);
+                };
+                bool okXMin = false, okXMax = false, okYMin = false, okYMax = false, okZMin = false, okZMax = false;
+                double xMin = parseDoubleOrInvalid(ui->gsensorDeltaXMinLineEdit->text(), okXMin);
+                double xMax = parseDoubleOrInvalid(ui->gsensorDeltaXMaxLineEdit->text(), okXMax);
+                double yMin = parseDoubleOrInvalid(ui->gsensorDeltaYMinLineEdit->text(), okYMin);
+                double yMax = parseDoubleOrInvalid(ui->gsensorDeltaYMaxLineEdit->text(), okYMax);
+                double zMin = parseDoubleOrInvalid(ui->gsensorDeltaZMinLineEdit->text(), okZMin);
+                double zMax = parseDoubleOrInvalid(ui->gsensorDeltaZMaxLineEdit->text(), okZMax);
+
+                bool samplePass = true;
+                if (okXMin && x < xMin) samplePass = false;
+                if (okXMax && x > xMax) samplePass = false;
+                if (okYMin && y < yMin) samplePass = false;
+                if (okYMax && y > yMax) samplePass = false;
+                if (okZMin && z < zMin) samplePass = false;
+                if (okZMax && z > zMax) samplePass = false;
+
+                if (!samplePass) {
+                    gSensorAllSamplesInRange = false;
+                    ui->recvTextEdit->append(">>> G-sensor采样超限");
                 }
             }
         }
-    } else {
-        qDebug() << "数据不包含gsensor和id关键字,跳过G-sensor处理";
     }
-    
-    // 单独检查错误响应
-    if (data.contains("ERROR") || data.contains("FAIL")) {
-        qDebug() << "G-sensor测试失败 - 设备响应错误";
-        ui->statusbar->showMessage("G-sensor测试失败 - 设备无响应", 2000);
+}
+
+// 10秒到时:发送GSENSORTEST=0并计算变化
+void MainWindow::onGsensorIntervalTimeout()
+{
+    if (!isProductionTestRunning || currentTestStep != TEST_GSENSOR) {
+        return;
+    }
+
+    gSensorIntervalTimer->stop();
+
+    if (serial && serial->isOpen()) {
+        ui->recvTextEdit->append(">>> 发送指令: AT^GSENSORTEST=0 (停止检测与打印)");
+        serial->write("AT^GSENSORTEST=0\r\n");
+        qDebug() << "发送指令: AT^GSENSORTEST=0";
+    }
+
+    // 给设备一点时间把最后一行XYZ打印出来
+    QTimer::singleShot(500, this, [this]() {
+        if (!isProductionTestRunning || currentTestStep != TEST_GSENSOR) {
+            return;
+        }
+        gSensorCollecting = false;
+        finalizeGsensorTest();
+    });
+}
+
+// 结束G-sensor判定:要求每条采样都在阈值范围内
+void MainWindow::finalizeGsensorTest()
+{
+    if (!isProductionTestRunning || currentTestStep != TEST_GSENSOR) {
+        return;
+    }
+
+    if (gSensorErrorDetected) {
+        ui->statusbar->showMessage("G-sensor测试失败:设备返回ERROR/FAIL", 3000);
         updateTestStatus("gsensor", STATUS_FAILED);
-        
-        // G-sensor测试失败,仍然继续下一个测试
-        if (isProductionTestRunning && currentTestStep == TEST_GSENSOR) {
-            qDebug() << "G-sensor测试失败,继续下一个测试";
-            skipToNextEnabledTest();
+        skipToNextEnabledTest();
+        return;
+    }
+
+    if (!gSensorAnySample) {
+        ui->statusbar->showMessage("G-sensor测试失败:10秒内未采到XYZ数据", 3000);
+        updateTestStatus("gsensor", STATUS_FAILED);
+        skipToNextEnabledTest();
+        return;
+    }
+
+    // 读取UI阈值(允许空:表示不校验该边界)
+    auto parseDoubleOrInvalid = [](const QString& s, bool& ok) -> double {
+        QString t = s.trimmed();
+        if (t.isEmpty()) {
+            ok = false;
+            return 0.0;
+        }
+        return t.toDouble(&ok);
+    };
+
+    bool okXMin = false, okXMax = false, okYMin = false, okYMax = false, okZMin = false, okZMax = false;
+    parseDoubleOrInvalid(ui->gsensorDeltaXMinLineEdit->text(), okXMin);
+    parseDoubleOrInvalid(ui->gsensorDeltaXMaxLineEdit->text(), okXMax);
+    parseDoubleOrInvalid(ui->gsensorDeltaYMinLineEdit->text(), okYMin);
+    parseDoubleOrInvalid(ui->gsensorDeltaYMaxLineEdit->text(), okYMax);
+    parseDoubleOrInvalid(ui->gsensorDeltaZMinLineEdit->text(), okZMin);
+    parseDoubleOrInvalid(ui->gsensorDeltaZMaxLineEdit->text(), okZMax);
+
+    // okXMin/okXMax 只有在“输入非空且可转 double”时才为 true
+    const bool xMinSet = okXMin;
+    const bool xMaxSet = okXMax;
+    const bool yMinSet = okYMin;
+    const bool yMaxSet = okYMax;
+    const bool zMinSet = okZMin;
+    const bool zMaxSet = okZMax;
+
+    if (!(xMinSet || xMaxSet || yMinSet || yMaxSet || zMinSet || zMaxSet)) {
+        // 未配置任何阈值时:只要采到有效 XYZ 即判定通过(有值就为通过)
+        ui->statusbar->showMessage("G-sensor测试通过:未配置阈值,已采到有效XYZ数据", 3000);
+        updateTestStatus("gsensor", STATUS_SUCCESS);
+        skipToNextEnabledTest();
+        return;
+    }
+
+    if (gSensorAllSamplesInRange) {
+        ui->statusbar->showMessage("G-sensor测试通过:10秒内所有XYZ采样均在阈值范围", 3000);
+        updateTestStatus("gsensor", STATUS_SUCCESS);
+    } else {
+        ui->statusbar->showMessage("G-sensor测试失败:存在超限采样", 3000);
+        updateTestStatus("gsensor", STATUS_FAILED);
+    }
+
+    skipToNextEnabledTest();
+}
+
+// 执行漏光测试
+void MainWindow::performLeaklightTest()
+{
+    if (!isTestItemEnabled("leaklight")) {
+        qDebug() << "漏光测试未启用,跳过";
+        updateTestStatus("leaklight", STATUS_UNTESTED);
+        skipToNextEnabledTest();
+        return;
+    }
+
+    if (!isProductionTestRunning || currentTestStep != TEST_LEAKLIGHT) {
+        return;
+    }
+
+    updateTestStatus("leaklight", STATUS_TESTING);
+    ui->recvTextEdit->append("========== 开始漏光测试 ==========");
+    ui->recvTextEdit->append(">>> 发送指令: AT^LEAKLIGHT");
+    ui->statusbar->showMessage("漏光测试中,等待green/red/ir数据...", 3000);
+
+    leaklightGreen = 0;
+    leaklightRed = 0;
+    leaklightIr = 0;
+    leaklightGreenSeen = false;
+    leaklightRedSeen = false;
+    leaklightIrSeen = false;
+
+    if (serial && serial->isOpen()) {
+        serial->write("AT^LEAKLIGHT\r\n");
+    }
+
+    testTimeoutTimer->start(10000);
+}
+
+void MainWindow::checkLeaklightTestResult(const QString& data)
+{
+    if (!isProductionTestRunning || currentTestStep != TEST_LEAKLIGHT) {
+        return;
+    }
+
+    QRegExp greenPattern("green\\s*:\\s*(\\d+)", Qt::CaseInsensitive);
+    QRegExp redPattern("red\\s*:\\s*(\\d+)", Qt::CaseInsensitive);
+    QRegExp irPattern("ir\\s*:\\s*(\\d+)", Qt::CaseInsensitive);
+
+    if (greenPattern.indexIn(data) != -1) {
+        leaklightGreen = greenPattern.cap(1).toInt();
+        leaklightGreenSeen = true;
+        ui->recvTextEdit->append(QString(">>> 漏光数据 green: %1").arg(leaklightGreen));
+    }
+    if (redPattern.indexIn(data) != -1) {
+        leaklightRed = redPattern.cap(1).toInt();
+        leaklightRedSeen = true;
+        ui->recvTextEdit->append(QString(">>> 漏光数据 red: %1").arg(leaklightRed));
+    }
+    if (irPattern.indexIn(data) != -1) {
+        leaklightIr = irPattern.cap(1).toInt();
+        leaklightIrSeen = true;
+        ui->recvTextEdit->append(QString(">>> 漏光数据 ir: %1").arg(leaklightIr));
+    }
+
+    if (leaklightGreenSeen && leaklightRedSeen && leaklightIrSeen) {
+        if (testTimeoutTimer->isActive()) {
+            testTimeoutTimer->stop();
         }
+        const bool pass = (leaklightGreen != 0 && leaklightRed != 0 && leaklightIr != 0);
+        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();
     }
 }
 
@@ -4658,39 +3577,33 @@ void MainWindow::performBluetoothTest()
     }
 
     qDebug() << "开始蓝牙测试";
-    ui->recvTextEdit->append("========== 开始蓝牙测试 ==========");
+    appendToLogFile("\n========== 开始蓝牙测试 ==========\n");
     updateTestStatus("bluetooth", STATUS_TESTING);
     
     // 重置蓝牙测试相关状态
     bluetoothAddresses.clear();
+    bluetoothAddressRssiMap.clear();
     bluetoothTestFinished = false;
     detectedBluetoothAddress = "";
-    detectedBluetoothRSSI = 0;
-    
-    // 检查是否使用指定地址+RSSI模式
-    QString targetAddress = ui->bluetoothAddressLineEdit->text().trimmed().toUpper();
-    QString rssiThresholdStr = ui->bluetoothRSSILineEdit->text().trimmed();
-    
-    if (!targetAddress.isEmpty() && !rssiThresholdStr.isEmpty()) {
-        // 使用指定地址+RSSI模式
-        bool ok;
-        int rssiThreshold = rssiThresholdStr.toInt(&ok);
-        if (ok) {
-            ui->statusbar->showMessage(QString("蓝牙测试中:扫描指定地址 %1,RSSI阈值 %2").arg(targetAddress).arg(rssiThreshold), 3000);
-            qDebug() << "蓝牙测试模式:指定地址+RSSI,地址:" << targetAddress << ",RSSI阈值:" << rssiThreshold;
-        } else {
-            ui->statusbar->showMessage("蓝牙测试中:RSSI阈值格式错误,使用3设备模式", 3000);
-            qDebug() << "RSSI阈值格式错误,切换到3设备模式";
-            targetAddress = "";
-        }
+    detectedBluetoothRSSI = -128;       // 初始化为很弱的值
+    bluetoothMaxRssi = -128;
+    
+    QString expectAddr = configManager.getTestConfig("bluetoothAddress", "").trimmed().toUpper();
+    QString expectRssiStr = configManager.getTestConfig("bluetoothRSSI", "").trimmed();
+    bool hasTargetAddr = !expectAddr.isEmpty();
+    bool hasRssiThresh = !expectRssiStr.isEmpty();
+    if (hasTargetAddr && hasRssiThresh) {
+        ui->statusbar->showMessage(QString("蓝牙测试中:必须扫到目标%1且该设备RSSI>%2dBm").arg(expectAddr).arg(expectRssiStr), 3000);
+    } else if (hasTargetAddr) {
+        ui->statusbar->showMessage(QString("蓝牙测试中:必须扫到目标设备 %1").arg(expectAddr), 3000);
+    } else if (hasRssiThresh) {
+        ui->statusbar->showMessage(QString("蓝牙测试中:未设目标地址时,最大RSSI需>%1dBm").arg(expectRssiStr), 3000);
     } else {
-        // 使用3设备模式
-        ui->statusbar->showMessage("蓝牙测试中:正在开启蓝牙扫描(3设备模式)...", 3000);
-        qDebug() << "蓝牙测试模式:3设备模式";
+        ui->statusbar->showMessage("蓝牙测试中:需扫描到3个设备且至少一个RSSI≥-90dBm通过", 3000);
     }
     
     // 先发送蓝牙使能指令
-    ui->recvTextEdit->append(">>> 发送指令: AT^BTENABLE (启用蓝牙)");
+    appendToLogFile(">>> 发送指令: AT^BTENABLE (启用蓝牙)\n");
     serial->write("AT^BTENABLE\r\n");
     qDebug() << "发送蓝牙使能指令: AT^BTENABLE";
     
@@ -4702,12 +3615,12 @@ void MainWindow::performBluetoothTest()
         }
         
         QString bluetoothCmd = "AT^BTSCANTEST=1\r\n";
-        ui->recvTextEdit->append(">>> 发送指令: AT^BTSCANTEST=1 (开始蓝牙扫描)");
+        appendToLogFile(">>> 发送指令: AT^BTSCANTEST=1 (开始蓝牙扫描)\n");
         serial->write(bluetoothCmd.toUtf8());
         qDebug() << "发送蓝牙测试开启指令:" << bluetoothCmd.trimmed();
         
-        // 启动15秒超时检测
-        bluetoothTestTimer->start(15000);
+        // 启动30秒超时检测
+        bluetoothTestTimer->start(30000);
     });
 }
 
@@ -4725,11 +3638,17 @@ void MainWindow::checkBluetoothTestResult(const QString& data)
     
     qDebug() << "蓝牙测试收到数据:" << data.trimmed();
     
-    // 解析蓝牙设备地址和RSSI:支持两种格式
+    // 解析蓝牙设备地址和RSSI:支持多种格式
     // 格式1: fac:Device: 1C:1D:2B:68:82:68, RSSI=-89 dBm
     // 格式2: [BtScan] Device: 1C:1D:2B:68:82:68, RSSI=-89 dBm
-    QRegExp bluetoothPattern1("fac:Device:\\s*([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})\\s*,\\s*RSSI=(-?\\d+)\\s*dBm");
-    QRegExp bluetoothPattern2("\\[BtScan\\]\\s*Device:\\s*([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})\\s*,\\s*RSSI=(-?\\d+)\\s*dBm");
+    // 格式3: MAC=20:A5:CB:CF:BD:8B,RSSI=-74dBm 或 MAC=20:A5:CB:CF:BD:8B, RSSI=-74 dBm
+    // 格式4: Device: 20:A5:CB:CF:BD:8B, RSSI=-74
+    // 格式5: 任意包含 MAC地址(xx:xx:xx:xx:xx:xx) 和 RSSI=数字 的行
+    QRegExp bluetoothPattern1("fac:Device:\\s*([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})\\s*,\\s*RSSI=(-?\\d+)\\s*dBm?");
+    QRegExp bluetoothPattern2("\\[BtScan\\]\\s*Device:\\s*([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})\\s*,\\s*RSSI=(-?\\d+)\\s*dBm?");
+    QRegExp bluetoothPattern3("MAC\\s*=\\s*([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})\\s*,\\s*RSSI\\s*=\\s*(-?\\d+)\\s*dBm?", Qt::CaseInsensitive);
+    QRegExp bluetoothPattern4("Device\\s*:\\s*([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})\\s*,\\s*RSSI\\s*=\\s*(-?\\d+)", Qt::CaseInsensitive);
+    QRegExp bluetoothPattern5("([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})\\s*[, ].*RSSI\\s*[=:]?\\s*(-?\\d+)", Qt::CaseInsensitive);
     
     QString address;
     int rssi = 0;
@@ -4740,123 +3659,93 @@ void MainWindow::checkBluetoothTestResult(const QString& data)
         address = bluetoothPattern1.cap(1).toUpper();
         rssi = bluetoothPattern1.cap(2).toInt(&rssiOk);
         matched = true;
-        qDebug() << "使用格式1 (fac:Device:) 解析";
+        qDebug() << "蓝牙解析: 格式1 (fac:Device:)";
     } else if (bluetoothPattern2.indexIn(data) != -1) {
         address = bluetoothPattern2.cap(1).toUpper();
         rssi = bluetoothPattern2.cap(2).toInt(&rssiOk);
         matched = true;
-        qDebug() << "使用格式2 ([BtScan] Device:) 解析";
+        qDebug() << "蓝牙解析: 格式2 ([BtScan] Device:)";
+    } else if (bluetoothPattern3.indexIn(data) != -1) {
+        address = bluetoothPattern3.cap(1).toUpper();
+        rssi = bluetoothPattern3.cap(2).toInt(&rssiOk);
+        matched = true;
+        qDebug() << "蓝牙解析: 格式3 (MAC=...)";
+    } else if (bluetoothPattern4.indexIn(data) != -1) {
+        address = bluetoothPattern4.cap(1).toUpper();
+        rssi = bluetoothPattern4.cap(2).toInt(&rssiOk);
+        matched = true;
+        qDebug() << "蓝牙解析: 格式4 (Device:...)";
+    } else if (bluetoothPattern5.indexIn(data) != -1) {
+        address = bluetoothPattern5.cap(1).toUpper();
+        rssi = bluetoothPattern5.cap(2).toInt(&rssiOk);
+        matched = true;
+        qDebug() << "蓝牙解析: 格式5 (通用MAC+RSSI)";
     }
     
     if (matched && rssiOk) {
         qDebug() << "解析到蓝牙地址:" << address << ",RSSI:" << rssi;
         
-        // 打印到UI:检测到蓝牙设备
-        ui->recvTextEdit->append(QString(">>> 检测到蓝牙设备: MAC=%1, RSSI=%2 dBm").arg(address).arg(rssi));
-        
-        // 检查是否使用指定地址+RSSI模式
-        QString targetAddress = ui->bluetoothAddressLineEdit->text().trimmed().toUpper();
-        QString rssiThresholdStr = ui->bluetoothRSSILineEdit->text().trimmed();
+        if (!bluetoothAddresses.contains(address)) {
+            bluetoothAddresses.insert(address);
+            appendToLogFile(QString(">>> 新蓝牙设备:MAC=%1,RSSI=%2dBm(已检测到%3个不同设备)\n")
+                                     .arg(address).arg(rssi).arg(bluetoothAddresses.size()));
+        }
+        // 更新该地址的最佳RSSI(同一设备可能多次上报,取信号最强的)
+        if (!bluetoothAddressRssiMap.contains(address) || rssi > bluetoothAddressRssiMap[address]) {
+            bluetoothAddressRssiMap[address] = rssi;
+        }
+        if (rssi > bluetoothMaxRssi) {
+            bluetoothMaxRssi = rssi;
+        }
+
+        QString expectAddr = configManager.getTestConfig("bluetoothAddress", "").trimmed().toUpper();
+        QString expectRssiStr = configManager.getTestConfig("bluetoothRSSI", "").trimmed();
+        bool hasTargetAddr = !expectAddr.isEmpty();
+        bool hasRssiThresh = !expectRssiStr.isEmpty();
+        int expectRssi = hasRssiThresh ? expectRssiStr.toInt() : 0;
+        
+        bool pass = false;
+        if (hasTargetAddr && hasRssiThresh) {
+            int addrRssi = bluetoothAddressRssiMap.value(expectAddr, -128);
+            pass = bluetoothAddresses.contains(expectAddr) && addrRssi > expectRssi;
+        } else if (hasTargetAddr) {
+            pass = bluetoothAddresses.contains(expectAddr);
+        } else if (hasRssiThresh) {
+            pass = (bluetoothMaxRssi > expectRssi);
+        } else {
+            pass = (bluetoothAddresses.size() >= 3 && bluetoothMaxRssi >= -90);
+        }
         
-        if (!targetAddress.isEmpty() && !rssiThresholdStr.isEmpty()) {
-            // 指定地址+RSSI模式
-            bool ok;
-            int rssiThreshold = rssiThresholdStr.toInt(&ok);
-            if (ok && address == targetAddress) {
-                qDebug() << "检测到目标地址:" << address << ",RSSI:" << rssi << ",阈值:" << rssiThreshold;
-                detectedBluetoothAddress = address;
-                detectedBluetoothRSSI = rssi;
-                
-                if (rssi >= rssiThreshold) {
-                    // RSSI满足要求,测试通过
-                    qDebug() << "蓝牙测试通过:检测到指定地址" << address << ",RSSI" << rssi << "大于等于阈值" << rssiThreshold;
-                    ui->recvTextEdit->append(QString(">>> 蓝牙测试通过: 目标地址 %1, RSSI=%2 dBm (>= 阈值 %3)").arg(address).arg(rssi).arg(rssiThreshold));
-                    ui->statusbar->showMessage(QString("蓝牙测试通过:地址 %1,RSSI %2").arg(address).arg(rssi), 3000);
-                    updateTestStatus("bluetooth", STATUS_SUCCESS);
-                    
-                    bluetoothTestFinished = true;
-                    
-                    // 停止10秒定时器
-                    if (bluetoothTestTimer->isActive()) {
-                        bluetoothTestTimer->stop();
-                    }
-                    
-                    // 发送结束蓝牙扫描指令
-                    QString bluetoothEndCmd = "AT^BTSCANTEST=2\r\n";
-                    ui->recvTextEdit->append(">>> 发送指令: AT^BTSCANTEST=2 (结束蓝牙扫描)");
-                    serial->write(bluetoothEndCmd.toUtf8());
-                    qDebug() << "发送蓝牙测试结束指令:" << bluetoothEndCmd.trimmed();
-                    
-                    // 延迟200ms后发送蓝牙禁用指令
-                    QTimer::singleShot(200, this, [this]() {
-                        if (isProductionTestRunning && currentTestStep == TEST_BLUETOOTH) {
-                            ui->recvTextEdit->append(">>> 发送指令: AT^BTDISABLE (禁用蓝牙)");
-                            serial->write("AT^BTDISABLE\r\n");
-                            qDebug() << "发送蓝牙禁用指令: AT^BTDISABLE";
-                            
-                            // 继续后续测试
-                            QTimer::singleShot(300, this, [this]() {
-                                if (isProductionTestRunning && currentTestStep == TEST_BLUETOOTH) {
-                                    skipToNextEnabledTest();
-                                }
-                            });
-                        }
-                    });
-    } else {
-                    qDebug() << "RSSI不满足要求:" << rssi << "<" << rssiThreshold;
-                    ui->recvTextEdit->append(QString(">>> 蓝牙测试: 目标地址 %1, RSSI=%2 dBm (< 阈值 %3,不满足要求)").arg(address).arg(rssi).arg(rssiThreshold));
-                    ui->statusbar->showMessage(QString("蓝牙测试:检测到地址 %1,但RSSI %2 小于阈值 %3").arg(address).arg(rssi).arg(rssiThreshold), 2000);
-                }
+        if (pass) {
+            updateTestStatus("bluetooth", STATUS_SUCCESS);
+            if (hasTargetAddr) {
+                int addrRssi = bluetoothAddressRssiMap.value(expectAddr, -128);
+                ui->statusbar->showMessage(QString("蓝牙测试通过:目标%1,RSSI=%2dBm").arg(expectAddr).arg(addrRssi), 3000);
+            } else if (hasRssiThresh) {
+                ui->statusbar->showMessage(QString("蓝牙测试通过:最大RSSI=%1dBm").arg(bluetoothMaxRssi), 3000);
+            } else {
+                ui->statusbar->showMessage(QString("蓝牙测试通过:检测到%1个设备,最大RSSI=%2dBm").arg(bluetoothAddresses.size()).arg(bluetoothMaxRssi), 3000);
             }
-        } else {
-            // 3设备模式
-            if (!bluetoothAddresses.contains(address)) {
-                bluetoothAddresses.insert(address);
-                qDebug() << "检测到新的蓝牙地址:" << address << ",当前已检测到" << bluetoothAddresses.size() << "个不同地址";
-                ui->recvTextEdit->append(QString(">>> 新蓝牙设备: MAC=%1, RSSI=%2 dBm (已检测到 %3 个不同设备)").arg(address).arg(rssi).arg(bluetoothAddresses.size()));
-                ui->statusbar->showMessage(QString("蓝牙测试:已检测到 %1 个不同地址").arg(bluetoothAddresses.size()), 2000);
-                
-                // 检查是否达到3个及以上不同的蓝牙地址
-                if (bluetoothAddresses.size() >= 3) {
-                    qDebug() << "蓝牙测试通过:已检测到" << bluetoothAddresses.size() << "个不同的蓝牙地址";
-                    ui->recvTextEdit->append(QString(">>> 蓝牙测试通过: 已检测到 %1 个不同设备 (要求>=3)").arg(bluetoothAddresses.size()));
-                    ui->statusbar->showMessage(QString("蓝牙测试通过:检测到 %1 个不同地址").arg(bluetoothAddresses.size()), 3000);
-                    updateTestStatus("bluetooth", STATUS_SUCCESS);
-                    
-                    bluetoothTestFinished = true;
-                    
-                    // 停止10秒定时器
-                    if (bluetoothTestTimer->isActive()) {
-                        bluetoothTestTimer->stop();
-                    }
-                    
-                    // 发送结束蓝牙扫描指令
-                    QString bluetoothEndCmd = "AT^BTSCANTEST=2\r\n";
-                    ui->recvTextEdit->append(">>> 发送指令: AT^BTSCANTEST=2 (结束蓝牙扫描)");
-                    serial->write(bluetoothEndCmd.toUtf8());
-                    qDebug() << "发送蓝牙测试结束指令:" << bluetoothEndCmd.trimmed();
-                    
-                    // 延迟200ms后发送蓝牙禁用指令
-                    QTimer::singleShot(200, this, [this]() {
+            bluetoothTestFinished = true;
+            if (bluetoothTestTimer->isActive()) {
+                bluetoothTestTimer->stop();
+            }
+
+            QString bluetoothEndCmd = "AT^BTSCANTEST=2\r\n";
+            ui->recvTextEdit->append(">>> 发送指令: AT^BTSCANTEST=2 (结束蓝牙扫描)");
+            serial->write(bluetoothEndCmd.toUtf8());
+
+            QTimer::singleShot(200, this, [this]() {
+                if (isProductionTestRunning && currentTestStep == TEST_BLUETOOTH) {
+                    ui->recvTextEdit->append(">>> 发送指令: AT^BTDISABLE (禁用蓝牙)");
+                    serial->write("AT^BTDISABLE\r\n");
+                    QTimer::singleShot(300, this, [this]() {
                         if (isProductionTestRunning && currentTestStep == TEST_BLUETOOTH) {
-                            ui->recvTextEdit->append(">>> 发送指令: AT^BTDISABLE (禁用蓝牙)");
-                            serial->write("AT^BTDISABLE\r\n");
-                            qDebug() << "发送蓝牙禁用指令: AT^BTDISABLE";
-                            
-                            // 继续后续测试
-                            QTimer::singleShot(300, this, [this]() {
-                                if (isProductionTestRunning && currentTestStep == TEST_BLUETOOTH) {
-                                    skipToNextEnabledTest();
-                                }
-                            });
+                            skipToNextEnabledTest();
                         }
                     });
                 }
-            } else {
-                qDebug() << "检测到重复的蓝牙地址:" << address << ",已存在,跳过";
-                // 即使是重复地址,也打印到UI(但标记为重复)
-                ui->recvTextEdit->append(QString(">>> 重复蓝牙设备: MAC=%1, RSSI=%2 dBm (已存在)").arg(address).arg(rssi));
-            }
+            });
         }
     }
 }
@@ -4871,34 +3760,29 @@ void MainWindow::onBluetoothTestTimer()
     }
     
     if (bluetoothTestFinished) {
-        qDebug() << "蓝牙测试已在10秒内完成,不再处理超时";
-            return;
-        }
+        qDebug() << "蓝牙测试已在30秒窗口内完成,不再处理超时";
+        return;
+    }
     
-    // 检查是否使用指定地址+RSSI模式
-    QString targetAddress = ui->bluetoothAddressLineEdit->text().trimmed().toUpper();
-    QString rssiThresholdStr = ui->bluetoothRSSILineEdit->text().trimmed();
-    
-    if (!targetAddress.isEmpty() && !rssiThresholdStr.isEmpty()) {
-        // 指定地址+RSSI模式超时
-        bool ok;
-        int rssiThreshold = rssiThresholdStr.toInt(&ok);
-        if (ok) {
-            if (detectedBluetoothAddress.isEmpty()) {
-                qDebug() << "蓝牙测试10秒超时,未检测到指定地址" << targetAddress;
-                ui->statusbar->showMessage(QString("蓝牙测试失败:10秒内未检测到指定地址 %1").arg(targetAddress), 3000);
-            } else {
-                qDebug() << "蓝牙测试10秒超时,检测到指定地址" << detectedBluetoothAddress << "但RSSI" << detectedBluetoothRSSI << "小于阈值" << rssiThreshold;
-                ui->statusbar->showMessage(QString("蓝牙测试失败:检测到地址 %1,但RSSI %2 小于阈值 %3").arg(detectedBluetoothAddress).arg(detectedBluetoothRSSI).arg(rssiThreshold), 3000);
-            }
+    QString expectAddr = configManager.getTestConfig("bluetoothAddress", "").trimmed().toUpper();
+    QString expectRssiStr = configManager.getTestConfig("bluetoothRSSI", "").trimmed();
+    bool hasTargetAddr = !expectAddr.isEmpty();
+    bool hasRssiThresh = !expectRssiStr.isEmpty();
+    int expectRssi = hasRssiThresh ? expectRssiStr.toInt() : 0;
+    if (hasTargetAddr && hasRssiThresh) {
+        if (!bluetoothAddresses.contains(expectAddr)) {
+            ui->statusbar->showMessage(QString("蓝牙测试失败:未扫到目标 %1").arg(expectAddr), 3000);
         } else {
-            qDebug() << "蓝牙测试10秒超时,RSSI阈值格式错误";
-            ui->statusbar->showMessage("蓝牙测试失败:RSSI阈值格式错误", 3000);
+            int addrRssi = bluetoothAddressRssiMap.value(expectAddr, -128);
+            ui->statusbar->showMessage(QString("蓝牙测试失败:目标%1 RSSI=%2dBm,需>%3dBm").arg(expectAddr).arg(addrRssi).arg(expectRssi), 3000);
         }
+    } else if (hasTargetAddr) {
+        ui->statusbar->showMessage(QString("蓝牙测试失败:未扫到目标设备 %1").arg(expectAddr), 3000);
+    } else if (hasRssiThresh) {
+        ui->statusbar->showMessage(QString("蓝牙测试失败:最大RSSI=%1dBm,需>%2dBm").arg(bluetoothMaxRssi).arg(expectRssi), 3000);
     } else {
-        // 3设备模式超时
-        qDebug() << "蓝牙测试10秒超时,仅检测到" << bluetoothAddresses.size() << "个不同地址(需要3个及以上),测试失败";
-        ui->statusbar->showMessage(QString("蓝牙测试失败:10秒内仅检测到 %1 个不同地址(需要3个及以上)").arg(bluetoothAddresses.size()), 3000);
+        qDebug() << "蓝牙测试30秒超时,检测到" << bluetoothAddresses.size() << "个设备,最大RSSI=" << bluetoothMaxRssi << "(需要>=3且至少一个RSSI>=-90)";
+        ui->statusbar->showMessage(QString("蓝牙测试失败:检测到%1个设备,最大RSSI=%2dBm(需要>=3且至少一个≥-90dBm)").arg(bluetoothAddresses.size()).arg(bluetoothMaxRssi), 3000);
     }
     
     updateTestStatus("bluetooth", STATUS_FAILED);
@@ -4928,496 +3812,214 @@ void MainWindow::onBluetoothTestTimer()
     });
 }
 
-// æ£\80æ\9f¥CAT1ä¿¡å\8f·æµ\8bè¯\95ç»\93æ\9e\9c
-void MainWindow::checkCat1SignalResult(const QString& data)
+// æ\89§è¡\8cSEè\8a¯ç\89\87æµ\8bè¯\95
+void MainWindow::performSeChipTest()
 {
-    if (!isProductionTestRunning || !hasReceivedFirstSignal) {
+    if (!isTestItemEnabled("sechip")) {
+        qDebug() << "SE芯片测试未启用,跳过";
+        updateTestStatus("sechip", STATUS_UNTESTED);
+        skipToNextEnabledTest();
         return;
     }
-    
-    qDebug() << "CAT1信号测试收到数据:" << data.trimmed();
-    qDebug() << "当前MODEM版本:" << currentModemVersion << ",类型判断:" << (isEGModem() ? "EG系列" : "非EG系列");
-    
-    // 只在CAT1测试期间统计信号值
-    if (cat1SignalReceivedCount >= 5) {
-        qDebug() << "CAT1测试已接收5次响应,跳过统计";
+
+    if (!isProductionTestRunning || currentTestStep != TEST_SECHIP) {
         return;
     }
-    
-    // 格式过滤已在调用前完成,这里不需要再次过滤
-    
-    // 解析信号值,可能的格式:signal:31 或 CAT1GETSIGNAL OK signal:31
-    QRegExp signalRegex("signal:(\\d+)");
-    if (signalRegex.indexIn(data) != -1) {
-        int signalValue = signalRegex.cap(1).toInt();
-        
-        // 改进的重复检查:基于信号值和时间戳的组合,避免相同信号值被误判为重复
-        // 只有在短时间内收到完全相同的数据包才认为是重复
-        static QString lastProcessedData;
-        static qint64 lastProcessedTime = 0;
-        qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
-        
-        // 如果是相同的数据且时间间隔小于100ms,则认为是重复数据
-        if (data.trimmed() == lastProcessedData && (currentTime - lastProcessedTime) < 100) {
-            qDebug() << "跳过重复的CAT1数据(时间间隔:" << (currentTime - lastProcessedTime) << "ms)";
-            return;
-        }
-        
-        // 更新最后处理的数据和时间
-        lastProcessedData = data.trimmed();
-        lastProcessedTime = currentTime;
-        
-        // 增加接收计数器
-        cat1SignalReceivedCount++;
-        
-        qDebug() << "CAT1信号测试解析到signal值:" << signalValue << ",接收次数:" << cat1SignalReceivedCount << "/5";
-        
-        // 将CAT1 signal值显示到界面
-        appendToLogFile(QString("CAT1 Signal值: %1 (%2/5)\n").arg(signalValue).arg(cat1SignalReceivedCount));
-        
-        // 统计信号值(用于计算平均值)
-        if (signalValue == 99) {
-            cat1Signal99Count++;
-            // signal=99不计入平均值计算
-            qDebug() << "CAT1信号值为99,无效信号次数:" << cat1Signal99Count << "/" << cat1SignalReceivedCount;
-            ui->statusbar->showMessage(QString("CAT1信号测试 (%1/5) 信号值:99 (无效信号)").arg(cat1SignalReceivedCount), 2000);
-            } else {
-            // 累加有效信号值用于计算平均值
-            cat1SignalTotalValue += signalValue;
-            if (signalValue > 25) {
-                cat1SignalGoodCount++;
-            }
-            
-            // 计算当前平均值(排除signal=99的值)
-            int validCount = cat1SignalReceivedCount - cat1Signal99Count;
-            double averageSignal = validCount > 0 ? (double)cat1SignalTotalValue / validCount : 0;
-            
-            qDebug() << "CAT1信号值" << signalValue << ",累计总和:" << cat1SignalTotalValue << ",当前平均值:" << averageSignal << ",有效次数:" << validCount << "/" << cat1SignalReceivedCount;
-            ui->statusbar->showMessage(QString("CAT1信号测试 (%1/5) 信号值:%2 平均值:%3").arg(cat1SignalReceivedCount).arg(signalValue).arg(averageSignal, 0, 'f', 1), 2000);
-            }
-        } else {
-        qDebug() << "CAT1信号测试未能解析signal值,数据:" << data.trimmed();
-    }
-}
 
-// 执行4G信号质量测试
-void MainWindow::perform4GSignalTest()
-{
-    if (!isProductionTestRunning || currentTestStep != TEST_PPP_SIGNAL) {
-        return;
+    updateTestStatus("sechip", STATUS_TESTING);
+    appendToLogFile("\n========== 开始SE芯片测试 ==========\n");
+    appendToLogFile(">>> 发送指令: AT^GETCYWEEACTIVE\n");
+    ui->statusbar->showMessage("SE芯片测试中,等待cywee active响应...", 3000);
+
+    if (serial && serial->isOpen()) {
+        serial->write("AT^GETCYWEEACTIVE\r\n");
     }
 
-    qDebug() << "开始4G信号质量测试(统一使用CAT1GETSIGNAL)";
-    ui->recvTextEdit->append("========== 开始4G信号质量测试 ==========");
-    ui->statusbar->showMessage("4G信号质量测试中...", 5000);
-    
-    // 统一使用CAT1GETSIGNAL测试方式,不再区分MODEM类型,不再使用PPP相关指令
-    // 初始化CAT1信号测试计数器
-    cat1SignalTestCount = 0;
-    cat1SignalReceivedCount = 0;
-    cat1SignalGoodCount = 0;
-    cat1Signal99Count = 0;
-    cat1SignalTotalValue = 0;
-    hasReceivedFirstSignal = true; // 标记为已启动,直接开始CAT1测试
-    
-    // 启动CAT1信号测试定时器,每2秒发送一次
-    cat1SignalTimer->start(2000);
-    ui->statusbar->showMessage("开始CAT1信号测试,每2秒检测一次...", 3000);
-    
-    // 立即发送第一次CAT1信号检测指令
-    serial->write("AT^CAT1GETSIGNAL\r\n");
-    cat1SignalTestCount++;
-    qDebug() << "发送第" << cat1SignalTestCount << "次CAT1信号检测指令: AT^CAT1GETSIGNAL";
-    ui->recvTextEdit->append(">>> 发送指令: AT^CAT1GETSIGNAL");
+    testTimeoutTimer->start(10000);
 }
 
-// 4G信号检测定时器槽函数
-void MainWindow::on4GSignalTimer()
+// 检查SE芯片测试结果
+void MainWindow::checkSeChipTestResult(const QString& data)
 {
-    if (!isProductionTestRunning || currentTestStep != TEST_PPP_SIGNAL) {
-        signal4GTimer->stop();
+    if (!isProductionTestRunning || currentTestStep != TEST_SECHIP) {
         return;
     }
+
+    // 打印SE芯片原始响应到产测日志(appendToLogFile会同时写入recvTextEdit、logtextEdit和testLogFile)
+    appendToLogFile(QString(">>> SE芯片原始响应: %1\n").arg(data.trimmed()));
     
-    if (isEGModem()) {
-        // EG系列MODEM:超时处理,如果长时间没有收到CAT1信号数据则失败
-        signal4GTimer->stop();
-        qDebug() << "EG系列MODEM等待CAT1信号数据超时,测试失败";
-        ui->statusbar->showMessage("等待CAT1信号数据超时,测试失败", 5000);
-        
-        // 显示4G重复测试弹窗
-        if (isProductionTestRunning) {
-            show4GRetryDialog("等待CAT1信号数据超时");
-        } else {
-            updateTestStatus("ppp", STATUS_FAILED);
+    if (data.contains("cywee is active !!!", Qt::CaseInsensitive)) {
+        if (testTimeoutTimer->isActive()) {
+            testTimeoutTimer->stop();
         }
-    } else {
-        // 非EG系列MODEM:统一使用CAT1GETSIGNAL,不再使用PPPSIGNALINFO
-        // 这个分支理论上不会执行到,因为perform4GSignalTest已经统一使用CAT1GETSIGNAL
-        signal4GTimer->stop();
-        qDebug() << "4G信号检测超时,测试失败";
-        ui->statusbar->showMessage("4G信号检测超时,测试失败", 5000);
-        
-        // 显示4G重复测试弹窗
-        if (isProductionTestRunning) {
-            show4GRetryDialog("4G信号检测超时");
-        } else {
-            updateTestStatus("ppp", STATUS_FAILED);
+        updateTestStatus("sechip", STATUS_SUCCESS);
+        ui->statusbar->showMessage("SE芯片测试通过(cywee is active)", 2500);
+        appendToLogFile(">>> SE芯片: cywee is active !!! (通过)\n");
+        skipToNextEnabledTest();
+    } else if (data.contains("cywee is not active !!!", Qt::CaseInsensitive)) {
+        if (testTimeoutTimer->isActive()) {
+            testTimeoutTimer->stop();
         }
+        updateTestStatus("sechip", STATUS_FAILED);
+        ui->statusbar->showMessage("SE芯片测试失败(cywee is not active)", 2500);
+        appendToLogFile(">>> SE芯片: cywee is not active !!! (失败)\n");
+        skipToNextEnabledTest();
     }
 }
 
-// CAT1信号测试定时器槽函数
-void MainWindow::onCat1SignalTimer()
+// 执行GPS测试
+void MainWindow::performGpsTest()
 {
-    if (!isProductionTestRunning || !hasReceivedFirstSignal) {
-        cat1SignalTimer->stop();
-        return;
-    }
-    
-    // 检查是否已经接收到5次响应
-    if (cat1SignalReceivedCount >= 5) {
-        cat1SignalTimer->stop();
-        
-        // 计算平均信号值并判断测试结果
-        int validCount = cat1SignalReceivedCount - cat1Signal99Count;
-        double averageSignal = validCount > 0 ? (double)cat1SignalTotalValue / validCount : 0;
-        
-        qDebug() << "CAT1信号测试完成,统计结果:";
-        qDebug() << "  发送指令次数:" << cat1SignalTestCount;
-        qDebug() << "  接收响应次数:" << cat1SignalReceivedCount;
-        qDebug() << "  有效信号次数:" << validCount;
-        qDebug() << "  无效信号(99)次数:" << cat1Signal99Count;
-        qDebug() << "  信号值总和:" << cat1SignalTotalValue;
-        qDebug() << "  平均信号值:" << averageSignal;
-        
-        if (cat1Signal99Count >= 5) {
-            // 连续5次都是99,判定为失败
-            qDebug() << "CAT1信号测试失败,5次测试均为无效信号值99";
-            ui->statusbar->showMessage("CAT1信号测试失败 - 连续5次无效信号值(99)", 3000);
-            
-            // 停止CAT1定时器
-            cat1SignalTimer->stop();
-            
-            // 显示4G重复测试弹窗
-            if (isProductionTestRunning) {
-                show4GRetryDialog("连续5次无效信号值(99)");
-            } else {
-                updateTestStatus("ppp", STATUS_FAILED);
-            }
-        } else if (validCount > 0 && averageSignal >= 25.0) {
-            // 平均信号值大于等于25,判定为通过
-            qDebug() << "CAT1信号测试通过,平均信号值" << averageSignal << "大于等于25";
-            ui->statusbar->showMessage(QString("CAT1信号测试通过 - 平均信号值:%1").arg(averageSignal, 0, 'f', 1), 3000);
-            
-            // CAT1信号测试通过后,进行Ping测试
-            if (isProductionTestRunning) {
-                qDebug() << "CAT1信号测试通过,开始Ping测试";
-                currentTestStep = TEST_PPP_PING;
-                performPingTest();
-            } else {
-                updateTestStatus("ppp", STATUS_SUCCESS);
-            }
-    } else {
-            // 平均信号值小于25或没有有效数据,判定为失败
-            QString failReason;
-            if (validCount == 0) {
-                failReason = "没有有效信号数据";
-            } else {
-                failReason = QString("平均信号值%1小于25").arg(averageSignal, 0, 'f', 1);
-            }
-            
-            qDebug() << "CAT1信号测试失败," << failReason;
-            ui->statusbar->showMessage(QString("CAT1信号测试失败 - %1").arg(failReason), 3000);
-            
-            // 停止CAT1定时器
-            cat1SignalTimer->stop();
-            
-            // 显示4G重复测试弹窗
-            if (isProductionTestRunning) {
-                show4GRetryDialog(failReason);
-            } else {
-                updateTestStatus("ppp", STATUS_FAILED);
-            }
-        }
+    if (!isTestItemEnabled("gps")) {
+        qDebug() << "GPS测试未启用,跳过";
+        updateTestStatus("gps", STATUS_UNTESTED);
+        skipToNextEnabledTest();
         return;
     }
-    
-    // 统一使用CAT1GETSIGNAL指令
-    serial->write("AT^CAT1GETSIGNAL\r\n");
-    cat1SignalTestCount++;
-    qDebug() << "发送第" << cat1SignalTestCount << "次CAT1信号检测指令: AT^CAT1GETSIGNAL";
-}
 
-// Ping测试函数
-void MainWindow::performPingTest()
-{
-    if (!isProductionTestRunning || currentTestStep != TEST_PPP_PING) {
-        qDebug() << "Ping测试跳过 - 测试状态不正确";
+    if (!isProductionTestRunning || currentTestStep != TEST_GPS) {
         return;
     }
-    
-    qDebug() << "========== 开始Ping测试 ==========";
-    ui->recvTextEdit->append("========== 开始Ping测试 ==========");
-    ui->statusbar->showMessage("Ping测试中 - 检查网络连通性...", 3000);
-    
-    // 重置Ping测试状态
-    pingTestFinished = false;
-    pingSuccessCount = 0;
-    
-    // 从配置管理器获取Ping指令
-    QString pingCmd = configManager.getPingCommand();
-    
-    if (pingCmd.isEmpty()) {
-        qDebug() << "Ping指令未配置,使用默认指令";
-        pingCmd = "AT^PING=www.163.com";
-    }
-    
-    // 确保指令以\r\n结尾
-    if (!pingCmd.endsWith("\r\n")) {
-        pingCmd += "\r\n";
-    }
-    
-    // 发送Ping指令
-    serial->write(pingCmd.toUtf8());
-    qDebug() << "发送Ping指令:" << pingCmd.trimmed();
-    appendToLogFile(QString("发送Ping指令: %1\n").arg(pingCmd.trimmed()));
-    
-    // 启动超时定时器(30秒超时)
-    pingTestTimer->start(30000);
+
+    updateTestStatus("gps", STATUS_TESTING);
+    appendToLogFile("\n========== 开始GPS测试 ==========\n");
+    ui->statusbar->showMessage("GPS测试中:10秒内应收到信息,30秒内 trace 卫星数≥3 通过", 5000);
+
+    gpsTestFinished = false;
+    gpsInfoSeenIn10Sec = false;
+    gpsMaxNumSats = 0;
+    gpsMaxTrace = 0;
+    gpsMaxCn = 0;
+
+    // 1. 先切换到GPS测试界面
+    ui->recvTextEdit->append(">>> 发送指令: AT^ENTERTESTVIEW (进入产测界面)");
+    serial->write("AT^ENTERTESTVIEW\r\n");
+    QTimer::singleShot(300, this, [this]() {
+        if (!isProductionTestRunning || currentTestStep != TEST_GPS) return;
+        ui->recvTextEdit->append(">>> 发送指令: AT^TESTJUMP=GPS (切换到GPS测试界面)");
+        serial->write("AT^TESTJUMP=GPS\r\n");
+        QTimer::singleShot(500, this, [this]() {
+            if (!isProductionTestRunning || currentTestStep != TEST_GPS) return;
+            // 2. 开启GPS
+            ui->recvTextEdit->append(">>> 发送指令: AT^GPS=1 (开启GPS)");
+            serial->write("AT^GPS=1\r\n");
+            QTimer::singleShot(500, this, [this]() {
+                if (!isProductionTestRunning || currentTestStep != TEST_GPS) return;
+                // 3. 开始轮询AT^GPSINFO,每2秒发一次
+                ui->recvTextEdit->append(">>> 发送指令: AT^GPSINFO (获取GPS信息)");
+                serial->write("AT^GPSINFO\r\n");
+                gpsPollTimer->start(2000);
+                gpsTestTimer->start(30000);
+            });
+        });
+    });
 }
 
-// 检查Ping测试结果
-void MainWindow::checkPingTestResult(const QString& data)
+// 检查GPS测试结果
+void MainWindow::checkGpsTestResult(const QString& data)
 {
-    if (!isProductionTestRunning || currentTestStep != TEST_PPP_PING || pingTestFinished) {
+    if (!isProductionTestRunning || currentTestStep != TEST_GPS || gpsTestFinished) {
         return;
-    }
-    
-    qDebug() << "Ping测试收到数据:" << data.trimmed();
-    
-    // 检测Ping成功的关键字
-    // 可能的成功响应格式:
-    // "PING OK" 或 "ping success" 或包含 "bytes from" 等
-    if (data.contains("PING OK", Qt::CaseInsensitive) || 
-        data.contains("ping success", Qt::CaseInsensitive) ||
-        data.contains("bytes from", Qt::CaseInsensitive) ||
-        data.contains("Reply from", Qt::CaseInsensitive)) {
-        
-        pingSuccessCount++;
-        qDebug() << "检测到Ping成功响应,成功次数:" << pingSuccessCount;
-        appendToLogFile(QString("Ping成功响应: %1\n").arg(data.trimmed()));
-        
-        // 只要检测到一次成功就认为网络连通
-        if (pingSuccessCount >= 1) {
-            pingTestFinished = true;
-            pingTestTimer->stop();
-            
-            qDebug() << "Ping测试通过 - 网络连通";
-            ui->statusbar->showMessage("Ping测试通过 - 网络连通", 3000);
-            ui->recvTextEdit->append("========== Ping测试通过 ==========");
-            appendToLogFile("========== Ping测试通过 ==========\n");
-            
-            // 4G测试完全通过
-            updateTestStatus("ppp", STATUS_SUCCESS);
-            
-            // 完成产测
-            if (isProductionTestRunning) {
-                currentTestStep = TEST_COMPLETED;
-                isProductionTestRunning = false;
-                qDebug() << "Ping测试通过,产测流程完成";
-                
-                // 保存测试记录
-                QString fileName = saveTestRecord();
-                
-                // 显示最终测试结果弹窗
-                showFinalTestResult();
-            }
+    }
+
+    if (!gpsInfoSeenIn10Sec) {
+        gpsInfoSeenIn10Sec = true;  // 收到第一条信息即满足10秒内看信息
+    }
+
+    // 打印AT^GPSINFO原始响应到产测日志
+    appendToLogFile(QString(">>> GPS原始响应: %1\n").arg(data.trimmed()));
+
+    // 解析 GPSINFO: stat=1, num_sats=0, valid_star=0, vga=2, quality=300000.00, last_ms=...
+    QRegExp numSatsPattern("num_sats\\s*=\\s*(\\d+)", Qt::CaseInsensitive);
+    if (numSatsPattern.indexIn(data) != -1) {
+        int numSats = numSatsPattern.cap(1).toInt();
+        if (numSats > gpsMaxNumSats) {
+            gpsMaxNumSats = numSats;
         }
+        appendToLogFile(QString(">>> GPS信息: num_sats=%1\n").arg(numSats));
     }
-    // 检测Ping失败的关键字
-    else if (data.contains("PING FAIL", Qt::CaseInsensitive) || 
-             data.contains("ping failed", Qt::CaseInsensitive) ||
-             data.contains("timeout", Qt::CaseInsensitive) ||
-             data.contains("unreachable", Qt::CaseInsensitive)) {
-        
-        pingTestFinished = true;
-        pingTestTimer->stop();
-        
-        qDebug() << "Ping测试失败 - 网络不通";
-        ui->statusbar->showMessage("Ping测试失败 - 网络不通", 3000);
-        ui->recvTextEdit->append("========== Ping测试失败 ==========");
-        appendToLogFile(QString("Ping失败响应: %1\n").arg(data.trimmed()));
-        appendToLogFile("========== Ping测试失败 ==========\n");
-        
-        // 显示4G重试对话框
-        if (isProductionTestRunning) {
-            show4GRetryDialog("Ping测试失败 - 网络不通");
-        } else {
-            updateTestStatus("ppp", STATUS_FAILED);
+
+    // 卫星数判定以 trace 为准(如 trace=3、trace:3、trace 3)
+    QRegExp tracePattern("trace\\s*[=:]?\\s*(\\d+)", Qt::CaseInsensitive);
+    if (tracePattern.indexIn(data) != -1) {
+        int tr = tracePattern.cap(1).toInt();
+        if (tr > gpsMaxTrace) {
+            gpsMaxTrace = tr;
+        }
+        appendToLogFile(QString(">>> GPS信息: trace=%1\n").arg(tr));
+    }
+
+    // 解析CN值(可能格式:cn=30 或 CN:30 或类似)
+    QRegExp cnPattern("cn\\s*[=:]\\s*(\\d+)", Qt::CaseInsensitive);
+    if (cnPattern.indexIn(data) != -1) {
+        int cn = cnPattern.cap(1).toInt();
+        if (cn > gpsMaxCn) {
+            gpsMaxCn = cn;
         }
     }
+
+    // 实时判断:trace 卫星数≥3 即通过
+    if (gpsInfoSeenIn10Sec && gpsMaxTrace >= 3) {
+        gpsTestTimer->stop();
+        gpsPollTimer->stop();
+        gpsTestFinished = true;
+        updateTestStatus("gps", STATUS_SUCCESS);
+        ui->statusbar->showMessage(QString("GPS测试通过:trace卫星数=%1").arg(gpsMaxTrace), 3000);
+        ui->recvTextEdit->append(QString(">>> GPS测试通过:trace=%1 (num_sats记录=%2, CNmax=%3)")
+                                       .arg(gpsMaxTrace).arg(gpsMaxNumSats).arg(gpsMaxCn));
+        serial->write("AT^GPS=0\r\n");
+        ui->recvTextEdit->append(">>> 发送指令: AT^GPS=0 (关闭GPS)");
+        skipToNextEnabledTest();
+    }
 }
 
-// Ping测试超时处理
-void MainWindow::onPingTestTimeout()
+// GPS轮询:每2秒发送AT^GPSINFO
+void MainWindow::onGpsPollTimer()
 {
-    pingTestTimer->stop();
-    
-    if (!isProductionTestRunning || currentTestStep != TEST_PPP_PING || pingTestFinished) {
-        qDebug() << "Ping超时但测试状态已改变,忽略";
+    if (!isProductionTestRunning || currentTestStep != TEST_GPS || gpsTestFinished) {
+        gpsPollTimer->stop();
         return;
     }
-    
-    pingTestFinished = true;
-    
-    qDebug() << "Ping测试超时 - 30秒内未收到响应";
-    ui->statusbar->showMessage("Ping测试超时 - 网络连通性检查失败", 3000);
-    ui->recvTextEdit->append("========== Ping测试超时 ==========");
-    appendToLogFile("========== Ping测试超时 - 30秒内未收到响应 ==========\n");
-    
-    // 显示4G重试对话框
-    if (isProductionTestRunning) {
-        show4GRetryDialog("Ping测试超时");
-    } else {
-        updateTestStatus("ppp", STATUS_FAILED);
+    if (serial && serial->isOpen()) {
+        serial->write("AT^GPSINFO\r\n");
     }
 }
 
-// 检查4G信号质量结果
-void MainWindow::check4GSignalResult(const QString& data)
+// GPS测试30秒超时处理
+void MainWindow::onGpsTestTimer()
 {
-    if (!isProductionTestRunning || currentTestStep != TEST_PPP_SIGNAL) {
+    gpsTestTimer->stop();
+    gpsPollTimer->stop();
+
+    if (!isProductionTestRunning || currentTestStep != TEST_GPS) {
         return;
     }
-    
-    // 如果已经启动了CAT1信号测试,则不再使用旧的4G信号测试逻辑
-    if (hasReceivedFirstSignal) {
-        qDebug() << "已启动CAT1信号测试,跳过旧的4G信号测试逻辑";
+
+    if (gpsTestFinished) {
         return;
     }
-    
-    qDebug() << "4G信号检测收到数据:" << data.trimmed();
-    
-    // 保存静态变量来跟踪RSRQ和RSRP值
-    static double currentRsrq = -6.8;   // 默认示例值
-    static double currentRsrp = -82;    // 默认示例值
-    static int rsrqRaw = 26;           // 原始RSRQ值
-    static int rsrpRaw = 58;           // 原始RSRP值
-    static bool rsrqReceived = false;
-    static bool rsrpReceived = false;
-    
-    // 解析APP|[PPP] RSRQ格式
-    QRegExp appRsrqPattern("APP\\|\\[PPP\\]\\s*RSRQ:\\s*(\\d+)\\s*\\(([\\d\\.-]+)\\s*dB\\)");
-    if (appRsrqPattern.indexIn(data) != -1) {
-        rsrqRaw = appRsrqPattern.cap(1).toInt();
-        currentRsrq = appRsrqPattern.cap(2).toDouble();
-        rsrqReceived = true;
-        qDebug() << "解析到APP|[PPP] RSRQ值:" << rsrqRaw << "(" << currentRsrq << "dB)";
-        
-        // 输出RSRQ评级(按要求格式)
-        QString rsrqGrade = getRsrqGrade(currentRsrq);
-        QString rsrqOutput = QString("RSRQ: %1 (%2 dB:%3)").arg(rsrqRaw).arg(currentRsrq).arg(rsrqGrade);
-        qDebug() << rsrqOutput;
-        appendToLogFile(rsrqOutput + "\n");
-        return; // 每行单独处理
-    }
-    
-    // 解析APP|[PPP] RSRP格式
-    QRegExp appRsrpPattern("APP\\|\\[PPP\\]\\s*RSRP:\\s*(\\d+)\\s*\\(([\\d\\.-]+)\\s*dBm\\)");
-    if (appRsrpPattern.indexIn(data) != -1) {
-        rsrpRaw = appRsrpPattern.cap(1).toInt();
-        currentRsrp = appRsrpPattern.cap(2).toDouble();
-        rsrpReceived = true;
-        qDebug() << "解析到APP|[PPP] RSRP值:" << rsrpRaw << "(" << currentRsrp << "dBm)";
-        
-        // 输出RSRP评级(按要求格式)
-        QString rsrpGrade = getRsrpGrade(currentRsrp);
-        QString rsrpOutput = QString("RSRP: %1 (%2 dBm:%3)").arg(rsrpRaw).arg(currentRsrp).arg(rsrpGrade);
-        qDebug() << rsrpOutput;
-        appendToLogFile(rsrpOutput + "\n");
-        return; // 每行单独处理
-    }
-    
-    // 解析Overall LTE Quality格式
-    if (data.contains("APP|[PPP] Overall LTE Quality:") || data.contains("总体评级:")) {
-        // 收到一次有效的信号质量数据
-        signalReceived4GCount++;
-        qDebug() << "收到第" << signalReceived4GCount << "次有效4G信号数据";
-        
-        // 如果还没收到RSRQ和RSRP,使用模拟数据进行示例显示
-        if (!rsrqReceived) {
-            QString rsrqGrade = getRsrqGrade(currentRsrq);
-            QString rsrqOutput = QString("RSRQ: %1 (%2 dB:%3)").arg(rsrqRaw).arg(currentRsrq).arg(rsrqGrade);
-            qDebug() << "模拟RSRQ输出:" << rsrqOutput;
-            ui->recvTextEdit->insertPlainText(rsrqOutput + "\n");
-            ui->recvTextEdit->moveCursor(QTextCursor::End);
-        }
-        
-        if (!rsrpReceived) {
-            QString rsrpGrade = getRsrpGrade(currentRsrp);
-            QString rsrpOutput = QString("RSRP: %1 (%2 dBm:%3)").arg(rsrpRaw).arg(currentRsrp).arg(rsrpGrade);
-            qDebug() << "模拟RSRP输出:" << rsrpOutput;
-            ui->recvTextEdit->insertPlainText(rsrpOutput + "\n");
-            ui->recvTextEdit->moveCursor(QTextCursor::End);
-        }
-        
-        // 输出完整的信号评估
-        QString overallAssessment = getOverallSignalAssessment(currentRsrp, currentRsrq);
-        QString overallOutput = QString("总体评级:%1").arg(overallAssessment);
-        qDebug() << overallOutput;
-        appendToLogFile(overallOutput + "\n");
-        
-        // 检查信号质量是否良好以上
-        bool rsrpGood = data.contains("覆盖极好") || data.contains("覆盖良好") || 
-                        overallAssessment.contains("覆盖强度极好") || overallAssessment.contains("覆盖强度良好");
-        bool rsrqGood = data.contains("质量极佳") || data.contains("质量良好") || 
-                        overallAssessment.contains("信号质量极佳") || overallAssessment.contains("信号质量良好");
-        bool isSignalGood = rsrpGood && rsrqGood;
-        
-        qDebug() << "信号质量检查: RSRP良好以上=" << rsrpGood << ", RSRQ良好以上=" << rsrqGood << ", 综合判断=" << isSignalGood;
-        
-        if (data.contains("LTE信号良好") || isSignalGood) {
-            signalGood4GCount++;
-            qDebug() << "检测到LTE信号良好以上,当前良好次数:" << signalGood4GCount << "/5,已接收数据次数:" << signalReceived4GCount << "/5";
-            ui->statusbar->showMessage(QString("LTE信号良好 (%1/5) 数据(%2/5)").arg(signalGood4GCount).arg(signalReceived4GCount), 2000);
+
+    gpsTestFinished = true;
+
+    bool pass = gpsInfoSeenIn10Sec && gpsMaxTrace >= 3;
+    if (pass) {
+        updateTestStatus("gps", STATUS_SUCCESS);
+        ui->statusbar->showMessage(QString("GPS测试通过:trace卫星数=%1").arg(gpsMaxTrace), 3000);
+    } else {
+        updateTestStatus("gps", STATUS_FAILED);
+        QString reason;
+        if (!gpsInfoSeenIn10Sec) {
+            reason = "10秒内未收到GPS信息";
         } else {
-            qDebug() << "LTE信号质量不达标(需要良好以上),已接收数据次数:" << signalReceived4GCount << "/5";
-            ui->statusbar->showMessage(QString("LTE信号质量不达标 数据(%1/5)").arg(signalReceived4GCount), 2000);
-        }
-        
-        // 检查是否已收到5次数据
-        if (signalReceived4GCount >= 5) {
-            signal4GTimer->stop();
-            
-            if (signalGood4GCount >= 5) {
-                // 5次数据都是良好以上,测试通过
-                qDebug() << "5次4G信号数据都达到良好以上标准,测试通过";
-                ui->statusbar->showMessage("4G信号测试通过 - 5次信号都达到良好以上!", 5000);
-                updateTestStatus("ppp", STATUS_SUCCESS);
-            } else {
-                // 有信号质量不达标,测试失败
-                qDebug() << "4G信号测试失败 - 只有" << signalGood4GCount << "次达到良好以上标准";
-                ui->statusbar->showMessage(QString("4G信号测试失败 - 只有%1次达到良好标准").arg(signalGood4GCount), 5000);
-                updateTestStatus("ppp", STATUS_FAILED);
-            }
-            
-            // 完成产测
-            currentTestStep = TEST_COMPLETED;
-            isProductionTestRunning = false;
-            qDebug() << "产测流程完成";
-            
-            // 保存测试记录
-            QString fileName = saveTestRecord();
-            
-            // 显示最终测试结果弹窗
-            showFinalTestResult();
+            reason = QString("trace卫星数=%1(需要≥3)").arg(gpsMaxTrace);
         }
-        
-        // 重置状态标志,为下次检测准备
-        rsrqReceived = false;
-        rsrpReceived = false;
+        ui->statusbar->showMessage(QString("GPS测试失败:%1").arg(reason), 3000);
+        ui->recvTextEdit->append(QString(">>> GPS测试失败:%1").arg(reason));
     }
+
+    serial->write("AT^GPS=0\r\n");
+    ui->recvTextEdit->append(">>> 发送指令: AT^GPS=0 (关闭GPS)");
+    skipToNextEnabledTest();
 }
 
 // 获取RSRQ信号质量评级
@@ -5528,12 +4130,9 @@ void MainWindow::showBatteryChargeFailDialog()
     chargeFailDialog.exec();
     
     if (chargeFailDialog.clickedButton() == confirmButton) {
-        qDebug() << "用户确认电池充电失败,标记测试失败";
+        qDebug() << "用户确认电池充电失败,标记测试失败,推进到下一项测试";
         updateTestStatus("battery", STATUS_FAILED);
-        
-        // 继续下一个测试
         if (isProductionTestRunning && currentTestStep == TEST_BATTERY) {
-            qDebug() << "电池测试失败确认,继续下一个测试";
             skipToNextEnabledTest();
         }
     } else if (chargeFailDialog.clickedButton() == retryButton) {
@@ -5567,125 +4166,22 @@ void MainWindow::showBatteryChargeFailDialog()
     }
 }
 
-// 显示4G重复测试弹窗
-void MainWindow::show4GRetryDialog(const QString& failReason)
-{
-    QMessageBox retryDialog(this);
-    retryDialog.setWindowTitle("4G测试失败");
-    retryDialog.setText("📶 4G信号测试失败");
-    
-    // 构建详细的失败信息,包括SIM卡状态
-    QString detailedInfo = QString("测试失败原因:%1").arg(failReason);
-    
-    // 添加SIM卡状态信息
-    if (currentSimStatus == 0) {
-        detailedInfo += "\n\n⚠️ SIM卡状态:未插入";
-        detailedInfo += "\n请插入SIM卡后重新测试";
-    } else if (currentSimStatus == 1) {
-        detailedInfo += "\n\n✅ SIM卡状态:已插入";
-    } else {
-        detailedInfo += "\n\n❓ SIM卡状态:未知";
-    }
-    
-    detailedInfo += "\n\n是否重新进行4G测试?";
-    
-    retryDialog.setInformativeText(detailedInfo);
-    retryDialog.setIcon(QMessageBox::Warning);
-    
-    // 添加自定义按钮
-    QPushButton *retryButton = retryDialog.addButton("重新测试", QMessageBox::AcceptRole);
-    QPushButton *skipButton = retryDialog.addButton("跳过测试", QMessageBox::RejectRole);
-    
-    retryDialog.exec();
-    
-    if (retryDialog.clickedButton() == retryButton) {
-        qDebug() << "用户选择重新进行4G测试";
-        ui->statusbar->showMessage("重新开始4G测试...", 2000);
-        
-        // 重置4G测试状态
-        hasReceivedFirstSignal = false;
-        cat1SignalTestCount = 0;
-        cat1SignalReceivedCount = 0;
-        cat1SignalGoodCount = 0;
-        cat1Signal99Count = 0;
-        cat1SignalTotalValue = 0;
-        // 移除了cat1ProcessedSignals的清理,改用时间戳方式
-        
-        // 重置4G测试计数器,确保重新测试时从头开始
-        initRetry4GCount = 0;
-        signalTest4GCount = 0;
-        signalReceived4GCount = 0;
-        signalGood4GCount = 0;
-        pppCommandCount = 0;
-        hasReceivedSignalSim = false;
-        hasReceivedPppInfo = false;
-        currentSimStatus = -1; // 重置SIM卡状态为未知
-        
-        // 停止所有4G相关定时器
-        if (cat1SignalTimer->isActive()) {
-            cat1SignalTimer->stop();
-        }
-        if (signal4GTimer->isActive()) {
-            signal4GTimer->stop();
-        }
-        if (test4GTimer->isActive()) {
-            test4GTimer->stop();
-        }
-        
-        // 重新开始4G测试
-        updateTestStatus("ppp", STATUS_TESTING);
-        currentTestStep = TEST_PPP_INIT;
-        perform4GTest();
-        
-    } else if (retryDialog.clickedButton() == skipButton) {
-        qDebug() << "用户选择跳过4G测试,标记测试失败";
-        updateTestStatus("ppp", STATUS_FAILED);
-        
-        // 停止所有4G相关定时器,确保测试完全停止
-        if (cat1SignalTimer->isActive()) {
-            cat1SignalTimer->stop();
-            qDebug() << "停止CAT1信号测试定时器";
-        }
-        if (signal4GTimer->isActive()) {
-            signal4GTimer->stop();
-            qDebug() << "停止4G信号检测定时器";
-        }
-        if (test4GTimer->isActive()) {
-            test4GTimer->stop();
-            qDebug() << "停止4G测试超时定时器";
-        }
-        
-        // 重置4G测试相关状态
-        hasReceivedFirstSignal = false;
-        hasReceivedSignalSim = false;
-        hasReceivedPppInfo = false;
-        
-        // 完成产测(失败)
-        if (isProductionTestRunning) {
-            currentTestStep = TEST_COMPLETED;
-            isProductionTestRunning = false;
-            qDebug() << "用户跳过4G测试,产测流程完成";
-            
-            // 保存测试记录
-            QString fileName = saveTestRecord();
-            
-            // 显示最终测试结果弹窗
-            showFinalTestResult();
-        }
-    }
-}
-
 void MainWindow::showFinalTestResult()
 {
     // 获取各项测试结果
     QString lcdResult = ui->lcdStatusText->text();
+    QString tpResult = ui->tpStatusText->text();
+    QString brightResult = ui->brightStatusText->text();
     QString motorResult = ui->motorStatusText->text();
     QString keyResult = ui->keyStatusText->text();
+    QString deepsleepResult = ui->deepsleepStatusText->text();
     QString audioResult = ui->audioStatusText->text();
     QString batteryResult = ui->batteryStatusText->text();
     QString gsensorResult = ui->gsensorStatusText->text();
+    QString leaklightResult = ui->leaklightStatusText->text();
     QString bluetoothResult = ui->bluetoothStatusText->text();
-    QString pppResult = ui->pppStatusText->text();
+    QString sechipResult = ui->sechipStatusText->text();
+    QString gpsResult = ui->gpsStatusText->text();
     QString overallResult = ui->overallStatusText->text();
     
     // 创建结果弹窗
@@ -5696,16 +4192,33 @@ void MainWindow::showFinalTestResult()
     QString resultContent = QString(
         "===== 产测完成 =====\n\n"
         "📱 LCD测试:%1\n"
-        "🔧 马达测试:%2\n"
-        "⌨️ 按键测试:%3\n"
-        "🎤 录音测试:%4\n"
-        "🔋 电池测试:%5\n"
-        "📡 G-sensor测试:%6\n"
-        "📶 蓝牙测试:%7\n"
-        "🌐 4G测试:%8\n\n"
-        "📊 整体结果:%9"
-    ).arg(lcdResult, motorResult, keyResult, audioResult, 
-          batteryResult, gsensorResult, bluetoothResult, pppResult, overallResult);
+        "🖐️ 触控测试:%2\n"
+        "🌞 亮度测试:%3\n"
+        "🔧 马达测试:%4\n"
+        "⌨️ 按键测试:%5\n"
+        "⚡ 功耗测试:%6\n"
+        "🎤 录音测试:%7\n"
+        "🔋 电池测试:%8\n"
+        "📡 G-sensor测试:%9\n"
+        "❤️ 漏光测试:%10\n"
+        "📶 蓝牙测试:%11\n"
+        "🔒 SE芯片测试:%12\n"
+        "📍 GPS测试:%13\n"
+        "📊 整体结果:%14"
+    ).arg(lcdResult)
+     .arg(tpResult)
+     .arg(brightResult)
+     .arg(motorResult)
+     .arg(keyResult)
+     .arg(deepsleepResult)
+     .arg(audioResult)
+     .arg(batteryResult)
+     .arg(gsensorResult)
+     .arg(leaklightResult)
+     .arg(bluetoothResult)
+     .arg(sechipResult)
+     .arg(gpsResult)
+     .arg(overallResult);
     
     resultDialog.setText(resultContent);
     
@@ -5755,15 +4268,14 @@ void MainWindow::showFinalTestResult()
     qDebug() << "显示最终测试结果弹窗";
     resultDialog.exec();
     
-    // 用户点击确定后的处理
-    qDebug() << "用户确认测试结果,发送退出产测指令";
+    // 用户点击确定后的处理:关机,不执行重启/RESTORE
+    qDebug() << "用户确认测试结果,发送关机指令";
     
-    // 发送退出产测指令
     if (serial->isOpen()) {
-        ui->recvTextEdit->append(">>> 发送指令: AT^RESTORE (退出产测)");
-        serial->write("AT^RESTORE\r\n");
-        qDebug() << "发送退出产测指令: AT^RESTORE";
-        ui->statusbar->showMessage("已退出产测模式", 3000);
+        ui->recvTextEdit->append(">>> 发送指令: AT^POWEROFF (产测结束关机)");
+        serial->write("AT^POWEROFF\r\n");
+        qDebug() << "发送关机指令: AT^POWEROFF";
+        ui->statusbar->showMessage("已发送关机指令", 3000);
     }
     
     // 恢复产测按钮状态 - 变回蓝色并启用
@@ -5809,6 +4321,10 @@ void MainWindow::onTestTimeout()
             testName = "motor";
             testDescription = "马达测试";
             break;
+        case TEST_AUDIO:
+            testName = "mic";
+            testDescription = "录音测试";
+            break;
         case TEST_BATTERY:
             testName = "battery";
             testDescription = "电池测试";
@@ -5817,10 +4333,22 @@ void MainWindow::onTestTimeout()
             testName = "gsensor";
             testDescription = "G-sensor测试";
             break;
+        case TEST_LEAKLIGHT:
+            testName = "leaklight";
+            testDescription = "漏光测试";
+            break;
         case TEST_BLUETOOTH:
             testName = "bluetooth";
             testDescription = "蓝牙测试";
             break;
+        case TEST_SECHIP:
+            testName = "sechip";
+            testDescription = "SE芯片测试";
+            break;
+        case TEST_GPS:
+            testName = "gps";
+            testDescription = "GPS测试";
+            break;
         default:
             qDebug() << "未知的测试步骤超时:" << currentTestStep;
             return;
@@ -5891,15 +4419,27 @@ void MainWindow::onTestTimeout()
                 case TEST_MOTOR:
                     performMotorTest();
                     break;
+                case TEST_AUDIO:
+                    performAudioTest();
+                    break;
                 case TEST_BATTERY:
                     performBatteryTest();
                     break;
                 case TEST_GSENSOR:
                     performGsensorTest();
                     break;
+                case TEST_LEAKLIGHT:
+                    performLeaklightTest();
+                    break;
                 case TEST_BLUETOOTH:
                     performBluetoothTest();
                     break;
+                case TEST_SECHIP:
+                    performSeChipTest();
+                    break;
+                case TEST_GPS:
+                    performGpsTest();
+                    break;
                 default:
                     break;
             }
@@ -5946,51 +4486,41 @@ void MainWindow::clearProductionTestState()
         qDebug() << "停止马达测试定时器";
     }
     
-    if (test4GTimer->isActive()) {
-        test4GTimer->stop();
-        qDebug() << "停止4G测试定时器";
-    }
-    
-    if (signal4GTimer->isActive()) {
-        signal4GTimer->stop();
-        qDebug() << "停止4G信号检测定时器";
-    }
-    
-    if (cat1SignalTimer->isActive()) {
-        cat1SignalTimer->stop();
-        qDebug() << "停止CAT1信号测试定时器";
-    }
-    
     if (testTimeoutTimer->isActive()) {
         testTimeoutTimer->stop();
         qDebug() << "停止通用超时定时器";
     }
+
+    if (gSensorIntervalTimer->isActive()) {
+        gSensorIntervalTimer->stop();
+        qDebug() << "停止G-sensor采集定时器";
+    }
+    if (gpsTestTimer->isActive()) {
+        gpsTestTimer->stop();
+        qDebug() << "停止GPS测试定时器";
+    }
+    if (gpsPollTimer->isActive()) {
+        gpsPollTimer->stop();
+        qDebug() << "停止GPS轮询定时器";
+    }
+    
+    // 清空测试时间记录
+    testStartTimeMap.clear();
+    testDurationSecMap.clear();
     
     // 重置产测状态变量
     isProductionTestRunning = false;
     currentTestStep = TEST_IDLE;
     currentLcdColor = 0;
-    initRetry4GCount = 0;
-    signalTest4GCount = 0;
-    signalReceived4GCount = 0;
-    signalGood4GCount = 0;
-    hasReceivedFirstSignal = false;
-    cat1SignalTestCount = 0;
-    cat1SignalReceivedCount = 0;
-    cat1SignalGoodCount = 0;
-    cat1Signal99Count = 0;
-    cat1SignalTotalValue = 0;
-    // 移除了cat1ProcessedSignals的清理,改用时间戳方式
-    currentModemVersion = ""; // 初始化MODEM版本为空
-    currentSimStatus = -1; // 重置SIM卡状态为未知
     
     // 重置弹窗状态
     lcdTestPopupShown = false;
     motorTestPopupShown = false;
+    touchTestPopupShown = false;
+    brightTestPopupShown = false;
+    deepsleepTestPopupShown = false;
     isWaitingForUserConfirm = false;
     keyTestStarted = false;
-    audioTestPopupShown = false;
-    audioPlaybackPopupShown = false;
     
     // 重置按键测试状态
     for (int i = 0; i < 4; i++) {
@@ -6016,18 +4546,15 @@ void MainWindow::on_agingTestButton_clicked()
 
     // 清空设备信息
     ui->VerlineEdit->clear();            // 清空版本信息
-    ui->IMEIlineEdit->clear();           // 清空IMEI
-    ui->ICCIDlineEdit->clear();          // 清空ICCID
-    ui->ModemVerlineEdit->clear();       // 清空调制解调器版本
-    ui->PYVerlineEdit->clear();       // 清空python版本
 
     // 重置弹窗状态
     lcdTestPopupShown = false;
     motorTestPopupShown = false;
+    touchTestPopupShown = false;
+    brightTestPopupShown = false;
+    deepsleepTestPopupShown = false;
     isWaitingForUserConfirm = false;
     keyTestStarted = false;
-    audioTestPopupShown = false;
-    audioPlaybackPopupShown = false;
 
     // 不清除状态指示器,保留上次测试结果供查看
     // 用户可以一边查看测试结果,一边执行老化测试
@@ -6044,18 +4571,15 @@ void MainWindow::on_agingTestButton_2_clicked()
 
     // 清空设备信息
     ui->VerlineEdit->clear();            // 清空版本信息
-    ui->IMEIlineEdit->clear();           // 清空IMEI
-    ui->ICCIDlineEdit->clear();          // 清空ICCID
-    ui->ModemVerlineEdit->clear();       // 清空调制解调器版本
-    ui->PYVerlineEdit->clear();       // 清空python版本
 
     // 重置弹窗状态
     lcdTestPopupShown = false;
     motorTestPopupShown = false;
+    touchTestPopupShown = false;
+    brightTestPopupShown = false;
+    deepsleepTestPopupShown = false;
     isWaitingForUserConfirm = false;
     keyTestStarted = false;
-    audioTestPopupShown = false;
-    audioPlaybackPopupShown = false;
 
     // 不清除状态指示器,保留上次测试结果供查看
     // 用户可以一边查看测试结果,一边执行恢复出厂操作
@@ -6072,18 +4596,15 @@ void MainWindow::on_openButton_3_clicked()
 
     // 清空设备信息
     ui->VerlineEdit->clear();            // 清空版本信息
-    ui->IMEIlineEdit->clear();           // 清空IMEI
-    ui->ICCIDlineEdit->clear();          // 清空ICCID
-    ui->ModemVerlineEdit->clear();       // 清空调制解调器版本
-    ui->PYVerlineEdit->clear();       // 清空python版本
 
     // 重置弹窗状态
     lcdTestPopupShown = false;
     motorTestPopupShown = false;
+    touchTestPopupShown = false;
+    brightTestPopupShown = false;
+    deepsleepTestPopupShown = false;
     isWaitingForUserConfirm = false;
     keyTestStarted = false;
-    audioTestPopupShown = false;
-    audioPlaybackPopupShown = false;
 
     // 不清除状态指示器,保留上次测试结果供查看
 
@@ -6338,10 +4859,6 @@ void MainWindow::on_getVersionInfoButton_clicked()
     
     // 首先清除所有版本信息相关的LineEdit
     ui->VerlineEdit->clear();
-    ui->IMEIlineEdit->clear();
-    ui->ModemVerlineEdit->clear();
-    ui->PYVerlineEdit->clear();
-    ui->ICCIDlineEdit->clear();
     ui->factoryModeLineEdit->clear();
     ui->factoryModeLineEdit->setStyleSheet(""); // 清除样式
     
@@ -6395,31 +4912,39 @@ void MainWindow::saveTestConfigToSettings()
     QString portText = ui->portNameBox->currentText();
     configManager.setTestConfig("portName", portText);
     
-    // 保存音频测试参数
-    configManager.setTestConfig("audioVolume", ui->audioVolumeLineEdit->text());
-    if (ui->recordVolumelineEdit) {
-        configManager.setTestConfig("audioLevelStandard", ui->recordVolumelineEdit->text().trimmed());
-    }
-    
     // 保存期望版本信息
-    configManager.setTestConfig("expectedModemVer", ui->expectedModemVerLineEdit->text());
-    configManager.setTestConfig("expectedPYVer", ui->expectedPYVerLineEdit->text());
     configManager.setTestConfig("expectedVer", ui->expectedVerLineEdit->text());
     
     // 保存蓝牙测试参数
     configManager.setTestConfig("bluetoothAddress", ui->bluetoothAddressLineEdit->text());
     configManager.setTestConfig("bluetoothRSSI", ui->bluetoothRSSILineEdit->text());
     
+    // 保存录音测试参数
+    configManager.setTestConfig("audioVolume", ui->audioVolumeLineEdit->text());
+    configManager.setTestConfig("audioLevelStandard", ui->audioLevelStandardLineEdit->text());
+    
     // 保存测试项checkbox状态
     configManager.setTestCheckbox("lcd", ui->checkBox_lcd->isChecked());
     configManager.setTestCheckbox("vib", ui->checkBox_vib->isChecked());
     configManager.setTestCheckbox("btn", ui->checkBox_btn->isChecked());
-    configManager.setTestCheckbox("mic", ui->checkBox_mic->isChecked());
-    configManager.setTestCheckbox("charge", ui->checkBox_charge->isChecked());
-    configManager.setTestCheckbox("ntc", ui->checkBox_ntc->isChecked());
+    configManager.setTestCheckbox("tp", ui->checkBox_tp->isChecked());
+    configManager.setTestCheckbox("bright", ui->checkBox_bright->isChecked());
+    configManager.setTestCheckbox("deepsleep", ui->checkBox_deepsleep->isChecked());
+    configManager.setTestCheckbox("battery", ui->checkBox_battery->isChecked());
+    configManager.setTestCheckbox("leaklight", ui->checkBox_leaklight->isChecked());
     // gsensor checkbox可能不存在,跳过
     configManager.setTestCheckbox("bt", ui->checkBox_bt->isChecked());
-    configManager.setTestCheckbox("4g", ui->checkBox_4g->isChecked());
+    configManager.setTestCheckbox("mic", ui->checkBox_mic->isChecked());
+    configManager.setTestCheckbox("sechip", ui->checkBox_sechip->isChecked());
+    configManager.setTestCheckbox("gps", ui->checkBox_gps->isChecked());
+
+    // 保存G-sensor XYZ变化标准(10秒)
+    configManager.setTestConfig("gsensorDeltaXMin", ui->gsensorDeltaXMinLineEdit->text());
+    configManager.setTestConfig("gsensorDeltaXMax", ui->gsensorDeltaXMaxLineEdit->text());
+    configManager.setTestConfig("gsensorDeltaYMin", ui->gsensorDeltaYMinLineEdit->text());
+    configManager.setTestConfig("gsensorDeltaYMax", ui->gsensorDeltaYMaxLineEdit->text());
+    configManager.setTestConfig("gsensorDeltaZMin", ui->gsensorDeltaZMinLineEdit->text());
+    configManager.setTestConfig("gsensorDeltaZMax", ui->gsensorDeltaZMaxLineEdit->text());
     
     // 保存到文件
     configManager.saveConfig();
@@ -6443,36 +4968,39 @@ void MainWindow::loadTestConfigFromSettings()
         }
     }
     
-    // 恢复音频测试参数
-    QString audioVolume = configManager.getTestConfig("audioVolume", "2.0");
-    ui->audioVolumeLineEdit->setText(audioVolume);
-    
-    QString audioLevelStandard = configManager.getTestConfig("audioLevelStandard", "中");
-    if (ui->recordVolumelineEdit) {
-        ui->recordVolumelineEdit->setText(audioLevelStandard);
-    }
-    
     // 恢复期望版本信息
-    ui->expectedModemVerLineEdit->setText(configManager.getTestConfig("expectedModemVer", ""));
-    ui->expectedPYVerLineEdit->setText(configManager.getTestConfig("expectedPYVer", ""));
     ui->expectedVerLineEdit->setText(configManager.getTestConfig("expectedVer", ""));
     
     // 恢复蓝牙测试参数
     ui->bluetoothAddressLineEdit->setText(configManager.getTestConfig("bluetoothAddress", ""));
     ui->bluetoothRSSILineEdit->setText(configManager.getTestConfig("bluetoothRSSI", ""));
     
+    // 恢复录音测试参数
+    ui->audioVolumeLineEdit->setText(configManager.getTestConfig("audioVolume", "2.0"));
+    ui->audioLevelStandardLineEdit->setText(configManager.getTestConfig("audioLevelStandard", "中"));
+    
     // 恢复测试项checkbox状态
     ui->checkBox_lcd->setChecked(configManager.getTestCheckbox("lcd", true));
     ui->checkBox_vib->setChecked(configManager.getTestCheckbox("vib", true));
     ui->checkBox_btn->setChecked(configManager.getTestCheckbox("btn", true));
-    ui->checkBox_mic->setChecked(configManager.getTestCheckbox("mic", true));
-    ui->checkBox_charge->setChecked(configManager.getTestCheckbox("charge", true));
-    ui->checkBox_ntc->setChecked(configManager.getTestCheckbox("ntc", true));
+    ui->checkBox_tp->setChecked(configManager.getTestCheckbox("tp", true));
+    ui->checkBox_bright->setChecked(configManager.getTestCheckbox("bright", true));
+    ui->checkBox_deepsleep->setChecked(configManager.getTestCheckbox("deepsleep", true));
+    ui->checkBox_battery->setChecked(configManager.getTestCheckbox("battery", configManager.getTestCheckbox("charge", true)));
+    ui->checkBox_leaklight->setChecked(configManager.getTestCheckbox("leaklight", true));
     // gsensor checkbox可能不存在,跳过
     ui->checkBox_bt->setChecked(configManager.getTestCheckbox("bt", true));
-    ui->checkBox_4g->setChecked(configManager.getTestCheckbox("4g", true));
-    ui->checkBox_bt->setChecked(configManager.getTestCheckbox("bt", true));
-    ui->checkBox_4g->setChecked(configManager.getTestCheckbox("4g", true));
+    ui->checkBox_mic->setChecked(configManager.getTestCheckbox("mic", true));
+    ui->checkBox_sechip->setChecked(configManager.getTestCheckbox("sechip", true));
+    ui->checkBox_gps->setChecked(configManager.getTestCheckbox("gps", true));
+
+    // 恢复G-sensor XYZ变化标准(10秒)
+    ui->gsensorDeltaXMinLineEdit->setText(configManager.getTestConfig("gsensorDeltaXMin", ""));
+    ui->gsensorDeltaXMaxLineEdit->setText(configManager.getTestConfig("gsensorDeltaXMax", ""));
+    ui->gsensorDeltaYMinLineEdit->setText(configManager.getTestConfig("gsensorDeltaYMin", ""));
+    ui->gsensorDeltaYMaxLineEdit->setText(configManager.getTestConfig("gsensorDeltaYMax", ""));
+    ui->gsensorDeltaZMinLineEdit->setText(configManager.getTestConfig("gsensorDeltaZMin", ""));
+    ui->gsensorDeltaZMaxLineEdit->setText(configManager.getTestConfig("gsensorDeltaZMax", ""));
     
     qDebug() << "测试项checkbox状态已恢复";
     
@@ -6488,19 +5016,27 @@ bool MainWindow::isTestItemEnabled(const QString& testName) const
         return ui->checkBox_vib->isChecked();
     } else if (testName == "btn" || testName == "key") {
         return ui->checkBox_btn->isChecked();
+    } else if (testName == "tp" || testName == "touch") {
+        return ui->checkBox_tp->isChecked();
+    } else if (testName == "bright") {
+        return ui->checkBox_bright->isChecked();
+    } else if (testName == "deepsleep") {
+        return ui->checkBox_deepsleep->isChecked();
     } else if (testName == "mic" || testName == "audio") {
         return ui->checkBox_mic->isChecked();
-    } else if (testName == "charge") {
-        return ui->checkBox_charge->isChecked();
-    } else if (testName == "ntc") {
-        return ui->checkBox_ntc->isChecked();
+    } else if (testName == "charge" || testName == "ntc" || testName == "battery") {
+        return ui->checkBox_battery->isChecked();
     } else if (testName == "gsensor") {
         // gsensor checkbox可能不存在,默认启用
         return true;
+    } else if (testName == "leaklight") {
+        return ui->checkBox_leaklight->isChecked();
     } else if (testName == "bt" || testName == "bluetooth") {
         return ui->checkBox_bt->isChecked();
-    } else if (testName == "4g" || testName == "ppp") {
-        return ui->checkBox_4g->isChecked();
+    } else if (testName == "sechip") {
+        return ui->checkBox_sechip->isChecked();
+    } else if (testName == "gps") {
+        return ui->checkBox_gps->isChecked();
     }
     return true; // 默认启用
 }
@@ -6517,7 +5053,49 @@ void MainWindow::skipToNextEnabledTest()
     
     // 根据当前测试步骤,推进到下一个启用的测试项
     if (currentTestStep == TEST_LCD) {
-        // LCD测试完成,检查马达测试
+        // LCD测试完成:按需求依次进入 触控 -> 亮度 -> 马达
+        if (isTestItemEnabled("tp")) {
+            currentTestStep = TEST_TOUCH;
+            qDebug() << "skipToNextEnabledTest: 推进到触控测试";
+            performTouchTest();
+        } else if (isTestItemEnabled("bright")) {
+            currentTestStep = TEST_BRIGHT;
+            qDebug() << "skipToNextEnabledTest: 推进到亮度测试";
+            performBrightTest();
+        } else {
+            // 触控/亮度都未启用,直接检查马达
+            if (isTestItemEnabled("motor")) {
+                currentTestStep = TEST_MOTOR;
+                qDebug() << "skipToNextEnabledTest: 推进到马达测试";
+                performMotorTest();
+            } else {
+                qDebug() << "马达测试未启用,跳过";
+                updateTestStatus("motor", STATUS_UNTESTED);
+                currentTestStep = TEST_MOTOR; // 推进到下一个测试步骤
+                skipToNextEnabledTest(); // 递归继续下一个
+            }
+        }
+    } else if (currentTestStep == TEST_TOUCH) {
+        // 触控测试完成:检查亮度 -> 马达
+        if (isTestItemEnabled("bright")) {
+            currentTestStep = TEST_BRIGHT;
+            qDebug() << "skipToNextEnabledTest: 推进到亮度测试";
+            performBrightTest();
+        } else {
+            // 直接检查马达
+            if (isTestItemEnabled("motor")) {
+                currentTestStep = TEST_MOTOR;
+                qDebug() << "skipToNextEnabledTest: 推进到马达测试";
+                performMotorTest();
+            } else {
+                qDebug() << "马达测试未启用,跳过";
+                updateTestStatus("motor", STATUS_UNTESTED);
+                currentTestStep = TEST_MOTOR;
+                skipToNextEnabledTest();
+            }
+        }
+    } else if (currentTestStep == TEST_BRIGHT) {
+        // 亮度测试完成:检查马达
         if (isTestItemEnabled("motor")) {
             currentTestStep = TEST_MOTOR;
             qDebug() << "skipToNextEnabledTest: 推进到马达测试";
@@ -6525,8 +5103,8 @@ void MainWindow::skipToNextEnabledTest()
         } else {
             qDebug() << "马达测试未启用,跳过";
             updateTestStatus("motor", STATUS_UNTESTED);
-            currentTestStep = TEST_MOTOR; // 推进到下一个测试步骤
-            skipToNextEnabledTest(); // 递归继续下一个
+            currentTestStep = TEST_MOTOR;
+            skipToNextEnabledTest();
         }
     } else if (currentTestStep == TEST_MOTOR) {
         // 马达测试完成,检查按键测试
@@ -6535,7 +5113,7 @@ void MainWindow::skipToNextEnabledTest()
         qDebug() << "  isWaitingForUserConfirm:" << isWaitingForUserConfirm;
         qDebug() << "  motorTestPopupShown:" << motorTestPopupShown;
         
-        if (isWaitingForUserConfirm && motorTestPopupShown) {
+        if (isWaitingForUserConfirm && (motorTestPopupShown || touchTestPopupShown || brightTestPopupShown)) {
             qDebug() << "skipToNextEnabledTest: 马达测试正在等待用户确认,暂不推进到按键测试";
             return; // 等待用户确认完成后再推进
         }
@@ -6552,26 +5130,51 @@ void MainWindow::skipToNextEnabledTest()
             skipToNextEnabledTest(); // 递归继续下一个
         }
     } else if (currentTestStep == TEST_KEY) {
-        // 按键测试完成,检查录音测试
-        if (isTestItemEnabled("audio")) {
+        // 按键测试完成:功耗测试 -> 录音 -> 电池
+        if (isTestItemEnabled("deepsleep")) {
+            currentTestStep = TEST_DEEPSLEEP;
+            qDebug() << "skipToNextEnabledTest: 推进到功耗测试";
+            performDeepsleepTest();
+        } else if (isTestItemEnabled("mic")) {
             currentTestStep = TEST_AUDIO;
+            qDebug() << "skipToNextEnabledTest: 推进到录音测试";
             performAudioTest();
+        } else if (isTestItemEnabled("battery")) {
+            currentTestStep = TEST_BATTERY;
+            performBatteryTest();
         } else {
-            qDebug() << "录音测试未启用,跳过";
-            updateTestStatus("audio", STATUS_UNTESTED);
-            currentTestStep = TEST_AUDIO; // 推进到下一个测试步骤
-            skipToNextEnabledTest(); // 递归继续下一个
+            qDebug() << "功耗/录音/电池测试均未启用,跳过";
+            updateTestStatus("deepsleep", STATUS_UNTESTED);
+            updateTestStatus("mic", STATUS_UNTESTED);
+            updateTestStatus("battery", STATUS_UNTESTED);
+            currentTestStep = TEST_BATTERY;
+            skipToNextEnabledTest();
+        }
+    } else if (currentTestStep == TEST_DEEPSLEEP) {
+        if (isTestItemEnabled("mic")) {
+            currentTestStep = TEST_AUDIO;
+            qDebug() << "skipToNextEnabledTest: 推进到录音测试";
+            performAudioTest();
+        } else if (isTestItemEnabled("battery")) {
+            currentTestStep = TEST_BATTERY;
+            performBatteryTest();
+        } else {
+            qDebug() << "录音和电池测试均未启用,跳过";
+            updateTestStatus("mic", STATUS_UNTESTED);
+            updateTestStatus("battery", STATUS_UNTESTED);
+            currentTestStep = TEST_BATTERY;
+            skipToNextEnabledTest();
         }
     } else if (currentTestStep == TEST_AUDIO) {
-        // 录音测试完成,检查电池测试(如果充电或ntc任一启用,都需要执行电池测试)
-        if (isTestItemEnabled("charge") || isTestItemEnabled("ntc")) {
+        // 录音测试完成,检查电池测试
+        if (isTestItemEnabled("battery")) {
             currentTestStep = TEST_BATTERY;
             performBatteryTest();
         } else {
             qDebug() << "电池测试未启用,跳过";
             updateTestStatus("battery", STATUS_UNTESTED);
-            currentTestStep = TEST_BATTERY; // 推进到下一个测试步骤
-            skipToNextEnabledTest(); // 递归继续下一个
+            currentTestStep = TEST_BATTERY;
+            skipToNextEnabledTest();
         }
     } else if (currentTestStep == TEST_BATTERY) {
         // 电池测试完成,检查G-sensor测试
@@ -6585,7 +5188,18 @@ void MainWindow::skipToNextEnabledTest()
             skipToNextEnabledTest(); // 递归继续下一个
         }
     } else if (currentTestStep == TEST_GSENSOR) {
-        // G-sensor测试完成,检查蓝牙测试
+        // G-sensor测试完成,检查漏光测试
+        if (isTestItemEnabled("leaklight")) {
+            currentTestStep = TEST_LEAKLIGHT;
+            performLeaklightTest();
+        } else {
+            qDebug() << "漏光测试未启用,跳过";
+            updateTestStatus("leaklight", STATUS_UNTESTED);
+            currentTestStep = TEST_LEAKLIGHT;
+            skipToNextEnabledTest();
+        }
+    } else if (currentTestStep == TEST_LEAKLIGHT) {
+        // 漏光测试完成,检查蓝牙测试
         if (isTestItemEnabled("bluetooth")) {
             currentTestStep = TEST_BLUETOOTH;
             performBluetoothTest();
@@ -6596,19 +5210,33 @@ void MainWindow::skipToNextEnabledTest()
             skipToNextEnabledTest(); // 递归继续下一个
         }
     } else if (currentTestStep == TEST_BLUETOOTH) {
-        // 蓝牙测试完成,检查4G测试
-        if (isTestItemEnabled("ppp")) {
-            currentTestStep = TEST_PPP_INIT;
-            perform4GTest();
+        // 蓝牙测试完成,检查SE芯片测试
+        if (isTestItemEnabled("sechip")) {
+            currentTestStep = TEST_SECHIP;
+            performSeChipTest();
         } else {
-            qDebug() << "4G测试未启用,跳过";
-            updateTestStatus("ppp", STATUS_UNTESTED);
-            // 所有测试完成
-            currentTestStep = TEST_COMPLETED;
-            isProductionTestRunning = false;
-            QString fileName = saveTestRecord();
-            showFinalTestResult();
+            qDebug() << "SE芯片测试未启用,跳过";
+            updateTestStatus("sechip", STATUS_UNTESTED);
+            currentTestStep = TEST_SECHIP;
+            skipToNextEnabledTest();
         }
+    } else if (currentTestStep == TEST_SECHIP) {
+        // SE芯片测试完成,检查GPS测试
+        if (isTestItemEnabled("gps")) {
+            currentTestStep = TEST_GPS;
+            performGpsTest();
+        } else {
+            qDebug() << "GPS测试未启用,跳过";
+            updateTestStatus("gps", STATUS_UNTESTED);
+            currentTestStep = TEST_GPS;
+            skipToNextEnabledTest();
+        }
+    } else if (currentTestStep == TEST_GPS) {
+        // GPS测试完成,所有测试完成
+        currentTestStep = TEST_COMPLETED;
+        isProductionTestRunning = false;
+        saveTestRecord();
+        showFinalTestResult();
     } else {
         qDebug() << "未知的测试步骤,无法推进";
     }
@@ -6625,15 +5253,6 @@ void MainWindow::saveRecvTextEditToLog()
         return;
     }
     
-    // 检查IMEI和ICCID
-    QString imei = ui->IMEIlineEdit->text().trimmed();
-    QString iccid = ui->ICCIDlineEdit->text().trimmed();
-    
-    if (imei.isEmpty() || imei == "未获取" || iccid.isEmpty() || iccid == "未获取") {
-        qDebug() << "IMEI或ICCID未获取,跳过保存日志。IMEI:" << imei << "ICCID:" << iccid;
-        return;
-    }
-    
     // 创建日志目录
     QString appPath = QCoreApplication::applicationDirPath();
     QString logsDir = appPath + "/产测记录/logs";
@@ -6644,11 +5263,9 @@ void MainWindow::saveRecvTextEditToLog()
     
     // 生成日志文件名
     QDateTime now = QDateTime::currentDateTime();
-    QString fileName = QString("%1_%2_%3_%4_UI日志.log")
+    QString fileName = QString("%1_%2_UI日志.log")
                        .arg(now.toString("yyyyMMdd"))
-                       .arg(now.toString("hhmmss"))
-                       .arg(imei)
-                       .arg(iccid);
+                       .arg(now.toString("hhmmss"));
     
     QString filePath = logsDir + "/" + fileName;
     
@@ -6658,8 +5275,6 @@ void MainWindow::saveRecvTextEditToLog()
         QTextStream stream(&logFile);
         stream.setCodec("UTF-8");
         stream << QString("=== UI日志保存时间: %1 ===\n").arg(now.toString("yyyy-MM-dd hh:mm:ss"));
-        stream << QString("IMEI: %1\n").arg(imei);
-        stream << QString("ICCID: %1\n").arg(iccid);
         stream << "========================================\n";
         stream << logContent;
         logFile.close();
@@ -6674,11 +5289,9 @@ void MainWindow::saveRecvTextEditToLog()
     QString fullLogContent = ui->logtextEdit->toPlainText();
     if (!fullLogContent.trimmed().isEmpty()) {
         // 生成完整日志文件名(加full后缀)
-        QString fullFileName = QString("%1_%2_%3_%4_UI日志full.log")
+        QString fullFileName = QString("%1_%2_UI日志full.log")
                                .arg(now.toString("yyyyMMdd"))
-                               .arg(now.toString("hhmmss"))
-                               .arg(imei)
-                               .arg(iccid);
+                               .arg(now.toString("hhmmss"));
         
         QString fullFilePath = logsDir + "/" + fullFileName;
         
@@ -6687,8 +5300,6 @@ void MainWindow::saveRecvTextEditToLog()
             QTextStream fullStream(&fullLogFile);
             fullStream.setCodec("UTF-8");
             fullStream << QString("=== 完整日志保存时间: %1 ===\n").arg(now.toString("yyyy-MM-dd hh:mm:ss"));
-            fullStream << QString("IMEI: %1\n").arg(imei);
-            fullStream << QString("ICCID: %1\n").arg(iccid);
             fullStream << "========================================\n";
             fullStream << fullLogContent;
             fullLogFile.close();
@@ -6712,17 +5323,6 @@ void MainWindow::createTestLogFile()
     // 获取当前时间
     QDateTime now = QDateTime::currentDateTime();
     
-    // 获取IMEI和ICCID(可能还没有,使用"未知"作为占位符)
-    QString imei = ui->IMEIlineEdit->text().trimmed();
-    QString iccid = ui->ICCIDlineEdit->text().trimmed();
-    
-    if (imei.isEmpty()) {
-        imei = "未知";
-    }
-    if (iccid.isEmpty()) {
-        iccid = "未知";
-    }
-    
     // 创建日志目录(在CSV记录目录下的logs子目录)
     QString appPath = QCoreApplication::applicationDirPath();
     QString logsDir = appPath + "/产测记录/logs";
@@ -6731,12 +5331,10 @@ void MainWindow::createTestLogFile()
         dir.mkpath(logsDir);
     }
     
-    // 生成文件名:yyyymmdd_hhmmss_imei_iccid.log
-    QString fileName = QString("%1_%2_%3_%4.log")
+    // 生成文件名:yyyymmdd_hhmmss.log
+    QString fileName = QString("%1_%2.log")
                        .arg(now.toString("yyyyMMdd"))
-                       .arg(now.toString("hhmmss"))
-                       .arg(imei)
-                       .arg(iccid);
+                       .arg(now.toString("hhmmss"));
     
     testLogFilePath = logsDir + "/" + fileName;
     
@@ -6754,8 +5352,6 @@ void MainWindow::createTestLogFile()
     
     // 写入日志文件头
     *testLogStream << QString("=== 测试开始时间: %1 ===\n").arg(now.toString("yyyy-MM-dd hh:mm:ss"));
-    *testLogStream << QString("IMEI: %1\n").arg(imei);
-    *testLogStream << QString("ICCID: %1\n").arg(iccid);
     *testLogStream << "========================================\n";
     testLogStream->flush();
     
@@ -6802,221 +5398,7 @@ void MainWindow::closeTestLogFile()
 // 使用设备信息重命名日志文件
 void MainWindow::renameLogFileWithDeviceInfo()
 {
-    // 如果日志文件未打开或路径为空,不处理
-    if (!testLogFile || testLogFilePath.isEmpty()) {
-        return;
-    }
-    
-    // 获取IMEI和ICCID
-    QString imei = ui->IMEIlineEdit->text().trimmed();
-    QString iccid = ui->ICCIDlineEdit->text().trimmed();
-    
-    // 如果IMEI和ICCID都还是"未知"或为空,不重命名
-    if (imei.isEmpty() || imei == "未知" || iccid.isEmpty() || iccid == "未知") {
-        return;
-    }
-    
-    // 从当前文件名中提取时间部分
-    QFileInfo fileInfo(testLogFilePath);
-    QString baseName = fileInfo.baseName();
-    QStringList parts = baseName.split("_");
-    
-    if (parts.size() >= 2) {
-        QString datePart = parts[0];  // yyyymmdd
-        QString timePart = parts[1];  // hhmmss
-        
-        // 生成新文件名
-        QString newFileName = QString("%1_%2_%3_%4.log")
-                             .arg(datePart)
-                             .arg(timePart)
-                             .arg(imei)
-                             .arg(iccid);
-        
-        QString newFilePath = fileInfo.absolutePath() + "/" + newFileName;
-        
-        // 如果新文件名与当前文件名不同,重命名
-        if (newFilePath != testLogFilePath) {
-            // 先关闭文件
-            if (testLogFile->isOpen()) {
-                testLogFile->close();
-            }
-            
-            // 重命名文件
-            QFile oldFile(testLogFilePath);
-            if (oldFile.rename(newFilePath)) {
-                testLogFilePath = newFilePath;
-                qDebug() << "日志文件已重命名:" << newFilePath;
-                
-                // 重新打开文件(追加模式)
-                if (testLogFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
-                    testLogStream = new QTextStream(testLogFile);
-                    testLogStream->setCodec("UTF-8");
-                } else {
-                    qDebug() << "重命名后无法重新打开日志文件:" << newFilePath;
-                    delete testLogFile;
-                    testLogFile = nullptr;
-                    testLogStream = nullptr;
-                }
-            } else {
-                qDebug() << "无法重命名日志文件:" << oldFile.errorString();
-                // 重新打开原文件
-                if (testLogFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
-                    testLogStream = new QTextStream(testLogFile);
-                    testLogStream->setCodec("UTF-8");
-                }
-            }
-        }
-    }
-}
-
-// ICCID输入框文本改变时触发
-void MainWindow::onICCIDLineEditTextChanged(const QString& text)
-{
-    refreshICCIDCodes();
-}
-
-// IMEI输入框文本改变时触发
-void MainWindow::onIMEILineEditTextChanged(const QString& text)
-{
-    refreshIMEICodes();
-}
-
-// ICCID复选框状态改变时触发
-void MainWindow::onICCIDCheckBoxStateChanged(int state)
-{
-    Q_UNUSED(state);
-    refreshICCIDCodes();
-}
-
-// IMEI复选框状态改变时触发
-void MainWindow::onIMEICheckBoxStateChanged(int state)
-{
-    Q_UNUSED(state);
-    refreshIMEICodes();
-}
-
-// 刷新ICCID的二维码和条形码
-void MainWindow::refreshICCIDCodes()
-{
-    QString text = ui->ICCIDlineEdit->text().trimmed();
-    if (ui->checkBox_iccid->isChecked() && !text.isEmpty()) {
-        qDebug() << "刷新ICCID二维码和条形码:" << text;
-        generateQRCode(text, ui->graphicsView_iccid_qr);
-        generateBarcode(text, ui->graphicsView_iccid_barcode);
-    } else {
-        // 如果未选中或文本为空,清空显示
-        if (ui->graphicsView_iccid_qr->scene()) {
-            ui->graphicsView_iccid_qr->scene()->clear();
-        }
-        if (ui->graphicsView_iccid_barcode->scene()) {
-            ui->graphicsView_iccid_barcode->scene()->clear();
-        }
-    }
-}
-
-// 刷新IMEI的二维码和条形码
-void MainWindow::refreshIMEICodes()
-{
-    QString text = ui->IMEIlineEdit->text().trimmed();
-    if (ui->checkBox_imei->isChecked() && !text.isEmpty()) {
-        qDebug() << "刷新IMEI二维码和条形码:" << text;
-        generateQRCode(text, ui->graphicsView_imei_qr);
-        generateBarcode(text, ui->graphicsView_imei_barcode);
-    } else {
-        // 如果未选中或文本为空,清空显示
-        if (ui->graphicsView_imei_qr->scene()) {
-            ui->graphicsView_imei_qr->scene()->clear();
-        }
-        if (ui->graphicsView_imei_barcode->scene()) {
-            ui->graphicsView_imei_barcode->scene()->clear();
-        }
-    }
-}
-
-// 生成二维码
-void MainWindow::generateQRCode(const QString& text, QGraphicsView* view)
-{
-    if (!view || text.isEmpty()) {
-        return;
-    }
-    
-    // 获取视图的实际可用尺寸(减去边距)
-    QSize viewSize = view->size();
-    int margin = 10;
-    int availableSize = qMin(viewSize.width(), viewSize.height()) - 2 * margin;
-    if (availableSize < 100) {
-        availableSize = 200; // 最小尺寸
-    }
-    
-    // 创建二维码图片,使用视图的实际尺寸
-    QPixmap qrPixmap = QRCodeBarcodeGenerator::createQRCodePixmap(text, availableSize);
-    
-    // 清空场景并添加二维码图片
-    if (!view->scene()) {
-        view->setScene(new QGraphicsScene(this));
-    }
-    view->scene()->clear();
-    QGraphicsPixmapItem* item = view->scene()->addPixmap(qrPixmap);
-    
-    // 设置场景大小
-    view->scene()->setSceneRect(0, 0, qrPixmap.width(), qrPixmap.height());
-    
-    // 铺满窗口,保持宽高比
-    view->fitInView(item, Qt::KeepAspectRatio);
-    
-    // 确保视图更新
-    view->update();
-}
-
-// 窗口大小改变时刷新二维码和条形码
-void MainWindow::resizeEvent(QResizeEvent* event)
-{
-    QMainWindow::resizeEvent(event);
-    
-    // 延迟刷新,确保窗口大小已经更新
-    QTimer::singleShot(100, this, [this]() {
-        refreshICCIDCodes();
-        refreshIMEICodes();
-    });
-}
-
-// 生成条形码
-void MainWindow::generateBarcode(const QString& text, QGraphicsView* view)
-{
-    if (!view || text.isEmpty()) {
-        return;
-    }
-    
-    // 获取视图的实际可用尺寸(减去边距)
-    QSize viewSize = view->size();
-    int margin = 10;
-    int availableWidth = viewSize.width() - 2 * margin;
-    int availableHeight = viewSize.height() - 2 * margin;
-    
-    if (availableWidth < 200) {
-        availableWidth = 300; // 最小宽度
-    }
-    if (availableHeight < 50) {
-        availableHeight = 100; // 最小高度
-    }
-    
-    // 创建条形码图片,使用视图的实际尺寸
-    QPixmap barcodePixmap = QRCodeBarcodeGenerator::createBarcodePixmap(text, availableWidth, availableHeight);
-    
-    // 清空场景并添加条形码图片
-    if (!view->scene()) {
-        view->setScene(new QGraphicsScene(this));
-    }
-    view->scene()->clear();
-    QGraphicsPixmapItem* item = view->scene()->addPixmap(barcodePixmap);
-    
-    // 设置场景大小
-    view->scene()->setSceneRect(0, 0, barcodePixmap.width(), barcodePixmap.height());
-    
-    // 铺满窗口,保持宽高比
-    view->fitInView(item, Qt::KeepAspectRatio);
-    
-    // 确保视图更新
-    view->update();
+    // 已移除 IMEI/ICCID 相关功能,不再按设备信息重命名日志
+    return;
 }
 
index e0861d3..cc16ffa 100644 (file)
 #include <QVBoxLayout>
 #include <QLabel>
 #include <QSet>
+#include <QMap>
 #include <QFile>
 #include <QTextStream>
 #include "config_manager.h"
-#include "qrcode_barcode_generator.h"
 
 QT_BEGIN_NAMESPACE
-class QGraphicsView;
 namespace Ui { class MainWindow; }
 QT_END_NAMESPACE
 
@@ -43,8 +42,6 @@ public:
     ~MainWindow();
     
 protected:
-    void resizeEvent(QResizeEvent* event) override;  // 窗口大小改变时刷新二维码和条形码
-
 private slots:
     void on_refreshButton_clicked();    // 刷新串口列表
     void on_openButton_clicked();      // 打开/关闭串口
@@ -62,16 +59,10 @@ private slots:
     void performLcdColorTest();             // LCD颜色测试
     void performMotorTest();                // 马达测试
     void performMotorTestContinue();        // 继续马达测试(LCD关闭后调用)
-    void perform4GTest();                   // 4G测试
-    void perform4GSignalTest();             // 4G信号质量测试
     void onLcdTestTimer();                  // LCD测试定时器
     void onMotorTestTimer();                // 马达测试定时器
-    void on4GTestTimeout();                 // 4G测试超时
-    void on4GSignalTimer();                 // 4G信号检测定时器
     void onTestTimeout();                   // 通用测试超时处理
     void checkFactoryModeResult(const QString& data); // 检查工厂模式进入结果
-    void check4GInitResult(const QString& data); // 检查4G初始化结果
-    void check4GSignalResult(const QString& data); // 检查4G信号质量结果
     QString getRsrqGrade(double rsrq); // 获取RSRQ信号质量评级
     QString getRsrpGrade(double rsrp); // 获取RSRP信号覆盖强度评级
     QString getOverallSignalAssessment(double rsrp, double rsrq); // 获取整体信号评估
@@ -82,24 +73,34 @@ private slots:
     void checkBuildVerResult(const QString& data); // 检查BuildVer启动完成
     void showLcdTestConfirmDialog(); // 显示LCD测试确认弹窗
     void showMotorTestConfirmDialog(); // 显示马达测试确认弹窗
+    void performTouchTest(); // 触控测试(AT^TESTJUMP=TP)
+    void performBrightTest(); // 亮度测试(AT^TESTJUMP=BRIGHT)
+    void showTouchTestConfirmDialog(); // 触控测试确认弹窗
+    void showBrightTestConfirmDialog(); // 亮度测试确认弹窗
     void performKeyTest(); // 执行按键测试
     void checkKeyTestResult(const QString& data); // 检查按键测试结果
     void showKeyTestConfirmDialog(bool allKeysTested); // 显示按键测试确认弹窗
-    void performAudioTest(); // 执行录音测试
-    void performAudioTestContinue(); // 继续录音测试(LCD关闭后调用)
-    void showAudioTestDialog(); // 显示录音测试弹窗
-    void onAudioRecordTimer(); // 录音测试定时器(3秒录音)
+    void performDeepsleepTest(); // 功耗:ENTERTESTVIEW -> TESTJUMP=DEEPSLEEP -> FACTORYDEEPSLEEPENTER
+    void showDeepsleepTestConfirmDialog(); // 功耗测试确认弹窗
+    void performAudioTest(); // 执行录音测试(有标准时共3轮,任一轮达标即通过)
+    void beginAudioTestAttempt(int roundIndex); // 发送单轮 AT^AUDIOTEST
     void checkAudioTestResult(const QString& data); // 检查录音测试结果
-    QString parseAudioLevel(const QString& data); // 解析音频等级(从文字或rms_db数值)
-    int getAudioLevelValue(const QString& level); // 获取音频等级数值(用于比较)
-    bool isAudioLevelPass(const QString& detectedLevel, const QString& requiredLevel); // 判断检测到的等级是否满足要求
     void performBatteryTest(); // 执行电池测试
     void checkBatteryTestResult(const QString& data); // 检查电池测试结果
     void performGsensorTest(); // 执行G-sensor测试
     void checkGsensorTestResult(const QString& data); // 检查G-sensor测试结果
+    void onGsensorIntervalTimeout(); // 10秒后关闭GSENSORTEST并计算变化
+    void performLeaklightTest(); // 心率漏光测试
+    void checkLeaklightTestResult(const QString& data); // 检查漏光测试结果
     void performBluetoothTest(); // 执行蓝牙测试
     void checkBluetoothTestResult(const QString& data); // 检查蓝牙测试结果
     void onBluetoothTestTimer(); // 蓝牙测试超时处理
+    void performSeChipTest(); // 执行SE芯片测试(AT^GETCYWEEACTIVE)
+    void checkSeChipTestResult(const QString& data); // 检查SE芯片测试结果
+    void performGpsTest(); // 执行GPS测试
+    void checkGpsTestResult(const QString& data); // 检查GPS测试结果
+    void onGpsTestTimer(); // GPS测试30秒超时处理
+    void onGpsPollTimer(); // GPS信息轮询定时器
     void updateTestStatus(const QString& testName, TestStatus status);
     void updateOverallStatus();
     QString saveTestRecord(); // 保存测试记录到Excel(静默保存)
@@ -108,23 +109,11 @@ private slots:
     void appendToLogFile(const QString& text); // 追加内容到日志文件(同时显示到recvTextEdit)
     void closeTestLogFile(); // 关闭测试日志文件
     void renameLogFileWithDeviceInfo(); // 使用设备信息重命名日志文件
-    void checkSignalAndSimValues(const QString& data); // 检查signal和sim值进行4G测试判断
     void on_openButton_4_clicked();
 
-    void sendPppCommand(); // 发送PPP指令
     void parseWstpPrintData(const QString& data); // 解析wstp_print格式的BTMAC和CyweeChipInfo数据
     
 
-    void onCat1SignalTimer();
-    void checkCat1SignalResult(const QString& data);
-    void show4GRetryDialog(const QString& failReason);
-    void performPingTest();                 // Ping测试
-    void checkPingTestResult(const QString& data); // 检查Ping测试结果
-    void onPingTestTimeout();               // Ping测试超时
-    bool isEGModem() const;
-    bool isValidIMEI(const QString& imei);
-    bool isValidICCID(const QString& iccid);
-
     void on_agingTestButton_clicked();
 
     void on_agingTestButton_2_clicked();
@@ -146,60 +135,49 @@ private:
     // 产测相关变量
     QTimer *lcdTestTimer;              // LCD测试定时器
     QTimer *motorTestTimer;            // 马达测试定时器
-    QTimer *test4GTimer;               // 4G测试超时定时器
-    QTimer *signal4GTimer;             // 4G信号检测定时器
     QTimer *testTimeoutTimer;          // 通用测试超时定时器
+    QTimer *gSensorIntervalTimer;     // G-sensor打印10秒后停止
 
-    QTimer *cat1SignalTimer;          // CAT1信号测试定时器
     QTimer *bluetoothTestTimer;        // 蓝牙测试定时器
-    QTimer *pingTestTimer;             // Ping测试超时定时器
+    QTimer *gpsTestTimer;              // GPS测试30秒总超时定时器
+    QTimer *gpsPollTimer;              // GPS信息轮询定时器(每2秒发AT^GPSINFO)
     int currentLcdColor;               // 当前LCD测试颜色 (1=红, 2=绿, 3=蓝)
-    int initRetry4GCount;              // 4G初始化重试次数
-    bool hasReceivedFirstSignal;
-    int cat1SignalTestCount;
-    int cat1SignalReceivedCount;
-    int cat1SignalGoodCount;
-    int cat1Signal99Count;
-    int cat1SignalTotalValue;
-    int signalTest4GCount;             // 4G信号检测发送次数
-    int signalReceived4GCount;         // 4G信号接收到有效数据次数
-    int signalGood4GCount;             // 4G信号良好以上计数
-    int pppCommandCount;               // PPP指令发送次数计数器
-    bool hasReceivedSignalSim;         // 是否已收到signal和sim数据
-    bool hasReceivedPppInfo;           // 是否已收到PPPINFO相关打印
-    QString currentModemVersion;       // 当前MODEM版本
-    QString currentPYVerVersion;       // 当前python版本
-    int currentSimStatus;              // 当前SIM卡状态:1=插入,0=未插入,-1=未知
     bool isProductionTestRunning;      // 产测是否正在运行
     QDateTime testStartTime;           // 测试开始时间
     bool lcdTestPopupShown;            // LCD测试弹窗是否已显示
     bool motorTestPopupShown;          // 马达测试弹窗是否已显示
+    bool touchTestPopupShown;         // 触控测试弹窗是否已显示
+    bool brightTestPopupShown;        // 亮度测试弹窗是否已显示
+    bool deepsleepTestPopupShown;     // 功耗测试弹窗是否已显示
     bool isWaitingForUserConfirm;      // 是否正在等待用户确认
     bool lcdBacklightOn;               // LCD背光状态:true=开启,false=关闭
     bool keyTestStarted;               // 按键测试是否已开始
     int keyTestStatus[4];              // 按键测试状态 0=未测试 1=已测试
     QDialog *keyTestDialog;            // 按键测试弹窗
     QPushButton *keyButtons[4];        // 按键测试按钮
-    bool audioTestPopupShown;          // 录音测试弹窗是否已显示
-    bool audioPlaybackPopupShown;     // 录音播放确认弹窗是否已显示
-    QTimer *audioRecordTimer;          // 录音测试定时器
-    QDialog *audioTestDialog;          // 录音测试对话框
-    QLabel *audioDialogLabel;          // 录音对话框标签
-    QLabel *audioDialogInfo;           // 录音对话框信息
-    QPushButton *audioStartButton;     // 开始录音按钮
-    QPushButton *audioPassButton;      // 录音通过按钮
-    QPushButton *audioFailButton;      // 录音失败按钮
-    QPushButton *audioRetestButton;    // 重新测试按钮
-    int audioMicMiddleConsecutiveCount; // 连续检测到设置的标准Level的次数
-    int audioMicTotalPassCount;         // 累计检测到满足要求的Level的次数(包括非连续的)
-    QString audioLevelStandard;         // 音频测试通过标准("中"或"低"等)
-    bool audioTestFinished;             // 录音测试是否已完成(成功或失败)
     QSet<QString> bluetoothAddresses;  // 蓝牙测试检测到的不同蓝牙地址集合
+    QMap<QString, int> bluetoothAddressRssiMap;  // 地址->最佳RSSI(用于配置了广播设备时校验信号强度)
     bool bluetoothTestFinished;         // 蓝牙测试是否已完成(成功或失败)
     QString detectedBluetoothAddress;   // 检测到的指定蓝牙地址
     int detectedBluetoothRSSI;          // 检测到的指定蓝牙地址的RSSI值
-    bool pingTestFinished;              // Ping测试是否已完成(成功或失败)
-    int pingSuccessCount;               // Ping成功次数
+    int bluetoothMaxRssi;               // 未配置广播设备时:检测到的最大RSSI(用于>=3设备且至少一个>=−45dBm判断)
+    bool gpsTestFinished;              // GPS测试是否已完成
+    bool gpsInfoSeenIn10Sec;           // 10秒内是否已收到GPS信息
+    int gpsMaxNumSats;                 // GPS测试期间 num_sats 最大值(仅日志)
+    int gpsMaxTrace;                   // GPS测试期间 trace 卫星数最大值(判定用,≥3通过)
+    int gpsMaxCn;                      // GPS测试期间最大CN值(仅日志)
+    QMap<QString, QDateTime> testStartTimeMap;   // 各项测试开始时间
+    QMap<QString, qint64> testDurationSecMap;   // 各项测试耗时(秒)
+    bool keyRotateDetected;             // 按键测试中是否检测到旋转事件
+    int audioTestRoundIndex;            // 录音测试当前轮次 0..2(有等级标准时共3轮)
+    bool audioTestAnyPass;             // 三轮中是否至少一轮满足等级标准
+    bool audioTestAwaitingMicLevel;     // 本轮是否仍在等待首条 mic level(防重复解析)
+    int leaklightGreen;
+    int leaklightRed;
+    int leaklightIr;
+    bool leaklightGreenSeen;
+    bool leaklightRedSeen;
+    bool leaklightIrSeen;
     // 日志文件相关变量
     QFile *testLogFile;                 // 测试日志文件对象
     QTextStream *testLogStream;         // 测试日志文件流
@@ -218,30 +196,23 @@ private:
     int currentVersionCommandIndex;     // 当前执行的版本信息指令索引
     QTimer *versionCommandTimer;        // 版本信息指令发送定时器
     
-    enum AudioTestStep {
-        AUDIO_STARTING_RECORD,  // 开始录音
-        AUDIO_PLAYING_1KHZ,     // 正在播放1kHz音频
-        AUDIO_STOPPING_1KHZ,    // 停止播放1kHz音频
-        AUDIO_STOPPING_RECORD,  // 停止录音
-        AUDIO_PLAYING_RECORDED, // 播放录制的音频
-        AUDIO_STOPPING_PLAYBACK // 停止播放录制的音频
-    } currentAudioStep;
-    
     enum ProductionTestStep {
         TEST_IDLE,
         TEST_RESET,
         TEST_WAIT_BUILDVER,
         TEST_LCD,
+        TEST_TOUCH,
+        TEST_BRIGHT,
         TEST_MOTOR,
         TEST_KEY,
+        TEST_DEEPSLEEP,
         TEST_AUDIO,
         TEST_BATTERY,
         TEST_GSENSOR,
+        TEST_LEAKLIGHT,
         TEST_BLUETOOTH,
-        TEST_PPP_INIT,
-        TEST_PPP_UPDATE,
-        TEST_PPP_SIGNAL,
-        TEST_PPP_PING,
+        TEST_SECHIP,
+        TEST_GPS,
         TEST_COMPLETED
     } currentTestStep;
     // 十六进制与字节数组转换工具函数
@@ -250,12 +221,6 @@ private:
     // 数据提取函数
     void ShowSoftVer(const QString& data);
     void CheckVibratorFunc(const QString& data);
-    // PPP_INFO格式数据提取函数
-    void ShowPppInfoIMEI(const QString& data);
-    void ShowPppInfoICCID(const QString& data);
-    void ShowPppInfoModemVer(const QString& data);
-    void ShowPppInfoPYVer(const QString& data);
-    // CAT1信号测试相关函数
     void parseNetworkStatChanged(const QString& data);
     
     // 开机检测和版本信息获取相关函数
@@ -273,15 +238,23 @@ private:
     // 测试项checkbox检查辅助函数
     bool isTestItemEnabled(const QString& testName) const;
     void skipToNextEnabledTest();  // 跳过当前测试项,推进到下一个启用的测试项
+
+    // G-sensor XYZ变化计算:解析UI阈值并更新测试结果
+    void finalizeGsensorTest();
+
+    // G-sensor XYZ窗口采集状态
+    bool gSensorCollecting;            // 是否正在收集XYZ数据
+    bool gSensorHasStart;             // 是否已记录起始XYZ
+    bool gSensorHasLast;              // 是否已记录末尾XYZ
+    bool gSensorErrorDetected;        // 是否检测到错误/异常响应
+    bool gSensorAnySample;            // 10秒窗口内是否采到至少一组XYZ
+    bool gSensorAllSamplesInRange;    // 10秒窗口内是否每组XYZ都在阈值范围内
+    double gSensorStartX;
+    double gSensorStartY;
+    double gSensorStartZ;
+    double gSensorLastX;
+    double gSensorLastY;
+    double gSensorLastZ;
     
-    // 二维码和条形码生成相关函数
-    void onICCIDLineEditTextChanged(const QString& text);  // ICCID输入框文本改变时触发
-    void onIMEILineEditTextChanged(const QString& text);  // IMEI输入框文本改变时触发
-    void onICCIDCheckBoxStateChanged(int state);  // ICCID复选框状态改变时触发
-    void onIMEICheckBoxStateChanged(int state);  // IMEI复选框状态改变时触发
-    void generateQRCode(const QString& text, QGraphicsView* view);  // 生成二维码
-    void generateBarcode(const QString& text, QGraphicsView* view);  // 生成条形码
-    void refreshICCIDCodes();  // 刷新ICCID的二维码和条形码
-    void refreshIMEICodes();  // 刷新IMEI的二维码和条形码
 };
 #endif // MAINWINDOW_H
index 1a63dab..487cae4 100644 (file)
@@ -280,125 +280,56 @@ background-color: #F57C00;
          </widget>
         </item>
         <item>
-         <layout class="QHBoxLayout" name="audioVolumeLayout">
-          <property name="spacing">
-           <number>6</number>
-          </property>
-          <item>
-           <widget class="QLabel" name="AudioVolumeLabel">
-            <property name="sizePolicy">
-             <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
-              <horstretch>0</horstretch>
-              <verstretch>0</verstretch>
-             </sizepolicy>
-            </property>
-            <property name="font">
-             <font>
-              <family>Agra</family>
-              <pointsize>14</pointsize>
-             </font>
-            </property>
-            <property name="text">
-             <string>测试音频播放音量</string>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <widget class="QLineEdit" name="audioVolumeLineEdit">
-            <property name="font">
-             <font>
-              <family>Arial</family>
-              <pointsize>14</pointsize>
-             </font>
-            </property>
-            <property name="toolTip">
-             <string/>
-            </property>
-            <property name="statusTip">
-             <string>0.0~2.0, 取值标准,麦克风能收音的最低喇叭音量。</string>
-            </property>
-            <property name="text">
-             <string>2.0</string>
-            </property>
-            <property name="alignment">
-             <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-            </property>
-            <property name="placeholderText">
-             <string>例如: 2</string>
-            </property>
-           </widget>
-          </item>
-         </layout>
-        </item>
-        <item>
-         <layout class="QHBoxLayout" name="horizontalLayout">
-          <item>
-           <widget class="QLabel" name="label_3">
-            <property name="text">
-             <string>录音音量标准:</string>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <widget class="QLineEdit" name="recordVolumelineEdit">
-            <property name="statusTip">
-             <string>可选:高,中,低,极低,静音。连续出现三个指定的音量标准,视为测试通过。</string>
-            </property>
-            <property name="text">
-             <string>中</string>
-            </property>
-            <property name="placeholderText">
-             <string>可选:高,中,低,极低,静音</string>
-            </property>
-           </widget>
-          </item>
-         </layout>
-        </item>
-       </layout>
-      </item>
-      <item>
-       <widget class="Line" name="line_4">
-        <property name="orientation">
-         <enum>Qt::Horizontal</enum>
-        </property>
-       </widget>
-      </item>
-      <item>
-       <layout class="QVBoxLayout" name="verticalLayout">
-        <item>
-         <widget class="QLabel" name="ExpectedModemLabel">
+         <widget class="QLabel" name="audioVolumeLabel">
           <property name="text">
-           <string>目标MODEM版本:</string>
+           <string>录音音量:</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QLineEdit" name="expectedModemVerLineEdit">
+         <widget class="QLineEdit" name="audioVolumeLineEdit">
           <property name="statusTip">
-           <string>为空不校验Modem版本是否正确</string>
+           <string>AT^AUDIOTEST音量参数,例如2.0</string>
           </property>
           <property name="alignment">
            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
           </property>
+          <property name="placeholderText">
+           <string>例如: 2.0</string>
+          </property>
          </widget>
         </item>
         <item>
-         <widget class="QLabel" name="ExpectedModemLabel_2">
+         <widget class="QLabel" name="audioLevelStandardLabel">
           <property name="text">
-           <string>目标PY版本:</string>
+           <string>目标录音等级:</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QLineEdit" name="expectedPYVerLineEdit">
+         <widget class="QLineEdit" name="audioLevelStandardLineEdit">
           <property name="statusTip">
-           <string>为空不校验Py版本是否正确</string>
+           <string>填写静音/极低/低/中/高之一:作为最低达标档位(设备上报 mic level 响度不低于该档则本轮通过)。留空不校验等级。</string>
           </property>
           <property name="alignment">
            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
           </property>
+          <property name="placeholderText">
+           <string>例如: 中</string>
+          </property>
          </widget>
         </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="Line" name="line_4">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QVBoxLayout" name="verticalLayout">
         <item>
          <widget class="QLabel" name="ExpectedVerLabel">
           <property name="text">
@@ -465,6 +396,235 @@ background-color: #F57C00;
           </property>
          </widget>
         </item>
+        <item>
+         <widget class="QLabel" name="gsensorDeltaStandardLabel">
+          <property name="text">
+           <string>G-sensor XYZ变化标准(10秒,留空表示不校验):</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="gsensorDeltaXRowLabel">
+          <property name="text">
+           <string>ΔX:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <layout class="QHBoxLayout" name="gsensorDeltaXRowLayout">
+          <property name="spacing">
+           <number>6</number>
+          </property>
+          <item>
+           <widget class="QLineEdit" name="gsensorDeltaXMinLineEdit">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+              <horstretch>1</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="minimumSize">
+             <size>
+              <width>0</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>16777215</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="placeholderText">
+             <string>最小 例如: -1.0</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLabel" name="gsensorDeltaXSepLabel">
+            <property name="text">
+             <string>~</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignCenter</set>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLineEdit" name="gsensorDeltaXMaxLineEdit">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+              <horstretch>1</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="minimumSize">
+             <size>
+              <width>0</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>16777215</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="placeholderText">
+             <string>最大 例如: 1.0</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <widget class="QLabel" name="gsensorDeltaYRowLabel">
+          <property name="text">
+           <string>ΔY:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <layout class="QHBoxLayout" name="gsensorDeltaYRowLayout">
+          <property name="spacing">
+           <number>6</number>
+          </property>
+          <item>
+           <widget class="QLineEdit" name="gsensorDeltaYMinLineEdit">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+              <horstretch>1</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="minimumSize">
+             <size>
+              <width>0</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>16777215</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="placeholderText">
+             <string>最小 例如: -1.0</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLabel" name="gsensorDeltaYSepLabel">
+            <property name="text">
+             <string>~</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignCenter</set>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLineEdit" name="gsensorDeltaYMaxLineEdit">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+              <horstretch>1</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="minimumSize">
+             <size>
+              <width>0</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>16777215</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="placeholderText">
+             <string>最大 例如: 1.0</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <widget class="QLabel" name="gsensorDeltaZRowLabel">
+          <property name="text">
+           <string>ΔZ:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <layout class="QHBoxLayout" name="gsensorDeltaZRowLayout">
+          <property name="spacing">
+           <number>6</number>
+          </property>
+          <item>
+           <widget class="QLineEdit" name="gsensorDeltaZMinLineEdit">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+              <horstretch>1</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="minimumSize">
+             <size>
+              <width>0</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>16777215</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="placeholderText">
+             <string>最小 例如: -1.0</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLabel" name="gsensorDeltaZSepLabel">
+            <property name="text">
+             <string>~</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignCenter</set>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLineEdit" name="gsensorDeltaZMaxLineEdit">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+              <horstretch>1</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="minimumSize">
+             <size>
+              <width>0</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>16777215</width>
+              <height>28</height>
+             </size>
+            </property>
+            <property name="placeholderText">
+             <string>最大 例如: 1.0</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
        </layout>
       </item>
       <item>
@@ -569,65 +729,6 @@ background-color: #F57C00;
           </property>
          </widget>
         </item>
-        <item row="3" column="0">
-         <widget class="QLabel" name="Verlabel_2">
-          <property name="text">
-           <string>IMEI:</string>
-          </property>
-         </widget>
-        </item>
-        <item row="3" column="1">
-         <widget class="QLineEdit" name="IMEIlineEdit">
-          <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-          </property>
-         </widget>
-        </item>
-        <item row="4" column="0">
-         <widget class="QLabel" name="Verlabel_3">
-          <property name="text">
-           <string>ICCID</string>
-          </property>
-         </widget>
-        </item>
-        <item row="4" column="1">
-         <widget class="QLineEdit" name="ICCIDlineEdit">
-          <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-          </property>
-         </widget>
-        </item>
-        <item row="5" column="0">
-         <widget class="QLabel" name="Verlabel_4">
-          <property name="text">
-           <string>Modem:</string>
-          </property>
-         </widget>
-        </item>
-        <item row="5" column="1">
-         <widget class="QLineEdit" name="ModemVerlineEdit">
-          <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-          </property>
-         </widget>
-        </item>
-        <item row="6" column="0">
-         <widget class="QLabel" name="Verlabel_5">
-          <property name="text">
-           <string>PYVER</string>
-          </property>
-         </widget>
-        </item>
-        <item row="6" column="1">
-         <widget class="QLineEdit" name="PYVerlineEdit">
-          <property name="text">
-           <string/>
-          </property>
-          <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-          </property>
-         </widget>
-        </item>
         <item row="1" column="0">
          <widget class="QLabel" name="label">
           <property name="font">
@@ -642,7 +743,7 @@ background-color: #F57C00;
           </property>
          </widget>
         </item>
-        <item row="7" column="1">
+        <item row="3" column="1">
          <widget class="QLineEdit" name="factoryModeLineEdit">
           <property name="font">
            <font>
@@ -664,7 +765,7 @@ background-color: #F57C00;
           </property>
          </widget>
         </item>
-        <item row="7" column="0">
+        <item row="3" column="0">
          <widget class="QLabel" name="factoryModeLabel">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
@@ -732,19 +833,69 @@ background-color: #F57C00;
            <number>15</number>
           </property>
           <item>
-           <layout class="QHBoxLayout" name="lcdTestLayout">
+           <layout class="QHBoxLayout" name="lcdTestLayout">
+            <property name="spacing">
+             <number>4</number>
+            </property>
+            <item>
+             <widget class="QLabel" name="lcdTestLabel">
+              <property name="text">
+               <string>LCD测试: </string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="lcdStatusIcon">
+              <property name="styleSheet">
+               <string>color: #888888; font-size: 18px; font-weight: bold;</string>
+              </property>
+              <property name="text">
+               <string>○</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignCenter</set>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="lcdStatusText">
+              <property name="styleSheet">
+               <string>color: #888888;</string>
+              </property>
+              <property name="text">
+               <string>未测试</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <spacer name="lcdSpacer">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <layout class="QHBoxLayout" name="tpTestLayout">
             <property name="spacing">
              <number>4</number>
             </property>
             <item>
-             <widget class="QLabel" name="lcdTestLabel">
+             <widget class="QLabel" name="tpTestLabel">
               <property name="text">
-               <string>LCD测试: </string>
+               <string>触控测试:</string>
               </property>
              </widget>
             </item>
             <item>
-             <widget class="QLabel" name="lcdStatusIcon">
+             <widget class="QLabel" name="tpStatusIcon">
               <property name="styleSheet">
                <string>color: #888888; font-size: 18px; font-weight: bold;</string>
               </property>
@@ -757,7 +908,7 @@ background-color: #F57C00;
              </widget>
             </item>
             <item>
-             <widget class="QLabel" name="lcdStatusText">
+             <widget class="QLabel" name="tpStatusText">
               <property name="styleSheet">
                <string>color: #888888;</string>
               </property>
@@ -767,7 +918,57 @@ background-color: #F57C00;
              </widget>
             </item>
             <item>
-             <spacer name="lcdSpacer">
+             <spacer name="tpSpacer">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <layout class="QHBoxLayout" name="brightTestLayout">
+            <property name="spacing">
+             <number>4</number>
+            </property>
+            <item>
+             <widget class="QLabel" name="brightTestLabel">
+              <property name="text">
+               <string>亮度测试:</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="brightStatusIcon">
+              <property name="styleSheet">
+               <string>color: #888888; font-size: 18px; font-weight: bold;</string>
+              </property>
+              <property name="text">
+               <string>○</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignCenter</set>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="brightStatusText">
+              <property name="styleSheet">
+               <string>color: #888888;</string>
+              </property>
+              <property name="text">
+               <string>未测试</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <spacer name="brightSpacer">
               <property name="orientation">
                <enum>Qt::Horizontal</enum>
               </property>
@@ -881,6 +1082,56 @@ background-color: #F57C00;
             </item>
            </layout>
           </item>
+          <item>
+           <layout class="QHBoxLayout" name="deepsleepTestLayout">
+            <property name="spacing">
+             <number>4</number>
+            </property>
+            <item>
+             <widget class="QLabel" name="deepsleepTestLabel">
+              <property name="text">
+               <string>功耗测试:</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="deepsleepStatusIcon">
+              <property name="styleSheet">
+               <string>color: #888888; font-size: 18px; font-weight: bold;</string>
+              </property>
+              <property name="text">
+               <string>○</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignCenter</set>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="deepsleepStatusText">
+              <property name="styleSheet">
+               <string>color: #888888;</string>
+              </property>
+              <property name="text">
+               <string>未测试</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <spacer name="deepsleepSpacer">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+           </layout>
+          </item>
           <item>
            <layout class="QHBoxLayout" name="audioTestLayout">
             <property name="spacing">
@@ -1031,6 +1282,56 @@ background-color: #F57C00;
             </item>
            </layout>
           </item>
+          <item>
+           <layout class="QHBoxLayout" name="leaklightTestLayout">
+            <property name="spacing">
+             <number>4</number>
+            </property>
+            <item>
+             <widget class="QLabel" name="leaklightTestLabel">
+              <property name="text">
+               <string>漏光测试:</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="leaklightStatusIcon">
+              <property name="styleSheet">
+               <string>color: #888888; font-size: 18px; font-weight: bold;</string>
+              </property>
+              <property name="text">
+               <string>○</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignCenter</set>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="leaklightStatusText">
+              <property name="styleSheet">
+               <string>color: #888888;</string>
+              </property>
+              <property name="text">
+               <string>未测试</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <spacer name="leaklightSpacer">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+           </layout>
+          </item>
           <item>
            <layout class="QHBoxLayout" name="bluetoothTestLayout">
             <property name="spacing">
@@ -1082,19 +1383,69 @@ background-color: #F57C00;
            </layout>
           </item>
           <item>
-           <layout class="QHBoxLayout" name="pppTestLayout">
+           <layout class="QHBoxLayout" name="sechipTestLayout">
+            <property name="spacing">
+             <number>4</number>
+            </property>
+            <item>
+             <widget class="QLabel" name="sechipTestLabel">
+              <property name="text">
+               <string>SE芯片测试:</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="sechipStatusIcon">
+              <property name="styleSheet">
+               <string>color: #888888; font-size: 18px; font-weight: bold;</string>
+              </property>
+              <property name="text">
+               <string>○</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignCenter</set>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="sechipStatusText">
+              <property name="styleSheet">
+               <string>color: #888888;</string>
+              </property>
+              <property name="text">
+               <string>未测试</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <spacer name="sechipSpacer">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <layout class="QHBoxLayout" name="gpsTestLayout">
             <property name="spacing">
              <number>4</number>
             </property>
             <item>
-             <widget class="QLabel" name="pppTestLabel">
+             <widget class="QLabel" name="gpsTestLabel">
               <property name="text">
-               <string>4G测试:  </string>
+               <string>GPS测试:</string>
               </property>
              </widget>
             </item>
             <item>
-             <widget class="QLabel" name="pppStatusIcon">
+             <widget class="QLabel" name="gpsStatusIcon">
               <property name="styleSheet">
                <string>color: #888888; font-size: 18px; font-weight: bold;</string>
               </property>
@@ -1107,7 +1458,7 @@ background-color: #F57C00;
              </widget>
             </item>
             <item>
-             <widget class="QLabel" name="pppStatusText">
+             <widget class="QLabel" name="gpsStatusText">
               <property name="styleSheet">
                <string>color: #888888;</string>
               </property>
@@ -1117,7 +1468,7 @@ background-color: #F57C00;
              </widget>
             </item>
             <item>
-             <spacer name="pppSpacer">
+             <spacer name="gpsSpacer">
               <property name="orientation">
                <enum>Qt::Horizontal</enum>
               </property>
@@ -1197,59 +1548,87 @@ background-color: #F57C00;
               </property>
              </widget>
             </item>
-            <item row="3" column="0">
+            <item row="2" column="0">
              <widget class="QCheckBox" name="checkBox_lcd">
               <property name="text">
                <string>LCD</string>
               </property>
              </widget>
             </item>
-            <item row="4" column="1">
+            <item row="2" column="1">
+             <widget class="QCheckBox" name="checkBox_tp">
+              <property name="text">
+               <string>触控测试</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="0">
+             <widget class="QCheckBox" name="checkBox_vib">
+              <property name="text">
+               <string>马达</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="1">
              <widget class="QCheckBox" name="checkBox_btn">
               <property name="text">
                <string>按键</string>
               </property>
              </widget>
             </item>
-            <item row="5" column="1">
-             <widget class="QCheckBox" name="checkBox_bt">
+            <item row="4" column="0">
+             <widget class="QCheckBox" name="checkBox_bright">
               <property name="text">
-               <string>蓝牙测试</string>
+               <string>亮度测试</string>
               </property>
              </widget>
             </item>
-            <item row="3" column="1">
-             <widget class="QCheckBox" name="checkBox_ntc">
+            <item row="4" column="1">
+             <widget class="QCheckBox" name="checkBox_mic">
               <property name="text">
-               <string>电池温度</string>
+               <string>录音测试</string>
               </property>
              </widget>
             </item>
             <item row="5" column="0">
-             <widget class="QCheckBox" name="checkBox_mic">
+             <widget class="QCheckBox" name="checkBox_battery">
               <property name="text">
-               <string>录音</string>
+               <string>电池测试</string>
               </property>
              </widget>
             </item>
-            <item row="4" column="0">
-             <widget class="QCheckBox" name="checkBox_vib">
+            <item row="5" column="1">
+             <widget class="QCheckBox" name="checkBox_bt">
               <property name="text">
-               <string>马达</string>
+               <string>蓝牙测试</string>
               </property>
              </widget>
             </item>
             <item row="6" column="0">
-             <widget class="QCheckBox" name="checkBox_4g">
+             <widget class="QCheckBox" name="checkBox_leaklight">
               <property name="text">
-               <string>4G测试</string>
+               <string>漏光测试</string>
               </property>
              </widget>
             </item>
             <item row="6" column="1">
-             <widget class="QCheckBox" name="checkBox_charge">
+             <widget class="QCheckBox" name="checkBox_sechip">
+              <property name="text">
+               <string>SE芯片</string>
+              </property>
+             </widget>
+            </item>
+            <item row="7" column="0">
+             <widget class="QCheckBox" name="checkBox_deepsleep">
+              <property name="text">
+               <string>功耗测试</string>
+              </property>
+             </widget>
+            </item>
+            <item row="7" column="1">
+             <widget class="QCheckBox" name="checkBox_gps">
               <property name="text">
-               <string>充电</string>
+               <string>GPS测试</string>
               </property>
              </widget>
             </item>
@@ -1263,42 +1642,17 @@ background-color: #F57C00;
            </widget>
           </item>
           <item>
-           <layout class="QHBoxLayout" name="horizontalLayout_2">
-            <item>
-             <layout class="QVBoxLayout" name="verticalLayout_4">
-              <item>
-               <widget class="QCheckBox" name="checkBox_iccid">
-                <property name="text">
-                 <string>ICCID 二维码</string>
-                </property>
-               </widget>
-              </item>
-              <item>
-               <widget class="QGraphicsView" name="graphicsView_iccid_qr"/>
-              </item>
-              <item>
-               <widget class="QGraphicsView" name="graphicsView_iccid_barcode"/>
-              </item>
-             </layout>
-            </item>
-            <item>
-             <layout class="QVBoxLayout" name="verticalLayout_2">
-              <item>
-               <widget class="QCheckBox" name="checkBox_imei">
-                <property name="text">
-                 <string>IMEI 二维码</string>
-                </property>
-               </widget>
-              </item>
-              <item>
-               <widget class="QGraphicsView" name="graphicsView_imei_qr"/>
-              </item>
-              <item>
-               <widget class="QGraphicsView" name="graphicsView_imei_barcode"/>
-              </item>
-             </layout>
-            </item>
-           </layout>
+           <spacer name="codesRemovedSpacer">
+            <property name="orientation">
+             <enum>Qt::Vertical</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>20</width>
+              <height>10</height>
+             </size>
+            </property>
+           </spacer>
           </item>
           <item>
            <spacer name="testStatusBottomSpacer">
diff --git a/qrcode_barcode_generator.cpp b/qrcode_barcode_generator.cpp
deleted file mode 100644 (file)
index 9af6a4f..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-#include "qrcode_barcode_generator.h"
-#include "qrcodegen.hpp"
-#include <QPixmap>
-#include <QPainter>
-#include <QPen>
-#include <QBrush>
-#include <QRect>
-#include <QFont>
-#include <QByteArray>
-
-using qrcodegen::QrCode;
-
-// 使用 qrcodegen 库创建真正可识别的二维码图片
-QPixmap QRCodeBarcodeGenerator::createQRCodePixmap(const QString& text, int size)
-{
-    // 防御性检查
-    if (text.isEmpty() || size <= 0) {
-        return QPixmap();
-    }
-
-    QPixmap pixmap(size, size);
-    pixmap.fill(Qt::white);
-
-    // 使用 qrcodegen 生成 QR 矩阵
-    QByteArray utf8 = text.toUtf8();
-    QrCode qr = QrCode::encodeText(utf8.constData(), QrCode::Ecc::LOW);
-    int modules = qr.getSize();          // 模块数(21~177)
-
-    // 计算模块尺寸和静区(白边),保证每个模块足够大且居中
-    int marginModules = 4;               // 静区 4 个模块
-    int totalModules = modules + marginModules * 2;
-    int moduleSize = size / totalModules;
-    if (moduleSize < 2) {
-        moduleSize = 2;                  // 每个模块至少 2 像素
-    }
-    int qrSize = moduleSize * totalModules;
-    int offset = (size - qrSize) / 2;    // 居中偏移
-
-    QPainter painter(&pixmap);
-    painter.setRenderHint(QPainter::Antialiasing, false);          // 关闭抗锯齿,保持锐利
-    painter.setRenderHint(QPainter::SmoothPixmapTransform, false);
-    painter.setPen(Qt::NoPen);
-    painter.setBrush(Qt::black);
-
-    // 绘制所有为 1 的模块
-    for (int y = 0; y < modules; ++y) {
-        for (int x = 0; x < modules; ++x) {
-            if (qr.getModule(x, y)) {
-                int px = offset + (x + marginModules) * moduleSize;
-                int py = offset + (y + marginModules) * moduleSize;
-                painter.drawRect(px, py, moduleSize, moduleSize);
-            }
-        }
-    }
-
-    return pixmap;
-}
-
-// 创建条形码图片(Code128格式简化版)
-QPixmap QRCodeBarcodeGenerator::createBarcodePixmap(const QString& text, int width, int height)
-{
-    QPixmap pixmap(width, height);
-    pixmap.fill(Qt::white);
-    
-    QPainter painter(&pixmap);
-    painter.setRenderHint(QPainter::Antialiasing, false); // 关闭抗锯齿,使条形码更清晰
-    painter.setRenderHint(QPainter::SmoothPixmapTransform, false);
-    
-    // 计算条形码区域(留出文本空间)
-    int margin = 10;
-    int textHeight = 20;
-    int barAreaHeight = height - 2 * margin - textHeight;
-    int barY = margin;
-    
-    // 根据宽度和文本长度动态计算条宽,确保条形码清晰可扫
-    QByteArray data = text.toUtf8();
-    int totalBars = data.length() * 8 + 6; // 数据条 + 起始码 + 结束码
-    int availableWidth = width - 2 * margin;
-    
-    // 计算条宽,确保至少2像素,最多不超过5像素
-    int barWidth = qMax(2, qMin(5, availableWidth / totalBars));
-    if (barWidth < 2) {
-        barWidth = 2; // 最小条宽
-    }
-    
-    int x = margin;
-    
-    painter.setPen(QPen(Qt::black, barWidth));
-    painter.setBrush(QBrush(Qt::black));
-    
-    // 起始码(两个宽条)
-    drawBar(&painter, x, barY, barWidth * 2, barAreaHeight);
-    x += barWidth * 3;
-    drawBar(&painter, x, barY, barWidth * 2, barAreaHeight);
-    x += barWidth * 3;
-    
-    // 根据文本内容生成条形码模式
-    for (int i = 0; i < data.length() && x < width - margin - barWidth * 6; i++) {
-        unsigned char ch = data[i];
-        // 每个字符用8个条表示(1个条 = 1位)
-        for (int bit = 0; bit < 8; bit++) {
-            if (x >= width - margin - barWidth * 6) break;
-            
-            bool isBlack = (ch >> (7 - bit)) & 1;
-            if (isBlack) {
-                drawBar(&painter, x, barY, barWidth, barAreaHeight);
-            }
-            x += barWidth;
-        }
-    }
-    
-    // 结束码(两个宽条)
-    if (x < width - margin - barWidth * 6) {
-        drawBar(&painter, x, barY, barWidth * 2, barAreaHeight);
-        x += barWidth * 3;
-        drawBar(&painter, x, barY, barWidth * 2, barAreaHeight);
-    }
-    
-    // 在底部添加文本
-    painter.setPen(QPen(Qt::black, 1));
-    QFont font = painter.font();
-    font.setPointSize(qMax(8, height / 15)); // 根据高度调整字体大小
-    painter.setFont(font);
-    QRect textRect(margin, height - textHeight, width - 2 * margin, textHeight);
-    painter.drawText(textRect, Qt::AlignCenter, text);
-    
-    return pixmap;
-}
-
-// 绘制定位图案(QR码的三个角)
-void QRCodeBarcodeGenerator::drawFinderPattern(QPainter* painter, int x, int y, int size, int moduleSize)
-{
-    // 外框(7x7模块)
-    painter->fillRect(x, y, size, size, Qt::black);
-    // 内框(5x5模块)
-    painter->fillRect(x + moduleSize, y + moduleSize, size - 2 * moduleSize, 
-                     size - 2 * moduleSize, Qt::white);
-    // 中心点(3x3模块)
-    painter->fillRect(x + 2 * moduleSize, y + 2 * moduleSize, size - 4 * moduleSize, 
-                     size - 4 * moduleSize, Qt::black);
-}
-
-// 绘制条形码的一条
-void QRCodeBarcodeGenerator::drawBar(QPainter* painter, int x, int y, int width, int height)
-{
-    painter->fillRect(x, y, width, height, Qt::black);
-}
-
diff --git a/qrcode_barcode_generator.h b/qrcode_barcode_generator.h
deleted file mode 100644 (file)
index 10276a0..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef QRCODE_BARCODE_GENERATOR_H
-#define QRCODE_BARCODE_GENERATOR_H
-
-#include <QPixmap>
-#include <QString>
-
-class QRCodeBarcodeGenerator
-{
-public:
-    // 创建二维码图片(简化版本,使用简单的矩阵模式)
-    static QPixmap createQRCodePixmap(const QString& text, int size);
-    
-    // 创建条形码图片(Code128格式简化版)
-    static QPixmap createBarcodePixmap(const QString& text, int width, int height);
-
-private:
-    // 绘制定位图案(QR码的三个角)
-    static void drawFinderPattern(class QPainter* painter, int x, int y, int size, int moduleSize);
-    
-    // 绘制条形码的一条
-    static void drawBar(class QPainter* painter, int x, int y, int width, int height);
-};
-
-#endif // QRCODE_BARCODE_GENERATOR_H
-
diff --git a/qrcodegen.cpp b/qrcodegen.cpp
deleted file mode 100644 (file)
index 693e7ff..0000000
+++ /dev/null
@@ -1,829 +0,0 @@
-/*
- * QR Code generator library (C++)
- *
- * Copyright (c) Project Nayuki. (MIT License)
- * https://www.nayuki.io/page/qr-code-generator-library
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- * the Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- * - The above copyright notice and this permission notice shall be included in
- *   all copies or substantial portions of the Software.
- * - The Software is provided "as is", without warranty of any kind, express or
- *   implied, including but not limited to the warranties of merchantability,
- *   fitness for a particular purpose and noninfringement. In no event shall the
- *   authors or copyright holders be liable for any claim, damages or other
- *   liability, whether in an action of contract, tort or otherwise, arising from,
- *   out of or in connection with the Software or the use or other dealings in the
- *   Software.
- */
-
-#include <algorithm>
-#include <cassert>
-#include <climits>
-#include <cstddef>
-#include <cstdlib>
-#include <cstring>
-#include <sstream>
-#include <utility>
-#include "qrcodegen.hpp"
-
-using std::int8_t;
-using std::uint8_t;
-using std::size_t;
-using std::vector;
-
-
-namespace qrcodegen {
-
-/*---- Class QrSegment ----*/
-
-QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) :
-        modeBits(mode) {
-    numBitsCharCount[0] = cc0;
-    numBitsCharCount[1] = cc1;
-    numBitsCharCount[2] = cc2;
-}
-
-
-int QrSegment::Mode::getModeBits() const {
-    return modeBits;
-}
-
-
-int QrSegment::Mode::numCharCountBits(int ver) const {
-    return numBitsCharCount[(ver + 7) / 17];
-}
-
-
-const QrSegment::Mode QrSegment::Mode::NUMERIC     (0x1, 10, 12, 14);
-const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2,  9, 11, 13);
-const QrSegment::Mode QrSegment::Mode::BYTE        (0x4,  8, 16, 16);
-const QrSegment::Mode QrSegment::Mode::KANJI       (0x8,  8, 10, 12);
-const QrSegment::Mode QrSegment::Mode::ECI         (0x7,  0,  0,  0);
-
-
-QrSegment QrSegment::makeBytes(const vector<uint8_t> &data) {
-    if (data.size() > static_cast<unsigned int>(INT_MAX))
-        throw std::length_error("Data too long");
-    BitBuffer bb;
-    for (uint8_t b : data)
-        bb.appendBits(b, 8);
-    return QrSegment(Mode::BYTE, static_cast<int>(data.size()), std::move(bb));
-}
-
-
-QrSegment QrSegment::makeNumeric(const char *digits) {
-    BitBuffer bb;
-    int accumData = 0;
-    int accumCount = 0;
-    int charCount = 0;
-    for (; *digits != '\0'; digits++, charCount++) {
-        char c = *digits;
-        if (c < '0' || c > '9')
-            throw std::domain_error("String contains non-numeric characters");
-        accumData = accumData * 10 + (c - '0');
-        accumCount++;
-        if (accumCount == 3) {
-            bb.appendBits(static_cast<uint32_t>(accumData), 10);
-            accumData = 0;
-            accumCount = 0;
-        }
-    }
-    if (accumCount > 0)  // 1 or 2 digits remaining
-        bb.appendBits(static_cast<uint32_t>(accumData), accumCount * 3 + 1);
-    return QrSegment(Mode::NUMERIC, charCount, std::move(bb));
-}
-
-
-QrSegment QrSegment::makeAlphanumeric(const char *text) {
-    BitBuffer bb;
-    int accumData = 0;
-    int accumCount = 0;
-    int charCount = 0;
-    for (; *text != '\0'; text++, charCount++) {
-        const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text);
-        if (temp == nullptr)
-            throw std::domain_error("String contains unencodable characters in alphanumeric mode");
-        accumData = accumData * 45 + static_cast<int>(temp - ALPHANUMERIC_CHARSET);
-        accumCount++;
-        if (accumCount == 2) {
-            bb.appendBits(static_cast<uint32_t>(accumData), 11);
-            accumData = 0;
-            accumCount = 0;
-        }
-    }
-    if (accumCount > 0)  // 1 character remaining
-        bb.appendBits(static_cast<uint32_t>(accumData), 6);
-    return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb));
-}
-
-
-vector<QrSegment> QrSegment::makeSegments(const char *text) {
-    // Select the most efficient segment encoding automatically
-    vector<QrSegment> result;
-    if (*text == '\0');  // Leave result empty
-    else if (isNumeric(text))
-        result.push_back(makeNumeric(text));
-    else if (isAlphanumeric(text))
-        result.push_back(makeAlphanumeric(text));
-    else {
-        vector<uint8_t> bytes;
-        for (; *text != '\0'; text++)
-            bytes.push_back(static_cast<uint8_t>(*text));
-        result.push_back(makeBytes(bytes));
-    }
-    return result;
-}
-
-
-QrSegment QrSegment::makeEci(long assignVal) {
-    BitBuffer bb;
-    if (assignVal < 0)
-        throw std::domain_error("ECI assignment value out of range");
-    else if (assignVal < (1 << 7))
-        bb.appendBits(static_cast<uint32_t>(assignVal), 8);
-    else if (assignVal < (1 << 14)) {
-        bb.appendBits(2, 2);
-        bb.appendBits(static_cast<uint32_t>(assignVal), 14);
-    } else if (assignVal < 1000000L) {
-        bb.appendBits(6, 3);
-        bb.appendBits(static_cast<uint32_t>(assignVal), 21);
-    } else
-        throw std::domain_error("ECI assignment value out of range");
-    return QrSegment(Mode::ECI, 0, std::move(bb));
-}
-
-
-QrSegment::QrSegment(const Mode &md, int numCh, const std::vector<bool> &dt) :
-        mode(&md),
-        numChars(numCh),
-        data(dt) {
-    if (numCh < 0)
-        throw std::domain_error("Invalid value");
-}
-
-
-QrSegment::QrSegment(const Mode &md, int numCh, std::vector<bool> &&dt) :
-        mode(&md),
-        numChars(numCh),
-        data(std::move(dt)) {
-    if (numCh < 0)
-        throw std::domain_error("Invalid value");
-}
-
-
-int QrSegment::getTotalBits(const vector<QrSegment> &segs, int version) {
-    int result = 0;
-    for (const QrSegment &seg : segs) {
-        int ccbits = seg.mode->numCharCountBits(version);
-        if (seg.numChars >= (1L << ccbits))
-            return -1;  // The segment's length doesn't fit the field's bit width
-        if (4 + ccbits > INT_MAX - result)
-            return -1;  // The sum will overflow an int type
-        result += 4 + ccbits;
-        if (seg.data.size() > static_cast<unsigned int>(INT_MAX - result))
-            return -1;  // The sum will overflow an int type
-        result += static_cast<int>(seg.data.size());
-    }
-    return result;
-}
-
-
-bool QrSegment::isNumeric(const char *text) {
-    for (; *text != '\0'; text++) {
-        char c = *text;
-        if (c < '0' || c > '9')
-            return false;
-    }
-    return true;
-}
-
-
-bool QrSegment::isAlphanumeric(const char *text) {
-    for (; *text != '\0'; text++) {
-        if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr)
-            return false;
-    }
-    return true;
-}
-
-
-const QrSegment::Mode &QrSegment::getMode() const {
-    return *mode;
-}
-
-
-int QrSegment::getNumChars() const {
-    return numChars;
-}
-
-
-const std::vector<bool> &QrSegment::getData() const {
-    return data;
-}
-
-
-const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
-
-
-
-/*---- Class QrCode ----*/
-
-int QrCode::getFormatBits(Ecc ecl) {
-    switch (ecl) {
-        case Ecc::LOW     :  return 1;
-        case Ecc::MEDIUM  :  return 0;
-        case Ecc::QUARTILE:  return 3;
-        case Ecc::HIGH    :  return 2;
-        default:  throw std::logic_error("Unreachable");
-    }
-}
-
-
-QrCode QrCode::encodeText(const char *text, Ecc ecl) {
-    vector<QrSegment> segs = QrSegment::makeSegments(text);
-    return encodeSegments(segs, ecl);
-}
-
-
-QrCode QrCode::encodeBinary(const vector<uint8_t> &data, Ecc ecl) {
-    vector<QrSegment> segs{QrSegment::makeBytes(data)};
-    return encodeSegments(segs, ecl);
-}
-
-
-QrCode QrCode::encodeSegments(const vector<QrSegment> &segs, Ecc ecl,
-        int minVersion, int maxVersion, int mask, bool boostEcl) {
-    if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7)
-        throw std::invalid_argument("Invalid value");
-
-    // Find the minimal version number to use
-    int version, dataUsedBits;
-    for (version = minVersion; ; version++) {
-        int dataCapacityBits = getNumDataCodewords(version, ecl) * 8;  // Number of data bits available
-        dataUsedBits = QrSegment::getTotalBits(segs, version);
-        if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits)
-            break;  // This version number is found to be suitable
-        if (version >= maxVersion) {  // All versions in the range could not fit the given data
-            std::ostringstream sb;
-            if (dataUsedBits == -1)
-                sb << "Segment too long";
-            else {
-                sb << "Data length = " << dataUsedBits << " bits, ";
-                sb << "Max capacity = " << dataCapacityBits << " bits";
-            }
-            throw data_too_long(sb.str());
-        }
-    }
-    assert(dataUsedBits != -1);
-
-    // Increase the error correction level while the data still fits in the current version number
-    for (Ecc newEcl : {Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) {  // From low to high
-        if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8)
-            ecl = newEcl;
-    }
-
-    // Concatenate all segments to create the data bit string
-    BitBuffer bb;
-    for (const QrSegment &seg : segs) {
-        bb.appendBits(static_cast<uint32_t>(seg.getMode().getModeBits()), 4);
-        bb.appendBits(static_cast<uint32_t>(seg.getNumChars()), seg.getMode().numCharCountBits(version));
-        bb.insert(bb.end(), seg.getData().begin(), seg.getData().end());
-    }
-    assert(bb.size() == static_cast<unsigned int>(dataUsedBits));
-
-    // Add terminator and pad up to a byte if applicable
-    size_t dataCapacityBits = static_cast<size_t>(getNumDataCodewords(version, ecl)) * 8;
-    assert(bb.size() <= dataCapacityBits);
-    bb.appendBits(0, std::min(4, static_cast<int>(dataCapacityBits - bb.size())));
-    bb.appendBits(0, (8 - static_cast<int>(bb.size() % 8)) % 8);
-    assert(bb.size() % 8 == 0);
-
-    // Pad with alternating bytes until data capacity is reached
-    for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11)
-        bb.appendBits(padByte, 8);
-
-    // Pack bits into bytes in big endian
-    vector<uint8_t> dataCodewords(bb.size() / 8);
-    for (size_t i = 0; i < bb.size(); i++)
-        dataCodewords.at(i >> 3) |= (bb.at(i) ? 1 : 0) << (7 - (i & 7));
-
-    // Create the QR Code object
-    return QrCode(version, ecl, dataCodewords, mask);
-}
-
-
-QrCode::QrCode(int ver, Ecc ecl, const vector<uint8_t> &dataCodewords, int msk) :
-        // Initialize fields and check arguments
-        version(ver),
-        errorCorrectionLevel(ecl) {
-    if (ver < MIN_VERSION || ver > MAX_VERSION)
-        throw std::domain_error("Version value out of range");
-    if (msk < -1 || msk > 7)
-        throw std::domain_error("Mask value out of range");
-    size = ver * 4 + 17;
-    size_t sz = static_cast<size_t>(size);
-    modules    = vector<vector<bool> >(sz, vector<bool>(sz));  // Initially all light
-    isFunction = vector<vector<bool> >(sz, vector<bool>(sz));
-
-    // Compute ECC, draw modules
-    drawFunctionPatterns();
-    const vector<uint8_t> allCodewords = addEccAndInterleave(dataCodewords);
-    drawCodewords(allCodewords);
-
-    // Do masking
-    if (msk == -1) {  // Automatically choose best mask
-        long minPenalty = LONG_MAX;
-        for (int i = 0; i < 8; i++) {
-            applyMask(i);
-            drawFormatBits(i);
-            long penalty = getPenaltyScore();
-            if (penalty < minPenalty) {
-                msk = i;
-                minPenalty = penalty;
-            }
-            applyMask(i);  // Undoes the mask due to XOR
-        }
-    }
-    assert(0 <= msk && msk <= 7);
-    mask = msk;
-    applyMask(msk);  // Apply the final choice of mask
-    drawFormatBits(msk);  // Overwrite old format bits
-
-    isFunction.clear();
-    isFunction.shrink_to_fit();
-}
-
-
-int QrCode::getVersion() const {
-    return version;
-}
-
-
-int QrCode::getSize() const {
-    return size;
-}
-
-
-QrCode::Ecc QrCode::getErrorCorrectionLevel() const {
-    return errorCorrectionLevel;
-}
-
-
-int QrCode::getMask() const {
-    return mask;
-}
-
-
-bool QrCode::getModule(int x, int y) const {
-    return 0 <= x && x < size && 0 <= y && y < size && module(x, y);
-}
-
-
-void QrCode::drawFunctionPatterns() {
-    // Draw horizontal and vertical timing patterns
-    for (int i = 0; i < size; i++) {
-        setFunctionModule(6, i, i % 2 == 0);
-        setFunctionModule(i, 6, i % 2 == 0);
-    }
-
-    // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
-    drawFinderPattern(3, 3);
-    drawFinderPattern(size - 4, 3);
-    drawFinderPattern(3, size - 4);
-
-    // Draw numerous alignment patterns
-    const vector<int> alignPatPos = getAlignmentPatternPositions();
-    size_t numAlign = alignPatPos.size();
-    for (size_t i = 0; i < numAlign; i++) {
-        for (size_t j = 0; j < numAlign; j++) {
-            // Don't draw on the three finder corners
-            if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)))
-                drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j));
-        }
-    }
-
-    // Draw configuration data
-    drawFormatBits(0);  // Dummy mask value; overwritten later in the constructor
-    drawVersion();
-}
-
-
-void QrCode::drawFormatBits(int msk) {
-    // Calculate error correction code and pack bits
-    int data = getFormatBits(errorCorrectionLevel) << 3 | msk;  // errCorrLvl is uint2, msk is uint3
-    int rem = data;
-    for (int i = 0; i < 10; i++)
-        rem = (rem << 1) ^ ((rem >> 9) * 0x537);
-    int bits = (data << 10 | rem) ^ 0x5412;  // uint15
-    assert(bits >> 15 == 0);
-
-    // Draw first copy
-    for (int i = 0; i <= 5; i++)
-        setFunctionModule(8, i, getBit(bits, i));
-    setFunctionModule(8, 7, getBit(bits, 6));
-    setFunctionModule(8, 8, getBit(bits, 7));
-    setFunctionModule(7, 8, getBit(bits, 8));
-    for (int i = 9; i < 15; i++)
-        setFunctionModule(14 - i, 8, getBit(bits, i));
-
-    // Draw second copy
-    for (int i = 0; i < 8; i++)
-        setFunctionModule(size - 1 - i, 8, getBit(bits, i));
-    for (int i = 8; i < 15; i++)
-        setFunctionModule(8, size - 15 + i, getBit(bits, i));
-    setFunctionModule(8, size - 8, true);  // Always dark
-}
-
-
-void QrCode::drawVersion() {
-    if (version < 7)
-        return;
-
-    // Calculate error correction code and pack bits
-    int rem = version;  // version is uint6, in the range [7, 40]
-    for (int i = 0; i < 12; i++)
-        rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
-    long bits = static_cast<long>(version) << 12 | rem;  // uint18
-    assert(bits >> 18 == 0);
-
-    // Draw two copies
-    for (int i = 0; i < 18; i++) {
-        bool bit = getBit(bits, i);
-        int a = size - 11 + i % 3;
-        int b = i / 3;
-        setFunctionModule(a, b, bit);
-        setFunctionModule(b, a, bit);
-    }
-}
-
-
-void QrCode::drawFinderPattern(int x, int y) {
-    for (int dy = -4; dy <= 4; dy++) {
-        for (int dx = -4; dx <= 4; dx++) {
-            int dist = std::max(std::abs(dx), std::abs(dy));  // Chebyshev/infinity norm
-            int xx = x + dx, yy = y + dy;
-            if (0 <= xx && xx < size && 0 <= yy && yy < size)
-                setFunctionModule(xx, yy, dist != 2 && dist != 4);
-        }
-    }
-}
-
-
-void QrCode::drawAlignmentPattern(int x, int y) {
-    for (int dy = -2; dy <= 2; dy++) {
-        for (int dx = -2; dx <= 2; dx++)
-            setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1);
-    }
-}
-
-
-void QrCode::setFunctionModule(int x, int y, bool isDark) {
-    size_t ux = static_cast<size_t>(x);
-    size_t uy = static_cast<size_t>(y);
-    modules   .at(uy).at(ux) = isDark;
-    isFunction.at(uy).at(ux) = true;
-}
-
-
-bool QrCode::module(int x, int y) const {
-    return modules.at(static_cast<size_t>(y)).at(static_cast<size_t>(x));
-}
-
-
-vector<uint8_t> QrCode::addEccAndInterleave(const vector<uint8_t> &data) const {
-    if (data.size() != static_cast<unsigned int>(getNumDataCodewords(version, errorCorrectionLevel)))
-        throw std::invalid_argument("Invalid argument");
-
-    // Calculate parameter numbers
-    int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(errorCorrectionLevel)][version];
-    int blockEccLen = ECC_CODEWORDS_PER_BLOCK  [static_cast<int>(errorCorrectionLevel)][version];
-    int rawCodewords = getNumRawDataModules(version) / 8;
-    int numShortBlocks = numBlocks - rawCodewords % numBlocks;
-    int shortBlockLen = rawCodewords / numBlocks;
-
-    // Split data into blocks and append ECC to each block
-    vector<vector<uint8_t> > blocks;
-    const vector<uint8_t> rsDiv = reedSolomonComputeDivisor(blockEccLen);
-    for (int i = 0, k = 0; i < numBlocks; i++) {
-        vector<uint8_t> dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)));
-        k += static_cast<int>(dat.size());
-        const vector<uint8_t> ecc = reedSolomonComputeRemainder(dat, rsDiv);
-        if (i < numShortBlocks)
-            dat.push_back(0);
-        dat.insert(dat.end(), ecc.cbegin(), ecc.cend());
-        blocks.push_back(std::move(dat));
-    }
-
-    // Interleave (not concatenate) the bytes from every block into a single sequence
-    vector<uint8_t> result;
-    for (size_t i = 0; i < blocks.at(0).size(); i++) {
-        for (size_t j = 0; j < blocks.size(); j++) {
-            // Skip the padding byte in short blocks
-            if (i != static_cast<unsigned int>(shortBlockLen - blockEccLen) || j >= static_cast<unsigned int>(numShortBlocks))
-                result.push_back(blocks.at(j).at(i));
-        }
-    }
-    assert(result.size() == static_cast<unsigned int>(rawCodewords));
-    return result;
-}
-
-
-void QrCode::drawCodewords(const vector<uint8_t> &data) {
-    if (data.size() != static_cast<unsigned int>(getNumRawDataModules(version) / 8))
-        throw std::invalid_argument("Invalid argument");
-
-    size_t i = 0;  // Bit index into the data
-    // Do the funny zigzag scan
-    for (int right = size - 1; right >= 1; right -= 2) {  // Index of right column in each column pair
-        if (right == 6)
-            right = 5;
-        for (int vert = 0; vert < size; vert++) {  // Vertical counter
-            for (int j = 0; j < 2; j++) {
-                size_t x = static_cast<size_t>(right - j);  // Actual x coordinate
-                bool upward = ((right + 1) & 2) == 0;
-                size_t y = static_cast<size_t>(upward ? size - 1 - vert : vert);  // Actual y coordinate
-                if (!isFunction.at(y).at(x) && i < data.size() * 8) {
-                    modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast<int>(i & 7));
-                    i++;
-                }
-                // If this QR Code has any remainder bits (0 to 7), they were assigned as
-                // 0/false/light by the constructor and are left unchanged by this method
-            }
-        }
-    }
-    assert(i == data.size() * 8);
-}
-
-
-void QrCode::applyMask(int msk) {
-    if (msk < 0 || msk > 7)
-        throw std::domain_error("Mask value out of range");
-    size_t sz = static_cast<size_t>(size);
-    for (size_t y = 0; y < sz; y++) {
-        for (size_t x = 0; x < sz; x++) {
-            bool invert;
-            switch (msk) {
-                case 0:  invert = (x + y) % 2 == 0;                    break;
-                case 1:  invert = y % 2 == 0;                          break;
-                case 2:  invert = x % 3 == 0;                          break;
-                case 3:  invert = (x + y) % 3 == 0;                    break;
-                case 4:  invert = (x / 3 + y / 2) % 2 == 0;            break;
-                case 5:  invert = x * y % 2 + x * y % 3 == 0;          break;
-                case 6:  invert = (x * y % 2 + x * y % 3) % 2 == 0;    break;
-                case 7:  invert = ((x + y) % 2 + x * y % 3) % 2 == 0;  break;
-                default:  throw std::logic_error("Unreachable");
-            }
-            modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x));
-        }
-    }
-}
-
-
-long QrCode::getPenaltyScore() const {
-    long result = 0;
-
-    // Adjacent modules in row having same color, and finder-like patterns
-    for (int y = 0; y < size; y++) {
-        bool runColor = false;
-        int runX = 0;
-        std::array<int,7> runHistory = {};
-        for (int x = 0; x < size; x++) {
-            if (module(x, y) == runColor) {
-                runX++;
-                if (runX == 5)
-                    result += PENALTY_N1;
-                else if (runX > 5)
-                    result++;
-            } else {
-                finderPenaltyAddHistory(runX, runHistory);
-                if (!runColor)
-                    result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
-                runColor = module(x, y);
-                runX = 1;
-            }
-        }
-        result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3;
-    }
-    // Adjacent modules in column having same color, and finder-like patterns
-    for (int x = 0; x < size; x++) {
-        bool runColor = false;
-        int runY = 0;
-        std::array<int,7> runHistory = {};
-        for (int y = 0; y < size; y++) {
-            if (module(x, y) == runColor) {
-                runY++;
-                if (runY == 5)
-                    result += PENALTY_N1;
-                else if (runY > 5)
-                    result++;
-            } else {
-                finderPenaltyAddHistory(runY, runHistory);
-                if (!runColor)
-                    result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
-                runColor = module(x, y);
-                runY = 1;
-            }
-        }
-        result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3;
-    }
-
-    // 2*2 blocks of modules having same color
-    for (int y = 0; y < size - 1; y++) {
-        for (int x = 0; x < size - 1; x++) {
-            bool  color = module(x, y);
-            if (  color == module(x + 1, y) &&
-                  color == module(x, y + 1) &&
-                  color == module(x + 1, y + 1))
-                result += PENALTY_N2;
-        }
-    }
-
-    // Balance of dark and light modules
-    int dark = 0;
-    for (const vector<bool> &row : modules) {
-        for (bool color : row) {
-            if (color)
-                dark++;
-        }
-    }
-    int total = size * size;  // Note that size is odd, so dark/total != 1/2
-    // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)%
-    int k = static_cast<int>((std::abs(dark * 20L - total * 10L) + total - 1) / total) - 1;
-    assert(0 <= k && k <= 9);
-    result += k * PENALTY_N4;
-    assert(0 <= result && result <= 2568888L);  // Non-tight upper bound based on default values of PENALTY_N1, ..., N4
-    return result;
-}
-
-
-vector<int> QrCode::getAlignmentPatternPositions() const {
-    if (version == 1)
-        return vector<int>();
-    else {
-        int numAlign = version / 7 + 2;
-        int step = (version * 8 + numAlign * 3 + 5) / (numAlign * 4 - 4) * 2;
-        vector<int> result;
-        for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step)
-            result.insert(result.begin(), pos);
-        result.insert(result.begin(), 6);
-        return result;
-    }
-}
-
-
-int QrCode::getNumRawDataModules(int ver) {
-    if (ver < MIN_VERSION || ver > MAX_VERSION)
-        throw std::domain_error("Version number out of range");
-    int result = (16 * ver + 128) * ver + 64;
-    if (ver >= 2) {
-        int numAlign = ver / 7 + 2;
-        result -= (25 * numAlign - 10) * numAlign - 55;
-        if (ver >= 7)
-            result -= 36;
-    }
-    assert(208 <= result && result <= 29648);
-    return result;
-}
-
-
-int QrCode::getNumDataCodewords(int ver, Ecc ecl) {
-    return getNumRawDataModules(ver) / 8
-        - ECC_CODEWORDS_PER_BLOCK    [static_cast<int>(ecl)][ver]
-        * NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(ecl)][ver];
-}
-
-
-vector<uint8_t> QrCode::reedSolomonComputeDivisor(int degree) {
-    if (degree < 1 || degree > 255)
-        throw std::domain_error("Degree out of range");
-    // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.
-    // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}.
-    vector<uint8_t> result(static_cast<size_t>(degree));
-    result.at(result.size() - 1) = 1;  // Start off with the monomial x^0
-
-    // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
-    // and drop the highest monomial term which is always 1x^degree.
-    // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
-    uint8_t root = 1;
-    for (int i = 0; i < degree; i++) {
-        // Multiply the current product by (x - r^i)
-        for (size_t j = 0; j < result.size(); j++) {
-            result.at(j) = reedSolomonMultiply(result.at(j), root);
-            if (j + 1 < result.size())
-                result.at(j) ^= result.at(j + 1);
-        }
-        root = reedSolomonMultiply(root, 0x02);
-    }
-    return result;
-}
-
-
-vector<uint8_t> QrCode::reedSolomonComputeRemainder(const vector<uint8_t> &data, const vector<uint8_t> &divisor) {
-    vector<uint8_t> result(divisor.size());
-    for (uint8_t b : data) {  // Polynomial division
-        uint8_t factor = b ^ result.at(0);
-        result.erase(result.begin());
-        result.push_back(0);
-        for (size_t i = 0; i < result.size(); i++)
-            result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor);
-    }
-    return result;
-}
-
-
-uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) {
-    // Russian peasant multiplication
-    int z = 0;
-    for (int i = 7; i >= 0; i--) {
-        z = (z << 1) ^ ((z >> 7) * 0x11D);
-        z ^= ((y >> i) & 1) * x;
-    }
-    assert(z >> 8 == 0);
-    return static_cast<uint8_t>(z);
-}
-
-
-int QrCode::finderPenaltyCountPatterns(const std::array<int,7> &runHistory) const {
-    int n = runHistory.at(1);
-    assert(n <= size * 3);
-    bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n;
-    return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0)
-         + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0);
-}
-
-
-int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &runHistory) const {
-    if (currentRunColor) {  // Terminate dark run
-        finderPenaltyAddHistory(currentRunLength, runHistory);
-        currentRunLength = 0;
-    }
-    currentRunLength += size;  // Add light border to final run
-    finderPenaltyAddHistory(currentRunLength, runHistory);
-    return finderPenaltyCountPatterns(runHistory);
-}
-
-
-void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array<int,7> &runHistory) const {
-    if (runHistory.at(0) == 0)
-        currentRunLength += size;  // Add light border to initial run
-    std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end());
-    runHistory.at(0) = currentRunLength;
-}
-
-
-bool QrCode::getBit(long x, int i) {
-    return ((x >> i) & 1) != 0;
-}
-
-
-/*---- Tables of constants ----*/
-
-const int QrCode::PENALTY_N1 =  3;
-const int QrCode::PENALTY_N2 =  3;
-const int QrCode::PENALTY_N3 = 40;
-const int QrCode::PENALTY_N4 = 10;
-
-
-const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = {
-    // Version: (note that index 0 is for padding, and is set to an illegal value)
-    //0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
-    {-1,  7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},  // Low
-    {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},  // Medium
-    {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},  // Quartile
-    {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},  // High
-};
-
-const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = {
-    // Version: (note that index 0 is for padding, and is set to an illegal value)
-    //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
-    {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4,  4,  4,  4,  4,  6,  6,  6,  6,  7,  8,  8,  9,  9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25},  // Low
-    {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5,  5,  8,  9,  9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49},  // Medium
-    {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8,  8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68},  // Quartile
-    {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81},  // High
-};
-
-
-data_too_long::data_too_long(const std::string &msg) :
-    std::length_error(msg) {}
-
-
-
-/*---- Class BitBuffer ----*/
-
-BitBuffer::BitBuffer()
-    : std::vector<bool>() {}
-
-
-void BitBuffer::appendBits(std::uint32_t val, int len) {
-    if (len < 0 || len > 31 || val >> len != 0)
-        throw std::domain_error("Value out of range");
-    for (int i = len - 1; i >= 0; i--)  // Append bit by bit
-        this->push_back(((val >> i) & 1) != 0);
-}
-
-}
diff --git a/qrcodegen.hpp b/qrcodegen.hpp
deleted file mode 100644 (file)
index 077d365..0000000
+++ /dev/null
@@ -1,550 +0,0 @@
-/*\r
- * QR Code generator library (C++)\r
- *\r
- * Copyright (c) Project Nayuki. (MIT License)\r
- * https://www.nayuki.io/page/qr-code-generator-library\r
- *\r
- * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
- * this software and associated documentation files (the "Software"), to deal in\r
- * the Software without restriction, including without limitation the rights to\r
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
- * the Software, and to permit persons to whom the Software is furnished to do so,\r
- * subject to the following conditions:\r
- * - The above copyright notice and this permission notice shall be included in\r
- *   all copies or substantial portions of the Software.\r
- * - The Software is provided "as is", without warranty of any kind, express or\r
- *   implied, including but not limited to the warranties of merchantability,\r
- *   fitness for a particular purpose and noninfringement. In no event shall the\r
- *   authors or copyright holders be liable for any claim, damages or other\r
- *   liability, whether in an action of contract, tort or otherwise, arising from,\r
- *   out of or in connection with the Software or the use or other dealings in the\r
- *   Software.\r
- */\r
-\r
-#pragma once\r
-\r
-#include <array>\r
-#include <cstdint>\r
-#include <stdexcept>\r
-#include <string>\r
-#include <vector>\r
-\r
-\r
-namespace qrcodegen {\r
-\r
-/*\r
- * A segment of character/binary/control data in a QR Code symbol.\r
- * Instances of this class are immutable.\r
- * The mid-level way to create a segment is to take the payload data\r
- * and call a static factory function such as QrSegment::makeNumeric().\r
- * The low-level way to create a segment is to custom-make the bit buffer\r
- * and call the QrSegment() constructor with appropriate values.\r
- * This segment class imposes no length restrictions, but QR Codes have restrictions.\r
- * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.\r
- * Any segment longer than this is meaningless for the purpose of generating QR Codes.\r
- */\r
-class QrSegment final {\r
-\r
-    /*---- Public helper enumeration ----*/\r
-\r
-    /*\r
-     * Describes how a segment's data bits are interpreted. Immutable.\r
-     */\r
-    public: class Mode final {\r
-\r
-        /*-- Constants --*/\r
-\r
-        public: static const Mode NUMERIC;\r
-        public: static const Mode ALPHANUMERIC;\r
-        public: static const Mode BYTE;\r
-        public: static const Mode KANJI;\r
-        public: static const Mode ECI;\r
-\r
-\r
-        /*-- Fields --*/\r
-\r
-        // The mode indicator bits, which is a uint4 value (range 0 to 15).\r
-        private: int modeBits;\r
-\r
-        // Number of character count bits for three different version ranges.\r
-        private: int numBitsCharCount[3];\r
-\r
-\r
-        /*-- Constructor --*/\r
-\r
-        private: Mode(int mode, int cc0, int cc1, int cc2);\r
-\r
-\r
-        /*-- Methods --*/\r
-\r
-        /*\r
-         * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15).\r
-         */\r
-        public: int getModeBits() const;\r
-\r
-        /*\r
-         * (Package-private) Returns the bit width of the character count field for a segment in\r
-         * this mode in a QR Code at the given version number. The result is in the range [0, 16].\r
-         */\r
-        public: int numCharCountBits(int ver) const;\r
-\r
-    };\r
-\r
-\r
-\r
-    /*---- Static factory functions (mid level) ----*/\r
-\r
-    /*\r
-     * Returns a segment representing the given binary data encoded in\r
-     * byte mode. All input byte vectors are acceptable. Any text string\r
-     * can be converted to UTF-8 bytes and encoded as a byte mode segment.\r
-     */\r
-    public: static QrSegment makeBytes(const std::vector<std::uint8_t> &data);\r
-\r
-\r
-    /*\r
-     * Returns a segment representing the given string of decimal digits encoded in numeric mode.\r
-     */\r
-    public: static QrSegment makeNumeric(const char *digits);\r
-\r
-\r
-    /*\r
-     * Returns a segment representing the given text string encoded in alphanumeric mode.\r
-     * The characters allowed are: 0 to 9, A to Z (uppercase only), space,\r
-     * dollar, percent, asterisk, plus, hyphen, period, slash, colon.\r
-     */\r
-    public: static QrSegment makeAlphanumeric(const char *text);\r
-\r
-\r
-    /*\r
-     * Returns a list of zero or more segments to represent the given text string. The result\r
-     * may use various segment modes and switch modes to optimize the length of the bit stream.\r
-     */\r
-    public: static std::vector<QrSegment> makeSegments(const char *text);\r
-\r
-\r
-    /*\r
-     * Returns a segment representing an Extended Channel Interpretation\r
-     * (ECI) designator with the given assignment value.\r
-     */\r
-    public: static QrSegment makeEci(long assignVal);\r
-\r
-\r
-    /*---- Public static helper functions ----*/\r
-\r
-    /*\r
-     * Tests whether the given string can be encoded as a segment in numeric mode.\r
-     * A string is encodable iff each character is in the range 0 to 9.\r
-     */\r
-    public: static bool isNumeric(const char *text);\r
-\r
-\r
-    /*\r
-     * Tests whether the given string can be encoded as a segment in alphanumeric mode.\r
-     * A string is encodable iff each character is in the following set: 0 to 9, A to Z\r
-     * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.\r
-     */\r
-    public: static bool isAlphanumeric(const char *text);\r
-\r
-\r
-\r
-    /*---- Instance fields ----*/\r
-\r
-    /* The mode indicator of this segment. Accessed through getMode(). */\r
-    private: const Mode *mode;\r
-\r
-    /* The length of this segment's unencoded data. Measured in characters for\r
-     * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.\r
-     * Always zero or positive. Not the same as the data's bit length.\r
-     * Accessed through getNumChars(). */\r
-    private: int numChars;\r
-\r
-    /* The data bits of this segment. Accessed through getData(). */\r
-    private: std::vector<bool> data;\r
-\r
-\r
-    /*---- Constructors (low level) ----*/\r
-\r
-    /*\r
-     * Creates a new QR Code segment with the given attributes and data.\r
-     * The character count (numCh) must agree with the mode and the bit buffer length,\r
-     * but the constraint isn't checked. The given bit buffer is copied and stored.\r
-     */\r
-    public: QrSegment(const Mode &md, int numCh, const std::vector<bool> &dt);\r
-\r
-\r
-    /*\r
-     * Creates a new QR Code segment with the given parameters and data.\r
-     * The character count (numCh) must agree with the mode and the bit buffer length,\r
-     * but the constraint isn't checked. The given bit buffer is moved and stored.\r
-     */\r
-    public: QrSegment(const Mode &md, int numCh, std::vector<bool> &&dt);\r
-\r
-\r
-    /*---- Methods ----*/\r
-\r
-    /*\r
-     * Returns the mode field of this segment.\r
-     */\r
-    public: const Mode &getMode() const;\r
-\r
-\r
-    /*\r
-     * Returns the character count field of this segment.\r
-     */\r
-    public: int getNumChars() const;\r
-\r
-\r
-    /*\r
-     * Returns the data bits of this segment.\r
-     */\r
-    public: const std::vector<bool> &getData() const;\r
-\r
-\r
-    // (Package-private) Calculates the number of bits needed to encode the given segments at\r
-    // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a\r
-    // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX.\r
-    public: static int getTotalBits(const std::vector<QrSegment> &segs, int version);\r
-\r
-\r
-    /*---- Private constant ----*/\r
-\r
-    /* The set of all legal characters in alphanumeric mode, where\r
-     * each character value maps to the index in the string. */\r
-    private: static const char *ALPHANUMERIC_CHARSET;\r
-\r
-};\r
-\r
-\r
-\r
-/*\r
- * A QR Code symbol, which is a type of two-dimension barcode.\r
- * Invented by Denso Wave and described in the ISO/IEC 18004 standard.\r
- * Instances of this class represent an immutable square grid of dark and light cells.\r
- * The class provides static factory functions to create a QR Code from text or binary data.\r
- * The class covers the QR Code Model 2 specification, supporting all versions (sizes)\r
- * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.\r
- *\r
- * Ways to create a QR Code object:\r
- * - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary().\r
- * - Mid level: Custom-make the list of segments and call QrCode::encodeSegments().\r
- * - Low level: Custom-make the array of data codeword bytes (including\r
- *   segment headers and final padding, excluding error correction codewords),\r
- *   supply the appropriate version number, and call the QrCode() constructor.\r
- * (Note that all ways require supplying the desired error correction level.)\r
- */\r
-class QrCode final {\r
-\r
-    /*---- Public helper enumeration ----*/\r
-\r
-    /*\r
-     * The error correction level in a QR Code symbol.\r
-     */\r
-    public: enum class Ecc {\r
-        LOW = 0 ,  // The QR Code can tolerate about  7% erroneous codewords\r
-        MEDIUM  ,  // The QR Code can tolerate about 15% erroneous codewords\r
-        QUARTILE,  // The QR Code can tolerate about 25% erroneous codewords\r
-        HIGH    ,  // The QR Code can tolerate about 30% erroneous codewords\r
-    };\r
-\r
-\r
-    // Returns a value in the range 0 to 3 (unsigned 2-bit integer).\r
-    private: static int getFormatBits(Ecc ecl);\r
-\r
-\r
-\r
-    /*---- Static factory functions (high level) ----*/\r
-\r
-    /*\r
-     * Returns a QR Code representing the given Unicode text string at the given error correction level.\r
-     * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer\r
-     * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible\r
-     * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than\r
-     * the ecl argument if it can be done without increasing the version.\r
-     */\r
-    public: static QrCode encodeText(const char *text, Ecc ecl);\r
-\r
-\r
-    /*\r
-     * Returns a QR Code representing the given binary data at the given error correction level.\r
-     * This function always encodes using the binary segment mode, not any text mode. The maximum number of\r
-     * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.\r
-     * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.\r
-     */\r
-    public: static QrCode encodeBinary(const std::vector<std::uint8_t> &data, Ecc ecl);\r
-\r
-\r
-    /*---- Static factory functions (mid level) ----*/\r
-\r
-    /*\r
-     * Returns a QR Code representing the given segments with the given encoding parameters.\r
-     * The smallest possible QR Code version within the given range is automatically\r
-     * chosen for the output. Iff boostEcl is true, then the ECC level of the result\r
-     * may be higher than the ecl argument if it can be done without increasing the\r
-     * version. The mask number is either between 0 to 7 (inclusive) to force that\r
-     * mask, or -1 to automatically choose an appropriate mask (which may be slow).\r
-     * This function allows the user to create a custom sequence of segments that switches\r
-     * between modes (such as alphanumeric and byte) to encode text in less space.\r
-     * This is a mid-level API; the high-level API is encodeText() and encodeBinary().\r
-     */\r
-    public: static QrCode encodeSegments(const std::vector<QrSegment> &segs, Ecc ecl,\r
-        int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true);  // All optional parameters\r
-\r
-\r
-\r
-    /*---- Instance fields ----*/\r
-\r
-    // Immutable scalar parameters:\r
-\r
-    /* The version number of this QR Code, which is between 1 and 40 (inclusive).\r
-     * This determines the size of this barcode. */\r
-    private: int version;\r
-\r
-    /* The width and height of this QR Code, measured in modules, between\r
-     * 21 and 177 (inclusive). This is equal to version * 4 + 17. */\r
-    private: int size;\r
-\r
-    /* The error correction level used in this QR Code. */\r
-    private: Ecc errorCorrectionLevel;\r
-\r
-    /* The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).\r
-     * Even if a QR Code is created with automatic masking requested (mask = -1),\r
-     * the resulting object still has a mask value between 0 and 7. */\r
-    private: int mask;\r
-\r
-    // Private grids of modules/pixels, with dimensions of size*size:\r
-\r
-    // The modules of this QR Code (false = light, true = dark).\r
-    // Immutable after constructor finishes. Accessed through getModule().\r
-    private: std::vector<std::vector<bool> > modules;\r
-\r
-    // Indicates function modules that are not subjected to masking. Discarded when constructor finishes.\r
-    private: std::vector<std::vector<bool> > isFunction;\r
-\r
-\r
-\r
-    /*---- Constructor (low level) ----*/\r
-\r
-    /*\r
-     * Creates a new QR Code with the given version number,\r
-     * error correction level, data codeword bytes, and mask number.\r
-     * This is a low-level API that most users should not use directly.\r
-     * A mid-level API is the encodeSegments() function.\r
-     */\r
-    public: QrCode(int ver, Ecc ecl, const std::vector<std::uint8_t> &dataCodewords, int msk);\r
-\r
-\r
-\r
-    /*---- Public instance methods ----*/\r
-\r
-    /*\r
-     * Returns this QR Code's version, in the range [1, 40].\r
-     */\r
-    public: int getVersion() const;\r
-\r
-\r
-    /*\r
-     * Returns this QR Code's size, in the range [21, 177].\r
-     */\r
-    public: int getSize() const;\r
-\r
-\r
-    /*\r
-     * Returns this QR Code's error correction level.\r
-     */\r
-    public: Ecc getErrorCorrectionLevel() const;\r
-\r
-\r
-    /*\r
-     * Returns this QR Code's mask, in the range [0, 7].\r
-     */\r
-    public: int getMask() const;\r
-\r
-\r
-    /*\r
-     * Returns the color of the module (pixel) at the given coordinates, which is false\r
-     * for light or true for dark. The top left corner has the coordinates (x=0, y=0).\r
-     * If the given coordinates are out of bounds, then false (light) is returned.\r
-     */\r
-    public: bool getModule(int x, int y) const;\r
-\r
-\r
-\r
-    /*---- Private helper methods for constructor: Drawing function modules ----*/\r
-\r
-    // Reads this object's version field, and draws and marks all function modules.\r
-    private: void drawFunctionPatterns();\r
-\r
-\r
-    // Draws two copies of the format bits (with its own error correction code)\r
-    // based on the given mask and this object's error correction level field.\r
-    private: void drawFormatBits(int msk);\r
-\r
-\r
-    // Draws two copies of the version bits (with its own error correction code),\r
-    // based on this object's version field, iff 7 <= version <= 40.\r
-    private: void drawVersion();\r
-\r
-\r
-    // Draws a 9*9 finder pattern including the border separator,\r
-    // with the center module at (x, y). Modules can be out of bounds.\r
-    private: void drawFinderPattern(int x, int y);\r
-\r
-\r
-    // Draws a 5*5 alignment pattern, with the center module\r
-    // at (x, y). All modules must be in bounds.\r
-    private: void drawAlignmentPattern(int x, int y);\r
-\r
-\r
-    // Sets the color of a module and marks it as a function module.\r
-    // Only used by the constructor. Coordinates must be in bounds.\r
-    private: void setFunctionModule(int x, int y, bool isDark);\r
-\r
-\r
-    // Returns the color of the module at the given coordinates, which must be in range.\r
-    private: bool module(int x, int y) const;\r
-\r
-\r
-    /*---- Private helper methods for constructor: Codewords and masking ----*/\r
-\r
-    // Returns a new byte string representing the given data with the appropriate error correction\r
-    // codewords appended to it, based on this object's version and error correction level.\r
-    private: std::vector<std::uint8_t> addEccAndInterleave(const std::vector<std::uint8_t> &data) const;\r
-\r
-\r
-    // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire\r
-    // data area of this QR Code. Function modules need to be marked off before this is called.\r
-    private: void drawCodewords(const std::vector<std::uint8_t> &data);\r
-\r
-\r
-    // XORs the codeword modules in this QR Code with the given mask pattern.\r
-    // The function modules must be marked and the codeword bits must be drawn\r
-    // before masking. Due to the arithmetic of XOR, calling applyMask() with\r
-    // the same mask value a second time will undo the mask. A final well-formed\r
-    // QR Code needs exactly one (not zero, two, etc.) mask applied.\r
-    private: void applyMask(int msk);\r
-\r
-\r
-    // Calculates and returns the penalty score based on state of this QR Code's current modules.\r
-    // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.\r
-    private: long getPenaltyScore() const;\r
-\r
-\r
-\r
-    /*---- Private helper functions ----*/\r
-\r
-    // Returns an ascending list of positions of alignment patterns for this version number.\r
-    // Each position is in the range [0,177), and are used on both the x and y axes.\r
-    // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.\r
-    private: std::vector<int> getAlignmentPatternPositions() const;\r
-\r
-\r
-    // Returns the number of data bits that can be stored in a QR Code of the given version number, after\r
-    // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.\r
-    // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.\r
-    private: static int getNumRawDataModules(int ver);\r
-\r
-\r
-    // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any\r
-    // QR Code of the given version number and error correction level, with remainder bits discarded.\r
-    // This stateless pure function could be implemented as a (40*4)-cell lookup table.\r
-    private: static int getNumDataCodewords(int ver, Ecc ecl);\r
-\r
-\r
-    // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be\r
-    // implemented as a lookup table over all possible parameter values, instead of as an algorithm.\r
-    private: static std::vector<std::uint8_t> reedSolomonComputeDivisor(int degree);\r
-\r
-\r
-    // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.\r
-    private: static std::vector<std::uint8_t> reedSolomonComputeRemainder(const std::vector<std::uint8_t> &data, const std::vector<std::uint8_t> &divisor);\r
-\r
-\r
-    // Returns the product of the two given field elements modulo GF(2^8/0x11D).\r
-    // All inputs are valid. This could be implemented as a 256*256 lookup table.\r
-    private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y);\r
-\r
-\r
-    // Can only be called immediately after a light run is added, and\r
-    // returns either 0, 1, or 2. A helper function for getPenaltyScore().\r
-    private: int finderPenaltyCountPatterns(const std::array<int,7> &runHistory) const;\r
-\r
-\r
-    // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().\r
-    private: int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &runHistory) const;\r
-\r
-\r
-    // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().\r
-    private: void finderPenaltyAddHistory(int currentRunLength, std::array<int,7> &runHistory) const;\r
-\r
-\r
-    // Returns true iff the i'th bit of x is set to 1.\r
-    private: static bool getBit(long x, int i);\r
-\r
-\r
-    /*---- Constants and tables ----*/\r
-\r
-    // The minimum version number supported in the QR Code Model 2 standard.\r
-    public: static constexpr int MIN_VERSION =  1;\r
-\r
-    // The maximum version number supported in the QR Code Model 2 standard.\r
-    public: static constexpr int MAX_VERSION = 40;\r
-\r
-\r
-    // For use in getPenaltyScore(), when evaluating which mask is best.\r
-    private: static const int PENALTY_N1;\r
-    private: static const int PENALTY_N2;\r
-    private: static const int PENALTY_N3;\r
-    private: static const int PENALTY_N4;\r
-\r
-\r
-    private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41];\r
-    private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41];\r
-\r
-};\r
-\r
-\r
-\r
-/*---- Public exception class ----*/\r
-\r
-/*\r
- * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include:\r
- * - Decrease the error correction level if it was greater than Ecc::LOW.\r
- * - If the encodeSegments() function was called with a maxVersion argument, then increase\r
- *   it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other\r
- *   factory functions because they search all versions up to QrCode::MAX_VERSION.)\r
- * - Split the text data into better or optimal segments in order to reduce the number of bits required.\r
- * - Change the text or binary data to be shorter.\r
- * - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric).\r
- * - Propagate the error upward to the caller/user.\r
- */\r
-class data_too_long : public std::length_error {\r
-\r
-    public: explicit data_too_long(const std::string &msg);\r
-\r
-};\r
-\r
-\r
-\r
-/*\r
- * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment.\r
- */\r
-class BitBuffer final : public std::vector<bool> {\r
-\r
-    /*---- Constructor ----*/\r
-\r
-    // Creates an empty bit buffer (length 0).\r
-    public: BitBuffer();\r
-\r
-\r
-\r
-    /*---- Method ----*/\r
-\r
-    // Appends the given number of low-order bits of the given value\r
-    // to this buffer. Requires 0 <= len <= 31 and val < 2^len.\r
-    public: void appendBits(std::uint32_t val, int len);\r
-\r
-};\r
-\r
-}\r
-\r