V0.33.0.250623_alpha:新增模型开发页面代码生成功能

This commit is contained in:
jinchao 2025-06-23 16:12:40 +08:00
parent ad5354f5d5
commit 884b0a52ac
37 changed files with 2050 additions and 89 deletions

View File

@ -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)

View File

@ -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()

View File

@ -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")

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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/>

View File

@ -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

View File

@ -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;
};

View File

@ -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.

1
Release/lib/libsqlite3.so.0 Symbolic link
View File

@ -0,0 +1 @@
libsqlite3.so.0.8.6

Binary file not shown.

1
Release/lib/libzip.so.4 Symbolic link
View File

@ -0,0 +1 @@
libzip.so.4.0

BIN
Release/lib/libzip.so.4.0 Normal file

Binary file not shown.

View File

@ -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;

View File

@ -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();

View File

@ -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"
}
}

View File

@ -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)

View 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 &currentPath, 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;
}

View 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);
};

View 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;
}

View 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);
};

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View 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}/.."
)

View 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;
}

View File

@ -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)

View File

@ -4,7 +4,7 @@
#include "DataMonitor.h"
//接口头文件
#include <C909_V1/C909_V1_Interface.h>
#include <IDL/C909_V1_Interface.h>
/**
* @brief DataMonitor工厂类DataMonitor实例

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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
};