修改道址计数问题,新增能谱数据处理
This commit is contained in:
parent
02cb0839b9
commit
9a8a102dcf
|
|
@ -20,7 +20,6 @@
|
||||||
#include "DataProcessWorkPool.h"
|
#include "DataProcessWorkPool.h"
|
||||||
#include "EnergyScaleForm.h"
|
#include "EnergyScaleForm.h"
|
||||||
#include "MeasureAnalysisHistoryForm.h"
|
#include "MeasureAnalysisHistoryForm.h"
|
||||||
#include "MeasureAnalysisProjectModel.h"
|
|
||||||
#include "NewMeasureAnalysisDlg.h"
|
#include "NewMeasureAnalysisDlg.h"
|
||||||
#include "MeasureAnalysisView.h"
|
#include "MeasureAnalysisView.h"
|
||||||
#include "MeasureAnalysisTreeView.h"
|
#include "MeasureAnalysisTreeView.h"
|
||||||
|
|
@ -33,6 +32,8 @@
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include "MeasureClient.h"
|
#include "MeasureClient.h"
|
||||||
#include "MeasureAnalysisDataTableView.h"
|
#include "MeasureAnalysisDataTableView.h"
|
||||||
|
#include "DataCalcProcess/GaussPolyCoe.h"
|
||||||
|
#include "EnergyScaleDataModel.h"
|
||||||
#include "csv.h"
|
#include "csv.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
|
@ -388,6 +389,7 @@ void MainWindow::closeProject(const QString& project_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::showEvent(QShowEvent *event)
|
void MainWindow::showEvent(QShowEvent *event)
|
||||||
{
|
{
|
||||||
QMainWindow::showEvent(event);
|
QMainWindow::showEvent(event);
|
||||||
|
|
@ -468,8 +470,8 @@ void MainWindow::on_action_start_measure_triggered()
|
||||||
}
|
}
|
||||||
MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel();
|
MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel();
|
||||||
QMap<QString, QMap<QString, QStandardItem *> > project_node_items = ProjectList::Instance()->getProjectNodeItems();
|
QMap<QString, QMap<QString, QStandardItem *> > project_node_items = ProjectList::Instance()->getProjectNodeItems();
|
||||||
QMap<QString, QStandardItem *> node = project_node_items[models->GetProjectName()];
|
nodeMap = project_node_items[models->GetProjectName()];
|
||||||
QStandardItem *nodeItem = node[models->GetProjectName()];
|
QStandardItem *nodeItem = nodeMap[models->GetProjectName()];
|
||||||
ProjectList::Instance()->SetNodeStatus(nodeItem,"测量中",true);
|
ProjectList::Instance()->SetNodeStatus(nodeItem,"测量中",true);
|
||||||
|
|
||||||
QString dir = ProjectList::Instance()->GetCurrentProjectModel()->GetProjectDir();
|
QString dir = ProjectList::Instance()->GetCurrentProjectModel()->GetProjectDir();
|
||||||
|
|
@ -479,7 +481,7 @@ void MainWindow::on_action_start_measure_triggered()
|
||||||
QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效");
|
QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效");
|
||||||
QVariant analys_type = QVariant::fromValue(AnalysisType::ParticleData);
|
QVariant analys_type = QVariant::fromValue(AnalysisType::ParticleData);
|
||||||
QString item_name = QStringLiteral(u"测量粒子数据");
|
QString item_name = QStringLiteral(u"测量粒子数据");
|
||||||
QStandardItem * particleData = node[item_name];
|
QStandardItem * particleData = nodeMap[item_name];
|
||||||
ProjectList::Instance()->SetNodeStatus(particleData,status,true);
|
ProjectList::Instance()->SetNodeStatus(particleData,status,true);
|
||||||
//创建通道道址计数文件夹
|
//创建通道道址计数文件夹
|
||||||
const QString& all_channel_particle_data_filename = models->GetAllChannelParticleDataFilename();
|
const QString& all_channel_particle_data_filename = models->GetAllChannelParticleDataFilename();
|
||||||
|
|
@ -601,7 +603,20 @@ void MainWindow::onRunningInfo(const QString &run_info)
|
||||||
void MainWindow::onGvfData(const QByteArray &data)
|
void MainWindow::onGvfData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
QList<ParticleData> particles = _gvfToCsv->parseParticleFrames(data);
|
QList<ParticleData> particles = _gvfToCsv->parseParticleFrames(data);
|
||||||
if (particles.isEmpty()) {
|
//处理粒子数据
|
||||||
|
changeParticleData(particles);
|
||||||
|
//处理道址计数
|
||||||
|
changeChannelParticleCount(particles);
|
||||||
|
//处理能谱数据
|
||||||
|
changeParticleEnergyData(particles);
|
||||||
|
//处理能量计数
|
||||||
|
changeEnergyCountData(particles);
|
||||||
|
}
|
||||||
|
|
||||||
|
//处理粒子数据
|
||||||
|
void MainWindow::changeParticleData(QList<ParticleData> &dataList)
|
||||||
|
{
|
||||||
|
if (dataList.isEmpty()) {
|
||||||
LOG_INFO(QStringLiteral(u"本次GVF数据未解析到有效粒子,跳过写入CSV"));
|
LOG_INFO(QStringLiteral(u"本次GVF数据未解析到有效粒子,跳过写入CSV"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -625,9 +640,9 @@ void MainWindow::onGvfData(const QByteArray &data)
|
||||||
}
|
}
|
||||||
|
|
||||||
QString csvBuffer;
|
QString csvBuffer;
|
||||||
csvBuffer.reserve(particles.size() * 64); // 单条数据约64字节,预分配足够内存
|
csvBuffer.reserve(dataList.size() * 64); // 单条数据约64字节,预分配足够内存
|
||||||
|
|
||||||
for (const auto &p : particles) {
|
for (const auto &p : dataList) {
|
||||||
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)
|
||||||
|
|
@ -638,126 +653,283 @@ void MainWindow::onGvfData(const QByteArray &data)
|
||||||
out << csvBuffer;
|
out << csvBuffer;
|
||||||
out.flush(); // 确保数据立即写入磁盘,避免程序崩溃丢失数据
|
out.flush(); // 确保数据立即写入磁盘,避免程序崩溃丢失数据
|
||||||
outFile.close();
|
outFile.close();
|
||||||
//处理粒子数据
|
updataTable();
|
||||||
changeUpdata(particles);
|
|
||||||
//处理道址计数
|
|
||||||
changeChannelParticleCount(particles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::changeUpdata(QList<ParticleData> &data)
|
void MainWindow::updataTable()
|
||||||
{
|
{
|
||||||
QString dir = ProjectList::Instance()->GetCurrentProjectModel()->GetProjectDir();
|
|
||||||
QString csvPath = QStringLiteral(u"%1/%2").arg(dir).arg("粒子数据.csv");
|
|
||||||
auto dockList = _dock_manager->dockWidgetsMap().values();
|
auto dockList = _dock_manager->dockWidgetsMap().values();
|
||||||
int i = 0;
|
|
||||||
for(auto dock : dockList)
|
for(auto dock : dockList)
|
||||||
{
|
{
|
||||||
MeasureAnalysisView* view = dynamic_cast<MeasureAnalysisView*>(dock->widget());
|
MeasureAnalysisView* view = dynamic_cast<MeasureAnalysisView*>(dock->widget());
|
||||||
if(!view) continue;
|
if(!view) continue;
|
||||||
if(view->GetAnalyzeType() == AnalysisType::ParticleData
|
if(view->GetViewType() == MeasureAnalysisView::DataTable)
|
||||||
&& view->GetViewType() == MeasureAnalysisView::DataTable)
|
{
|
||||||
|
switch(view->GetAnalyzeType())
|
||||||
|
{
|
||||||
|
case AnalysisType::ParticleData:{
|
||||||
|
MeasureAnalysisDataTableView* table = dynamic_cast<MeasureAnalysisDataTableView*>(view);
|
||||||
|
table->AppendRow();
|
||||||
|
};break;
|
||||||
|
// case AnalysisType::AddressCountData:
|
||||||
|
// {
|
||||||
|
// MeasureAnalysisDataTableView* table = dynamic_cast<MeasureAnalysisDataTableView*>(view);
|
||||||
|
// table->AppendRow();
|
||||||
|
// };break;
|
||||||
|
case AnalysisType::ParticleEnergyData:
|
||||||
{
|
{
|
||||||
MeasureAnalysisDataTableView* table = dynamic_cast<MeasureAnalysisDataTableView*>(view);
|
MeasureAnalysisDataTableView* table = dynamic_cast<MeasureAnalysisDataTableView*>(view);
|
||||||
QString boardId = QString::number(data.at(i).boardId);
|
table->AppendRow();
|
||||||
QString channelId = QString::number(data.at(i).channelId);
|
};break;
|
||||||
QString address = QString::number(data.at(i).address);
|
case AnalysisType::EnergyCountData:
|
||||||
QString timestampCount = QString::number(data.at(i).timestampCount);
|
{
|
||||||
QStringList dataList;
|
MeasureAnalysisDataTableView* table = dynamic_cast<MeasureAnalysisDataTableView*>(view);
|
||||||
dataList << boardId << channelId << address << timestampCount;
|
table->AppendRow();
|
||||||
|
};break;
|
||||||
// dataList << QString("%1,%2,%3,%4").arg(boardId).arg(channelId).arg(address).arg(timestampCount);
|
}
|
||||||
table->AppendRow(dataList,false);
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::changeChannelParticleCount(QList<ParticleData> &data)
|
void MainWindow::changeChannelParticleCount(QList<ParticleData> &data)
|
||||||
{
|
{
|
||||||
bool ret_ok = true;
|
if (data.isEmpty()) {
|
||||||
|
return;
|
||||||
// 通道号 -> 地址 -> 计数
|
|
||||||
for(auto info : data)
|
|
||||||
{
|
|
||||||
// 板卡和通道号计算,通道号 = 板卡号 * 4 + 通道号
|
|
||||||
int channel_num = (info.boardId) * 4 + (info.channelId + 1);
|
|
||||||
// 统计每个通道的粒子计数
|
|
||||||
if (!channel_address_counts.contains(channel_num)) {
|
|
||||||
channel_address_counts[channel_num] = QMap<uint, unsigned long long>();
|
|
||||||
}
|
}
|
||||||
channel_address_counts[channel_num][info.address]++;
|
|
||||||
|
QHash<uint, QHash<uint, unsigned long long>> deltaCounts; // 通道号 -> 道址 -> 增量
|
||||||
|
int totalParticles = 0;
|
||||||
|
|
||||||
|
// 计算本次数据的增量计数
|
||||||
|
for (const auto &info : data) {
|
||||||
|
int channel_num = (info.boardId) * 4 + (info.channelId + 1);
|
||||||
|
deltaCounts[channel_num][info.address]++;
|
||||||
|
totalParticles++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deltaCounts.isEmpty()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel();
|
MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel();
|
||||||
|
if (!models) {
|
||||||
|
LOG_ERROR(QStringLiteral(u"当前没有打开的测量项目,无法写入道址计数数据"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& projectName = models->GetProjectName();
|
||||||
const QString& every_ch_count_dir = QDir(models->GetProjectDir()).filePath(QStringLiteral(u"通道道址计数"));
|
const QString& every_ch_count_dir = QDir(models->GetProjectDir()).filePath(QStringLiteral(u"通道道址计数"));
|
||||||
QDir every_ch_count_output_dir(every_ch_count_dir);
|
QDir every_ch_count_output_dir(every_ch_count_dir);
|
||||||
|
|
||||||
// 写入每个通道的粒子计数数据(优化:使用一次打开文件,批量写入)
|
if (!every_ch_count_output_dir.exists()) {
|
||||||
QMap<uint, std::shared_ptr<std::ofstream>> channel_file_streams;
|
if (!every_ch_count_output_dir.mkpath(every_ch_count_dir)) {
|
||||||
// 预创建所有通道的文件流
|
LOG_ERROR(QStringLiteral(u"无法创建通道道址计数目录: %1").arg(every_ch_count_dir));
|
||||||
for (auto channel_it = channel_address_counts.begin(); channel_it != channel_address_counts.end(); ++channel_it) {
|
|
||||||
uint channel_num = channel_it.key();
|
|
||||||
QString count_data_filename = every_ch_count_output_dir.filePath(QStringLiteral(u"通道%1粒子计数.csv").arg(channel_num));
|
|
||||||
particle_count_filename_list.insert(channel_num, count_data_filename);
|
|
||||||
// 创建文件流
|
|
||||||
std::shared_ptr<std::ofstream> out(new std::ofstream(QStrToSysPath(count_data_filename)));
|
|
||||||
channel_file_streams[channel_num] = out;
|
|
||||||
*out << QString(QStringLiteral(u"道址")).toStdString() << "," << QString(QStringLiteral(u"计数")).toStdString() << std::endl;
|
|
||||||
}
|
|
||||||
// 批量写入数据
|
|
||||||
for (auto channel_it = channel_address_counts.begin(); channel_it != channel_address_counts.end(); ++channel_it) {
|
|
||||||
uint channel_num = channel_it.key();
|
|
||||||
const QMap<uint, unsigned long long>& address_counts = channel_it.value();
|
|
||||||
auto out_stream = channel_file_streams[channel_num];
|
|
||||||
for (auto address_it = address_counts.begin(); address_it != address_counts.end(); ++address_it) {
|
|
||||||
uint address = address_it.key();
|
|
||||||
unsigned long long count = address_it.value();
|
|
||||||
*out_stream << address << "," << count << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
channel_file_streams.clear();
|
|
||||||
const QString& project_name = models->GetProjectName();
|
|
||||||
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
|
|
||||||
if (project_model == nullptr) {
|
|
||||||
ret_ok = false;
|
|
||||||
} else {
|
|
||||||
// 更新项目模型中的通道粒子计数数据文件名
|
|
||||||
for (auto it = particle_count_filename_list.begin(); it != particle_count_filename_list.end(); ++it) {
|
|
||||||
project_model->SetChannelAddressCountDataFilename(it.key(), it.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
QMap<QString, QMap<QString, QStandardItem *> > project_node_items = ProjectList::Instance()->getProjectNodeItems();
|
|
||||||
QMap<QString, QStandardItem *> node_map = project_node_items[models->GetProjectName()];
|
|
||||||
|
|
||||||
const QString& adrr_count_item_name = QStringLiteral(u"道址计数");
|
|
||||||
const QMap<uint, QString>& filename_list = models->GetChannelAddressCountDataFilenameList();
|
|
||||||
bool status_ok = false;
|
|
||||||
QString status = QStringLiteral(u"无效");
|
|
||||||
if (!filename_list.isEmpty()) {
|
|
||||||
status_ok = true;
|
|
||||||
status = QStringLiteral(u"有效");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node_map.contains(adrr_count_item_name)) {
|
|
||||||
auto adrr_count_item = node_map[adrr_count_item_name];
|
|
||||||
ProjectList::Instance()->SetNodeStatus(adrr_count_item, status, status_ok);
|
|
||||||
for (auto it = filename_list.begin(); it != filename_list.end(); ++it) {
|
|
||||||
uint ch_num = it.key();
|
|
||||||
QString item_name = QStringLiteral(u"通道%1道址计数").arg(ch_num);
|
|
||||||
if(node_map.contains(item_name))
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_channelCountMutex);
|
||||||
|
|
||||||
|
bool hasError = false;
|
||||||
|
QHash<uint, QString> newChannelFiles; // 本次新增的通道文件
|
||||||
|
|
||||||
|
// 处理每个通道的计数更新
|
||||||
|
for (auto channelIt = deltaCounts.constBegin(); channelIt != deltaCounts.constEnd(); ++channelIt) {
|
||||||
|
uint channel_num = channelIt.key();
|
||||||
|
const auto& addressDeltas = channelIt.value();
|
||||||
|
|
||||||
|
QString count_data_filename;
|
||||||
|
if (particle_count_filename_list.contains(channel_num)) {
|
||||||
|
count_data_filename = particle_count_filename_list[channel_num];
|
||||||
|
} else {
|
||||||
|
count_data_filename = every_ch_count_output_dir.filePath(QStringLiteral(u"通道%1粒子计数.csv").arg(channel_num));
|
||||||
|
models->SetChannelAddressCountDataFilename(channel_num, count_data_filename);
|
||||||
|
particle_count_filename_list.insert(channel_num, count_data_filename);
|
||||||
|
newChannelFiles.insert(channel_num, count_data_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto addressIt = addressDeltas.constBegin(); addressIt != addressDeltas.constEnd(); ++addressIt) {
|
||||||
|
uint address = addressIt.key();
|
||||||
|
unsigned long long delta = addressIt.value();
|
||||||
|
channel_address_counts[channel_num][address] += delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile outFile(count_data_filename);
|
||||||
|
if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||||
|
LOG_ERROR(QStringLiteral(u"无法打开通道%1的计数文件: %2,错误: %3")
|
||||||
|
.arg(channel_num)
|
||||||
|
.arg(count_data_filename)
|
||||||
|
.arg(outFile.errorString()));
|
||||||
|
hasError = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream out(&outFile);
|
||||||
|
out.setCodec("UTF-8");
|
||||||
|
|
||||||
|
// 写入表头
|
||||||
|
out << QStringLiteral(u"道址,计数\n");
|
||||||
|
|
||||||
|
// 写入所有道址的最新总计数(按道址排序)
|
||||||
|
QString csvBuffer;
|
||||||
|
csvBuffer.reserve(channel_address_counts[channel_num].size() * 32); // 预分配内存
|
||||||
|
|
||||||
|
// 获取排序后的道址列表
|
||||||
|
QList<uint> sortedAddresses = channel_address_counts[channel_num].keys();
|
||||||
|
std::sort(sortedAddresses.begin(), sortedAddresses.end());
|
||||||
|
|
||||||
|
for (uint address : sortedAddresses) {
|
||||||
|
unsigned long long total = channel_address_counts[channel_num][address];
|
||||||
|
csvBuffer += QString("%1,%2\n").arg(address).arg(total);
|
||||||
|
}
|
||||||
|
|
||||||
|
out << csvBuffer;
|
||||||
|
out.flush(); // 确保数据立即写入磁盘
|
||||||
|
outFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新项目树节点状态
|
||||||
|
if (!newChannelFiles.isEmpty() || !hasError) {
|
||||||
|
for (auto it = newChannelFiles.constBegin(); it != newChannelFiles.constEnd(); ++it) {
|
||||||
|
models->SetChannelAddressCountDataFilename(it.key(), it.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& adrr_count_item_name = QStringLiteral(u"道址计数");
|
||||||
|
if (nodeMap.contains(adrr_count_item_name)) {
|
||||||
|
auto adrr_count_item = nodeMap[adrr_count_item_name];
|
||||||
|
bool status_ok = !particle_count_filename_list.isEmpty();
|
||||||
|
QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效");
|
||||||
|
ProjectList::Instance()->SetNodeStatus(adrr_count_item, status, status_ok);
|
||||||
|
|
||||||
|
for (auto it = newChannelFiles.constBegin(); it != newChannelFiles.constEnd(); ++it) {
|
||||||
|
uint ch_num = it.key();
|
||||||
|
QString item_name = QStringLiteral(u"通道%1道址计数").arg(ch_num);
|
||||||
|
if (nodeMap.contains(item_name)) {
|
||||||
|
continue; // 节点已存在,跳过
|
||||||
|
}
|
||||||
|
models->SaveProjectModel();
|
||||||
const QVariant& analys_type = QVariant::fromValue(AnalysisType::AddressCountData);
|
const QVariant& analys_type = QVariant::fromValue(AnalysisType::AddressCountData);
|
||||||
QStandardItem* node_item = ProjectList::Instance()->AddChildNode(adrr_count_item, item_name, status, analys_type, true, status_ok);
|
QStandardItem* node_item = ProjectList::Instance()->AddChildNode(
|
||||||
node_item->setData(project_name, Qt::UserRole + 2);
|
adrr_count_item, item_name, status, analys_type, true, status_ok);
|
||||||
|
node_item->setData(projectName, Qt::UserRole + 2);
|
||||||
node_item->setData(ch_num, Qt::UserRole + 3);
|
node_item->setData(ch_num, Qt::UserRole + 3);
|
||||||
node_map[item_name] = node_item;
|
nodeMap[item_name] = node_item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updataTable();
|
||||||
|
|
||||||
|
if (hasError) {
|
||||||
|
LOG_WARN(QStringLiteral(u"部分通道的道址计数数据写入失败,请检查磁盘空间和文件权限"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//处理粒子能量数据
|
||||||
|
void MainWindow::changeParticleEnergyData(QList<ParticleData> &dataList)
|
||||||
|
{
|
||||||
|
if (dataList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetCurrentProjectModel();
|
||||||
|
if (!project_model) {
|
||||||
|
LOG_ERROR(QStringLiteral(u"当前没有打开的测量项目,无法处理能谱数据"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& project_name = project_model->GetProjectName();
|
||||||
|
|
||||||
|
EnergyScaleDataModel energy_scale_data_model(project_model->GetEnergyScaleFilename());
|
||||||
|
if (!energy_scale_data_model.LoadData()) {
|
||||||
|
LOG_WARN(QStringLiteral(u"[%1]加载能量刻度文件失败,跳过本次能谱处理").arg(project_name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!energy_scale_data_model.IsValid()) {
|
||||||
|
LOG_WARN(QStringLiteral(u"[%1]能量刻度数据无效,请检查刻度配置").arg(project_name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_energyDataMutex);
|
||||||
|
|
||||||
|
QString energy_spectrum_filename = QDir(project_model->GetProjectDir()).filePath(QStringLiteral(u"能谱数据.csv"));
|
||||||
|
|
||||||
|
QFile outFile(energy_spectrum_filename);
|
||||||
|
if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
|
||||||
|
LOG_ERROR(QStringLiteral(u"[%1]无法打开能谱文件: %2,错误: %3")
|
||||||
|
.arg(project_name)
|
||||||
|
.arg(energy_spectrum_filename)
|
||||||
|
.arg(outFile.errorString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream out(&outFile);
|
||||||
|
out.setCodec("UTF-8");
|
||||||
|
|
||||||
|
if ( outFile.size() == 0) {
|
||||||
|
out << QStringLiteral(u"板卡号,通道号,道址,能量(KeV),时间计数\n");
|
||||||
|
LOG_DEBUG(QStringLiteral(u"[%1]能谱文件为空,已写入标准表头").arg(project_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString csvBuffer;
|
||||||
|
csvBuffer.reserve(dataList.size() * 80); // 单条数据约80字节,预分配足够内存
|
||||||
|
|
||||||
|
int totalProcessed = dataList.size();
|
||||||
|
int successCount = 0;
|
||||||
|
int skipCount = 0;
|
||||||
|
|
||||||
|
for (const auto& particle : dataList) {
|
||||||
|
int channel_num = (particle.boardId) * 4 + (particle.channelId + 1);
|
||||||
|
const QString& channel_name = QStringLiteral(u"通道%1").arg(channel_num);
|
||||||
|
|
||||||
|
std::vector<double> coeffs = energy_scale_data_model.GetEnergyFitResultCoeffs(channel_name);
|
||||||
|
if (coeffs.empty()) {
|
||||||
|
skipCount++;
|
||||||
|
continue; // 该通道未配置能量刻度,跳过
|
||||||
|
}
|
||||||
|
|
||||||
|
double energy = GaussPolyCoe::Predict(coeffs, particle.address);
|
||||||
|
|
||||||
|
csvBuffer += QString("%1,%2,%3,%4,%5\n")
|
||||||
|
.arg(particle.boardId)
|
||||||
|
.arg(particle.channelId)
|
||||||
|
.arg(particle.address)
|
||||||
|
.arg(energy, 0, 'f', 4)
|
||||||
|
.arg(particle.timestampCount);
|
||||||
|
|
||||||
|
successCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!csvBuffer.isEmpty()) {
|
||||||
|
out << csvBuffer;
|
||||||
|
out.flush(); // 强制写入磁盘,防止程序崩溃丢失数据
|
||||||
|
}
|
||||||
|
|
||||||
|
outFile.close();
|
||||||
|
|
||||||
|
if (successCount > 0) {
|
||||||
|
project_model->SetParticleEnergyDataFilename(energy_spectrum_filename);
|
||||||
|
|
||||||
|
if (!project_model->SaveProjectModel()) {
|
||||||
|
LOG_WARN(QStringLiteral(u"[%1]保存项目模型失败,能谱文件路径未持久化").arg(project_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& energy_node_name = QStringLiteral(u"粒子能量数据");
|
||||||
|
QMap<QString, QMap<QString, QStandardItem *>> all_nodes = ProjectList::Instance()->getProjectNodeItems();
|
||||||
|
QMap<QString, QStandardItem *> project_nodes = all_nodes[project_name];
|
||||||
|
|
||||||
|
if (project_nodes.contains(energy_node_name)) {
|
||||||
|
QStandardItem* energy_node = project_nodes[energy_node_name];
|
||||||
|
ProjectList::Instance()->SetNodeStatus(energy_node, QStringLiteral(u"有效"), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updataTable();
|
||||||
|
}
|
||||||
|
//处理能量计数
|
||||||
|
void MainWindow::changeEnergyCountData(QList<ParticleData> &dataList)
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include "GvfToCsv/GvfToCsv.h"
|
#include "GvfToCsv/GvfToCsv.h"
|
||||||
|
#include "MeasureAnalysisProjectModel.h"
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
@ -52,10 +53,19 @@ private:
|
||||||
void initStatusBar();
|
void initStatusBar();
|
||||||
void applyStyleSheet();
|
void applyStyleSheet();
|
||||||
void closeProject(const QString &project_name);
|
void closeProject(const QString &project_name);
|
||||||
|
|
||||||
|
//更新表格
|
||||||
|
void updataTable();
|
||||||
|
|
||||||
//处理粒子数据
|
//处理粒子数据
|
||||||
void changeUpdata(QList<ParticleData> &data);
|
void changeParticleData(QList<ParticleData> &dataList);
|
||||||
//处理道址计数
|
//处理道址计数
|
||||||
void changeChannelParticleCount(QList<ParticleData> &data);
|
void changeChannelParticleCount(QList<ParticleData> &data);
|
||||||
|
//处理粒子能量数据
|
||||||
|
void changeParticleEnergyData(QList<ParticleData> &dataList);
|
||||||
|
//处理能量计数
|
||||||
|
void changeEnergyCountData(QList<ParticleData> &dataList);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void newProject(const QString &project_name);
|
void newProject(const QString &project_name);
|
||||||
|
|
||||||
|
|
@ -107,8 +117,16 @@ private:
|
||||||
|
|
||||||
QMap<uint, QMap<uint, unsigned long long>> channel_address_counts; // 通道号 -> 地址 -> 计数
|
QMap<uint, QMap<uint, unsigned long long>> channel_address_counts; // 通道号 -> 地址 -> 计数
|
||||||
QMap<uint, QString> particle_count_filename_list;
|
QMap<uint, QString> particle_count_filename_list;
|
||||||
|
//2026-06-11
|
||||||
|
QMap<QString, QStandardItem *> nodeMap;
|
||||||
|
QMutex m_channelCountMutex;//
|
||||||
|
QMutex m_energyDataMutex;//能量谱锁
|
||||||
|
|
||||||
|
//能量计数
|
||||||
|
QHash<uint, QString> m_channelEnergyCountFiles; // 通道号 -> 能量计数文件路径
|
||||||
|
QHash<uint, QMap<double, unsigned long long>> m_channelEnergyStats; // 通道号 -> 能量bin -> 计数
|
||||||
|
QMap<double, unsigned long long> m_allChannelEnergyStats; // 全通道能量bin -> 计数
|
||||||
|
QMutex m_energyCountMutex; // 保护能量计数数据的互斥锁
|
||||||
|
QHash<QString, std::vector<double>> m_energyScaleCoeffCache;// 能量刻度系数缓存
|
||||||
};
|
};
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|
|
||||||
|
|
@ -95,64 +95,21 @@ void MeasureAnalysisDataTableView::RefreshTableData(const QString &csvFilePath)
|
||||||
_tableView->setBufferSize(_buffer_size);
|
_tableView->setBufferSize(_buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeasureAnalysisDataTableView::AppendRow(const QVariantList &rowData, bool writeToFile)
|
void MeasureAnalysisDataTableView::AppendRow(/*const QVariantList &rowData, bool writeToFile*/)
|
||||||
{
|
{
|
||||||
// 1. 前置校验
|
|
||||||
if (!_tableModel || !_tableModel->dataSource()) {
|
|
||||||
LOG_WARN(QStringLiteral(u"追加行失败:表格模型或数据源未初始化"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dataSource = std::dynamic_pointer_cast<CsvDataSource>(_tableModel->dataSource());
|
auto dataSource = std::dynamic_pointer_cast<CsvDataSource>(_tableModel->dataSource());
|
||||||
if (!dataSource || !dataSource->isValid()) {
|
|
||||||
LOG_WARN(QStringLiteral(u"追加行失败:CSV数据源无效"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 列数匹配
|
|
||||||
const int expectedColumns = _tableModel->columnCount();
|
|
||||||
if (rowData.size() != expectedColumns) {
|
|
||||||
LOG_WARN(QStringLiteral(u"追加行失败:列数不匹配,期望%1列,实际%2列")
|
|
||||||
.arg(expectedColumns).arg(rowData.size()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//需要写入文件的
|
|
||||||
if (writeToFile) {
|
|
||||||
QFile file(dataSource->filePath());
|
|
||||||
QTextStream out(&file);
|
|
||||||
out.setCodec("UTF-8"); // 与读取时的 QString::fromUtf8 保持一致
|
|
||||||
|
|
||||||
QStringList escapedFields;
|
|
||||||
for (const QVariant& field : rowData) {
|
|
||||||
escapedFields.append(escapeCsvField(field.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
out << escapedFields.join(',') << "\n";
|
|
||||||
file.close();
|
|
||||||
LOG_DEBUG(QStringLiteral(u"已成功将新行写入CSV文件:%1").arg(dataSource->filePath()));
|
|
||||||
RefreshTableData(dataSource->filePath());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile file(dataSource->filePath());
|
|
||||||
if (!file.open(QIODevice::Append | QIODevice::Text)) {
|
|
||||||
LOG_ERROR(QStringLiteral(u"追加行失败:无法打开文件 %1,错误:%2")
|
|
||||||
.arg(file.fileName()).arg(file.errorString()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 重新加载整个表格,确保模型与文件同步
|
|
||||||
RefreshTableData(dataSource->filePath());
|
RefreshTableData(dataSource->filePath());
|
||||||
|
|
||||||
// 5. 自动滚动到底部
|
|
||||||
_tableView->scrollToBottom();
|
_tableView->scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeasureAnalysisDataTableView::AppendRow(const QStringList &rowData, bool writeToFile)
|
// void MeasureAnalysisDataTableView::AppendRow(/*const QStringList &rowData, bool writeToFile*/)
|
||||||
{
|
// {
|
||||||
QVariantList varList;
|
// // QVariantList varList;
|
||||||
for (const QString& field : rowData) {
|
// // for (const QString& field : rowData) {
|
||||||
varList.append(QVariant(field));
|
// // varList.append(QVariant(field));
|
||||||
}
|
// // }
|
||||||
AppendRow(varList, writeToFile);
|
// AppendRow(varList, writeToFile);
|
||||||
}
|
// }
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ public:
|
||||||
|
|
||||||
|
|
||||||
// 2026-06-10 表尾插入数据函数(支持两种参数类型,默认同时写入CSV文件)
|
// 2026-06-10 表尾插入数据函数(支持两种参数类型,默认同时写入CSV文件)
|
||||||
void AppendRow(const QVariantList& rowData, bool writeToFile = true);
|
void AppendRow(/*const QVariantList& rowData, bool writeToFile = true*/);
|
||||||
void AppendRow(const QStringList& rowData, bool writeToFile = true);
|
// void AppendRow(const QStringList& rowData, bool writeToFile = true);
|
||||||
private:
|
private:
|
||||||
// 私有成员变量
|
// 私有成员变量
|
||||||
VirtualTableView *_tableView;
|
VirtualTableView *_tableView;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user