release LN factorytool V1.1
authorSnickerSun <snickersun@163.com>
Fri, 5 Dec 2025 05:03:23 +0000 (13:03 +0800)
committerSnickerSun <snickersun@163.com>
Fri, 5 Dec 2025 05:03:23 +0000 (13:03 +0800)
config_manager.cpp [new file with mode: 0644]
config_manager.h [new file with mode: 0644]
factory_tool.pro
mainwindow.cpp
mainwindow.h
mainwindow.ui
settings.json [new file with mode: 0644]

diff --git a/config_manager.cpp b/config_manager.cpp
new file mode 100644 (file)
index 0000000..36f73a7
--- /dev/null
@@ -0,0 +1,279 @@
+#include "config_manager.h"\r
+#include <QFile>\r
+#include <QFileInfo>\r
+#include <QJsonDocument>\r
+#include <QJsonArray>\r
+#include <QDebug>\r
+#include <QCoreApplication>\r
+#include <QDir>\r
+\r
+ConfigManager::ConfigManager()\r
+    : configLoaded(false)\r
+{\r
+    setDefaultConfig();\r
+}\r
+\r
+ConfigManager::~ConfigManager()\r
+{\r
+}\r
+\r
+void ConfigManager::setDefaultConfig()\r
+{\r
+    // 设置默认配置\r
+    QJsonObject defaultConfig;\r
+    \r
+    // 默认开机检测关键字\r
+    QJsonArray bootKeywords;\r
+    bootKeywords.append("BuildVer");\r
+    bootKeywords.append("NAND flash ID");\r
+    bootKeywords.append("NAND ID:");\r
+    defaultConfig["bootDetectionKeywords"] = bootKeywords;\r
+    \r
+    // 默认版本信息获取指令\r
+    QJsonArray versionCommands;\r
+    versionCommands.append("AT^CAT1GETVER");\r
+    versionCommands.append("AT^CAT1GETSIGNAL");\r
+    versionCommands.append("AT^FACTORY");\r
+    defaultConfig["versionInfoCommands"] = versionCommands;\r
+    \r
+    // 默认产测模式检测指令\r
+    defaultConfig["factoryModeCheckCommand"] = "AT^FACTORY";\r
+    \r
+    // 默认产测模式关键字\r
+    QJsonArray factoryKeywords;\r
+    factoryKeywords.append("FACTORY TEST MODE:");\r
+    defaultConfig["factoryModeKeywords"] = factoryKeywords;\r
+    \r
+    // 默认测试参数配置\r
+    QJsonObject testConfig;\r
+    testConfig["portName"] = "";\r
+    testConfig["audioVolume"] = "2.0";\r
+    testConfig["audioLevelStandard"] = "中";\r
+    testConfig["expectedModemVer"] = "";\r
+    testConfig["expectedPYVer"] = "";\r
+    testConfig["expectedVer"] = "";\r
+    testConfig["bluetoothAddress"] = "";\r
+    testConfig["bluetoothRSSI"] = "";\r
+    defaultConfig["testConfig"] = testConfig;\r
+    \r
+    configObject = defaultConfig;\r
+    configLoaded = true;\r
+}\r
+\r
+bool ConfigManager::loadConfig(const QString& configPath)\r
+{\r
+    QString filePath = configPath;\r
+    if (filePath.isEmpty()) {\r
+        // 默认路径:应用程序目录下的settings.json\r
+        QString appPath = QCoreApplication::applicationDirPath();\r
+        filePath = appPath + "/settings.json";\r
+    }\r
+    \r
+    QFile file(filePath);\r
+    if (!file.exists()) {\r
+        qDebug() << "配置文件不存在,使用默认配置并创建文件:" << filePath;\r
+        setDefaultConfig();\r
+        \r
+        // 创建配置文件目录(如果不存在)\r
+        QFileInfo fileInfo(filePath);\r
+        QDir dir = fileInfo.absoluteDir();\r
+        if (!dir.exists()) {\r
+            dir.mkpath(".");\r
+        }\r
+        \r
+        // 将默认配置保存到文件\r
+        if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {\r
+            QJsonDocument doc(configObject);\r
+            file.write(doc.toJson());\r
+            file.close();\r
+            qDebug() << "已创建默认配置文件:" << filePath;\r
+            configLoaded = true;\r
+            return true;\r
+        } else {\r
+            qDebug() << "无法创建配置文件:" << filePath << "错误:" << file.errorString();\r
+            configLoaded = true; // 即使文件创建失败,也使用默认配置\r
+            return false;\r
+        }\r
+    }\r
+    \r
+    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {\r
+        qDebug() << "无法打开配置文件:" << filePath << "错误:" << file.errorString();\r
+        setDefaultConfig();\r
+        return false;\r
+    }\r
+    \r
+    QByteArray data = file.readAll();\r
+    file.close();\r
+    \r
+    QJsonParseError error;\r
+    QJsonDocument doc = QJsonDocument::fromJson(data, &error);\r
+    \r
+    if (error.error != QJsonParseError::NoError) {\r
+        qDebug() << "配置文件JSON解析错误:" << error.errorString();\r
+        setDefaultConfig();\r
+        return false;\r
+    }\r
+    \r
+    if (!doc.isObject()) {\r
+        qDebug() << "配置文件格式错误:不是JSON对象";\r
+        setDefaultConfig();\r
+        return false;\r
+    }\r
+    \r
+    configObject = doc.object();\r
+    configLoaded = true;\r
+    \r
+    // 合并默认配置(如果配置文件中缺少某些项,使用默认值)\r
+    QJsonObject defaultConfig;\r
+    setDefaultConfig();\r
+    defaultConfig = configObject;\r
+    \r
+    // 重新加载文件配置\r
+    doc = QJsonDocument::fromJson(data, &error);\r
+    configObject = doc.object();\r
+    \r
+    // 合并缺失的配置项\r
+    if (!configObject.contains("bootDetectionKeywords")) {\r
+        configObject["bootDetectionKeywords"] = defaultConfig["bootDetectionKeywords"];\r
+    }\r
+    if (!configObject.contains("versionInfoCommands")) {\r
+        configObject["versionInfoCommands"] = defaultConfig["versionInfoCommands"];\r
+    }\r
+    if (!configObject.contains("factoryModeCheckCommand")) {\r
+        configObject["factoryModeCheckCommand"] = defaultConfig["factoryModeCheckCommand"];\r
+    }\r
+    if (!configObject.contains("factoryModeKeywords")) {\r
+        configObject["factoryModeKeywords"] = defaultConfig["factoryModeKeywords"];\r
+    }\r
+    if (!configObject.contains("testConfig")) {\r
+        configObject["testConfig"] = defaultConfig["testConfig"];\r
+    } else {\r
+        // 合并testConfig中的缺失项\r
+        QJsonObject savedTestConfig = configObject["testConfig"].toObject();\r
+        QJsonObject defaultTestConfig = defaultConfig["testConfig"].toObject();\r
+        for (auto it = defaultTestConfig.begin(); it != defaultTestConfig.end(); ++it) {\r
+            if (!savedTestConfig.contains(it.key())) {\r
+                savedTestConfig[it.key()] = it.value();\r
+            }\r
+        }\r
+        configObject["testConfig"] = savedTestConfig;\r
+    }\r
+    \r
+    qDebug() << "配置文件加载成功:" << filePath;\r
+    return true;\r
+}\r
+\r
+QStringList ConfigManager::getBootDetectionKeywords() const\r
+{\r
+    QStringList keywords;\r
+    if (configObject.contains("bootDetectionKeywords") && \r
+        configObject["bootDetectionKeywords"].isArray()) {\r
+        QJsonArray array = configObject["bootDetectionKeywords"].toArray();\r
+        for (int i = 0; i < array.size(); ++i) {\r
+            if (array[i].isString()) {\r
+                keywords.append(array[i].toString());\r
+            }\r
+        }\r
+    }\r
+    return keywords;\r
+}\r
+\r
+QStringList ConfigManager::getVersionInfoCommands() const\r
+{\r
+    QStringList commands;\r
+    if (configObject.contains("versionInfoCommands") && \r
+        configObject["versionInfoCommands"].isArray()) {\r
+        QJsonArray array = configObject["versionInfoCommands"].toArray();\r
+        for (int i = 0; i < array.size(); ++i) {\r
+            if (array[i].isString()) {\r
+                commands.append(array[i].toString());\r
+            }\r
+        }\r
+    }\r
+    return commands;\r
+}\r
+\r
+QString ConfigManager::getFactoryModeCheckCommand() const\r
+{\r
+    if (configObject.contains("factoryModeCheckCommand") && \r
+        configObject["factoryModeCheckCommand"].isString()) {\r
+        return configObject["factoryModeCheckCommand"].toString();\r
+    }\r
+    return "AT^FACTORY";\r
+}\r
+\r
+QStringList ConfigManager::getFactoryModeKeywords() const\r
+{\r
+    QStringList keywords;\r
+    if (configObject.contains("factoryModeKeywords") && \r
+        configObject["factoryModeKeywords"].isArray()) {\r
+        QJsonArray array = configObject["factoryModeKeywords"].toArray();\r
+        for (int i = 0; i < array.size(); ++i) {\r
+            if (array[i].isString()) {\r
+                keywords.append(array[i].toString());\r
+            }\r
+        }\r
+    }\r
+    return keywords;\r
+}\r
+\r
+bool ConfigManager::isValid() const\r
+{\r
+    return configLoaded;\r
+}\r
+\r
+// 获取测试参数配置值\r
+QString ConfigManager::getTestConfig(const QString& key, const QString& defaultValue) const\r
+{\r
+    if (configObject.contains("testConfig") && configObject["testConfig"].isObject()) {\r
+        QJsonObject testConfig = configObject["testConfig"].toObject();\r
+        if (testConfig.contains(key) && testConfig[key].isString()) {\r
+            return testConfig[key].toString();\r
+        }\r
+    }\r
+    return defaultValue;\r
+}\r
+\r
+// 设置测试参数配置值\r
+void ConfigManager::setTestConfig(const QString& key, const QString& value)\r
+{\r
+    if (!configObject.contains("testConfig") || !configObject["testConfig"].isObject()) {\r
+        configObject["testConfig"] = QJsonObject();\r
+    }\r
+    QJsonObject testConfig = configObject["testConfig"].toObject();\r
+    testConfig[key] = value;\r
+    configObject["testConfig"] = testConfig;\r
+}\r
+\r
+// 保存配置到文件\r
+bool ConfigManager::saveConfig(const QString& configPath)\r
+{\r
+    QString filePath = configPath;\r
+    if (filePath.isEmpty()) {\r
+        // 默认路径:应用程序目录下的settings.json\r
+        QString appPath = QCoreApplication::applicationDirPath();\r
+        filePath = appPath + "/settings.json";\r
+    }\r
+    \r
+    // 创建配置文件目录(如果不存在)\r
+    QFileInfo fileInfo(filePath);\r
+    QDir dir = fileInfo.absoluteDir();\r
+    if (!dir.exists()) {\r
+        dir.mkpath(".");\r
+    }\r
+    \r
+    // 保存到文件\r
+    QFile file(filePath);\r
+    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {\r
+        qDebug() << "无法保存配置文件:" << filePath << "错误:" << file.errorString();\r
+        return false;\r
+    }\r
+    \r
+    QJsonDocument doc(configObject);\r
+    file.write(doc.toJson());\r
+    file.close();\r
+    \r
+    qDebug() << "配置文件已保存:" << filePath;\r
+    return true;\r
+}\r
+\r
diff --git a/config_manager.h b/config_manager.h
new file mode 100644 (file)
index 0000000..cd4a45c
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef CONFIG_MANAGER_H\r
+#define CONFIG_MANAGER_H\r
+\r
+#include <QString>\r
+#include <QStringList>\r
+#include <QJsonObject>\r
+\r
+class ConfigManager\r
+{\r
+public:\r
+    ConfigManager();\r
+    ~ConfigManager();\r
+    \r
+    // 加载配置文件\r
+    bool loadConfig(const QString& configPath = QString());\r
+    \r
+    // 获取配置项\r
+    QStringList getBootDetectionKeywords() const;\r
+    QStringList getVersionInfoCommands() const;\r
+    QString getFactoryModeCheckCommand() const;\r
+    QStringList getFactoryModeKeywords() const;\r
+    \r
+    // 测试参数配置相关方法\r
+    QString getTestConfig(const QString& key, const QString& defaultValue = "") const;\r
+    void setTestConfig(const QString& key, const QString& value);\r
+    bool saveConfig(const QString& configPath = QString());  // 保存配置到文件\r
+    \r
+    // 检查配置是否有效\r
+    bool isValid() const;\r
+\r
+private:\r
+    QJsonObject configObject;\r
+    bool configLoaded;\r
+    \r
+    // 默认配置\r
+    void setDefaultConfig();\r
+};\r
+\r
+#endif // CONFIG_MANAGER_H\r
+\r
index b9290e6..5b5038e 100644 (file)
@@ -14,11 +14,13 @@ CODECFORSRC = UTF-8
 # 源文件列表
 SOURCES += \
     main.cpp \
