diff --git a/src/EnergyCountPlotView/EnergyCountPlotView.cpp b/src/EnergyCountPlotView/EnergyCountPlotView.cpp index f9fc31e..fd9bc90 100644 --- a/src/EnergyCountPlotView/EnergyCountPlotView.cpp +++ b/src/EnergyCountPlotView/EnergyCountPlotView.cpp @@ -143,6 +143,27 @@ void EnergyCountPlotView::loadDataFromFile(const QString &data_name, const QStri _plot->AddCurve(curve); } +void EnergyCountPlotView::updateView(const QMap& data_files_set) +{ + // 清除现有所有曲线 + for (QwtPlotCurve* curve : this->_plot->GetCurveList()) { + curve->detach(); + delete curve; + } + _plot->clearCurve(); + + + // 重新加载数据 + if (!data_files_set.isEmpty()) { + SetAnalyzeDataFilename(data_files_set); + } + // 清除标记并重绘 + this->_plot->CleanMarkers(); + this->_plot->CleanZoneItems(); + this->_plot->ResetPlot(); + this->_plot->replot(); +} + void EnergyCountPlotView::onActionCurveShowSetting() { if (!_curve_show_setting_dlg) { diff --git a/src/EnergyCountPlotView/EnergyCountPlotView.h b/src/EnergyCountPlotView/EnergyCountPlotView.h index fe8f4d9..c099bdf 100644 --- a/src/EnergyCountPlotView/EnergyCountPlotView.h +++ b/src/EnergyCountPlotView/EnergyCountPlotView.h @@ -20,6 +20,7 @@ public: virtual void InitViewWorkspace(const QString& project_name) override final; virtual void SetAnalyzeDataFilename(const QMap& data_files_set); + void updateView(const QMap& data_files_set); private: void setupPlot(); void setupMenu(); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 93777ed..d618431 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -34,6 +34,7 @@ #include "MeasureAnalysisDataTableView.h" #include "DataCalcProcess/GaussPolyCoe.h" #include "EnergyScaleDataModel.h" +#include "EnergyCountPlotView.h" #include "ParticleCountPlotView.h" #include #include "csv.h" @@ -104,7 +105,10 @@ MainWindow::MainWindow(QWidget* parent) _measure_client = new MeasureClient; m_AddressCountTimer = new QTimer(this); m_AddressCountTimer->setInterval(10000);//10秒刷新一次 + m_EnergyCountTimer = new QTimer(this); + m_EnergyCountTimer->setInterval(10000);//10秒刷新一次 connect(m_AddressCountTimer, &QTimer::timeout, this, &MainWindow::on_AddressCountTimer); + connect(m_EnergyCountTimer, &QTimer::timeout, this, &MainWindow::on_EnergyCountTimer); connect(_measure_client, &MeasureClient::getDeviceListResult, this, &MainWindow::onGetDeviceListResult); connect(_measure_client, &MeasureClient::startMeasureResult, this, &MainWindow::onStartMeasureResult); @@ -598,7 +602,7 @@ void MainWindow::onGetDeviceListResult(bool success, const QString &message, con void MainWindow::onErrorOccurred(const QString &error_string) { - LOG_INFO(QStringLiteral(u"错误: %1").arg(error_string)); + // LOG_INFO(QStringLiteral(u"错误: %1").arg(error_string)); } void MainWindow::onRunningInfo(const QString &run_info) @@ -619,6 +623,8 @@ void MainWindow::onGvfData(const QByteArray &data) changeEnergyCountData(particles); //处理道址计数谱 changeAddressCountView(particles); + //处理能量计数谱 + changeEnergyCountView(particles); } //处理粒子数据 @@ -690,11 +696,11 @@ void MainWindow::updataTable() MeasureAnalysisDataTableView* table = dynamic_cast(view); table->AppendRow(); };break; - case AnalysisType::EnergyCountData: - { - MeasureAnalysisDataTableView* table = dynamic_cast(view); - table->AppendRow(); - };break; + // case AnalysisType::EnergyCountData: + // { + // MeasureAnalysisDataTableView* table = dynamic_cast(view); + // table->AppendRow(); + // };break; } } } @@ -947,7 +953,216 @@ void MainWindow::changeParticleEnergyData(QList &dataList) //处理能量计数 void MainWindow::changeEnergyCountData(QList &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; + } + // 计算本次数据的增量计数 + QHash> deltaCounts; // 通道号 -> 能量值 -> 增量 + QHash deltaAllChannelCounts; // 全通道增量 + + int totalParticles = 0; + 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 coeffs = energy_scale_data_model.GetEnergyFitResultCoeffs(channel_name); + if (coeffs.empty()) { + skipCount++; + continue; // 该通道未配置能量刻度,跳过 + } + + // 道址转能量(保留1位小数,作为分组键,避免浮点数精度问题) + double energy = GaussPolyCoe::Predict(coeffs, particle.address); + double energy_key = qRound(energy * 10.0) / 10.0; // 精确到0.1KeV + + deltaCounts[channel_num][energy_key]++; + deltaAllChannelCounts[energy_key]++; + totalParticles++; + successCount++; + } + + if (deltaCounts.isEmpty()) { + if (skipCount > 0) { + LOG_DEBUG(QStringLiteral(u"本次%1个粒子全部因无能量刻度配置而跳过").arg(skipCount)); + } + return; + } + const QString& energy_count_dir = QDir(project_model->GetProjectDir()).filePath(QStringLiteral(u"通道能量计数")); + QDir energy_count_output_dir(energy_count_dir); + + if (!energy_count_output_dir.exists()) { + if (!energy_count_output_dir.mkpath(energy_count_dir)) { + LOG_ERROR(QStringLiteral(u"无法创建通道能量计数目录: %1").arg(energy_count_dir)); + return; + } + } + + QMutexLocker locker(&m_energyCountMutex); + + bool hasError = false; + QHash newChannelFiles; // 本次新增的通道文件 + QMap> all_nodes = ProjectList::Instance()->getProjectNodeItems(); + QMap project_nodes = all_nodes[project_name]; + + for (auto channelIt = deltaCounts.constBegin(); channelIt != deltaCounts.constEnd(); ++channelIt) { + uint channel_num = channelIt.key(); + const auto& energyDeltas = channelIt.value(); + + auto& channelCounts = channel_energy_counts[channel_num]; + + for (auto energyIt = energyDeltas.constBegin(); energyIt != energyDeltas.constEnd(); ++energyIt) { + double energy_key = energyIt.key(); + unsigned long long delta = energyIt.value(); + channelCounts[energy_key] += delta; + } + + QString count_data_filename; + if (energy_count_filename_list.contains(channel_num)) { + count_data_filename = energy_count_filename_list[channel_num]; + } else { + count_data_filename = energy_count_output_dir.filePath(QStringLiteral(u"通道%1能量计数.csv").arg(channel_num)); + project_model->SetChannelEnergyCountDataFilename(channel_num, count_data_filename); + energy_count_filename_list.insert(channel_num, count_data_filename); + newChannelFiles.insert(channel_num, count_data_filename); + } + + 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"能量(KeV),计数\n"); + + // 按能量值排序后输出 + QList sortedEnergies = channelCounts.keys(); + std::sort(sortedEnergies.begin(), sortedEnergies.end()); + + QString csvBuffer; + csvBuffer.reserve(sortedEnergies.size() * 40); + for (double energy : sortedEnergies) { + unsigned long long total = channelCounts[energy]; + csvBuffer += QString("%1,%2\n").arg(energy, 0, 'f', 1).arg(total); + } + + out << csvBuffer; + out.flush(); // 确保数据立即写入磁盘 + outFile.close(); + } + + if (!deltaAllChannelCounts.isEmpty()) { + // 更新内存中的全通道总计数 + for (auto energyIt = deltaAllChannelCounts.constBegin(); energyIt != deltaAllChannelCounts.constEnd(); ++energyIt) { + double bin_energy = energyIt.key(); + unsigned long long delta = energyIt.value(); + all_channel_energy_counts[bin_energy] += delta; + } + + // 全通道输出文件路径 + + QString all_channel_energy_count_filename = energy_count_output_dir.filePath(QStringLiteral(u"全通道.csv")); + + + QFile allChannelFile(all_channel_energy_count_filename); + if (allChannelFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + QTextStream outAll(&allChannelFile); + outAll.setCodec("UTF-8"); + + // 写入表头 + outAll << QStringLiteral(u"能量(KeV),计数\n"); + + // 按能量值升序排序 + QList sortedAllEnergies = all_channel_energy_counts.keys(); + std::sort(sortedAllEnergies.begin(), sortedAllEnergies.end()); + + QString allCsvBuffer; + allCsvBuffer.reserve(sortedAllEnergies.size() * 40); + for (double bin_energy : sortedAllEnergies) { + unsigned long long total = all_channel_energy_counts[bin_energy]; + allCsvBuffer += QString("%1,%2\n").arg(bin_energy, 0, 'f', 3).arg(total); + } + + outAll << allCsvBuffer; + outAll.flush(); + allChannelFile.close(); + + // 保存全通道文件路径到项目模型 + project_model->SetAllChannelEnergyTotalCountDataFilename(all_channel_energy_count_filename); + } else { + LOG_ERROR(QStringLiteral(u"无法打开全通道能量计数文件: %1,错误: %2") + .arg(all_channel_energy_count_filename) + .arg(allChannelFile.errorString())); + hasError = true; + } + } + + // 更新项目树节点状态 + if (!newChannelFiles.isEmpty() || !hasError) { + for (auto it = newChannelFiles.constBegin(); it != newChannelFiles.constEnd(); ++it) { + project_model->SetChannelEnergyCountDataFilename(it.key(), it.value()); + } + + const QString& energy_count_item_name = QStringLiteral(u"能量计数"); + if (project_nodes.contains(energy_count_item_name)) { + auto energy_count_item = project_nodes[energy_count_item_name]; + bool status_ok = !energy_count_filename_list.isEmpty(); + QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效"); + ProjectList::Instance()->SetNodeStatus(energy_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 (project_nodes.contains(item_name)) { + continue; // 节点已存在,跳过 + } + project_model->SaveProjectModel(); + const QVariant& analys_type = QVariant::fromValue(AnalysisType::EnergyCountData); + QStandardItem* node_item = ProjectList::Instance()->AddChildNode( + energy_count_item, item_name, status, analys_type, true, status_ok); + node_item->setData(project_name, Qt::UserRole + 2); + node_item->setData(ch_num, Qt::UserRole + 3); + project_nodes[item_name] = node_item; + } + } + } + + // 更新数据表格视图 + // updataTable(); + + if (hasError) { + LOG_WARN(QStringLiteral(u"部分通道的能量计数数据写入失败,请检查磁盘空间和文件权限")); + } } //处理道址计数谱 void MainWindow::changeAddressCountView(QList &dataList) @@ -955,7 +1170,6 @@ void MainWindow::changeAddressCountView(QList &dataList) if (dataList.isEmpty()) { return; } - MeasureAnalysisProjectModel* pro_model = ProjectList::Instance()->GetCurrentProjectModel(); if (!pro_model) { LOG_ERROR(QStringLiteral(u"当前没有打开的测量项目,无法处理能谱数据")); @@ -975,9 +1189,32 @@ void MainWindow::changeAddressCountView(QList &dataList) } } +void MainWindow::changeEnergyCountView(QList &dataList) +{ + if (dataList.isEmpty()) { + return; + } + MeasureAnalysisProjectModel* pro_model = ProjectList::Instance()->GetCurrentProjectModel(); + if (!pro_model) { + LOG_ERROR(QStringLiteral(u"当前没有打开的测量项目,无法处理能谱数据")); + return; + } + + bool status_ok = !pro_model->GetChannelAddressCountDataFilenameList().isEmpty(); + QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效"); + QString item_name = QStringLiteral(u"能量计数谱"); + QStandardItem * particleData = nodeMap[item_name]; + //获取能量计数谱状态 + bool bStatus = ProjectList::Instance()->GetNodeStatus(particleData); + if(!bStatus) + { + ProjectList::Instance()->SetNodeStatus(particleData,status,true); + m_EnergyCountTimer->start(); + } +} + void MainWindow::on_AddressCountTimer() { - QMap data_files_set; //获取道址计数谱 auto dockList = _dock_manager->dockWidgetsMap().values(); for(auto dock : dockList) @@ -986,6 +1223,7 @@ void MainWindow::on_AddressCountTimer() if(!view) continue; if(view->GetAnalyzeType() == AnalysisType::AddressCountSpectrumView) { + QMap data_files_set; MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetCurrentProjectModel(); if (project_model) { auto file_name_list = project_model->GetChannelAddressCountDataFilenameList(); @@ -993,7 +1231,7 @@ void MainWindow::on_AddressCountTimer() auto ch_num_list = file_name_list.keys(); for(auto ch_num : ch_num_list) { auto file_name = file_name_list[ch_num]; - if ( !file_name.isEmpty() ) { + if ( !file_name.isEmpty()) { data_files_set[QStringLiteral(u"通道%1").arg(ch_num)] = file_name; } } @@ -1004,6 +1242,40 @@ void MainWindow::on_AddressCountTimer() } } +void MainWindow::on_EnergyCountTimer() +{ + //获取道址计数谱 + auto dockList = _dock_manager->dockWidgetsMap().values(); + for(auto dock : dockList) + { + EnergyCountPlotView* view = dynamic_cast(dock->widget()); + if(!view) continue; + + if(view->GetAnalyzeType() == AnalysisType::EnergyCountSpectrumView) + { + QMap data_files_set; + MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetCurrentProjectModel(); + if (project_model) { + auto all_ch_energy_count_file_name = project_model->GetAllChannelEnergyTotalCountDataFilename(); + if (!all_ch_energy_count_file_name.isEmpty()) { + data_files_set[QStringLiteral(u"全通道")] = all_ch_energy_count_file_name; + } + auto file_name_list = project_model->GetChannelEnergyCountDataFilenameList(); + if ( !file_name_list.isEmpty() ) { + auto ch_num_list = file_name_list.keys(); + for(auto ch_num : ch_num_list) { + auto file_name = file_name_list[ch_num]; + if ( !file_name.isEmpty()) { + data_files_set[QStringLiteral(u"通道%1").arg(ch_num)] = file_name; + } + } + } + } + view->updateView(data_files_set); + } + } +} + void MainWindow::on_action_stop_measure_triggered() { const QString& device_guid = deviceList.at(0); diff --git a/src/MainWindow.h b/src/MainWindow.h index 8c095e6..e2cdc40 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -68,6 +68,8 @@ private: void changeEnergyCountData(QList &dataList); //处理道址计数谱 void changeAddressCountView(QList &dataList); + //处理能量计数谱 + void changeEnergyCountView(QList &dataList); signals: void newProject(const QString &project_name); @@ -96,6 +98,8 @@ private slots: void on_action_stop_measure_triggered(); void on_AddressCountTimer(); + + void on_EnergyCountTimer(); private: QMutex _mutex_info_output; QPlainTextEdit* _plain_edit_info_output; @@ -125,13 +129,16 @@ private: QMutex m_energyDataMutex;//能量谱锁 //能量计数 - QHash m_channelEnergyCountFiles; // 通道号 -> 能量计数文件路径 - QHash> m_channelEnergyStats; // 通道号 -> 能量bin -> 计数 - QMap m_allChannelEnergyStats; // 全通道能量bin -> 计数 + QHash energy_count_filename_list; // 通道号 -> 能量计数文件路径 + QHash> channel_energy_counts; // 通道号 -> 能量bin -> 计数 + QMap all_channel_energy_counts; // 全通道能量bin -> 计数 QMutex m_energyCountMutex; // 保护能量计数数据的互斥锁 QHash> m_energyScaleCoeffCache;// 能量刻度系数缓存 //道址计数视图定时器 QTimer* m_AddressCountTimer; + //能量计数视图定时器 + QTimer* m_EnergyCountTimer; + }; #endif // MAINWINDOW_H