新增缓存数据库存储、转码进度显示

This commit is contained in:
taocong 2026-05-26 14:33:03 +08:00
parent fd38553be1
commit c097120ca3
15 changed files with 1239 additions and 100 deletions

View File

@ -1,4 +1,4 @@
QT += core gui widgets
QT += core gui widgets sql
TARGET = Uft30ChangeCode
TEMPLATE = app
@ -12,7 +12,11 @@ SOURCES += main.cpp \
src/pages/batchconvert/batchconvertpage.cpp \
src/pages/functionsearch/functionsearchpage.cpp \
src/pages/help/helppage.cpp \
src/pages/settings/settingspage.cpp
src/pages/settings/settingspage.cpp \
src/utils/datacache.cpp \
src/utils/uf2configreader.cpp \
src/utils/uft3configreader.cpp \
src/utils/logmanager.cpp
HEADERS += src/mainwindow/mainwindow.h \
src/pythonrunner/PythonRunner.h \
@ -20,7 +24,11 @@ HEADERS += src/mainwindow/mainwindow.h \
src/pages/batchconvert/batchconvertpage.h \
src/pages/functionsearch/functionsearchpage.h \
src/pages/help/helppage.h \
src/pages/settings/settingspage.h
src/pages/settings/settingspage.h \
src/utils/datacache.h \
src/utils/uf2configreader.h \
src/utils/uft3configreader.h \
src/utils/logmanager.h
RESOURCES += resources.qrc

View File

@ -1,7 +1,7 @@
<RCC>
<qresource prefix="/">
<file>resources/images/ChangeCode.png</file>
<file>resources/images/delect.png</file>
<file>resources/images/bianji.png</file>
<file>resources/images/zhedie_left.png</file>
<file>resources/images/zhedie_right.png</file>
<file>resources/images/max.png</file>

