V0.33.0.250623_alpha:新增模型开发页面代码生成功能
This commit is contained in:
parent
ad5354f5d5
commit
884b0a52ac
@ -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)
|
||||
|
@ -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()
|
||||
|
Binary file not shown.
@ -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")
|
||||
|
@ -0,0 +1,118 @@
|
||||
#include "XNAerodynamics.h"
|
||||
#include "XNAerodynamics_p.h"
|
||||
#include <XNCore/XNModelManager.h>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "XNAerodynamics_global.h"
|
||||
#include <XNCore/XNModelObject.h>
|
||||
|
||||
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)
|
||||
|
@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Model>
|
||||
<Name>XNAerodynamics</Name>
|
||||
<Description>ATA04气动模型</Description>
|
||||
<Description>ATA04Aerodynamics</Description>
|
||||
<Author>Jin</Author>
|
||||
<Version>1.0.0</Version>
|
||||
<CreateTime>2025-02-04 10:00:00</CreateTime>
|
||||
<ChangeTime>2025-02-04 10:00:00</ChangeTime>
|
||||
<Node>0-0</Node>
|
||||
<Version>2.0.3.5</Version>
|
||||
<CreateTime>2025-04-27 13:58:13</CreateTime>
|
||||
<ChangeTime>2025-04-27 13:58:13</ChangeTime>
|
||||
<Node>0</Node>
|
||||
<Priority>99</Priority>
|
||||
<MathLib>ATA04_SACSCAerodynamics_2.0.3.5H_20241106/libSACSCAerodynamics.so</MathLib>
|
||||
<CommandList/>
|
@ -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
|
||||
|
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <XNCore/XNModelObject_p.h>
|
||||
#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;
|
||||
};
|
||||
|
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Model>
|
||||
<Name>XNAerodynamics</Name>
|
||||
<Description>ATA04Aerodynamics</Description>
|
||||
<Author>Jin</Author>
|
||||
<Version>2.0.3.5</Version>
|
||||
<CreateTime>2025-04-27 13:58:13</CreateTime>
|
||||
<ChangeTime>2025-04-27 13:58:13</ChangeTime>
|
||||
<Node>0</Node>
|
||||
<Priority>99</Priority>
|
||||
<MathLib>ATA04_SACSCAerodynamics_2.0.3.5H_20241106/libSACSCAerodynamics.so</MathLib>
|
||||
<CommandList/>
|
||||
</Model>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
1
Release/lib/libsqlite3.so.0
Symbolic link
1
Release/lib/libsqlite3.so.0
Symbolic link
@ -0,0 +1 @@
|
||||
libsqlite3.so.0.8.6
|
BIN
Release/lib/libsqlite3.so.0.8.6
Normal file
BIN
Release/lib/libsqlite3.so.0.8.6
Normal file
Binary file not shown.
1
Release/lib/libzip.so.4
Symbolic link
1
Release/lib/libzip.so.4
Symbolic link
@ -0,0 +1 @@
|
||||
libzip.so.4.0
|
BIN
Release/lib/libzip.so.4.0
Normal file
BIN
Release/lib/libzip.so.4.0
Normal file
Binary file not shown.
@ -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;
|
||||
|
@ -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();
|
||||
|
13
XNModelGenServer/.vscode/settings.json
vendored
13
XNModelGenServer/.vscode/settings.json
vendored
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
216
XNModelGenServer/XNCodeZip.cpp
Normal file
216
XNModelGenServer/XNCodeZip.cpp
Normal file
@ -0,0 +1,216 @@
|
||||
#include "XNCodeZip.h"
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <zip.h>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
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<void(const fs::path &, const fs::path &)> 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;
|
||||
}
|
13
XNModelGenServer/XNCodeZip.h
Normal file
13
XNModelGenServer/XNCodeZip.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
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);
|
||||
};
|
64
XNModelGenServer/XNModelCompile.cpp
Normal file
64
XNModelGenServer/XNModelCompile.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "XNModelCompile.h"
|
||||
#include <filesystem>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
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<std::uintmax_t>(-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;
|
||||
}
|
12
XNModelGenServer/XNModelCompile.h
Normal file
12
XNModelGenServer/XNModelCompile.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class XNModelCompile
|
||||
{
|
||||
public:
|
||||
XNModelCompile() = delete;
|
||||
~XNModelCompile() = delete;
|
||||
|
||||
static int Compile(const std::string &srcPath, std::string &errorMsg);
|
||||
};
|
@ -6,6 +6,7 @@
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <sstream>
|
||||
|
||||
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 <XNCore/XNModelObject_p.h>";
|
||||
pHeaderFile << "#include <XNCore/XNModelObject_p.h>" << 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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
|
||||
configFile << "<Model>" << std::endl;
|
||||
configFile << " <Name>" << m_className << "</Name>" << std::endl;
|
||||
configFile << " <Description>" << m_description << "</Description>" << std::endl;
|
||||
configFile << " <Author>" << m_author << "</Author>" << std::endl;
|
||||
configFile << " <Version>" << m_version << "</Version>" << std::endl;
|
||||
configFile << " <CreateTime>" << m_createTime << "</CreateTime>" << std::endl;
|
||||
configFile << " <ChangeTime>" << m_changeTime << "</ChangeTime>" << std::endl;
|
||||
configFile << " <Node>" << m_runNode << "</Node>" << std::endl;
|
||||
configFile << " <Priority>" << m_priority << "</Priority>" << std::endl;
|
||||
configFile << " <MathLib>" << m_dataPackagePath << "/" << m_dataPackageName << "</MathLib>"
|
||||
<< std::endl;
|
||||
// TODO: 添加命令列表
|
||||
configFile << " <CommandList/>" << std::endl;
|
||||
configFile << "</Model>" << 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<std::string> XNModelGen::SplitString(const std::string &str)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
std::stringstream ss(str);
|
||||
std::string token;
|
||||
|
||||
while (ss >> token) {
|
||||
result.push_back(token);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void XNModelGen::ParseStructOriginalName(InterfaceStructInfo &structInfo)
|
||||
{
|
||||
std::vector<std::string> 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);
|
||||
}
|
@ -2,6 +2,15 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<std::string> 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;
|
||||
|
@ -1 +1,163 @@
|
||||
#include "XNModelGenServer.h"
|
||||
#include "XNModelGenServer.h"
|
||||
#include "XNModelGen.h"
|
||||
#include "XNCodeZip.h"
|
||||
#include "XNModelCompile.h"
|
||||
#include <cstring>
|
||||
|
||||
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;
|
||||
}
|
@ -1,3 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "XNModelGenServer_global.h"
|
||||
#include <string>
|
||||
|
||||
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);
|
21
XNModelGenServer/test/CMakeLists.txt
Normal file
21
XNModelGenServer/test/CMakeLists.txt
Normal file
@ -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}/.."
|
||||
)
|
129
XNModelGenServer/test/test_xnmodel.cpp
Normal file
129
XNModelGenServer/test/test_xnmodel.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <dlfcn.h>
|
||||
|
||||
// 函数指针类型定义
|
||||
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;
|
||||
}
|
@ -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)
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "DataMonitor.h"
|
||||
|
||||
//接口头文件
|
||||
#include <C909_V1/C909_V1_Interface.h>
|
||||
#include <IDL/C909_V1_Interface.h>
|
||||
|
||||
/**
|
||||
* @brief DataMonitor工厂类,用于创建不同类型的DataMonitor实例
|
||||
|
@ -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);
|
@ -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;
|
||||
|
@ -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;
|
@ -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
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user