From a78e1bb85f250727783d20a75f493cbfbdd146ed Mon Sep 17 00:00:00 2001 From: jinchao <383321154@qq.com> Date: Tue, 24 Jun 2025 16:07:07 +0800 Subject: [PATCH] =?UTF-8?q?V0.34.0.250624=5Falpha:=E6=9B=B4=E6=94=B9?= =?UTF-8?q?=E4=BA=86=E7=9B=91=E6=8E=A7=E5=90=8E=E7=AB=AFC++=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../XNGroundHandling_2.0.143.1.zip | Bin 0 -> 4939 bytes .../XNGroundHandling_2.0.143.1/CMakeLists.txt | 52 +++ .../XNGroundHandling.cpp | 319 +++++++++++++++ .../XNGroundHandling.h | 30 ++ .../XNGroundHandling.mcfg | 13 + .../XNGroundHandling_global.h | 11 + .../XNGroundHandling_p.h | 19 + .../XNWeightBalance_2.0.14.6.zip | Bin 0 -> 4273 bytes .../XNWeightBalance_2.0.14.6/CMakeLists.txt | 52 +++ .../XNWeightBalance.cpp | 106 +++++ .../XNWeightBalance.h | 30 ++ .../XNWeightBalance.mcfg | 13 + .../XNWeightBalance_global.h | 11 + .../XNWeightBalance_p.h | 19 + .../Models/XNGroundHandling_V1.0.0.0.mcfg | 13 - .../Models/XNGroundHandling_V2.0.143.1.mcfg | 20 +- .../Models/XNWeightBalance_V1.0.0.0.mcfg | 13 - .../Models/XNWeightBalance_V2.0.14.6.mcfg | 13 + .../Models/libXNGroundHandling.so.1.0.0.0 | Bin 41208 -> 0 bytes .../Models/libXNGroundHandling.so.2.0.143.1 | Bin 41208 -> 178440 bytes .../Models/libXNWeightBalance.so.1.0.0.0 | Bin 40976 -> 0 bytes .../Models/libXNWeightBalance.so.2.0.14.6 | Bin 0 -> 173016 bytes .../C909_V1/PluginCode/C909_V1_plugin.cpp | 84 ++++ .../C909_V1/PluginCode/CMakeLists.txt | 44 ++ Release/database/XNSim.db | Bin 1224704 -> 1224704 bytes .../include/XNMonitor/CSVDataInjectThread.h | 66 +++ Release/include/XNMonitor/DataCollect.h | 52 +++ Release/include/XNMonitor/DataInjectThread.h | 63 +++ Release/include/XNMonitor/DataMonitor.h | 80 ++++ .../include/XNMonitor/DataMonitorFactory.h | 25 ++ Release/include/XNMonitor/ModelInfoMonitor.h | 37 ++ Release/include/XNMonitor/PluginGenerator.h | 66 +++ Release/include/XNMonitor/PluginInterface.h | 34 ++ Release/include/XNMonitor/PluginManager.h | 51 +++ Release/include/XNMonitor/SystemControl.h | 24 ++ Release/include/XNMonitor/SystemInfoMonitor.h | 52 +++ Release/include/XNMonitor/TopicManager.h | 310 ++++++++++++++ Release/include/XNMonitor/TypeDefine.h | 270 +++++++++++++ .../XNMonitor/XNDataReaderListenerImpl.h | 51 +++ .../include/XNMonitor/XNMonitorInterface.h | 281 +++++++++++++ .../XNMonitor/XNMonitorServer_global.h | 7 + XNModelGenServer/XNModelGen.cpp | 9 +- XNMonitorServer/.vscode/settings.json | 3 +- XNMonitorServer/CMakeLists.txt | 17 +- XNMonitorServer/DataMonitorFactory.cpp | 24 +- XNMonitorServer/DataMonitorFactory.h | 97 +---- XNMonitorServer/PluginGenerator.cpp | 379 ++++++++++++++++++ XNMonitorServer/PluginGenerator.h | 66 +++ XNMonitorServer/PluginInterface.h | 34 ++ XNMonitorServer/PluginManager.cpp | 126 ++++++ XNMonitorServer/PluginManager.h | 51 +++ XNMonitorServer/SystemControl.cpp | 2 +- XNMonitorServer/TypeDefine.h | 7 + XNMonitorServer/XNMonitorInterface.cpp | 121 +++++- XNMonitorServer/XNMonitorInterface.h | 10 +- XNMonitorServer/test/CMakeLists.txt | 56 +++ XNMonitorServer/test/test_initialize.cpp | 96 +++++ XNSimPortal/components/run-sim.js | 9 +- XNSimPortal/routes/DDSMonitor.js | 20 +- XNSimPortal/utils/xnCoreService.js | 6 +- 60 files changed, 3301 insertions(+), 163 deletions(-) create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1.zip create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/CMakeLists.txt create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.cpp create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.h create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.mcfg create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling_global.h create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling_p.h create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6.zip create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/CMakeLists.txt create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.cpp create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.h create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.mcfg create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance_global.h create mode 100644 Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance_p.h delete mode 100644 Release/Configuration/C909_V1/Models/XNGroundHandling_V1.0.0.0.mcfg delete mode 100644 Release/Configuration/C909_V1/Models/XNWeightBalance_V1.0.0.0.mcfg create mode 100644 Release/Configuration/C909_V1/Models/XNWeightBalance_V2.0.14.6.mcfg delete mode 100644 Release/Configuration/C909_V1/Models/libXNGroundHandling.so.1.0.0.0 delete mode 100644 Release/Configuration/C909_V1/Models/libXNWeightBalance.so.1.0.0.0 create mode 100644 Release/Configuration/C909_V1/Models/libXNWeightBalance.so.2.0.14.6 create mode 100644 Release/Configuration/C909_V1/PluginCode/C909_V1_plugin.cpp create mode 100644 Release/Configuration/C909_V1/PluginCode/CMakeLists.txt create mode 100644 Release/include/XNMonitor/CSVDataInjectThread.h create mode 100644 Release/include/XNMonitor/DataCollect.h create mode 100644 Release/include/XNMonitor/DataInjectThread.h create mode 100644 Release/include/XNMonitor/DataMonitor.h create mode 100644 Release/include/XNMonitor/DataMonitorFactory.h create mode 100644 Release/include/XNMonitor/ModelInfoMonitor.h create mode 100644 Release/include/XNMonitor/PluginGenerator.h create mode 100644 Release/include/XNMonitor/PluginInterface.h create mode 100644 Release/include/XNMonitor/PluginManager.h create mode 100644 Release/include/XNMonitor/SystemControl.h create mode 100644 Release/include/XNMonitor/SystemInfoMonitor.h create mode 100644 Release/include/XNMonitor/TopicManager.h create mode 100644 Release/include/XNMonitor/TypeDefine.h create mode 100644 Release/include/XNMonitor/XNDataReaderListenerImpl.h create mode 100644 Release/include/XNMonitor/XNMonitorInterface.h create mode 100644 Release/include/XNMonitor/XNMonitorServer_global.h create mode 100644 XNMonitorServer/PluginGenerator.cpp create mode 100644 XNMonitorServer/PluginGenerator.h create mode 100644 XNMonitorServer/PluginInterface.h create mode 100644 XNMonitorServer/PluginManager.cpp create mode 100644 XNMonitorServer/PluginManager.h create mode 100644 XNMonitorServer/test/CMakeLists.txt create mode 100644 XNMonitorServer/test/test_initialize.cpp diff --git a/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1.zip b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1.zip new file mode 100644 index 0000000000000000000000000000000000000000..98e157f410bc48d230ec4097ee1bf5131aee3abc GIT binary patch literal 4939 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 ZR^?E2yt8Wr01%vgbkF|qnk7H~_8*;->IVP- literal 0 HcmV?d00001 diff --git a/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/CMakeLists.txt b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/CMakeLists.txt new file mode 100644 index 0000000..ea7764f --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.16) + +project(XNGroundHandling LANGUAGES CXX) + +set(MODEL_VERSION "2.0.143.1") + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +if(DEFINED ENV{XNCore}) + set(XNCore_PATH $ENV{XNCore}) +else() + message(FATAL_ERROR "Environment variable XNCore is not set.") +endif() + +include_directories(${XNCore_PATH}/include) +include_directories(${XNCore_PATH}/IDL) + +add_library(XNGroundHandling SHARED + XNGroundHandling_global.h + XNGroundHandling.cpp + XNGroundHandling.h + XNGroundHandling_p.h +) + +set_target_properties(XNGroundHandling PROPERTIES + LIBRARY_OUTPUT_NAME "libXNGroundHandling.so.2.0.143.1" + PREFIX "" + SUFFIX "" + SKIP_BUILD_RPATH TRUE + BUILD_WITH_INSTALL_RPATH TRUE +) + +target_link_libraries(XNGroundHandling PRIVATE + ${XNCore_PATH}/lib/libXNCore.so + ${XNCore_PATH}/lib/libC909_V1_Interface.so + dl +) + +target_compile_definitions(XNGroundHandling PRIVATE XNGROUNDHANDLING_LIBRARY) + +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${XNCore_PATH}/Configuration/C909_V1/Models" CACHE PATH "Install path prefix" FORCE) +endif() + +install(TARGETS XNGroundHandling BUNDLE DESTINATION . LIBRARY DESTINATION . RUNTIME DESTINATION .) + +file(GLOB CONFIG_FILE "*.mcfg") + +install(FILES ${CONFIG_FILE} DESTINATION ${CMAKE_INSTALL_PREFIX} RENAME "${CMAKE_PROJECT_NAME}_V${MODEL_VERSION}.mcfg") + diff --git a/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.cpp b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.cpp new file mode 100644 index 0000000..0d9acb2 --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.cpp @@ -0,0 +1,319 @@ +#include "XNGroundHandling.h" +#include "XNGroundHandling_p.h" +#include + +XN_MODEL_INITIALIZE(XNGroundHandling) + +XNGroundHandling::XNGroundHandling() : XNModelObject(new XNGroundHandlingPrivate()) +{ +} + +XNGroundHandling::~XNGroundHandling() +{ +} + +XNGroundHandling::XNGroundHandling(PrivateType *p) : XNModelObject(p) +{ +} + +void XNGroundHandling::Initialize() +{ + T_D(); + SuperType::Initialize(); + if (d->_dynamicLib) { + d->_fun = (FunctionType)dlsym(d->_dynamicLib, d->_entryPointName.c_str()); + if (!d->_fun) { + LOG_WARNING( + "Failed to resolve _Z29SACSCGroundHandlingEntryPointP20ComacDataStructure_S"); + } + } + /* 在这里进行其它初始化 */ +} + +void XNGroundHandling::PrepareForExecute() +{ + T_D(); + SuperType::PrepareForExecute(); + InitializeData(); + d->_inputInterface.Initialize(GetFramework(), GetUniqueId(), 1); + d->_outputInterface.Initialize(GetFramework(), GetUniqueId(), 2); + d->_heartbeatInterface.Initialize(GetFramework(), GetUniqueId(), 2); + /* 在这里进行其它运行前准备工作 */ +} + +void XNGroundHandling::StepUpdate() +{ + T_D(); + SuperType::StepUpdate(); + if (d->_fun) { + d->_inputInterface.getData(d->_data.input_ground); + d->_fun(&d->_data); + d->_outputInterface.setData(d->_data.output_ground); + d->_heartbeatInterface.setData(&d->_data); + } + /* 在这里进行其它运行时计算 */ +} + +void XNGroundHandling::InitializeData() +{ + T_D(); + d->_data.input_ground = new input_ground_S; + // TODO: 在这里初始化输入数据 + d->_data.input_ground->l_04_i_gdcomac_brake_torq_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.input_ground->l_04_i_gdcomac_brake_torq_f8[i] = new double[2]; + } + d->_data.input_ground->l_04_i_gdcomac_gear_f8 = new double[3]; + d->_data.input_ground->l_04_i_gdcomac_gsteer_f8 = new double[3]; + d->_data.input_ground->l_04_i_gdcomac_tire_pres_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.input_ground->l_04_i_gdcomac_tire_pres_f8[i] = new double[2]; + } + d->_data.input_ground->l_04_i_gdcomac_contdep_f8 = new double[7]; + d->_data.input_ground->l_04_i_gdcomac_brake_temp_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.input_ground->l_04_i_gdcomac_brake_temp_f8[i] = new double[2]; + } + d->_data.input_ground->l_04_i_gdcomac_tire_tburst_l1 = new unsigned char *[3]; + for (int i = 0; i < 3; i++) { + d->_data.input_ground->l_04_i_gdcomac_tire_tburst_l1[i] = new unsigned char[2]; + } + d->_data.input_ground->l_04_i_gdcomac_tire_tflat_l1 = new unsigned char *[3]; + for (int i = 0; i < 3; i++) { + d->_data.input_ground->l_04_i_gdcomac_tire_tflat_l1[i] = new unsigned char[2]; + } + d->_data.input_ground->l_04_i_gdcomac_rcon_ci_f8 = new double[14]; + + d->_data.output_ground = new output_ground_S; + // TODO: 在这里初始化输出数据 + d->_data.output_ground->l_04_o_gdcomac_fygs_f8 = new double[3]; + d->_data.output_ground->l_04_o_gdcomac_mzgs_f8 = new double[3]; + d->_data.output_ground->l_04_o_gdcomac_mu_f8 = new double[3]; + d->_data.output_ground->l_04_o_gdcomac_dstroke_f8 = new double[3]; + d->_data.output_ground->l_04_o_gdcomac_sr_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.output_ground->l_04_o_gdcomac_sr_f8[i] = new double[2]; + } + d->_data.output_ground->l_04_o_gdcomac_sy_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.output_ground->l_04_o_gdcomac_sy_f8[i] = new double[2]; + } + d->_data.output_ground->l_04_o_gdcomac_sx_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.output_ground->l_04_o_gdcomac_sx_f8[i] = new double[2]; + } + d->_data.output_ground->l_04_o_gdcomac_xft_f8 = new double[3]; + d->_data.output_ground->l_04_o_gdcomac_yft_f8 = new double[3]; + d->_data.output_ground->l_04_o_gdcomac_zft_f8 = new double[3]; + d->_data.output_ground->l_04_o_gdcomac_tire_vel_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.output_ground->l_04_o_gdcomac_tire_vel_f8[i] = new double[2]; + } + d->_data.output_ground->l_04_o_gdcomac_tire_temp_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.output_ground->l_04_o_gdcomac_tire_temp_f8[i] = new double[2]; + } + d->_data.output_ground->l_04_o_gdcomac_tire_burst_l1 = new unsigned char *[3]; + for (int i = 0; i < 3; i++) { + d->_data.output_ground->l_04_o_gdcomac_tire_burst_l1[i] = new unsigned char[2]; + } + d->_data.output_ground->l_04_o_gdcomac_utirew_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.output_ground->l_04_o_gdcomac_utirew_f8[i] = new double[2]; + } + d->_data.output_ground->l_04_o_gdcomac_vtirew_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.output_ground->l_04_o_gdcomac_vtirew_f8[i] = new double[2]; + } + d->_data.output_ground->l_04_o_gdcomac_whl_omega_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.output_ground->l_04_o_gdcomac_whl_omega_f8[i] = new double[2]; + } + d->_data.output_ground->l_04_o_gdcomac_dstruc_f8 = new double[6]; + d->_data.output_ground->l_04_o_gdcomac_nd_f8 = new double[3]; + d->_data.output_ground->l_04_o_gdcomac_wor_par_f8 = new double[3]; + d->_data.output_ground->l_04_o_gdcomac_vczt_f8 = new double *[3]; + for (int i = 0; i < 3; i++) { + d->_data.output_ground->l_04_o_gdcomac_vczt_f8[i] = new double[2]; + } + d->_data.groundhandling_model_heartbeat = 0; +} + +void XNGroundHandling::ReleaseData() +{ + T_D(); + // TODO: 在这里释放输入数据 + + if (d->_data.input_ground) { + if (d->_data.input_ground->l_04_i_gdcomac_brake_torq_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.input_ground->l_04_i_gdcomac_brake_torq_f8[i]) { + delete[] d->_data.input_ground->l_04_i_gdcomac_brake_torq_f8[i]; + } + } + delete[] d->_data.input_ground->l_04_i_gdcomac_brake_torq_f8; + } + if (d->_data.input_ground->l_04_i_gdcomac_gear_f8) { + delete[] d->_data.input_ground->l_04_i_gdcomac_gear_f8; + } + if (d->_data.input_ground->l_04_i_gdcomac_gsteer_f8) { + delete[] d->_data.input_ground->l_04_i_gdcomac_gsteer_f8; + } + if (d->_data.input_ground->l_04_i_gdcomac_tire_pres_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.input_ground->l_04_i_gdcomac_tire_pres_f8[i]) { + delete[] d->_data.input_ground->l_04_i_gdcomac_tire_pres_f8[i]; + } + } + delete[] d->_data.input_ground->l_04_i_gdcomac_tire_pres_f8; + } + if (d->_data.input_ground->l_04_i_gdcomac_contdep_f8) { + delete[] d->_data.input_ground->l_04_i_gdcomac_contdep_f8; + } + if (d->_data.input_ground->l_04_i_gdcomac_brake_temp_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.input_ground->l_04_i_gdcomac_brake_temp_f8[i]) { + delete[] d->_data.input_ground->l_04_i_gdcomac_brake_temp_f8[i]; + } + } + delete[] d->_data.input_ground->l_04_i_gdcomac_brake_temp_f8; + } + if (d->_data.input_ground->l_04_i_gdcomac_tire_tburst_l1) { + for (int i = 0; i < 3; i++) { + if (d->_data.input_ground->l_04_i_gdcomac_tire_tburst_l1[i]) { + delete[] d->_data.input_ground->l_04_i_gdcomac_tire_tburst_l1[i]; + } + } + delete[] d->_data.input_ground->l_04_i_gdcomac_tire_tburst_l1; + } + if (d->_data.input_ground->l_04_i_gdcomac_tire_tflat_l1) { + for (int i = 0; i < 3; i++) { + if (d->_data.input_ground->l_04_i_gdcomac_tire_tflat_l1[i]) { + delete[] d->_data.input_ground->l_04_i_gdcomac_tire_tflat_l1[i]; + } + } + delete[] d->_data.input_ground->l_04_i_gdcomac_tire_tflat_l1; + } + if (d->_data.input_ground->l_04_i_gdcomac_rcon_ci_f8) { + delete[] d->_data.input_ground->l_04_i_gdcomac_rcon_ci_f8; + } + delete d->_data.input_ground; + d->_data.input_ground = nullptr; + } + // TODO: 在这里释放输出数据 + + if (d->_data.output_ground) { + if (d->_data.output_ground->l_04_o_gdcomac_fygs_f8) { + delete[] d->_data.output_ground->l_04_o_gdcomac_fygs_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_mzgs_f8) { + delete[] d->_data.output_ground->l_04_o_gdcomac_mzgs_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_mu_f8) { + delete[] d->_data.output_ground->l_04_o_gdcomac_mu_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_dstroke_f8) { + delete[] d->_data.output_ground->l_04_o_gdcomac_dstroke_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_sr_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.output_ground->l_04_o_gdcomac_sr_f8[i]) { + delete[] d->_data.output_ground->l_04_o_gdcomac_sr_f8[i]; + } + } + delete[] d->_data.output_ground->l_04_o_gdcomac_sr_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_sy_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.output_ground->l_04_o_gdcomac_sy_f8[i]) { + delete[] d->_data.output_ground->l_04_o_gdcomac_sy_f8[i]; + } + } + delete[] d->_data.output_ground->l_04_o_gdcomac_sy_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_sx_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.output_ground->l_04_o_gdcomac_sx_f8[i]) { + delete[] d->_data.output_ground->l_04_o_gdcomac_sx_f8[i]; + } + } + delete[] d->_data.output_ground->l_04_o_gdcomac_sx_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_xft_f8) { + delete[] d->_data.output_ground->l_04_o_gdcomac_xft_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_yft_f8) { + delete[] d->_data.output_ground->l_04_o_gdcomac_yft_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_zft_f8) { + delete[] d->_data.output_ground->l_04_o_gdcomac_zft_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_tire_vel_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.output_ground->l_04_o_gdcomac_tire_vel_f8[i]) { + delete[] d->_data.output_ground->l_04_o_gdcomac_tire_vel_f8[i]; + } + } + delete[] d->_data.output_ground->l_04_o_gdcomac_tire_vel_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_tire_temp_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.output_ground->l_04_o_gdcomac_tire_temp_f8[i]) { + delete[] d->_data.output_ground->l_04_o_gdcomac_tire_temp_f8[i]; + } + } + delete[] d->_data.output_ground->l_04_o_gdcomac_tire_temp_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_tire_burst_l1) { + for (int i = 0; i < 3; i++) { + if (d->_data.output_ground->l_04_o_gdcomac_tire_burst_l1[i]) { + delete[] d->_data.output_ground->l_04_o_gdcomac_tire_burst_l1[i]; + } + } + delete[] d->_data.output_ground->l_04_o_gdcomac_tire_burst_l1; + } + if (d->_data.output_ground->l_04_o_gdcomac_utirew_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.output_ground->l_04_o_gdcomac_utirew_f8[i]) { + delete[] d->_data.output_ground->l_04_o_gdcomac_utirew_f8[i]; + } + } + delete[] d->_data.output_ground->l_04_o_gdcomac_utirew_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_vtirew_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.output_ground->l_04_o_gdcomac_vtirew_f8[i]) { + delete[] d->_data.output_ground->l_04_o_gdcomac_vtirew_f8[i]; + } + } + delete[] d->_data.output_ground->l_04_o_gdcomac_vtirew_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_whl_omega_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.output_ground->l_04_o_gdcomac_whl_omega_f8[i]) { + delete[] d->_data.output_ground->l_04_o_gdcomac_whl_omega_f8[i]; + } + } + delete[] d->_data.output_ground->l_04_o_gdcomac_whl_omega_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_dstruc_f8) { + delete[] d->_data.output_ground->l_04_o_gdcomac_dstruc_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_nd_f8) { + delete[] d->_data.output_ground->l_04_o_gdcomac_nd_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_wor_par_f8) { + delete[] d->_data.output_ground->l_04_o_gdcomac_wor_par_f8; + } + if (d->_data.output_ground->l_04_o_gdcomac_vczt_f8) { + for (int i = 0; i < 3; i++) { + if (d->_data.output_ground->l_04_o_gdcomac_vczt_f8[i]) { + delete[] d->_data.output_ground->l_04_o_gdcomac_vczt_f8[i]; + } + } + delete[] d->_data.output_ground->l_04_o_gdcomac_vczt_f8; + } + delete d->_data.output_ground; + d->_data.output_ground = nullptr; + } +} diff --git a/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.h b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.h new file mode 100644 index 0000000..dc35fbe --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.h @@ -0,0 +1,30 @@ +#pragma once + +#include "XNGroundHandling_global.h" +#include + +struct XNGroundHandlingPrivate; + +class XNGROUNDHANDLING_EXPORT XNGroundHandling : public XNModelObject +{ +XN_METATYPE(XNGroundHandling, XNModelObject) +XN_DECLARE_PRIVATE(XNGroundHandling) +public: + XNGroundHandling(); + virtual ~XNGroundHandling(); + +protected: + XNGroundHandling(PrivateType *p); + +public: + virtual void Initialize() override; + virtual void PrepareForExecute() override; + virtual void StepUpdate() override; + +protected: + void InitializeData(); + void ReleaseData(); +}; + +XNCLASS_PTR_DECLARE(XNGroundHandling) + diff --git a/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.mcfg b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.mcfg new file mode 100644 index 0000000..dafd5d7 --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling.mcfg @@ -0,0 +1,13 @@ + + + XNGroundHandling + ATA04GroundHandling + Jin + 2.0.143.1 + 2025-04-03 14:47:42 + 2025-04-03 14:47:42 + 0 + 99 + ATA04_SACSCGroundHandling_2.0.143.1H_20250506/libSACSCGroundHandling.so.2.0.143.1H + + diff --git a/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling_global.h b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling_global.h new file mode 100644 index 0000000..e989176 --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling_global.h @@ -0,0 +1,11 @@ +#ifndef XNGROUNDHANDLING_GLOBAL_H +#define XNGROUNDHANDLING_GLOBAL_H + +#if defined(XNGROUNDHANDLING_LIBRARY) +#define XNGROUNDHANDLING_EXPORT __attribute__((visibility("default"))) +#else +#define XNGROUNDHANDLING_EXPORT __attribute__((visibility("default"))) +#endif + +#endif // XNGROUNDHANDLING_GLOBAL_H + diff --git a/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling_p.h b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling_p.h new file mode 100644 index 0000000..7d4d0e5 --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNGroundHandling_2.0.143.1/XNGroundHandling_p.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../../Packages/ATA04_SACSCGroundHandling_2.0.143.1H_20250506/std_04_dll.h" +#include "../../IDL/C909_V1_Interface.h" + +using InterfaceType = ComacDataStructure_S; +typedef void (*FunctionType)(InterfaceType *); + +struct XNGroundHandlingPrivate : public XNModelObjectPrivate{ + FunctionType _fun = nullptr; + InterfaceType _data; + std::string _entryPointName = "_Z29SACSCGroundHandlingEntryPointP20ComacDataStructure_S"; + std::mutex _mutex; + XNSim::C909::ATA04::GroundHandling_input_Interface _inputInterface; + XNSim::C909::ATA04::GroundHandling_output_Interface _outputInterface; + XNSim::C909::ATA04::GroundHandling_heartbeat_Interface _heartbeatInterface; +}; + diff --git a/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6.zip b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6.zip new file mode 100644 index 0000000000000000000000000000000000000000..fb3925e2781eab9b49953ba948716c2f10036cb8 GIT binary patch literal 4273 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 literal 0 HcmV?d00001 diff --git a/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/CMakeLists.txt b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/CMakeLists.txt new file mode 100644 index 0000000..cd9ff57 --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.16) + +project(XNWeightBalance LANGUAGES CXX) + +set(MODEL_VERSION "2.0.14.6") + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +if(DEFINED ENV{XNCore}) + set(XNCore_PATH $ENV{XNCore}) +else() + message(FATAL_ERROR "Environment variable XNCore is not set.") +endif() + +include_directories(${XNCore_PATH}/include) +include_directories(${XNCore_PATH}/IDL) + +add_library(XNWeightBalance SHARED + XNWeightBalance_global.h + XNWeightBalance.cpp + XNWeightBalance.h + XNWeightBalance_p.h +) + +set_target_properties(XNWeightBalance PROPERTIES + LIBRARY_OUTPUT_NAME "libXNWeightBalance.so.2.0.14.6" + PREFIX "" + SUFFIX "" + SKIP_BUILD_RPATH TRUE + BUILD_WITH_INSTALL_RPATH TRUE +) + +target_link_libraries(XNWeightBalance PRIVATE + ${XNCore_PATH}/lib/libXNCore.so + ${XNCore_PATH}/lib/libC909_V1_Interface.so + dl +) + +target_compile_definitions(XNWeightBalance PRIVATE XNWEIGHTBALANCE_LIBRARY) + +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${XNCore_PATH}/Configuration/C909_V1/Models" CACHE PATH "Install path prefix" FORCE) +endif() + +install(TARGETS XNWeightBalance BUNDLE DESTINATION . LIBRARY DESTINATION . RUNTIME DESTINATION .) + +file(GLOB CONFIG_FILE "*.mcfg") + +install(FILES ${CONFIG_FILE} DESTINATION ${CMAKE_INSTALL_PREFIX} RENAME "${CMAKE_PROJECT_NAME}_V${MODEL_VERSION}.mcfg") + diff --git a/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.cpp b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.cpp new file mode 100644 index 0000000..4b098e4 --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.cpp @@ -0,0 +1,106 @@ +#include "XNWeightBalance.h" +#include "XNWeightBalance_p.h" +#include + +XN_MODEL_INITIALIZE(XNWeightBalance) + +XNWeightBalance::XNWeightBalance() : XNModelObject(new XNWeightBalancePrivate()) +{ +} + +XNWeightBalance::~XNWeightBalance() +{ +} + +XNWeightBalance::XNWeightBalance(PrivateType *p) : XNModelObject(p) +{ +} + +void XNWeightBalance::Initialize() +{ + T_D(); + SuperType::Initialize(); + if (d->_dynamicLib) { + d->_fun = (FunctionType)dlsym(d->_dynamicLib, d->_entryPointName.c_str()); + if (!d->_fun) { + LOG_WARNING( + "Failed to resolve _Z28SACSCWeightBalanceEntryPointP20ComacDataStructure_S"); + } + } + /* 在这里进行其它初始化 */ +} + +void XNWeightBalance::PrepareForExecute() +{ + T_D(); + SuperType::PrepareForExecute(); + InitializeData(); + d->_inputInterface.Initialize(GetFramework(), GetUniqueId(), 1); + d->_outputInterface.Initialize(GetFramework(), GetUniqueId(), 2); + d->_heartbeatInterface.Initialize(GetFramework(), GetUniqueId(), 2); + /* 在这里进行其它运行前准备工作 */ +} + +void XNWeightBalance::StepUpdate() +{ + T_D(); + SuperType::StepUpdate(); + if (d->_fun) { + d->_inputInterface.getData(d->_data.input_weight); + d->_fun(&d->_data); + d->_outputInterface.setData(d->_data.output_weight); + d->_heartbeatInterface.setData(&d->_data); + } + /* 在这里进行其它运行时计算 */ +} + +void XNWeightBalance::InitializeData() +{ + T_D(); + d->_data.input_weight = new input_weight_S; + // TODO: 在这里初始化输入数据 + d->_data.input_weight->l_04_i_wbcomac_acset_tankfuel_f4 = new float[20]; + d->_data.input_weight->l_04_i_wbcomac_eng_efsep_l1 = new unsigned char[4]; + d->_data.input_weight->l_04_i_wbcomac_fuel_f8 = new double[20]; + d->_data.input_weight->l_04_i_wbcomac_kice_f8 = new double[20]; + + d->_data.output_weight = new output_weight_S; + // TODO: 在这里初始化输出数据 + d->_data.output_weight->l_04_o_wbcomac_fuel_cmd_f8 = new double[20]; + d->_data.output_weight->l_04_o_wbcomac_ice_eng_f8 = new double[4]; + + d->_data.weightbody_model_heartbeat = 0; +} + +void XNWeightBalance::ReleaseData() +{ + T_D(); + if (d->_data.input_weight) { + // TODO: 在这里释放输入数据 + if (d->_data.input_weight->l_04_i_wbcomac_acset_tankfuel_f4) { + delete[] d->_data.input_weight->l_04_i_wbcomac_acset_tankfuel_f4; + } + if (d->_data.input_weight->l_04_i_wbcomac_eng_efsep_l1) { + delete[] d->_data.input_weight->l_04_i_wbcomac_eng_efsep_l1; + } + if (d->_data.input_weight->l_04_i_wbcomac_fuel_f8) { + delete[] d->_data.input_weight->l_04_i_wbcomac_fuel_f8; + } + if (d->_data.input_weight->l_04_i_wbcomac_kice_f8) { + delete[] d->_data.input_weight->l_04_i_wbcomac_kice_f8; + } + delete d->_data.input_weight; + d->_data.input_weight = nullptr; + } + if (d->_data.output_weight) { + // TODO: 在这里释放输出数据 + if (d->_data.output_weight->l_04_o_wbcomac_fuel_cmd_f8) { + delete[] d->_data.output_weight->l_04_o_wbcomac_fuel_cmd_f8; + } + if (d->_data.output_weight->l_04_o_wbcomac_ice_eng_f8) { + delete[] d->_data.output_weight->l_04_o_wbcomac_ice_eng_f8; + } + delete d->_data.output_weight; + d->_data.output_weight = nullptr; + } +} diff --git a/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.h b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.h new file mode 100644 index 0000000..be3a712 --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.h @@ -0,0 +1,30 @@ +#pragma once + +#include "XNWeightBalance_global.h" +#include + +struct XNWeightBalancePrivate; + +class XNWEIGHTBALANCE_EXPORT XNWeightBalance : public XNModelObject +{ +XN_METATYPE(XNWeightBalance, XNModelObject) +XN_DECLARE_PRIVATE(XNWeightBalance) +public: + XNWeightBalance(); + virtual ~XNWeightBalance(); + +protected: + XNWeightBalance(PrivateType *p); + +public: + virtual void Initialize() override; + virtual void PrepareForExecute() override; + virtual void StepUpdate() override; + +protected: + void InitializeData(); + void ReleaseData(); +}; + +XNCLASS_PTR_DECLARE(XNWeightBalance) + diff --git a/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.mcfg b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.mcfg new file mode 100644 index 0000000..99abe38 --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance.mcfg @@ -0,0 +1,13 @@ + + + XNWeightBalance + ATA04WeightBalance + Jin + 2.0.14.6 + 2025-04-27 13:57:50 + 2025-04-27 13:57:50 + 0 + 99 + ATA04_WeightBalance_2.0.14.6H_20241106/libSACSCWeightBalance.so + + diff --git a/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance_global.h b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance_global.h new file mode 100644 index 0000000..84583ae --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance_global.h @@ -0,0 +1,11 @@ +#ifndef XNWEIGHTBALANCE_GLOBAL_H +#define XNWEIGHTBALANCE_GLOBAL_H + +#if defined(XNWEIGHTBALANCE_LIBRARY) +#define XNWEIGHTBALANCE_EXPORT __attribute__((visibility("default"))) +#else +#define XNWEIGHTBALANCE_EXPORT __attribute__((visibility("default"))) +#endif + +#endif // XNWEIGHTBALANCE_GLOBAL_H + diff --git a/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance_p.h b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance_p.h new file mode 100644 index 0000000..67b7fcb --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNWeightBalance_2.0.14.6/XNWeightBalance_p.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../../Packages/ATA04_WeightBalance_2.0.14.6H_20241106/std_04_dll.h" +#include "../../IDL/C909_V1_Interface.h" + +using InterfaceType = ComacDataStructure_S; +typedef void (*FunctionType)(InterfaceType *); + +struct XNWeightBalancePrivate : public XNModelObjectPrivate{ + FunctionType _fun = nullptr; + InterfaceType _data; + std::string _entryPointName = "_Z28SACSCWeightBalanceEntryPointP20ComacDataStructure_S"; + std::mutex _mutex; + XNSim::C909::ATA04::WeightBalance_input_Interface _inputInterface; + XNSim::C909::ATA04::WeightBalance_output_Interface _outputInterface; + XNSim::C909::ATA04::WeightBalance_heartbeat_Interface _heartbeatInterface; +}; + diff --git a/Release/Configuration/C909_V1/Models/XNGroundHandling_V1.0.0.0.mcfg b/Release/Configuration/C909_V1/Models/XNGroundHandling_V1.0.0.0.mcfg deleted file mode 100644 index 99fb1a1..0000000 --- a/Release/Configuration/C909_V1/Models/XNGroundHandling_V1.0.0.0.mcfg +++ /dev/null @@ -1,13 +0,0 @@ - - - XNGroundHandling - ATA04地面操纵模型 - Jin - 1.0.0 - 2025-02-19 16:22:17 - 2025-02-19 16:22:19 - 0-0 - 99 - ATA04_SACSCGroundHandling_2.0.143.1H_20250506/libSACSCGroundHandling.so.2.0.143.1H - - diff --git a/Release/Configuration/C909_V1/Models/XNGroundHandling_V2.0.143.1.mcfg b/Release/Configuration/C909_V1/Models/XNGroundHandling_V2.0.143.1.mcfg index 99fb1a1..dafd5d7 100644 --- a/Release/Configuration/C909_V1/Models/XNGroundHandling_V2.0.143.1.mcfg +++ b/Release/Configuration/C909_V1/Models/XNGroundHandling_V2.0.143.1.mcfg @@ -1,13 +1,13 @@ - XNGroundHandling - ATA04地面操纵模型 - Jin - 1.0.0 - 2025-02-19 16:22:17 - 2025-02-19 16:22:19 - 0-0 - 99 - ATA04_SACSCGroundHandling_2.0.143.1H_20250506/libSACSCGroundHandling.so.2.0.143.1H - + XNGroundHandling + ATA04GroundHandling + Jin + 2.0.143.1 + 2025-04-03 14:47:42 + 2025-04-03 14:47:42 + 0 + 99 + ATA04_SACSCGroundHandling_2.0.143.1H_20250506/libSACSCGroundHandling.so.2.0.143.1H + diff --git a/Release/Configuration/C909_V1/Models/XNWeightBalance_V1.0.0.0.mcfg b/Release/Configuration/C909_V1/Models/XNWeightBalance_V1.0.0.0.mcfg deleted file mode 100644 index 85e8ac9..0000000 --- a/Release/Configuration/C909_V1/Models/XNWeightBalance_V1.0.0.0.mcfg +++ /dev/null @@ -1,13 +0,0 @@ - - - XNWeightBalance - ATA04质量模型 - Jin - 1.0.0 - 2025-02-20 09:29:18 - 2025-02-20 09:29:20 - 0-0 - 99 - ATA04_WeightBalance_2.0.14.6H_20241106/libSACSCWeightBalance.so - - diff --git a/Release/Configuration/C909_V1/Models/XNWeightBalance_V2.0.14.6.mcfg b/Release/Configuration/C909_V1/Models/XNWeightBalance_V2.0.14.6.mcfg new file mode 100644 index 0000000..99abe38 --- /dev/null +++ b/Release/Configuration/C909_V1/Models/XNWeightBalance_V2.0.14.6.mcfg @@ -0,0 +1,13 @@ + + + XNWeightBalance + ATA04WeightBalance + Jin + 2.0.14.6 + 2025-04-27 13:57:50 + 2025-04-27 13:57:50 + 0 + 99 + ATA04_WeightBalance_2.0.14.6H_20241106/libSACSCWeightBalance.so + + diff --git a/Release/Configuration/C909_V1/Models/libXNGroundHandling.so.1.0.0.0 b/Release/Configuration/C909_V1/Models/libXNGroundHandling.so.1.0.0.0 deleted file mode 100644 index 5d62bbaec27fd8ddf37a3c16709804459c637991..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41208 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 E07JT2RR910 diff --git a/Release/Configuration/C909_V1/Models/libXNWeightBalance.so.1.0.0.0 b/Release/Configuration/C909_V1/Models/libXNWeightBalance.so.1.0.0.0 deleted file mode 100644 index 0395e4f857544ef7e7689e84c0853fe109d306ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40976 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 diff --git a/Release/Configuration/C909_V1/Models/libXNWeightBalance.so.2.0.14.6 b/Release/Configuration/C909_V1/Models/libXNWeightBalance.so.2.0.14.6 new file mode 100644 index 0000000000000000000000000000000000000000..be5c9c2143c7442810c5866b9f0f17a6b84bdb56 GIT binary patch literal 173016 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 literal 0 HcmV?d00001 diff --git a/Release/Configuration/C909_V1/PluginCode/C909_V1_plugin.cpp b/Release/Configuration/C909_V1/PluginCode/C909_V1_plugin.cpp new file mode 100644 index 0000000..3a2fd50 --- /dev/null +++ b/Release/Configuration/C909_V1/PluginCode/C909_V1_plugin.cpp @@ -0,0 +1,84 @@ +#include +#include +// C909_V1接口头文件 - 只在插件中包含 +#include "../IDL/C909_V1_Interface.h" + +// 插件信息 +static PluginInfo plugin_info = {"C909_V1", "", DATAMONITOR_PLUGIN_INTERFACE_VERSION}; + +// 支持的接口列表 +static const char *supported_interfaces[] = { + "Aerodynamics_input", + "GroundHandling_input", + "GroundHandling_output", + "Aerodynamics_output", + "WeightBalance_input", + "WeightBalance_output", + "GroundHandling_heartbeat", + "WeightBalance_heartbeat", + "Aerodynamics_heartbeat" +}; + +static const int interface_count = sizeof(supported_interfaces) / sizeof(supported_interfaces[0]); + +// 导出的插件函数 +extern "C" +{ + + PluginInfo *get_plugin_info() + { + return &plugin_info; + } + + DataMonitorBasePtr create_monitor(const char *interfaceName) + { + std::string name(interfaceName); + + if (name == "Aerodynamics_input") { + return std::make_shared>(); + } else + if (name == "GroundHandling_input") { + return std::make_shared>(); + } else + if (name == "GroundHandling_output") { + return std::make_shared>(); + } else + if (name == "Aerodynamics_output") { + return std::make_shared>(); + } else + if (name == "WeightBalance_input") { + return std::make_shared>(); + } else + if (name == "WeightBalance_output") { + return std::make_shared>(); + } else + if (name == "GroundHandling_heartbeat") { + return std::make_shared>(); + } else + if (name == "WeightBalance_heartbeat") { + return std::make_shared>(); + } else + if (name == "Aerodynamics_heartbeat") { + return std::make_shared>(); + } + return nullptr; + } + + void destroy_monitor(const char *interfaceName) + { + // 智能指针会自动管理内存,这里可以添加额外的清理逻辑 + } + + const char **get_supported_interfaces(int *count) + { + if (count) { + *count = interface_count; + } + return const_cast(supported_interfaces); + } + + void free_string_array(const char **array) + { + // 这里不需要释放,因为使用的是静态数组 + } +} diff --git a/Release/Configuration/C909_V1/PluginCode/CMakeLists.txt b/Release/Configuration/C909_V1/PluginCode/CMakeLists.txt new file mode 100644 index 0000000..420e4d9 --- /dev/null +++ b/Release/Configuration/C909_V1/PluginCode/CMakeLists.txt @@ -0,0 +1,44 @@ +# CMakeLists.txt for C909_V1_Monitor plugin +cmake_minimum_required(VERSION 3.10) + +# 设置项目名称 +project(C909_V1_Monitor_plugin) + +# 设置C++标准 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 查找必要的包 +find_package(PkgConfig REQUIRED) +find_package(FastDDS REQUIRED) +if(DEFINED ENV{XNCore}) + set(XNCore_PATH $ENV{XNCore}) +else() + message(FATAL_ERROR "Environment variable XNCore is not set.") +endif() +include_directories(${XNCore_PATH}/include) +# 创建插件库 +add_library(C909_V1_Monitor SHARED + C909_V1_plugin.cpp +) + +# 链接库 +target_link_libraries(C909_V1_Monitor + fastcdr + fastdds + OpenSSL::SSL + OpenSSL::Crypto + ${XNCore_PATH}/lib/libC909_V1_Interface.so + ${XNCore_PATH}/lib/libXNMonitorServer.so +) + +target_compile_definitions(C909_V1_Monitor PRIVATE C909_V1_Monitor_LIBRARY) +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${XNCore_PATH}/Configuration/C909_V1/Plugins" CACHE PATH "Install path prefix" FORCE) +endif() +include(GNUInstallDirs) +install(TARGETS C909_V1_Monitor + BUNDLE DESTINATION . + LIBRARY DESTINATION . + RUNTIME DESTINATION . +) diff --git a/Release/database/XNSim.db b/Release/database/XNSim.db index d1135b37e4ee9b4c0d0ce864fec1edbdba15dea1..bff11536980d536703df88e86583e6d5fa598c05 100644 GIT binary patch delta 621 zcmZp8;MMTJYl1Yd00RS~HUk5L3lNJ>)G%ih*qE^7z5omV8=wG#82@knZ~Pzm-)t6C zxXsU`6T121d;SD2UZ4~s-yR0OJ(~puHu2RrIfijcDvHZ`HrI+9=oy>n85kKDnd%yt z=^B|N<`iTk8k$)d7y;3<*;}4X+4*Ew+w<8IpY<(&I-&3R>h;dr+Rx_nKJ8j)sP|-N z_tVba=d)U{%0JoB_pEQ})BeRz_w0Jnu?t_i=WO}@nrY@=X2&ib(^sMY1fKp)3-gJGXGh}1crcS{Ry{8=L9nY injectDataInfos); + + /** + * @brief 启动数据注入线程 + */ + void start(); + + /** + * @brief 停止数据注入线程 + */ + void stop(); + + /** + * @brief 从CSV文件读取下一行数据并更新执行时间 + * 如果文件读取完毕,将停止线程 + */ + void updateData(); + + bool isRunning() const; + +private: + /** + * @brief 线程执行函数 + */ + void threadFunc(); + + void parseHeaderField(const std::string &headerField); + +private: + std::string m_csvFilePath; + std::ifstream m_csvFile; + std::vector m_injectDataInfos; + std::vector m_headerFields; + std::thread m_thread; ///< 数据注入线程 + std::atomic m_running; ///< 线程运行标志 + std::mutex m_mutex; ///< 互斥锁 + std::condition_variable m_cv; ///< 条件变量 + std::unordered_map + m_alreadyStartedMonitors; ///< 已经启动的数据监控器 + std::unordered_map + m_notStartedMonitors; ///< 未启动的数据监控器 + std::unordered_map> + m_data; ///< 要注入的数据 + std::atomic m_nextExecuteTime; ///< 下一次执行的时间点 +}; \ No newline at end of file diff --git a/Release/include/XNMonitor/DataCollect.h b/Release/include/XNMonitor/DataCollect.h new file mode 100644 index 0000000..4fcae71 --- /dev/null +++ b/Release/include/XNMonitor/DataCollect.h @@ -0,0 +1,52 @@ +#pragma once + +#include "DataMonitor.h" + +class DataCollect +{ +public: + DataCollect(); + ~DataCollect(); + +public: + bool Initialize(std::vector 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 m_collectDataInfos; + std::vector m_headerFields; + std::thread m_thread; ///< 数据采集线程 + std::atomic m_running; ///< 线程运行标志 + std::mutex m_mutex; ///< 互斥锁 + std::condition_variable m_cv; ///< 条件变量 + std::unordered_map + m_alreadyStartedMonitors; ///< 已经启动的数据监控器 + std::unordered_map + m_notStartedMonitors; ///< 未启动的数据监控器 + std::unordered_map> + m_data; ///< 采集到的数据 + std::atomic m_nextExecuteTime; ///< 下一次执行的时间点 + int m_collectDuration; ///< 采集时长(秒) + int m_collectFrequency; ///< 采集频率(Hz) +}; \ No newline at end of file diff --git a/Release/include/XNMonitor/DataInjectThread.h b/Release/include/XNMonitor/DataInjectThread.h new file mode 100644 index 0000000..1e55fec --- /dev/null +++ b/Release/include/XNMonitor/DataInjectThread.h @@ -0,0 +1,63 @@ +#pragma once + +#include "DataMonitor.h" + +/** + * @brief 数据注入线程类,用于持续向数据监控器注入数据 + */ +class DataInjectThread +{ +public: + /** + * @brief 构造函数 + * @param dataMonitor 数据监控器指针 + * @param data 要注入的数据 + * @param frequency 注入频率(Hz) + */ + DataInjectThread(DataMonitorBasePtr dataMonitor, + std::unordered_map data, double frequency); + + /** + * @brief 析构函数 + */ + ~DataInjectThread(); + + /** + * @brief 启动数据注入线程 + */ + void start(); + + /** + * @brief 停止数据注入线程 + */ + void stop(); + + /** + * @brief 更新要注入的数据 + * @param data 新的数据 + */ + void updateData(const std::unordered_map &data); + + /** + * @brief 更新注入频率 + * @param frequency 新的频率(Hz) + */ + void updateFrequency(double frequency); + +private: + /** + * @brief 线程执行函数 + */ + void threadFunc(); + +private: + std::thread m_thread; ///< 数据注入线程 + std::atomic m_running; ///< 线程运行标志 + std::mutex m_mutex; ///< 互斥锁 + std::condition_variable m_cv; ///< 条件变量 + DataMonitorBasePtr m_dataMonitor; ///< 数据监控器指针 + std::unordered_map m_data; ///< 要注入的数据 + std::atomic m_frequency; ///< 注入频率 +}; + +using DataInjectThreadPtr = std::shared_ptr; \ No newline at end of file diff --git a/Release/include/XNMonitor/DataMonitor.h b/Release/include/XNMonitor/DataMonitor.h new file mode 100644 index 0000000..68d82bb --- /dev/null +++ b/Release/include/XNMonitor/DataMonitor.h @@ -0,0 +1,80 @@ +#pragma once + +#include "XNMonitorServer_global.h" +#include "TypeDefine.h" +#include "TopicManager.h" + +#define THISUNUSED(x) (void)(x) + +class XNFramework; +using XNFrameworkPtr = std::shared_ptr; + +class XNMONITORSERVER_EXPORT DataMonitorBase +{ +public: + virtual void Initialize(XNFrameworkPtr framework, uint32_t modelId, uint32_t DDS_type) = 0; + virtual std::unordered_map + getStringData(std::vector varNames) = 0; + virtual void setDataByString(std::unordered_map data) = 0; + virtual bool isInitialized() { return _isInitialized; } + +protected: + bool _isInitialized = false; +}; + +template +class XNMONITORSERVER_EXPORT DataMonitorProduct : public T, public DataMonitorBase +{ +public: + DataMonitorProduct() : T() {}; + virtual ~DataMonitorProduct() + { + try { + if (auto topicManager = TopicManager::Instance()) { + topicManager->unregisterSubscriber(T::topic_name); + topicManager->unregisterPublisher(T::topic_name); + } + } catch (...) { + return; + } + _isInitialized = false; + }; + + virtual void Initialize(XNFrameworkPtr framework, uint32_t modelId, uint32_t DDS_type) override + { + THISUNUSED(framework); + THISUNUSED(modelId); + THISUNUSED(DDS_type); + XNDDSErrorCode ret = + TopicManager::Instance()->registerSubscriber( + T::topic_name, + std::bind(&DataMonitorProduct::inputDataListener, this, std::placeholders::_1)); + if (ret != XNDDSErrorCode::SUCCESS) { + return; + } + ret = TopicManager::Instance()->registerPublisher( + T::topic_name, this->dataWriter); + if (ret != XNDDSErrorCode::SUCCESS || this->dataWriter == nullptr) { + return; + } + _isInitialized = true; + }; + + virtual std::unordered_map + getStringData(std::vector varNames) override + { + if (!isInitialized()) { + return {}; + } + return T::getStringData(varNames); + } + virtual void setDataByString(std::unordered_map data) override + { + if (!isInitialized()) { + return; + } + T::setDataByString(data); + } +}; + +using DataMonitorBasePtr = std::shared_ptr; diff --git a/Release/include/XNMonitor/DataMonitorFactory.h b/Release/include/XNMonitor/DataMonitorFactory.h new file mode 100644 index 0000000..a981523 --- /dev/null +++ b/Release/include/XNMonitor/DataMonitorFactory.h @@ -0,0 +1,25 @@ +#pragma once + +#include "XNMonitorServer_global.h" +#include "DataMonitor.h" + +/** + * @brief DataMonitor工厂类,用于创建不同类型的DataMonitor实例 + */ +class XNMONITORSERVER_EXPORT DataMonitorFactory +{ +public: + static DataMonitorBasePtr GetInstance(const std::string &interfaceName); + + static void ReleaseInstance(const std::string &interfaceName); + + // 检查接口是否存在 + static bool HasInterface(const std::string &interfaceName); + + // 获取所有已注册的接口名称 + static std::vector GetRegisteredInterfaces(); + +private: + DataMonitorFactory() = delete; + ~DataMonitorFactory() = delete; +}; diff --git a/Release/include/XNMonitor/ModelInfoMonitor.h b/Release/include/XNMonitor/ModelInfoMonitor.h new file mode 100644 index 0000000..7c9a4fb --- /dev/null +++ b/Release/include/XNMonitor/ModelInfoMonitor.h @@ -0,0 +1,37 @@ +#pragma once + +#include "TopicManager.h" +#include + +class XNMONITORSERVER_EXPORT ModelInfoMonitor +{ +public: + explicit ModelInfoMonitor() {} + virtual ~ModelInfoMonitor(); + +public: + std::string Initialize(); + + std::string GetAllModelInfo(); + +private: + /** + * @brief 模型状态监听 + * @param status 模型状态 + */ + void ModelStatusListener(const XNSim::XNSimStatus::XNModelStatus &status); + +private: + /** + * @brief 互斥锁 + */ + std::mutex m_ModelStatusMutex; + /** + * @brief 模型状态 + */ + std::map m_ModelStatus; + /** + * @brief 模型周期计数 + */ + std::map m_ModelCycleCount; +}; diff --git a/Release/include/XNMonitor/PluginGenerator.h b/Release/include/XNMonitor/PluginGenerator.h new file mode 100644 index 0000000..e2ed0ce --- /dev/null +++ b/Release/include/XNMonitor/PluginGenerator.h @@ -0,0 +1,66 @@ +/** + * @file PluginGenerator.h + * @brief 简化的插件生成器 - 从数据库生成插件cpp文件和CMakeLists + */ + +#pragma once +#include "TypeDefine.h" + +// 接口信息结构 +struct InterfaceInfo { + std::string interfaceName; // 接口名称(如:Aerodynamics_heartbeat) + std::string + templateType; // 模板类型(如:XNSim::C909::ATA04::Aerodynamics_heartbeat_Interface) +}; + +// 插件信息 +struct GenPluginInfo { + std::string pluginName; // 插件名称(如:C909_V1) + std::string pluginDescription; // 插件描述 + std::string interfaceHeaderPath; // 接口头文件路径(如:IDL/C909_V1_Interface.h) + std::vector interfaces; // 接口列表 + std::string outputDirectory; // 输出目录 +}; + +class PluginGenerator +{ +public: + PluginGenerator(); + ~PluginGenerator(); + + // 从数据库加载插件信息 + bool loadPluginFromDatabase(const int confID); + + // 生成插件cpp文件 + bool generatePluginCpp(); + + // 生成CMakeLists.txt + bool generateCMakeLists(); + + // 编译插件 + bool compilePlugin(); + + // 获取错误信息 + std::string getLastError() const; + + // 获取插件信息 + const GenPluginInfo &getPluginInfo() const; + +private: + // 生成插件cpp文件内容 + std::string generatePluginCppContent(); + + // 生成CMakeLists.txt内容 + std::string generateCMakeListsContent(); + + // 写入文件 + bool writeFile(const std::string &filePath, const std::string &content); + + // 创建目录 + bool createDirectory(const std::string &dirPath); + +private: + std::string m_lastError; + + GenPluginInfo m_pluginInfo; +}; diff --git a/Release/include/XNMonitor/PluginInterface.h b/Release/include/XNMonitor/PluginInterface.h new file mode 100644 index 0000000..c5c10ed --- /dev/null +++ b/Release/include/XNMonitor/PluginInterface.h @@ -0,0 +1,34 @@ +#pragma once + +#include "XNMonitorServer_global.h" +#include "DataMonitor.h" +#include "TypeDefine.h" + +// 插件信息结构 +struct PluginInfo { + const char *name; // 插件名称,如 "C909_V1" + const char *description; // 描述信息 + const char *interface_version; // 接口版本,必须与主项目兼容 +}; + +// C风格插件接口 - 确保ABI兼容性 +extern "C" +{ + // 获取插件信息 + typedef PluginInfo *(*GetPluginInfoFunc)(); + + // 创建监控器实例 + typedef DataMonitorBasePtr (*CreateMonitorFunc)(const char *interfaceName); + + // 销毁监控器实例 + typedef void (*DestroyMonitorFunc)(const char *interfaceName); + + // 获取支持的接口列表 + typedef const char **(*GetSupportedInterfacesFunc)(int *count); + + // 释放字符串数组 + typedef void (*FreeStringArrayFunc)(const char **array); +} + +// 插件接口版本 - 主项目一旦发布就不再修改 +#define DATAMONITOR_PLUGIN_INTERFACE_VERSION "1.0.0" \ No newline at end of file diff --git a/Release/include/XNMonitor/PluginManager.h b/Release/include/XNMonitor/PluginManager.h new file mode 100644 index 0000000..7f8bbf3 --- /dev/null +++ b/Release/include/XNMonitor/PluginManager.h @@ -0,0 +1,51 @@ +#pragma once + +#include "XNMonitorServer_global.h" +#include "PluginInterface.h" +#include "PluginGenerator.h" +#include "TypeDefine.h" + +class XNMONITORSERVER_EXPORT PluginManager +{ +public: + static PluginManager &Instance(); + + // 设置生成的插件信息 + void SetGeneratedPluginInfo(const GenPluginInfo &pluginInfo); + + // 从生成器加载插件 + bool LoadPluginFromGenerator(); + + // 卸载当前插件 + void UnloadCurrentPlugin(); + + // 获取监控器实例 + DataMonitorBasePtr GetMonitor(const std::string &interfaceName); + + // 获取所有支持的接口 + std::vector GetSupportedInterfaces(); + + // 检查插件是否已加载 + bool IsPluginLoaded() const; + +private: + PluginManager() = default; + ~PluginManager(); + PluginManager(const PluginManager &) = delete; + PluginManager &operator=(const PluginManager &) = delete; + + // 当前加载的插件信息 + void *m_pluginHandle = nullptr; + PluginInfo *m_pluginInfo = nullptr; + GetPluginInfoFunc m_getPluginInfoFunc = nullptr; + CreateMonitorFunc m_createMonitorFunc = nullptr; + DestroyMonitorFunc m_destroyMonitorFunc = nullptr; + GetSupportedInterfacesFunc m_getSupportedInterfacesFunc = nullptr; + FreeStringArrayFunc m_freeStringArrayFunc = nullptr; + + // 生成的插件信息 + GenPluginInfo m_generatedPluginInfo; + + // 监控器实例缓存 + std::unordered_map m_monitorCache; +}; \ No newline at end of file diff --git a/Release/include/XNMonitor/SystemControl.h b/Release/include/XNMonitor/SystemControl.h new file mode 100644 index 0000000..3f4b176 --- /dev/null +++ b/Release/include/XNMonitor/SystemControl.h @@ -0,0 +1,24 @@ +#pragma once + +#include "XNMonitorServer_global.h" +#include "TypeDefine.h" +#include "TopicManager.h" +#include + +class XNMONITORSERVER_EXPORT SystemControl +{ +public: + SystemControl() {}; + virtual ~SystemControl(); + +public: + std::string Initialize(); + void Pause(); + void Resume(); + void Stop(); + + void cleanup(); + +private: + XNDataWriter *m_EngineControlWriter; +}; diff --git a/Release/include/XNMonitor/SystemInfoMonitor.h b/Release/include/XNMonitor/SystemInfoMonitor.h new file mode 100644 index 0000000..defa515 --- /dev/null +++ b/Release/include/XNMonitor/SystemInfoMonitor.h @@ -0,0 +1,52 @@ +#pragma once + +#include "TopicManager.h" +#include + +class XNMONITORSERVER_EXPORT SystemInfoMonitor +{ +public: + explicit SystemInfoMonitor() {} + virtual ~SystemInfoMonitor(); + +public: + std::string Initialize(); + + std::string GetSystemInfo(); + std::string GetAllThreadInfo(); + +private: + /** + * @brief 引擎状态监听器 + * @param status 引擎状态 + */ + void EngineStatusListener(const XNSim::XNSimStatus::XNEngineStatus &status); + /** + * @brief 线程状态监听器 + * @param status 线程状态 + */ + void ThreadStatusListener(const XNSim::XNSimStatus::XNThreadStatus &status); + +private: + /** + * @brief 互斥锁 + */ + std::mutex m_EngineStatusMutex; + std::mutex m_ThreadStatusMutex; + /** + * @brief 引擎状态 + */ + XNSim::XNSimStatus::XNEngineStatus m_EngineStatus; + /** + * @brief 引擎状态更新 + */ + bool m_EngineStatusUpdate = false; + /** + * @brief 线程状态 + */ + std::map m_ThreadStatus; + /** + * @brief 线程周期计数 + */ + std::map m_ThreadCycleCount; +}; diff --git a/Release/include/XNMonitor/TopicManager.h b/Release/include/XNMonitor/TopicManager.h new file mode 100644 index 0000000..3e3cf81 --- /dev/null +++ b/Release/include/XNMonitor/TopicManager.h @@ -0,0 +1,310 @@ +/** + * @file TopicManager.h + * @author jinchao + * @brief 主题管理类 + * @version 1.0 + * @date 2025-03-10 + * + * @copyright Copyright (c) 2025 COMAC + * + */ +#pragma once + +#include "XNMonitorServer_global.h" +#include "XNDataReaderListenerImpl.h" + +/** + * @brief 主题管理类 + */ +class TopicManager +{ +public: + /** + * @brief 删除拷贝构造 + */ + TopicManager(const TopicManager &) = delete; + /** + * @brief 删除赋值操作 + */ + TopicManager &operator=(const TopicManager &) = delete; + + /** + * @brief 获取单例 + * @return TopicManager*: 主题管理类实例 + */ + static TopicManager *Instance() + { + if (instance == nullptr) { + std::lock_guard locker(instanceMutex); + if (instance == nullptr) { // 双重检查锁定 + instance = new TopicManager(); + } + } + return instance; + } + + /** + * @brief 清理参与者 + */ + static void cleanupParticipant() + { + std::lock_guard locker(instanceMutex); + if (instance != nullptr) { + instance->clearAllTopic(); // 清理所有主题 + if (instance->m_Participant != nullptr) { + eprosima::fastdds::dds::DomainParticipantFactory::get_instance() + ->delete_participant(instance->m_Participant); // 删除参与者 + instance->m_Participant = nullptr; // 设置参与者为空 + } + } + } + +private: + /** + * @brief 显式构造函数 + */ + explicit TopicManager() {} + + /** + * @brief 析构函数 + */ + virtual ~TopicManager() {} + +public: + /** + * @brief 初始化参与者 + * @param domainId: 域ID + */ + XNDDSErrorCode initializeParticipant(int domainId) + { + XNParticipantQos participantQos = eprosima::fastdds::dds::PARTICIPANT_QOS_DEFAULT; + participantQos.name("XNMonitor"); // 设置参与者名称 + m_Participant = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->create_participant( + domainId, participantQos); // 创建参与者 + if (m_Participant == nullptr) { + return XNDDSErrorCode::INIT_FAILED; + } + return XNDDSErrorCode::SUCCESS; + }; + + /** + * @brief 注册发布者模板函数 + * @tparam T: 类型 + * @param topicName: 主题名称 + * @return XNDataWriter*: 数据写入器 + */ + template + XNDDSErrorCode registerPublisher(const std::string &topicName, XNDataWriter *&dataWriter) + { + std::lock_guard locker(m_Mutex); + if (topics_.find(topicName) == topics_.end()) { + topics_[topicName] = MonitorTopicInfo(); // 创建主题信息 + MonitorTopicInfo &tmp = topics_[topicName]; // 获取主题信息 + XNTypeSupport typeSupport(new T()); // 创建类型支持 + typeSupport.register_type(m_Participant); // 注册类型 + tmp.topic = + m_Participant->create_topic(topicName.c_str(), typeSupport.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); // 创建主题 + if (tmp.topic == nullptr) { + topics_.erase(topicName); // 移除主题 + return XNDDSErrorCode::TOPIC_CREATE_FAILED; + } + } + MonitorTopicInfo &topicInfo = topics_[topicName]; // 获取主题信息 + if (topicInfo.publisher == nullptr) { + topicInfo.publisher = m_Participant->create_publisher( + eprosima::fastdds::dds::PUBLISHER_QOS_DEFAULT); // 创建发布者 + if (topicInfo.publisher == nullptr) { + return XNDDSErrorCode::PUBLISHER_CREATE_FAILED; + } + } + + if (topicInfo.dataWriter == nullptr) { + XNDataWriterQos dataWriterQos; + // 设置数据写入器的持久性策略, 使用瞬态本地持久性 + dataWriterQos.durability().kind = eprosima::fastdds::dds::VOLATILE_DURABILITY_QOS; + // 设置数据写入器的生命周期策略, 设置为5秒 + dataWriterQos.lifespan().duration = eprosima::fastdds::dds::Duration_t(5, 0); + topicInfo.dataWriter = topicInfo.publisher->create_datawriter( + topicInfo.topic, dataWriterQos); // 创建数据写入器 + if (topicInfo.dataWriter == nullptr) { + return XNDDSErrorCode::DATAWRITER_CREATE_FAILED; + } + } + dataWriter = topicInfo.dataWriter; + return XNDDSErrorCode::SUCCESS; + } + + /** + * @brief 注销发布者 + * @param topicName: 主题名称 + */ + void unregisterPublisher(const std::string &topicName) + { + std::lock_guard locker(m_Mutex); + unregisterPublisherWithoutLock(topicName); + } + + /** + * @brief 注销发布者, 不带锁 + * @param topicName: 主题名称 + */ + void unregisterPublisherWithoutLock(const std::string &topicName) + { + auto it = topics_.find(topicName); + if (it != topics_.end()) { + MonitorTopicInfo &topicInfo = it->second; // 获取主题信息 + if (topicInfo.dataWriter != nullptr) { + topicInfo.publisher->delete_datawriter(topicInfo.dataWriter); // 删除数据写入器 + topicInfo.dataWriter = nullptr; // 设置数据写入器为空 + } + if (topicInfo.publisher != nullptr) { + m_Participant->delete_publisher(topicInfo.publisher); // 删除发布者 + topicInfo.publisher = nullptr; // 设置发布者为空 + } + if (topicInfo.publisher == nullptr && topicInfo.subscriber == nullptr + && topicInfo.topic != nullptr) { + m_Participant->delete_topic(topicInfo.topic); // 删除主题 + topicInfo.topic = nullptr; // 设置主题为空 + topics_.erase(it); // 移除主题 + } + } + } + + /** + * @brief 注册订阅者模板函数 + * @tparam T: 类型 + * @param topicName: 主题名称 + * @param fun: 回调函数 + */ + template + XNDDSErrorCode registerSubscriber(const std::string &topicName, + std::function fun) + { + std::lock_guard locker(m_Mutex); + if (topics_.find(topicName) == topics_.end()) { + topics_[topicName] = MonitorTopicInfo(); // 创建主题信息 + MonitorTopicInfo &tmp = topics_[topicName]; // 获取主题信息 + XNTypeSupport typeSupport(new T()); // 创建类型支持 + typeSupport.register_type(m_Participant); // 注册类型 + tmp.topic = + m_Participant->create_topic(topicName.c_str(), typeSupport.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); // 创建主题 + if (tmp.topic == nullptr) { + topics_.erase(topicName); // 移除主题 + return XNDDSErrorCode::TOPIC_CREATE_FAILED; // 返回 + } + } + MonitorTopicInfo &topicInfo = topics_[topicName]; // 获取主题信息 + if (topicInfo.subscriber == nullptr) { + topicInfo.subscriber = m_Participant->create_subscriber( + eprosima::fastdds::dds::SUBSCRIBER_QOS_DEFAULT); // 创建订阅者 + if (topicInfo.subscriber == nullptr) { + return XNDDSErrorCode::SUBSCRIBER_CREATE_FAILED; // 返回 + } + } + if (topicInfo.dataReader == nullptr) { + XNDataReaderQos dataReaderQos; + dataReaderQos.durability().kind = + eprosima::fastdds::dds::VOLATILE_DURABILITY_QOS; // 设置数据读取器的持久性策略 + topicInfo.listener = + new XNDataReaderListenerImpl(fun); // 创建数据读取器监听器 + topicInfo.dataReader = topicInfo.subscriber->create_datareader( + topicInfo.topic, dataReaderQos, topicInfo.listener); // 创建数据读取器 + if (topicInfo.dataReader == nullptr) { + return XNDDSErrorCode::DATAREADER_CREATE_FAILED; // 返回 + } + } else { + auto oldListener = topicInfo.dataReader->get_listener(); // 获取旧的监听器 + topicInfo.listener = + new XNDataReaderListenerImpl(fun); // 创建新的监听器 + topicInfo.dataReader->set_listener( + topicInfo.listener, + eprosima::fastdds::dds::StatusMask::all()); // 设置新的监听器 + delete oldListener; // 删除旧的监听器 + } + return XNDDSErrorCode::SUCCESS; + } + + /** + * @brief 注销订阅者, 带锁 + * @param topicName: 主题名称 + */ + void unregisterSubscriber(const std::string &topicName) + { + std::lock_guard locker(m_Mutex); + unregisterSubscriberWithoutLock(topicName); + } + + /** + * @brief 注销订阅者, 不带锁 + * @param topicName: 主题名称 + */ + void unregisterSubscriberWithoutLock(const std::string &topicName) + { + auto it = topics_.find(topicName); + if (it != topics_.end()) { + MonitorTopicInfo &topicInfo = it->second; // 获取主题信息 + if (topicInfo.dataReader != nullptr) { + topicInfo.subscriber->delete_datareader(topicInfo.dataReader); // 删除数据读取器 + topicInfo.dataReader = nullptr; // 设置数据读取器为空 + } + if (topicInfo.listener != nullptr) { + delete topicInfo.listener; // 删除监听器 + topicInfo.listener = nullptr; // 设置监听器为空 + } + if (topicInfo.subscriber != nullptr) { + m_Participant->delete_subscriber(topicInfo.subscriber); // 删除订阅者 + topicInfo.subscriber = nullptr; // 设置订阅者为空 + } + if (topicInfo.subscriber == nullptr && topicInfo.publisher == nullptr + && topicInfo.topic != nullptr) { + m_Participant->delete_topic(topicInfo.topic); // 删除主题 + topicInfo.topic = nullptr; // 设置主题为空 + topics_.erase(it); // 移除主题 + } + } + } + +private: + /** + * @brief 清除所有主题 + */ + void clearAllTopic() + { + std::lock_guard locker(m_Mutex); + if (m_Participant != nullptr) { + while (!topics_.empty()) { + unregisterPublisherWithoutLock(topics_.begin()->first); // 注销发布者 + unregisterSubscriberWithoutLock(topics_.begin()->first); // 注销订阅者 + } + } + } + +private: + /** + * @brief 单例指针 + */ + static TopicManager *instance; + + /** + * @brief 单例互斥锁 + */ + static std::mutex instanceMutex; + + /** + * @brief 参与者 + */ + XNParticipant *m_Participant = nullptr; + + /** + * @brief 主题映射 + */ + std::map topics_; + + /** + * @brief 主题访问互斥锁 + */ + std::mutex m_Mutex; +}; diff --git a/Release/include/XNMonitor/TypeDefine.h b/Release/include/XNMonitor/TypeDefine.h new file mode 100644 index 0000000..8cc00c0 --- /dev/null +++ b/Release/include/XNMonitor/TypeDefine.h @@ -0,0 +1,270 @@ +/** + * @file TypeDefine.h + * @brief 类型定义头文件 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief 域参与者 + */ +using XNParticipant = eprosima::fastdds::dds::DomainParticipant; +/** + * @brief 域参与者Qos + */ +using XNParticipantQos = eprosima::fastdds::dds::DomainParticipantQos; +/** + * @brief 域参与者工厂 + */ +using XNParticipantFactory = eprosima::fastdds::dds::DomainParticipantFactory; +/** + * @brief 数据读取器监听器 + */ +using XNDataReaderListener = eprosima::fastdds::dds::DataReaderListener; +/** + * @brief 数据读取器 + */ +using XNDataReader = eprosima::fastdds::dds::DataReader; +/** + * @brief 数据读取器Qos + */ +using XNDataReaderQos = eprosima::fastdds::dds::DataReaderQos; +/** + * @brief 样本信息 + */ +using XNSampleInfo = eprosima::fastdds::dds::SampleInfo; +/** + * @brief 订阅者 + */ +using XNSubscriber = eprosima::fastdds::dds::Subscriber; +/** + * @brief 类型支持 + */ +using XNTypeSupport = eprosima::fastdds::dds::TypeSupport; +/** + * @brief 数据写入器 + */ +using XNDataWriter = eprosima::fastdds::dds::DataWriter; +/** + * @brief 数据写入器Qos + */ +using XNDataWriterQos = eprosima::fastdds::dds::DataWriterQos; +/** + * @brief 发布者 + */ +using XNPublisher = eprosima::fastdds::dds::Publisher; +/** + * @brief 发布者Qos + */ +using XNPublisherQos = eprosima::fastdds::dds::PublisherQos; +/** + * @brief 主题 + */ +using XNTopic = eprosima::fastdds::dds::Topic; +/** + * @brief 主题数据类型 + */ +using XNTopicDataType = eprosima::fastdds::dds::TopicDataType; + +using json = nlohmann::json; +/** + * @brief 主题信息 + */ +struct MonitorTopicInfo { + /** + * @brief 主题 + */ + XNTopic *topic; + /** + * @brief 发布者 + */ + XNPublisher *publisher; + /** + * @brief 数据写入器 + */ + XNDataWriter *dataWriter; + /** + * @brief 订阅者 + */ + XNSubscriber *subscriber; + /** + * @brief 数据读取器 + */ + XNDataReader *dataReader; + /** + * @brief 数据读取器监听器 + */ + XNDataReaderListener *listener; +}; + +// 错误码定义 +enum class XNDDSErrorCode { + SUCCESS = 0, + INIT_FAILED = -1, + TOPIC_CREATE_FAILED = -2, + PUBLISHER_CREATE_FAILED = -3, + SUBSCRIBER_CREATE_FAILED = -4, + DATAWRITER_CREATE_FAILED = -5, + DATAREADER_CREATE_FAILED = -6, + INVALID_PARAM = -7, + NOT_INITIALIZED = -8 +}; + +/** + * @brief 成员变量定义结构体 + * @note 成员变量包含数据类型、变量名、是否为数组、数组大小、描述 + */ +struct MemberVariable { + /** + * @brief 数据类型 + */ + std::string dataType; + /** + * @brief 变量名 + */ + std::string variableName; + /** + * @brief 是否为数组 + */ + bool isArray; + /** + * @brief 数组大小 + */ + std::vector arraySizes; + /** + * @brief 描述 + */ + std::string description; +}; + +/** + * @brief 接口结构体定义结构体 + * @note 接口结构体定义包含结构体名称、成员变量 + */ +struct StructDefinition { + /** + * @brief 结构体名称 + */ + std::string structName; + /** + * @brief 成员变量 + */ + std::vector> memberVariables; +}; + +/** + * @brief 命名空间定义结构体 + * @note 命名空间定义包含命名空间名称、接口结构体定义、子命名空间定义 + */ +struct NamespaceDefinition { + /** + * @brief 命名空间名称 + */ + std::string namespaceName; + /** + * @brief 结构体定义 + */ + std::vector> structDefinitions; + /** + * @brief 子命名空间 + */ + std::vector> childNamespaces; +}; + +/** + * @brief 模型接口定义 + * @note 模型接口定义包含模型名称和接口命名空间定义 + */ +struct ModelDefinition { + /** + * @brief 模型名称 + */ + std::string modelName; + /** + * @brief 命名空间定义 + */ + std::vector> namespaceDefinitions; +}; + +struct MonitorDataInfo { + /** + * @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 arraySize2; +}; \ No newline at end of file diff --git a/Release/include/XNMonitor/XNDataReaderListenerImpl.h b/Release/include/XNMonitor/XNDataReaderListenerImpl.h new file mode 100644 index 0000000..59aaa0d --- /dev/null +++ b/Release/include/XNMonitor/XNDataReaderListenerImpl.h @@ -0,0 +1,51 @@ +/** + * @file XNDataReaderListenerImpl.h + * @author jinchao + * @brief 数据读取器监听器模板类 + * @version 1.0 + * @date 2025-03-10 + * + * @copyright Copyright (c) 2025 COMAC + * + */ +#pragma once + +#include "TypeDefine.h" + +/** + * @brief 数据读取器监听器模板类 + * @tparam T 数据类型 + */ +template +class XNDataReaderListenerImpl : public XNDataReaderListener +{ +public: + /** + * @brief 构造函数 + * @param callback 回调函数 + */ + XNDataReaderListenerImpl(std::function callback) : callback_(callback) {} + + /** + * @brief 数据可用回调函数 + * @param reader 数据读取器 + */ + void on_data_available(XNDataReader *reader) override + { + XNSampleInfo info; + if (reader->take_next_sample(&data_, &info) == eprosima::fastdds::dds::RETCODE_OK + && info.valid_data) { + callback_(data_); + } + } + +private: + /** + * @brief 数据 + */ + T data_; + /** + * @brief 回调函数 + */ + std::function callback_; +}; \ No newline at end of file diff --git a/Release/include/XNMonitor/XNMonitorInterface.h b/Release/include/XNMonitor/XNMonitorInterface.h new file mode 100644 index 0000000..5be2d21 --- /dev/null +++ b/Release/include/XNMonitor/XNMonitorInterface.h @@ -0,0 +1,281 @@ +/** + * @file XNMonitorInterface.h + * @brief 监控服务器接口定义 + */ +#pragma once + +#include "TypeDefine.h" +#include "XNMonitorServer_global.h" +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + /** + * @brief 初始化DDS监控服务器 + * @param domainId 域ID + * @param domainIdLen 域ID长度 + * @param confID 构型ID + * @param errorMsg 错误信息 + * @param errorMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_Initialize(const char *domainId, int domainIdLen, int confID, + int forceGen, char *errorMsg, int errorMsgSize); + + /** + * @brief 清理DDS监控服务器 + */ + void XNMONITORSERVER_EXPORT XN_Cleanup(); + + //******************** 系统信息监控 ********************* + + /** + * @brief 启动监控系统信息 + * @param errorMsg 错误信息 + * @param errorMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_StartMonitorSystemInfo(char *errorMsg, int errorMsgSize); + + /** + * @brief 获取系统信息 + * @param infoMsg 系统信息 + * @param infoMsgSize 系统信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_GetSystemInfo(char *infoMsg, int infoMsgSize); + + /** + * @brief 停止监控系统信息 + */ + void XNMONITORSERVER_EXPORT XN_StopMonitorSystemInfo(); + + //******************** 线程信息监控 ********************* + + /** + * @brief 获取所有线程信息 + * @param infoMsg 线程信息 + * @param infoMsgSize 线程信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_GetAllThreadInfo(char *infoMsg, int infoMsgSize); + + //******************** 模型信息监控 ********************* + + /** + * @brief 启动监控模型信息 + * @param errorMsg 错误信息 + * @param errorMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_StartMonitorModelInfo(char *errorMsg, int errorMsgSize); + + /** + * @brief 获取模型信息 + * @param infoMsg 模型信息 + * @param infoMsgSize 模型信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_GetModelInfo(char *infoMsg, int infoMsgSize); + + /** + * @brief 停止监控模型信息 + */ + void XNMONITORSERVER_EXPORT XN_StopMonitorModelInfo(); + + //******************** 引擎控制 ********************* + + /** + * @brief 初始化引擎控制 + * @param errorMsg 错误信息 + * @param errorMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_InitializeEngineControl(char *errorMsg, int errorMsgSize); + + /** + * @brief 暂停引擎 + * @param errorMsg 错误信息 + * @param errorMsgSize 错误信息大小 + */ + void XNMONITORSERVER_EXPORT XN_PauseEngine(char *errorMsg, int errorMsgSize); + + /** + * @brief 恢复引擎 + * @param errorMsg 错误信息 + * @param errorMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + void XNMONITORSERVER_EXPORT XN_ResumeEngine(char *errorMsg, int errorMsgSize); + + /** + * @brief 停止引擎 + * @param errorMsg 错误信息 + * @param errorMsgSize 错误信息大小 + */ + void XNMONITORSERVER_EXPORT XN_StopEngine(char *errorMsg, int errorMsgSize); + + //******************** 数据监控 ********************* + + /** + * @brief 启动数据监控 + * @param structName 结构体名称 + * @param structNameLen 结构体名称长度 + * @param errorMsg 错误信息 + * @param errorMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_StartDataMonitor(const char *structName, const int structNameLen, + char *errorMsg, int errorMsgSize); + + /** + * @brief 停止数据监控 + * @param structName 结构体名称 + * @param structNameLen 结构体名称长度 + * @param errorMsg 错误信息 + * @param errorMsgSize 错误信息大小 + */ + void XNMONITORSERVER_EXPORT XN_StopDataMonitor(const char *structName, const int structNameLen, + char *errorMsg, int errorMsgSize); + + //******************** 数据注入 ********************* + + /** + * @brief 获取数据监控信息 + * @param structName 结构体名称 + * @param structNameLen 结构体名称长度 + * @param interfaceName 接口名称JSON数组字符串 + * @param interfaceNameLen 接口名称JSON数组字符串长度 + * @param data 数据JSON字符串 + * @param dataLen 数据JSON字符串长度 + * @param infoMsg 错误信息 + * @param infoMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_GetDataMonitorInfo(const char *structName, + const int structNameLen, + const char *interfaceName, + const int interfaceNameLen, char *data, + int dataLen, char *infoMsg, int infoMsgSize); + + /** + * @brief 注入数据接口 + * @param structName 结构体名称 + * @param structNameLen 结构体名称长度 + * @param interfaceNameAndData 接口名称和数据JSON字符串 + * @param interfaceNameAndDataLen 接口名称和数据JSON字符串长度 + * @param infoMsg 错误信息 + * @param infoMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_InjectDataInterface(const char *structName, + const int structNameLen, + const char *interfaceNameAndData, + const int interfaceNameAndDataLen, + char *infoMsg, int infoMsgSize); + + /** + * @brief 持续注入数据接口 + * @param structName 结构体名称 + * @param structNameLen 结构体名称长度 + * @param interfaceNameAndData 接口名称和数据JSON字符串 + * @param interfaceNameAndDataLen 接口名称和数据JSON字符串长度 + * @param frequency 注入频率 + * @param infoMsg 错误信息 + * @param infoMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_StartInjectContinuous( + const char *structName, const int structNameLen, const char *interfaceNameAndData, + const int interfaceNameAndDataLen, double frequency, char *infoMsg, int infoMsgSize); + + /** + * @brief 停止持续注入数据接口 + * @param structName 结构体名称 + * @param structNameLen 结构体名称长度 + * @param infoMsg 错误信息 + * @param infoMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_StopInjectContinuous(const char *structName, + const int structNameLen, char *infoMsg, + int infoMsgSize); + + //******************** csv数据注入 ********************* + + /** + * @brief 从csv文件中注入数据接口 + * @param structName 结构体名称 + * @param structNameLen 结构体名称长度 + * @param csvFilePath csv文件路径 + * @param csvFilePathLen csv文件路径长度 + * @param infoMsg 错误信息 + * @param infoMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_InjectDataInterfaceFromCsv(const char *InjectDataInfo, + const int InjectDataInfoLen, + const char *csvFilePath, + const int csvFilePathLen, + char *infoMsg, int infoMsgSize); + + /** + * @brief 获取csv数据注入状态 + * @param infoMsg 错误信息 + * @param infoMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败, 1: 正在注入 + */ + int XNMONITORSERVER_EXPORT XN_GetCsvDataInjectStatus(char *infoMsg, int infoMsgSize); + + /** + * @brief 停止csv数据注入 + * @param infoMsg 错误信息 + * @param infoMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ + int XNMONITORSERVER_EXPORT XN_StopCsvDataInject(char *infoMsg, int infoMsgSize); + + //******************** csv数据采集 ********************* + + /** + * @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 *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); + + /** + * @brief 清理所有资源 + */ + void XNMONITORSERVER_EXPORT XN_Cleanup(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/Release/include/XNMonitor/XNMonitorServer_global.h b/Release/include/XNMonitor/XNMonitorServer_global.h new file mode 100644 index 0000000..b8cd139 --- /dev/null +++ b/Release/include/XNMonitor/XNMonitorServer_global.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef XNMonitorServer_EXPORTS +# define XNMONITORSERVER_EXPORT __attribute__((visibility("default"))) +#else +# define XNMONITORSERVER_EXPORT __attribute__((visibility("default"))) +#endif diff --git a/XNModelGenServer/XNModelGen.cpp b/XNModelGenServer/XNModelGen.cpp index 6e83f00..14d7171 100644 --- a/XNModelGenServer/XNModelGen.cpp +++ b/XNModelGenServer/XNModelGen.cpp @@ -439,10 +439,11 @@ bool XNModelGen::GenerateSourceFile() sourceFile << "void " << m_className << "::ReleaseData() {" << std::endl; sourceFile << " T_D();" << std::endl; if (!m_inputStructName.structName.empty()) { - sourceFile << " // TODO: 在这里释放输入数据" << std::endl; - sourceFile << std::endl; + if (m_inputStructName.isPointer) { sourceFile << " if (d->_data." << m_inputStructName.structName << ") {" << std::endl; + sourceFile << " // TODO: 在这里释放输入数据" << std::endl; + sourceFile << std::endl; sourceFile << " delete d->_data." << m_inputStructName.structName << ";" << std::endl; sourceFile << " d->_data." << m_inputStructName.structName << " = nullptr;" @@ -451,11 +452,11 @@ bool XNModelGen::GenerateSourceFile() } } if (!m_outputStructName.structName.empty()) { - sourceFile << " // TODO: 在这里释放输出数据" << std::endl; - sourceFile << std::endl; if (m_outputStructName.isPointer) { sourceFile << " if (d->_data." << m_outputStructName.structName << ") {" << std::endl; + sourceFile << " // TODO: 在这里释放输出数据" << std::endl; + sourceFile << std::endl; sourceFile << " delete d->_data." << m_outputStructName.structName << ";" << std::endl; sourceFile << " d->_data." << m_outputStructName.structName << " = nullptr;" diff --git a/XNMonitorServer/.vscode/settings.json b/XNMonitorServer/.vscode/settings.json index fc63933..d7747f7 100644 --- a/XNMonitorServer/.vscode/settings.json +++ b/XNMonitorServer/.vscode/settings.json @@ -73,6 +73,7 @@ "any": "cpp", "forward_list": "cpp", "fstream": "cpp", - "valarray": "cpp" + "valarray": "cpp", + "*.ipp": "cpp" } } diff --git a/XNMonitorServer/CMakeLists.txt b/XNMonitorServer/CMakeLists.txt index 00194e4..a57eae7 100644 --- a/XNMonitorServer/CMakeLists.txt +++ b/XNMonitorServer/CMakeLists.txt @@ -20,12 +20,11 @@ else() message(FATAL_ERROR "Environment variable XNCore is not set.") endif() -# file(GLOB DDS_XNIDL_SOURCES_CXX "../XNCore/XNIDL/*.cxx") include_directories(${XNCore_PATH}/include) -include_directories(${XNCore_PATH}/Configuration/C909_V1) find_package(OpenSSL REQUIRED) find_package(nlohmann_json 3.9.1 REQUIRED) +find_package(SQLite3 REQUIRED) add_library(XNMonitorServer SHARED XNMonitorServer_global.h @@ -50,6 +49,11 @@ add_library(XNMonitorServer SHARED CSVDataInjectThread.cpp DataCollect.h DataCollect.cpp + PluginManager.h + PluginManager.cpp + PluginInterface.h + PluginGenerator.h + PluginGenerator.cpp ) # 添加头文件搜索路径 @@ -61,8 +65,9 @@ target_link_libraries(XNMonitorServer PRIVATE fastdds OpenSSL::SSL OpenSSL::Crypto + SQLite::SQLite3 ${XNCore_PATH}/lib/libXNCore.so - ${XNCore_PATH}/lib/libC909_V1_Interface.so + dl ) target_compile_definitions(XNMonitorServer PRIVATE XNMONITOR_SERVER_LIBRARY) @@ -77,3 +82,9 @@ install(TARGETS XNMonitorServer LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION . ) + +#添加自定义命令和目标以拷贝所有头文件 +file(GLOB HEADER_FILES "*.h") + +# 使用 install 命令在安装时拷贝头文件 +install(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/XNMonitor) diff --git a/XNMonitorServer/DataMonitorFactory.cpp b/XNMonitorServer/DataMonitorFactory.cpp index d142fe0..6dee257 100644 --- a/XNMonitorServer/DataMonitorFactory.cpp +++ b/XNMonitorServer/DataMonitorFactory.cpp @@ -1,6 +1,24 @@ #include "DataMonitorFactory.h" +#include "PluginManager.h" -// 静态成员初始化 -std::mutex DataMonitorFactory::mutex_; +DataMonitorBasePtr DataMonitorFactory::GetInstance(const std::string &interfaceName) +{ + return PluginManager::Instance().GetMonitor(interfaceName); +} -std::unordered_map DataMonitorFactory::instances_; \ No newline at end of file +void DataMonitorFactory::ReleaseInstance(const std::string &interfaceName) +{ + // 插件管理器会自动管理实例生命周期 + // 这里可以添加额外的清理逻辑 +} + +bool DataMonitorFactory::HasInterface(const std::string &interfaceName) +{ + auto interfaces = PluginManager::Instance().GetSupportedInterfaces(); + return std::find(interfaces.begin(), interfaces.end(), interfaceName) != interfaces.end(); +} + +std::vector DataMonitorFactory::GetRegisteredInterfaces() +{ + return PluginManager::Instance().GetSupportedInterfaces(); +} \ No newline at end of file diff --git a/XNMonitorServer/DataMonitorFactory.h b/XNMonitorServer/DataMonitorFactory.h index b412961..a981523 100644 --- a/XNMonitorServer/DataMonitorFactory.h +++ b/XNMonitorServer/DataMonitorFactory.h @@ -3,108 +3,23 @@ #include "XNMonitorServer_global.h" #include "DataMonitor.h" -//接口头文件 -#include - /** * @brief DataMonitor工厂类,用于创建不同类型的DataMonitor实例 */ class XNMONITORSERVER_EXPORT DataMonitorFactory { public: - static DataMonitorBasePtr GetInstance(const std::string &interfaceName) - { - if (interfaceName == "Aerodynamics_heartbeat") { - return GetInstance(); - } else if (interfaceName == "Aerodynamics_input") { - return GetInstance(); - } else if (interfaceName == "Aerodynamics_output") { - return GetInstance(); - } else if (interfaceName == "GroundHandling_heartbeat") { - return GetInstance(); - } else if (interfaceName == "GroundHandling_input") { - return GetInstance(); - } else if (interfaceName == "GroundHandling_output") { - return GetInstance(); - } else if (interfaceName == "WeightBalance_heartbeat") { - return GetInstance(); - } else if (interfaceName == "WeightBalance_input") { - return GetInstance(); - } else if (interfaceName == "WeightBalance_output") { - return GetInstance(); - } - return nullptr; - } + static DataMonitorBasePtr GetInstance(const std::string &interfaceName); - static void ReleaseInstance(const std::string &interfaceName) - { - if (interfaceName == "Aerodynamics_heartbeat") { - ReleaseInstance(); - } else if (interfaceName == "Aerodynamics_input") { - ReleaseInstance(); - } else if (interfaceName == "Aerodynamics_output") { - ReleaseInstance(); - } else if (interfaceName == "GroundHandling_heartbeat") { - ReleaseInstance(); - } else if (interfaceName == "GroundHandling_input") { - ReleaseInstance(); - } else if (interfaceName == "GroundHandling_output") { - ReleaseInstance(); - } else if (interfaceName == "WeightBalance_heartbeat") { - ReleaseInstance(); - } else if (interfaceName == "WeightBalance_input") { - ReleaseInstance(); - } else if (interfaceName == "WeightBalance_output") { - ReleaseInstance(); - } - } + static void ReleaseInstance(const std::string &interfaceName); -private: - /** - * @brief 创建DataMonitor实例 - * @param framework 框架指针 - * @param modelId 模型ID - * @param DDS_type DDS类型 - * @return 返回创建的DataMonitor实例的智能指针 - */ - template - static DataMonitorBasePtr GetInstance() - { - std::lock_guard lock(mutex_); + // 检查接口是否存在 + static bool HasInterface(const std::string &interfaceName); - // 使用type_index作为键,更可靠的类型标识 - std::type_index typeIndex(typeid(T)); - - // 检查是否已存在实例 - auto it = instances_.find(typeIndex); - if (it != instances_.end()) { - return it->second; - } - - // 创建新实例 - auto monitor = std::make_shared>(); - instances_[typeIndex] = monitor; - return monitor; - } - - /** - * @brief 释放指定类型的实例 - */ - template - static void ReleaseInstance() - { - std::lock_guard lock(mutex_); - std::type_index typeIndex(typeid(T)); - if (instances_.find(typeIndex) == instances_.end()) { - return; - } - instances_.erase(typeIndex); - } + // 获取所有已注册的接口名称 + static std::vector GetRegisteredInterfaces(); private: DataMonitorFactory() = delete; ~DataMonitorFactory() = delete; - - static std::mutex mutex_; - static std::unordered_map instances_; }; diff --git a/XNMonitorServer/PluginGenerator.cpp b/XNMonitorServer/PluginGenerator.cpp new file mode 100644 index 0000000..5509e35 --- /dev/null +++ b/XNMonitorServer/PluginGenerator.cpp @@ -0,0 +1,379 @@ +/** + * @file PluginGenerator.cpp + * @brief 简化的插件生成器实现 + */ +#include "PluginGenerator.h" + +std::string GetXNCorePath() +{ + const char *xnCorePath = std::getenv("XNCore"); + if (xnCorePath != nullptr) { + return std::string(xnCorePath); + } + return ""; +} + +PluginGenerator::PluginGenerator() +{ +} + +PluginGenerator::~PluginGenerator() +{ +} + +bool PluginGenerator::loadPluginFromDatabase(const int confID) +{ + std::string xncorePath = GetXNCorePath(); + if (xncorePath.empty()) { + m_lastError = "无法获取XNCore环境变量的值!"; + return false; + } + + std::string dbPath = xncorePath + "/database/XNSim.db"; + if (!std::filesystem::exists(dbPath)) { + m_lastError = "数据库文件不存在!"; + return false; + } + + sqlite3 *db = nullptr; + int rc = sqlite3_open(dbPath.c_str(), &db); + if (rc != SQLITE_OK) { + m_lastError = "无法打开数据库: " + std::string(sqlite3_errmsg(db)); + sqlite3_close(db); + return false; + } + + // 准备SQL语句查询Configuration表 + const char *sql = "SELECT ConfName FROM Configuration WHERE ConfID = ?"; + sqlite3_stmt *stmt = nullptr; + rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); + if (rc != SQLITE_OK) { + m_lastError = "SQL准备失败: " + std::string(sqlite3_errmsg(db)); + sqlite3_close(db); + return false; + } + + // 绑定参数 + sqlite3_bind_int(stmt, 1, confID); + + // 执行查询 + if (sqlite3_step(stmt) == SQLITE_ROW) { + const char *text = reinterpret_cast(sqlite3_column_text(stmt, 0)); + m_pluginInfo.pluginName = text ? text : ""; + m_pluginInfo.interfaceHeaderPath = "../IDL/" + m_pluginInfo.pluginName + "_Interface.h"; + m_pluginInfo.outputDirectory = + xncorePath + "/Configuration/" + m_pluginInfo.pluginName + "/PluginCode"; + } else { + m_lastError = "未找到ConfID为 " + std::to_string(confID) + " 的配置"; + sqlite3_finalize(stmt); + sqlite3_close(db); + return false; + } + + // 清理资源 + sqlite3_finalize(stmt); + + // 查询DataInterface_{ConfID}表获取接口信息 + std::string dataInterfaceTableName = "DataInterface_" + std::to_string(confID); + std::string dataInterfaceSql = + "SELECT DISTINCT ModelStructName, SystemName, PlaneName, ATAName FROM " + + dataInterfaceTableName; + + sqlite3_stmt *dataInterfaceStmt = nullptr; + rc = sqlite3_prepare_v2(db, dataInterfaceSql.c_str(), -1, &dataInterfaceStmt, nullptr); + if (rc != SQLITE_OK) { + m_lastError = "DataInterface表SQL准备失败: " + std::string(sqlite3_errmsg(db)); + sqlite3_close(db); + return false; + } + + // 执行查询并收集接口信息 + while (sqlite3_step(dataInterfaceStmt) == SQLITE_ROW) { + InterfaceInfo interface; + interface.interfaceName = + reinterpret_cast(sqlite3_column_text(dataInterfaceStmt, 0)); + std::string systemName = + reinterpret_cast(sqlite3_column_text(dataInterfaceStmt, 1)); + std::string planeName = + reinterpret_cast(sqlite3_column_text(dataInterfaceStmt, 2)); + std::string ataName = + reinterpret_cast(sqlite3_column_text(dataInterfaceStmt, 3)); + interface.templateType = systemName + "::" + planeName + "::" + ataName + + "::" + interface.interfaceName + "_Interface"; + + // 使用ModelStructName去重 + bool isDuplicate = false; + for (const auto &existingInterface : m_pluginInfo.interfaces) { + if (existingInterface.interfaceName == interface.interfaceName) { + isDuplicate = true; + break; + } + } + + if (!isDuplicate) { + m_pluginInfo.interfaces.push_back(interface); + } + } + + // 清理DataInterface查询资源 + sqlite3_finalize(dataInterfaceStmt); + sqlite3_close(db); + return true; +} + +bool PluginGenerator::generatePluginCpp() +{ + std::string content = generatePluginCppContent(); + std::string filePath = + m_pluginInfo.outputDirectory + "/" + m_pluginInfo.pluginName + "_plugin.cpp"; + return writeFile(filePath, content); +} + +bool PluginGenerator::generateCMakeLists() +{ + std::string content = generateCMakeListsContent(); + std::string filePath = m_pluginInfo.outputDirectory + "/CMakeLists.txt"; + return writeFile(filePath, content); +} + +bool PluginGenerator::compilePlugin() +{ + std::string buildDir = m_pluginInfo.outputDirectory + "/build"; + // 如果build目录已存在则删除 + if (std::filesystem::exists(buildDir)) { + try { + std::filesystem::remove_all(buildDir); + } catch (const std::exception &e) { + m_lastError = "Failed to remove existing build directory: " + std::string(e.what()); + return false; + } + } + // 创建build目录 + if (!createDirectory(buildDir)) { + m_lastError = "Failed to create build directory: " + buildDir; + return false; + } + + // 构建命令 + std::string cmakeCmd = "cd " + buildDir + " && cmake .."; + std::string makeCmd = "cd " + buildDir + " && make"; + std::string installCmd = "cd " + buildDir + " && make install"; + + // 执行cmake + int cmakeResult = system(cmakeCmd.c_str()); + if (cmakeResult != 0) { + m_lastError = "CMake failed with exit code: " + std::to_string(cmakeResult); + return false; + } + + // 执行make + int makeResult = system(makeCmd.c_str()); + if (makeResult != 0) { + m_lastError = "Make failed with exit code: " + std::to_string(makeResult); + return false; + } + + // 执行make install + int installResult = system(installCmd.c_str()); + if (installResult != 0) { + m_lastError = "Make install failed with exit code: " + std::to_string(installResult); + return false; + } + + return true; +} + +std::string PluginGenerator::getLastError() const +{ + return m_lastError; +} + +std::string PluginGenerator::generatePluginCppContent() +{ + std::stringstream ss; + + // 生成头文件包含 + ss << "#include \n"; + ss << "#include \n"; + ss << "// " << m_pluginInfo.pluginName << "接口头文件 - 只在插件中包含\n"; + ss << "#include \"" << m_pluginInfo.interfaceHeaderPath << "\"\n\n"; + + // 生成插件信息 + ss << "// 插件信息\n"; + ss << "static PluginInfo plugin_info = {\"" << m_pluginInfo.pluginName << "\", \"" + << "" << "\", DATAMONITOR_PLUGIN_INTERFACE_VERSION};\n\n"; + + // 生成支持的接口列表 + ss << "// 支持的接口列表\n"; + ss << "static const char *supported_interfaces[] = {\n"; + for (size_t i = 0; i < m_pluginInfo.interfaces.size(); ++i) { + ss << " \"" << m_pluginInfo.interfaces[i].interfaceName << "\""; + if (i < m_pluginInfo.interfaces.size() - 1) { + ss << ","; + } + ss << "\n"; + } + ss << "};\n\n"; + + ss << "static const int interface_count = sizeof(supported_interfaces) / " + "sizeof(supported_interfaces[0]);\n\n"; + + // 生成导出的插件函数 + ss << "// 导出的插件函数\n"; + ss << "extern \"C\"\n"; + ss << "{\n\n"; + + // get_plugin_info函数 + ss << " PluginInfo *get_plugin_info()\n"; + ss << " {\n"; + ss << " return &plugin_info;\n"; + ss << " }\n\n"; + + // create_monitor函数 + ss << " DataMonitorBasePtr create_monitor(const char *interfaceName)\n"; + ss << " {\n"; + ss << " std::string name(interfaceName);\n\n"; + + for (size_t i = 0; i < m_pluginInfo.interfaces.size(); ++i) { + const auto &interface = m_pluginInfo.interfaces[i]; + ss << " if (name == \"" << interface.interfaceName << "\") {\n"; + ss << " return std::make_shared>();\n"; + ss << " }"; + if (i < m_pluginInfo.interfaces.size() - 1) { + ss << " else"; + } + ss << "\n"; + } + + ss << " return nullptr;\n"; + ss << " }\n\n"; + + // destroy_monitor函数 + ss << " void destroy_monitor(const char *interfaceName)\n"; + ss << " {\n"; + ss << " // 智能指针会自动管理内存,这里可以添加额外的清理逻辑\n"; + ss << " }\n\n"; + + // get_supported_interfaces函数 + ss << " const char **get_supported_interfaces(int *count)\n"; + ss << " {\n"; + ss << " if (count) {\n"; + ss << " *count = interface_count;\n"; + ss << " }\n"; + ss << " return const_cast(supported_interfaces);\n"; + ss << " }\n\n"; + + // free_string_array函数 + ss << " void free_string_array(const char **array)\n"; + ss << " {\n"; + ss << " // 这里不需要释放,因为使用的是静态数组\n"; + ss << " }\n"; + ss << "}\n"; + + return ss.str(); +} + +std::string PluginGenerator::generateCMakeListsContent() +{ + std::stringstream ss; + std::string projectName = m_pluginInfo.pluginName + "_Monitor"; + + ss << "# CMakeLists.txt for " << projectName << " plugin\n"; + ss << "cmake_minimum_required(VERSION 3.10)\n\n"; + + ss << "# 设置项目名称\n"; + ss << "project(" << projectName << "_plugin)\n\n"; + + ss << "# 设置C++标准\n"; + ss << "set(CMAKE_CXX_STANDARD 17)\n"; + ss << "set(CMAKE_CXX_STANDARD_REQUIRED ON)\n\n"; + + ss << "# 查找必要的包\n"; + ss << "find_package(PkgConfig REQUIRED)\n"; + ss << "find_package(FastDDS REQUIRED)\n"; + + ss << "if(DEFINED ENV{XNCore})\n"; + ss << " set(XNCore_PATH $ENV{XNCore})\n"; + ss << "else()\n"; + ss << " message(FATAL_ERROR \"Environment variable XNCore is not set.\")\n"; + ss << "endif()\n"; + + ss << "include_directories(${XNCore_PATH}/include)\n"; + + ss << "# 创建插件库\n"; + ss << "add_library(" << projectName << " SHARED\n"; + ss << " " << m_pluginInfo.pluginName << "_plugin.cpp\n"; + ss << ")\n\n"; + + ss << "# 链接库\n"; + ss << "target_link_libraries(" << projectName << "\n"; + ss << " fastcdr\n"; + ss << " fastdds\n"; + ss << " OpenSSL::SSL\n"; + ss << " OpenSSL::Crypto\n"; + ss << " ${XNCore_PATH}/lib/lib" << m_pluginInfo.pluginName << "_Interface.so\n"; + ss << " ${XNCore_PATH}/lib/libXNMonitorServer.so\n"; + ss << ")\n\n"; + + ss << "target_compile_definitions(" << projectName << " PRIVATE " << projectName + << "_LIBRARY)\n"; + + ss << "if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n"; + ss << " set(CMAKE_INSTALL_PREFIX \"${XNCore_PATH}/Configuration/" << m_pluginInfo.pluginName + << "/Plugins\" CACHE PATH \"Install path prefix\" " + "FORCE)\n"; + ss << "endif()\n"; + + ss << "include(GNUInstallDirs)\n"; + ss << "install(TARGETS " << projectName << "\n"; + ss << " BUNDLE DESTINATION .\n"; + ss << " LIBRARY DESTINATION .\n"; + ss << " RUNTIME DESTINATION .\n"; + ss << ")\n"; + + return ss.str(); +} + +bool PluginGenerator::writeFile(const std::string &filePath, const std::string &content) +{ + try { + // 确保目录存在 + std::filesystem::path path(filePath); + if (path.has_parent_path()) { + createDirectory(path.parent_path().string()); + } + + std::ofstream file(filePath); + if (!file.is_open()) { + m_lastError = "无法创建文件: " + filePath; + return false; + } + + file << content; + file.close(); + + return true; + } catch (const std::exception &e) { + m_lastError = "写入文件错误: " + std::string(e.what()); + return false; + } +} + +bool PluginGenerator::createDirectory(const std::string &dirPath) +{ + try { + if (!std::filesystem::exists(dirPath)) { + return std::filesystem::create_directories(dirPath); + } + return true; + } catch (const std::exception &e) { + m_lastError = "创建目录错误: " + std::string(e.what()); + return false; + } +} + +const GenPluginInfo &PluginGenerator::getPluginInfo() const +{ + return m_pluginInfo; +} \ No newline at end of file diff --git a/XNMonitorServer/PluginGenerator.h b/XNMonitorServer/PluginGenerator.h new file mode 100644 index 0000000..e2ed0ce --- /dev/null +++ b/XNMonitorServer/PluginGenerator.h @@ -0,0 +1,66 @@ +/** + * @file PluginGenerator.h + * @brief 简化的插件生成器 - 从数据库生成插件cpp文件和CMakeLists + */ + +#pragma once +#include "TypeDefine.h" + +// 接口信息结构 +struct InterfaceInfo { + std::string interfaceName; // 接口名称(如:Aerodynamics_heartbeat) + std::string + templateType; // 模板类型(如:XNSim::C909::ATA04::Aerodynamics_heartbeat_Interface) +}; + +// 插件信息 +struct GenPluginInfo { + std::string pluginName; // 插件名称(如:C909_V1) + std::string pluginDescription; // 插件描述 + std::string interfaceHeaderPath; // 接口头文件路径(如:IDL/C909_V1_Interface.h) + std::vector interfaces; // 接口列表 + std::string outputDirectory; // 输出目录 +}; + +class PluginGenerator +{ +public: + PluginGenerator(); + ~PluginGenerator(); + + // 从数据库加载插件信息 + bool loadPluginFromDatabase(const int confID); + + // 生成插件cpp文件 + bool generatePluginCpp(); + + // 生成CMakeLists.txt + bool generateCMakeLists(); + + // 编译插件 + bool compilePlugin(); + + // 获取错误信息 + std::string getLastError() const; + + // 获取插件信息 + const GenPluginInfo &getPluginInfo() const; + +private: + // 生成插件cpp文件内容 + std::string generatePluginCppContent(); + + // 生成CMakeLists.txt内容 + std::string generateCMakeListsContent(); + + // 写入文件 + bool writeFile(const std::string &filePath, const std::string &content); + + // 创建目录 + bool createDirectory(const std::string &dirPath); + +private: + std::string m_lastError; + + GenPluginInfo m_pluginInfo; +}; diff --git a/XNMonitorServer/PluginInterface.h b/XNMonitorServer/PluginInterface.h new file mode 100644 index 0000000..c5c10ed --- /dev/null +++ b/XNMonitorServer/PluginInterface.h @@ -0,0 +1,34 @@ +#pragma once + +#include "XNMonitorServer_global.h" +#include "DataMonitor.h" +#include "TypeDefine.h" + +// 插件信息结构 +struct PluginInfo { + const char *name; // 插件名称,如 "C909_V1" + const char *description; // 描述信息 + const char *interface_version; // 接口版本,必须与主项目兼容 +}; + +// C风格插件接口 - 确保ABI兼容性 +extern "C" +{ + // 获取插件信息 + typedef PluginInfo *(*GetPluginInfoFunc)(); + + // 创建监控器实例 + typedef DataMonitorBasePtr (*CreateMonitorFunc)(const char *interfaceName); + + // 销毁监控器实例 + typedef void (*DestroyMonitorFunc)(const char *interfaceName); + + // 获取支持的接口列表 + typedef const char **(*GetSupportedInterfacesFunc)(int *count); + + // 释放字符串数组 + typedef void (*FreeStringArrayFunc)(const char **array); +} + +// 插件接口版本 - 主项目一旦发布就不再修改 +#define DATAMONITOR_PLUGIN_INTERFACE_VERSION "1.0.0" \ No newline at end of file diff --git a/XNMonitorServer/PluginManager.cpp b/XNMonitorServer/PluginManager.cpp new file mode 100644 index 0000000..e3283e5 --- /dev/null +++ b/XNMonitorServer/PluginManager.cpp @@ -0,0 +1,126 @@ +#include "PluginManager.h" + +PluginManager &PluginManager::Instance() +{ + static PluginManager instance; + return instance; +} + +PluginManager::~PluginManager() +{ + UnloadCurrentPlugin(); +} + +void PluginManager::SetGeneratedPluginInfo(const GenPluginInfo &pluginInfo) +{ + m_generatedPluginInfo = pluginInfo; +} + +bool PluginManager::LoadPluginFromGenerator() +{ + if (m_generatedPluginInfo.pluginName.empty()) { + return false; + } + + // 构建插件路径 - 安装到outputDirectory上级目录的Plugins下 + std::filesystem::path outputPath(m_generatedPluginInfo.outputDirectory); + std::string pluginPath = outputPath.parent_path().string() + "/Plugins/lib" + + m_generatedPluginInfo.pluginName + "_Monitor.so"; + + // 卸载当前插件 + UnloadCurrentPlugin(); + + // 加载插件库 + m_pluginHandle = dlopen(pluginPath.c_str(), RTLD_LAZY); + if (!m_pluginHandle) { + std::cerr << "Failed to load plugin: " << dlerror() << std::endl; + return false; + } + + // 获取插件函数 + m_getPluginInfoFunc = + reinterpret_cast(dlsym(m_pluginHandle, "get_plugin_info")); + m_createMonitorFunc = + reinterpret_cast(dlsym(m_pluginHandle, "create_monitor")); + m_destroyMonitorFunc = + reinterpret_cast(dlsym(m_pluginHandle, "destroy_monitor")); + m_getSupportedInterfacesFunc = reinterpret_cast( + dlsym(m_pluginHandle, "get_supported_interfaces")); + m_freeStringArrayFunc = + reinterpret_cast(dlsym(m_pluginHandle, "free_string_array")); + + // 检查函数是否都加载成功 + if (!m_getPluginInfoFunc || !m_createMonitorFunc || !m_getSupportedInterfacesFunc) { + std::cerr << "Failed to load plugin functions" << std::endl; + UnloadCurrentPlugin(); + return false; + } + + // 获取插件信息 + m_pluginInfo = m_getPluginInfoFunc(); + + return true; +} + +void PluginManager::UnloadCurrentPlugin() +{ + // 先清理监控器缓存(这会触发DataMonitorProduct的析构函数) + m_monitorCache.clear(); + + if (m_pluginHandle) { + dlclose(m_pluginHandle); + m_pluginHandle = nullptr; + } + m_pluginInfo = nullptr; + m_getPluginInfoFunc = nullptr; + m_createMonitorFunc = nullptr; + m_destroyMonitorFunc = nullptr; + m_getSupportedInterfacesFunc = nullptr; + m_freeStringArrayFunc = nullptr; +} + +DataMonitorBasePtr PluginManager::GetMonitor(const std::string &interfaceName) +{ + if (!m_createMonitorFunc) { + return nullptr; + } + + // 检查缓存中是否已有实例 + auto it = m_monitorCache.find(interfaceName); + if (it != m_monitorCache.end()) { + return it->second; + } + + // 创建新实例(不自动初始化) + auto monitor = m_createMonitorFunc(interfaceName.c_str()); + + // 缓存实例 + if (monitor) { + m_monitorCache[interfaceName] = monitor; + } + + return monitor; +} + +std::vector PluginManager::GetSupportedInterfaces() +{ + std::vector interfaces; + + if (!m_getSupportedInterfacesFunc) { + return interfaces; + } + + int count = 0; + const char **interfaceNames = m_getSupportedInterfacesFunc(&count); + + for (int i = 0; i < count; ++i) { + interfaces.push_back(interfaceNames[i]); + } + + return interfaces; +} + +bool PluginManager::IsPluginLoaded() const +{ + return m_pluginHandle != nullptr && m_createMonitorFunc != nullptr; +} \ No newline at end of file diff --git a/XNMonitorServer/PluginManager.h b/XNMonitorServer/PluginManager.h new file mode 100644 index 0000000..7f8bbf3 --- /dev/null +++ b/XNMonitorServer/PluginManager.h @@ -0,0 +1,51 @@ +#pragma once + +#include "XNMonitorServer_global.h" +#include "PluginInterface.h" +#include "PluginGenerator.h" +#include "TypeDefine.h" + +class XNMONITORSERVER_EXPORT PluginManager +{ +public: + static PluginManager &Instance(); + + // 设置生成的插件信息 + void SetGeneratedPluginInfo(const GenPluginInfo &pluginInfo); + + // 从生成器加载插件 + bool LoadPluginFromGenerator(); + + // 卸载当前插件 + void UnloadCurrentPlugin(); + + // 获取监控器实例 + DataMonitorBasePtr GetMonitor(const std::string &interfaceName); + + // 获取所有支持的接口 + std::vector GetSupportedInterfaces(); + + // 检查插件是否已加载 + bool IsPluginLoaded() const; + +private: + PluginManager() = default; + ~PluginManager(); + PluginManager(const PluginManager &) = delete; + PluginManager &operator=(const PluginManager &) = delete; + + // 当前加载的插件信息 + void *m_pluginHandle = nullptr; + PluginInfo *m_pluginInfo = nullptr; + GetPluginInfoFunc m_getPluginInfoFunc = nullptr; + CreateMonitorFunc m_createMonitorFunc = nullptr; + DestroyMonitorFunc m_destroyMonitorFunc = nullptr; + GetSupportedInterfacesFunc m_getSupportedInterfacesFunc = nullptr; + FreeStringArrayFunc m_freeStringArrayFunc = nullptr; + + // 生成的插件信息 + GenPluginInfo m_generatedPluginInfo; + + // 监控器实例缓存 + std::unordered_map m_monitorCache; +}; \ No newline at end of file diff --git a/XNMonitorServer/SystemControl.cpp b/XNMonitorServer/SystemControl.cpp index 3f354ea..3d10b8a 100644 --- a/XNMonitorServer/SystemControl.cpp +++ b/XNMonitorServer/SystemControl.cpp @@ -1,5 +1,5 @@ #include "SystemControl.h" -#include "../XNCore/XNIDL/XNSimStatusPubSubTypes.hpp" +#include "XNIDL/XNSimStatusPubSubTypes.hpp" SystemControl::~SystemControl() { diff --git a/XNMonitorServer/TypeDefine.h b/XNMonitorServer/TypeDefine.h index b7f51e7..8cc00c0 100755 --- a/XNMonitorServer/TypeDefine.h +++ b/XNMonitorServer/TypeDefine.h @@ -16,6 +16,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include diff --git a/XNMonitorServer/XNMonitorInterface.cpp b/XNMonitorServer/XNMonitorInterface.cpp index cdc8c13..db137ee 100644 --- a/XNMonitorServer/XNMonitorInterface.cpp +++ b/XNMonitorServer/XNMonitorInterface.cpp @@ -10,6 +10,8 @@ #include "DataInjectThread.h" #include "CSVDataInjectThread.h" #include "DataCollect.h" +#include "PluginManager.h" +#include "PluginGenerator.h" // 全局变量 static bool g_initialized = false; @@ -25,10 +27,19 @@ std::unordered_map g_dataInjectThreads; CSVDataInjectThread *g_csvDataInjectThread = nullptr; DataCollect *g_dataCollect = nullptr; -// 初始化函数实现 -int XN_Initialize(const char *domainId, int domainIdLen, char *errorMsg, int errorMsgSize) -{ +/** + * @brief 生成插件 + * @param confID 构型ID + * @param errorMsg 错误信息 + * @param errorMsgSize 错误信息大小 + * @return 0: 成功, -1: 失败 + */ +int XN_GenPlugin(int confID, int forceGen, char *errorMsg, int errorMsgSize); +// 初始化函数实现 +int XN_Initialize(const char *domainId, int domainIdLen, int confID, int forceGen, char *errorMsg, + int errorMsgSize) +{ if (g_initialized) { if (errorMsg && errorMsgSize > 0) { strncpy(errorMsg, "DDSMonitor Initialized Successfully", errorMsgSize - 1); @@ -56,6 +67,12 @@ int XN_Initialize(const char *domainId, int domainIdLen, char *errorMsg, int err return -1; } + // 生成插件 + if (XN_GenPlugin(confID, forceGen, errorMsg, errorMsgSize) != 0) { + return -1; + } + + // 先初始化DDS参与者 XNDDSErrorCode ret = TopicManager::Instance()->initializeParticipant(domainIdInt); if (ret != XNDDSErrorCode::SUCCESS) { if (errorMsg && errorMsgSize > 0) { @@ -67,6 +84,19 @@ int XN_Initialize(const char *domainId, int domainIdLen, char *errorMsg, int err return -1; } + // 初始化插件管理器 + auto &pluginManager = PluginManager::Instance(); + + // 加载生成的插件 + if (!pluginManager.LoadPluginFromGenerator()) { + if (errorMsg && errorMsgSize > 0) { + std::string error = "Failed to load generated plugin"; + strncpy(errorMsg, error.c_str(), errorMsgSize - 1); + errorMsg[errorMsgSize - 1] = '\0'; + } + return -1; + } + g_initialized = true; if (errorMsg && errorMsgSize > 0) { strncpy(errorMsg, "DDSMonitor Initialized Successfully", errorMsgSize - 1); @@ -855,8 +885,6 @@ int XN_StopCollectData(char *infoMsg, int infoMsgSize) 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); @@ -876,6 +904,75 @@ void XN_CleanupDataCollect() g_dataCollect = nullptr; } } + +// 生成插件函数实现 +int XN_GenPlugin(int confID, int forceGen, char *errorMsg, int errorMsgSize) +{ + try { + // 创建插件生成器 + PluginGenerator generator; + + // 从数据库加载插件信息 + if (!generator.loadPluginFromDatabase(confID)) { + if (errorMsg && errorMsgSize > 0) { + std::string error = + "Failed to load plugin info from database: " + generator.getLastError(); + strncpy(errorMsg, error.c_str(), errorMsgSize - 1); + errorMsg[errorMsgSize - 1] = '\0'; + } + return -1; + } + if (forceGen == 1) { + // 生成插件cpp文件 + if (!generator.generatePluginCpp()) { + if (errorMsg && errorMsgSize > 0) { + std::string error = + "Failed to generate plugin cpp: " + generator.getLastError(); + strncpy(errorMsg, error.c_str(), errorMsgSize - 1); + errorMsg[errorMsgSize - 1] = '\0'; + } + return -1; + } + + // 生成CMakeLists.txt + if (!generator.generateCMakeLists()) { + if (errorMsg && errorMsgSize > 0) { + std::string error = + "Failed to generate CMakeLists: " + generator.getLastError(); + strncpy(errorMsg, error.c_str(), errorMsgSize - 1); + errorMsg[errorMsgSize - 1] = '\0'; + } + return -1; + } + + // 编译插件 + if (!generator.compilePlugin()) { + if (errorMsg && errorMsgSize > 0) { + std::string error = "Failed to compile plugin: " + generator.getLastError(); + strncpy(errorMsg, error.c_str(), errorMsgSize - 1); + errorMsg[errorMsgSize - 1] = '\0'; + } + return -1; + } + } + + // 保存插件信息到插件管理器 + PluginManager::Instance().SetGeneratedPluginInfo(generator.getPluginInfo()); + + if (errorMsg && errorMsgSize > 0) { + strncpy(errorMsg, "Plugin generated and compiled successfully", errorMsgSize - 1); + errorMsg[errorMsgSize - 1] = '\0'; + } + return 0; + } catch (const std::exception &e) { + if (errorMsg && errorMsgSize > 0) { + strncpy(errorMsg, e.what(), errorMsgSize - 1); + errorMsg[errorMsgSize - 1] = '\0'; + } + return -1; + } +} + // 清理函数实现 void XN_Cleanup() { @@ -892,15 +989,19 @@ void XN_Cleanup() if (g_systemControlStarted) { XN_CleanupEngineControl(); } - // 停止并清理数据采集 - XN_CleanupDataCollect(); - // 停止并清理CSV数据注入 - XN_CleanupCsvDataInject(); // 停止并清理数据注入 XN_CleanupInjectContinuous(); + // 停止并清理CSV数据注入 + XN_CleanupCsvDataInject(); + // 停止并清理数据采集 + XN_CleanupDataCollect(); - // 清理DDS参与者 + // 先清理DDS参与者,再卸载插件 TopicManager::cleanupParticipant(); + + // 卸载动态加载的插件 + PluginManager::Instance().UnloadCurrentPlugin(); + g_initialized = false; } } \ No newline at end of file diff --git a/XNMonitorServer/XNMonitorInterface.h b/XNMonitorServer/XNMonitorInterface.h index e9d9c9b..5be2d21 100644 --- a/XNMonitorServer/XNMonitorInterface.h +++ b/XNMonitorServer/XNMonitorInterface.h @@ -16,12 +16,13 @@ extern "C" * @brief 初始化DDS监控服务器 * @param domainId 域ID * @param domainIdLen 域ID长度 + * @param confID 构型ID * @param errorMsg 错误信息 * @param errorMsgSize 错误信息大小 * @return 0: 成功, -1: 失败 */ - int XNMONITORSERVER_EXPORT XN_Initialize(const char *domainId, int domainIdLen, char *errorMsg, - int errorMsgSize); + int XNMONITORSERVER_EXPORT XN_Initialize(const char *domainId, int domainIdLen, int confID, + int forceGen, char *errorMsg, int errorMsgSize); /** * @brief 清理DDS监控服务器 @@ -270,6 +271,11 @@ extern "C" */ int XNMONITORSERVER_EXPORT XN_StopCollectData(char *infoMsg, int infoMsgSize); + /** + * @brief 清理所有资源 + */ + void XNMONITORSERVER_EXPORT XN_Cleanup(); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/XNMonitorServer/test/CMakeLists.txt b/XNMonitorServer/test/CMakeLists.txt new file mode 100644 index 0000000..0089fa6 --- /dev/null +++ b/XNMonitorServer/test/CMakeLists.txt @@ -0,0 +1,56 @@ +# 测试程序的CMakeLists.txt +cmake_minimum_required(VERSION 3.10) + +# 设置项目名称 +project(XNMonitorServer_Test) + +# 设置C++标准 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 查找必要的包 +find_package(PkgConfig REQUIRED) +find_package(FastDDS REQUIRED) + +# 设置XNCore路径 +if(DEFINED ENV{XNCore}) + set(XNCore_PATH $ENV{XNCore}) +else() + message(FATAL_ERROR "Environment variable XNCore is not set.") +endif() + +# 包含目录 +include_directories( + ${CMAKE_SOURCE_DIR} + ${XNCore_PATH}/include +) + +# 创建测试可执行文件 +add_executable(test_initialize test_initialize.cpp) + +# 链接库 +target_link_libraries(test_initialize + fastcdr + fastdds + OpenSSL::SSL + OpenSSL::Crypto + pthread + dl + ${XNCore_PATH}/lib/libXNMonitorServer.so +) + +# 链接主项目的库(如果存在) +if(TARGET XNMonitorServer) + target_link_libraries(test_initialize XNMonitorServer) +endif() + +# 设置运行时库路径 +set_target_properties(test_initialize PROPERTIES + INSTALL_RPATH "${XNCore_PATH}/lib" + BUILD_WITH_INSTALL_RPATH TRUE +) + +# 安装目标 +install(TARGETS test_initialize + RUNTIME DESTINATION bin +) \ No newline at end of file diff --git a/XNMonitorServer/test/test_initialize.cpp b/XNMonitorServer/test/test_initialize.cpp new file mode 100644 index 0000000..5740ac4 --- /dev/null +++ b/XNMonitorServer/test/test_initialize.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include "../XNMonitorInterface.h" + +int main() +{ + std::cout << "=== XNMonitorServer 初始化测试 ===" << std::endl; + + // 测试参数 + const char *domainId = "10"; + int domainIdLen = strlen(domainId); + int confID = 1; + int forceGen = 0; + + char errorMsg[1024]; + int errorMsgSize = sizeof(errorMsg); + + std::cout << "测试参数:" << std::endl; + std::cout << " domainId: " << domainId << std::endl; + std::cout << " confID: " << confID << std::endl; + std::cout << " forceGen: " << forceGen << std::endl; + std::cout << std::endl; + + // 调用XN_Initialize + std::cout << "正在调用 XN_Initialize..." << std::endl; + int result = XN_Initialize(domainId, domainIdLen, confID, forceGen, errorMsg, errorMsgSize); + + if (result == 0) { + std::cout << "✓ XN_Initialize 调用成功!" << std::endl; + std::cout << "返回信息: " << errorMsg << std::endl; + + // 测试其他功能 + std::cout << std::endl << "=== 测试其他功能 ===" << std::endl; + + // 测试系统信息监控 + std::cout << "正在启动系统信息监控..." << std::endl; + int sysResult = XN_StartMonitorSystemInfo(errorMsg, errorMsgSize); + if (sysResult == 0) { + std::cout << "✓ 系统信息监控启动成功" << std::endl; + + // 获取系统信息 + char sysInfo[2048]; + int sysInfoSize = sizeof(sysInfo); + int getSysResult = XN_GetSystemInfo(sysInfo, sysInfoSize); + if (getSysResult == 0) { + std::cout << "✓ 获取系统信息成功" << std::endl; + std::cout << "系统信息: " << sysInfo << std::endl; + } else { + std::cout << "✗ 获取系统信息失败: " << sysInfo << std::endl; + } + + // 停止系统信息监控 + XN_StopMonitorSystemInfo(); + std::cout << "✓ 系统信息监控已停止" << std::endl; + } else { + std::cout << "✗ 系统信息监控启动失败: " << errorMsg << std::endl; + } + + // 测试模型信息监控 + std::cout << std::endl << "正在启动模型信息监控..." << std::endl; + int modelResult = XN_StartMonitorModelInfo(errorMsg, errorMsgSize); + if (modelResult == 0) { + std::cout << "✓ 模型信息监控启动成功" << std::endl; + + // 获取模型信息 + char modelInfo[2048]; + int modelInfoSize = sizeof(modelInfo); + int getModelResult = XN_GetModelInfo(modelInfo, modelInfoSize); + if (getModelResult == 0) { + std::cout << "✓ 获取模型信息成功" << std::endl; + std::cout << "模型信息: " << modelInfo << std::endl; + } else { + std::cout << "✗ 获取模型信息失败: " << modelInfo << std::endl; + } + + // 停止模型信息监控 + XN_StopMonitorModelInfo(); + std::cout << "✓ 模型信息监控已停止" << std::endl; + } else { + std::cout << "✗ 模型信息监控启动失败: " << errorMsg << std::endl; + } + + // 清理资源 + std::cout << std::endl << "=== 清理资源 ===" << std::endl; + XN_Cleanup(); + std::cout << "✓ 资源清理完成" << std::endl; + + } else { + std::cout << "✗ XN_Initialize 调用失败!" << std::endl; + std::cout << "错误信息: " << errorMsg << std::endl; + } + + std::cout << std::endl << "=== 测试完成 ===" << std::endl; + return result; +} \ No newline at end of file diff --git a/XNSimPortal/components/run-sim.js b/XNSimPortal/components/run-sim.js index 87be1c2..96b01fa 100644 --- a/XNSimPortal/components/run-sim.js +++ b/XNSimPortal/components/run-sim.js @@ -341,7 +341,8 @@ class RunSim extends HTMLElement { 'Content-Type': 'application/json' }, body: JSON.stringify({ - domainId: domainId, + domainId: domainId, + confID: confID }) }); @@ -569,7 +570,11 @@ class RunSim extends HTMLElement { headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ domainId }) + body: JSON.stringify({ + domainId: domainId, + confID: confID, + forceGen : 1 + }) }); if (!ddsInitResponse.ok) { diff --git a/XNSimPortal/routes/DDSMonitor.js b/XNSimPortal/routes/DDSMonitor.js index 3fa3cf8..b69c589 100644 --- a/XNSimPortal/routes/DDSMonitor.js +++ b/XNSimPortal/routes/DDSMonitor.js @@ -6,22 +6,24 @@ const { initializeMonitor, cleanupMonitor } = require('../utils/xnCoreService'); let monitorStatus = { isInitialized: false, domainId: null, + confID: null, + forceGen: 0, lastError: null }; // 初始化监控服务 router.post('/initialize', async (req, res) => { try { - const { domainId } = req.body; + const { domainId, confID, forceGen = 0 } = req.body; - if (!domainId) { - return res.status(400).json({ error: '缺少必要的参数' }); + if (!domainId || confID === undefined) { + return res.status(400).json({ error: '缺少必要的参数: domainId 和 confID' }); } - // 如果已经初始化,检查域ID是否匹配 + // 如果已经初始化,检查域ID和配置ID是否匹配 if (monitorStatus.isInitialized) { - if (monitorStatus.domainId !== domainId) { - return res.status(400).json({ error: 'DDS域ID不匹配' }); + if (monitorStatus.domainId !== domainId || monitorStatus.confID !== confID) { + return res.status(400).json({ error: 'DDS域ID或配置ID不匹配' }); } return res.json({ message: '监控服务已初始化', @@ -30,7 +32,7 @@ router.post('/initialize', async (req, res) => { } // 首次初始化 - const result = initializeMonitor(domainId); + const result = initializeMonitor(domainId, confID, forceGen); if (result && result.includes('失败')) { monitorStatus.lastError = result; return res.status(500).json({ error: result }); @@ -38,6 +40,8 @@ router.post('/initialize', async (req, res) => { monitorStatus.isInitialized = true; monitorStatus.domainId = domainId; + monitorStatus.confID = confID; + monitorStatus.forceGen = forceGen; monitorStatus.lastError = null; res.json({ @@ -59,6 +63,8 @@ router.post('/unregister', async (req, res) => { monitorStatus = { isInitialized: false, domainId: null, + confID: null, + forceGen: 0, lastError: null }; diff --git a/XNSimPortal/utils/xnCoreService.js b/XNSimPortal/utils/xnCoreService.js index 48221ac..3bfe521 100644 --- a/XNSimPortal/utils/xnCoreService.js +++ b/XNSimPortal/utils/xnCoreService.js @@ -58,7 +58,7 @@ try { try { monitorLib = ffi.Library(monitorLibPath, { - 'XN_Initialize': ['int', [StringType, 'int', StringType, 'int']], + 'XN_Initialize': ['int', [StringType, 'int', 'int', 'int', StringType, 'int']], 'XN_Cleanup': ['void', []], 'XN_StartMonitorSystemInfo': ['int', [StringType, 'int']], 'XN_GetSystemInfo': ['int', [StringType, 'int']], @@ -156,14 +156,14 @@ function stringToBuffer(str) { } // 初始化监控服务器 -function initializeMonitor(domainId) { +function initializeMonitor(domainId, confID, forceGen = 0) { if (!monitorLib) { return '监控服务器库未加载'; } try { // 创建错误消息缓冲区 const errorMsg = Buffer.alloc(1024); - const result = monitorLib.XN_Initialize(domainId, domainId.length, errorMsg, errorMsg.length); + const result = monitorLib.XN_Initialize(domainId, domainId.length, confID, forceGen, errorMsg, errorMsg.length); if (result !== 0) { return `初始化失败: ${errorMsg.toString('utf8').replace(/\0/g, '')}`;