BIN
resources/images/bianji.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -3,6 +3,9 @@
#include "ElaText.h"
#include "ElaTheme.h"
#include "ElaToolButton.h"
#include "utils/uf2configreader.h"
#include "utils/uft3configreader.h"
#include "utils/logmanager.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
@ -30,39 +33,73 @@ MainWindow::MainWindow(QWidget *parent)
initWindow();
initContent();
// 获取应用程序目录
LogManager::instance()->initLogFile();
LogManager::instance()->logInfo("应用程序启动");
QString appDir = QCoreApplication::applicationDirPath();
// 配置 Python 运行器 - 只使用打包好的 exe路径是 uf2touft3/uf2touft3.exe
loadConfigCache();
m_pythonRunner->setRunMode(PythonRunner::UsePackagedExe);
// 设置打包好的可执行文件路径和工作目录
QString exePath = appDir + "/uf2touft3/uf2touft3.exe";
QString workDir = appDir + "/uf2touft3";
m_pythonRunner->setPackagedExePath(exePath);
m_pythonRunner->setWorkingDirectory(workDir);
// 检查 exe 是否存在
QFileInfo exeInfo(exePath);
if (!exeInfo.exists() || !exeInfo.isFile()) {
m_batchLogEdit->append("⚠️ 警告:未找到 uf2touft3/uf2touft3.exe");
m_batchLogEdit->append(QString(" 期望位置:%1").arg(exePath));
LogManager::instance()->logWarning("⚠️ 警告:未找到 uf2touft3/uf2touft3.exe");
LogManager::instance()->log(QString(" 期望位置:%1").arg(exePath));
} else {
m_batchLogEdit->append("✓ Python 工具准备就绪");
LogManager::instance()->log("✓ Python 工具准备就绪");
}
connect(m_pythonRunner, &PythonRunner::finished, this, &MainWindow::onPythonRunnerFinished);
connect(m_pythonRunner, &PythonRunner::standardOutput, this, &MainWindow::onPythonRunnerOutput);
connect(m_pythonRunner, &PythonRunner::standardError, this, &MainWindow::onPythonRunnerError);
connect(m_pythonRunner, &PythonRunner::started, [this]() {
m_batchLogEdit->append("转换任务已启动...");
connect(m_pythonRunner, &PythonRunner::started, []() {
LogManager::instance()->log("转换任务已启动...");
});
// 延迟查找并更新导航按钮(等待窗口完全显示后)
QTimer::singleShot(100, this, &MainWindow::updateNavigationButtonIcon);
}
void MainWindow::loadConfigCache()
{
LogManager::instance()->log("正在加载配置缓存...");
UF2ConfigReader uf2Reader;
bool uf2Loaded = uf2Reader.loadAllUF20Config();
UFT3ConfigReader uft3Reader;
bool uft3Loaded = uft3Reader.loadAllUFT3Config();
if (uf2Loaded && uft3Loaded) {
LogManager::instance()->log("✓ 配置缓存加载完成");
} else {
LogManager::instance()->logWarning("⚠️ 部分配置加载失败");
QStringList uf2Failed = uf2Reader.getFailedFiles();
QStringList uft3Failed = uft3Reader.getFailedFiles();
if (!uf2Failed.isEmpty()) {
LogManager::instance()->log(" UF20 加载失败:");
foreach (const QString& file, uf2Failed) {
LogManager::instance()->log(QString(" - %1").arg(file));
}
}
if (!uft3Failed.isEmpty()) {
LogManager::instance()->log(" UFT3 加载失败:");
foreach (const QString& file, uft3Failed) {
LogManager::instance()->log(QString(" - %1").arg(file));
}
}
}
}
MainWindow::~MainWindow()
{
}
@ -72,6 +109,7 @@ void MainWindow::initWindow()
setWindowTitle("UFT30 Change Code");
setWindowIcon(QIcon(":/resources/images/ChangeCode.png"));
resize(900, 600);
setMinimumSize(800, 500);
setUserInfoCardVisible(false);
// 隐藏返回和前进按钮
@ -94,15 +132,10 @@ void MainWindow::initContent()
QWidget* MainWindow::createBatchConvertPage()
{
BatchConvertPage *page = new BatchConvertPage;
m_batchLogEdit = page->getLogEdit();
connect(page, &BatchConvertPage::startConvert, this, &MainWindow::onBatchConvertStart);
connect(m_pythonRunner, &PythonRunner::started, [this]() {
if (m_batchLogEdit) {
m_batchLogEdit->append("转换任务已启动...");
}
});
return page;
m_batchConvertPage = new BatchConvertPage;
connect(m_batchConvertPage, &BatchConvertPage::startConvert, this, &MainWindow::onBatchConvertStart);
connect(m_batchConvertPage, &BatchConvertPage::stopConvert, this, &MainWindow::onBatchConvertStop);
return m_batchConvertPage;
}
QWidget* MainWindow::createFunctionSearchPage()
@ -128,7 +161,7 @@ QWidget* MainWindow::createSettingsPage()
void MainWindow::onBatchConvertStart()
{
if (m_pythonRunner->isRunning()) {
m_batchLogEdit->append("警告: 上一个任务还在运行中...");
LogManager::instance()->logWarning("警告: 上一个任务还在运行中...");
QMessageBox::warning(this, "提示", "上一个转换任务正在运行,请等待完成或停止当前任务。");
return;
}
@ -137,47 +170,124 @@ void MainWindow::onBatchConvertStart()
QString exePath = appDir + "/uf2touft3/uf2touft3.exe";
if (!m_pythonRunner->canRun()) {
m_batchLogEdit->append("错误: 找不到 uf2touft3/uf2touft3.exe!");
LogManager::instance()->logError("错误: 找不到 uf2touft3/uf2touft3.exe!");
QMessageBox::critical(this, "错误",
QString("无法启动转换任务!\n\n请确保:\nuf2touft3/uf2touft3.exe 存在,且该目录下包含 config.ini\n\n期望路径:\n%1")
.arg(exePath));
return;
}
m_batchLogEdit->append("========================================");
m_batchLogEdit->append("开始执行 UFT2 到 UFT3 的代码转换...");
m_batchLogEdit->append(QString("工具路径: %1").arg(exePath));
m_batchLogEdit->append("========================================");
if (m_batchConvertPage) {
m_totalFunctions = m_batchConvertPage->getFunctionCount();
m_functionList = m_batchConvertPage->getFunctionList();
m_currentFunctionIndex = 0;
} else {
m_totalFunctions = 0;
m_currentFunctionIndex = 0;
m_functionList.clear();
}
LogManager::instance()->log("========================================");
LogManager::instance()->log("开始执行 UFT2 到 UFT3 的代码转换...");
LogManager::instance()->log(QString("工具路径: %1").arg(exePath));
LogManager::instance()->log(QString("待转换函数数量: %1").arg(m_totalFunctions));
LogManager::instance()->log("========================================");
if (!m_pythonRunner->start()) {
m_batchLogEdit->append("错误: 无法启动转换进程!");
LogManager::instance()->logError("错误: 无法启动转换进程!");
} else {
if (m_batchConvertPage) {
m_batchConvertPage->onConvertStarted();
}
}
}
void MainWindow::onBatchConvertStop()
{
if (m_pythonRunner->isRunning()) {
LogManager::instance()->log("正在停止转换进程...");
m_pythonRunner->stop();
}
}
void MainWindow::onPythonRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
m_batchLogEdit->append("========================================");
if (m_batchConvertPage) {
m_batchConvertPage->onConvertFinished();
}
LogManager::instance()->log("========================================");
if (exitStatus == QProcess::NormalExit && exitCode == 0) {
m_batchLogEdit->append("✓ 代码转换完成!");
LogManager::instance()->log("✓ 代码转换完成!");
QMessageBox::information(this, "完成", "代码转换任务已成功完成!");
} else {
m_batchLogEdit->append(QString("✗ 转换失败! 退出码: %1").arg(exitCode));
LogManager::instance()->logError(QString("✗ 转换失败! 退出码: %1").arg(exitCode));
QMessageBox::warning(this, "失败", QString("代码转换任务失败,退出码: %1").arg(exitCode));
}
m_batchLogEdit->append("========================================");
LogManager::instance()->log("========================================");
}
void MainWindow::onPythonRunnerOutput(const QString &output)
{
if (!output.trimmed().isEmpty()) {
m_batchLogEdit->append(output);
LogManager::instance()->log(output);
QString trimmedOutput = output.trimmed();
QRegularExpression progressRegExp(R"(progress\s*[:=]\s*(\d+))");
QRegularExpressionMatch match = progressRegExp.match(trimmedOutput);
if (match.hasMatch()) {
int progress = match.captured(1).toInt();
QString status = trimmedOutput;
if (status.contains("progress", Qt::CaseInsensitive)) {
status = status.replace(progressRegExp, "").trimmed();
if (status.isEmpty()) {
status = QString("转换中 %1%").arg(progress);
}
}
if (m_batchConvertPage) {
m_batchConvertPage->onProgressUpdate(progress, status);
}
} else {
if (m_batchConvertPage) {
QString currentFunction;
QRegularExpression funcRegExp(R"(([\w_]+))");
QRegularExpressionMatch funcMatch = funcRegExp.match(trimmedOutput);
if (funcMatch.hasMatch()) {
currentFunction = funcMatch.captured(1);
}
if (!currentFunction.isEmpty() && m_totalFunctions > 0) {
int index = m_functionList.indexOf(currentFunction);
if (index >= 0 && index >= m_currentFunctionIndex) {
m_currentFunctionIndex = index + 1;
int progress = 20 + (m_currentFunctionIndex * 80) / m_totalFunctions;
QString status = QString("正在转换: %1").arg(currentFunction);
m_batchConvertPage->onProgressUpdate(progress, status);
return;
}
}
int currentProgress = 20;
if (trimmedOutput.contains("开始转换", Qt::CaseInsensitive)) {
currentProgress = 25;
} else if (trimmedOutput.contains("处理文件", Qt::CaseInsensitive)) {
currentProgress = 30;
} else if (trimmedOutput.contains("生成代码", Qt::CaseInsensitive)) {
currentProgress = 75;
} else if (trimmedOutput.contains("完成", Qt::CaseInsensitive)) {
currentProgress = 95;
}
m_batchConvertPage->onProgressUpdate(currentProgress, trimmedOutput);
}
}
}
}
void MainWindow::onPythonRunnerError(const QString &error)
{
if (!error.trimmed().isEmpty()) {
m_batchLogEdit->append(QString("[ERROR] %1").arg(error));
LogManager::instance()->logError(QString("[ERROR] %1").arg(error));
}
}

