387 lines
11 KiB
C++
387 lines
11 KiB
C++
/**
|
||
* @file PluginGenerator.cpp
|
||
* @brief 简化的插件生成器实现
|
||
*/
|
||
#include "PluginGenerator.h"
|
||
|
||
std::string GetXNCorePath()
|
||
{
|
||
const char *xnCorePath = std::getenv("XNCore");
|
||
if (xnCorePath != nullptr) {
|
||
return std::string(xnCorePath);
|
||
}
|
||
return "";
|
||
}
|
||
|
||
PluginGenerator::PluginGenerator()
|
||
{
|
||
}
|
||
|
||
PluginGenerator::~PluginGenerator()
|
||
{
|
||
}
|
||
|
||
bool PluginGenerator::loadPluginFromDatabase(const int confID)
|
||
{
|
||
std::string xncorePath = GetXNCorePath();
|
||
if (xncorePath.empty()) {
|
||
m_lastError = "无法获取XNCore环境变量的值!";
|
||
return false;
|
||
}
|
||
|
||
std::string dbPath = xncorePath + "/database/XNSim.db";
|
||
if (!std::filesystem::exists(dbPath)) {
|
||
m_lastError = "数据库文件不存在!";
|
||
return false;
|
||
}
|
||
|
||
sqlite3 *db = nullptr;
|
||
int rc = sqlite3_open(dbPath.c_str(), &db);
|
||
if (rc != SQLITE_OK) {
|
||
m_lastError = "无法打开数据库: " + std::string(sqlite3_errmsg(db));
|
||
sqlite3_close(db);
|
||
return false;
|
||
}
|
||
|
||
// 准备SQL语句查询Configuration表
|
||
const char *sql = "SELECT ConfName FROM Configuration WHERE ConfID = ?";
|
||
sqlite3_stmt *stmt = nullptr;
|
||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
|
||
if (rc != SQLITE_OK) {
|
||
m_lastError = "SQL准备失败: " + std::string(sqlite3_errmsg(db));
|
||
sqlite3_close(db);
|
||
return false;
|
||
}
|
||
|
||
// 绑定参数
|
||
sqlite3_bind_int(stmt, 1, confID);
|
||
|
||
// 执行查询
|
||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||
const char *text = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
|
||
m_pluginInfo.pluginName = text ? text : "";
|
||
m_pluginInfo.interfaceHeaderPath = "../IDL/" + m_pluginInfo.pluginName + "_Interface.h";
|
||
m_pluginInfo.outputDirectory =
|
||
xncorePath + "/Configuration/" + m_pluginInfo.pluginName + "/PluginCode";
|
||
} else {
|
||
m_lastError = "未找到ConfID为 " + std::to_string(confID) + " 的配置";
|
||
sqlite3_finalize(stmt);
|
||
sqlite3_close(db);
|
||
return false;
|
||
}
|
||
|
||
// 清理资源
|
||
sqlite3_finalize(stmt);
|
||
|
||
// 查询DataInterface_{ConfID}表获取接口信息
|
||
std::string dataInterfaceTableName = "DataInterface_" + std::to_string(confID);
|
||
std::string dataInterfaceSql =
|
||
"SELECT DISTINCT ModelStructName, SystemName, PlaneName, ATAName FROM "
|
||
+ dataInterfaceTableName;
|
||
|
||
sqlite3_stmt *dataInterfaceStmt = nullptr;
|
||
rc = sqlite3_prepare_v2(db, dataInterfaceSql.c_str(), -1, &dataInterfaceStmt, nullptr);
|
||
if (rc != SQLITE_OK) {
|
||
m_lastError = "DataInterface表SQL准备失败: " + std::string(sqlite3_errmsg(db));
|
||
sqlite3_close(db);
|
||
return false;
|
||
}
|
||
|
||
// 执行查询并收集接口信息
|
||
while (sqlite3_step(dataInterfaceStmt) == SQLITE_ROW) {
|
||
InterfaceInfo interface;
|
||
interface.interfaceName =
|
||
reinterpret_cast<const char *>(sqlite3_column_text(dataInterfaceStmt, 0));
|
||
std::string systemName =
|
||
reinterpret_cast<const char *>(sqlite3_column_text(dataInterfaceStmt, 1));
|
||
std::string planeName =
|
||
reinterpret_cast<const char *>(sqlite3_column_text(dataInterfaceStmt, 2));
|
||
std::string ataName =
|
||
reinterpret_cast<const char *>(sqlite3_column_text(dataInterfaceStmt, 3));
|
||
interface.templateType = systemName + "::" + planeName + "::" + ataName
|
||
+ "::" + interface.interfaceName + "_Interface";
|
||
|
||
// 使用ModelStructName去重
|
||
bool isDuplicate = false;
|
||
for (const auto &existingInterface : m_pluginInfo.interfaces) {
|
||
if (existingInterface.interfaceName == interface.interfaceName) {
|
||
isDuplicate = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!isDuplicate) {
|
||
m_pluginInfo.interfaces.push_back(interface);
|
||
}
|
||
}
|
||
|
||
// 清理DataInterface查询资源
|
||
sqlite3_finalize(dataInterfaceStmt);
|
||
sqlite3_close(db);
|
||
return true;
|
||
}
|
||
|
||
bool PluginGenerator::generatePluginCpp()
|
||
{
|
||
std::string content = generatePluginCppContent();
|
||
std::string filePath =
|
||
m_pluginInfo.outputDirectory + "/" + m_pluginInfo.pluginName + "_plugin.cpp";
|
||
return writeFile(filePath, content);
|
||
}
|
||
|
||
bool PluginGenerator::generateCMakeLists()
|
||
{
|
||
std::string content = generateCMakeListsContent();
|
||
std::string filePath = m_pluginInfo.outputDirectory + "/CMakeLists.txt";
|
||
return writeFile(filePath, content);
|
||
}
|
||
|
||
bool PluginGenerator::compilePlugin()
|
||
{
|
||
std::string buildDir = m_pluginInfo.outputDirectory + "/build";
|
||
// 如果build目录已存在则删除
|
||
if (std::filesystem::exists(buildDir)) {
|
||
try {
|
||
std::filesystem::remove_all(buildDir);
|
||
} catch (const std::exception &e) {
|
||
m_lastError = "Failed to remove existing build directory: " + std::string(e.what());
|
||
return false;
|
||
}
|
||
}
|
||
// 创建build目录
|
||
if (!createDirectory(buildDir)) {
|
||
m_lastError = "Failed to create build directory: " + buildDir;
|
||
return false;
|
||
}
|
||
|
||
// 构建命令
|
||
std::string cmakeCmd = "cd " + buildDir + " && cmake ..";
|
||
std::string makeCmd = "cd " + buildDir + " && make";
|
||
std::string installCmd = "cd " + buildDir + " && make install";
|
||
|
||
// 执行cmake
|
||
int cmakeResult = system(cmakeCmd.c_str());
|
||
if (cmakeResult != 0) {
|
||
m_lastError = "CMake failed with exit code: " + std::to_string(cmakeResult);
|
||
return false;
|
||
}
|
||
|
||
// 执行make
|
||
int makeResult = system(makeCmd.c_str());
|
||
if (makeResult != 0) {
|
||
m_lastError = "Make failed with exit code: " + std::to_string(makeResult);
|
||
return false;
|
||
}
|
||
|
||
// 执行make install
|
||
int installResult = system(installCmd.c_str());
|
||
if (installResult != 0) {
|
||
m_lastError = "Make install failed with exit code: " + std::to_string(installResult);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
std::string PluginGenerator::getLastError() const
|
||
{
|
||
return m_lastError;
|
||
}
|
||
|
||
std::string PluginGenerator::generatePluginCppContent()
|
||
{
|
||
std::stringstream ss;
|
||
|
||
// 生成头文件包含
|
||
ss << "#include <XNMonitor/PluginInterface.h>\n";
|
||
ss << "#include <XNMonitor/DataMonitor.h>\n";
|
||
ss << "// " << m_pluginInfo.pluginName << "接口头文件 - 只在插件中包含\n";
|
||
ss << "#include \"" << m_pluginInfo.interfaceHeaderPath << "\"\n\n";
|
||
|
||
// 生成插件信息
|
||
ss << "// 插件信息\n";
|
||
ss << "static PluginInfo plugin_info = {\"" << m_pluginInfo.pluginName << "\", \""
|
||
<< "" << "\", DATAMONITOR_PLUGIN_INTERFACE_VERSION};\n\n";
|
||
|
||
// 生成支持的接口列表
|
||
ss << "// 支持的接口列表\n";
|
||
ss << "static const char *supported_interfaces[] = {\n";
|
||
for (size_t i = 0; i < m_pluginInfo.interfaces.size(); ++i) {
|
||
ss << " \"" << m_pluginInfo.interfaces[i].interfaceName << "\"";
|
||
if (i < m_pluginInfo.interfaces.size() - 1) {
|
||
ss << ",";
|
||
}
|
||
ss << "\n";
|
||
}
|
||
ss << "};\n\n";
|
||
|
||
ss << "static const int interface_count = sizeof(supported_interfaces) / "
|
||
"sizeof(supported_interfaces[0]);\n\n";
|
||
|
||
// 生成导出的插件函数
|
||
ss << "// 导出的插件函数\n";
|
||
ss << "extern \"C\"\n";
|
||
ss << "{\n\n";
|
||
|
||
// get_plugin_info函数
|
||
ss << " PluginInfo *get_plugin_info()\n";
|
||
ss << " {\n";
|
||
ss << " return &plugin_info;\n";
|
||
ss << " }\n\n";
|
||
|
||
// create_monitor函数
|
||
ss << " DataMonitorBasePtr create_monitor(const char *interfaceName)\n";
|
||
ss << " {\n";
|
||
ss << " std::string name(interfaceName);\n\n";
|
||
|
||
for (size_t i = 0; i < m_pluginInfo.interfaces.size(); ++i) {
|
||
const auto &interface = m_pluginInfo.interfaces[i];
|
||
ss << " if (name == \"" << interface.interfaceName << "\") {\n";
|
||
ss << " return std::make_shared<DataMonitorProduct<" << interface.templateType
|
||
<< ">>();\n";
|
||
ss << " }";
|
||
if (i < m_pluginInfo.interfaces.size() - 1) {
|
||
ss << " else";
|
||
}
|
||
ss << "\n";
|
||
}
|
||
|
||
ss << " return nullptr;\n";
|
||
ss << " }\n\n";
|
||
|
||
// destroy_monitor函数
|
||
ss << " void destroy_monitor(const char *interfaceName)\n";
|
||
ss << " {\n";
|
||
ss << " // 智能指针会自动管理内存,这里可以添加额外的清理逻辑\n";
|
||
ss << " }\n\n";
|
||
|
||
// get_supported_interfaces函数
|
||
ss << " const char **get_supported_interfaces(int *count)\n";
|
||
ss << " {\n";
|
||
ss << " if (count) {\n";
|
||
ss << " *count = interface_count;\n";
|
||
ss << " }\n";
|
||
ss << " return const_cast<const char **>(supported_interfaces);\n";
|
||
ss << " }\n\n";
|
||
|
||
// free_string_array函数
|
||
ss << " void free_string_array(const char **array)\n";
|
||
ss << " {\n";
|
||
ss << " // 这里不需要释放,因为使用的是静态数组\n";
|
||
ss << " }\n";
|
||
ss << "}\n";
|
||
|
||
return ss.str();
|
||
}
|
||
|
||
std::string PluginGenerator::generateCMakeListsContent()
|
||
{
|
||
std::stringstream ss;
|
||
std::string projectName = m_pluginInfo.pluginName + "_Monitor";
|
||
|
||
ss << "# CMakeLists.txt for " << projectName << " plugin\n";
|
||
ss << "cmake_minimum_required(VERSION 3.10)\n\n";
|
||
|
||
ss << "# 设置项目名称\n";
|
||
ss << "project(" << projectName << "_plugin)\n\n";
|
||
|
||
ss << "# 设置C++标准\n";
|
||
ss << "set(CMAKE_CXX_STANDARD 17)\n";
|
||
ss << "set(CMAKE_CXX_STANDARD_REQUIRED ON)\n\n";
|
||
|
||
// 设置默认构建类型为Release
|
||
ss << "if(NOT CMAKE_BUILD_TYPE)" << std::endl;
|
||
ss << "\tset(CMAKE_BUILD_TYPE Release CACHE STRING "
|
||
<< "\"Choose the type of build (Debug, Release, RelWithDebInfo, MinSizeRel)\""
|
||
<< " FORCE)" << std::endl;
|
||
ss << "endif()" << std::endl;
|
||
ss << std::endl;
|
||
|
||
ss << "# 查找必要的包\n";
|
||
ss << "find_package(PkgConfig REQUIRED)\n";
|
||
ss << "find_package(FastDDS REQUIRED)\n";
|
||
|
||
ss << "if(DEFINED ENV{XNCore})\n";
|
||
ss << " set(XNCore_PATH $ENV{XNCore})\n";
|
||
ss << "else()\n";
|
||
ss << " message(FATAL_ERROR \"Environment variable XNCore is not set.\")\n";
|
||
ss << "endif()\n";
|
||
|
||
ss << "include_directories(${XNCore_PATH}/include)\n";
|
||
|
||
ss << "# 创建插件库\n";
|
||
ss << "add_library(" << projectName << " SHARED\n";
|
||
ss << " " << m_pluginInfo.pluginName << "_plugin.cpp\n";
|
||
ss << ")\n\n";
|
||
|
||
ss << "# 链接库\n";
|
||
ss << "target_link_libraries(" << projectName << "\n";
|
||
ss << " fastcdr\n";
|
||
ss << " fastdds\n";
|
||
ss << " OpenSSL::SSL\n";
|
||
ss << " OpenSSL::Crypto\n";
|
||
ss << " ${XNCore_PATH}/lib/lib" << m_pluginInfo.pluginName << "_Interface.so\n";
|
||
ss << " ${XNCore_PATH}/lib/libXNMonitorServer.so\n";
|
||
ss << ")\n\n";
|
||
|
||
ss << "target_compile_definitions(" << projectName << " PRIVATE " << projectName
|
||
<< "_LIBRARY)\n";
|
||
|
||
ss << "if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n";
|
||
ss << " set(CMAKE_INSTALL_PREFIX \"${XNCore_PATH}/Configuration/" << m_pluginInfo.pluginName
|
||
<< "/Plugins\" CACHE PATH \"Install path prefix\" "
|
||
"FORCE)\n";
|
||
ss << "endif()\n";
|
||
|
||
ss << "include(GNUInstallDirs)\n";
|
||
ss << "install(TARGETS " << projectName << "\n";
|
||
ss << " BUNDLE DESTINATION .\n";
|
||
ss << " LIBRARY DESTINATION .\n";
|
||
ss << " RUNTIME DESTINATION .\n";
|
||
ss << ")\n";
|
||
|
||
return ss.str();
|
||
}
|
||
|
||
bool PluginGenerator::writeFile(const std::string &filePath, const std::string &content)
|
||
{
|
||
try {
|
||
// 确保目录存在
|
||
std::filesystem::path path(filePath);
|
||
if (path.has_parent_path()) {
|
||
createDirectory(path.parent_path().string());
|
||
}
|
||
|
||
std::ofstream file(filePath);
|
||
if (!file.is_open()) {
|
||
m_lastError = "无法创建文件: " + filePath;
|
||
return false;
|
||
}
|
||
|
||
file << content;
|
||
file.close();
|
||
|
||
return true;
|
||
} catch (const std::exception &e) {
|
||
m_lastError = "写入文件错误: " + std::string(e.what());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool PluginGenerator::createDirectory(const std::string &dirPath)
|
||
{
|
||
try {
|
||
if (!std::filesystem::exists(dirPath)) {
|
||
return std::filesystem::create_directories(dirPath);
|
||
}
|
||
return true;
|
||
} catch (const std::exception &e) {
|
||
m_lastError = "创建目录错误: " + std::string(e.what());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
const GenPluginInfo &PluginGenerator::getPluginInfo() const
|
||
{
|
||
return m_pluginInfo;
|
||
} |