-    mainwindow.cpp
+    mainwindow.cpp \
+    config_manager.cpp
 
 # 头文件列表
 HEADERS += \
-    mainwindow.h
+    mainwindow.h \
+    config_manager.h
 
 # UI 文件列表(关键:必须包含 mainwindow.ui)
 FORMS += \
index d7e7963..33a9162 100644 (file)
@@ -1,5 +1,6 @@
 #include "mainwindow.h"
 #include "ui_mainwindow.h"
+#include "config_manager.h"
 #include <QMessageBox>
 #include <QDateTime>
 #include <QDebug>
@@ -35,13 +36,16 @@ MainWindow::MainWindow(QWidget *parent)
     QTextCodec::setCodecForCStrings(utf8Codec);
     QTextCodec::setCodecForTr(utf8Codec);
     #endif
-
+    
     // 初始化串口对象
     serial = new QSerialPort(this);
     connect(serial, &QSerialPort::readyRead, this, &MainWindow::readSerialData);
 
     // 初始刷新串口列表
     updateSerialPorts();
+    
+    // 从settings.json加载测试参数
+    loadTestConfigFromSettings();
 
     // 初始化按钮样式(可选:设置默认样式)
     ui->openButton->setStyleSheet("QPushButton { padding: 5px 10px; }");
@@ -101,6 +105,20 @@ MainWindow::MainWindow(QWidget *parent)
     testLogFile = nullptr;
     testLogStream = nullptr;
     testLogFilePath = "";
+    // 初始化配置管理器
+    configManager.loadConfig();
+    // 初始化开机检测相关变量
+    bootDetected = false;
+    factoryModeChecked = false;
+    versionInfoFetched = false;
+    // 初始化版本信息获取相关变量
+    versionCommandQueue.clear();
+    currentVersionCommandIndex = -1;
+    versionCommandTimer = new QTimer(this);
+    connect(versionCommandTimer, &QTimer::timeout, this, &MainWindow::onVersionCommandTimeout);
+    
+    // 从settings.json加载测试参数
+    loadTestConfigFromSettings();
     
     // 初始化按键测试状态
     for (int i = 0; i < 4; i++) {
@@ -121,6 +139,9 @@ MainWindow::MainWindow(QWidget *parent)
 
 MainWindow::~MainWindow()
 {
+    // 保存测试参数到settings.json
+    saveTestConfigToSettings();
+    
     // 关闭日志文件
     closeTestLogFile();
     
@@ -175,13 +196,13 @@ void MainWindow::on_openButton_clicked()
         ui->openButton->setStyleSheet("QPushButton { padding: 5px 10px; }");
         return;
     }
-
+    
     // 检查是否选择了串口
     if (ui->portNameBox->currentText().isEmpty()) {
         QMessageBox::warning(this, "警告", "请先选择串口");
         return;
     }
-
+    
     // 配置串口参数(固定值)
     QString portText = ui->portNameBox->currentText();
     QString portName = portText.left(portText.indexOf(" ("));
@@ -221,7 +242,7 @@ void MainWindow::on_sendButton_clicked()
         QMessageBox::warning(this, "警告", "请先打开串口");
         return;
     }
-
+    
     // 获取发送区文本
     QString sendStr = ui->sendTextEdit->toPlainText();
     if (sendStr.isEmpty()) return;
@@ -499,7 +520,7 @@ void MainWindow::ShowPppInfoIMEI(const QString& data)
                 // 提取IMEI信息
                 imeiInfo = data.mid(start, end - start).trimmed();
                 qDebug() << "使用imei:格式提取IMEI:" << imeiInfo;
-            } else {
+        } else {
                 qDebug() << "未找到任何IMEI格式,数据:" << data.trimmed();
                 return;
             }
@@ -637,11 +658,11 @@ void MainWindow::ShowPppInfoModemVer(const QString& data)
             if (modemPattern.indexIn(data) != -1) {
                 modemVerInfo = modemPattern.cap(1);
                 qDebug() << "使用括号格式提取Modem Version:" << modemVerInfo;
-            } else {
+                } else {
                 qDebug() << "未找到Modem Version信息,数据格式:" << data.trimmed();
-                return;
+                    return;
+                }
             }
-        }
     }
 
     // 验证Modem Version是否有效(不为空即可)
@@ -945,7 +966,7 @@ void MainWindow::readSerialData()
     if (recvData.isEmpty()) {
         return;
     }
-
+    
     // 将新接收的数据追加到缓存
     recvBuffer += QString::fromUtf8(recvData);
 
@@ -966,6 +987,30 @@ void MainWindow::readSerialData()
         if (line.startsWith("fac:")) {
             appendToLogFile(processLine);
         }