View File

@ -4,7 +4,6 @@
#include "ElaWindow.h"
#include "ElaToolButton.h"
#include <QWidget>
#include <QTextEdit>
#include "src/pythonrunner/PythonRunner.h"
#include "src/pages/batchconvert/batchconvertpage.h"
#include "src/pages/functionsearch/functionsearchpage.h"
@ -30,7 +29,10 @@ private:
QWidget* createAboutPage();
QWidget* createSettingsPage();
void loadConfigCache();
void onBatchConvertStart();
void onBatchConvertStop();
void onPythonRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onPythonRunnerOutput(const QString &output);
void onPythonRunnerError(const QString &error);
@ -40,10 +42,14 @@ private:
void onNavigationButtonClicked();
PythonRunner *m_pythonRunner;
QTextEdit *m_batchLogEdit;
bool m_iconsSet;
ElaToolButton* m_navigationButton;
bool m_isNavigationExpanded;
BatchConvertPage *m_batchConvertPage;
int m_totalFunctions;
int m_currentFunctionIndex;
QStringList m_functionList;
};
#endif // MAINWINDOW_H

View File

@ -1,4 +1,5 @@
#include "batchconvertpage.h"
#include "src/utils/uf2configreader.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
@ -12,6 +13,7 @@
#include <QInputDialog>
#include <QDialog>
#include <QTextEdit>
#include "src/utils/logmanager.h"
BatchConvertPage::BatchConvertPage(QWidget *parent)
: QWidget(parent)
@ -63,37 +65,62 @@ void BatchConvertPage::initUI()
layout->addWidget(funcBox);
QHBoxLayout *btnLayout = new QHBoxLayout;
QPushButton *startBtn = new QPushButton("开始转换");
startBtn->setStyleSheet("background-color: #1abc9c; color: white; padding: 10px 30px; font-size: 14px; border: none; border-radius: 4px;");
connect(startBtn, &QPushButton::clicked, this, &BatchConvertPage::onStartConvert);
QPushButton *clearLogBtn = new QPushButton("清空日志");
connect(clearLogBtn, &QPushButton::clicked, this, &BatchConvertPage::onClearLog);
btnLayout->addStretch();
btnLayout->addWidget(startBtn);
btnLayout->addWidget(clearLogBtn);
layout->addLayout(btnLayout);
QGroupBox *progressBox = new QGroupBox("转换进度");
progressBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
QVBoxLayout *progressLayout = new QVBoxLayout(progressBox);
progressLayout->setSpacing(10);
m_logEdit = new QTextEdit;
m_logEdit->setReadOnly(true);
m_logEdit->setStyleSheet("background-color: #2c3e50; color: #ecf0f1;");
m_logEdit->append("转码工具已准备就绪...");
layout->addWidget(m_logEdit);
m_progressLabel = new QLabel("等待开始...");
m_progressLabel->setStyleSheet("color: #666;");
m_progressLabel->setFixedHeight(20);
m_progressLabel->setWordWrap(false);
m_progressLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
progressLayout->addWidget(m_progressLabel);
m_progressBar = new QProgressBar;
m_progressBar->setRange(0, 100);
m_progressBar->setValue(0);
m_progressBar->setFixedHeight(25);
m_progressBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_progressBar->setStyleSheet(R"(
QProgressBar {
border: 1px solid #ddd;
border-radius: 4px;
background-color: #f0f0f0;
text-align: center;
}
QProgressBar::chunk {
background-color: #1abc9c;
border-radius: 4px;
}
)");
progressLayout->addWidget(m_progressBar);
layout->addWidget(progressBox);
QHBoxLayout *btnLayout = new QHBoxLayout;
m_startBtn = new QPushButton("开始转换");
m_startBtn->setStyleSheet("background-color: #1abc9c; color: white; padding: 10px 30px; font-size: 14px; border: none; border-radius: 4px;");
connect(m_startBtn, &QPushButton::clicked, this, &BatchConvertPage::onStartConvert);
btnLayout->addStretch();
btnLayout->addWidget(m_startBtn);
layout->addLayout(btnLayout);
}
void BatchConvertPage::onProgressUpdate(int progress, const QString& status)
{
m_progressBar->setValue(progress);
QString displayStatus = status;
if (displayStatus.length() > 80) {
displayStatus = displayStatus.left(80) + "...";
}
m_progressLabel->setText(displayStatus);
}
bool BatchConvertPage::checkFunctionExists(const QString &funcName)
{
QString jsonPath = QCoreApplication::applicationDirPath() + "/uf2touft3/cust.json";
QFile file(jsonPath);
if (!file.open(QIODevice::ReadOnly)) {
return false;
}
QByteArray data = file.readAll();
file.close();
QString content = QString::fromUtf8(data);
return content.contains("\"" + funcName + "\"");
return UF2ConfigReader::checkFuncExistsStatic(funcName);
}
void BatchConvertPage::updateTable()
@ -123,15 +150,15 @@ void BatchConvertPage::updateTable()
}
m_funcTable->setItem(i, 1, statusItem);
QPushButton *delBtn = new QPushButton;
delBtn->setIcon(QIcon(":/resources/images/delect.png"));
delBtn->setIconSize(QSize(20, 20));
delBtn->setStyleSheet("padding: 2px; border: none; background-color: transparent;");
delBtn->setToolTip("删除");
connect(delBtn, &QPushButton::clicked, this, [this, i]() {
onDeleteRow(i);
QPushButton *editBtn = new QPushButton;
editBtn->setIcon(QIcon(":/resources/images/bianji.png"));
editBtn->setIconSize(QSize(20, 20));
editBtn->setStyleSheet("padding: 2px; border: none; background-color: transparent;");
editBtn->setToolTip("修改");
connect(editBtn, &QPushButton::clicked, this, [this, i]() {
onEditRow(i);
});
m_funcTable->setCellWidget(i, 2, delBtn);
m_funcTable->setCellWidget(i, 2, editBtn);
}
int totalWidth = m_funcTable->width();
@ -151,12 +178,47 @@ void BatchConvertPage::resizeEvent(QResizeEvent *event)
}
}
void BatchConvertPage::onDeleteRow(int row)
void BatchConvertPage::onEditRow(int row)
{
if (row >= 0 && row < m_funcList.size()) {
m_funcList.removeAt(row);
updateTable();
if (row < 0 || row >= m_funcList.size()) {
return;
}
QString oldName = m_funcList[row];
QString newName = oldName;
while (true) {
QInputDialog dialog(this);
dialog.setWindowTitle("修改函数名");
dialog.setLabelText("请输入新的函数名:");
dialog.setTextValue(oldName);
dialog.resize(300, 150);
if (dialog.exec() != QDialog::Accepted) {
return;
}
newName = dialog.textValue().trimmed();
if (newName.isEmpty()) {
QMessageBox::warning(this, "提示", "函数名不能为空!");
continue;
}
if (newName == oldName) {
return;
}
if (m_funcList.contains(newName)) {
QMessageBox::warning(this, "提示", "该函数名已存在,请重新输入!");
continue;
}
break;
}
m_funcList[row] = newName;
updateTable();
}
void BatchConvertPage::onAddFunction()
@ -264,7 +326,7 @@ bool BatchConvertPage::saveToCustJson(const QStringList &funcList)
QFile file(jsonPath);
if (!file.open(QIODevice::ReadOnly)) {
m_logEdit->append("[ERROR] 无法打开文件: " + jsonPath);
LogManager::instance()->logError(QString("无法打开文件: %1").arg(jsonPath));
return false;
}
@ -278,7 +340,7 @@ bool BatchConvertPage::saveToCustJson(const QStringList &funcList)
dirStart = content.indexOf("\"dir\" : [");
}
if (dirStart < 0) {
m_logEdit->append("[ERROR] 未找到 \"dir\" 节点");
LogManager::instance()->logError("未找到 \"dir\" 节点");
return false;
}
@ -302,7 +364,7 @@ bool BatchConvertPage::saveToCustJson(const QStringList &funcList)
content.replace(arrayStart, arrayEnd - arrayStart, newDirArray);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
m_logEdit->append("[ERROR] 无法写入文件: " + jsonPath);
LogManager::instance()->logError(QString("无法写入文件: %1").arg(jsonPath));
return false;
}
@ -314,33 +376,54 @@ bool BatchConvertPage::saveToCustJson(const QStringList &funcList)
void BatchConvertPage::onStartConvert()
{
if (m_funcList.isEmpty()) {
m_logEdit->append("请先添加需要转码的函数!");
if (m_startBtn->text() == "停止转换") {
LogManager::instance()->log("用户请求停止转换...");
m_progressLabel->setText("正在停止转换...");
emit stopConvert();
return;
}
m_logEdit->append("========================================");
m_logEdit->append("正在写入 cust.json ...");
m_logEdit->append("功能列表:");
if (m_funcList.isEmpty()) {
QMessageBox::warning(this, "提示", "请先添加需要转码的函数!");
return;
}
m_progressBar->setValue(0);
m_progressLabel->setText("正在准备转换...");
LogManager::instance()->log("========================================");
LogManager::instance()->log("正在写入 cust.json ...");
LogManager::instance()->log("功能列表:");
for (const QString &func : m_funcList) {
m_logEdit->append(" - " + func + ".service_design");
LogManager::instance()->log(QString(" - %1.service_design").arg(func));
}
if (saveToCustJson(m_funcList)) {
m_logEdit->append("[OK] cust.json 写入成功!");
LogManager::instance()->log("cust.json 写入成功!");
m_progressLabel->setText("配置加载成功,开始转换...");
m_progressBar->setValue(20);
} else {
m_logEdit->append("[FAIL] cust.json 写入失败!");
m_logEdit->append("========================================");
LogManager::instance()->logError("cust.json 写入失败!");
LogManager::instance()->log("========================================");
m_progressLabel->setText("配置加载失败");
return;
}
m_logEdit->append("========================================");
LogManager::instance()->log("========================================");
emit startConvert();
}
void BatchConvertPage::onClearLog()
void BatchConvertPage::onConvertStarted()
{
m_logEdit->clear();
m_logEdit->append("转码工具已准备就绪...");
}
m_startBtn->setText("停止转换");
m_startBtn->setStyleSheet("background-color: #e74c3c; color: white; padding: 10px 30px; font-size: 14px; border: none; border-radius: 4px;");
}
void BatchConvertPage::onConvertFinished()
{
m_progressBar->setValue(100);
m_progressLabel->setText("转换完成");
m_startBtn->setText("开始转换");
m_startBtn->setStyleSheet("background-color: #1abc9c; color: white; padding: 10px 30px; font-size: 14px; border: none; border-radius: 4px;");
}

