#pragma execution_character_set("utf-8") #include "widget.h" #include "ui_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include "countdownmessagebox.h" #include "common.h" #include #define MAXITEMS 20 Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) , m_settingDlg(new SettingWgt(this)) , m_udpDataThread(new UdpDataHandler(this)) , m_showSetting(false) { ui->setupUi(this); initUI(); connect(m_settingDlg, &SettingWgt::SettingFinishSig, this, &Widget::on_pushButton_setting_clicked); connect(m_udpDataThread, &UdpDataHandler::GetDoneSig, this, &Widget::RegisterDoneSlot); connect(m_udpDataThread, &UdpDataHandler::GetConnStatusSig, this, &Widget::GetConnStatusSlot); addLogItem("程序初始化成功!"); ProcessJudgment(); } Widget::~Widget() { delete ui; if (m_db.isOpen()) { m_db.close(); QSqlDatabase::removeDatabase(QSqlDatabase::defaultConnection); } } void Widget::initUI() { this->setWindowTitle("LaunchRegisterKey"); this->setWindowFlags(Qt::Window | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint); //this->setWindowFlags(Qt::Window | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint | Qt::CustomizeWindowHint); this->setWindowIcon(QIcon(":/resources/icon.ico")); ui->list_log->setEditTriggers(QAbstractItemView::NoEditTriggers); QHBoxLayout *layout = new QHBoxLayout(this); layout->addWidget(m_settingDlg); ui->widget_setting->setLayout(layout); ui->widget_setting->hide(); ui->lineEdit_snId->setFocus(); } void Widget::setSNLineEditFocus() { // 全选LineEdit并且焦点放在LineEdit上 #ifdef TEST_MODE QString tmpSn = ui->lineEdit_snId->text(); ui->lineEdit_snId->setText("1234567890123456"); #endif ui->lineEdit_snId->clear(); ui->lineEdit_snId->setFocus(); } int Widget::checkSNId(const QString &snId) { if (snId.isEmpty()) { QMessageBox::warning(this, "产品SN错误", "产品SN为空! 请重新扫码!"); addLogItem("产品SN为空! 请重新扫码!"); LWarn("Product SN id is empty!"); return SNEmptyError; } QRegExp reg("[a-z]"); if (reg.indexIn(snId) != -1) { QMessageBox::information(this,"提示",QString("产品SN(%1)包含小写! 请重新扫码!").arg(snId)); addLogItem(QString("产品SN(%1)包含小写! 请重新扫码!").arg(snId)); LWarn("Product SN contains lowercase!"); ui->lineEdit_snId->setText(""); return SNLowerCaseError; } if (snId.contains(" ")) { QMessageBox::information(this,"提示",QString("产品SN(%1)包含空格! 请重新扫码!").arg(snId)); addLogItem(QString("产品SN(%1)包含空格! 请重新扫码!").arg(snId)); LWarn("Product SN contains Spaces!"); ui->lineEdit_snId->setText(""); return SNSpaceError; } return NoError; } int Widget::startIECSProgram(const QString& workPath, const QString& snId) { LInfo("Function enter"); QProcess *process = new QProcess(this); connect(process, QOverload::of(&QProcess::finished), process, &QProcess::deleteLater); // 使用 cmd.exe 来启动进程并使用 start 命令使其脱离父进程 QString command = QString("cmd.exe /C start \"\" \"%1\"").arg(workPath); process->start(command); if (!process->waitForStarted()) { LError("Failed to start the program with arguments: {}", process->errorString()); process->deleteLater(); addLogItem(QString("%1启动失败!").arg(workPath)); return ExecIECSError; } LInfo("Process started: {}", workPath); return NoError; } int Widget::checkForRepairProgram(const int mode, const QString& sn, bool& isFind) { auto workModeCfg = Common::GetInstance()->getWorkModeConfigs(); QDir workDir(workModeCfg[mode].workPath); if (!workDir.cdUp()) { LError("Workdir cd up failed!"); return -1; } QString dbPath = workDir.absolutePath() + "/config.db"; return databaseQuery(dbPath, sn, isFind); } int Widget::databaseQuery(const QString& databasePath, const QString& deviceSn, bool& isFind) { // 静态变量来保存上一次使用的数据库路径 static QString previousDatabasePath; // 如果数据库路径发生变动,关闭之前的连接并移除数据库 if (m_db.isOpen() && previousDatabasePath != databasePath) { m_db.close(); QSqlDatabase::removeDatabase(QSqlDatabase::defaultConnection); } // 如果数据库尚未打开或路径已变动,重新打开数据库 if (!m_db.isOpen() || previousDatabasePath != databasePath) { m_db = QSqlDatabase::addDatabase("QSQLITE"); m_db.setDatabaseName(databasePath); // 使用传入的数据库路径 if (!m_db.open()) { LError("Failed to open database:{}", m_db.lastError().text()); isFind = false; return DatabaseOpenError; } // 更新路径 previousDatabasePath = databasePath; } // 创建查询对象 QSqlQuery query(m_db); // 准备 SQL 查询 query.prepare("SELECT COUNT(1) FROM history WHERE device_sn = :device_sn;"); query.bindValue(":device_sn", deviceSn); // 执行查询 if (!query.exec()) { LError("Failed to execute query:{}", query.lastError().text()); isFind = false; return DatabaseSelectError; } // 处理查询结果 if (query.next()) { int count = query.value(0).toInt(); isFind = (count > 0); return NoError; } isFind = false; return DatabaseUnknownError; } // 判断是否为单板 SN static bool isSingleBoardSN(const QString& sn) { // 单板 SN 格式:9位前缀-固定12位尾数 QRegularExpression regex("^\\d{9}-[A-Z0-9]{12}$"); return regex.match(sn).hasMatch(); } // 判断是否为整机 SN static bool isCompleteMachineSN(const QString& sn) { // 整机 SN 格式:前25位前缀-固定8位尾数 QRegularExpression regex("^.{25}[A-Z0-9]{10,}$"); return sn.contains('#') && regex.match(sn).hasMatch(); } bool Widget::identifyProduct(const QString &sn, int &mode) { const auto selectedProducts = Common::GetInstance()->getSelectedProducts(); if (isSingleBoardSN(sn)) { QString productKey = sn.mid(9); // 提取单板 SN 的尾数部分 if (selectedProducts.contains(productKey)) { mode = selectedProducts[productKey]; return true; // 返回匹配的产品名称 } } else if (isCompleteMachineSN(sn)) { QString productKey = sn.mid(25); // 提取整机 SN 的尾数部分 if (selectedProducts.contains(productKey)) { mode = selectedProducts[productKey]; return true; // 返回匹配的产品名称 } } return false; } void Widget::RegisterDoneSlot(bool ret) { if (ret) { addLogItem("注KEY成功"); } else { addLogItem("注KEY失败,请检查对应程序注KEY详情!"); } ui->lineEdit_snId->setEnabled(true); ui->pushButton_sendSnId->setEnabled(true); setSNLineEditFocus(); } void Widget::GetConnStatusSlot(bool ret) { if (ret) { //Common::GetInstance()-> setWindowTitle(QString("LaunchRegisterKey-已连接")); } else { setWindowTitle("LaunchRegisterKey-未连接"); } } // 辅助函数:比较进程路径和目标路径 static bool isMatchingProcess(const QString& processPath, const QString& targetPath) { return processPath.compare(targetPath, Qt::CaseInsensitive) == 0; } bool Widget::ProcessJudgment() { // 创建一个快照来获取当前系统的所有进程 HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) { qWarning() << "Failed to create process snapshot."; return false; } PROCESSENTRY32 pe32{}; pe32.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(hProcessSnap, &pe32)) { qWarning() << "Failed to retrieve process information."; CloseHandle(hProcessSnap); return false; } const auto& workModeCfgs = Common::GetInstance()->getWorkModeConfigs(); do { HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pe32.th32ProcessID); if (!hProcess) { continue; // 无法打开进程,继续下一个进程 } WCHAR exePath[MAX_PATH]; DWORD size = MAX_PATH; if (QueryFullProcessImageName(hProcess, 0, exePath, &size)) { QString processPath = QDir::fromNativeSeparators(QString::fromWCharArray(exePath)); for (const auto& workmodeCfg : workModeCfgs) { int matchedType = -1; // 用于保存匹配的路径类型 if (isMatchingProcess(processPath, workmodeCfg.workPath)) { matchedType = 0; // workPath 匹配 } else if (isMatchingProcess(processPath, workmodeCfg.reworkPath)) { matchedType = 1; // reworkPath 匹配 } if (matchedType != -1) { Common::GetInstance()->setCurrentWorkMode(workmodeCfg.index); Common::GetInstance()->setRepairFlag(matchedType == 1); // 如果是 reworkPath,设置为 true QString logStr = QString("正在使用%1").arg(Common::GetInstance()->getCurrWorkModeStr()); addLogItem(logStr); qDebug() << "Matching process found:" << QString::fromWCharArray(pe32.szExeFile) << "PID:" << pe32.th32ProcessID << "Path:" << processPath << "Matched Type:" << (matchedType == 0 ? "workPath" : "reworkPath"); CloseHandle(hProcess); CloseHandle(hProcessSnap); return true; } } } CloseHandle(hProcess); // 确保句柄被关闭 } while (Process32Next(hProcessSnap, &pe32)); CloseHandle(hProcessSnap); qDebug() << "No matching process found."; return false; } bool Widget::isProcessRunningWithName(const QString& processNamePart) { // 创建一个快照来获取当前系统的所有进程 HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) { qWarning() << "Failed to create process snapshot."; return false; } // 初始化进程信息结构体 PROCESSENTRY32 pe32; pe32.dwSize = sizeof(PROCESSENTRY32); // 获取第一个进程信息 if (!Process32First(hProcessSnap, &pe32)) { qWarning() << "Failed to retrieve process information."; CloseHandle(hProcessSnap); return false; } // 遍历所有进程 do { // 将进程名称转换为QString QString processName = QString::fromWCharArray(pe32.szExeFile); // 判断进程名称是否包含指定字符串 if (processName.contains(processNamePart, Qt::CaseInsensitive)) { qDebug() << "Found matching process:" << processName; CloseHandle(hProcessSnap); return true; // 找到匹配的进程,返回 true } } while (Process32Next(hProcessSnap, &pe32)); // 关闭快照句柄 CloseHandle(hProcessSnap); return false; } void Widget::on_pushButton_setting_clicked() { static auto initSize = this->size(); if (ui->widget_setting->isHidden()) { ui->pushButton_setting->setIcon(QIcon(":/resources/folding.png")); ui->widget_setting->show(); m_settingDlg->loadConfig(); int currentWidth = this->width(); this->adjustSize(); this->resize(currentWidth, this->height()); // 只保持高度变化 } else { ui->pushButton_setting->setIcon(QIcon(":/resources/unfolding.png")); ui->widget_setting->hide(); int currentWidth = this->width(); this->adjustSize(); this->resize(initSize); // 只保持高度变化 setSNLineEditFocus(); } } void Widget::addLogItem(const QString &text) { if (ui->list_log->count() >= MAXITEMS) { delete ui->list_log->takeItem(0); } QString dateTimeStr = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"); ui->list_log->addItem(dateTimeStr + " " + text); // 添加新条目 ui->list_log->scrollToBottom(); // 滚动到底部 } int Widget::switchProgram(const QString snId, const int mode, const bool isRepair) { LTrace("Function enter"); static bool isFirstRepair = true; if (isRepair && isFirstRepair) { isFirstRepair = false; CountdownMessageBox::showCountdownMessageBox(this, 3, "启用返修程序包!"); } else if (!isRepair) { isFirstRepair = true; } auto workModeCfgs = Common::GetInstance()->getWorkModeConfigs(); auto workModeCfg = workModeCfgs[mode]; QString workPath = isRepair ? workModeCfg.reworkPath : workModeCfg.workPath; QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, [this, timer, snId, workPath]() { static int times = 0; if (Common::GetInstance()->getCurrentWorkMode() == -1) { m_udpDataThread->stopClose(); if (!isProcessRunningWithName("IECS")) { timer->stop(); // 停止定时器 times = 0; // 启动 IECS 程序 if (startIECSProgram(workPath, snId) != NoError) { ui->lineEdit_snId->setEnabled(true); ui->pushButton_sendSnId->setEnabled(true); QMessageBox::warning(this, "程序启动失败!", QString("启动%1失败,请检查程序路径").arg(workPath)); return; } // 发送 SN ID 和记录日志 m_udpDataThread->sendSnId(snId); addLogItem(QString("已发送产品SN:%1").arg(snId)); } else { times++; if (times >= 10) { times = 0; timer->stop(); ui->lineEdit_snId->setEnabled(true); ui->pushButton_sendSnId->setEnabled(true); QMessageBox::warning(this, "程序启动失败!", QString("启动%1失败,进程已存在").arg(workPath)); } } } }); timer->start(1000); return 0; } void Widget::on_lineEdit_snId_returnPressed() { LTrace("Function enter"); QString snId = ui->lineEdit_snId->text(); int ret = 0; ret = checkSNId(snId); if (ret != 0) { LError("checkSNId error!code:{}", ret); return; } int snMode = -1; if (!identifyProduct(snId, snMode)) { QMessageBox::information(this, "警告", "当前SN与启用设值SN不匹配,请检查设置或重新扫码"); return; } ui->lineEdit_snId->setEnabled(false); ui->pushButton_sendSnId->setEnabled(false); LInfo("SN ID:{}, Mode:{}", snId, snMode); auto currWorkMode = Common::GetInstance()->getCurrentWorkMode(); bool repairFlag = Common::GetInstance()->getRepairFlag(); bool isRepair = false; // 查找数据库,判断是否需要返修程序 ret = checkForRepairProgram(snMode, snId, isRepair); if (ret != NoError) { LError("checkForRepairProgram error!{}", ret); return; } if (snMode != currWorkMode || isRepair != repairFlag) { Common::GetInstance()->setRepairFlag(isRepair); QString reStr = isRepair ? "反修" : "正常"; QString logStr = QString("启动%1-%2").arg(Common::getWorkModeStr(snMode)).arg(reStr); addLogItem(logStr); // 切换工作模式 if (currWorkMode != -1) { m_udpDataThread->sendClose(); } //TODO:不相等时,m_udpDataThread发送关闭信号,等待Common单例类中的标志位变成-1后,m_udpDataThread停止发送关闭信号并启动IECS程序 //同时需要去IECS程序路径中查找数据库,判断是否使用返修程序 switchProgram(snId, snMode, isRepair); return; } m_udpDataThread->sendSnId(snId); addLogItem(QString("已发送产品SN:%1").arg(snId)); } void Widget::on_pushButton_sendSnId_clicked() { on_lineEdit_snId_returnPressed(); }