读取开始测量时间,编写生成死时间活时间实时间实现,修改生成报告内容

This commit is contained in:
anxinglong 2026-07-13 18:03:04 +08:00
parent e83492e7a9
commit f3eb0cbf59
8 changed files with 248 additions and 9 deletions

View File

@ -1,9 +1,12 @@
#include "GvfToCsv.h" #include "GvfToCsv.h"
#include <QFile> #include <QFile>
#include <QFileInfo>
#include <QTextStream> #include <QTextStream>
#include <QEventLoop> #include <QEventLoop>
#include <QTimer> #include <QTimer>
#include <QDir> #include <QDir>
#include <QJsonDocument>
#include <QJsonObject>
#include "GlobalDefine.h" #include "GlobalDefine.h"
GvfToCsv::GvfToCsv(QObject *parent) GvfToCsv::GvfToCsv(QObject *parent)
: QObject(parent) : QObject(parent)
@ -234,6 +237,9 @@ void GvfToCsv::processDBData()
throw std::runtime_error("GVF数据库中无任何记录"); throw std::runtime_error("GVF数据库中无任何记录");
return; return;
} }
// 从lmstatisticinfov2读取软件记录的测量启动时间精度最高
QString measureStartAbsTime = m_sqliteWorker->getMeasureStartTime();
QFile outFile(m_csvPath); QFile outFile(m_csvPath);
if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
throw std::runtime_error(QString("无法创建CSV文件: %1错误: %2") throw std::runtime_error(QString("无法创建CSV文件: %1错误: %2")
@ -250,6 +256,9 @@ void GvfToCsv::processDBData()
csvBuffer.reserve(4 * 1024 * 1024); // 4MB缓冲区 csvBuffer.reserve(4 * 1024 * 1024); // 4MB缓冲区
quint64 totalParticles = 0; quint64 totalParticles = 0;
quint64 totalIcrSum = 0; // 所有粒子ICR输入触发总计数 Cicr
quint64 tsMin = ULLONG_MAX; // 全部粒子最小时间戳计数(用于计算总时长)
quint64 tsMax = 0; // 全部粒子最大时间戳计数
int emptyFrameCount = 0; int emptyFrameCount = 0;
int processedFrames = 0; int processedFrames = 0;
const int totalFrames = dbList.size(); const int totalFrames = dbList.size();
@ -260,13 +269,18 @@ void GvfToCsv::processDBData()
emptyFrameCount++; emptyFrameCount++;
} else { } else {
totalParticles += particles.size(); totalParticles += particles.size();
for (const auto &p : particles) { for (const auto &p : particles) {
csvBuffer += QString("%1,%2,%3,%4\n") csvBuffer += QString("%1,%2,%3,%4\n")
.arg(p.boardId) .arg(p.boardId)
.arg(p.channelId) .arg(p.channelId)
.arg(p.address) .arg(p.address)
.arg(p.timestampCount); .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; 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<double>(tsDelta) / (GvfConst::CLOCK_FREQ_MHZ * 1e6);
// 防止ICR总和为0除零崩溃
if(totalIcrSum > 0)
{
// 有效粒子占输入触发总计数比例
double countRatio = static_cast<double>(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(); out.flush();
outFile.close(); outFile.close();

View File

@ -270,6 +270,29 @@ QVector<DataBaseStruct> SQLiteReadWrite::DataBaseList() const
return m_DataBaseList; 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) void SQLiteReadWrite::setIdValue(int value)
{ {
idValue = value; idValue = value;

View File

@ -44,6 +44,8 @@ public:
QVector<DataBaseStruct> DataBaseList() const; QVector<DataBaseStruct> DataBaseList() const;
// 获取测量官方起始时间 lmstatisticinfov2.starttime
QString getMeasureStartTime();
signals: signals:
void progressUpdated(int percent, qint64 processed, qint64 total); void progressUpdated(int percent, qint64 processed, qint64 total);
void operationCompleted(bool success, const QString &message); void operationCompleted(bool success, const QString &message);

View File

@ -150,7 +150,7 @@ QString ReportGenerator::generateMeasurementSection() const
.arg(formatDateTime(m_data.measurement.startTime)) .arg(formatDateTime(m_data.measurement.startTime))
.arg(formatDecimal(m_data.measurement.liveTime, 0)); .arg(formatDecimal(m_data.measurement.liveTime, 0));
html += QString("<tr><td>实时间S</td><td>%1</td>" html += QString("<tr><td>实时间S</td><td>%1</td>"
"<td>死时间:</td><td>%2 %</td></tr>") "<td>死时间:</td><td>%2</td></tr>")
.arg(formatDecimal(m_data.measurement.realTime, 0)) .arg(formatDecimal(m_data.measurement.realTime, 0))
.arg(formatDecimal(m_data.measurement.deadTime, 2)); .arg(formatDecimal(m_data.measurement.deadTime, 2));
html += "</table>"; html += "</table>";
@ -345,7 +345,7 @@ QString ReportGenerator::generateTextReport() const
stream << QStringLiteral(u" 开始时间: ") << formatDateTime(m_data.measurement.startTime) << "\n"; 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.liveTime, 0) << "\n";
stream << QStringLiteral(u" 实时间: ") << formatDecimal(m_data.measurement.realTime, 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 << QStringLiteral(u"刻度\n");
stream << "----------------------------------------\n"; stream << "----------------------------------------\n";

View File

@ -14,6 +14,10 @@
#include <QLabel> #include <QLabel>
#include <QTableWidget> #include <QTableWidget>
#include <QScrollArea> #include <QScrollArea>
#include <QDir>
#include <QDebug>
#include "MeasureAnalysisProjectModel.h"
ReportWidget::ReportWidget(QWidget *parent) ReportWidget::ReportWidget(QWidget *parent)
: QWidget(parent) : QWidget(parent)
{ {
@ -467,9 +471,17 @@ void ReportWidget::exportText()
void ReportWidget::loadSampleData() void ReportWidget::loadSampleData()
{ {
SpectrumReportData data; auto project_model = ProjectList::Instance()->GetCurrentProjectModel();
data.fillSampleData(); if (project_model == nullptr) {
setReportData(data); QMessageBox::information(this, "提示:", "请先加载项目!");
return;
}
//获取当前可执行目录下
QString jsonPath = project_model->GetProjectDir() + "/report_params.json";
m_data.loadReportParamsJson(jsonPath);
// SpectrumReportData data;
// data.fillSampleData();
setReportData(m_data);
} }

View File

@ -1,5 +1,5 @@
#include "spectrumdata.h" #include "spectrumdata.h"
#include <QDebug>
SpectrumReportData::SpectrumReportData() SpectrumReportData::SpectrumReportData()
{ {
} }
@ -107,3 +107,138 @@ void SpectrumReportData::fillSampleData()
signature.laboratory = "GAMMASPEC"; signature.laboratory = "GAMMASPEC";
signature.reportDate = QDate(2025, 11, 13); 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;
}

View File

@ -4,7 +4,10 @@
#include <QString> #include <QString>
#include <QDateTime> #include <QDateTime>
#include <QVector> #include <QVector>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
// 能量刻度参数 // 能量刻度参数
struct EnergyCalibration { struct EnergyCalibration {
QDateTime generateTime; // 生成时间 QDateTime generateTime; // 生成时间
@ -109,6 +112,9 @@ public:
// 示例数据填充 // 示例数据填充
void fillSampleData(); void fillSampleData();
//读取report_params.json填充measurement时间参数
bool loadReportParamsJson(const QString &jsonPath);
}; };
#endif // SPECTRUMDATA_H #endif // SPECTRUMDATA_H

View File

@ -6,7 +6,6 @@
#include <QStyleFactory> #include <QStyleFactory>
#include "QsLogManage.h" #include "QsLogManage.h"
#include <QThreadPool> #include <QThreadPool>
void InitAppRunEnv() { void InitAppRunEnv() {
// 在应用程序可执行文件所在目录检查并创建Projects目录 // 在应用程序可执行文件所在目录检查并创建Projects目录
QString projects_dir_path = QDir(qApp->applicationDirPath()).filePath("Projects"); QString projects_dir_path = QDir(qApp->applicationDirPath()).filePath("Projects");