View File

@ -3,9 +3,10 @@
#include <QWidget>
#include <QLineEdit>
#include <QTextEdit>
#include <QTableWidget>
#include <QPushButton>
#include <QProgressBar>
#include <QLabel>
class BatchConvertPage : public QWidget
{
@ -15,18 +16,24 @@ public:
explicit BatchConvertPage(QWidget *parent = nullptr);
~BatchConvertPage();
QTextEdit* getLogEdit() { return m_logEdit; }
signals:
void startConvert();
void stopConvert();
public slots:
void onProgressUpdate(int progress, const QString& status);
void onConvertFinished();
void onConvertStarted();
int getFunctionCount() const { return m_funcList.size(); }
QStringList getFunctionList() const { return m_funcList; }
private slots:
void onStartConvert();
void onClearLog();
void onAddFunction();
void onRemoveFunction();
void onClearTable();
void onDeleteRow(int row);
void onEditRow(int row);
private:
void initUI();
@ -36,8 +43,10 @@ private:
void resizeEvent(QResizeEvent *event) override;
QTableWidget *m_funcTable;
QTextEdit *m_logEdit;
QStringList m_funcList;
QProgressBar *m_progressBar;
QLabel *m_progressLabel;
QPushButton *m_startBtn;
};
#endif // BATCHCONVERTPAGE_H
#endif

