2026-03-02 11:07:51 +08:00
|
|
|
|
#include "DataProcessWorkPool.h"
|
|
|
|
|
|
#include "MeasureAnalysisProjectModel.h"
|
2026-03-03 18:15:34 +08:00
|
|
|
|
#include "GlobalDefine.h"
|
2026-03-02 21:39:41 +08:00
|
|
|
|
#include "csv.h"
|
2026-03-03 18:15:34 +08:00
|
|
|
|
#include <QDir>
|
|
|
|
|
|
#include <QThreadPool>
|
2026-03-02 21:39:41 +08:00
|
|
|
|
#include <algorithm>
|
2026-03-03 18:15:34 +08:00
|
|
|
|
#include <fstream>
|
2026-03-02 21:39:41 +08:00
|
|
|
|
#include <memory>
|
2026-03-03 18:15:34 +08:00
|
|
|
|
#include <queue>
|
2026-03-02 21:39:41 +08:00
|
|
|
|
#include <sstream>
|
2026-03-03 18:15:34 +08:00
|
|
|
|
#include <string>
|
|
|
|
|
|
#include <vector>
|
2026-03-11 13:59:46 +08:00
|
|
|
|
#include <QVariant>
|
|
|
|
|
|
#include <QFuture>
|
|
|
|
|
|
#include <QtConcurrent>
|
2026-03-18 19:31:24 +08:00
|
|
|
|
#include "DataCalcProcess/MathModelDefine.h"
|
2026-03-11 13:59:46 +08:00
|
|
|
|
#include "DataCalcProcess/FindPeaksBySvd.h"
|
2026-03-18 19:31:24 +08:00
|
|
|
|
#include "DataCalcProcess/GaussPolyCoe.h"
|
|
|
|
|
|
#include "DataCalcProcess/NolinearLeastSquaresCurveFit.h"
|
2026-03-19 18:43:03 +08:00
|
|
|
|
#include "EnergyScaleDataModel.h"
|
2026-03-17 10:50:33 +08:00
|
|
|
|
#include <QDebug>
|
2026-03-02 11:07:51 +08:00
|
|
|
|
|
|
|
|
|
|
using namespace DataProcessWorkPool;
|
2026-03-02 18:56:21 +08:00
|
|
|
|
using namespace io;
|
2026-03-02 11:07:51 +08:00
|
|
|
|
|
2026-03-09 21:57:26 +08:00
|
|
|
|
|
|
|
|
|
|
void DataProcessTask::SetFinishedNotifier(QObject* finished_notifier, const char* finished_process, const QString& project_name)
|
2026-03-02 11:07:51 +08:00
|
|
|
|
{
|
|
|
|
|
|
this->_finished_notifier = finished_notifier;
|
|
|
|
|
|
this->_finished_notifier_process = finished_process;
|
|
|
|
|
|
this->_project_name = project_name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:57:26 +08:00
|
|
|
|
const QString& DataProcessTask::GetProjectName() const
|
2026-03-02 11:07:51 +08:00
|
|
|
|
{
|
|
|
|
|
|
return this->_project_name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:57:26 +08:00
|
|
|
|
const char* DataProcessTask::GetFinishedNotifierProcess() const
|
2026-03-02 11:07:51 +08:00
|
|
|
|
{
|
|
|
|
|
|
return this->_finished_notifier_process;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:57:26 +08:00
|
|
|
|
QObject* DataProcessTask::GetFinishedNotifier() const
|
2026-03-02 11:07:51 +08:00
|
|
|
|
{
|
|
|
|
|
|
return this->_finished_notifier;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:57:26 +08:00
|
|
|
|
bool DataProcessTask::IsValidSetWorkParameters() const
|
2026-03-02 11:07:51 +08:00
|
|
|
|
{
|
2026-03-11 13:59:46 +08:00
|
|
|
|
return !(this->_project_name.isEmpty());
|
2026-03-02 11:07:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:57:26 +08:00
|
|
|
|
void DataProcessTask::StartTask()
|
2026-03-02 11:07:51 +08:00
|
|
|
|
{
|
|
|
|
|
|
QThreadPool::globalInstance()->start(this);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:57:26 +08:00
|
|
|
|
void DataProcessTask::run()
|
2026-03-02 11:07:51 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (!IsValidSetWorkParameters()) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-20 15:45:18 +08:00
|
|
|
|
bool task_ok = processTask();
|
2026-03-03 18:15:34 +08:00
|
|
|
|
if ((GetFinishedNotifier() != nullptr) && (GetFinishedNotifierProcess() != nullptr)) {
|
2026-03-20 15:45:18 +08:00
|
|
|
|
QMetaObject::invokeMethod(
|
|
|
|
|
|
_finished_notifier,
|
|
|
|
|
|
_finished_notifier_process,
|
|
|
|
|
|
Qt::QueuedConnection,
|
|
|
|
|
|
Q_ARG(bool, task_ok),
|
|
|
|
|
|
Q_ARG(QString, _project_name),
|
|
|
|
|
|
Q_ARG(QVariant, _task_result_data)
|
|
|
|
|
|
);
|
2026-03-03 18:15:34 +08:00
|
|
|
|
}
|
2026-03-02 11:07:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-20 15:45:18 +08:00
|
|
|
|
void DataProcessTask::updateTaskResultData(const QVariant &task_result_data)
|
|
|
|
|
|
{
|
|
|
|
|
|
this->_task_result_data = task_result_data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:57:26 +08:00
|
|
|
|
void ParticleDataTask::SetAllChannelParticleDataFilename(const QString& all_channel_particle_data_filename)
|
|
|
|
|
|
{
|
|
|
|
|
|
this->_all_channel_particle_data_filename = all_channel_particle_data_filename;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const QString& ParticleDataTask::GetAllChannelParticleDataFilename() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return this->_all_channel_particle_data_filename;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ParticleDataTask::IsValidSetWorkParameters() const
|
|
|
|
|
|
{
|
2026-03-11 13:59:46 +08:00
|
|
|
|
return (!GetAllChannelParticleDataFilename().isEmpty()) && DataProcessTask::IsValidSetWorkParameters();
|
2026-03-09 21:57:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ParticleDataTask::processTask()
|
|
|
|
|
|
{
|
|
|
|
|
|
return processEveryChannelParticleData();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
void EveryChannelParticleDataSeparateTask::SetResultDataDir(const QString& result_data_dir)
|
|
|
|
|
|
{
|
|
|
|
|
|
this->_result_data_dir = result_data_dir;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const QString& EveryChannelParticleDataSeparateTask::GetResultDataDir() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return this->_result_data_dir;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool EveryChannelParticleDataSeparateTask::IsValidSetWorkParameters() const
|
|
|
|
|
|
{
|
2026-03-02 21:39:41 +08:00
|
|
|
|
return (!GetResultDataDir().isEmpty()) && ParticleDataTask::IsValidSetWorkParameters();
|
2026-03-02 18:56:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 11:07:51 +08:00
|
|
|
|
bool EveryChannelParticleDataSeparateTask::processEveryChannelParticleData()
|
|
|
|
|
|
{
|
|
|
|
|
|
bool ret_ok = true;
|
|
|
|
|
|
const QString& result_data_output_dir_path = GetResultDataDir();
|
|
|
|
|
|
QDir result_data_output_dir(result_data_output_dir_path);
|
|
|
|
|
|
result_data_output_dir.mkpath(result_data_output_dir_path);
|
|
|
|
|
|
|
|
|
|
|
|
const QString& all_channel_particle_data_filename = GetAllChannelParticleDataFilename();
|
|
|
|
|
|
QMap<uint, QString> particle_data_filename_list;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
QMap<uint, std::shared_ptr<std::ofstream>> ch_particle_data_of_list;
|
|
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
|
|
|
|
|
|
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
|
|
|
|
|
|
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
|
|
|
|
|
|
std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString();
|
|
|
|
|
|
|
2026-03-02 21:39:41 +08:00
|
|
|
|
// 使用更灵活的方式处理CSV文件,忽略额外列
|
|
|
|
|
|
io::CSVReader<
|
|
|
|
|
|
4,
|
|
|
|
|
|
io::trim_chars<' ', '\t'>,
|
|
|
|
|
|
io::double_quote_escape<',', '"'>,
|
|
|
|
|
|
io::throw_on_overflow,
|
2026-03-03 18:15:34 +08:00
|
|
|
|
io::empty_line_comment>
|
|
|
|
|
|
reader(QStrToSysPath(all_channel_particle_data_filename));
|
2026-03-02 21:39:41 +08:00
|
|
|
|
reader.read_header(io::ignore_extra_column, board_id_str, channel_id_str, address_str, time_str);
|
2026-03-02 18:56:21 +08:00
|
|
|
|
uint board_id;
|
|
|
|
|
|
uint channel_id;
|
|
|
|
|
|
uint address;
|
|
|
|
|
|
unsigned long long time;
|
|
|
|
|
|
while (reader.read_row(board_id, channel_id, address, time)) {
|
2026-03-02 11:07:51 +08:00
|
|
|
|
// 板卡和通道号计算,通道号 = 板卡号 * 4 + 通道号
|
|
|
|
|
|
int channel_num = (board_id) * 4 + (channel_id + 1);
|
2026-03-09 13:07:22 +08:00
|
|
|
|
QString particle_data_filename = result_data_output_dir.filePath(QStringLiteral(u"通道%1粒子数据.csv").arg(channel_num));
|
2026-03-02 11:07:51 +08:00
|
|
|
|
if (!particle_data_filename_list.contains(channel_num)) {
|
|
|
|
|
|
particle_data_filename_list.insert(channel_num, particle_data_filename);
|
|
|
|
|
|
}
|
2026-03-03 18:15:34 +08:00
|
|
|
|
if (!ch_particle_data_of_list.contains(channel_num)) {
|
2026-03-02 11:07:51 +08:00
|
|
|
|
std::shared_ptr<std::ofstream> out(
|
2026-03-03 18:15:34 +08:00
|
|
|
|
new std::ofstream(QStrToSysPath(particle_data_filename), std::ios::out | std::ios::app),
|
|
|
|
|
|
[](std::ofstream* p) { p->close(); });
|
2026-03-02 18:56:21 +08:00
|
|
|
|
*out << QString(QStringLiteral(u"板卡号,通道号,道址,时间计数")).toStdString() << std::endl;
|
2026-03-03 18:15:34 +08:00
|
|
|
|
ch_particle_data_of_list.insert(channel_num, out);
|
2026-03-02 11:07:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
auto ch_particle_data_of = ch_particle_data_of_list.value(channel_num);
|
2026-03-02 18:56:21 +08:00
|
|
|
|
*ch_particle_data_of << board_id << "," << channel_id << "," << address << "," << time << std::endl;
|
2026-03-02 11:07:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (const std::runtime_error& e) {
|
2026-03-17 18:38:50 +08:00
|
|
|
|
const QString& e_what = QString::fromLatin1(e.what());
|
|
|
|
|
|
QString error = QString(QStringLiteral(u"处理%1发生运行时异常:%2")).arg(all_channel_particle_data_filename).arg(e_what);
|
2026-03-02 11:07:51 +08:00
|
|
|
|
LOG_ERROR(error)
|
|
|
|
|
|
ret_ok = false;
|
|
|
|
|
|
} catch (const std::exception& e) {
|
2026-03-17 18:38:50 +08:00
|
|
|
|
const QString& e_what = QString::fromLatin1(e.what());
|
|
|
|
|
|
QString error = QString(QStringLiteral(u"处理%1异常:%2")).arg(all_channel_particle_data_filename).arg(e_what);
|
2026-03-02 11:07:51 +08:00
|
|
|
|
LOG_ERROR(error)
|
|
|
|
|
|
ret_ok = false;
|
|
|
|
|
|
} catch (...) {
|
|
|
|
|
|
QString error = QString(QStringLiteral(u"处理%1未知异常.")).arg(all_channel_particle_data_filename);
|
|
|
|
|
|
LOG_ERROR(error)
|
|
|
|
|
|
ret_ok = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const QString& project_name = GetProjectName();
|
2026-03-05 20:48:07 +08:00
|
|
|
|
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
|
2026-03-02 11:07:51 +08:00
|
|
|
|
if (project_model == nullptr) {
|
|
|
|
|
|
ret_ok = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
for (auto it = particle_data_filename_list.begin(); it != particle_data_filename_list.end(); ++it) {
|
2026-03-05 20:48:07 +08:00
|
|
|
|
// project_model->SetChannelParticleDataFilename(it.key(), it.value());
|
2026-03-02 11:07:51 +08:00
|
|
|
|
}
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 11:07:51 +08:00
|
|
|
|
return ret_ok;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 18:15:34 +08:00
|
|
|
|
void EveryChannelParticleCountDataTask::SetAllChannelCountResultDir(const QString& dir_path)
|
2026-03-02 18:56:21 +08:00
|
|
|
|
{
|
|
|
|
|
|
this->_all_ch_count_dir = dir_path;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 18:15:34 +08:00
|
|
|
|
const QString& EveryChannelParticleCountDataTask::GetAllChannelCountResultDir() const
|
2026-03-02 18:56:21 +08:00
|
|
|
|
{
|
|
|
|
|
|
return this->_all_ch_count_dir;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 18:15:34 +08:00
|
|
|
|
void EveryChannelParticleCountDataTask::SetEveryChannelCountResultDir(const QString& dir_path)
|
2026-03-02 18:56:21 +08:00
|
|
|
|
{
|
|
|
|
|
|
this->_every_ch_count_dir = dir_path;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 18:15:34 +08:00
|
|
|
|
const QString& EveryChannelParticleCountDataTask::GetEveryChannelCountResultDir() const
|
2026-03-02 18:56:21 +08:00
|
|
|
|
{
|
|
|
|
|
|
return this->_every_ch_count_dir;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool EveryChannelParticleCountDataTask::IsValidSetWorkParameters() const
|
|
|
|
|
|
{
|
2026-03-03 18:15:34 +08:00
|
|
|
|
return (!GetAllChannelCountResultDir().isEmpty()) && (!GetEveryChannelCountResultDir().isEmpty()) && ParticleDataTask::IsValidSetWorkParameters();
|
2026-03-02 18:56:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 11:07:51 +08:00
|
|
|
|
bool EveryChannelParticleCountDataTask::processEveryChannelParticleData()
|
|
|
|
|
|
{
|
|
|
|
|
|
bool ret_ok = true;
|
2026-03-02 18:56:21 +08:00
|
|
|
|
const QString& all_ch_count_dir = GetAllChannelCountResultDir();
|
|
|
|
|
|
const QString& every_ch_count_dir = GetEveryChannelCountResultDir();
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
QDir all_ch_count_output_dir(all_ch_count_dir);
|
|
|
|
|
|
all_ch_count_output_dir.mkpath(all_ch_count_dir);
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
QDir every_ch_count_output_dir(every_ch_count_dir);
|
|
|
|
|
|
every_ch_count_output_dir.mkpath(every_ch_count_dir);
|
2026-03-02 11:07:51 +08:00
|
|
|
|
|
|
|
|
|
|
const QString& all_channel_particle_data_filename = GetAllChannelParticleDataFilename();
|
2026-03-02 18:56:21 +08:00
|
|
|
|
QMap<uint, QString> particle_count_filename_list;
|
2026-03-09 13:07:22 +08:00
|
|
|
|
// QString all_channel_total_count_filename;
|
2026-03-02 11:07:51 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-02 18:56:21 +08:00
|
|
|
|
// 统计每个通道的粒子计数(相同板卡号通道号相同道址)
|
2026-03-21 00:26:34 +08:00
|
|
|
|
QMap<uint, QMap<uint, unsigned long long>> channel_address_counts; // 通道号 -> 地址 -> 计数
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
// 统计所有通道的粒子计数(不同板卡号通道号相同道址)
|
2026-03-04 16:17:04 +08:00
|
|
|
|
// QMap<uint, uint> all_channel_address_counts; // 地址 -> 计数
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
|
|
|
|
|
|
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
|
|
|
|
|
|
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
|
|
|
|
|
|
std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString();
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 21:39:41 +08:00
|
|
|
|
// 使用更灵活的方式处理CSV文件,忽略额外列
|
|
|
|
|
|
io::CSVReader<
|
|
|
|
|
|
4,
|
|
|
|
|
|
io::trim_chars<' ', '\t'>,
|
|
|
|
|
|
io::double_quote_escape<',', '"'>,
|
|
|
|
|
|
io::throw_on_overflow,
|
2026-03-03 18:15:34 +08:00
|
|
|
|
io::empty_line_comment>
|
|
|
|
|
|
reader(QStrToSysPath(all_channel_particle_data_filename));
|
2026-03-02 21:39:41 +08:00
|
|
|
|
reader.read_header(io::ignore_extra_column, board_id_str, channel_id_str, address_str, time_str);
|
2026-03-02 18:56:21 +08:00
|
|
|
|
uint board_id;
|
|
|
|
|
|
uint channel_id;
|
|
|
|
|
|
uint address;
|
|
|
|
|
|
unsigned long long time;
|
|
|
|
|
|
while (reader.read_row(board_id, channel_id, address, time)) {
|
2026-03-02 11:07:51 +08:00
|
|
|
|
// 板卡和通道号计算,通道号 = 板卡号 * 4 + 通道号
|
|
|
|
|
|
int channel_num = (board_id) * 4 + (channel_id + 1);
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
// 统计每个通道的粒子计数
|
|
|
|
|
|
if (!channel_address_counts.contains(channel_num)) {
|
2026-03-21 00:26:34 +08:00
|
|
|
|
channel_address_counts[channel_num] = QMap<uint, unsigned long long>();
|
2026-03-02 11:07:51 +08:00
|
|
|
|
}
|
2026-03-02 18:56:21 +08:00
|
|
|
|
channel_address_counts[channel_num][address]++;
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
// 统计所有通道的粒子计数
|
2026-03-04 16:17:04 +08:00
|
|
|
|
// all_channel_address_counts[address]++;
|
2026-03-02 18:56:21 +08:00
|
|
|
|
}
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
// 写入每个通道的粒子计数数据(优化:使用一次打开文件,批量写入)
|
|
|
|
|
|
QMap<uint, std::shared_ptr<std::ofstream>> channel_file_streams;
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
// 预创建所有通道的文件流
|
|
|
|
|
|
for (auto channel_it = channel_address_counts.begin(); channel_it != channel_address_counts.end(); ++channel_it) {
|
|
|
|
|
|
uint channel_num = channel_it.key();
|
2026-03-09 13:07:22 +08:00
|
|
|
|
QString count_data_filename = every_ch_count_output_dir.filePath(QStringLiteral(u"通道%1粒子计数.csv").arg(channel_num));
|
2026-03-02 18:56:21 +08:00
|
|
|
|
particle_count_filename_list.insert(channel_num, count_data_filename);
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
// 创建文件流
|
2026-03-03 18:15:34 +08:00
|
|
|
|
std::shared_ptr<std::ofstream> out(new std::ofstream( QStrToSysPath(count_data_filename)));
|
2026-03-02 18:56:21 +08:00
|
|
|
|
channel_file_streams[channel_num] = out;
|
|
|
|
|
|
*out << QString(QStringLiteral(u"道址")).toStdString() << "," << QString(QStringLiteral(u"计数")).toStdString() << std::endl;
|
|
|
|
|
|
}
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
// 批量写入数据
|
|
|
|
|
|
for (auto channel_it = channel_address_counts.begin(); channel_it != channel_address_counts.end(); ++channel_it) {
|
|
|
|
|
|
uint channel_num = channel_it.key();
|
2026-03-21 00:26:34 +08:00
|
|
|
|
const QMap<uint, unsigned long long>& address_counts = channel_it.value();
|
2026-03-02 18:56:21 +08:00
|
|
|
|
auto out_stream = channel_file_streams[channel_num];
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
for (auto address_it = address_counts.begin(); address_it != address_counts.end(); ++address_it) {
|
|
|
|
|
|
uint address = address_it.key();
|
2026-03-21 00:26:34 +08:00
|
|
|
|
unsigned long long count = address_it.value();
|
2026-03-02 18:56:21 +08:00
|
|
|
|
*out_stream << address << "," << count << std::endl;
|
2026-03-02 11:07:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
// 文件流会在shared_ptr析构时自动关闭
|
|
|
|
|
|
channel_file_streams.clear();
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 18:56:21 +08:00
|
|
|
|
// 写入所有通道的粒子计数数据
|
2026-03-04 16:17:04 +08:00
|
|
|
|
// all_channel_total_count_filename = all_ch_count_output_dir.filePath("AllChannelParticleTotalCountData.csv");
|
|
|
|
|
|
// std::ofstream all_channel_out(QStrToSysPath(all_channel_total_count_filename));
|
|
|
|
|
|
// all_channel_out << QString(QStringLiteral(u"道址")).toStdString() << "," << QString(QStringLiteral(u"计数")).toStdString() << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
// for (auto address_it = all_channel_address_counts.begin(); address_it != all_channel_address_counts.end(); ++address_it) {
|
|
|
|
|
|
// uint address = address_it.key();
|
|
|
|
|
|
// uint count = address_it.value();
|
|
|
|
|
|
// all_channel_out << address << "," << count << std::endl;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// all_channel_out.close();
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 11:07:51 +08:00
|
|
|
|
} catch (const std::runtime_error& e) {
|
2026-03-17 18:38:50 +08:00
|
|
|
|
const QString& e_what = QString::fromLatin1(e.what());
|
|
|
|
|
|
QString error = QStringLiteral(u"处理%1发生运行时异常:%2").arg(all_channel_particle_data_filename).arg(e_what);
|
2026-03-02 11:07:51 +08:00
|
|
|
|
LOG_ERROR(error)
|
|
|
|
|
|
ret_ok = false;
|
|
|
|
|
|
} catch (const std::exception& e) {
|
2026-03-17 18:38:50 +08:00
|
|
|
|
const QString& e_what = QString::fromLatin1(e.what());
|
|
|
|
|
|
QString error = QStringLiteral(u"处理%1异常:%2").arg(all_channel_particle_data_filename).arg(e_what);
|
2026-03-02 11:07:51 +08:00
|
|
|
|
LOG_ERROR(error)
|
|
|
|
|
|
ret_ok = false;
|
|
|
|
|
|
} catch (...) {
|
2026-03-17 18:38:50 +08:00
|
|
|
|
QString error = QStringLiteral(u"处理%1未知异常.").arg(all_channel_particle_data_filename);
|
2026-03-02 11:07:51 +08:00
|
|
|
|
LOG_ERROR(error)
|
|
|
|
|
|
ret_ok = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const QString& project_name = GetProjectName();
|
2026-03-05 20:48:07 +08:00
|
|
|
|
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
|
2026-03-02 11:07:51 +08:00
|
|
|
|
if (project_model == nullptr) {
|
|
|
|
|
|
ret_ok = false;
|
2026-03-02 18:56:21 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 更新项目模型中的通道粒子计数数据文件名
|
|
|
|
|
|
for (auto it = particle_count_filename_list.begin(); it != particle_count_filename_list.end(); ++it) {
|
2026-03-05 20:48:07 +08:00
|
|
|
|
project_model->SetChannelAddressCountDataFilename(it.key(), it.value());
|
2026-03-02 18:56:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 更新项目模型中的所有通道粒子总计数数据文件名
|
2026-03-04 16:17:04 +08:00
|
|
|
|
// project_model->SetAllChannelParticleTotalCountDataFilename(all_channel_total_count_filename);
|
2026-03-02 11:07:51 +08:00
|
|
|
|
}
|
2026-03-20 15:45:18 +08:00
|
|
|
|
const QString& info = QStringLiteral(u"所有通道粒子计数处理完成.");
|
|
|
|
|
|
LOG_INFO(info);
|
2026-03-02 11:07:51 +08:00
|
|
|
|
return ret_ok;
|
|
|
|
|
|
}
|
2026-03-02 21:39:41 +08:00
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
void ParticleDataSortTask::SetSortedResultDir(const QString& sorted_result_dir)
|
|
|
|
|
|
{
|
|
|
|
|
|
this->_sorted_result_dir = sorted_result_dir;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const QString& ParticleDataSortTask::GetSortedResultDir() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return this->_sorted_result_dir;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ParticleDataSortTask::IsValidSetWorkParameters() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return (!GetSortedResultDir().isEmpty()) && ParticleDataTask::IsValidSetWorkParameters();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct CsvRow {
|
|
|
|
|
|
uint board_id;
|
|
|
|
|
|
uint channel_id;
|
|
|
|
|
|
uint address;
|
|
|
|
|
|
unsigned long long time;
|
|
|
|
|
|
size_t chunk_index;
|
|
|
|
|
|
|
2026-03-03 18:15:34 +08:00
|
|
|
|
bool operator<(const CsvRow& other) const
|
|
|
|
|
|
{
|
2026-03-02 21:39:41 +08:00
|
|
|
|
return time > other.time;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-03 18:15:34 +08:00
|
|
|
|
std::vector<std::string> splitFile(const std::string& input_file, size_t chunk_size)
|
|
|
|
|
|
{
|
2026-03-02 21:39:41 +08:00
|
|
|
|
std::vector<std::string> chunks;
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 21:39:41 +08:00
|
|
|
|
try {
|
|
|
|
|
|
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
|
|
|
|
|
|
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
|
|
|
|
|
|
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
|
|
|
|
|
|
std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString();
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
|
|
|
|
|
io::CSVReader<
|
|
|
|
|
|
4,
|
|
|
|
|
|
io::trim_chars<' ', '\t'>,
|
|
|
|
|
|
io::double_quote_escape<',', '"'>,
|
|
|
|
|
|
io::throw_on_overflow,
|
|
|
|
|
|
io::empty_line_comment
|
|
|
|
|
|
> reader(input_file);
|
2026-03-02 21:39:41 +08:00
|
|
|
|
reader.read_header(io::ignore_extra_column, board_id_str, channel_id_str, address_str, time_str);
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-20 15:45:18 +08:00
|
|
|
|
int chunk_index = 0;
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 21:39:41 +08:00
|
|
|
|
while (true) {
|
|
|
|
|
|
std::vector<CsvRow> rows;
|
2026-03-20 15:45:18 +08:00
|
|
|
|
size_t current_size = 0;
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 21:39:41 +08:00
|
|
|
|
uint board_id;
|
|
|
|
|
|
uint channel_id;
|
|
|
|
|
|
uint address;
|
|
|
|
|
|
unsigned long long time;
|
|
|
|
|
|
while (reader.read_row(board_id, channel_id, address, time)) {
|
|
|
|
|
|
CsvRow row;
|
|
|
|
|
|
row.board_id = board_id;
|
|
|
|
|
|
row.channel_id = channel_id;
|
|
|
|
|
|
row.address = address;
|
|
|
|
|
|
row.time = time;
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 21:39:41 +08:00
|
|
|
|
// Estimate row size
|
2026-03-20 15:45:18 +08:00
|
|
|
|
current_size += std::to_string(board_id).size() + std::to_string(channel_id).size() + std::to_string(address).size() + std::to_string(time).size() + 4;
|
|
|
|
|
|
if (current_size > chunk_size && !rows.empty()) {
|
2026-03-02 21:39:41 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
rows.push_back(row);
|
|
|
|
|
|
}
|
2026-03-03 18:15:34 +08:00
|
|
|
|
if (rows.empty())
|
|
|
|
|
|
break;
|
2026-03-02 21:39:41 +08:00
|
|
|
|
std::sort(rows.begin(), rows.end(), [](const CsvRow& a, const CsvRow& b) {
|
|
|
|
|
|
return a.time < b.time;
|
|
|
|
|
|
});
|
2026-03-20 15:45:18 +08:00
|
|
|
|
std::string chunk_file = input_file + ".chunk" + std::to_string(chunk_index);
|
2026-03-20 19:12:27 +08:00
|
|
|
|
std::ofstream out_file(chunk_file);
|
2026-03-02 21:39:41 +08:00
|
|
|
|
for (const auto& row : rows) {
|
2026-03-20 19:12:27 +08:00
|
|
|
|
out_file << row.board_id << "," << row.channel_id << "," << row.address << "," << row.time << "\n";
|
2026-03-02 21:39:41 +08:00
|
|
|
|
}
|
2026-03-20 19:12:27 +08:00
|
|
|
|
out_file.close();
|
2026-03-20 15:45:18 +08:00
|
|
|
|
chunks.push_back(chunk_file);
|
|
|
|
|
|
chunk_index++;
|
2026-03-02 21:39:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (const std::exception& e) {
|
2026-03-17 10:50:33 +08:00
|
|
|
|
throw(e);
|
2026-03-02 21:39:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
return chunks;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 18:15:34 +08:00
|
|
|
|
void mergeChunks(const std::vector<std::string>& chunks, const std::string& output_file)
|
|
|
|
|
|
{
|
2026-03-20 15:45:18 +08:00
|
|
|
|
std::vector<std::unique_ptr<io::CSVReader<4>>> chunk_readers;
|
|
|
|
|
|
std::priority_queue<CsvRow> min_heap;
|
2026-03-02 21:39:41 +08:00
|
|
|
|
|
|
|
|
|
|
for (const auto& chunk : chunks) {
|
|
|
|
|
|
auto reader = std::make_unique<io::CSVReader<4>>(chunk);
|
2026-03-20 15:45:18 +08:00
|
|
|
|
chunk_readers.push_back(std::move(reader));
|
2026-03-02 21:39:41 +08:00
|
|
|
|
}
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-20 15:45:18 +08:00
|
|
|
|
for (size_t i = 0; i < chunk_readers.size(); i++) {
|
2026-03-02 21:39:41 +08:00
|
|
|
|
uint board_id;
|
|
|
|
|
|
uint channel_id;
|
|
|
|
|
|
uint address;
|
|
|
|
|
|
unsigned long long time;
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-20 15:45:18 +08:00
|
|
|
|
if (chunk_readers[i]->read_row(board_id, channel_id, address, time)) {
|
2026-03-02 21:39:41 +08:00
|
|
|
|
CsvRow row;
|
|
|
|
|
|
row.board_id = board_id;
|
|
|
|
|
|
row.channel_id = channel_id;
|
|
|
|
|
|
row.address = address;
|
|
|
|
|
|
row.time = time;
|
|
|
|
|
|
row.chunk_index = i;
|
2026-03-20 15:45:18 +08:00
|
|
|
|
min_heap.push(row);
|
2026-03-02 21:39:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 21:39:41 +08:00
|
|
|
|
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
|
|
|
|
|
|
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
|
|
|
|
|
|
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
|
|
|
|
|
|
std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString();
|
2026-03-03 18:15:34 +08:00
|
|
|
|
std::ofstream outFile(output_file);
|
2026-03-02 21:39:41 +08:00
|
|
|
|
outFile << board_id_str << "," << channel_id_str << "," << address_str << "," << time_str << "\n";
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-20 15:45:18 +08:00
|
|
|
|
while (!min_heap.empty()) {
|
|
|
|
|
|
CsvRow current = min_heap.top();
|
|
|
|
|
|
min_heap.pop();
|
2026-03-02 21:39:41 +08:00
|
|
|
|
|
|
|
|
|
|
outFile << current.board_id << "," << current.channel_id << "," << current.address << "," << current.time << "\n";
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 21:39:41 +08:00
|
|
|
|
size_t chunk_index = current.chunk_index;
|
2026-03-20 15:45:18 +08:00
|
|
|
|
if (chunk_readers[chunk_index]) {
|
2026-03-02 21:39:41 +08:00
|
|
|
|
uint board_id;
|
|
|
|
|
|
uint channel_id;
|
|
|
|
|
|
uint address;
|
|
|
|
|
|
unsigned long long time;
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-20 15:45:18 +08:00
|
|
|
|
if (chunk_readers[chunk_index]->read_row(board_id, channel_id, address, time)) {
|
2026-03-02 21:39:41 +08:00
|
|
|
|
CsvRow row;
|
|
|
|
|
|
row.board_id = board_id;
|
|
|
|
|
|
row.channel_id = channel_id;
|
|
|
|
|
|
row.address = address;
|
|
|
|
|
|
row.time = time;
|
|
|
|
|
|
row.chunk_index = chunk_index;
|
2026-03-20 15:45:18 +08:00
|
|
|
|
min_heap.push(row);
|
2026-03-02 21:39:41 +08:00
|
|
|
|
} else {
|
2026-03-20 15:45:18 +08:00
|
|
|
|
chunk_readers[chunk_index].reset();
|
2026-03-02 21:39:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
outFile.close();
|
|
|
|
|
|
for (const auto& chunk : chunks) {
|
|
|
|
|
|
std::remove(chunk.c_str());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ParticleDataSortTask::processEveryChannelParticleData()
|
|
|
|
|
|
{
|
|
|
|
|
|
bool ret_ok = true;
|
|
|
|
|
|
const QString& sorted_result_dir = GetSortedResultDir();
|
|
|
|
|
|
QDir sorted_result_output_dir(sorted_result_dir);
|
|
|
|
|
|
sorted_result_output_dir.mkpath(sorted_result_dir);
|
|
|
|
|
|
|
|
|
|
|
|
const QString& all_channel_particle_data_filename = GetAllChannelParticleDataFilename();
|
2026-03-20 15:45:18 +08:00
|
|
|
|
QString sorted_output_filename = sorted_result_output_dir.filePath(QStringLiteral(u"粒子数据.csv"));
|
2026-03-02 21:39:41 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const size_t CHUNK_SIZE = 100 * 1024 * 1024; // 100MB chunks
|
2026-03-03 18:15:34 +08:00
|
|
|
|
std::vector<std::string> chunks = splitFile(QStrToSysPath(all_channel_particle_data_filename), CHUNK_SIZE);
|
|
|
|
|
|
|
2026-03-02 21:39:41 +08:00
|
|
|
|
if (chunks.empty()) {
|
2026-03-03 18:15:34 +08:00
|
|
|
|
std::ifstream in_file(QStrToSysPath(all_channel_particle_data_filename));
|
|
|
|
|
|
std::ofstream out_file(QStrToSysPath(sorted_output_filename));
|
2026-03-02 21:39:41 +08:00
|
|
|
|
std::string line;
|
2026-03-03 18:15:34 +08:00
|
|
|
|
while (std::getline(in_file, line)) {
|
|
|
|
|
|
out_file << line << "\n";
|
2026-03-02 21:39:41 +08:00
|
|
|
|
}
|
2026-03-03 18:15:34 +08:00
|
|
|
|
in_file.close();
|
|
|
|
|
|
out_file.close();
|
2026-03-02 21:39:41 +08:00
|
|
|
|
} else {
|
2026-03-03 18:15:34 +08:00
|
|
|
|
mergeChunks(chunks, QStrToSysPath(sorted_output_filename));
|
2026-03-02 21:39:41 +08:00
|
|
|
|
}
|
2026-03-03 18:15:34 +08:00
|
|
|
|
|
2026-03-02 21:39:41 +08:00
|
|
|
|
} catch (const std::exception& e) {
|
2026-03-17 18:38:50 +08:00
|
|
|
|
const QString& e_what = QString::fromLatin1(e.what());
|
2026-03-20 15:45:18 +08:00
|
|
|
|
QString error = QString(QStringLiteral(u"处理%1异常:%2")).arg(all_channel_particle_data_filename).arg(e_what);
|
2026-03-02 21:39:41 +08:00
|
|
|
|
LOG_ERROR(error)
|
|
|
|
|
|
ret_ok = false;
|
|
|
|
|
|
} catch (...) {
|
|
|
|
|
|
QString error = QString(QStringLiteral(u"处理%1未知异常.")).arg(all_channel_particle_data_filename);
|
|
|
|
|
|
LOG_ERROR(error)
|
|
|
|
|
|
ret_ok = false;
|
|
|
|
|
|
}
|
2026-03-20 15:45:18 +08:00
|
|
|
|
this->updateTaskResultData(QVariant(sorted_output_filename));
|
2026-03-02 21:39:41 +08:00
|
|
|
|
return ret_ok;
|
|
|
|
|
|
}
|
2026-03-11 13:59:46 +08:00
|
|
|
|
|
|
|
|
|
|
void AutoFindPeaksTask::SetAnalysisType(AnalysisType analysis_type)
|
|
|
|
|
|
{
|
|
|
|
|
|
this->_analysis_type = analysis_type;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AutoFindPeaksTask::SetDataFileList(const QMap<QString, QVariant> &data_files_set)
|
|
|
|
|
|
{
|
|
|
|
|
|
this->_data_files_set = data_files_set;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AutoFindPeaksTask::SetResultDir(const QString &result_dir)
|
|
|
|
|
|
{
|
|
|
|
|
|
this->_result_dir = result_dir;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AutoFindPeaksTask::SetFindPeakSetpWinWidth(int step_win_width)
|
|
|
|
|
|
{
|
|
|
|
|
|
this->_step_win_width = step_win_width;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool AutoFindPeaksTask::IsValidSetWorkParameters() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return (!this->_data_files_set.isEmpty()) && (!this->_result_dir.isEmpty()) && DataProcessTask::IsValidSetWorkParameters();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool AutoFindPeaksTask::processTask()
|
|
|
|
|
|
{
|
2026-03-17 18:38:50 +08:00
|
|
|
|
QString result_filename = QDir(this->_result_dir).filePath(QStringLiteral(u"自动寻峰结果.csv"));
|
2026-03-11 13:59:46 +08:00
|
|
|
|
std::ofstream out_file(QStrToSysPath(result_filename));
|
|
|
|
|
|
std::string channel_str = QString(QStringLiteral(u"通道")).toStdString();
|
|
|
|
|
|
std::string addr_str = QString(QStringLiteral(u"峰位")).toStdString();
|
|
|
|
|
|
std::string left_addr_str = QString(QStringLiteral(u"左边界")).toStdString();
|
|
|
|
|
|
std::string lright_addr_str = QString(QStringLiteral(u"右边界")).toStdString();
|
|
|
|
|
|
std::string width_str = QString(QStringLiteral(u"峰宽")).toStdString();
|
2026-03-17 10:50:33 +08:00
|
|
|
|
std::string height_str = QString(QStringLiteral(u"峰高")).toStdString();
|
|
|
|
|
|
std::string fwhm_str = QString(QStringLiteral(u"FWHM")).toStdString();
|
2026-03-17 18:38:50 +08:00
|
|
|
|
std::string area_str = QString(QStringLiteral(u"峰面积")).toStdString();
|
|
|
|
|
|
out_file << channel_str << "," << addr_str << "," << left_addr_str << "," << lright_addr_str
|
|
|
|
|
|
<< "," << width_str << "," << height_str << "," << fwhm_str << "," << area_str << "\n";
|
|
|
|
|
|
|
|
|
|
|
|
QStringList ch_count_data_name = this->_data_files_set.keys();
|
|
|
|
|
|
std::sort(ch_count_data_name.begin(), ch_count_data_name.end(), [](const QString& a, const QString& b) {
|
|
|
|
|
|
int num_a = ExtractNumberFromString(a);
|
|
|
|
|
|
int num_b = ExtractNumberFromString(b);
|
|
|
|
|
|
return num_a < num_b;
|
|
|
|
|
|
});
|
|
|
|
|
|
for (const QString& ch_count_data_name : ch_count_data_name) {
|
|
|
|
|
|
if (ch_count_data_name.contains(ch_count_data_name)) {
|
|
|
|
|
|
const QString& ch_count_data_filename = this->_data_files_set.value(ch_count_data_name).toString();
|
|
|
|
|
|
std::string channel = ch_count_data_name.toStdString();
|
|
|
|
|
|
arma::mat data;
|
|
|
|
|
|
const std::string data_filename = QStrToSysPath(ch_count_data_filename);
|
|
|
|
|
|
if (!data.load(data_filename, arma::csv_ascii)) {
|
|
|
|
|
|
QString error = QString(QStringLiteral(u"%1自动寻峰数据加载异常!")).arg(ch_count_data_name);
|
|
|
|
|
|
LOG_WARN(error);
|
|
|
|
|
|
continue;
|
2026-03-11 13:59:46 +08:00
|
|
|
|
}
|
2026-03-18 19:31:24 +08:00
|
|
|
|
std::vector<FindPeaksBySvd::PeakInfo> peak_info_vec;
|
|
|
|
|
|
try {
|
|
|
|
|
|
peak_info_vec = FindPeaksBySvd().FindPeaks(data, this->_step_win_width);
|
|
|
|
|
|
} catch (const std::string& e) {
|
|
|
|
|
|
QString error = QString(QStringLiteral(u"%1自动寻峰异常:%2!")).arg(ch_count_data_name).arg(QString::fromStdString(e));
|
|
|
|
|
|
LOG_WARN(error);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2026-03-17 18:38:50 +08:00
|
|
|
|
if (!peak_info_vec.empty()) {
|
|
|
|
|
|
for(auto peak_info : peak_info_vec) {
|
|
|
|
|
|
int addr = peak_info.pos;
|
|
|
|
|
|
int left = peak_info.left;
|
|
|
|
|
|
int right = peak_info.left + peak_info.width;
|
|
|
|
|
|
int width = peak_info.width;
|
|
|
|
|
|
int height = peak_info.height;
|
|
|
|
|
|
int fwhm = peak_info.fwhm;
|
|
|
|
|
|
double area = peak_info.area;
|
|
|
|
|
|
out_file << channel << "," << addr << "," << left << "," << right << ","
|
|
|
|
|
|
<< width << "," << height << "," << fwhm << "," << area << "\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const QString& error = QStringLiteral(u"%1自动寻峰异常!").arg(ch_count_data_name);
|
|
|
|
|
|
LOG_WARN(error);
|
|
|
|
|
|
}
|
2026-03-20 15:45:18 +08:00
|
|
|
|
const QString& info = QStringLiteral(u"%1自动寻峰完成.").arg(ch_count_data_name);
|
2026-03-17 18:38:50 +08:00
|
|
|
|
LOG_INFO(info);
|
2026-03-11 13:59:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const QString& project_name = GetProjectName();
|
|
|
|
|
|
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
|
|
|
|
|
|
if (project_model == nullptr) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
project_model->SetAnalysisCustomData(this->_analysis_type, QString("AutoFindPeaksResult"), result_filename);
|
|
|
|
|
|
}
|
2026-03-20 15:45:18 +08:00
|
|
|
|
const QString& info = QStringLiteral(u"自动寻峰完成.");
|
2026-03-11 22:09:06 +08:00
|
|
|
|
LOG_INFO(info);
|
2026-03-11 13:59:46 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2026-03-18 19:31:24 +08:00
|
|
|
|
|
2026-03-20 19:12:27 +08:00
|
|
|
|
void ChannelEnergyScaleFittingTask::SetData(const FitDataMap& channel_energy_scale_fit_data_map, const QMap<QString, int>& fit_degree_map)
|
2026-03-18 19:31:24 +08:00
|
|
|
|
{
|
2026-03-19 18:43:03 +08:00
|
|
|
|
this->_channel_energy_scale_fit_data_map = channel_energy_scale_fit_data_map;
|
2026-03-18 19:31:24 +08:00
|
|
|
|
this->_fit_degree_map = fit_degree_map;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-19 18:43:03 +08:00
|
|
|
|
void ChannelEnergyScaleFittingTask::SetResultDir(const QString &result_dir)
|
|
|
|
|
|
{
|
|
|
|
|
|
this->_result_dir = result_dir;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-18 19:31:24 +08:00
|
|
|
|
bool ChannelEnergyScaleFittingTask::IsValidSetWorkParameters() const
|
|
|
|
|
|
{
|
2026-03-19 18:43:03 +08:00
|
|
|
|
return (!this->_channel_energy_scale_fit_data_map.isEmpty()) && (!this->_fit_degree_map.isEmpty())&& (!this->_result_dir.isEmpty()) && DataProcessTask::IsValidSetWorkParameters();
|
2026-03-18 19:31:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ChannelEnergyScaleFittingTask::processTask()
|
|
|
|
|
|
{
|
2026-03-19 18:43:03 +08:00
|
|
|
|
QDir result_dir(this->_result_dir);
|
2026-03-19 23:36:29 +08:00
|
|
|
|
const QString& energy_scale_data_filename = result_dir.filePath(QStringLiteral(u"多通道能量刻度拟合结果.json"));
|
2026-03-19 18:43:03 +08:00
|
|
|
|
EnergyScaleDataModel energy_scale_data_model(energy_scale_data_filename);
|
|
|
|
|
|
|
|
|
|
|
|
for (const QString& channel : this->_channel_energy_scale_fit_data_map.keys()) {
|
|
|
|
|
|
const QMap<int, QList<double> >& energy_scale_fit_data = this->_channel_energy_scale_fit_data_map.value(channel);
|
|
|
|
|
|
if (energy_scale_fit_data.isEmpty()) {
|
2026-03-18 19:31:24 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
int fit_degree = this->_fit_degree_map.value(channel);
|
|
|
|
|
|
if (fit_degree <= 0) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
std::vector<double> vec_x, vec_y1, vec_y2;
|
2026-03-19 18:43:03 +08:00
|
|
|
|
for (const int& addr : energy_scale_fit_data.keys()) {
|
2026-03-18 19:31:24 +08:00
|
|
|
|
vec_x.push_back(addr);
|
2026-03-19 18:43:03 +08:00
|
|
|
|
vec_y1.push_back(energy_scale_fit_data.value(addr)[0]);
|
|
|
|
|
|
vec_y2.push_back(energy_scale_fit_data.value(addr)[1]);
|
2026-03-18 19:31:24 +08:00
|
|
|
|
}
|
2026-03-19 18:43:03 +08:00
|
|
|
|
std::vector<double> energy_scale_fit_result_coeffs;
|
2026-03-18 19:31:24 +08:00
|
|
|
|
try {
|
2026-03-19 18:43:03 +08:00
|
|
|
|
energy_scale_fit_result_coeffs = GaussPolyCoe::PolynomialFit(vec_x, vec_y1, fit_degree);
|
2026-03-18 19:31:24 +08:00
|
|
|
|
} catch(const std::exception& e) {
|
|
|
|
|
|
QString error = QString(QStringLiteral(u"%1能量刻度多项式%2次拟合异常:%3!")).arg(channel).arg(fit_degree).arg(QString::fromStdString(e.what()));
|
|
|
|
|
|
LOG_WARN(error);
|
|
|
|
|
|
}
|
|
|
|
|
|
arma::vec fwhm_fit_result_coeffs;
|
|
|
|
|
|
try {
|
|
|
|
|
|
fwhm_fit_result_coeffs = NolinearLeastSquaresCurveFit::Lsqcurvefit(FwhmModel, vec_y1, vec_y2, { 1.0, 1.0 });
|
|
|
|
|
|
} catch(const std::exception& e) {
|
|
|
|
|
|
QString error = QString(QStringLiteral(u"%1分辨率拟合异常:%2!")).arg(channel).arg(QString::fromStdString(e.what()));
|
|
|
|
|
|
LOG_WARN(error);
|
|
|
|
|
|
}
|
|
|
|
|
|
const QString& info = QStringLiteral(u"%1能量刻度拟合完成.").arg(channel);
|
|
|
|
|
|
LOG_INFO(info);
|
|
|
|
|
|
|
2026-03-19 18:43:03 +08:00
|
|
|
|
std::vector<std::vector<double> > fit_result_data;
|
2026-03-18 19:31:24 +08:00
|
|
|
|
for (int i = 0; i < vec_x.size(); i++) {
|
|
|
|
|
|
int addr = vec_x[i];
|
|
|
|
|
|
double set_energy = vec_y1[i];
|
2026-03-19 18:43:03 +08:00
|
|
|
|
double fit_energy = GaussPolyCoe::Predict(energy_scale_fit_result_coeffs, addr);
|
2026-03-18 19:31:24 +08:00
|
|
|
|
double peak_fwhm = vec_y2[i];
|
|
|
|
|
|
double fit_fwhm = FwhmModel(addr, fwhm_fit_result_coeffs);
|
2026-03-19 18:43:03 +08:00
|
|
|
|
std::vector<double> fit_result_item_data;
|
|
|
|
|
|
fit_result_item_data.push_back(addr);
|
|
|
|
|
|
fit_result_item_data.push_back(set_energy);
|
|
|
|
|
|
fit_result_item_data.push_back(fit_energy);
|
|
|
|
|
|
fit_result_item_data.push_back(set_energy - fit_energy);
|
|
|
|
|
|
fit_result_item_data.push_back(peak_fwhm);
|
|
|
|
|
|
fit_result_item_data.push_back(fit_fwhm);
|
|
|
|
|
|
fit_result_item_data.push_back(peak_fwhm - fit_fwhm);
|
|
|
|
|
|
fit_result_data.push_back(fit_result_item_data);
|
2026-03-18 19:31:24 +08:00
|
|
|
|
}
|
2026-03-19 18:43:03 +08:00
|
|
|
|
energy_scale_data_model.SetEnergyFitDegree(channel, fit_degree);
|
|
|
|
|
|
energy_scale_data_model.SetEnergyFitResultCoeffs(channel, energy_scale_fit_result_coeffs);
|
|
|
|
|
|
energy_scale_data_model.SetFwhmFitResultCoeffs(channel, std::vector<double>(fwhm_fit_result_coeffs.begin(), fwhm_fit_result_coeffs.end()));
|
|
|
|
|
|
energy_scale_data_model.SetFitData(channel, fit_result_data);
|
2026-03-18 19:31:24 +08:00
|
|
|
|
}
|
2026-03-19 18:43:03 +08:00
|
|
|
|
|
|
|
|
|
|
if (!energy_scale_data_model.SaveData()) {
|
2026-03-18 19:31:24 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-19 18:43:03 +08:00
|
|
|
|
|
2026-03-18 19:31:24 +08:00
|
|
|
|
const QString& info = QStringLiteral(u"能量刻度拟合完成.");
|
|
|
|
|
|
LOG_INFO(info);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2026-03-20 15:45:18 +08:00
|
|
|
|
|
2026-03-21 00:26:34 +08:00
|
|
|
|
bool ApplyEnergyScaleTask::scaleParticleData(MeasureAnalysisProjectModel* project_model, EnergyScaleDataModel& energy_scale_data_model)
|
2026-03-20 19:12:27 +08:00
|
|
|
|
{
|
|
|
|
|
|
bool ok = true;
|
2026-03-21 00:26:34 +08:00
|
|
|
|
const QString& all_channel_particle_data_filename = project_model->GetAllChannelParticleDataFilename();
|
|
|
|
|
|
if (all_channel_particle_data_filename.isEmpty()) {
|
|
|
|
|
|
return ok &= false;
|
|
|
|
|
|
}
|
|
|
|
|
|
const QString& energy_spectrum_filename = QDir(project_model->GetProjectDir()).filePath(QStringLiteral(u"能谱数据.csv"));
|
2026-03-20 19:12:27 +08:00
|
|
|
|
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
|
|
|
|
|
|
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
|
|
|
|
|
|
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
|
|
|
|
|
|
std::string energy_str = QString(QStringLiteral(u"能量(KeV)")).toStdString();
|
|
|
|
|
|
std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString();
|
2026-03-21 00:26:34 +08:00
|
|
|
|
std::ofstream out(QStrToSysPath(energy_spectrum_filename));
|
2026-03-20 19:12:27 +08:00
|
|
|
|
out << board_id_str << "," << channel_id_str << "," << energy_str << "," << time_str<< "\n" ;
|
|
|
|
|
|
try {
|
|
|
|
|
|
io::CSVReader<
|
|
|
|
|
|
4,
|
|
|
|
|
|
io::trim_chars<' ', '\t'>,
|
|
|
|
|
|
io::double_quote_escape<',', '"'>,
|
|
|
|
|
|
io::throw_on_overflow,
|
|
|
|
|
|
io::empty_line_comment>
|
2026-03-21 00:26:34 +08:00
|
|
|
|
reader(QStrToSysPath(all_channel_particle_data_filename));
|
2026-03-20 19:12:27 +08:00
|
|
|
|
reader.read_header(io::ignore_extra_column, board_id_str, channel_id_str, address_str, time_str);
|
|
|
|
|
|
uint board_id;
|
|
|
|
|
|
uint channel_id;
|
|
|
|
|
|
uint address;
|
|
|
|
|
|
unsigned long long time;
|
|
|
|
|
|
while (reader.read_row(board_id, channel_id, address, time)) {
|
|
|
|
|
|
int channel_num = (board_id) * 4 + (channel_id + 1);
|
|
|
|
|
|
const QString& channel_name = QStringLiteral(u"通道%1").arg(channel_num);
|
|
|
|
|
|
auto coeffs = energy_scale_data_model.GetEnergyFitResultCoeffs(channel_name);
|
|
|
|
|
|
double energy = GaussPolyCoe::Predict(coeffs, address);
|
|
|
|
|
|
out << board_id << "," << channel_id << "," << energy << "," << time << "\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
out.close();
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
|
out.close();
|
2026-03-21 00:26:34 +08:00
|
|
|
|
std::remove(QStrToSysPath(energy_spectrum_filename));
|
|
|
|
|
|
ok &= false;
|
|
|
|
|
|
const QString& e_what = QString::fromStdString(e.what());
|
|
|
|
|
|
LOG_WARN(QStringLiteral(u"能谱数据异常:%1").arg(e_what));
|
2026-03-20 19:12:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
return ok;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-21 00:26:34 +08:00
|
|
|
|
bool ApplyEnergyScaleTask::energyCountProcess(MeasureAnalysisProjectModel* project_model, EnergyScaleDataModel& energy_scale_data_model)
|
2026-03-20 19:12:27 +08:00
|
|
|
|
{
|
|
|
|
|
|
bool ok = true;
|
2026-03-21 00:26:34 +08:00
|
|
|
|
const QMap<uint, QString>& ch_addr_count_filename_list = project_model->GetChannelAddressCountDataFilenameList();
|
|
|
|
|
|
if (ch_addr_count_filename_list.isEmpty()) {
|
|
|
|
|
|
LOG_WARN(QStringLiteral(u"能量计数统计需要的通道道址计数文件异常!"));
|
|
|
|
|
|
return ok &= false;
|
|
|
|
|
|
}
|
|
|
|
|
|
const QString& out_path = QDir(project_model->GetProjectDir()).filePath(QStringLiteral(u"能量计数"));
|
|
|
|
|
|
if ( !QDir(out_path).mkpath(out_path) ) {
|
|
|
|
|
|
LOG_WARN(QStringLiteral(u"创建能量计数数据目录\"%1\"异常!").arg(out_path));
|
|
|
|
|
|
return ok &= false;
|
|
|
|
|
|
}
|
|
|
|
|
|
QMap<uint, QString> every_ch_energy_count_filename_list;
|
|
|
|
|
|
for (const int& channel_num : ch_addr_count_filename_list.keys()) {
|
|
|
|
|
|
const QString& channel_name = QStringLiteral(u"通道%1").arg(channel_num);
|
|
|
|
|
|
const QString& data_filename = ch_addr_count_filename_list[channel_num];
|
|
|
|
|
|
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
|
|
|
|
|
|
std::string energy_str = QString(QStringLiteral(u"能量(KeV)")).toStdString();
|
|
|
|
|
|
std::string count_str = QString(QStringLiteral(u"计数")).toStdString();
|
|
|
|
|
|
const QString& out_filename = QDir(out_path).filePath(channel_name + ".csv");
|
|
|
|
|
|
std::ofstream out(QStrToSysPath(out_filename));
|
|
|
|
|
|
out << energy_str << "," << count_str << "\n" ;
|
|
|
|
|
|
try {
|
|
|
|
|
|
io::CSVReader<
|
|
|
|
|
|
2,
|
|
|
|
|
|
io::trim_chars<' ', '\t'>,
|
|
|
|
|
|
io::double_quote_escape<',', '"'>,
|
|
|
|
|
|
io::throw_on_overflow,
|
|
|
|
|
|
io::empty_line_comment>
|
|
|
|
|
|
reader(QStrToSysPath(data_filename));
|
|
|
|
|
|
reader.read_header(io::ignore_extra_column, address_str, count_str);
|
|
|
|
|
|
uint address;
|
|
|
|
|
|
unsigned long long count;
|
|
|
|
|
|
while (reader.read_row(address, count)) {
|
|
|
|
|
|
auto coeffs = energy_scale_data_model.GetEnergyFitResultCoeffs(channel_name);
|
|
|
|
|
|
double energy = GaussPolyCoe::Predict(coeffs, address);
|
|
|
|
|
|
out << energy << "," << count << "\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
out.close();
|
|
|
|
|
|
every_ch_energy_count_filename_list[channel_num] = out_filename;
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
|
out.close();
|
|
|
|
|
|
std::remove(QStrToSysPath(out_filename));
|
|
|
|
|
|
const QString& e_what = QString::fromStdString(e.what());
|
|
|
|
|
|
LOG_WARN(QStringLiteral(u"%1能量计数异常:%2").arg(channel_name).arg(e_what));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-20 19:12:27 +08:00
|
|
|
|
return ok;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ApplyEnergyScaleTask::coincidenceProcess()
|
2026-03-20 15:45:18 +08:00
|
|
|
|
{
|
|
|
|
|
|
bool ok = true;
|
2026-03-20 19:12:27 +08:00
|
|
|
|
return ok;
|
|
|
|
|
|
}
|
2026-03-20 15:45:18 +08:00
|
|
|
|
|
2026-03-20 19:12:27 +08:00
|
|
|
|
bool ApplyEnergyScaleTask::processTask()
|
|
|
|
|
|
{
|
2026-03-20 15:45:18 +08:00
|
|
|
|
const QString& project_name = GetProjectName();
|
|
|
|
|
|
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
|
|
|
|
|
|
if (project_model == nullptr) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-20 19:12:27 +08:00
|
|
|
|
EnergyScaleDataModel energy_scale_data_model(project_model->GetEnergyScaleFilename());
|
|
|
|
|
|
if (!energy_scale_data_model.LoadData()) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!energy_scale_data_model.IsValid()) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-21 00:26:34 +08:00
|
|
|
|
if (!scaleParticleData(project_model, energy_scale_data_model)) {
|
2026-03-20 19:12:27 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-21 00:26:34 +08:00
|
|
|
|
if (!energyCountProcess(project_model, energy_scale_data_model)) {
|
2026-03-20 19:12:27 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!coincidenceProcess()) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-20 15:45:18 +08:00
|
|
|
|
const QString& info = QStringLiteral(u"应用能量刻度完成.");
|
|
|
|
|
|
LOG_INFO(info);
|
2026-03-20 19:12:27 +08:00
|
|
|
|
return true;
|
2026-03-20 15:45:18 +08:00
|
|
|
|
}
|