From 922f73adb5684412ba2766b05941ebd146269c67 Mon Sep 17 00:00:00 2001 From: lgv Date: Tue, 30 Jun 2026 16:02:28 +0800 Subject: [PATCH] refactor(runtime): add shared application runtime --- CMakeLists.txt | 3 +- cmvr-es/CMakeLists.txt | 4 +- cmvr-es/common/base/protobuf_utils.h | 24 +++ cmvr-es/common/base/string_utils.h | 25 ++++ cmvr-es/main.cpp | 140 +++--------------- cmvr-es/runtime/CMakeLists.txt | 19 +++ cmvr-es/runtime/include/cmvr_runtime.h | 47 ++++++ cmvr-es/runtime/src/cmvr_runtime.cpp | 195 +++++++++++++++++++++++++ 8 files changed, 335 insertions(+), 122 deletions(-) create mode 100644 cmvr-es/common/base/protobuf_utils.h create mode 100644 cmvr-es/common/base/string_utils.h create mode 100644 cmvr-es/runtime/CMakeLists.txt create mode 100644 cmvr-es/runtime/include/cmvr_runtime.h create mode 100644 cmvr-es/runtime/src/cmvr_runtime.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e12aaf6c..ca7498b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,7 @@ add_subdirectory(cmvr-es) add_executable(cmvr_es cmvr-es/main.cpp) target_include_directories(cmvr_es PRIVATE ${GLOG_INCLUDE_DIRS}) target_link_libraries(cmvr_es PRIVATE + cmvr_es::runtime cmvr_es::proto cmvr_es::logging service @@ -113,7 +114,7 @@ target_link_libraries(cmvr_es PRIVATE cmvr_es::service cmvr_es::hardware cmvr_es::device::canbus - cmvr_es::device::ti5motor + cmvr_es::device::ti5_canopen_motor_driver cmvr_es::algorithms::controller cmvr_es::ik_solver cmvr_es::base_motion diff --git a/cmvr-es/CMakeLists.txt b/cmvr-es/CMakeLists.txt index eb16b40f..012c09a4 100644 --- a/cmvr-es/CMakeLists.txt +++ b/cmvr-es/CMakeLists.txt @@ -4,9 +4,11 @@ link_libraries(cmvr_es::logging) add_subdirectory(common) add_subdirectory(hardware) add_subdirectory(algorithms) +add_subdirectory(simulate) add_subdirectory(devices) add_subdirectory(manager/device_manager) add_subdirectory(task) add_subdirectory(manager/task_manager) add_subdirectory(service) -add_subdirectory(simulate) +add_subdirectory(runtime) +add_subdirectory(test) diff --git a/cmvr-es/common/base/protobuf_utils.h b/cmvr-es/common/base/protobuf_utils.h new file mode 100644 index 00000000..2e19e126 --- /dev/null +++ b/cmvr-es/common/base/protobuf_utils.h @@ -0,0 +1,24 @@ +#ifndef CMVR_ES_PROTOBUF_UTILS_H +#define CMVR_ES_PROTOBUF_UTILS_H + +#include + +#include + +namespace cmvr::common { + +template +std::vector repeatedToVector(const google::protobuf::RepeatedPtrField& values) +{ + return {values.begin(), values.end()}; +} + +template +std::vector repeatedToVector(const google::protobuf::RepeatedField& values) +{ + return {values.begin(), values.end()}; +} + +} // namespace cmvr::common + +#endif // CMVR_ES_PROTOBUF_UTILS_H diff --git a/cmvr-es/common/base/string_utils.h b/cmvr-es/common/base/string_utils.h new file mode 100644 index 00000000..79fe46bb --- /dev/null +++ b/cmvr-es/common/base/string_utils.h @@ -0,0 +1,25 @@ +#ifndef CMVR_ES_STRING_UTILS_H +#define CMVR_ES_STRING_UTILS_H + +#include +#include +#include + +namespace cmvr::common { + +inline std::string joinStrings(const std::vector& values, + const std::string& separator = ", ") +{ + std::ostringstream oss; + for (std::size_t i = 0; i < values.size(); ++i) { + if (i > 0) { + oss << separator; + } + oss << values[i]; + } + return oss.str(); +} + +} // namespace cmvr::common + +#endif // CMVR_ES_STRING_UTILS_H diff --git a/cmvr-es/main.cpp b/cmvr-es/main.cpp index 4affd835..3e46bbfa 100644 --- a/cmvr-es/main.cpp +++ b/cmvr-es/main.cpp @@ -1,149 +1,49 @@ -// -// Created by xtkuang on 2025/5/6. -// - -#include -#include #include -#include #include + #include "common/base/logging/logger.h" -#include "cmvr/config/cmvr_es_config/cmvr_es_config.pb.h" -#include "cmvr/config/device_manager_config/device_manager_config.pb.h" -#include "cmvr/config/task_manager_config/task_manager_config.pb.h" -#include "common/config/config_files.h" -#include "common/io/proto_file_io.h" -#include "manager/device_manager/include/device_manager.h" -#include "manager/task_manager/include/task_manager.h" -#include "task/grpc_server_task/include/grpc_server_task.h" +#include "runtime/include/cmvr_runtime.h" namespace { -void logSection(const char* title) +bool blockShutdownSignals(sigset_t& shutdown_signals) { - CMVR_LOG(INFO) << "---------------- " << title << " ----------------"; -} - -bool executableDirectory(std::filesystem::path& directory) -{ - char executable_path[PATH_MAX] = {0}; - const ssize_t count = readlink("/proc/self/exe", executable_path, PATH_MAX - 1); - if (count < 0) { - return false; - } - executable_path[count] = '\0'; - directory = std::filesystem::path(executable_path).parent_path(); - return true; + sigemptyset(&shutdown_signals); + sigaddset(&shutdown_signals, SIGINT); + sigaddset(&shutdown_signals, SIGTERM); + return pthread_sigmask(SIG_BLOCK, &shutdown_signals, nullptr) == 0; } } // namespace -int main(int argc, char* argv[]) +int main() { - std::filesystem::path executable_dir; - if (!executableDirectory(executable_dir)) { - CMVR_LOG(ERROR) << "Failed to read /proc/self/exe"; - return 1; - } - - std::string config_path; - if (argc == 1) { - config_path = (executable_dir / "config/cmvr_es.pb.txt").string(); - CMVR_LOG(INFO) << "Using default config path: " << config_path; - } else if (argc == 2) { - config_path = argv[1]; - } else { - CMVR_LOG(ERROR) << "Usage: " << argv[0] << " [cmvr_es.pb.txt]"; - return 1; - } - - cmvr::ConfigHelper::setConfigRootFromFile(config_path); - cmvr::config::CMVRESRootConfig root_config; - if (!ProtoMessageIo::getProtoFromAsciiFile(config_path, &root_config)) { - CMVR_LOG(ERROR) << "Failed to load config: " << config_path; - return 1; - } - - const auto& app_config = root_config.cmvr_es(); - if (app_config.logger_config_file().empty()) { - CMVR_LOG(ERROR) << "Logger config file is empty"; - return 1; - } - - cmvr::config::LoggerRootConfig logger_root; - if (!cmvr::ConfigHelper::loadConfigFile(app_config.logger_config_file(), logger_root)) { - CMVR_LOG(ERROR) << "Failed to load logger config: " << app_config.logger_config_file(); - return 1; - } - if (!cmvr::logging::initLogging(logger_root.logger(), "cmvr_es", executable_dir)) { - CMVR_LOG(FATAL) << "Failed to initialize logging"; - return 1; - } - - logSection("Startup"); - CMVR_LOG(INFO) << "Logging initialized"; - CMVR_LOG(INFO) << "[Startup] Runtime config file: " << config_path; - CMVR_LOG(INFO) << "[Startup] Logger config file: " - << cmvr::ConfigHelper::resolveConfigFile(app_config.logger_config_file()); sigset_t shutdown_signals; - sigemptyset(&shutdown_signals); - sigaddset(&shutdown_signals, SIGINT); - sigaddset(&shutdown_signals, SIGTERM); - if (pthread_sigmask(SIG_BLOCK, &shutdown_signals, nullptr) != 0) { - CMVR_LOG(ERROR) << "Failed to block shutdown signals"; - cmvr::logging::shutdownLogging(); + if (!blockShutdownSignals(shutdown_signals)) { return 1; } - if (app_config.device_manager_config_file().empty()) { - CMVR_LOG(ERROR) << "DeviceManager config file is empty"; - cmvr::logging::shutdownLogging(); + cmvr::Runtime runtime; + if (!runtime.init()) { return 1; } - logSection("DeviceManager"); - CMVR_LOG(INFO) << "[Startup] Load DeviceManager config: " - << cmvr::ConfigHelper::resolveConfigFile(app_config.device_manager_config_file()); - cmvr::config::DeviceManagerRootConfig device_manager_root; - if (!cmvr::ConfigHelper::loadConfigFile(app_config.device_manager_config_file(), device_manager_root)) { - CMVR_LOG(ERROR) << "Failed to load DeviceManager config: " << app_config.device_manager_config_file(); - cmvr::logging::shutdownLogging(); - return 1; - } - CMVR_LOG(INFO) << "[Startup] Initialize DeviceManager"; - cmvr::device::DeviceManager::getInstance(device_manager_root.device_manager()); - - cmvr::task::registerGrpcServerTaskFactory(); - - if (app_config.task_manager_config_file().empty()) { - CMVR_LOG(ERROR) << "TaskManager config file is empty"; - cmvr::logging::shutdownLogging(); - return 1; - } - logSection("TaskManager"); - CMVR_LOG(INFO) << "[Startup] Load TaskManager config: " - << cmvr::ConfigHelper::resolveConfigFile(app_config.task_manager_config_file()); - cmvr::config::TaskManagerRootConfig task_manager_root; - if (!cmvr::ConfigHelper::loadConfigFile(app_config.task_manager_config_file(), task_manager_root)) { - CMVR_LOG(ERROR) << "Failed to load TaskManager config: " << app_config.task_manager_config_file(); - cmvr::logging::shutdownLogging(); + if (!runtime.startTasks()) { return 1; } - CMVR_LOG(INFO) << "[Startup] Initialize TaskManager"; - auto& task_manager = cmvr::task::TaskManager::getInstance(task_manager_root.task_manager()); - logSection("Start Tasks"); - CMVR_LOG(INFO) << "[Startup] Start tasks"; - task_manager.startRunTask(); + if (auto viewer = runtime.mujocoViewer("mujoco_viewer")) { + CMVR_LOG(INFO) << "[Startup] Run MuJoCo viewer on main thread"; + viewer->runOnMainThread(); + runtime.shutdown(); + return 0; + } int received_signal = 0; if (sigwait(&shutdown_signals, &received_signal) != 0) { - CMVR_LOG(ERROR) << "Failed to wait for shutdown signal"; - task_manager.stopRunTask(); - cmvr::logging::shutdownLogging(); + runtime.shutdown(); return 1; } - task_manager.stopRunTask(); - cmvr::logging::shutdownLogging(); + runtime.shutdown(); return 0; } diff --git a/cmvr-es/runtime/CMakeLists.txt b/cmvr-es/runtime/CMakeLists.txt new file mode 100644 index 00000000..e03b852a --- /dev/null +++ b/cmvr-es/runtime/CMakeLists.txt @@ -0,0 +1,19 @@ +add_library(cmvr_runtime STATIC + src/cmvr_runtime.cpp +) + +target_include_directories(cmvr_runtime PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +target_link_libraries(cmvr_runtime PUBLIC + cmvr_es::proto + cmvr_es::common + cmvr_es::device_manager + cmvr_es::task_manager + cmvr_es::service + cmvr_es::mujoco_viewer +) + +add_library(cmvr_es::runtime ALIAS cmvr_runtime) +install(TARGETS cmvr_runtime LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) diff --git a/cmvr-es/runtime/include/cmvr_runtime.h b/cmvr-es/runtime/include/cmvr_runtime.h new file mode 100644 index 00000000..d1cef467 --- /dev/null +++ b/cmvr-es/runtime/include/cmvr_runtime.h @@ -0,0 +1,47 @@ +#ifndef CMVR_ES_RUNTIME_CMVR_RUNTIME_H +#define CMVR_ES_RUNTIME_CMVR_RUNTIME_H + +#include +#include +#include + +#include "manager/device_manager/include/device_manager.h" +#include "manager/task_manager/include/task_manager.h" +#include "simulate/mujoco/mujoco_viewer/include/mujoco_viewer.h" + +namespace cmvr { + +class Runtime { +public: + Runtime() = default; + ~Runtime(); + + Runtime(const Runtime&) = delete; + Runtime& operator=(const Runtime&) = delete; + + bool init(); + bool init(const std::string& config_path); + bool startTasks(double control_period_s = 0.001); + void stopTasks(); + void shutdown(); + + bool initialized() const { return initialized_; } + bool tasksStarted() const { return tasks_started_; } + + device::DeviceManager& deviceManager(); + task::TaskManager& taskManager(); + std::shared_ptr mujocoViewer(const std::string& id = "mujoco_viewer"); + +private: + bool init_(const std::string& config_path, + const std::filesystem::path& executable_directory); + bool executableDirectory_(std::filesystem::path& directory) const; + + bool initialized_{false}; + bool tasks_started_{false}; + bool logging_initialized_{false}; +}; + +} // namespace cmvr + +#endif // CMVR_ES_RUNTIME_CMVR_RUNTIME_H diff --git a/cmvr-es/runtime/src/cmvr_runtime.cpp b/cmvr-es/runtime/src/cmvr_runtime.cpp new file mode 100644 index 00000000..24cf7e2e --- /dev/null +++ b/cmvr-es/runtime/src/cmvr_runtime.cpp @@ -0,0 +1,195 @@ +#include "runtime/include/cmvr_runtime.h" + +#include +#include + +#include "cmvr/config/cmvr_es_config/cmvr_es_config.pb.h" +#include "cmvr/config/device_manager_config/device_manager_config.pb.h" +#include "cmvr/config/logger_config/logger_config.pb.h" +#include "cmvr/config/task_manager_config/task_manager_config.pb.h" +#include "common/base/logging/logger.h" +#include "common/config/config_files.h" +#include "common/io/proto_file_io.h" +#include "task/grpc_server_task/include/grpc_server_task.h" + +namespace cmvr { +namespace { + +void logSection(const char* title) +{ + CMVR_LOG(INFO) << "---------------- " << title << " ----------------"; +} + +} // namespace + +Runtime::~Runtime() +{ + shutdown(); +} + +bool Runtime::init() +{ + std::filesystem::path executable_directory; + if (!executableDirectory_(executable_directory)) { + return false; + } + return init_((executable_directory / "config/cmvr_es.pb.txt").string(), executable_directory); +} + +bool Runtime::init(const std::string& config_path) +{ + std::filesystem::path executable_directory; + if (!executableDirectory_(executable_directory)) { + return false; + } + return init_(config_path, executable_directory); +} + +bool Runtime::init_(const std::string& config_path, + const std::filesystem::path& executable_directory) +{ + if (initialized_) { + return true; + } + if (config_path.empty()) { + return false; + } + + ConfigHelper::setConfigRootFromFile(config_path); + config::CMVRESRootConfig root_config; + if (!ProtoMessageIo::getProtoFromAsciiFile(config_path, &root_config)) { + return false; + } + + const auto& app_config = root_config.cmvr_es(); + if (app_config.logger_config_file().empty()) { + return false; + } + + config::LoggerRootConfig logger_root; + if (!ConfigHelper::loadConfigFile(app_config.logger_config_file(), logger_root)) { + return false; + } + + if (executable_directory.empty()) { + return false; + } + if (!logging::initLogging(logger_root.logger(), "cmvr_es", executable_directory)) { + return false; + } + logging_initialized_ = true; + + logSection("Startup"); + CMVR_LOG(INFO) << "Logging initialized"; + CMVR_LOG(INFO) << "[Startup] Runtime config file: " << config_path; + CMVR_LOG(INFO) << "[Startup] Logger config file: " + << ConfigHelper::resolveConfigFile(app_config.logger_config_file()); + + if (app_config.device_manager_config_file().empty()) { + CMVR_LOG(ERROR) << "DeviceManager config file is empty"; + return false; + } + logSection("DeviceManager"); + CMVR_LOG(INFO) << "[Startup] Load DeviceManager config: " + << ConfigHelper::resolveConfigFile(app_config.device_manager_config_file()); + config::DeviceManagerRootConfig device_manager_root; + if (!ConfigHelper::loadConfigFile(app_config.device_manager_config_file(), device_manager_root)) { + CMVR_LOG(ERROR) << "Failed to load DeviceManager config: " + << app_config.device_manager_config_file(); + return false; + } + CMVR_LOG(INFO) << "[Startup] Initialize DeviceManager"; + device::DeviceManager::getInstance(device_manager_root.device_manager()); + + task::registerGrpcServerTaskFactory(); + + if (app_config.task_manager_config_file().empty()) { + CMVR_LOG(ERROR) << "TaskManager config file is empty"; + return false; + } + logSection("TaskManager"); + CMVR_LOG(INFO) << "[Startup] Load TaskManager config: " + << ConfigHelper::resolveConfigFile(app_config.task_manager_config_file()); + config::TaskManagerRootConfig task_manager_root; + if (!ConfigHelper::loadConfigFile(app_config.task_manager_config_file(), task_manager_root)) { + CMVR_LOG(ERROR) << "Failed to load TaskManager config: " + << app_config.task_manager_config_file(); + return false; + } + + CMVR_LOG(INFO) << "[Startup] Initialize TaskManager"; + task::TaskManager::getInstance(task_manager_root.task_manager()); + initialized_ = true; + return true; +} + +bool Runtime::startTasks(const double control_period_s) +{ + if (!initialized_) { + return false; + } + if (tasks_started_) { + return true; + } + + logSection("Start Tasks"); + CMVR_LOG(INFO) << "[Startup] Start tasks"; + task::TaskManager::getInstance().startRunTask(control_period_s); + tasks_started_ = true; + return true; +} + +void Runtime::stopTasks() +{ + if (!tasks_started_) { + return; + } + task::TaskManager::getInstance().stopRunTask(); + tasks_started_ = false; +} + +void Runtime::shutdown() +{ + if (tasks_started_) { + stopTasks(); + } + if (initialized_) { + device::DeviceManager::getInstance().stop(); + task::TaskManager::destroyInstance(); + device::DeviceManager::destroyInstance(); + initialized_ = false; + } + if (logging_initialized_) { + logging::shutdownLogging(); + logging_initialized_ = false; + } +} + +device::DeviceManager& Runtime::deviceManager() +{ + return device::DeviceManager::getInstance(); +} + +task::TaskManager& Runtime::taskManager() +{ + return task::TaskManager::getInstance(); +} + +std::shared_ptr Runtime::mujocoViewer(const std::string& id) +{ + return device::DeviceManager::getInstance().getDevice(id); +} + +bool Runtime::executableDirectory_(std::filesystem::path& directory) const +{ + char executable_path[PATH_MAX] = {0}; + const ssize_t count = readlink("/proc/self/exe", executable_path, PATH_MAX - 1); + if (count < 0) { + return false; + } + executable_path[count] = '\0'; + directory = std::filesystem::path(executable_path).parent_path(); + return true; +} + +} // namespace cmvr