365
src/utils/datacache.cpp Normal file
View File

@ -0,0 +1,365 @@
#include "datacache.h"
#include "logmanager.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QJsonDocument>
#include <QCoreApplication>
#include <QDir>
#include <QDateTime>
#include <QElapsedTimer>
DataCache* DataCache::m_instance = nullptr;
DataCache::DataCache(QObject *parent) : QObject(parent), m_dataLoaded(false)
{
}
DataCache::~DataCache()
{
if (m_db.isOpen()) {
m_db.close();
}
}
DataCache* DataCache::instance()
{
if (!m_instance) {
m_instance = new DataCache();
}
return m_instance;
}
QString DataCache::getDatabasePath()
{
QString appDir = QCoreApplication::applicationDirPath();
QDir dbDir(appDir + "/cache");
if (!dbDir.exists()) {
if (!dbDir.mkpath(dbDir.absolutePath())) {
LogManager::instance()->logError(QString("数据缓存 - 创建缓存目录失败: %1").arg(dbDir.absolutePath()));
}
}
return dbDir.absoluteFilePath("config_cache.db");
}
bool DataCache::createTables()
{
QSqlQuery query(m_db);
query.exec("PRAGMA synchronous = OFF");
query.exec("PRAGMA journal_mode = MEMORY");
QString createUF20Table = R"(
CREATE TABLE IF NOT EXISTS uf20_cname_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
cname TEXT UNIQUE NOT NULL,
e_name TEXT,
function_no TEXT,
update_time TEXT NOT NULL
)
)";
QString createUFT3Table = R"(
CREATE TABLE IF NOT EXISTS uft3_cname_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
cname TEXT UNIQUE NOT NULL,
e_name TEXT,
function_no TEXT,
update_time TEXT NOT NULL
)
)";
if (!query.exec(createUF20Table)) {
LogManager::instance()->logError(QString("数据缓存 - 创建uf20_cname_table失败: %1").arg(query.lastError().text()));
return false;
}
if (!query.exec(createUFT3Table)) {
LogManager::instance()->logError(QString("数据缓存 - 创建uft3_cname_table失败: %1").arg(query.lastError().text()));
return false;
}
LogManager::instance()->log("数据缓存 - 表创建成功");
return true;
}
bool DataCache::initDatabase()
{
QString dbPath = getDatabasePath();
LogManager::instance()->log(QString("数据缓存 - 数据库路径: %1").arg(dbPath));
if (QSqlDatabase::contains("qt_sql_default_connection")) {
m_db = QSqlDatabase::database("qt_sql_default_connection");
if (m_db.isOpen()) {
LogManager::instance()->log("数据缓存 - 数据库已打开");
return true;
}
}
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName(dbPath);
if (!m_db.open()) {
LogManager::instance()->logError(QString("数据缓存 - 打开数据库失败: %1").arg(m_db.lastError().text()));
QString drivers = QSqlDatabase::drivers().join(", ");
LogManager::instance()->logError(QString("数据缓存 - 可用的数据库驱动: %1").arg(drivers));
return false;
}
m_db.exec("PRAGMA synchronous = OFF");
m_db.exec("PRAGMA journal_mode = MEMORY");
LogManager::instance()->log("数据缓存 - 数据库打开成功");
return createTables();
}
bool DataCache::isDataLoaded() const
{
return m_dataLoaded;
}
void DataCache::setDataLoaded(bool loaded)
{
m_dataLoaded = loaded;
}
bool DataCache::saveUF20Config(const QString& fileName, const QJsonObject& data)
{
if (!m_db.isOpen()) {
return false;
}
if (fileName != "uf2.json") {
return true;
}
QElapsedTimer timer;
timer.start();
QSqlQuery query(m_db);
query.exec("DELETE FROM uf20_cname_table");
QStringList cnames;
QStringList eNames;
QStringList functionNos;
for (auto it = data.begin(); it != data.end(); ++it) {
if (it.value().isObject()) {
QJsonObject obj = it.value().toObject();
QString cname = obj.value("cname").toString();
if (!cname.isEmpty()) {
cnames.append(cname);
eNames.append(obj.value("eName").toString());
functionNos.append(obj.value("function_no").toString());
}
}
}
if (cnames.isEmpty()) {
LogManager::instance()->logWarning("数据缓存 - UF20没有找到cname记录");
return true;
}
m_db.transaction();
query.prepare("INSERT INTO uf20_cname_table (cname, e_name, function_no, update_time) VALUES (?, ?, ?, ?)");
QString updateTime = QDateTime::currentDateTime().toString(Qt::ISODate);
for (int i = 0; i < cnames.size(); ++i) {
query.addBindValue(cnames[i]);
query.addBindValue(eNames[i]);
query.addBindValue(functionNos[i]);
query.addBindValue(updateTime);
query.exec();
}
m_db.commit();
LogManager::instance()->logInfo(QString("数据缓存 - UF20成功保存 %1 个cname记录耗时: %2ms").arg(cnames.size()).arg(timer.elapsed()));
return true;
}
bool DataCache::saveUFT3Config(const QString& fileName, const QJsonObject& data)
{
if (!m_db.isOpen()) {
return false;
}
if (fileName != "uft3.json") {
return true;
}
QElapsedTimer timer;
timer.start();
QSqlQuery query(m_db);
query.exec("DELETE FROM uft3_cname_table");
QStringList cnames;
QStringList eNames;
QStringList functionNos;
for (auto it = data.begin(); it != data.end(); ++it) {
if (it.value().isObject()) {
QJsonObject obj = it.value().toObject();
QString cname = obj.value("cname").toString();
if (!cname.isEmpty()) {
cnames.append(cname);
eNames.append(obj.value("eName").toString());
functionNos.append(obj.value("function_no").toString());
}
}
}
if (cnames.isEmpty()) {
LogManager::instance()->logWarning("数据缓存 - UFT3没有找到cname记录");
return true;
}
m_db.transaction();
query.prepare("INSERT INTO uft3_cname_table (cname, e_name, function_no, update_time) VALUES (?, ?, ?, ?)");
QString updateTime = QDateTime::currentDateTime().toString(Qt::ISODate);
for (int i = 0; i < cnames.size(); ++i) {
query.addBindValue(cnames[i]);
query.addBindValue(eNames[i]);
query.addBindValue(functionNos[i]);
query.addBindValue(updateTime);
query.exec();
}
m_db.commit();
LogManager::instance()->logInfo(QString("数据缓存 - UFT3成功保存 %1 个cname记录耗时: %2ms").arg(cnames.size()).arg(timer.elapsed()));
return true;
}
bool DataCache::hasUF20Config(const QString& fileName)
{
if (!m_db.isOpen()) {
return false;
}
if (fileName != "uf2.json") {
return true;
}
QSqlQuery query(m_db);
if (query.exec("SELECT COUNT(*) FROM uf20_cname_table")) {
if (query.next()) {
return query.value(0).toInt() > 0;
}
}
return false;
}
bool DataCache::hasUFT3Config(const QString& fileName)
{
if (!m_db.isOpen()) {
return false;
}
if (fileName != "uft3.json") {
return true;
}
QSqlQuery query(m_db);
if (query.exec("SELECT COUNT(*) FROM uft3_cname_table")) {
if (query.next()) {
return query.value(0).toInt() > 0;
}
}
return false;
}
bool DataCache::checkUF20CnameExists(const QString& cname)
{
if (!m_db.isOpen()) {
return false;
}
QSqlQuery query(m_db);
query.prepare("SELECT COUNT(*) FROM uf20_cname_table WHERE cname = :cname");
query.bindValue(":cname", cname);
if (query.exec() && query.next()) {
return query.value(0).toInt() > 0;
}
return false;
}
QSet<QString> DataCache::getAllUF20Cnames()
{
QSet<QString> result;
if (!m_db.isOpen()) {
return result;
}
QElapsedTimer timer;
timer.start();
QSqlQuery query(m_db);
if (query.exec("SELECT cname FROM uf20_cname_table")) {
while (query.next()) {
result.insert(query.value(0).toString());
}
}
LogManager::instance()->logInfo(QString("数据缓存 - 获取UF20 Cname缓存耗时: %1ms").arg(timer.elapsed()));
return result;
}
bool DataCache::checkUFT3CnameExists(const QString& cname)
{
if (!m_db.isOpen()) {
return false;
}
QSqlQuery query(m_db);
query.prepare("SELECT COUNT(*) FROM uft3_cname_table WHERE cname = :cname");
query.bindValue(":cname", cname);
if (query.exec() && query.next()) {
return query.value(0).toInt() > 0;
}
return false;
}
QSet<QString> DataCache::getAllUFT3Cnames()
{
QSet<QString> result;
if (!m_db.isOpen()) {
return result;
}
QElapsedTimer timer;
timer.start();
QSqlQuery query(m_db);
if (query.exec("SELECT cname FROM uft3_cname_table")) {
while (query.next()) {
result.insert(query.value(0).toString());
}
}
LogManager::instance()->logInfo(QString("数据缓存 - 获取UFT3 Cname缓存耗时: %1ms").arg(timer.elapsed()));
return result;
}
void DataCache::clearCache()
{
if (!m_db.isOpen()) {
return;
}
QSqlQuery query(m_db);
query.exec("DELETE FROM uf20_cname_table");
query.exec("DELETE FROM uft3_cname_table");
m_dataLoaded = false;
}

