V0.22.2.250616_alpha:增加数据采集页面的CSV数据采集
This commit is contained in:
parent
fc788edcb5
commit
746bba9583
Binary file not shown.
@ -280,6 +280,12 @@ extern "C" std::string XNCORE_EXPORT getFileNameWithoutExt(const std::string &pa
|
|||||||
|
|
||||||
extern "C" int XNCORE_EXPORT safe_stoi(const std::string &str, int defaultValue = 0);
|
extern "C" int XNCORE_EXPORT safe_stoi(const std::string &str, int defaultValue = 0);
|
||||||
|
|
||||||
|
extern "C" int XNCORE_EXPORT safe_stol(const std::string &str, int defaultValue = 0);
|
||||||
|
|
||||||
|
extern "C" double XNCORE_EXPORT safe_stod(const std::string &str, double defaultValue = 0);
|
||||||
|
|
||||||
|
extern "C" long long XNCORE_EXPORT safe_stoll(const std::string &str, long long defaultValue = 0);
|
||||||
|
|
||||||
inline std::string getStringFromSqlite3(sqlite3_stmt *stmt, int column)
|
inline std::string getStringFromSqlite3(sqlite3_stmt *stmt, int column)
|
||||||
{
|
{
|
||||||
const char *text = reinterpret_cast<const char *>(sqlite3_column_text(stmt, column));
|
const char *text = reinterpret_cast<const char *>(sqlite3_column_text(stmt, column));
|
||||||
|
@ -175,7 +175,7 @@ protected:
|
|||||||
if (data) {
|
if (data) {
|
||||||
return std::to_string(data.value());
|
return std::to_string(data.value());
|
||||||
} else {
|
} else {
|
||||||
return "Unknown";
|
return "0";
|
||||||
}
|
}
|
||||||
} else if constexpr (is_std_array_v<T>) {
|
} else if constexpr (is_std_array_v<T>) {
|
||||||
if (data) {
|
if (data) {
|
||||||
@ -201,9 +201,9 @@ protected:
|
|||||||
{
|
{
|
||||||
if constexpr (std::is_arithmetic_v<T>) {
|
if constexpr (std::is_arithmetic_v<T>) {
|
||||||
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
||||||
data = std::stod(value);
|
data = XNSim::safe_stod(value);
|
||||||
} else {
|
} else {
|
||||||
data = std::stoll(value);
|
data = XNSim::safe_stoll(value);
|
||||||
}
|
}
|
||||||
} else if constexpr (is_std_array_v<T>) {
|
} else if constexpr (is_std_array_v<T>) {
|
||||||
// 解析输入字符串
|
// 解析输入字符串
|
||||||
@ -238,7 +238,13 @@ protected:
|
|||||||
if (i > 0)
|
if (i > 0)
|
||||||
ss << ",";
|
ss << ",";
|
||||||
if constexpr (std::is_arithmetic_v<T>) {
|
if constexpr (std::is_arithmetic_v<T>) {
|
||||||
|
if constexpr (std::is_same_v<T, char>) {
|
||||||
|
ss << static_cast<int>(data[i]);
|
||||||
|
} else if constexpr (std::is_same_v<T, unsigned char>) {
|
||||||
|
ss << static_cast<unsigned int>(data[i]);
|
||||||
|
} else {
|
||||||
ss << data[i];
|
ss << data[i];
|
||||||
|
}
|
||||||
} else if constexpr (is_std_array_v<T>) {
|
} else if constexpr (is_std_array_v<T>) {
|
||||||
ss << getStringFromStdArray(data[i]);
|
ss << getStringFromStdArray(data[i]);
|
||||||
} else {
|
} else {
|
||||||
@ -267,9 +273,9 @@ protected:
|
|||||||
if constexpr (std::is_arithmetic_v<T>) {
|
if constexpr (std::is_arithmetic_v<T>) {
|
||||||
// 对于基本类型,直接转换
|
// 对于基本类型,直接转换
|
||||||
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
||||||
data[i] = static_cast<T>(std::stod(value[start_pos + i]));
|
data[i] = static_cast<T>(XNSim::safe_stod(value[start_pos + i]));
|
||||||
} else {
|
} else {
|
||||||
data[i] = static_cast<T>(std::stoll(value[start_pos + i]));
|
data[i] = static_cast<T>(XNSim::safe_stoll(value[start_pos + i]));
|
||||||
}
|
}
|
||||||
} else if constexpr (is_std_array_v<T>) {
|
} else if constexpr (is_std_array_v<T>) {
|
||||||
// 对于嵌套数组,递归处理
|
// 对于嵌套数组,递归处理
|
||||||
|
@ -55,4 +55,41 @@ int safe_stoi(const std::string &str, int defaultValue)
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int safe_stol(const std::string &str, int defaultValue)
|
||||||
|
{
|
||||||
|
if (str.empty()) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return std::stol(str);
|
||||||
|
} catch (const std::exception &) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double safe_stod(const std::string &str, double defaultValue)
|
||||||
|
{
|
||||||
|
if (str.empty()) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return std::stod(str);
|
||||||
|
} catch (const std::exception &) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long long safe_stoll(const std::string &str, long long defaultValue)
|
||||||
|
{
|
||||||
|
if (str.empty()) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return std::stoll(str);
|
||||||
|
} catch (const std::exception &) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace XNSim
|
} // namespace XNSim
|
@ -280,6 +280,12 @@ extern "C" std::string XNCORE_EXPORT getFileNameWithoutExt(const std::string &pa
|
|||||||
|
|
||||||
extern "C" int XNCORE_EXPORT safe_stoi(const std::string &str, int defaultValue = 0);
|
extern "C" int XNCORE_EXPORT safe_stoi(const std::string &str, int defaultValue = 0);
|
||||||
|
|
||||||
|
extern "C" int XNCORE_EXPORT safe_stol(const std::string &str, int defaultValue = 0);
|
||||||
|
|
||||||
|
extern "C" double XNCORE_EXPORT safe_stod(const std::string &str, double defaultValue = 0);
|
||||||
|
|
||||||
|
extern "C" long long XNCORE_EXPORT safe_stoll(const std::string &str, long long defaultValue = 0);
|
||||||
|
|
||||||
inline std::string getStringFromSqlite3(sqlite3_stmt *stmt, int column)
|
inline std::string getStringFromSqlite3(sqlite3_stmt *stmt, int column)
|
||||||
{
|
{
|
||||||
const char *text = reinterpret_cast<const char *>(sqlite3_column_text(stmt, column));
|
const char *text = reinterpret_cast<const char *>(sqlite3_column_text(stmt, column));
|
||||||
|
@ -175,7 +175,7 @@ protected:
|
|||||||
if (data) {
|
if (data) {
|
||||||
return std::to_string(data.value());
|
return std::to_string(data.value());
|
||||||
} else {
|
} else {
|
||||||
return "Unknown";
|
return "0";
|
||||||
}
|
}
|
||||||
} else if constexpr (is_std_array_v<T>) {
|
} else if constexpr (is_std_array_v<T>) {
|
||||||
if (data) {
|
if (data) {
|
||||||
@ -201,9 +201,9 @@ protected:
|
|||||||
{
|
{
|
||||||
if constexpr (std::is_arithmetic_v<T>) {
|
if constexpr (std::is_arithmetic_v<T>) {
|
||||||
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
||||||
data = std::stod(value);
|
data = XNSim::safe_stod(value);
|
||||||
} else {
|
} else {
|
||||||
data = std::stoll(value);
|
data = XNSim::safe_stoll(value);
|
||||||
}
|
}
|
||||||
} else if constexpr (is_std_array_v<T>) {
|
} else if constexpr (is_std_array_v<T>) {
|
||||||
// 解析输入字符串
|
// 解析输入字符串
|
||||||
@ -238,7 +238,13 @@ protected:
|
|||||||
if (i > 0)
|
if (i > 0)
|
||||||
ss << ",";
|
ss << ",";
|
||||||
if constexpr (std::is_arithmetic_v<T>) {
|
if constexpr (std::is_arithmetic_v<T>) {
|
||||||
|
if constexpr (std::is_same_v<T, char>) {
|
||||||
|
ss << static_cast<int>(data[i]);
|
||||||
|
} else if constexpr (std::is_same_v<T, unsigned char>) {
|
||||||
|
ss << static_cast<unsigned int>(data[i]);
|
||||||
|
} else {
|
||||||
ss << data[i];
|
ss << data[i];
|
||||||
|
}
|
||||||
} else if constexpr (is_std_array_v<T>) {
|
} else if constexpr (is_std_array_v<T>) {
|
||||||
ss << getStringFromStdArray(data[i]);
|
ss << getStringFromStdArray(data[i]);
|
||||||
} else {
|
} else {
|
||||||
@ -267,9 +273,9 @@ protected:
|
|||||||
if constexpr (std::is_arithmetic_v<T>) {
|
if constexpr (std::is_arithmetic_v<T>) {
|
||||||
// 对于基本类型,直接转换
|
// 对于基本类型,直接转换
|
||||||
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
||||||
data[i] = static_cast<T>(std::stod(value[start_pos + i]));
|
data[i] = static_cast<T>(XNSim::safe_stod(value[start_pos + i]));
|
||||||
} else {
|
} else {
|
||||||
data[i] = static_cast<T>(std::stoll(value[start_pos + i]));
|
data[i] = static_cast<T>(XNSim::safe_stoll(value[start_pos + i]));
|
||||||
}
|
}
|
||||||
} else if constexpr (is_std_array_v<T>) {
|
} else if constexpr (is_std_array_v<T>) {
|
||||||
// 对于嵌套数组,递归处理
|
// 对于嵌套数组,递归处理
|
||||||
|
@ -48,6 +48,8 @@ add_library(XNMonitorServer SHARED
|
|||||||
DataInjectThread.cpp
|
DataInjectThread.cpp
|
||||||
CSVDataInjectThread.h
|
CSVDataInjectThread.h
|
||||||
CSVDataInjectThread.cpp
|
CSVDataInjectThread.cpp
|
||||||
|
DataCollect.h
|
||||||
|
DataCollect.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# 添加头文件搜索路径
|
# 添加头文件搜索路径
|
||||||
|
@ -11,7 +11,7 @@ CSVDataInjectThread::~CSVDataInjectThread()
|
|||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSVDataInjectThread::Initialize(std::vector<InjectDataInfo> injectDataInfos)
|
bool CSVDataInjectThread::Initialize(std::vector<MonitorDataInfo> injectDataInfos)
|
||||||
{
|
{
|
||||||
m_csvFile.open(m_csvFilePath);
|
m_csvFile.open(m_csvFilePath);
|
||||||
if (!m_csvFile.is_open()) {
|
if (!m_csvFile.is_open()) {
|
||||||
@ -49,6 +49,7 @@ bool CSVDataInjectThread::Initialize(std::vector<InjectDataInfo> injectDataInfos
|
|||||||
if (dataMonitor->isInitialized()) {
|
if (dataMonitor->isInitialized()) {
|
||||||
m_alreadyStartedMonitors[injectDataInfo.structName] = dataMonitor;
|
m_alreadyStartedMonitors[injectDataInfo.structName] = dataMonitor;
|
||||||
} else {
|
} else {
|
||||||
|
dataMonitor->Initialize(nullptr, 0, 0);
|
||||||
m_notStartedMonitors[injectDataInfo.structName] = dataMonitor;
|
m_notStartedMonitors[injectDataInfo.structName] = dataMonitor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,9 +97,9 @@ void CSVDataInjectThread::parseHeaderField(const std::string &headerField)
|
|||||||
if (injectDataInfo.interfaceNames[i] == csvHeaderField.fieldName) {
|
if (injectDataInfo.interfaceNames[i] == csvHeaderField.fieldName) {
|
||||||
csvHeaderField.structName = injectDataInfo.structName;
|
csvHeaderField.structName = injectDataInfo.structName;
|
||||||
if (injectDataInfo.arraySizes[i].second > 1) {
|
if (injectDataInfo.arraySizes[i].second > 1) {
|
||||||
csvHeaderField.arraySize1 = injectDataInfo.arraySizes[i].first;
|
csvHeaderField.arraySize2 = injectDataInfo.arraySizes[i].second;
|
||||||
} else {
|
} else {
|
||||||
csvHeaderField.arraySize1 = 0;
|
csvHeaderField.arraySize2 = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -161,7 +162,13 @@ void CSVDataInjectThread::updateData()
|
|||||||
std::stringstream ss(line);
|
std::stringstream ss(line);
|
||||||
std::string field;
|
std::string field;
|
||||||
std::getline(ss, field, ',');
|
std::getline(ss, field, ',');
|
||||||
double timeStamp = std::stod(field);
|
double timeStamp = 0;
|
||||||
|
try {
|
||||||
|
timeStamp = std::stod(field);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cout << "field: " << field << " is not a number" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_nextExecuteTime = static_cast<int64_t>(timeStamp * 1000); // 转换为毫秒
|
m_nextExecuteTime = static_cast<int64_t>(timeStamp * 1000); // 转换为毫秒
|
||||||
|
|
||||||
// 为每个结构体初始化数据
|
// 为每个结构体初始化数据
|
||||||
@ -197,7 +204,7 @@ void CSVDataInjectThread::updateData()
|
|||||||
[m_headerFields[i].arrayIndex1] = field;
|
[m_headerFields[i].arrayIndex1] = field;
|
||||||
} else if (m_headerFields[i].arrayDim == 2) {
|
} else if (m_headerFields[i].arrayDim == 2) {
|
||||||
tempDataMap[m_headerFields[i].structName][m_headerFields[i].fieldName]
|
tempDataMap[m_headerFields[i].structName][m_headerFields[i].fieldName]
|
||||||
[m_headerFields[i].arrayIndex1 * m_headerFields[i].arraySize1
|
[m_headerFields[i].arrayIndex1 * m_headerFields[i].arraySize2
|
||||||
+ m_headerFields[i].arrayIndex2] = field;
|
+ m_headerFields[i].arrayIndex2] = field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,9 +255,17 @@ void CSVDataInjectThread::threadFunc()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 执行数据注入
|
// 执行数据注入
|
||||||
for (const auto &[structName, dataMonitor] : m_alreadyStartedMonitors) {
|
for (const auto &monitorDataInfo : m_injectDataInfos) {
|
||||||
if (dataMonitor && m_data.find(structName) != m_data.end()) {
|
DataMonitorBasePtr dataMonitor;
|
||||||
dataMonitor->setDataByString(m_data[structName]);
|
if (m_notStartedMonitors.find(monitorDataInfo.structName)
|
||||||
|
!= m_notStartedMonitors.end()) {
|
||||||
|
dataMonitor = m_notStartedMonitors[monitorDataInfo.structName];
|
||||||
|
} else if (m_alreadyStartedMonitors.find(monitorDataInfo.structName)
|
||||||
|
!= m_alreadyStartedMonitors.end()) {
|
||||||
|
dataMonitor = m_alreadyStartedMonitors[monitorDataInfo.structName];
|
||||||
|
}
|
||||||
|
if (dataMonitor && m_data.find(monitorDataInfo.structName) != m_data.end()) {
|
||||||
|
dataMonitor->setDataByString(m_data[monitorDataInfo.structName]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "DataMonitor.h"
|
#include "DataMonitor.h"
|
||||||
#include <thread>
|
|
||||||
#include <atomic>
|
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 数据注入线程类,用于持续向数据监控器注入数据
|
* @brief 数据注入线程类,用于持续向数据监控器注入数据
|
||||||
@ -24,7 +19,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
~CSVDataInjectThread();
|
~CSVDataInjectThread();
|
||||||
|
|
||||||
bool Initialize(std::vector<InjectDataInfo> injectDataInfos);
|
bool Initialize(std::vector<MonitorDataInfo> injectDataInfos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 启动数据注入线程
|
* @brief 启动数据注入线程
|
||||||
@ -55,7 +50,7 @@ private:
|
|||||||
private:
|
private:
|
||||||
std::string m_csvFilePath;
|
std::string m_csvFilePath;
|
||||||
std::ifstream m_csvFile;
|
std::ifstream m_csvFile;
|
||||||
std::vector<InjectDataInfo> m_injectDataInfos;
|
std::vector<MonitorDataInfo> m_injectDataInfos;
|
||||||
std::vector<CSVHeaderField> m_headerFields;
|
std::vector<CSVHeaderField> m_headerFields;
|
||||||
std::thread m_thread; ///< 数据注入线程
|
std::thread m_thread; ///< 数据注入线程
|
||||||
std::atomic<bool> m_running; ///< 线程运行标志
|
std::atomic<bool> m_running; ///< 线程运行标志
|
||||||
|
326
XNMonitorServer/DataCollect.cpp
Normal file
326
XNMonitorServer/DataCollect.cpp
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
#include "DataCollect.h"
|
||||||
|
#include "DataMonitorFactory.h"
|
||||||
|
|
||||||
|
DataCollect::DataCollect()
|
||||||
|
: m_running(false), m_nextExecuteTime(0), m_collectDuration(0), m_collectFrequency(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataCollect::~DataCollect()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DataCollect::Initialize(std::vector<MonitorDataInfo> collectDataInfos, std::string dcsFilePath)
|
||||||
|
{
|
||||||
|
m_collectDataInfos = collectDataInfos;
|
||||||
|
|
||||||
|
// 打开并读取 dcs 文件
|
||||||
|
std::ifstream dcsFile(dcsFilePath);
|
||||||
|
if (!dcsFile.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
bool foundCollectList = false;
|
||||||
|
std::string collectListContent;
|
||||||
|
|
||||||
|
// 查找 define collect_list 部分
|
||||||
|
while (std::getline(dcsFile, line)) {
|
||||||
|
// 跳过注释行
|
||||||
|
if (line.empty() || line[0] == '!') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.find("define collect_list") != std::string::npos) {
|
||||||
|
foundCollectList = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundCollectList) {
|
||||||
|
// 检查是否到达结束引号
|
||||||
|
if (line.find("\"") != std::string::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
collectListContent += line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 继续读取文件,查找采集时长和频率以及输出文件名
|
||||||
|
while (std::getline(dcsFile, line)) {
|
||||||
|
// 跳过注释行
|
||||||
|
if (line.empty() || line[0] == '!') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找 "for X at Y" 格式的行
|
||||||
|
if (line.find("for") != std::string::npos && line.find("at") != std::string::npos) {
|
||||||
|
std::stringstream ss(line);
|
||||||
|
std::string token;
|
||||||
|
ss >> token; // 读取 "for"
|
||||||
|
ss >> m_collectDuration; // 读取时长
|
||||||
|
ss >> token; // 读取 "at"
|
||||||
|
ss >> m_collectFrequency; // 读取频率
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找输出文件名
|
||||||
|
if (line.find("put/extend/all result") != std::string::npos) {
|
||||||
|
std::stringstream ss(line);
|
||||||
|
std::string token;
|
||||||
|
ss >> token; // 读取 "put/extend/all"
|
||||||
|
ss >> token; // 读取 "result"
|
||||||
|
ss >> m_outputFileName; // 读取文件名
|
||||||
|
m_outputFileName += ".csv"; // 添加.csv后缀
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 collect_list 内容
|
||||||
|
std::stringstream ss(collectListContent);
|
||||||
|
std::string field;
|
||||||
|
while (std::getline(ss, field, '-')) {
|
||||||
|
// 去除字段前后的空白字符
|
||||||
|
field.erase(0, field.find_first_not_of(" \t\r\n"));
|
||||||
|
field.erase(field.find_last_not_of(" \t\r\n") + 1);
|
||||||
|
|
||||||
|
if (!field.empty()) {
|
||||||
|
parseHeaderField(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭 dcs 文件
|
||||||
|
dcsFile.close();
|
||||||
|
|
||||||
|
for (const auto &collectDataInfo : m_collectDataInfos) {
|
||||||
|
auto dataMonitor = DataMonitorFactory::GetInstance(collectDataInfo.structName);
|
||||||
|
if (dataMonitor == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dataMonitor->isInitialized()) {
|
||||||
|
m_alreadyStartedMonitors[collectDataInfo.structName] = dataMonitor;
|
||||||
|
} else {
|
||||||
|
dataMonitor->Initialize(nullptr, 0, 0);
|
||||||
|
m_notStartedMonitors[collectDataInfo.structName] = dataMonitor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取dcs文件所在目录
|
||||||
|
std::filesystem::path dcsPath(dcsFilePath);
|
||||||
|
std::string dcsDir = dcsPath.parent_path().string();
|
||||||
|
|
||||||
|
// 将输出文件名设置为dcs文件同目录下的完整路径
|
||||||
|
m_outputFileName = dcsDir + "/" + m_outputFileName;
|
||||||
|
if (std::filesystem::exists(m_outputFileName)) {
|
||||||
|
int suffix = 1;
|
||||||
|
std::filesystem::path originalPath(m_outputFileName);
|
||||||
|
std::string stem = originalPath.stem().string();
|
||||||
|
std::string extension = originalPath.extension().string();
|
||||||
|
std::string newFileName;
|
||||||
|
|
||||||
|
do {
|
||||||
|
newFileName = stem + "_" + std::to_string(suffix) + extension;
|
||||||
|
suffix++;
|
||||||
|
} while (std::filesystem::exists(originalPath.parent_path() / newFileName));
|
||||||
|
|
||||||
|
std::filesystem::rename(m_outputFileName,
|
||||||
|
(originalPath.parent_path() / newFileName).string());
|
||||||
|
}
|
||||||
|
m_outputFile.open(m_outputFileName);
|
||||||
|
if (!m_outputFile.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入表头
|
||||||
|
m_outputFile << "Time";
|
||||||
|
|
||||||
|
for (const auto &headerField : m_headerFields) {
|
||||||
|
if (headerField.arrayDim == 0) {
|
||||||
|
m_outputFile << "," << headerField.fieldName;
|
||||||
|
} else if (headerField.arrayDim == 1) {
|
||||||
|
m_outputFile << "," << headerField.fieldName << "(" << headerField.arrayIndex1 + 1
|
||||||
|
<< ")";
|
||||||
|
} else if (headerField.arrayDim == 2) {
|
||||||
|
m_outputFile << "," << headerField.fieldName << "(" << headerField.arrayIndex1 + 1
|
||||||
|
<< "_" << headerField.arrayIndex2 + 1 << ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_outputFile << std::endl;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataCollect::parseHeaderField(const std::string &headerField)
|
||||||
|
{
|
||||||
|
CSVHeaderField csvHeaderField;
|
||||||
|
csvHeaderField.fieldName = headerField.substr(0, headerField.find('('));
|
||||||
|
csvHeaderField.arrayDim = 0;
|
||||||
|
csvHeaderField.arrayIndex1 = 0;
|
||||||
|
csvHeaderField.arrayIndex2 = 0;
|
||||||
|
|
||||||
|
if (headerField.find('(') != std::string::npos) {
|
||||||
|
// 处理一维或二维数组,格式为interfaceName(1)或interfaceName(1,2)
|
||||||
|
size_t firstParenPos = headerField.find('(');
|
||||||
|
size_t lastParenPos = headerField.find(')');
|
||||||
|
std::string indexStr =
|
||||||
|
headerField.substr(firstParenPos + 1, lastParenPos - firstParenPos - 1);
|
||||||
|
if (indexStr.find('_') != std::string::npos) {
|
||||||
|
// 二维数组
|
||||||
|
size_t commaPos = indexStr.find('_');
|
||||||
|
csvHeaderField.arrayIndex1 = std::stoi(indexStr.substr(0, commaPos)) - 1;
|
||||||
|
if (csvHeaderField.arrayIndex1 < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
csvHeaderField.arrayIndex2 = std::stoi(indexStr.substr(commaPos + 1)) - 1;
|
||||||
|
if (csvHeaderField.arrayIndex2 < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
csvHeaderField.arrayDim = 2;
|
||||||
|
} else {
|
||||||
|
// 一维数组
|
||||||
|
csvHeaderField.arrayIndex1 = std::stoi(indexStr) - 1;
|
||||||
|
if (csvHeaderField.arrayIndex1 < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
csvHeaderField.arrayDim = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &collectDataInfo : m_collectDataInfos) {
|
||||||
|
for (int i = 0; i < collectDataInfo.interfaceNames.size(); i++) {
|
||||||
|
if (collectDataInfo.interfaceNames[i] == csvHeaderField.fieldName) {
|
||||||
|
csvHeaderField.structName = collectDataInfo.structName;
|
||||||
|
if (collectDataInfo.arraySizes[i].second > 1) {
|
||||||
|
csvHeaderField.arraySize2 = collectDataInfo.arraySizes[i].second;
|
||||||
|
} else {
|
||||||
|
csvHeaderField.arraySize2 = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (csvHeaderField.structName.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_headerFields.push_back(csvHeaderField);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataCollect::start()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
if (!m_running) {
|
||||||
|
m_running = true;
|
||||||
|
m_thread = std::thread(&DataCollect::threadFunc, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataCollect::stop()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
if (m_running) {
|
||||||
|
m_running = false;
|
||||||
|
m_cv.notify_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_thread.joinable()) {
|
||||||
|
m_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭文件
|
||||||
|
if (m_outputFile.is_open()) {
|
||||||
|
m_outputFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放未启动的监控器
|
||||||
|
for (const auto &[structName, dataMonitor] : m_notStartedMonitors) {
|
||||||
|
DataMonitorFactory::ReleaseInstance(structName);
|
||||||
|
}
|
||||||
|
m_notStartedMonitors.clear();
|
||||||
|
m_alreadyStartedMonitors.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataCollect::updateData()
|
||||||
|
{
|
||||||
|
for (const auto &headerField : m_headerFields) {
|
||||||
|
if (m_data.find(headerField.structName) != m_data.end()) {
|
||||||
|
if (m_data[headerField.structName].find(headerField.fieldName)
|
||||||
|
!= m_data[headerField.structName].end()) {
|
||||||
|
if (headerField.arrayDim == 0) {
|
||||||
|
m_outputFile << "," << m_data[headerField.structName][headerField.fieldName];
|
||||||
|
} else {
|
||||||
|
std::string value = m_data[headerField.structName][headerField.fieldName];
|
||||||
|
std::stringstream ss(value);
|
||||||
|
std::string item;
|
||||||
|
std::vector<std::string> values;
|
||||||
|
while (std::getline(ss, item, ',')) {
|
||||||
|
values.push_back(item);
|
||||||
|
}
|
||||||
|
if (headerField.arrayDim == 1) {
|
||||||
|
m_outputFile << "," << values[headerField.arrayIndex1];
|
||||||
|
} else if (headerField.arrayDim == 2) {
|
||||||
|
m_outputFile << ","
|
||||||
|
<< values[headerField.arrayIndex1 * headerField.arraySize2
|
||||||
|
+ headerField.arrayIndex2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_outputFile << "," << "0";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_outputFile << "," << "0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_outputFile << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DataCollect::isRunning() const
|
||||||
|
{
|
||||||
|
return m_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataCollect::threadFunc()
|
||||||
|
{
|
||||||
|
auto startTime = std::chrono::steady_clock::now();
|
||||||
|
auto step = std::chrono::milliseconds(static_cast<int64_t>(1000 / m_collectFrequency));
|
||||||
|
auto nextTime = step;
|
||||||
|
auto endTime =
|
||||||
|
startTime + std::chrono::milliseconds(static_cast<int64_t>(m_collectDuration * 1000));
|
||||||
|
|
||||||
|
while (m_running) {
|
||||||
|
// 等待直到到达执行时间
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
auto targetTime = startTime + nextTime;
|
||||||
|
|
||||||
|
if (now < targetTime) {
|
||||||
|
std::this_thread::sleep_until(targetTime);
|
||||||
|
}
|
||||||
|
m_data.clear();
|
||||||
|
// 执行数据注入
|
||||||
|
for (const auto &monitorDataInfo : m_collectDataInfos) {
|
||||||
|
DataMonitorBasePtr dataMonitor;
|
||||||
|
if (m_notStartedMonitors.find(monitorDataInfo.structName)
|
||||||
|
!= m_notStartedMonitors.end()) {
|
||||||
|
dataMonitor = m_notStartedMonitors[monitorDataInfo.structName];
|
||||||
|
} else if (m_alreadyStartedMonitors.find(monitorDataInfo.structName)
|
||||||
|
!= m_alreadyStartedMonitors.end()) {
|
||||||
|
dataMonitor = m_alreadyStartedMonitors[monitorDataInfo.structName];
|
||||||
|
}
|
||||||
|
if (dataMonitor && m_data.find(monitorDataInfo.structName) == m_data.end()) {
|
||||||
|
m_data[monitorDataInfo.structName] =
|
||||||
|
dataMonitor->getStringData(monitorDataInfo.interfaceNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入一行数据
|
||||||
|
nextTime += step;
|
||||||
|
if (targetTime > endTime) {
|
||||||
|
m_running = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
double timeStamp =
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(now - startTime).count() / 1000.0;
|
||||||
|
m_outputFile << std::fixed << std::setprecision(3) << timeStamp;
|
||||||
|
updateData();
|
||||||
|
}
|
||||||
|
}
|
52
XNMonitorServer/DataCollect.h
Normal file
52
XNMonitorServer/DataCollect.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DataMonitor.h"
|
||||||
|
|
||||||
|
class DataCollect
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DataCollect();
|
||||||
|
~DataCollect();
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool Initialize(std::vector<MonitorDataInfo> collectDataInfos, std::string dcsFilePath);
|
||||||
|
|
||||||
|
void start();
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从CSV文件读取下一行数据并更新执行时间
|
||||||
|
* 如果文件读取完毕,将停止线程
|
||||||
|
*/
|
||||||
|
void updateData();
|
||||||
|
|
||||||
|
bool isRunning() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 线程执行函数
|
||||||
|
*/
|
||||||
|
void threadFunc();
|
||||||
|
|
||||||
|
void parseHeaderField(const std::string &headerField);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_outputFileName;
|
||||||
|
std::ofstream m_outputFile;
|
||||||
|
std::vector<MonitorDataInfo> m_collectDataInfos;
|
||||||
|
std::vector<CSVHeaderField> m_headerFields;
|
||||||
|
std::thread m_thread; ///< 数据采集线程
|
||||||
|
std::atomic<bool> m_running; ///< 线程运行标志
|
||||||
|
std::mutex m_mutex; ///< 互斥锁
|
||||||
|
std::condition_variable m_cv; ///< 条件变量
|
||||||
|
std::unordered_map<std::string, DataMonitorBasePtr>
|
||||||
|
m_alreadyStartedMonitors; ///< 已经启动的数据监控器
|
||||||
|
std::unordered_map<std::string, DataMonitorBasePtr>
|
||||||
|
m_notStartedMonitors; ///< 未启动的数据监控器
|
||||||
|
std::unordered_map<std::string, std::unordered_map<std::string, std::string>>
|
||||||
|
m_data; ///< 采集到的数据
|
||||||
|
std::atomic<int64_t> m_nextExecuteTime; ///< 下一次执行的时间点
|
||||||
|
int m_collectDuration; ///< 采集时长(秒)
|
||||||
|
int m_collectFrequency; ///< 采集频率(Hz)
|
||||||
|
};
|
@ -1,10 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "DataMonitor.h"
|
#include "DataMonitor.h"
|
||||||
#include <thread>
|
|
||||||
#include <atomic>
|
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 数据注入线程类,用于持续向数据监控器注入数据
|
* @brief 数据注入线程类,用于持续向数据监控器注入数据
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <fstream>
|
||||||
#include <fastdds/dds/domain/DomainParticipant.hpp>
|
#include <fastdds/dds/domain/DomainParticipant.hpp>
|
||||||
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
|
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
|
||||||
#include <fastdds/dds/topic/TypeSupport.hpp>
|
#include <fastdds/dds/topic/TypeSupport.hpp>
|
||||||
@ -210,7 +214,7 @@ struct ModelDefinition {
|
|||||||
std::vector<std::shared_ptr<NamespaceDefinition>> namespaceDefinitions;
|
std::vector<std::shared_ptr<NamespaceDefinition>> namespaceDefinitions;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InjectDataInfo {
|
struct MonitorDataInfo {
|
||||||
/**
|
/**
|
||||||
* @brief 结构体名称
|
* @brief 结构体名称
|
||||||
*/
|
*/
|
||||||
@ -253,7 +257,7 @@ struct CSVHeaderField {
|
|||||||
int arrayIndex2;
|
int arrayIndex2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 一维数组大小
|
* @brief 列大小
|
||||||
*/
|
*/
|
||||||
int arraySize1;
|
int arraySize2;
|
||||||
};
|
};
|
@ -9,6 +9,7 @@
|
|||||||
#include "DataMonitorFactory.h"
|
#include "DataMonitorFactory.h"
|
||||||
#include "DataInjectThread.h"
|
#include "DataInjectThread.h"
|
||||||
#include "CSVDataInjectThread.h"
|
#include "CSVDataInjectThread.h"
|
||||||
|
#include "DataCollect.h"
|
||||||
|
|
||||||
// 全局变量
|
// 全局变量
|
||||||
static bool g_initialized = false;
|
static bool g_initialized = false;
|
||||||
@ -21,6 +22,7 @@ SystemControl *systemControl = nullptr;
|
|||||||
bool g_systemControlStarted = false;
|
bool g_systemControlStarted = false;
|
||||||
|
|
||||||
CSVDataInjectThread *g_csvDataInjectThread = nullptr;
|
CSVDataInjectThread *g_csvDataInjectThread = nullptr;
|
||||||
|
DataCollect *g_dataCollect = nullptr;
|
||||||
|
|
||||||
// 初始化函数实现
|
// 初始化函数实现
|
||||||
int XN_Initialize(const char *domainId, int domainIdLen, char *errorMsg, int errorMsgSize)
|
int XN_Initialize(const char *domainId, int domainIdLen, char *errorMsg, int errorMsgSize)
|
||||||
@ -602,12 +604,12 @@ int XNMONITORSERVER_EXPORT XN_InjectDataInterfaceFromCsv(const char *injectDataI
|
|||||||
delete g_csvDataInjectThread;
|
delete g_csvDataInjectThread;
|
||||||
g_csvDataInjectThread = nullptr;
|
g_csvDataInjectThread = nullptr;
|
||||||
}
|
}
|
||||||
std::vector<InjectDataInfo> injectDataInfos;
|
std::vector<MonitorDataInfo> injectDataInfos;
|
||||||
std::string injectDataInfoStr(injectDataInfo, injectDataInfoLen);
|
std::string injectDataInfoStr(injectDataInfo, injectDataInfoLen);
|
||||||
try {
|
try {
|
||||||
nlohmann::json injectDataInfoJson = nlohmann::json::parse(injectDataInfoStr);
|
nlohmann::json injectDataInfoJson = nlohmann::json::parse(injectDataInfoStr);
|
||||||
for (const auto &[structName, interfaceInfo] : injectDataInfoJson.items()) {
|
for (const auto &[structName, interfaceInfo] : injectDataInfoJson.items()) {
|
||||||
InjectDataInfo info;
|
MonitorDataInfo info;
|
||||||
info.structName = structName;
|
info.structName = structName;
|
||||||
for (const auto &[interfaceName, size] : interfaceInfo.items()) {
|
for (const auto &[interfaceName, size] : interfaceInfo.items()) {
|
||||||
info.interfaceNames.push_back(interfaceName);
|
info.interfaceNames.push_back(interfaceName);
|
||||||
@ -722,12 +724,139 @@ int XN_StopCsvDataInject(char *infoMsg, int infoMsgSize)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int XNMONITORSERVER_EXPORT XN_StartCollectData(const char *structName, const int structNameLen,
|
int XNMONITORSERVER_EXPORT XN_StartCollectData(const char *CollectDataInfo,
|
||||||
const char *interfaceName,
|
const int CollectDataInfoLen,
|
||||||
const int interfaceNameLen, const char *csvFilePath,
|
const char *dcsFilePath, const int dcsFilePathLen,
|
||||||
const int csvFilePathLen, char *infoMsg,
|
char *infoMsg, int infoMsgSize)
|
||||||
int infoMsgSize)
|
|
||||||
{
|
{
|
||||||
// TODO: 持续采集数据并保存到csv文件接口
|
if (!g_initialized) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, "DDSMonitor Not Initialized", infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
if (g_dataCollect != nullptr) {
|
||||||
|
g_dataCollect->stop();
|
||||||
|
delete g_dataCollect;
|
||||||
|
g_dataCollect = nullptr;
|
||||||
|
}
|
||||||
|
std::vector<MonitorDataInfo> collectDataInfos;
|
||||||
|
std::string collectDataInfoStr(CollectDataInfo, CollectDataInfoLen);
|
||||||
|
try {
|
||||||
|
nlohmann::json collectDataInfoJson = nlohmann::json::parse(collectDataInfoStr);
|
||||||
|
for (const auto &[structName, interfaceInfo] : collectDataInfoJson.items()) {
|
||||||
|
MonitorDataInfo info;
|
||||||
|
info.structName = structName;
|
||||||
|
for (const auto &[interfaceName, size] : interfaceInfo.items()) {
|
||||||
|
info.interfaceNames.push_back(interfaceName);
|
||||||
|
if (size.is_array()) {
|
||||||
|
info.arraySizes.push_back({size[0].get<int>(), size[1].get<int>()});
|
||||||
|
} else {
|
||||||
|
info.arraySizes.push_back({0, 0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collectDataInfos.push_back(info);
|
||||||
|
}
|
||||||
|
} catch (const nlohmann::json::parse_error &e) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, "Invalid JSON format", infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string dcsFilePathStr(dcsFilePath, dcsFilePathLen);
|
||||||
|
if (dcsFilePathStr.empty()) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, "DCS 文件路径为空", infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!std::filesystem::exists(dcsFilePathStr)) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, "DCS 文件不存在", infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
g_dataCollect = new DataCollect();
|
||||||
|
if (!g_dataCollect->Initialize(collectDataInfos, dcsFilePathStr)) {
|
||||||
|
delete g_dataCollect;
|
||||||
|
g_dataCollect = nullptr;
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, "数据采集线程初始化失败", infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
g_dataCollect->start();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
if (g_dataCollect) {
|
||||||
|
delete g_dataCollect;
|
||||||
|
g_dataCollect = nullptr;
|
||||||
|
}
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, e.what(), infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int XNMONITORSERVER_EXPORT XN_GetCollectDataStatus(char *infoMsg, int infoMsgSize)
|
||||||
|
{
|
||||||
|
if (!g_initialized) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, "DDSMonitor Not Initialized", infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_dataCollect == nullptr) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, "数据采集线程已不存在", infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_dataCollect->isRunning() ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int XNMONITORSERVER_EXPORT XN_StopCollectData(char *infoMsg, int infoMsgSize)
|
||||||
|
{
|
||||||
|
if (!g_initialized) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, "DDSMonitor Not Initialized", infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_dataCollect == nullptr) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, "数据采集线程已不存在", infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
g_dataCollect->stop();
|
||||||
|
delete g_dataCollect;
|
||||||
|
g_dataCollect = nullptr;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, e.what(), infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
@ -224,7 +224,7 @@ extern "C"
|
|||||||
* @brief 获取csv数据注入状态
|
* @brief 获取csv数据注入状态
|
||||||
* @param infoMsg 错误信息
|
* @param infoMsg 错误信息
|
||||||
* @param infoMsgSize 错误信息大小
|
* @param infoMsgSize 错误信息大小
|
||||||
* @return 0: 成功, -1: 失败
|
* @return 0: 成功, -1: 失败, 1: 正在注入
|
||||||
*/
|
*/
|
||||||
int XNMONITORSERVER_EXPORT XN_GetCsvDataInjectStatus(char *infoMsg, int infoMsgSize);
|
int XNMONITORSERVER_EXPORT XN_GetCsvDataInjectStatus(char *infoMsg, int infoMsgSize);
|
||||||
|
|
||||||
@ -239,25 +239,37 @@ extern "C"
|
|||||||
//******************** csv数据采集 *********************
|
//******************** csv数据采集 *********************
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 持续采集数据并保存到csv文件接口
|
* @brief 读取dcs采集脚本并将数据保存到csv文件接口
|
||||||
* @param structName 结构体名称
|
* @param CollectDataInfo 采集数据信息JSON数组字符串
|
||||||
* @param structNameLen 结构体名称长度
|
* @param CollectDataInfoLen 采集数据信息JSON数组字符串长度
|
||||||
* @param interfaceName 接口名称JSON数组字符串
|
* @param dcsFilePath dcs文件路径
|
||||||
* @param interfaceNameLen 接口名称JSON数组字符串长度
|
* @param dcsFilePathLen dcs文件路径长度
|
||||||
* @param csvFilePath csv文件路径
|
|
||||||
* @param csvFilePathLen csv文件路径长度
|
|
||||||
* @param frequency 采集频率
|
|
||||||
* @param infoMsg 错误信息
|
* @param infoMsg 错误信息
|
||||||
* @param infoMsgSize 错误信息大小
|
* @param infoMsgSize 错误信息大小
|
||||||
* @return 0: 成功, -1: 失败
|
* @return 0: 成功, -1: 失败
|
||||||
*/
|
*/
|
||||||
int XNMONITORSERVER_EXPORT XN_StartCollectData(const char *structName, const int structNameLen,
|
int XNMONITORSERVER_EXPORT XN_StartCollectData(const char *CollectDataInfo,
|
||||||
const char *interfaceName,
|
const int CollectDataInfoLen,
|
||||||
const int interfaceNameLen,
|
const char *dcsFilePath,
|
||||||
const char *csvFilePath,
|
const int dcsFilePathLen, char *infoMsg,
|
||||||
const int csvFilePathLen, char *infoMsg,
|
|
||||||
int infoMsgSize);
|
int infoMsgSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取csv数据采集状态
|
||||||
|
* @param infoMsg 错误信息
|
||||||
|
* @param infoMsgSize 错误信息大小
|
||||||
|
* @return 0: 成功, -1: 失败, 1: 正在采集
|
||||||
|
*/
|
||||||
|
int XNMONITORSERVER_EXPORT XN_GetCollectDataStatus(char *infoMsg, int infoMsgSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 停止采集数据
|
||||||
|
* @param infoMsg 错误信息
|
||||||
|
* @param infoMsgSize 错误信息大小
|
||||||
|
* @return 0: 成功, -1: 失败
|
||||||
|
*/
|
||||||
|
int XNMONITORSERVER_EXPORT XN_StopCollectData(char *infoMsg, int infoMsgSize);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
@ -510,6 +510,20 @@ class DataCollection extends HTMLElement {
|
|||||||
background: #d9d9d9;
|
background: #d9d9d9;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
/* 卸载脚本按钮样式 */
|
||||||
|
.action-btn.unload {
|
||||||
|
background: #ff4d4f;
|
||||||
|
}
|
||||||
|
.action-btn.unload:hover {
|
||||||
|
background: #ff7875;
|
||||||
|
}
|
||||||
|
/* 停止采集按钮样式 */
|
||||||
|
.action-btn.stop {
|
||||||
|
background: #faad14;
|
||||||
|
}
|
||||||
|
.action-btn.stop:hover {
|
||||||
|
background: #ffc53d;
|
||||||
|
}
|
||||||
.input-row {
|
.input-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -654,8 +668,8 @@ class DataCollection extends HTMLElement {
|
|||||||
<div class="left-panel">
|
<div class="left-panel">
|
||||||
<div class="panel-section">
|
<div class="panel-section">
|
||||||
<div class="button-row">
|
<div class="button-row">
|
||||||
<button class="action-btn" id="loadScriptBtn">${this.scriptFile ? '卸载脚本' : '载入脚本'}</button>
|
<button class="action-btn ${this.scriptFile ? 'unload' : ''}" id="loadScriptBtn">${this.scriptFile ? '卸载脚本' : '载入脚本'}</button>
|
||||||
<button class="action-btn" id="startCollectBtn" ${!this.scriptFile ? 'disabled' : ''}>开始采集</button>
|
<button class="action-btn ${this.collectStatus === 2 ? 'stop' : ''}" id="startCollectBtn" ${!this.scriptFile ? 'disabled' : ''}>${this.collectStatus === 2 ? '停止采集' : '开始采集'}</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-row">
|
<div class="input-row">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -713,6 +727,78 @@ class DataCollection extends HTMLElement {
|
|||||||
this.isActive = true;
|
this.isActive = true;
|
||||||
this.startStatusTimer();
|
this.startStatusTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleStartCollect() {
|
||||||
|
// 检查监控状态
|
||||||
|
if (this.monitorStatus !== 1) {
|
||||||
|
alert('请先启动监控');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已加载脚本
|
||||||
|
if (!this.scriptFile) {
|
||||||
|
alert('请先加载脚本');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startCollectBtn = this.shadowRoot.getElementById('startCollectBtn');
|
||||||
|
|
||||||
|
// 如果正在采集,则停止采集
|
||||||
|
if (this.collectStatus === 2) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/data-collect/stop', {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// 更新采集状态
|
||||||
|
this.collectStatus = 1; // 改为已加载脚本状态
|
||||||
|
// 更新按钮状态
|
||||||
|
startCollectBtn.textContent = '开始采集';
|
||||||
|
startCollectBtn.disabled = false;
|
||||||
|
startCollectBtn.classList.remove('stop');
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('停止采集失败:', error);
|
||||||
|
alert('停止采集失败: ' + error.message);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始采集
|
||||||
|
try {
|
||||||
|
// 调用后端接口启动采集
|
||||||
|
const response = await fetch('/api/data-collect/start', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
collectDataInfo: JSON.stringify(this.structData),
|
||||||
|
dcsFilePath: this.scriptFile.name
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// 更新采集状态
|
||||||
|
this.collectStatus = 2; // 设置为采集中
|
||||||
|
// 更新按钮状态
|
||||||
|
startCollectBtn.textContent = '停止采集';
|
||||||
|
startCollectBtn.classList.add('stop');
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('启动采集失败:', error);
|
||||||
|
alert('启动采集失败: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('data-collection', DataCollection);
|
customElements.define('data-collection', DataCollection);
|
94
XNSimHtml/routes/DataCollect.js
Normal file
94
XNSimHtml/routes/DataCollect.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { startCollectData, getCollectDataStatus, stopCollectData } = require('../utils/xnCoreService');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// 启动数据采集
|
||||||
|
router.post('/start', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { collectDataInfo, dcsFilePath } = req.body;
|
||||||
|
|
||||||
|
if (!collectDataInfo || !dcsFilePath) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: '缺少必要参数'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建上传文件的完整路径
|
||||||
|
const fullPath = path.join(__dirname, '..', 'upload', dcsFilePath);
|
||||||
|
|
||||||
|
const result = startCollectData(collectDataInfo, fullPath);
|
||||||
|
|
||||||
|
if (result === '启动数据采集成功') {
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: result
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('启动数据采集失败:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: '启动数据采集失败: ' + error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取数据采集状态
|
||||||
|
router.get('/status', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const status = getCollectDataStatus();
|
||||||
|
|
||||||
|
if (typeof status === 'number') {
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
status: status, // 0-成功,1-正在采集
|
||||||
|
message: status === 1 ? '正在采集' : '采集完成'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: status
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取数据采集状态失败:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: '获取数据采集状态失败: ' + error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 停止数据采集
|
||||||
|
router.post('/stop', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const result = stopCollectData();
|
||||||
|
|
||||||
|
if (result === '停止数据采集成功') {
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: result
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('停止数据采集失败:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: '停止数据采集失败: ' + error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
@ -28,6 +28,7 @@ const systemMonitorRoutes = require('./routes/SystemMonitor');
|
|||||||
const modelMonitorRoutes = require('./routes/ModelMonitor');
|
const modelMonitorRoutes = require('./routes/ModelMonitor');
|
||||||
const systemControlRoutes = require('./routes/SystemControl');
|
const systemControlRoutes = require('./routes/SystemControl');
|
||||||
const dataMonitorRoutes = require('./routes/DataMonitor');
|
const dataMonitorRoutes = require('./routes/DataMonitor');
|
||||||
|
const dataCollectRoutes = require('./routes/DataCollect');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
@ -97,6 +98,7 @@ app.use('/api/system-monitor', systemMonitorRoutes);
|
|||||||
app.use('/api/model-monitor', modelMonitorRoutes);
|
app.use('/api/model-monitor', modelMonitorRoutes);
|
||||||
app.use('/api/system-control', systemControlRoutes);
|
app.use('/api/system-control', systemControlRoutes);
|
||||||
app.use('/api/data-monitor', dataMonitorRoutes);
|
app.use('/api/data-monitor', dataMonitorRoutes);
|
||||||
|
app.use('/api/data-collect', dataCollectRoutes);
|
||||||
|
|
||||||
// 接口配置页面路由
|
// 接口配置页面路由
|
||||||
app.get('/interface-config', (req, res) => {
|
app.get('/interface-config', (req, res) => {
|
||||||
|
@ -62,7 +62,10 @@ try {
|
|||||||
'XN_StopInjectContinuous': ['int', [StringType, 'int', StringType, 'int']],
|
'XN_StopInjectContinuous': ['int', [StringType, 'int', StringType, 'int']],
|
||||||
'XN_InjectDataInterfaceFromCsv': ['int', [StringType, 'int', StringType, 'int', StringType, 'int']],
|
'XN_InjectDataInterfaceFromCsv': ['int', [StringType, 'int', StringType, 'int', StringType, 'int']],
|
||||||
'XN_StopCsvDataInject': ['int', [StringType, 'int']],
|
'XN_StopCsvDataInject': ['int', [StringType, 'int']],
|
||||||
'XN_GetCsvDataInjectStatus': ['int', [StringType, 'int']]
|
'XN_GetCsvDataInjectStatus': ['int', [StringType, 'int']],
|
||||||
|
'XN_StartCollectData': ['int', [StringType, 'int', StringType, 'int', StringType, 'int']],
|
||||||
|
'XN_GetCollectDataStatus': ['int', [StringType, 'int']],
|
||||||
|
'XN_StopCollectData': ['int', [StringType, 'int']]
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`加载 ${monitorLibName} 失败:`, error);
|
console.error(`加载 ${monitorLibName} 失败:`, error);
|
||||||
@ -501,6 +504,64 @@ function getCsvDataInjectStatus() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 启动数据采集
|
||||||
|
function startCollectData(collectDataInfo, dcsFilePath) {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return '监控服务器库未加载';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const errorMsg = Buffer.alloc(1024);
|
||||||
|
const result = monitorLib.XN_StartCollectData(
|
||||||
|
collectDataInfo, collectDataInfo.length,
|
||||||
|
dcsFilePath, dcsFilePath.length,
|
||||||
|
errorMsg, errorMsg.length
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result !== 0) {
|
||||||
|
return `启动数据采集失败: ${errorMsg.toString('utf8').replace(/\0/g, '')}`;
|
||||||
|
}
|
||||||
|
return '启动数据采集成功';
|
||||||
|
} catch (error) {
|
||||||
|
return `启动数据采集失败: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取数据采集状态
|
||||||
|
function getCollectDataStatus() {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return '监控服务器库未加载';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const infoMsg = Buffer.alloc(1024);
|
||||||
|
const result = monitorLib.XN_GetCollectDataStatus(infoMsg, infoMsg.length);
|
||||||
|
|
||||||
|
if (result === -1) {
|
||||||
|
return `获取状态失败: ${infoMsg.toString('utf8').replace(/\0/g, '')}`;
|
||||||
|
}
|
||||||
|
return result; // 返回状态:0-成功,1-正在采集
|
||||||
|
} catch (error) {
|
||||||
|
return `获取状态失败: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止数据采集
|
||||||
|
function stopCollectData() {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return '监控服务器库未加载';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const errorMsg = Buffer.alloc(1024);
|
||||||
|
const result = monitorLib.XN_StopCollectData(errorMsg, errorMsg.length);
|
||||||
|
|
||||||
|
if (result !== 0) {
|
||||||
|
return `停止数据采集失败: ${errorMsg.toString('utf8').replace(/\0/g, '')}`;
|
||||||
|
}
|
||||||
|
return '停止数据采集成功';
|
||||||
|
} catch (error) {
|
||||||
|
return `停止数据采集失败: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
loginLib,
|
loginLib,
|
||||||
monitorLib,
|
monitorLib,
|
||||||
@ -527,5 +588,8 @@ module.exports = {
|
|||||||
stopInjectContinuous,
|
stopInjectContinuous,
|
||||||
injectDataInterfaceFromCsv,
|
injectDataInterfaceFromCsv,
|
||||||
stopCsvDataInject,
|
stopCsvDataInject,
|
||||||
getCsvDataInjectStatus
|
getCsvDataInjectStatus,
|
||||||
|
startCollectData,
|
||||||
|
getCollectDataStatus,
|
||||||
|
stopCollectData
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user