增加道址计数

This commit is contained in:
anxinglong 2026-06-10 18:54:59 +08:00
parent 7cda2890ce
commit 02cb0839b9
6 changed files with 255 additions and 29 deletions

View File

@ -33,7 +33,10 @@
#include <QJsonObject>
#include "MeasureClient.h"
#include "MeasureAnalysisDataTableView.h"
#include "GvfToCsv/GvfToCsv.h"
#include "csv.h"
#include <fstream>
using namespace io;
using namespace ads;
MainWindow* MainWindow::_s_main_win = nullptr;
@ -383,6 +386,8 @@ void MainWindow::closeProject(const QString& project_name)
}
}
void MainWindow::showEvent(QShowEvent *event)
{
QMainWindow::showEvent(event);
@ -476,6 +481,15 @@ void MainWindow::on_action_start_measure_triggered()
QString item_name = QStringLiteral(u"测量粒子数据");
QStandardItem * particleData = node[item_name];
ProjectList::Instance()->SetNodeStatus(particleData,status,true);
//创建通道道址计数文件夹
const QString& all_channel_particle_data_filename = models->GetAllChannelParticleDataFilename();
const QString& all_ch_count_dir = models->GetProjectDir();
if (!all_channel_particle_data_filename.isEmpty()) {
const QString& every_ch_count_dir = QDir(models->GetProjectDir()).filePath(QStringLiteral(u"通道道址计数"));
QDir every_ch_count_output_dir(every_ch_count_dir);
every_ch_count_output_dir.mkpath(every_ch_count_dir);
}
QString projectName;
QString deviceCfg;
@ -586,7 +600,6 @@ void MainWindow::onRunningInfo(const QString &run_info)
void MainWindow::onGvfData(const QByteArray &data)
{
// LOG_INFO(QStringLiteral(u"GVFDATA: %1").arg(QString::fromUtf8(data.toHex().toUpper())));
QList<ParticleData> particles = _gvfToCsv->parseParticleFrames(data);
if (particles.isEmpty()) {
LOG_INFO(QStringLiteral(u"本次GVF数据未解析到有效粒子跳过写入CSV"));
@ -625,9 +638,19 @@ void MainWindow::onGvfData(const QByteArray &data)
out << csvBuffer;
out.flush(); // 确保数据立即写入磁盘,避免程序崩溃丢失数据
outFile.close();
//处理粒子数据
changeUpdata(particles);
//处理道址计数
changeChannelParticleCount(particles);
}
void MainWindow::changeUpdata(QList<ParticleData> &data)
{
QString dir = ProjectList::Instance()->GetCurrentProjectModel()->GetProjectDir();
QString csvPath = QStringLiteral(u"%1/%2").arg(dir).arg("粒子数据.csv");
auto dockList = _dock_manager->dockWidgetsMap().values();
int i = 0;
for(auto dock : dockList)
{
MeasureAnalysisView* view = dynamic_cast<MeasureAnalysisView*>(dock->widget());
@ -636,26 +659,108 @@ void MainWindow::onGvfData(const QByteArray &data)
&& view->GetViewType() == MeasureAnalysisView::DataTable)
{
MeasureAnalysisDataTableView* table = dynamic_cast<MeasureAnalysisDataTableView*>(view);
table->RefreshTableData(csvPath);
QString boardId = QString::number(data.at(i).boardId);
QString channelId = QString::number(data.at(i).channelId);
QString address = QString::number(data.at(i).address);
QString timestampCount = QString::number(data.at(i).timestampCount);
QStringList dataList;
dataList << boardId << channelId << address << timestampCount;
ProjectList* project_list_model = ProjectList::Instance();
auto project_model = project_list_model->GetCurrentProjectModel();
const QString& all_channel_particle_data_filename = project_model->GetAllChannelParticleDataFilename();
if (!all_channel_particle_data_filename.isEmpty()) {
const QString& all_ch_count_dir = project_model->GetProjectDir();
const QString& every_ch_count_dir = QDir(project_model->GetProjectDir()).filePath(QStringLiteral(u"通道道址计数"));
auto count_task = new DataProcessWorkPool::EveryChannelParticleCountDataTask;
count_task->SetAllChannelParticleDataFilename(all_channel_particle_data_filename);
count_task->SetAllChannelCountResultDir(all_ch_count_dir);
count_task->SetEveryChannelCountResultDir(every_ch_count_dir);
count_task->SetFinishedNotifier(project_list_model, "onChannelAddressCountProcessFinished", project_model->GetProjectName());
count_task->StartTask();
}
// initAction();
// dataList << QString("%1,%2,%3,%4").arg(boardId).arg(channelId).arg(address).arg(timestampCount);
table->AppendRow(dataList,false);
i++;
}
}
}
void MainWindow::changeChannelParticleCount(QList<ParticleData> &data)
{
bool ret_ok = true;
// 通道号 -> 地址 -> 计数
for(auto info : data)
{
// 板卡和通道号计算,通道号 = 板卡号 * 4 + 通道号
int channel_num = (info.boardId) * 4 + (info.channelId + 1);
// 统计每个通道的粒子计数
if (!channel_address_counts.contains(channel_num)) {
channel_address_counts[channel_num] = QMap<uint, unsigned long long>();
}
channel_address_counts[channel_num][info.address]++;
}
MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel();
const QString& every_ch_count_dir = QDir(models->GetProjectDir()).filePath(QStringLiteral(u"通道道址计数"));
QDir every_ch_count_output_dir(every_ch_count_dir);
// 写入每个通道的粒子计数数据(优化:使用一次打开文件,批量写入)
QMap<uint, std::shared_ptr<std::ofstream>> 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<std::ofstream> out(new std::ofstream(QStrToSysPath(count_data_filename)));
channel_file_streams[channel_num] = out;
*out << QString(QStringLiteral(u"道址")).toStdString() << "," << QString(QStringLiteral(u"计数")).toStdString() << std::endl;
}
// 批量写入数据
for (auto channel_it = channel_address_counts.begin(); channel_it != channel_address_counts.end(); ++channel_it) {
uint channel_num = channel_it.key();
const QMap<uint, unsigned long long>& address_counts = channel_it.value();
auto out_stream = channel_file_streams[channel_num];
for (auto address_it = address_counts.begin(); address_it != address_counts.end(); ++address_it) {
uint address = address_it.key();
unsigned long long count = address_it.value();
*out_stream << address << "," << count << std::endl;
}
}
channel_file_streams.clear();
const QString& project_name = models->GetProjectName();
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
if (project_model == nullptr) {
ret_ok = false;
} else {
// 更新项目模型中的通道粒子计数数据文件名
for (auto it = particle_count_filename_list.begin(); it != particle_count_filename_list.end(); ++it) {
project_model->SetChannelAddressCountDataFilename(it.key(), it.value());
}
QMap<QString, QMap<QString, QStandardItem *> > project_node_items = ProjectList::Instance()->getProjectNodeItems();
QMap<QString, QStandardItem *> node_map = project_node_items[models->GetProjectName()];
const QString& adrr_count_item_name = QStringLiteral(u"道址计数");
const QMap<uint, QString>& filename_list = models->GetChannelAddressCountDataFilenameList();
bool status_ok = false;
QString status = QStringLiteral(u"无效");
if (!filename_list.isEmpty()) {
status_ok = true;
status = QStringLiteral(u"有效");
}
if (node_map.contains(adrr_count_item_name)) {
auto adrr_count_item = node_map[adrr_count_item_name];
ProjectList::Instance()->SetNodeStatus(adrr_count_item, status, status_ok);
for (auto it = filename_list.begin(); it != filename_list.end(); ++it) {
uint ch_num = it.key();
QString item_name = QStringLiteral(u"通道%1道址计数").arg(ch_num);
if(node_map.contains(item_name))
{
return;
}
const QVariant& analys_type = QVariant::fromValue(AnalysisType::AddressCountData);
QStandardItem* node_item = ProjectList::Instance()->AddChildNode(adrr_count_item, item_name, status, analys_type, true, status_ok);
node_item->setData(project_name, Qt::UserRole + 2);
node_item->setData(ch_num, Qt::UserRole + 3);
node_map[item_name] = node_item;
}
}
}
}
void MainWindow::on_action_stop_measure_triggered()
{

View File

@ -3,6 +3,7 @@
#include <QMainWindow>
#include <QMutex>
#include "GvfToCsv/GvfToCsv.h"
QT_BEGIN_NAMESPACE
namespace Ui {
@ -18,7 +19,6 @@ class MeasureAnalysisTreeView;
class BackgroundTaskListView;
class QPushButton;
class MeasureClient;
class GvfToCsv;
QT_END_NAMESPACE
class MainWindow : public QMainWindow
@ -52,7 +52,10 @@ private:
void initStatusBar();
void applyStyleSheet();
void closeProject(const QString &project_name);
//处理粒子数据
void changeUpdata(QList<ParticleData> &data);
//处理道址计数
void changeChannelParticleCount(QList<ParticleData> &data);
signals:
void newProject(const QString &project_name);
@ -80,6 +83,7 @@ private slots:
void on_action_stop_measure_triggered();
private:
QMutex _mutex_info_output;
QPlainTextEdit* _plain_edit_info_output;
@ -100,5 +104,11 @@ private:
QStringList deviceList;
GvfToCsv *_gvfToCsv = nullptr;
QMap<uint, QMap<uint, unsigned long long>> channel_address_counts; // 通道号 -> 地址 -> 计数
QMap<uint, QString> particle_count_filename_list;
};
#endif // MAINWINDOW_H

View File

@ -5,6 +5,17 @@
#include <QHBoxLayout>
#include "GlobalDefine.h"
//2026-06-10
static QString escapeCsvField(const QString& field)
{
if (field.contains(',') || field.contains('"') || field.contains('\n') || field.contains('\r')) {
QString escaped = field;
escaped.replace('"', "\"\"");
return "\"" + escaped + "\"";
}
return field;
}
MeasureAnalysisDataTableView::MeasureAnalysisDataTableView(QWidget* parent)
: MeasureAnalysisView { parent }
, _preload_policy(PreloadPolicy::Conservative)
@ -36,16 +47,28 @@ void MeasureAnalysisDataTableView::SetAnalyzeDataFilename(const QMap<QString, QV
if (data_files_set.isEmpty()) {
return;
}
auto csv_ddata_source = std::make_shared<CsvDataSource>(data_files_set.first().toString());
if (!csv_ddata_source->isValid()) {
return;
}
VirtualTableModel* table_model = new VirtualTableModel;
table_model->setDataSource(csv_ddata_source);
table_model->setPreloadPolicy(_preload_policy);
table_model->setBlockSize(_block_size);
_tableView->setVirtualModel(table_model);
//2026-06-10
if (_tableModel) {
_tableModel->deleteLater();
}
_tableModel = new VirtualTableModel;
_tableModel->setDataSource(csv_ddata_source);
_tableModel->setPreloadPolicy(_preload_policy);
_tableModel->setBlockSize(_block_size);
// VirtualTableModel* table_model = new VirtualTableModel;
// table_model->setDataSource(csv_ddata_source);
// table_model->setPreloadPolicy(_preload_policy);
// table_model->setBlockSize(_block_size);
_tableView->setVirtualModel(_tableModel);
_tableView->setBufferSize(_buffer_size);
}
@ -54,10 +77,82 @@ void MeasureAnalysisDataTableView::RefreshTableData(const QString &csvFilePath)
auto csv_source = std::make_shared<CsvDataSource>(csvFilePath);
if(!csv_source->isValid()) return;
VirtualTableModel* newModel = new VirtualTableModel;
newModel->setDataSource(csv_source);
newModel->setPreloadPolicy(_preload_policy);
newModel->setBlockSize(_block_size);
_tableView->setVirtualModel(newModel);
//2026-06-10
if (_tableModel) {
_tableModel->deleteLater();
}
_tableModel = new VirtualTableModel;
_tableModel->setDataSource(csv_source);
_tableModel->setPreloadPolicy(_preload_policy);
_tableModel->setBlockSize(_block_size);
// VirtualTableModel* newModel = new VirtualTableModel;
// newModel->setDataSource(csv_source);
// newModel->setPreloadPolicy(_preload_policy);
// newModel->setBlockSize(_block_size);
_tableView->setVirtualModel(_tableModel);
_tableView->setBufferSize(_buffer_size);
}
void MeasureAnalysisDataTableView::AppendRow(const QVariantList &rowData, bool writeToFile)
{
// 1. 前置校验
if (!_tableModel || !_tableModel->dataSource()) {
LOG_WARN(QStringLiteral(u"追加行失败:表格模型或数据源未初始化"));
return;
}
auto dataSource = std::dynamic_pointer_cast<CsvDataSource>(_tableModel->dataSource());
if (!dataSource || !dataSource->isValid()) {
LOG_WARN(QStringLiteral(u"追加行失败CSV数据源无效"));
return;
}
// 2. 列数匹配
const int expectedColumns = _tableModel->columnCount();
if (rowData.size() != expectedColumns) {
LOG_WARN(QStringLiteral(u"追加行失败:列数不匹配,期望%1列实际%2列")
.arg(expectedColumns).arg(rowData.size()));
return;
}
//需要写入文件的
if (writeToFile) {
QFile file(dataSource->filePath());
QTextStream out(&file);
out.setCodec("UTF-8"); // 与读取时的 QString::fromUtf8 保持一致
QStringList escapedFields;
for (const QVariant& field : rowData) {
escapedFields.append(escapeCsvField(field.toString()));
}
out << escapedFields.join(',') << "\n";
file.close();
LOG_DEBUG(QStringLiteral(u"已成功将新行写入CSV文件%1").arg(dataSource->filePath()));
RefreshTableData(dataSource->filePath());
return;
}
QFile file(dataSource->filePath());
if (!file.open(QIODevice::Append | QIODevice::Text)) {
LOG_ERROR(QStringLiteral(u"追加行失败:无法打开文件 %1错误%2")
.arg(file.fileName()).arg(file.errorString()));
return;
}
// 4. 重新加载整个表格,确保模型与文件同步
RefreshTableData(dataSource->filePath());
// 5. 自动滚动到底部
_tableView->scrollToBottom();
}
void MeasureAnalysisDataTableView::AppendRow(const QStringList &rowData, bool writeToFile)
{
QVariantList varList;
for (const QString& field : rowData) {
varList.append(QVariant(field));
}
AppendRow(varList, writeToFile);
}