46
src/utils/datacache.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef DATACACHE_H
#define DATACACHE_H
#include <QObject>
#include <QSqlDatabase>
#include <QJsonObject>
#include <QSet>
class DataCache : public QObject
{
Q_OBJECT
public:
static DataCache* instance();
bool initDatabase();
bool isDataLoaded() const;
void setDataLoaded(bool loaded);
bool saveUF20Config(const QString& fileName, const QJsonObject& data);
bool saveUFT3Config(const QString& fileName, const QJsonObject& data);
bool hasUF20Config(const QString& fileName);
bool hasUFT3Config(const QString& fileName);
bool checkUF20CnameExists(const QString& cname);
QSet<QString> getAllUF20Cnames();
bool checkUFT3CnameExists(const QString& cname);
QSet<QString> getAllUFT3Cnames();
void clearCache();
private:
explicit DataCache(QObject *parent = nullptr);
~DataCache();
bool createTables();
QString getDatabasePath();
static DataCache* m_instance;
QSqlDatabase m_db;
bool m_dataLoaded;
};
#endif

93
src/utils/logmanager.cpp Normal file
View File

@ -0,0 +1,93 @@
#include "logmanager.h"
#include <QCoreApplication>
#include <QDir>
#include <QDateTime>
#include <QDebug>
LogManager* LogManager::m_instance = nullptr;
LogManager::LogManager(QObject *parent) : QObject(parent)
{
}
LogManager::~LogManager()
{
if (m_logFile.isOpen()) {
m_logFile.close();
}
}
LogManager* LogManager::instance()
{
if (!m_instance) {
m_instance = new LogManager();
}
return m_instance;
}
void LogManager::initLogFile()
{
QString appDir = QCoreApplication::applicationDirPath();
QDir logDir(appDir + "/logs");
if (!logDir.exists()) {
logDir.mkpath(".");
}
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd");
m_logFilePath = logDir.absoluteFilePath(QString("uft3_change_code_%1.log").arg(timestamp));
m_logFile.setFileName(m_logFilePath);
if (!m_logFile.open(QIODevice::Append | QIODevice::Text)) {
qDebug() << "无法打开日志文件:" << m_logFilePath;
return;
}
writeLog("INFO", "日志系统初始化完成");
}
QString LogManager::getLogFilePath() const
{
return m_logFilePath;
}
void LogManager::writeLog(const QString& level, const QString& message)
{
QMutexLocker locker(&m_mutex);
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
QString logLine = QString("[%1] [%2] %3\n").arg(timestamp, level, message);
if (m_logFile.isOpen()) {
QTextStream stream(&m_logFile);
stream << logLine;
stream.flush();
}
if (level == "ERROR") {
qCritical() << message;
} else if (level == "WARNING") {
qWarning() << message;
} else {
qDebug() << message;
}
}
void LogManager::log(const QString& message)
{
writeLog("INFO", message);
}
void LogManager::logInfo(const QString& message)
{
writeLog("INFO", message);
}
void LogManager::logWarning(const QString& message)
{
writeLog("WARNING", message);
}
void LogManager::logError(const QString& message)
{
writeLog("ERROR", message);
}

