diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fb44455..5aae23d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ set(lithium_src_dir ${Lithium_PROJECT_ROOT_DIR}/src) set(lithium_module_dir ${lithium_src_dir}/atom) set(lithium_client_dir ${lithium_src_dir}/client) set(lithium_component_dir ${lithium_src_dir}/addon) +set(lithium_task_dir ${lithium_src_dir}/task) add_custom_target(CmakeAdditionalFiles SOURCES @@ -160,10 +161,14 @@ set(script_module ) set(task_module - #${lithium_src_dir}/task/task_manager.cpp - #${lithium_src_dir}/task/task_stack.cpp - #${lithium_src_dir}/task/task_generator.cpp - #${lithium_src_dir}/task/task_container.cpp + ${lithium_task_dir}/manager.cpp + ${lithium_task_dir}/generator.cpp + ${lithium_task_dir}/container.cpp + ${lithium_task_dir}/tick.cpp + ${lithium_task_dir}/loader.cpp + ${lithium_task_dir}/list.cpp + ${lithium_task_dir}/pool.cpp + # ${lithium_task_dir}/chekcer.cpp ) set(Lithium_module @@ -214,12 +219,6 @@ include_directories(${CMAKE_SOURCE_DIR}/libs/oatpp) include_directories(${CMAKE_SOURCE_DIR}/libs/oatpp-swagger) include_directories(${CMAKE_SOURCE_DIR}/libs/oatpp-websocket) -# TODO : 更好的构建系统,不需要这样引用 -include_directories(${lithium_src_dir}/) -include_directories(${lithium_src_dir}/core) -include_directories(${lithium_src_dir}/core/base) -include_directories(${lithium_src_dir}/core/property) - # Build all dependencies # 构建所有需要的依赖库 add_subdirectory(libs/) @@ -236,6 +235,8 @@ add_subdirectory(${lithium_module_dir}) include_directories(modules) add_subdirectory(modules) +add_subdirectory(${lithium_client_dir}) + add_executable(lithium_server ${component_module} ${config_module} ${module_module} ${device_module} ${task_module} ${server_module} ${script_module} ${Lithium_module}) diff --git a/locale/lithium.pot b/locale/lithium.pot index 12321985..c376be68 100644 --- a/locale/lithium.pot +++ b/locale/lithium.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: astro_air@126.com\n" -"POT-Creation-Date: 2024-01-26 20:43+0800\n" +"POT-Creation-Date: 2024-02-06 04:56+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: E:/msys64/home/Lithium/src/LithiumApp.cpp:589 +#: E:/msys64/home/Lithium/src/LithiumApp.cpp:549 #, c++-format msgid "Dispatched command {} with result: {}" msgstr "" diff --git a/locale/po/en_US.UTF-8/lithium.po b/locale/po/en_US.UTF-8/lithium.po index 19054fbf..5ee50c90 100644 --- a/locale/po/en_US.UTF-8/lithium.po +++ b/locale/po/en_US.UTF-8/lithium.po @@ -1,4 +1,4 @@ -#: E:/msys64/home/Lithium/src/LithiumApp.cpp:589 +#: E:/msys64/home/Lithium/src/LithiumApp.cpp:549 #, c++-format msgid "Dispatched command {} with result: {}" msgstr "" diff --git a/src/LithiumApp.cpp b/src/LithiumApp.cpp index a550f764..c18211e7 100644 --- a/src/LithiumApp.cpp +++ b/src/LithiumApp.cpp @@ -17,7 +17,6 @@ Description: Lithium App Enter #include "config.h" #include "atom/server/global_ptr.hpp" -#include "atom/driver/iproperty.hpp" #include "atom/log/loguru.hpp" #include "atom/type/json.hpp" @@ -82,16 +81,9 @@ namespace Lithium // Specialized Managers and Threads m_ConfigManager = GetPtr("lithium.config"); m_DeviceManager = GetPtr("lithium.device"); - m_ThreadManager = GetPtr("lithium.async.thread"); m_ProcessManager = GetPtr("lithium.system.process"); m_MessageBus = GetPtr("lithium.bus"); - // Specialized Message Processing Threads for Device and Device Manager - m_MessageBus->StartProcessingThread(); - m_MessageBus->StartProcessingThread(); - m_MessageBus->StartProcessingThread(); - m_MessageBus->StartProcessingThread(); - // Common Message Processing Threads // Max : Maybe we only need one thread for Message, and dynamically cast message // to the right type to process. @@ -124,18 +116,28 @@ namespace Lithium void InitLithiumApp() { LOG_F(INFO, "Init Lithium App"); + // Config AddPtr("lithium.config", ConfigManager::createShared()); + // Message Bus AddPtr("lithium.bus", Atom::Server::MessageBus::createShared()); // AddPtr("ModuleLoader", ModuleLoader::createShared()); - AddPtr("lithium.async.thread", Atom::Async::ThreadManager::createShared(GetIntConfig("config/server/maxthread"))); + // AddPtr("lithium.async.thread", Atom::Async::ThreadManager::createShared(GetIntConfig("config/server/maxthread"))); AddPtr("lithium.system.process", Atom::System::ProcessManager::createShared(GetIntConfig("config/server/maxprocess"))); // AddPtr("PluginManager", PluginManager::createShared(GetPtr("ProcessManager"))); // AddPtr("TaskManager", std::make_shared("tasks.json")); // AddPtr("TaskGenerator", std::make_shared(GetPtr("DeviceManager"))); // AddPtr("TaskStack", std::make_shared()); // AddPtr("ScriptManager", ScriptManager::createShared(GetPtr("MessageBus"))); - AddPtr("lithium.device", DeviceManager::createShared(GetPtr("lithium.bus"), GetPtr("ConfigManager"))); + AddPtr("lithium.device", DeviceManager::createShared(GetPtr("lithium.bus"), GetPtr("lithium.config"))); AddPtr("lithium.error.stack", std::make_shared()); + + AddPtr("lithium.task.container", TaskContainer::createShared()); + AddPtr("lithiun.task.generator", TaskGenerator::createShared()); + AddPtr("lithium.task.loader", TaskLoader::createShared()); + AddPtr("lithium.task.pool", TaskPool::createShared(std::thread::hardware_concurrency())); + AddPtr("lithium.task.tick", TickScheduler::createShared(std::thread::hardware_concurrency())); + AddPtr("lithium.task.manager", TaskManager::createShared()); + } json createSuccessResponse(const std::string &command, const json &value) @@ -530,48 +532,6 @@ namespace Lithium return createSuccessResponse(__func__, output); } - // ----------------------------------------------------------------- - // Thread - // ----------------------------------------------------------------- - - json LithiumApp::joinAllThreads(const json ¶ms) - { - INIT_FUNC(); - if (!m_ThreadManager->joinAllThreads()) - { - return createErrorResponse(__func__, json(), "Failed to join all threads"); - } - return createSuccessResponse(__func__, json()); - } - - json LithiumApp::joinThreadByName(const json ¶ms) - { - INIT_FUNC(); - CHECK_PARAM("name"); - std::string name = params["name"].get(); - if (!m_ThreadManager->isThreadRunning(name)) - { - return createErrorResponse(__func__, json(), std::format("Thread {} does not exist", name)); - } - if (!m_ThreadManager->joinThreadByName(name)) - { - return createErrorResponse(__func__, json(), std::format("Failed to join thread {}", name)); - } - return createSuccessResponse(__func__, json()); - } - - json LithiumApp::isThreadRunning(const json ¶ms) - { - INIT_FUNC(); - CHECK_PARAM("name"); - std::string name = params["name"].get(); - if (!m_ThreadManager->isThreadRunning(name)) - { - return createErrorResponse(__func__, json(), std::format("Thread {} does not exist", name)); - } - return createSuccessResponse(__func__, json()); - } - json LithiumApp::DispatchCommand(const std::string &name, const json ¶ms) { if (name.empty()) diff --git a/src/LithiumApp.hpp b/src/LithiumApp.hpp index 2e83f27d..0abcdb06 100644 --- a/src/LithiumApp.hpp +++ b/src/LithiumApp.hpp @@ -18,7 +18,6 @@ Description: Lithium App Enter #include -#include "atom/async/thread.hpp" #include "config/configor.hpp" #include "device/manager.hpp" #include "atom/system/process.hpp" @@ -28,6 +27,7 @@ Description: Lithium App Enter #include "atom/server/commander.hpp" #include "addon/manager.hpp" #include "script/python.hpp" +#include "task/manager.hpp" // ------------------------------------------------------------------- // About the LithiumApp @@ -131,9 +131,6 @@ namespace Lithium ReturnMessage returnMessage(const std::string &message); public: - json joinThreadByName(const json ¶ms); - json joinAllThreads(const json ¶ms); - json isThreadRunning(const json ¶ms); // ------------------------------------------------------------------- // Lithium Command methods (the main entry point) @@ -176,13 +173,14 @@ namespace Lithium std::unique_ptr> m_CommandDispatcher; private: - std::shared_ptr m_ThreadManager; + std::shared_ptr m_TaskPool; std::shared_ptr m_ConfigManager; std::shared_ptr m_DeviceManager; std::shared_ptr m_ProcessManager; std::shared_ptr m_MessageBus; std::shared_ptr m_ErrorStack; std::shared_ptr m_ComponentManager; + std::shared_ptr m_TaskManager; std::shared_ptr m_PyScriptManager; }; diff --git a/src/addon/loader.cpp b/src/addon/loader.cpp index 3c35b16c..a1142788 100644 --- a/src/addon/loader.cpp +++ b/src/addon/loader.cpp @@ -21,6 +21,7 @@ Description: C++ and Modules Loader #include #include #include +#include #include "atom/io/io.hpp" @@ -45,9 +46,8 @@ namespace fs = std::filesystem; namespace Lithium { - ModuleLoader::ModuleLoader(const std::string &dir_name = "modules", std::shared_ptr threadManager = Atom::Async::ThreadManager::createShared()) + ModuleLoader::ModuleLoader(const std::string &dir_name = "modules") { - m_ThreadManager = threadManager; DLOG_F(INFO, "C++ module manager loaded successfully."); } @@ -65,12 +65,12 @@ namespace Lithium std::shared_ptr ModuleLoader::createShared() { - return std::make_shared("modules", Atom::Async::ThreadManager::createShared()); + return std::make_shared("modules"); } - std::shared_ptr ModuleLoader::createShared(const std::string &dir_name = "modules", std::shared_ptr threadManager = Atom::Async::ThreadManager::createShared()) + std::shared_ptr ModuleLoader::createShared(const std::string &dir_name = "modules") { - return std::make_shared(dir_name, threadManager); + return std::make_shared(dir_name); } bool ModuleLoader::LoadModule(const std::string &path, const std::string &name) diff --git a/src/addon/loader.hpp b/src/addon/loader.hpp index 8743f25c..a5c43afa 100644 --- a/src/addon/loader.hpp +++ b/src/addon/loader.hpp @@ -57,8 +57,6 @@ Description: C++ and Modules Loader #include "atom/log/loguru.hpp" #include "error/error_code.hpp" -#include "atom/async/thread.hpp" - using json = nlohmann::json; namespace Lithium @@ -77,7 +75,7 @@ namespace Lithium * @param dir_name 模块所在的目录名称。 * @param threadManager 线程管理器的共享指针。 */ - ModuleLoader(const std::string &dir_name, std::shared_ptr threadManager); + ModuleLoader(const std::string &dir_name); /** * @brief 析构函数,释放 ModuleLoader 对象。 @@ -101,7 +99,7 @@ namespace Lithium * @param threadManager 线程管理器的共享指针。 * @return 新创建的共享 ModuleLoader 指针对象。 */ - static std::shared_ptr createShared(const std::string &dir_name, std::shared_ptr threadManager); + static std::shared_ptr createShared(const std::string &dir_name); // ------------------------------------------------------------------- // Module methods @@ -336,8 +334,6 @@ namespace Lithium #else std::unordered_map> modules_; // 模块哈希表 #endif - // Injected Thread Manager - std::shared_ptr m_ThreadManager; mutable std::shared_mutex m_SharedMutex; }; diff --git a/src/atom/CMakeLists.txt b/src/atom/CMakeLists.txt index c8bb4ef6..9bcdf0ed 100644 --- a/src/atom/CMakeLists.txt +++ b/src/atom/CMakeLists.txt @@ -1,17 +1,28 @@ +# CMakeLists.txt for Atom +# This project is licensed under the terms of the GPL3 license. +# +# Project Name: Atom +# Description: Atom Library for all of the Element Astro Project +# Author: Max Qian +# License: GPL3 + cmake_minimum_required(VERSION 3.20) project(atom C CXX) CHECK_INCLUDE_FILE(format HAS_STD_FORMAT) add_subdirectory(async) +add_subdirectory(components) +add_subdirectory(connection) +add_subdirectory(driver) +add_subdirectory(extra) add_subdirectory(io) add_subdirectory(log) -add_subdirectory(type) -add_subdirectory(web) -add_subdirectory(components) +add_subdirectory(server) add_subdirectory(task) -add_subdirectory(driver) +add_subdirectory(type) add_subdirectory(utils) +add_subdirectory(web) if(NOT HAS_STD_FORMAT) find_package(fmt REQUIRED) @@ -99,12 +110,12 @@ list(APPEND ${PROJECT_NAME}_LIBS loguru cpp_httplib libzippp - atom-asyncstatic - atom-utilsstatic - atom-driverstatic + atom-async + atom-task + atom-driver atom-component - atom-utilsstatic - atom-webstatic + atom-utils + atom-web ) # Build Object Library diff --git a/src/atom/async/CMakeLists.txt b/src/atom/async/CMakeLists.txt index 6ac2fc3d..b56a9459 100644 --- a/src/atom/async/CMakeLists.txt +++ b/src/atom/async/CMakeLists.txt @@ -1,26 +1,27 @@ +# CMakeLists.txt for Atom-Async +# This project is licensed under the terms of the GPL3 license. +# +# Project Name: Atom-Task +# Description: Async Implementation of Lithium Server and Driver +# Author: Max Qian +# License: GPL3 + cmake_minimum_required(VERSION 3.20) project(atom-async C CXX) # Sources set(${PROJECT_NAME}_SOURCES - thread.cpp timer.cpp ) # Headers set(${PROJECT_NAME}_HEADERS async.hpp - thread_pool.hpp - thread.hpp timer.hpp trigger.hpp trigger_impl.hpp ) -# Private Headers -set(${PROJECT_NAME}_PRIVATE_HEADERS -) - # Build Object Library add_library(${PROJECT_NAME}_OBJECT OBJECT) set_property(TARGET ${PROJECT_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1) @@ -32,23 +33,22 @@ target_sources(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_HEADERS} PRIVATE ${${PROJECT_NAME}_SOURCES} - ${${PROJECT_NAME}_PRIVATE_HEADERS} ) target_link_libraries(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) -add_library(${PROJECT_NAME}static STATIC) +add_library(${PROJECT_NAME} STATIC) -target_link_libraries(${PROJECT_NAME}static ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) -target_link_libraries(${PROJECT_NAME}static ${CMAKE_THREAD_LIBS_INIT}) -target_include_directories(${PROJECT_NAME}static PUBLIC .) +target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) +target_include_directories(${PROJECT_NAME} PUBLIC .) -set_target_properties(${PROJECT_NAME}static PROPERTIES +set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${CMAKE_HYDROGEN_VERSION_STRING} SOVERSION ${HYDROGEN_SOVERSION} - OUTPUT_NAME ${PROJECT_NAME} # this same name like shared library - backwards compatibility + OUTPUT_NAME ${PROJECT_NAME} ) -install(TARGETS ${PROJECT_NAME}static +install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) \ No newline at end of file diff --git a/src/atom/async/async.hpp b/src/atom/async/async.hpp index 4674aa41..4aeb1d38 100644 --- a/src/atom/async/async.hpp +++ b/src/atom/async/async.hpp @@ -12,7 +12,8 @@ Description: A simple but useful async worker manager **************************************************/ -#pragma once +#ifndef ATOM_ASYNC_ASYNC_HPP +#define ATOM_ASYNC_ASYNC_HPP #include #include @@ -180,173 +181,8 @@ namespace Atom::Async private: std::vector>> workers_; ///< The list of managed AsyncWorker instances. }; - - template - template - void AsyncWorker::StartAsync(Func &&func, Args &&...args) - { - static_assert(std::is_invocable_r_v, "Function must return a result"); - task_ = std::async(std::launch::async, std::forward(func), std::forward(args)...); - } - - template - ResultType AsyncWorker::GetResult() - { - if (!task_.valid()) - { - throw Utils::Exception::InvalidArgument_Error("Task is not valid"); - } - return task_.get(); - } - - template - void AsyncWorker::Cancel() - { - if (task_.valid()) - { - task_.wait(); // 等待任务完成 - } - } - - template - bool AsyncWorker::IsDone() const - { - return task_.valid() && (task_.wait_for(std::chrono::seconds(0)) == std::future_status::ready); - } - - template - bool AsyncWorker::IsActive() const - { - return task_.valid() && (task_.wait_for(std::chrono::seconds(0)) == std::future_status::timeout); - } - - template - bool AsyncWorker::Validate(std::function validator) - { - if (!IsDone()) - { - } - ResultType result = GetResult(); - return validator(result); - } - - template - void AsyncWorker::SetCallback(std::function callback) - { - callback_ = callback; - } - - template - void AsyncWorker::SetTimeout(std::chrono::seconds timeout) - { - timeout_ = timeout; - } - - template - void AsyncWorker::WaitForCompletion() - { - if (timeout_ != std::chrono::seconds(0)) - { - auto start_time = std::chrono::steady_clock::now(); - while (!IsDone()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - if (std::chrono::steady_clock::now() - start_time > timeout_) - { - Cancel(); - break; - } - } - } - else - { - while (!IsDone()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - } - - if (callback_ && IsDone()) - { - callback_(GetResult()); - } - } - - template - template - std::shared_ptr> AsyncWorkerManager::CreateWorker(Func &&func, Args &&...args) - { - auto worker = std::make_shared>(); - workers_.push_back(worker); - worker->StartAsync(std::forward(func), std::forward(args)...); - return worker; - } - - template - void AsyncWorkerManager::CancelAll() - { - for (auto &worker : workers_) - { - worker->Cancel(); - } - } - - template - bool AsyncWorkerManager::AllDone() const - { - return std::all_of(workers_.begin(), workers_.end(), [](const auto &worker) - { return worker->IsDone(); }); - } - - template - void AsyncWorkerManager::WaitForAll() - { - while (!AllDone()) - { - // 可以加入一些逻辑来检查任务完成情况 - } - } - - template - bool AsyncWorkerManager::IsDone(std::shared_ptr> worker) const - { - return worker->IsDone(); - } - - template - void AsyncWorkerManager::Cancel(std::shared_ptr> worker) - { - worker->Cancel(); - } } -/* -int main() -{ - AsyncWorkerManager manager; - - auto worker1 = manager.CreateWorker([]() - { - std::this_thread::sleep_for(std::chrono::seconds(3)); - return 42; }); +#include "async_impl.hpp" - auto worker2 = manager.CreateWorker([]() - { - std::this_thread::sleep_for(std::chrono::seconds(2)); - return 100; }); - - manager.WaitForAll(); - - std::cout << "All workers are done." << std::endl; - - if (manager.IsDone(worker1)) - { - std::cout << "Worker 1 is done." << std::endl; - } - - manager.Cancel(worker2); - std::cout << "Worker 2 is cancelled." << std::endl; - - return 0; -} -*/ +#endif diff --git a/src/atom/async/async_impl.hpp b/src/atom/async/async_impl.hpp new file mode 100644 index 00000000..b5553df9 --- /dev/null +++ b/src/atom/async/async_impl.hpp @@ -0,0 +1,159 @@ +/* + * async_impl.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-11-10 + +Description: A simple but useful async worker manager + +**************************************************/ + +#ifndef ATOM_ASYNC_ASYNC_IMPL_HPP +#define ATOM_ASYNC_ASYNC_IMPL_HPP + +namespace Atom::Async +{ + template + template + void AsyncWorker::StartAsync(Func &&func, Args &&...args) + { + static_assert(std::is_invocable_r_v, "Function must return a result"); + task_ = std::async(std::launch::async, std::forward(func), std::forward(args)...); + } + + template + ResultType AsyncWorker::GetResult() + { + if (!task_.valid()) + { + throw Utils::Exception::InvalidArgument_Error("Task is not valid"); + } + return task_.get(); + } + + template + void AsyncWorker::Cancel() + { + if (task_.valid()) + { + task_.wait(); // 等待任务完成 + } + } + + template + bool AsyncWorker::IsDone() const + { + return task_.valid() && (task_.wait_for(std::chrono::seconds(0)) == std::future_status::ready); + } + + template + bool AsyncWorker::IsActive() const + { + return task_.valid() && (task_.wait_for(std::chrono::seconds(0)) == std::future_status::timeout); + } + + template + bool AsyncWorker::Validate(std::function validator) + { + if (!IsDone()) + { + } + ResultType result = GetResult(); + return validator(result); + } + + template + void AsyncWorker::SetCallback(std::function callback) + { + callback_ = callback; + } + + template + void AsyncWorker::SetTimeout(std::chrono::seconds timeout) + { + timeout_ = timeout; + } + + template + void AsyncWorker::WaitForCompletion() + { + if (timeout_ != std::chrono::seconds(0)) + { + auto start_time = std::chrono::steady_clock::now(); + while (!IsDone()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if (std::chrono::steady_clock::now() - start_time > timeout_) + { + Cancel(); + break; + } + } + } + else + { + while (!IsDone()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + + if (callback_ && IsDone()) + { + callback_(GetResult()); + } + } + + template + template + std::shared_ptr> AsyncWorkerManager::CreateWorker(Func &&func, Args &&...args) + { + auto worker = std::make_shared>(); + workers_.push_back(worker); + worker->StartAsync(std::forward(func), std::forward(args)...); + return worker; + } + + template + void AsyncWorkerManager::CancelAll() + { + for (auto &worker : workers_) + { + worker->Cancel(); + } + } + + template + bool AsyncWorkerManager::AllDone() const + { + return std::all_of(workers_.begin(), workers_.end(), [](const auto &worker) + { return worker->IsDone(); }); + } + + template + void AsyncWorkerManager::WaitForAll() + { + while (!AllDone()) + { + // 可以加入一些逻辑来检查任务完成情况 + } + } + + template + bool AsyncWorkerManager::IsDone(std::shared_ptr> worker) const + { + return worker->IsDone(); + } + + template + void AsyncWorkerManager::Cancel(std::shared_ptr> worker) + { + worker->Cancel(); + } +} // namespace Atom::Async + +#endif \ No newline at end of file diff --git a/src/atom/async/thread.cpp b/src/atom/async/thread.cpp deleted file mode 100644 index e253e23e..00000000 --- a/src/atom/async/thread.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* - * thread.cpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-3-29 - -Description: Thread Manager - -**************************************************/ - -#include "thread.hpp" -#include "config.h" - -#include -#include -#include - -#include "atom/log/loguru.hpp" -#include "atom/utils/random.hpp" - -namespace Atom::Async -{ - ThreadManager::ThreadManager(int maxThreads) - : m_maxThreads(maxThreads), m_stopFlag(false) - { - } - - ThreadManager::~ThreadManager() - { - try - { - std::unique_lock lock(m_mtx); - if (!m_stopFlag) - { - m_stopFlag = true; - m_cv.notify_all(); - } - lock.unlock(); - joinAllThreads(); - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Failed to destroy ThreadManager: {}", e.what()); - } - } - - std::shared_ptr ThreadManager::createShared(int maxThreads) - { - return std::make_shared(maxThreads); - } - - void ThreadManager::addThread(std::function func, const std::string &name) - { - if (m_stopFlag) - { - throw std::runtime_error("Thread manager has stopped, cannot add new thread"); - } - try - { - std::unique_lock lock(m_mtx); - m_cv.wait(lock, [this] - { return static_cast(m_threads.size()) < m_maxThreads || m_stopFlag; }); - if (name != "") - { - auto t = std::make_tuple( -#if __cplusplus >= 202002L - std::make_unique([func] - { -#else - std::make_unique([func] - { -#endif - try - { - func(); - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Unhandled exception in thread: {}", e.what()); - } }), - name, - false); - m_threads.emplace_back(std::move(t)); - } - else - { - auto t = std::make_tuple( -#if __cplusplus >= 202002L - std::make_unique([func] -#else - std::make_unique([func] -#endif - - { - try - { - func(); - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Unhandled exception in thread: {}", e.what()); - } }), - Atom::Utils::generateRandomString(16), - false); - m_threads.emplace_back(std::move(t)); - } - m_cv.notify_all(); - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Failed to add thread: {}", e.what()); - } - } - - bool ThreadManager::joinAllThreads() - { - try - { - std::unique_lock lock(m_mtx); - m_cv.wait(lock, [this] - { return m_threads.empty(); }); - for (auto &t : m_threads) - { - joinThread(lock, t); - } - m_threads.clear(); - } - catch (const std::exception &e) - { - return false; - } - return true; - } - - bool ThreadManager::joinThreadByName(const std::string &name) - { - try - { - if (m_threads.empty()) - { - DLOG_F(WARNING, "Thread {} not found", name); - return false; - } - std::unique_lock lock(m_mtx); - for (auto &t : m_threads) - { - if (std::get<1>(t) == name) - { - DLOG_F(INFO, "Thread {} found", name); - joinThread(lock, t); - DLOG_F(INFO, "Thread {} joined", name); - m_threads.erase(std::remove_if(m_threads.begin(), m_threads.end(), - [&](auto &x) - { return !std::get<0>(x); }), - m_threads.end()); - return true; - } - } - DLOG_F(WARNING, "Thread {} not found", name); - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Failed to join thread {}: {}", name, e.what()); - } - return false; - } - - bool ThreadManager::isThreadRunning(const std::string &name) - { - try - { - if (m_threads.empty()) - { - return false; - } - std::unique_lock lock(m_mtx); - for (auto &t : m_threads) - { - if (std::get<1>(t) == name) - { - return !std::get<2>(t); - } - } - return false; - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Failed to check if thread {} is running: {}", name, e.what()); - return false; - } - } - -#if __cplusplus >= 202002L - void ThreadManager::joinThread(std::unique_lock &lock, std::tuple, std::string, bool> &t) -#else - void ThreadManager::joinThread(std::unique_lock &lock, std::tuple, std::string, bool> &t) -#endif - { - auto &threadPtr = std::get<0>(t); - if (threadPtr && threadPtr->joinable()) - { - threadPtr->join(); -#if __cplusplus >= 202002L - threadPtr->request_stop(); -#endif - threadPtr.reset(); - } - std::get<2>(t) = true; - m_cv.notify_all(); - lock.unlock(); - threadPtr.reset(); - lock.lock(); - std::get<2>(t) = false; - m_cv.notify_all(); - } - - void ThreadManager::setMaxThreads(int maxThreads) - { - if (maxThreads <= 0) - { - m_maxThreads = std::thread::hardware_concurrency(); - } - else - { - m_maxThreads = maxThreads; - } - } - - int ThreadManager::getMaxThreads() const - { - return m_maxThreads; - } -} \ No newline at end of file diff --git a/src/atom/async/thread.hpp b/src/atom/async/thread.hpp deleted file mode 100644 index e54f86fe..00000000 --- a/src/atom/async/thread.hpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * thread.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-3-27 - -Description: Thread Manager - -**************************************************/ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "thread_pool.hpp" - -namespace Atom::Async -{ - class ThreadContainer - { - public: - ThreadContainer() = default; - virtual ~ThreadContainer() = default; - - private: -#if __cplusplus >= 202002L - std::unique_ptr m_thread; -#else - std::unique_ptr m_thread; -#endif - std::string m_name; - std::atomic_bool m_isRunning; - }; - - /** - * @brief 线程管理器类,用于管理多个线程 - */ - class ThreadManager - { - public: - /** - * @brief 构造函数 - * @param maxThreads 最大线程数 - */ - explicit ThreadManager(int maxThreads); - - /** - * @brief 析构函数 - */ - ~ThreadManager(); - - static std::shared_ptr createShared(int maxThreads = 10); - - /** - * @brief 添加线程 - * @param func 线程函数 - * @param name 线程名称 - */ - void addThread(std::function func, const std::string &name); - - /** - * @brief 等待所有线程执行完毕 - */ - bool joinAllThreads(); - - /** - * @brief 根据名称等待指定线程执行完毕 - * @param name 线程名称 - */ - bool joinThreadByName(const std::string &name); - - /** - * @brief 判断指定线程是否正在运行 - * @param name 线程名称 - * @return 是否正在运行 - */ - [[nodiscard]] bool isThreadRunning(const std::string &name); - - void setMaxThreads(int maxThreads); - - [[nodiscard]] int getMaxThreads() const; - - private: -#if __cplusplus >= 202002L - /** - * @brief 等待线程执行完毕 - * @param lock 互斥锁 - * @param t 线程相关信息(包括线程对象、名称和是否正在运行) - */ - void joinThread(std::unique_lock &lock, std::tuple, std::string, bool> &t); -#else - /** - * @brief 等待线程执行完毕 - * @param lock 互斥锁 - * @param t 线程相关信息(包括线程对象、名称和是否正在运行) - */ - void joinThread(std::unique_lock &lock, std::tuple, std::string, bool> &t); -#endif - - int m_maxThreads; ///< 最大线程数 -#if __cplusplus >= 202002L - std::vector, std::string, bool>> m_threads; ///< 线程列表(包括线程对象、名称和是否正在运行) -#else - std::vector, std::string, bool>> m_threads; ///< 线程列表(包括线程对象、名称和是否正在运行) -#endif - std::mutex m_mtx; ///< 互斥锁 - std::condition_variable m_cv; ///< 条件变量 - std::atomic m_stopFlag; ///< 停止标志位 - }; - -} \ No newline at end of file diff --git a/src/atom/async/thread_pool.hpp b/src/atom/async/thread_pool.hpp deleted file mode 100644 index f7abd061..00000000 --- a/src/atom/async/thread_pool.hpp +++ /dev/null @@ -1,532 +0,0 @@ -/* - * thread_pool.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-3-27 - -Description: Thread Pool - -**************************************************/ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if __cplusplus >= 202003L -#include -#include -#endif -#ifdef __has_include -#if __has_include() -#include -#endif -#endif - -namespace Atom::Async -{ -#if __cplusplus >= 202003L - template - concept is_lockable = requires(Lock &&lock) { - lock.lock(); - lock.unlock(); - { - lock.try_lock() - } -> std::convertible_to; - }; - - template - requires is_lockable - class thread_safe_queue - { - public: - using value_type = T; - using size_type = typename std::deque::size_type; - - thread_safe_queue() = default; - - void push_back(T &&value) - { - std::scoped_lock lock(mutex_); - data_.push_back(std::forward(value)); - } - - void push_front(T &&value) - { - std::scoped_lock lock(mutex_); - data_.push_front(std::forward(value)); - } - - [[nodiscard]] bool empty() const - { - std::scoped_lock lock(mutex_); - return data_.empty(); - } - - [[nodiscard]] std::optional pop_front() - { - std::scoped_lock lock(mutex_); - if (data_.empty()) - return std::nullopt; - - auto front = std::move(data_.front()); - data_.pop_front(); - return front; - } - - [[nodiscard]] std::optional pop_back() - { - std::scoped_lock lock(mutex_); - if (data_.empty()) - return std::nullopt; - - auto back = std::move(data_.back()); - data_.pop_back(); - return back; - } - - [[nodiscard]] std::optional steal() - { - std::scoped_lock lock(mutex_); - if (data_.empty()) - return std::nullopt; - - auto back = std::move(data_.back()); - data_.pop_back(); - return back; - } - - void rotate_to_front(const T &item) - { - std::scoped_lock lock(mutex_); - auto iter = std::find(data_.begin(), data_.end(), item); - - if (iter != data_.end()) - { - std::ignore = data_.erase(iter); - } - - data_.push_front(item); - } - - [[nodiscard]] std::optional copy_front_and_rotate_to_back() - { - std::scoped_lock lock(mutex_); - - if (data_.empty()) - return std::nullopt; - - auto front = data_.front(); - data_.pop_front(); - - data_.push_back(front); - - return front; - } - - private: - std::deque data_{}; - mutable Lock mutex_{}; - }; - -#ifdef __cpp_lib_move_only_function - using default_function_type = std::move_only_function; -#else - using default_function_type = std::function; -#endif - -#if __cplusplus >= 202002L - template - requires std::invocable && - std::is_same_v> -#else - template - requires std::invocable && - std::is_same>::value -#endif - class ThreadPool - { - public: - explicit ThreadPool( - const unsigned int &number_of_threads = std::thread::hardware_concurrency()) - : tasks_(number_of_threads) - { - std::size_t current_id = 0; - for (std::size_t i = 0; i < number_of_threads; ++i) - { - priority_queue_.push_back(size_t(current_id)); - try - { - threads_.emplace_back([&, id = current_id](const std::stop_token &stop_tok) - { - do { - // wait until signaled - tasks_[id].signal.acquire(); - - do { - // invoke the task - while (auto task = tasks_[id].tasks.pop_front()) { - try { - pending_tasks_.fetch_sub(1, std::memory_order_release); - std::invoke(std::move(task.value())); - } catch (...) { - } - } - - // try to steal a task - for (std::size_t j = 1; j < tasks_.size(); ++j) { - const std::size_t index = (id + j) % tasks_.size(); - if (auto task = tasks_[index].tasks.steal()) { - // steal a task - pending_tasks_.fetch_sub(1, std::memory_order_release); - std::invoke(std::move(task.value())); - // stop stealing once we have invoked a stolen task - break; - } - } - - } while (pending_tasks_.load(std::memory_order_acquire) > 0); - - priority_queue_.rotate_to_front(id); - - } while (!stop_tok.stop_requested()); }); - // increment the thread id - ++current_id; - } - catch (...) - { - // catch all - - // remove one item from the tasks - tasks_.pop_back(); - - // remove our thread from the priority queue - std::ignore = priority_queue_.pop_back(); - } - } - } - - ~ThreadPool() - { - // stop all threads - for (std::size_t i = 0; i < threads_.size(); ++i) - { - threads_[i].request_stop(); - tasks_[i].signal.release(); - threads_[i].join(); - } - } - - /// thread pool is non-copyable - ThreadPool(const ThreadPool &) = delete; - ThreadPool &operator=(const ThreadPool &) = delete; - - /** - * @brief Enqueue a task into the thread pool that returns a result. - * @details Note that task execution begins once the task is enqueued. - * @tparam Function An invokable type. - * @tparam Args Argument parameter pack - * @tparam ReturnType The return type of the Function - * @param f The callable function - * @param args The parameters that will be passed (copied) to the function. - * @return A std::future that can be used to retrieve the returned value. - */ - template > - requires std::invocable - [[nodiscard]] std::future enqueue(Function f, Args... args) - { -#if __cpp_lib_move_only_function - // we can do this in C++23 because we now have support for move only functions - std::promise promise; - auto future = promise.get_future(); - auto task = [func = std::move(f), ... largs = std::move(args), - promise = std::move(promise)]() mutable - { - try - { - if constexpr (std::is_same_v) - { - func(largs...); - promise.set_value(); - } - else - { - promise.set_value(func(largs...)); - } - } - catch (...) - { - promise.set_exception(std::current_exception()); - } - }; - enqueue_task(std::move(task)); - return future; -#else - /* - * use shared promise here so that we don't break the promise later (until C++23) - * - * with C++23 we can do the following: - * - * std::promise promise; - * auto future = promise.get_future(); - * auto task = [func = std::move(f), ...largs = std::move(args), - promise = std::move(promise)]() mutable {...}; - */ - auto shared_promise = std::make_shared>(); - auto task = [func = std::move(f), ... largs = std::move(args), - promise = shared_promise]() - { - try - { - if constexpr (std::is_same_v) - { - func(largs...); - promise->set_value(); - } - else - { - promise->set_value(func(largs...)); - } - } - catch (...) - { - promise->set_exception(std::current_exception()); - } - }; - - // get the future before enqueuing the task - auto future = shared_promise->get_future(); - // enqueue the task - enqueue_task(std::move(task)); - return future; -#endif - } - - /** - * @brief Enqueue a task to be executed in the thread pool that returns void. - * @tparam Function An invokable type. - * @tparam Args Argument parameter pack for Function - * @param func The callable to be executed - * @param args Arguments that will be passed to the function. - */ - template - requires std::invocable && - std::is_same_v> - void enqueue_detach(Function &&func, Args &&...args) - { - enqueue_task( - std::move([f = std::forward(func), - ... largs = std::forward(args)]() mutable -> decltype(auto) - { - // suppress exceptions - try { - std::invoke(f, largs...); - } catch (...) { - } })); - } - - [[nodiscard]] auto size() const { return threads_.size(); } - - private: - template - void enqueue_task(Function &&f) - { - auto i_opt = priority_queue_.copy_front_and_rotate_to_back(); - if (!i_opt.has_value()) - { - // would only be a problem if there are zero threads - return; - } - auto i = *(i_opt); - pending_tasks_.fetch_add(1, std::memory_order_relaxed); - tasks_[i].tasks.push_back(std::forward(f)); - tasks_[i].signal.release(); - } - - struct task_item - { - thread_safe_queue tasks{}; - std::binary_semaphore signal{0}; - }; - - std::vector threads_; - std::deque tasks_; - thread_safe_queue priority_queue_; - std::atomic_int_fast64_t pending_tasks_{}; - }; -#else - class ThreadPool - { - public: - explicit ThreadPool(size_t numThreads) - : stop(false), - maxQueueSize(1000), - maxIdleTime(std::chrono::seconds(60)), - numThreads(numThreads), - idleThreads(0) - { - for (size_t i = 0; i < numThreads; ++i) - { - workers.emplace_back([this, &numThreads] - { - for (;;) { - std::function task; - { - std::unique_lock lock(this->queue_mutex); - this->condition.wait_for(lock, maxIdleTime, [this] { - return this->stop || !this->tasks.empty(); - }); - - if (this->stop && this->tasks.empty()) { - return; - } - - if (!this->tasks.empty()) { - task = std::move(this->tasks.front()); - this->tasks.pop(); - --idleThreads; - } else { - if (numThreads > 1) { - if (++idleThreads < numThreads) { - continue; - } - } - return; - } - } - task(); - if (onTaskCompleted) { - onTaskCompleted(); - } - } }); - } - } - - template - auto enqueue(F &&f, Args &&...args) -> std::future::type> - { - using return_type = typename std::result_of::type; - - auto task = std::make_shared>( - std::bind(std::forward(f), std::forward(args)...)); - - std::future res = task->get_future(); - { - std::unique_lock lock(queue_mutex); - - while (tasks.size() >= maxQueueSize) - { - condition.wait(lock); - } - - // don't allow enqueueing after stopping the pool - if (stop) - { - throw std::runtime_error("enqueue on stopped ThreadPool"); - } - - tasks.emplace([task]() - { (*task)(); }); - - if (idleThreads > 0) - { - condition.notify_one(); - } - else if (workers.size() < numThreads) - { - workers.emplace_back([this] - { - for (;;) { - std::function task; - { - std::unique_lock lock(this->queue_mutex); - this->condition.wait(lock, [this] { - return this->stop || !this->tasks.empty(); - }); - - if (this->stop && this->tasks.empty()) { - return; - } - - if (!this->tasks.empty()) { - task = std::move(this->tasks.front()); - this->tasks.pop(); - } else { - return; - } - } - task(); - } }); - } - } - return res; - } - - void setMaxQueueSize(size_t size) - { - maxQueueSize = size; - } - - void setMaxIdleTime(std::chrono::seconds time) - { - maxIdleTime = time; - } - - void setNumThreads(size_t num) - { - numThreads = num; - } - - void setOnTaskCompleted(std::function callback) - { - onTaskCompleted = std::move(callback); - } - - ~ThreadPool() - { - { - std::unique_lock lock(queue_mutex); - stop = true; - } - condition.notify_all(); - for (std::thread &worker : workers) - { - worker.join(); - } - } - - /// thread pool is non-copyable - ThreadPool(const ThreadPool &) = delete; - ThreadPool &operator=(const ThreadPool &) = delete; - - private: - std::vector workers; - std::queue> tasks; - - std::mutex queue_mutex; - std::condition_variable condition; - bool stop; - size_t maxQueueSize; - std::chrono::seconds maxIdleTime; - size_t numThreads; - std::atomic idleThreads; - std::function onTaskCompleted; - }; -#endif -} diff --git a/src/atom/async/timer.hpp b/src/atom/async/timer.hpp index 4cb1771b..6e061aee 100644 --- a/src/atom/async/timer.hpp +++ b/src/atom/async/timer.hpp @@ -12,7 +12,8 @@ Description: Timer class for C++ **************************************************/ -#pragma once +#ifndef ATOM_ASYNC_TIMER_HPP +#define ATOM_ASYNC_TIMER_HPP #include #include @@ -221,3 +222,5 @@ namespace Atom::Async m_callback = std::forward(func); } } + +#endif diff --git a/src/atom/async/trigger.hpp b/src/atom/async/trigger.hpp index 5adbf2eb..e4633da5 100644 --- a/src/atom/async/trigger.hpp +++ b/src/atom/async/trigger.hpp @@ -12,7 +12,8 @@ Description: Trigger class for C++ **************************************************/ -#pragma once +#ifndef ATOM_ASYNC_TRIGGER_HPP +#define ATOM_ASYNC_TRIGGER_HPP #include #include @@ -115,3 +116,5 @@ class Trigger }; #include "trigger_impl.hpp" + +#endif diff --git a/src/atom/async/trigger_impl.hpp b/src/atom/async/trigger_impl.hpp index ab42ab1c..1b9ab497 100644 --- a/src/atom/async/trigger_impl.hpp +++ b/src/atom/async/trigger_impl.hpp @@ -1,4 +1,19 @@ -#pragma once +/* + * trigger_impl.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-12-14 + +Description: Trigger class for C++ + +**************************************************/ + +#ifndef ATOM_ASYNC_TRIGGER_IMPL_HPP +#define ATOM_ASYNC_TRIGGER_IMPL_HPP #include #include @@ -97,3 +112,5 @@ void Trigger::cancelAllTriggers() std::lock_guard lock(m_mutex); m_callbacks.clear(); } + +#endif diff --git a/src/atom/components/CMakeLists.txt b/src/atom/components/CMakeLists.txt index 9e0d844b..1f6cf968 100644 --- a/src/atom/components/CMakeLists.txt +++ b/src/atom/components/CMakeLists.txt @@ -15,6 +15,8 @@ list(APPEND ${PROJECT_NAME}_SOURCES # Headers list(APPEND ${PROJECT_NAME}_HEADERS component.hpp + macro.hpp + types.hpp templates/alone_component.hpp @@ -24,10 +26,6 @@ list(APPEND ${PROJECT_NAME}_HEADERS templates/task_component.hpp ) -# Private Headers -list(APPEND ${PROJECT_NAME}_PRIVATE_HEADERS -) - # Build Object Library add_library(${PROJECT_NAME}_OBJECT OBJECT) set_property(TARGET ${PROJECT_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1) @@ -37,7 +35,6 @@ target_sources(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_HEADERS} PRIVATE ${${PROJECT_NAME}_SOURCES} - ${${PROJECT_NAME}_PRIVATE_HEADERS} ) target_link_libraries(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) @@ -46,7 +43,8 @@ add_library(${PROJECT_NAME} STATIC) target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) -target_link_libraries(${PROJECT_NAME} atom-utilsstatic) +target_link_libraries(${PROJECT_NAME} atom-utils) +target_link_libraries(${PROJECT_NAME} atom-type) target_include_directories(${PROJECT_NAME} PUBLIC .) set_target_properties(${PROJECT_NAME} PROPERTIES diff --git a/src/atom/components/component.cpp b/src/atom/components/component.cpp index e549c239..6a6c7be3 100644 --- a/src/atom/components/component.cpp +++ b/src/atom/components/component.cpp @@ -16,31 +16,22 @@ Description: Basic Component Definition #include "atom/utils/exception.hpp" +#include + Component::Component() { // Just for safety, initialize the members - m_CommandDispatcher = std::make_unique>(); + m_CommandDispatcher = std::make_unique>(); m_VariableRegistry = std::make_unique(); - - m_ComponentInfo = std::make_shared(); - m_ComponentConfig = std::make_shared(); } Component::~Component() { - // Save the config file - // if (!m_ConfigPath.empty()) - // m_ComponentConfig->save(m_ConfigPath); - // Save the info file - // if (!m_InfoPath.empty()) - // m_ComponentInfo->save(m_InfoPath); - // Just for safety + m_CommandDispatcher->RemoveAll(); m_VariableRegistry->RemoveAll(); m_CommandDispatcher.reset(); m_VariableRegistry.reset(); - m_ComponentInfo.reset(); - m_ComponentConfig.reset(); } bool Component::Initialize() @@ -58,35 +49,11 @@ std::string Component::GetName() const return m_name; } -bool Component::LoadComponentInfo(const std::string &path) -{ - try - { - // m_ComponentInfo->load(path); - } - catch (const Atom::Utils::Exception::FileNotReadable_Error &e) - { - return false; - } - m_InfoPath = path; - return true; -} - -std::string Component::getJsonInfo() const -{ - return ""; -} - -std::string Component::getXmlInfo() const -{ - return ""; -} - -bool Component::LoadComponentConfig(const std::string &path) +bool Component::LoadConfig(const std::string &path) { try { - // m_ComponentConfig->load(path); + m_Config->load(path); } catch (const Atom::Utils::Exception::FileNotReadable_Error &e) { @@ -98,12 +65,12 @@ bool Component::LoadComponentConfig(const std::string &path) std::string Component::getJsonConfig() const { - // return m_ComponentConfig->toJson(); + return m_Config->toJson(); } std::string Component::getXmlConfig() const { - // return m_ComponentConfig->toXml(); + return m_Config->toXml(); } std::string Component::GetVariableInfo(const std::string &name) const @@ -115,7 +82,7 @@ std::string Component::GetVariableInfo(const std::string &name) const return m_VariableRegistry->GetDescription(name); } -bool Component::RunFunc(const std::string &name, const Args ¶ms) +bool Component::RunFunc(const std::string &name, const json ¶ms) { if (!m_CommandDispatcher->HasHandler(name)) { @@ -125,11 +92,11 @@ bool Component::RunFunc(const std::string &name, const Args ¶ms) return true; } -Args Component::GetFuncInfo(const std::string &name) +json Component::GetFuncInfo(const std::string &name) { if (m_CommandDispatcher->HasHandler(name)) { - Args args; + json args; args = { {"name", name}, {"description", m_CommandDispatcher->GetFunctionDescription(name)}}; @@ -138,7 +105,7 @@ Args Component::GetFuncInfo(const std::string &name) return {}; } -std::function Component::GetFunc(const std::string &name) +std::function Component::GetFunc(const std::string &name) { if (!m_CommandDispatcher->HasHandler(name)) { @@ -146,3 +113,62 @@ std::function Component::GetFunc(const std::string &name) } return m_CommandDispatcher->GetHandler(name); } + +json Component::createSuccessResponse(const std::string &command, const json &value) +{ + json res; + res["command"] = command; + res["value"] = value; + res["status"] = "ok"; + res["code"] = 200; +#if __cplusplus >= 202003L + res["message"] = std::format("{} operated on success", command); +#else + res["message"] = fmt::format("{} operated on success", command); +#endif + return res; +} + +json Component::createErrorResponse(const std::string &command, const json &error, const std::string &message = "") +{ + json res; + res["command"] = command; + res["status"] = "error"; + res["code"] = 500; +#if __cplusplus >= 202003L + res["message"] = std::format("{} operated on failure, message: {}", command, message); +#else + res["message"] = std::format("{} operated on failure, message: {}", command, message); +#endif + if (!error.empty()) + { + res["error"] = error; + } + else + { + res["error"] = "Unknown Error"; + } + return res; +} + +json Component::createWarningResponse(const std::string &command, const json &warning, const std::string &message = "") +{ + json res; + res["command"] = command; + res["status"] = "warning"; + res["code"] = 400; +#if __cplusplus >= 202003L + res["message"] = std::format("{} operated on warning, message: {}", command, message); +#else + res["message"] = std::format("{} operated on warning, message: {}", command, message); +#endif + if (!warning.empty()) + { + res["warning"] = warning; + } + else + { + res["warning"] = "Unknown Warning"; + } + return res; +} \ No newline at end of file diff --git a/src/atom/components/component.hpp b/src/atom/components/component.hpp index 6c3065f0..986848dd 100644 --- a/src/atom/components/component.hpp +++ b/src/atom/components/component.hpp @@ -12,9 +12,10 @@ Description: Basic Component Definition **************************************************/ -#pragma once +// Max: Obviously, directly use json.hpp is much simpler than self-implement type -#define ATOM_COMPONENT_DECLARATION +#ifndef ATOM_COMPONENT_HPP +#define ATOM_COMPONENT_HPP #include #include @@ -24,8 +25,9 @@ Description: Basic Component Definition #include "atom/server/commander.hpp" #include "atom/server/variables.hpp" -#include "atom/type/args.hpp" #include "atom/type/ini.hpp" +#include "atom/type/json.hpp" +using json = nlohmann::json; #define SETVAR_STR(name, value) \ m_VariableRegistry->RegisterVariable(name); \ @@ -43,7 +45,6 @@ Description: Basic Component Definition m_VariableRegistry->RegisterVariable(name); \ m_VariableRegistry->SetVariable(name, value); -using ComponentInfo = std::shared_ptr; using ComponentConfig = std::shared_ptr; class Component : public std::enable_shared_from_this @@ -85,39 +86,12 @@ class Component : public std::enable_shared_from_this std::string GetName() const; - // ------------------------------------------------------------------- - // Component Infomation methods - // ------------------------------------------------------------------- - - /** - * @brief Loads the component information from a file. - * - * @param path The path to the component information file. - * @return True if the component information was loaded successfully, false otherwise. - * @note Usually, the component information file is stored in the plugin's directory. - */ - bool LoadComponentInfo(const std::string &path); - - template - std::optional getInfo(const std::string section, const std::string &key) - { - return m_ComponentInfo->get(section, key); - } - - std::string getJsonInfo() const; - - std::string getXmlInfo() const; - - template - bool setInfo(const std::string §ion, const std::string &key, const T &value) - { - return m_ComponentInfo->set(key, value); - } - // ------------------------------------------------------------------- // Component Configuration methods // ------------------------------------------------------------------- + // Max: Should we use JSON here? + /** * @brief Loads the component configuration from a file. * @@ -125,24 +99,24 @@ class Component : public std::enable_shared_from_this * @return True if the component configuration was loaded successfully, false otherwise. * @note Usually, the component configuration file is stored in the plugin's directory. */ - bool LoadComponentConfig(const std::string &path); + bool LoadConfig(const std::string &path); template std::optional getConfig(const std::string §ion, const std::string &key) { - return m_ComponentConfig->get(section, key); + return m_Config->get(section, key); } - std::string getJsonConfig() const; - - std::string getXmlConfig() const; - template bool setConfig(const std::string §ion, const std::string &key, const T &value) { - return m_ComponentConfig->set(section, key, value); + return m_Config->set(section, key, value); } + std::string getJsonConfig() const; + + std::string getXmlConfig() const; + // ------------------------------------------------------------------- // Variable methods // ------------------------------------------------------------------- @@ -213,7 +187,7 @@ class Component : public std::enable_shared_from_this * @return True if the variable was set successfully, false otherwise. */ template - void RegisterFunc(const std::string &name, void (ClassType::*handler)(const Args &), ClassType *object); + void RegisterFunc(const std::string &name, json (ClassType::*handler)(const json &), ClassType *object); /** * @brief Gets the information about the function with the specified name. @@ -221,7 +195,7 @@ class Component : public std::enable_shared_from_this * @param name The name of the function. * @return The information about the function in JSON format. */ - Args GetFuncInfo(const std::string &name); + json GetFuncInfo(const std::string &name); /** * @brief Runs the function with the specified name and parameters. @@ -232,28 +206,33 @@ class Component : public std::enable_shared_from_this * @param params The parameters for the function. * @return True if the function was executed successfully, false otherwise. */ - bool RunFunc(const std::string &name, const Args ¶ms); + bool RunFunc(const std::string &name, const json ¶ms); + + std::function GetFunc(const std::string &name); + + json createSuccessResponse(const std::string &command, const json &value); - std::function GetFunc(const std::string &name); + json createErrorResponse(const std::string &command, const json &error, const std::string &message); + + json createWarningResponse(const std::string &command, const json &warning, const std::string &message); private: std::string m_name; std::string m_ConfigPath; std::string m_InfoPath; - std::unique_ptr> m_CommandDispatcher; ///< The command dispatcher for handling functions. + std::unique_ptr> m_CommandDispatcher; ///< The command dispatcher for handling functions. std::unique_ptr m_VariableRegistry; ///< The variable registry for managing variables. - // Component info in INI format - ComponentInfo m_ComponentInfo; - // Component Config in INI format - ComponentConfig m_ComponentConfig; + ComponentConfig m_Config; }; template -void Component::RegisterFunc(const std::string &name, void (ClassType::*handler)(const Args &), ClassType *object) +void Component::RegisterFunc(const std::string &name, json (ClassType::*handler)(const json &), ClassType *object) { if (!m_CommandDispatcher->HasHandler(name)) m_CommandDispatcher->RegisterMemberHandler(name, object, handler); } + +#endif diff --git a/src/atom/components/component_marco.hpp b/src/atom/components/component_marco.hpp deleted file mode 100644 index 7048d146..00000000 --- a/src/atom/components/component_marco.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#define GET_COMPONENT_ARG(name, type) \ - if (!m_params.get(#name).has_value()) \ - { \ - LOG_F(ERROR, "{}: Missing argument: {}", __func__, #name); \ - return; \ - } \ - const type name = m_params.get(#name).value(); diff --git a/src/atom/components/macro.hpp b/src/atom/components/macro.hpp new file mode 100644 index 00000000..00d7fa4d --- /dev/null +++ b/src/atom/components/macro.hpp @@ -0,0 +1,30 @@ +#define CHECK_PARAM(key) \ + if (!params.contains(key)) \ + { \ + LOG_F(ERROR, "Failed to execute {}: Invalid Parameters", __func__); \ + return { \ + {"command", __func__}, \ + {"error", "Invalid Parameters"}, \ + {"status", "error"}, \ + {"code", 1000}, \ + {"message", std::format("Invalid Parameters, {} need {}", __func__, key)}}; \ + }\ + +#define CHECK_PARAMS(...) \ + { \ + std::vector __params = {__VA_ARGS__}; \ + for (const auto &key : __params) \ + { \ + if (!params.contains(key)) \ + { \ + LOG_F(ERROR, "Failed to execute {}: Invalid Parameters", __func__); \ + return { \ + {"command", __func__}, \ + {"error", "Invalid Parameters"}, \ + {"status", "error"}, \ + {"code", 1000}, \ + {"message", std::format("Invalid Parameters, {} need {}", __func__, key)}}; \ + } \ + } \ + } \ + \ No newline at end of file diff --git a/src/atom/components/templates/alone_component.cpp b/src/atom/components/templates/alone_component.cpp index 0c837ee5..8832c004 100644 --- a/src/atom/components/templates/alone_component.cpp +++ b/src/atom/components/templates/alone_component.cpp @@ -13,7 +13,7 @@ Description: Alone component(run in different process) **************************************************/ #include "alone_component.hpp" -#include "component_marco.hpp" +#include "atom/components/macro.hpp" #include "atom/log/loguru.hpp" #include "atom/utils/random.hpp" diff --git a/src/atom/components/templates/exe_component.cpp b/src/atom/components/templates/exe_component.cpp index b48d8a70..1186606e 100644 --- a/src/atom/components/templates/exe_component.cpp +++ b/src/atom/components/templates/exe_component.cpp @@ -13,7 +13,7 @@ Description: Executable Plugin **************************************************/ #include "exe_component.hpp" -#include "component_marco.hpp" +#include "atom/components/macro.hpp" #include "atom/log/loguru.hpp" #include "atom/utils/random.hpp" @@ -24,97 +24,104 @@ Description: Executable Plugin ExecutableComponent::ExecutableComponent() : Component() { - RegisterFunc("run_system_command", &ExecutableComponent::RunSystemCommand, this); RegisterFunc("run_system_command_with_output", &ExecutableComponent::RunSystemCommandOutput, this); RegisterFunc("run_script", &ExecutableComponent::RunScript, this); RegisterFunc("run_script_with_output", &ExecutableComponent::RunScriptOutput, this); } -void ExecutableComponent::RunSystemCommand(const Args &m_params) +json ExecutableComponent::RunSystemCommand(const json ¶ms) { - GET_COMPONENT_ARG(command,std::string); - GET_COMPONENT_ARG(identifier,std::string); + CHECK_PARAM("command"); + CHECK_PARAM("identifier"); + std::string command = params["command"].get(); + std::string identifier = params["identifier"].get(); DLOG_F(INFO, "Running command: {}", command); if (m_ProcessManager) { if (!m_ProcessManager->createProcess(command, identifier.empty() ? Atom::Utils::generateRandomString(10) : identifier)) { LOG_F(ERROR, "Failed to run executable plugin : {}", command); + return createErrorResponse(__func__, json(), std::format("Failed to run executable plugin : {}", command)); } else { LOG_F(ERROR, "Started {} successfully", command); + return createSuccessResponse(__func__, json()); } } - else - { - LOG_F(ERROR, "Process manager is not initialized"); - } + LOG_F(ERROR, "Process manager is not initialized"); + return createErrorResponse(__func__, json(), "Process manager is not initialized"); } -void ExecutableComponent::RunSystemCommandOutput(const Args &m_params) +json ExecutableComponent::RunSystemCommandOutput(const json ¶ms) { - GET_COMPONENT_ARG(command,std::string); - GET_COMPONENT_ARG(identifier,std::string); + CHECK_PARAM("command"); + CHECK_PARAM("identifier"); + std::string command = params["command"].get(); + std::string identifier = params["identifier"].get(); if (m_ProcessManager) { DLOG_F(INFO, "Running command: {}", command); if (m_ProcessManager->createProcess(command, identifier.empty() ? Atom::Utils::generateRandomString(10) : identifier)) { LOG_F(ERROR, "Started {} successfully", command); + return createSuccessResponse(__func__, json()); } else { LOG_F(ERROR, "Failed to run executable plugin : {}", command); + return createErrorResponse(__func__, json(), std::format("Failed to run executable plugin : {}", command)); } } - else - { - LOG_F(ERROR, "Process manager is not initialized"); - } + LOG_F(ERROR, "Process manager is not initialized"); + return createErrorResponse(__func__, json(), "Process manager is not initialized"); } -void ExecutableComponent::RunScript(const Args &m_params) +json ExecutableComponent::RunScript(const json ¶ms) { - GET_COMPONENT_ARG(script,std::string); - GET_COMPONENT_ARG(identifier,std::string); + CHECK_PARAM("script"); + CHECK_PARAM("identifier"); + std::string script = params["script"].get(); + std::string identifier = params["identifier"].get(); if (m_ProcessManager) { DLOG_F(INFO, "Running script: {}", script); if (m_ProcessManager->createProcess(script, identifier.empty() ? Atom::Utils::generateRandomString(10) : identifier)) { LOG_F(ERROR, "Started {} successfully", script); + return createSuccessResponse(__func__, json()); } else { LOG_F(ERROR, "Failed to run executable plugin : {}", script); + return createErrorResponse(__func__, json(), std::format("Failed to run executable plugin : {}", script)); } } - else - { - LOG_F(ERROR, "Process manager is not initialized"); - } + LOG_F(ERROR, "Process manager is not initialized"); + return createErrorResponse(__func__, json(), "Process manager is not initialized"); } -void ExecutableComponent::RunScriptOutput(const Args &m_params) +json ExecutableComponent::RunScriptOutput(const json ¶ms) { - GET_COMPONENT_ARG(script,std::string); - GET_COMPONENT_ARG(identifier,std::string); + CHECK_PARAM("script"); + CHECK_PARAM("identifier"); + std::string script = params["script"].get(); + std::string identifier = params["identifier"].get(); if (m_ProcessManager) { DLOG_F(INFO, "Running script: {}", script); if (m_ProcessManager->createProcess(script, identifier.empty() ? Atom::Utils::generateRandomString(10) : identifier)) { LOG_F(ERROR, "Started {} successfully", script); + return createSuccessResponse(__func__, json()); } else { LOG_F(ERROR, "Failed to run executable plugin : {}", script); + return createErrorResponse(__func__, json(), std::format("Failed to run executable plugin : {}", script)); } } - else - { - LOG_F(ERROR, "Process manager is not initialized"); - } + LOG_F(ERROR, "Process manager is not initialized"); + return createErrorResponse(__func__, json(), "Process manager is not initialized"); } diff --git a/src/atom/components/templates/exe_component.hpp b/src/atom/components/templates/exe_component.hpp index 91924658..8dba2b70 100644 --- a/src/atom/components/templates/exe_component.hpp +++ b/src/atom/components/templates/exe_component.hpp @@ -23,13 +23,13 @@ class ExecutableComponent : public Component public: ExecutableComponent(); - void RunSystemCommand(const Args &m_parmas); + json RunSystemCommand(const json &m_parmas); - void RunSystemCommandOutput(const Args &m_parmas); + json RunSystemCommandOutput(const json &m_parmas); - void RunScript(const Args &m_parmas); + json RunScript(const json &m_parmas); - void RunScriptOutput(const Args &m_parmas); + json RunScriptOutput(const json &m_parmas); private: std::shared_ptr m_ProcessManager; }; diff --git a/src/atom/components/templates/shared_component.cpp b/src/atom/components/templates/shared_component.cpp index 307a30a9..cb40d248 100644 --- a/src/atom/components/templates/shared_component.cpp +++ b/src/atom/components/templates/shared_component.cpp @@ -1,13 +1,13 @@ #include "shared_component.hpp" -#include "atom/async/thread.hpp" #include "atom/server/message_bus.hpp" - #include "atom/utils/string.hpp" - #include "atom/log/loguru.hpp" + #include "config.h" +#include "macro.hpp" + #define GET_ARGUMENT_S(command, type, name) \ if (!args.get(#name).has_value()) \ { \ @@ -87,13 +87,6 @@ SharedComponent::~SharedComponent() bool SharedComponent::Initialize() { // Initialize message handlers - // Register message handlers - m_handleVoid->registerCase("getVersion", [this](const std::shared_ptr &message) - { this->SendTextMessage("getVersion", getInfo("basic", "version").value()); }); - m_handleVoid->registerCase("getName", [this](const std::shared_ptr &message) - { this->SendTextMessage("getName", getInfo("basic", "name").value()); }); - m_handleVoid->registerCase("getAllInfo", [this](const std::shared_ptr &message) - { this->SendTextMessage("getAllInfo", this->getJsonInfo()); }); m_handleVoid->registerCase("getAllConfig", [this](const std::shared_ptr &message) { this->SendTextMessage("getAllConfig", this->getJsonConfig()); }); @@ -220,19 +213,3 @@ bool SharedComponent::SendParamsMessage(const std::string &message, const Args & m_MessageBus->Publish>(message, std::make_shared(message, params, "lithium.app", GetName())); return true; } - -bool SharedComponent::NeedThreadPool() -{ - return true; -} - -bool SharedComponent::InjectThreadPool(std::shared_ptr threadPool) -{ - if (!threadPool) - { - LOG_F(ERROR, "Thread pool is null."); - return false; - } - DLOG_F(INFO, "Thread pool is injected."); - return true; -} \ No newline at end of file diff --git a/src/atom/components/templates/shared_component.hpp b/src/atom/components/templates/shared_component.hpp index 159ec80a..f05bb0df 100644 --- a/src/atom/components/templates/shared_component.hpp +++ b/src/atom/components/templates/shared_component.hpp @@ -4,7 +4,6 @@ #include "atom/type/message.hpp" #include "atom/utils/switch.hpp" #include "atom/server/message_bus.hpp" -#include "atom/async/thread.hpp" class SharedComponent : public Component { @@ -41,17 +40,9 @@ class SharedComponent : public Component m_handleFunction = handleFunction; } - // ------------------------------------------------------------------- - // Thread methods - // ------------------------------------------------------------------- - - bool NeedThreadPool(); - bool InjectThreadPool(std::shared_ptr threadPool); - private: // This is a little bit hacky, but it works. std::shared_ptr m_MessageBus; - std::shared_ptr m_ThreadPool; std::function)> m_handleFunction; diff --git a/src/atom/connection/CMakeLists.txt b/src/atom/connection/CMakeLists.txt new file mode 100644 index 00000000..8a1f0e2e --- /dev/null +++ b/src/atom/connection/CMakeLists.txt @@ -0,0 +1,58 @@ +# CMakeLists.txt for Atom-Connection +# This project is licensed under the terms of the GPL3 license. +# +# Project Name: Atom-Task +# Description: Connection Between Lithium Drivers, TCP and IPC +# Author: Max Qian +# License: GPL3 + +cmake_minimum_required(VERSION 3.20) +project(atom-connnection C CXX) + +# Sources +list(APPEND ${PROJECT_NAME}_SOURCES + fifoclient.cpp + fifoserver.cpp + shared_memory.cpp + sockethub.cpp + udp_server.cpp +) + +# Headers +list(APPEND ${PROJECT_NAME}_HEADERS + fifoclient.hpp + fifoserver.hpp + shared_memory.hpp + sockethub.hpp + udp_server.hpp +) + +# Build Object Library +add_library(${PROJECT_NAME}_OBJECT OBJECT) +set_property(TARGET ${PROJECT_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1) + +target_sources(${PROJECT_NAME}_OBJECT + PUBLIC + ${${PROJECT_NAME}_HEADERS} + PRIVATE + ${${PROJECT_NAME}_SOURCES} +) + +target_link_libraries(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) + +add_library(${PROJECT_NAME} STATIC) + +target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(${PROJECT_NAME} loguru) +target_include_directories(${PROJECT_NAME} PUBLIC .) + +set_target_properties(${PROJECT_NAME} PROPERTIES + VERSION ${CMAKE_HYDROGEN_VERSION_STRING} + SOVERSION ${HYDROGEN_SOVERSION} + OUTPUT_NAME ${PROJECT_NAME} # this same name like shared library - backwards compatibility +) + +install(TARGETS ${PROJECT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) \ No newline at end of file diff --git a/src/atom/connection/fifoclient.cpp b/src/atom/connection/fifoclient.cpp index ac4d69c2..aa643307 100644 --- a/src/atom/connection/fifoclient.cpp +++ b/src/atom/connection/fifoclient.cpp @@ -13,89 +13,94 @@ Description: FIFO CLient *************************************************/ #include "fifoclient.hpp" + #include + #include "atom/log/loguru.hpp" +namespace Atom::Connection +{ #ifdef _WIN32 -FifoClient::FifoClient(const std::string &fifoPath) : fifoPath(fifoPath), pipeHandle(INVALID_HANDLE_VALUE) + FifoClient::FifoClient(const std::string &fifoPath) : fifoPath(fifoPath), pipeHandle(INVALID_HANDLE_VALUE) #else -FifoClient::FifoClient(const std::string &fifoPath) : fifoPath(fifoPath), pipeFd() + FifoClient::FifoClient(const std::string &fifoPath) : fifoPath(fifoPath), pipeFd() #endif -{ -} - -void FifoClient::connect() -{ - DLOG_F(INFO, "Connecting to FIFO..."); - -#ifdef _WIN32 - if (!WaitNamedPipeA(fifoPath.c_str(), NMPWAIT_WAIT_FOREVER)) { - throw std::runtime_error("Failed to connect to FIFO"); } - pipeHandle = CreateFileA( - fifoPath.c_str(), - GENERIC_WRITE, - 0, - nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - nullptr); - - if (pipeHandle == INVALID_HANDLE_VALUE) + void FifoClient::connect() { - throw std::runtime_error("Failed to open FIFO"); - } + DLOG_F(INFO, "Connecting to FIFO..."); + +#ifdef _WIN32 + if (!WaitNamedPipeA(fifoPath.c_str(), NMPWAIT_WAIT_FOREVER)) + { + throw std::runtime_error("Failed to connect to FIFO"); + } + + pipeHandle = CreateFileA( + fifoPath.c_str(), + GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if (pipeHandle == INVALID_HANDLE_VALUE) + { + throw std::runtime_error("Failed to open FIFO"); + } #else - int fd = open(fifoPath.c_str(), O_WRONLY); - if (fd == -1) - { - throw std::runtime_error("Failed to open FIFO"); - } + int fd = open(fifoPath.c_str(), O_WRONLY); + if (fd == -1) + { + throw std::runtime_error("Failed to open FIFO"); + } - pipeFd = fd; + pipeFd = fd; #endif - DLOG_F(INFO, "Connected to FIFO"); -} + DLOG_F(INFO, "Connected to FIFO"); + } -void FifoClient::sendMessage(const std::string &message) -{ - DLOG_F(INFO, "Sending message..."); + void FifoClient::sendMessage(const std::string &message) + { + DLOG_F(INFO, "Sending message..."); #ifdef _WIN32 - DWORD numBytesWritten; - if (!WriteFile( - pipeHandle, - message.c_str(), - message.length(), - &numBytesWritten, - nullptr) || - numBytesWritten != message.length()) - { - throw std::runtime_error("Failed to write message to FIFO"); - } + DWORD numBytesWritten; + if (!WriteFile( + pipeHandle, + message.c_str(), + message.length(), + &numBytesWritten, + nullptr) || + numBytesWritten != message.length()) + { + throw std::runtime_error("Failed to write message to FIFO"); + } #else - ssize_t numBytesWritten = write(pipeFd, message.c_str(), message.length()); - if (numBytesWritten == -1 || static_cast(numBytesWritten) != message.length()) - { - throw std::runtime_error("Failed to write message to FIFO"); - } + ssize_t numBytesWritten = write(pipeFd, message.c_str(), message.length()); + if (numBytesWritten == -1 || static_cast(numBytesWritten) != message.length()) + { + throw std::runtime_error("Failed to write message to FIFO"); + } #endif - DLOG_F(INFO, "Message sent"); -} + DLOG_F(INFO, "Message sent"); + } -void FifoClient::disconnect() -{ - DLOG_F(INFO, "Disconnecting from FIFO..."); + void FifoClient::disconnect() + { + DLOG_F(INFO, "Disconnecting from FIFO..."); #ifdef _WIN32 - CloseHandle(pipeHandle); + CloseHandle(pipeHandle); #else - close(pipeFd); + close(pipeFd); #endif - DLOG_F(INFO, "Disconnected from FIFO"); -} + DLOG_F(INFO, "Disconnected from FIFO"); + } +} // namespace Atom::Connection diff --git a/src/atom/connection/fifoclient.hpp b/src/atom/connection/fifoclient.hpp index 8864dd1e..09e0e9ef 100644 --- a/src/atom/connection/fifoclient.hpp +++ b/src/atom/connection/fifoclient.hpp @@ -12,8 +12,8 @@ Description: FIFO CLient *************************************************/ -#ifndef FIFOCLIENT_H -#define FIFOCLIENT_H +#ifndef ATOM_CONNECTION_FIFOCLIENT_HPP +#define ATOM_CONNECTION_FIFOCLIENT_HPP #include @@ -26,23 +26,26 @@ Description: FIFO CLient #include #endif -class FifoClient +namespace Atom::Connection { -public: - FifoClient(const std::string &fifoPath); + class FifoClient + { + public: + FifoClient(const std::string &fifoPath); - void connect(); - void sendMessage(const std::string &message); - void disconnect(); + void connect(); + void sendMessage(const std::string &message); + void disconnect(); -private: - std::string fifoPath; + private: + std::string fifoPath; #ifdef _WIN32 - HANDLE pipeHandle; + HANDLE pipeHandle; #else - int pipeFd; + int pipeFd; #endif -}; + }; +} #endif // FIFOSERVER_H diff --git a/src/atom/connection/fifoserver.cpp b/src/atom/connection/fifoserver.cpp index df7c6e7a..fbc49e3b 100644 --- a/src/atom/connection/fifoserver.cpp +++ b/src/atom/connection/fifoserver.cpp @@ -13,110 +13,116 @@ Description: FIFO Server *************************************************/ #include "fifoserver.hpp" + #include -#include "atom/log/loguru.hpp" -#ifdef _WIN32 -FifoServer::FifoServer(const std::string &fifoPath) : fifoPath(fifoPath), pipeHandle(INVALID_HANDLE_VALUE) -#else -FifoServer::FifoServer(const std::string &fifoPath) : fifoPath(fifoPath), pipeFd() -#endif -{ -} +#include "atom/log/loguru.hpp" -void FifoServer::start() +namespace Atom::Connection { - DLOG_F(INFO, "Starting FIFO server..."); #ifdef _WIN32 - if (!WaitNamedPipeA(fifoPath.c_str(), NMPWAIT_WAIT_FOREVER)) - { - throw std::runtime_error("Failed to connect to FIFO"); - } - - pipeHandle = CreateNamedPipeA( - fifoPath.c_str(), - PIPE_ACCESS_INBOUND, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - 1, - bufferSize, - bufferSize, - 0, - nullptr); - - if (pipeHandle == INVALID_HANDLE_VALUE) - { - throw std::runtime_error("Failed to create FIFO"); - } - - if (!ConnectNamedPipe(pipeHandle, nullptr)) - { - throw std::runtime_error("Failed to establish connection with client"); - } + FifoServer::FifoServer(const std::string &fifoPath) : fifoPath(fifoPath), pipeHandle(INVALID_HANDLE_VALUE) #else - if (mkfifo(fifoPath.c_str(), 0666) == -1) + FifoServer::FifoServer(const std::string &fifoPath) : fifoPath(fifoPath), pipeFd() +#endif { - throw std::runtime_error("Failed to create FIFO"); } - int fd = open(fifoPath.c_str(), O_RDONLY); - if (fd == -1) + void FifoServer::start() { - throw std::runtime_error("Failed to open FIFO"); - } + DLOG_F(INFO, "Starting FIFO server..."); - pipeFd = fd; +#ifdef _WIN32 + if (!WaitNamedPipeA(fifoPath.c_str(), NMPWAIT_WAIT_FOREVER)) + { + throw std::runtime_error("Failed to connect to FIFO"); + } + + pipeHandle = CreateNamedPipeA( + fifoPath.c_str(), + PIPE_ACCESS_INBOUND, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + 1, + bufferSize, + bufferSize, + 0, + nullptr); + + if (pipeHandle == INVALID_HANDLE_VALUE) + { + throw std::runtime_error("Failed to create FIFO"); + } + + if (!ConnectNamedPipe(pipeHandle, nullptr)) + { + throw std::runtime_error("Failed to establish connection with client"); + } +#else + if (mkfifo(fifoPath.c_str(), 0666) == -1) + { + throw std::runtime_error("Failed to create FIFO"); + } + + int fd = open(fifoPath.c_str(), O_RDONLY); + if (fd == -1) + { + throw std::runtime_error("Failed to open FIFO"); + } + + pipeFd = fd; #endif - DLOG_F(INFO, "FIFO server started"); -} + DLOG_F(INFO, "FIFO server started"); + } -std::string FifoServer::receiveMessage() -{ - DLOG_F(INFO, "Receiving message..."); + std::string FifoServer::receiveMessage() + { + DLOG_F(INFO, "Receiving message..."); - char buffer[bufferSize]; + char buffer[bufferSize]; #ifdef _WIN32 - DWORD numBytesRead; - if (!ReadFile( - pipeHandle, - buffer, - bufferSize - 1, - &numBytesRead, - nullptr) || - numBytesRead == 0) - { - return ""; - } + DWORD numBytesRead; + if (!ReadFile( + pipeHandle, + buffer, + bufferSize - 1, + &numBytesRead, + nullptr) || + numBytesRead == 0) + { + return ""; + } #else - ssize_t numBytesRead = read(pipeFd, buffer, bufferSize - 1); - if (numBytesRead == -1 || numBytesRead == 0) - { - return ""; - } + ssize_t numBytesRead = read(pipeFd, buffer, bufferSize - 1); + if (numBytesRead == -1 || numBytesRead == 0) + { + return ""; + } #endif - buffer[numBytesRead] = '\0'; - std::string receivedMessage(buffer); + buffer[numBytesRead] = '\0'; + std::string receivedMessage(buffer); - DLOG_F(INFO, "Received message: %s", receivedMessage.c_str()); + DLOG_F(INFO, "Received message: %s", receivedMessage.c_str()); - return receivedMessage; -} + return receivedMessage; + } -void FifoServer::stop() -{ - DLOG_F(INFO, "Stopping FIFO server..."); + void FifoServer::stop() + { + DLOG_F(INFO, "Stopping FIFO server..."); #ifdef _WIN32 - DisconnectNamedPipe(pipeHandle); - CloseHandle(pipeHandle); - DeleteFileA(fifoPath.c_str()); + DisconnectNamedPipe(pipeHandle); + CloseHandle(pipeHandle); + DeleteFileA(fifoPath.c_str()); #else - close(pipeFd); - unlink(fifoPath.c_str()); + close(pipeFd); + unlink(fifoPath.c_str()); #endif - DLOG_F(INFO, "FIFO server stopped"); -} + DLOG_F(INFO, "FIFO server stopped"); + } +} // namespace Atom::Connection diff --git a/src/atom/connection/fifoserver.hpp b/src/atom/connection/fifoserver.hpp index 440fd5c1..4ea5f612 100644 --- a/src/atom/connection/fifoserver.hpp +++ b/src/atom/connection/fifoserver.hpp @@ -12,8 +12,8 @@ Description: FIFO Server *************************************************/ -#ifndef FIFOSERVER_H -#define FIFOSERVER_H +#ifndef ATOM_CONNECTION_FIFOSERVER_HPP +#define ATOM_CONNECTION_FIFOSERVER_HPP #include @@ -26,24 +26,27 @@ Description: FIFO Server #include #endif -class FifoServer +namespace Atom::Connection { -public: - FifoServer(const std::string &fifoPath); + class FifoServer + { + public: + FifoServer(const std::string &fifoPath); - void start(); - std::string receiveMessage(); - void stop(); + void start(); + std::string receiveMessage(); + void stop(); -private: - std::string fifoPath; - static const int bufferSize = 1024; + private: + std::string fifoPath; + static const int bufferSize = 1024; #ifdef _WIN32 - HANDLE pipeHandle; + HANDLE pipeHandle; #else - int pipeFd; + int pipeFd; #endif -}; + }; +} -#endif // FIFOSERVER_H +#endif // ATOM_CONNECTION_FIFOSERVER_HPP diff --git a/src/atom/connection/shared_memory.cpp b/src/atom/connection/shared_memory.cpp index aa3cee47..4d6e2837 100644 --- a/src/atom/connection/shared_memory.cpp +++ b/src/atom/connection/shared_memory.cpp @@ -14,29 +14,32 @@ Description: Inter-process shared memory for local driver communication. #include "shared_memory.hpp" -SharedMemory::~SharedMemory() +namespace Atom::Connection { + SharedMemory::~SharedMemory() + { #if defined(_WIN32) || defined(_WIN64) // Windows - UnmapViewOfFile(buffer_); - CloseHandle(handle_); + UnmapViewOfFile(buffer_); + CloseHandle(handle_); #else // Unix-like - munmap(buffer_, sizeof(T) + sizeof(bool)); - if (is_creator_) - { - shm_unlink(name_.c_str()); - } + munmap(buffer_, sizeof(T) + sizeof(bool)); + if (is_creator_) + { + shm_unlink(name_.c_str()); + } #endif -} + } -void SharedMemory::clear() -{ - std::unique_lock lock(mutex_); - *reinterpret_cast(buffer_) = false; + void SharedMemory::clear() + { + std::unique_lock lock(mutex_); + *reinterpret_cast(buffer_) = false; - DLOG_F(INFO, "Shared memory cleared."); -} + DLOG_F(INFO, "Shared memory cleared."); + } -bool SharedMemory::isOccupied() const -{ - return mutex_->test_and_set(); -} \ No newline at end of file + bool SharedMemory::isOccupied() const + { + return flag_->test_and_set(); + } +} diff --git a/src/atom/connection/shared_memory.hpp b/src/atom/connection/shared_memory.hpp index 72b7aa1b..4853402b 100644 --- a/src/atom/connection/shared_memory.hpp +++ b/src/atom/connection/shared_memory.hpp @@ -12,18 +12,19 @@ Description: Inter-process shared memory for local driver communication. *************************************************/ -#ifndef ATOM_DRIVER_SHARED_MEMORY_HPP -#define ATOM_DRIVER_SHARED_MEMORY_HPP +#ifndef ATOM_CONNECTION_SHARED_MEMORY_HPP +#define ATOM_CONNECTION_SHARED_MEMORY_HPP #include #include #include #include #include +#include #include "atom/log/loguru.hpp" -#if defined(_WIN32) || defined(_WIN64) // Windows +#ifdef _WIN32 #include #else // Unix-like #include @@ -31,205 +32,208 @@ Description: Inter-process shared memory for local driver communication. #include #endif -/** - * @brief 实现共享内存,可用于进程间通信 - */ -class SharedMemory +namespace Atom::Connection { -public: - /** - * @brief 构造函数,创建指定大小的共享内存 - * @param name 共享内存名称 - * @param create 是否创建新的共享内存 - */ - template - SharedMemory(const std::string &name, bool create); - /** - * @brief 析构函数,释放共享内存 + * @brief 实现共享内存,可用于进程间通信 */ - ~SharedMemory(); - - /** - * @brief 将数据写入到共享内存中 - * @param data 要写入的数据 - * @param timeout 超时时间,默认为0,表示不设超时时间 - */ - template - void write(const T &data, std::chrono::milliseconds timeout); - - /** - * @brief 从共享内存中读取数据 - * @param timeout 超时时间,默认为0,表示不设超时时间 - * @return 读取到的数据 - */ - template - T read(std::chrono::milliseconds timeout) const; - - /** - * @brief 清空共享内存中的数据 - */ - void clear(); - - /** - * @brief 判断共享内存是否被占用 - * @return 如果共享内存已被占用,返回true;否则返回false - */ - bool isOccupied() const; - -private: - std::string name_; ///< 共享内存名称 + class SharedMemory + { + public: + /** + * @brief 构造函数,创建指定大小的共享内存 + * @param name 共享内存名称 + * @param create 是否创建新的共享内存 + */ + template + SharedMemory(const std::string &name, bool create = true); + + /** + * @brief 析构函数,释放共享内存 + */ + ~SharedMemory(); + + /** + * @brief 将数据写入到共享内存中 + * @param data 要写入的数据 + * @param timeout 超时时间,默认为0,表示不设超时时间 + */ + template + void write(const T &data, std::chrono::milliseconds timeout = std::chrono::milliseconds(0)); + + /** + * @brief 从共享内存中读取数据 + * @param timeout 超时时间,默认为0,表示不设超时时间 + * @return 读取到的数据 + */ + template + T read(std::chrono::milliseconds timeout = std::chrono::milliseconds(0)) const; + + /** + * @brief 清空共享内存中的数据 + */ + void clear(); + + /** + * @brief 判断共享内存是否被占用 + * @return 如果共享内存已被占用,返回true;否则返回false + */ + bool isOccupied() const; + + private: + std::string name_; ///< 共享内存名称 #if defined(_WIN32) || defined(_WIN64) // Windows - HANDLE handle_; ///< 共享内存句柄 + HANDLE handle_; ///< 共享内存句柄 #else // Unix-like - int fd_; ///< 共享内存文件描述符 + int fd_; ///< 共享内存文件描述符 #endif - void *buffer_; ///< 共享内存指针 - std::atomic_flag *mutex_; ///< 互斥锁,用于保证并发访问时数据的一致性 - mutable std::mutex mutex_; ///< 互斥量,用于保护互斥锁的修改 - bool is_creator_; ///< 是否是创建者 -}; - -template -SharedMemory::SharedMemory(const std::string &name, bool create = true) - : name_(name), handle_(nullptr), buffer_(nullptr), mutex_(), is_creator_(create) -{ - static_assert(std::is_trivially_copyable::value, "T must be a trivially copyable type."); - static_assert(std::is_standard_layout::value, "T must be a standard layout type."); + void *buffer_; ///< 共享内存指针 + std::atomic_flag *flag_; ///< 互斥锁,用于保证并发访问时数据的一致性 + mutable std::mutex mutex_; ///< 互斥量,用于保护互斥锁的修改 + bool is_creator_; ///< 是否是创建者 + }; -#if defined(_WIN32) || defined(_WIN64) // Windows - if (is_creator_) + template + SharedMemory::SharedMemory(const std::string &name, bool create) + : name_(name), handle_(nullptr), buffer_(nullptr), flag_(), is_creator_(create) { - handle_ = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, sizeof(T) + sizeof(bool), name.c_str()); - if (handle_ == nullptr) + static_assert(std::is_trivially_copyable::value, "T must be a trivially copyable type."); + static_assert(std::is_standard_layout::value, "T must be a standard layout type."); + +#if defined(_WIN32) || defined(_WIN64) // Windows + if (is_creator_) { - LOG_F(ERROR, "Failed to create file mapping."); - throw std::runtime_error("Failed to create file mapping."); + handle_ = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, sizeof(T) + sizeof(bool), name.c_str()); + if (handle_ == nullptr) + { + LOG_F(ERROR, "Failed to create file mapping."); + throw std::runtime_error("Failed to create file mapping."); + } } - } - else - { - handle_ = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, name.c_str()); - if (handle_ == nullptr) + else { - LOG_F(ERROR, "Failed to open file mapping."); - throw std::runtime_error("Failed to open file mapping."); + handle_ = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, name.c_str()); + if (handle_ == nullptr) + { + LOG_F(ERROR, "Failed to open file mapping."); + throw std::runtime_error("Failed to open file mapping."); + } } - } - buffer_ = MapViewOfFile(handle_, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(T) + sizeof(bool)); - if (buffer_ == nullptr) - { - CloseHandle(handle_); - LOG_F(ERROR, "Failed to map view of file."); - throw std::runtime_error("Failed to map view of file."); - } -#else // Unix-like - if (is_creator_) - { - fd_ = shm_open(name.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); - if (fd_ == -1) + buffer_ = MapViewOfFile(handle_, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(T) + sizeof(bool)); + if (buffer_ == nullptr) { - LOG_F(ERROR, "Failed to create shared memory."); - throw std::runtime_error("Failed to create shared memory."); + CloseHandle(handle_); + LOG_F(ERROR, "Failed to map view of file."); + throw std::runtime_error("Failed to map view of file."); } +#else // Unix-like + if (is_creator_) + { + fd_ = shm_open(name.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + if (fd_ == -1) + { + LOG_F(ERROR, "Failed to create shared memory."); + throw std::runtime_error("Failed to create shared memory."); + } - ftruncate(fd_, sizeof(T) + sizeof(bool)); - } - else - { - fd_ = shm_open(name.c_str(), O_RDWR, S_IRUSR | S_IWUSR); - if (fd_ == -1) + ftruncate(fd_, sizeof(T) + sizeof(bool)); + } + else { - LOG_F(ERROR, "Failed to open shared memory."); - throw std::runtime_error("Failed to open shared memory."); + fd_ = shm_open(name.c_str(), O_RDWR, S_IRUSR | S_IWUSR); + if (fd_ == -1) + { + LOG_F(ERROR, "Failed to open shared memory."); + throw std::runtime_error("Failed to open shared memory."); + } } - } - buffer_ = mmap(nullptr, sizeof(T) + sizeof(bool), PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); - close(fd_); + buffer_ = mmap(nullptr, sizeof(T) + sizeof(bool), PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); + close(fd_); - if (buffer_ == MAP_FAILED) - { - if (is_creator_) + if (buffer_ == MAP_FAILED) { - shm_unlink(name.c_str()); + if (is_creator_) + { + shm_unlink(name.c_str()); + } + LOG_F(ERROR, "Failed to map shared memory."); + throw std::runtime_error("Failed to map shared memory."); } - LOG_F(ERROR, "Failed to map shared memory."); - throw std::runtime_error("Failed to map shared memory."); - } #endif - mutex_ = reinterpret_cast(buffer_); - new (mutex_) std::atomic_flag(); -} + flag_ = reinterpret_cast(buffer_); + new (flag_) std::atomic_flag(); + } -template -void SharedMemory::write(const T &data, std::chrono::milliseconds timeout = std::chrono::milliseconds(0)) -{ - static_assert(std::is_trivially_copyable::value, "T must be a trivially copyable type."); - static_assert(std::is_standard_layout::value, "T must be a standard layout type."); + template + void SharedMemory::write(const T &data, std::chrono::milliseconds timeout) + { + static_assert(std::is_trivially_copyable::value, "T must be a trivially copyable type."); + static_assert(std::is_standard_layout::value, "T must be a standard layout type."); - std::unique_lock lock(mutex_); + std::unique_lock lock(mutex_); - auto start_time = std::chrono::steady_clock::now(); - while (mutex_->test_and_set()) - { - if (timeout.count() > 0) + auto start_time = std::chrono::steady_clock::now(); + while (flag_->test_and_set()) { - if (std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time) >= timeout) + if (timeout.count() > 0) { - LOG_F(ERROR, "Failed to acquire mutex within timeout."); - throw std::runtime_error("Failed to acquire mutex within timeout."); + if (std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time) >= timeout) + { + LOG_F(ERROR, "Failed to acquire mutex within timeout."); + throw std::runtime_error("Failed to acquire mutex within timeout."); + } + } + else + { + std::this_thread::yield(); } } - else - { - std::this_thread::yield(); - } - } - std::memcpy(static_cast(buffer_) + sizeof(bool), &data, sizeof(T)); + std::memcpy(static_cast(buffer_) + sizeof(bool), &data, sizeof(T)); - DLOG_F(INFO, "Data written to shared memory."); - *reinterpret_cast(buffer_) = true; + DLOG_F(INFO, "Data written to shared memory."); + *reinterpret_cast(buffer_) = true; - mutex_->clear(); -} + flag_->clear(); + } -template -T SharedMemory::read(std::chrono::milliseconds timeout = std::chrono::milliseconds(0)) const -{ - static_assert(std::is_trivially_copyable::value, "T must be a trivially copyable type."); - static_assert(std::is_standard_layout::value, "T must be a standard layout type."); + template + T SharedMemory::read(std::chrono::milliseconds timeout) const + { + static_assert(std::is_trivially_copyable::value, "T must be a trivially copyable type."); + static_assert(std::is_standard_layout::value, "T must be a standard layout type."); - std::unique_lock lock(mutex_); + std::unique_lock lock(mutex_); - auto start_time = std::chrono::steady_clock::now(); - while (!(*reinterpret_cast(buffer_))) - { - if (timeout.count() > 0) + auto start_time = std::chrono::steady_clock::now(); + while (!(*reinterpret_cast(buffer_))) { - if (std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time) >= timeout) + if (timeout.count() > 0) { - LOG_F(ERROR, "Failed to acquire mutex within timeout."); - throw std::runtime_error("Failed to acquire mutex within timeout."); + if (std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time) >= timeout) + { + LOG_F(ERROR, "Failed to acquire mutex within timeout."); + throw std::runtime_error("Failed to acquire mutex within timeout."); + } + } + else + { + std::this_thread::yield(); } } - else - { - std::this_thread::yield(); - } - } - T data; - std::memcpy(&data, static_cast(buffer_) + sizeof(bool), sizeof(T)); - *reinterpret_cast(buffer_) = false; + T data; + std::memcpy(&data, static_cast(buffer_) + sizeof(bool), sizeof(T)); + *reinterpret_cast(buffer_) = false; - DLOG_F(INFO, "Data read from shared memory."); + DLOG_F(INFO, "Data read from shared memory."); - mutex_->clear(); + flag_->clear(); - return data; + return data; + } } #endif diff --git a/src/atom/connection/sockethub.cpp b/src/atom/connection/sockethub.cpp index 2ed34c2c..92d4af84 100644 --- a/src/atom/connection/sockethub.cpp +++ b/src/atom/connection/sockethub.cpp @@ -148,7 +148,7 @@ namespace Atom::Connection clients.push_back(clientSocket); -#ifdef __cplusplus >= 202002L +#if __cplusplus >= 202002L clientThreads.push_back(std::make_unique([this, clientSocket]() { handleClientMessages(clientSocket); })); #else diff --git a/src/atom/connection/udp_server.cpp b/src/atom/connection/udp_server.cpp index 0e0503e3..59a8cb09 100644 --- a/src/atom/connection/udp_server.cpp +++ b/src/atom/connection/udp_server.cpp @@ -16,138 +16,142 @@ Description: A simple UDP server. #include "atom/log/loguru.hpp" -SocketHub::SocketHub() : m_running(false), m_serverSocket(INVALID_SOCKET) {} - -SocketHub::~SocketHub() +namespace Atom::Connection { - stop(); -} + UdpSocketHub::UdpSocketHub() : m_running(false), m_serverSocket(INVALID_SOCKET) {} -bool SocketHub::initNetworking() -{ + UdpSocketHub::~UdpSocketHub() + { + stop(); + } + + bool UdpSocketHub::initNetworking() + { #ifdef _WIN32 - WSADATA wsaData; - return WSAStartup(MAKEWORD(2, 2), &wsaData) == 0; + WSADATA wsaData; + return WSAStartup(MAKEWORD(2, 2), &wsaData) == 0; #else - return true; // 在Linux上不需要初始化 + return true; // 在Linux上不需要初始化 #endif -} + } -void SocketHub::cleanupNetworking() -{ + void UdpSocketHub::cleanupNetworking() + { #ifdef _WIN32 - WSACleanup(); + WSACleanup(); #endif -} - -void SocketHub::start(int port) -{ - if (m_running.load()) - return; // 防止重复启动 - m_running.store(true); + } - if (!initNetworking()) + void UdpSocketHub::start(int port) { - LOG_F(ERROR, "Networking initialization failed."); - return; - } + if (m_running.load()) + return; // 防止重复启动 + m_running.store(true); - sockaddr_in serverAddr; - serverAddr.sin_family = AF_INET; - serverAddr.sin_port = htons(port); - serverAddr.sin_addr.s_addr = INADDR_ANY; + if (!initNetworking()) + { + LOG_F(ERROR, "Networking initialization failed."); + return; + } - m_serverSocket = socket(AF_INET, SOCK_DGRAM, 0); - if (m_serverSocket == INVALID_SOCKET) - { - LOG_F(ERROR, "Failed to create socket."); - cleanupNetworking(); - return; - } + sockaddr_in serverAddr; + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(port); + serverAddr.sin_addr.s_addr = INADDR_ANY; - if (bind(m_serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) - { - LOG_F(ERROR, "Bind failed with error."); - closesocket(m_serverSocket); - cleanupNetworking(); - return; - } + m_serverSocket = socket(AF_INET, SOCK_DGRAM, 0); + if (m_serverSocket == INVALID_SOCKET) + { + LOG_F(ERROR, "Failed to create socket."); + cleanupNetworking(); + return; + } + + if (bind(m_serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) + { + LOG_F(ERROR, "Bind failed with error."); + closesocket(m_serverSocket); + cleanupNetworking(); + return; + } #if __cplusplus >= 202002L - m_acceptThread = std::make_unique([this]() - { this->handleMessages(); }); + m_acceptThread = std::make_unique([this]() + { this->handleMessages(); }); #else - m_acceptThread = std::make_unique([this]() - { this->handleMessages(); }); + m_acceptThread = std::make_unique([this]() + { this->handleMessages(); }); #endif -} + } -void SocketHub::stop() -{ - if (!m_running.load()) - return; - m_running.store(false); + void UdpSocketHub::stop() + { + if (!m_running.load()) + return; + m_running.store(false); - closesocket(m_serverSocket); - cleanupNetworking(); + closesocket(m_serverSocket); + cleanupNetworking(); - if (m_acceptThread && m_acceptThread->joinable()) - { - m_acceptThread->join(); + if (m_acceptThread && m_acceptThread->joinable()) + { + m_acceptThread->join(); + } } -} - -void SocketHub::addHandler(std::function handler) -{ - m_handler = handler; -} -void SocketHub::sendTo(const std::string &message, const std::string &ip, int port) -{ - if (!m_running.load()) + void UdpSocketHub::addHandler(std::function handler) { - LOG_F(ERROR, "Server is not running."); - return; + m_handler = handler; } - sockaddr_in targetAddr; - targetAddr.sin_family = AF_INET; - targetAddr.sin_port = htons(port); - inet_pton(AF_INET, ip.c_str(), &targetAddr.sin_addr); - - int sentBytes = sendto(m_serverSocket, message.c_str(), message.length(), 0, - (struct sockaddr *)&targetAddr, sizeof(targetAddr)); - if (sentBytes == SOCKET_ERROR) + void UdpSocketHub::sendTo(const std::string &message, const std::string &ip, int port) { - LOG_F(ERROR, "Failed to send message."); + if (!m_running.load()) + { + LOG_F(ERROR, "Server is not running."); + return; + } + + sockaddr_in targetAddr; + targetAddr.sin_family = AF_INET; + targetAddr.sin_port = htons(port); + inet_pton(AF_INET, ip.c_str(), &targetAddr.sin_addr); + + int sentBytes = sendto(m_serverSocket, message.c_str(), message.length(), 0, + (struct sockaddr *)&targetAddr, sizeof(targetAddr)); + if (sentBytes == SOCKET_ERROR) + { + LOG_F(ERROR, "Failed to send message."); #ifdef _WIN32 - LOG_F(ERROR, "Error: {}", WSAGetLastError()); + LOG_F(ERROR, "Error: {}", WSAGetLastError()); #else - LOG_F(ERROR, "Error: {}", errno); + LOG_F(ERROR, "Error: {}", errno); #endif + } } -} - -void SocketHub::handleMessages() -{ - char buffer[1024]; - sockaddr_in clientAddr; - socklen_t clientAddrSize = sizeof(clientAddr); - while (m_running.load()) + void UdpSocketHub::handleMessages() { - int bytesReceived = recvfrom(m_serverSocket, buffer, sizeof(buffer), 0, - (struct sockaddr *)&clientAddr, &clientAddrSize); - if (bytesReceived == SOCKET_ERROR) - { - LOG_F(ERROR, "recvfrom failed with error."); - continue; - } + char buffer[1024]; + sockaddr_in clientAddr; + socklen_t clientAddrSize = sizeof(clientAddr); - if (m_handler) + while (m_running.load()) { - std::string message(buffer, bytesReceived); - m_handler(message); + int bytesReceived = recvfrom(m_serverSocket, buffer, sizeof(buffer), 0, + (struct sockaddr *)&clientAddr, &clientAddrSize); + if (bytesReceived == SOCKET_ERROR) + { + LOG_F(ERROR, "recvfrom failed with error."); + continue; + } + + if (m_handler) + { + std::string message(buffer, bytesReceived); + m_handler(message); + } } } + } diff --git a/src/atom/connection/udp_server.hpp b/src/atom/connection/udp_server.hpp index bf377abf..897b0b44 100644 --- a/src/atom/connection/udp_server.hpp +++ b/src/atom/connection/udp_server.hpp @@ -39,113 +39,116 @@ using socklen_t = int; using SOCKET = int; #endif -/** - * @class SocketHub - * @brief A simple UDP socket server class that handles incoming messages and allows sending messages to specified addresses. - * - * 一个简单的UDP套接字服务器类,用于处理接收到的消息,并允许向指定地址发送消息。 - */ -class SocketHub +namespace Atom::Connection { -public: - /** - * @brief Constructor for SocketHub. Initializes the server state. - * - * SocketHub的构造函数。初始化服务器状态。 - */ - SocketHub(); - - /** - * @brief Destructor for SocketHub. Ensures proper resource cleanup. - * - * SocketHub的析构函数。确保适当的资源清理。 - */ - ~SocketHub(); - - /** - * @brief Starts the UDP server on the specified port. - * - * 在指定端口上启动UDP服务器。 - * - * @param port The port number on which the server will listen for incoming messages. - * - * @param port 服务器监听传入消息的端口号。 - */ - void start(int port); - /** - * @brief Stops the server and cleans up resources. - * - * 停止服务器并清理资源。 - */ - void stop(); - - /** - * @brief Adds a message handler function that will be called whenever a new message is received. - * - * 添加一个消息处理函数,每当接收到新消息时都会调用此函数。 - * - * @param handler A function to handle incoming messages. It takes a string as input. + * @class UdpSocketHub + * @brief A simple UDP socket server class that handles incoming messages and allows sending messages to specified addresses. * - * @param handler 一个处理传入消息的函数。它接受一个字符串作为输入。 + * 一个简单的UDP套接字服务器类,用于处理接收到的消息,并允许向指定地址发送消息。 */ - void addHandler(std::function handler); - - /** - * @brief Sends a message to the specified IP address and port. - * - * 向指定的IP地址和端口发送消息。 - * - * @param message The message to be sent. - * @param ip The target IP address. - * @param port The target port number. - * - * @param message 要发送的消息。 - * @param ip 目标IP地址。 - * @param port 目标端口号。 - */ - void sendTo(const std::string &message, const std::string &ip, int port); - -private: - std::atomic m_running; ///< Indicates whether the server is running or not. - ///< 指示服务器是否正在运行。 - SOCKET m_serverSocket; ///< The socket descriptor for the server. - ///< 服务器的套接字描述符。 + class UdpSocketHub + { + public: + /** + * @brief Constructor for UdpSocketHub. Initializes the server state. + * + * UdpSocketHub的构造函数。初始化服务器状态。 + */ + UdpSocketHub(); + + /** + * @brief Destructor for UdpSocketHub. Ensures proper resource cleanup. + * + * UdpSocketHub的析构函数。确保适当的资源清理。 + */ + ~UdpSocketHub(); + + /** + * @brief Starts the UDP server on the specified port. + * + * 在指定端口上启动UDP服务器。 + * + * @param port The port number on which the server will listen for incoming messages. + * + * @param port 服务器监听传入消息的端口号。 + */ + void start(int port); + + /** + * @brief Stops the server and cleans up resources. + * + * 停止服务器并清理资源。 + */ + void stop(); + + /** + * @brief Adds a message handler function that will be called whenever a new message is received. + * + * 添加一个消息处理函数,每当接收到新消息时都会调用此函数。 + * + * @param handler A function to handle incoming messages. It takes a string as input. + * + * @param handler 一个处理传入消息的函数。它接受一个字符串作为输入。 + */ + void addHandler(std::function handler); + + /** + * @brief Sends a message to the specified IP address and port. + * + * 向指定的IP地址和端口发送消息。 + * + * @param message The message to be sent. + * @param ip The target IP address. + * @param port The target port number. + * + * @param message 要发送的消息。 + * @param ip 目标IP地址。 + * @param port 目标端口号。 + */ + void sendTo(const std::string &message, const std::string &ip, int port); + + private: + std::atomic m_running; ///< Indicates whether the server is running or not. + ///< 指示服务器是否正在运行。 + SOCKET m_serverSocket; ///< The socket descriptor for the server. + ///< 服务器的套接字描述符。 #if __cplusplus >= 202002L - std::unique_ptr m_acceptThread; ///< The thread for handling incoming messages. - ///< 用于处理传入消息的线程。 + std::unique_ptr m_acceptThread; ///< The thread for handling incoming messages. + ///< 用于处理传入消息的线程。 #else - std::unique_ptr m_acceptThread; ///< The thread for handling incoming messages (for C++ standards before C++20). - ///< 用于处理传入消息的线程(针对C++20之前的C++标准)。 + std::unique_ptr m_acceptThread; ///< The thread for handling incoming messages (for C++ standards before C++20). + ///< 用于处理传入消息的线程(针对C++20之前的C++标准)。 #endif - std::function m_handler; ///< The function to handle incoming messages. - ///< 处理传入消息的函数。 - - /** - * @brief Initializes networking. Required for Windows. - * - * 初始化网络。Windows系统需要。 - * - * @return true if successful, false otherwise. - * - * @return 如果成功,则为true;否则为false。 - */ - bool initNetworking(); - - /** - * @brief Cleans up networking resources. Required for Windows. - * - * 清理网络资源。Windows系统需要。 - */ - void cleanupNetworking(); - - /** - * @brief The main loop for receiving messages. Runs in a separate thread. - * - * 接收消息的主循环。在单独的线程中运行。 - */ - void handleMessages(); -}; + std::function m_handler; ///< The function to handle incoming messages. + ///< 处理传入消息的函数。 + + /** + * @brief Initializes networking. Required for Windows. + * + * 初始化网络。Windows系统需要。 + * + * @return true if successful, false otherwise. + * + * @return 如果成功,则为true;否则为false。 + */ + bool initNetworking(); + + /** + * @brief Cleans up networking resources. Required for Windows. + * + * 清理网络资源。Windows系统需要。 + */ + void cleanupNetworking(); + + /** + * @brief The main loop for receiving messages. Runs in a separate thread. + * + * 接收消息的主循环。在单独的线程中运行。 + */ + void handleMessages(); + }; +} #endif \ No newline at end of file diff --git a/src/atom/driver/CMakeLists.txt b/src/atom/driver/CMakeLists.txt index d56c3d30..30d4fe49 100644 --- a/src/atom/driver/CMakeLists.txt +++ b/src/atom/driver/CMakeLists.txt @@ -19,9 +19,6 @@ list(APPEND ${PROJECT_NAME}_SOURCES focuser.cpp filterwheel.cpp guider.cpp - - iproperty.cpp - iphoto.cpp ) # Headers @@ -37,43 +34,31 @@ list(APPEND ${PROJECT_NAME}_HEADERS solver.hpp ) -# Private Headers -list(APPEND ${PROJECT_NAME}_PRIVATE_HEADERS - # TODO -) - # Build Object Library add_library(${PROJECT_NAME}_OBJECT OBJECT) set_property(TARGET ${PROJECT_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1) -target_compile_definitions(${PROJECT_NAME}_OBJECT PRIVATE "-DHAVE_LIBNOVA") - -if(WIN32) -target_link_libraries(${PROJECT_NAME}_OBJECT setupapi wsock32 ws2_32 shlwapi iphlpapi) -endif() - target_sources(${PROJECT_NAME}_OBJECT PUBLIC ${${PROJECT_NAME}_HEADERS} PRIVATE ${${PROJECT_NAME}_SOURCES} - ${${PROJECT_NAME}_PRIVATE_HEADERS} ) target_link_libraries(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_link_libraries(${PROJECT_NAME}_OBJECT atom-component) +target_link_libraries(${PROJECT_NAME}_OBJECT atom-server) +add_library(${PROJECT_NAME} SHARED) -add_library(${PROJECT_NAME}static STATIC) - -target_link_libraries(${PROJECT_NAME}static ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) -target_link_libraries(${PROJECT_NAME}static ${CMAKE_THREAD_LIBS_INIT}) -target_include_directories(${PROJECT_NAME}static PUBLIC .) +target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_include_directories(${PROJECT_NAME} PUBLIC .) -set_target_properties(${PROJECT_NAME}static PROPERTIES +set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${CMAKE_HYDROGEN_VERSION_STRING} SOVERSION ${HYDROGEN_SOVERSION} - OUTPUT_NAME ${PROJECT_NAME} # this same name like shared library - backwards compatibility + OUTPUT_NAME ${PROJECT_NAME} ) -install(TARGETS ${PROJECT_NAME}static +install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) \ No newline at end of file diff --git a/src/atom/driver/camera.cpp b/src/atom/driver/camera.cpp index 094f59b3..2b79bfd8 100644 --- a/src/atom/driver/camera.cpp +++ b/src/atom/driver/camera.cpp @@ -14,9 +14,8 @@ Description: Camera Simulator and Basic Definition #include "camera.hpp" -Camera::Camera(const std::string &name) : Device(name) +Camera::Camera(const std::string &name) : AtomDriver(name) { - init(); } Camera::~Camera() diff --git a/src/atom/driver/camera.hpp b/src/atom/driver/camera.hpp index 5127fee7..2cdef3da 100644 --- a/src/atom/driver/camera.hpp +++ b/src/atom/driver/camera.hpp @@ -45,7 +45,7 @@ class CameraFrame std::atomic_bool is_fastread; }; -class Camera : public Device +class Camera : public AtomDriver { public: /** diff --git a/src/atom/driver/device.cpp b/src/atom/driver/device.cpp index add6911f..d0b46906 100644 --- a/src/atom/driver/device.cpp +++ b/src/atom/driver/device.cpp @@ -13,6 +13,7 @@ Description: Basic Device Defination *************************************************/ #include "device.hpp" + #include "atom/utils/uuid.hpp" #ifdef __cpp_lib_format @@ -23,334 +24,18 @@ Description: Basic Device Defination #include #include -Device::Device(const std::string &name) : _name(name) -{ - _uuid = Atom::Property::UUIDGenerator::generateUUIDWithFormat(); -} - -Device::~Device() -{ -} - -void Device::init() -{ - setProperty("name", _name); - setProperty("uuid", _uuid); -} - -const std::string Device::getDeviceName() -{ - return _name; -} - -void Device::insertProperty(const std::string &name, const std::any &value, const std::string &bind_get_func, const std::string &bind_set_func, const std::any &possible_values, PossibleValueType possible_type, bool need_check) -{ - if (name.empty() || !value.has_value()) - throw InvalidParameters("Property name and value are required."); - try - { - // IStringProperty - if (value.type() == typeid(std::string) || value.type() == typeid(const char *)) - { - std::shared_ptr property = std::make_shared(); - property->device_name = _name; - property->name = name; - property->device_uuid = _uuid; - property->value = std::any_cast(value); - property->need_check = need_check; - property->pv_type = possible_type; - if (possible_values.has_value() && possible_type != PossibleValueType::None) - property->possible_values = std::any_cast>(possible_values); - m_properties[name] = property; - } - // INumberProperty - else if (value.type() == typeid(int) || value.type() == typeid(float) || value.type() == typeid(double)) - { - std::shared_ptr property = std::make_shared(); - property->device_name = _name; - property->name = name; - property->device_uuid = _uuid; - property->value = std::any_cast(value); - property->need_check = need_check; - property->pv_type = possible_type; - if (possible_values.has_value() && possible_type != PossibleValueType::None) - property->possible_values = std::any_cast>(possible_values); - property->get_func = bind_get_func; - property->set_func = bind_set_func; - m_properties[name] = property; - } - // IBoolProperty - else if (value.type() == typeid(bool)) - { - std::shared_ptr property = std::make_shared(); - property->device_name = _name; - property->name = name; - property->device_uuid = _uuid; - property->value = std::any_cast(value); - property->need_check = need_check; - property->pv_type = possible_type; - if (possible_values.has_value() && possible_type != PossibleValueType::None) - property->possible_values = std::any_cast>(possible_values); - m_properties[name] = property; - } - } - catch (const std::bad_any_cast &e) - { - throw InvalidProperty(e.what()); - } - catch (const std::exception &e) - { - } -} - -void Device::setProperty(const std::string &name, const std::any &value) -{ - if (m_properties.find(name) != m_properties.end()) - { - try - { - if (m_properties[name].type() == typeid(std::shared_ptr) || - m_properties[name].type() == typeid(std::shared_ptr) || - m_properties[name].type() == typeid(std::shared_ptr) || - m_properties[name].type() == typeid(std::shared_ptr)) - { - const auto property = m_properties[name]; - if (!std::any_cast>(property)->set_func.empty()) - { -#ifdef __cpp_lib_format - auto res = m_commander->Dispatch(std::format("set_{}", name), std::any_cast(value)); -#else - auto res = m_commander->Dispatch(fmt::format("set_{}", name), {{name, value}}); -#endif - if (res.find("error") != res.end()) - { - try - { - throw DispatchError(std::any_cast(res["error"])); - } - catch (const std::bad_any_cast &e) - { - throw InvalidReturn(e.what()); - } - } - } - if (property.type() == typeid(std::shared_ptr)) - { - auto pp = std::any_cast>(property); - pp->value = std::any_cast(value); - m_properties[name] = pp; - } - else if (property.type() == typeid(std::shared_ptr)) - { - auto pp = std::any_cast>(property); - pp->value = std::any_cast(value); - m_properties[name] = pp; - } - else if (property.type() == typeid(std::shared_ptr)) - { - auto pp = std::any_cast>(property); - pp->value = std::any_cast(value); - m_properties[name] = pp; - } - else if (property.type() == typeid(std::shared_ptr)) - { - auto pp = std::any_cast>(property); - pp->value = std::any_cast>(value); - m_properties[name] = pp; - } - else - { -#ifdef __cpp_lib_format - throw InvalidProperty(std::format("Unknown type of property {}", name)); -#else - -#endif - } - } - else - { -#ifdef __cpp_lib_format - throw InvalidProperty(std::format("Unknown type of property {}", name)); -#else - throw InvalidProperty(fmt::format("Unknown type of property {}", name)); -#endif - } - } - catch (const std::bad_any_cast &e) - { -#ifdef __cpp_lib_format - throw InvalidProperty(std::format("Failed to convert property {} with {}", name, e.what())); -#else - throw InvalidProperty(fmt::format("Failed to convert property {} with {}", name, e.what())); -#endif - } - } - else - { - insertProperty(name, value, "","", {}, PossibleValueType::None); - } -} - -std::any Device::getProperty(const std::string &name, bool need_refresh) +AtomDriver::AtomDriver(const std::string &name) : SharedComponent(), m_name(name), m_uuid(Atom::Property::UUIDGenerator::generateUUIDWithFormat()) { - if (m_properties.find(name) != m_properties.end()) - { - if (need_refresh) - { - std::any property = m_properties[name]; - bool has_func = false; - - try - { - if (property.type() == typeid(std::shared_ptr) || - property.type() == typeid(std::shared_ptr) || - property.type() == typeid(std::shared_ptr) || - property.type() == typeid(std::shared_ptr)) - { - if (!std::any_cast>(property)->get_func.empty()) - { - has_func = true; - } - } - else - { -#ifdef __cpp_lib_format - throw InvalidProperty(std::format("Unknown type of property {}", name)); -#else - throw InvalidProperty(fmt::format("Unknown type of property {}", name)); -#endif - } - } - catch (const std::bad_any_cast &e) - { -#ifdef __cpp_lib_format - throw InvalidProperty(std::format("Failed to convert property {} with {}", name, e.what())); -#else - throw InvalidProperty(fmt::format("Failed to convert property {} with {}", name, e.what())); -#endif - } - if (has_func) - { -#ifdef __cpp_lib_format - m_commander->Dispatch(std::format("get_{}", name), {}); -#else - m_commander->Dispatch(fmt::format("get_{}", name), {}); -#endif - } - } - return m_properties[name]; - } - return std::any(); + SetVariable("uuid", m_uuid); + SetVariable("name", m_name); } -std::shared_ptr Device::getNumberProperty(const std::string &name) -{ - try - { - auto property = getProperty("name"); - if (property.has_value()) - { - try - { - return std::any_cast>(property); - } - catch (const std::bad_any_cast &e) - { - throw InvalidProperty(e.what()); - } - } - else - return nullptr; - } - catch (const std::bad_any_cast &e) - { - throw InvalidProperty(e.what()); - } -} +AtomDriver::~AtomDriver() {} -std::shared_ptr Device::getStringProperty(const std::string &name) -{ - try - { - auto property = getProperty("name"); - if (property.has_value()) - { - try - { - return std::any_cast>(property); - } - catch (const std::exception &e) - { - throw InvalidProperty(e.what()); - } - } - else - return nullptr; - } - catch (const std::bad_any_cast &e) - { - throw InvalidProperty(e.what()); - } -} - -std::shared_ptr Device::getBoolProperty(const std::string &name) -{ - try - { - auto property = getProperty("name"); - if (property.has_value()) - { - try - { - return std::any_cast>(property); - } - catch (const std::exception &e) - { - throw InvalidProperty(e.what()); - } - } - else - return nullptr; - } - catch (const std::bad_any_cast &e) - { - throw InvalidProperty(e.what()); - } -} - -void Device::removeProperty(const std::string &name) -{ - if (m_properties.find(name) != m_properties.end()) - { - m_properties.erase(name); - } -} +bool AtomDriver::connect(const json ¶ms) { return true; }; -void Device::insertTask(const std::string &name, std::any defaultValue, json params_template, - const std::function &func, - const std::function &stop_func, - bool isBlock) -{ - if (name.empty() || !defaultValue.has_value()) - { - return; - } -} +bool AtomDriver::disconnect(const json ¶ms) { return true; }; -bool Device::removeTask(const std::string &name) -{ - if (name.empty()) - { - return false; - } - return true; -} +bool AtomDriver::reconnect(const json ¶ms) { return true; }; -std::shared_ptr Device::getTask(const std::string &name, const json ¶ms) -{ - if (name.empty()) - { - return nullptr; - } - return nullptr; -} +bool AtomDriver::isConnected() { return true; } diff --git a/src/atom/driver/device.hpp b/src/atom/driver/device.hpp index d77863f9..22feddba 100644 --- a/src/atom/driver/device.hpp +++ b/src/atom/driver/device.hpp @@ -12,9 +12,8 @@ Description: Basic Device Defination *************************************************/ -#pragma once - -#define ATOM_DRIVER_DEFINITION +#ifndef ATOM_DRIVER_HPP +#define ATOM_DRIVER_HPP #include #include @@ -27,88 +26,37 @@ Description: Basic Device Defination #include #endif -#include "atom/task/device_task.hpp" - -#include "atom/components/component.hpp" -#include "atom/type/args.hpp" +#include "atom/components/templates/shared_component.hpp" #include "device_exception.hpp" -#include "iproperty.hpp" - - -class Device : public Component +class AtomDriver : public SharedComponent { public: - explicit Device(const std::string &name); - virtual ~Device(); - -public: - virtual bool connect(const json ¶ms) { return true; }; - - virtual bool disconnect(const json ¶ms) { return true; }; - - virtual bool reconnect(const json ¶ms) { return true; }; - - virtual bool isConnected() { return true; } - - virtual void init(); - - const std::string getDeviceName(); + // ------------------------------------------------------------------- + // Common methods + // ------------------------------------------------------------------- - void insertProperty(const std::string &name, const std::any &value, const std::string &bind_get_func, const std::string &bind_set_func, const std::any &possible_values, PossibleValueType possible_type, bool need_check = false); + explicit AtomDriver(const std::string &name); - void setProperty(const std::string &name, const std::any &value); + virtual ~AtomDriver(); - std::any getProperty(const std::string &name, bool need_refresh = true); + // ------------------------------------------------------------------- + // Driver basic methods + // ------------------------------------------------------------------- - void removeProperty(const std::string &name); + virtual bool connect(const json ¶ms); - std::shared_ptr getNumberProperty(const std::string &name); + virtual bool disconnect(const json ¶ms); - std::shared_ptr getStringProperty(const std::string &name); + virtual bool reconnect(const json ¶ms); - std::shared_ptr getBoolProperty(const std::string &name); - - void insertTask(const std::string &name, std::any defaultValue, json params_template, - const std::function &func, - const std::function &stop_func, - bool isBlock = false); - - bool removeTask(const std::string &name); - - std::shared_ptr getTask(const std::string &name, const json ¶ms); + virtual bool isConnected(); private: - // Why we use emhash8 : because it is so fast! - // Map of the properties -#if ENABLE_FASTHASH - emhash8::HashMap m_properties; - emhash8::HashMap> m_task_map; -#else - std::unordered_map m_properties; - std::unordered_map> m_task_map; -#endif - std::unique_ptr> m_commander; - - // Basic Device name and UUID - std::string _name; - std::string _uuid; - -public: - typedef bool (*ConnectFunc)(const json ¶ms); - typedef bool (*DisconnectFunc)(const json ¶ms); - typedef bool (*ReconnectFunc)(const json ¶ms); - typedef void (*InitFunc)(); - typedef void (*InsertPropertyFunc)(const std::string &name, const std::any &value, const std::string &bind_get_func, const std::string &bind_set_func, const std::any &possible_values, PossibleValueType possible_type, bool need_check); - typedef void (*SetPropertyFunc)(const std::string &name, const std::any &value); - typedef std::any (*GetPropertyFunc)(const std::string &name); - typedef void (*RemovePropertyFunc)(const std::string &name); - typedef void (*InsertTaskFunc)(const std::string &name, std::any defaultValue, json params_template, const std::function &func, const std::function &stop_func, bool isBlock); - typedef bool (*RemoveTaskFunc)(const std::string &name); - typedef std::shared_ptr (*GetTaskFunc)(const std::string &name, const json ¶ms); - typedef void (*AddObserverFunc)(const std::function &observer); - typedef void (*RemoveObserverFunc)(const std::function &observer); - typedef const json (*ExportDeviceInfoTojsonFunc)(); + std::string m_name; + std::string m_uuid; }; + +#endif diff --git a/src/atom/driver/filterwheel.cpp b/src/atom/driver/filterwheel.cpp index 855181b1..e50dcf21 100644 --- a/src/atom/driver/filterwheel.cpp +++ b/src/atom/driver/filterwheel.cpp @@ -14,11 +14,8 @@ Description: Filterwheel Simulator and Basic Definition #include "filterwheel.hpp" -#include "atom/log/loguru.hpp" - -Filterwheel::Filterwheel(const std::string &name) : Device(name) +Filterwheel::Filterwheel(const std::string &name) : AtomDriver(name) { - init(); } Filterwheel::~Filterwheel() diff --git a/src/atom/driver/filterwheel.hpp b/src/atom/driver/filterwheel.hpp index 2b3eab60..d60d79a1 100644 --- a/src/atom/driver/filterwheel.hpp +++ b/src/atom/driver/filterwheel.hpp @@ -16,7 +16,7 @@ Description: Basic FilterWheel Defination #include "device.hpp" -class Filterwheel : public Device +class Filterwheel : public AtomDriver { public: Filterwheel(const std::string &name); diff --git a/src/atom/driver/focuser.cpp b/src/atom/driver/focuser.cpp index 00403ff1..1b3a35b1 100644 --- a/src/atom/driver/focuser.cpp +++ b/src/atom/driver/focuser.cpp @@ -14,28 +14,21 @@ Description: Focuser Simulator and Basic Definition #include "focuser.hpp" -#include "atom/log/loguru.hpp" - -Focuser::Focuser(const std::string &name) : Device(name) +Focuser::Focuser(const std::string &name) : AtomDriver(name) { - DLOG_F(INFO, "Focuser Simulator Loaded : %s", name.c_str()); - init(); } Focuser::~Focuser() { - DLOG_F(INFO, "Focuser Simulator Destructed"); } bool Focuser::connect(const json ¶ms) { - DLOG_F(INFO, "%s is connected", getDeviceName()); return true; } bool Focuser::disconnect(const json ¶ms) { - DLOG_F(INFO, "%s is disconnected", getDeviceName()); return true; } diff --git a/src/atom/driver/focuser.hpp b/src/atom/driver/focuser.hpp index 5981f8a1..65ed2745 100644 --- a/src/atom/driver/focuser.hpp +++ b/src/atom/driver/focuser.hpp @@ -16,7 +16,7 @@ Description: Basic Focuser Defination #include "device.hpp" -class Focuser : public Device +class Focuser : public AtomDriver { public: /** diff --git a/src/atom/driver/guider.cpp b/src/atom/driver/guider.cpp index 48e0b219..ba181857 100644 --- a/src/atom/driver/guider.cpp +++ b/src/atom/driver/guider.cpp @@ -14,28 +14,21 @@ Description: Guider Simulator and Basic Definition #include "guider.hpp" -#include "atom/log/loguru.hpp" - -Guider::Guider(const std::string &name) : Device(name) +Guider::Guider(const std::string &name) : AtomDriver(name) { - DLOG_F(INFO, "Guider Simulator Loaded : %s", name.c_str()); - init(); } Guider::~Guider() { - DLOG_F(INFO, "Guider Simulator Destructed"); } bool Guider::connect(const nlohmann::json ¶ms) { - DLOG_F(INFO, "%s is connected", getDeviceName()); return true; } bool Guider::disconnect(const nlohmann::json ¶ms) { - DLOG_F(INFO, "%s is disconnected", getDeviceName()); return true; } diff --git a/src/atom/driver/guider.hpp b/src/atom/driver/guider.hpp index 97005778..2a968a99 100644 --- a/src/atom/driver/guider.hpp +++ b/src/atom/driver/guider.hpp @@ -16,7 +16,7 @@ Description: Basic Guider Defination #include "device.hpp" -class Guider : public Device +class Guider : public AtomDriver { public: /** diff --git a/src/atom/driver/iphoto.cpp b/src/atom/driver/iphoto.cpp deleted file mode 100644 index e8a7461d..00000000 --- a/src/atom/driver/iphoto.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * iphoto.cpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-3-29 - -Description: Photo type definition - -**************************************************/ - -#include "iphoto.hpp" -#include "atom/utils/uuid.hpp" - -IPhoto::IPhoto() -{ - message_uuid = Atom::Property::UUIDGenerator::generateUUIDWithFormat(); -} - -const std::string IPhoto::toJson() const -{ - std::stringstream ss; - ss << "{"; - ss << "\"device_name\":\"" << device_name << "\","; - ss << "\"device_uuid\":\"" << device_uuid << "\","; - ss << "\"message_uuid\":\"" << message_uuid << "\","; - ss << "\"name\":\"" << name << "\","; - ss << "\"value\":{"; - ss << "\"width\":" << width << ","; - ss << "\"height\":" << height << ","; - ss << "\"depth\":" << depth << ","; - ss << "\"gain\":" << gain << ","; - ss << "\"iso\":" << iso << ","; - ss << "\"offset\":" << offset << ","; - ss << "\"binning\":" << binning << ","; - ss << "\"duration\":" << duration << ","; - ss << "\"is_color\":" << std::boolalpha << is_color << ","; - ss << "\"center_ra\":\"" << center_ra << "\","; - ss << "\"center_dec\":\"" << center_dec << "\","; - ss << "\"author\":\"" << author << "\","; - ss << "\"time\":\"" << time << "\","; - ss << "\"software\":\"" << software << "\""; - ss << "}"; - ss << "}"; - return ss.str(); -} - -const std::string IPhoto::toXml() const -{ - std::stringstream ss; - ss << ""; - ss << "" << device_name << ""; - ss << "" << device_uuid << ""; - ss << "" << message_uuid << ""; - ss << "" << name << ""; - ss << ""; - ss << "" << width << ""; - ss << "" << height << ""; - ss << "" << depth << ""; - ss << "" << gain << ""; - ss << "" << iso << ""; - ss << "" << offset << ""; - ss << "" << binning << ""; - ss << "" << duration << ""; - ss << "" << std::boolalpha << is_color << ""; - ss << "" << center_ra << ""; - ss << "" << center_dec << ""; - ss << "" << author << ""; - ss << ""; - ss << "" << software << ""; - ss << ""; - ss << ""; - return ss.str(); -} \ No newline at end of file diff --git a/src/atom/driver/iphoto.hpp b/src/atom/driver/iphoto.hpp deleted file mode 100644 index 985e8794..00000000 --- a/src/atom/driver/iphoto.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * iphoto.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-3-29 - -Description: Photo type definition - -**************************************************/ - -#pragma once - -#include - -struct IPhoto -{ - IPhoto(); - std::string device_name; - std::string device_uuid; - std::string message_uuid; - std::string name; - int width; - int height; - int depth; - - int gain; - int iso; - int offset; - int binning; - - double duration; - - bool is_color; - - std::string center_ra; - std::string center_dec; - - std::string author; - std::string time; - std::string software = "Lithium-Server"; - - const std::string toJson() const; - const std::string toXml() const; -}; \ No newline at end of file diff --git a/src/atom/driver/iproperty.cpp b/src/atom/driver/iproperty.cpp deleted file mode 100644 index 20afafea..00000000 --- a/src/atom/driver/iproperty.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * iproperty.cpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-3-29 - -Description: Property type definition - -**************************************************/ - -#include "iproperty.hpp" -#include "atom/utils/uuid.hpp" - -IPropertyBase::IPropertyBase() -{ - message_uuid = Atom::Property::UUIDGenerator::generateUUIDWithFormat(); -} - -INumberProperty::INumberProperty() : IPropertyBase() -{ -} - -IStringProperty::IStringProperty() : IPropertyBase() -{ -} - -IBoolProperty::IBoolProperty() : IPropertyBase() -{ -} - -INumberVector::INumberVector() : IPropertyBase() -{ -} \ No newline at end of file diff --git a/src/atom/driver/iproperty.hpp b/src/atom/driver/iproperty.hpp deleted file mode 100644 index 1bafe3aa..00000000 --- a/src/atom/driver/iproperty.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * iproperty.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-3-29 - -Description: Property type definition - -**************************************************/ - -#pragma once - -#include -#include -#include - -enum class PossibleValueType -{ - None, - Range, - Value -}; - -struct IPropertyBase -{ - IPropertyBase(); - std::string device_name; - std::string device_uuid; - std::string message_uuid; - std::string name; - bool need_check; - PossibleValueType pv_type; - - std::string get_func; - std::string set_func; -}; - -struct INumberProperty : public IPropertyBase -{ - INumberProperty(); - double value; - std::vector possible_values; -}; - -struct IStringProperty : public IPropertyBase -{ - IStringProperty(); - std::string value; - std::vector possible_values; -}; - -struct IBoolProperty : public IPropertyBase -{ - IBoolProperty(); - bool value; - std::vector possible_values; -}; - -struct INumberVector : public IPropertyBase -{ - INumberVector(); - std::vector value; - std::vector> possible_values; -}; diff --git a/src/atom/driver/solver.cpp b/src/atom/driver/solver.cpp index 9af10ee9..2e07cf3b 100644 --- a/src/atom/driver/solver.cpp +++ b/src/atom/driver/solver.cpp @@ -14,7 +14,7 @@ Description: Basic Solver Defination #include "solver.hpp" -Solver::Solver(const std::string &name) : Device(name) +Solver::Solver(const std::string &name) : AtomDriver(name) { } diff --git a/src/atom/driver/solver.hpp b/src/atom/driver/solver.hpp index 34265a6f..8da6d4e7 100644 --- a/src/atom/driver/solver.hpp +++ b/src/atom/driver/solver.hpp @@ -16,7 +16,7 @@ Description: Basic Solver Defination #include "device.hpp" -class Solver : public Device +class Solver : public AtomDriver { public: Solver(const std::string &name); diff --git a/src/atom/driver/telescope.cpp b/src/atom/driver/telescope.cpp index 2ffff57d..3ae421a1 100644 --- a/src/atom/driver/telescope.cpp +++ b/src/atom/driver/telescope.cpp @@ -14,11 +14,8 @@ Description: Telescope Simulator and Basic Definition #include "telescope.hpp" -#include "atom/log/loguru.hpp" - -Telescope::Telescope(const std::string &name) : Device(name) +Telescope::Telescope(const std::string &name) : AtomDriver(name) { - init(); } Telescope::~Telescope() diff --git a/src/atom/driver/telescope.hpp b/src/atom/driver/telescope.hpp index 417c3866..1606ecd9 100644 --- a/src/atom/driver/telescope.hpp +++ b/src/atom/driver/telescope.hpp @@ -16,7 +16,7 @@ Description: Basic Telescope Defination #include "device.hpp" -class Telescope : public Device +class Telescope : public AtomDriver { public: /** diff --git a/src/atom/extra/CMakeLists.txt b/src/atom/extra/CMakeLists.txt new file mode 100644 index 00000000..c0f42928 --- /dev/null +++ b/src/atom/extra/CMakeLists.txt @@ -0,0 +1,11 @@ +# CMakeLists.txt for Atom-Extra +# This project is licensed under the terms of the GPL3 license. +# +# Project Name: Atom-Task +# Description: All of the built-in thirdparty libraries +# Author: Max Qian +# License: GPL3 + +add_subdirectory(httplib) + +add_subdirectory(tinyxml2) \ No newline at end of file diff --git a/src/atom/extra/httplib/CMakeLists.txt b/src/atom/extra/httplib/CMakeLists.txt new file mode 100644 index 00000000..83e84f3a --- /dev/null +++ b/src/atom/extra/httplib/CMakeLists.txt @@ -0,0 +1,11 @@ +find_package(OpenSSL REQUIRED) +add_library(cpp_httplib httplib.cpp) +target_link_libraries(cpp_httplib OpenSSL::SSL OpenSSL::Crypto) +if(WIN32) +target_link_libraries(cpp_httplib crypt32 wsock32 ws2_32) +endif() +if(LINUX) + set_target_properties(cpp_httplib PROPERTIES + POSITION_INDEPENDENT_CODE ON + ) +endif() diff --git a/src/atom/web/httplib.cpp b/src/atom/extra/httplib/httplib.cpp similarity index 100% rename from src/atom/web/httplib.cpp rename to src/atom/extra/httplib/httplib.cpp diff --git a/src/atom/web/httplib.h b/src/atom/extra/httplib/httplib.h similarity index 100% rename from src/atom/web/httplib.h rename to src/atom/extra/httplib/httplib.h diff --git a/src/atom/extra/tinyxml2/CMakeLists.txt b/src/atom/extra/tinyxml2/CMakeLists.txt new file mode 100644 index 00000000..68beac4b --- /dev/null +++ b/src/atom/extra/tinyxml2/CMakeLists.txt @@ -0,0 +1,114 @@ +cmake_minimum_required(VERSION 3.15) +project(tinyxml2 VERSION 9.0.0) + +## +## Honor tinyxml2_SHARED_LIBS to match install interface +## + +if (DEFINED tinyxml2_SHARED_LIBS) + set(BUILD_SHARED_LIBS "${tinyxml2_SHARED_LIBS}") +endif () + +## +## Main library build +## + +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN YES) + +add_library(tinyxml2 tinyxml2.cpp tinyxml2.h) +add_library(tinyxml2::tinyxml2 ALIAS tinyxml2) + +# Uncomment the following line to require C++11 (or greater) to use tinyxml2 +# target_compile_features(tinyxml2 PUBLIC cxx_std_11) +target_include_directories(tinyxml2 PUBLIC "$") + +target_compile_definitions( + tinyxml2 + PUBLIC $<$:TINYXML2_DEBUG> + INTERFACE $<$:TINYXML2_IMPORT> + PRIVATE $<$:_CRT_SECURE_NO_WARNINGS> + PUBLIC _FILE_OFFSET_BITS=64 +) + +set_target_properties( + tinyxml2 + PROPERTIES + DEFINE_SYMBOL "TINYXML2_EXPORT" + VERSION "${tinyxml2_VERSION}" + SOVERSION "${tinyxml2_VERSION_MAJOR}" +) + +## +## Installation +## + +## Standard modules +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +## Custom locations +set(tinyxml2_INSTALL_PKGCONFIGDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + CACHE PATH "Directory for pkgconfig files") + +set(tinyxml2_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/tinyxml2" + CACHE STRING "Path to tinyxml2 CMake files") + +## CMake targets and export scripts + +install( + TARGETS tinyxml2 EXPORT tinyxml2-targets + RUNTIME COMPONENT tinyxml2_runtime + LIBRARY COMPONENT tinyxml2_runtime + NAMELINK_COMPONENT tinyxml2_development + ARCHIVE COMPONENT tinyxml2_development + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) + +# Type-specific targets + +if (BUILD_SHARED_LIBS) + set(type shared) +else () + set(type static) +endif () + +install( + EXPORT tinyxml2-targets + DESTINATION "${tinyxml2_INSTALL_CMAKEDIR}" + NAMESPACE tinyxml2:: + FILE tinyxml2-${type}-targets.cmake + COMPONENT tinyxml2_development +) + +# Auto-generated version compatibility file +write_basic_package_version_file( + tinyxml2-config-version.cmake + COMPATIBILITY SameMajorVersion +) + +install( + FILES + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/tinyxml2-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/tinyxml2-config-version.cmake" + DESTINATION "${tinyxml2_INSTALL_CMAKEDIR}" + COMPONENT tinyxml2_development +) + +## Headers + +install( + FILES tinyxml2.h + TYPE INCLUDE + COMPONENT tinyxml2_development +) + +## pkg-config + +configure_file(cmake/tinyxml2.pc.in tinyxml2.pc.gen @ONLY) +file(GENERATE OUTPUT tinyxml2.pc INPUT "${CMAKE_CURRENT_BINARY_DIR}/tinyxml2.pc.gen") +install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/tinyxml2.pc" + DESTINATION "${tinyxml2_INSTALL_PKGCONFIGDIR}" + COMPONENT tinyxml2_development +) diff --git a/src/atom/type/cmake/tinyxml2-config.cmake b/src/atom/extra/tinyxml2/cmake/tinyxml2-config.cmake similarity index 100% rename from src/atom/type/cmake/tinyxml2-config.cmake rename to src/atom/extra/tinyxml2/cmake/tinyxml2-config.cmake diff --git a/src/atom/type/cmake/tinyxml2.pc.in b/src/atom/extra/tinyxml2/cmake/tinyxml2.pc.in similarity index 100% rename from src/atom/type/cmake/tinyxml2.pc.in rename to src/atom/extra/tinyxml2/cmake/tinyxml2.pc.in diff --git a/src/atom/type/tinyxml2.cpp b/src/atom/extra/tinyxml2/tinyxml2.cpp similarity index 100% rename from src/atom/type/tinyxml2.cpp rename to src/atom/extra/tinyxml2/tinyxml2.cpp diff --git a/src/atom/type/tinyxml2.h b/src/atom/extra/tinyxml2/tinyxml2.h similarity index 100% rename from src/atom/type/tinyxml2.h rename to src/atom/extra/tinyxml2/tinyxml2.h diff --git a/src/atom/io/io.hpp b/src/atom/io/io.hpp index fa4d260a..74bbc359 100644 --- a/src/atom/io/io.hpp +++ b/src/atom/io/io.hpp @@ -18,7 +18,7 @@ Description: IO #include #include #include -using fs = std::filesystem; +namespace fs = std::filesystem; namespace Atom::IO { diff --git a/src/atom/log/logger.cpp b/src/atom/log/logger.cpp index 572f9e8f..bb79a32e 100644 --- a/src/atom/log/logger.cpp +++ b/src/atom/log/logger.cpp @@ -24,7 +24,7 @@ Description: Custom Logger Manager #include -#include "atom/web/httplib.h" +#include "atom/extra/httplib/httplib.h" #include "atom/log/loguru.hpp" namespace Lithium diff --git a/src/atom/server/search.cpp b/src/atom/search/search.cpp similarity index 100% rename from src/atom/server/search.cpp rename to src/atom/search/search.cpp diff --git a/src/atom/server/search.hpp b/src/atom/search/search.hpp similarity index 100% rename from src/atom/server/search.hpp rename to src/atom/search/search.hpp diff --git a/src/atom/server/CMakeLists.txt b/src/atom/server/CMakeLists.txt new file mode 100644 index 00000000..594b29e9 --- /dev/null +++ b/src/atom/server/CMakeLists.txt @@ -0,0 +1,64 @@ +# CMakeLists.txt for Atom-Server +# This project is licensed under the terms of the GPL3 license. +# +# Project Name: Atom-Server +# Description: Core Server Components for Element Astro Projects +# Author: Max Qian +# License: GPL3 + +cmake_minimum_required(VERSION 3.20) +project(atom-server C CXX) + +# Sources +set(${PROJECT_NAME}_SOURCES + daemon.cpp + deserialize.cpp + global_ptr.cpp + json_checker.cpp + serialize.cpp + variables.cpp +) + +# Headers +set(${PROJECT_NAME}_HEADERS + commander_impl.hpp + commander.hpp + daemon.hpp + deserialize.hpp + global_ptr.hpp + json_checker.hpp + message_bus.hpp + message_queue.hpp + serialize.hpp + variables.hpp +) + +# Build Object Library +add_library(${PROJECT_NAME}_OBJECT OBJECT) +set_property(TARGET ${PROJECT_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1) + +target_link_libraries(${PROJECT_NAME}_OBJECT loguru) + +target_sources(${PROJECT_NAME}_OBJECT + PUBLIC + ${${PROJECT_NAME}_HEADERS} + PRIVATE + ${${PROJECT_NAME}_SOURCES} +) + +target_link_libraries(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) + +add_library(${PROJECT_NAME} STATIC) + +target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_include_directories(${PROJECT_NAME} PUBLIC .) + +set_target_properties(${PROJECT_NAME} PROPERTIES + VERSION ${CMAKE_HYDROGEN_VERSION_STRING} + SOVERSION ${HYDROGEN_SOVERSION} + OUTPUT_NAME ${PROJECT_NAME} +) + +install(TARGETS ${PROJECT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) diff --git a/src/atom/server/commander.hpp b/src/atom/server/commander.hpp index a2414f7f..54a347ad 100644 --- a/src/atom/server/commander.hpp +++ b/src/atom/server/commander.hpp @@ -12,7 +12,8 @@ Description: Commander **************************************************/ -#pragma once +#ifndef ATOM_SERVER_COMMANDER_HPP +#define ATOM_SERVER_COMMANDER_HPP #include #include @@ -128,154 +129,6 @@ class CommandDispatcher mutable std::shared_mutex m_sharedMutex; }; -template -void CommandDispatcher::RegisterHandler(const std::string &name, const HandlerFunc &handler, const HandlerFunc &undoHandler) -{ - if (name.empty()) - { - return; - } - std::unique_lock lock(m_sharedMutex); - if (handler) - { - handlers_[name] = handler; - } - if (undoHandler) - { - undoHandlers_[name] = undoHandler; - } -} - -template -template -void CommandDispatcher::RegisterMemberHandler(const std::string &name, T *object, Result (T::*memberFunc)(const Argument &)) -{ - auto handler = std::bind(memberFunc, object, std::placeholders::_1); - RegisterHandler(name, handler); -} - -template -bool CommandDispatcher::HasHandler(const std::string &name) -{ - std::shared_lock lock(m_sharedMutex); - return handlers_.find(name) != handlers_.end(); -} - -template -std::function CommandDispatcher::GetHandler(const std::string &name) -{ - std::shared_lock lock(m_sharedMutex); - auto it = handlers_.find(name); - if (it != handlers_.end()) - { - return it->second; - } - return nullptr; -} - -template -Result CommandDispatcher::Dispatch(const std::string &name, const Argument &data) -{ - std::shared_lock lock(m_sharedMutex); - auto it = handlers_.find(name); - if (it != handlers_.end()) - { - return it->second(data); - } - return Result(); -} - -template -bool CommandDispatcher::Undo() -{ - if (commandHistory_.empty()) - { - return false; - } - auto lastCommand = commandHistory_.top(); - commandHistory_.pop(); - undoneCommands_.push(lastCommand); - - auto it = undoHandlers_.find(Djb2Hash(lastCommand.first.c_str())); - if (it != undoHandlers_.end()) - { - it->second(lastCommand.second); - } +#include "commander_impl.hpp" - return true; -} - -template -bool CommandDispatcher::Redo() -{ - if (undoneCommands_.empty()) - { - return false; - } - auto lastUndoneCommand = undoneCommands_.top(); - undoneCommands_.pop(); - commandHistory_.push(lastUndoneCommand); - - auto it = handlers_.find(Djb2Hash(lastUndoneCommand.first.c_str())); - if (it != handlers_.end()) - { - it->second(lastUndoneCommand.second); - } - - return true; -} - -template -bool CommandDispatcher::RemoveAll() -{ - std::unique_lock lock(m_sharedMutex); - handlers_.clear(); - undoHandlers_.clear(); - descriptions_.clear(); - while (!commandHistory_.empty()) - { - commandHistory_.pop(); - } - return true; -} - -template -void CommandDispatcher::RegisterFunctionDescription(const std::string &name, const std::string &description) -{ - if (name.empty() || description.empty()) - { - return; - } - std::unique_lock lock(m_sharedMutex); - descriptions_[name] = description; -} - -template -std::string CommandDispatcher::GetFunctionDescription(const std::string &name) -{ - std::shared_lock lock(m_sharedMutex); - auto it = descriptions_.find(name); - if (it != descriptions_.end()) - { - return it->second; - } - return ""; -} - -template -void CommandDispatcher::RemoveFunctionDescription(const std::string &name) -{ - std::unique_lock lock(m_sharedMutex); - if (name.empty()) - { - return; - } - descriptions_.erase(name); -} - -template -void CommandDispatcher::ClearFunctionDescriptions() -{ - std::unique_lock lock(m_sharedMutex); - descriptions_.clear(); -} \ No newline at end of file +#endif diff --git a/src/atom/server/commander_impl.hpp b/src/atom/server/commander_impl.hpp new file mode 100644 index 00000000..450b11e9 --- /dev/null +++ b/src/atom/server/commander_impl.hpp @@ -0,0 +1,170 @@ +/* + * commander_impl.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-6-17 + +Description: Commander + +**************************************************/ + +#ifndef ATOM_SERVER_COMMANDER_IMPL_HPP +#define ATOM_SERVER_COMMANDER_IMPL_HPP + +template +void CommandDispatcher::RegisterHandler(const std::string &name, const HandlerFunc &handler, const HandlerFunc &undoHandler) +{ + if (name.empty()) + { + return; + } + std::unique_lock lock(m_sharedMutex); + if (handler) + { + handlers_[name] = handler; + } + if (undoHandler) + { + undoHandlers_[name] = undoHandler; + } +} + +template +template +void CommandDispatcher::RegisterMemberHandler(const std::string &name, T *object, Result (T::*memberFunc)(const Argument &)) +{ + auto handler = std::bind(memberFunc, object, std::placeholders::_1); + RegisterHandler(name, handler); +} + +template +bool CommandDispatcher::HasHandler(const std::string &name) +{ + std::shared_lock lock(m_sharedMutex); + return handlers_.find(name) != handlers_.end(); +} + +template +std::function CommandDispatcher::GetHandler(const std::string &name) +{ + std::shared_lock lock(m_sharedMutex); + auto it = handlers_.find(name); + if (it != handlers_.end()) + { + return it->second; + } + return nullptr; +} + +template +Result CommandDispatcher::Dispatch(const std::string &name, const Argument &data) +{ + std::shared_lock lock(m_sharedMutex); + auto it = handlers_.find(name); + if (it != handlers_.end()) + { + return it->second(data); + } + return Result(); +} + +template +bool CommandDispatcher::Undo() +{ + if (commandHistory_.empty()) + { + return false; + } + auto lastCommand = commandHistory_.top(); + commandHistory_.pop(); + undoneCommands_.push(lastCommand); + + auto it = undoHandlers_.find(Djb2Hash(lastCommand.first.c_str())); + if (it != undoHandlers_.end()) + { + it->second(lastCommand.second); + } + + return true; +} + +template +bool CommandDispatcher::Redo() +{ + if (undoneCommands_.empty()) + { + return false; + } + auto lastUndoneCommand = undoneCommands_.top(); + undoneCommands_.pop(); + commandHistory_.push(lastUndoneCommand); + + auto it = handlers_.find(Djb2Hash(lastUndoneCommand.first.c_str())); + if (it != handlers_.end()) + { + it->second(lastUndoneCommand.second); + } + + return true; +} + +template +bool CommandDispatcher::RemoveAll() +{ + std::unique_lock lock(m_sharedMutex); + handlers_.clear(); + undoHandlers_.clear(); + descriptions_.clear(); + while (!commandHistory_.empty()) + { + commandHistory_.pop(); + } + return true; +} + +template +void CommandDispatcher::RegisterFunctionDescription(const std::string &name, const std::string &description) +{ + if (name.empty() || description.empty()) + { + return; + } + std::unique_lock lock(m_sharedMutex); + descriptions_[name] = description; +} + +template +std::string CommandDispatcher::GetFunctionDescription(const std::string &name) +{ + std::shared_lock lock(m_sharedMutex); + auto it = descriptions_.find(name); + if (it != descriptions_.end()) + { + return it->second; + } + return ""; +} + +template +void CommandDispatcher::RemoveFunctionDescription(const std::string &name) +{ + std::unique_lock lock(m_sharedMutex); + if (name.empty()) + { + return; + } + descriptions_.erase(name); +} + +template +void CommandDispatcher::ClearFunctionDescriptions() +{ + std::unique_lock lock(m_sharedMutex); + descriptions_.clear(); +} + +#endif \ No newline at end of file diff --git a/src/atom/server/deserialize.cpp b/src/atom/server/deserialize.cpp index ab152178..fd44ab28 100644 --- a/src/atom/server/deserialize.cpp +++ b/src/atom/server/deserialize.cpp @@ -14,7 +14,6 @@ Description: This file contains the declaration of the DeserializationEngine cla #include "deserialize.hpp" -#include "atom/type/iparams.hpp" #include "atom/type/json.hpp" #include "atom/log/loguru.hpp" @@ -36,27 +35,6 @@ std::optional JsonDeserializer::deserialize(const std::string &data) c } } -std::optional JsonParamsDeserializer::deserialize(const std::string &data) const -{ - DLOG_F(INFO, "JsonParamsDeserializer::deserialize called with {}", data); - try - { - std::shared_ptr params; - if (!params->fromJson(data)) - { - LOG_F(ERROR, "JsonParamsDeserializer::deserialize: Failed to deserialize json data."); - return std::nullopt; - } - DLOG_F(INFO, "JsonParamsDeserializer::deserialize: Successfully deserialized json data."); - return params; - } - catch (const std::exception &) - { - LOG_F(ERROR, "JsonParamsDeserializer::deserialize: Failed to deserialize json data."); - return std::nullopt; - } -} - namespace Atom::Server { void DeserializationEngine::addDeserializeEngine(const std::string &name, const std::shared_ptr &DeserializeEngine) diff --git a/src/atom/server/deserialize.hpp b/src/atom/server/deserialize.hpp index 1490f8b1..54495d23 100644 --- a/src/atom/server/deserialize.hpp +++ b/src/atom/server/deserialize.hpp @@ -38,12 +38,6 @@ class JsonDeserializer : public DeserializeEngine std::optional deserialize(const std::string &data) const override; }; -class JsonParamsDeserializer : public DeserializeEngine -{ -public: - std::optional deserialize(const std::string &data) const override; -}; - namespace Atom::Server { /** diff --git a/src/atom/server/serialize.cpp b/src/atom/server/serialize.cpp index 2567cdb2..3e6a7226 100644 --- a/src/atom/server/serialize.cpp +++ b/src/atom/server/serialize.cpp @@ -23,8 +23,6 @@ Description: This file contains the declaration of the SerializationEngine class #include #endif -#include "atom/driver/iproperty.hpp" -#include "atom/type/iparams.hpp" #include "atom/log/loguru.hpp" std::string JsonSerializationEngine::serialize(const std::any &data, bool format) const @@ -41,82 +39,6 @@ std::string JsonSerializationEngine::serialize(const std::any &data, bool format LOG_F(ERROR, "Failed to serialize message: {}", e.what()); } } - else - { - if (data.type() == typeid(std::shared_ptr)) - { - try - { - std::shared_ptr prop = std::any_cast>(data); - _data["device_name"] = prop->device_name; - _data["device_uuid"] = prop->device_uuid; - _data["message_uuid"] = prop->message_uuid; - _data["name"] = prop->name; - _data["need_check"] = prop->need_check ? "true" : "false"; - _data["get_func"] = prop->get_func; - _data["set_func"] = prop->set_func; - _data["value"] = prop->value ? "true" : "false"; - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Failed to serialize bool property message: {}", e.what()); - } - } - else if (data.type() == typeid(std::shared_ptr)) - { - try - { - std::shared_ptr prop = std::any_cast>(data); - _data["device_name"] = prop->device_name; - _data["device_uuid"] = prop->device_uuid; - _data["message_uuid"] = prop->message_uuid; - _data["name"] = prop->name; - _data["need_check"] = prop->need_check ? "true" : "false"; - _data["get_func"] = prop->get_func; - _data["set_func"] = prop->set_func; - _data["value"] = prop->value; - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Failed to serialize bool property message: {}", e.what()); - } - } - else if (data.type() == typeid(std::shared_ptr)) - { - try - { - std::shared_ptr prop = std::any_cast>(data); - _data["device_name"] = prop->device_name; - _data["device_uuid"] = prop->device_uuid; - _data["message_uuid"] = prop->message_uuid; - _data["name"] = prop->name; - _data["need_check"] = prop->need_check ? "true" : "false"; - _data["get_func"] = prop->get_func; - _data["set_func"] = prop->set_func; - _data["value"] = std::to_string(prop->value); - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Failed to serialize bool property message: {}", e.what()); - } - } - else if (data.type() == typeid(std::shared_ptr)) - { - try - { - return std::any_cast>(data)->toJson(); - } - catch (const std::bad_any_cast &e) - { - LOG_F(ERROR, "Failed to serialize bool property message: {}", e.what()); - } - } - else - { - LOG_F(ERROR, "Unknown type of message!"); - return ""; - } - } std::ostringstream oss; if (format) { diff --git a/src/atom/system/system.cpp b/src/atom/system/system.cpp index 202f96ff..b5c2e1ae 100644 --- a/src/atom/system/system.cpp +++ b/src/atom/system/system.cpp @@ -502,7 +502,7 @@ namespace Atom::System storage_device_models.push_back(std::make_pair(drivePath, model)); } } - drive += strlen_s(drive) + 1; + drive += strlen(drive) + 1; } } #else diff --git a/src/atom/task/CMakeLists.txt b/src/atom/task/CMakeLists.txt index 4b73db9f..d56adf2a 100644 --- a/src/atom/task/CMakeLists.txt +++ b/src/atom/task/CMakeLists.txt @@ -1,3 +1,11 @@ +# CMakeLists.txt for Atom-Task +# This project is licensed under the terms of the GPL3 license. +# +# Project Name: Atom-Task +# Description: Core Task Definitions +# Author: Max Qian +# License: GPL3 + cmake_minimum_required(VERSION 3.20) project(atom-task C CXX) @@ -15,11 +23,6 @@ list(APPEND ${PROJECT_NAME}_HEADERS task.hpp ) -# Private Headers -list(APPEND ${PROJECT_NAME}_PRIVATE_HEADERS - -) - # Build Object Library add_library(${PROJECT_NAME}_OBJECT OBJECT) set_property(TARGET ${PROJECT_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1) @@ -29,23 +32,22 @@ target_sources(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_HEADERS} PRIVATE ${${PROJECT_NAME}_SOURCES} - ${${PROJECT_NAME}_PRIVATE_HEADERS} ) target_link_libraries(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) -add_library(${PROJECT_NAME}static STATIC) +add_library(${PROJECT_NAME} STATIC) -target_link_libraries(${PROJECT_NAME}static ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) -target_link_libraries(${PROJECT_NAME}static ${CMAKE_THREAD_LIBS_INIT}) -target_include_directories(${PROJECT_NAME}static PUBLIC .) +target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) +target_include_directories(${PROJECT_NAME} PUBLIC .) -set_target_properties(${PROJECT_NAME}static PROPERTIES +set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${CMAKE_HYDROGEN_VERSION_STRING} SOVERSION ${HYDROGEN_SOVERSION} OUTPUT_NAME ${PROJECT_NAME} # this same name like shared library - backwards compatibility ) -install(TARGETS ${PROJECT_NAME}static +install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) \ No newline at end of file diff --git a/src/atom/task/conditional_task.cpp b/src/atom/task/conditional_task.cpp index 59a58bab..12f10c46 100644 --- a/src/atom/task/conditional_task.cpp +++ b/src/atom/task/conditional_task.cpp @@ -14,15 +14,17 @@ Description: Conditional Task Definition #include "conditional_task.hpp" -ConditionalTask::ConditionalTask(const std::function &func, - const std::function &stop_fn, - const json ¶ms_template, - const std::function &condition_fn, - bool isForce = false) - : SimpleTask(func, stop_fn, params_template), m_conditionFunc(condition_fn), m_isForce(isForce){} +ConditionalTask::ConditionalTask(const std::function &task_fn, + const std::function &condition_fn, + const std::function &stop_fn, + const json ¶ms_template, + bool isForce) + : SimpleTask(task_fn, stop_fn, params_template), m_conditionFunc(condition_fn), m_isForce(isForce) +{ +} // Executes the task -const json ConditionalTask::execute() +json ConditionalTask::execute() { m_isExecuting.store(true); if (!m_paramsTemplate.is_null() && !m_params.is_null()) @@ -45,7 +47,7 @@ const json ConditionalTask::execute() } // Serializes the task to a JSON object -const json ConditionalTask::toJson() const +json ConditionalTask::toJson() { auto json = SimpleTask::toJson(); json["type"] = "conditional"; diff --git a/src/atom/task/conditional_task.hpp b/src/atom/task/conditional_task.hpp index 76164c0c..051b9272 100644 --- a/src/atom/task/conditional_task.hpp +++ b/src/atom/task/conditional_task.hpp @@ -29,10 +29,10 @@ class ConditionalTask : public SimpleTask * @param isForce A flag indicating whether to force execution of the task even if the condition is not met. Default is false. */ ConditionalTask(const std::function &task_fn, - const std::function &stop_fn = nullptr, - const json ¶ms_template = json(), - const std::function &condition_fn, - bool isForce = false); + const std::function &condition_fn, + const std::function &stop_fn = nullptr, + const json ¶ms_template = json(), + bool isForce = false); /** * @brief Executes the task. @@ -41,14 +41,14 @@ class ConditionalTask : public SimpleTask * * @return The result of the task execution in JSON format. */ - virtual const json execute() override; + virtual json execute() override; /** * @brief Serializes the task to a JSON object. * * @return A JSON object representing the task. */ - virtual const json toJson() const override; + virtual json toJson() override; private: // The condition function used to determine whether to execute the task. diff --git a/src/atom/task/loop_task.cpp b/src/atom/task/loop_task.cpp index 3f640cdc..a1f4cecd 100644 --- a/src/atom/task/loop_task.cpp +++ b/src/atom/task/loop_task.cpp @@ -22,7 +22,7 @@ LoopTask::LoopTask(const std::function &func, { } -const json LoopTask::execute() +json LoopTask::execute() { if (m_isExecuting.load()) { @@ -70,7 +70,7 @@ const json LoopTask::execute() return m_returns; } -const json LoopTask::toJson() const +json LoopTask::toJson() { auto json = SimpleTask::toJson(); json["type"] = "loop"; diff --git a/src/atom/task/loop_task.hpp b/src/atom/task/loop_task.hpp index bb0afc9c..8465e8d8 100644 --- a/src/atom/task/loop_task.hpp +++ b/src/atom/task/loop_task.hpp @@ -39,14 +39,14 @@ class LoopTask : public SimpleTask * * @return The result of the task execution in JSON format. */ - virtual const json execute() override; + virtual json execute() override; /** * @brief Serializes the task to a JSON object. * * @return A JSON object representing the task. */ - virtual const json toJson() const override; + virtual json toJson() override; private: int m_loopCount; diff --git a/src/atom/task/task.cpp b/src/atom/task/task.cpp index 18dad4b5..35ec1ba1 100644 --- a/src/atom/task/task.cpp +++ b/src/atom/task/task.cpp @@ -15,7 +15,7 @@ Description: Basic and Simple Task Definition #include "task.hpp" SimpleTask::SimpleTask(const std::function &func, - const std::function &stop_fn + const std::function &stop_fn, const json ¶ms_template) : m_function(func), m_paramsTemplate(params_template), m_stopFn(stop_fn), m_stopFlag(false) { @@ -33,7 +33,7 @@ SimpleTask::~SimpleTask() } } -const json SimpleTask::toJson() const +json SimpleTask::toJson() { return { {"type", "merged"}, @@ -43,12 +43,12 @@ const json SimpleTask::toJson() const {"can_stop", m_canStop}}; } -const json SimpleTask::getResult() const +json SimpleTask::getResult() { return m_returns; } -const json SimpleTask::getParamsTemplate() const +json SimpleTask::getParamsTemplate() { return m_paramsTemplate; } @@ -187,7 +187,7 @@ bool SimpleTask::validateJsonString(const std::string &jsonString, const std::st return validateJsonValue(jsonData, templateData); } -const json SimpleTask::execute() +json SimpleTask::execute() { m_isExecuting.store(true); if (!m_paramsTemplate.is_null() && !m_params.is_null()) diff --git a/src/atom/task/task.hpp b/src/atom/task/task.hpp index 0eef684d..5e4381aa 100644 --- a/src/atom/task/task.hpp +++ b/src/atom/task/task.hpp @@ -12,7 +12,8 @@ Description: Basic and Simple Task Definition **************************************************/ -#pragma once +#ifndef ATOM_TASK_TASK_HPP +#define ATOM_TASK_TASK_HPP #include #include @@ -49,7 +50,7 @@ class SimpleTask * current status of the task. */ SimpleTask(const std::function &func, - const std::function &stop_fn + const std::function &stop_fn, const json ¶ms_template); /** @@ -70,14 +71,14 @@ class SimpleTask * - "description": The description of the task. * - "can_stop": A flag indicating whether the task can be stopped. */ - const json toJson() const; + virtual json toJson(); /** * @brief Returns a JSON object representing the result of the SimpleTask object. * * For a SimpleTask object, the result is always an empty JSON object. */ - const json getResult() const; + json getResult(); /** * @brief Returns a JSON object representing the template of the parameters required by the @@ -86,7 +87,7 @@ class SimpleTask * A SimpleTask object does not require any parameters, so the returned JSON object is always * an empty JSON object. */ - const json getParamsTemplate() const; + json getParamsTemplate(); /** * @brief Sets the parameters of the SimpleTask object. @@ -220,8 +221,12 @@ class SimpleTask * * @return A JSON object with the current status of the task. */ - const json execute(); + virtual json execute() ; + std::function m_function; + json m_paramsTemplate; + json m_params; + json m_returns; std::function m_stopFn; bool m_canStop; std::atomic_bool m_stopFlag; @@ -230,9 +235,6 @@ class SimpleTask std::string m_name; std::string m_description; bool m_canExecute; +}; - std::function m_function; - json m_paramsTemplate; - json m_params; - json m_returns; -}; \ No newline at end of file +#endif diff --git a/src/atom/type/CMakeLists.txt b/src/atom/type/CMakeLists.txt index 68beac4b..b8d7b655 100644 --- a/src/atom/type/CMakeLists.txt +++ b/src/atom/type/CMakeLists.txt @@ -1,114 +1,58 @@ -cmake_minimum_required(VERSION 3.15) -project(tinyxml2 VERSION 9.0.0) +# CMakeLists.txt for Atom-Type +# This project is licensed under the terms of the GPL3 license. +# +# Project Name: Atom-Type +# Description: All of the self-implement types +# Author: Max Qian +# License: GPL3 -## -## Honor tinyxml2_SHARED_LIBS to match install interface -## +cmake_minimum_required(VERSION 3.20) +project(atom-type C CXX) -if (DEFINED tinyxml2_SHARED_LIBS) - set(BUILD_SHARED_LIBS "${tinyxml2_SHARED_LIBS}") -endif () - -## -## Main library build -## - -set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_VISIBILITY_INLINES_HIDDEN YES) - -add_library(tinyxml2 tinyxml2.cpp tinyxml2.h) -add_library(tinyxml2::tinyxml2 ALIAS tinyxml2) - -# Uncomment the following line to require C++11 (or greater) to use tinyxml2 -# target_compile_features(tinyxml2 PUBLIC cxx_std_11) -target_include_directories(tinyxml2 PUBLIC "$") - -target_compile_definitions( - tinyxml2 - PUBLIC $<$:TINYXML2_DEBUG> - INTERFACE $<$:TINYXML2_IMPORT> - PRIVATE $<$:_CRT_SECURE_NO_WARNINGS> - PUBLIC _FILE_OFFSET_BITS=64 -) - -set_target_properties( - tinyxml2 - PROPERTIES - DEFINE_SYMBOL "TINYXML2_EXPORT" - VERSION "${tinyxml2_VERSION}" - SOVERSION "${tinyxml2_VERSION_MAJOR}" +# Sources +set(${PROJECT_NAME}_SOURCES + args.cpp + ini.cpp + message.cpp ) -## -## Installation -## - -## Standard modules -include(GNUInstallDirs) -include(CMakePackageConfigHelpers) - -## Custom locations -set(tinyxml2_INSTALL_PKGCONFIGDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig" - CACHE PATH "Directory for pkgconfig files") - -set(tinyxml2_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/tinyxml2" - CACHE STRING "Path to tinyxml2 CMake files") - -## CMake targets and export scripts - -install( - TARGETS tinyxml2 EXPORT tinyxml2-targets - RUNTIME COMPONENT tinyxml2_runtime - LIBRARY COMPONENT tinyxml2_runtime - NAMELINK_COMPONENT tinyxml2_development - ARCHIVE COMPONENT tinyxml2_development - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +# Headers +set(${PROJECT_NAME}_HEADERS + args.hpp + expected.hpp + ini_impl.hpp + ini.hpp + json.hpp + message.hpp + small_vector.hpp ) -# Type-specific targets +# Build Object Library +add_library(${PROJECT_NAME}_OBJECT OBJECT) +set_property(TARGET ${PROJECT_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1) -if (BUILD_SHARED_LIBS) - set(type shared) -else () - set(type static) -endif () - -install( - EXPORT tinyxml2-targets - DESTINATION "${tinyxml2_INSTALL_CMAKEDIR}" - NAMESPACE tinyxml2:: - FILE tinyxml2-${type}-targets.cmake - COMPONENT tinyxml2_development +target_sources(${PROJECT_NAME}_OBJECT + PUBLIC + ${${PROJECT_NAME}_HEADERS} + PRIVATE + ${${PROJECT_NAME}_SOURCES} ) -# Auto-generated version compatibility file -write_basic_package_version_file( - tinyxml2-config-version.cmake - COMPATIBILITY SameMajorVersion -) +target_link_libraries(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) -install( - FILES - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/tinyxml2-config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/tinyxml2-config-version.cmake" - DESTINATION "${tinyxml2_INSTALL_CMAKEDIR}" - COMPONENT tinyxml2_development -) +add_library(${PROJECT_NAME} STATIC) -## Headers +target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_link_libraries(${PROJECT_NAME} atom-utils) +target_include_directories(${PROJECT_NAME} PUBLIC .) -install( - FILES tinyxml2.h - TYPE INCLUDE - COMPONENT tinyxml2_development +set_target_properties(${PROJECT_NAME} PROPERTIES + VERSION ${CMAKE_HYDROGEN_VERSION_STRING} + SOVERSION ${HYDROGEN_SOVERSION} + OUTPUT_NAME ${PROJECT_NAME} + TARGET ${CMAKE_BINARY_DIR} ) -## pkg-config - -configure_file(cmake/tinyxml2.pc.in tinyxml2.pc.gen @ONLY) -file(GENERATE OUTPUT tinyxml2.pc INPUT "${CMAKE_CURRENT_BINARY_DIR}/tinyxml2.pc.gen") -install( - FILES "${CMAKE_CURRENT_BINARY_DIR}/tinyxml2.pc" - DESTINATION "${tinyxml2_INSTALL_PKGCONFIGDIR}" - COMPONENT tinyxml2_development +install(TARGETS ${PROJECT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/src/atom/type/ini.hpp b/src/atom/type/ini.hpp index 25aed34b..a00532d6 100644 --- a/src/atom/type/ini.hpp +++ b/src/atom/type/ini.hpp @@ -12,14 +12,15 @@ Description: INI File Read/Write Library **************************************************/ -#pragma once +#ifndef ATOM_TYPE_INI_HPP +#define ATOM_TYPE_INI_HPP #include #include #include #include #include -#ifdef ENABLE_FASTHASH +#if ENABLE_FASTHASH #include "emhash/hash_table8.hpp" #else #include @@ -102,7 +103,7 @@ class INIFile std::string toXml() const; private: -#ifdef ENABlE_FASTHASH +#if ENABlE_FASTHASH emhash8::HashMap> data; // 存储数据的映射表 #else std::unordered_map> data; // 存储数据的映射表 @@ -123,32 +124,6 @@ class INIFile std::string trim(const std::string &str); }; -template -void INIFile::set(const std::string §ion, const std::string &key, const T &value) -{ - std::unique_lock lock(m_sharedMutex); - data[section][key] = value; -} +#include "ini_impl.hpp" -template -std::optional INIFile::get(const std::string §ion, const std::string &key) const -{ - std::shared_lock lock(m_sharedMutex); - auto it = data.find(section); - if (it != data.end()) - { - auto entryIt = it->second.find(key); - if (entryIt != it->second.end()) - { - try - { - return std::any_cast(entryIt->second); - } - catch (const std::bad_any_cast &) - { - return std::nullopt; - } - } - } - return std::nullopt; -} \ No newline at end of file +#endif diff --git a/src/atom/type/ini_impl.hpp b/src/atom/type/ini_impl.hpp new file mode 100644 index 00000000..e57ab202 --- /dev/null +++ b/src/atom/type/ini_impl.hpp @@ -0,0 +1,48 @@ +/* + * ini_impl.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-6-17 + +Description: INI File Read/Write Library + +**************************************************/ + +#ifndef ATOM_TYPE_INI_IMPL_HPP +#define ATOM_TYPE_INI_IMPL_HPP + +template +void INIFile::set(const std::string §ion, const std::string &key, const T &value) +{ + std::unique_lock lock(m_sharedMutex); + data[section][key] = value; +} + +template +std::optional INIFile::get(const std::string §ion, const std::string &key) const +{ + std::shared_lock lock(m_sharedMutex); + auto it = data.find(section); + if (it != data.end()) + { + auto entryIt = it->second.find(key); + if (entryIt != it->second.end()) + { + try + { + return std::any_cast(entryIt->second); + } + catch (const std::bad_any_cast &) + { + return std::nullopt; + } + } + } + return std::nullopt; +} + +#endif \ No newline at end of file diff --git a/src/atom/type/message.cpp b/src/atom/type/message.cpp index 7c95601d..9c78f0cf 100644 --- a/src/atom/type/message.cpp +++ b/src/atom/type/message.cpp @@ -15,8 +15,6 @@ Description: A message class, which can be used to store different types of mess #include "message.hpp" #include "atom/utils/time.hpp" -#include "atom/type/iparams.hpp" - #include "atom/utils/uuid.hpp" using namespace std; diff --git a/src/atom/utils/CMakeLists.txt b/src/atom/utils/CMakeLists.txt index 8146b341..933cc769 100644 --- a/src/atom/utils/CMakeLists.txt +++ b/src/atom/utils/CMakeLists.txt @@ -1,3 +1,11 @@ +# CMakeLists.txt for Atom-Utils +# This project is licensed under the terms of the GPL3 license. +# +# Project Name: Atom-Utils +# Description: A collection of useful functions +# Author: Max Qian +# License: GPL3 + cmake_minimum_required(VERSION 3.20) project(atom-utils C CXX) @@ -13,6 +21,7 @@ set(${PROJECT_NAME}_SOURCES random.cpp string.cpp static_switch.cpp + stopwatcher.cpp time.cpp uuid.cpp ) @@ -30,7 +39,6 @@ set(${PROJECT_NAME}_HEADERS math.hpp random.hpp refl.hpp - refl_clang.hpp sha256.hpp string.hpp static_switch.hpp @@ -61,18 +69,18 @@ target_sources(${PROJECT_NAME}_OBJECT target_link_libraries(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) -add_library(${PROJECT_NAME}static STATIC) +add_library(${PROJECT_NAME} STATIC) -target_link_libraries(${PROJECT_NAME}static ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) -target_link_libraries(${PROJECT_NAME}static ${CMAKE_THREAD_LIBS_INIT}) -target_include_directories(${PROJECT_NAME}static PUBLIC .) +target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) +target_include_directories(${PROJECT_NAME} PUBLIC .) -set_target_properties(${PROJECT_NAME}static PROPERTIES +set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${CMAKE_HYDROGEN_VERSION_STRING} SOVERSION ${HYDROGEN_SOVERSION} - OUTPUT_NAME ${PROJECT_NAME} # this same name like shared library - backwards compatibility + OUTPUT_NAME ${PROJECT_NAME} ) -install(TARGETS ${PROJECT_NAME}static +install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) \ No newline at end of file diff --git a/src/atom/utils/md5.cpp b/src/atom/utils/md5.cpp new file mode 100644 index 00000000..acfc3c5d --- /dev/null +++ b/src/atom/utils/md5.cpp @@ -0,0 +1,188 @@ +/* + * md5.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-11-10 + +Description: Self implemented MD5 algorithm. + +**************************************************/ + +#include "md5.hpp" + +#include +#include +#include + +namespace Atom::Utils +{ + constexpr uint32_t T[64] = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}; + + constexpr uint32_t s[64] = { + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; + + void MD5::init() + { + _a = 0x67452301; + _b = 0xefcdab89; + _c = 0x98badcfe; + _d = 0x10325476; + _count = 0; + _buffer.clear(); + } + + void MD5::update(const std::string &input) + { + for (char ch : input) + { + _buffer.push_back(static_cast(ch)); + if (_buffer.size() == 64) + { + processBlock(_buffer.data()); + _count += 512; + _buffer.clear(); + } + } + + // Padding + size_t bitLen = _count + _buffer.size() * 8; + _buffer.push_back(0x80); + while (_buffer.size() < 56) + _buffer.push_back(0x00); + + for (int i = 0; i < 8; ++i) + { + _buffer.push_back(static_cast((bitLen >> (i * 8)) & 0xff)); + } + + processBlock(_buffer.data()); + } + + std::string MD5::finalize() + { + std::stringstream ss; + ss << std::hex << std::setfill('0'); + ss << std::setw(8) << reverseBytes(_a); + ss << std::setw(8) << reverseBytes(_b); + ss << std::setw(8) << reverseBytes(_c); + ss << std::setw(8) << reverseBytes(_d); + return ss.str(); + } + + void MD5::processBlock(const uint8_t *block) + { + uint32_t M[16]; + for (int i = 0; i < 16; ++i) + { + // 将block的每四个字节转换为32位整数(考虑到小端字节序) + M[i] = (uint32_t)block[i * 4] | ((uint32_t)block[i * 4 + 1] << 8) | + ((uint32_t)block[i * 4 + 2] << 16) | ((uint32_t)block[i * 4 + 3] << 24); + } + + uint32_t a = _a; + uint32_t b = _b; + uint32_t c = _c; + uint32_t d = _d; + + // 主循环 + for (uint32_t i = 0; i < 64; ++i) + { + uint32_t f, g; + if (i < 16) + { + f = F(b, c, d); + g = i; + } + else if (i < 32) + { + f = G(b, c, d); + g = (5 * i + 1) % 16; + } + else if (i < 48) + { + f = H(b, c, d); + g = (3 * i + 5) % 16; + } + else + { + f = I(b, c, d); + g = (7 * i) % 16; + } + + uint32_t temp = d; + d = c; + c = b; + b = b + leftRotate((a + f + T[i] + M[g]), s[i]); + a = temp; + } + + _a += a; + _b += b; + _c += c; + _d += d; + } + + uint32_t MD5::F(uint32_t x, uint32_t y, uint32_t z) + { + return (x & y) | (~x & z); + } + + uint32_t MD5::G(uint32_t x, uint32_t y, uint32_t z) + { + return (x & z) | (y & ~z); + } + + uint32_t MD5::H(uint32_t x, uint32_t y, uint32_t z) + { + return x ^ y ^ z; + } + + uint32_t MD5::I(uint32_t x, uint32_t y, uint32_t z) + { + return y ^ (x | ~z); + } + + uint32_t MD5::leftRotate(uint32_t x, uint32_t n) + { + return (x << n) | (x >> (32 - n)); + } + + uint32_t MD5::reverseBytes(uint32_t x) + { + return ((x & 0x000000FF) << 24) | + ((x & 0x0000FF00) << 8) | + ((x & 0x00FF0000) >> 8) | + ((x & 0xFF000000) >> 24); + } + + std::string MD5::encrypt(const std::string &input) + { + MD5 md5; + md5.init(); + md5.update(input); + return md5.finalize(); + } +} \ No newline at end of file diff --git a/src/atom/utils/md5.hpp b/src/atom/utils/md5.hpp new file mode 100644 index 00000000..a0c3c42f --- /dev/null +++ b/src/atom/utils/md5.hpp @@ -0,0 +1,47 @@ +/* + * md5.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-11-10 + +Description: Self implemented MD5 algorithm. + +**************************************************/ + +#ifndef ATOM_UTILS_MD5_HPP +#define ATOM_UTILS_MD5_HPP + +#include +#include +#include + +namespace Atom::Utils +{ + class MD5 + { + public: + static std::string encrypt(const std::string &input); + + private: + void init(); + void update(const std::string &input); + std::string finalize(); + void processBlock(const uint8_t *block); + static uint32_t F(uint32_t x, uint32_t y, uint32_t z); + static uint32_t G(uint32_t x, uint32_t y, uint32_t z); + static uint32_t H(uint32_t x, uint32_t y, uint32_t z); + static uint32_t I(uint32_t x, uint32_t y, uint32_t z); + static uint32_t leftRotate(uint32_t x, uint32_t n); + static uint32_t reverseBytes(uint32_t x); + + uint32_t _a, _b, _c, _d; + uint64_t _count; + std::vector _buffer; + }; +} + +#endif // MD5_H diff --git a/src/atom/utils/stopwatcher.cpp b/src/atom/utils/stopwatcher.cpp new file mode 100644 index 00000000..1a4deea2 --- /dev/null +++ b/src/atom/utils/stopwatcher.cpp @@ -0,0 +1,139 @@ +/* + * stopwatcher.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-12-25 + +Description: Simple implementation of a stopwatch + +**************************************************/ + +#include "stopwatcher.hpp" + +#include +#include +#include +#include + +namespace Atom::Utils +{ + StopWatcher::StopWatcher() : m_running(false), m_paused(false) {} + + void StopWatcher::start() + { + if (!m_running) + { + m_start = std::chrono::high_resolution_clock::now(); + m_running = true; + m_paused = false; + m_intervals.push_back(m_start); + } + } + + void StopWatcher::stop() + { + if (m_running && !m_paused) + { + auto stopTime = std::chrono::high_resolution_clock::now(); + m_end = stopTime; + m_running = false; + m_intervals.push_back(stopTime); + checkCallbacks(stopTime); + } + } + + void StopWatcher::pause() + { + if (m_running && !m_paused) + { + m_pauseTime = std::chrono::high_resolution_clock::now(); + m_paused = true; + m_intervals.push_back(m_pauseTime); + } + } + + void StopWatcher::resume() + { + if (m_running && m_paused) + { + auto resumeTime = std::chrono::high_resolution_clock::now(); + m_start += resumeTime - m_pauseTime; // Adjust start time by pause duration + m_paused = false; + m_intervals.push_back(resumeTime); + } + } + + void StopWatcher::reset() + { + m_running = false; + m_paused = false; + m_intervals.clear(); + } + + double StopWatcher::elapsedMilliseconds() const + { + auto endTime = m_running ? (m_paused ? m_pauseTime : std::chrono::high_resolution_clock::now()) : m_end; + return std::chrono::duration_cast(endTime - m_start).count(); + } + + double StopWatcher::elapsedSeconds() const + { + return elapsedMilliseconds() / 1000.0; + } + + std::string StopWatcher::elapsedFormatted() const + { + auto totalSeconds = static_cast(elapsedSeconds()); + int hours = totalSeconds / 3600; + int minutes = (totalSeconds % 3600) / 60; + int seconds = totalSeconds % 60; + + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << hours << ":" + << std::setw(2) << std::setfill('0') << minutes << ":" + << std::setw(2) << std::setfill('0') << seconds; + + return ss.str(); + } + + void StopWatcher::registerCallback(std::function callback, int milliseconds) + { + m_callbacks.push_back({callback, milliseconds}); + } + + void StopWatcher::checkCallbacks(const std::chrono::time_point ¤tTime) + { + for (auto &callbackPair : m_callbacks) + { + auto targetTime = m_start + std::chrono::milliseconds(callbackPair.second); + if (currentTime >= targetTime) + { + callbackPair.first(); + } + } + } +} + +/* +int main() +{ + StopWatcher timer; + + // Register a simple callback to be called after 2.5 seconds + timer.registerCallback([]() + { std::cout << "Callback triggered!" << std::endl; }, + 2500); + + timer.start(); + std::this_thread::sleep_for(std::chrono::seconds(3)); // Simulate work by sleeping for 3 seconds + timer.stop(); + + std::cout << "Total elapsed time in formatted string: " << timer.elapsedFormatted() << std::endl; + + return 0; +} +*/ diff --git a/src/atom/utils/stopwatcher.hpp b/src/atom/utils/stopwatcher.hpp index 5a171a03..8b59679d 100644 --- a/src/atom/utils/stopwatcher.hpp +++ b/src/atom/utils/stopwatcher.hpp @@ -14,98 +14,43 @@ Description: Simple implementation of a stopwatch #pragma once +#include #include -#include -#include -#include +#include +#include namespace Atom::Utils { - template - class stopwatch : public ClockT + class StopWatcher { - using base_t = ClockT; + public: - static_assert(CountN > 0, "The count must be greater than 0"); + StopWatcher(); - public: - using rep = typename ClockT::rep; - using period = typename ClockT::period; - using duration = typename ClockT::duration; - using time_point = typename ClockT::time_point; + void start(); - private: - using pair_t = std::pair; + void stop(); - std::array points_; - bool is_stopped_ = true; + void pause(); - public: - stopwatch(bool start_watch = false) - { - if (start_watch) - start(); - } + void resume(); - public: - bool is_stopped() const noexcept - { - return is_stopped_; - } - - template - bool is_paused() const noexcept - { - return (points_[N].second != points_[N].first); - } - - template - duration elapsed() - { - if (is_stopped()) - return duration::zero(); - else if (is_paused()) - return (points_[N].second - points_[N].first); - else - return ClockT::now() - points_[N].first; - } - - template - auto elapsed() -> decltype(std::declval().count()) - { - return std::chrono::duration_cast(elapsed()).count(); - } - - template - void pause() noexcept - { - points_[N].second = ClockT::now(); - } - - template - void restart() noexcept - { - points_[N].second = points_[N].first = - ClockT::now() - (points_[N].second - points_[N].first); - } - - void start() noexcept - { - time_point now = ClockT::now(); - for (auto &pt : points_) - { - pt.second = pt.first = now - (pt.second - pt.first); - } - is_stopped_ = false; - } - - void stop() noexcept - { - for (auto &pt : points_) - { - pt.second = pt.first; - } - is_stopped_ = true; - } + void reset(); + + double elapsedMilliseconds() const; + + double elapsedSeconds() const; + + std::string elapsedFormatted() const; + + void registerCallback(std::function callback, int milliseconds); + + private: + std::chrono::time_point m_start, m_end, m_pauseTime; + std::vector> m_intervals; + bool m_running, m_paused; + std::vector, int>> m_callbacks; + + void checkCallbacks(const std::chrono::time_point ¤tTime); }; } \ No newline at end of file diff --git a/src/atom/web/CMakeLists.txt b/src/atom/web/CMakeLists.txt index 77e0e43c..aef1b56c 100644 --- a/src/atom/web/CMakeLists.txt +++ b/src/atom/web/CMakeLists.txt @@ -1,3 +1,11 @@ +# CMakeLists.txt for Atom-Web +# This project is licensed under the terms of the GPL3 license. +# +# Project Name: Atom-Web +# Description: Web API +# Author: Max Qian +# License: GPL3 + cmake_minimum_required(VERSION 3.20) project(atom-web C CXX) @@ -21,10 +29,6 @@ set(${PROJECT_NAME}_HEADERS time.hpp ) -# Private Headers -set(${PROJECT_NAME}_PRIVATE_HEADERS -) - # Build Object Library add_library(${PROJECT_NAME}_OBJECT OBJECT) set_property(TARGET ${PROJECT_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1) @@ -36,35 +40,27 @@ target_sources(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_HEADERS} PRIVATE ${${PROJECT_NAME}_SOURCES} - ${${PROJECT_NAME}_PRIVATE_HEADERS} ) target_link_libraries(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) -add_library(${PROJECT_NAME}static STATIC) +add_library(${PROJECT_NAME} STATIC) + +if(WIN32) +target_link_libraries(${PROJECT_NAME}_OBJECT wsock32 ws2_32) +endif() -target_link_libraries(${PROJECT_NAME}static ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) -target_link_libraries(${PROJECT_NAME}static ${CMAKE_THREAD_LIBS_INIT}) -target_include_directories(${PROJECT_NAME}static PUBLIC .) +target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(${PROJECT_NAME} cpp_httplib) +target_include_directories(${PROJECT_NAME} PUBLIC .) -set_target_properties(${PROJECT_NAME}static PROPERTIES +set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${CMAKE_HYDROGEN_VERSION_STRING} SOVERSION ${HYDROGEN_SOVERSION} - OUTPUT_NAME ${PROJECT_NAME} # this same name like shared library - backwards compatibility + OUTPUT_NAME ${PROJECT_NAME} ) -install(TARGETS ${PROJECT_NAME}static +install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) - -find_package(OpenSSL REQUIRED) -add_library(cpp_httplib httplib.cpp) -target_link_libraries(cpp_httplib OpenSSL::SSL OpenSSL::Crypto) -if(WIN32) -target_link_libraries(cpp_httplib crypt32 wsock32 ws2_32) -endif() -if(LINUX) - set_target_properties(cpp_httplib PROPERTIES - POSITION_INDEPENDENT_CODE ON - ) -endif() diff --git a/src/atom/web/downloader.cpp b/src/atom/web/downloader.cpp index 648a25ac..cd33cbf5 100644 --- a/src/atom/web/downloader.cpp +++ b/src/atom/web/downloader.cpp @@ -18,7 +18,7 @@ Description: Downloader #include #include -#include "atom/web/httplib.h" +#include "atom/extra/httplib/httplib.h" #include "atom/log/loguru.hpp" #include "atom/utils/exception.hpp" diff --git a/src/atom/web/httpclient.cpp b/src/atom/web/httpclient.cpp index cbe988e3..43c91630 100644 --- a/src/atom/web/httpclient.cpp +++ b/src/atom/web/httpclient.cpp @@ -14,7 +14,7 @@ Description: Http Client #include "httpclient.hpp" -#include "atom/web/httplib.h" +#include "atom/extra/httplib/httplib.h" #include "atom/log/loguru.hpp" diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 0a63004a..66c6b547 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -4,8 +4,4 @@ project(lithium-driver C CXX) find_package(Threads REQUIRED) add_subdirectory(ascom) -if(NOT WIN32) -add_subdirectory(hydrogen) -endif() -add_subdirectory(phd2) -add_subdirectory(platesolve) \ No newline at end of file +add_subdirectory(hydrogen) \ No newline at end of file diff --git a/src/client/hydrogen/CMakeLists.txt b/src/client/hydrogen/CMakeLists.txt index 65f30edf..d359241d 100644 --- a/src/client/hydrogen/CMakeLists.txt +++ b/src/client/hydrogen/CMakeLists.txt @@ -23,11 +23,6 @@ list(APPEND ${PROJECT_NAME}_HEADERS hydrogentelescope.hpp ) -# Private Headers -list(APPEND ${PROJECT_NAME}_PRIVATE_HEADERS - # TODO -) - # Build Object Library add_library(${PROJECT_NAME}_OBJECT OBJECT) set_property(TARGET ${PROJECT_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1) @@ -37,23 +32,22 @@ target_sources(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_HEADERS} PRIVATE ${${PROJECT_NAME}_SOURCES} - ${${PROJECT_NAME}_PRIVATE_HEADERS} ) target_link_libraries(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_link_libraries(${PROJECT_NAME}_OBJECT atom-driver hydrogenclient) +add_library(${PROJECT_NAME} STATIC) -add_library(${PROJECT_NAME}static STATIC) - -target_link_libraries(${PROJECT_NAME}static ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) -target_link_libraries(${PROJECT_NAME}static ${CMAKE_THREAD_LIBS_INIT}) -target_include_directories(${PROJECT_NAME}static PUBLIC .) +target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) +target_include_directories(${PROJECT_NAME} PUBLIC .) -set_target_properties(${PROJECT_NAME}static PROPERTIES +set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${CMAKE_HYDROGEN_VERSION_STRING} SOVERSION ${HYDROGEN_SOVERSION} - OUTPUT_NAME ${PROJECT_NAME} # this same name like shared library - backwards compatibility + OUTPUT_NAME ${PROJECT_NAME} ) -install(TARGETS ${PROJECT_NAME}static +install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) \ No newline at end of file diff --git a/src/config/configor.cpp b/src/config/configor.cpp index df9207dd..d5c8d8df 100644 --- a/src/config/configor.cpp +++ b/src/config/configor.cpp @@ -184,6 +184,7 @@ namespace Lithium p = &(*p)[key]; } p->clear(); + return true; } bool ConfigManager::hasValue(const std::string &key_path) const diff --git a/src/device/manager.cpp b/src/device/manager.cpp index 3891863c..a64bbbdc 100644 --- a/src/device/manager.cpp +++ b/src/device/manager.cpp @@ -14,6 +14,7 @@ Description: Device Manager #include "manager.hpp" #include "atom/server/global_ptr.hpp" +#include "task/pool.hpp" #include "atom/driver/camera.hpp" #include "atom/driver/telescope.hpp" @@ -72,7 +73,7 @@ Description: Device Manager if (!(device)->isConnected()) \ { \ LOG_F(ERROR, "{} is not connected when call {}", \ - (device)->getDeviceName(), __func__); \ + (device)->GetName(), __func__); \ return DeviceError::NotConnected; \ } \ } while (false) @@ -83,7 +84,7 @@ namespace Lithium // Constructor DeviceManager::DeviceManager(std::shared_ptr messageBus, std::shared_ptr configManager) { - m_ModuleLoader = ModuleLoader::createShared("drivers", GetPtr("lithium.async.thread")); + m_ModuleLoader = ModuleLoader::createShared("drivers"); m_ConfigManager = configManager; m_MessageBus = messageBus; for (auto &devices : m_devices) @@ -159,7 +160,7 @@ namespace Lithium { if (device) { - deviceList.emplace_back(device->getDeviceName()); + deviceList.emplace_back(device->GetName()); } } } @@ -174,7 +175,7 @@ namespace Lithium { if (device) { - deviceList.emplace_back(device->getDeviceName()); + deviceList.emplace_back(device->GetName()); } } return deviceList; @@ -300,11 +301,11 @@ namespace Lithium auto &devices = m_devices[static_cast(type)]; for (auto it = devices.begin(); it != devices.end(); ++it) { - if (*it && (*it)->getDeviceName() == name) + if (*it && (*it)->GetName() == name) { /* (*it)->AddObserverFunc([this](const std::any &message) - { + { if(message.has_value()) { try @@ -348,9 +349,9 @@ namespace Lithium auto &devices = m_devices[static_cast(type)]; for (auto it = devices.begin(); it != devices.end(); ++it) { - if (*it && (*it)->getDeviceName() == name) + if (*it && (*it)->GetName() == name) { - (*it)->getTask("disconnect", {}); + (*it)->RunFunc("disconnect", {}); devices.erase(it); DLOG_F(INFO, "Remove device {} successfully", name); if (m_ConfigManager) @@ -379,8 +380,8 @@ namespace Lithium for (auto &devices : m_devices) { devices.erase(std::remove_if(devices.begin(), devices.end(), - [&](const std::shared_ptr &device) - { return device && device->getDeviceName() == name; }), + [&](const std::shared_ptr &device) + { return device && device->GetName() == name; }), devices.end()); } if (m_ConfigManager) @@ -413,7 +414,7 @@ namespace Lithium return true; } - std::shared_ptr DeviceManager::getDevice(DeviceType type, const std::string &name) + std::shared_ptr DeviceManager::getDevice(DeviceType type, const std::string &name) { std::lock_guard lock(m_mutex); @@ -434,7 +435,7 @@ namespace Lithium auto &devices = m_devices[static_cast(type)]; for (size_t i = 0; i < devices.size(); ++i) { - if (devices[i] && devices[i]->getDeviceName() == name) + if (devices[i] && devices[i]->GetName() == name) { return i; } @@ -442,13 +443,13 @@ namespace Lithium return -1; } - std::shared_ptr DeviceManager::findDeviceByName(const std::string &name) const + std::shared_ptr DeviceManager::findDeviceByName(const std::string &name) const { for (const auto &devices : m_devices) { for (const auto &device : devices) { - if (device && device->getDeviceName() == name) + if (device && device->GetName() == name) { return device; } @@ -457,127 +458,10 @@ namespace Lithium return nullptr; } - std::shared_ptr DeviceManager::getTask(DeviceType type, const std::string &device_name, const std::string &task_name, const json ¶ms) - { - std::lock_guard lock(m_mutex); - DLOG_F(INFO, "Trying to find {} and get {} task", device_name, task_name); - auto device = findDeviceByName(device_name); - - if (device != nullptr) - { - switch (type) - { - case DeviceType::Camera: - { - DLOG_F(INFO, "Found Camera device: {} with task: {}", device_name, task_name); - return std::dynamic_pointer_cast(device)->getTask(task_name, params); - break; - } - case DeviceType::Telescope: - { - DLOG_F(INFO, "Found Telescope device: {} with driver: {}", device_name, task_name); - return std::dynamic_pointer_cast(device)->getTask(task_name, params); - break; - } - case DeviceType::Focuser: - { - DLOG_F(INFO, "Found Focuser device: {} with driver: {}", device_name, task_name); - return std::dynamic_pointer_cast(device)->getTask(task_name, params); - break; - } - case DeviceType::FilterWheel: - { - DLOG_F(INFO, "Found FilterWheel device: {} with driver: {}", device_name, task_name); - return std::dynamic_pointer_cast(device)->getTask(task_name, params); - break; - } - case DeviceType::Solver: - { - break; - } - case DeviceType::Guider: - { - break; - } - default: - LOG_F(ERROR, "Invalid device type"); - break; - } - } - else - { - DLOG_F(INFO, "Device {} not found", device_name); - } - return nullptr; - } - - void DeviceManager::messageBusPublishString(const std::shared_ptr &message) - { - if (m_MessageBus) - { - m_MessageBus->Publish>("main", message); - } - if (!m_ConfigManager) - { - LOG_F(ERROR, "Config manager not initialized"); - } - else - { - if (!message->value.empty()) - { -#ifdef __cpp_lib_format - m_ConfigManager->setValue(std::format("driver/{}/{}", message->device_name, message->name), message->value); -#else - m_ConfigManager->setValue(fmt::format("driver/{}/{}", message->device_name, message->name), message->value); -#endif - } - } - } - - void DeviceManager::messageBusPublishNumber(const std::shared_ptr &message) - { - if (m_MessageBus) - { - m_MessageBus->Publish>("main", message); - } - if (!m_ConfigManager) - { - LOG_F(ERROR, "Config manager not initialized"); - } - else - { -#ifdef __cpp_lib_format - m_ConfigManager->setValue(std::format("driver/{}/{}", message->device_name, message->name), message->value); -#else - m_ConfigManager->setValue(fmt::format("driver/{}/{}", message->device_name, message->name), message->value); -#endif - } - } - - void DeviceManager::messageBusPublishBool(const std::shared_ptr &message) - { - if (m_MessageBus) - { - m_MessageBus->Publish>("main", message); - } - if (!m_ConfigManager) - { - LOG_F(ERROR, "Config manager not initialized"); - } - else - { -#ifdef __cpp_lib_format - m_ConfigManager->setValue(std::format("driver/{}/{}", message->device_name, message->name), message->value); -#else - m_ConfigManager->setValue(fmt::format("driver/{}/{}", message->device_name, message->name), message->value); -#endif - } - } - bool DeviceManager::setDeviceProperty(DeviceType type, const std::string &name, const std::string &value_name, const std::any &value) { - m_ThreadManager->addThread([this, &value, &type, &name, &value_name]() - { + m_TaskPool->enqueue([this, &value, &type, &name, &value_name]() + { auto device = getDevice(type, name); if (!device) { @@ -586,20 +470,22 @@ namespace Lithium } try { - device->setProperty(value_name,value); + //if (value.type() == typeid(std::string) || value.type() == typeid(const char *)) + // device->SetVariable(value_name,std::any_cast(value)); + //if (value.type() == typeid(int) || value.type() == typeid(double)) + // device->SetVariable(value_name, std::any_cast(value)); } catch (const std::bad_any_cast &e) { LOG_F(ERROR, "Failed to convert {} of {} with {}", value_name, name, e.what()); - } }, - Atom::Utils::generateRandomString(16)); + } }); return true; } bool DeviceManager::setDevicePropertyByName(const std::string &name, const std::string &value_name, const std::any &value) { - m_ThreadManager->addThread([this, &value, &name, &value_name]() - { + m_TaskPool->enqueue([this, &value, &name, &value_name]() + { auto device = findDeviceByName(name); if (!device) { @@ -608,13 +494,16 @@ namespace Lithium } try { - device->setProperty(value_name,value); + //if (value.type() == typeid(std::string) || value.type() == typeid(const char *)) + // device->SetVariable(value_name,std::any_cast(value)); + //if (value.type() == typeid(int) || value.type() == typeid(double)) + // device->SetVariable(value_name, std::any_cast(value)); + } catch (const std::bad_any_cast &e) { LOG_F(ERROR, "Failed to convert {} of {} with {}", value_name, name, e.what()); - } }, - Atom::Utils::generateRandomString(16)); + } }); return true; } @@ -766,7 +655,7 @@ namespace Lithium } if (!m_main_camera->startExposure(m_params)) { - LOG_F(ERROR, "{} failed to start exposure", m_main_camera->getDeviceName()); + LOG_F(ERROR, "{} failed to start exposure", m_main_camera->GetName()); return DeviceError::ExposureError; } return DeviceError::None; @@ -778,16 +667,16 @@ namespace Lithium if (!m_main_camera->getExposureStatus({})) { // TODO: 这里需要一个错误返回吗? - DLOG_F(WARNING, "{} is not exposed", m_main_camera->getDeviceName()); + DLOG_F(WARNING, "{} is not exposed", m_main_camera->GetName()); } else { if (!m_main_camera->abortExposure(m_params)) { - LOG_F(ERROR, "{} failed to stop exposure", m_main_camera->getDeviceName()); + LOG_F(ERROR, "{} failed to stop exposure", m_main_camera->GetName()); return DeviceError::ExposureError; } - DLOG_F(INFO, "{} is aborted successfully", m_main_camera->getDeviceName()); + DLOG_F(INFO, "{} is aborted successfully", m_main_camera->GetName()); } return DeviceError::None; } @@ -797,13 +686,13 @@ namespace Lithium CHECK_DEVICE(m_main_camera); if (!m_main_camera->isCoolingAvailable()) { - LOG_F(ERROR, "{} did not support cooling mode", m_main_camera->getDeviceName()); + LOG_F(ERROR, "{} did not support cooling mode", m_main_camera->GetName()); return DeviceError::NotSupported; } // TODO: 这里是否需要一个温度的检查,或者说是在启动制冷时是否需要指定问温度 if (!m_main_camera->startCooling(m_params)) { - LOG_F(ERROR, "{} failed to start cooling mode", m_main_camera->getDeviceName()); + LOG_F(ERROR, "{} failed to start cooling mode", m_main_camera->GetName()); return DeviceError::CoolingError; } return DeviceError::None; @@ -814,12 +703,12 @@ namespace Lithium CHECK_DEVICE(m_main_camera); if (!m_main_camera->isCoolingAvailable()) { - LOG_F(ERROR, "{} did not support cooling mode", m_main_camera->getDeviceName()); + LOG_F(ERROR, "{} did not support cooling mode", m_main_camera->GetName()); return DeviceError::NotSupported; } if (!m_main_camera->stopCooling(m_params)) { - LOG_F(ERROR, "{} failed to stop cooling mode", m_main_camera->getDeviceName()); + LOG_F(ERROR, "{} failed to stop cooling mode", m_main_camera->GetName()); return DeviceError::CoolingError; } return DeviceError::None; @@ -837,7 +726,7 @@ namespace Lithium { if (!m_main_camera->isGainAvailable()) { - DLOG_F(WARNING, "{} did not support set gain", m_main_camera->getDeviceName()); + DLOG_F(WARNING, "{} did not support set gain", m_main_camera->GetName()); return DeviceError::NotSupported; } else @@ -852,7 +741,7 @@ namespace Lithium { if (!m_main_camera->setGain({"gain", value})) { - LOG_F(ERROR, "Failed to set gain of main camera {}", m_main_camera->getDeviceName()); + LOG_F(ERROR, "Failed to set gain of main camera {}", m_main_camera->GetName()); return DeviceError::GainError; } } @@ -873,7 +762,7 @@ namespace Lithium { if (!m_main_camera->isOffsetAvailable()) { - DLOG_F(WARNING, "{} did not support set offset", m_main_camera->getDeviceName()); + DLOG_F(WARNING, "{} did not support set offset", m_main_camera->GetName()); return DeviceError::NotSupported; } else @@ -888,7 +777,7 @@ namespace Lithium { if (!m_main_camera->setOffset({"offset", value})) { - LOG_F(ERROR, "Failed to set offset of main camera {}", m_main_camera->getDeviceName()); + LOG_F(ERROR, "Failed to set offset of main camera {}", m_main_camera->GetName()); return DeviceError::OffsetError; } } @@ -909,7 +798,7 @@ namespace Lithium { if (!m_main_camera->isISOAvailable()) { - DLOG_F(WARNING, "{} did not support set iso", m_main_camera->getDeviceName()); + DLOG_F(WARNING, "{} did not support set iso", m_main_camera->GetName()); return DeviceError::NotSupported; } else @@ -918,7 +807,7 @@ namespace Lithium // TODO: There needs a ISO value check if (!m_main_camera->setISO({"iso", value})) { - LOG_F(ERROR, "Failed to set iso of main camera {}", m_main_camera->getDeviceName()); + LOG_F(ERROR, "Failed to set iso of main camera {}", m_main_camera->GetName()); return DeviceError::ISOError; } } @@ -952,7 +841,13 @@ namespace Lithium { for (auto it = params.begin(); it != params.end(); ++it) { - m_main_camera->setProperty(it.key(), it.value()); + // Max: How to fix the left and right value problem? + //if (it.value().is_number()) + // m_main_camera->SetVariable(it.key(), it.value()); + //else if (it.value().is_string()) + // m_main_camera->SetVariable(it.key(), it.value()); + //else if (it.value().is_boolean()) + // m_main_camera->SetVariable(it.key(), it.value()); } } } @@ -960,7 +855,12 @@ namespace Lithium { for (auto it = m_params.begin(); it != m_params.end(); ++it) { - m_main_camera->setProperty(it.key(), it.value()); + //if (it.value().is_number()) + // m_main_camera->SetVariable(it.key(), it.value()); + //else if (it.value().is_string()) + // m_main_camera->SetVariable(it.key(), it.value()); + //else if (it.value().is_boolean()) + // m_main_camera->SetVariable(it.key(), it.value()); } } return DeviceError::None; @@ -979,12 +879,12 @@ namespace Lithium { for (auto it = m_params.begin(); it != m_params.end(); ++it) { - res[it.key()] = m_main_camera->getStringProperty(it.key())->value; + res[it.key()] = m_main_camera->GetVariable(it.key()).value(); } } else { - res["value"] = m_main_camera->getStringProperty(m_params["name"])->value; + res["value"] = m_main_camera->GetVariable(m_params["name"].get()).value(); } return res; } @@ -995,12 +895,12 @@ namespace Lithium CHECK_DEVICE(m_telescope); if (m_telescope->isAtPark({})) { - LOG_F(ERROR, "{} had already parked, please unpark before {}", m_telescope->getDeviceName(), __func__); + LOG_F(ERROR, "{} had already parked, please unpark before {}", m_telescope->GetName(), __func__); return DeviceError::ParkedError; } if (!m_params.contains("ra") || !m_params.contains("dec")) { - LOG_F(ERROR, "{} failed to goto: Missing RA or DEC value", m_telescope->getDeviceName()); + LOG_F(ERROR, "{} failed to goto: Missing RA or DEC value", m_telescope->GetName()); return DeviceError::MissingValue; } std::string ra = m_params["ra"]; @@ -1038,7 +938,7 @@ namespace Lithium } if (!m_telescope->SlewTo(m_params)) { - LOG_F(ERROR, "{} failed to slew to {} {}", m_telescope->getDeviceName(), ra, dec); + LOG_F(ERROR, "{} failed to slew to {} {}", m_telescope->GetName(), ra, dec); return DeviceError::GotoError; } return DeviceError::None; @@ -1049,20 +949,20 @@ namespace Lithium CHECK_DEVICE(m_telescope); if (!m_telescope->isParkAvailable(m_params)) { - LOG_F(ERROR, "{} is not support park function", m_telescope->getDeviceName()); + LOG_F(ERROR, "{} is not support park function", m_telescope->GetName()); return DeviceError::NotSupported; } if (m_telescope->isAtPark(m_params)) { - DLOG_F(WARNING, "{} is already parked, please do not park again!", m_telescope->getDeviceName()); + DLOG_F(WARNING, "{} is already parked, please do not park again!", m_telescope->GetName()); return DeviceError::None; } if (m_telescope->Park(m_params)) { - LOG_F(ERROR, "{} failed to park", m_telescope->getDeviceName()); + LOG_F(ERROR, "{} failed to park", m_telescope->GetName()); return DeviceError::ParkError; } - DLOG_F(INFO, "{} parked successfully", m_telescope->getDeviceName()); + DLOG_F(INFO, "{} parked successfully", m_telescope->GetName()); return DeviceError::None; } @@ -1071,20 +971,20 @@ namespace Lithium CHECK_DEVICE(m_telescope); if (!m_telescope->isParkAvailable(m_params)) { - LOG_F(ERROR, "{} is not support park function", m_telescope->getDeviceName()); + LOG_F(ERROR, "{} is not support park function", m_telescope->GetName()); return DeviceError::NotSupported; } if (!m_telescope->isAtPark(m_params)) { - DLOG_F(WARNING, "{} is not parked, please do not unpark before!", m_telescope->getDeviceName()); + DLOG_F(WARNING, "{} is not parked, please do not unpark before!", m_telescope->GetName()); return DeviceError::None; } if (m_telescope->Unpark(m_params)) { - LOG_F(ERROR, "{} failed to unpark", m_telescope->getDeviceName()); + LOG_F(ERROR, "{} failed to unpark", m_telescope->GetName()); return DeviceError::ParkError; } - DLOG_F(INFO, "{} parked successfully", m_telescope->getDeviceName()); + DLOG_F(INFO, "{} parked successfully", m_telescope->GetName()); return DeviceError::None; } @@ -1093,17 +993,17 @@ namespace Lithium CHECK_DEVICE(m_telescope); if (!m_telescope->isConnected()) { - LOG_F(ERROR, "{} is not connected when call {}", m_telescope->getDeviceName(), __func__); + LOG_F(ERROR, "{} is not connected when call {}", m_telescope->GetName(), __func__); return DeviceError::NotConnected; } if (!m_telescope->isHomeAvailable({})) { - LOG_F(ERROR, "{} is not support home", m_telescope->getDeviceName()); + LOG_F(ERROR, "{} is not support home", m_telescope->GetName()); return DeviceError::NotSupported; } if (m_telescope->isAtPark({})) { - LOG_F(ERROR, "{} had already parked, please unpark before {}", m_telescope->getDeviceName(), __func__); + LOG_F(ERROR, "{} had already parked, please unpark before {}", m_telescope->GetName(), __func__); return DeviceError::ParkedError; } if (!m_telescope->Home(m_params)) @@ -1111,7 +1011,7 @@ namespace Lithium LOG_F(ERROR, "{} Failed to go home position"); return DeviceError::HomeError; } - DLOG_F(INFO, "{} go home position successfully!", m_telescope->getDeviceName()); + DLOG_F(INFO, "{} go home position successfully!", m_telescope->GetName()); return DeviceError::None; } diff --git a/src/device/manager.hpp b/src/device/manager.hpp index 605c8e02..87a1157f 100644 --- a/src/device/manager.hpp +++ b/src/device/manager.hpp @@ -24,11 +24,10 @@ Description: Device Manager #include "atom/driver/device_type.hpp" #include "atom/type/message.hpp" #include "atom/server/message_bus.hpp" -#include "atom/async/thread.hpp" -#include "atom/driver/iproperty.hpp" #include "addon/loader.hpp" #include "config/configor.hpp" +#include "task/pool.hpp" #include "server/hydrogen.hpp" @@ -155,7 +154,7 @@ namespace Lithium * @param name 设备名称。 * @return 返回指向设备对象的共享指针,如果设备不存在则返回空指针。 */ - std::shared_ptr getDevice(DeviceType type, const std::string &name); + std::shared_ptr getDevice(DeviceType type, const std::string &name); /** * @brief 查找指定设备类型和名称的设备在设备管理器中的索引。 @@ -170,35 +169,7 @@ namespace Lithium * @param name 设备名称。 * @return 返回指向设备对象的共享指针,如果设备不存在则返回空指针。 */ - std::shared_ptr findDeviceByName(const std::string &name) const; - - /** - * @brief 获取指定设备类型、设备名称、任务名称和参数的简单任务对象。 - * @param type 设备类型枚举值。 - * @param device_name 设备名称。 - * @param task_name 任务名称。 - * @param params 任务参数的JSON对象。 - * @return 返回指向简单任务对象的共享指针。 - */ - std::shared_ptr getTask(DeviceType type, const std::string &device_name, const std::string &task_name, const json ¶ms); - - /** - * @brief 发布字符串类型的消息到消息总线。 - * @param message 字符串属性的共享指针。 - */ - void messageBusPublishString(const std::shared_ptr &message); - - /** - * @brief 发布数字类型的消息到消息总线。 - * @param message 数字属性的共享指针。 - */ - void messageBusPublishNumber(const std::shared_ptr &message); - - /** - * @brief 发布布尔类型的消息到消息总线。 - * @param message 布尔属性的共享指针。 - */ - void messageBusPublishBool(const std::shared_ptr &message); + std::shared_ptr findDeviceByName(const std::string &name) const; /** * @brief 设置设备属性值。 @@ -296,14 +267,14 @@ namespace Lithium bool stopASCOMDevice(); private: - std::vector> m_devices[static_cast(DeviceType::NumDeviceTypes)]; ///< 存储设备对象的数组,每个设备类型对应一个向量。 + std::vector> m_devices[static_cast(DeviceType::NumDeviceTypes)]; ///< 存储设备对象的数组,每个设备类型对应一个向量。 std::mutex m_mutex; ///< 互斥锁,用于保护设备管理器的并发访问。 std::shared_ptr m_ModuleLoader; ///< 模块加载器对象的共享指针。 std::shared_ptr m_MessageBus; ///< 消息总线对象的共享指针。 std::shared_ptr m_ConfigManager; ///< 配置管理器对象的共享指针。 - std::shared_ptr m_ThreadManager; + std::shared_ptr m_TaskPool; // Device for quick performance private: diff --git a/src/device/server/hydrogen_driver.cpp b/src/device/server/hydrogen_driver.cpp index 975e676c..b3d1329e 100644 --- a/src/device/server/hydrogen_driver.cpp +++ b/src/device/server/hydrogen_driver.cpp @@ -20,7 +20,7 @@ Description: Hydrogen Web Driver #include #include -#include "atom/type/tinyxml2.h" +#include "atom/extra/tinyxml2/tinyxml2.h" #define LOGURU_USE_FMTLIB 1 #include "atom/log/loguru.hpp" @@ -83,6 +83,7 @@ bool HydrogenDriverCollection::parseDrivers(const std::string &path) // Sort drivers by label std::sort(drivers.begin(), drivers.end(), [](const std::shared_ptr a, const std::shared_ptr b) { return a->label < b->label; }); + return true; } bool HydrogenDriverCollection::parseCustomDrivers(const json &drivers) diff --git a/src/task/container.cpp b/src/task/container.cpp index e68ab6e9..4177235a 100644 --- a/src/task/container.cpp +++ b/src/task/container.cpp @@ -18,6 +18,11 @@ Description: Task container class. namespace Lithium { + std::shared_ptr TaskContainer::createShared() + { + return std::make_shared(); + } + void TaskContainer::addTask(const std::shared_ptr &task) { if (!task) diff --git a/src/task/container.hpp b/src/task/container.hpp index dd59a0f0..c39af190 100644 --- a/src/task/container.hpp +++ b/src/task/container.hpp @@ -38,6 +38,8 @@ namespace Lithium class TaskContainer { public: + static std::shared_ptr createShared(); + /** * @brief Add a task to the container. * diff --git a/src/task/generator.cpp b/src/task/generator.cpp index 67f0d8d6..f52ac46e 100644 --- a/src/task/generator.cpp +++ b/src/task/generator.cpp @@ -17,20 +17,21 @@ Description: Task Generator #include "atom/io/io.hpp" #include "atom/log/loguru.hpp" +#include "atom/server/global_ptr.hpp" + #include +#include namespace Lithium { - TaskGenerator::TaskGenerator(std::shared_ptr deviceManager) : m_DeviceManager(deviceManager) {} + TaskGenerator::TaskGenerator() + { + m_DeviceManager = GetPtr("lithium.device"); + } - std::shared_ptr createShared(std::shared_ptr deviceManager) + std::shared_ptr TaskGenerator::createShared() { - if (!deviceManager) - { - LOG_F(ERROR, "Device manager is null"); - throw std::runtime_error("Device manager is null"); - } - return std::make_shared(deviceManager); + return std::make_shared(); } bool TaskGenerator::loadMacros(const std::string ¯oFileName) @@ -72,7 +73,7 @@ namespace Lithium std::vector> futures; for (const auto &entry : fs::directory_iterator(folderPath)) { - futures.push_back(std::async(std::launch::async, &TaskGenerator::processMacroFile, this, entry.path())); + futures.push_back(std::async(std::launch::async, &TaskGenerator::processMacroFile, this, entry.path().string())); } for (auto &fut : futures) @@ -115,7 +116,7 @@ namespace Lithium { if (!Atom::IO::isFileExists(sfilePath)) { - LOG_F(ERROR, "Macro file not found: {}", filePath); + LOG_F(ERROR, "Macro file not found: {}", sfilePath); return; } fs::path filePath = sfilePath; diff --git a/src/task/generator.hpp b/src/task/generator.hpp index 680f8b37..8f68dcfb 100644 --- a/src/task/generator.hpp +++ b/src/task/generator.hpp @@ -33,14 +33,14 @@ namespace Lithium class TaskGenerator { public: - explicit TaskGenerator(std::shared_ptr deviceManager); + explicit TaskGenerator(); ~TaskGenerator() = default; // ------------------------------------------------------------------- // Common methods // ------------------------------------------------------------------- - static std::shared_ptr createShared(std::shared_ptr deviceManager); + static std::shared_ptr createShared(); // ------------------------------------------------------------------- // Macro methods diff --git a/src/task/list.cpp b/src/task/list.cpp new file mode 100644 index 00000000..5a45c134 --- /dev/null +++ b/src/task/list.cpp @@ -0,0 +1,91 @@ +/* + * list.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-4-3 + +Description: Thread safe task list + +**************************************************/ + +#include "list.hpp" + +#include +#include +#include + +namespace Lithium +{ + std::shared_ptr TaskList::createShared() + { + return std::make_shared(); + } + + bool TaskList::addOrUpdateTask(const std::string &name, const json ¶ms) + { + std::unique_lock lock(mtx); // 用于写操作 + auto [it, inserted] = tasks.try_emplace(name, params); + if (!inserted) + { + it->second = params; // 更新现有任务 + } + return inserted; + } + + bool TaskList::insertTask(const std::string &name, const json ¶ms, const int &position) + { + std::unique_lock lock(mtx); // 用于写操作 + if (tasks.find(name) != tasks.end()) + { + return false; // 任务名已存在 + } + if (position > tasks.size()) + { + return false; // 位置超出当前任务列表范围 + } + // 插入任务到指定位置(需要转换为vector进行操作) + std::vector> temp(tasks.begin(), tasks.end()); + temp.insert(temp.begin() + position, {name, params}); + tasks.clear(); + for (const auto &task : temp) + { + tasks[task.first] = task.second; + } + return true; + } + + bool TaskList::removeTask(const std::string &name) + { + std::unique_lock lock(mtx); // 用于写操作 + return tasks.erase(name) > 0; + } + + std::optional TaskList::getTaskParams(const std::string &name) const + { + std::shared_lock lock(mtx); // 用于读操作 + auto it = tasks.find(name); + if (it != tasks.end()) + { + return it->second; + } + return std::nullopt; + } + + void TaskList::listTasks() const + { + std::shared_lock lock(mtx); // 用于读操作 + for (const auto &[name, params] : tasks) + { + std::cout << name << ": " << params.dump() << std::endl; + } + } + + std::unordered_map TaskList::getTasks() + { + return tasks; + } +} // namespace Lithium diff --git a/src/task/list.hpp b/src/task/list.hpp new file mode 100644 index 00000000..98b580b1 --- /dev/null +++ b/src/task/list.hpp @@ -0,0 +1,59 @@ +/* + * list.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-4-3 + +Description: Thread safe task list + +**************************************************/ + +#ifndef LITHIUM_TASK_LIST_HPP +#define LITHIUM_TASK_LIST_HPP + +#include +#include +#include +#if ENABLE_FASTHASH +#include "emhash/hash_table8.hpp" +#else +#include +#endif + +#include "atom/type/json.hpp" +using json = nlohmann::json; + +namespace Lithium +{ + class TaskList + { + public: + static std::shared_ptr createShared(); + + bool addOrUpdateTask(const std::string &name, const json ¶ms); + + bool insertTask(const std::string &name, const json ¶ms, const int &position); + + bool removeTask(const std::string &name); + + std::optional getTaskParams(const std::string &name) const; + + void listTasks() const; + + std::unordered_map getTasks(); + + private: + mutable std::shared_mutex mtx; +#if ENABLE_FASTHASH + emhash8::HashMap tasks; +#else + std::unordered_map tasks; +#endif + }; +} + +#endif \ No newline at end of file diff --git a/src/task/loader.cpp b/src/task/loader.cpp index 704e4c41..e5838dd3 100644 --- a/src/task/loader.cpp +++ b/src/task/loader.cpp @@ -20,135 +20,142 @@ Description: Json file manager #include "atom/log/loguru.hpp" -std::optional JsonFileManager::readJsonFile(const fs::path &filePath) +namespace Lithium { - if (!fs::exists(filePath) || !fs::is_regular_file(filePath)) + std::shared_ptr TaskLoader::createShared() { - LOG_F(ERROR, "File not found: {}", filePath.string()); - return std::nullopt; + return std::make_shared(); } - - std::ifstream inputFile(filePath); - json j; - try - { - inputFile >> j; - } - catch (const json::parse_error &e) + + std::optional TaskLoader::readJsonFile(const fs::path &filePath) { - LOG_F(ERROR, "Parse error in {}: {}", filePath.string(), e.what()); - return std::nullopt; + if (!fs::exists(filePath) || !fs::is_regular_file(filePath)) + { + LOG_F(ERROR, "File not found: {}", filePath.string()); + return std::nullopt; + } + + std::ifstream inputFile(filePath); + json j; + try + { + inputFile >> j; + } + catch (const json::parse_error &e) + { + LOG_F(ERROR, "Parse error in {}: {}", filePath.string(), e.what()); + return std::nullopt; + } + catch (const json::type_error &e) + { + LOG_F(ERROR, "Type error in {}: {}", filePath.string(), e.what()); + return std::nullopt; + } + catch (const std::exception &e) + { + LOG_F(ERROR, "Exception in {}: {}", filePath.string(), e.what()); + return std::nullopt; + } + inputFile.close(); + return j; } - catch (const json::type_error &e) + + std::optional TaskLoader::readJsonFile(const std::string &filePath) { - LOG_F(ERROR, "Type error in {}: {}", filePath.string(), e.what()); - return std::nullopt; + return readJsonFile(fs::path(filePath)); } - catch (const std::exception &e) + + bool TaskLoader::writeJsonFile(const fs::path &filePath, const json &j) { - LOG_F(ERROR, "Exception in {}: {}", filePath.string(), e.what()); - return std::nullopt; + std::ofstream outputFile(filePath); + if (!outputFile.is_open()) + { + LOG_F(ERROR, "Failed to open: {}", filePath.string()); + return false; + } + outputFile << j.dump(4); + outputFile.close(); + return true; } - inputFile.close(); - return j; -} - -static std::optional JsonFileManager::readJsonFile(const std::string &filePath) -{ - return readJsonFile(fs::path(filePath)); -} -bool JsonFileManager::writeJsonFile(const fs::path &filePath, const json &j) -{ - std::ofstream outputFile(filePath); - if (!outputFile.is_open()) + bool TaskLoader::writeJsonFile(const std::string &filePath, const json &j) { - LOG_F(ERROR, "Failed to open: {}", filePath.string()); - return false; + return writeJsonFile(fs::path(filePath), j); } - outputFile << j.dump(4); - outputFile.close(); - return true; -} - -bool JsonFileManager::writeJsonFile(const std::string &filePath, const json &j) -{ - return writeJsonFile(fs::path(filePath), j); -} -void JsonFileManager::asyncReadJsonFile(const fs::path &filePath, std::function)> callback) -{ - std::jthread([filePath, callback = std::move(callback)]() - { + void TaskLoader::asyncReadJsonFile(const fs::path &filePath, std::function)> callback) + { + std::jthread([filePath, callback = std::move(callback)]() + { auto j = readJsonFile(filePath); callback(j); }); -} + } -void JsonFileManager::asyncReadJsonFile(const std::string &filePath, std::function)> callback) -{ - asyncReadJsonFile(fs::path(filePath), callback); -} + void TaskLoader::asyncReadJsonFile(const std::string &filePath, std::function)> callback) + { + asyncReadJsonFile(fs::path(filePath), callback); + } -void JsonFileManager::asyncWriteJsonFile(const fs::path &filePath, const json &j, std::function callback) -{ - std::jthread([filePath, j, callback = std::move(callback)]() mutable - { + void TaskLoader::asyncWriteJsonFile(const fs::path &filePath, const json &j, std::function callback) + { + std::jthread([filePath, j, callback = std::move(callback)]() mutable + { bool success = writeJsonFile(filePath, j); callback(success); }); -} + } -void JsonFileManager::asyncWriteJsonFile(const std::string &filePath, const json &j, std::function callback) -{ - asyncWriteJsonFile(fs::path(filePath), j, callback); -} + void TaskLoader::asyncWriteJsonFile(const std::string &filePath, const json &j, std::function callback) + { + asyncWriteJsonFile(fs::path(filePath), j, callback); + } -void JsonFileManager::mergeJsonObjects(json &base, const json &toMerge) -{ - for (auto &[key, value] : toMerge.items()) + void TaskLoader::mergeJsonObjects(json &base, const json &toMerge) { - base[key] = value; + for (auto &[key, value] : toMerge.items()) + { + base[key] = value; + } } -} -void JsonFileManager::batchAsyncProcess(const std::vector &filePaths, std::function)> process, std::function onComplete) -{ - std::atomic filesProcessed = 0; - for (const auto &path : filePaths) + void TaskLoader::batchAsyncProcess(const std::vector &filePaths, std::function)> process, std::function onComplete) { - asyncReadJsonFile(path, [&filesProcessed, &filePaths, process, onComplete](std::optional j) - { + std::atomic filesProcessed = 0; + for (const auto &path : filePaths) + { + asyncReadJsonFile(path, [&filesProcessed, &filePaths, process, onComplete](std::optional j) + { if (j) process(j); if (++filesProcessed == filePaths.size()) onComplete(); }); + } } -} -void JsonFileManager::batchAsyncProcess(const std::vector &filePaths, std::function)> process, std::function onComplete) -{ - std::vector paths; - for (const auto &path : filePaths) + void TaskLoader::batchAsyncProcess(const std::vector &filePaths, std::function)> process, std::function onComplete) { - paths.push_back(fs::path(path)); + std::vector paths; + for (const auto &path : filePaths) + { + paths.push_back(fs::path(path)); + } + batchAsyncProcess(paths, process, onComplete); } - batchAsyncProcess(paths, process, onComplete); -} -void JsonFileManager::asyncDeleteJsonFile(const fs::path &filePath, std::function callback) -{ - std::jthread([filePath, callback = std::move(callback)]() - { + void TaskLoader::asyncDeleteJsonFile(const fs::path &filePath, std::function callback) + { + std::jthread([filePath, callback = std::move(callback)]() + { bool success = fs::remove(filePath); callback(success); }); -} + } -void JsonFileManager::asyncDeleteJsonFile(const std::string &filePath, std::function callback) -{ - asyncDeleteJsonFile(fs::path(filePath), callback); -} + void TaskLoader::asyncDeleteJsonFile(const std::string &filePath, std::function callback) + { + asyncDeleteJsonFile(fs::path(filePath), callback); + } -void JsonFileManager::asyncQueryJsonValue(const fs::path &filePath, const std::string &key, std::function)> callback) -{ - asyncReadJsonFile(filePath, [key, callback = std::move(callback)](std::optional jOpt) - { + void TaskLoader::asyncQueryJsonValue(const fs::path &filePath, const std::string &key, std::function)> callback) + { + asyncReadJsonFile(filePath, [key, callback = std::move(callback)](std::optional jOpt) + { if (!jOpt.has_value()) { callback(std::nullopt); return; @@ -159,36 +166,37 @@ void JsonFileManager::asyncQueryJsonValue(const fs::path &filePath, const std::s } else { callback(std::nullopt); } }); -} - -void JsonFileManager::asyncQueryJsonValue(const std::string &filePath, const std::string &key, std::function)> callback) -{ - asyncQueryJsonValue(fs::path(filePath), key, callback); -} + } -void JsonFileManager::batchProcessDirectory(const fs::path &directoryPath, std::function)> process, std::function onComplete) -{ - if (!fs::exists(directoryPath) || !fs::is_directory(directoryPath)) + void TaskLoader::asyncQueryJsonValue(const std::string &filePath, const std::string &key, std::function)> callback) { - LOG_F(ERROR, "Invalid directory path: {}", directoryPath.string()); - return; + asyncQueryJsonValue(fs::path(filePath), key, callback); } - std::vector filePaths; - for (const auto &entry : fs::directory_iterator(directoryPath)) + void TaskLoader::batchProcessDirectory(const fs::path &directoryPath, std::function)> process, std::function onComplete) { - if (entry.path().extension() == ".json") + if (!fs::exists(directoryPath) || !fs::is_directory(directoryPath)) { - filePaths.push_back(entry.path()); + LOG_F(ERROR, "Invalid directory path: {}", directoryPath.string()); + return; } - } - batchAsyncProcess(filePaths, process, onComplete); -} + std::vector filePaths; + for (const auto &entry : fs::directory_iterator(directoryPath)) + { + if (entry.path().extension() == ".json") + { + filePaths.push_back(entry.path()); + } + } -void JsonFileManager::batchProcessDirectory(const std::string &directoryPath, std::function)> process, std::function onComplete) -{ - batchProcessDirectory(fs::path(directoryPath), process, onComplete); + batchAsyncProcess(filePaths, process, onComplete); + } + + void TaskLoader::batchProcessDirectory(const std::string &directoryPath, std::function)> process, std::function onComplete) + { + batchProcessDirectory(fs::path(directoryPath), process, onComplete); + } } /* @@ -199,7 +207,7 @@ int main() std::filesystem::path filePath2 = "file2.json"; // 异步读取、修改、保存第一个JSON文件 - JsonFileManager::asyncReadJsonFile(filePath1, [filePath1](std::optional jOpt) + TaskLoader::asyncReadJsonFile(filePath1, [filePath1](std::optional jOpt) { if (!jOpt) { std::cerr << "Failed to read file: " << filePath1 << std::endl; @@ -213,7 +221,7 @@ int main() j["newKey"] = "newValue"; // 异步保存修改 - JsonFileManager::asyncWriteJsonFile(filePath1, j, [](bool success) { + TaskLoader::asyncWriteJsonFile(filePath1, j, [](bool success) { if (success) { std::cout << "file1.json saved successfully." << std::endl; } else { @@ -223,7 +231,7 @@ int main() // 批量异步处理多个JSON文件 std::vector files = {filePath1, filePath2}; - JsonFileManager::batchAsyncProcess( + TaskLoader::batchAsyncProcess( files, [](std::optional jOpt) { diff --git a/src/task/loader.hpp b/src/task/loader.hpp index d1194b2f..4814e250 100644 --- a/src/task/loader.hpp +++ b/src/task/loader.hpp @@ -25,145 +25,138 @@ Description: Json file manager using json = nlohmann::json; namespace fs = std::filesystem; -/** - * @brief JSON文件管理器 - * @details - * 读取、修改、保存JSON文件,支持异步操作 - * - * JSON file manager - * Provides asynchronous operations for reading, modifying and saving JSON files - */ -class JsonFileManager +namespace Lithium { -public: - /** - * @brief 读取JSON文件 - * @param filePath 文件路径 - * @return std::optional 如果文件存在且格式正确,则返回json对象;否则返回std::nullopt - * - * Read a JSON file - * @param filePath Path to the file - * @return std::optional Returns a json object if the file exists and is correctly formatted; otherwise returns std::nullopt - */ - static std::optional readJsonFile(const fs::path &filePath); - static std::optional readJsonFile(const std::string &filePath); - - /** - * @brief 写入/修改JSON文件 - * @param filePath 文件路径 - * @param j 要写入的json对象 - * @return bool 成功写入返回true,否则返回false - * - * Write/Modify a JSON file - * @param filePath Path to the file - * @param j json object to write - * @return bool Returns true if writing was successful, false otherwise - */ - static bool writeJsonFile(const fs::path &filePath, const json &j); - static bool writeJsonFile(const std::string &filePath, const json &j); - - /** - * @brief 异步读取JSON文件 - * @param filePath 文件路径 - * @param callback 完成读取后的回调函数 - * - * Asynchronously read a JSON file - * @param filePath Path to the file - * @param callback Callback function to be called upon completion - */ - static void asyncReadJsonFile(const fs::path &filePath, std::function)> callback); - static void asyncReadJsonFile(const std::string &filePath, std::function)> callback); - - /** - * @brief 异步写入JSON文件 - * @param filePath 文件路径 - * @param j 要写入的json对象 - * @param callback 完成写入后的回调函数 - * - * Asynchronously write a JSON file - * @param filePath Path to the file - * @param j json object to write - * @param callback Callback function to be called upon completion - */ - static void asyncWriteJsonFile(const fs::path &filePath, const json &j, std::function callback); - static void asyncWriteJsonFile(const std::string &filePath, const json &j, std::function callback); - - /** - * @brief 合并两个JSON对象 - * @param base 基础json对象,合并的结果将存储在此对象中 - * @param toMerge 要合并的json对象 - * - * Merge two JSON objects - * @param base Base json object, the result of the merge will be stored in this object - * @param toMerge json object to merge - */ - static void mergeJsonObjects(json &base, const json &toMerge); - - /** - * @brief 批量异步处理JSON文件 - * @param filePaths 文件路径集合 - * @param process 对每个文件执行的处理函数 - * @param onComplete 全部文件处理完成后的回调函数 - * - * Batch process JSON files asynchronously - * @param filePaths Collection of file paths - * @param process Processing function to execute on each file - * @param onComplete Callback function to be called after all files have been processed - */ - static void batchAsyncProcess(const std::vector &filePaths, std::function)> process, std::function onComplete); - static void batchAsyncProcess(const std::vector &filePaths, std::function)> process, std::function onComplete); - - /** - * @brief 异步删除JSON文件 - * @param filePath 文件路径 - * @param callback 完成删除后的回调函数 - * - * Asynchronously delete a JSON file - * @param filePath Path to the file - * @param callback Callback function to be called upon deletion - */ - static void asyncDeleteJsonFile(const fs::path &filePath, std::function callback); - static void asyncDeleteJsonFile(const std::string &filePath, std::function callback); - - /** - * @brief 异步删除JSON文件 - * @param filePath 文件路径 - * @param callback 完成删除后的回调函数 - * - * Asynchronously delete a JSON file - * @param filePath Path to the file - * @param callback Callback function to be called upon deletion - */ - static void asyncDeleteJsonFile(const std::string &filePath, std::function callback); - static void asyncDeleteJsonFile(const fs::path &filePath, std::function callback); - - /** - * @brief 异步查询JSON文件中的键值 - * @param filePath 文件路径 - * @param key 要查询的键 - * @param callback 完成查询后的回调函数,返回查询到的值或std::nullopt - * - * Asynchronously query a value by key in a JSON file - * @param filePath Path to the file - * @param key Key to query - * @param callback Callback function to be called upon completion, returning the found value or std::nullopt - */ - static void asyncQueryJsonValue(const fs::path &filePath, const std::string &key, std::function)> callback); - static void asyncQueryJsonValue(const std::string &filePath, const std::string &key, std::function)> callback); - /** - * @brief 批量处理目录下的所有JSON文件 - * @param directoryPath 目录路径 - * @param process 对每个文件执行的处理函数 - * @param onComplete 全部文件处理完成后的回调函数 + * @brief JSON文件管理器 + * @details + * 读取、修改、保存JSON文件,支持异步操作 * - * Batch process all JSON files in a directory - * @param directoryPath Path to the directory - * @param process Processing function to execute on each file - * @param onComplete Callback function to be called after all files in the directory have been processed + * JSON file manager + * Provides asynchronous operations for reading, modifying and saving JSON files */ - static void batchProcessDirectory(const fs::path &directoryPath, std::function)> process, std::function onComplete); - static void batchProcessDirectory(const std::string &directoryPath, std::function)> process, std::function onComplete); -}; + class TaskLoader + { + public: + static std::shared_ptr createShared(); + + /** + * @brief 读取JSON文件 + * @param filePath 文件路径 + * @return std::optional 如果文件存在且格式正确,则返回json对象;否则返回std::nullopt + * + * Read a JSON file + * @param filePath Path to the file + * @return std::optional Returns a json object if the file exists and is correctly formatted; otherwise returns std::nullopt + */ + static std::optional readJsonFile(const fs::path &filePath); + static std::optional readJsonFile(const std::string &filePath); + + /** + * @brief 写入/修改JSON文件 + * @param filePath 文件路径 + * @param j 要写入的json对象 + * @return bool 成功写入返回true,否则返回false + * + * Write/Modify a JSON file + * @param filePath Path to the file + * @param j json object to write + * @return bool Returns true if writing was successful, false otherwise + */ + static bool writeJsonFile(const fs::path &filePath, const json &j); + static bool writeJsonFile(const std::string &filePath, const json &j); + + /** + * @brief 异步读取JSON文件 + * @param filePath 文件路径 + * @param callback 完成读取后的回调函数 + * + * Asynchronously read a JSON file + * @param filePath Path to the file + * @param callback Callback function to be called upon completion + */ + static void asyncReadJsonFile(const fs::path &filePath, std::function)> callback); + static void asyncReadJsonFile(const std::string &filePath, std::function)> callback); + + /** + * @brief 异步写入JSON文件 + * @param filePath 文件路径 + * @param j 要写入的json对象 + * @param callback 完成写入后的回调函数 + * + * Asynchronously write a JSON file + * @param filePath Path to the file + * @param j json object to write + * @param callback Callback function to be called upon completion + */ + static void asyncWriteJsonFile(const fs::path &filePath, const json &j, std::function callback); + static void asyncWriteJsonFile(const std::string &filePath, const json &j, std::function callback); + + /** + * @brief 合并两个JSON对象 + * @param base 基础json对象,合并的结果将存储在此对象中 + * @param toMerge 要合并的json对象 + * + * Merge two JSON objects + * @param base Base json object, the result of the merge will be stored in this object + * @param toMerge json object to merge + */ + static void mergeJsonObjects(json &base, const json &toMerge); + + /** + * @brief 批量异步处理JSON文件 + * @param filePaths 文件路径集合 + * @param process 对每个文件执行的处理函数 + * @param onComplete 全部文件处理完成后的回调函数 + * + * Batch process JSON files asynchronously + * @param filePaths Collection of file paths + * @param process Processing function to execute on each file + * @param onComplete Callback function to be called after all files have been processed + */ + static void batchAsyncProcess(const std::vector &filePaths, std::function)> process, std::function onComplete); + static void batchAsyncProcess(const std::vector &filePaths, std::function)> process, std::function onComplete); + + /** + * @brief 异步删除JSON文件 + * @param filePath 文件路径 + * @param callback 完成删除后的回调函数 + * + * Asynchronously delete a JSON file + * @param filePath Path to the file + * @param callback Callback function to be called upon deletion + */ + static void asyncDeleteJsonFile(const fs::path &filePath, std::function callback); + static void asyncDeleteJsonFile(const std::string &filePath, std::function callback); + + /** + * @brief 异步查询JSON文件中的键值 + * @param filePath 文件路径 + * @param key 要查询的键 + * @param callback 完成查询后的回调函数,返回查询到的值或std::nullopt + * + * Asynchronously query a value by key in a JSON file + * @param filePath Path to the file + * @param key Key to query + * @param callback Callback function to be called upon completion, returning the found value or std::nullopt + */ + static void asyncQueryJsonValue(const fs::path &filePath, const std::string &key, std::function)> callback); + static void asyncQueryJsonValue(const std::string &filePath, const std::string &key, std::function)> callback); + + /** + * @brief 批量处理目录下的所有JSON文件 + * @param directoryPath 目录路径 + * @param process 对每个文件执行的处理函数 + * @param onComplete 全部文件处理完成后的回调函数 + * + * Batch process all JSON files in a directory + * @param directoryPath Path to the directory + * @param process Processing function to execute on each file + * @param onComplete Callback function to be called after all files in the directory have been processed + */ + static void batchProcessDirectory(const fs::path &directoryPath, std::function)> process, std::function onComplete); + static void batchProcessDirectory(const std::string &directoryPath, std::function)> process, std::function onComplete); + }; +} #endif \ No newline at end of file diff --git a/src/task/manager.cpp b/src/task/manager.cpp index 921ed2b8..48ad314d 100644 --- a/src/task/manager.cpp +++ b/src/task/manager.cpp @@ -12,17 +12,28 @@ Description: Task Manager **************************************************/ -#include "task_manager.hpp" +#include "manager.hpp" +#include "atom/server/global_ptr.hpp" #include "atom/log/loguru.hpp" namespace Lithium { TaskManager::TaskManager() - : m_StopFlag(false) {} + : m_StopFlag(false) + { + // Load Task Component from Global Ptr Manager + m_TaskContainer = GetPtr("lithium.task.contianer"); + m_TaskPool = GetPtr("lithium.task.pool"); + m_TaskList = GetPtr("lithium.task.list"); + m_TaskGenerator = GetPtr("lithium.task.generator"); + m_TickScheduler = GetPtr("lithium.task.tick"); + m_TaskLoader = GetPtr("ltihium.task.loader"); + } TaskManager::~TaskManager() { + saveTasksToJson(); } std::shared_ptr TaskManager::createShared() @@ -30,214 +41,118 @@ namespace Lithium return std::make_shared(); } - bool TaskManager::addTask(const std::shared_ptr &task) + bool TaskManager::addTask(const std::string name, const json ¶ms) { - if (!task) - { - LOG_F(ERROR, "Invalid task!"); - return false; - } - m_TaskList.push_back(task); - m_TaskMap[task->getName()] = task; - DLOG_F(INFO, "Task added: {}", task->getName()); + // Task Check Needed + m_TaskList->addOrUpdateTask(name, params); return true; } - bool TaskManager::insertTask(const std::shared_ptr &task, int position) + bool TaskManager::insertTask(const int &position, const std::string &name, const json ¶ms) { - if (!task) + if (m_TaskList->insertTask(name, params, position)) { - LOG_F(ERROR, "Error: Invalid task!"); - return false; + DLOG_F(INFO, "Insert {} task to {}", name, position); } - - if (position < 0 || position >= static_cast(m_TaskList.size())) + else { - LOG_F(ERROR, "Error: Invalid position!"); - return false; } + return true; + } - auto it = m_TaskList.begin() + position; - m_TaskList.insert(it, task); - DLOG_F(INFO, "Task inserted at position %d: {}", position, task->getName()); + bool TaskManager::modifyTask(const std::string &name, const json ¶ms) + { + m_TaskList->addOrUpdateTask(name, params); + return true; + } + + bool TaskManager::deleteTask(const std::string &name) + { + m_TaskList->removeTask(name); return true; } bool TaskManager::executeAllTasks() { - for (auto it = m_TaskList.begin(); it != m_TaskList.end();) + for (const auto &[name, params] : m_TaskList->getTasks()) { - auto &task = *it; - if (!m_StopFlag && task) + DLOG_F(INFO, "Run task {}", name); + std::string task_type = params["type"].get(); + if (auto task = m_TaskContainer->getTask(task_type); task.has_value()) { - try + json t_params = params["params"]; + auto handle = m_TickScheduler->scheduleTask(1, task.value()->m_function, t_params); + + if (params.contains("callbacks")) { - if (task->execute()) + std::vector callbacks = params["callbacks"]; + for (auto callback : callbacks) { - DLOG_F(INFO, "Task executed: {}", task->getName()); - it = m_TaskList.erase(it); + auto c_task = m_TaskContainer->getTask(task_type); + if (c_task.has_value()) + { + m_TickScheduler->setCompletionCallback(handle, [c_task]() + { c_task.value()->m_function({}); }); + } } - else + } + if (params.contains("timers")) + { + std::vector timers = params["timers"]; + for (auto timer : timers) { - ++it; + if (!timer.contains("name") || !timer.contains("params") || !timer.contains("delay")) + { + continue; + } + else + { + std::string timer_name = timer["name"]; + int tick = timer["delay"]; + if (auto tt_task = m_TaskContainer->getTask(name); tt_task.has_value()) + { + m_TickScheduler->scheduleTask(tick, tt_task.value()->m_function, timer["params"]); + } + } } } - catch (const std::exception &e) + m_Timer->start(); + while (!handle->completed.load() || !m_StopFlag.load()) { - LOG_F(ERROR, "Error: Failed to execute task {} - {}", task->getName(), e.what()); - ++it; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if (params.contains("timeout") && m_Timer->elapsedSeconds() > params["timeout"].get()) + { + LOG_F(ERROR, "Timeout"); + break; + } } + m_Timer->stop(); + m_Timer->reset(); } else { - ++it; - } - } - return true; - } - - void TaskManager::stopTask() - { - m_StopFlag = true; - } - - bool TaskManager::executeTaskByName(const std::string &name) - { - auto it = findTaskByName(name); - if (it != m_TaskMap.end() && !m_StopFlag && it->second) - { - try - { - if (it->second->execute()) - { - DLOG_F(INFO, "Task executed: {}", it->second->getName()); - } - else - { - LOG_F(ERROR, "Error: Failed to execute task {}", it->second->getName()); - } - return true; + LOG_F(ERROR, "Task {} contains a invalid function target"); } - catch (const std::exception &e) + if (m_StopFlag.load()) { - LOG_F(ERROR, "Error: Failed to execute task {} - {}", it->second->getName(), e.what()); + break; } } - else - { - LOG_F(ERROR, "Error: Task not found or invalid!"); - } - return false; - } - - bool TaskManager::modifyTask(int index, const std::shared_ptr &task) - { - if (!task) - { - LOG_F(ERROR, "Error: Invalid task!"); - return false; - } - - if (index < 0 || index >= static_cast(m_TaskList.size())) - { - LOG_F(ERROR, "Error: Invalid index!"); - return false; - } - - m_TaskList[index] = task; - DLOG_F(INFO, "Task modified at index %d: {}", index, task->getName()); return true; } - bool TaskManager::modifyTaskByName(const std::string &name, const std::shared_ptr &task) - { - auto it = findTaskByName(name); - if (it != m_TaskMap.end() && task) - { - it->second = task; - DLOG_F(INFO, "Task modified : {}", task->getName()); - return true; - } - return false; - } - - bool TaskManager::deleteTask(int index) - { - if (index < 0 || index >= static_cast(m_TaskList.size())) - { - LOG_F(ERROR, "Error: Invalid index!"); - return false; - } - - auto it = m_TaskList.begin() + index; - auto task = *it; - m_TaskList.erase(it); - DLOG_F(INFO, "Task deleted at index %d: {}", index, task->getName()); - return true; - } - - bool TaskManager::deleteTaskByName(const std::string &name) + void TaskManager::stopTask() { - auto it = findTaskByName(name); - if (it != m_TaskMap.end()) - { - auto task = it->second; - m_TaskList.erase(std::remove(m_TaskList.begin(), m_TaskList.end(), task), m_TaskList.end()); - m_TaskMap.erase(it); - DLOG_F(INFO, "Task deleted: {}", task->getName()); - return true; - } - LOG_F(ERROR, "Error: Task not found!"); - return false; + m_StopFlag.store(true); } - bool TaskManager::queryTaskByName(const std::string &name) + bool TaskManager::executeTaskByName(const std::string &name) { - auto it = findTaskByName(name); - if (it != m_TaskMap.end()) - { - DLOG_F(INFO, "Task found: {}", it->second->getName()); - return true; - } - DLOG_F(INFO, "Task not found!"); return false; } - const std::vector> &TaskManager::getTaskList() const - { - return m_TaskList; - } - bool TaskManager::saveTasksToJson() const { - json jsonArray; - for (const auto &task : m_TaskList) - { - if (task) - { - jsonArray.push_back(task->toJson()); - } - } - - std::ofstream outputFile(m_FileName); - if (!outputFile.is_open()) - { - LOG_F(ERROR, "Error: Failed to open file for writing!"); - return false; - } - - outputFile << jsonArray.dump(4); - outputFile.close(); - DLOG_F(INFO, "Tasks saved to JSON file: {}", m_FileName); return true; } - - std::unordered_map>::iterator TaskManager::findTaskByName(const std::string &name) - { - return std::find_if(m_TaskMap.begin(), m_TaskMap.end(), - [&](const std::pair> &item) - { - return item.second->getName() == name; - }); - } } \ No newline at end of file diff --git a/src/task/manager.hpp b/src/task/manager.hpp index def442fc..8e5c4cd4 100644 --- a/src/task/manager.hpp +++ b/src/task/manager.hpp @@ -29,9 +29,18 @@ Description: Task Manager #include #endif +// #include "checker.hpp" +#include "container.hpp" +#include "generator.hpp" +#include "list.hpp" +#include "loader.hpp" +#include "pool.hpp" +#include "tick.hpp" #include "atom/task/task.hpp" +#include "atom/utils/stopwatcher.hpp" + #include "atom/type/expected.hpp" namespace Lithium @@ -71,52 +80,28 @@ namespace Lithium static std::shared_ptr createShared(); // ------------------------------------------------------------------- - // Task methods + // Task methods (logic) // ------------------------------------------------------------------- /** * @brief 添加任务到任务列表末尾。 * @brief Add task to the end of task list. - * @param nama 任务名称。 - * + * @param name 任务名称。 + * @param name The name of the task * @param params 任务参数。 * @return 添加成功返回 true,否则返回 false。 + * @note 在这里的名称对应的是任务的名称,实际存储为序号 */ - bool addTask(const std::string nama, const json& params); - - /** - * @brief 在指定位置插入任务到任务列表。 - * @param task 要插入的任务指针。 - * @param position 要插入的位置索引。 - * @return 插入成功返回 true,否则返回 false。 - */ - bool insertTask(const std::string nama, const json& params, int position); - - /** - * @brief 执行所有任务。 - * @return 执行成功返回 true,否则返回 false。 - */ - bool executeAllTasks(); - - /** - * @brief 停止当前正在执行的任务。 - */ - void stopTask(); + bool addTask(const std::string name, const json ¶ms); /** - * @brief 根据任务名称执行任务。 + * @brief 根据任务名称删除任务。 * @param name 任务名称。 - * @return 执行成功返回 true,否则返回 false。 + * @return 删除成功返回 true,否则返回 false。 */ - bool executeTaskByName(const std::string &name); + bool deleteTask(const std::string &name); - /** - * @brief 修改指定位置的任务。 - * @param index 任务在列表中的位置索引。 - * @param task 新的任务指针。 - * @return 修改成功返回 true,否则返回 false。 - */ - bool modifyTask(int index, const std::shared_ptr &task); + bool insertTask(const int &position, const std::string &name, const json ¶ms); /** * @brief 根据任务名称修改任务。 @@ -124,34 +109,31 @@ namespace Lithium * @param task 新的任务指针。 * @return 修改成功返回 true,否则返回 false。 */ - bool modifyTaskByName(const std::string &name, const std::shared_ptr &task); + bool modifyTask(const std::string &name, const json ¶ms); /** - * @brief 删除指定位置的任务。 - * @param index 任务在列表中的位置索引。 - * @return 删除成功返回 true,否则返回 false。 + * @brief 根据任务名称执行任务。 + * @param name 任务名称。 + * @return 执行成功返回 true,否则返回 false。 */ - bool deleteTask(int index); + bool executeTaskByName(const std::string &name); /** - * @brief 根据任务名称删除任务。 - * @param name 任务名称。 - * @return 删除成功返回 true,否则返回 false。 + * @brief 执行所有任务。 + * @return 执行成功返回 true,否则返回 false。 */ - bool deleteTaskByName(const std::string &name); + bool executeAllTasks(); /** - * @brief 根据任务名称查询任务是否存在。 - * @param name 任务名称。 - * @return 如果任务存在,则返回 true,否则返回 false。 + * @brief 停止当前正在执行的任务。 */ - bool queryTaskByName(const std::string &name); + void stopTask(); /** * @brief 获取任务列表。 * @return 任务列表的常量引用。 */ - const std::vector> &getTaskList() const; + [[nodiscard]] std::vector> &getTaskList() const; /** * @brief 将任务列表保存为 JSON 文件。 @@ -159,18 +141,23 @@ namespace Lithium */ bool saveTasksToJson() const; + // ------------------------------------------------------------------- + // Task methods (function) + // ------------------------------------------------------------------- + + void loadBuiltinTask(); + private: - std::vector> m_TaskList; /**< 任务列表 */ - std::unordered_map> m_TaskMap; /**< 任务名称到任务指针的映射表 */ - std::string m_FileName; /**< 任务列表的文件名 */ - bool m_StopFlag; /**< 停止标志,用于中止当前正在执行的任务 */ + std::shared_ptr m_TaskContainer; + std::shared_ptr m_TaskGenerator; + std::shared_ptr m_TaskList; + std::shared_ptr m_TaskLoader; + std::shared_ptr m_TaskPool; + std::shared_ptr m_TickScheduler; - /** - * @brief 根据任务名称查找任务。 - * @param name 任务名称。 - * @return 找到的任务指针的迭代器,如果未找到则返回 m_TaskMap.end()。 - */ - std::unordered_map>::iterator findTaskByName(const std::string &name); + std::unique_ptr m_Timer; + + std::atomic_bool m_StopFlag; }; } // namespace Lithium diff --git a/src/task/pool.cpp b/src/task/pool.cpp index 64a366d1..ceb6e4b5 100644 --- a/src/task/pool.cpp +++ b/src/task/pool.cpp @@ -16,76 +16,147 @@ Description: Specialized task pool namespace Lithium { - DynamicThreadPool::DynamicThreadPool(size_t threads = std::thread::hardware_concurrency(), size_t maxThreads = std::numeric_limits::max(), TaskSchedulingStrategy strategy = TaskSchedulingStrategy::FIFO) - : m_stop(false), m_sleep(false), m_defaultThreadCount(threads), m_maxThreadCount(maxThreads), m_activeThreads(0), m_schedulingStrategy(strategy) + // Initialize static thread_local variables + thread_local WorkerQueue *TaskPool::t_localQueue = nullptr; + thread_local size_t TaskPool::t_index = 0; + + bool WorkerQueue::tryPop(std::shared_ptr &task) { - assert(threads > 0 && maxThreads >= threads); - adjustThreadCount(threads); + std::lock_guard lock(mutex); + if (queue.empty()) + { + return false; + } + task = std::move(queue.front()); + queue.pop_front(); + return true; } - DynamicThreadPool::~DynamicThreadPool() + bool WorkerQueue::trySteal(std::shared_ptr &task) { + std::lock_guard lock(mutex); + if (queue.empty()) { - std::unique_lock lock(m_queueMutex); - m_stop = true; + return false; } - m_condition.notify_all(); - for (std::thread &worker : m_workers) - worker.join(); + task = std::move(queue.back()); + queue.pop_back(); + return true; + } + + void WorkerQueue::push(std::shared_ptr task) + { + std::lock_guard lock(mutex); + queue.push_front(std::move(task)); } - void DynamicThreadPool::sleepFor(std::chrono::milliseconds duration) + std::optional> WorkerQueue::tryPop() { + std::lock_guard lock(mutex); + if (queue.empty()) { - std::unique_lock lock(m_queueMutex); - m_sleep = true; + return {}; } - std::this_thread::sleep_for(duration); + auto task = std::move(queue.front()); + queue.pop_front(); + return task; + } + + std::optional> WorkerQueue::trySteal() + { + std::lock_guard lock(mutex); + if (queue.empty()) { - std::unique_lock lock(m_queueMutex); - m_sleep = false; + return {}; } + auto task = std::move(queue.back()); + queue.pop_back(); + return task; } - void DynamicThreadPool::adjustThreadCount(size_t newSize) + TaskPool::TaskPool(size_t threads = std::thread::hardware_concurrency()) + : m_defaultThreadCount(threads) { - newSize = std::min(newSize, m_maxThreadCount); - while (m_workers.size() < newSize) + for (size_t i = 0; i < m_defaultThreadCount; ++i) { - m_workers.emplace_back([this] - { - for (;;) { - std::shared_ptr task; - { - std::unique_lock lock(this->m_queueMutex); - this->m_condition.wait(lock, [this] { return this->m_stop || !this->m_tasks.empty(); }); - if (this->m_stop && this->m_tasks.empty()) - return; - task = std::move(this->m_tasks.top()); - this->m_tasks.pop(); - ++m_activeThreads; - } + m_queues.emplace_back(std::make_unique()); + } + start(); + } + + TaskPool::~TaskPool() + { + stop(); + } + + std::shared_ptr TaskPool::createShared(size_t threads) + { + return std::make_shared(threads); + } + + void TaskPool::workerThread(size_t index) + { + t_index = index; + t_localQueue = m_queues[t_index].get(); + + while (!m_stop) + { + std::shared_ptr task; + for (size_t i = 0; i < m_queues.size(); ++i) + { + if (m_queues[(t_index + i) % m_queues.size()]->tryPop(task)) + { + break; + } + } + if (!task && !tryStealing(task)) + { + std::unique_lock lock(m_conditionMutex); + m_condition.wait(lock, [this, &task] + { return m_stop || tryStealing(task); }); + if (task) + { task->func(); - { - std::lock_guard lock(this->m_queueMutex); - --m_activeThreads; - if (this->shouldReduceThreads()) { - break; - } - } - } }); + } + } + else if (task) + { + task->func(); + } } } - void DynamicThreadPool::wait() + bool TaskPool::tryStealing(std::shared_ptr &task) { - std::unique_lock lock(m_queueMutex); - m_condition.wait(lock, [this] - { return this->m_tasks.empty(); }); + for (size_t i = 0; i < m_queues.size(); ++i) + { + if (m_queues[(t_index + i + 1) % m_queues.size()]->trySteal(task)) + { + return true; + } + } + return false; } - bool DynamicThreadPool::shouldReduceThreads() + void TaskPool::start() { - return m_workers.size() > m_defaultThreadCount && m_tasks.empty(); + for (size_t i = 0; i < m_defaultThreadCount; ++i) + { + m_workers.emplace_back([this, i] + { workerThread(i); }); + } + } + + void TaskPool::stop() + { + m_stop = true; + m_condition.notify_all(); + for (auto &worker : m_workers) + { + if (worker.joinable()) + { + worker.join(); + } + } } } diff --git a/src/task/pool.hpp b/src/task/pool.hpp index 5420f3ef..c4b54019 100644 --- a/src/task/pool.hpp +++ b/src/task/pool.hpp @@ -16,7 +16,7 @@ Description: Specialized task pool #define LITHIUM_TASK_POOL_HPP #include -#include +#include #include #include #include @@ -26,122 +26,207 @@ Description: Specialized task pool #include #include #include -#include #include #include #include #include +#include namespace Lithium { - - enum class TaskSchedulingStrategy - { - FIFO, // First-In-First-Out, 默认行为,使用priority_queue - LIFO // Last-In-First-Out, 使用stack - }; - + /** + * @struct Task + * @brief Represents a task that can be executed by the thread pool. + * + * @brief 表示可以由线程池执行的任务。 + */ struct Task { - int priority; std::function func; - Task(int priority, std::function func) : priority(priority), func(std::move(func)) {} - - bool operator<(const Task &other) const - { - return priority < other.priority; // Lower integer value means higher priority - } + /** + * @brief Constructor for creating a task. + * @param func The function that this task should execute. + * + * @brief 创建任务的构造函数。 + * @param func 该任务应执行的函数。 + */ + explicit Task(std::function func) : func(std::move(func)) {} }; /** - * @class DynamicThreadPool - * @brief A dynamic thread pool that can adjust the number of threads according to the number of tasks - * 一个可以根据任务数量动态调整线程数量的线程池 + * @class WorkerQueue + * @brief A queue of tasks specific to a worker thread in the pool. + * + * @brief 线程池中特定工作线程的任务队列。 */ - class DynamicThreadPool + class WorkerQueue { public: + std::deque> queue; + std::mutex mutex; + /** - * @brief Constructor - * @param threads Initial number of threads 初始线程数量 - * @param maxThreads Maximum number of threads 最大线程数量 - * @param strategy Task scheduling strategy 任务调度策略 + * @brief Attempts to pop a task from the queue. + * @param task The task that was popped from the queue. + * + * @brief 尝试从队列中弹出一个任务。 + * @param task 从队列中弹出的任务。 */ - DynamicThreadPool(size_t threads, size_t maxThreads, TaskSchedulingStrategy strategy); + bool tryPop(std::shared_ptr &task); /** - * @brief Destructor + * @brief Attempts to steal a task from another worker thread. + * @param task The task that was stolen from another worker thread. + * + * @brief 尝试从另一个工作线程偷取一个任务。 + * @param task 从另一个工作线程偷取的任务。 */ - ~DynamicThreadPool(); + bool trySteal(std::shared_ptr &task); /** - * @brief Add a task to the thread pool and return a future - * 将一个任务添加到线程池并返回一个future - * @param priority Priority of the task 任务的优先级 - * @param f Function to be executed 函数 - * @param args Arguments for the function 函数的参数 - * @return std::future Future for retrieving the result 结果的future + * @brief Pushes a task onto the queue. + * @param task The task to push onto the queue. + * + * @brief 将任务推入队列。 + * @param task 要推入队列的任务。 */ - template ::value>> - auto enqueue(int priority, F &&f, Args &&...args) - -> std::future::type>; + void push(std::shared_ptr task); /** - * @brief Add a task to the thread pool without returning a future - * 将一个任务添加到线程池但不返回future - * @param priority Priority of the task 任务的优先级 - * @param f Function to be executed 函数 - * @param args Arguments for the function 函数的参数 + * @brief Attempts to pop a task from the queue. + * @param task The task that was popped from the queue. + * + * @brief 尝试从队列中弹出一个任务。 + * @param task 从队列中弹出的任务。 */ - template ::value>> - void enqueueDetach(int priority, F &&f, Args &&...args); + std::optional> tryPop(); /** - * @brief Sleep for a given duration - * 睡眠一段时间 - * @param duration Duration to sleep 睡眠的时长 + * @brief Attempts to steal a task from another worker thread. + * @param task The task that was stolen from another worker thread. + * + * @brief 尝试从另一个工作线程偷取一个任务。 + * @param task 从另一个工作线程偷取的任务。 */ - void sleepFor(std::chrono::milliseconds duration); + std::optional> trySteal(); + }; + + /** + * @class TaskPool + * @brief A thread pool for executing tasks asynchronously. + * + * TaskPool allows you to enqueue tasks which are functions to be executed + * by a pool of worker threads. It supports task stealing for load balancing + * between threads. + * + * @brief 一个用于异步执行任务的线程池。 + * + * TaskPool 允许你将任务(即要被工作线程池执行的函数)加入队列。它支持任务窃取,以实现线程间的负载平衡。 + */ + class TaskPool + { + private: + std::atomic m_stop{false}; + std::vector m_workers; + std::vector> m_queues; + std::condition_variable m_condition; + std::mutex m_conditionMutex; + size_t m_defaultThreadCount; + static thread_local WorkerQueue *t_localQueue; + static thread_local size_t t_index; + + public: /** - * @brief Adjust the number of threads in the pool - * 调整线程池中的线程数量 - * @param newSize New number of threads 新的线程数量 + * @brief Constructor that initializes the thread pool with a specified number of threads. + * @param threads The number of worker threads to create. + * + * @brief 用指定数量的线程初始化线程池的构造函数。 + * @param threads 要创建的工作线程数量。 */ - void adjustThreadCount(size_t newSize); + explicit TaskPool(size_t threads); /** - * @brief Wait for all tasks to complete - * 等待所有任务完成 + * @brief Destructor that stops all worker threads and cleans up resources. + * + * @brief 析构函数,停止所有工作线程并清理资源。 */ - void wait(); + ~TaskPool(); + + static std::shared_ptr createShared(size_t threads); /** - * @brief Determine whether to reduce the number of threads - * 判断是否需要减少线程数量 - * @return bool Whether to reduce the number of threads 是否需要减少线程数量 + * @brief Enqueues a task for execution by the thread pool. + * @param f The function to execute. + * @param args Arguments to pass to the function. + * @return A future representing the result of the task. + * + * @brief 将一个任务入队以供线程池执行。 + * @param f 要执行的函数。 + * @param args 传递给函数的参数。 + * @return 表示任务结果的未来值。 */ - bool shouldReduceThreads(); + template + auto enqueue(F &&f, Args &&...args) + -> std::future::type> + { + using return_type = typename std::invoke_result::type; + + static_assert(std::is_invocable_v, "Function must be invocable with arguments"); + + auto task = std::make_shared>( + std::bind(std::forward(f), std::forward(args)...)); + + std::future res = task->get_future(); + auto wrappedTask = std::make_shared([task]() + { (*task)(); }); + + if (t_localQueue) + { + t_localQueue->push(wrappedTask); + } + else + { + m_queues[0]->push(wrappedTask); + } + m_condition.notify_one(); + return res; + } private: - // 添加用于存储LIFO策略任务的堆栈 - std::stack> m_taskStack; - TaskSchedulingStrategy m_schedulingStrategy = TaskSchedulingStrategy::FIFO; - std::priority_queue> m_tasks; -#if __cplusplus >= 202002L - std::vector m_workers; -#else - std::vector m_workers; -#endif - - std::mutex m_queueMutex; - std::condition_variable m_condition; - std::atomic m_stop; - std::atomic m_sleep; - size_t m_defaultThreadCount; - size_t m_maxThreadCount; - std::atomic m_activeThreads; + /** + * @brief The main worker thread function. + * @param index The index of the worker thread. + * + * @brief 主工作线程函数。 + * @param index 工作线程的索引。 + */ + void workerThread(size_t index); + + /** + * @brief Tries to steal a task from another worker thread. + * @param task The task to steal. + * + * @brief 尝试从另一个工作线程窃取一个任务。 + * @param task 要窃取的任务。 + */ + bool tryStealing(std::shared_ptr &task); + + /** + * @brief Starts all worker threads. + * + * @brief 启动所有工作线程。 + */ + void start(); + + /** + * @brief Stops all worker threads. + * + * @brief 停止所有工作线程。 + */ + void stop(); }; } + #endif diff --git a/src/task/tick.cpp b/src/task/tick.cpp new file mode 100644 index 00000000..8918a79b --- /dev/null +++ b/src/task/tick.cpp @@ -0,0 +1,124 @@ +/* + * tick.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-4-3 + +Description: Tick Sheduler, just like Minecraft's + +**************************************************/ + +#include "tick.hpp" + +#include "atom/server/global_ptr.hpp" + +namespace Lithium +{ + TickScheduler::TickScheduler(size_t threads): currentTick(0), stop(false) + { +#if __cplusplus >= 202002L + schedulerThread = std::jthread([this] + { this->taskSchedulerLoop(); }); +#else + schedulerThread = std::thread([this] + { this->taskSchedulerLoop(); }); +#endif + pool = GetPtr("lithium.task.pool"); + } + + TickScheduler::~TickScheduler() + { + stopScheduler(); + } + + std::shared_ptr TickScheduler::createShared(size_t threads) + { + return std::make_shared(threads); + } + + void TickScheduler::addDependency(const std::shared_ptr &task, const std::shared_ptr &dependency) + { + task->dependencies.push_back(dependency); + } + + void TickScheduler::setCompletionCallback(const std::shared_ptr &task, std::function callback) + { + task->onCompletion = callback; + } + + void TickScheduler::pause() + { + isPaused.store(true); + } + + void TickScheduler::resume() + { + isPaused.store(false); + cv.notify_all(); + } + + void TickScheduler::taskSchedulerLoop() + { + while (!stop.load()) + { + { + std::unique_lock lock(tasksMutex); + cv.wait(lock, [this] + { return stop.load() || !tasks.empty() || isPaused.load(); }); + + if (stop.load()) + break; + + auto it = tasks.begin(); + while (it != tasks.end()) + { + auto task = *it; + // Check if the task's tick has come and dependencies are met + if (task->tick <= currentTick.load() && allDependenciesMet(task)) + { + pool->enqueue([task]() + { + task->func(); + task->completed.store(true); + if (task->onCompletion) { + task->onCompletion(); + } }); + it = tasks.erase(it); // Remove the task from the list + } + else + { + ++it; + } + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Simulate a tick + currentTick++; + } + } + + bool TickScheduler::allDependenciesMet(const std::shared_ptr &task) + { + for (const auto &dep : task->dependencies) + { + if (!dep->completed.load()) + { + return false; + } + } + return true; + } + + void TickScheduler::stopScheduler() + { + stop.store(true); + cv.notify_all(); + if (schedulerThread.joinable()) + { + schedulerThread.join(); + } + } +} diff --git a/src/task/tick.hpp b/src/task/tick.hpp new file mode 100644 index 00000000..4253b103 --- /dev/null +++ b/src/task/tick.hpp @@ -0,0 +1,174 @@ +/* + * tick.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-4-3 + +Description: Tick Sheduler, just like Minecraft's + +**************************************************/ + +#ifndef LITHIUM_TASK_TICK_HPP +#define LITHIUM_TASK_TICK_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ENABLE_FASTHASH +#include "emhash/hash_table8.hpp" +#else +#include +#endif +#include + +#include "pool.hpp" + +namespace Lithium +{ + struct TickTask + { + /** + * @brief 任务结构体,表示一个待执行的任务 + */ + std::function func; // 任务函数 + int priority; // 任务优先级 + unsigned long long tick; // 任务执行的计划刻 + std::vector> dependencies; // 任务依赖 + std::function onCompletion; // 任务完成时的回调函数 + std::atomic isRunning = false; // 任务是否正在执行的标志 + std::atomic_bool completed = false; // 任务是否已完成的标志 + + /** + * @brief 比较函数,用于优先级队列的排序 + * @param other 另一个TickTask对象 + * @return 当前任务是否小于另一个任务 + */ + bool operator<(const TickTask &other) const + { + return std::tie(priority, tick) < std::tie(other.priority, other.tick); + } + + /** + * @brief 构造函数,初始化任务对象 + * @param func 任务函数 + * @param tick 任务执行的计划刻 + * @param dependencies 任务依赖 + * @param onCompletion 任务完成时的回调函数 + */ + TickTask(std::function func, unsigned long long tick, std::vector> dependencies, + std::function onCompletion) + : func(std::move(func)), priority(0), tick(tick), dependencies(std::move(dependencies)), + onCompletion(std::move(onCompletion)) + { + } + }; + + class TickScheduler + { + public: + /** + * @brief 构造函数,初始化TickScheduler对象 + * @param threads 线程池中的线程数量 + */ + TickScheduler(size_t threads); + + /** + * @brief 析构函数,释放资源 + */ + ~TickScheduler(); + + static std::shared_ptr createShared(size_t threads); + + /** + * @brief 调度一个任务,并返回一个指向该任务的智能指针 + * @param tick 任务执行的计划刻 + * @param f 任务函数 + * @param args 任务函数的参数 + * @return 指向调度的任务的智能指针 + */ + template + auto scheduleTask(unsigned long long tick, F &&f, Args &&...args) -> std::shared_ptr + { + auto taskFunc = [f = std::forward(f), ... args = std::forward(args)]() mutable + { + f(std::forward(args)...); + }; + std::vector> task_dependencies; + auto task = std::make_shared(taskFunc, tick, task_dependencies, nullptr); + { + std::lock_guard lock(tasksMutex); + tasks.push_back(task); + } + cv.notify_one(); + return task; + } + + /** + * @brief 添加一个任务依赖关系 + * @param task 被依赖的任务 + * @param dependency 依赖的任务 + */ + void addDependency(const std::shared_ptr &task, const std::shared_ptr &dependency); + + /** + * @brief 设置任务完成时的回调函数 + * @param task 需要设置回调函数的任务 + * @param callback 回调函数 + */ + void setCompletionCallback(const std::shared_ptr &task, std::function callback); + + /** + * @brief 暂停任务调度器的执行 + */ + void pause(); + + /** + * @brief 恢复任务调度器的执行 + */ + void resume(); + + private: + std::shared_ptr pool; // 线程池对象 + std::vector> tasks; // 所有待执行的任务 + std::mutex tasksMutex; // 任务队列的互斥锁 + std::condition_variable cv; // 条件变量,用于暂停和恢复任务调度器的执行 + std::atomic currentTick; // 当前的计划刻 + std::atomic stop; // 停止任务调度器的标志 + std::atomic isPaused; // 暂停任务调度器的标志 +#if __cplusplus >= 202002L + std::jthread schedulerThread; // 任务调度器的线程 +#else + std::thread schedulerThread; // 任务调度器的线程 +#endif + + /** + * @brief 任务调度循环函数,在单独的线程中运行 + */ + void taskSchedulerLoop(); + + /** + * @brief 检查一个任务的所有依赖是否已满足 + * @param task 待检查的任务 + * @return 是否所有依赖都已满足 + */ + bool allDependenciesMet(const std::shared_ptr &task); + + /** + * @brief 停止任务调度器的执行 + */ + void stopScheduler(); + }; +} + +#endif \ No newline at end of file diff --git a/tests/atom/async/async.cpp b/tests/atom/async/async.cpp new file mode 100644 index 00000000..c961214c --- /dev/null +++ b/tests/atom/async/async.cpp @@ -0,0 +1,31 @@ +#include "atom/async/async.hpp" + +using namespace Atom::Async; +int main() +{ + AsyncWorkerManager manager; + + auto worker1 = manager.CreateWorker([]() + { + std::this_thread::sleep_for(std::chrono::seconds(3)); + return 42; }); + + auto worker2 = manager.CreateWorker([]() + { + std::this_thread::sleep_for(std::chrono::seconds(2)); + return 100; }); + + manager.WaitForAll(); + + std::cout << "All workers are done." << std::endl; + + if (manager.IsDone(worker1)) + { + std::cout << "Worker 1 is done." << std::endl; + } + + manager.Cancel(worker2); + std::cout << "Worker 2 is cancelled." << std::endl; + + return 0; +} \ No newline at end of file diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 7bf94315..4bdc9fe3 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -21,3 +21,6 @@ target_link_libraries(xml2json tinyxml2) add_executable(csv2json csv2json.cpp) target_link_libraries(csv2json loguru) + +add_executable(tcp_proxy tcp_proxy.cpp) +target_link_libraries(tcp_proxy loguru) diff --git a/src/atom/connection/tcp_proxy.cpp b/tools/tcp_proxy.cpp similarity index 100% rename from src/atom/connection/tcp_proxy.cpp rename to tools/tcp_proxy.cpp