439 lines
15 KiB
C++
439 lines
15 KiB
C++
#include "XNModelGen.h"
|
||
#include <sqlite3.h>
|
||
#include <stdexcept>
|
||
#include <filesystem>
|
||
#include <cstdlib>
|
||
#include <fstream>
|
||
#include <algorithm>
|
||
#include <nlohmann/json.hpp>
|
||
|
||
using json = nlohmann::json;
|
||
namespace fs = std::filesystem;
|
||
|
||
XNModelGen::XNModelGen()
|
||
{
|
||
}
|
||
|
||
XNModelGen::~XNModelGen()
|
||
{
|
||
}
|
||
|
||
int XNModelGen::Initialize(const std::string &className, const std::string &version,
|
||
const std::string &planeName, std::string &errorMsg)
|
||
{
|
||
std::string dbPath = GetXNCorePath() + "/database/XNSim.db";
|
||
sqlite3 *db;
|
||
int rc = sqlite3_open(dbPath.c_str(), &db);
|
||
if (rc != SQLITE_OK) {
|
||
errorMsg = "无法打开数据库: " + std::string(sqlite3_errmsg(db));
|
||
return -1;
|
||
}
|
||
|
||
std::string sql =
|
||
"SELECT * FROM XNModelsVersion WHERE ClassName = ? AND Version = ? AND PlaneName = ?";
|
||
sqlite3_stmt *stmt;
|
||
rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr);
|
||
if (rc != SQLITE_OK) {
|
||
errorMsg = "准备XNModelsVersion表SQL语句失败: " + std::string(sqlite3_errmsg(db));
|
||
sqlite3_close(db);
|
||
return -1;
|
||
}
|
||
|
||
// 绑定参数
|
||
sqlite3_bind_text(stmt, 1, className.c_str(), -1, SQLITE_STATIC);
|
||
sqlite3_bind_text(stmt, 2, version.c_str(), -1, SQLITE_STATIC);
|
||
sqlite3_bind_text(stmt, 3, planeName.c_str(), -1, SQLITE_STATIC);
|
||
|
||
// 执行查询
|
||
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
|
||
m_dataPackageHeaderName =
|
||
(const char *)sqlite3_column_text(stmt, 15); // DataPackageHeaderName
|
||
m_dataPackageEntryPoint =
|
||
(const char *)sqlite3_column_text(stmt, 16); // 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);
|
||
// TODO cmdList暂不读取
|
||
} else if (rc == SQLITE_DONE) {
|
||
// 没有找到数据
|
||
errorMsg = "未找到匹配的XNModelsVersion记录";
|
||
sqlite3_finalize(stmt);
|
||
sqlite3_close(db);
|
||
return -2;
|
||
} else {
|
||
// 查询出错
|
||
errorMsg = "查询XNModelsVersion表执行失败: " + std::string(sqlite3_errmsg(db));
|
||
sqlite3_finalize(stmt);
|
||
sqlite3_close(db);
|
||
return -3;
|
||
}
|
||
|
||
sqlite3_finalize(stmt);
|
||
|
||
// 根据m_confID查询Configuration表
|
||
if (!m_confID.empty()) {
|
||
std::string confSql = "SELECT ConfName FROM Configuration WHERE ConfID = ?";
|
||
sqlite3_stmt *confStmt;
|
||
rc = sqlite3_prepare_v2(db, confSql.c_str(), -1, &confStmt, nullptr);
|
||
if (rc != SQLITE_OK) {
|
||
errorMsg = "准备Configuration表SQL语句失败: " + std::string(sqlite3_errmsg(db));
|
||
sqlite3_close(db);
|
||
return -4;
|
||
}
|
||
|
||
// 绑定参数
|
||
sqlite3_bind_text(confStmt, 1, m_confID.c_str(), -1, SQLITE_STATIC);
|
||
|
||
// 执行查询
|
||
rc = sqlite3_step(confStmt);
|
||
if (rc == SQLITE_ROW) {
|
||
// 读取Configuration表数据
|
||
m_confName = (const char *)sqlite3_column_text(confStmt, 0); // ConfName
|
||
} else if (rc == SQLITE_DONE) {
|
||
// 没有找到Configuration记录
|
||
errorMsg = "未找到匹配的Configuration记录,ConfID: " + m_confID;
|
||
sqlite3_finalize(confStmt);
|
||
sqlite3_close(db);
|
||
return -5;
|
||
} else {
|
||
// Configuration查询出错
|
||
errorMsg = "查询Configuration表执行失败: " + std::string(sqlite3_errmsg(db));
|
||
sqlite3_finalize(confStmt);
|
||
sqlite3_close(db);
|
||
return -6;
|
||
}
|
||
|
||
sqlite3_finalize(confStmt);
|
||
}
|
||
|
||
sqlite3_close(db);
|
||
|
||
//目录组装
|
||
if (m_confName.empty()) {
|
||
errorMsg = "Configuration表ConfName为空";
|
||
return -7;
|
||
}
|
||
m_workPath = GetXNCorePath() + "/Configuration/" + m_confName;
|
||
m_modelsPath = m_workPath + "/Models";
|
||
m_codePath = m_workPath + "/ModelProjects/" + className + "_" + version;
|
||
|
||
m_hasDataPackage = false;
|
||
if (!m_dataPackagePath.empty() && !m_dataPackageHeaderName.empty()
|
||
&& !m_dataPackageInterfaceName.empty() && !m_dataPackageEntryPoint.empty()) {
|
||
m_hasDataPackage = true;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int XNModelGen::GenerateCode(std::string &errorMsg)
|
||
{
|
||
// 检查代码目录是否存在,不存在则创建
|
||
if (!fs::exists(m_codePath)) {
|
||
try {
|
||
fs::create_directories(m_codePath);
|
||
} catch (const fs::filesystem_error &e) {
|
||
errorMsg = "创建代码目录失败: " + std::string(e.what());
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
bool ret = GenerateHeaderFile();
|
||
|
||
if (!ret) {
|
||
errorMsg = "生成头文件失败";
|
||
return -2;
|
||
}
|
||
|
||
ret = GenerateSourceFile();
|
||
|
||
if (!ret) {
|
||
errorMsg = "生成源文件失败";
|
||
return -3;
|
||
}
|
||
|
||
ret = GenerateConfigFile();
|
||
if (!ret) {
|
||
errorMsg = "生成配置文件失败";
|
||
return -4;
|
||
}
|
||
|
||
ret = GenerateCMakeLists();
|
||
if (!ret) {
|
||
errorMsg = "生成CMakeLists.txt失败";
|
||
return -5;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
bool XNModelGen::GenerateHeaderFile()
|
||
{
|
||
std::string globalHeaderPath = m_codePath + "/" + m_className + "_global.h";
|
||
std::ofstream globalHeaderFile(globalHeaderPath);
|
||
if (!globalHeaderFile.is_open()) {
|
||
return false;
|
||
}
|
||
std::string upperClassName = UpperCase(m_className);
|
||
|
||
// 导入导出定义文件
|
||
globalHeaderFile << "#ifndef " << upperClassName << "_GLOBAL_H" << std::endl;
|
||
globalHeaderFile << "#define " << upperClassName << "_GLOBAL_H" << std::endl;
|
||
globalHeaderFile << std::endl;
|
||
globalHeaderFile << "#if defined(" << upperClassName << "_LIBRARY)" << std::endl;
|
||
globalHeaderFile << "#define " << upperClassName
|
||
<< "_EXPORT __attribute__((visibility(\"default\")))" << std::endl;
|
||
globalHeaderFile << "#else" << std::endl;
|
||
globalHeaderFile << "#define " << upperClassName
|
||
<< "_EXPORT __attribute__((visibility(\"default\")))" << std::endl;
|
||
globalHeaderFile << "#endif" << std::endl;
|
||
globalHeaderFile << std::endl;
|
||
globalHeaderFile << "#endif // " << upperClassName << "_GLOBAL_H" << std::endl;
|
||
globalHeaderFile << std::endl;
|
||
globalHeaderFile.close();
|
||
|
||
// 模型头文件
|
||
std::string headerPath = m_codePath + "/" + m_className + ".h";
|
||
std::ofstream headerFile(headerPath);
|
||
if (!headerFile.is_open()) {
|
||
return false;
|
||
}
|
||
headerFile << "#pragma once" << std::endl;
|
||
headerFile << std::endl;
|
||
headerFile << "#include \"" << m_className << "_global.h\"" << std::endl;
|
||
headerFile << "#include <XNCore/XNModelObject.h>" << std::endl;
|
||
headerFile << std::endl;
|
||
headerFile << "struct " << m_className << "Private;" << std::endl;
|
||
headerFile << std::endl;
|
||
headerFile << "class " << upperClassName << "_EXPORT " << m_className
|
||
<< " : public XNModelObject" << std::endl;
|
||
headerFile << "{" << std::endl;
|
||
headerFile << "XN_METATYPE(" << m_className << ", XNModelObject)" << std::endl;
|
||
headerFile << "XN_DECLARE_PRIVATE(" << m_className << ")" << std::endl;
|
||
headerFile << "public:" << std::endl;
|
||
headerFile << " " << m_className << "();" << std::endl;
|
||
headerFile << " virtual ~" << m_className << "();" << std::endl;
|
||
headerFile << std::endl;
|
||
headerFile << "protected:" << std::endl;
|
||
headerFile << " " << m_className << "(PrivateType *p);" << std::endl;
|
||
headerFile << std::endl;
|
||
headerFile << "public:" << std::endl;
|
||
headerFile << " virtual void Initialize() override;" << std::endl;
|
||
headerFile << " virtual void PrepareForExecute() override;" << std::endl;
|
||
headerFile << " virtual void StepUpdate() override;" << std::endl;
|
||
headerFile << std::endl;
|
||
headerFile << "protected:" << std::endl;
|
||
headerFile << " void InitializeData();" << std::endl;
|
||
headerFile << " void ReleaseData();" << std::endl;
|
||
headerFile << "};" << std::endl;
|
||
headerFile << std::endl;
|
||
headerFile << "XNCLASS_PTR_DECLARE(" << m_className << ")" << std::endl;
|
||
headerFile << std::endl;
|
||
headerFile.close();
|
||
|
||
// 私有结构体头文件
|
||
std::string pHeaderPath = m_codePath + "/" + m_className + "_p.h";
|
||
std::ofstream pHeaderFile(pHeaderPath);
|
||
if (!pHeaderFile.is_open()) {
|
||
return false;
|
||
}
|
||
|
||
pHeaderFile << "#pragma once" << std::endl;
|
||
pHeaderFile << std::endl;
|
||
pHeaderFile << "#include <XNCore/XNModelObject_p.h>";
|
||
if (m_hasDataPackage) {
|
||
pHeaderFile << "#include \"" << m_workPath << "/Packages/" << m_dataPackagePath << "/"
|
||
<< m_dataPackageHeaderName << "\"" << std::endl;
|
||
}
|
||
pHeaderFile << "#include \"" << m_workPath << "/IDL/" << m_confName << "_Interface.h\""
|
||
<< std::endl;
|
||
pHeaderFile << std::endl;
|
||
if (m_hasDataPackage) {
|
||
pHeaderFile << "using InterfaceType = " << m_dataPackageInterfaceName << ";" << std::endl;
|
||
pHeaderFile << "typedef void (*FunctionType)(InterfaceType *);" << std::endl;
|
||
pHeaderFile << std::endl;
|
||
}
|
||
pHeaderFile << "struct " << m_className << "Private : public XNModelObjectPrivate{"
|
||
<< std::endl;
|
||
if (m_hasDataPackage) {
|
||
pHeaderFile << " FunctionType _fun = nullptr;" << std::endl;
|
||
pHeaderFile << " InterfaceType _data;" << std::endl;
|
||
pHeaderFile << " std::string _entryPointName = \"" << m_dataPackageEntryPoint << "\";"
|
||
<< std::endl;
|
||
}
|
||
pHeaderFile << "std::mutex _mutex" << std::endl;
|
||
if (!m_inputStructName_Interface.empty()) {
|
||
pHeaderFile << " " << m_inputStructName_Interface << "_Interface _inputInterface;"
|
||
<< std::endl;
|
||
}
|
||
if (!m_outputStructName_Interface.empty()) {
|
||
pHeaderFile << " " << m_outputStructName_Interface << "_Interface _outputInterface;"
|
||
<< std::endl;
|
||
}
|
||
if (!m_heartStructName_Interface.empty()) {
|
||
pHeaderFile << " " << m_heartStructName_Interface << "_Interface _heartbeatInterface;"
|
||
<< std::endl;
|
||
}
|
||
pHeaderFile << "};" << std::endl;
|
||
pHeaderFile << std::endl;
|
||
pHeaderFile.close();
|
||
|
||
return true;
|
||
}
|
||
|
||
bool XNModelGen::GenerateSourceFile()
|
||
{
|
||
// 源文件
|
||
std::string sourcePath = m_codePath + "/" + m_className + ".cpp";
|
||
std::ofstream sourceFile(sourcePath);
|
||
if (!sourceFile.is_open()) {
|
||
return false;
|
||
}
|
||
sourceFile << "#include \"" << m_className << ".h\"" << std::endl;
|
||
sourceFile << "#include \"" << m_className << "_p.h\"" << std::endl;
|
||
sourceFile << "#include <XNCore/XNModelManager.h>" << std::endl;
|
||
sourceFile << std::endl;
|
||
sourceFile << "XN_MODEL_INITIALIZE(" << m_className << ")" << std::endl;
|
||
sourceFile << std::endl;
|
||
sourceFile << m_className << "::" << m_className << "() : XNModelObject(" << m_className
|
||
<< "new " << m_className << "Private())" << std::endl;
|
||
sourceFile << "{" << std::endl;
|
||
sourceFile << "}" << std::endl;
|
||
sourceFile << std::endl;
|
||
sourceFile << m_className << "::~" << m_className << "() {" << std::endl;
|
||
sourceFile << "}" << std::endl;
|
||
sourceFile << std::endl;
|
||
sourceFile << m_className << "::" << m_className << "(PrivateType *p) : XNModelObject(p)"
|
||
<< std::endl;
|
||
sourceFile << "{" << std::endl;
|
||
sourceFile << "}" << std::endl;
|
||
sourceFile << std::endl;
|
||
sourceFile << "void " << m_className << "::Initialize() {" << std::endl;
|
||
sourceFile << " T_D();" << std::endl;
|
||
sourceFile << " SuperType::Initialize();" << std::endl;
|
||
if (m_hasDataPackage) {
|
||
sourceFile << " if (d->_dynamicLib) {" << std::endl;
|
||
sourceFile
|
||
<< " d->_fun = (FunctionType)dlsym(d->_dynamicLib, d->_entryPointName.c_str());"
|
||
<< std::endl;
|
||
sourceFile << " if (!d->_fun) {" << std::endl;
|
||
sourceFile << " LOG_WARNING(\"Failed to resolve " << m_dataPackageEntryPoint
|
||
<< "\");" << std::endl;
|
||
sourceFile << " }" << std::endl;
|
||
sourceFile << " }" << std::endl;
|
||
}
|
||
sourceFile << " /* 在这里进行其它初始化 */" << std::endl;
|
||
sourceFile << "}" << std::endl;
|
||
sourceFile << std::endl;
|
||
sourceFile << "void " << m_className << "::PrepareForExecute() {" << std::endl;
|
||
sourceFile << " T_D();" << std::endl;
|
||
sourceFile << " SuperType::PrepareForExecute();" << std::endl;
|
||
sourceFile << " InitializeData();" << std::endl;
|
||
if (!m_inputStructName_Interface.empty()) {
|
||
sourceFile << " d->_inputInterface.Initialize(GetFramework(), GetUniqueId(), 1);"
|
||
<< std::endl;
|
||
}
|
||
if (!m_outputStructName_Interface.empty()) {
|
||
sourceFile << " d->_outputInterface.Initialize(GetFramework(), GetUniqueId(), 2);"
|
||
<< std::endl;
|
||
}
|
||
if (!m_heartStructName_Interface.empty()) {
|
||
sourceFile << " d->_heartbeatInterface.Initialize(GetFramework(), GetUniqueId(), 2);"
|
||
<< std::endl;
|
||
}
|
||
sourceFile << " /* 在这里进行其它运行前准备工作 */" << std::endl;
|
||
sourceFile << "}" << std::endl;
|
||
sourceFile << std::endl;
|
||
sourceFile << "void " << m_className << "::StepUpdate() {" << std::endl;
|
||
sourceFile << " T_D();" << std::endl;
|
||
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;
|
||
}
|
||
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_heartStructName_Interface.empty() && !m_heartStructName.empty()
|
||
&& m_heartStructName == m_dataPackageInterfaceName) {
|
||
sourceFile << " d->_heartbeatInterface.setData(&d->_data);" << std::endl;
|
||
}
|
||
sourceFile << " }" << std::endl;
|
||
}
|
||
sourceFile << " /* 在这里进行其它运行时计算 */" << std::endl;
|
||
sourceFile << "}" << std::endl;
|
||
sourceFile << std::endl;
|
||
|
||
return true;
|
||
}
|
||
|
||
bool XNModelGen::GenerateConfigFile()
|
||
{
|
||
return true;
|
||
}
|
||
|
||
bool XNModelGen::GenerateCMakeLists()
|
||
{
|
||
return true;
|
||
}
|
||
|
||
std::string XNModelGen::GetXNCorePath()
|
||
{
|
||
const char *env_value = std::getenv("XNCore");
|
||
return env_value ? env_value : "";
|
||
}
|
||
|
||
std::string XNModelGen::UpperCase(const std::string &str)
|
||
{
|
||
std::string result = str;
|
||
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
|
||
return result;
|
||
}
|
||
|
||
void XNModelGen::ParseStructName(const std::string &str, std::string &structName,
|
||
std::string &interfaceName)
|
||
{
|
||
try {
|
||
json j = json::parse(str);
|
||
// 遍历JSON对象,第一个键作为structName,第一个值作为interfaceName
|
||
if (!j.empty() && j.is_object()) {
|
||
auto it = j.begin();
|
||
if (it != j.end()) {
|
||
structName = it.key();
|
||
if (it.value().is_string()) {
|
||
interfaceName = it.value();
|
||
}
|
||
}
|
||
}
|
||
} catch (const json::exception &e) {
|
||
// JSON解析失败,将整个字符串作为structName
|
||
structName = "";
|
||
interfaceName = "";
|
||
}
|
||
} |