37
src/utils/logmanager.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef LOGMANAGER_H
#define LOGMANAGER_H
#include <QObject>
#include <QString>
#include <QFile>
#include <QTextStream>
#include <QMutex>
class LogManager : public QObject
{
Q_OBJECT
public:
static LogManager* instance();
void initLogFile();
void log(const QString& message);
void logInfo(const QString& message);
void logWarning(const QString& message);
void logError(const QString& message);
QString getLogFilePath() const;
private:
explicit LogManager(QObject *parent = nullptr);
~LogManager();
void writeLog(const QString& level, const QString& message);
static LogManager* m_instance;
QFile m_logFile;
QMutex m_mutex;
QString m_logFilePath;
};
#endif // LOGMANAGER_H

View File

@ -0,0 +1,154 @@
#include "uf2configreader.h"
#include "datacache.h"
#include "logmanager.h"
#include <QFile>
#include <QJsonDocument>
#include <QCoreApplication>
#include <QDir>
QSet<QString> UF2ConfigReader::m_cnameCache;
bool UF2ConfigReader::m_cnameCacheLoaded = false;
UF2ConfigReader::UF2ConfigReader(QObject *parent) : QObject(parent), m_loaded(false)
{
}
QString UF2ConfigReader::getBinPath()
{
QString appDir = QCoreApplication::applicationDirPath();
return appDir + "/uf2touft3";
}
bool UF2ConfigReader::loadAllUF20Config()
{
m_failedFiles.clear();
if (!DataCache::instance()->initDatabase()) {
LogManager::instance()->logError("UF20配置加载 - 数据库初始化失败");
m_failedFiles.append("uf2.json");
return false;
}
QString binPath = getBinPath();
QString filePath = binPath + "/uf2.json";
if (DataCache::instance()->hasUF20Config("uf2.json")) {
LogManager::instance()->log("UF20配置加载 - 已缓存,跳过: uf2.json");
} else {
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
LogManager::instance()->logError(QString("UF20配置加载 - 无法打开文件: %1").arg(filePath));
m_failedFiles.append("uf2.json");
return false;
}
QByteArray data = file.readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
file.close();
if (doc.isNull() || !doc.isObject()) {
LogManager::instance()->logError(QString("UF20配置加载 - JSON解析失败: %1").arg(filePath));
m_failedFiles.append("uf2.json");
return false;
}
QJsonObject config = doc.object();
if (!DataCache::instance()->saveUF20Config("uf2.json", config)) {
LogManager::instance()->logError("UF20配置加载 - 保存失败: uf2.json");
m_failedFiles.append("uf2.json");
return false;
}
LogManager::instance()->log("UF20配置加载 - 成功: uf2.json");
}
m_loaded = true;
DataCache::instance()->setDataLoaded(true);
LogManager::instance()->logInfo("UF20配置加载 - 全部加载完成");
return true;
}
QStringList UF2ConfigReader::getFailedFiles() const
{
return m_failedFiles;
}
bool UF2ConfigReader::reloadUF20Config()
{
QString binPath = getBinPath();
QString filePath = binPath + "/uf2.json";
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
LogManager::instance()->logError(QString("UF20配置加载 - 无法打开文件: %1").arg(filePath));
return false;
}
QByteArray data = file.readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
file.close();
if (doc.isNull() || !doc.isObject()) {
LogManager::instance()->logError(QString("UF20配置加载 - JSON解析失败: %1").arg(filePath));
return false;
}
QJsonObject config = doc.object();
if (!DataCache::instance()->saveUF20Config("uf2.json", config)) {
LogManager::instance()->logError("UF20配置加载 - 保存失败: uf2.json");
return false;
}
clearCnameCache();
LogManager::instance()->logInfo("UF20配置重新加载完成");
return true;
}
bool UF2ConfigReader::isUF20Loaded() const
{
return m_loaded;
}
bool UF2ConfigReader::loadCnameCache()
{
if (m_cnameCacheLoaded) {
return true;
}
m_cnameCache.clear();
if (!DataCache::instance()->initDatabase()) {
LogManager::instance()->logError("Cname缓存加载 - 数据库初始化失败");
return false;
}
m_cnameCache = DataCache::instance()->getAllUF20Cnames();
m_cnameCacheLoaded = true;
LogManager::instance()->logInfo(QString("Cname缓存加载完成共加载 %1 个函数名").arg(m_cnameCache.size()));
return true;
}
bool UF2ConfigReader::checkFuncExistsStatic(const QString& funcName)
{
if (!m_cnameCacheLoaded) {
loadCnameCache();
}
bool exists = m_cnameCache.contains(funcName);
LogManager::instance()->log(QString("检查函数名 [%1] 是否存在: %2").arg(funcName).arg(exists ? "" : ""));
return exists;
}
bool UF2ConfigReader::checkFunctionExists(const QString& funcName)
{
return checkFuncExistsStatic(funcName);
}
void UF2ConfigReader::clearCnameCache()
{
m_cnameCache.clear();
m_cnameCacheLoaded = false;
LogManager::instance()->logInfo("Cname缓存已清空");
}

View File

