#include "DataProcessWorkPool.h" #include "MeasureAnalysisProjectModel.h" #include "GlobalDefine.h" #include "csv.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DataCalcProcess/MathModelDefine.h" #include "DataCalcProcess/FindPeaksBySvd.h" #include "DataCalcProcess/GaussPolyCoe.h" #include "DataCalcProcess/NolinearLeastSquaresCurveFit.h" #include "EnergyScaleDataModel.h" #include "DataCalcProcess/CoincidenceSpectrumProcess.h" #include using namespace DataProcessWorkPool; using namespace io; void DataProcessTask::SetFinishedNotifier(QObject* finished_notifier, const char* finished_process, const QString& project_name) { this->_finished_notifier = finished_notifier; this->_finished_notifier_process = finished_process; this->_project_name = project_name; } const QString& DataProcessTask::GetProjectName() const { return this->_project_name; } const char* DataProcessTask::GetFinishedNotifierProcess() const { return this->_finished_notifier_process; } QObject* DataProcessTask::GetFinishedNotifier() const { return this->_finished_notifier; } bool DataProcessTask::IsValidSetWorkParameters() const { return !(this->_project_name.isEmpty()); } void DataProcessTask::StartTask() { QThreadPool::globalInstance()->start(this); } void DataProcessTask::run() { if (!IsValidSetWorkParameters()) { return; } bool task_ok = processTask(); if ((GetFinishedNotifier() != nullptr) && (GetFinishedNotifierProcess() != nullptr)) { 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) ); } } void DataProcessTask::updateTaskResultData(const QVariant &task_result_data) { this->_task_result_data = task_result_data; } 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 { return (!GetAllChannelParticleDataFilename().isEmpty()) && DataProcessTask::IsValidSetWorkParameters(); } bool ParticleDataTask::processTask() { return processEveryChannelParticleData(); } void EveryChannelParticleCountDataTask::SetAllChannelCountResultDir(const QString& dir_path) { this->_all_ch_count_dir = dir_path; } const QString& EveryChannelParticleCountDataTask::GetAllChannelCountResultDir() const { return this->_all_ch_count_dir; } void EveryChannelParticleCountDataTask::SetEveryChannelCountResultDir(const QString& dir_path) { this->_every_ch_count_dir = dir_path; } const QString& EveryChannelParticleCountDataTask::GetEveryChannelCountResultDir() const { return this->_every_ch_count_dir; } bool EveryChannelParticleCountDataTask::IsValidSetWorkParameters() const { return (!GetAllChannelCountResultDir().isEmpty()) && (!GetEveryChannelCountResultDir().isEmpty()) && ParticleDataTask::IsValidSetWorkParameters(); } bool EveryChannelParticleCountDataTask::processEveryChannelParticleData() { bool ret_ok = true; const QString& all_ch_count_dir = GetAllChannelCountResultDir(); const QString& every_ch_count_dir = GetEveryChannelCountResultDir(); QDir all_ch_count_output_dir(all_ch_count_dir); all_ch_count_output_dir.mkpath(all_ch_count_dir); QDir every_ch_count_output_dir(every_ch_count_dir); every_ch_count_output_dir.mkpath(every_ch_count_dir); const QString& all_channel_particle_data_filename = GetAllChannelParticleDataFilename(); QMap particle_count_filename_list; try { // 统计每个通道的粒子计数(相同板卡号通道号相同道址) QMap> channel_address_counts; // 通道号 -> 地址 -> 计数 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(); // 使用更灵活的方式处理CSV文件,忽略额外列 io::CSVReader< 4, io::trim_chars<' ', '\t'>, io::double_quote_escape<',', '"'>, io::throw_on_overflow, io::empty_line_comment> reader(QStrToSysPath(all_channel_particle_data_filename)); 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)) { // 板卡和通道号计算,通道号 = 板卡号 * 4 + 通道号 int channel_num = (board_id) * 4 + (channel_id + 1); // 统计每个通道的粒子计数 if (!channel_address_counts.contains(channel_num)) { channel_address_counts[channel_num] = QMap(); } channel_address_counts[channel_num][address]++; } // 写入每个通道的粒子计数数据(优化:使用一次打开文件,批量写入) QMap> channel_file_streams; // 预创建所有通道的文件流 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 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& 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(); } catch (const std::runtime_error& e) { const QString& e_what = QString::fromLatin1(e.what()); QString error = QStringLiteral(u"处理%1发生运行时异常:%2").arg(all_channel_particle_data_filename).arg(e_what); LOG_ERROR(error) ret_ok = false; } catch (const std::exception& e) { const QString& e_what = QString::fromLatin1(e.what()); QString error = QStringLiteral(u"处理%1异常:%2").arg(all_channel_particle_data_filename).arg(e_what); LOG_ERROR(error) ret_ok = false; } catch (...) { QString error = QStringLiteral(u"处理%1未知异常.").arg(all_channel_particle_data_filename); LOG_ERROR(error) ret_ok = false; } const QString& project_name = 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()); } } const QString& info = QStringLiteral(u"所有通道粒子计数处理完成."); LOG_INFO(info); return ret_ok; } //////////////////////////////////////////////////////////////////////////////////// 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; bool operator<(const CsvRow& other) const { return time > other.time; } }; std::vector splitFile(const std::string& input_file, size_t chunk_size) { std::vector chunks; 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(); io::CSVReader< 4, io::trim_chars<' ', '\t'>, io::double_quote_escape<',', '"'>, io::throw_on_overflow, io::empty_line_comment > reader(input_file); reader.read_header(io::ignore_extra_column, board_id_str, channel_id_str, address_str, time_str); int chunk_index = 0; while (true) { std::vector rows; size_t current_size = 0; 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; // Estimate row size 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()) { break; } rows.push_back(row); } if (rows.empty()) break; std::sort(rows.begin(), rows.end(), [](const CsvRow& a, const CsvRow& b) { return a.time < b.time; }); std::string chunk_file = input_file + ".chunk" + std::to_string(chunk_index); std::ofstream out_file(chunk_file); for (const auto& row : rows) { out_file << row.board_id << "," << row.channel_id << "," << row.address << "," << row.time << "\n"; } out_file.close(); chunks.push_back(chunk_file); chunk_index++; } } catch (const std::exception& e) { throw(e); } return chunks; } void mergeChunks(const std::vector& chunks, const std::string& output_file) { std::vector>> chunk_readers; std::priority_queue min_heap; for (const auto& chunk : chunks) { auto reader = std::make_unique>(chunk); chunk_readers.push_back(std::move(reader)); } for (size_t i = 0; i < chunk_readers.size(); i++) { uint board_id; uint channel_id; uint address; unsigned long long time; if (chunk_readers[i]->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; row.chunk_index = i; min_heap.push(row); } } 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(); std::ofstream outFile(output_file); outFile << board_id_str << "," << channel_id_str << "," << address_str << "," << time_str << "\n"; while (!min_heap.empty()) { CsvRow current = min_heap.top(); min_heap.pop(); outFile << current.board_id << "," << current.channel_id << "," << current.address << "," << current.time << "\n"; size_t chunk_index = current.chunk_index; if (chunk_readers[chunk_index]) { uint board_id; uint channel_id; uint address; unsigned long long time; if (chunk_readers[chunk_index]->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; row.chunk_index = chunk_index; min_heap.push(row); } else { chunk_readers[chunk_index].reset(); } } } 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(); QString sorted_output_filename = sorted_result_output_dir.filePath(QStringLiteral(u"粒子数据.csv")); try { const size_t CHUNK_SIZE = 100 * 1024 * 1024; // 100MB chunks std::vector chunks = splitFile(QStrToSysPath(all_channel_particle_data_filename), CHUNK_SIZE); if (chunks.empty()) { std::ifstream in_file(QStrToSysPath(all_channel_particle_data_filename)); std::ofstream out_file(QStrToSysPath(sorted_output_filename)); std::string line; while (std::getline(in_file, line)) { out_file << line << "\n"; } in_file.close(); out_file.close(); } else { mergeChunks(chunks, QStrToSysPath(sorted_output_filename)); } } catch (const std::exception& e) { const QString& e_what = QString::fromLatin1(e.what()); QString error = QString(QStringLiteral(u"处理%1异常:%2")).arg(all_channel_particle_data_filename).arg(e_what); 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; } this->updateTaskResultData(QVariant(sorted_output_filename)); return ret_ok; } bool ParticleDataSortByMinimysTask::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& data_filename = GetAllChannelParticleDataFilename(); QString output_filename = sorted_result_output_dir.filePath(QStringLiteral(u"粒子数据.csv")); QString minimsys = QDir(qApp->applicationDirPath()).filePath(QString("minimsys")); QString bash = QDir(minimsys).filePath(QString("bash")); QString head = QDir(minimsys).filePath(QString("head")); QString tail = QDir(minimsys).filePath(QString("tail")); QString sort = QDir(minimsys).filePath(QString("sort")); QString cmd = QString("(%1 -n 1 %4 && %2 -n +2 %4 | %3 -t ',' -k4 -n --parallel=8 -S 4G) > %5").arg(head).arg(tail).arg(sort).arg(data_filename).arg(output_filename); QProcess proc; QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.clear(); env.insert("PATH", minimsys); proc.setProcessEnvironment(env); proc.setWorkingDirectory(minimsys); proc.start(bash, { "-c", cmd }); proc.waitForFinished(-1); if (proc.exitCode() != 0) { QString process_error = QString(proc.readAllStandardError()); QString error = QStringLiteral(u"处理%1异常:%2").arg(data_filename).arg(process_error); LOG_ERROR(error); ret_ok = false; } this->updateTaskResultData(QVariant(output_filename)); return ret_ok; } bool CoincidenceEventAnalysisTask::processTask() { const QString& project_name = GetProjectName(); MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name); if (project_model == nullptr) { return false; } const QString& particle_data_filename = project_model->GetAllChannelParticleDataFilename(); if (particle_data_filename.isEmpty()) { return false; } std::string event_id_str = QString(QStringLiteral(u"事件ID")).toStdString(); std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString(); std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString(); std::string addr_str = QString(QStringLiteral(u"道址")).toStdString(); std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString(); try { io::CSVReader< 4, io::trim_chars<' ', '\t'>, io::double_quote_escape<',', '"'>, io::throw_on_overflow, io::empty_line_comment> reader(QStrToSysPath(particle_data_filename)); reader.read_header(io::ignore_extra_column, board_id_str, channel_id_str, addr_str, time_str); using namespace CoincidenceSpectrum::F2t9Order; std::vector spec_data; SpectrumData spec_data_item; while (reader.read_row( spec_data_item.board_id, spec_data_item.channel_id, spec_data_item.energy, spec_data_item.timestamp)) { spec_data.push_back(spec_data_item); } std::vector events; if (!ProcessCoincidence(spec_data, events, project_model->GetConformTimeWin())) { LOG_WARN(QStringLiteral(u"粒子符合数据处理异常!")); } const QString& coincidence_data_dir_name = QStringLiteral(u"粒子符合数据"); QDir project_dir(project_model->GetProjectDir()); project_dir.mkpath(coincidence_data_dir_name); const QString& coincidence_data_dir_path = project_dir.filePath(coincidence_data_dir_name); QMap > coincidence_data_out_stream_map; unsigned long long event_id = 0; for (const CoincidenceEvent& event : events) { ++ event_id; const QString& event_data_filename = QDir(coincidence_data_dir_path).filePath(QStringLiteral(u"[%1ns]%2个粒子符合事件[未刻度].csv").arg(project_model->GetConformTimeWin()).arg(event.coincidence_order)); if ( !coincidence_data_out_stream_map.contains(event.coincidence_order) ) { std::shared_ptr out_stream(new std::ofstream(QStrToSysPath(event_data_filename))); *out_stream << event_id_str << "," << board_id_str << "," << channel_id_str << "," << addr_str << "," << time_str << std::endl; coincidence_data_out_stream_map[event.coincidence_order] = out_stream; } std::shared_ptr out_stream = coincidence_data_out_stream_map[event.coincidence_order]; for (const SpectrumData& data_item : event.events) { *out_stream << event_id << "," << data_item.board_id << "," << data_item.channel_id << "," << data_item.energy << "," << data_item.timestamp << std::endl; } project_model->SetTimeWinConformParticleData(project_model->GetConformTimeWin(), event.coincidence_order, event_data_filename); } } catch (const std::exception& e) { const QString& e_what = QString::fromUtf8(e.what()); LOG_WARN(QStringLiteral(u"粒子符合数据处理异常:%1").arg(e_what)); return false; } const QString& info = QStringLiteral(u"粒子符合数据处理完成."); LOG_INFO(info); return true; } void AutoFindPeaksTask::SetAnalysisType(AnalysisType analysis_type) { this->_analysis_type = analysis_type; } void AutoFindPeaksTask::SetDataFileList(const QMap &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() { QString result_filename = QDir(this->_result_dir).filePath(QStringLiteral(u"自动寻峰结果.csv")); 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(); std::string height_str = QString(QStringLiteral(u"峰高")).toStdString(); std::string fwhm_str = QString(QStringLiteral(u"FWHM")).toStdString(); 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; } std::vector 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; } 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); } const QString& info = QStringLiteral(u"%1自动寻峰完成.").arg(ch_count_data_name); LOG_INFO(info); } } 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); } const QString& info = QStringLiteral(u"自动寻峰完成."); LOG_INFO(info); return true; } void ChannelEnergyScaleFittingTask::SetData(const FitDataMap& channel_energy_scale_fit_data_map, const QMap& fit_degree_map) { this->_channel_energy_scale_fit_data_map = channel_energy_scale_fit_data_map; this->_fit_degree_map = fit_degree_map; } void ChannelEnergyScaleFittingTask::SetResultDir(const QString &result_dir) { this->_result_dir = result_dir; } bool ChannelEnergyScaleFittingTask::IsValidSetWorkParameters() const { return (!this->_channel_energy_scale_fit_data_map.isEmpty()) && (!this->_fit_degree_map.isEmpty())&& (!this->_result_dir.isEmpty()) && DataProcessTask::IsValidSetWorkParameters(); } bool ChannelEnergyScaleFittingTask::processTask() { QDir result_dir(this->_result_dir); const QString& energy_scale_data_filename = result_dir.filePath(QStringLiteral(u"多通道能量刻度拟合结果.json")); EnergyScaleDataModel energy_scale_data_model(energy_scale_data_filename); for (const QString& channel : this->_channel_energy_scale_fit_data_map.keys()) { const QMap >& energy_scale_fit_data = this->_channel_energy_scale_fit_data_map.value(channel); if (energy_scale_fit_data.isEmpty()) { continue; } int fit_degree = this->_fit_degree_map.value(channel); if (fit_degree <= 0) { continue; } std::vector vec_x, vec_y1, vec_y2; for (const int& addr : energy_scale_fit_data.keys()) { vec_x.push_back(addr); vec_y1.push_back(energy_scale_fit_data.value(addr)[0]); vec_y2.push_back(energy_scale_fit_data.value(addr)[1]); } std::vector energy_scale_fit_result_coeffs; try { energy_scale_fit_result_coeffs = GaussPolyCoe::PolynomialFit(vec_x, vec_y1, fit_degree); } 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); std::vector > fit_result_data; for (int i = 0; i < vec_x.size(); i++) { int addr = vec_x[i]; double set_energy = vec_y1[i]; double fit_energy = GaussPolyCoe::Predict(energy_scale_fit_result_coeffs, addr); double peak_fwhm = vec_y2[i]; double fit_fwhm = FwhmModel(addr, fwhm_fit_result_coeffs); std::vector 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); } 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(fwhm_fit_result_coeffs.begin(), fwhm_fit_result_coeffs.end())); energy_scale_data_model.SetFitData(channel, fit_result_data); } if (!energy_scale_data_model.SaveData()) { return false; } const QString& info = QStringLiteral(u"能量刻度拟合完成."); LOG_INFO(info); return true; } bool EnergyScaleParticleDataTask::processTask() { const QString& project_name = GetProjectName(); MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name); if (project_model == nullptr) { return false; } EnergyScaleDataModel energy_scale_data_model(project_model->GetEnergyScaleFilename()); if (!energy_scale_data_model.LoadData()) { return false; } if (!energy_scale_data_model.IsValid()) { return false; } const QString& all_channel_particle_data_filename = project_model->GetAllChannelParticleDataFilename(); if (all_channel_particle_data_filename.isEmpty()) { return false; } const QString& energy_spectrum_filename = QDir(project_model->GetProjectDir()).filePath(QStringLiteral(u"能谱数据.csv")); 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(); std::ofstream out(QStrToSysPath(energy_spectrum_filename)); 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> reader(QStrToSysPath(all_channel_particle_data_filename)); 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); if (!coeffs.empty()) { double energy = GaussPolyCoe::Predict(coeffs, address); out << board_id << "," << channel_id << "," << energy << "," << time << "\n"; } } out.close(); project_model->SetParticleEnergyDataFilename(energy_spectrum_filename); } catch (const std::exception& e) { out.close(); std::remove(QStrToSysPath(energy_spectrum_filename)); const QString& e_what = QString::fromStdString(e.what()); LOG_WARN(QStringLiteral(u"能谱数据处理异常:%1").arg(e_what)); return false; } const QString& info = QStringLiteral(u"能谱数据处理完成."); LOG_INFO(info); return true; } bool EnergyCountProcessTask::processTask() { const QString& project_name = GetProjectName(); MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name); if (project_model == nullptr) { return false; } EnergyScaleDataModel energy_scale_data_model(project_model->GetEnergyScaleFilename()); if (!energy_scale_data_model.LoadData()) { return false; } if (!energy_scale_data_model.IsValid()) { return false; } const QMap& ch_addr_count_filename_list = project_model->GetChannelAddressCountDataFilenameList(); if (ch_addr_count_filename_list.isEmpty()) { LOG_WARN(QStringLiteral(u"能量计数统计需要的通道道址计数文件异常!")); return 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 false; } 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(); double bin_width = 0.1f; std::map stat_map; for (const uint& 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]; const QString& ch_out_filename = QDir(out_path).filePath(channel_name + ".csv"); std::ofstream ch_out(QStrToSysPath(ch_out_filename)); ch_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); if (!coeffs.empty()) { double energy = GaussPolyCoe::Predict(coeffs, address); ch_out << energy << "," << count << "\n"; // 计算属于哪个能量 bin double bin_energy = floor(energy / bin_width) * bin_width; // 统一保留 3 位 bin_energy = round(bin_energy * 1000) / 1000.0; stat_map[bin_energy] += count; } } ch_out.close(); project_model->SetChannelEnergyCountDataFilename(channel_num, ch_out_filename); } catch (const std::exception& e) { ch_out.close(); std::remove(QStrToSysPath(ch_out_filename)); const QString& e_what = QString::fromStdString(e.what()); LOG_WARN(QStringLiteral(u"%1能量计数异常:%2").arg(channel_name).arg(e_what)); } } const QString& out_filename = QDir(out_path).filePath(QStringLiteral(u"全通道.csv")); std::ofstream out(QStrToSysPath(out_filename)); out << energy_str << "," << count_str << "\n" ; for (const auto& [energy, count] : stat_map) { out << energy << "," << count << "\n"; } project_model->SetAllChannelEnergyTotalCountDataFilename(out_filename); const QString& info = QStringLiteral(u"能量计数处理完成."); LOG_INFO(info); return true; } bool EnergyScaleCoincidenceDataTask::processTask() { const QString& project_name = GetProjectName(); MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name); if (project_model == nullptr) { return false; } EnergyScaleDataModel energy_scale_data_model(project_model->GetEnergyScaleFilename()); if (!energy_scale_data_model.LoadData()) { return false; } if (!energy_scale_data_model.IsValid()) { return false; } const QString& coincidence_data_dir_name = QStringLiteral(u"符合能谱数据"); QDir project_dir(project_model->GetProjectDir()); project_dir.mkpath(coincidence_data_dir_name); const QString& coincidence_data_dir_path = project_dir.filePath(coincidence_data_dir_name); std::string event_id_str = QString(QStringLiteral(u"事件ID")).toStdString(); std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString(); std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString(); std::string addr_str = QString(QStringLiteral(u"道址")).toStdString(); std::string energy_str = QString(QStringLiteral(u"能量(KeV)")).toStdString(); std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString(); auto conformParticleDataEnergyScale = [&](int time_win, int coincidence_order, const QString& conform_particle_data_filename) -> bool { try { io::CSVReader< 5, io::trim_chars<' ', '\t'>, io::double_quote_escape<',', '"'>, io::throw_on_overflow, io::empty_line_comment> reader(QStrToSysPath(conform_particle_data_filename)); reader.read_header(io::ignore_extra_column, event_id_str, board_id_str, channel_id_str, addr_str, time_str); const QString& data_name = QStringLiteral(u"[%1ns]%2个粒子符合事件.csv").arg(time_win).arg(coincidence_order); const QString& coincidence_energy_data_filename = QDir(coincidence_data_dir_path).filePath(data_name); std::ofstream out_stream(QStrToSysPath(coincidence_energy_data_filename)); out_stream << event_id_str << "," << board_id_str << "," << channel_id_str << "," << energy_str << "," << time_str << "\n" ; using namespace CoincidenceSpectrum::F2t9Order; unsigned long long event_id; SpectrumData data_item; while (reader.read_row( event_id, data_item.board_id, data_item.channel_id, data_item.energy, data_item.timestamp)) { int channel_num = (data_item.board_id) * 4 + (data_item.channel_id + 1); const QString& channel_name = QStringLiteral(u"通道%1").arg(channel_num); auto coeffs = energy_scale_data_model.GetEnergyFitResultCoeffs(channel_name); if (!coeffs.empty()) { data_item.energy = GaussPolyCoe::Predict(coeffs, data_item.energy); out_stream << event_id << "," << data_item.board_id << "," << data_item.channel_id << "," << data_item.energy << "," << data_item.timestamp << "\n" ; } else { LOG_WARN(QStringLiteral(u"符合能谱数据处理异常:%1能量刻度拟合参数为空!").arg(channel_name)); // out_stream.close(); // QFile::remove(coincidence_energy_data_filename); // return false; } } out_stream.close(); project_model->SetTimeWinConformEnergyData(time_win, coincidence_order, coincidence_energy_data_filename); } catch (const std::exception& e) { const QString& e_what = QString::fromStdString(e.what()); LOG_WARN(QStringLiteral(u"符合能谱数据处理异常:%1").arg(e_what)); return false; } return true; }; const auto& conform_particle_data_filename_list = project_model->GetConformParticleDataFilenameList(); for (const auto& time_win : conform_particle_data_filename_list.keys()) { const auto& time_win_event_data_filename_list = conform_particle_data_filename_list.value(time_win); for (const auto& coincidence_order : time_win_event_data_filename_list.keys()) { const QString& conform_particle_data_filename = time_win_event_data_filename_list.value(coincidence_order); conformParticleDataEnergyScale(time_win, coincidence_order, conform_particle_data_filename); } } const QString& info = QStringLiteral(u"符合能谱数据处理完成."); LOG_INFO(info); return true; }