From e1b4139d0499f4827b593d4686dadc29882870c3 Mon Sep 17 00:00:00 2001 From: jinchao <383321154@qq.com> Date: Fri, 13 Jun 2025 15:12:15 +0800 Subject: [PATCH] =?UTF-8?q?V0.21.18.250613=5Falpha:=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=9B=91=E6=8E=A7=E7=9A=84CSV=E6=96=87=E4=BB=B6=E6=B3=A8?= =?UTF-8?q?=E5=85=A5=E5=8A=9F=E8=83=BD=E5=AE=8C=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Release/database/XNSim.db | Bin 1224704 -> 1224704 bytes Release/include/XNCore/XNDDSInterface.h | 2 +- XNCore/XNDDSInterface.h | 2 +- XNMonitorServer/CSVDataInjectThread.cpp | 143 ++++++++++++++++++++---- XNMonitorServer/CSVDataInjectThread.h | 11 +- XNMonitorServer/TypeDefine.h | 48 ++++++++ XNMonitorServer/XNMonitorInterface.cpp | 104 +++++++++++------ XNMonitorServer/XNMonitorInterface.h | 4 +- XNSimHtml/components/data-monitor.js | 49 ++++++-- XNSimHtml/routes/DataMonitor.js | 39 ++++++- 10 files changed, 329 insertions(+), 73 deletions(-) diff --git a/Release/database/XNSim.db b/Release/database/XNSim.db index 7167d3baa6eb267b27d710a28baa92f243b0d1f4..1aae7f283527f86e51345797b40eb4468a634038 100644 GIT binary patch delta 414 zcmZp8;MMTJYl1Z6?1?hYjI$dPS`!$zCNM2|&uqy%eLDLGCU+*@>D&E)G%pV~voxcD zo{^!Rp@rr4#ebQO3Nm*rbe(>NkJ(z)+|t~@!qn8#LN6yj-N?YmRM)^v*U&h`z|6|T z#LC1>&&_E%`#GKQ=@N)?WuyKE9;JeFL z#mB*$$a9nX`({Of$K0BY@s^y@isG_~P4ePUrywj#%qhr7G&Hp`G_W$6-X+VWP~SEG zMf2XL>w2CpX*bk+x_uLfw`Bga%`2X^E`2t2!}HxO@Au5+GSYjpf8En1y-#+vJ=xXs zeD@ZJ6ja&tZ4rLX`9-NucC39qtL4d#h9^5#y;!&Z<@9wgwsk$9+5=>~Tr}hTo}L%$ zmqOT2_RM;^e$R{cEmBV>^gUm_{@I+~r(Fx5Zdv(kSM%eBro@!o%)IuSGF(8+4a7V^ W%nQVPK+F%s0zfRd{ickN`eXp8R7 zGPivap*;>`y(8Aoz)XdO8FDF0U$iT={*T78I&?v;f!phXr%E(O5%)~fqdLcKn z{B&7vX0G-x{LDbi0>rF9%m&2lK+FNeoYTMXa|!UV^4w(LyUSO_$HAM(b91wz!f~GV en=)KL%nigKHZKtK0Wm)i3jnd;_M0+7>XQNf8$zc5 diff --git a/Release/include/XNCore/XNDDSInterface.h b/Release/include/XNCore/XNDDSInterface.h index 6206ebc..eb6103b 100644 --- a/Release/include/XNCore/XNDDSInterface.h +++ b/Release/include/XNCore/XNDDSInterface.h @@ -273,7 +273,7 @@ protected: } } else if constexpr (is_std_array_v) { // 对于嵌套数组,递归处理 - start_pos = setStdArrayFromString(data[i], value, start_pos + i); + start_pos = setStdArrayFromString(data[i], value, start_pos); } else { static_assert( std::is_arithmetic_v || is_std_array_v, diff --git a/XNCore/XNDDSInterface.h b/XNCore/XNDDSInterface.h index 6206ebc..eb6103b 100644 --- a/XNCore/XNDDSInterface.h +++ b/XNCore/XNDDSInterface.h @@ -273,7 +273,7 @@ protected: } } else if constexpr (is_std_array_v) { // 对于嵌套数组,递归处理 - start_pos = setStdArrayFromString(data[i], value, start_pos + i); + start_pos = setStdArrayFromString(data[i], value, start_pos); } else { static_assert( std::is_arithmetic_v || is_std_array_v, diff --git a/XNMonitorServer/CSVDataInjectThread.cpp b/XNMonitorServer/CSVDataInjectThread.cpp index 7b3b052..699d226 100644 --- a/XNMonitorServer/CSVDataInjectThread.cpp +++ b/XNMonitorServer/CSVDataInjectThread.cpp @@ -11,49 +11,96 @@ CSVDataInjectThread::~CSVDataInjectThread() stop(); } -bool CSVDataInjectThread::Initialize(std::vector structNames) +bool CSVDataInjectThread::Initialize(std::vector injectDataInfos) { m_csvFile.open(m_csvFilePath); if (!m_csvFile.is_open()) { return false; } + m_injectDataInfos = injectDataInfos; + std::string headerLine; if (!std::getline(m_csvFile, headerLine)) { return false; } - std::vector interfaceNames; + // 修改CSV头解析逻辑 std::stringstream ss(headerLine); std::string field; - std::getline(ss, field, ','); + std::getline(ss, field, ','); // 跳过第一列时间戳 + + // 使用getline读取所有字段,包括最后一个 while (std::getline(ss, field, ',')) { - interfaceNames.push_back(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); + } } - // 将结构体和接口名称一一对应 - if (structNames.size() != interfaceNames.size()) { - return false; - } - - for (int i = 0; i < structNames.size(); i++) { - m_structInterfaceMap[structNames[i]].push_back(interfaceNames[i]); - } - - for (const auto &[structName, interfaceNames] : m_structInterfaceMap) { - auto dataMonitor = DataMonitorFactory::GetInstance(structName); + for (const auto &injectDataInfo : m_injectDataInfos) { + auto dataMonitor = DataMonitorFactory::GetInstance(injectDataInfo.structName); if (dataMonitor == nullptr) { return false; } if (dataMonitor->isInitialized()) { - m_alreadyStartedMonitors[structName] = dataMonitor; + m_alreadyStartedMonitors[injectDataInfo.structName] = dataMonitor; } else { - m_notStartedMonitors[structName] = dataMonitor; + m_notStartedMonitors[injectDataInfo.structName] = dataMonitor; } } return true; } +void CSVDataInjectThread::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) { + // 处理一维数组 + size_t firstBracketPos = headerField.find('['); + size_t firstBracketEndPos = headerField.find(']'); + csvHeaderField.arrayIndex1 = std::stoi( + headerField.substr(firstBracketPos + 1, firstBracketEndPos - firstBracketPos - 1)); + csvHeaderField.arrayDim = 1; + + // 检查是否有第二个方括号,判断是否为二维数组 + if (headerField.find('[', firstBracketEndPos) != std::string::npos) { + size_t secondBracketPos = headerField.find('[', firstBracketEndPos); + size_t secondBracketEndPos = headerField.find(']', secondBracketPos); + csvHeaderField.arrayIndex2 = std::stoi(headerField.substr( + secondBracketPos + 1, secondBracketEndPos - secondBracketPos - 1)); + csvHeaderField.arrayDim = 2; + } + } + + for (const auto &injectDataInfo : m_injectDataInfos) { + for (int i = 0; i < injectDataInfo.interfaceNames.size(); i++) { + if (injectDataInfo.interfaceNames[i] == csvHeaderField.fieldName) { + csvHeaderField.structName = injectDataInfo.structName; + if (injectDataInfo.arraySizes[i].second > 1) { + csvHeaderField.arraySize1 = injectDataInfo.arraySizes[i].first; + } else { + csvHeaderField.arraySize1 = 0; + } + break; + } + } + } + if (csvHeaderField.structName.empty()) { + return; + } + + m_headerFields.push_back(csvHeaderField); +} + void CSVDataInjectThread::start() { std::lock_guard lock(m_mutex); @@ -107,14 +154,62 @@ void CSVDataInjectThread::updateData() double timeStamp = std::stod(field); m_nextExecuteTime = static_cast(timeStamp * 1000); // 转换为毫秒 - // 解析每个结构体的数据 - for (const auto &[structName, interfaceNames] : m_structInterfaceMap) { - std::unordered_map dataMap; - for (const auto &interfaceName : interfaceNames) { - std::getline(ss, field, ','); - dataMap[interfaceName] = field; + // 为每个结构体初始化数据 + std::unordered_map>> + tempDataMap; + for (const auto &injectDataInfo : m_injectDataInfos) { + std::unordered_map> dataMap; + for (int i = 0; i < injectDataInfo.interfaceNames.size(); i++) { + if (injectDataInfo.arraySizes[i].first > 1) { + for (int j = 0; j < injectDataInfo.arraySizes[i].first; j++) { + if (injectDataInfo.arraySizes[i].second > 1) { + for (int k = 0; k < injectDataInfo.arraySizes[i].second; k++) { + dataMap[injectDataInfo.interfaceNames[i]].push_back("0"); + } + } else { + dataMap[injectDataInfo.interfaceNames[i]].push_back("0"); + } + } + } else { + dataMap[injectDataInfo.interfaceNames[i]].push_back("0"); + } } - m_data[structName] = dataMap; + tempDataMap[injectDataInfo.structName] = dataMap; + } + + // 读取下一行数据 + for (int i = 0; i < m_headerFields.size(); i++) { + std::getline(ss, field, ','); + if (m_headerFields[i].arrayDim == 0) { + tempDataMap[m_headerFields[i].structName][m_headerFields[i].fieldName][0] = field; + } else if (m_headerFields[i].arrayDim == 1) { + tempDataMap[m_headerFields[i].structName][m_headerFields[i].fieldName] + [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].arrayIndex2] = field; + } + } + + // 将tempDataMap转换为m_data格式 + for (const auto &[structName, dataMap] : tempDataMap) { + std::unordered_map structData; + for (const auto &[interfaceName, values] : dataMap) { + if (values.empty()) { + structData[interfaceName] = "0"; + } else { + std::stringstream ss; + for (size_t i = 0; i < values.size(); ++i) { + ss << values[i]; + if (i < values.size() - 1) { + ss << ","; + } + } + structData[interfaceName] = ss.str(); + } + } + m_data[structName] = structData; } } diff --git a/XNMonitorServer/CSVDataInjectThread.h b/XNMonitorServer/CSVDataInjectThread.h index 9be1181..a447992 100644 --- a/XNMonitorServer/CSVDataInjectThread.h +++ b/XNMonitorServer/CSVDataInjectThread.h @@ -24,7 +24,7 @@ public: */ ~CSVDataInjectThread(); - bool Initialize(std::vector structNames); + bool Initialize(std::vector injectDataInfos); /** * @brief 启动数据注入线程 @@ -50,10 +50,13 @@ private: */ void threadFunc(); + void parseHeaderField(const std::string &headerField); + private: std::string m_csvFilePath; std::ifstream m_csvFile; - std::unordered_map> m_structInterfaceMap; + std::vector m_injectDataInfos; + std::vector m_headerFields; std::thread m_thread; ///< 数据注入线程 std::atomic m_running; ///< 线程运行标志 std::mutex m_mutex; ///< 互斥锁 @@ -65,6 +68,4 @@ private: std::unordered_map> m_data; ///< 要注入的数据 std::atomic m_nextExecuteTime; ///< 下一次执行的时间点 -}; - -using CSVDataInjectThreadPtr = std::shared_ptr; \ No newline at end of file +}; \ No newline at end of file diff --git a/XNMonitorServer/TypeDefine.h b/XNMonitorServer/TypeDefine.h index eb55a78..39dd4f2 100755 --- a/XNMonitorServer/TypeDefine.h +++ b/XNMonitorServer/TypeDefine.h @@ -209,3 +209,51 @@ struct ModelDefinition { */ std::vector> namespaceDefinitions; }; + +struct InjectDataInfo { + /** + * @brief 结构体名称 + */ + std::string structName; + /** + * @brief 接口名称 + */ + std::vector interfaceNames; + + /** + * @brief 数组大小 + */ + std::vector> arraySizes; +}; + +struct CSVHeaderField { + /** + * @brief 字段名 + */ + std::string fieldName; + + /** + * @brief 结构体名称 + */ + std::string structName; + + /** + * @brief 数组维度 + */ + int arrayDim; + + /** + * @brief 数组索引1 + */ + int arrayIndex1; + + /** + * @brief 数组索引2 + */ + int arrayIndex2; + + /** + * @brief 一维数组大小 + */ + int arraySize1; +}; \ No newline at end of file diff --git a/XNMonitorServer/XNMonitorInterface.cpp b/XNMonitorServer/XNMonitorInterface.cpp index bfde074..db4e49c 100644 --- a/XNMonitorServer/XNMonitorInterface.cpp +++ b/XNMonitorServer/XNMonitorInterface.cpp @@ -20,7 +20,7 @@ bool g_modelInfoMonitorStarted = false; SystemControl *systemControl = nullptr; bool g_systemControlStarted = false; -CSVDataInjectThreadPtr g_csvDataInjectThread; +CSVDataInjectThread *g_csvDataInjectThread = nullptr; // 初始化函数实现 int XN_Initialize(const char *domainId, int domainIdLen, char *errorMsg, int errorMsgSize) @@ -584,26 +584,40 @@ int XN_StopInjectContinuous(const char *structName, const int structNameLen, cha } // 从csv文件中注入数据接口 -int XNMONITORSERVER_EXPORT XN_InjectDataInterfaceFromCsv(const char *structName, - const int structNameLen, +int XNMONITORSERVER_EXPORT XN_InjectDataInterfaceFromCsv(const char *injectDataInfo, + const int injectDataInfoLen, const char *csvFilePath, const int csvFilePathLen, char *infoMsg, int infoMsgSize) { - std::vector structNames; - std::string structNameStr(structName, structNameLen); - try { - nlohmann::json structNamesJson = nlohmann::json::parse(structNameStr); - if (!structNamesJson.is_array()) { - if (infoMsg && infoMsgSize > 0) { - strncpy(infoMsg, "Invalid struct name format - expected JSON array", - infoMsgSize - 1); - infoMsg[infoMsgSize - 1] = '\0'; - } - return -1; + if (!g_initialized) { + if (infoMsg && infoMsgSize > 0) { + strncpy(infoMsg, "DDSMonitor Not Initialized", infoMsgSize - 1); + infoMsg[infoMsgSize - 1] = '\0'; } - for (const auto &structNameJson : structNamesJson) { - structNames.push_back(structNameJson.get()); + return -1; + } + if (g_csvDataInjectThread != nullptr) { + g_csvDataInjectThread->stop(); + delete g_csvDataInjectThread; + g_csvDataInjectThread = nullptr; + } + std::vector injectDataInfos; + std::string injectDataInfoStr(injectDataInfo, injectDataInfoLen); + try { + nlohmann::json injectDataInfoJson = nlohmann::json::parse(injectDataInfoStr); + for (const auto &[structName, interfaceInfo] : injectDataInfoJson.items()) { + InjectDataInfo 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(), size[1].get()}); + } else { + info.arraySizes.push_back({0, 0}); + } + } + injectDataInfos.push_back(info); } } catch (const nlohmann::json::parse_error &e) { if (infoMsg && infoMsgSize > 0) { @@ -628,31 +642,42 @@ int XNMONITORSERVER_EXPORT XN_InjectDataInterfaceFromCsv(const char *structName, } return -1; } - if (g_csvDataInjectThread == nullptr) { - g_csvDataInjectThread = std::make_shared(csvFilePathStr); - bool ret = g_csvDataInjectThread->Initialize(structNames); - if (ret) { - g_csvDataInjectThread->start(); - } else { + try { + g_csvDataInjectThread = new CSVDataInjectThread(csvFilePathStr); + if (!g_csvDataInjectThread->Initialize(injectDataInfos)) { + delete g_csvDataInjectThread; + g_csvDataInjectThread = nullptr; if (infoMsg && infoMsgSize > 0) { strncpy(infoMsg, "CSV 注入线程初始化失败", infoMsgSize - 1); infoMsg[infoMsgSize - 1] = '\0'; } return -1; } - } else { + g_csvDataInjectThread->start(); + } catch (const std::exception &e) { + if (g_csvDataInjectThread) { + delete g_csvDataInjectThread; + g_csvDataInjectThread = nullptr; + } if (infoMsg && infoMsgSize > 0) { - strncpy(infoMsg, "CSV 注入线程已在运行", infoMsgSize - 1); + strncpy(infoMsg, e.what(), infoMsgSize - 1); infoMsg[infoMsgSize - 1] = '\0'; } return -1; } - std::cout << "CSV注入线程已启动" << std::endl; return 0; } int XN_GetCsvDataInjectStatus(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_csvDataInjectThread == nullptr) { if (infoMsg && infoMsgSize > 0) { strncpy(infoMsg, "CSV 注入线程已不存在", infoMsgSize - 1); @@ -660,14 +685,20 @@ int XN_GetCsvDataInjectStatus(char *infoMsg, int infoMsgSize) } return -1; } - if (g_csvDataInjectThread->isRunning()) { - return 1; - } - return 0; + + return g_csvDataInjectThread->isRunning() ? 1 : 0; } int XN_StopCsvDataInject(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_csvDataInjectThread == nullptr) { if (infoMsg && infoMsgSize > 0) { strncpy(infoMsg, "CSV 注入线程已不存在", infoMsgSize - 1); @@ -676,9 +707,18 @@ int XN_StopCsvDataInject(char *infoMsg, int infoMsgSize) return -1; } - g_csvDataInjectThread->stop(); - g_csvDataInjectThread.reset(); // 释放智能指针 - std::cout << "CSV注入线程已停止" << std::endl; + try { + g_csvDataInjectThread->stop(); + delete g_csvDataInjectThread; + g_csvDataInjectThread = nullptr; + } catch (const std::exception &e) { + if (infoMsg && infoMsgSize > 0) { + strncpy(infoMsg, e.what(), infoMsgSize - 1); + infoMsg[infoMsgSize - 1] = '\0'; + } + return -1; + } + return 0; } diff --git a/XNMonitorServer/XNMonitorInterface.h b/XNMonitorServer/XNMonitorInterface.h index 9b03bc9..7ae3f54 100644 --- a/XNMonitorServer/XNMonitorInterface.h +++ b/XNMonitorServer/XNMonitorInterface.h @@ -214,8 +214,8 @@ extern "C" * @param infoMsgSize 错误信息大小 * @return 0: 成功, -1: 失败 */ - int XNMONITORSERVER_EXPORT XN_InjectDataInterfaceFromCsv(const char *structName, - const int structNameLen, + int XNMONITORSERVER_EXPORT XN_InjectDataInterfaceFromCsv(const char *InjectDataInfo, + const int InjectDataInfoLen, const char *csvFilePath, const int csvFilePathLen, char *infoMsg, int infoMsgSize); diff --git a/XNSimHtml/components/data-monitor.js b/XNSimHtml/components/data-monitor.js index 1b1eab1..767ea74 100644 --- a/XNSimHtml/components/data-monitor.js +++ b/XNSimHtml/components/data-monitor.js @@ -21,6 +21,7 @@ class DataMonitor extends HTMLElement { isInjecting: false, fileName: '', structNames: [], + structData: {}, // 用于存储结构体数据 filePath: '' // 添加文件路径 }; this.monitorStatus = 0; // 监控状态:0-未监控,1-监控中,2-错误 @@ -419,7 +420,6 @@ class DataMonitor extends HTMLElement { throw new Error(`获取CSV注入状态失败: ${csvStatusResponse.status} ${csvStatusResponse.statusText}`); } const csvStatusData = await csvStatusResponse.json(); - console.log('CSV注入状态:', csvStatusData.data); // 如果状态为0,触发停止注入 if (csvStatusData.data === 0) { // 模拟点击停止CSV注入按钮 @@ -1916,7 +1916,7 @@ class DataMonitor extends HTMLElement { 'Content-Type': 'application/json' }, body: JSON.stringify({ - structName: JSON.stringify(this.csvState.structNames), + structName: JSON.stringify(this.csvState.structData), csvFilePath: this.csvState.filePath }) }); @@ -2127,7 +2127,7 @@ class DataMonitor extends HTMLElement { // 检查每个头部是否在接口表中 const missingInterfaces = []; const invalidInterfaces = []; - const structNames = []; // 用于按顺序收集结构体名称 + const structData = {}; // 用于存储结构体数据 // 检查第一个接口(时间)是否在接口表中 const firstHeader = validateResult.headers[0]; @@ -2141,14 +2141,49 @@ class DataMonitor extends HTMLElement { // 检查其他接口是否在接口表中 for (let i = 1; i < validateResult.headers.length; i++) { const header = validateResult.headers[i]; + // 提取接口名称和数组索引 + const match = header.match(/^(.+?)(?:\[(\d+)\](?:\[(\d+)\])?)?$/); + if (!match) continue; + + const baseInterfaceName = match[1]; + const index1 = match[2] ? parseInt(match[2]) : null; + const index2 = match[3] ? parseInt(match[3]) : null; + const interfaceInfo = this.interfaces.find(interfaceItem => - interfaceItem.InterfaceName === header + interfaceItem.InterfaceName === baseInterfaceName ); + if (!interfaceInfo) { missingInterfaces.push(header); } else { - // 按顺序收集结构体名称 - structNames.push(interfaceInfo.ModelStructName); + // 构造结构体数据 + if (!structData[interfaceInfo.ModelStructName]) { + structData[interfaceInfo.ModelStructName] = {}; + } + + if (!structData[interfaceInfo.ModelStructName][baseInterfaceName]) { + const size1 = interfaceInfo.InterfaceArraySize_1 || 0; + const size2 = interfaceInfo.InterfaceArraySize_2 || 0; + + structData[interfaceInfo.ModelStructName][baseInterfaceName] = [size1, size2]; + } + + // 检查数组索引是否越界 + if (index1 !== null) { + if (index2 !== null) { + // 二维数组 + if (index1 >= interfaceInfo.InterfaceArraySize_1 || index2 >= interfaceInfo.InterfaceArraySize_2) { + console.warn(`接口 ${baseInterfaceName} 的数组索引 [${index1}][${index2}] 超出范围`); + continue; + } + } else { + // 一维数组 + if (index1 >= interfaceInfo.InterfaceArraySize_1) { + console.warn(`接口 ${baseInterfaceName} 的数组索引 [${index1}] 超出范围`); + continue; + } + } + } } } @@ -2183,7 +2218,7 @@ class DataMonitor extends HTMLElement { // 更新CSV状态 this.csvState.fileName = result.file.name; - this.csvState.structNames = structNames; + this.csvState.structData = structData; // 保存结构体数据 this.csvState.filePath = result.file.path; } diff --git a/XNSimHtml/routes/DataMonitor.js b/XNSimHtml/routes/DataMonitor.js index 5201415..ee123b7 100644 --- a/XNSimHtml/routes/DataMonitor.js +++ b/XNSimHtml/routes/DataMonitor.js @@ -326,7 +326,7 @@ router.post('/stop-all-continuous', async (req, res) => { /** * @brief 从CSV文件注入数据 * @route POST /api/data-monitor/inject-csv - * @param {string} structName - 结构体名称 + * @param {string} structName - 结构体数据JSON字符串,格式为 {structName1: {interfaceName1: [size1, size2], ...}, structName2: ...} * @param {string} csvFilePath - CSV文件路径 * @returns {Object} 返回注入结果 */ @@ -340,6 +340,43 @@ router.post('/inject-csv', async (req, res) => { }); } + // 解析并验证 structName JSON 字符串 + let structData; + try { + structData = JSON.parse(structName); + // 验证数据结构 + if (typeof structData !== 'object' || structData === null) { + return res.status(400).json({ + success: false, + message: '结构体数据格式错误:必须是有效的对象' + }); + } + // 验证每个结构体的接口数据 + for (const [structName, interfaces] of Object.entries(structData)) { + if (typeof interfaces !== 'object' || interfaces === null) { + return res.status(400).json({ + success: false, + message: `结构体 ${structName} 的接口数据格式错误:必须是有效的对象` + }); + } + for (const [interfaceName, sizes] of Object.entries(interfaces)) { + if (!Array.isArray(sizes) || sizes.length !== 2 || + typeof sizes[0] !== 'number' || typeof sizes[1] !== 'number' || + sizes[0] < 0 || sizes[1] < 0) { + return res.status(400).json({ + success: false, + message: `接口 ${interfaceName} 的数据格式错误:必须是包含两个非负数字的数组 [size1, size2]` + }); + } + } + } + } catch (error) { + return res.status(400).json({ + success: false, + message: `结构体数据解析失败: ${error.message}` + }); + } + const result = systemMonitor.injectDataInterfaceFromCsv(structName, csvFilePath); if (result.includes('失败')) { return res.status(500).json({ success: false, message: result });