|
|
#pragma execution_character_set("utf-8")
|
|
|
#include "widget.h"
|
|
|
#include "ui_widget.h"
|
|
|
|
|
|
#include <windows.h>
|
|
|
#include <tlhelp32.h>
|
|
|
|
|
|
#include <QDateTime>
|
|
|
#include <QHBoxLayout>
|
|
|
#include <QMessageBox>
|
|
|
#include <QEventLoop>
|
|
|
|
|
|
#include <QSqlQuery>
|
|
|
#include <QSqlError>
|
|
|
#include <QProcess>
|
|
|
#include <QDir>
|
|
|
#include <QRegularExpression>
|
|
|
|
|
|
#include "countdownmessagebox.h"
|
|
|
#include "common.h"
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
#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上
|
|
|
ui->lineEdit_snId->clear();
|
|
|
ui->lineEdit_snId->setFocus();
|
|
|
this->activateWindow();
|
|
|
}
|
|
|
|
|
|
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<int, QProcess::ExitStatus>::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();
|
|
|
}
|