diff --git a/Release/Configuration/C909_V1/IDL/ATA04/CMakeLists.txt b/Release/Configuration/C909_V1/IDL/ATA04/CMakeLists.txt index 52faa11..90ab26a 100644 --- a/Release/Configuration/C909_V1/IDL/ATA04/CMakeLists.txt +++ b/Release/Configuration/C909_V1/IDL/ATA04/CMakeLists.txt @@ -1,22 +1,22 @@ # ATA04接口的源文件 set(ATA04_SOURCES - Aerodynamics_input.hpp - Aerodynamics_input.cxx - GroundHandling_input.hpp - GroundHandling_input.cxx - GroundHandling_output.hpp - GroundHandling_output.cxx - Aerodynamics_output.hpp - Aerodynamics_output.cxx - WeightBalance_input.hpp - WeightBalance_input.cxx - WeightBalance_output.hpp - WeightBalance_output.cxx - GroundHandling_heartbeat.hpp - GroundHandling_heartbeat.cxx - WeightBalance_heartbeat.hpp - WeightBalance_heartbeat.cxx - Aerodynamics_heartbeat.hpp - Aerodynamics_heartbeat.cxx + ATA04/Aerodynamics_input.hpp + ATA04/Aerodynamics_input.cxx + ATA04/GroundHandling_input.hpp + ATA04/GroundHandling_input.cxx + ATA04/GroundHandling_output.hpp + ATA04/GroundHandling_output.cxx + ATA04/Aerodynamics_output.hpp + ATA04/Aerodynamics_output.cxx + ATA04/WeightBalance_input.hpp + ATA04/WeightBalance_input.cxx + ATA04/WeightBalance_output.hpp + ATA04/WeightBalance_output.cxx + ATA04/GroundHandling_heartbeat.hpp + ATA04/GroundHandling_heartbeat.cxx + ATA04/WeightBalance_heartbeat.hpp + ATA04/WeightBalance_heartbeat.cxx + ATA04/Aerodynamics_heartbeat.hpp + ATA04/Aerodynamics_heartbeat.cxx ) -list(APPEND ALL_SUBDIR_SOURCES ${ATA04_SOURCES}) +set(ALL_SUBDIR_SOURCES ${ALL_SUBDIR_SOURCES} ${ATA04_SOURCES} PARENT_SCOPE) diff --git a/Release/Configuration/C909_V1/IDL/CMakeLists.txt b/Release/Configuration/C909_V1/IDL/CMakeLists.txt index cc320fb..3d3f0bf 100644 --- a/Release/Configuration/C909_V1/IDL/CMakeLists.txt +++ b/Release/Configuration/C909_V1/IDL/CMakeLists.txt @@ -3,6 +3,13 @@ project(C909_V1_Interface LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build (Debug, Release, RelWithDebInfo, MinSizeRel)" FORCE) +endif() +if(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -DNDEBUG") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -DNDEBUG") +endif() if(DEFINED ENV{XNCore}) set(XNCore_PATH $ENV{XNCore}) else() diff --git a/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5.zip b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5.zip new file mode 100644 index 0000000..6b1528c Binary files /dev/null and b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5.zip differ diff --git a/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/CMakeLists.txt b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/CMakeLists.txt new file mode 100644 index 0000000..88c91d4 --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.16) + +project(XNAerodynamics LANGUAGES CXX) + +set(MODEL_VERSION "2.0.3.5") + +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(XNAerodynamics SHARED + XNAerodynamics_global.h + XNAerodynamics.cpp + XNAerodynamics.h + XNAerodynamics_p.h +) + +set_target_properties(XNAerodynamics PROPERTIES + LIBRARY_OUTPUT_NAME "libXNAerodynamics.so.2.0.3.5" + PREFIX "" + SUFFIX "" + SKIP_BUILD_RPATH TRUE + BUILD_WITH_INSTALL_RPATH TRUE +) + +target_link_libraries(XNAerodynamics PRIVATE + ${XNCore_PATH}/lib/libXNCore.so + ${XNCore_PATH}/lib/libC909_V1_Interface.so + dl +) + +target_compile_definitions(XNAerodynamics PRIVATE XNAERODYNAMICS_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 XNAerodynamics 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/XNAerodynamics_2.0.3.5/XNAerodynamics.cpp b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics.cpp new file mode 100644 index 0000000..cbf8aff --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics.cpp @@ -0,0 +1,118 @@ +#include "XNAerodynamics.h" +#include "XNAerodynamics_p.h" +#include + +XN_MODEL_INITIALIZE(XNAerodynamics) + +XNAerodynamics::XNAerodynamics() : XNModelObject(new XNAerodynamicsPrivate()) +{ +} + +XNAerodynamics::~XNAerodynamics() +{ +} + +XNAerodynamics::XNAerodynamics(PrivateType *p) : XNModelObject(p) +{ +} + +void XNAerodynamics::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 _Z27SACSCAerodynamicsEntryPointP20ComacDataStructure_S"); + } + } + /* 在这里进行其它初始化 */ +} + +void XNAerodynamics::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 XNAerodynamics::StepUpdate() +{ + T_D(); + SuperType::StepUpdate(); + if (d->_fun) { + d->_inputInterface.getData(d->_data.input_aero); + d->_fun(&d->_data); + d->_outputInterface.setData(d->_data.output_aero); + d->_heartbeatInterface.setData(&d->_data); + } + /* 在这里进行其它运行时计算 */ +} + +void XNAerodynamics::InitializeData() +{ + T_D(); + d->_data.input_aero = new input_aero_S; + // TODO: 在这里初始化输入数据 + d->_data.input_aero->l_04_i_aerocomac_ail_f8 = new double[10]; + d->_data.input_aero->l_04_i_aerocomac_elv_f8 = new double[4]; + d->_data.input_aero->l_04_i_aerocomac_rud_f8 = new double[2]; + d->_data.input_aero->l_04_i_aerocomac_gear_f8 = new double[7]; + d->_data.input_aero->l_04_i_aerocomac_flap_f8 = new double[10]; + d->_data.input_aero->l_04_i_aerocomac_slat_f8 = new double[20]; + d->_data.input_aero->l_04_i_aerocomac_spl_f8 = new double[20]; + d->_data.input_aero->l_04_i_aerocomac_tnet_f8 = new double[4]; + d->_data.input_aero->l_04_i_aerocomac_kice_f8 = new double[20]; + d->_data.output_aero = new output_aero_S; + // TODO: 在这里初始化输出数据 + + d->_data.aero_model_heartbeat = 0; +} + +void XNAerodynamics::ReleaseData() +{ + T_D(); + // TODO: 在这里释放输入数据 + + if (d->_data.input_aero) { + if (d->_data.input_aero->l_04_i_aerocomac_ail_f8) { + delete[] d->_data.input_aero->l_04_i_aerocomac_ail_f8; + } + if (d->_data.input_aero->l_04_i_aerocomac_elv_f8) { + delete[] d->_data.input_aero->l_04_i_aerocomac_elv_f8; + } + if (d->_data.input_aero->l_04_i_aerocomac_rud_f8) { + delete[] d->_data.input_aero->l_04_i_aerocomac_rud_f8; + } + if (d->_data.input_aero->l_04_i_aerocomac_gear_f8) { + delete[] d->_data.input_aero->l_04_i_aerocomac_gear_f8; + } + if (d->_data.input_aero->l_04_i_aerocomac_flap_f8) { + delete[] d->_data.input_aero->l_04_i_aerocomac_flap_f8; + } + if (d->_data.input_aero->l_04_i_aerocomac_slat_f8) { + delete[] d->_data.input_aero->l_04_i_aerocomac_slat_f8; + } + if (d->_data.input_aero->l_04_i_aerocomac_spl_f8) { + delete[] d->_data.input_aero->l_04_i_aerocomac_spl_f8; + } + if (d->_data.input_aero->l_04_i_aerocomac_tnet_f8) { + delete[] d->_data.input_aero->l_04_i_aerocomac_tnet_f8; + } + if (d->_data.input_aero->l_04_i_aerocomac_kice_f8) { + delete[] d->_data.input_aero->l_04_i_aerocomac_kice_f8; + } + delete d->_data.input_aero; + d->_data.input_aero = nullptr; + } + // TODO: 在这里释放输出数据 + + if (d->_data.output_aero) { + delete d->_data.output_aero; + d->_data.output_aero = nullptr; + } +} diff --git a/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics.h b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics.h new file mode 100644 index 0000000..d943e92 --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics.h @@ -0,0 +1,30 @@ +#pragma once + +#include "XNAerodynamics_global.h" +#include + +struct XNAerodynamicsPrivate; + +class XNAERODYNAMICS_EXPORT XNAerodynamics : public XNModelObject +{ +XN_METATYPE(XNAerodynamics, XNModelObject) +XN_DECLARE_PRIVATE(XNAerodynamics) +public: + XNAerodynamics(); + virtual ~XNAerodynamics(); + +protected: + XNAerodynamics(PrivateType *p); + +public: + virtual void Initialize() override; + virtual void PrepareForExecute() override; + virtual void StepUpdate() override; + +protected: + void InitializeData(); + void ReleaseData(); +}; + +XNCLASS_PTR_DECLARE(XNAerodynamics) + diff --git a/Release/Configuration/C909_V1/Models/XNAerodynamics_V1.0.0.0.mcfg b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics.mcfg similarity index 56% rename from Release/Configuration/C909_V1/Models/XNAerodynamics_V1.0.0.0.mcfg rename to Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics.mcfg index c565d59..c8f5b97 100644 --- a/Release/Configuration/C909_V1/Models/XNAerodynamics_V1.0.0.0.mcfg +++ b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics.mcfg @@ -1,12 +1,12 @@ XNAerodynamics - ATA04气动模型 + ATA04Aerodynamics Jin - 1.0.0 - 2025-02-04 10:00:00 - 2025-02-04 10:00:00 - 0-0 + 2.0.3.5 + 2025-04-27 13:58:13 + 2025-04-27 13:58:13 + 0 99 ATA04_SACSCAerodynamics_2.0.3.5H_20241106/libSACSCAerodynamics.so diff --git a/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics_global.h b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics_global.h new file mode 100644 index 0000000..3c5a54b --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics_global.h @@ -0,0 +1,11 @@ +#ifndef XNAERODYNAMICS_GLOBAL_H +#define XNAERODYNAMICS_GLOBAL_H + +#if defined(XNAERODYNAMICS_LIBRARY) +#define XNAERODYNAMICS_EXPORT __attribute__((visibility("default"))) +#else +#define XNAERODYNAMICS_EXPORT __attribute__((visibility("default"))) +#endif + +#endif // XNAERODYNAMICS_GLOBAL_H + diff --git a/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics_p.h b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics_p.h new file mode 100644 index 0000000..8b21da5 --- /dev/null +++ b/Release/Configuration/C909_V1/ModelProjects/XNAerodynamics_2.0.3.5/XNAerodynamics_p.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../../Packages/ATA04_SACSCAerodynamics_2.0.3.5H_20241106/std_04_dll.h" +#include "../../IDL/C909_V1_Interface.h" + +using InterfaceType = ComacDataStructure_S; +typedef void (*FunctionType)(InterfaceType *); + +struct XNAerodynamicsPrivate : public XNModelObjectPrivate{ + FunctionType _fun = nullptr; + InterfaceType _data; + std::string _entryPointName = "_Z27SACSCAerodynamicsEntryPointP20ComacDataStructure_S"; + std::mutex _mutex; + XNSim::C909::ATA04::Aerodynamics_input_Interface _inputInterface; + XNSim::C909::ATA04::Aerodynamics_output_Interface _outputInterface; + XNSim::C909::ATA04::Aerodynamics_heartbeat_Interface _heartbeatInterface; +}; + diff --git a/Release/Configuration/C909_V1/Models/XNAerodynamics_V2.0.3.5.mcfg b/Release/Configuration/C909_V1/Models/XNAerodynamics_V2.0.3.5.mcfg new file mode 100644 index 0000000..c8f5b97 --- /dev/null +++ b/Release/Configuration/C909_V1/Models/XNAerodynamics_V2.0.3.5.mcfg @@ -0,0 +1,13 @@ + + + XNAerodynamics + ATA04Aerodynamics + Jin + 2.0.3.5 + 2025-04-27 13:58:13 + 2025-04-27 13:58:13 + 0 + 99 + ATA04_SACSCAerodynamics_2.0.3.5H_20241106/libSACSCAerodynamics.so + + diff --git a/Release/Configuration/C909_V1/Models/libXNAerodynamics.so.1.0.0.0 b/Release/Configuration/C909_V1/Models/libXNAerodynamics.so.1.0.0.0 deleted file mode 100644 index 7716a43..0000000 Binary files a/Release/Configuration/C909_V1/Models/libXNAerodynamics.so.1.0.0.0 and /dev/null differ diff --git a/Release/Configuration/C909_V1/Models/libXNAerodynamics.so.2.0.3.5 b/Release/Configuration/C909_V1/Models/libXNAerodynamics.so.2.0.3.5 new file mode 100644 index 0000000..c621a9e Binary files /dev/null and b/Release/Configuration/C909_V1/Models/libXNAerodynamics.so.2.0.3.5 differ diff --git a/Release/database/XNSim.db b/Release/database/XNSim.db index 7879cd4..d7759ae 100644 Binary files a/Release/database/XNSim.db and b/Release/database/XNSim.db differ diff --git a/Release/lib/libsqlite3.so.0 b/Release/lib/libsqlite3.so.0 new file mode 120000 index 0000000..026b67c --- /dev/null +++ b/Release/lib/libsqlite3.so.0 @@ -0,0 +1 @@ +libsqlite3.so.0.8.6 \ No newline at end of file diff --git a/Release/lib/libsqlite3.so.0.8.6 b/Release/lib/libsqlite3.so.0.8.6 new file mode 100644 index 0000000..71d858b Binary files /dev/null and b/Release/lib/libsqlite3.so.0.8.6 differ diff --git a/Release/lib/libzip.so.4 b/Release/lib/libzip.so.4 new file mode 120000 index 0000000..11783be --- /dev/null +++ b/Release/lib/libzip.so.4 @@ -0,0 +1 @@ +libzip.so.4.0 \ No newline at end of file diff --git a/Release/lib/libzip.so.4.0 b/Release/lib/libzip.so.4.0 new file mode 100644 index 0000000..1b87d03 Binary files /dev/null and b/Release/lib/libzip.so.4.0 differ diff --git a/XNInterfaceGenServer/CMakeListsGen.cpp b/XNInterfaceGenServer/CMakeListsGen.cpp index e7f7ebb..e5ca4e2 100644 --- a/XNInterfaceGenServer/CMakeListsGen.cpp +++ b/XNInterfaceGenServer/CMakeListsGen.cpp @@ -18,6 +18,21 @@ bool CMakeListsGen::generateCMakeLists(const AllInterfaceData &interfaceData, cmakeFile << "set(CMAKE_CXX_STANDARD_REQUIRED ON)" << std::endl; cmakeFile << "set(CMAKE_POSITION_INDEPENDENT_CODE ON)" << std::endl; + // 设置默认构建类型为Release + cmakeFile << "if(NOT CMAKE_BUILD_TYPE)" << std::endl; + cmakeFile << "\tset(CMAKE_BUILD_TYPE Release CACHE STRING " + << "\"Choose the type of build (Debug, Release, RelWithDebInfo, MinSizeRel)\"" + << " FORCE)" << std::endl; + cmakeFile << "endif()" << std::endl; + + // Release模式优化设置 + cmakeFile << "if(CMAKE_BUILD_TYPE STREQUAL \"Release\")" << std::endl; + cmakeFile << "\tset(CMAKE_CXX_FLAGS_RELEASE \"${CMAKE_CXX_FLAGS_RELEASE} -O3 -DNDEBUG\")" + << std::endl; + cmakeFile << "\tset(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE} -O3 -DNDEBUG\")" + << std::endl; + cmakeFile << "endif()" << std::endl; + // 获取环境变量 cmakeFile << "if(DEFINED ENV{XNCore})" << std::endl; cmakeFile << "\tset(XNCore_PATH $ENV{XNCore})" << std::endl; diff --git a/XNInterfaceGenServer/DDSInterfaceGen.cpp b/XNInterfaceGenServer/DDSInterfaceGen.cpp index 373cab8..e2ef6f9 100644 --- a/XNInterfaceGenServer/DDSInterfaceGen.cpp +++ b/XNInterfaceGenServer/DDSInterfaceGen.cpp @@ -32,13 +32,13 @@ bool DDSInterfaceGen::generateDDSInterface(const AllInterfaceData &interfaceData structData.interfaceData); headerFile << "#include \"" << ataName << "/" << structName << ".hpp\"" << std::endl; - cmakeListsPath << " " << structName << ".hpp" << std::endl; - cmakeListsPath << " " << structName << ".cxx" << std::endl; + cmakeListsPath << " " << ataName << "/" << structName << ".hpp" << std::endl; + cmakeListsPath << " " << ataName << "/" << structName << ".cxx" << std::endl; } cmakeListsPath << ")" << std::endl; - // 将ATA04的源文件添加到父目录的ALL_SUBDIR_SOURCES变量中 - cmakeListsPath << "list(APPEND ALL_SUBDIR_SOURCES ${" << ataName << "_SOURCES})" - << std::endl; + // 将ATA章节子文件夹的源文件添加到父目录的ALL_SUBDIR_SOURCES变量中 + cmakeListsPath << "set(ALL_SUBDIR_SOURCES ${ALL_SUBDIR_SOURCES} ${" << ataName + << "_SOURCES} PARENT_SCOPE)" << std::endl; cmakeListsPath.close(); } headerFile.close(); diff --git a/XNModelGenServer/.vscode/settings.json b/XNModelGenServer/.vscode/settings.json index 6fd6067..d956cd1 100644 --- a/XNModelGenServer/.vscode/settings.json +++ b/XNModelGenServer/.vscode/settings.json @@ -56,6 +56,17 @@ "streambuf": "cpp", "cinttypes": "cpp", "typeinfo": "cpp", - "valarray": "cpp" + "valarray": "cpp", + "bitset": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "list": "cpp", + "set": "cpp", + "regex": "cpp", + "mutex": "cpp", + "stop_token": "cpp", + "thread": "cpp", + "typeindex": "cpp", + "variant": "cpp" } } diff --git a/XNModelGenServer/CMakeLists.txt b/XNModelGenServer/CMakeLists.txt index 1a8c80e..b6cad66 100644 --- a/XNModelGenServer/CMakeLists.txt +++ b/XNModelGenServer/CMakeLists.txt @@ -15,6 +15,8 @@ endif() # 查找依赖包 find_package(SQLite3 REQUIRED) find_package(nlohmann_json 3.9.1 REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(LIBZIP REQUIRED libzip) add_library(XNModelGenServer SHARED XNModelGenServer.h @@ -22,15 +24,21 @@ add_library(XNModelGenServer SHARED XNModelGenServer_global.h XNModelGen.h XNModelGen.cpp + XNCodeZip.h + XNCodeZip.cpp + XNModelCompile.h + XNModelCompile.cpp ) # 添加头文件搜索路径 target_include_directories(XNModelGenServer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(XNModelGenServer PRIVATE ${LIBZIP_INCLUDE_DIRS}) # 链接依赖库 target_link_libraries(XNModelGenServer PRIVATE SQLite::SQLite3 nlohmann_json::nlohmann_json + ${LIBZIP_LIBRARIES} ) target_compile_definitions(XNModelGenServer PRIVATE XNModelGenServer_LIBRARY) diff --git a/XNModelGenServer/XNCodeZip.cpp b/XNModelGenServer/XNCodeZip.cpp new file mode 100644 index 0000000..3a989bd --- /dev/null +++ b/XNModelGenServer/XNCodeZip.cpp @@ -0,0 +1,216 @@ +#include "XNCodeZip.h" +#include +#include +#include +#include +#include +namespace fs = std::filesystem; + +int XNCodeZip::Zip(const std::string &srcPath, const std::string &dstPath, std::string &errorMsg) +{ + // 检查源路径是否存在 + if (!fs::exists(srcPath)) { + errorMsg = "源路径不存在: " + srcPath; + return -1; + } + + // 确保目标目录存在 + fs::path dstDir = fs::path(dstPath).parent_path(); + if (!fs::exists(dstDir)) { + fs::create_directories(dstDir); + } + + // 打开zip文件 + int err = 0; + zip_t *zip = zip_open(dstPath.c_str(), ZIP_CREATE | ZIP_TRUNCATE, &err); + if (zip == nullptr) { + char errstr[1024]; + zip_error_to_str(errstr, sizeof(errstr), err, errno); + errorMsg = "无法创建zip文件: " + std::string(errstr); + return -1; + } + + // 递归添加文件和目录 + std::function addToZip = + [&](const fs::path ¤tPath, const fs::path &relativePath) { + if (fs::is_directory(currentPath)) { + // 添加目录 + std::string dirName = relativePath.string() + "/"; + zip_dir_add(zip, dirName.c_str(), ZIP_FL_OVERWRITE); + + // 递归处理子目录和文件 + for (const auto &entry : fs::directory_iterator(currentPath)) { + fs::path newRelativePath = relativePath / entry.path().filename(); + addToZip(entry.path(), newRelativePath); + } + } else if (fs::is_regular_file(currentPath)) { + // 添加文件 + std::string fileName = relativePath.string(); + zip_source_t *source = zip_source_file(zip, currentPath.c_str(), 0, 0); + if (source == nullptr) { + errorMsg = "无法创建zip源: " + fileName; + return; + } + + if (zip_file_add(zip, fileName.c_str(), source, ZIP_FL_OVERWRITE) < 0) { + zip_source_free(source); + errorMsg = "无法添加文件到zip: " + fileName; + return; + } + } + }; + + // 开始压缩 + fs::path srcDir(srcPath); + fs::path baseName = srcDir.filename(); + addToZip(srcDir, baseName); + + // 关闭zip文件 + zip_close(zip); + + return 0; +} + +int XNCodeZip::Unzip(const std::string &srcPath, const std::string &dstPath, std::string &errorMsg) +{ + // 检查源路径是否存在 + if (!fs::exists(srcPath)) { + errorMsg = "源路径不存在: " + srcPath; + return -1; + } + + // 确保目标目录存在 + fs::path dstDir = fs::path(dstPath); + if (!fs::exists(dstDir)) { + fs::create_directories(dstDir); + } + + // 打开zip文件 + int err = 0; + zip_t *zip = zip_open(srcPath.c_str(), 0, &err); + if (zip == nullptr) { + char errstr[1024]; + zip_error_to_str(errstr, sizeof(errstr), err, errno); + errorMsg = "无法打开zip文件: " + std::string(errstr); + return -1; + } + + // 获取zip文件中的条目数量 + zip_int64_t numEntries = zip_get_num_entries(zip, 0); + if (numEntries < 0) { + errorMsg = "无法获取zip文件条目数量"; + zip_close(zip); + return -1; + } + + // 检测根目录名称(去除额外层级) + std::string rootDirName; + bool hasRootDir = false; + + // 检查第一个条目来确定根目录结构 + if (numEntries > 0) { + struct zip_stat st; + if (zip_stat_index(zip, 0, 0, &st) >= 0) { + std::string firstEntry = st.name; + size_t slashPos = firstEntry.find('/'); + if (slashPos != std::string::npos) { + rootDirName = firstEntry.substr(0, slashPos); + hasRootDir = true; + } + } + } + + // 遍历并解压所有条目 + for (zip_int64_t i = 0; i < numEntries; ++i) { + // 获取条目信息 + struct zip_stat st; + if (zip_stat_index(zip, i, 0, &st) < 0) { + errorMsg = "无法获取zip条目信息"; + zip_close(zip); + return -1; + } + + // 跳过空条目 + if (st.size == 0 && st.name[strlen(st.name) - 1] == '/') { + continue; + } + + // 构建目标文件路径,去除根目录层级 + std::string relativePath = st.name; + if (hasRootDir && relativePath.find(rootDirName + "/") == 0) { + relativePath = relativePath.substr(rootDirName.length() + 1); + } + + // 如果去除根目录后路径为空,跳过 + if (relativePath.empty()) { + continue; + } + + fs::path targetPath = dstDir / relativePath; + + // 如果是目录,创建目录 + if (relativePath[relativePath.length() - 1] == '/') { + fs::create_directories(targetPath); + continue; + } + + // 确保文件的父目录存在 + fs::create_directories(targetPath.parent_path()); + + // 检查文件是否已存在 + if (fs::exists(targetPath)) { + // 可以选择跳过、覆盖或询问用户 + // 这里选择覆盖已有文件 + fs::remove(targetPath); + } + + // 打开zip条目 + zip_file_t *zipFile = zip_fopen_index(zip, i, 0); + if (zipFile == nullptr) { + errorMsg = "无法打开zip条目: " + std::string(st.name); + zip_close(zip); + return -1; + } + + // 创建目标文件 + std::ofstream outFile(targetPath, std::ios::binary); + if (!outFile.is_open()) { + errorMsg = "无法创建目标文件: " + targetPath.string(); + zip_fclose(zipFile); + zip_close(zip); + return -1; + } + + // 读取并写入文件内容 + char buffer[8192]; + zip_int64_t bytesRead; + while ((bytesRead = zip_fread(zipFile, buffer, sizeof(buffer))) > 0) { + outFile.write(buffer, bytesRead); + if (!outFile.good()) { + errorMsg = "写入文件失败: " + targetPath.string(); + outFile.close(); + zip_fclose(zipFile); + zip_close(zip); + return -1; + } + } + + // 检查读取是否成功 + if (bytesRead < 0) { + errorMsg = "读取zip条目失败: " + std::string(st.name); + outFile.close(); + zip_fclose(zipFile); + zip_close(zip); + return -1; + } + + // 关闭文件 + outFile.close(); + zip_fclose(zipFile); + } + + // 关闭zip文件 + zip_close(zip); + + return 0; +} \ No newline at end of file diff --git a/XNModelGenServer/XNCodeZip.h b/XNModelGenServer/XNCodeZip.h new file mode 100644 index 0000000..240accb --- /dev/null +++ b/XNModelGenServer/XNCodeZip.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +class XNCodeZip +{ +public: + XNCodeZip() = delete; + ~XNCodeZip() = delete; + + static int Zip(const std::string &srcPath, const std::string &dstPath, std::string &errorMsg); + static int Unzip(const std::string &srcPath, const std::string &dstPath, std::string &errorMsg); +}; \ No newline at end of file diff --git a/XNModelGenServer/XNModelCompile.cpp b/XNModelGenServer/XNModelCompile.cpp new file mode 100644 index 0000000..6a4c0b9 --- /dev/null +++ b/XNModelGenServer/XNModelCompile.cpp @@ -0,0 +1,64 @@ +#include "XNModelCompile.h" +#include +#include +#include +#include + +namespace fs = std::filesystem; +int XNModelCompile::Compile(const std::string &srcPath, std::string &errorMsg) +{ + // 检查源路径是否存在 + if (!fs::exists(srcPath)) { + errorMsg = "源路径不存在: " + srcPath; + return -1; + } + + // 检查build目录是否存在 + fs::path buildPath = fs::path(srcPath) / "build"; + if (fs::exists(buildPath)) { + // 删除build目录 + if (fs::remove_all(buildPath) != static_cast(-1)) { + std::cout << "已删除build目录: " << buildPath.string() << std::endl; + } else { + errorMsg = "无法删除build目录: " + buildPath.string(); + return -1; + } + } + + // 创建build目录 + if (!fs::exists(buildPath)) { + if (!fs::create_directory(buildPath)) { + errorMsg = "无法创建build目录: " + buildPath.string(); + return -1; + } + } + + // 切换到build目录 + if (chdir(buildPath.c_str()) != 0) { + errorMsg = "无法切换到build目录: " + buildPath.string(); + return -1; + } + + // 执行 cmake .. + int cmakeResult = system("cmake .."); + if (cmakeResult != 0) { + errorMsg = "cmake配置失败"; + return -1; + } + + // 执行 make + int makeResult = system("make"); + if (makeResult != 0) { + errorMsg = "make 编译失败"; + return -1; + } + + // 执行 make install + int makeInstallResult = system("make install"); + if (makeInstallResult != 0) { + errorMsg = "make install失败"; + return -1; + } + + return 0; +} \ No newline at end of file diff --git a/XNModelGenServer/XNModelCompile.h b/XNModelGenServer/XNModelCompile.h new file mode 100644 index 0000000..08f4791 --- /dev/null +++ b/XNModelGenServer/XNModelCompile.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +class XNModelCompile +{ +public: + XNModelCompile() = delete; + ~XNModelCompile() = delete; + + static int Compile(const std::string &srcPath, std::string &errorMsg); +}; \ No newline at end of file diff --git a/XNModelGenServer/XNModelGen.cpp b/XNModelGenServer/XNModelGen.cpp index 7f7e255..6e83f00 100644 --- a/XNModelGenServer/XNModelGen.cpp +++ b/XNModelGenServer/XNModelGen.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using json = nlohmann::json; namespace fs = std::filesystem; @@ -18,6 +19,11 @@ XNModelGen::~XNModelGen() { } +const std::string &XNModelGen::GetCodePath() const +{ + return m_codePath; +} + int XNModelGen::Initialize(const std::string &className, const std::string &version, const std::string &planeName, std::string &errorMsg) { @@ -47,33 +53,37 @@ int XNModelGen::Initialize(const std::string &className, const std::string &vers // 执行查询 rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { - // 读取数据到成员变量 - m_planeName = (const char *)sqlite3_column_text(stmt, 1); // PlaneName - m_className = (const char *)sqlite3_column_text(stmt, 2); // ClassName - m_version = (const char *)sqlite3_column_text(stmt, 3); // Version - m_confID = (const char *)sqlite3_column_text(stmt, 4); // ConfID - m_name = (const char *)sqlite3_column_text(stmt, 5); // Name - m_author = (const char *)sqlite3_column_text(stmt, 6); // Author - m_description = (const char *)sqlite3_column_text(stmt, 7); // Description - m_createTime = (const char *)sqlite3_column_text(stmt, 8); // CreateTime - m_changeTime = (const char *)sqlite3_column_text(stmt, 9); // ChangeTime - m_runFreq = (const char *)sqlite3_column_text(stmt, 10); // RunFreq - m_runNode = (const char *)sqlite3_column_text(stmt, 11); // RunNode - m_priority = (const char *)sqlite3_column_text(stmt, 12); // Priority - m_dataPackagePath = (const char *)sqlite3_column_text(stmt, 13); // DataPackagePath - m_dataPackageName = (const char *)sqlite3_column_text(stmt, 14); // DataPackageName + // 读取数据到成员变量 - 按照CREATE TABLE语句的字段顺序 + m_planeName = (const char *)sqlite3_column_text(stmt, 0); // PlaneName + m_className = (const char *)sqlite3_column_text(stmt, 1); // ClassName + m_version = (const char *)sqlite3_column_text(stmt, 2); // Version + m_confID = sqlite3_column_int(stmt, 3); // ConfID (INTEGER) + m_name = (const char *)sqlite3_column_text(stmt, 4); // Name + m_author = (const char *)sqlite3_column_text(stmt, 5); // Author + m_description = (const char *)sqlite3_column_text(stmt, 6); // Description + m_createTime = (const char *)sqlite3_column_text(stmt, 7); // CreatTime + m_changeTime = (const char *)sqlite3_column_text(stmt, 8); // ChangeTime + m_runFreq = std::to_string(sqlite3_column_int(stmt, 9)); // RunFreqGroup (INTEGER) + m_runNode = std::to_string(sqlite3_column_int(stmt, 10)); // RunNode (INTEGER) + m_priority = std::to_string(sqlite3_column_int(stmt, 11)); // Priority (INTEGER) + m_dataPackagePath = (const char *)sqlite3_column_text(stmt, 12); // DataPackagePath + m_dataPackageName = (const char *)sqlite3_column_text(stmt, 13); // DataPackageName m_dataPackageHeaderName = - (const char *)sqlite3_column_text(stmt, 15); // DataPackageHeaderName + (const char *)sqlite3_column_text(stmt, 14); // DataPackageHeaderName m_dataPackageEntryPoint = - (const char *)sqlite3_column_text(stmt, 16); // DataPackageEntryPoint + (const char *)sqlite3_column_text(stmt, 15); // DataPackageEntryPoint m_dataPackageInterfaceName = - (const char *)sqlite3_column_text(stmt, 17); // DataPackageInterfaceName - std::string inputStruct = (const char *)sqlite3_column_text(stmt, 18); // InputStructName - ParseStructName(inputStruct, m_inputStructName, m_inputStructName_Interface); - std::string outputStruct = (const char *)sqlite3_column_text(stmt, 19); // OutputStructName - ParseStructName(outputStruct, m_outputStructName, m_outputStructName_Interface); - std::string heartStruct = (const char *)sqlite3_column_text(stmt, 20); // HeartStructName - ParseStructName(outputStruct, m_heartStructName, m_heartStructName_Interface); + (const char *)sqlite3_column_text(stmt, 16); // DataPackageInterfaceName + std::string inputStruct = (const char *)sqlite3_column_text(stmt, 17); // InputStruct + ParseStructName(inputStruct, m_inputStructName_Interface, m_inputStructName.originalName); + ParseStructOriginalName(m_inputStructName); + std::string outputStruct = (const char *)sqlite3_column_text(stmt, 18); // OutputStruct + ParseStructName(outputStruct, m_outputStructName_Interface, + m_outputStructName.originalName); + ParseStructOriginalName(m_outputStructName); + std::string heartStruct = (const char *)sqlite3_column_text(stmt, 19); // HeartStruct + ParseStructName(heartStruct, m_heartStructName_Interface, m_heartStructName.originalName); + ParseStructOriginalName(m_heartStructName); // TODO cmdList暂不读取 } else if (rc == SQLITE_DONE) { // 没有找到数据 @@ -92,7 +102,7 @@ int XNModelGen::Initialize(const std::string &className, const std::string &vers sqlite3_finalize(stmt); // 根据m_confID查询Configuration表 - if (!m_confID.empty()) { + if (m_confID > 0) { std::string confSql = "SELECT ConfName FROM Configuration WHERE ConfID = ?"; sqlite3_stmt *confStmt; rc = sqlite3_prepare_v2(db, confSql.c_str(), -1, &confStmt, nullptr); @@ -102,8 +112,8 @@ int XNModelGen::Initialize(const std::string &className, const std::string &vers return -4; } - // 绑定参数 - sqlite3_bind_text(confStmt, 1, m_confID.c_str(), -1, SQLITE_STATIC); + // 绑定参数 - ConfID是INTEGER类型 + sqlite3_bind_int(confStmt, 1, m_confID); // 执行查询 rc = sqlite3_step(confStmt); @@ -112,7 +122,7 @@ int XNModelGen::Initialize(const std::string &className, const std::string &vers m_confName = (const char *)sqlite3_column_text(confStmt, 0); // ConfName } else if (rc == SQLITE_DONE) { // 没有找到Configuration记录 - errorMsg = "未找到匹配的Configuration记录,ConfID: " + m_confID; + errorMsg = "未找到匹配的Configuration记录,ConfID: " + std::to_string(m_confID); sqlite3_finalize(confStmt); sqlite3_close(db); return -5; @@ -261,13 +271,12 @@ bool XNModelGen::GenerateHeaderFile() pHeaderFile << "#pragma once" << std::endl; pHeaderFile << std::endl; - pHeaderFile << "#include "; + pHeaderFile << "#include " << std::endl; if (m_hasDataPackage) { - pHeaderFile << "#include \"" << m_workPath << "/Packages/" << m_dataPackagePath << "/" + pHeaderFile << "#include \"../../Packages/" << m_dataPackagePath << "/" << m_dataPackageHeaderName << "\"" << std::endl; } - pHeaderFile << "#include \"" << m_workPath << "/IDL/" << m_confName << "_Interface.h\"" - << std::endl; + pHeaderFile << "#include \"../../IDL/" << m_confName << "_Interface.h\"" << std::endl; pHeaderFile << std::endl; if (m_hasDataPackage) { pHeaderFile << "using InterfaceType = " << m_dataPackageInterfaceName << ";" << std::endl; @@ -282,7 +291,7 @@ bool XNModelGen::GenerateHeaderFile() pHeaderFile << " std::string _entryPointName = \"" << m_dataPackageEntryPoint << "\";" << std::endl; } - pHeaderFile << "std::mutex _mutex" << std::endl; + pHeaderFile << " std::mutex _mutex;" << std::endl; if (!m_inputStructName_Interface.empty()) { pHeaderFile << " " << m_inputStructName_Interface << "_Interface _inputInterface;" << std::endl; @@ -316,7 +325,7 @@ bool XNModelGen::GenerateSourceFile() sourceFile << std::endl; sourceFile << "XN_MODEL_INITIALIZE(" << m_className << ")" << std::endl; sourceFile << std::endl; - sourceFile << m_className << "::" << m_className << "() : XNModelObject(" << m_className + sourceFile << m_className << "::" << m_className << "() : XNModelObject(" << "new " << m_className << "Private())" << std::endl; sourceFile << "{" << std::endl; sourceFile << "}" << std::endl; @@ -370,19 +379,30 @@ bool XNModelGen::GenerateSourceFile() sourceFile << " SuperType::StepUpdate();" << std::endl; if (m_hasDataPackage) { sourceFile << " if(d->_fun){" << std::endl; - if (!m_inputStructName_Interface.empty() && !m_inputStructName.empty()) { - sourceFile << " d->_inputInterface.getData(&d->_data." << m_inputStructName - << ");" << std::endl; + if (!m_inputStructName_Interface.empty() && !m_inputStructName.structName.empty()) { + if (m_inputStructName.isPointer) { + sourceFile << " d->_inputInterface.getData(d->_data." + << m_inputStructName.structName << ");" << std::endl; + } else { + sourceFile << " d->_inputInterface.getData(&d->_data." + << m_inputStructName.structName << ");" << std::endl; + } } sourceFile << " d->_fun(&d->_data);" << std::endl; - if (!m_outputStructName_Interface.empty() && !m_outputStructName.empty()) { - sourceFile << " d->_outputInterface.setData(&d->_data." << m_outputStructName - << ");" << std::endl; + if (!m_outputStructName_Interface.empty() && !m_outputStructName.structName.empty()) { + if (m_outputStructName.isPointer) { + sourceFile << " d->_outputInterface.setData(d->_data." + << m_outputStructName.structName << ");" << std::endl; + } else { + sourceFile << " d->_outputInterface.setData(&d->_data." + << m_outputStructName.structName << ");" << std::endl; + } } // 这里只适用于本体模型 - if (!m_heartStructName_Interface.empty() && !m_heartStructName.empty() - && m_heartStructName == m_dataPackageInterfaceName) { - sourceFile << " d->_heartbeatInterface.setData(&d->_data);" << std::endl; + if (!m_heartStructName_Interface.empty() && !m_heartStructName.structName.empty()) { + if (m_heartStructName.isNumeric) { + sourceFile << " d->_heartbeatInterface.setData(&d->_data);" << std::endl; + } } sourceFile << " }" << std::endl; } @@ -390,16 +410,160 @@ bool XNModelGen::GenerateSourceFile() sourceFile << "}" << std::endl; sourceFile << std::endl; + sourceFile << "void " << m_className << "::InitializeData() {" << std::endl; + sourceFile << " T_D();" << std::endl; + if (!m_inputStructName.structName.empty()) { + if (m_inputStructName.isPointer) { + sourceFile << " d->_data." << m_inputStructName.structName << " = new " + << m_inputStructName.structType << ";" << std::endl; + } + sourceFile << " // TODO: 在这里初始化输入数据" << std::endl; + sourceFile << std::endl; + } + if (!m_outputStructName.structName.empty()) { + if (m_outputStructName.isPointer) { + sourceFile << " d->_data." << m_outputStructName.structName << " = new " + << m_outputStructName.structType << ";" << std::endl; + } + sourceFile << " // TODO: 在这里初始化输出数据" << std::endl; + sourceFile << std::endl; + } + // 心跳数据,仅适用于本体模型 + if (!m_heartStructName.structName.empty()) { + if (m_heartStructName.isNumeric) { + sourceFile << " d->_data." << m_heartStructName.structName << " = 0;" << std::endl; + } + } + sourceFile << "}" << std::endl; + sourceFile << std::endl; + 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 << " delete d->_data." << m_inputStructName.structName << ";" + << std::endl; + sourceFile << " d->_data." << m_inputStructName.structName << " = nullptr;" + << std::endl; + sourceFile << " }" << std::endl; + } + } + 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 << " delete d->_data." << m_outputStructName.structName << ";" + << std::endl; + sourceFile << " d->_data." << m_outputStructName.structName << " = nullptr;" + << std::endl; + sourceFile << " }" << std::endl; + } + } + sourceFile << "}" << std::endl; + sourceFile << std::endl; return true; } bool XNModelGen::GenerateConfigFile() { + std::string configPath = m_codePath + "/" + m_className + ".mcfg"; + std::ofstream configFile(configPath); + if (!configFile.is_open()) { + return false; + } + configFile << "" << std::endl; + configFile << "" << std::endl; + configFile << " " << m_className << "" << std::endl; + configFile << " " << m_description << "" << std::endl; + configFile << " " << m_author << "" << std::endl; + configFile << " " << m_version << "" << std::endl; + configFile << " " << m_createTime << "" << std::endl; + configFile << " " << m_changeTime << "" << std::endl; + configFile << " " << m_runNode << "" << std::endl; + configFile << " " << m_priority << "" << std::endl; + configFile << " " << m_dataPackagePath << "/" << m_dataPackageName << "" + << std::endl; + // TODO: 添加命令列表 + configFile << " " << std::endl; + configFile << "" << std::endl; + configFile.close(); return true; } bool XNModelGen::GenerateCMakeLists() { + std::string cmakeListsPath = m_codePath + "/CMakeLists.txt"; + std::ofstream cmakeListsFile(cmakeListsPath); + if (!cmakeListsFile.is_open()) { + return false; + } + cmakeListsFile << "cmake_minimum_required(VERSION 3.16)" << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile << "project(" << m_className << " LANGUAGES CXX)" << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile << "set(MODEL_VERSION \"" << m_version << "\")" << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile << "set(CMAKE_CXX_STANDARD 17)" << std::endl; + cmakeListsFile << "set(CMAKE_CXX_STANDARD_REQUIRED ON)" << std::endl; + cmakeListsFile << "set(CMAKE_POSITION_INDEPENDENT_CODE ON)" << std::endl; + cmakeListsFile << std::endl; + // 获取环境变量 + cmakeListsFile << "if(DEFINED ENV{XNCore})" << std::endl; + cmakeListsFile << " set(XNCore_PATH $ENV{XNCore})" << std::endl; + cmakeListsFile << "else()" << std::endl; + cmakeListsFile << " message(FATAL_ERROR \"Environment variable XNCore is not set.\")" + << std::endl; + cmakeListsFile << "endif()" << std::endl; + cmakeListsFile << std::endl; + // 添加 XNCore_PATH 下的 include 目录为包含目录 + cmakeListsFile << "include_directories(${XNCore_PATH}/include)" << std::endl; + cmakeListsFile << "include_directories(${XNCore_PATH}/IDL)" << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile << "add_library(" << m_className << " SHARED" << std::endl; + cmakeListsFile << " " << m_className << "_global.h" << std::endl; + cmakeListsFile << " " << m_className << ".cpp" << std::endl; + cmakeListsFile << " " << m_className << ".h" << std::endl; + cmakeListsFile << " " << m_className << "_p.h" << std::endl; + cmakeListsFile << ")" << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile << "set_target_properties(" << m_className << " PROPERTIES" << std::endl; + cmakeListsFile << " LIBRARY_OUTPUT_NAME \"lib" << m_className << ".so." << m_version << "\"" + << std::endl; + cmakeListsFile << " PREFIX \"\"" << std::endl; + cmakeListsFile << " SUFFIX \"\"" << std::endl; + cmakeListsFile << " SKIP_BUILD_RPATH TRUE" << std::endl; + cmakeListsFile << " BUILD_WITH_INSTALL_RPATH TRUE" << std::endl; + cmakeListsFile << ")" << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile << "target_link_libraries(" << m_className << " PRIVATE" << std::endl; + cmakeListsFile << " ${XNCore_PATH}/lib/libXNCore.so" << std::endl; + cmakeListsFile << " ${XNCore_PATH}/lib/lib" << m_confName << "_Interface.so" << std::endl; + cmakeListsFile << " dl" << std::endl; + cmakeListsFile << ")" << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile << "target_compile_definitions(" << m_className << " PRIVATE " + << UpperCase(m_className) << "_LIBRARY)" << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile << "if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)" << std::endl; + cmakeListsFile << " set(CMAKE_INSTALL_PREFIX \"${XNCore_PATH}/Configuration/" << m_confName + << "/Models\" CACHE PATH \"Install path prefix\" FORCE)" << std::endl; + cmakeListsFile << "endif()" << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile << "install(TARGETS " << m_className + << " BUNDLE DESTINATION . LIBRARY DESTINATION . RUNTIME DESTINATION .)" + << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile << "file(GLOB CONFIG_FILE \"*.mcfg\")" << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile << "install(FILES ${CONFIG_FILE} DESTINATION ${CMAKE_INSTALL_PREFIX} RENAME " + "\"${CMAKE_PROJECT_NAME}_V${MODEL_VERSION}.mcfg\")" + << std::endl; + cmakeListsFile << std::endl; + cmakeListsFile.close(); return true; } @@ -416,18 +580,31 @@ std::string XNModelGen::UpperCase(const std::string &str) return result; } -void XNModelGen::ParseStructName(const std::string &str, std::string &structName, - std::string &interfaceName) +bool XNModelGen::IsPointer(const std::string &str) +{ + return str.find("*") != std::string::npos; +} + +bool XNModelGen::IsNumeric(const std::string &str) +{ + return str.find("int") != std::string::npos || str.find("float") != std::string::npos + || str.find("double") != std::string::npos || str.find("long") != std::string::npos + || str.find("short") != std::string::npos || str.find("char") != std::string::npos + || str.find("bool") != std::string::npos; +} + +void XNModelGen::ParseStructName(const std::string &str, std::string &interfaceName, + std::string &structName) { try { json j = json::parse(str); - // 遍历JSON对象,第一个键作为structName,第一个值作为interfaceName + // 遍历JSON对象,第一个键作为interfaceName(数据库中的字段名),第一个值作为structName(头文件中的结构体名) if (!j.empty() && j.is_object()) { auto it = j.begin(); if (it != j.end()) { - structName = it.key(); + interfaceName = it.key(); if (it.value().is_string()) { - interfaceName = it.value(); + structName = it.value(); } } } @@ -436,4 +613,81 @@ void XNModelGen::ParseStructName(const std::string &str, std::string &structName structName = ""; interfaceName = ""; } +} + +std::vector XNModelGen::SplitString(const std::string &str) +{ + std::vector result; + std::stringstream ss(str); + std::string token; + + while (ss >> token) { + result.push_back(token); + } + + return result; +} + +void XNModelGen::ParseStructOriginalName(InterfaceStructInfo &structInfo) +{ + std::vector tokens = SplitString(structInfo.originalName); + if (tokens.empty()) { + return; + } + + // 处理 "struct" 关键字 + int startIndex = 0; + if (tokens[0] == "struct") { + startIndex = 1; + } + + if (startIndex >= tokens.size()) { + return; + } + + // 获取结构体类型名,检查是否包含指针符号 + std::string typeToken = tokens[startIndex]; + bool hasPointer = false; + std::string structType = typeToken; + + // 检查类型名本身是否包含指针符号 + if (typeToken.find("*") != std::string::npos) { + hasPointer = true; + // 移除指针符号,获取纯类型名 + structType = typeToken; + structType.erase(std::remove(structType.begin(), structType.end(), '*'), structType.end()); + } + + structInfo.structType = structType; + + // 获取结构体名称 + std::string structName; + int nameIndex = startIndex + 1; + while (nameIndex < tokens.size()) { + std::string token = tokens[nameIndex]; + + // 如果当前token是单独的指针符号 + if (token == "*") { + hasPointer = true; + nameIndex++; + continue; + } + + // 如果当前token包含指针符号 + if (token.find("*") != std::string::npos) { + hasPointer = true; + // 移除指针符号,获取结构体名称 + structName = token; + structName.erase(std::remove(structName.begin(), structName.end(), '*'), + structName.end()); + } else { + // 普通的结构体名称 + structName = token; + } + break; // 找到结构体名称后退出 + } + + structInfo.isPointer = hasPointer; + structInfo.structName = structName; + structInfo.isNumeric = IsNumeric(structInfo.structType); } \ No newline at end of file diff --git a/XNModelGenServer/XNModelGen.h b/XNModelGenServer/XNModelGen.h index 02213ae..c840bb6 100644 --- a/XNModelGenServer/XNModelGen.h +++ b/XNModelGenServer/XNModelGen.h @@ -2,6 +2,15 @@ #include #include + +struct InterfaceStructInfo { + std::string originalName; + std::string structType; + std::string structName; + bool isPointer; + bool isNumeric; +}; + class XNModelGen { public: @@ -13,11 +22,17 @@ public: int GenerateCode(std::string &errorMsg); + const std::string &GetCodePath() const; + private: std::string GetXNCorePath(); std::string UpperCase(const std::string &str); - void ParseStructName(const std::string &str, std::string &structName, - std::string &interfaceName); + std::vector SplitString(const std::string &str); + bool IsPointer(const std::string &str); + bool IsNumeric(const std::string &str); + void ParseStructName(const std::string &str, std::string &interfaceName, + std::string &structName); + void ParseStructOriginalName(InterfaceStructInfo &structInfo); bool GenerateHeaderFile(); bool GenerateSourceFile(); @@ -28,7 +43,7 @@ private: std::string m_planeName; std::string m_className; std::string m_version; - std::string m_confID; + int m_confID; std::string m_name; std::string m_author; std::string m_description; @@ -43,11 +58,11 @@ private: std::string m_dataPackageEntryPoint; std::string m_dataPackageInterfaceName; bool m_hasDataPackage; - std::string m_inputStructName; + InterfaceStructInfo m_inputStructName; std::string m_inputStructName_Interface; - std::string m_outputStructName; + InterfaceStructInfo m_outputStructName; std::string m_outputStructName_Interface; - std::string m_heartStructName; + InterfaceStructInfo m_heartStructName; std::string m_heartStructName_Interface; std::string m_confName; std::string m_workPath; diff --git a/XNModelGenServer/XNModelGenServer.cpp b/XNModelGenServer/XNModelGenServer.cpp index d3d484c..cf631bb 100644 --- a/XNModelGenServer/XNModelGenServer.cpp +++ b/XNModelGenServer/XNModelGenServer.cpp @@ -1 +1,163 @@ -#include "XNModelGenServer.h" \ No newline at end of file +#include "XNModelGenServer.h" +#include "XNModelGen.h" +#include "XNCodeZip.h" +#include "XNModelCompile.h" +#include + +int XNModelCodeGen(const char *className, int classNameLen, const char *version, int versionLen, + const char *planeName, int planeNameLen, char *errorMsg, int errorMsgLen) +{ + std::string classNameStr(className, classNameLen); + std::string versionStr(version, versionLen); + std::string planeNameStr(planeName, planeNameLen); + if (classNameStr.empty() || versionStr.empty() || planeNameStr.empty()) { + if (errorMsg != nullptr) { + strncpy(errorMsg, "className, version or planeName is empty", errorMsgLen); + } + return -1; + } + + std::string errorMsgStr; + XNModelGen modelGen; + int ret = modelGen.Initialize(classNameStr, versionStr, planeNameStr, errorMsgStr); + if (ret != 0) { + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return ret; + } + ret = modelGen.GenerateCode(errorMsgStr); + if (ret != 0) { + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return ret; + } + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return 0; +} + +int XNModelCodeZip(const char *className, int classNameLen, const char *version, int versionLen, + const char *planeName, int planeNameLen, char *dstPath, int dstPathLen, + char *errorMsg, int errorMsgLen) +{ + std::string classNameStr(className, classNameLen); + std::string versionStr(version, versionLen); + std::string planeNameStr(planeName, planeNameLen); + if (classNameStr.empty() || versionStr.empty() || planeNameStr.empty()) { + if (errorMsg != nullptr) { + strncpy(errorMsg, "className, version or planeName is empty", errorMsgLen); + } + return -1; + } + XNModelGen modelGen; + std::string errorMsgStr; + int ret = modelGen.Initialize(classNameStr, versionStr, planeNameStr, errorMsgStr); + if (ret != 0) { + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return ret; + } + std::string srcPath = modelGen.GetCodePath(); + std::string dstPathStr = srcPath; + size_t lastSlash = dstPathStr.find_last_of("/\\"); + if (lastSlash != std::string::npos) { + dstPathStr = dstPathStr.substr(0, lastSlash); + std::string dirName = srcPath.substr(lastSlash + 1); + dstPathStr += "/" + dirName + ".zip"; + } else { + dstPathStr += ".zip"; + } + ret = XNCodeZip::Zip(srcPath, dstPathStr, errorMsgStr); + if (ret != 0) { + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return ret; + } + + // 将生成的zip文件路径复制到输出参数 + if (dstPath != nullptr) { + strncpy(dstPath, dstPathStr.c_str(), dstPathLen); + } + + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return 0; +} + +int XNModelCodeUnzip(const char *className, int classNameLen, const char *version, int versionLen, + const char *planeName, int planeNameLen, const char *srcPath, int srcPathLen, + char *errorMsg, int errorMsgLen) +{ + std::string classNameStr(className, classNameLen); + std::string versionStr(version, versionLen); + std::string planeNameStr(planeName, planeNameLen); + if (classNameStr.empty() || versionStr.empty() || planeNameStr.empty()) { + if (errorMsg != nullptr) { + strncpy(errorMsg, "className, version or planeName is empty", errorMsgLen); + } + return -1; + } + XNModelGen modelGen; + std::string errorMsgStr; + int ret = modelGen.Initialize(classNameStr, versionStr, planeNameStr, errorMsgStr); + if (ret != 0) { + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return ret; + } + std::string srcPathStr(srcPath, srcPathLen); + std::string dstPathStr = modelGen.GetCodePath(); + ret = XNCodeZip::Unzip(srcPathStr, dstPathStr, errorMsgStr); + if (ret != 0) { + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return ret; + } + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return 0; +} + +int XNModelCodeCompile(const char *className, int classNameLen, const char *version, int versionLen, + const char *planeName, int planeNameLen, char *errorMsg, int errorMsgLen) +{ + std::string classNameStr(className, classNameLen); + std::string versionStr(version, versionLen); + std::string planeNameStr(planeName, planeNameLen); + if (classNameStr.empty() || versionStr.empty() || planeNameStr.empty()) { + if (errorMsg != nullptr) { + strncpy(errorMsg, "className, version or planeName is empty", errorMsgLen); + } + return -1; + } + XNModelGen modelGen; + std::string errorMsgStr; + int ret = modelGen.Initialize(classNameStr, versionStr, planeNameStr, errorMsgStr); + if (ret != 0) { + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return ret; + } + std::string srcPath = modelGen.GetCodePath(); + ret = XNModelCompile::Compile(srcPath, errorMsgStr); + if (ret != 0) { + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return ret; + } + if (errorMsg != nullptr) { + strncpy(errorMsg, errorMsgStr.c_str(), errorMsgLen); + } + return 0; +} \ No newline at end of file diff --git a/XNModelGenServer/XNModelGenServer.h b/XNModelGenServer/XNModelGenServer.h index cd17038..83f3529 100644 --- a/XNModelGenServer/XNModelGenServer.h +++ b/XNModelGenServer/XNModelGenServer.h @@ -1,3 +1,26 @@ #pragma once #include "XNModelGenServer_global.h" +#include + +extern "C" XNMODELGENSERVER_EXPORT int XNModelCodeGen(const char *className, int classNameLen, + const char *version, int versionLen, + const char *planeName, int planeNameLen, + char *errorMsg, int errorMsgLen); + +extern "C" XNMODELGENSERVER_EXPORT int XNModelCodeZip(const char *className, int classNameLen, + const char *version, int versionLen, + const char *planeName, int planeNameLen, + char *dstPath, int dstPathLen, char *errorMsg, + int errorMsgLen); + +extern "C" XNMODELGENSERVER_EXPORT int XNModelCodeUnzip(const char *className, int classNameLen, + const char *version, int versionLen, + const char *planeName, int planeNameLen, + const char *srcPath, int srcPathLen, + char *errorMsg, int errorMsgLen); + +extern "C" XNMODELGENSERVER_EXPORT int XNModelCodeCompile(const char *className, int classNameLen, + const char *version, int versionLen, + const char *planeName, int planeNameLen, + char *errorMsg, int errorMsgLen); \ No newline at end of file diff --git a/XNModelGenServer/test/CMakeLists.txt b/XNModelGenServer/test/CMakeLists.txt new file mode 100644 index 0000000..5e2dc7d --- /dev/null +++ b/XNModelGenServer/test/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.10) +project(XNModelGenTest) + +# 设置C++标准 +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 查找必要的库 +find_library(DL_LIBRARY dl) + +# 创建测试可执行文件 +add_executable(test_xnmodel test_xnmodel.cpp) + +# 链接动态库 +target_link_libraries(test_xnmodel ${DL_LIBRARY}) + +# 设置运行时库路径 +set_target_properties(test_xnmodel PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "${CMAKE_SOURCE_DIR}/.." +) \ No newline at end of file diff --git a/XNModelGenServer/test/test_xnmodel.cpp b/XNModelGenServer/test/test_xnmodel.cpp new file mode 100644 index 0000000..0d46c44 --- /dev/null +++ b/XNModelGenServer/test/test_xnmodel.cpp @@ -0,0 +1,129 @@ +#include +#include +#include + +// 函数指针类型定义 +typedef int (*XNModelCodeGenFunc)(const char *className, int classNameLen, const char *version, + int versionLen, const char *planeName, int planeNameLen, + char *errorMsg, int errorMsgLen); + +typedef int (*XNModelCodeZipFunc)(const char *className, int classNameLen, const char *version, + int versionLen, const char *planeName, int planeNameLen, + char *dstPath, int dstPathLen, char *errorMsg, int errorMsgLen); + +typedef int (*XNModelCodeUnzipFunc)(const char *className, int classNameLen, const char *version, + int versionLen, const char *planeName, int planeNameLen, + const char *srcPath, int srcPathLen, char *errorMsg, + int errorMsgLen); + +typedef int (*XNModelCodeCompileFunc)(const char *className, int classNameLen, const char *version, + int versionLen, const char *planeName, int planeNameLen, + char *errorMsg, int errorMsgLen); + +// 测试参数 +const char *className = "XNAerodynamics"; +const char *version = "2.0.3.5"; +const char *planeName = "C909"; + +void printResult(const std::string &testName, int result, const char *errorMsg) +{ + std::cout << testName << " 结果: " << result << std::endl; + if (result == 0) { + std::cout << "✓ " << testName << " 成功!" << std::endl; + } else { + std::cout << "✗ " << testName << " 失败!" << std::endl; + } + + if (strlen(errorMsg) > 0) { + std::cout << "错误信息: " << errorMsg << std::endl; + } + std::cout << std::endl; +} + +int main() +{ + // 动态加载.so库 - 修改路径指向build目录 + void *handle = dlopen("../../build/libXNModelGenServer.so", RTLD_LAZY); + if (!handle) { + std::cerr << "无法加载库文件: " << dlerror() << std::endl; + return -1; + } + + // 获取函数指针 + XNModelCodeGenFunc XNModelCodeGen = (XNModelCodeGenFunc)dlsym(handle, "XNModelCodeGen"); + XNModelCodeZipFunc XNModelCodeZip = (XNModelCodeZipFunc)dlsym(handle, "XNModelCodeZip"); + XNModelCodeUnzipFunc XNModelCodeUnzip = (XNModelCodeUnzipFunc)dlsym(handle, "XNModelCodeUnzip"); + XNModelCodeCompileFunc XNModelCodeCompile = + (XNModelCodeCompileFunc)dlsym(handle, "XNModelCodeCompile"); + + if (!XNModelCodeGen || !XNModelCodeZip || !XNModelCodeUnzip || !XNModelCodeCompile) { + std::cerr << "无法找到函数: " << dlerror() << std::endl; + dlclose(handle); + return -1; + } + + int classNameLen = strlen(className); + int versionLen = strlen(version); + int planeNameLen = strlen(planeName); + + // 错误消息缓冲区 + char errorMsg[1024]; + int errorMsgLen = sizeof(errorMsg); + + std::cout << "开始测试XNModelGen函数..." << std::endl; + std::cout << "参数:" << std::endl; + std::cout << " className: " << className << " (长度: " << classNameLen << ")" << std::endl; + std::cout << " version: " << version << " (长度: " << versionLen << ")" << std::endl; + std::cout << " planeName: " << planeName << " (长度: " << planeNameLen << ")" << std::endl; + std::cout << std::endl; + + // 测试1: XNModelCodeGen + std::cout << "=== 测试1: XNModelCodeGen ===" << std::endl; + memset(errorMsg, 0, sizeof(errorMsg)); + int result1 = XNModelCodeGen(className, classNameLen, version, versionLen, planeName, + planeNameLen, errorMsg, errorMsgLen); + printResult("XNModelCodeGen", result1, errorMsg); + + // 测试2: XNModelCodeZip + std::cout << "=== 测试2: XNModelCodeZip ===" << std::endl; + char dstPath[1024]; + int dstPathLen = sizeof(dstPath); + memset(errorMsg, 0, sizeof(errorMsg)); + memset(dstPath, 0, sizeof(dstPath)); + + int result2 = XNModelCodeZip(className, classNameLen, version, versionLen, planeName, + planeNameLen, dstPath, dstPathLen, errorMsg, errorMsgLen); + printResult("XNModelCodeZip", result2, errorMsg); + + if (result2 == 0 && strlen(dstPath) > 0) { + std::cout << "生成的zip文件路径: " << dstPath << std::endl; + } + + // 测试3: XNModelCodeUnzip + std::cout << "=== 测试3: XNModelCodeUnzip ===" << std::endl; + const char *srcZipPath = dstPath; // 使用上面生成的zip文件路径 + int srcZipPathLen = strlen(srcZipPath); + memset(errorMsg, 0, sizeof(errorMsg)); + + int result3 = XNModelCodeUnzip(className, classNameLen, version, versionLen, planeName, + planeNameLen, srcZipPath, srcZipPathLen, errorMsg, errorMsgLen); + printResult("XNModelCodeUnzip", result3, errorMsg); + + // 测试4: XNModelCodeCompile + std::cout << "=== 测试4: XNModelCodeCompile ===" << std::endl; + memset(errorMsg, 0, sizeof(errorMsg)); + int result4 = XNModelCodeCompile(className, classNameLen, version, versionLen, planeName, + planeNameLen, errorMsg, errorMsgLen); + printResult("XNModelCodeCompile", result4, errorMsg); + + // 清理资源 + dlclose(handle); + + std::cout << "=== 测试总结 ===" << std::endl; + std::cout << "XNModelCodeGen: " << (result1 == 0 ? "成功" : "失败") << std::endl; + std::cout << "XNModelCodeZip: " << (result2 == 0 ? "成功" : "失败") << std::endl; + std::cout << "XNModelCodeUnzip: " << (result3 == 0 ? "成功" : "失败") << std::endl; + std::cout << "XNModelCodeCompile: " << (result4 == 0 ? "成功" : "失败") << std::endl; + + return (result1 == 0 && result2 == 0 && result3 == 0 && result4 == 0) ? 0 : -1; +} \ No newline at end of file diff --git a/XNMonitorServer/CMakeLists.txt b/XNMonitorServer/CMakeLists.txt index 2e8b4e3..00194e4 100644 --- a/XNMonitorServer/CMakeLists.txt +++ b/XNMonitorServer/CMakeLists.txt @@ -22,7 +22,7 @@ endif() # file(GLOB DDS_XNIDL_SOURCES_CXX "../XNCore/XNIDL/*.cxx") include_directories(${XNCore_PATH}/include) -include_directories(${XNCore_PATH}/IDL) +include_directories(${XNCore_PATH}/Configuration/C909_V1) find_package(OpenSSL REQUIRED) find_package(nlohmann_json 3.9.1 REQUIRED) diff --git a/XNMonitorServer/DataMonitorFactory.h b/XNMonitorServer/DataMonitorFactory.h index 13dbc7f..b412961 100644 --- a/XNMonitorServer/DataMonitorFactory.h +++ b/XNMonitorServer/DataMonitorFactory.h @@ -4,7 +4,7 @@ #include "DataMonitor.h" //接口头文件 -#include +#include /** * @brief DataMonitor工厂类,用于创建不同类型的DataMonitor实例 diff --git a/XNSimPortal/components/model-development.js b/XNSimPortal/components/model-development.js index c131e72..91721e0 100644 --- a/XNSimPortal/components/model-development.js +++ b/XNSimPortal/components/model-development.js @@ -787,7 +787,26 @@ class ModelDevelopment extends HTMLElement { color: '#dd6b20', action: () => this.uploadDataPackage() }, - { text: '自动封装', color: '#e53e3e', action: () => alert('自动封装功能即将上线') } + { + text: '模板代码生成', + color: '#805ad5', + action: () => this.generateTemplateCode() + }, + { + text: '模板代码下载', + color: '#3182ce', + action: () => this.downloadTemplateCode() + }, + { + text: '封装代码上传', + color: '#38a169', + action: () => this.uploadWrapperCode() + }, + { + text: '模型编译发布', + color: '#e53e3e', + action: () => this.compileAndPublishModel() + } ]; buttonConfigs.forEach(config => { @@ -2636,6 +2655,361 @@ class ModelDevelopment extends HTMLElement { alert(`数据包上传失败: ${error.message}`); } } + + /** + * 生成模板代码 + */ + async generateTemplateCode() { + try { + // 获取当前选择的构型信息 + const savedSelection = localStorage.getItem('xnsim-selection'); + const selection = savedSelection ? JSON.parse(savedSelection) : {}; + + if (!selection.configurationName) { + alert('请先选择构型!'); + return; + } + + // 获取表单数据 + const className = this.currentVersion.ClassName; + const version = this.currentVersion.Version; + const planeName = this.currentVersion.PlaneName; + + if (!className || !version || !planeName) { + alert('缺少必要信息:类名、版本号或飞机名称!'); + return; + } + + // 显示生成进度 + const generateButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(4)'); + if (generateButton) { + const originalText = generateButton.textContent; + generateButton.textContent = '生成中...'; + generateButton.disabled = true; + } + + // 调用后端API生成模板代码 + const response = await fetch('/api/model-code-gen', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + className: className, + version: version, + planeName: planeName + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || `生成失败: ${response.status}`); + } + + const result = await response.json(); + + if (result.result && result.result.includes('成功')) { + alert('模板代码生成成功!'); + } else { + throw new Error(result.result || '生成失败'); + } + + } catch (error) { + console.error('生成模板代码失败:', error); + alert(`生成模板代码失败: ${error.message}`); + } finally { + // 恢复按钮状态 + const generateButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(4)'); + if (generateButton) { + generateButton.textContent = '模板代码生成'; + generateButton.disabled = false; + } + } + } + + /** + * 下载模板代码 + */ + async downloadTemplateCode() { + try { + // 获取当前选择的构型信息 + const savedSelection = localStorage.getItem('xnsim-selection'); + const selection = savedSelection ? JSON.parse(savedSelection) : {}; + + if (!selection.configurationName) { + alert('请先选择构型!'); + return; + } + + // 获取表单数据 + const className = this.currentVersion.ClassName; + const version = this.currentVersion.Version; + const planeName = this.currentVersion.PlaneName; + + if (!className || !version || !planeName) { + alert('缺少必要信息:类名、版本号或飞机名称!'); + return; + } + + // 显示下载进度 + const downloadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(5)'); + if (downloadButton) { + const originalText = downloadButton.textContent; + downloadButton.textContent = '生成中...'; + downloadButton.disabled = true; + } + + // 第一步:调用后端API生成压缩文件 + const zipResponse = await fetch('/api/model-code-zip', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + className: className, + version: version, + planeName: planeName + }) + }); + + if (!zipResponse.ok) { + const errorData = await zipResponse.json(); + throw new Error(errorData.error || `生成压缩文件失败: ${zipResponse.status}`); + } + + const zipResult = await zipResponse.json(); + + if (!zipResult.success || !zipResult.dstPath) { + throw new Error(zipResult.message || '生成压缩文件失败'); + } + + // 第二步:触发文件下载,直接使用绝对路径 + const downloadUrl = `/api/filesystem/download-zip?filePath=${encodeURIComponent(zipResult.dstPath)}`; + window.open(downloadUrl); + + alert('模板代码下载成功!'); + + } catch (error) { + console.error('下载模板代码失败:', error); + alert(`下载模板代码失败: ${error.message}`); + } finally { + // 恢复按钮状态 + const downloadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(5)'); + if (downloadButton) { + downloadButton.textContent = '模板代码下载'; + downloadButton.disabled = false; + } + } + } + + /** + * 上传封装代码 + */ + async uploadWrapperCode() { + try { + // 获取当前选择的构型信息 + const savedSelection = localStorage.getItem('xnsim-selection'); + const selection = savedSelection ? JSON.parse(savedSelection) : {}; + + if (!selection.configurationName) { + alert('请先选择构型!'); + return; + } + + // 创建文件输入元素 + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = '.zip'; + fileInput.style.display = 'none'; + + // 添加到DOM + document.body.appendChild(fileInput); + + // 监听文件选择 + fileInput.addEventListener('change', async (event) => { + try { + const files = Array.from(event.target.files); + + if (files.length === 0) { + alert('请选择ZIP文件!'); + return; + } + + const file = files[0]; + + if (!file.name.toLowerCase().endsWith('.zip')) { + alert('请选择ZIP格式的文件!'); + return; + } + + // 创建FormData + const formData = new FormData(); + formData.append('confName', selection.configurationName); + formData.append('file', file); + + // 显示上传进度 + const uploadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(6)'); + if (uploadButton) { + const originalText = uploadButton.textContent; + uploadButton.textContent = '上传中...'; + uploadButton.disabled = true; + } + + // 发送上传请求 + const response = await fetch('/api/filesystem/upload-zip', { + method: 'POST', + body: formData + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || `上传失败: ${response.status}`); + } + + const result = await response.json(); + + if (result.success) { + // 检查文件名是否符合 className_version.zip 格式 + const fileName = result.file.name; + const expectedFileName = `${this.currentVersion.ClassName}_${this.currentVersion.Version}.zip`; + + if (fileName === expectedFileName) { + // 文件名符合格式,自动调用解压 + uploadButton.textContent = '解压中...'; + + try { + const unzipResponse = await fetch('/api/model-code-unzip', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + className: this.currentVersion.ClassName, + version: this.currentVersion.Version, + planeName: this.currentVersion.PlaneName, + srcPath: result.file.path + }) + }); + + if (!unzipResponse.ok) { + const unzipErrorData = await unzipResponse.json(); + throw new Error(unzipErrorData.error || `解压失败: ${unzipResponse.status}`); + } + + const unzipResult = await unzipResponse.json(); + + if (unzipResult.result && unzipResult.result.includes('成功')) { + alert(`封装代码上传并解压成功!\n文件名: ${result.file.name}\n大小: ${(result.file.size / 1024 / 1024).toFixed(2)} MB\n解压状态: 成功`); + } else { + throw new Error(unzipResult.result || '解压失败'); + } + } catch (unzipError) { + console.error('自动解压失败:', unzipError); + alert(`封装代码上传成功,但自动解压失败: ${unzipError.message}\n文件名: ${result.file.name}\n大小: ${(result.file.size / 1024 / 1024).toFixed(2)} MB`); + } + } else { + // 文件名不符合格式,只显示上传成功 + alert(`封装代码上传成功!\n文件名: ${result.file.name}\n大小: ${(result.file.size / 1024 / 1024).toFixed(2)} MB\n注意: 文件名不符合 ${expectedFileName} 格式,未自动解压`); + } + } else { + throw new Error(result.message || '上传失败'); + } + + } catch (error) { + console.error('封装代码上传失败:', error); + alert(`封装代码上传失败: ${error.message}`); + } finally { + // 恢复按钮状态 + const uploadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(6)'); + if (uploadButton) { + uploadButton.textContent = '封装代码上传'; + uploadButton.disabled = false; + } + + // 清理文件输入元素 + document.body.removeChild(fileInput); + } + }); + + // 触发文件选择对话框 + fileInput.click(); + + } catch (error) { + console.error('封装代码上传初始化失败:', error); + alert(`封装代码上传失败: ${error.message}`); + } + } + + /** + * 模型编译发布 + */ + async compileAndPublishModel() { + try { + // 获取当前选择的构型信息 + const savedSelection = localStorage.getItem('xnsim-selection'); + const selection = savedSelection ? JSON.parse(savedSelection) : {}; + + if (!selection.configurationName) { + alert('请先选择构型!'); + return; + } + + // 获取表单数据 + const className = this.currentVersion.ClassName; + const version = this.currentVersion.Version; + const planeName = this.currentVersion.PlaneName; + + if (!className || !version || !planeName) { + alert('缺少必要信息:类名、版本号或飞机名称!'); + return; + } + + // 显示编译进度 + const compileButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(7)'); + if (compileButton) { + const originalText = compileButton.textContent; + compileButton.textContent = '编译中...'; + compileButton.disabled = true; + } + + // 调用后端API编译模型 + const response = await fetch('/api/model-code-compile', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + className: className, + version: version, + planeName: planeName + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || `编译失败: ${response.status}`); + } + + const result = await response.json(); + + if (result.result && result.result.includes('成功')) { + alert('模型编译发布成功!'); + } else { + throw new Error(result.result || '编译失败'); + } + + } catch (error) { + console.error('模型编译发布失败:', error); + alert(`模型编译发布失败: ${error.message}`); + } finally { + // 恢复按钮状态 + const compileButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(7)'); + if (compileButton) { + compileButton.textContent = '模型编译发布'; + compileButton.disabled = false; + } + } + } } customElements.define('model-development', ModelDevelopment); \ No newline at end of file diff --git a/XNSimPortal/routes/filesystem.js b/XNSimPortal/routes/filesystem.js index 5c65b8b..02bdec7 100644 --- a/XNSimPortal/routes/filesystem.js +++ b/XNSimPortal/routes/filesystem.js @@ -50,6 +50,17 @@ const packageFileFilter = (req, file, cb) => { } }; +// ZIP文件上传过滤器 +const zipFileFilter = (req, file, cb) => { + // 只允许.zip文件 + const fileName = file.originalname.toLowerCase(); + if (fileName.endsWith('.zip')) { + cb(null, true); + } else { + cb(new Error('只能上传.zip文件')); + } +}; + const upload = multer({ storage: storage, fileFilter: fileFilter, @@ -67,6 +78,15 @@ const packageUpload = multer({ } }); +// ZIP文件上传专用multer配置 +const zipUpload = multer({ + storage: storage, + fileFilter: zipFileFilter, + limits: { + fileSize: 100 * 1024 * 1024 // 限制文件大小为100MB + } +}); + // 读取目录内容 router.get('/readdir', async (req, res) => { try { @@ -940,4 +960,154 @@ router.post('/get-struct-members', async (req, res) => { } }); +// 上传ZIP文件 +router.post('/upload-zip', zipUpload.single('file'), async (req, res) => { + try { + const { confName } = req.body; + + if (!confName) { + return res.status(400).json({ + success: false, + message: '未提供构型名称' + }); + } + + if (!req.file) { + return res.status(400).json({ + success: false, + message: '未提供ZIP文件' + }); + } + + // 获取XNCore环境变量 + const xnCorePath = process.env.XNCore || ''; + if (!xnCorePath) { + return res.status(500).json({ + success: false, + message: 'XNCore环境变量未设置' + }); + } + + // 构建ModelProjects目录路径 + const modelProjectsPath = path.join(xnCorePath, 'Configuration', confName, 'ModelProjects'); + + // 确保ModelProjects目录存在 + try { + await fsPromises.mkdir(modelProjectsPath, { recursive: true }); + } catch (error) { + if (error.code !== 'EEXIST') { + console.error('创建ModelProjects目录失败:', error); + return res.status(500).json({ + success: false, + message: '创建ModelProjects目录失败: ' + error.message + }); + } + } + + // 移动文件到ModelProjects目录 + const finalPath = path.join(modelProjectsPath, req.file.originalname); + + try { + await fsPromises.copyFile(req.file.path, finalPath); + + // 删除临时文件 + try { + await fsPromises.unlink(req.file.path); + } catch (unlinkError) { + console.warn('删除临时文件失败:', unlinkError); + } + } catch (copyError) { + console.error('移动ZIP文件失败:', copyError); + return res.status(500).json({ + success: false, + message: '移动ZIP文件失败: ' + copyError.message + }); + } + + // 获取文件状态 + const stats = await fsPromises.stat(finalPath); + + res.json({ + success: true, + message: 'ZIP文件上传成功', + confName: confName, + file: { + name: req.file.originalname, + size: stats.size, + path: finalPath, + created: stats.birthtime, + modified: stats.mtime + } + }); + } catch (error) { + console.error('ZIP文件上传失败:', error); + res.status(500).json({ + success: false, + message: 'ZIP文件上传失败: ' + error.message + }); + } +}); + +// 下载ZIP文件 +router.get('/download-zip', async (req, res) => { + try { + const { filePath } = req.query; + + if (!filePath) { + return res.status(400).json({ + success: false, + message: '未提供文件路径' + }); + } + + // 验证文件扩展名 + if (!filePath.toLowerCase().endsWith('.zip')) { + return res.status(400).json({ + success: false, + message: '只能下载ZIP文件' + }); + } + + // 检查文件是否存在 + try { + await fsPromises.access(filePath); + } catch (error) { + return res.status(404).json({ + success: false, + message: 'ZIP文件不存在' + }); + } + + // 获取文件名 + const filename = path.basename(filePath); + + // 设置响应头 + res.setHeader('Content-Disposition', `attachment; filename=${encodeURIComponent(filename)}`); + res.setHeader('Content-Type', 'application/zip'); + + // 创建文件流并发送 + const fileStream = fs.createReadStream(filePath); + fileStream.pipe(res); + + // 处理流错误 + fileStream.on('error', (error) => { + console.error('ZIP文件下载失败:', error); + if (!res.headersSent) { + res.status(500).json({ + success: false, + message: 'ZIP文件下载失败: ' + error.message + }); + } + }); + } catch (error) { + console.error('ZIP文件下载失败:', error); + if (!res.headersSent) { + res.status(500).json({ + success: false, + message: 'ZIP文件下载失败: ' + error.message + }); + } + } +}); + module.exports = router; diff --git a/XNSimPortal/routes/model-dev.js b/XNSimPortal/routes/model-dev.js index e71d6da..60b7c7b 100644 --- a/XNSimPortal/routes/model-dev.js +++ b/XNSimPortal/routes/model-dev.js @@ -6,6 +6,12 @@ const { getModelVersionsByClassName, saveModelVersion } = require('../utils/model-utils'); +const { + modelCodeGen, + modelCodeZip, + modelCodeUnzip, + modelCodeCompile +} = require('../utils/xnCoreService'); // 获取所有ATA章节 router.get('/ata-chapters', (req, res) => { @@ -94,4 +100,99 @@ router.post('/model-versions', (req, res) => { } }); +// 生成模型代码 +router.post('/model-code-gen', (req, res) => { + try { + const { className, version, planeName } = req.body; + + if (!className) { + return res.status(400).json({ error: '模型类名不能为空' }); + } + if (!version) { + return res.status(400).json({ error: '版本号不能为空' }); + } + if (!planeName) { + return res.status(400).json({ error: '飞机名称不能为空' }); + } + + const result = modelCodeGen(className, version, planeName); + res.json({ result }); + } catch (error) { + console.error(`生成模型代码失败: ${error.message}`); + res.status(500).json({ error: '生成模型代码失败', details: error.message }); + } +}); + +// 压缩模型代码 +router.post('/model-code-zip', (req, res) => { + try { + const { className, version, planeName } = req.body; + + if (!className) { + return res.status(400).json({ error: '模型类名不能为空' }); + } + if (!version) { + return res.status(400).json({ error: '版本号不能为空' }); + } + if (!planeName) { + return res.status(400).json({ error: '飞机名称不能为空' }); + } + + const result = modelCodeZip(className, version, planeName); + res.json(result); + } catch (error) { + console.error(`压缩模型代码失败: ${error.message}`); + res.status(500).json({ error: '压缩模型代码失败', details: error.message }); + } +}); + +// 解压模型代码 +router.post('/model-code-unzip', (req, res) => { + try { + const { className, version, planeName, srcPath } = req.body; + + if (!className) { + return res.status(400).json({ error: '模型类名不能为空' }); + } + if (!version) { + return res.status(400).json({ error: '版本号不能为空' }); + } + if (!planeName) { + return res.status(400).json({ error: '飞机名称不能为空' }); + } + if (!srcPath) { + return res.status(400).json({ error: '源文件路径不能为空' }); + } + + const result = modelCodeUnzip(className, version, planeName, srcPath); + res.json({ result }); + } catch (error) { + console.error(`解压模型代码失败: ${error.message}`); + res.status(500).json({ error: '解压模型代码失败', details: error.message }); + } +}); + +// 编译模型代码 +router.post('/model-code-compile', (req, res) => { + try { + const { className, version, planeName } = req.body; + + if (!className) { + return res.status(400).json({ error: '模型类名不能为空' }); + } + if (!version) { + return res.status(400).json({ error: '版本号不能为空' }); + } + if (!planeName) { + return res.status(400).json({ error: '飞机名称不能为空' }); + } + + const result = modelCodeCompile(className, version, planeName); + res.json({ result }); + } catch (error) { + console.error(`编译模型代码失败: ${error.message}`); + res.status(500).json({ error: '编译模型代码失败', details: error.message }); + } +}); + module.exports = router; \ No newline at end of file diff --git a/XNSimPortal/utils/xnCoreService.js b/XNSimPortal/utils/xnCoreService.js index 61080d8..48221ac 100644 --- a/XNSimPortal/utils/xnCoreService.js +++ b/XNSimPortal/utils/xnCoreService.js @@ -23,6 +23,10 @@ const monitorLibPath = path.join(xnCorePath, 'lib', monitorLibName); const interfaceGenLibName = `${libPrefix}XNInterfaceGenServer${libExtension}`; const interfaceGenLibPath = path.join(xnCorePath, 'lib', interfaceGenLibName); +// ModelGenServer库配置 +const modelGenLibName = `${libPrefix}XNModelGenServer${libExtension}`; +const modelGenLibPath = path.join(xnCorePath, 'lib', modelGenLibName); + // 定义Buffer类型 const BufferType = ref.refType(ref.types.void); const StringType = ref.types.CString; @@ -32,6 +36,7 @@ const IntType = ref.types.int; let loginLib; let monitorLib; let interfaceGenLib; +let modelGenLib; try { loginLib = ffi.Library(loginLibPath, { @@ -99,6 +104,17 @@ try { console.error(`加载 ${interfaceGenLibName} 失败:`, error); } +try { + modelGenLib = ffi.Library(modelGenLibPath, { + 'XNModelCodeGen': ['int', [StringType, 'int', StringType, 'int', StringType, 'int', StringType, 'int']], + 'XNModelCodeZip': ['int', [StringType, 'int', StringType, 'int', StringType, 'int', StringType, 'int', StringType, 'int']], + 'XNModelCodeUnzip': ['int', [StringType, 'int', StringType, 'int', StringType, 'int', StringType, 'int', StringType, 'int']], + 'XNModelCodeCompile': ['int', [StringType, 'int', StringType, 'int', StringType, 'int', StringType, 'int']] + }); +} catch (error) { + console.error(`加载 ${modelGenLibName} 失败:`, error); +} + // 注册进程退出时的清理函数 function performCleanup() { console.log('正在执行清理操作...'); @@ -791,10 +807,111 @@ function interfaceGenStep9SudoLdconfig() { } } +// ========== XNModelGenServer 封装函数 ========== + +// 生成模型代码 +function modelCodeGen(className, version, planeName) { + if (!modelGenLib) { + return '模型生成库未加载'; + } + try { + const errorMsg = Buffer.alloc(1024); + const result = modelGenLib.XNModelCodeGen( + className, className.length, + version, version.length, + planeName, planeName.length, + errorMsg, errorMsg.length + ); + if (result !== 0) { + return `生成模型代码失败: ${errorMsg.toString('utf8').replace(/\0/g, '')}`; + } + return '生成模型代码成功'; + } catch (error) { + return `生成模型代码失败: ${error.message}`; + } +} + +// 压缩模型代码 +function modelCodeZip(className, version, planeName) { + if (!modelGenLib) { + return '模型生成库未加载'; + } + try { + const dstPath = Buffer.alloc(1024); + const errorMsg = Buffer.alloc(1024); + const result = modelGenLib.XNModelCodeZip( + className, className.length, + version, version.length, + planeName, planeName.length, + dstPath, dstPath.length, + errorMsg, errorMsg.length + ); + if (result !== 0) { + return `压缩模型代码失败: ${errorMsg.toString('utf8').replace(/\0/g, '')}`; + } + return { + success: true, + dstPath: dstPath.toString('utf8').replace(/\0/g, ''), + message: '压缩模型代码成功' + }; + } catch (error) { + return { + success: false, + message: `压缩模型代码失败: ${error.message}` + }; + } +} + +// 解压模型代码 +function modelCodeUnzip(className, version, planeName, srcPath) { + if (!modelGenLib) { + return '模型生成库未加载'; + } + try { + const errorMsg = Buffer.alloc(1024); + const result = modelGenLib.XNModelCodeUnzip( + className, className.length, + version, version.length, + planeName, planeName.length, + srcPath, srcPath.length, + errorMsg, errorMsg.length + ); + if (result !== 0) { + return `解压模型代码失败: ${errorMsg.toString('utf8').replace(/\0/g, '')}`; + } + return '解压模型代码成功'; + } catch (error) { + return `解压模型代码失败: ${error.message}`; + } +} + +// 编译模型代码 +function modelCodeCompile(className, version, planeName) { + if (!modelGenLib) { + return '模型生成库未加载'; + } + try { + const errorMsg = Buffer.alloc(1024); + const result = modelGenLib.XNModelCodeCompile( + className, className.length, + version, version.length, + planeName, planeName.length, + errorMsg, errorMsg.length + ); + if (result !== 0) { + return `编译模型代码失败: ${errorMsg.toString('utf8').replace(/\0/g, '')}`; + } + return '编译模型代码成功'; + } catch (error) { + return `编译模型代码失败: ${error.message}`; + } +} + module.exports = { loginLib, monitorLib, interfaceGenLib, + modelGenLib, performCleanup, stringToBuffer, initializeMonitor, @@ -833,5 +950,9 @@ module.exports = { interfaceGenStep6GenerateDDSInterface, interfaceGenStep7GenerateCMakeLists, interfaceGenStep8BuildAndInstall, - interfaceGenStep9SudoLdconfig + interfaceGenStep9SudoLdconfig, + modelCodeGen, + modelCodeZip, + modelCodeUnzip, + modelCodeCompile }; \ No newline at end of file