+        
+        // 开机检测(仅在未检测到开机时执行)
+        // 同时检查原始行和去掉fac:前缀的行,确保能匹配到关键字
+        if (!bootDetected && !isProductionTestRunning) {
+            checkBootDetection(line);
+            // 如果原始行没匹配到,也检查去掉fac:前缀的行
+            if (!bootDetected && processLine != line) {
+                checkBootDetection(processLine);
+            }
+        }
+        
+        // 产测模式检测(在检测到开机后执行)
+        if (bootDetected && !factoryModeChecked && !isProductionTestRunning) {
+            checkFactoryMode(line);
+        }
+        
+        // 版本信息获取:当正在获取版本信息时,接收到数据后继续发送下一条指令
+        if (versionInfoFetched && currentVersionCommandIndex >= 0 && 
+            currentVersionCommandIndex < versionCommandQueue.size() && 
+            !isProductionTestRunning) {
+            // 如果当前指令的响应已收到(通过超时或检测到响应),继续下一条
+            // 这里主要通过超时机制来推进,但也可以检测到特定响应后立即推进
+            // 暂时依赖超时机制,如果需要可以添加响应检测逻辑
+        }
 
         // 处理所有数据提取(不管是否以fac:开头)(大小写不敏感)
         if (processLine.indexOf("BuildVer:", 0, Qt::CaseInsensitive) != -1) {
@@ -985,6 +1030,46 @@ void MainWindow::readSerialData()
             parseWstpPrintData(line);
         }
         
+        // 检测FACTORY TEST MODE状态(wstp_print格式)
+        if (line.contains("FACTORY TEST MODE:", Qt::CaseInsensitive)) {
+            qDebug() << "检测到FACTORY TEST MODE信息:" << line.trimmed();
+            appendToLogFile(QString("检测到FACTORY TEST MODE信息: %1\n").arg(line.trimmed()));
+            
+            if (line.contains("FACTORY TEST MODE: ENABLED", Qt::CaseInsensitive)) {
+                // 产测模式已开启
+                ui->factoryModeLineEdit->setText("是");
+                ui->factoryModeLineEdit->setStyleSheet("background-color: #ffcccc;"); // 红底
+                qDebug() << "产测模式: 已开启";
+                appendToLogFile(QString("=== 产测模式状态: 已开启 ===\n"));
+                
+                // 如果还未标记为已检测,标记并开始获取版本信息
+                if (!factoryModeChecked) {
+                    factoryModeChecked = true;
+                    QTimer::singleShot(1000, this, [this]() {
+                        if (!versionInfoFetched) {
+                            startVersionInfoFetch();
+                        }
+                    });
+                }
+            } else if (line.contains("FACTORY TEST MODE: DISABLED", Qt::CaseInsensitive)) {
+                // 产测模式已关闭
+                ui->factoryModeLineEdit->setText("否");
+                ui->factoryModeLineEdit->setStyleSheet("background-color: #ccffcc;"); // 绿底
+                qDebug() << "产测模式: 已关闭";
+                appendToLogFile(QString("=== 产测模式状态: 已关闭 ===\n"));
+                
+                // 如果还未标记为已检测,标记并开始获取版本信息
+                if (!factoryModeChecked) {
+                    factoryModeChecked = true;
+                    QTimer::singleShot(1000, this, [this]() {
+                        if (!versionInfoFetched) {
+                            startVersionInfoFetch();
+                        }
+                    });
+                }
+            }
+        }
+        
         // 检测设备信息数据(去掉[PPP_INFO]限制,支持多种格式)(大小写不敏感)
         // 检测包含@sim:的数据(可能是IMEI数据和4G信号数据)
         if (line.contains("@sim:") || processLine.contains("@sim:")) {
@@ -1280,22 +1365,22 @@ void MainWindow::performLcdColorTest()
     QString colorName;
     int colorCommand;
     switch (currentLcdColor) {
-        case 1: 
+    case 1:
             colorName = "红色"; 
             colorCommand = 6; // AT^LCDTEST=6 红色
-            break;
-        case 2: 
+        break;
+    case 2:
             colorName = "绿色"; 
             colorCommand = 3; // AT^LCDTEST=3 绿色
-            break;
-        case 3: 
+        break;
+    case 3:
             colorName = "蓝色"; 
             colorCommand = 4; // AT^LCDTEST=4 蓝色
-            break;
-        default:
+        break;
+    default:
             colorName = "未知";
             colorCommand = 6;
-            break;
+        break;
     }
     
     ui->statusbar->showMessage("LCD测试中 - " + colorName + " (1秒)", 1000);
@@ -1367,7 +1452,7 @@ void MainWindow::performMotorTest()
         qDebug() << "currentTestStep:" << currentTestStep << "期望:" << TEST_MOTOR;
         return;
     }
-
+    
     qDebug() << "开始马达测试";
     
     // 重置马达测试状态
@@ -1431,7 +1516,7 @@ void MainWindow::perform4GTest()
         qDebug() << "4G测试跳过 - 产测未运行";
         return;
     }
-
+    
     initRetry4GCount++;
     qDebug() << "4G测试 - 初始化尝试次数:" << initRetry4GCount;
     
@@ -1766,7 +1851,7 @@ void MainWindow::showLcdTestConfirmDialog()
         qDebug() << "LCD确认弹窗已显示过,跳过";
         return;
     }
-    
+
     qDebug() << "显示LCD测试确认弹窗";
     
     // 标记弹窗已显示,避免重复弹出
@@ -2238,7 +2323,7 @@ void MainWindow::checkKeyTestResult(const QString& data)
             for (int i = 0; i < 4; i++) {
                 if (keyTestStatus[i] == 0) {
                     allKeysTested = false;
-                    break;
+        break;
                 }
             }
             
@@ -2401,7 +2486,7 @@ void MainWindow::updateTestStatus(const QString& testName, TestStatus status)
             statusText = "未测试";
             iconColor = "#888888";
             textColor = "#888888";
-            break;
+                        break;
         case STATUS_TESTING:
             iconText = "●";
             statusText = "测试中";
@@ -2491,7 +2576,7 @@ void MainWindow::updateOverallStatus()
         overallIcon = "✓";
         overallText = "通过";
         overallColor = "#4CAF50";
-    } else {
+                    } else {
         // 任何其他情况(失败、测试中、等待开始等)都显示为未通过
         overallIcon = "✗";
         overallText = "未通过";
@@ -2611,7 +2696,7 @@ QString MainWindow::saveTestRecord()
         finalContent += "开始时间,结束时间,测试耗时,软件版本,IMEI,ICCID,Modem版本,PYVER版本,LCD测试,马达测试,按键测试,录音测试,电池测试,G-sensor测试,蓝牙测试,4G测试,整体结果\n";
         finalContent += newDataLine;
         qDebug() << "创建新的CSV文件:" << csvFilePath;
-    } else {
+            } else {
         // 现有文件,需要检查ICCID是否重复
         QFile existingFile(csvFilePath);
         if (!existingFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
@@ -2649,7 +2734,7 @@ QString MainWindow::saveTestRecord()
                         finalContent += newDataLine;
                         iccidFound = true;
                         qDebug() << "找到相同ICCID,替换记录:" << iccid;
-                    } else {
+        } else {
                         // 保留不同ICCID的记录
                         finalContent += line + "\n";
                     }
@@ -2703,10 +2788,25 @@ QString MainWindow::saveTestRecord()
         QDateTime endTime = QDateTime::currentDateTime();
         *testLogStream << QString("=== 测试结束时间: %1 ===\n").arg(endTime.toString("yyyy-MM-dd hh:mm:ss"));
         *testLogStream << QString("测试记录已保存: %1\n").arg(csvFileName);
+        
+        // 将recvTextEdit的内容追加到测试日志文件
+        QString recvContent = ui->recvTextEdit->toPlainText();
+        if (!recvContent.isEmpty()) {
+            *testLogStream << "\n=== UI日志内容 ===\n";
+            *testLogStream << recvContent;
+            *testLogStream << "\n=== UI日志结束 ===\n";
+        }
+        
         testLogStream->flush();
     }
     closeTestLogFile();
     
+    // 保存UI日志到独立文件(如果有效)
+    saveRecvTextEditToLog();
+    
+    // 清除UI中的日志内容,防止重复保存
+    ui->recvTextEdit->clear();
+    
     // 返回文件名供外部使用
     return csvFileName;
 }
@@ -2751,7 +2851,7 @@ void MainWindow::checkSignalAndSimValues(const QString& data)
         // 将signal值显示到界面
         appendToLogFile(QString("Signal值: %1\n").arg(signalValue));
         ui->statusbar->showMessage(QString("检测到Signal值: %1").arg(signalValue), 2000);
-    } else {
+            } else {
         qDebug() << "未找到signal值";
         return;
     }
