diff --git a/src/DataProcessWorkPool.cpp b/src/DataProcessWorkPool.cpp index e684ae4..08ce5b7 100644 --- a/src/DataProcessWorkPool.cpp +++ b/src/DataProcessWorkPool.cpp @@ -23,6 +23,7 @@ #include "EnergyScaleDataModel.h" #include "DataCalcProcess/CoincidenceSpectrumProcess.h" #include "BackgroundTaskListModel.h" +#include "GvfToCsv/GvfToCsv.h" #include using namespace DataProcessWorkPool; @@ -1073,3 +1074,67 @@ bool EnergyScaleaAntiCoincidenceDataTask::processTask() return true; } + +QString GvfToCsvDataTask::GetTaskName() +{ + return QStringLiteral(u"[%1]GVF文件转CSV处理").arg(this->GetProjectName()); +} + +void GvfToCsvDataTask::setGvfName(const QString& gvfName) +{ + m_gvfName = gvfName; +} + +void GvfToCsvDataTask::setCsvName(const QString& csvName) +{ + QDir projectDir(csvName); + if (!projectDir.exists()) { + projectDir.mkpath("."); + } + m_csvName = projectDir.filePath("粒子数据_gvf.csv"); +} + +bool GvfToCsvDataTask::processTask() +{ + QFileInfo csvFileInfo(m_csvName); + QDir csvDir = csvFileInfo.absoluteDir(); + + if (!csvDir.exists()) { + if (!csvDir.mkpath(".")) { + m_error = QStringLiteral(u"无法创建输出目录: %1").arg(csvDir.path()); + LOG_ERROR(m_error.toUtf8().constData()); + return false; + } + } + + QString testFile = csvDir.filePath(".test_write.tmp"); + QFile test(testFile); + if (!test.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + m_error = QStringLiteral(u"输出目录不可写: %1").arg(csvDir.path()); + LOG_ERROR(m_error.toUtf8().constData()); + return false; + } + test.remove(); + + if (m_gvfName.isEmpty() || m_csvName.isEmpty()) { + m_error = "GVF文件路径或CSV输出路径为空"; + LOG_ERROR(m_error.toUtf8().constData()); + return false; + } + std::unique_ptr gvf2Csv(new GvfToCsv()); + bool success = gvf2Csv->convertGVF2CSVSync(m_gvfName, m_csvName); + + if (success) { + m_resultCsvPath = m_csvName; + m_conversionSuccess = true; + updateTaskResultData(QVariant(m_resultCsvPath)); + } else { + m_error = gvf2Csv->getLastError(); + m_conversionSuccess = false; + LOG_ERROR(QStringLiteral(u"GVF转换失败: %1") + .arg(m_error) + .toUtf8().constData()); + } + + return m_conversionSuccess; +} \ No newline at end of file diff --git a/src/DataProcessWorkPool.h b/src/DataProcessWorkPool.h index 47c51b6..6849716 100644 --- a/src/DataProcessWorkPool.h +++ b/src/DataProcessWorkPool.h @@ -7,7 +7,7 @@ #include "AnalysisTypeDefine.h" #include #include - +#include class EnergyScaleDataModel; class MeasureAnalysisProjectModel; @@ -167,6 +167,23 @@ namespace DataProcessWorkPool private: virtual bool processTask() override; }; + + class GvfToCsvDataTask : public DataProcessTask + { + public: + QString GetTaskName() override; + void setGvfName(const QString& gvfName); + void setCsvName(const QString& csvName); + QString getError() const { return m_error; } + QString getResultCsvPath() const { return m_resultCsvPath; } + private: + bool processTask() override; + QString m_gvfName; + QString m_csvName; + QString m_resultCsvPath; + QString m_error; + bool m_conversionSuccess = false; + }; } #endif // DATAPROCESSWORKPOOL_H diff --git a/src/GvfToCsv/GvfToCsv.cpp b/src/GvfToCsv/GvfToCsv.cpp new file mode 100644 index 0000000..08b1f25 --- /dev/null +++ b/src/GvfToCsv/GvfToCsv.cpp @@ -0,0 +1,296 @@ +#include "GvfToCsv.h" +#include +#include +#include +#include +#include +#include "GlobalDefine.h" +GvfToCsv::GvfToCsv(QObject *parent) + : QObject(parent) +{ + connect(m_sqliteWorker.get(), &SQLiteReadWrite::operationCompleted, + this, &GvfToCsv::onSqliteOperationCompleted); + connect(m_sqliteWorker.get(), &SQLiteReadWrite::progressUpdated, + this, &GvfToCsv::conversionProgress); + connect(m_sqliteWorker.get(), &SQLiteReadWrite::logMessage, + this, [](const QString &msg) { qDebug() << "[GVF转换日志]" << msg; }); +} + +GvfToCsv::~GvfToCsv() +{ + cleanUp(); + if (m_syncEventLoop) { + delete m_syncEventLoop; + } +} + +QList GvfToCsv::parseParticleFrames(const QByteArray &data) +{ + QList particles; + const int minDataSize = GvfConst::HEADER_SIZE + GvfConst::PARTICLE_BLOCK_SIZE; + if (data.size() < minDataSize) { + qDebug() << "粒子数据长度不足,跳过解析"; + return particles; + } + + const quint8* rawData = reinterpret_cast(data.constData()); + if (rawData[0] != GvfConst::HEADER_BYTE1 || rawData[1] != GvfConst::HEADER_BYTE2) { + qDebug() << "粒子数据头部校验失败,跳过解析"; + return particles; + } + + const quint8* ptr = rawData + GvfConst::HEADER_SIZE; + int remaining = data.size() - GvfConst::HEADER_SIZE; + particles.reserve(remaining / GvfConst::PARTICLE_BLOCK_SIZE); + + while (remaining >= GvfConst::PARTICLE_BLOCK_SIZE) + { + quint16 alignWord = qFromLittleEndian(static_cast(ptr)); + quint8 lowByte = alignWord & 0xFF; + quint8 highByte = (alignWord >> 8) & 0xFF; + + if (highByte != GvfConst::ALIGN_HIGH_BYTE) { + ptr += GvfConst::PARTICLE_BLOCK_SIZE; + remaining -= GvfConst::PARTICLE_BLOCK_SIZE; + continue; + } + + const quint8 board = (lowByte >> 4) & 0x0F; + const quint8 channel = lowByte & 0x0F; + const int channelIndex = board * 4 + channel; + + if (channelIndex >= GvfConst::MAX_CHANNEL_COUNT) { + ptr += GvfConst::PARTICLE_BLOCK_SIZE; + remaining -= GvfConst::PARTICLE_BLOCK_SIZE; + continue; + } + + quint64 timestamp = 0; + const quint8* timestampPtr = ptr + 2; + for (int i = 0; i < 6; ++i) { + timestamp |= static_cast(timestampPtr[i]) << (8 * i); + } + + const quint16 amplitude = qFromLittleEndian(static_cast(ptr + 8)); + const quint32 icr = qFromLittleEndian(static_cast(ptr + 10)); + const quint16 riseTime = qFromLittleEndian(static_cast(ptr + 14)); + const quint16 fallTime = qFromLittleEndian(static_cast(ptr + 16)); + + const int address = static_cast( + static_cast(amplitude) / GvfConst::AMPLITUDE_MAX * GvfConst::MAX_ADDRESS + ); + + ParticleData p( + board, + channel, + timestamp, + timestamp * GvfConst::TIME_PER_COUNT, + amplitude, + address, + icr, + riseTime, + fallTime + ); + + particles.append(p); + ptr += GvfConst::PARTICLE_BLOCK_SIZE; + remaining -= GvfConst::PARTICLE_BLOCK_SIZE; + } + + return particles; +} + +void GvfToCsv::cleanUp() +{ + if (m_sqliteWorker) { + // 先断开所有信号连接 + disconnect(m_sqliteWorker.get(), nullptr, this, nullptr); + // 停止操作 + m_sqliteWorker->stopOperation(); + + // 等待操作完成 + QEventLoop loop; + QTimer timeoutTimer; + timeoutTimer.setSingleShot(true); + + connect(m_sqliteWorker.get(), &SQLiteReadWrite::operationCompleted, &loop, &QEventLoop::quit); + connect(&timeoutTimer, &QTimer::timeout, &loop, &QEventLoop::quit); + + timeoutTimer.start(3000); + loop.exec(QEventLoop::ExcludeUserInputEvents); + m_sqliteWorker.reset(); + } + m_gvfPath.clear(); + m_csvPath.clear(); + m_lastError.clear(); +} + +bool GvfToCsv::convertGVF2CSVSync(const QString &gvfPath, const QString &csvPath) +{ + if (!m_syncEventLoop) { + m_syncEventLoop = new QEventLoop(); + } + + bool result = false; + QObject signalReceiver; + + connect(this, &GvfToCsv::conversionFinished, &signalReceiver, [&](bool success) { + result = success; + m_syncEventLoop->quit(); + }); + + connect(this, &GvfToCsv::errorOccurred, &signalReceiver, [](const QString &msg) { + LOG_ERROR(QStringLiteral(u"GVF转换错误: %1").arg(msg).toUtf8().constData()); + }); + + convertGVF2CSVAsync(gvfPath, csvPath); + + m_syncEventLoop->exec(); + + return result; +} + +void GvfToCsv::convertGVF2CSVAsync(const QString &gvfPath, const QString &csvPath) +{ + m_lastError.clear(); + cleanUp(); + + if (gvfPath.isEmpty() || csvPath.isEmpty()) { + setLastError("GVF/CSV路径不能为空"); + emit conversionFinished(false); + return; + } + + QFileInfo gvfFileInfo(gvfPath); + if (!gvfFileInfo.exists() || !gvfFileInfo.isFile()) { + setLastError(QString("GVF文件不存在: %1").arg(gvfPath)); + emit conversionFinished(false); + return; + } + + QFileInfo csvFileInfo(csvPath); + QDir csvDir = csvFileInfo.absoluteDir(); + if (!csvDir.exists() && !csvDir.mkpath(".")) { + setLastError(QString("无法创建CSV目录: %1").arg(csvDir.path())); + emit conversionFinished(false); + return; + } + + m_gvfPath = gvfPath; + m_csvPath = csvPath; + + m_sqliteWorker = std::make_unique(this); + + connect(m_sqliteWorker.get(), &SQLiteReadWrite::operationCompleted, + this, &GvfToCsv::onSqliteOperationCompleted, Qt::QueuedConnection); + connect(m_sqliteWorker.get(), &SQLiteReadWrite::progressUpdated, + this, &GvfToCsv::conversionProgress, Qt::QueuedConnection); + if (!m_sqliteWorker->openDatabase(m_gvfPath)) { + setLastError(QString("打开GVF数据库失败: %1").arg(m_gvfPath)); + emit conversionFinished(false); + return; + } + + m_sqliteWorker->startReadWriteOperation(); +} + +void GvfToCsv::onSqliteOperationCompleted(bool success, const QString &msg) +{ + if (!success) { + setLastError(QString("GVF数据读取失败: %1").arg(msg)); + cleanUp(); + emit conversionFinished(false); + return; + } + + try { + processDBData(); + emit conversionFinished(true); + } catch (const std::exception &e) { + setLastError(QString("CSV写入异常: %1").arg(e.what())); + emit conversionFinished(false); + } catch (...) { + setLastError("CSV写入未知异常"); + emit conversionFinished(false); + } + + cleanUp(); +} + +void GvfToCsv::processDBData() +{ + QVector dbList = m_sqliteWorker->DataBaseList(); + if (dbList.isEmpty()) { + throw std::runtime_error("GVF数据库中无任何记录"); + } + QFile outFile(m_csvPath); + if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + throw std::runtime_error(QString("无法创建CSV文件: %1,错误: %2") + .arg(m_csvPath) + .arg(outFile.errorString()) + .toStdString()); + } + + QTextStream out(&outFile); + out.setCodec("UTF-8"); + out << QStringLiteral(u"板卡号,通道号,道址,时间计数\n"); + + QString csvBuffer; + csvBuffer.reserve(4 * 1024 * 1024); // 4MB缓冲区 + + quint64 totalParticles = 0; + int emptyFrameCount = 0; + int processedFrames = 0; + const int totalFrames = dbList.size(); + + for (const auto &data : dbList) { + QList particles = parseParticleFrames(data.data); + if (particles.isEmpty()) { + emptyFrameCount++; + } else { + totalParticles += particles.size(); + + for (const auto &p : particles) { + csvBuffer += QString("%1,%2,%3,%4\n") + .arg(p.boardId) + .arg(p.channelId) + .arg(p.address) + .arg(p.timestampCount); + } + } + + if (csvBuffer.size() >= 4 * 1024 * 1024) { + out << csvBuffer; + csvBuffer.clear(); + } + + processedFrames++; + if (processedFrames % 1000 == 0) { + int remainingFrames = totalFrames - processedFrames; + int progress = (processedFrames * 100) / totalFrames; + emit conversionProgress(progress); + } + } + + if (!csvBuffer.isEmpty()) { + out << csvBuffer; + } + + out.flush(); + outFile.close(); + + if (outFile.error() != QFile::NoError) { + throw std::runtime_error(QString("CSV文件写入失败: %1,错误: %2") + .arg(m_csvPath) + .arg(outFile.errorString()) + .toStdString()); + } + + if (totalParticles == 0) { + QFile::remove(m_csvPath); + throw std::runtime_error(QString("GVF文件中未解析到任何有效粒子数据,空帧数: %1") + .arg(emptyFrameCount) + .toStdString()); + } + emit conversionProgress(100); +} \ No newline at end of file diff --git a/src/GvfToCsv/GvfToCsv.h b/src/GvfToCsv/GvfToCsv.h new file mode 100644 index 0000000..b0e5145 --- /dev/null +++ b/src/GvfToCsv/GvfToCsv.h @@ -0,0 +1,88 @@ +#ifndef GVFTOCSV_H +#define GVFTOCSV_H + +#include "sqliteread.h" +#include +#include +#include +#include +#include +#include +#include +// 常量定义:替换魔法数,提升可读性和可维护性 +namespace GvfConst { +constexpr quint8 HEADER_BYTE1 = 0xA7; // 头部固定字节1 +constexpr quint8 HEADER_BYTE2 = 0xA2; // 头部固定字节2 +constexpr int HEADER_SIZE = 3; // 头部总字节数 +constexpr int PARTICLE_BLOCK_SIZE = 20; // 单个粒子数据块大小 +constexpr quint8 ALIGN_HIGH_BYTE = 0xFE; // 对齐标记高字节 +constexpr int MAX_CHANNEL_COUNT = 32; // 硬件最大通道数 +constexpr double CLOCK_FREQ_MHZ = 200.0; // 时钟频率(200MHz) +constexpr double TIME_PER_COUNT = 1.0 / (CLOCK_FREQ_MHZ * 1e6); // 单计数时间(5ns) +constexpr int MAX_ADDRESS = 4096; // 最大道址 +constexpr int AMPLITUDE_MAX = 65535; // 幅度最大值 +} + +struct ParticleData { + int boardId = 0; // 板卡号 (0-15) + int channelId = 0; // 通道号 (0-15) + quint64 timestampCount = 0; // 原始时间计数(6字节) + double timeSeconds = 0.0; // 换算后的时间(秒) + quint16 amplitude = 0; // 原始幅度值 (0-65535) + int address = 0; // 转换后的道址 + quint32 icr = 0; // 输入计数率相关值 + quint16 riseTime = 0; // 上升时间 + quint16 fallTime = 0; // 下降时间 + + // 添加构造函数 + ParticleData() = default; // 保留默认构造 + ParticleData(int board, int ch, quint64 ts, double ts_sec, + quint16 amp, int addr, quint32 icr_val, + quint16 rise, quint16 fall) + : boardId(board) + , channelId(ch) + , timestampCount(ts) + , timeSeconds(ts_sec) + , amplitude(amp) + , address(addr) + , icr(icr_val) + , riseTime(rise) + , fallTime(fall) + {} +}; +class GvfToCsv : public QObject +{ + Q_OBJECT +public: + explicit GvfToCsv(QObject *parent = nullptr); + ~GvfToCsv() override; + + void convertGVF2CSVAsync(const QString &gvfPath, const QString &csvPath); + // 添加同步转换方法 + bool convertGVF2CSVSync(const QString &gvfPath, const QString &csvPath); + // 添加错误信息获取 + QString getLastError() const { return m_lastError; } + +signals: + void conversionProgress(int percent); + void conversionFinished(bool success); + void errorOccurred(const QString &msg); + +private slots: + void onSqliteOperationCompleted(bool success, const QString &msg); + +private: + void processDBData(); + QList parseParticleFrames(const QByteArray &data); + void cleanUp(); + // 添加错误信息存储 + void setLastError(const QString& error) { m_lastError = error; emit errorOccurred(error); } + + std::unique_ptr m_sqliteWorker; + QString m_gvfPath; + QString m_csvPath; + QString m_lastError; + // 用于同步转换 + QEventLoop* m_syncEventLoop = nullptr; +}; +#endif // GVFTOCSV_H \ No newline at end of file diff --git a/src/GvfToCsv/sqliteread.cpp b/src/GvfToCsv/sqliteread.cpp new file mode 100644 index 0000000..ab644a8 --- /dev/null +++ b/src/GvfToCsv/sqliteread.cpp @@ -0,0 +1,276 @@ +#include "sqliteread.h" +#include +SQLiteReadWrite::SQLiteReadWrite(QObject *parent) + : QObject(parent) +{ + m_connectionName = QString("SQLiteConnection_%1").arg(reinterpret_cast(this)); + idValue = 0; + m_stopRequested = false; + +} + +SQLiteReadWrite::~SQLiteReadWrite() +{ + if (!m_isClosed) { + closeDatabase(); + } + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + +} + +bool SQLiteReadWrite::openDatabase(const QString &dbPath) +{ + QMutexLocker locker(&m_mutex); + + if (!m_isClosed) { + closeDatabase(); + } + + // 确定数据库路径 + QString path = dbPath; + if (path.isEmpty()) { + QString appDir = QCoreApplication::applicationDirPath(); + path = QDir(appDir).filePath("test_readwrite.db"); + } + + m_dbPath = path; + + // 打开数据库 + m_database = QSqlDatabase::addDatabase("QSQLITE", m_connectionName); + m_database.setDatabaseName(m_dbPath); + + if (!m_database.open()) { + log(QString("打开数据库失败: %1").arg(m_database.lastError().text())); + return false; + } + + // 启用外键和WAL模式(提高并发性能) + QSqlQuery query(m_database); + // 开启外键约束 + if (!query.exec("PRAGMA foreign_keys = ON;")) { + log(QString("启用外键失败: %1").arg(query.lastError().text())); + } + + // 开启WAL模式(写时复制,提高读写并发性能) + if (!query.exec("PRAGMA journal_mode = WAL;")) { + log(QString("启用WAL模式失败: %1").arg(query.lastError().text())); + } + +// // 设置同步模式(在安全性和性能之间权衡) +// if (!query.exec("PRAGMA synchronous = NORMAL;")) { +// log(QString("设置同步模式失败: %1").arg(query.lastError().text())); +// } + + // 设置缓存大小 + if (!query.exec("PRAGMA cache_size = 10000;")) { + log(QString("设置缓存大小失败: %1").arg(query.lastError().text())); + } + + m_isClosed = false; + log(QString("数据库打开成功: %1").arg(m_dbPath)); + return true; +} + +void SQLiteReadWrite::closeDatabase() +{ + QMutexLocker locker(&m_mutex); + + if (m_isClosed) { + return; + } + m_isClosed = true; + + if (m_database.isOpen()) { + m_database.close(); + log("数据库已关闭"); + } + if (QSqlDatabase::contains(m_connectionName)) { + QSqlDatabase::removeDatabase(m_connectionName); + } + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); +} + + +void SQLiteReadWrite::startReadWriteOperation() +{ + if (!m_database.isOpen()) { + emit operationCompleted(false, "数据库未打开"); + return; + } + + m_stopRequested = false; + //在单独的线程中执行,避免阻塞UI + QtConcurrent::run(this, &SQLiteReadWrite::processReadWrite); +} + +void SQLiteReadWrite::stopOperation() +{ + m_stopRequested = true; + log("stop requese send"); +} + +void SQLiteReadWrite::processReadWrite() +{ + m_timer.start(); + log("开始边读边写操作..."); + try { + processWithMultipleConnections(); + if (!m_stopRequested) { + qint64 elapsed = m_timer.elapsed(); + QString message = QString("操作完成,总耗时: %1 毫秒").arg(elapsed); + log(message); + emit operationCompleted(true, message); + } else { + emit operationCompleted(false, "quxiao"); + } + + } catch (const std::exception &e) { + log(QString("发生异常: %1").arg(e.what())); + emit operationCompleted(false, QString("异常: %1").arg(e.what())); + } +} + + +void SQLiteReadWrite::processWithMultipleConnections() +{ + const QString readConnName = m_connectionName + "_read"; + + int totalRecords = 0; + int processedRecords = 0; + bool readSuccess = false; + + { + QSqlDatabase readDb = QSqlDatabase::addDatabase("QSQLITE", readConnName); + readDb.setDatabaseName(m_dbPath); + + if (!readDb.open()) { + QString logMsg = QString("打开读取连接失败: %1").arg(readDb.lastError().text()); + log(logMsg.toUtf8().constData()); + emit operationCompleted(false, "无法打开数据库连接"); + return; + } + + { + QSqlQuery optimizeQuery(readDb); + optimizeQuery.exec("PRAGMA journal_mode = WAL;"); + optimizeQuery.exec("PRAGMA synchronous = OFF;"); + optimizeQuery.exec("PRAGMA cache_size = -200000;"); // 200MB缓存 + optimizeQuery.exec("PRAGMA temp_store = MEMORY;"); + optimizeQuery.exec("PRAGMA mmap_size = 2147483648;"); // 2GB内存映射 + optimizeQuery.exec("PRAGMA query_only = ON;"); + optimizeQuery.finish(); + } + + { + QSqlQuery countQuery(readDb); + if (countQuery.exec("SELECT COUNT(*) FROM lmdatainfov2")) { + countQuery.next(); + totalRecords = countQuery.value(0).toInt(); + } + countQuery.finish(); + } + + if (totalRecords == 0) { + log("数据库中无记录"); + readDb.close(); + emit operationCompleted(false, "数据库中无有效数据"); + return; + } + + QString totalMsg = QString("GVF数据库总记录数: %1 条").arg(totalRecords); + log(totalMsg.toUtf8().constData()); + + const int pageSize = 200; + int lastId = 0; + + m_DataBaseList.clear(); + m_DataBaseList.reserve(totalRecords); + + while (!m_stopRequested) { + QSqlQuery readQuery(readDb); + readQuery.setForwardOnly(true); + readQuery.prepare("SELECT id, data FROM lmdatainfov2 WHERE id > :lastId ORDER BY id LIMIT :limit"); + readQuery.bindValue(":lastId", lastId); + readQuery.bindValue(":limit", pageSize); + + if (!readQuery.exec()) { + QString errMsg = QString("数据读取失败: %1").arg(readQuery.lastError().text()); + log(errMsg.toUtf8().constData()); + readQuery.finish(); + break; + } + + int pageRecords = 0; + while (readQuery.next() && !m_stopRequested) { + DataBaseStruct data; + data.id = readQuery.value(0).toInt(); + data.data = readQuery.value(1).toByteArray(); + m_DataBaseList.append(data); + lastId = data.id; + pageRecords++; + } + + readQuery.finish(); + readQuery.clear(); + + if (pageRecords == 0) { + break; + } + + processedRecords += pageRecords; + int remainingRecords = totalRecords - processedRecords; + int progress = (processedRecords * 100) / totalRecords; + emit progressUpdated(progress, processedRecords, totalRecords); + } + + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + readDb.close(); + readSuccess = true; + } + + QThread::msleep(50); + + if (QSqlDatabase::contains(readConnName)) { + QSqlDatabase::removeDatabase(readConnName); + } + + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); +} + + + +QSqlDatabase SQLiteReadWrite::createConnection() +{ + QString connName = QString("TempConnection_%1_%2") + .arg(m_connectionName) + .arg(QDateTime::currentMSecsSinceEpoch()); + + QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connName); + db.setDatabaseName(m_dbPath); + + if (!db.open()) { + log(QString("创建临时连接失败: %1").arg(db.lastError().text())); + } + + return db; +} + + +void SQLiteReadWrite::log(const QString &message) +{ + QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss.zzz"); + QString logMsg = QString("[%1] %2").arg(timestamp).arg(message); + + emit logMessage(logMsg); +} + +QVector SQLiteReadWrite::DataBaseList() const +{ + QMutexLocker locker(&m_mutex); + return m_DataBaseList; +} + +void SQLiteReadWrite::setIdValue(int value) +{ + idValue = value; +} diff --git a/src/GvfToCsv/sqliteread.h b/src/GvfToCsv/sqliteread.h new file mode 100644 index 0000000..951fe86 --- /dev/null +++ b/src/GvfToCsv/sqliteread.h @@ -0,0 +1,89 @@ +// sqlitereadwrite.h +#ifndef SQLITERREADWRITE_H +#define SQLITERREADWRITE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +struct DataBaseStruct +{ + int id;//ID + int curdevconfigindex; + int orgdevconfigindex; + int icr; + int datanum; + int tick; + QByteArray data; + QString reserve; +}; + +class SQLiteReadWrite : public QObject +{ + Q_OBJECT + +public: + explicit SQLiteReadWrite(QObject *parent = nullptr); + ~SQLiteReadWrite(); + + bool openDatabase(const QString &dbPath); + void closeDatabase(); + + // 边读边写操作 + void startReadWriteOperation(); + void stopOperation(); + + void setIdValue(int value); + + QVector DataBaseList() const; + +signals: + void progressUpdated(int percent, qint64 processed, qint64 total); + void operationCompleted(bool success, const QString &message); + void logMessage(const QString &message); + void emitReadId(int readId); + void emitStopTime(); + +private slots: + void processReadWrite(); + +private: + // 数据库连接管理 + QSqlDatabase m_database; + QString m_dbPath; + + // 线程安全 + mutable QMutex m_mutex; + QAtomicInteger m_stopRequested{false}; + + // 性能统计 + QElapsedTimer m_timer; + + // 连接名(确保多线程连接唯一性) + QString m_connectionName; + + // 私有方法 + QSqlDatabase createConnection(); + + + // 具体操作 + void processWithMultipleConnections(); + + void log(const QString &message); + + int idValue; + + QVector m_DataBaseList; + + bool m_isClosed = true; + +}; + +#endif // SQLITERREADWRITE_H diff --git a/src/MainWindow.ui b/src/MainWindow.ui index 334da95..6ac767b 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -20,7 +20,7 @@ 0 0 892 - 23 + 21 diff --git a/src/NewMeasureAnalysisDlg.cpp b/src/NewMeasureAnalysisDlg.cpp index 16fbc66..59c3a2a 100644 --- a/src/NewMeasureAnalysisDlg.cpp +++ b/src/NewMeasureAnalysisDlg.cpp @@ -7,7 +7,7 @@ #include #include #include "DataProcessWorkPool.h" - +#include "GlobalDefine.h" NewMeasureAnalysisDlg::NewMeasureAnalysisDlg(QWidget *parent) : QDialog(parent) , ui(new Ui::NewMeasureAnalysisDlg) @@ -242,17 +242,33 @@ void NewMeasureAnalysisDlg::on_btn_ok_clicked() QMessageBox::warning(this, QStringLiteral(u"警告"), QStringLiteral(u"创建测量分析项目工作目录失败:\n%1!").arg(project_dir_path)); return; } - if ( ui->checkBox_file_data->isChecked() ) { + if ( ui->checkBox_file_data->isChecked() ) + { const QString& data_file_path = ui->lineEdit_filename->property("data_file_path").toString(); if (data_file_path.isEmpty()) { QMessageBox::warning(this, QStringLiteral(u"警告"), QStringLiteral(u"请选择粒子数据文件!")); return; } - auto separate_task = new DataProcessWorkPool::ParticleDataSortByMinimysTask; - separate_task->SetAllChannelParticleDataFilename(data_file_path); - separate_task->SetSortedResultDir(project_dir_path); - separate_task->SetFinishedNotifier(this, "onNewProjectFromFileFinished", project_name); - separate_task->StartTask(); + + if(QFileInfo(data_file_path).suffix().toLower() == "gvf") + { + // 正确使用任务池处理GVF转换 + auto gvfTask = new DataProcessWorkPool::GvfToCsvDataTask; + gvfTask->setGvfName(data_file_path); + gvfTask->setCsvName(project_dir_path); + gvfTask->SetFinishedNotifier(this, "onGvfConversionFinished", project_name); + gvfTask->StartTask(); + } + else + { + // 直接处理CSV文件 + startParticleSortTask(data_file_path, project_name, project_dir_path); + } + // auto separate_task = new DataProcessWorkPool::ParticleDataSortByMinimysTask; + // separate_task->SetAllChannelParticleDataFilename(data_file_path); + // separate_task->SetSortedResultDir(project_dir_path); + // separate_task->SetFinishedNotifier(this, "onNewProjectFromFileFinished", project_name); + // separate_task->StartTask(); ui->stackedWidget->setEnabled(false); ui->label_note->setEnabled(false); @@ -274,3 +290,59 @@ void NewMeasureAnalysisDlg::on_btn_ok_clicked() // separate_task->SetFinishedNotifier(this->_tree_measure_analysis, "onFinishedSeparateEveryChannelParticleData", project_name); // separate_task->StartTask(); } + +// 添加新的槽函数处理GVF转换完成 +void NewMeasureAnalysisDlg::onGvfConversionFinished(bool ok, const QString& project_name, const QVariant &data) +{ + this->_task_wait_timer->stop(); + ui->progressBar->setValue(100); + + if (ok) { + QString csvFilePath = data.toString(); + QFileInfo fileInfo(csvFilePath); + + if (fileInfo.exists() && fileInfo.size() > 100) { + startParticleSortTask(csvFilePath, project_name, fileInfo.absolutePath()); + return; + } else { + ok = false; + QString errorMsg; + if (!fileInfo.exists()) { + errorMsg = QStringLiteral(u"输出文件不存在: %1").arg(csvFilePath); + } else { + errorMsg = QStringLiteral(u"输出文件为空: %1").arg(csvFilePath); + QFile::remove(csvFilePath); + } + LOG_ERROR(errorMsg.toUtf8().constData()); + } + } + QString projects_dir_path = QDir(qApp->applicationDirPath()).filePath("Projects"); + QString project_dir_path = QDir(projects_dir_path).filePath(project_name); + QDir project_dir(project_dir_path); + if (project_dir.exists()) { + project_dir.removeRecursively(); + } + + QMessageBox::critical(this, QStringLiteral(u"GVF转换失败"), + QStringLiteral(u"错误信息: %1\n\n请检查GVF文件格式是否正确。") + .arg(data.isValid() ? data.toString() : "未知错误")); + + ui->progressBar->setVisible(false); + ui->stackedWidget->setEnabled(true); + ui->label_note->setEnabled(true); + ui->plainTextEdit_description->setEnabled(true); + ui->btn_previous_step->setEnabled(true); + ui->btn_next_step->setEnabled(true); + ui->btn_ok->setEnabled(true); +} + +void NewMeasureAnalysisDlg::startParticleSortTask(const QString& data_file_path, + const QString& project_name, + const QString& project_dir_path) +{ + auto separate_task = new DataProcessWorkPool::ParticleDataSortByMinimysTask; + separate_task->SetAllChannelParticleDataFilename(data_file_path); + separate_task->SetSortedResultDir(project_dir_path); + separate_task->SetFinishedNotifier(this, "onNewProjectFromFileFinished", project_name); + separate_task->StartTask(); +} diff --git a/src/NewMeasureAnalysisDlg.h b/src/NewMeasureAnalysisDlg.h index bfd099b..6cdfee7 100644 --- a/src/NewMeasureAnalysisDlg.h +++ b/src/NewMeasureAnalysisDlg.h @@ -20,10 +20,13 @@ public: private: void initialization(); void newProject(const QString &particle_data_filename = QString()); - + void startParticleSortTask(const QString& data_file_path, + const QString& project_name, + const QString& project_dir_path); private slots: void onNewProjectFromFileFinished(bool ok, const QString& project_name, const QVariant &data); void on_btn_ok_clicked(); + void onGvfConversionFinished(bool ok, const QString& project_name, const QVariant &data); private: Ui::NewMeasureAnalysisDlg *ui; diff --git a/src/src.pro b/src/src.pro index 1cbd14c..8f3fe69 100644 --- a/src/src.pro +++ b/src/src.pro @@ -83,6 +83,8 @@ SOURCES += \ EnergyCountPlotView/EnergyCountPlotView.cpp \ EnergyScaleDataModel.cpp \ EnergyScaleForm.cpp \ + GvfToCsv/GvfToCsv.cpp \ + GvfToCsv/sqliteread.cpp \ MainWindow.cpp \ MeasureAnalysisDataTableView/MeasureAnalysisDataTableView.cpp \ MeasureAnalysisHistoryForm/MeasureAnalysisHistoryForm.cpp \ @@ -136,6 +138,8 @@ HEADERS += \ EnergyScaleDataModel.h \ EnergyScaleForm.h \ GlobalDefine.h \ + GvfToCsv/GvfToCsv.h \ + GvfToCsv/sqliteread.h \ MainWindow.h \ MeasureAnalysisDataTableView/MeasureAnalysisDataTableView.h \ MeasureAnalysisHistoryForm/MeasureAnalysisHistoryForm.h \