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_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)
|
||||
{
|
||||
const char *text = reinterpret_cast<const char *>(sqlite3_column_text(stmt, column));
|
||||
|
@ -175,7 +175,7 @@ protected:
|
||||
if (data) {
|
||||
return std::to_string(data.value());
|
||||
} else {
|
||||
return "Unknown";
|
||||
return "0";
|
||||
}
|
||||
} else if constexpr (is_std_array_v<T>) {
|
||||
if (data) {
|
||||
@ -201,9 +201,9 @@ protected:
|
||||
{
|
||||
if constexpr (std::is_arithmetic_v<T>) {
|
||||
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
||||
data = std::stod(value);
|
||||
data = XNSim::safe_stod(value);
|
||||
} else {
|
||||
data = std::stoll(value);
|
||||
data = XNSim::safe_stoll(value);
|
||||
}
|
||||
} else if constexpr (is_std_array_v<T>) {
|
||||
// 解析输入字符串
|
||||
@ -238,7 +238,13 @@ protected:
|
||||
if (i > 0)
|
||||
ss << ",";
|
||||
if constexpr (std::is_arithmetic_v<T>) {
|
||||
ss << data[i];
|
||||
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];
|
||||
}
|
||||
} else if constexpr (is_std_array_v<T>) {
|
||||
ss << getStringFromStdArray(data[i]);
|
||||
} else {
|
||||
@ -267,9 +273,9 @@ protected:
|
||||
if constexpr (std::is_arithmetic_v<T>) {
|
||||
// 对于基本类型,直接转换
|
||||
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 {
|
||||
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>) {
|
||||
// 对于嵌套数组,递归处理
|
||||
|
@ -55,4 +55,41 @@ int safe_stoi(const std::string &str, int 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
|
@ -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_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)
|
||||
{
|
||||
const char *text = reinterpret_cast<const char *>(sqlite3_column_text(stmt, column));
|
||||
|
@ -175,7 +175,7 @@ protected:
|
||||
if (data) {
|
||||
return std::to_string(data.value());
|
||||
} else {
|
||||
return "Unknown";
|
||||
return "0";
|
||||
}
|
||||
} else if constexpr (is_std_array_v<T>) {
|
||||
if (data) {
|
||||
@ -201,9 +201,9 @@ protected:
|
||||
{
|
||||
if constexpr (std::is_arithmetic_v<T>) {
|
||||
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
||||
data = std::stod(value);
|
||||
data = XNSim::safe_stod(value);
|
||||
} else {
|
||||
data = std::stoll(value);
|
||||
data = XNSim::safe_stoll(value);
|
||||
}
|
||||
} else if constexpr (is_std_array_v<T>) {
|
||||
// 解析输入字符串
|
||||
@ -238,7 +238,13 @@ protected:
|
||||
if (i > 0)
|
||||
ss << ",";
|
||||
if constexpr (std::is_arithmetic_v<T>) {
|
||||
ss << data[i];
|
||||
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];
|
||||
}
|
||||
} else if constexpr (is_std_array_v<T>) {
|
||||
ss << getStringFromStdArray(data[i]);
|
||||
} else {
|
||||
@ -267,9 +273,9 @@ protected:
|
||||
if constexpr (std::is_arithmetic_v<T>) {
|
||||
// 对于基本类型,直接转换
|
||||
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 {
|
||||
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>) {
|
||||
// 对于嵌套数组,递归处理
|
||||
|
@ -48,6 +48,8 @@ add_library(XNMonitorServer SHARED
|
||||
DataInjectThread.cpp
|
||||
CSVDataInjectThread.h
|
||||
CSVDataInjectThread.cpp
|
||||
DataCollect.h
|
||||
DataCollect.cpp
|
||||
)
|
||||
|
||||
# 添加头文件搜索路径
|
||||
|
@ -11,7 +11,7 @@ CSVDataInjectThread::~CSVDataInjectThread()
|
||||
stop();
|
||||
}
|
||||
|
||||
bool CSVDataInjectThread::Initialize(std::vector<InjectDataInfo> injectDataInfos)
|
||||
bool CSVDataInjectThread::Initialize(std::vector<MonitorDataInfo> injectDataInfos)
|
||||
{
|
||||
m_csvFile.open(m_csvFilePath);
|
||||
if (!m_csvFile.is_open()) {
|
||||
@ -49,6 +49,7 @@ bool CSVDataInjectThread::Initialize(std::vector<InjectDataInfo> injectDataInfos
|
||||
if (dataMonitor->isInitialized()) {
|
||||
m_alreadyStartedMonitors[injectDataInfo.structName] = dataMonitor;
|
||||
} else {
|
||||
dataMonitor->Initialize(nullptr, 0, 0);
|
||||
m_notStartedMonitors[injectDataInfo.structName] = dataMonitor;
|
||||
}
|
||||
}
|
||||
@ -96,9 +97,9 @@ void CSVDataInjectThread::parseHeaderField(const std::string &headerField)
|
||||
if (injectDataInfo.interfaceNames[i] == csvHeaderField.fieldName) {
|
||||
csvHeaderField.structName = injectDataInfo.structName;
|
||||
if (injectDataInfo.arraySizes[i].second > 1) {
|
||||
csvHeaderField.arraySize1 = injectDataInfo.arraySizes[i].first;
|
||||
csvHeaderField.arraySize2 = injectDataInfo.arraySizes[i].second;
|
||||
} else {
|
||||
csvHeaderField.arraySize1 = 0;
|
||||
csvHeaderField.arraySize2 = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -161,7 +162,13 @@ void CSVDataInjectThread::updateData()
|
||||
std::stringstream ss(line);
|
||||
std::string 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); // 转换为毫秒
|
||||
|
||||
// 为每个结构体初始化数据
|
||||
@ -197,7 +204,7 @@ void CSVDataInjectThread::updateData()
|
||||
[m_headerFields[i].arrayIndex1] = field;
|
||||
} else if (m_headerFields[i].arrayDim == 2) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -248,9 +255,17 @@ void CSVDataInjectThread::threadFunc()
|
||||
}
|
||||
|
||||
// 执行数据注入
|
||||
for (const auto &[structName, dataMonitor] : m_alreadyStartedMonitors) {
|
||||
if (dataMonitor && m_data.find(structName) != m_data.end()) {
|
||||
dataMonitor->setDataByString(m_data[structName]);
|
||||
for (const auto &monitorDataInfo : m_injectDataInfos) {
|
||||
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()) {
|
||||
dataMonitor->setDataByString(m_data[monitorDataInfo.structName]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "DataMonitor.h"
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <fstream>
|
||||
|
||||
/**
|
||||
* @brief 数据注入线程类,用于持续向数据监控器注入数据
|
||||
@ -24,7 +19,7 @@ public:
|
||||
*/
|
||||
~CSVDataInjectThread();
|
||||
|
||||
bool Initialize(std::vector<InjectDataInfo> injectDataInfos);
|
||||
bool Initialize(std::vector<MonitorDataInfo> injectDataInfos);
|
||||
|
||||
/**
|
||||
* @brief 启动数据注入线程
|
||||
@ -55,7 +50,7 @@ private:
|
||||
private:
|
||||
std::string m_csvFilePath;
|
||||
std::ifstream m_csvFile;
|
||||
std::vector<InjectDataInfo> m_injectDataInfos;
|
||||
std::vector<MonitorDataInfo> m_injectDataInfos;
|
||||
std::vector<CSVHeaderField> m_headerFields;
|
||||
std::thread m_thread; ///< 数据注入线程
|
||||
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
|
||||
|
||||
#include "DataMonitor.h"
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
/**
|
||||
* @brief 数据注入线程类,用于持续向数据监控器注入数据
|
||||
|
@ -12,6 +12,10 @@
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <typeindex>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <fstream>
|
||||
#include <fastdds/dds/domain/DomainParticipant.hpp>
|
||||
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
|
||||
#include <fastdds/dds/topic/TypeSupport.hpp>
|
||||
@ -210,7 +214,7 @@ struct ModelDefinition {
|
||||
std::vector<std::shared_ptr<NamespaceDefinition>> namespaceDefinitions;
|
||||
};
|
||||
|
||||
struct InjectDataInfo {
|
||||
struct MonitorDataInfo {
|
||||
/**
|
||||
* @brief 结构体名称
|
||||
*/
|
||||
@ -253,7 +257,7 @@ struct CSVHeaderField {
|
||||
int arrayIndex2;
|
||||
|
||||
/**
|
||||
* @brief 一维数组大小
|
||||
* @brief 列大小
|
||||
*/
|
||||
int arraySize1;
|
||||
int arraySize2;
|
||||
};
|
@ -9,6 +9,7 @@
|
||||
#include "DataMonitorFactory.h"
|
||||
#include "DataInjectThread.h"
|
||||
#include "CSVDataInjectThread.h"
|
||||
#include "DataCollect.h"
|
||||
|
||||
// 全局变量
|
||||
static bool g_initialized = false;
|
||||
@ -21,6 +22,7 @@ SystemControl *systemControl = nullptr;
|
||||
bool g_systemControlStarted = false;
|
||||
|
||||
CSVDataInjectThread *g_csvDataInjectThread = nullptr;
|
||||
DataCollect *g_dataCollect = nullptr;
|
||||
|
||||
// 初始化函数实现
|
||||
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;
|
||||
g_csvDataInjectThread = nullptr;
|
||||
}
|
||||
std::vector<InjectDataInfo> injectDataInfos;
|
||||
std::vector<MonitorDataInfo> injectDataInfos;
|
||||
std::string injectDataInfoStr(injectDataInfo, injectDataInfoLen);
|
||||
try {
|
||||
nlohmann::json injectDataInfoJson = nlohmann::json::parse(injectDataInfoStr);
|
||||
for (const auto &[structName, interfaceInfo] : injectDataInfoJson.items()) {
|
||||
InjectDataInfo info;
|
||||
MonitorDataInfo info;
|
||||
info.structName = structName;
|
||||
for (const auto &[interfaceName, size] : interfaceInfo.items()) {
|
||||
info.interfaceNames.push_back(interfaceName);
|
||||
@ -722,12 +724,139 @@ int XN_StopCsvDataInject(char *infoMsg, int infoMsgSize)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int XNMONITORSERVER_EXPORT XN_StartCollectData(const char *structName, const int structNameLen,
|
||||
const char *interfaceName,
|
||||
const int interfaceNameLen, const char *csvFilePath,
|
||||
const int csvFilePathLen, char *infoMsg,
|
||||
int infoMsgSize)
|
||||
int XNMONITORSERVER_EXPORT XN_StartCollectData(const char *CollectDataInfo,
|
||||
const int CollectDataInfoLen,
|
||||
const char *dcsFilePath, const int dcsFilePathLen,
|
||||
char *infoMsg, int infoMsgSize)
|
||||
{
|
||||
// TODO: 持续采集数据并保存到csv文件接口
|
||||
return -1;
|
||||
if (!g_initialized) {
|
||||
if (infoMsg && infoMsgSize > 0) {
|
||||
strncpy(infoMsg, "DDSMonitor Not Initialized", infoMsgSize - 1);
|
||||
infoMsg[infoMsgSize - 1] = '\0';
|
||||
}
|
||||
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数据注入状态
|
||||
* @param infoMsg 错误信息
|
||||
* @param infoMsgSize 错误信息大小
|
||||
* @return 0: 成功, -1: 失败
|
||||
* @return 0: 成功, -1: 失败, 1: 正在注入
|
||||
*/
|
||||
int XNMONITORSERVER_EXPORT XN_GetCsvDataInjectStatus(char *infoMsg, int infoMsgSize);
|
||||
|
||||
@ -239,25 +239,37 @@ extern "C"
|
||||
//******************** csv数据采集 *********************
|
||||
|
||||
/**
|
||||
* @brief 持续采集数据并保存到csv文件接口
|
||||
* @param structName 结构体名称
|
||||
* @param structNameLen 结构体名称长度
|
||||
* @param interfaceName 接口名称JSON数组字符串
|
||||
* @param interfaceNameLen 接口名称JSON数组字符串长度
|
||||
* @param csvFilePath csv文件路径
|
||||
* @param csvFilePathLen csv文件路径长度
|
||||
* @param frequency 采集频率
|
||||
* @brief 读取dcs采集脚本并将数据保存到csv文件接口
|
||||
* @param CollectDataInfo 采集数据信息JSON数组字符串
|
||||
* @param CollectDataInfoLen 采集数据信息JSON数组字符串长度
|
||||
* @param dcsFilePath dcs文件路径
|
||||
* @param dcsFilePathLen dcs文件路径长度
|
||||
* @param infoMsg 错误信息
|
||||
* @param infoMsgSize 错误信息大小
|
||||
* @return 0: 成功, -1: 失败
|
||||
*/
|
||||
int XNMONITORSERVER_EXPORT XN_StartCollectData(const char *structName, const int structNameLen,
|
||||
const char *interfaceName,
|
||||
const int interfaceNameLen,
|
||||
const char *csvFilePath,
|
||||
const int csvFilePathLen, char *infoMsg,
|
||||
int XNMONITORSERVER_EXPORT XN_StartCollectData(const char *CollectDataInfo,
|
||||
const int CollectDataInfoLen,
|
||||
const char *dcsFilePath,
|
||||
const int dcsFilePathLen, char *infoMsg,
|
||||
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
|
||||
}
|
||||
#endif
|
@ -510,6 +510,20 @@ class DataCollection extends HTMLElement {
|
||||
background: #d9d9d9;
|
||||
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 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -654,8 +668,8 @@ class DataCollection extends HTMLElement {
|
||||
<div class="left-panel">
|
||||
<div class="panel-section">
|
||||
<div class="button-row">
|
||||
<button class="action-btn" id="loadScriptBtn">${this.scriptFile ? '卸载脚本' : '载入脚本'}</button>
|
||||
<button class="action-btn" id="startCollectBtn" ${!this.scriptFile ? 'disabled' : ''}>开始采集</button>
|
||||
<button class="action-btn ${this.scriptFile ? 'unload' : ''}" id="loadScriptBtn">${this.scriptFile ? '卸载脚本' : '载入脚本'}</button>
|
||||
<button class="action-btn ${this.collectStatus === 2 ? 'stop' : ''}" id="startCollectBtn" ${!this.scriptFile ? 'disabled' : ''}>${this.collectStatus === 2 ? '停止采集' : '开始采集'}</button>
|
||||
</div>
|
||||
<div class="input-row">
|
||||
<div class="input-group">
|
||||
@ -713,6 +727,78 @@ class DataCollection extends HTMLElement {
|
||||
this.isActive = true;
|
||||
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);
|
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 systemControlRoutes = require('./routes/SystemControl');
|
||||
const dataMonitorRoutes = require('./routes/DataMonitor');
|
||||
const dataCollectRoutes = require('./routes/DataCollect');
|
||||
|
||||
const app = express();
|
||||
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/system-control', systemControlRoutes);
|
||||
app.use('/api/data-monitor', dataMonitorRoutes);
|
||||
app.use('/api/data-collect', dataCollectRoutes);
|
||||
|
||||
// 接口配置页面路由
|
||||
app.get('/interface-config', (req, res) => {
|
||||
|
@ -62,7 +62,10 @@ try {
|
||||
'XN_StopInjectContinuous': ['int', [StringType, 'int', StringType, 'int']],
|
||||
'XN_InjectDataInterfaceFromCsv': ['int', [StringType, 'int', StringType, '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) {
|
||||
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 = {
|
||||
loginLib,
|
||||
monitorLib,
|
||||
@ -527,5 +588,8 @@ module.exports = {
|
||||
stopInjectContinuous,
|
||||
injectDataInterfaceFromCsv,
|
||||
stopCsvDataInject,
|
||||
getCsvDataInjectStatus
|
||||
getCsvDataInjectStatus,
|
||||
startCollectData,
|
||||
getCollectDataStatus,
|
||||
stopCollectData
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user