View File

@ -21,12 +21,19 @@ public:
virtual void SetAnalyzeDataFilename(const QMap<QString, QVariant>& data_files_set);
void RefreshTableData(const QString& csvFilePath);
// 2026-06-10 表尾插入数据函数支持两种参数类型默认同时写入CSV文件
void AppendRow(const QVariantList& rowData, bool writeToFile = true);
void AppendRow(const QStringList& rowData, bool writeToFile = true);
private:
// 私有成员变量
VirtualTableView *_tableView;
PreloadPolicy _preload_policy; // 预加载策略
uint _block_size; // 块大小输入框
uint _buffer_size; // 缓冲区大小输入框
//2026-06-10
VirtualTableModel* _tableModel = nullptr; // 持有模型指针,避免内存泄漏
};
#endif // MEASUREANALYSISDATATABLEVIEW_H

View File

@ -111,6 +111,11 @@ QVariant VirtualTableModel::headerData(int section, Qt::Orientation orientation,
return QVariant();
}
std::shared_ptr<DataSource> VirtualTableModel::dataSource() const
{
return m_dataSource;
}
void VirtualTableModel::setDataSource(std::shared_ptr<DataSource> source)
{
beginResetModel();

View File

@ -64,6 +64,10 @@ public:
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
//2026-06-10
std::shared_ptr<DataSource> dataSource() const;
// 公共接口方法
/**
* @brief