@@ -2821,8 +2921,8 @@ void MainWindow::checkSignalAndSimValues(const QString& data)
             
             // 显示最终测试结果弹窗
             showFinalTestResult();
-        }
-    } else {
+            }
+        } else {
         // 测试失败
         QString failReason;
         if (simValue != 1) {
@@ -2928,7 +3028,7 @@ void MainWindow::sendPppCommand()
         // 启动5秒超时定时器,等待PPPINFO回复
         test4GTimer->start(5000);
         qDebug() << "启动5秒超时定时器,等待PPPINFO回复";
-    } else {
+                } else {
         // 超过3次后,改为等待signal和sim数据
         qDebug() << "PPP数据已等待3次,改为等待signal和sim数据";
         ui->statusbar->showMessage("4G测试中 - 等待signal和sim数据...", 5000);
@@ -2958,7 +3058,7 @@ void MainWindow::on_openRecordsFolderButton_clicked()
     if (success) {
         qDebug() << "成功打开文件夹:" << saveDir;
         ui->statusbar->showMessage("已打开产测记录文件夹", 2000);
-    } else {
+            } else {
         qDebug() << "打开文件夹失败:" << saveDir;
         QMessageBox::warning(this, "打开失败", 
             QString("无法打开文件夹:\n%1\n请检查路径是否存在或权限是否足够").arg(saveDir));
@@ -3014,6 +3114,21 @@ void MainWindow::on_openButton_2_clicked()
     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";
+    }
+    
     // 创建测试日志文件
     createTestLogFile();
     
@@ -3022,6 +3137,11 @@ void MainWindow::on_openButton_2_clicked()
     ui->sendTextEdit->clear();           // 清空发送文本框
     ui->statusbar->clearMessage();       // 清空状态栏
     
+    // 重置开机检测状态
+    bootDetected = false;
+    factoryModeChecked = false;
+    versionInfoFetched = false;
+    
     // 清空设备信息
     ui->VerlineEdit->clear();            // 清空版本信息
     ui->IMEIlineEdit->clear();           // 清空IMEI
@@ -3092,6 +3212,16 @@ void MainWindow::performAudioTest()
     // 重置录音测试相关状态
     audioMicMiddleConsecutiveCount = 0;
     audioTestFinished = false;
+    
+    // 获取音频测试通过标准(从UI中读取)
+    audioLevelStandard = "中"; // 默认值
+    if (ui->recordVolumelineEdit) {
+        QString levelText = ui->recordVolumelineEdit->text().trimmed();
+        if (!levelText.isEmpty()) {
+            audioLevelStandard = levelText;
+        }
+    }
+    qDebug() << "音频测试通过标准设置为:" << audioLevelStandard;
 
     // 发送录音测试指令:先 AT^AUDIOTEST=5 再 AT^AUDIOTEST=7 <音量>
     QString audioCmd1 = "AT^AUDIOTEST=5\r\n";
@@ -3117,7 +3247,7 @@ void MainWindow::performAudioTest()
             float v = volumeStr.toFloat(&ok);
             if (ok && v >= 0) {
                 volume = v;
-            } else {
+        } else {
                 qDebug() << "音量文本框内容非法,使用默认音量 2,原始值:" << volumeStr;
             }
         } else {
@@ -3378,9 +3508,9 @@ void MainWindow::onAudioRecordTimer()
         return;
     }
 
-    // 8秒内未检测到连续5个"fac:mic level:中",判定录音测试失败
-    qDebug() << "录音测试8秒超时,未检测到连续5个\"fac:mic level:中\",测试失败";
-    ui->statusbar->showMessage("录音测试失败:8秒内未检测到连续5个\"fac:mic level:中\"", 3000);
+    // 8秒内未检测到连续3个设置的标准Level,判定录音测试失败
+    qDebug() << QString("录音测试8秒超时,未检测到连续3个\"fac:mic level:%1\",测试失败").arg(audioLevelStandard);
+    ui->statusbar->showMessage(QString("录音测试失败:8秒内未检测到连续3个\"fac:mic level:%1\"").arg(audioLevelStandard), 3000);
     updateTestStatus("audio", STATUS_FAILED);
 
     audioTestFinished = true;
@@ -3402,26 +3532,28 @@ void MainWindow::checkAudioTestResult(const QString& data)
         return;
     }
     
-    qDebug() << "录音测试收到数据:" << data.trimmed();
-    
     qDebug() << "录音测试收到数据:" << data.trimmed();
 
-    // 只在录音测试过程中统计“fac:mic level:中”
+    // 只在录音测试过程中统计"fac:mic level:"的值
     if (data.contains("fac:mic level:")) {
-        if (data.contains("fac:mic level:中")) {
+        // 构建要检测的字符串
+        QString targetLevel = QString("fac:mic level:%1").arg(audioLevelStandard);
+        
+        if (data.contains(targetLevel)) {
             audioMicMiddleConsecutiveCount++;
-            qDebug() << "检测到 \"fac:mic level:中\" ,当前连续计数:"
-                     << audioMicMiddleConsecutiveCount;
+            qDebug() << QString("检测到 \"fac:mic level:%1\" ,当前连续计数: %2")
+                        .arg(audioLevelStandard)
+                        .arg(audioMicMiddleConsecutiveCount);
         } else {
             // 遇到其他 mic level 打印,连续计数清零
             qDebug() << "检测到其他 mic level 打印,重置连续计数";
             audioMicMiddleConsecutiveCount = 0;
         }
 
-        // 达到连续5次"fac:mic level:中"视为录音测试通过
+        // 达到连续3次设置的标准Level视为录音测试通过
         if (!audioTestFinished && audioMicMiddleConsecutiveCount >= 3) {
-            qDebug() << "录音测试通过:已检测到连续5个 \"fac:mic level:中\"";
-            ui->statusbar->showMessage("录音测试通过:检测到连续3个\"fac:mic level:中\"", 3000);
+            qDebug() << QString("录音测试通过:已检测到连续3个 \"fac:mic level:%1\"").arg(audioLevelStandard);
+            ui->statusbar->showMessage(QString("录音测试通过:检测到连续3个\"fac:mic level:%1\"").arg(audioLevelStandard), 3000);
             updateTestStatus("audio", STATUS_SUCCESS);
 
             audioTestFinished = true;
@@ -3474,7 +3606,7 @@ void MainWindow::checkBatteryTestResult(const QString& data)
     if (!isProductionTestRunning || currentTestStep != TEST_BATTERY) {
         return;
     }
-
+    
     qDebug() << "电池测试收到数据:" << data.trimmed();
 
     // 解析电池数据,支持多种格式:
@@ -3559,7 +3691,7 @@ void MainWindow::checkBatteryTestResult(const QString& data)
             int ret = msgBox.exec();
             if (ret == QMessageBox::No) {
                 updateTestStatus("battery", STATUS_FAILED);
-                return;
+        return;
             }
         }
 
@@ -3597,7 +3729,7 @@ void MainWindow::checkBatteryTestResult(const QString& data)
             qDebug() << successMsg;
             ui->statusbar->showMessage(successMsg, 2000);
             updateTestStatus("battery", STATUS_SUCCESS);
-        } else {
+    } else {
             // 电压或温度异常
             QString failMsg = QString("电池测试失败 - %1").arg(failReason);
             if (temp == -1.0 && !isVoltageNormal) {
@@ -3886,7 +4018,7 @@ void MainWindow::checkBluetoothTestResult(const QString& data)
                             perform4GTest();
                         }
                     });
-                } else {
+    } else {
                     qDebug() << "RSSI不满足要求:" << rssi << "<" << rssiThreshold;
                     ui->statusbar->showMessage(QString("蓝牙测试:检测到地址 %1,但RSSI %2 小于阈值 %3").arg(address).arg(rssi).arg(rssiThreshold), 2000);
                 }
@@ -3942,8 +4074,8 @@ void MainWindow::onBluetoothTestTimer()
     
     if (bluetoothTestFinished) {
         qDebug() << "蓝牙测试已在10秒内完成,不再处理超时";
-        return;
-    }
+            return;
+        }
     
     // 检查是否使用指定地址+RSSI模式
     QString targetAddress = ui->bluetoothAddressLineEdit->text().trimmed().toUpper();
@@ -4042,7 +4174,7 @@ void MainWindow::checkCat1SignalResult(const QString& data)
             // signal=99不计入平均值计算
             qDebug() << "CAT1信号值为99,无效信号次数:" << cat1Signal99Count << "/" << cat1SignalReceivedCount;
             ui->statusbar->showMessage(QString("CAT1信号测试 (%1/5) 信号值:99 (无效信号)").arg(cat1SignalReceivedCount), 2000);
-        } else {
+            } else {
             // 累加有效信号值用于计算平均值
             cat1SignalTotalValue += signalValue;
             if (signalValue > 25) {
@@ -4055,8 +4187,8 @@ void MainWindow::checkCat1SignalResult(const QString& data)
             
             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 {
+            }
+        } else {
         qDebug() << "CAT1信号测试未能解析signal值,数据:" << data.trimmed();
     }
 }
