From f3eb0cbf59d182f8de06dc67fa76dc1dc7696ac2 Mon Sep 17 00:00:00 2001 From: anxinglong <2910824064@qq.com> Date: Mon, 13 Jul 2026 18:03:04 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=BB=E5=8F=96=E5=BC=80=E5=A7=8B=E6=B5=8B?= =?UTF-8?q?=E9=87=8F=E6=97=B6=E9=97=B4=EF=BC=8C=E7=BC=96=E5=86=99=E7=94=9F?= =?UTF-8?q?=E6=88=90=E6=AD=BB=E6=97=B6=E9=97=B4=E6=B4=BB=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E5=AE=9E=E6=97=B6=E9=97=B4=E5=AE=9E=E7=8E=B0=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E7=94=9F=E6=88=90=E6=8A=A5=E5=91=8A=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/GvfToCsv/GvfToCsv.cpp | 64 +++++++++++- src/GvfToCsv/sqliteread.cpp | 23 +++++ src/GvfToCsv/sqliteread.h | 2 + src/analysisReport/reportgenerator.cpp | 4 +- src/analysisReport/reportwidget.cpp | 18 +++- src/analysisReport/spectrumdata.cpp | 137 ++++++++++++++++++++++++- src/analysisReport/spectrumdata.h | 8 +- src/main.cpp | 1 - 8 files changed, 248 insertions(+), 9 deletions(-) diff --git a/src/GvfToCsv/GvfToCsv.cpp b/src/GvfToCsv/GvfToCsv.cpp index 93c393d..45a9ed7 100644 --- a/src/GvfToCsv/GvfToCsv.cpp +++ b/src/GvfToCsv/GvfToCsv.cpp @@ -1,9 +1,12 @@ #include "GvfToCsv.h" #include +#include #include #include #include #include +#include +#include #include "GlobalDefine.h" GvfToCsv::GvfToCsv(QObject *parent) : QObject(parent) @@ -234,6 +237,9 @@ void GvfToCsv::processDBData() throw std::runtime_error("GVF数据库中无任何记录"); return; } + + // 从lmstatisticinfov2读取软件记录的测量启动时间,精度最高 + QString measureStartAbsTime = m_sqliteWorker->getMeasureStartTime(); QFile outFile(m_csvPath); if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { throw std::runtime_error(QString("无法创建CSV文件: %1,错误: %2") @@ -250,6 +256,9 @@ void GvfToCsv::processDBData() csvBuffer.reserve(4 * 1024 * 1024); // 4MB缓冲区 quint64 totalParticles = 0; + quint64 totalIcrSum = 0; // 所有粒子ICR输入触发总计数 Cicr + quint64 tsMin = ULLONG_MAX; // 全部粒子最小时间戳计数(用于计算总时长) + quint64 tsMax = 0; // 全部粒子最大时间戳计数 int emptyFrameCount = 0; int processedFrames = 0; const int totalFrames = dbList.size(); @@ -260,13 +269,18 @@ void GvfToCsv::processDBData() 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); + //硬件输入触发ICR总和累加 + totalIcrSum += p.icr; + //更新全局最小时间戳 + if(p.timestampCount < tsMin) tsMin = p.timestampCount; + //更新全局最大时间戳 + if(p.timestampCount > tsMax) tsMax = p.timestampCount; } } @@ -287,6 +301,54 @@ void GvfToCsv::processDBData() out << csvBuffer; } + double realTime = 0.0; // 实时间RealTime Tr:物理测量总时长(单位s) + double liveTime = 0.0; // 活时间LiveTime Tl:探测器有效采集时长(单位s) + double deadTime = 0.0; // 死时间DeadTime Td:硬件阻塞无法采集时长(单位s) + // 存在有效粒子且时间戳有差值,才具备计算时间的条件 + if(totalParticles > 0 && tsMax > tsMin) + { + // 时间戳差值 = 最大计数 - 最小计数 + quint64 tsDelta = tsMax - tsMin; + // 计算公式:实时间 = 总计数差 / 时钟频率(200MHz) + realTime = static_cast(tsDelta) / (GvfConst::CLOCK_FREQ_MHZ * 1e6); + // 防止ICR总和为0除零崩溃 + if(totalIcrSum > 0) + { + // 有效粒子占输入触发总计数比例 + double countRatio = static_cast(totalParticles) / totalIcrSum; + // 活时间 = 实时间 × 有效粒子占比 + liveTime = realTime * countRatio; + // 死时间 = 实时间 - 活时间 + deadTime = realTime - liveTime; + } + } + QFileInfo csvFileInfo(m_csvPath); + QString jsonFilePath = csvFileInfo.absoluteDir().filePath("report_params.json"); + QFile jsonFile(jsonFilePath); + + QJsonObject rootObj; + rootObj["real_time_s"] = realTime; // 真时间,单位秒 + rootObj["live_time_s"] = liveTime; // 活时间,单位秒 + rootObj["dead_time_s"] = deadTime; // 死时间,单位秒 + rootObj["start_absolute_time"] = measureStartAbsTime; + QFileInfo gvfInfo(m_gvfPath); + rootObj["gvf_file_name"] = gvfInfo.fileName(); + // 生成带缩进格式化JSON文本,便于阅读 + QJsonDocument jsonDoc(rootObj); + QByteArray jsonContent = jsonDoc.toJson(QJsonDocument::Indented); + + // 写入JSON文件,覆盖原有文件 + if (!jsonFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + { + throw std::runtime_error(QString("Generate report_params.json failed, path:%1 error:%2") + .arg(jsonFilePath) + .arg(jsonFile.errorString()) + .toStdString()); + } + jsonFile.write(jsonContent); + jsonFile.flush(); + jsonFile.close(); + out.flush(); outFile.close(); diff --git a/src/GvfToCsv/sqliteread.cpp b/src/GvfToCsv/sqliteread.cpp index ab644a8..f45bbfd 100644 --- a/src/GvfToCsv/sqliteread.cpp +++ b/src/GvfToCsv/sqliteread.cpp @@ -270,6 +270,29 @@ QVector SQLiteReadWrite::DataBaseList() const return m_DataBaseList; } +QString SQLiteReadWrite::getMeasureStartTime() +{ + QMutexLocker locker(&m_mutex); + if (m_isClosed || !m_database.isOpen()) + { + log("Database not open, cannot read statistic starttime"); + return ""; + } + QSqlQuery query(m_database); + // lmstatisticinfov2 存储本次测量标准起止时间 + if (!query.exec("SELECT starttime FROM lmstatisticinfov2 LIMIT 1;")) + { + log(QString("Query lmstatisticinfov2 failed: %1").arg(query.lastError().text())); + return ""; + } + if (query.next()) + { + return query.value("starttime").toString(); + } + log("lmstatisticinfov2 table has no starttime record"); + return ""; +} + void SQLiteReadWrite::setIdValue(int value) { idValue = value; diff --git a/src/GvfToCsv/sqliteread.h b/src/GvfToCsv/sqliteread.h index 951fe86..32e677e 100644 --- a/src/GvfToCsv/sqliteread.h +++ b/src/GvfToCsv/sqliteread.h @@ -44,6 +44,8 @@ public: QVector DataBaseList() const; + // 获取测量官方起始时间 lmstatisticinfov2.starttime + QString getMeasureStartTime(); signals: void progressUpdated(int percent, qint64 processed, qint64 total); void operationCompleted(bool success, const QString &message); diff --git a/src/analysisReport/reportgenerator.cpp b/src/analysisReport/reportgenerator.cpp index 1ca94ef..ff3b52e 100644 --- a/src/analysisReport/reportgenerator.cpp +++ b/src/analysisReport/reportgenerator.cpp @@ -150,7 +150,7 @@ QString ReportGenerator::generateMeasurementSection() const .arg(formatDateTime(m_data.measurement.startTime)) .arg(formatDecimal(m_data.measurement.liveTime, 0)); html += QString("实时间(S):%1" - "死时间:%2 %") + "死时间:%2") .arg(formatDecimal(m_data.measurement.realTime, 0)) .arg(formatDecimal(m_data.measurement.deadTime, 2)); html += ""; @@ -345,7 +345,7 @@ QString ReportGenerator::generateTextReport() const stream << QStringLiteral(u" 开始时间: ") << formatDateTime(m_data.measurement.startTime) << "\n"; stream << QStringLiteral(u" 活时间: ") << formatDecimal(m_data.measurement.liveTime, 0) << "\n"; stream << QStringLiteral(u" 实时间: ") << formatDecimal(m_data.measurement.realTime, 0) << "\n"; - stream << QStringLiteral(u" 死时间: ") << formatDecimal(m_data.measurement.deadTime, 2) << " %\n\n"; + stream << QStringLiteral(u" 死时间: ") << formatDecimal(m_data.measurement.deadTime, 2) << " \n\n"; stream << QStringLiteral(u"刻度\n"); stream << "----------------------------------------\n"; diff --git a/src/analysisReport/reportwidget.cpp b/src/analysisReport/reportwidget.cpp index 366bcab..3540a6a 100644 --- a/src/analysisReport/reportwidget.cpp +++ b/src/analysisReport/reportwidget.cpp @@ -14,6 +14,10 @@ #include #include #include +#include +#include +#include "MeasureAnalysisProjectModel.h" + ReportWidget::ReportWidget(QWidget *parent) : QWidget(parent) { @@ -467,9 +471,17 @@ void ReportWidget::exportText() void ReportWidget::loadSampleData() { - SpectrumReportData data; - data.fillSampleData(); - setReportData(data); + auto project_model = ProjectList::Instance()->GetCurrentProjectModel(); + if (project_model == nullptr) { + QMessageBox::information(this, "提示:", "请先加载项目!"); + return; + } + //获取当前可执行目录下 + QString jsonPath = project_model->GetProjectDir() + "/report_params.json"; + m_data.loadReportParamsJson(jsonPath); + // SpectrumReportData data; + // data.fillSampleData(); + setReportData(m_data); } diff --git a/src/analysisReport/spectrumdata.cpp b/src/analysisReport/spectrumdata.cpp index 4fdbc0d..ff3a0ef 100644 --- a/src/analysisReport/spectrumdata.cpp +++ b/src/analysisReport/spectrumdata.cpp @@ -1,5 +1,5 @@ #include "spectrumdata.h" - +#include SpectrumReportData::SpectrumReportData() { } @@ -106,4 +106,139 @@ void SpectrumReportData::fillSampleData() signature.reviewer = "管理员"; signature.laboratory = "GAMMASPEC"; signature.reportDate = QDate(2025, 11, 13); +} + +bool SpectrumReportData::loadReportParamsJson(const QString &jsonPath) +{ + QFile jsonFile(jsonPath); + // 判断文件是否存在 + if (!jsonFile.exists()) + { + qDebug() << "loadReportParamsJson: 文件不存在 " << jsonPath; + return false; + } + // 只读打开 + if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) + { + qDebug() << "loadReportParamsJson: 打开失败 " << jsonFile.errorString(); + return false; + } + QByteArray jsonRaw = jsonFile.readAll(); + jsonFile.close(); + + QJsonParseError parseErr; + QJsonDocument doc = QJsonDocument::fromJson(jsonRaw, &parseErr); + // JSON语法错误判断 + if (parseErr.error != QJsonParseError::NoError || doc.isNull() || !doc.isObject()) + { + qDebug() << "loadReportParamsJson: JSON解析错误 " << parseErr.errorString(); + return false; + } + QJsonObject rootObj = doc.object(); + + // 临时拷贝原有测量参数,只覆盖时间相关字段 + SpectrumReportData tempData = *this; + // ===================== 1. 测量参数模块 ===================== + //读取能谱文件名 + if (rootObj.contains("gvf_file_name")) + { + tempData.measurement.spectrumFile = rootObj["gvf_file_name"].toString(); + } + + // 读取实时间 real_time_s + if (rootObj.contains("real_time_s")) + { + tempData.measurement.realTime = rootObj["real_time_s"].toDouble(0.0); + } + // 读取活时间 live_time_s + if (rootObj.contains("live_time_s")) + { + tempData.measurement.liveTime = rootObj["live_time_s"].toDouble(0.0); + } + // 读取死时间秒,换算百分比存入deadTime + if (rootObj.contains("dead_time_s")) + { + tempData.measurement.deadTime = rootObj["dead_time_s"].toDouble(0.0); + } + // 读取测量开始时间字符串 start_absolute_time + if (rootObj.contains("start_absolute_time")) + { + QString dtStr = rootObj["start_absolute_time"].toString(); + QDateTime dt = QDateTime::fromString(dtStr, "yyyy-M-d hh:mm:ss"); + if (dt.isValid()) + { + tempData.measurement.startTime = dt; + } + } + // ===================== 2. 刻度参数模块 ===================== + // 刻度文件名称 + if (rootObj.contains("calib_file")) + tempData.calibration.fileName = rootObj["calib_file"].toString(); + // 标准源 + if (rootObj.contains("standard_source")) + tempData.calibration.standardSource = rootObj["standard_source"].toString(); + // 能量刻度生成时间 + if (rootObj.contains("energy_calib_time")) + { + QString dtStr = rootObj["energy_calib_time"].toString(); + QDateTime dt = QDateTime::fromString(dtStr, "yyyy-M-d hh:mm:ss"); + if (dt.isValid()) + tempData.calibration.energy.generateTime = dt; + } + // 能量截距 + if (rootObj.contains("energy_intercept")) + tempData.calibration.energy.intercept = rootObj["energy_intercept"].toDouble(0.0); + // 能量斜率 + if (rootObj.contains("energy_slope")) + tempData.calibration.energy.slope = rootObj["energy_slope"].toDouble(0.0); + // 二次项系数 + if (rootObj.contains("energy_quadratic")) + tempData.calibration.energy.quadratic = rootObj["energy_quadratic"].toDouble(0.0); + // 效率刻度生成时间 + if (rootObj.contains("eff_calib_time"))// 生成时间 + { + QString dtStr = rootObj["eff_calib_time"].toString(); + QDateTime dt = QDateTime::fromString(dtStr, "yyyy-M-d hh:mm:ss"); + if (dt.isValid()) + tempData.calibration.efficiency.generateTime = dt; + } + // 拐点能量 + if (rootObj.contains("inflection_energy"))// 拐点能量 (keV) + tempData.calibration.efficiency.inflectionEnergy = rootObj["inflection_energy"].toDouble(0.0); + // 拐点以上拟合公式 + if (rootObj.contains("above_eq"))// 拐点以上方法 + tempData.calibration.efficiency.aboveInflection = rootObj["above_eq"].toString(); + // 拐点以上误差 + if (rootObj.contains("above_err"))// 拐点以上误差 + tempData.calibration.efficiency.aboveError = rootObj["above_err"].toDouble(0.0); + // 拐点以下拟合公式 + if (rootObj.contains("below_eq"))// 拐点以下方法 + tempData.calibration.efficiency.belowInflection = rootObj["below_eq"].toString(); + // 拐点以下误差 + if (rootObj.contains("below_err"))// 拐点以下误差 + tempData.calibration.efficiency.belowError = rootObj["below_err"].toDouble(0.0); + // 效率对数方程式 + if (rootObj.contains("eff_log_eq"))// 效率对数方程式 + tempData.calibration.efficiency.logEquation = rootObj["eff_log_eq"].toString(); + // ===================== 3. 核素库模块 ===================== + if (rootObj.contains("nuclide_lib_file"))// 库文件名 + tempData.nuclideLib.fileName = rootObj["nuclide_lib_file"].toString(); + if (rootObj.contains("peak_match_width"))// 峰匹配宽度 + tempData.nuclideLib.peakMatchWidth = rootObj["peak_match_width"].toDouble(0.0); + // ===================== 4. 分析参数模块 ===================== + if (rootObj.contains("start_energy"))//起始能量 + tempData.analysis.startEnergy = rootObj["start_energy"].toDouble(0.0); + if (rootObj.contains("end_energy"))//终止能量 + tempData.analysis.endEnergy = rootObj["end_energy"].toDouble(0.0); + if (rootObj.contains("stat_error_limit"))//统计误差限值 + tempData.analysis.statErrorLimit = rootObj["stat_error_limit"].toDouble(0.0); + if (rootObj.contains("activity_ratio"))// 活度比例系数 + tempData.analysis.activityRatio = rootObj["activity_ratio"].toDouble(0.0); + if (rootObj.contains("mda_method"))// 探测限计算方法 + tempData.analysis.mdaMethod = rootObj["mdaMethod"].toString(); + + + // 替换本类测量参数,其余刻度/核素/峰数据不变 + *this = tempData; + return true; } \ No newline at end of file diff --git a/src/analysisReport/spectrumdata.h b/src/analysisReport/spectrumdata.h index 4ad8dbd..0468646 100644 --- a/src/analysisReport/spectrumdata.h +++ b/src/analysisReport/spectrumdata.h @@ -4,7 +4,10 @@ #include #include #include - +#include +#include +#include +#include // 能量刻度参数 struct EnergyCalibration { QDateTime generateTime; // 生成时间 @@ -109,6 +112,9 @@ public: // 示例数据填充 void fillSampleData(); + //读取report_params.json,填充measurement时间参数 + bool loadReportParamsJson(const QString &jsonPath); + }; #endif // SPECTRUMDATA_H \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 7650cfa..f2f4295 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,6 @@ #include #include "QsLogManage.h" #include - void InitAppRunEnv() { // 在应用程序可执行文件所在目录检查并创建Projects目录 QString projects_dir_path = QDir(qApp->applicationDirPath()).filePath("Projects");