修改XNEngine支持数据库读取,还未完成
This commit is contained in:
parent
88d2ffb032
commit
7da7a8a5ee
Binary file not shown.
@ -15,6 +15,7 @@ endif()
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
|
||||
add_executable(XNEngine
|
||||
main.cpp
|
||||
@ -30,6 +31,7 @@ target_link_libraries(XNEngine PRIVATE
|
||||
pthread
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
sqlite3
|
||||
dl
|
||||
)
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "../XNCore/XNServiceManager.h"
|
||||
#include "../XNCore/XNModelManager.h"
|
||||
#include "../XNCore/XNScenarioManager.h"
|
||||
#include <sqlite3.h>
|
||||
|
||||
// 引擎类构造函数
|
||||
XNEngine::XNEngine()
|
||||
@ -55,35 +56,42 @@ void XNEngine::SimControlListener(const XNSim::XNSimControl::XNRuntimeControl &c
|
||||
}
|
||||
}
|
||||
|
||||
// 设置日志级别
|
||||
bool XNEngine::SetLogLevel(const std::string &XmlPath)
|
||||
// 解析配置文件
|
||||
bool XNEngine::ParseConfig(const std::string &XmlPath)
|
||||
{
|
||||
size_t index = XmlPath.find_last_of('.');
|
||||
if (index != std::string::npos) {
|
||||
std::string suffix = XmlPath.substr(index);
|
||||
if (suffix != ".xml" && suffix != ".sce") {
|
||||
std::cerr << "0x1005 配置文件不是 .xml 或 .sce 文件, 引擎将退出!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "0x1006 配置文件没有 .xml 或 .sce 的后缀名, 引擎将退出!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// 打开配置文件
|
||||
std::ifstream file(XmlPath);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "0x1003 Failed to open the runtime environment configuration file, "
|
||||
"the engine will exit!"
|
||||
<< std::endl;
|
||||
std::cerr << "0x1007 打开配置文件失败, 引擎将退出!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// 解析配置文件
|
||||
tinyxml2::XMLDocument doc;
|
||||
if (doc.LoadFile(XmlPath.c_str()) != tinyxml2::XML_SUCCESS) {
|
||||
std::cerr << "0x1004 Failed to parse the runtime environment configuration file, "
|
||||
"the engine will exit!"
|
||||
<< std::endl;
|
||||
std::cerr << "0x1008 解析配置文件失败, 引擎将退出!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// 获取根元素
|
||||
tinyxml2::XMLElement *root = doc.FirstChildElement("Scenario");
|
||||
if (!root) {
|
||||
std::cerr << "0x1005 Failed to find Scenario element in configuration file!" << std::endl;
|
||||
std::cerr << "0x1009 配置文件中未找到 Scenario 根元素, 引擎将退出!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 顺便读取一下CPU亲和性
|
||||
int cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
std::cout << "Current number of CPU cores-> " << cpus << std::endl;
|
||||
std::cout << "当前CPU核心数-> " << cpus << std::endl;
|
||||
|
||||
// 设置CPU亲和性
|
||||
cpu_set_t mask;
|
||||
@ -106,30 +114,34 @@ bool XNEngine::SetLogLevel(const std::string &XmlPath)
|
||||
CPU_SET(index, &mask);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "Invalid CPU affinity value: " << cpuIndex << std::endl;
|
||||
std::cerr << "0x1010 无效的CPU亲和性值: " << cpuIndex << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {
|
||||
std::cerr << "0x1020 Failed to set engine CPU affinity-> " << strerror(errno) << std::endl;
|
||||
std::cerr << "0x1011 设置引擎CPU亲和性失败-> " << strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::cout << "Successfully set engine CPU affinity-> " << CPUAffinity << std::endl;
|
||||
std::cout << "成功设置引擎CPU亲和性-> " << CPUAffinity << std::endl;
|
||||
|
||||
// 锁定内存
|
||||
if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
|
||||
std::cerr << "0x1021 Failed to lock engine memory-> " << strerror(errno) << std::endl;
|
||||
std::cerr << "0x1012 锁定引擎内存失败-> " << strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::cout << "Successfully locked engine memory!" << std::endl;
|
||||
std::cout << "成功锁定引擎内存!" << std::endl;
|
||||
|
||||
// 获取配置文件中的日志元素
|
||||
bool isDebug = false;
|
||||
bool isInfo = false;
|
||||
bool isWarn = false;
|
||||
bool isError = false;
|
||||
// 获取配置文件中的控制台输出元素
|
||||
tinyxml2::XMLElement *consoleOutputNode = root->FirstChildElement("ConsoleOutput");
|
||||
if (!consoleOutputNode) {
|
||||
std::cout << "The runtime environment configuration file does not contain "
|
||||
"the ConsoleOutput element, the Console will Output all Log!"
|
||||
std::cout << "0x1013 配置文件中未找到 ConsoleOutput 元素, 控制台将输出所有日志!"
|
||||
<< std::endl;
|
||||
} else {
|
||||
// 获取配置文件中的调试日志输出元素
|
||||
@ -138,9 +150,9 @@ bool XNEngine::SetLogLevel(const std::string &XmlPath)
|
||||
&& (strcmp(debugConsoleOutput, "true") == 0 || strcmp(debugConsoleOutput, "1") == 0
|
||||
|| strcmp(debugConsoleOutput, "True") == 0
|
||||
|| strcmp(debugConsoleOutput, "TRUE") == 0)) {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Debug, true);
|
||||
isDebug = true;
|
||||
} else {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Debug, false);
|
||||
isDebug = false;
|
||||
}
|
||||
|
||||
// 获取配置文件中的信息日志输出元素
|
||||
@ -149,9 +161,9 @@ bool XNEngine::SetLogLevel(const std::string &XmlPath)
|
||||
&& (strcmp(infoConsoleOutput, "true") == 0 || strcmp(infoConsoleOutput, "1") == 0
|
||||
|| strcmp(infoConsoleOutput, "True") == 0
|
||||
|| strcmp(infoConsoleOutput, "TRUE") == 0)) {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Info, true);
|
||||
isInfo = true;
|
||||
} else {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Info, false);
|
||||
isInfo = false;
|
||||
}
|
||||
|
||||
// 获取配置文件中的警告日志输出元素
|
||||
@ -160,9 +172,9 @@ bool XNEngine::SetLogLevel(const std::string &XmlPath)
|
||||
&& (strcmp(warningConsoleOutput, "true") == 0 || strcmp(warningConsoleOutput, "1") == 0
|
||||
|| strcmp(warningConsoleOutput, "True") == 0
|
||||
|| strcmp(warningConsoleOutput, "TRUE") == 0)) {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Warning, true);
|
||||
isWarn = true;
|
||||
} else {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Warning, false);
|
||||
isWarn = false;
|
||||
}
|
||||
|
||||
// 获取配置文件中的错误日志输出元素
|
||||
@ -171,27 +183,26 @@ bool XNEngine::SetLogLevel(const std::string &XmlPath)
|
||||
&& (strcmp(errorConsoleOutput, "true") == 0 || strcmp(errorConsoleOutput, "1") == 0
|
||||
|| strcmp(errorConsoleOutput, "True") == 0
|
||||
|| strcmp(errorConsoleOutput, "TRUE") == 0)) {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Error, true);
|
||||
isError = true;
|
||||
} else {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Error, false);
|
||||
isError = false;
|
||||
}
|
||||
}
|
||||
// 设置控制台输出
|
||||
SetConsoleOutput(isDebug, isInfo, isWarn, isError);
|
||||
|
||||
// 获取配置文件中的日志元素
|
||||
tinyxml2::XMLElement *logNode = root->FirstChildElement("Log");
|
||||
if (!logNode) {
|
||||
std::cout << "The runtime environment configuration file does not contain "
|
||||
"the Log element, the Log File will record all Log!"
|
||||
<< std::endl;
|
||||
std::cout << "0x1014 配置文件中未找到 Log 元素, 日志文件将记录所有日志!" << std::endl;
|
||||
} else {
|
||||
// 获取配置文件中的调试日志输出元素
|
||||
const char *debugLog = logNode->Attribute("Debug");
|
||||
if (debugLog
|
||||
&& (strcmp(debugLog, "true") == 0 || strcmp(debugLog, "1") == 0
|
||||
|| strcmp(debugLog, "True") == 0 || strcmp(debugLog, "TRUE") == 0)) {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Debug, true);
|
||||
isDebug = true;
|
||||
} else {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Debug, false);
|
||||
isDebug = false;
|
||||
}
|
||||
|
||||
// 获取配置文件中的信息日志输出元素
|
||||
@ -199,9 +210,9 @@ bool XNEngine::SetLogLevel(const std::string &XmlPath)
|
||||
if (infoLog
|
||||
&& (strcmp(infoLog, "true") == 0 || strcmp(infoLog, "1") == 0
|
||||
|| strcmp(infoLog, "True") == 0 || strcmp(infoLog, "TRUE") == 0)) {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Info, true);
|
||||
isInfo = true;
|
||||
} else {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Info, false);
|
||||
isInfo = false;
|
||||
}
|
||||
|
||||
// 获取配置文件中的警告日志输出元素
|
||||
@ -209,9 +220,9 @@ bool XNEngine::SetLogLevel(const std::string &XmlPath)
|
||||
if (warningLog
|
||||
&& (strcmp(warningLog, "true") == 0 || strcmp(warningLog, "1") == 0
|
||||
|| strcmp(warningLog, "True") == 0 || strcmp(warningLog, "TRUE") == 0)) {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Warning, true);
|
||||
isWarn = true;
|
||||
} else {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Warning, false);
|
||||
isWarn = false;
|
||||
}
|
||||
|
||||
// 获取配置文件中的错误日志输出元素
|
||||
@ -219,18 +230,73 @@ bool XNEngine::SetLogLevel(const std::string &XmlPath)
|
||||
if (errorLog
|
||||
&& (strcmp(errorLog, "true") == 0 || strcmp(errorLog, "1") == 0
|
||||
|| strcmp(errorLog, "True") == 0 || strcmp(errorLog, "TRUE") == 0)) {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Error, true);
|
||||
isError = true;
|
||||
} else {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Error, false);
|
||||
isError = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置日志级别
|
||||
SetLogLevel(isDebug, isInfo, isWarn, isError);
|
||||
|
||||
// 关闭配置文件
|
||||
file.close();
|
||||
// 返回成功
|
||||
return true;
|
||||
}
|
||||
|
||||
// 设置控制台输出
|
||||
bool XNEngine::SetConsoleOutput(bool isDebug, bool isInfo, bool isWarn, bool isError)
|
||||
{
|
||||
if (isDebug) {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Debug, true);
|
||||
} else {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Debug, false);
|
||||
}
|
||||
if (isInfo) {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Info, true);
|
||||
} else {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Info, false);
|
||||
}
|
||||
if (isWarn) {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Warning, true);
|
||||
} else {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Warning, false);
|
||||
}
|
||||
if (isError) {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Error, true);
|
||||
} else {
|
||||
XNLogger::instance().enableConsoleOutput(XNLogger::LogLevel::Error, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 设置日志级别
|
||||
bool XNEngine::SetLogLevel(bool isDebug, bool isInfo, bool isWarn, bool isError)
|
||||
{
|
||||
if (isDebug) {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Debug, true);
|
||||
} else {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Debug, false);
|
||||
}
|
||||
if (isInfo) {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Info, true);
|
||||
} else {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Info, false);
|
||||
}
|
||||
if (isWarn) {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Warning, true);
|
||||
} else {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Warning, false);
|
||||
}
|
||||
if (isError) {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Error, true);
|
||||
} else {
|
||||
XNLogger::instance().enableFileOutput(XNLogger::LogLevel::Error, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 发布引擎状态
|
||||
void XNEngine::PublishEngineStatus()
|
||||
{
|
||||
@ -332,7 +398,7 @@ void XNEngine::PublishEngineStatus()
|
||||
// 写入引擎状态
|
||||
engineStatusWriter->write(&engineStatus);
|
||||
// 记录调试日志
|
||||
LOG_DEBUG("XNEngine Write DDS!");
|
||||
LOG_DEBUG("引擎运行状态 DDS 写入成功!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,9 +408,9 @@ bool XNEngine::Run(const std::string &XmlPath)
|
||||
if (!framework) {
|
||||
return false;
|
||||
}
|
||||
// 设置日志级别
|
||||
bool isReady = SetLogLevel(XmlPath);
|
||||
// 如果设置日志级别失败
|
||||
// 解析配置文件
|
||||
bool isReady = ParseConfig(XmlPath);
|
||||
// 如果解析配置文件失败
|
||||
if (!isReady) {
|
||||
// 返回失败
|
||||
return false;
|
||||
@ -357,6 +423,7 @@ bool XNEngine::Run(const std::string &XmlPath)
|
||||
bool ret = framework->Initialize(0);
|
||||
// 如果初始化失败
|
||||
if (!ret) {
|
||||
LOG_ERROR("0x1012 初始化失败, 引擎将退出!");
|
||||
// 返回失败
|
||||
return false;
|
||||
}
|
||||
@ -364,17 +431,18 @@ bool XNEngine::Run(const std::string &XmlPath)
|
||||
// 设置框架状态
|
||||
frameworkStatus = XNFrameObjectStatus::Initialized;
|
||||
// 记录信息日志
|
||||
LOG_INFO("XNEngine Initialize Success!");
|
||||
LOG_INFO("引擎初始化成功!");
|
||||
// 如果测试模式
|
||||
if (isTestMode) {
|
||||
// 记录信息日志
|
||||
LOG_INFO("Verification passed!");
|
||||
LOG_INFO("引擎测试通过!");
|
||||
// 返回成功
|
||||
return true;
|
||||
}
|
||||
ret = framework->PrepareForExecute();
|
||||
// 如果准备执行失败
|
||||
if (!ret) {
|
||||
LOG_ERROR("0x1013 准备执行失败, 引擎将退出!");
|
||||
// 返回失败
|
||||
return false;
|
||||
}
|
||||
@ -412,15 +480,197 @@ bool XNEngine::Run(const std::string &XmlPath)
|
||||
}
|
||||
} else {
|
||||
// 记录错误日志
|
||||
LOG_ERROR("0x1007 Failed to prepare for execution, the engine will exit!");
|
||||
LOG_ERROR("0x1014 无法发送引擎运行状态, 引擎将退出!");
|
||||
}
|
||||
|
||||
// 返回成功
|
||||
return true;
|
||||
}
|
||||
|
||||
// 运行引擎
|
||||
bool XNEngine::ParseDataBase(const uint32_t &ConfigId)
|
||||
{
|
||||
// 获取数据库路径
|
||||
std::string dbPath = std::getenv("XNCore");
|
||||
if (dbPath.empty()) {
|
||||
LOG_ERROR("0x1015 未设置XNCore环境变量, 引擎将退出!");
|
||||
return false;
|
||||
}
|
||||
dbPath += "/database/XNSim.db";
|
||||
|
||||
// 打开数据库
|
||||
sqlite3 *db;
|
||||
if (sqlite3_open(dbPath.c_str(), &db) != SQLITE_OK) {
|
||||
LOG_ERROR("0x1016 打开数据库失败: {}", sqlite3_errmsg(db));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 准备SQL语句
|
||||
std::string sql = "SELECT * FROM Configuration WHERE ConfID = ?";
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
LOG_ERROR("0x1017 准备SQL语句失败: {}", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 绑定参数
|
||||
if (sqlite3_bind_int(stmt, 1, ConfigId) != SQLITE_OK) {
|
||||
LOG_ERROR("0x1018 绑定参数失败: {}", sqlite3_errmsg(db));
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
if (sqlite3_step(stmt) != SQLITE_ROW) {
|
||||
LOG_ERROR("0x1019 未找到配置ID为{}的记录", ConfigId);
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
return false;
|
||||
}
|
||||
|
||||
int cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
std::cout << "当前CPU核心数-> " << cpus << std::endl;
|
||||
|
||||
// 设置CPU亲和性
|
||||
cpu_set_t mask;
|
||||
CPU_ZERO(&mask);
|
||||
CPUAffinity = 0;
|
||||
|
||||
// 读取配置信息
|
||||
std::string CPUAff = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 6));
|
||||
|
||||
std::istringstream iss(CPUAff);
|
||||
std::string cpuIndex;
|
||||
while (std::getline(iss, cpuIndex, ',')) {
|
||||
try {
|
||||
int index = std::stoi(cpuIndex);
|
||||
if (index >= 0 && index < cpus) {
|
||||
CPUAffinity |= (1 << index);
|
||||
CPU_SET(index, &mask);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "0x1010 无效的CPU亲和性值: " << cpuIndex << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {
|
||||
std::cerr << "0x1011 设置引擎CPU亲和性失败-> " << strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::cout << "成功设置引擎CPU亲和性-> " << CPUAffinity << std::endl;
|
||||
|
||||
// 锁定内存
|
||||
if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
|
||||
std::cerr << "0x1012 锁定引擎内存失败-> " << strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::cout << "成功锁定引擎内存!" << std::endl;
|
||||
|
||||
auto readBoolean = [](sqlite3_stmt *stmt, int column) -> bool {
|
||||
return sqlite3_column_int(stmt, column) != 0;
|
||||
};
|
||||
|
||||
bool isDebug = readBoolean(stmt, 11);
|
||||
bool isInfo = readBoolean(stmt, 12);
|
||||
bool isWarn = readBoolean(stmt, 13);
|
||||
bool isError = readBoolean(stmt, 14);
|
||||
|
||||
SetConsoleOutput(isDebug, isInfo, isWarn, isError);
|
||||
|
||||
isDebug = readBoolean(stmt, 15);
|
||||
isInfo = readBoolean(stmt, 16);
|
||||
isWarn = readBoolean(stmt, 17);
|
||||
isError = readBoolean(stmt, 18);
|
||||
|
||||
SetLogLevel(isDebug, isInfo, isWarn, isError);
|
||||
|
||||
// 清理资源
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XNEngine::Run(const uint32_t &ConfigId)
|
||||
{
|
||||
if (!framework) {
|
||||
return false;
|
||||
}
|
||||
// 解析数据库
|
||||
bool isReady = ParseDataBase(ConfigId);
|
||||
// 如果解析数据库失败
|
||||
if (!isReady) {
|
||||
// 返回失败
|
||||
return false;
|
||||
}
|
||||
// 设置构型ID
|
||||
//framework->SetScenarioId(ConfigId);
|
||||
// 设置CPU亲和性
|
||||
framework->SetCpuAffinity(CPUAffinity);
|
||||
// 单次触发初始化信号
|
||||
bool ret = framework->Initialize(1);
|
||||
// 如果初始化失败
|
||||
if (!ret) {
|
||||
LOG_ERROR("0x1012 初始化失败, 引擎将退出!");
|
||||
// 返回失败
|
||||
return false;
|
||||
}
|
||||
// 如果初始化成功
|
||||
// 设置框架状态
|
||||
frameworkStatus = XNFrameObjectStatus::Initialized;
|
||||
// 记录信息日志
|
||||
LOG_INFO("引擎初始化成功!");
|
||||
// 如果测试模式
|
||||
if (isTestMode) {
|
||||
// 记录信息日志
|
||||
LOG_INFO("引擎测试通过!");
|
||||
// 返回成功
|
||||
return true;
|
||||
}
|
||||
ret = framework->PrepareForExecute();
|
||||
// 如果准备执行失败
|
||||
if (!ret) {
|
||||
LOG_ERROR("0x1013 准备执行失败, 引擎将退出!");
|
||||
// 返回失败
|
||||
return false;
|
||||
}
|
||||
// 设置框架状态
|
||||
frameworkStatus = XNFrameObjectStatus::Ready;
|
||||
// 获取DDS管理器
|
||||
XNDDSManagerPtr ddsManager = framework->GetDDSManager();
|
||||
// 如果DDS管理器存在
|
||||
if (ddsManager) {
|
||||
// 注册仿真控制订阅者
|
||||
auto func = std::bind(&XNEngine::SimControlListener, this, std::placeholders::_1);
|
||||
ddsManager->RegisterSubscriber<XNSim::XNSimControl::XNRuntimeControlPubSubType>(
|
||||
"XNSim::XNSimControl::XNRuntimeControl", 0, func);
|
||||
// 触发仿真控制信号
|
||||
framework->SimControl(0, SimControlCmd::Start);
|
||||
// 注册引擎状态发布者
|
||||
engineStatusWriter =
|
||||
ddsManager->RegisterPublisher<XNSim::XNSimStatus::XNEngineStatusPubSubType>(
|
||||
"XNSim::XNSimStatus::XNEngineStatus", 0);
|
||||
// 如果引擎状态写入器存在
|
||||
if (engineStatusWriter) {
|
||||
// 设置引擎运行标志
|
||||
engineRunning = true;
|
||||
while (engineRunning) {
|
||||
// 发布一次初始状态
|
||||
PublishEngineStatus();
|
||||
// 等待1秒
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
if (!engineRunning) {
|
||||
// 取消注册引擎状态发布者
|
||||
//ddsManager->UnregisterPublisher("XNSim::XNSimStatus::XNEngineStatus");
|
||||
//ddsManager->UnregisterSubscriber("XNSim::XNSimControl::XNRuntimeControl");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 记录错误日志
|
||||
LOG_ERROR("0x1014 无法发送引擎运行状态, 引擎将退出!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// 设置测试模式
|
||||
|
@ -27,13 +27,14 @@ public:
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~XNEngine();
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 仿真控制监听器
|
||||
* @param cmd 仿真控制命令
|
||||
*/
|
||||
void SimControlListener(const XNSim::XNSimControl::XNRuntimeControl &cmd);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 运行引擎
|
||||
* @param XmlPath 场景XML路径
|
||||
@ -54,11 +55,37 @@ public:
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 设置日志级别
|
||||
* @param XmlPath 场景XML路径
|
||||
* @brief 解析配置文件
|
||||
* @param XmlPath 配置文件路径
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool SetLogLevel(const std::string &XmlPath);
|
||||
bool ParseConfig(const std::string &XmlPath);
|
||||
|
||||
/**
|
||||
* @brief 解析数据库
|
||||
* @param ConfigId 配置ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool ParseDataBase(const uint32_t &ConfigId);
|
||||
|
||||
/**
|
||||
* @brief 设置控制台输出
|
||||
* @param isDebug 是否输出调试日志
|
||||
* @param isInfo 是否输出信息日志
|
||||
* @param isWarn 是否输出警告日志
|
||||
* @param isError 是否输出错误日志
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool SetConsoleOutput(bool isDebug, bool isInfo, bool isWarn, bool isError);
|
||||
/**
|
||||
* @brief 设置日志级别
|
||||
* @param isDebug 是否输出调试日志
|
||||
* @param isInfo 是否输出信息日志
|
||||
* @param isWarn 是否输出警告日志
|
||||
* @param isError 是否输出错误日志
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool SetLogLevel(bool isDebug, bool isInfo, bool isWarn, bool isError);
|
||||
/**
|
||||
* @brief 发布引擎状态
|
||||
*/
|
||||
|
@ -24,7 +24,7 @@ int main(int argc, char *argv[])
|
||||
XNEngine engine;
|
||||
//检测输入参数个数
|
||||
if (argc <= 2) {
|
||||
std::cerr << "0x1000 The input parameters is too less!" << std::endl;
|
||||
std::cerr << "0x1000 输入参数太少!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -42,10 +42,7 @@ int main(int argc, char *argv[])
|
||||
hasConfigPath = true;
|
||||
i += 2;
|
||||
} else {
|
||||
std::cerr << "0x1004 After the -f parameter, the configuration file path is not "
|
||||
"specified, "
|
||||
"the engine will exit!"
|
||||
<< std::endl;
|
||||
std::cerr << "0x1001 在-f参数后,未指定配置文件路径,引擎将退出!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
} else if ("-id" == arg) {
|
||||
@ -54,52 +51,30 @@ int main(int argc, char *argv[])
|
||||
hasConfigId = true;
|
||||
i += 2;
|
||||
} else {
|
||||
std::cerr
|
||||
<< "0x1005 After the -id parameter, the configuration ID is not specified, "
|
||||
"the engine will exit!"
|
||||
<< std::endl;
|
||||
std::cerr << "0x1002 在-id参数后,未指定配置ID,引擎将退出!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
} else if ("-test" == arg) {
|
||||
engine.SetTestMode(true);
|
||||
i++;
|
||||
} else {
|
||||
std::cerr << "0x1007 The parameter " << arg << " is not valid, the engine will exit!"
|
||||
<< std::endl;
|
||||
std::cerr << "0x1003 无法识别的参数 " << arg << " ,引擎将退出!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
//检查是否同时包含-f和-id参数
|
||||
if (hasConfigPath && hasConfigId) {
|
||||
std::cerr
|
||||
<< "0x1006 Please specify either -f <config_file> or -id <config_id>, but not both. "
|
||||
"The engine will exit!"
|
||||
<< std::endl;
|
||||
std::cerr << "0x1004 请指定 -f <config_file> 或 -id <config_id> ,但不要同时指定!"
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//检测配置文件格式
|
||||
if (hasConfigPath) {
|
||||
size_t index = configPath.find_last_of('.');
|
||||
if (index != std::string::npos) {
|
||||
std::string suffix = configPath.substr(index);
|
||||
if (suffix != ".xml" && suffix != ".sce") {
|
||||
std::cerr << "0x1001 The configuration file is not a .xml or .sce "
|
||||
"file, the engine will exit!"
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "0x1002 The configuration file is not a .xml or .sce "
|
||||
"file, the engine will exit!"
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
return engine.Run(configPath);
|
||||
} else if (hasConfigId) {
|
||||
return engine.Run(configId);
|
||||
}
|
||||
// 引擎运行
|
||||
return engine.Run(configId);
|
||||
return -1;
|
||||
}
|
||||
|
@ -410,7 +410,7 @@ class RunSimulation extends HTMLElement {
|
||||
this.currentSimulationId = Date.now().toString();
|
||||
|
||||
// 准备启动参数
|
||||
const simulationArgs = [selectedScenario];
|
||||
const simulationArgs = ['-f', selectedScenario];
|
||||
|
||||
// 调用后端API执行仿真
|
||||
const response = await fetch('/api/run-simulation', {
|
||||
|
@ -6,7 +6,6 @@ class RunTest extends HTMLElement {
|
||||
this.currentScenario = null;
|
||||
this.modelGroups = [];
|
||||
this.services = [];
|
||||
this.currentSimulationId = null;
|
||||
this.eventSource = null;
|
||||
}
|
||||
|
||||
@ -173,12 +172,6 @@ class RunTest extends HTMLElement {
|
||||
runButton.disabled = true;
|
||||
runButton.textContent = '运行中...';
|
||||
|
||||
// 显示终止按钮
|
||||
const stopButton = this.shadowRoot.querySelector('#stop-button');
|
||||
if (stopButton) {
|
||||
stopButton.style.display = 'block';
|
||||
}
|
||||
|
||||
// 清空并初始化输出框
|
||||
const outputContent = this.shadowRoot.querySelector('#output-content');
|
||||
outputContent.innerHTML = '开始执行测试...\n';
|
||||
@ -189,20 +182,16 @@ class RunTest extends HTMLElement {
|
||||
this.eventSource = null;
|
||||
}
|
||||
|
||||
// 保存当前仿真信息
|
||||
this.currentSimulationId = Date.now().toString();
|
||||
|
||||
// 准备启动参数
|
||||
const simulationArgs = [selectedScenario, '-test'];
|
||||
const simulationArgs = ['-f', selectedScenario, '-test'];
|
||||
|
||||
// 调用后端API执行仿真
|
||||
// 调用后端API执行测试
|
||||
const response = await fetch('/api/run-simulation', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: this.currentSimulationId,
|
||||
args: simulationArgs,
|
||||
timeout: 120000 // 2分钟超时
|
||||
})
|
||||
@ -218,14 +207,15 @@ class RunTest extends HTMLElement {
|
||||
|
||||
const responseData = await response.json();
|
||||
|
||||
// 获取仿真ID
|
||||
const simulationId = responseData.simulationId || this.currentSimulationId;
|
||||
this.currentSimulationId = simulationId;
|
||||
// 连接到SSE获取实时输出
|
||||
this.connectToEventSource(responseData.simulationId);
|
||||
|
||||
// 设置SSE连接获取实时输出
|
||||
this.connectToEventSource(simulationId);
|
||||
|
||||
this.showSuccess(`测试已启动`);
|
||||
// 根据测试结果更新UI
|
||||
if (responseData.success) {
|
||||
this.showSuccess('测试已启动');
|
||||
} else {
|
||||
this.showError(`测试启动失败: ${responseData.message || '未知错误'}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('执行测试失败:', error);
|
||||
@ -234,16 +224,20 @@ class RunTest extends HTMLElement {
|
||||
// 显示错误详情
|
||||
const outputContent = this.shadowRoot.querySelector('#output-content');
|
||||
outputContent.innerHTML += `\n\n执行错误: ${error.message}`;
|
||||
|
||||
} finally {
|
||||
// 重置UI
|
||||
this.resetUIAfterCompletion();
|
||||
const runButton = this.shadowRoot.querySelector('#run-button');
|
||||
runButton.disabled = false;
|
||||
runButton.textContent = '运行测试';
|
||||
}
|
||||
}
|
||||
|
||||
// 连接到SSE事件源获取实时输出
|
||||
connectToEventSource(simulationId) {
|
||||
// 关闭之前的连接
|
||||
this.closeEventSource();
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
}
|
||||
|
||||
// 创建新的SSE连接
|
||||
const url = `/api/simulation-output/${simulationId}`;
|
||||
@ -274,11 +268,9 @@ class RunTest extends HTMLElement {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
if (data.running === false) {
|
||||
// 仿真已经不存在或已结束
|
||||
this.showMessage(data.message || '测试不存在或已结束');
|
||||
|
||||
// 重置UI
|
||||
this.resetUIAfterCompletion();
|
||||
// 测试已经结束
|
||||
this.showMessage(data.message || '测试已结束');
|
||||
this.closeEventSource();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('处理状态事件失败:', error);
|
||||
@ -296,8 +288,7 @@ class RunTest extends HTMLElement {
|
||||
this.showError(`测试执行失败: ${data.message}`);
|
||||
}
|
||||
|
||||
// 重置UI
|
||||
this.resetUIAfterCompletion();
|
||||
this.closeEventSource();
|
||||
} catch (error) {
|
||||
console.error('处理完成事件失败:', error);
|
||||
}
|
||||
@ -308,59 +299,17 @@ class RunTest extends HTMLElement {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
this.showError(`测试错误: ${data.message}`);
|
||||
|
||||
// 重置UI
|
||||
this.resetUIAfterCompletion();
|
||||
this.closeEventSource();
|
||||
} catch (error) {
|
||||
console.error('处理错误事件失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 仿真终止
|
||||
this.eventSource.addEventListener('terminated', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
this.showMessage(`测试已终止: ${data.message}`);
|
||||
|
||||
// 在输出框中添加终止信息
|
||||
const outputContent = this.shadowRoot.querySelector('#output-content');
|
||||
outputContent.innerHTML += `\n\n测试已终止:${data.message}`;
|
||||
|
||||
// 重置UI
|
||||
this.resetUIAfterCompletion();
|
||||
} catch (error) {
|
||||
console.error('处理终止事件失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 仿真超时
|
||||
this.eventSource.addEventListener('timeout', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
this.showError(`测试超时: ${data.message}`);
|
||||
|
||||
// 在输出框中添加超时信息
|
||||
const outputContent = this.shadowRoot.querySelector('#output-content');
|
||||
outputContent.innerHTML += `\n\n测试超时:${data.message}`;
|
||||
|
||||
// 重置UI
|
||||
this.resetUIAfterCompletion();
|
||||
} catch (error) {
|
||||
console.error('处理超时事件失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 连接错误
|
||||
// 连接错误处理
|
||||
this.eventSource.onerror = (error) => {
|
||||
console.error('SSE连接错误:', error);
|
||||
|
||||
// 检查是否已经清理了资源
|
||||
if (this.eventSource && this.eventSource.readyState === 2) { // CLOSED
|
||||
this.showError('实时输出连接已断开');
|
||||
|
||||
// 重置UI
|
||||
this.resetUIAfterCompletion();
|
||||
}
|
||||
this.showError('实时输出连接已断开');
|
||||
this.closeEventSource();
|
||||
};
|
||||
}
|
||||
|
||||
@ -372,69 +321,6 @@ class RunTest extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
// 在仿真完成后重置UI
|
||||
resetUIAfterCompletion() {
|
||||
// 隐藏终止按钮
|
||||
const stopButton = this.shadowRoot.querySelector('#stop-button');
|
||||
if (stopButton) {
|
||||
stopButton.style.display = 'none';
|
||||
}
|
||||
|
||||
// 重置运行按钮
|
||||
const runButton = this.shadowRoot.querySelector('#run-button');
|
||||
if (runButton) {
|
||||
runButton.disabled = false;
|
||||
runButton.textContent = '运行测试';
|
||||
}
|
||||
|
||||
// 关闭SSE连接
|
||||
this.closeEventSource();
|
||||
|
||||
// 清除当前仿真ID
|
||||
this.currentSimulationId = null;
|
||||
}
|
||||
|
||||
async stopSimulation() {
|
||||
if (!this.currentSimulationId) {
|
||||
this.showMessage('没有正在运行的测试');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.showMessage('正在终止测试...');
|
||||
|
||||
const response = await fetch('/api/stop-simulation', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: this.currentSimulationId
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
this.showSuccess(`${result.message || '测试已终止'}`);
|
||||
|
||||
// 终止信息会通过SSE推送到UI
|
||||
// 此处不需要额外操作
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
this.showError(`终止测试失败: ${errorData.message || '未知错误'}`);
|
||||
|
||||
// 可能是SSE已断开,手动重置UI
|
||||
this.resetUIAfterCompletion();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('终止测试失败:', error);
|
||||
this.showError('终止测试失败: ' + error.message);
|
||||
|
||||
// 手动重置UI
|
||||
this.resetUIAfterCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
const messageElement = this.shadowRoot.querySelector('#message');
|
||||
messageElement.textContent = message;
|
||||
@ -515,16 +401,6 @@ class RunTest extends HTMLElement {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
#stop-button {
|
||||
background-color: #d32f2f;
|
||||
display: none;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#stop-button:hover {
|
||||
background-color: #b71c1c;
|
||||
}
|
||||
|
||||
#message {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
@ -645,10 +521,6 @@ class RunTest extends HTMLElement {
|
||||
.output-content .ansi-bold { font-weight: bold; }
|
||||
.output-content .ansi-italic { font-style: italic; }
|
||||
.output-content .ansi-underline { text-decoration: underline; }
|
||||
|
||||
.buttons-container {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
<div class="test-container">
|
||||
<div class="config-selector">
|
||||
@ -656,10 +528,7 @@ class RunTest extends HTMLElement {
|
||||
<select id="scenario-select">
|
||||
<option value="" disabled selected>-- 加载配置文件中... --</option>
|
||||
</select>
|
||||
<div class="buttons-container">
|
||||
<button id="run-button">运行测试</button>
|
||||
<button id="stop-button">终止测试</button>
|
||||
</div>
|
||||
<button id="run-button">运行测试</button>
|
||||
</div>
|
||||
<div id="message"></div>
|
||||
<div class="content-container">
|
||||
@ -685,9 +554,6 @@ class RunTest extends HTMLElement {
|
||||
const runButton = this.shadowRoot.querySelector('#run-button');
|
||||
runButton.addEventListener('click', () => this.runTest());
|
||||
|
||||
const stopButton = this.shadowRoot.querySelector('#stop-button');
|
||||
stopButton.addEventListener('click', () => this.stopSimulation());
|
||||
|
||||
const scenarioSelect = this.shadowRoot.querySelector('#scenario-select');
|
||||
scenarioSelect.addEventListener('change', (event) => this.onScenarioSelected(event));
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ router.post('/run-simulation', async (req, res) => {
|
||||
const logFile = path.join(logDir, `xnengine_${mainProcess.pid}.log`);
|
||||
|
||||
// 从命令行参数中提取配置文件路径
|
||||
const scenarioFile = args[0];
|
||||
const scenarioFile = args[1];
|
||||
|
||||
// 将进程信息写入数据库
|
||||
await saveXNEngineProcess({
|
||||
@ -261,7 +261,7 @@ router.post('/run-simulation', async (req, res) => {
|
||||
isExisting: true,
|
||||
startTime: mainProcess.startTime,
|
||||
totalProcesses: sortedProcesses.length,
|
||||
scenarioFile: (existingProcess && existingProcess.scenario_file) || args[0]
|
||||
scenarioFile: (existingProcess && existingProcess.scenario_file) || args[1]
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
@ -313,14 +313,17 @@ router.post('/run-simulation', async (req, res) => {
|
||||
const isRunning = await isProcessRunning(processId);
|
||||
const isXNEngine = await isXNEngineProcess(processId);
|
||||
|
||||
if (isRunning && isXNEngine) {
|
||||
// 检查是否是测试模式
|
||||
const isTestMode = args.includes('-test');
|
||||
|
||||
if ((isRunning && isXNEngine) || isTestMode) {
|
||||
// 将进程信息写入数据库
|
||||
await saveXNEngineProcess({
|
||||
pid: processId,
|
||||
log_file: logFile,
|
||||
start_time: new Date().toISOString(),
|
||||
cmd: `${enginePath} ${args.join(' ')}`,
|
||||
scenario_file: args[0]
|
||||
scenario_file: args[1]
|
||||
});
|
||||
|
||||
// 保存到运行中的仿真Map,但不启动tail进程
|
||||
@ -335,15 +338,17 @@ router.post('/run-simulation', async (req, res) => {
|
||||
// 立即响应,返回仿真ID
|
||||
res.json({
|
||||
success: true,
|
||||
message: '仿真已启动',
|
||||
message: isTestMode ? '测试已启动' : '仿真已启动',
|
||||
simulationId: processId.toString(),
|
||||
scenarioFile: args[0]
|
||||
scenarioFile: args[1],
|
||||
isTestMode: isTestMode,
|
||||
logFile: logFile
|
||||
});
|
||||
} else {
|
||||
// 进程启动失败或不是XNEngine进程
|
||||
await deleteXNEngineProcess(processId);
|
||||
res.status(500).json({
|
||||
error: '启动仿真失败',
|
||||
error: '启动失败',
|
||||
message: '进程启动后立即退出或不是XNEngine进程'
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user