@@ -4197,7 +4329,7 @@ void MainWindow::onCat1SignalTimer()
                 // 显示最终测试结果弹窗
                 showFinalTestResult();
             }
-        } else {
+    } else {
             // 平均信号值小于25或没有有效数据,判定为失败
             QString failReason;
             if (validCount == 0) {
@@ -4670,13 +4802,18 @@ void MainWindow::showFinalTestResult()
         "}"
     );
     
-    // 设置弹窗样式
+    // 设置弹窗样式和字体(14号字,与UI一致)
+    QFont dialogFont = resultDialog.font();
+    dialogFont.setPointSize(14);
+    resultDialog.setFont(dialogFont);
+    
     resultDialog.setStyleSheet(
         "QMessageBox { "
         "background-color: #f8f9fa; "
+        "font-size: 14pt; "
         "}"
         "QMessageBox QLabel { "
-        "font-size: 14px; "
+        "font-size: 14pt; "
         "line-height: 1.4; "
         "}"
     );
@@ -4753,8 +4890,8 @@ void MainWindow::onTestTimeout()
         default:
             qDebug() << "未知的测试步骤超时:" << currentTestStep;
             return;
-    }
-    
+        }
+
     qDebug() << testDescription << "超时,10秒内未收到响应";
     
     // 弹出超时提示对话框
@@ -4872,7 +5009,7 @@ void MainWindow::onTestTimeout()
                 break;
         }
         
-    } else {
+            } else {
         // 用户选择停止测试
         qDebug() << "用户选择停止测试";
         ui->statusbar->showMessage("测试已停止", 2000);
@@ -5082,6 +5219,376 @@ void MainWindow::on_openButton_4_clicked()
     serial->write("AT^FACTORY=3\r\n");
 }
 
