diff --git a/Release/database/XNSim.db b/Release/database/XNSim.db index 453ad3c..9440b07 100644 Binary files a/Release/database/XNSim.db and b/Release/database/XNSim.db differ diff --git a/XNEngine/CMakeLists.txt b/XNEngine/CMakeLists.txt index 11ec6ae..414e3a4 100755 --- a/XNEngine/CMakeLists.txt +++ b/XNEngine/CMakeLists.txt @@ -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 ) diff --git a/XNEngine/XNEngine.cpp b/XNEngine/XNEngine.cpp index 37a7b2b..e28c70c 100755 --- a/XNEngine/XNEngine.cpp +++ b/XNEngine/XNEngine.cpp @@ -16,6 +16,7 @@ #include "../XNCore/XNServiceManager.h" #include "../XNCore/XNModelManager.h" #include "../XNCore/XNScenarioManager.h" +#include // 引擎类构造函数 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(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::XNRuntimeControl", 0, func); + // 触发仿真控制信号 + framework->SimControl(0, SimControlCmd::Start); + // 注册引擎状态发布者 + engineStatusWriter = + ddsManager->RegisterPublisher( + "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; } // 设置测试模式 diff --git a/XNEngine/XNEngine.h b/XNEngine/XNEngine.h index e026c7e..8c0d66c 100755 --- a/XNEngine/XNEngine.h +++ b/XNEngine/XNEngine.h @@ -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 发布引擎状态 */ diff --git a/XNEngine/main.cpp b/XNEngine/main.cpp index 35c2160..d4acff3 100755 --- a/XNEngine/main.cpp +++ b/XNEngine/main.cpp @@ -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 or -id , but not both. " - "The engine will exit!" - << std::endl; + std::cerr << "0x1004 请指定 -f 或 -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; } diff --git a/XNSimHtml/components/run-simulation.js b/XNSimHtml/components/run-simulation.js index 9e83259..f5228d7 100644 --- a/XNSimHtml/components/run-simulation.js +++ b/XNSimHtml/components/run-simulation.js @@ -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', { diff --git a/XNSimHtml/components/run-test.js b/XNSimHtml/components/run-test.js index 50606e9..38edc87 100644 --- a/XNSimHtml/components/run-test.js +++ b/XNSimHtml/components/run-test.js @@ -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(); }; } @@ -371,69 +320,6 @@ class RunTest extends HTMLElement { this.eventSource = null; } } - - // 在仿真完成后重置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'); @@ -514,16 +400,6 @@ class RunTest extends HTMLElement { background-color: #cccccc; cursor: not-allowed; } - - #stop-button { - background-color: #d32f2f; - display: none; - margin-left: 10px; - } - - #stop-button:hover { - background-color: #b71c1c; - } #message { margin: 10px 0; @@ -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; - }
@@ -656,10 +528,7 @@ class RunTest extends HTMLElement { -
- - -
+
@@ -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)); } diff --git a/XNSimHtml/routes/run-simulation.js b/XNSimHtml/routes/run-simulation.js index 32de16f..fdb9bc4 100644 --- a/XNSimHtml/routes/run-simulation.js +++ b/XNSimHtml/routes/run-simulation.js @@ -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进程' }); }