@ -0,0 +1,37 @@
#ifndef UF2CONFIGREADER_H
#define UF2CONFIGREADER_H
#include <QObject>
#include <QStringList>
#include <QJsonObject>
#include <QSet>
class UF2ConfigReader : public QObject
{
Q_OBJECT
public:
explicit UF2ConfigReader(QObject *parent = nullptr);
bool loadAllUF20Config();
bool reloadUF20Config();
bool isUF20Loaded() const;
QStringList getFailedFiles() const;
bool checkFunctionExists(const QString& funcName);
static bool checkFuncExistsStatic(const QString& funcName);
static void clearCnameCache();
private:
static bool loadCnameCache();
QString getBinPath();
QStringList m_failedFiles;
bool m_loaded;
static QSet<QString> m_cnameCache;
static bool m_cnameCacheLoaded;
};
#endif

View File

@ -0,0 +1,154 @@
#include "uft3configreader.h"
#include "datacache.h"
#include "logmanager.h"
#include <QFile>
#include <QJsonDocument>
#include <QCoreApplication>
#include <QDir>
QSet<QString> UFT3ConfigReader::m_cnameCache;
bool UFT3ConfigReader::m_cnameCacheLoaded = false;
UFT3ConfigReader::UFT3ConfigReader(QObject *parent) : QObject(parent), m_loaded(false)
{
}
QString UFT3ConfigReader::getBinPath()
{
QString appDir = QCoreApplication::applicationDirPath();
return appDir + "/uf2touft3";
}
bool UFT3ConfigReader::loadAllUFT3Config()
{
m_failedFiles.clear();
if (!DataCache::instance()->initDatabase()) {
LogManager::instance()->logError("UFT3配置加载 - 数据库初始化失败");
m_failedFiles.append("uft3.json");
return false;
}
QString binPath = getBinPath();
QString filePath = binPath + "/uft3.json";
if (DataCache::instance()->hasUFT3Config("uft3.json")) {
LogManager::instance()->log("UFT3配置加载 - 已缓存,跳过: uft3.json");
} else {
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
LogManager::instance()->logError(QString("UFT3配置加载 - 无法打开文件: %1").arg(filePath));
m_failedFiles.append("uft3.json");
return false;
}
QByteArray data = file.readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
file.close();
if (doc.isNull() || !doc.isObject()) {
LogManager::instance()->logError(QString("UFT3配置加载 - JSON解析失败: %1").arg(filePath));
m_failedFiles.append("uft3.json");
return false;
}
QJsonObject config = doc.object();
if (!DataCache::instance()->saveUFT3Config("uft3.json", config)) {
LogManager::instance()->logError("UFT3配置加载 - 保存失败: uft3.json");
m_failedFiles.append("uft3.json");
return false;
}
LogManager::instance()->log("UFT3配置加载 - 成功: uft3.json");
}
m_loaded = true;
DataCache::instance()->setDataLoaded(true);
LogManager::instance()->logInfo("UFT3配置加载 - 全部加载完成");
return true;
}
QStringList UFT3ConfigReader::getFailedFiles() const
{
return m_failedFiles;
}
bool UFT3ConfigReader::reloadUFT3Config()
{
QString binPath = getBinPath();
QString filePath = binPath + "/uft3.json";
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
LogManager::instance()->logError(QString("UFT3配置加载 - 无法打开文件: %1").arg(filePath));
return false;
}
QByteArray data = file.readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
file.close();
if (doc.isNull() || !doc.isObject()) {
LogManager::instance()->logError(QString("UFT3配置加载 - JSON解析失败: %1").arg(filePath));
return false;
}
QJsonObject config = doc.object();
if (!DataCache::instance()->saveUFT3Config("uft3.json", config)) {
LogManager::instance()->logError("UFT3配置加载 - 保存失败: uft3.json");
return false;
}
clearCnameCache();
LogManager::instance()->logInfo("UFT3配置重新加载完成");
return true;
}
bool UFT3ConfigReader::isUFT3Loaded() const
{
return m_loaded;
}
bool UFT3ConfigReader::loadCnameCache()
{
if (m_cnameCacheLoaded) {
return true;
}
m_cnameCache.clear();
if (!DataCache::instance()->initDatabase()) {
LogManager::instance()->logError("UFT3 Cname缓存加载 - 数据库初始化失败");
return false;
}
m_cnameCache = DataCache::instance()->getAllUFT3Cnames();
m_cnameCacheLoaded = true;
LogManager::instance()->logInfo(QString("UFT3 Cname缓存加载完成共加载 %1 个函数名").arg(m_cnameCache.size()));
return true;
}
bool UFT3ConfigReader::checkFuncExistsStatic(const QString& funcName)
{
if (!m_cnameCacheLoaded) {
loadCnameCache();
}
bool exists = m_cnameCache.contains(funcName);
LogManager::instance()->log(QString("检查UFT3函数名 [%1] 是否存在: %2").arg(funcName).arg(exists ? "" : ""));
return exists;
}
bool UFT3ConfigReader::checkFunctionExists(const QString& funcName)
{
return checkFuncExistsStatic(funcName);
}
void UFT3ConfigReader::clearCnameCache()
{
m_cnameCache.clear();
m_cnameCacheLoaded = false;
LogManager::instance()->logInfo("UFT3 Cname缓存已清空");
}

View File

@ -0,0 +1,37 @@
#ifndef UFT3CONFIGREADER_H
#define UFT3CONFIGREADER_H
#include <QObject>
#include <QStringList>
#include <QJsonObject>
#include <QSet>
class UFT3ConfigReader : public QObject
{
Q_OBJECT
public:
explicit UFT3ConfigReader(QObject *parent = nullptr);
bool loadAllUFT3Config();
bool reloadUFT3Config();
bool isUFT3Loaded() const;
QStringList getFailedFiles() const;
bool checkFunctionExists(const QString& funcName);
static bool checkFuncExistsStatic(const QString& funcName);
static void clearCnameCache();
private:
static bool loadCnameCache();
QString getBinPath();
QStringList m_failedFiles;
bool m_loaded;
static QSet<QString> m_cnameCache;
static bool m_cnameCacheLoaded;
};
#endif