+// 检测设备开机
+void MainWindow::checkBootDetection(const QString& data)
+{
+    if (bootDetected) {
+        return; // 已经检测到开机,不再重复检测
+    }
+    
+    QStringList keywords = configManager.getBootDetectionKeywords();
+    if (keywords.isEmpty()) {
+        qDebug() << "警告:bootDetectionKeywords 为空,无法进行开机检测";
+        return;
+    }
+    
+    for (const QString& keyword : keywords) {
+        if (data.contains(keyword, Qt::CaseInsensitive)) {
+            qDebug() << "检测到设备开机关键字:" << keyword << ",数据片段:" << data.left(100);
+            appendToLogFile(QString("=== 检测到设备开机: %1 ===\n").arg(keyword));
+            
+            bootDetected = true;
+            
+            // 延迟1秒后自动发送产测模式检测指令(无论是否有日志,都要检测)
+            QTimer::singleShot(1000, this, [this]() {
+                if (!factoryModeChecked && serial->isOpen()) {
+                    QString cmd = configManager.getFactoryModeCheckCommand();
+                    if (!cmd.isEmpty()) {
+                        // 如果是查询指令,确保使用 ? 参数
+                        if (!cmd.contains("?") && !cmd.contains("=")) {
+                            cmd = cmd + "?";
+                        }
+                        serial->write((cmd + "\r\n").toUtf8());
+                        qDebug() << "检测到开机,自动发送产测模式检测指令:" << cmd;
+                        appendToLogFile(QString("=== 检测到开机,自动发送产测模式检测指令: %1 ===\n").arg(cmd));
+                    } else {
+                        qDebug() << "产测模式检测指令为空,跳过检测";
+                        appendToLogFile(QString("=== 产测模式检测指令为空,跳过检测 ===\n"));
+                        // 如果指令为空,直接标记为已检测,并开始获取版本信息
+                        factoryModeChecked = true;
+                        QTimer::singleShot(1000, this, [this]() {
+                            if (!versionInfoFetched) {
+                                startVersionInfoFetch();
+                            }
+                        });
+                    }
+                } else {
+                    if (!serial->isOpen()) {
+                        qDebug() << "串口未打开,无法发送产测模式检测指令";
+                    } else {
+                        qDebug() << "产测模式已检测过,跳过发送检测指令";
+                    }
+                }
+            });
+            
+            break;
+        }
+    }
+}
+
+// 检测产测模式
+void MainWindow::checkFactoryMode(const QString& data)
+{
+    if (factoryModeChecked) {
+        return; // 已经检测过,不再重复检测
+    }
+    
+    QStringList keywords = configManager.getFactoryModeKeywords();
+    bool found = false;
+    for (const QString& keyword : keywords) {
+        if (data.contains(keyword, Qt::CaseInsensitive)) {
+            qDebug() << "检测到产测模式关键字:" << keyword;
+            appendToLogFile(QString("=== 检测到产测模式: %1 ===\n").arg(keyword));
+            
+            factoryModeChecked = true;
+            found = true;
+            
+            // 解析产测模式状态并更新UI
+            if (data.contains("FACTORY TEST MODE: ENABLED", Qt::CaseInsensitive)) {
+                ui->factoryModeLineEdit->setText("是");
+                ui->factoryModeLineEdit->setStyleSheet("background-color: #ffcccc;"); // 红底
+                qDebug() << "产测模式: 已开启";
+            } else if (data.contains("FACTORY TEST MODE: DISABLED", Qt::CaseInsensitive)) {
+                ui->factoryModeLineEdit->setText("否");
+                ui->factoryModeLineEdit->setStyleSheet("background-color: #ccffcc;"); // 绿底
+                qDebug() << "产测模式: 已关闭";
+            }
+            
+            // 延迟1秒后开始获取版本信息
+            QTimer::singleShot(1000, this, [this]() {
+                if (!versionInfoFetched) {
+                    startVersionInfoFetch();
+                }
+            });
+            
+            break;
+        }
+    }
+    
+    // 如果没有检测到产测模式关键字,但收到了产测模式检测指令的响应(可能是OK或ERROR)
+    // 解析 AT^FACTORY 或 AT^FACTORY? 的响应
+    if (!found) {
+        // 检查是否是 AT^FACTORY 相关指令的响应
+        // AT^FACTORY? 的响应可能是:+FACTORY: 1 或 +FACTORY: 0
+        QRegExp factoryResponsePattern("\\+FACTORY:\\s*(\\d+)", Qt::CaseInsensitive);
+        if (factoryResponsePattern.indexIn(data) != -1) {
+            int factoryValue = factoryResponsePattern.cap(1).toInt();
+            qDebug() << "解析到产测模式响应值:" << factoryValue;
+            appendToLogFile(QString("=== 产测模式检测响应: %1 ===\n").arg(factoryValue));
+            
+            factoryModeChecked = true;
+            
+            // 更新UI
+            if (factoryValue == 1) {
+                ui->factoryModeLineEdit->setText("是");
+                ui->factoryModeLineEdit->setStyleSheet("background-color: #ffcccc;"); // 红底
+                qDebug() << "产测模式: 已开启 (响应值=1)";
+            } else {
+                ui->factoryModeLineEdit->setText("否");
+                ui->factoryModeLineEdit->setStyleSheet("background-color: #ccffcc;"); // 绿底
+                qDebug() << "产测模式: 已关闭 (响应值=0)";
+            }
+            
+            // 延迟1秒后开始获取版本信息
+            QTimer::singleShot(1000, this, [this]() {
+                if (!versionInfoFetched) {
+                    startVersionInfoFetch();
+                }
+            });
+        }
+        // 如果响应是OK,可能是 AT^FACTORY 的确认响应(进入产测模式)
+        else if (data.contains("OK", Qt::CaseInsensitive) && 
+                 (data.contains("FACTORY", Qt::CaseInsensitive) || 
+                  data.contains("AT^FACTORY", Qt::CaseInsensitive))) {
+            qDebug() << "产测模式检测指令返回OK,认为已进入产测模式";
+            appendToLogFile(QString("=== 产测模式检测指令返回OK,认为已进入产测模式 ===\n"));
+            
+            factoryModeChecked = true;
+            
+            // 更新UI(假设OK表示已开启)
+            ui->factoryModeLineEdit->setText("是");
+            ui->factoryModeLineEdit->setStyleSheet("background-color: #ffcccc;"); // 红底
+            
+            // 延迟1秒后开始获取版本信息
+            QTimer::singleShot(1000, this, [this]() {
+                if (!versionInfoFetched) {
+                    startVersionInfoFetch();
+                }
+            });
+        }
+        // 记录其他响应用于调试
+        else if (data.contains("ERROR", Qt::CaseInsensitive) ||
+                 data.contains("+CME ERROR", Qt::CaseInsensitive) ||
+                 data.contains("OK", Qt::CaseInsensitive)) {
+            qDebug() << "产测模式检测指令响应:" << data.trimmed();
+            appendToLogFile(QString("产测模式检测指令响应: %1\n").arg(data.trimmed()));
+        }
+    }
+}
+
+// 开始获取版本信息
+void MainWindow::startVersionInfoFetch()
+{
+    if (versionInfoFetched && currentVersionCommandIndex >= 0) {
+        qDebug() << "版本信息获取正在进行中,跳过重复请求";
+        return; // 正在获取中,不再重复获取
+    }
+    
+    versionCommandQueue = configManager.getVersionInfoCommands();
+    if (versionCommandQueue.isEmpty()) {
+        qDebug() << "版本信息指令列表为空";
+        appendToLogFile(QString("=== 版本信息指令列表为空 ===\n"));
+        return;
+    }
+    
+    qDebug() << "开始获取版本信息,共" << versionCommandQueue.size() << "条指令";
+    appendToLogFile(QString("=== 开始获取版本信息,共%1条指令 ===\n").arg(versionCommandQueue.size()));
+    
+    currentVersionCommandIndex = 0;
+    versionInfoFetched = true;
+    
+    // 延迟500ms后发送第一条指令,确保串口就绪
+    QTimer::singleShot(500, this, [this]() {
+        if (serial->isOpen()) {
+            sendNextVersionCommand();
+        } else {
+            qDebug() << "串口未打开,无法发送版本信息指令";
+            appendToLogFile(QString("=== 串口未打开,无法获取版本信息 ===\n"));
+            versionInfoFetched = false;
+            currentVersionCommandIndex = -1;
+        }
+    });
+}
+
+// 发送下一个版本信息指令
+void MainWindow::sendNextVersionCommand()
+{
+    if (currentVersionCommandIndex < 0 || currentVersionCommandIndex >= versionCommandQueue.size()) {
+        qDebug() << "版本信息获取完成";
+        appendToLogFile(QString("=== 版本信息获取完成 ===\n"));
+        versionCommandTimer->stop();
+        return;
+    }
+    
+    QString cmd = versionCommandQueue[currentVersionCommandIndex];
+    if (serial->isOpen()) {
+        serial->write((cmd + "\r\n").toUtf8());
+        qDebug() << "发送版本信息指令[" << (currentVersionCommandIndex + 1) << "/" << versionCommandQueue.size() << "]:" << cmd;
+        appendToLogFile(QString("发送版本信息指令: %1\n").arg(cmd));
+        
+        // 启动超时定时器(每个指令等待2秒)
+        versionCommandTimer->start(2000);
+        currentVersionCommandIndex++;
+    }
+}
+
+// 版本信息指令超时处理
+void MainWindow::onVersionCommandTimeout()
+{
+    versionCommandTimer->stop();
+    
+    // 继续发送下一条指令
+    if (currentVersionCommandIndex < versionCommandQueue.size()) {
+        sendNextVersionCommand();
+    } else {
+        qDebug() << "所有版本信息指令已发送完成";
+        appendToLogFile(QString("=== 所有版本信息指令已发送完成 ===\n"));
+        // 重置状态,允许再次获取
+        versionInfoFetched = false;
+        currentVersionCommandIndex = -1;
+    }
+}
+
+// 获取版本信息按钮点击事件
+void MainWindow::on_getVersionInfoButton_clicked()
+{
+    // 检查串口是否已打开
+    if (!serial->isOpen()) {
+        QMessageBox::warning(this, "警告", "请先打开串口连接");
+        return;
+    }
+    
+    // 重置版本信息获取状态
+    versionInfoFetched = false;
+    currentVersionCommandIndex = -1;
+    versionCommandQueue.clear();
+    
+    // 开始获取版本信息
+    startVersionInfoFetch();
+}
+
+// 保存测试参数到settings.json
+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.saveConfig();
+    
+    qDebug() << "测试参数已保存到settings.json";
+}
+
+// 从settings.json加载测试参数
+void MainWindow::loadTestConfigFromSettings()
+{
+    // 恢复串口选择(在updateSerialPorts之后调用,确保串口列表已更新)
+    QString savedPortName = configManager.getTestConfig("portName", "");
+    if (!savedPortName.isEmpty()) {
+        // 查找匹配的串口项
+        int index = ui->portNameBox->findText(savedPortName);
+        if (index >= 0) {
+            ui->portNameBox->setCurrentIndex(index);
+            qDebug() << "恢复串口选择:" << savedPortName;
+        } else {
+            qDebug() << "保存的串口不存在,使用默认选择";
+        }
+    }
+    
+    // 恢复音频测试参数
+    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", ""));
+    
+    qDebug() << "测试参数已从settings.json加载";
+}
+
+// 保存recvTextEdit内容到日志文件
+void MainWindow::saveRecvTextEditToLog()
+{
+    QString logContent = ui->recvTextEdit->toPlainText();
+    
+    // 检查是否有内容
+    if (logContent.trimmed().isEmpty()) {
+        qDebug() << "recvTextEdit内容为空,跳过保存";
+        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";
+    QDir dir;
+    if (!dir.exists(logsDir)) {
+        dir.mkpath(logsDir);
+    }
+    
+    // 生成日志文件名
+    QDateTime now = QDateTime::currentDateTime();
+    QString fileName = QString("%1_%2_%3_%4_UI日志.log")
+                       .arg(now.toString("yyyyMMdd"))
+                       .arg(now.toString("hhmmss"))
+                       .arg(imei)
+                       .arg(iccid);
+    
+    QString filePath = logsDir + "/" + fileName;
+    
+    // 保存到文件
+    QFile logFile(filePath);
+    if (logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+        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();
+        
+        qDebug() << "UI日志已保存到:" << filePath;
+        ui->statusbar->showMessage(QString("UI日志已保存: %1").arg(fileName), 3000);
+    } else {
+        qDebug() << "无法保存UI日志文件:" << filePath << "错误:" << logFile.errorString();
+    }
+}
+
 // 创建测试日志文件
 void MainWindow::createTestLogFile()
 {
index df94d91..1c705ae 100644 (file)
@@ -19,6 +19,7 @@
 #include <QSet>
 #include <QFile>
 #include <QTextStream>
+#include "config_manager.h"
 
 QT_BEGIN_NAMESPACE
 namespace Ui { class MainWindow; }
@@ -116,6 +117,9 @@ private slots:
     void on_agingTestButton_2_clicked();
 
     void on_openButton_3_clicked();
+    
+    // 版本信息获取按钮
+    void on_getVersionInfoButton_clicked();
 
 private:
     Ui::MainWindow *ui;
@@ -168,7 +172,8 @@ private:
     QPushButton *audioPassButton;      // 录音通过按钮
     QPushButton *audioFailButton;      // 录音失败按钮
     QPushButton *audioRetestButton;    // 重新测试按钮
-    int audioMicMiddleConsecutiveCount; // 连续检测到"fac:mic level:中"的次数
+    int audioMicMiddleConsecutiveCount; // 连续检测到设置的标准Level的次数
+    QString audioLevelStandard;         // 音频测试通过标准("中"或"低"等)
     bool audioTestFinished;             // 录音测试是否已完成(成功或失败)
     QSet<QString> bluetoothAddresses;  // 蓝牙测试检测到的不同蓝牙地址集合
     bool bluetoothTestFinished;         // 蓝牙测试是否已完成(成功或失败)
@@ -179,6 +184,19 @@ private:
     QTextStream *testLogStream;         // 测试日志文件流
     QString testLogFilePath;            // 测试日志文件路径
     
+    // 配置管理
+    ConfigManager configManager;       // 配置管理器(不使用指针,直接作为成员)
+    
+    // 开机检测相关变量
+    bool bootDetected;                  // 是否已检测到开机
+    bool factoryModeChecked;            // 是否已检测产测模式
+    bool versionInfoFetched;           // 是否已获取版本信息
+    
+    // 版本信息获取相关变量
+    QStringList versionCommandQueue;    // 版本信息AT指令队列
+    int currentVersionCommandIndex;     // 当前执行的版本信息指令索引
+    QTimer *versionCommandTimer;        // 版本信息指令发送定时器
+    
     enum AudioTestStep {
         AUDIO_STARTING_RECORD,  // 开始录音
         AUDIO_PLAYING_1KHZ,     // 正在播放1kHz音频
@@ -217,5 +235,17 @@ private:
     void ShowPppInfoPYVer(const QString& data);
     // CAT1信号测试相关函数
     void parseNetworkStatChanged(const QString& data);
+    
+    // 开机检测和版本信息获取相关函数
+    void checkBootDetection(const QString& data);        // 检测设备开机
+    void checkFactoryMode(const QString& data);          // 检测产测模式
+    void startVersionInfoFetch();                        // 开始获取版本信息
+    void sendNextVersionCommand();                       // 发送下一个版本信息指令
+    void onVersionCommandTimeout();                      // 版本信息指令超时处理
+    void saveRecvTextEditToLog();                        // 保存recvTextEdit内容到日志文件
+    
+    // 测试参数配置保存和恢复(使用settings.json)
+    void saveTestConfigToSettings();                     // 保存测试参数到settings.json
+    void loadTestConfigFromSettings();                  // 从settings.json加载测试参数
 };
 #endif // MAINWINDOW_H
index b0ecb5e..53e5a2d 100644 (file)
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>1309</width>
-    <height>641</height>
+    <height>823</height>
    </rect>
   </property>
   <property name="font">
      <number>6</number>
     </property>
     <item>
-     <widget class="QWidget" name="leftPanel">
-      <property name="maximumSize">
-       <size>
-        <width>280</width>
-        <height>16777215</height>
-       </size>
-      </property>
-      <property name="sizePolicy">
-       <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
-        <horstretch>0</horstretch>
-        <verstretch>0</verstretch>
-       </sizepolicy>
+     <layout class="QVBoxLayout" name="leftLayout">
+      <property name="spacing">
+       <number>6</number>
       </property>
-      <layout class="QVBoxLayout" name="leftLayout">
-       <property name="spacing">
-        <number>6</number>
-       </property>
-      <item>
-       <widget class="QComboBox" name="portNameBox">
-        <property name="font">
-         <font>
-          <family>Arial</family>
-          <pointsize>14</pointsize>
-         </font>
-        </property>
-        <property name="sizeAdjustPolicy">
-         <enum>QComboBox::AdjustToContentsOnFirstShow</enum>
-        </property>
-       </widget>
-      </item>
       <item>
        <layout class="QGridLayout" name="buttonGridLayout">
         <property name="spacing">
          <number>6</number>
         </property>
-        <item row="2" column="0">
-         <widget class="QPushButton" name="openButton_2">
+        <item row="7" column="0">
+         <widget class="Line" name="line">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+         </widget>
+        </item>
+        <item row="8" column="0">
+         <layout class="QVBoxLayout" name="verticalLayout_3">
+          <item>
+           <widget class="QLabel" name="label_aging">
+            <property name="font">
+             <font>
+              <family>Arial Black</family>
+              <weight>75</weight>
+              <bold>true</bold>
+             </font>
+            </property>
+            <property name="text">
+             <string>老化测试:</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="openButton_agingresult">
+            <property name="font">
+             <font>
+              <family>Agra</family>
+              <pointsize>14</pointsize>
+             </font>
+            </property>
+            <property name="text">
+             <string>老化结果</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="agingTestButton">
+            <property name="font">
+             <font>
+              <family>Agra</family>
+              <pointsize>14</pointsize>
+              <weight>75</weight>
+              <bold>true</bold>
+             </font>
+            </property>
+            <property name="styleSheet">
+             <string>QPushButton { 
+background-color: #9C27B0; 
+color: white; 
+border-radius: 4px; 
+font-weight: bold; 
+}
+QPushButton:hover { 
+background-color: #7B1FA2; 
+}</string>
+            </property>
+            <property name="text">
+             <string>老化测试</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item row="4" column="0">
+         <widget class="QPushButton" name="clearRecvButton">
           <property name="font">
            <font>
             <family>Agra</family>
            </font>
           </property>
           <property name="text">
-           <string>开始产测</string>
+           <string>清空数据</string>
           </property>
          </widget>
         </item>
-        <item row="0" column="0">
-         <widget class="QPushButton" name="openButton">
+        <item row="6" column="0">
+         <widget class="QPushButton" name="getVersionInfoButton">
           <property name="font">
            <font>
             <family>Agra</family>
            </font>
           </property>
           <property name="text">
-           <string>打开串口</string>
+           <string>获取设备信息</string>
           </property>
          </widget>
         </item>
-        <item row="4" column="0">
-         <widget class="QPushButton" name="agingTestButton_2">
+        <item row="3" column="0">
+         <widget class="QPushButton" name="openButton_2">
           <property name="font">
            <font>
             <family>Agra</family>
             <pointsize>14</pointsize>
-            <weight>75</weight>
-            <bold>true</bold>
            </font>
           </property>
-          <property name="styleSheet">
-           <string>QPushButton { 
-background-color: #9C27B0; 
-color: white; 
-border-radius: 4px; 
-font-weight: bold; 
-}
-QPushButton:hover { 
-background-color: #7B1FA2; 
-}</string>
-          </property>
           <property name="text">
-           <string>恢复出厂</string>
+           <string>开始产测</string>
           </property>
          </widget>
         </item>
         <item row="5" column="0">
-         <widget class="QPushButton" name="agingTestButton">
+         <widget class="QPushButton" name="agingTestButton_2">
           <property name="font">
            <font>
             <family>Agra</family>
@@ -145,12 +170,12 @@ background-color: #7B1FA2;
 }</string>
           </property>
           <property name="text">
-           <string>老化测试</string>
+           <string>恢复出厂</string>
           </property>
          </widget>
         </item>
-        <item row="3" column="0">
-         <widget class="QPushButton" name="clearRecvButton">
+        <item row="1" column="0">
+         <widget class="QPushButton" name="openButton">
           <property name="font">
            <font>
             <family>Agra</family>
@@ -158,24 +183,31 @@ background-color: #7B1FA2;
            </font>
           </property>
           <property name="text">
-           <string>æ¸\85空æ\95°æ\8d®</string>
+           <string>æ\89\93å¼\80串å\8f£</string>
           </property>
          </widget>
         </item>
-        <item row="6" column="0">
-         <widget class="QPushButton" name="openButton_3">
+        <item row="0" column="0">
+         <widget class="QComboBox" name="portNameBox">
           <property name="font">
            <font>
-            <family>Agra</family>
+            <family>Arial</family>
             <pointsize>14</pointsize>
            </font>
           </property>
-          <property name="text">
-           <string>老化结果</string>
+          <property name="sizeAdjustPolicy">
+           <enum>QComboBox::AdjustToContentsOnFirstShow</enum>
           </property>
          </widget>
         </item>
-        <item row="7" column="0">
+        <item row="9" column="0">
+         <widget class="Line" name="line_2">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+         </widget>
+        </item>
+        <item row="12" column="0">
          <widget class="QPushButton" name="openRecordsFolderButton">
           <property name="font">
            <font>
@@ -204,45 +236,211 @@ background-color: #F57C00;
        </layout>
       </item>
       <item>
-       <layout class="QHBoxLayout" name="audioVolumeLayout">
-        <property name="spacing">
-         <number>6</number>
-        </property>
+       <layout class="QVBoxLayout" name="verticalLayout_">
         <item>
-         <widget class="QLabel" name="AudioVolumeLabel">
+         <widget class="Line" name="line_3">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="label_2">
           <property name="font">
            <font>
-            <family>Agra</family>
-            <pointsize>14</pointsize>
+            <family>Arial Black</family>
+            <weight>75</weight>
+            <bold>true</bold>
            </font>
           </property>
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
+          <property name="text">
+           <string>测试参数及通过标准:</string>
+          </property>
+         </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">
           <property name="text">
-           <string>音频测试音量</string>
+           <string>目标MODEM版本:</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QLineEdit" name="audioVolumeLineEdit">
-          <property name="font">
-           <font>
-            <family>Agra</family>
-            <pointsize>14</pointsize>
-           </font>
+         <widget class="QLineEdit" name="expectedModemVerLineEdit">
+          <property name="statusTip">
+           <string>为空不校验Modem版本是否正确</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeft|Qt::AlignVCenter</set>
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
           </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="ExpectedModemLabel_2">
           <property name="text">
-           <string>2</string>
+           <string>目标PY版本:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="expectedPYVerLineEdit">
+          <property name="statusTip">
+           <string>为空不校验Py版本是否正确</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="ExpectedVerLabel">
+          <property name="text">
+           <string>目标固件版本:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="expectedVerLineEdit">
+          <property name="toolTip">
+           <string/>
+          </property>
+          <property name="statusTip">
+           <string>为空不校验固件版本是否正确。</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="BluetoothAddressLabel">
+          <property name="text">
+           <string>广播设备-蓝牙地址:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="bluetoothAddressLineEdit">
+          <property name="toolTip">
+           <string/>
+          </property>
+          <property name="statusTip">
+           <string>供测试用常广播的经典蓝牙设备地址。可为空,为空时,扫描到3个以上设备为通过。</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+          </property>
+          <property name="placeholderText">
+           <string>例如: 1C:1D:2B:68:82:68</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="BluetoothRSSILabel">
+          <property name="text">
+           <string>广播设备- Min_RSSI:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="bluetoothRSSILineEdit">
+          <property name="toolTip">
+           <string/>
+          </property>
+          <property name="statusTip">
+           <string>如Min_RSSI = -80,则扫到广播设备且RSSI &gt; -80为成功。</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
           </property>
           <property name="placeholderText">
-           <string>例如: 2</string>
+           <string>例如: -80</string>
           </property>
          </widget>
         </item>
@@ -262,17 +460,9 @@ background-color: #F57C00;
        </spacer>
       </item>
      </layout>
-      </widget>
     </item>
     <item>
-     <widget class="QWidget" name="centerPanel">
-      <property name="sizePolicy">
-       <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-        <horstretch>5</horstretch>
-        <verstretch>0</verstretch>
-       </sizepolicy>
-      </property>
-      <layout class="QVBoxLayout" name="centerLayout">
+     <layout class="QVBoxLayout" name="centerLayout">
       <property name="spacing">
        <number>6</number>
       </property>
@@ -316,24 +506,10 @@ background-color: #F57C00;
         </item>
        </layout>
       </item>
-       </layout>
-      </widget>
-     </item>
+     </layout>
+    </item>
     <item>
-     <widget class="QWidget" name="rightPanel">
-      <property name="minimumSize">
-       <size>
-        <width>380</width>
-        <height>0</height>
-       </size>
-      </property>
-      <property name="sizePolicy">
-       <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-        <horstretch>2</horstretch>
-        <verstretch>0</verstretch>
-       </sizepolicy>
-      </property>
-      <layout class="QVBoxLayout" name="rightLayout">
+     <layout class="QVBoxLayout" name="rightLayout">
       <property name="spacing">
        <number>6</number>
       </property>
@@ -345,157 +521,143 @@ background-color: #F57C00;
         <property name="verticalSpacing">
          <number>4</number>
         </property>
-        <item row="0" column="0">
+        <item row="2" column="0">
          <widget class="QLabel" name="Verlabel">
           <property name="text">
            <string>Ver:</string>
           </property>
          </widget>
         </item>
-        <item row="0" column="1">
+        <item row="2" column="1">
          <widget class="QLineEdit" name="VerlineEdit">
           <property name="alignment">
-           <set>Qt::AlignLeft|Qt::AlignVCenter</set>
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
           </property>
          </widget>
         </item>
-        <item row="1" column="0">
+        <item row="3" column="0">
          <widget class="QLabel" name="Verlabel_2">
           <property name="text">
            <string>IMEI:</string>
           </property>
          </widget>
         </item>
-        <item row="1" column="1">
+        <item row="3" column="1">
          <widget class="QLineEdit" name="IMEIlineEdit">
           <property name="alignment">
-           <set>Qt::AlignLeft|Qt::AlignVCenter</set>
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
           </property>
          </widget>
         </item>
-        <item row="2" column="0">
+        <item row="4" column="0">
          <widget class="QLabel" name="Verlabel_3">
           <property name="text">
            <string>ICCID</string>
           </property>
          </widget>
         </item>
-        <item row="2" column="1">
+        <item row="4" column="1">
          <widget class="QLineEdit" name="ICCIDlineEdit">
           <property name="alignment">
-           <set>Qt::AlignLeft|Qt::AlignVCenter</set>
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
           </property>
          </widget>
         </item>
-        <item row="3" column="0">
+        <item row="5" column="0">
          <widget class="QLabel" name="Verlabel_4">
           <property name="text">
            <string>Modem:</string>
           </property>
          </widget>
         </item>
-        <item row="3" column="1">
+        <item row="5" column="1">
          <widget class="QLineEdit" name="ModemVerlineEdit">
           <property name="alignment">
-           <set>Qt::AlignLeft|Qt::AlignVCenter</set>
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
           </property>
          </widget>
         </item>
-        <item row="4" column="0">
+        <item row="6" column="0">
          <widget class="QLabel" name="Verlabel_5">
           <property name="text">
            <string>PYVER</string>
           </property>
          </widget>
         </item>
-        <item row="4" column="1">
+        <item row="6" column="1">
          <widget class="QLineEdit" name="PYVerlineEdit">
-          <property name="alignment">
-           <set>Qt::AlignLeft|Qt::AlignVCenter</set>
-          </property>
           <property name="text">
            <string/>
           </property>
-         </widget>
-        </item>
-        <item row="5" column="0">
-         <widget class="QLabel" name="ExpectedVerLabel">
-          <property name="text">
-           <string>最新VER:</string>
-          </property>
-         </widget>
-        </item>
-        <item row="5" column="1">
-         <widget class="QLineEdit" name="expectedVerLineEdit">
           <property name="alignment">
-           <set>Qt::AlignLeft|Qt::AlignVCenter</set>
-          </property>
-         </widget>
-        </item>
-        <item row="6" column="0">
-         <widget class="QLabel" name="ExpectedModemLabel">
-          <property name="text">
-           <string>最新MODEM:</string>
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
           </property>
          </widget>
         </item>
-        <item row="6" column="1">
-         <widget class="QLineEdit" name="expectedModemVerLineEdit">
-          <property name="alignment">
-           <set>Qt::AlignLeft|Qt::AlignVCenter</set>
+        <item row="1" column="0">
+         <widget class="QLabel" name="label">
+          <property name="font">
+           <font>
+            <family>Arial Black</family>
+            <weight>75</weight>
+            <bold>true</bold>
+           </font>
           </property>
-         </widget>
-        </item>
-        <item row="7" column="0">
-         <widget class="QLabel" name="ExpectedModemLabel_2">
           <property name="text">
-           <string>最新PY:</string>
+           <string>设备状态:</string>
           </property>
          </widget>
         </item>
         <item row="7" column="1">
-         <widget class="QLineEdit" name="expectedPYVerLineEdit">
-          <property name="alignment">
-           <set>Qt::AlignLeft|Qt::AlignVCenter</set>
+         <widget class="QLineEdit" name="factoryModeLineEdit">
+          <property name="font">
+           <font>
+            <family>Arial</family>
+            <pointsize>14</pointsize>
+           </font>
           </property>
-         </widget>
-        </item>
-        <item row="8" column="0">
-         <widget class="QLabel" name="BluetoothAddressLabel">
           <property name="text">
-           <string>蓝牙地址:</string>
+           <string/>
           </property>
-         </widget>
-        </item>
-        <item row="8" column="1">
-         <widget class="QLineEdit" name="bluetoothAddressLineEdit">
           <property name="alignment">
-           <set>Qt::AlignLeft|Qt::AlignVCenter</set>
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+          </property>
+          <property name="readOnly">
+           <bool>true</bool>
           </property>
           <property name="placeholderText">
-           <string>例如: 1C:1D:2B:68:82:68</string>
+           <string>例如: 是/否</string>
           </property>
          </widget>
         </item>
-        <item row="9" column="0">
-         <widget class="QLabel" name="BluetoothRSSILabel">
-          <property name="text">
-           <string>RSSI阈值:</string>
+        <item row="7" column="0">
+         <widget class="QLabel" name="factoryModeLabel">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
           </property>
-         </widget>
-        </item>
-        <item row="9" column="1">
-         <widget class="QLineEdit" name="bluetoothRSSILineEdit">
-          <property name="alignment">
-           <set>Qt::AlignLeft|Qt::AlignVCenter</set>
+          <property name="font">
+           <font>
+            <family>Agra</family>
+            <pointsize>14</pointsize>
+           </font>
           </property>
-          <property name="placeholderText">
-           <string>例如: -80</string>
+          <property name="text">
+           <string>设备在产测模式</string>
           </property>
          </widget>
         </item>
        </layout>
       </item>
+      <item>
+       <widget class="Line" name="line_5">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+       </widget>
+      </item>
       <item>
        <widget class="QScrollArea" name="testStatusScrollArea">
         <property name="sizePolicy">
@@ -513,7 +675,7 @@ background-color: #F57C00;
            <x>0</x>
            <y>0</y>
            <width>424</width>
-           <height>318</height>
+           <height>523</height>
           </rect>
          </property>
          <property name="title">
@@ -1003,7 +1165,6 @@ background-color: #F57C00;
        </widget>
       </item>
      </layout>
-      </widget>
     </item>
    </layout>
   </widget>
diff --git a/settings.json b/settings.json
new file mode 100644 (file)
index 0000000..fa7504d
--- /dev/null
@@ -0,0 +1,27 @@
+{\r
+    "bootDetectionKeywords": [\r
+        "BuildVer",\r
+        "NAND flash ID",\r
+        "NAND ID:"\r
+    ],\r
+    "versionInfoCommands": [\r
+        "AT^CAT1GETVER",\r
+        "AT^CAT1GETSIGNAL",\r
+        "AT^FACTORY"\r
+    ],\r
+    "factoryModeCheckCommand": "AT^FACTORY",\r
+    "factoryModeKeywords": [\r
+        "FACTORY TEST MODE:"\r
+    ],\r
+    "testConfig": {\r
+        "portName": "",\r
+        "audioVolume": "2.0",\r
+        "audioLevelStandard": "中",\r
+        "expectedModemVer": "",\r
+        "expectedPYVer": "",\r
+        "expectedVer": "",\r
+        "bluetoothAddress": "",\r
+        "bluetoothRSSI": ""\r
+    }\r
+}\r
+\r