浏览代码

更新了基础部件的配置方式

jcsyshc 2 年之前
父节点
当前提交
7eadd46a8c
共有 63 个文件被更改,包括 2218 次插入2673 次删除
  1. 6 3
      CMakeLists.txt
  2. 0 56
      doc/manager_format.txt
  3. 1 1
      src/algorithm/landmark_registration.cpp
  4. 37 59
      src/algorithm/transform_tree.cpp
  5. 0 0
      src/algorithm/transform_tree.h
  6. 1 1
      src/algorithm/transform_utility.hpp
  7. 32 10
      src/core/basic_obj_types.hpp
  8. 215 0
      src/core/external_controller.cpp
  9. 28 0
      src/core/external_controller.h
  10. 95 0
      src/core/global_defs.cpp
  11. 84 0
      src/core/global_defs.h
  12. 161 433
      src/core/sophiar_manager.cpp
  13. 18 72
      src/core/sophiar_manager.h
  14. 1 11
      src/core/sophiar_obj.hpp
  15. 240 0
      src/core/sophiar_pool.cpp
  16. 124 0
      src/core/sophiar_pool.h
  17. 49 49
      src/core/tristate_obj.cpp
  18. 26 17
      src/core/tristate_obj.h
  19. 0 162
      src/core/types/geometry_types.hpp
  20. 0 42
      src/core/types/physical_types.hpp
  21. 0 18
      src/core/types/robot_types.hpp
  22. 0 141
      src/core/types/versatile_data.hpp
  23. 0 27
      src/extern_defs/core/sophiar_manager.cpp
  24. 0 7
      src/extern_defs/core/timestamp_helper.cpp
  25. 0 17
      src/extern_defs/core/types/versatile_data.cpp
  26. 7 7
      src/main.cpp
  27. 0 1
      src/robot/ur/ur_interface.cpp
  28. 0 1
      src/tracker/ndi/ndi_interface.cpp
  29. 30 5
      src/utility/config_utility.hpp
  30. 0 117
      src/utility/coro_signal.hpp
  31. 25 23
      src/utility/coro_signal2.hpp
  32. 8 13
      src/utility/coro_signal_group.hpp
  33. 33 38
      src/utility/coro_worker.hpp
  34. 2 2
      src/utility/coro_worker_helper_func.hpp
  35. 27 198
      src/utility/debug_utility.hpp
  36. 82 0
      src/utility/dynamic_pool.hpp
  37. 0 102
      src/utility/global_obj_helper.hpp
  38. 9 7
      src/utility/name_translator.hpp
  39. 15 14
      src/utility/named_vector.hpp
  40. 0 42
      src/utility/signal_muxer.hpp
  41. 5 5
      src/utility/simple_tristate_obj.hpp
  42. 0 52
      src/utility/slot_demuxer.hpp
  43. 54 0
      src/utility/string_map.hpp
  44. 0 87
      src/utility/tiny_signal.hpp
  45. 106 0
      src/utility/variable_helper.hpp
  46. 184 0
      src/utility/variable_utility.hpp
  47. 0 225
      src/utility/versatile_buffer.hpp
  48. 210 153
      src/utility/versatile_buffer2.hpp
  49. 16 14
      tests/CMakeLists.txt
  50. 12 13
      tests/algorithm/transform_tree.cpp
  51. 0 48
      tests/core/geometry_types.cpp
  52. 54 66
      tests/core/sophiar_manager.cpp
  53. 19 18
      tests/core/tristate_obj.cpp
  54. 0 155
      tests/data/datanode_base_config.json
  55. 16 6
      tests/data/sophiar_manager_config.json
  56. 19 5
      tests/data/transform_tree_config.json
  57. 1 2
      tests/interface/ndi.cpp
  58. 19 15
      tests/utility/coro_signal2.cpp
  59. 29 26
      tests/utility/coro_signal_group.cpp
  60. 15 13
      tests/utility/coro_worker.cpp
  61. 34 0
      tests/utility/dynamic_pool.cpp
  62. 16 20
      tests/utility/global_obj_helper.cpp
  63. 53 54
      tests/utility/global_obj_record_playback.cpp

+ 6 - 3
CMakeLists.txt

@@ -5,6 +5,9 @@ set(CMAKE_CXX_STANDARD 20)
 
 project(${PROJECT_NAME})
 
+add_compile_options(-march=native)
+add_compile_options(-mno-avx) # enable avx will cause some stack pointer alignment error with Eigen
+
 include_directories(./src)
 
 find_package(Boost REQUIRED COMPONENTS iostreams)
@@ -24,6 +27,9 @@ list(APPEND EXTRA_LIBS Eigen3::Eigen)
 find_package(nlohmann_json REQUIRED)
 list(APPEND EXTRA_LIBS nlohmann_json::nlohmann_json)
 
+file(GLOB_RECURSE CORE_IMPL_FILES ./src/core/*.cpp)
+file(GLOB_RECURSE ALGORITHM_IMPL_FILES ./src/algorithm/*.cpp)
+
 file(GLOB_RECURSE SRC_FILES ./src/*.cpp)
 add_executable(${PROJECT_NAME} ${SRC_FILES})
 
@@ -33,8 +39,5 @@ ENDIF ()
 
 target_link_libraries(${PROJECT_NAME} ${EXTRA_LIBS})
 
-file(GLOB_RECURSE EXTERN_DEF_FILES ./src/extern_defs/*.cpp)
-file(GLOB_RECURSE CORE_IMPL_FILES ./src/core/*.cpp)
-
 add_subdirectory(benchmark)
 add_subdirectory(tests)

+ 0 - 56
doc/manager_format.txt

@@ -1,56 +0,0 @@
-Request Format:
-Length [2]
-Command [1]
-Command content [n]
-
-Reply Format:
-Length [2]
-Reply content [n]
-
-Command List:
-0x00 Query manager status
-0x01 Query obj status
-0x02 Query global obj value
-0x0A Query obj index
-0x0B Query global obj index
-0x10 Init obj
-0x11 Start obj
-0x12 Stop obj
-0x13 Reset obj
-
-Command Content List:
-0x00
-None [0]
-0x01
-Query number [2]
-Obj index list [2 * n]
-0x02
-Query number [2]
-Global obj index list [2 * n]
-0x0A
-Obj name length [2]
-Obj name str [n]
-0x0B
-Global obj name length [2]
-Global obj name str [n]
-0x10 - 0x13
-Obj index [2]
-
-Reply Content List:
-0x00
-Manager status [1] 0x00 Initial, 0x01 Normal, 0x02 Busy
-0x01
-Obj status list [1 * n] 0x00 Inital, 0x01 Initializing, 0x02 Resetting,
-                        0x03 Pending, 0x04 Starting, 0x05 Stopping, 0x06 Running
-                        0xFF 
-0x02
-Repeat of the follow [*n]
-  Global obj type [2]
-  Value length [1]
-  Value content [n]
-0x0A
-Obj index [2] (-1 means not found)
-0x0B
-Global obj index [2] (-1 means not found)
-0x10 - 0x13
-Finished [1] (Always 0xFF)

+ 1 - 1
src/algorithm/landmark_registration.cpp

@@ -5,7 +5,7 @@
 #include "utility/bit_operations.hpp"
 #include "utility/config_utility.hpp"
 #include "utility/coro_worker.hpp"
-#include "utility/global_obj_helper.hpp"
+#include "utility/variable_helper.hpp"
 
 #include <Eigen/Geometry>
 

+ 37 - 59
src/core/transform_tree.cpp → src/algorithm/transform_tree.cpp

@@ -1,7 +1,9 @@
 #include "transform_tree.h"
 
 #include "core/basic_obj_types.hpp"
+#include "core/global_defs.h"
 #include "utility/bit_operations.hpp"
+#include "utility/config_utility.hpp"
 #include "utility/coro_worker.hpp"
 #include "utility/coro_signal_group.hpp"
 #include "utility/named_vector.hpp"
@@ -9,8 +11,6 @@
 #include <boost/asio/co_spawn.hpp>
 #include <boost/asio/detached.hpp>
 
-#include <fmt/format.h>
-
 #include <Eigen/Geometry>
 
 #include <cassert>
@@ -35,8 +35,8 @@ namespace sophiar {
 
         struct node_type { // 在父亲下观察自己
             node_index_type parent = -1, depth = 0; // 父亲的编号, 节点深度
-            global_obj_index_type transform_obj_index = -1;
-            trans_type fixed_transform; // 如果不提供 transform_obj_index, 则表示这是一个固定变换
+            variable_index_type transform_var_index = -1;
+            trans_type fixed_transform; // 如果不提供 transform_var_index, 则表示这是一个固定变换
         };
 
         struct query_scheme {
@@ -62,11 +62,10 @@ namespace sophiar {
             auto ret = trans_type::Identity();
             for (auto index: path) {
                 const auto &node_info = nodes[index];
-                if (!is_valid_id(node_info.transform_obj_index)) {
+                if (!is_valid_id(node_info.transform_var_index)) {
                     ret = ret * node_info.fixed_transform;
                 } else {
-                    auto trans_obj = get_manager().
-                            get_global_obj<transform_obj>(node_info.transform_obj_index);
+                    auto trans_obj = QUERY_VARIABLE(transform_obj, node_info.transform_var_index);
                     if (trans_obj == nullptr) return std::nullopt;
                     ret = ret * trans_obj->value;
                 }
@@ -83,40 +82,36 @@ namespace sophiar {
 
         void create_worker(node_index_type observer,
                            node_index_type target,
-                           global_obj_index_type out_obj_index) {
+                           variable_index_type out_var_index) {
             assert(is_valid_id(observer) && is_valid_id(target));
             assert(observer != target);
 
             query_scheme scheme;
-            auto signal_group = coro_signal_any_group::new_instance(get_context());
+            auto signal_group = coro_signal_any_group::new_instance();
             while (observer != target) {
                 const auto &obs_info = nodes[observer];
                 const auto &tar_info = nodes[target];
                 if (obs_info.depth > tar_info.depth) {
                     assert(is_valid_id(obs_info.parent));
                     scheme.left.push_front(observer);
-                    if (is_valid_id(obs_info.transform_obj_index)) {
-                        signal_group->add_watcher(
-                                get_manager().request_global_obj_update_watcher(
-                                        obs_info.transform_obj_index));
+                    if (is_valid_id(obs_info.transform_var_index)) {
+                        signal_group->add_watcher(REQUIRE_VARIABLE_WATCHER(obs_info.transform_var_index));
                     }
                     observer = obs_info.parent;
                 } else { // target is deeper
                     assert(is_valid_id(tar_info.parent));
                     scheme.right.push_front(target);
-                    if (is_valid_id(tar_info.transform_obj_index)) {
-                        signal_group->add_watcher(
-                                get_manager().request_global_obj_update_watcher(
-                                        tar_info.transform_obj_index));
+                    if (is_valid_id(tar_info.transform_var_index)) {
+                        signal_group->add_watcher(REQUIRE_VARIABLE_WATCHER(tar_info.transform_var_index));
                     }
                     target = tar_info.parent;
                 }
             }
 
-            signal_group->start(get_context());
-            auto signal_watcher = signal_group->new_watcher(get_context());
+            signal_group->start();
+            auto signal_watcher = signal_group->new_watcher();
             auto exit_func = [signal_group = std::move(signal_group)]() mutable {
-                co_spawn(global_context, [
+                co_spawn(*global_context, [
                         signal_group = std::move(signal_group)]() mutable
                         -> boost::asio::awaitable<void> {
                     co_await signal_group->stop();
@@ -124,21 +119,19 @@ namespace sophiar {
                 }, detached);
             };
 
-            auto worker_func = [
-                    this, out_obj_index,
+            auto worker_func = [=,
                     scheme = std::move(scheme),
                     signal_watcher = std::move(signal_watcher)]() mutable
                     -> boost::asio::awaitable<bool> {
                 co_await signal_watcher.coro_wait();
                 auto new_trans = get_transform_from_scheme(scheme);
-                get_manager().update_global_obj<transform_obj>(
-                        out_obj_index,
-                        new_trans.has_value() ? transform_obj::new_instance(std::move(new_trans.value())) : nullptr);
+                auto new_trans_obj = new_trans.has_value() ?
+                                     transform_obj::new_instance(std::move(new_trans.value())) : nullptr;
+                UPDATE_VARIABLE(transform_obj, out_var_index, new_trans_obj);
                 co_return true;
             };
-            auto worker = make_infinite_coro_worker(get_context(),
-                                                    std::move(worker_func),
-                                                    std::move(exit_func)); // signal group will be delete here
+            auto worker = make_infinite_coro_worker(std::move(worker_func),
+                                                    std::move(exit_func)); // signal group will be deleted here
             worker->run();
             worker_list.push_back(std::move(worker));
         }
@@ -155,40 +148,33 @@ namespace sophiar {
         }
 
         void build_tree(const nlohmann::json &config) {
-            assert(config.contains("node_list"));
-            assert(config["node_list"].is_array());
+            ENSURE_ARRAY("node_list");
             for (auto &node: config["node_list"]) {
 
-                assert(node.contains("name"));
-                assert(node["name"].is_string());
-                auto name = node["name"].get<std::string>();
+                auto name = LOAD_STRING_ITEM2(node, "name");
                 auto index = nodes.new_elem(name);
 
                 node_index_type parent_index = -1;
                 if (node.contains("parent")) {
-                    assert(node["parent"].is_string());
-                    auto parent_name = node["parent"].get<std::string>();
+                    auto parent_name = LOAD_STRING_ITEM2(node, "parent");
                     parent_index = nodes.to_index_by_name(parent_name);
                 }
 
-                global_obj_index_type transform_obj_index = -1;
-                if (node.contains("transform_obj_name")) {
-                    assert(node["transform_obj_name"].is_string());
-                    auto transform_obj_name = node["transform_obj_name"].get<std::string>();
-                    transform_obj_index = get_manager().
-                            register_global_obj<transform_obj>(transform_obj_name);
+                variable_index_type transform_var_index = invalid_variable_index;
+                if (node.contains("transform_var_name")) {
+                    transform_var_index = LOAD_VARIABLE_INDEX2(node, transform_obj, "transform_var_name");
                 }
 
                 trans_type default_trans = trans_type::Identity();
                 if (node.contains("transform")) {
                     default_trans = transform_from_json_array(node["transform"]);
                 } else if (is_valid_id(parent_index)) {
-                    assert(is_valid_id(transform_obj_index));
+                    assert(is_valid_id(transform_var_index));
                 }
 
                 auto &node_info = nodes[index];
                 node_info.parent = parent_index;
-                node_info.transform_obj_index = transform_obj_index;
+                node_info.transform_var_index = transform_var_index;
                 node_info.fixed_transform = default_trans;
                 if (is_valid_id(parent_index)) {
                     node_info.depth = nodes[parent_index].depth + 1;
@@ -197,21 +183,13 @@ namespace sophiar {
         }
 
         void config_watcher(const nlohmann::json &config) {
-            assert(config.contains("watch_list"));
+            ENSURE_ARRAY("watch_list");
             for (auto &watch_config: config["watch_list"]) {
-                assert(watch_config.contains("target"));
-                assert(watch_config["target"].is_string());
-                assert(watch_config.contains("observer"));
-                assert(watch_config["observer"].is_string());
-                assert(watch_config.contains("transform_name"));
-                assert(watch_config["transform_name"].is_string());
-                auto target_name = watch_config["target"].get<std::string>();
-                auto observer_name = watch_config["observer"].get<std::string>();
+                auto target_name = LOAD_STRING_ITEM2(watch_config, "target");
+                auto observer_name = LOAD_STRING_ITEM2(watch_config, "observer");
                 auto target_index = nodes.to_index_by_name(target_name);
                 auto observer_index = nodes.to_index_by_name(observer_name);
-                auto transform_obj_name = watch_config["transform_name"].get<std::string>();
-                auto transform_obj_index = get_manager().
-                        register_global_obj<transform_obj>(transform_obj_name);
+                auto transform_obj_index = LOAD_VARIABLE_INDEX2(watch_config, transform_obj, "transform_var_name");
                 create_worker(observer_index, target_index, transform_obj_index);
             }
         }
@@ -223,22 +201,22 @@ namespace sophiar {
 
     transform_tree::~transform_tree() = default;
 
-    boost::asio::awaitable<bool> transform_tree::on_init(const nlohmann::json &config) {
+    boost::asio::awaitable<bool> transform_tree::on_init(const nlohmann::json &config) noexcept {
         pimpl->build_tree(config);
         co_return true;
     }
 
-    boost::asio::awaitable<bool> transform_tree::on_start(const nlohmann::json &config) {
+    boost::asio::awaitable<bool> transform_tree::on_start(const nlohmann::json &config) noexcept {
         pimpl->config_watcher(config);
         co_return true;
     }
 
-    boost::asio::awaitable<void> transform_tree::on_stop() {
+    boost::asio::awaitable<void> transform_tree::on_stop() noexcept {
         co_await pimpl->stop_workers();
         co_return;
     }
 
-    boost::asio::awaitable<void> transform_tree::on_reset() {
+    boost::asio::awaitable<void> transform_tree::on_reset() noexcept {
         pimpl->nodes.clear();
         co_return;
     }

+ 0 - 0
src/core/transform_tree.h → src/algorithm/transform_tree.h


+ 1 - 1
src/algorithm/transform_utility.hpp

@@ -5,7 +5,7 @@
 #include "utility/coro_signal_group.hpp"
 #include "utility/coro_worker.hpp"
 #include "utility/simple_tristate_obj.hpp"
-#include "utility/global_obj_helper.hpp"
+#include "utility/variable_helper.hpp"
 
 #include <cassert>
 

+ 32 - 10
src/core/basic_obj_types.hpp

@@ -12,6 +12,7 @@ namespace sophiar {
     template<typename T>
     struct small_obj_wrapper : public small_obj<small_obj_wrapper<T>> {
 
+        using this_type = small_obj_wrapper<T>;
         using value_type = T;
 
         T value;
@@ -42,11 +43,16 @@ namespace sophiar {
 
         template<typename WriterType>
         static void raw_pointer_write_to(WriterType &writer, void *raw_ptr) {
-            using this_type = small_obj_wrapper<T>;
             auto &real_ptr = *static_cast<typename this_type::pointer *>(raw_ptr);
             real_ptr->write_to(writer); // TODO handle empty
         }
 
+        template<typename ReaderType>
+        static void raw_pointer_fill_from(ReaderType &reader, void *raw_ptr) {
+            auto &real_ptr = *static_cast<typename this_type::pointer *>(raw_ptr);
+            real_ptr->fill_from(reader); // TODO handle empty
+        }
+
         static constexpr size_t binary_length() {
             return sizeof(T);
         }
@@ -57,20 +63,20 @@ namespace sophiar {
     using array_obj = small_obj_wrapper<std::array<double, Length>>;
 
     using bool_obj = small_obj_wrapper<bool>;       // 0
-    using u8int_obj = small_obj_wrapper<uint8_t>;   // 1
-    using u16int_obj = small_obj_wrapper<uint16_t>; // 2
-    using u32int_obj = small_obj_wrapper<uint32_t>; // 3
+//    using u8int_obj = small_obj_wrapper<uint8_t>;   // 1
+//    using u16int_obj = small_obj_wrapper<uint16_t>; // 2
+//    using u32int_obj = small_obj_wrapper<uint32_t>; // 3
     using u64int_obj = small_obj_wrapper<uint64_t>; // 4
-    using i8int_obj = small_obj_wrapper<int8_t>;    // 5
-    using i16int_obj = small_obj_wrapper<int16_t>;  // 6
-    using i32int_obj = small_obj_wrapper<int32_t>;  // 7
-    using i64int_obj = small_obj_wrapper<int64_t>;  // 8
-    using float_obj = small_obj_wrapper<float>;     // 9
+//    using i8int_obj = small_obj_wrapper<int8_t>;    // 5
+//    using i16int_obj = small_obj_wrapper<int16_t>;  // 6
+//    using i32int_obj = small_obj_wrapper<int32_t>;  // 7
+//    using i64int_obj = small_obj_wrapper<int64_t>;  // 8
+//    using float_obj = small_obj_wrapper<float>;     // 9
     using double_obj = small_obj_wrapper<double>;   // 10
     using scalarxyz_obj = small_obj_wrapper<Eigen::Vector3d>;   // 11
     using transform_obj = small_obj_wrapper<Eigen::Isometry3d>; // 12
     using array6_obj = array_obj<6>; // 13
-    using array7_obj = array_obj<7>; // 14
+//    using array7_obj = array_obj<7>; // 14
 
     // specialization
 
@@ -88,6 +94,14 @@ namespace sophiar {
         reader >> value.x() >> value.y() >> value.z();
     }
 
+    template<>
+    template<typename ReaderType>
+    inline void array6_obj::fill_from(ReaderType &reader) {
+        for (auto &val: value) {
+            reader >> val;
+        }
+    }
+
     template<>
     template<typename WriterType>
     inline void transform_obj::write_to(WriterType &writer) const {
@@ -103,6 +117,14 @@ namespace sophiar {
         writer << value.x() << value.y() << value.z();
     }
 
+    template<>
+    template<typename WriterType>
+    inline void array6_obj::write_to(WriterType &writer) const {
+        for (auto val: value) {
+            writer << val;
+        }
+    }
+
     template<>
     constexpr size_t transform_obj::binary_length() {
         return 7 * sizeof(double);

+ 215 - 0
src/core/external_controller.cpp

@@ -0,0 +1,215 @@
+#include "external_controller.h"
+#include "core/global_defs.h"
+#include "core/sophiar_manager.h"
+#include "core/tristate_obj.h"
+#include "utility/config_utility.hpp"
+#include "utility/coro_worker.hpp"
+#include "utility/name_translator.hpp"
+#include "utility/versatile_buffer2.hpp"
+
+#include <boost/asio/awaitable.hpp>
+#include <boost/asio/defer.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/redirect_error.hpp>
+#include <boost/asio/use_awaitable.hpp>
+#include <boost/system/error_code.hpp>
+
+#include <spdlog/spdlog.h>
+
+namespace sophiar {
+
+    using namespace boost::asio::ip;
+    using boost::asio::awaitable;
+    using boost::asio::redirect_error;
+    using boost::asio::use_awaitable;
+    using boost::system::error_code;
+
+    struct external_controller::impl {
+
+        static constexpr uint16_t DEFAULT_LISTEN_PORT = 5277;
+
+        struct client_instance;
+
+        uint16_t listen_port = DEFAULT_LISTEN_PORT;
+        coro_worker::pointer listen_worker;
+
+        static void create_client_worker(tcp::socket &&s);
+
+        void load_config(const nlohmann::json &config) {
+            if (config.contains("controller_port")) {
+                listen_port = LOAD_UINT_ITEM("controller_port");
+            } else {
+                listen_port = DEFAULT_LISTEN_PORT;
+                // TODO show log
+            }
+        }
+
+        bool start() {
+            assert(listen_worker == nullptr);
+            auto listen_endpoint = tcp::endpoint(tcp::v4(), listen_port);
+            try {
+                auto listen_func = [=,
+                        acceptor = tcp::acceptor(*global_context, listen_endpoint)
+                ]() mutable -> awaitable<bool> {
+                    error_code ec;
+                    auto client = co_await acceptor.async_accept(redirect_error(use_awaitable, ec));
+                    if (ec) {
+                        SPDLOG_ERROR("Error while waiting for client: {}", ec.what());
+                        co_return false;
+                    } else {
+                        create_client_worker(std::move(client));
+                    }
+                    co_return true;
+                };
+                listen_worker = make_infinite_coro_worker(std::move(listen_func));
+                listen_worker->run();
+                SPDLOG_INFO("External controller is listen on {}:{}",
+                            listen_endpoint.address().to_string(),
+                            listen_endpoint.port());
+            } catch (std::exception &e) {
+                // TODO show log, maybe port is occupied.
+                return false;
+            }
+
+            return true;
+        }
+
+        static awaitable<bool> init_object(std::string_view obj_name) {
+            return global_sophiar_manager->init_object(obj_name);
+        }
+
+        static awaitable<bool> start_object(std::string_view obj_name) {
+            return global_sophiar_manager->start_object(obj_name);
+        }
+
+        static awaitable<bool> stop_object(std::string_view obj_name) {
+            return global_sophiar_manager->stop_object(obj_name);
+        }
+
+        static awaitable<bool> reset_object(std::string_view obj_name) {
+            return global_sophiar_manager->reset_object(obj_name);
+        }
+
+        static uint8_t query_object_state(std::string_view obj_name) {
+            return global_sophiar_manager->query_object_state(obj_name);
+        }
+    };
+
+    struct external_controller::impl::client_instance {
+        using header_type = uint16_t;
+        static constexpr size_t header_offset = sizeof(header_type);
+
+        tcp::socket s;
+        dynamic_memory::pointer buf_in, buf_out;
+        dynamic_vector<std::string_view> params;
+
+        explicit client_instance(tcp::socket &&_s)
+                : s(std::move(_s)),
+                  buf_in(dynamic_memory::new_instance()),
+                  buf_out(dynamic_memory::new_instance()) {
+            s.set_option(tcp::no_delay(true)); // decrease latency
+        }
+
+        awaitable<bool> work_once() {
+            // receive command
+            auto length = co_await async_read_value<sophiar_endian, header_type>(s);
+            buf_in->resize(length);
+            co_await async_fill_memory_from(s, *buf_in);
+
+            auto reader = versatile_reader<sophiar_endian>(*buf_in);
+            auto writer = dynamic_memory_writer<sophiar_endian>(buf_out.get(), header_offset);
+
+            // load command and params
+            auto cmd = reader.read_string_until(' ');
+            params.clear();
+            while (!reader.empty()) {
+                params.push_back(reader.read_string_until(','));
+            }
+
+            // handle command
+            if (cmd == "INIT") {
+                for (auto obj: params) {
+                    auto ok = co_await init_object(obj);
+                    writer << (ok ? "True" : "False") << ',';
+                }
+            } else if (cmd == "START") {
+                for (auto obj: params) {
+                    auto ok = co_await start_object(obj);
+                    writer << (ok ? "True" : "False") << ',';
+                }
+            } else if (cmd == "STOP") {
+                for (auto obj: params) {
+                    auto ok = co_await stop_object(obj);
+                    writer << (ok ? "True" : "False") << ',';
+                }
+            } else if (cmd == "RESET") {
+                for (auto obj: params) {
+                    auto ok = co_await reset_object(obj);
+                    writer << (ok ? "True" : "False") << ',';
+                }
+            } else if (cmd == "QUERY") {
+                for (auto obj: params) {
+                    auto state = query_object_state(obj);
+                    writer << state_name_translator->translate(state) << ',';
+                }
+            } else {
+                writer << "Unknown command.";
+            }
+
+            // make reply looks good
+            if (!params.empty()) {
+                buf_out->increase_size(-1); // remove the trailing ','
+            }
+
+            // send reply
+            assert(buf_out->size() >= header_offset);
+            assert(buf_out->size() - header_offset <= std::numeric_limits<header_type>::max());
+            length = buf_out->size() - header_offset;
+            write_binary_value<sophiar_endian>(buf_out->data(), length);
+            co_await async_write_memory_to(s, *buf_out);
+
+            co_return true;
+        }
+
+    };
+
+    void external_controller::impl::create_client_worker(tcp::socket &&s) {
+        auto re = s.remote_endpoint();
+        auto worker_func = [
+                client = client_instance(std::move(s))]() mutable
+                -> awaitable<bool> {
+            return client.work_once();
+        };
+        auto noexcept_worker_func = make_noexcept_func(
+                std::move(worker_func), [=](std::exception &e) {
+                    SPDLOG_WARN("Client {}:{} left: {}",
+                                re.address().to_string(), re.port(), e.what());
+                });
+        auto worker_ptr_ptr = new coro_worker *;
+        auto exit_func = [=]() {
+            assert(worker_ptr_ptr != nullptr);
+            boost::asio::defer(*global_context, [=]() {
+                delete *worker_ptr_ptr;
+                delete worker_ptr_ptr;
+            });
+        };
+        auto worker = make_infinite_coro_worker(std::move(noexcept_worker_func),
+                                                std::move(exit_func));
+        worker->run();
+        SPDLOG_INFO("Working with client {}:{}",
+                    re.address().to_string(), re.port());
+        *worker_ptr_ptr = worker.release();
+    }
+
+    external_controller::external_controller()
+            : pimpl(std::make_unique<impl>()) {}
+
+    external_controller::~external_controller() = default;
+
+
+    bool external_controller::load_config_and_start(const nlohmann::json &config) {
+        pimpl->load_config(config);
+        return pimpl->start();
+    }
+
+}

+ 28 - 0
src/core/external_controller.h

@@ -0,0 +1,28 @@
+#ifndef SOPHIAR2_EXTERNAL_CONTROLLER_H
+#define SOPHIAR2_EXTERNAL_CONTROLLER_H
+
+#include <nlohmann/json.hpp>
+
+#include <memory>
+
+namespace sophiar {
+
+    class external_controller {
+    public:
+
+        external_controller();
+
+        ~external_controller();
+
+        bool load_config_and_start(const nlohmann::json &config);
+
+    private:
+        struct impl;
+        std::unique_ptr<impl> pimpl;
+
+    };
+
+}
+
+
+#endif //SOPHIAR2_EXTERNAL_CONTROLLER_H

+ 95 - 0
src/core/global_defs.cpp

@@ -0,0 +1,95 @@
+#include "global_defs.h"
+#include "algorithm/transform_tree.h"
+#include "core/external_controller.h"
+#include "core/sophiar_manager.h"
+#include "core/sophiar_pool.h"
+#include "core/timestamp_helper.hpp"
+#include "utility/debug_utility.hpp"
+#include "utility/dynamic_pool.hpp"
+#include "utility/variable_utility.hpp"
+
+#ifdef BOOST_OS_WINDOWS_AVAILABLE
+
+#include <timeapi.h>
+#include <cstdlib>
+
+#endif // BOOST_OS_WINDOWS_AVAILABLE
+
+namespace sophiar {
+
+    dynamic_pool *global_dynamic_pool = nullptr;
+    boost::asio::io_context *global_context = nullptr;
+    sophiar_manager *global_sophiar_manager = nullptr;
+    sophiar_pool *global_sophiar_pool = nullptr;
+    external_controller *global_external_controller = nullptr;
+
+    local_time_type program_start_time;
+
+    void register_variable_utility_types() {
+#define REGISTER_VARIABLE_TYPE(var_type) \
+        using var_type##_watcher = variable_debug_watcher<var_type>; \
+        REGISTER_TYPE(var_type##_watcher); \
+        using var_type##_recorder = variable_recorder<var_type>; \
+        REGISTER_TYPE(var_type##_recorder); \
+        using var_type##_replayer = variable_replayer<var_type>; \
+        REGISTER_TYPE(var_type##_replayer);
+
+        REGISTER_VARIABLE_TYPE(bool_obj);
+        REGISTER_VARIABLE_TYPE(u64int_obj);
+        REGISTER_VARIABLE_TYPE(double_obj);
+        REGISTER_VARIABLE_TYPE(scalarxyz_obj);
+        REGISTER_VARIABLE_TYPE(transform_obj);
+        REGISTER_VARIABLE_TYPE(array6_obj);
+
+#undef REGISTER_VARIABLE_TYPE
+    }
+
+    void register_object_types() {
+
+#if !SOPHIAR_TEST || SOPHIAR_TEST_ALGORITHM
+
+        REGISTER_TYPE(transform_tree);
+
+#endif
+
+        register_variable_utility_types();
+    }
+
+    bool initialize(const nlohmann::json &config) {
+
+        program_start_time = get_local_time();
+
+        global_context = new boost::asio::io_context{};
+        global_dynamic_pool = new dynamic_pool{};
+
+#ifdef BOOST_OS_WINDOWS_AVAILABLE
+
+        timeBeginPeriod(1); // make windows timer more precise
+        std::atexit([]() { timeEndPeriod(1); });
+
+#endif // BOOST_OS_WINDOWS_AVAILABLE
+
+        global_sophiar_pool = new sophiar_pool{};
+        global_sophiar_manager = new sophiar_manager{};
+        global_external_controller = new external_controller{};
+
+        register_object_types();
+
+#ifdef SOPHIAR_TEST
+
+        if (config.empty()) return true;
+
+#else
+
+        assert(!config.empty());
+
+#endif // SOPHIAR_TEST
+
+        global_sophiar_pool->load_config(config);
+        global_sophiar_manager->load_config_and_start(config);
+        ENSURE(global_external_controller->load_config_and_start(config))
+
+        return true;
+    }
+
+}

+ 84 - 0
src/core/global_defs.h

@@ -0,0 +1,84 @@
+#ifndef SOPHIAR2_GLOBAL_DEFS_H
+#define SOPHIAR2_GLOBAL_DEFS_H
+
+#include <boost/asio/io_context.hpp>
+#include <boost/endian.hpp>
+
+#include <nlohmann/json.hpp>
+
+#include <memory>
+
+namespace sophiar {
+
+    class dynamic_pool;
+    class sophiar_manager;
+    class sophiar_pool;
+
+    extern dynamic_pool *global_dynamic_pool;
+    extern boost::asio::io_context *global_context;
+    extern sophiar_manager *global_sophiar_manager;
+    extern sophiar_pool *global_sophiar_pool;
+
+#ifdef SOPHIAR_TEST
+
+    class external_controller;
+    extern external_controller *global_external_controller;
+
+#endif // SOPHIAR_TEST
+
+    static constexpr auto sophiar_endian = boost::endian::order::big;
+
+    bool initialize(const nlohmann::json &config);
+
+#define REQUIRE_VARIABLE(var_type, var_name) \
+    global_sophiar_pool->require_variable<var_type>(var_name)
+
+#define QUERY_VARIABLE(var_type, var_index) \
+    global_sophiar_pool->query_variable<var_type>(var_index)
+
+#define QUERY_VARIABLE_WITH_TS(var_type, var_index, ts_ptr) \
+    global_sophiar_pool->query_variable<var_type>(var_index, ts_ptr)
+
+#define REQUIRE_VARIABLE_WATCHER(var_index) \
+    global_sophiar_pool->require_variable_watcher(var_index)
+
+#define UPDATE_VARIABLE(var_type, var_index, val_ptr) \
+    global_sophiar_pool->update_variable<var_type>(var_index, val_ptr)
+
+#define UPDATE_VARIABLE_WITH_TS(var_type, var_index, val_ptr, ts) \
+    global_sophiar_pool->update_variable<var_type>(var_index, val_ptr, ts)
+
+#define UPDATE_VARIABLE_VAL(var_type, var_index, val) \
+    global_sophiar_pool->update_variable<var_type>(var_index, var_type::new_instance(val))
+
+#define UPDATE_VARIABLE_VAL_WITH_TS(var_type, var_index, val, ts) \
+    global_sophiar_pool->update_variable<var_type>(var_index, var_type::new_instance(val), ts)
+
+#ifdef SOPHIAR_TEST
+
+#define REGISTER_VARIABLE(var_name, var_type) \
+    global_sophiar_pool->register_variable(var_name, var_type)
+
+#endif // SOPHIAR_TEST
+
+#define QUERY_OBJECT_NAME(obj_ptr) \
+    global_sophiar_manager->get_object_name(obj_ptr)
+
+#define ALLOCATE_DYNAMIC_MEMORY(s) \
+    global_dynamic_pool->allocate(s)
+
+#define ALLOCATE_DYNAMIC_MEMORY_N(s, n) \
+    global_dynamic_pool->allocate(s, n)
+
+#define DEALLOCATE_DYNAMIC_MEMORY(ptr, s) \
+    global_dynamic_pool->deallocate(ptr, s)
+
+#define DEALLOCATE_DYNAMIC_MEMORY_N(ptr, s, n) \
+    global_dynamic_pool->deallocate(ptr, s, n)
+
+#define ACTUAL_DYNAMIC_MEMORY_SIZE(s) \
+    global_dynamic_pool->actual_allocate_size(s)
+
+}
+
+#endif //SOPHIAR2_GLOBAL_DEFS_H

+ 161 - 433
src/core/sophiar_manager.cpp

@@ -1,9 +1,10 @@
 #include "sophiar_manager.h"
 #include "core/basic_obj_types.hpp"
+#include "core/global_defs.h"
 #include "core/small_obj.hpp"
-#include "core/sophiar_obj.hpp"
 #include "core/tristate_obj.h"
 #include "third_party/scope_guard.hpp"
+#include "utility/config_utility.hpp"
 #include "utility/coro_signal2.hpp"
 #include "utility/coro_worker.hpp"
 #include "utility/debug_utility.hpp"
@@ -14,11 +15,7 @@
 #include <boost/asio/co_spawn.hpp>
 #include <boost/asio/detached.hpp>
 #include <boost/asio/experimental/channel.hpp>
-#include <boost/asio/ip/address.hpp>
-#include <boost/asio/ip/tcp.hpp>
-#include <boost/asio/redirect_error.hpp>
 #include <boost/asio/use_awaitable.hpp>
-#include <boost/iterator/counting_iterator.hpp>
 #include <boost/system/error_code.hpp>
 
 #include <fmt/format.h>
@@ -40,12 +37,9 @@ namespace sophiar {
     using boost::asio::co_spawn;
     using boost::asio::detached;
     using boost::asio::experimental::channel;
-    using boost::asio::redirect_error;
     using boost::asio::use_awaitable;
     using boost::system::error_code;
 
-    using namespace boost::asio::ip;
-
     struct sophiar_manager::impl {
 
         enum class manager_status_type {
@@ -54,15 +48,7 @@ namespace sophiar {
             BUSY = 0x02
         };
 
-        static constexpr uint16_t default_listen_port = 5277;
-        static constexpr size_t command_initial_length = 1024; // TODO 考虑怎么支持超长报文
-        static constexpr size_t reply_max_length = 4096; // TODO 考虑支持更长的回复
-
-        using command_reader_type = versatile_readable_buffer<boost::endian::order::big, extern_memory>;
-        using reply_writer_type = versatile_writable_buffer<boost::endian::order::big, extern_memory>;
-
         using obj_type_index_type = uint8_t;
-        using obj_factory_func_pool_type = named_vector<obj_type_index_type, obj_factory_func_type>;
 
         using obj_index_type = uint16_t;
         struct obj_info {
@@ -73,9 +59,6 @@ namespace sophiar {
             std::vector<obj_index_type> reverse_dependency_list; // 哪些节点需要这个节点
         };
 
-        using obj_pool_type = named_vector<obj_index_type, obj_info>;
-        using obj_ptr_index_map_type = std::unordered_map<sophiar_obj *, obj_index_type>;
-
         enum class manager_event_type {
             START, STOP
         };
@@ -84,53 +67,25 @@ namespace sophiar {
             manager_event_type event;
             obj_type_index_type obj_index;
             std::unique_ptr<coro_signal2> callback;
-
-            manager_event(manager_event_type _event, obj_type_index_type _index,
-                          std::unique_ptr<coro_signal2> _callback = nullptr) :
-                    event(_event), obj_index(_index), callback(std::move(_callback)) {}
         };
 
-        using manager_event_channel_type = channel<void(error_code, manager_event::pointer)>;
-
-        using global_obj_writer_func_type = void (*)(reply_writer_type &, void *);
-        using global_obj_type_index = uint16_t;
-        using global_obj_writer_func_pool_type = std::vector<global_obj_writer_func_type>;
-        using global_obj_type_to_index_pool_type = std::unordered_map<std::type_index, global_obj_type_index>;
-
-        struct global_obj_info {
-            void *placeholder;
-            coro_signal2 *update_signal;
-            timestamp_type last_update_ts;
-            std::type_index obj_type = typeid(void);
-            global_obj_type_index obj_type_index = ~static_cast<global_obj_type_index>(0);
-        };
-
-        using global_obj_pool_type = named_vector<global_obj_index_type, global_obj_info>;
-
         sophiar_manager *q_this = nullptr;
         manager_status_type manager_status = manager_status_type::INITIAL;
 
+        using obj_factory_func_pool_type = named_vector<obj_type_index_type, obj_factory_func_type>;
+        using obj_pool_type = named_vector<obj_index_type, obj_info>;
+        using obj_ptr_index_map_type = std::unordered_map<sophiar_obj *, obj_index_type>;
+
         obj_factory_func_pool_type obj_factory_func_pool;
         obj_pool_type obj_pool;
         obj_ptr_index_map_type obj_ptr_index_map;
 
-        global_obj_writer_func_pool_type global_obj_writer_func_pool;
-        global_obj_type_to_index_pool_type global_obj_type_to_index_pool;
-        global_obj_pool_type global_obj_pool;
-
+        using manager_event_channel_type = channel<void(error_code, manager_event::pointer)>;
         manager_event_channel_type manager_event_channel;
         coro_worker::pointer manager_worker;
 
-        using client_worker_list_type = std::list<coro_worker::pointer>;
-
-        uint16_t listen_port = default_listen_port;
-        coro_worker::pointer listen_worker;
-        client_worker_list_type client_worker_list;
-
         impl() :
-                manager_event_channel(global_context) {
-            register_basic_global_obj_types();
-        }
+                manager_event_channel(*global_context) {}
 
         std::string get_obj_name_by_ptr(sophiar_obj *obj) const {
             assert(obj_ptr_index_map.contains(obj));
@@ -138,40 +93,16 @@ namespace sophiar {
             return obj_pool.to_name_by_index(obj_index);
         }
 
-        template<typename SmallObjType>
-        void register_global_obj_type() {
-            std::type_index obj_type = typeid(SmallObjType);
-            static_assert(SmallObjType::binary_length() <= std::numeric_limits<uint8_t>::max());
-            assert(!global_obj_type_to_index_pool.contains(obj_type));
-            auto writer_func = &SmallObjType::template raw_pointer_write_to<reply_writer_type>;
-            auto obj_type_index = global_obj_writer_func_pool.size();
-            global_obj_type_to_index_pool[obj_type] = obj_type_index;
-            global_obj_writer_func_pool.push_back(writer_func);
-        }
-
-        void register_basic_global_obj_types() {
-            register_global_obj_type<bool_obj>();
-            register_global_obj_type<u8int_obj>();
-            register_global_obj_type<u16int_obj>();
-            register_global_obj_type<u32int_obj>();
-            register_global_obj_type<u64int_obj>();
-            register_global_obj_type<i8int_obj>();
-            register_global_obj_type<i16int_obj>();
-            register_global_obj_type<i32int_obj>();
-            register_global_obj_type<i64int_obj>();
-            register_global_obj_type<float_obj>();
-            register_global_obj_type<double_obj>();
-            register_global_obj_type<scalarxyz_obj>();
-            register_global_obj_type<transform_obj>();
-            register_global_obj_type<array6_obj>();
-            register_global_obj_type<array7_obj>();
-            assert(global_obj_type_to_index_pool[typeid(array7_obj)] == 14);
-        }
-
         auto get_tristate_ptr(obj_index_type obj_index) const {
             return dynamic_cast<tristate_obj *>(obj_pool[obj_index].ptr);
         }
 
+        bool check_object_name_for_controller(std::string_view obj_name) {
+            if (obj_pool.contains(obj_name)) [[likely]] return true;
+            SPDLOG_ERROR("Object [name = {}] is not registered.", obj_name);
+            return false;
+        }
+
         // start obj and its dependencies recursively
         awaitable<bool> start_obj(obj_index_type obj_index) {
             assert(manager_status == manager_status_type::BUSY);
@@ -179,7 +110,7 @@ namespace sophiar {
             // check loop
             auto &info = obj_pool[obj_index];
             if (info.in_queue) {
-                assert(false);
+                assert(false); // TODO 先检测是否有环,这里就可以直接 assert 了
                 co_return false;
             }
             info.in_queue = true;
@@ -200,7 +131,7 @@ namespace sophiar {
             co_return true;
         }
 
-        // stop obj and objs depending on it recursively
+        // stop obj and objects depending on it recursively
         awaitable<void> stop_obj(obj_index_type obj_index) {
             assert(manager_status == manager_status_type::BUSY);
 
@@ -229,7 +160,7 @@ namespace sophiar {
 
         void start_manager_worker() { // 启动一个处理 start 和 stop 事件的协程
             assert(manager_worker == nullptr);
-            manager_worker = make_infinite_coro_worker(global_context, [this]() -> awaitable<bool> {
+            manager_worker = make_infinite_coro_worker([this]() -> awaitable<bool> {
                 auto event = co_await manager_event_channel.async_receive(use_awaitable);
                 assert(event != nullptr);
                 manager_status = manager_status_type::BUSY;
@@ -249,13 +180,19 @@ namespace sophiar {
         }
 
         void on_object_stopped(sophiar_obj *obj_ptr) {
+
+#ifdef SOPHIAR_TEST
+
+            if (manager_status == manager_status_type::INITIAL) return;
+
+#endif // SOPHIAR_TEST
+
+            auto event = manager_event::new_instance();
             assert(obj_ptr_index_map.contains(obj_ptr));
-            auto obj_index = obj_ptr_index_map.at(obj_ptr);
-            auto event = manager_event::new_instance(manager_event_type::STOP, obj_index);
-            co_spawn(global_context, [this, event = std::move(event)]() mutable -> awaitable<void> {
-                co_await manager_event_channel.async_send(error_code{}, std::move(event), use_awaitable);
-                co_return;
-            }, detached);
+            event->obj_index = obj_ptr_index_map.at(obj_ptr);
+            event->event = manager_event_type::STOP;
+            event->callback = nullptr;
+            manager_event_channel.async_send(error_code{}, std::move(event), detached);
         }
 
         obj_index_type create_object(const std::string &type_name,
@@ -274,12 +211,8 @@ namespace sophiar {
         }
 
         void create_obj_and_load_config(const nlohmann::json &config) {
-            assert(config.contains("type"));
-            assert(config.contains("name"));
-            assert(config["type"].is_string());
-            assert(config["name"].is_string());
-            auto type_name = config["type"].get<std::string>();
-            auto obj_name = config["name"].get<std::string>();
+            auto type_name = LOAD_STRING_ITEM("type");
+            auto obj_name = LOAD_STRING_ITEM("name");
             auto obj_index = create_object(type_name, obj_name);
             auto &obj_info = obj_pool[obj_index];
 
@@ -299,11 +232,11 @@ namespace sophiar {
         }
 
         void load_obj_dependencies(const nlohmann::json &config) {
-            auto obj_name = config["name"].get<std::string>();
+            auto obj_name = LOAD_STRING_ITEM("name");
             auto obj_index = obj_pool.to_index_by_name(obj_name);
             auto &obj_info = obj_pool[obj_index];
             if (!config.contains("dependencies")) return;
-            assert(config["dependencies"].is_array());
+            ENSURE_ARRAY("dependencies");
             for (auto &dep_json: config["dependencies"]) {
                 assert(dep_json.is_string());
                 auto dep_name = dep_json.get<std::string>();
@@ -315,17 +248,8 @@ namespace sophiar {
         }
 
         void build_graph(const nlohmann::json &config) {
-            // load listen port
-            if (config.contains("listen_port")) {
-                assert(config["listen_port"].is_number_unsigned());
-                listen_port = config["listen_port"].get<std::uint64_t>();
-            } else {
-                listen_port = default_listen_port;
-                // TODO show log, use default
-            }
             // create obj and config them
-            assert(config.contains("object_list"));
-            assert(config["object_list"].is_array());
+            ENSURE_ARRAY("object_list");
             for (auto &obj_config: config["object_list"]) { // pass one
                 create_obj_and_load_config(obj_config);
             }
@@ -336,286 +260,82 @@ namespace sophiar {
             // TODO check for loop in the dependency map
         }
 
-        static awaitable<uint8_t> receive_command(tcp::socket &client_socket, dynamic_memory &buffer_memory) {
-            using header_reader_type = versatile_readable_buffer<boost::endian::order::big, static_memory<3>>;
-            header_reader_type header_reader;
-            co_await header_reader.async_read_from(client_socket, 3);
-            auto cmd_length = header_reader.read_value<uint16_t>() - 1;
-            assert(std::is_signed_v<decltype(cmd_length)> && cmd_length >= 0);
-            auto cmd_code = header_reader.read_value<uint8_t>();
-            buffer_memory.ensure_length(cmd_length);
-            auto cmd_buffer = buffer(buffer_memory.data(), cmd_length);
-            co_await async_read(client_socket, cmd_buffer, use_awaitable);
-            co_return cmd_code;
-        }
-
-        void handle_query_manager_status(reply_writer_type &writer) const {
-            writer << (uint8_t) manager_status;
-        }
-
-        void handle_query_obj_status(command_reader_type &reader,
-                                     reply_writer_type &writer) const {
-            auto query_number = reader.read_value<uint16_t>();
-            while (query_number--) {
-                auto obj_index = reader.read_value<obj_index_type>();
-                if (!obj_pool.contains(obj_index) || get_tristate_ptr(obj_index) == nullptr) {
-                    // TODO show log
-                    writer << (uint8_t) 0xFF;
-                } else {
-                    writer << (uint8_t) get_tristate_ptr(obj_index)->get_state();
-                }
-            }
-        }
-
-        void handle_query_global_obj_value(command_reader_type &reader,
-                                           reply_writer_type &writer) const {
-            auto query_number = reader.read_value<uint16_t>();
-            while (query_number--) {
-                auto obj_index = reader.read_value<global_obj_index_type>();
-                if (!global_obj_pool.contains(obj_index)) {
-                    // TODO show log
-                    writer << (uint8_t) 0xFF;
-                } else {
-                    auto &obj_info = global_obj_pool[obj_index];
-                    auto obj_type_index = obj_info.obj_type_index;
-                    assert(is_valid_id(obj_type_index));
-                    writer << obj_type_index;
-                    auto length_ptr = (uint8_t *) writer.cur_data();
-                    writer.manual_offset(1); // skip length
-                    auto writer_func = global_obj_writer_func_pool[obj_type_index];
-                    writer_func(writer, obj_info.placeholder);
-                    auto obj_length = writer.cur_data() - (char *) length_ptr - 1;
-                    *length_ptr = obj_length;
-                }
-            }
-        }
-
-        void handle_query_obj_index(command_reader_type &reader,
-                                    reply_writer_type &writer) const {
-            auto name_length = reader.read_value<uint16_t>();
-            auto obj_name = reader.read_string(name_length);
-            if (!obj_pool.contains(obj_name)) {
-                writer << (obj_index_type) -1;
-            } else {
-                writer << obj_pool.to_index_by_name(obj_name);
-            }
-        }
-
-        void handle_query_global_obj_index(command_reader_type &reader,
-                                           reply_writer_type &writer) const {
-            auto name_length = reader.read_value<uint16_t>();
-            auto global_obj_name = reader.read_string(name_length);
-            if (!global_obj_pool.contains(global_obj_name)) {
-                writer << (global_obj_index_type) -1;
-            } else {
-                writer << global_obj_pool.to_index_by_name(global_obj_name);
-            }
-        }
-
-        awaitable<void> handle_init_obj(command_reader_type &reader,
-                                        reply_writer_type &writer) {
-            auto obj_index = reader.read_value<obj_index_type>();
-            if (obj_pool.contains(obj_index)) {
-                auto tri_obj_ptr = get_tristate_ptr(obj_index);
-                if (tri_obj_ptr != nullptr) {
-                    auto &obj_info = obj_pool[obj_index];
-                    co_await tri_obj_ptr->init(obj_info.init_config);
-                }
-            }
-            writer << (uint8_t) 0xFF;
-            co_return;
-        }
+//        void handle_query_global_obj_value(command_reader_type &reader,
+//                                           reply_writer_type &writer) const {
+//            auto query_number = reader.read_value<uint16_t>();
+//            while (query_number--) {
+//                auto obj_index = reader.read_value<global_obj_index_type>();
+//                if (!global_obj_pool.contains(obj_index)) {
+//                    // TODO show log
+//                    writer << (uint8_t) 0xFF;
+//                } else {
+//                    auto &obj_info = global_obj_pool[obj_index];
+//                    auto obj_type_index = obj_info.obj_type_index;
+//                    assert(is_valid_id(obj_type_index));
+//                    writer << obj_type_index;
+//                    auto length_ptr = (uint8_t *) writer.cur_data();
+//                    writer.manual_offset(1); // skip length
+//                    auto writer_func = global_obj_writer_func_pool[obj_type_index];
+//                    writer_func(writer, obj_info.placeholder);
+//                    auto obj_length = writer.cur_data() - (char *) length_ptr - 1;
+//                    *length_ptr = obj_length;
+//                }
+//            }
+//        }
+
+//        void handle_query_obj_index(command_reader_type &reader,
+//                                    reply_writer_type &writer) const {
+//            auto name_length = reader.read_value<uint16_t>();
+//            auto obj_name = reader.read_string(name_length);
+//            if (!obj_pool.contains(obj_name)) {
+//                writer << (obj_index_type) -1;
+//            } else {
+//                writer << obj_pool.to_index_by_name(obj_name);
+//            }
+//        }
+
+//        void handle_query_global_obj_index(command_reader_type &reader,
+//                                           reply_writer_type &writer) const {
+//            auto name_length = reader.read_value<uint16_t>();
+//            auto global_obj_name = reader.read_string(name_length);
+//            if (!global_obj_pool.contains(global_obj_name)) {
+//                writer << (global_obj_index_type) -1;
+//            } else {
+//                writer << global_obj_pool.to_index_by_name(global_obj_name);
+//            }
+//        }
 
         awaitable<void> coro_start_obj(obj_index_type obj_index) {
             assert(obj_pool.contains(obj_index));
-            auto callback_signal = std::make_unique<coro_signal2>(global_context);
-            auto callback_watcher = callback_signal->new_watcher(global_context);
-            auto event = manager_event::new_instance(manager_event_type::START,
-                                                     obj_index,
-                                                     std::move(callback_signal));
-            co_await manager_event_channel.async_send(error_code{}, std::move(event), use_awaitable);
+            auto callback_signal = std::make_unique<coro_signal2>();
+            auto callback_watcher = callback_signal->new_watcher();
+            auto event = manager_event::new_instance();
+            event->event = manager_event_type::START;
+            event->obj_index = obj_index;
+            event->callback = std::move(callback_signal);
+            manager_event_channel.async_send(error_code{}, std::move(event), detached);
             co_await callback_watcher.coro_wait(false);
             co_return;
         }
 
-        awaitable<void> coro_stop_obj(obj_index_type obj_index) {
+//        awaitable<void> coro_stop_obj(obj_index_type obj_index) {
+//            assert(obj_pool.contains(obj_index));
+//            auto callback_signal = std::make_unique<coro_signal2>();
+//            auto callback_watcher = callback_signal->new_watcher();
+//            auto event = manager_event::new_instance();
+//            event->event = manager_event_type::STOP;
+//            event->obj_index = obj_index;
+//            event->callback = std::move(callback_signal);
+//            manager_event_channel.async_send(error_code{}, std::move(event), detached);
+//            co_await callback_watcher.coro_wait(false);
+//            co_return;
+//        }
+
+        tristate_obj::state_type query_object_state(obj_index_type obj_index) {
             assert(obj_pool.contains(obj_index));
-            auto callback_signal = std::make_unique<coro_signal2>(global_context);
-            auto callback_watcher = callback_signal->new_watcher(global_context);
-            auto event = manager_event::new_instance(manager_event_type::STOP,
-                                                     obj_index,
-                                                     std::move(callback_signal));
-            co_await manager_event_channel.async_send(error_code{}, std::move(event), use_awaitable);
-            co_await callback_watcher.coro_wait(false);
-            co_return;
-        }
-
-        awaitable<void> handle_start_obj(command_reader_type &reader,
-                                         reply_writer_type &writer) {
-            auto obj_index = reader.read_value<obj_index_type>();
-            if (obj_pool.contains(obj_index)) {
-                co_await coro_start_obj(obj_index);
-            } else {
-                // TODO show log
-            }
-            writer << (uint8_t) 0xFF;
-            co_return;
-        }
-
-        awaitable<void> handle_stop_obj(command_reader_type &reader,
-                                        reply_writer_type &writer) {
-            auto obj_index = reader.read_value<obj_index_type>();
-            if (obj_pool.contains(obj_index)) {
-                co_await coro_stop_obj(obj_index);
-            } else {
-                // TODO show log
-            }
-            writer << (uint8_t) 0xFF;
-            co_return;
-        }
-
-        awaitable<void> handle_reset_obj(command_reader_type &reader,
-                                         reply_writer_type &writer) {
-            auto obj_index = reader.read_value<obj_index_type>();
-            if (obj_pool.contains(obj_index)) {
-                auto tri_obj_ptr = get_tristate_ptr(obj_index);
-                if (tri_obj_ptr != nullptr) {
-                    // check if stop is needed
-                    if (tri_obj_ptr->get_state() > tristate_obj::state_type::PENDING) {
-                        co_await coro_stop_obj(obj_index); // TODO 这里可能会有重复,应该直接把对应的对象停止也可以
-                    }
-                    auto &obj_info = obj_pool[obj_index];
-                    co_await tri_obj_ptr->init(obj_info.init_config);
-                }
-            }
-            writer << (uint8_t) 0xFF;
-            co_return;
-        }
-
-        awaitable<void> handle_command(tcp::socket &client_socket,
-                                       uint8_t cmd_code,
-                                       dynamic_memory &cmd_content,
-                                       dynamic_memory &reply_memory) {
-            auto command_reader = command_reader_type(extern_memory(cmd_content));
-            auto reply_content_memory = extern_memory(reply_memory.data() + 2,  // save space for length
-                                                      reply_memory.max_length() - 2);
-            auto reply_writer = reply_writer_type(std::move(reply_content_memory));
-            switch (cmd_code) {
-                case 0x00: {
-                    handle_query_manager_status(reply_writer);
-                    break;
-                }
-                case 0x01: {
-                    handle_query_obj_status(command_reader, reply_writer);
-                    break;
-                }
-                case 0x02: {
-                    handle_query_global_obj_value(command_reader, reply_writer);
-                    break;
-                }
-                case 0x0A: {
-                    handle_query_obj_index(command_reader, reply_writer);
-                    break;
-                }
-                case 0x0B: {
-                    handle_query_global_obj_index(command_reader, reply_writer);
-                    break;
-                }
-                case 0x10: {
-                    co_await handle_init_obj(command_reader, reply_writer);
-                    break;
-                }
-                case 0x11: {
-                    co_await handle_start_obj(command_reader, reply_writer);
-                    break;
-                }
-                case 0x12: {
-                    co_await handle_stop_obj(command_reader, reply_writer);
-                    break;
-                }
-                case 0x13: {
-                    co_await handle_reset_obj(command_reader, reply_writer);
-                    break;
-                }
-                default: // TODO 增加退出的功能
-                    // TODO show
-                    break;
-            }
-
-            // send back reply
-            auto reply_length = reply_writer.get_cur_pos();
-            assert(reply_length <= reply_max_length - 2);
-            auto &rep_length_tmp = *reinterpret_cast<uint16_t *>(reply_memory.data() + 0);
-            rep_length_tmp = reply_length;
-            swap_net_loc_endian<boost::endian::order::big>(rep_length_tmp);
-            auto reply_buffer = buffer(reply_memory.data(), reply_length + 2);
-            co_await async_write(client_socket, reply_buffer, use_awaitable);
-            co_return;
-        }
-
-        void create_client_worker(tcp::socket &&client_socket) {
-            auto remote_endpoint = client_socket.remote_endpoint();
-            auto worker_func = [
-                    this, // TODO 把这几个 memory 放进一个 struct 里面
-                    client_socket = std::move(client_socket),
-                    command_memory = dynamic_memory(command_initial_length),
-                    reply_memory = dynamic_memory(reply_max_length)]() mutable
-                    -> awaitable<bool> {
-                auto cmd_code = co_await receive_command(client_socket, command_memory);
-                co_await handle_command(client_socket, cmd_code, command_memory, reply_memory);
-                co_return true;
-            };
-            auto noexcept_worker_func = make_noexcept_func(std::move(worker_func), [](std::exception &e) {
-                SPDLOG_WARN("Client left."); // TODO better log
-            });
-            auto worker_iter_ptr = new client_worker_list_type::iterator;
-            auto exit_func = [=]() {
-                assert(worker_iter_ptr != nullptr);
-                client_worker_list.erase(*worker_iter_ptr);
-//                delete worker_iter_ptr; // TODO 这句话会报错,怀疑其他地方有内存访问越界
-            };
-            auto worker = make_infinite_coro_worker(global_context,
-                                                    std::move(noexcept_worker_func),
-                                                    std::move(exit_func));
-            worker->run();
-            SPDLOG_INFO("Working with client {}:{}",
-                        remote_endpoint.address().to_string(),
-                        remote_endpoint.port());
-            client_worker_list.push_front(std::move(worker));
-            *worker_iter_ptr = client_worker_list.begin();
-        }
-
-        bool start_manager() {
-            assert(listen_worker == nullptr);
-            auto listen_endpoint = tcp::endpoint(tcp::v4(), listen_port);
-            try {
-                auto listen_func = [
-                        this,
-                        acceptor = tcp::acceptor(global_context, listen_endpoint)
-                ]() mutable -> awaitable<bool> {
-                    error_code ec;
-                    auto client = co_await acceptor.async_accept(redirect_error(use_awaitable, ec));
-                    if (ec) {
-                        // TODO show log
-                    } else {
-                        create_client_worker(std::move(client));
-                    }
-                    co_return true;
-                };
-                listen_worker = make_infinite_coro_worker(global_context, std::move(listen_func));
-                listen_worker->run();
-                SPDLOG_INFO("Sophiar manager is listen on {}:{}",
-                            listen_endpoint.address().to_string(),
-                            listen_endpoint.port());
-            } catch (std::exception &e) {
-                // TODO show log, failed to create coro_worker
-                return false;
-            }
-
-            // start node event handler
-            start_manager_worker();
-
-            return true;
+            auto ptr = get_tristate_ptr(obj_index);
+            if (ptr == nullptr) return tristate_obj::state_type::UNKNOWN;
+            return ptr->get_state();
         }
 
     };
@@ -625,7 +345,7 @@ namespace sophiar {
         pimpl->q_this = this;
     }
 
-    void sophiar_manager::register_object_type_impl(const std::string &type_name,
+    void sophiar_manager::register_object_type_impl(std::string_view type_name,
                                                     obj_factory_func_type func) {
         auto index = pimpl->obj_factory_func_pool.new_elem(type_name);
         pimpl->obj_factory_func_pool[index] = func;
@@ -635,11 +355,7 @@ namespace sophiar {
         assert(pimpl->manager_status == impl::manager_status_type::INITIAL);
         pimpl->build_graph(config);
         pimpl->manager_status = impl::manager_status_type::NORMAL;
-        bool ok = pimpl->start_manager();
-        if (!ok) {
-            // TODO show fatal error
-            return false;
-        }
+        pimpl->start_manager_worker();
         return true;
     }
 
@@ -658,63 +374,75 @@ namespace sophiar {
         pimpl->on_object_stopped(obj);
     }
 
-    global_obj_index_type sophiar_manager::register_global_obj_impl(const std::string &obj_name,
-                                                                    std::type_index obj_type,
-                                                                    void *placeholder) {
-        if (placeholder == nullptr) {
-            if (!pimpl->global_obj_pool.contains(obj_name))
-                return ~(global_obj_index_type) 0; // indicate the caller to create a placeholder
-            assert(pimpl->global_obj_pool[obj_name].obj_type == obj_type);
-            return pimpl->global_obj_pool.to_index_by_name(obj_name);
-        }
+#ifdef SOPHIAR_TEST
 
-        // create new one
-        auto obj_index = pimpl->global_obj_pool.new_elem(obj_name);
-        auto &obj_info = pimpl->global_obj_pool[obj_index];
-        obj_info.obj_type = obj_type;
-        obj_info.placeholder = placeholder;
-        obj_info.update_signal = new coro_signal2{global_context};
-        if (pimpl->global_obj_type_to_index_pool.contains(obj_type)) {
-            obj_info.obj_type_index = pimpl->global_obj_type_to_index_pool[obj_type];
-        } else {
-            // TODO show log, not registered type
-        }
-        return obj_index;
+    sophiar_obj *sophiar_manager::get_object(std::string_view obj_name) const {
+        if (!pimpl->obj_pool.contains(obj_name)) return nullptr;
+        return pimpl->obj_pool[obj_name].ptr;
     }
 
-    void *sophiar_manager::get_global_obj_placeholder(global_obj_index_type obj_index,
-                                                      std::type_index obj_type) {
-        auto &obj_info = pimpl->global_obj_pool[obj_index];
-        assert(obj_info.obj_type == obj_type);
-        return obj_info.placeholder;
-    }
+#endif // SOPHIAR_TEST
 
-    timestamp_type sophiar_manager::get_global_obj_update_timestamp(
-            sophiar::global_obj_index_type obj_index) {
-        auto &obj_info = pimpl->global_obj_pool[obj_index];
-        return obj_info.last_update_ts;
+    boost::asio::awaitable<bool> sophiar_manager::init_object(std::string_view obj_name) noexcept {
+        if (!pimpl->check_object_name_for_controller(obj_name)) co_return false;
+        auto obj_index = pimpl->obj_pool.to_index_by_name(obj_name);
+        auto tri_obj_ptr = pimpl->get_tristate_ptr(obj_index);
+        if (tri_obj_ptr != nullptr) {
+            const auto &config = pimpl->obj_pool[obj_index].init_config;
+            co_await tri_obj_ptr->init(config);
+            co_return tri_obj_ptr->is_stable()
+                      && tri_obj_ptr->get_state() >= tristate_obj::state_type::PENDING;
+        } else {
+            co_return true;
+        }
+        assert(false);
+        co_return false;
     }
 
-    void sophiar_manager::update_global_obj_timestamp(global_obj_index_type obj_index,
-                                                      timestamp_type ts) {
-        auto &obj_info = pimpl->global_obj_pool[obj_index];
-        obj_info.last_update_ts = ts;
-        obj_info.update_signal->try_notify_all();
+    boost::asio::awaitable<bool> sophiar_manager::start_object(std::string_view obj_name) noexcept {
+        if (!pimpl->check_object_name_for_controller(obj_name)) co_return false;
+        auto obj_index = pimpl->obj_pool.to_index_by_name(obj_name);
+        co_await pimpl->coro_start_obj(obj_index);
+        auto obj_state = pimpl->query_object_state(obj_index);
+        co_return obj_state == tristate_obj::state_type::UNKNOWN
+                  || tristate_obj::is_state_stable(obj_state)
+                     && obj_state >= tristate_obj::state_type::RUNNING;
     }
 
-    signal_watcher sophiar_manager::request_global_obj_update_watcher(global_obj_index_type obj_index) {
-        return pimpl->global_obj_pool[obj_index]
-                .update_signal->new_watcher(global_context);
+    boost::asio::awaitable<bool> sophiar_manager::stop_object(std::string_view obj_name) noexcept {
+        if (!pimpl->check_object_name_for_controller(obj_name)) co_return false;
+        auto obj_index = pimpl->obj_pool.to_index_by_name(obj_name);
+        auto tri_obj_ptr = pimpl->get_tristate_ptr(obj_index);
+        if (tri_obj_ptr != nullptr) {
+            co_await tri_obj_ptr->stop();
+            co_return tri_obj_ptr->is_stable()
+                      && tri_obj_ptr->get_state() <= tristate_obj::state_type::PENDING;
+        } else {
+            co_return true;
+        }
+        assert(false);
+        co_return false;
     }
 
-#ifdef SOPHIAR_TEST
-
-    sophiar_obj *sophiar_manager::get_object(const std::string &obj_name) const {
-        if (!pimpl->obj_pool.contains(obj_name)) return nullptr;
-        return pimpl->obj_pool[obj_name].ptr;
+    boost::asio::awaitable<bool> sophiar_manager::reset_object(std::string_view obj_name) noexcept {
+        if (!pimpl->check_object_name_for_controller(obj_name)) co_return false;
+        auto obj_index = pimpl->obj_pool.to_index_by_name(obj_name);
+        auto tri_obj_ptr = pimpl->get_tristate_ptr(obj_index);
+        if (tri_obj_ptr != nullptr) {
+            co_await tri_obj_ptr->reset();
+            co_return tri_obj_ptr->get_state() == tristate_obj::state_type::INITIAL;
+        } else {
+            co_return true;
+        }
+        assert(false);
+        co_return false;
     }
 
-#endif // SOPHIAR_TEST
+    uint8_t sophiar_manager::query_object_state(std::string_view obj_name) {
+        if (!pimpl->check_object_name_for_controller(obj_name)) return -1; // 0xFF means unknown
+        auto obj_index = pimpl->obj_pool.to_index_by_name(obj_name);
+        return (uint8_t) pimpl->query_object_state(obj_index);
+    }
 
     sophiar_manager::~sophiar_manager() = default;
 

+ 18 - 72
src/core/sophiar_manager.h

@@ -15,16 +15,13 @@
 #include <coroutine>
 #include <exception>
 #include <memory>
-#include <string>
+#include <string_view>
 #include <type_traits>
 #include <typeindex>
 #include <typeinfo>
 
 namespace sophiar {
 
-    using global_obj_index_type = uint16_t;
-    static constexpr auto invalid_global_obj_index = ~(global_obj_index_type) 0;
-
     class sophiar_obj;
 
     class sophiar_manager {
@@ -39,45 +36,11 @@ namespace sophiar {
         void notify_object_stop(sophiar_obj *obj);
 
         template<typename Derived>
-        void register_object_type(const std::string &type_name) {
+        void register_object_type(std::string_view type_name) {
             static_assert(std::is_convertible_v<decltype(Derived::new_instance()), sophiar_obj *>);
             register_object_type_impl(type_name, &Derived::new_instance);
         }
 
-        template<typename SmallObjType>
-        global_obj_index_type register_global_obj(const std::string &obj_name) {
-            auto ret = register_global_obj_impl(obj_name, typeid(SmallObjType), nullptr);
-            if (static_cast<decltype(ret)>(~ret) == 0) { // first time of register
-                using ptr_type = typename SmallObjType::pointer;
-                auto placeholder = new ptr_type;
-                ret = register_global_obj_impl(obj_name, typeid(SmallObjType), placeholder);
-            }
-            return ret;
-        }
-
-        template<typename SmallObjType>
-        void update_global_obj(global_obj_index_type obj_index,
-                               const typename SmallObjType::pointer &value,
-                               timestamp_type ts = current_timestamp()) {
-            using ptr_type = typename SmallObjType::pointer;
-            auto placeholder = get_global_obj_placeholder(obj_index, typeid(SmallObjType));
-            auto &inner_ptr = *static_cast<ptr_type *>(placeholder);
-            if (inner_ptr == nullptr && value == nullptr) return; // nullptr value will not be duplicated
-            inner_ptr = value;
-            update_global_obj_timestamp(obj_index, ts);
-        }
-
-        template<typename SmallObjType>
-        typename SmallObjType::pointer get_global_obj(global_obj_index_type obj_index) {
-            using ptr_type = typename SmallObjType::pointer;
-            auto placeholder = get_global_obj_placeholder(obj_index, typeid(SmallObjType));
-            return *static_cast<ptr_type *>(placeholder);
-        }
-
-        timestamp_type get_global_obj_update_timestamp(global_obj_index_type obj_index);
-
-        signal_watcher request_global_obj_update_watcher(global_obj_index_type obj_index);
-
         sophiar_manager();
 
         ~sophiar_manager();
@@ -86,9 +49,7 @@ namespace sophiar {
 
     public:
 
-        sophiar_obj *get_object(const std::string &obj_name) const;
-
-    private:
+        sophiar_obj *get_object(std::string_view obj_name) const;
 
 #endif // SOPHIAR_TEST
 
@@ -97,49 +58,34 @@ namespace sophiar {
         struct impl;
         std::unique_ptr<impl> pimpl;
 
-        void register_object_type_impl(const std::string &type_name, obj_factory_func_type func);
-
-        global_obj_index_type register_global_obj_impl(const std::string &obj_name,
-                                                       std::type_index obj_type,
-                                                       void *placeholder); // small_obj<Derive>::pointer
+        friend class external_controller;
 
-        void *get_global_obj_placeholder(global_obj_index_type obj_index,
-                                         std::type_index obj_type);
+        void register_object_type_impl(std::string_view type_name, obj_factory_func_type func);
 
-        void update_global_obj_timestamp(global_obj_index_type obj_index,
-                                         timestamp_type ts);
+#ifdef SOPHIAR_TEST
 
-    };
+    public:
 
-    extern boost::asio::io_context global_context;
-    extern sophiar_manager global_sophiar_manager;
+#else
 
-#define REGISTER_TYPE(DerivedT) \
-    global_sophiar_manager.register_object_type<DerivedT>(#DerivedT)
+    private:
 
-#define REGISTER_GLOBAL_OBJ(obj_type, obj_name) \
-    global_sophiar_manager.register_global_obj<obj_type>(obj_name)
+#endif // SOPHIAR_TEST
 
-#define GET_GLOBAL_OBJ(obj_type, obj_index) \
-    global_sophiar_manager.get_global_obj<obj_type>(obj_index)
+        boost::asio::awaitable<bool> init_object(std::string_view obj_name) noexcept;
 
-#define NEW_GLOBAL_OBJ_WATCHER(obj_index) \
-    global_sophiar_manager.request_global_obj_update_watcher(obj_index)
+        boost::asio::awaitable<bool> start_object(std::string_view obj_name) noexcept;
 
-#define GLOBAL_OBJ_UPDATE_TS(obj_index) \
-    global_sophiar_manager.get_global_obj_update_timestamp(obj_index)
+        boost::asio::awaitable<bool> stop_object(std::string_view obj_name) noexcept;
 
-#define UPDATE_GLOBAL_OBJ(obj_type, obj_index, obj) \
-    global_sophiar_manager.update_global_obj<obj_type>(obj_index, obj)
+        boost::asio::awaitable<bool> reset_object(std::string_view obj_name) noexcept;
 
-#define UPDATE_GLOBAL_OBJ_TS(obj_type, obj_index, obj, ts) \
-    global_sophiar_manager.update_global_obj<obj_type>(obj_index, obj, ts)
+        uint8_t query_object_state(std::string_view obj_name);
 
-#define UPDATE_GLOBAL_OBJ_VALUE(obj_type, obj_index, obj_value) \
-    global_sophiar_manager.update_global_obj<obj_type>(obj_index, obj_type::new_instance(obj_value))
+    };
 
-#define UPDATE_GLOBAL_OBJ_VALUE_TS(obj_type, obj_index, obj_value, ts) \
-    global_sophiar_manager.update_global_obj<obj_type>(obj_index, obj_type::new_instance(obj_value), ts)
+#define REGISTER_TYPE(DerivedT) \
+    global_sophiar_manager->register_object_type<DerivedT>(#DerivedT)
 
 }
 

+ 1 - 11
src/core/sophiar_obj.hpp

@@ -2,6 +2,7 @@
 #define SOPHIAR2_SOPHIAR_OBJ_HPP
 
 #include "core/sophiar_manager.h"
+#include "core/sophiar_pool.h"
 
 #include <nlohmann/json.hpp>
 
@@ -12,17 +13,6 @@ namespace sophiar {
 
         virtual ~sophiar_obj() = default;
 
-        // 加载初始化配置
-        [[deprecated]] virtual void load_construct_config(const nlohmann::json &config) {};
-
-        static constexpr auto &get_context() {
-            return global_context;
-        }
-
-        static constexpr auto &get_manager() {
-            return global_sophiar_manager;
-        }
-
     };
 
 #define DEFAULT_NEW_INSTANCE(DerivedT) \

+ 240 - 0
src/core/sophiar_pool.cpp

@@ -0,0 +1,240 @@
+#include "sophiar_pool.h"
+#include "core/basic_obj_types.hpp"
+#include "utility/config_utility.hpp"
+#include "utility/named_vector.hpp"
+#include "utility/string_map.hpp"
+#include "utility/versatile_buffer2.hpp"
+
+#include <cassert>
+#include <vector>
+#include <unordered_map>
+
+namespace sophiar {
+
+    struct sophiar_pool::impl {
+
+        struct variable_type_info {
+            using creator_func_type = void *(*)();
+
+            size_t binary_length;
+            std::type_index type = typeid(void);
+            std::string type_name;
+
+            // function list
+            creator_func_type creator_func;
+            std::unordered_map<std::type_index, void *> writer_func_pool;
+            std::unordered_map<std::type_index, void *> reader_func_pool;
+        };
+
+        struct variable_info_impl {
+            void *placeholder;
+            coro_signal2 *update_signal;
+            const variable_type_info *type_info;
+            timestamp_type last_update_ts;
+        };
+
+        using variable_type_index_type = uint16_t;
+        string_map<variable_type_index_type> variable_type_name_index_map; // typename -> index
+        std::unordered_map<std::type_index, variable_type_index_type> variable_type_index_index_map; // type_index -> index
+        std::vector<variable_type_info> variable_type_info_pool;
+
+        named_vector<variable_index_type, variable_info_impl> variable_pool;
+
+        template<typename SmallObjType>
+        void register_variable_type(std::string_view type_name) {
+            static_assert(SmallObjType::binary_length() <= std::numeric_limits<uint8_t>::max());
+
+            auto var_type_index = variable_type_info_pool.size();
+            variable_type_info_pool.emplace_back();
+
+            assert(!variable_type_name_index_map.contains(type_name));
+            variable_type_name_index_map.insert(type_name, var_type_index);
+
+            const std::type_index type = typeid(SmallObjType);
+            assert(!variable_type_index_index_map.contains(type));
+            variable_type_index_index_map[type] = var_type_index;
+
+            auto &type_info = variable_type_info_pool[var_type_index];
+            type_info.binary_length = SmallObjType::binary_length();
+            type_info.type = type;
+            type_info.type_name = type_name;
+            type_info.creator_func = []() { return (void *) new SmallObjType::pointer; };
+
+            // create IO functions
+#define REGISTER_WRITER_FUNC(writer_type) { \
+            const std::type_index writer_type_index = typeid(writer_type); \
+            type_info.writer_func_pool[writer_type_index] =                \
+                (void *) &SmallObjType::template raw_pointer_write_to<writer_type>; }
+
+            using writer_big_extern = versatile_writer<boost::endian::order::big>;
+            using writer_little_extern = versatile_writer<boost::endian::order::little>;
+
+            REGISTER_WRITER_FUNC(writer_big_extern)
+            REGISTER_WRITER_FUNC(writer_little_extern)
+#undef REGISTER_WRITER_FUNC
+
+#define REGISTER_READER_FUNC(reader_type) { \
+            const std::type_index reader_type_index = typeid(reader_type); \
+            type_info.reader_func_pool[reader_type_index] =                \
+                (void *) &SmallObjType::template raw_pointer_fill_from<reader_type>; }
+
+            using reader_big_extern = versatile_reader<boost::endian::order::big>;
+            using reader_little_extern = versatile_reader<boost::endian::order::little>;
+
+            REGISTER_READER_FUNC(reader_big_extern)
+            REGISTER_READER_FUNC(reader_little_extern)
+#undef REGISTER_READER_FUNC
+        }
+
+        void register_basic_variable_types() {
+#define REGISTER_TYPE(var_type) \
+            register_variable_type<var_type>(#var_type)
+
+            REGISTER_TYPE(bool_obj);
+//            REGISTER_TYPE(u8int_obj);
+//            REGISTER_TYPE(u16int_obj);
+//            REGISTER_TYPE(u32int_obj);
+            REGISTER_TYPE(u64int_obj);
+//            REGISTER_TYPE(i8int_obj);
+//            REGISTER_TYPE(i16int_obj);
+//            REGISTER_TYPE(i32int_obj);
+//            REGISTER_TYPE(i64int_obj);
+//            REGISTER_TYPE(float_obj);
+            REGISTER_TYPE(double_obj);
+            REGISTER_TYPE(scalarxyz_obj);
+            REGISTER_TYPE(transform_obj);
+            REGISTER_TYPE(array6_obj);
+//            REGISTER_TYPE(array7_obj);
+#undef REGISTER_TYPE
+        }
+
+        void register_variable(std::string_view name,
+                               std::string_view type_name) {
+            auto index = variable_pool.new_elem(name);
+            auto &info = variable_pool[index];
+
+            assert(variable_type_name_index_map.contains(type_name));
+            auto var_type_index = variable_type_name_index_map.query(type_name);
+            const auto &type_info = variable_type_info_pool[var_type_index];
+
+            info.placeholder = type_info.creator_func();
+            info.update_signal = new coro_signal2{};
+            info.type_info = &type_info;
+            info.last_update_ts = 0;
+        }
+
+        void load_config(const nlohmann::json &config) {
+
+#ifdef SOPHIAR_TEST
+
+            if (config.empty()) return;
+
+#endif // SOPHIAR_TEST
+
+            ENSURE_ARRAY("variable_list");
+            for (auto &var_config: config["variable_list"]) {
+                auto var_name = LOAD_STRING_ITEM2(var_config, "name");
+                auto type_name = LOAD_STRING_ITEM2(var_config, "type");
+                register_variable(var_name, type_name);
+            }
+        }
+
+        impl() {
+            register_basic_variable_types();
+        }
+
+    };
+
+    sophiar_pool::sophiar_pool()
+            : pimpl(std::make_unique<impl>()) {}
+
+    signal_watcher sophiar_pool::require_variable_watcher(variable_index_type var_index) {
+        assert(pimpl->variable_pool.contains(var_index));
+        return pimpl->variable_pool[var_index].update_signal->new_watcher();
+    }
+
+    std::string sophiar_pool::query_variable_name(variable_index_type var_index) {
+        assert(pimpl->variable_pool.contains(var_index));
+        return pimpl->variable_pool.to_name_by_index(var_index);
+    }
+
+    sophiar_pool::variable_info sophiar_pool::query_variable_information(std::string_view var_name) {
+        assert(pimpl->variable_pool.contains(var_name));
+        const auto &info = pimpl->variable_pool[var_name];
+        variable_info ret;
+        ret.type = info.type_info->type;
+        ret.last_update_ts = info.last_update_ts;
+        ret.binary_length = info.type_info->binary_length;
+        ret.type_name = info.type_info->type_name;
+        return ret;
+    }
+
+    variable_index_type sophiar_pool::require_variable_impl(std::string_view var_name,
+                                                            std::type_index var_type) {
+        assert(pimpl->variable_pool.contains(var_name));
+        const auto &info = pimpl->variable_pool[var_name];
+        if (info.type_info->type != var_type) { // ensure type consistency
+            assert(false);
+            return invalid_variable_index;
+        }
+        return pimpl->variable_pool.to_index_by_name(var_name);
+    }
+
+    void *sophiar_pool::require_variable_placeholder_impl(variable_index_type var_index,
+                                                          std::type_index var_type,
+                                                          timestamp_type *ts_out) {
+        assert(pimpl->variable_pool.contains(var_index));
+        const auto &info = pimpl->variable_pool[var_index];
+        if (info.type_info->type != var_type) { // ensure type consistency
+            assert(false);
+            return nullptr;
+        }
+        if (ts_out != nullptr) {
+            *ts_out = info.last_update_ts;
+        }
+        return info.placeholder;
+    }
+
+    void sophiar_pool::update_variable_timestamp_impl(variable_index_type var_index,
+                                                      timestamp_type ts) {
+        assert(pimpl->variable_pool.contains(var_index));
+        auto &info = pimpl->variable_pool[var_index];
+        assert(ts > info.last_update_ts);
+        info.last_update_ts = ts;
+        info.update_signal->try_notify_all(ts);
+    }
+
+    void *sophiar_pool::require_raw_pointer_writer_impl(std::type_index writer_type,
+                                                        std::type_index var_type) {
+        assert(pimpl->variable_type_index_index_map.contains(var_type));
+        auto var_type_index = pimpl->variable_type_index_index_map[var_type];
+        auto &info = pimpl->variable_type_info_pool[var_type_index];
+        assert(info.writer_func_pool.contains(writer_type));
+        return info.writer_func_pool[writer_type];
+    }
+
+    void *sophiar_pool::require_raw_pointer_reader_impl(std::type_index reader_type,
+                                                        std::type_index var_type) {
+        assert(pimpl->variable_type_index_index_map.contains(var_type));
+        auto var_type_index = pimpl->variable_type_index_index_map[var_type];
+        auto &info = pimpl->variable_type_info_pool[var_type_index];
+        assert(info.reader_func_pool.contains(reader_type));
+        return info.reader_func_pool[reader_type];
+    }
+
+    void sophiar_pool::load_config(const nlohmann::json &config) {
+        pimpl->load_config(config);
+    }
+
+    sophiar_pool::~sophiar_pool() = default;
+
+#ifdef SOPHIAR_TEST
+
+    void sophiar_pool::register_variable(std::string_view var_name,
+                                         std::string_view type_name) {
+        pimpl->register_variable(var_name, type_name);
+    }
+
+#endif
+
+}

+ 124 - 0
src/core/sophiar_pool.h

@@ -0,0 +1,124 @@
+#ifndef SOPHIAR2_SOPHIAR_POOL_H
+#define SOPHIAR2_SOPHIAR_POOL_H
+
+#include "core/timestamp_helper.hpp"
+#include "utility/coro_signal2.hpp"
+#include "utility/versatile_buffer2.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <cstdint>
+#include <string_view>
+#include <typeindex>
+#include <typeinfo>
+
+namespace sophiar {
+
+    using variable_index_type = uint16_t;
+    static constexpr auto invalid_variable_index = ~(variable_index_type) 0;
+
+    class sophiar_pool {
+    public:
+
+        sophiar_pool();
+
+        ~sophiar_pool();
+
+        void load_config(const nlohmann::json &config);
+
+        template<typename SmallObjType>
+        variable_index_type require_variable(std::string_view var_name) {
+            return require_variable_impl(var_name, typeid(SmallObjType));
+        }
+
+        template<typename SmallObjType>
+        void update_variable(variable_index_type var_index,
+                             const typename SmallObjType::pointer &value,
+                             timestamp_type ts = current_timestamp()) {
+            using ptr_type = typename SmallObjType::pointer;
+            auto placeholder = require_variable_placeholder_impl(var_index, typeid(SmallObjType), nullptr);
+            auto &inner_ptr = *static_cast<ptr_type *>(placeholder);
+            if (inner_ptr == nullptr && value == nullptr) return; // nullptr value will not be duplicated
+            inner_ptr = value;
+            update_variable_timestamp_impl(var_index, ts);
+        }
+
+        template<typename SmallObjType>
+        typename SmallObjType::pointer query_variable(variable_index_type var_index,
+                                                      timestamp_type *ts_out = nullptr) {
+            using ptr_type = typename SmallObjType::pointer;
+            auto placeholder = require_variable_placeholder_impl(var_index, typeid(SmallObjType), ts_out);
+            return *static_cast<ptr_type *>(placeholder);
+        }
+
+        signal_watcher require_variable_watcher(variable_index_type var_index);
+
+        struct variable_info {
+            std::type_index type = typeid(void);
+            timestamp_type last_update_ts;
+            size_t binary_length;
+            std::string type_name;
+        };
+
+        variable_info query_variable_information(std::string_view var_name);
+
+        std::string query_variable_name(variable_index_type var_index);
+
+#ifdef SOPHIAR_TEST
+
+    public:
+
+        void register_variable(std::string_view var_name,
+                               std::string_view type_name);
+
+#endif // SOPHIAR_TEST
+
+    private:
+
+        struct impl;
+        std::unique_ptr<impl> pimpl;
+
+        variable_index_type require_variable_impl(std::string_view var_name,
+                                                  std::type_index var_type);
+
+        void *require_variable_placeholder_impl(variable_index_type var_index,
+                                                std::type_index var_type,
+                                                timestamp_type *ts_out); // return update timestamp
+
+        void update_variable_timestamp_impl(variable_index_type var_index,
+                                            timestamp_type ts);
+
+        // for friend classes
+        template<typename WriterType>
+        auto require_raw_pointer_writer(std::type_index var_type) {
+            using RealWriterType = std::remove_cvref_t<WriterType>;
+            using WriterFuncType = void (*)(RealWriterType &, void *);
+            auto func_ptr = require_raw_pointer_writer_impl(typeid(RealWriterType), var_type);
+            assert(func_ptr != nullptr);
+            return (WriterFuncType) func_ptr;
+        }
+
+        template<typename ReaderType>
+        auto require_raw_pointer_reader(std::type_index var_type) {
+            using RealReaderType = std::remove_cvref_t<ReaderType>;
+            using ReaderFuncType = void (*)(RealReaderType &, void *);
+            auto func_ptr = require_raw_pointer_writer_impl(typeid(RealReaderType), var_type);
+            assert(func_ptr != nullptr);
+            return (ReaderFuncType) func_ptr;
+        }
+
+        void *require_raw_pointer_writer_impl(std::type_index writer_type,
+                                              std::type_index var_type);
+
+        void *require_raw_pointer_reader_impl(std::type_index reader_type,
+                                              std::type_index var_type);
+
+        friend class variable_watcher;
+
+        friend class variable_updater;
+
+    };
+
+}
+
+#endif //SOPHIAR2_SOPHIAR_POOL_H

+ 49 - 49
src/core/tristate_obj.cpp

@@ -1,6 +1,5 @@
 #include "tristate_obj.h"
-
-#include "third_party/static_block.hpp"
+#include "core/global_defs.h"
 #include "utility/coro_signal2.hpp"
 #include "utility/name_translator.hpp"
 
@@ -12,18 +11,7 @@
 
 namespace sophiar {
 
-    static name_translator<tristate_obj::state_type> state_name_translator;
-
-    static_block {
-        state_name_translator.register_item("Initial", tristate_obj::state_type::INITIAL);
-        state_name_translator.register_item("Initializing", tristate_obj::state_type::INITIALIZING);
-        state_name_translator.register_item("Resetting", tristate_obj::state_type::RESETTING);
-        state_name_translator.register_item("Pending", tristate_obj::state_type::PENDING);
-        state_name_translator.register_item("Starting", tristate_obj::state_type::STARTING);
-        state_name_translator.register_item("Stopping", tristate_obj::state_type::STOPPING);
-        state_name_translator.register_item("Running", tristate_obj::state_type::RUNNING);
-
-    };
+    name_translator<uint8_t> *state_name_translator;
 
     using boost::asio::awaitable;
 
@@ -41,31 +29,39 @@ namespace sophiar {
         coro_signal2 stop_finished_signal;
         coro_signal2 reset_finished_signal;
 
-        impl()
-                : init_cancel_signal(get_context()),
-                  start_cancel_signal(get_context()),
-                  init_finished_signal(get_context()),
-                  start_finished_signal(get_context()),
-                  stop_finished_signal(get_context()),
-                  reset_finished_signal(get_context()) {}
+        static void initialize_translator() {
+            static bool is_called = false;
+            if (is_called) return;
+            is_called = true;
+
+            state_name_translator = new name_translator<uint8_t>;
+            state_name_translator->register_item("Initial", (uint8_t) tristate_obj::state_type::INITIAL);
+            state_name_translator->register_item("Initializing", (uint8_t) tristate_obj::state_type::INITIALIZING);
+            state_name_translator->register_item("Resetting", (uint8_t) tristate_obj::state_type::RESETTING);
+            state_name_translator->register_item("Pending", (uint8_t) tristate_obj::state_type::PENDING);
+            state_name_translator->register_item("Starting", (uint8_t) tristate_obj::state_type::STARTING);
+            state_name_translator->register_item("Stopping", (uint8_t) tristate_obj::state_type::STOPPING);
+            state_name_translator->register_item("Running", (uint8_t) tristate_obj::state_type::RUNNING);
+            state_name_translator->register_item("Unknown", (uint8_t) tristate_obj::state_type::UNKNOWN);
+        }
 
         void log_state_change(state_type old_state, state_type new_state) const {
             SPDLOG_TRACE("Object [name = {}], State: {} => {}.",
-                         get_manager().get_object_name(q_this),
-                         state_name_translator.translate(old_state),
-                         state_name_translator.translate(new_state));
+                         QUERY_OBJECT_NAME(q_this),
+                         state_name_translator->translate((uint8_t) old_state),
+                         state_name_translator->translate((uint8_t) new_state));
         }
 
         awaitable<bool> init(const nlohmann::json &config) {
             if (state == state_type::INITIALIZING) {
-                auto init_finished_watcher = init_finished_signal.new_watcher(get_context());
-                auto reset_finished_watcher = reset_finished_signal.new_watcher(get_context());
+                auto init_finished_watcher = init_finished_signal.new_watcher();
+                auto reset_finished_watcher = reset_finished_signal.new_watcher();
                 auto result = co_await (init_finished_watcher.coro_wait() ||
                                         reset_finished_watcher.coro_wait());
                 co_return result.index() == 0;
             }
             if (state == state_type::RESETTING) {
-                auto reset_finished_watcher = reset_finished_signal.new_watcher(get_context());
+                auto reset_finished_watcher = reset_finished_signal.new_watcher();
                 co_await reset_finished_watcher.coro_wait();
                 co_return false;
             }
@@ -73,20 +69,20 @@ namespace sophiar {
             state = state_type::INITIALIZING;
             log_state_change(state_type::INITIAL, state_type::INITIALIZING);
             SPDLOG_TRACE("Initializing object [name = {}] with config {}.",
-                         get_manager().get_object_name(q_this), config.dump());
-            auto init_cancel_watcher = init_cancel_signal.new_watcher(get_context());
+                         QUERY_OBJECT_NAME(q_this), config.dump());
+            auto init_cancel_watcher = init_cancel_signal.new_watcher();
             auto result = co_await (q_this->on_init(config) || init_cancel_watcher.coro_wait());
             if (result.index() == 0 && std::get<0>(result) == true) { // succeeded
                 state = state_type::PENDING;
                 log_state_change(state_type::INITIALIZING, state_type::PENDING);
-                SPDLOG_DEBUG("Initialize object [name = {}] succeeded.", get_manager().get_object_name(q_this));
+                SPDLOG_DEBUG("Initialize object [name = {}] succeeded.", QUERY_OBJECT_NAME(q_this));
                 init_finished_signal.try_notify_all();
                 co_return true;
             } else { // failed
                 co_await q_this->on_reset();
                 state = state_type::INITIAL;
                 log_state_change(state_type::INITIALIZING, state_type::INITIAL);
-                SPDLOG_WARN("Initialize object [name = {}] failed.", get_manager().get_object_name(q_this));
+                SPDLOG_WARN("Initialize object [name = {}] failed.", QUERY_OBJECT_NAME(q_this));
                 reset_finished_signal.try_notify_all();
                 co_return false;
             }
@@ -94,14 +90,14 @@ namespace sophiar {
 
         awaitable<bool> start(const nlohmann::json &config) {
             if (state == state_type::STARTING) {
-                auto start_finished_watcher = start_finished_signal.new_watcher(get_context());
-                auto stop_finished_watcher = stop_finished_signal.new_watcher(get_context());
+                auto start_finished_watcher = start_finished_signal.new_watcher();
+                auto stop_finished_watcher = stop_finished_signal.new_watcher();
                 auto result = co_await (start_finished_watcher.coro_wait() ||
                                         stop_finished_watcher.coro_wait());
                 co_return result.index() == 0;
             }
             if (state == state_type::STOPPING) {
-                auto stop_finished_watcher = stop_finished_signal.new_watcher(get_context());
+                auto stop_finished_watcher = stop_finished_signal.new_watcher();
                 co_await stop_finished_watcher.coro_wait();
                 co_return false;
             }
@@ -110,20 +106,20 @@ namespace sophiar {
             state = state_type::STARTING;
             log_state_change(state_type::PENDING, state_type::STARTING);
             SPDLOG_TRACE("Starting object [name = {}] with config {}.",
-                         get_manager().get_object_name(q_this), config.dump());
-            auto start_cancel_watcher = start_cancel_signal.new_watcher(get_context());
+                         QUERY_OBJECT_NAME(q_this), config.dump());
+            auto start_cancel_watcher = start_cancel_signal.new_watcher();
             auto result = co_await (q_this->on_start(config) || start_cancel_watcher.coro_wait());
             if (result.index() == 0 && std::get<0>(result) == true) { // succeeded
                 state = state_type::RUNNING;
                 log_state_change(state_type::STARTING, state_type::RUNNING);
-                SPDLOG_DEBUG("Start object [name = {}] succeeded.", get_manager().get_object_name(q_this));
+                SPDLOG_DEBUG("Start object [name = {}] succeeded.", QUERY_OBJECT_NAME(q_this));
                 start_finished_signal.try_notify_all();
                 co_return true;
             } else { // failed
                 co_await q_this->on_stop();
                 state = state_type::PENDING;
                 log_state_change(state_type::STARTING, state_type::PENDING);
-                SPDLOG_WARN("Start object [name = {}] failed.", get_manager().get_object_name(q_this));
+                SPDLOG_WARN("Start object [name = {}] failed.", QUERY_OBJECT_NAME(q_this));
                 stop_finished_signal.try_notify_all();
                 co_return false;
             }
@@ -136,15 +132,15 @@ namespace sophiar {
                 co_await q_this->on_stop();
                 state = state_type::PENDING;
                 log_state_change(state_type::STOPPING, state_type::PENDING);
-                SPDLOG_DEBUG("Stopped object [name = {}].", get_manager().get_object_name(q_this));
+                SPDLOG_DEBUG("Stopped object [name = {}].", QUERY_OBJECT_NAME(q_this));
                 stop_finished_signal.try_notify_all();
-                get_manager().notify_object_stop(q_this);
+                global_sophiar_manager->notify_object_stop(q_this);
             } else if (state == state_type::STOPPING) {
-                auto stop_finished_watcher = stop_finished_signal.new_watcher(get_context());
+                auto stop_finished_watcher = stop_finished_signal.new_watcher();
                 co_await stop_finished_watcher.coro_wait();
             } else if (state == state_type::STARTING) {
                 start_cancel_signal.try_notify_all();
-                auto stop_finished_watcher = stop_finished_signal.new_watcher(get_context());
+                auto stop_finished_watcher = stop_finished_signal.new_watcher();
                 co_await stop_finished_watcher.coro_wait();
             }
             co_return;
@@ -165,38 +161,42 @@ namespace sophiar {
                 co_await q_this->on_reset();
                 state = state_type::INITIAL;
                 log_state_change(state_type::RESETTING, state_type::INITIAL);
-                SPDLOG_DEBUG("Reset object [name = {}].", get_manager().get_object_name(q_this));
+                SPDLOG_DEBUG("Reset object [name = {}].", QUERY_OBJECT_NAME(q_this));
                 reset_finished_signal.try_notify_all();
             } else if (state == state_type::RESETTING) {
-                auto reset_finished_watcher = reset_finished_signal.new_watcher(get_context());
+                auto reset_finished_watcher = reset_finished_signal.new_watcher();
                 co_await reset_finished_watcher.coro_wait();
             } else if (state == state_type::INITIALIZING) {
                 init_cancel_signal.try_notify_all();
-                auto reset_finished_watcher = reset_finished_signal.new_watcher(get_context());
+                auto reset_finished_watcher = reset_finished_signal.new_watcher();
                 co_await reset_finished_watcher.coro_wait();
             }
             co_return;
         }
 
+        impl() {
+            initialize_translator();
+        }
+
     };
 
     tristate_obj::state_type tristate_obj::get_state() const {
         return pimpl->state;
     }
 
-    awaitable<bool> tristate_obj::init(const nlohmann::json &config) {
+    awaitable<bool> tristate_obj::init(const nlohmann::json &config) noexcept {
         return pimpl->init(config);
     }
 
-    awaitable<bool> tristate_obj::start(const nlohmann::json &config) {
+    awaitable<bool> tristate_obj::start(const nlohmann::json &config) noexcept {
         return pimpl->start(config);
     }
 
-    awaitable<void> tristate_obj::stop() {
+    awaitable<void> tristate_obj::stop() noexcept {
         return pimpl->stop();
     }
 
-    awaitable<void> tristate_obj::reset() {
+    awaitable<void> tristate_obj::reset() noexcept {
         return pimpl->reset();
     }
 

+ 26 - 17
src/core/tristate_obj.h

@@ -17,7 +17,7 @@ namespace sophiar {
                          private boost::noncopyable {
     public:
 
-        enum class state_type {
+        enum class state_type : uint8_t {
             INITIAL = 0x00,
             INITIALIZING = 0x01,
             RESETTING = 0x02,
@@ -25,42 +25,46 @@ namespace sophiar {
             STARTING = 0x04,
             STOPPING = 0x05,
             RUNNING = 0x06,
+            UNKNOWN = 0xFF, // maybe not a tristate obj
         };
 
         tristate_obj();
 
         ~tristate_obj() override;
 
-        boost::asio::awaitable<bool> init(const nlohmann::json &config);
+        boost::asio::awaitable<bool> init(const nlohmann::json &config) noexcept;
 
-        boost::asio::awaitable<bool> start(const nlohmann::json &config);
+        boost::asio::awaitable<bool> start(const nlohmann::json &config) noexcept;
 
-        boost::asio::awaitable<void> stop();
+        boost::asio::awaitable<void> stop() noexcept;
 
-        boost::asio::awaitable<void> reset();
+        boost::asio::awaitable<void> reset() noexcept;
 
         state_type get_state() const;
 
+        static bool is_state_stable(state_type state) {
+            return state == state_type::INITIAL ||
+                   state == state_type::PENDING ||
+                   state == state_type::RUNNING;
+        }
+
         bool is_stable() const { // 是否处于稳定状态
-            auto cur_state = get_state();
-            return cur_state == state_type::INITIAL ||
-                   cur_state == state_type::PENDING ||
-                   cur_state == state_type::RUNNING;
+            return is_state_stable(get_state());
         }
 
     protected:
 
-        virtual boost::asio::awaitable<bool> on_init(const nlohmann::json &config) {
+        virtual boost::asio::awaitable<bool> on_init(const nlohmann::json &config) noexcept {
             co_return true;
         }
 
-        virtual boost::asio::awaitable<bool> on_start(const nlohmann::json &config) {
+        virtual boost::asio::awaitable<bool> on_start(const nlohmann::json &config) noexcept {
             co_return true;
         }
 
-        virtual boost::asio::awaitable<void> on_stop() { co_return; }
+        virtual boost::asio::awaitable<void> on_stop() noexcept { co_return; }
 
-        virtual boost::asio::awaitable<void> on_reset() { co_return; }
+        virtual boost::asio::awaitable<void> on_reset() noexcept { co_return; }
 
     private:
 
@@ -69,6 +73,11 @@ namespace sophiar {
 
     };
 
+    template<typename T>
+    class name_translator;
+
+    extern name_translator<uint8_t> *state_name_translator;
+
 #define DEFAULT_TRISTATE_OBJ_DEF(type_name) \
     namespace sophiar { \
         class type_name : public tristate_obj { \
@@ -77,10 +86,10 @@ namespace sophiar {
             type_name(); \
             ~type_name() override; \
         protected: \
-            boost::asio::awaitable<bool> on_init(const nlohmann::json &config) override; \
-            boost::asio::awaitable<bool> on_start(const nlohmann::json &config) override; \
-            boost::asio::awaitable<void> on_stop() override; \
-            boost::asio::awaitable<void> on_reset() override; \
+            boost::asio::awaitable<bool> on_init(const nlohmann::json &config) noexcept override; \
+            boost::asio::awaitable<bool> on_start(const nlohmann::json &config) noexcept override; \
+            boost::asio::awaitable<void> on_stop() noexcept override; \
+            boost::asio::awaitable<void> on_reset() noexcept override; \
         private: \
             struct impl; \
             std::unique_ptr<impl> pimpl; \

+ 0 - 162
src/core/types/geometry_types.hpp

@@ -1,162 +0,0 @@
-#ifndef SOPHIAR2_GEOMETRY_TYPES_HPP
-#define SOPHIAR2_GEOMETRY_TYPES_HPP
-
-#include "core/types/versatile_data.hpp"
-
-#include <Eigen/Geometry>
-
-#include <nlohmann/json.hpp>
-
-#include <cassert>
-
-namespace sophiar {
-
-    struct coordinate_pose_compact;
-
-    // translation unit: mm
-    struct coordinate_pose : public Eigen::Isometry3d {
-        EIGEN_DERIVED(coordinate_pose, Eigen::Isometry3d);
-
-        static constexpr auto type_id = (uint32_t) versatile_type_id::COORDINATE_POSE;
-
-    protected:
-
-        void fill_from_versatile_data(const versatile_data &data) {
-            auto rot_matrix = data.view_as_matrix<3, 3, 0>();
-            auto trans_vec = data.view_as_matrix<3, 1, 9>();
-            this->linear().noalias() = rot_matrix;
-            this->translation().noalias() = trans_vec;
-        }
-
-        void write_to_versatile_data(versatile_data &data) const {
-            auto rot_matrix = data.view_as_matrix<3, 3, 0>();
-            auto trans_vec = data.view_as_matrix<3, 1, 9>();
-            rot_matrix.noalias() = this->linear();
-            trans_vec.noalias() = this->translation();
-        }
-
-    private:
-
-        friend class versatile_data;
-
-    };
-
-    // translation unit: mm
-    // quaternion form: [x, y, z, w]
-    struct coordinate_pose_compact : public Eigen::Isometry3d {
-        EIGEN_DERIVED(coordinate_pose_compact, Eigen::Isometry3d);
-
-        static constexpr auto type_id = (uint32_t) versatile_type_id::COORDINATE_POSE_COMPACT;
-
-        void fill_from_json(const nlohmann::json &data) {
-            assert(data.is_array());
-            assert(data.size() == 7);
-            for (int i = 0; i < 7; ++i) {
-                assert(data[i].is_number());
-            }
-            auto trans_part = Eigen::Translation3d(data[0].get<double>(),
-                                                   data[1].get<double>(),
-                                                   data[2].get<double>());
-            auto quat_part = Eigen::Quaterniond(data[3].get<double>(),
-                                                data[4].get<double>(),
-                                                data[5].get<double>(),
-                                                data[6].get<double>());
-            this->view_as_base() = trans_part * quat_part;
-        }
-
-        void write_to_json(nlohmann::json &data) {
-            auto trans_part = this->translation();
-            auto quat_part = Eigen::Quaterniond(this->rotation());
-            data.push_back(trans_part.x());
-            data.push_back(trans_part.y());
-            data.push_back(trans_part.z());
-            data.push_back(quat_part.w());
-            data.push_back(quat_part.x());
-            data.push_back(quat_part.y());
-            data.push_back(quat_part.z());
-        }
-
-    protected:
-
-        void fill_from_versatile_data(const versatile_data &data) {
-            auto trans_part = data.view_as_matrix<3, 1, 0>();
-            auto quat_part = data.view_as_matrix<4, 1, 3>();
-            this->view_as_base() = Eigen::Translation3d(trans_part) * Eigen::Quaterniond(quat_part);
-        }
-
-        void write_to_versatile_data(versatile_data &data) const {
-            auto trans_part = data.view_as_matrix<3, 1, 0>();
-            auto quat_part = data.view_as_matrix<4, 1, 3>();
-            trans_part.noalias() = this->translation();
-            quat_part.noalias() = Eigen::Quaterniond(this->rotation()).coeffs();
-        }
-
-    private:
-
-        friend class versatile_data;
-
-    };
-
-    // angular unit: rad
-    // translation unit: mm
-    struct coordinate_twist : public default_versatile_wrapper<
-            versatile_type_id::COORDINATE_TWIST, 6, 1> {
-
-        auto angular_part() {
-            return this->block<3, 1>(0, 0);
-        }
-
-        auto angular_part() const {
-            return this->block<3, 1>(0, 0);
-        }
-
-        auto linear_part() {
-            return this->block<3, 1>(3, 0);
-        }
-
-        auto linear_part() const {
-            return this->block<3, 1>(3, 0);
-        }
-
-        // 从点 rot_origin 按角速度 angular_velocity 旋转
-        static auto from_rot_axis(const Eigen::Vector3d &angular_velocity,
-                                  const Eigen::Vector3d &rot_origin = Eigen::Vector3d::Zero()) {
-            coordinate_twist twist;
-            twist.angular_part().noalias() = angular_velocity;
-            twist.linear_part().noalias() = -angular_velocity.cross(rot_origin);
-            return twist;
-        }
-
-    };
-
-    inline auto operator*(const Eigen::Isometry3d &trans, const coordinate_twist &old_twist) { // TODO 不知道怎么优化
-        coordinate_twist new_twist;
-        auto rot_matrix = trans.linear();
-        auto trans_vec = trans.translation();
-        auto old_angular_part = old_twist.angular_part();
-        auto old_linear_part = old_twist.linear_part();
-        new_twist.angular_part().noalias() = rot_matrix * old_angular_part;
-        new_twist.linear_part().noalias() = trans_vec.cross(rot_matrix * old_angular_part) +
-                                            rot_matrix * old_linear_part;
-        return new_twist;
-    }
-
-    // unit: mm
-    struct point : public default_versatile_wrapper<
-            versatile_type_id::POINT, 3, 1> {
-    };
-
-    // unit: mm/s
-    struct point_velocity : public default_versatile_wrapper<
-            versatile_type_id::POINT_VELOCITY, 3, 1> {
-    };
-
-    inline auto operator*(const Eigen::Isometry3d &trans, const point_velocity &old_vec) {
-        point_velocity new_vec;
-        new_vec.noalias() = trans.linear() * old_vec;
-        return new_vec;
-    }
-
-}
-
-#endif //SOPHIAR2_GEOMETRY_TYPES_HPP

+ 0 - 42
src/core/types/physical_types.hpp

@@ -1,42 +0,0 @@
-#ifndef SOPHIAR2_PHYSICAL_TYPES_HPP
-#define SOPHIAR2_PHYSICAL_TYPES_HPP
-
-#include "core/types/versatile_data.hpp"
-
-#include <Eigen/Geometry>
-
-namespace sophiar {
-
-    // force unit: N
-    // torque unit: Nm
-    struct force_torque_xyz : public default_versatile_wrapper<
-            versatile_type_id::FORCE_TORQUE_XYZ, 6, 1> {
-
-        auto force_part() {
-            return this->block<3, 1>(0, 0);
-        }
-
-        auto force_part() const {
-            return this->block<3, 1>(0, 0);
-        }
-
-        auto torque_part() {
-            return this->block<3, 1>(3, 0);
-        }
-
-        auto torque_part() const {
-            return this->block<3, 1>(3, 0);
-        }
-
-    };
-
-    auto operator*(const Eigen::Isometry3d &trans, const force_torque_xyz &ft_old) {
-        force_torque_xyz ft_new;
-        ft_new.force_part().noalias() = trans.linear() * ft_old.force_part();
-        ft_new.torque_part().noalias() = trans.linear() * ft_old.torque_part();
-        return ft_new;
-    }
-
-}
-
-#endif //SOPHIAR2_PHYSICAL_TYPES_HPP

+ 0 - 18
src/core/types/robot_types.hpp

@@ -1,18 +0,0 @@
-#ifndef SOPHIAR2_ROBOT_TYPES_HPP
-#define SOPHIAR2_ROBOT_TYPES_HPP
-
-#include "core/types/versatile_data.hpp"
-
-namespace sophiar {
-
-    struct angle_arr6 : default_versatile_wrapper<
-            versatile_type_id::ANGLE_ARR6, 6, 1> {
-    };
-
-    struct angle_arr7 : default_versatile_wrapper<
-            versatile_type_id::ANGLE_ARR7, 7, 1> {
-    };
-
-}
-
-#endif //SOPHIAR2_ROBOT_TYPES_HPP

+ 0 - 141
src/core/types/versatile_data.hpp

@@ -1,141 +0,0 @@
-#ifndef SOPHIAR2_VERSATILE_DATA_HPP
-#define SOPHIAR2_VERSATILE_DATA_HPP
-
-#include "core/small_obj.hpp"
-#include "core/timestamp_helper.hpp"
-
-#include <Eigen/Core>
-
-#include <type_traits>
-
-namespace Eigen {
-    using Vector6d = Vector<double, 6>;
-    using Vector7d = Vector<double, 7>;
-}
-
-namespace sophiar {
-
-    bool _is_compatible(uint32_t given_type, uint32_t requested_type);
-
-    struct versatile_data {
-
-        static constexpr auto FLOAT_FILED_LENGTH = 12;
-        static constexpr auto EXTRA_FILED_LENGTH = 20;
-
-        double floats[FLOAT_FILED_LENGTH];
-        timestamp_type timestamp;
-        uint8_t extra_data[EXTRA_FILED_LENGTH];
-        uint32_t data_type;
-
-        void make_timestamp_current() {
-            timestamp = current_timestamp();
-        }
-
-        void copy_from(const versatile_data &other) {
-            std::memcpy(this, &other, sizeof(*this));
-        }
-
-        double &operator[](size_t index) {
-            assert(index < FLOAT_FILED_LENGTH);
-            return floats[index];
-        }
-
-        double operator[](size_t index) const {
-            assert(index < FLOAT_FILED_LENGTH);
-            return floats[index];
-        }
-
-        template<uint16_t Rows, uint16_t Columns, uint16_t Offset = 0>
-        auto view_as_matrix() {
-            static_assert(Offset + Rows * Columns <= FLOAT_FILED_LENGTH);
-            return Eigen::Map<typename Eigen::Matrix<double, Rows, Columns>>(floats + Offset);
-        }
-
-        template<uint16_t Rows, uint16_t Columns, uint16_t Offset = 0>
-        auto view_as_matrix() const {
-            static_assert(Offset + Rows * Columns <= FLOAT_FILED_LENGTH);
-            return Eigen::Map<const typename Eigen::Matrix<double, Rows, Columns>>(floats + Offset);
-        }
-
-        template<typename Derived>
-        versatile_data &operator>>(Derived &data) {
-            assert(_is_compatible(this->data_type, Derived::type_id));
-            data.fill_from_versatile_data(*this);
-            return *this;
-        }
-
-        template<typename Derived>
-        versatile_data &operator<<(const Derived &data) {
-            this->data_type = Derived::type_id;
-            data.write_to_versatile_data(*this);
-            return *this;
-        }
-
-    };
-
-    static_assert(sizeof(versatile_data) == 128);
-    static_assert(std::is_trivial_v<versatile_data>);
-
-    struct versatile_obj : public versatile_data,
-                           public small_obj<versatile_obj> {
-    };
-
-    enum class versatile_type_id : uint32_t {
-        COORDINATE_POSE,
-        COORDINATE_POSE_COMPACT, // 平移 + 四元数
-        COORDINATE_POSE_TRANSFORM_TREE,
-        COORDINATE_TWIST,
-        POINT,
-        POINT_VELOCITY,
-        FORCE_TORQUE_XYZ, // 六轴力力矩
-        ANGLE_ARR6,
-        ANGLE_ARR7,
-    };
-
-// https://eigen.tuxfamily.org/dox/TopicCustomizing_InheritingMatrix.html
-#define EIGEN_DERIVED(class_name, base_name) \
-    class_name() : \
-            base_name() {} \
-    template<typename OtherDerived> \
-    explicit class_name(const Eigen::MatrixBase<OtherDerived> &other) \
-            :base_name(other) {} \
-    template<typename OtherDerived> \
-    class_name &operator=(const Eigen::MatrixBase<OtherDerived> &other) { \
-        base_name::operator=(other); \
-        return *this; \
-    } \
-    base_name &view_as_base() { \
-        return *this; \
-    } \
-    const base_name &view_as_base() const { \
-        return *this; \
-    }
-
-    template<versatile_type_id TypeId, uint16_t Rows, uint16_t Cols>
-    struct default_versatile_wrapper : public Eigen::Matrix<double, Rows, Cols> {
-        static_assert(Rows * Cols <= versatile_data::FLOAT_FILED_LENGTH);
-
-        using base_type = Eigen::Matrix<double, Rows, Cols>;
-        EIGEN_DERIVED(default_versatile_wrapper, base_type);
-
-        static constexpr auto type_id = (uint32_t) TypeId;
-
-    protected:
-
-        void fill_from_versatile_data(const versatile_data &data) {
-            this->view_as_base() = data.view_as_matrix<Rows, Cols>();
-        }
-
-        void write_to_versatile_data(versatile_data &data) const {
-            data.view_as_matrix<Rows, Cols>() = this->view_as_base();
-        }
-
-    private:
-
-        friend class versatile_data;
-
-    };
-
-}
-
-#endif //SOPHIAR2_VERSATILE_DATA_HPP

+ 0 - 27
src/extern_defs/core/sophiar_manager.cpp

@@ -1,27 +0,0 @@
-#include "core/sophiar_manager.h"
-#include "third_party/static_block.hpp"
-
-#include <boost/predef.h>
-
-#ifdef BOOST_OS_WINDOWS_AVAILABLE
-
-#include <timeapi.h>
-#include <cstdlib>
-
-#endif // BOOST_OS_WINDOWS_AVAILABLE
-
-namespace sophiar {
-
-    boost::asio::io_context global_context;
-    sophiar_manager global_sophiar_manager;
-
-#ifdef BOOST_OS_WINDOWS_AVAILABLE
-
-    static_block { // make windows timer more precise
-        timeBeginPeriod(1);
-        std::atexit([]() { timeEndPeriod(1); });
-    };
-
-#endif // BOOST_OS_WINDOWS_AVAILABLE
-
-}

+ 0 - 7
src/extern_defs/core/timestamp_helper.cpp

@@ -1,7 +0,0 @@
-#include "core/timestamp_helper.hpp"
-
-namespace sophiar {
-
-    local_time_type program_start_time = get_local_time();
-
-}

+ 0 - 17
src/extern_defs/core/types/versatile_data.cpp

@@ -1,17 +0,0 @@
-#include "core/types/versatile_data.hpp"
-
-namespace sophiar {
-
-    bool _is_compatible(uint32_t _given_type, uint32_t _requested_type) {
-        auto given_type = (versatile_type_id) _given_type;
-        auto requested_type = (versatile_type_id) _requested_type;
-        if (given_type == requested_type) return true;
-        switch (given_type) {
-            case versatile_type_id::COORDINATE_POSE_TRANSFORM_TREE:
-                if (requested_type == versatile_type_id::COORDINATE_POSE) return true;
-            default:
-                return false;
-        }
-    }
-
-}

+ 7 - 7
src/main.cpp

@@ -5,8 +5,8 @@
 #include "algorithm/transform_utility.hpp"
 #include "algorithm/measure_window.hpp"
 #include "robot/ur/ur_interface.h"
-#include "utility/debug_utility.hpp"
-#include "core/types/geometry_types.hpp"
+#include "utility/variable_utility.hpp"
+#include "utility/dynamic_pool.hpp"
 
 #include <boost/asio/co_spawn.hpp>
 #include <boost/asio/detached.hpp>
@@ -23,11 +23,11 @@ using boost::asio::detached;
 using namespace sophiar;
 
 void test() {
-    uint16_t data = 0x0123;
-    char out[16] = {0x00};
-    memset(out, 0x00, 16);
-    boost::algorithm::hex((uint8_t *) &data, (uint8_t *) &data + sizeof(uint16_t), out);
-    std::cout << out << std::endl;
+    std::vector<uint16_t, global_dynamic_allocator<uint16_t>> v;
+    for (int i = 0; i < 1000; ++i) {
+        v.push_back(i);
+    }
+    std::cout << std::accumulate(v.begin(), v.end(), 1) << std::endl;
 }
 
 int main() {

+ 0 - 1
src/robot/ur/ur_interface.cpp

@@ -1,7 +1,6 @@
 #include "ur_interface.h"
 
 #include "core/basic_obj_types.hpp"
-#include "utility/debug_utility.hpp"
 #include "utility/coro_worker.hpp"
 #include "utility/coro_worker_helper_func.hpp"
 #include "utility/versatile_buffer2.hpp"

+ 0 - 1
src/tracker/ndi/ndi_interface.cpp

@@ -2,7 +2,6 @@
 
 #include "core/basic_obj_types.hpp"
 #include "core/small_obj.hpp"
-#include "utility/debug_utility.hpp"
 #include "utility/coro_worker.hpp"
 #include "utility/name_translator.hpp"
 #include "utility/versatile_buffer2.hpp"

+ 30 - 5
src/utility/config_utility.hpp

@@ -1,11 +1,36 @@
 #ifndef SOPHIAR2_CONFIG_UTILITY_HPP
 #define SOPHIAR2_CONFIG_UTILITY_HPP
 
-#define LOAD_GLOBAL_OBJ_INDEX(obj_type, obj_name) ({ \
-    assert(config.contains(#obj_name)); \
-    assert(config[#obj_name].is_string()); \
-    auto obj_name = config[#obj_name].get<std::string>(); \
-    REGISTER_GLOBAL_OBJ(obj_type, obj_name); })
+#define ENSURE_ARRAY(item_name) \
+    assert(config.contains(item_name)); \
+    assert(config[item_name].is_array());
+
+#define LOAD_UINT_ITEM(item_name) ({ \
+    assert(config.contains(item_name)); \
+    assert(config[item_name].is_number_unsigned()); \
+    config[item_name].get<std::uint64_t>(); })
+
+#define LOAD_STRING_ITEM(item_name) ({ \
+    assert(config.contains(item_name)); \
+    assert(config[item_name].is_string()); \
+    config[item_name].get<std::string>(); })
+
+#define LOAD_STRING_ITEM2(config, item_name) ({ \
+    assert(config.contains(item_name)); \
+    assert(config[item_name].is_string()); \
+    config[item_name].get<std::string>(); })
+
+#define LOAD_VARIABLE_INDEX(var_type, var_name) ({ \
+    auto _name = LOAD_STRING_ITEM(var_name); \
+    REQUIRE_VARIABLE(var_type, _name); })
+
+#define LOAD_VARIABLE_INDEX2(config, var_type, var_name) ({ \
+    auto _name = LOAD_STRING_ITEM2(config, var_name); \
+    REQUIRE_VARIABLE(var_type, _name); })
+
+#define LOAD_VARIABLE_INDEX_WITH_NAME(var_type, var_name, var_name_out) ({ \
+    var_name_out = LOAD_STRING_ITEM(var_name); \
+    REQUIRE_VARIABLE(var_type, var_name_out); })
 
 #define SAFE_CLOSE_CORO_WORKER(worker) \
     worker->cancel(); \

+ 0 - 117
src/utility/coro_signal.hpp

@@ -1,117 +0,0 @@
-#ifndef SOPHIAR2_CORO_SIGNAL_HPP
-#define SOPHIAR2_CORO_SIGNAL_HPP
-
-#include <boost/asio/awaitable.hpp>
-#include <boost/asio/experimental/channel.hpp>
-#include <boost/asio/io_context.hpp>
-#include <boost/asio/use_awaitable.hpp>
-#include <boost/core/noncopyable.hpp>
-#include <boost/system/error_code.hpp>
-
-#include <cassert>
-
-namespace sophiar {
-
-    class [[deprecated]] coro_signal : private boost::noncopyable {
-    public:
-
-        class signal_token : private boost::noncopyable {
-        public:
-
-            explicit signal_token(coro_signal &_s)
-                    : signal(_s) {
-                ++signal.waiting_count;
-            }
-
-            ~signal_token() {
-                if (!is_fulfilled) {
-                    --signal.waiting_count;
-                }
-                // handle leaking signal
-                if (signal.waiting_count == 0 &&
-                        signal.channel.ready()) {
-                    assert(false);
-                    signal.channel.reset();
-                    signal.is_notifying = false;
-                }
-            }
-
-            void fulfill() {
-                is_fulfilled = true;
-                --signal.waiting_count;
-            }
-
-        private:
-
-            coro_signal &signal;
-            bool is_fulfilled = false;
-
-            friend class coro_signal;
-
-        };
-
-        coro_signal(boost::asio::io_context &context)
-                : channel(context, 1) {}
-
-        auto new_token() {
-            return signal_token{*this};
-        }
-
-        auto new_token_by_ptr() {
-            return new signal_token{*this};
-        }
-
-        boost::asio::awaitable<void> coro_wait(signal_token &token) {
-            if (token.is_fulfilled) co_return;
-            co_await channel.async_receive(boost::asio::use_awaitable);
-            token.fulfill();
-            try_notify_more();
-            co_return;
-        }
-
-        boost::asio::awaitable<void> coro_wait() {
-            signal_token token(*this);
-            co_await coro_wait(token);
-            co_return;
-        }
-
-        bool try_wait(signal_token &token) {
-            if (!channel.ready()) return false;
-            token.fulfill();
-            try_notify_more();
-            return true;
-        }
-
-        void try_notify_all() {
-            if (is_notifying || waiting_count == 0) return;
-            is_notifying = true;
-            send_signal();
-        }
-
-    private:
-
-        using channel_type = boost::asio::experimental::channel<
-                void(boost::system::error_code, bool)>;
-
-        channel_type channel;
-        size_t waiting_count = 0;
-        bool is_notifying = false;
-
-        void send_signal() {
-            assert(!channel.ready());
-            channel.try_send(boost::system::error_code{}, true);
-        }
-
-        void try_notify_more() {
-            if (waiting_count > 0) {
-                send_signal();
-            } else {
-                is_notifying = false;
-            }
-        }
-
-    };
-
-}
-
-#endif //SOPHIAR2_CORO_SIGNAL_HPP

+ 25 - 23
src/utility/coro_signal2.hpp

@@ -1,6 +1,7 @@
 #ifndef SOPHIAR2_CORO_SIGNAL2_HPP
 #define SOPHIAR2_CORO_SIGNAL2_HPP
 
+#include "core/global_defs.h"
 #include "core/timestamp_helper.hpp"
 
 #include <boost/asio/awaitable.hpp>
@@ -44,17 +45,20 @@ namespace sophiar {
 
     public:
 
-        template<typename Executor>
-        explicit signal_watcher(Executor &executor, coro_signal2 &_sig)
+        explicit signal_watcher(coro_signal2 *_sig)
 
 #ifdef CORO_SIGNAL2_USE_TIMER
 
-        : sig(_sig) {}
+                : sig(_sig) {
+            assert(sig != nullptr);
+        }
 
 #else
 
-                : sig(_sig),
-                  chan(executor, 1) {}
+        : sig(_sig),
+          chan(*global_context, 1) {
+    assert(sig != nullptr);
+}
 
 #endif
 
@@ -63,8 +67,8 @@ namespace sophiar {
 
 #ifdef CORO_SIGNAL2_USE_TIMER
 
-        : sig(other.sig),
-          last_watch_ts(other.last_watch_ts) {}
+                : sig(other.sig),
+                  last_watch_ts(other.last_watch_ts) {}
 
 #else
 
@@ -99,7 +103,7 @@ namespace sophiar {
 #endif
 
         timestamp_type last_watch_ts = 0;
-        coro_signal2 &sig;
+        coro_signal2 *sig;
 
     };
 
@@ -109,24 +113,22 @@ namespace sophiar {
         // if coro signal is moved, signal watcher will not work
         coro_signal2(coro_signal2 &&other) noexcept = delete;
 
-        template<typename Executor>
-        explicit coro_signal2(Executor &executor)
 
 #ifdef CORO_SIGNAL2_USE_TIMER
 
-        :timer(executor) {
-    timer.expires_at(boost::posix_time::pos_infin);
-}
+        coro_signal2()
+                : timer(*global_context) {
+            timer.expires_at(boost::posix_time::pos_infin);
+        }
 
 #else
 
-        {}
+        coro_signal2() = default;
 
 #endif
 
-        template<typename Executor>
-        auto new_watcher(Executor &executor) {
-            return signal_watcher{executor, *this};
+        auto new_watcher() {
+            return signal_watcher{this};
         }
 
         void try_notify_all(timestamp_type ts = current_timestamp()) {
@@ -179,7 +181,7 @@ namespace sophiar {
     };
 
     inline bool signal_watcher::try_wait() {
-        if (last_watch_ts < sig.last_notify_ts) {
+        if (last_watch_ts < sig->last_notify_ts) {
             sync();
             return true;
         }
@@ -197,7 +199,7 @@ namespace sophiar {
         if (auto_sync) {
             sync();
         } else {
-            if (last_watch_ts < sig.last_notify_ts) {
+            if (last_watch_ts < sig->last_notify_ts) {
                 sync();
                 co_return;
             }
@@ -210,7 +212,7 @@ namespace sophiar {
             if (chan.ready()) chan.reset();
         });
         assert(!is_linked());
-        sig.watcher_list.push_back(*this);
+        sig->watcher_list.push_back(*this);
 
 #endif
 
@@ -219,7 +221,7 @@ namespace sophiar {
 #ifdef CORO_SIGNAL2_USE_TIMER
 
             boost::system::error_code ec;
-            co_await sig.timer.async_wait(
+            co_await sig->timer.async_wait(
                     boost::asio::redirect_error(boost::asio::use_awaitable, ec));
             assert(ec == boost::asio::error::operation_aborted);
 
@@ -229,14 +231,14 @@ namespace sophiar {
 
 #endif
 
-            if (last_watch_ts < sig.last_notify_ts) break;
+            if (last_watch_ts < sig->last_notify_ts) break;
         }
         sync();
         co_return;
     }
 
     inline void signal_watcher::sync() {
-        last_watch_ts = sig.last_notify_ts;
+        last_watch_ts = sig->last_notify_ts;
     }
 
 }

+ 8 - 13
src/utility/coro_signal_group.hpp

@@ -19,17 +19,14 @@ namespace sophiar {
 
         using pointer = std::unique_ptr<coro_signal_group<CondFunc>>;
 
-        template<typename Executor>
-        explicit coro_signal_group(Executor &executor)
-                : final_signal(executor) {}
+        coro_signal_group() = default;
 
         ~coro_signal_group() {
             assert(!is_running);
         }
 
-        template<typename Executor>
-        static pointer new_instance(Executor &executor) {
-            return std::make_unique<coro_signal_group<CondFunc>>(executor);
+        static pointer new_instance() {
+            return std::make_unique<coro_signal_group<CondFunc>>();
         }
 
         void add_watcher(signal_watcher &&watcher) {
@@ -37,18 +34,16 @@ namespace sophiar {
             watcher_list.push_back(std::move(watcher));
         }
 
-        template<typename Executor>
-        auto new_watcher(Executor &executor) {
-            return final_signal.new_watcher(executor);
+        auto new_watcher() {
+            return final_signal.new_watcher();
         }
 
-        template<typename Executor>
-        void start(Executor &executor, bool auto_sync = true) { // 思考如果 auto_sync == false 有什么副作用?
+        void start(bool auto_sync = true) { // 思考如果 auto_sync == false 有什么副作用?
             item_mask.resize(watcher_list.size());
             is_running = true;
             for (size_t index = 0; index < watcher_list.size(); ++index) {
-                auto worker = make_infinite_coro_worker(executor, [this, index, auto_sync]()
-                        -> boost::asio::awaitable<bool> {
+                auto worker = make_infinite_coro_worker([this, index, auto_sync]()
+                                                                -> boost::asio::awaitable<bool> {
                     co_await watcher_list[index].coro_wait(auto_sync);
                     item_mask.set(index);
                     check_and_notify();

+ 33 - 38
src/utility/coro_worker.hpp

@@ -1,6 +1,7 @@
 #ifndef SOPHIAR2_CORO_WORKER_HPP
 #define SOPHIAR2_CORO_WORKER_HPP
 
+#include "core/global_defs.h"
 #include "third_party/scope_guard.hpp"
 #include "utility/coro_signal2.hpp"
 
@@ -27,22 +28,31 @@ namespace sophiar {
             assert(!is_running);
         }
 
-        virtual void run() = 0;
+        void run() {
+            assert(!is_running);
+            request_stop_watcher.sync();
+            boost::asio::co_spawn(*global_context, run_impl(), boost::asio::detached);
+        }
 
         void cancel() {
             if (!is_running) return;
             request_stop_signal.try_notify_all();
         }
 
-        virtual boost::asio::awaitable<void> coro_wait_stop() = 0;
+        boost::asio::awaitable<void> coro_wait_stop() {
+            if (!is_running) co_return;
+            auto stop_finished_watcher = stop_finished_signal.new_watcher();
+            co_await stop_finished_watcher.coro_wait();
+            assert(!is_running);
+            co_return;
+        }
 
     protected:
 
-        template<typename Executor>
-        explicit coro_worker(Executor &executor)
-                :request_stop_signal(executor),
-                 stop_finished_signal(executor),
-                 request_stop_watcher(request_stop_signal.new_watcher(executor)) {
+        explicit coro_worker()
+                : request_stop_signal(),
+                  stop_finished_signal(),
+                  request_stop_watcher(request_stop_signal.new_watcher()) {
         }
 
         coro_signal2 request_stop_signal;
@@ -51,9 +61,11 @@ namespace sophiar {
 
         bool is_running = false;
 
+        virtual boost::asio::awaitable<void> run_impl() = 0;
+
     };
 
-    template<typename Executor, typename FuncType, typename ExitFuncType>
+    template<typename FuncType, typename ExitFuncType>
     class coro_worker_impl : public coro_worker {
     public:
 
@@ -64,28 +76,13 @@ namespace sophiar {
         static_assert(std::is_void<
                 decltype(std::declval<ExitFuncType>()())>());
 
-        coro_worker_impl(Executor &_executor, FuncType &&_func, ExitFuncType &&_exit_func)
-                : coro_worker(_executor),
-                  executor(_executor),
+        coro_worker_impl(FuncType &&_func, ExitFuncType &&_exit_func)
+                : coro_worker(),
                   func(std::forward<FuncType>(_func)),
                   exit_func(std::forward<ExitFuncType>(_exit_func)) {}
 
         ~coro_worker_impl() override = default;
 
-        void run() override {
-            assert(!is_running);
-            request_stop_watcher.sync();
-            boost::asio::co_spawn(executor, run_impl(), boost::asio::detached);
-        }
-
-        boost::asio::awaitable<void> coro_wait_stop() override {
-            if (!is_running) co_return;
-            auto stop_finished_watcher = stop_finished_signal.new_watcher(executor);
-            co_await stop_finished_watcher.coro_wait();
-            assert(!is_running);
-            co_return;
-        }
-
     private:
 
         using func_store_type = std::remove_cvref_t<FuncType>;
@@ -93,9 +90,7 @@ namespace sophiar {
         func_store_type func;
         exit_func_store_type exit_func;
 
-        Executor &executor;
-
-        boost::asio::awaitable<void> run_impl() {
+        boost::asio::awaitable<void> run_impl() override {
             is_running = true;
             auto closer = sg::make_scope_guard([this]() {
                 is_running = false;
@@ -113,30 +108,30 @@ namespace sophiar {
 
     };
 
-    template<typename Executor, typename FuncType,
+    template<typename FuncType,
             typename ExitFuncType = decltype(coro_worker::empty_func) const &>
-    inline auto make_infinite_coro_worker(Executor &executor, FuncType &&func,
-                                   ExitFuncType &&exit_func = coro_worker::empty_func) {
-        return coro_worker::pointer(new coro_worker_impl<Executor, FuncType, ExitFuncType>(
-                executor, std::forward<FuncType>(func), std::forward<ExitFuncType>(exit_func))
+    inline auto make_infinite_coro_worker(FuncType &&func,
+                                          ExitFuncType &&exit_func = coro_worker::empty_func) {
+        return coro_worker::pointer(new coro_worker_impl<FuncType, ExitFuncType>(
+                std::forward<FuncType>(func), std::forward<ExitFuncType>(exit_func))
         );
     }
 
-    template<typename Executor, typename FuncType,
+    template<typename FuncType,
             typename ExitFuncType = decltype(coro_worker::empty_func) const &>
-    inline auto make_interval_coro_worker(Executor &executor, std::chrono::milliseconds interval,
-                                   FuncType &&func, ExitFuncType &&exit_func = coro_worker::empty_func) {
+    inline auto make_interval_coro_worker(std::chrono::milliseconds interval,
+                                          FuncType &&func, ExitFuncType &&exit_func = coro_worker::empty_func) {
         auto worker_func = [
                 interval,
                 func = std::forward<FuncType>(func),
-                timer = boost::asio::high_resolution_timer(executor)]() mutable
+                timer = boost::asio::high_resolution_timer(*global_context)]() mutable
                 -> boost::asio::awaitable<bool> {
             timer.expires_from_now(interval);
             auto ret = co_await func();
             co_await timer.async_wait(boost::asio::use_awaitable);
             co_return ret;
         };
-        return make_infinite_coro_worker(executor, std::move(worker_func),
+        return make_infinite_coro_worker(std::move(worker_func),
                                          std::forward<ExitFuncType>(exit_func));
     }
 

+ 2 - 2
src/utility/coro_worker_helper_func.hpp

@@ -14,7 +14,7 @@ namespace sophiar {
         auto func = [obj]() {
             assert(obj != nullptr);
             boost::asio::co_spawn(
-                    global_context,
+                    *global_context,
                     std::bind(&tristate_obj::stop, obj),
                     boost::asio::detached);
         };
@@ -25,7 +25,7 @@ namespace sophiar {
         auto func = [obj]() {
             assert(obj != nullptr);
             boost::asio::co_spawn(
-                    global_context,
+                    *global_context,
                     std::bind(&tristate_obj::reset, obj),
                     boost::asio::detached);
         };

+ 27 - 198
src/utility/debug_utility.hpp

@@ -1,227 +1,56 @@
-#ifndef SOPHIAR2_DEBUG_UTILITY_HPP
-#define SOPHIAR2_DEBUG_UTILITY_HPP
+#ifndef SOPHIAR2_DEBUG_MACRO_H
+#define SOPHIAR2_DEBUG_MACRO_H
 
-#include "core/basic_obj_types.hpp"
 #include "core/timestamp_helper.hpp"
-#include "utility/simple_tristate_obj.hpp"
-#include "utility/global_obj_helper.hpp"
 
 #include <boost/asio/awaitable.hpp>
 #include <boost/asio/high_resolution_timer.hpp>
 #include <boost/asio/this_coro.hpp>
 #include <boost/asio/use_awaitable.hpp>
 
-#include <fmt/format.h>
 #include <spdlog/spdlog.h>
 
-#include <chrono>
-#include <coroutine>
-#include <cstring>
-#include <fstream>
 #include <iostream>
 
-using boost::asio::awaitable;
-using boost::asio::use_awaitable;
-
 #define FILE_LINE_TRACE {\
     std::cout << fmt::format("F:{} L:{} T:{}", \
                              __FILE_NAME__, __LINE__, sophiar::current_timestamp()) \
-              << std::endl; }\
-
-#define CO_ENSURE(func) \
-    { \
-        bool ok = co_await (func); \
-        if (!ok) co_return false; \
-    }
-
-template<typename DurationType>
-inline awaitable<void> coro_sleep(DurationType t) {
-    boost::asio::high_resolution_timer timer(co_await boost::asio::this_coro::executor);
-    timer.expires_from_now(t);
-    co_await timer.async_wait(use_awaitable);
-    co_return;
-}
+              << std::endl; }
 
-class string_writer {
-public:
+#define FILE_LINE_TRACE_WITH_THIS {\
+    std::cout << fmt::format("F:{} L:{} T:{} TH:{}", \
+                             __FILE_NAME__, __LINE__, sophiar::current_timestamp(), (void *) this) \
+              << std::endl; }
 
-    explicit string_writer(std::string _sep = ", ")
-            : sep(std::move(_sep)) {}
+#define PRINT_PTR(obj) {\
+    std::cout << fmt::format("F:{} L:{} PTR: {}", __FILE_NAME__, __LINE__, \
+                             (void*)std::addressof(obj)) << std::endl; }
 
-    template<typename T>
-    string_writer &operator<<(const T &val) {
-        if (!is_empty) {
-            ss << sep;
-        }
-        ss << val;
-        is_empty = false;
-        return *this;
-    }
-
-    std::string get_string_and_reset() {
-        auto ret = ss.str();
-        ss.str("");
-        ss.clear();
-        is_empty = true;
-        return ret;
-    }
-
-private:
-    std::stringstream ss;
-    std::string sep;
-    bool is_empty = true;
-};
-
-class string_reader {
-public:
-
-    explicit string_reader(int _sep_length = 1)
-            : sep_length(_sep_length) {}
-
-    void set_string(std::string str) {
-        ss.clear();
-        ss.str(std::move(str));
-        ss.seekg(0, std::ios::beg);
-    }
-
-    template<typename T>
-    string_reader &operator>>(T &val) {
-        assert(!empty());
-        ss >> val;
-        if (!empty()) {
-            ss.ignore(sep_length);
-        }
-        return *this;
+#define ENSURE(func) \
+    { \
+        bool ok = (func); \
+        if (!ok) return false; \
     }
 
-    bool empty() const {
-        return ss.rdbuf()->in_avail() == 0;
+#define CO_ENSURE(func) \
+    { \
+        bool ok = co_await (func); \
+        if (!ok) co_return false; \
     }
 
-private:
-    std::stringstream ss;
-    int sep_length;
-};
-
 namespace sophiar {
 
-    template<typename SmallObjType>
-    inline coro_worker::pointer global_obj_watcher_func(const nlohmann::json &config) {
-        assert(config.contains("obj_name"));
-        assert(config["obj_name"].is_string());
-        auto obj_name = config["obj_name"].get<std::string>();
-        auto obj_index = REGISTER_GLOBAL_OBJ(SmallObjType, obj_name);
-        auto worker = make_infinite_coro_worker(global_context, [
-                obj_name, buffer = string_writer(),
-                obj_helper = GLOBAL_OBJ_AUTO_DELEGATE(SmallObjType, obj_index)]() mutable
-                -> awaitable<bool> {
-            co_await obj_helper.coro_wait_update();
-            if (obj_helper.empty()) {
-                SPDLOG_DEBUG("{} is empty.", obj_name);
-                co_return true;
-            }
-            obj_helper->write_to(buffer);
-            SPDLOG_DEBUG("{} = {}", obj_name, buffer.get_string_and_reset());
-            co_return true;
-        });
-        return std::move(worker);
-    }
-
-    template<typename SmallObjType>
-    inline coro_worker::pointer global_obj_recorder_func(const nlohmann::json &config) {
-        // global obj_config
-        assert(config.contains("obj_name"));
-        assert(config["obj_name"].is_string());
-        auto obj_name = config["obj_name"].get<std::string>();
-        auto obj_index = REGISTER_GLOBAL_OBJ(SmallObjType, obj_name);
-        // output file config
-        assert(config.contains("save_file"));
-        assert(config["save_file"].is_string());
-        auto save_file_path = config["save_file"].get<std::string>();
-        auto ofs = std::ofstream(save_file_path, std::ofstream::out);
-        assert(ofs.is_open());
-        // create worker
-        auto worker = make_infinite_coro_worker(global_context, [
-                obj_index, buffer = string_writer(","), ofs = std::move(ofs),
-                obj_helper = GLOBAL_OBJ_AUTO_DELEGATE(SmallObjType, obj_index)]() mutable
-                -> awaitable<bool> {
-            co_await obj_helper.coro_wait_update();
-            auto ts = GLOBAL_OBJ_UPDATE_TS(obj_index);
-            buffer << (static_cast<double>(ts) / 1000.0); // us -> ms
-            if (!obj_helper.empty()) {
-                obj_helper->write_to(buffer);
-            }
-            ofs << buffer.get_string_and_reset() << std::endl;
-            co_return true;
-        });
-        return std::move(worker);
-    }
+    using boost::asio::awaitable;
+    using boost::asio::use_awaitable;
 
-    template<typename SmallObjType>
-    inline coro_worker::pointer global_obj_replayer_func(const nlohmann::json &config) {
-        // global obj_config
-        assert(config.contains("obj_name"));
-        assert(config["obj_name"].is_string());
-        auto obj_name = config["obj_name"].get<std::string>();
-        auto obj_index = REGISTER_GLOBAL_OBJ(SmallObjType, obj_name);
-        // output file config
-        assert(config.contains("record_file"));
-        assert(config["record_file"].is_string());
-        auto record_file_path = config["record_file"].get<std::string>();
-        auto ifs = std::ifstream(record_file_path, std::ifstream::in);
-        assert(ifs.is_open());
-        // create worker
-        auto worker = make_infinite_coro_worker(global_context, [
-                timer = boost::asio::high_resolution_timer(global_context),
-                obj_index, buffer = string_reader(), ifs = std::move(ifs)]() mutable
-                -> awaitable<bool> {
-            std::string str_buf;
-            if (!std::getline(ifs, str_buf)) co_return false; // EOF
-            buffer.set_string(std::move(str_buf));
-            double next_ts_ms;
-            buffer >> next_ts_ms;
-            auto next_ts = static_cast<timestamp_type>(next_ts_ms * 1000); // ms -> us
-            timestamp_type cur_ts = current_timestamp();
-            if (next_ts > cur_ts) {
-                timer.expires_at(get_time_from_timestamp(next_ts));
-                co_await timer.async_wait(boost::asio::use_awaitable);
-            } else if (next_ts != cur_ts) { // if time is missed, simply ignore
-                co_return true;
-            }
-            if (buffer.empty()) { // empty obj
-                UPDATE_GLOBAL_OBJ(SmallObjType, obj_index, nullptr);
-            } else {
-                auto new_obj = SmallObjType::new_instance();
-                new_obj->fill_from(buffer);
-                assert(buffer.empty());
-                UPDATE_GLOBAL_OBJ(SmallObjType, obj_index, std::move(new_obj));
-            }
-            co_return true;
-        });
-        return std::move(worker);
+    template<typename DurationType>
+    inline awaitable<void> coro_sleep(DurationType t) {
+        boost::asio::high_resolution_timer timer(co_await boost::asio::this_coro::executor);
+        timer.expires_from_now(t);
+        co_await timer.async_wait(use_awaitable);
+        co_return;
     }
 
-    template<typename SmallObjType>
-    using global_obj_watcher = simple_tristate_obj_wrapper<global_obj_watcher_func<SmallObjType>>;
-
-    template<typename SmallObjType>
-    using global_obj_recorder = simple_tristate_obj_wrapper<global_obj_recorder_func<SmallObjType>>;
-
-    template<typename SmallObjType>
-    using global_obj_replayer = simple_tristate_obj_wrapper<global_obj_replayer_func<SmallObjType>>;
-
-    using double_obj_watcher = global_obj_watcher<double_obj>;
-    using scalarxyz_obj_watcher = global_obj_watcher<scalarxyz_obj>;
-    using transform_obj_watcher = global_obj_watcher<transform_obj>;
-
-    using double_obj_recorder = global_obj_recorder<double_obj>;
-    using scalarxyz_obj_recorder = global_obj_recorder<scalarxyz_obj>;
-    using transform_obj_recorder = global_obj_recorder<transform_obj>;
-
-    using double_obj_replayer = global_obj_replayer<double_obj>;
-    using scalarxyz_obj_replayer = global_obj_replayer<scalarxyz_obj>;
-    using transform_obj_replayer = global_obj_replayer<transform_obj>;
-
 }
 
-#endif //SOPHIAR2_DEBUG_UTILITY_HPP
+#endif //SOPHIAR2_DEBUG_MACRO_H

+ 82 - 0
src/utility/dynamic_pool.hpp

@@ -0,0 +1,82 @@
+#ifndef SOPHIAR2_DYNAMIC_POOL_HPP
+#define SOPHIAR2_DYNAMIC_POOL_HPP
+
+#include "core/global_defs.h"
+
+#include <boost/pool/pool.hpp>
+
+#include <array>
+
+namespace sophiar {
+
+    class dynamic_pool {
+    public:
+
+        dynamic_pool() {
+            for (int i = 0; i < extra_num; ++i) {
+                pools[i] = new pool_type(base_size << i);
+            }
+        }
+
+        ~dynamic_pool() {
+            for (auto pool: pools) {
+                delete pool;
+            }
+        }
+
+        static constexpr size_t actual_allocate_size(size_t s) {
+            return base_size << determine_index(s);
+        }
+
+        void *allocate(size_t s, size_t n = 1) {
+            assert(n >= 1);
+            auto index = determine_index(n == 1 ? s : s * n);
+            return pools[index]->malloc();
+        }
+
+        void deallocate(void *p, size_t s, size_t n = 1) {
+            assert(n >= 1);
+            auto index = determine_index(n == 1 ? s : s * n);
+            pools[index]->free(p);
+        }
+
+    private:
+        static constexpr size_t base_size = 32;
+        static constexpr uint8_t extra_num = 20;
+
+        static_assert(std::has_single_bit(base_size));
+
+        using pool_type = boost::pool<>;
+        std::array<pool_type *, extra_num> pools;
+
+        static constexpr uint8_t determine_index(size_t s) {
+            constexpr auto shift_length = std::bit_width(base_size);
+            auto index = std::bit_width(s);
+            if (!std::has_single_bit(s)) ++index;
+            if (index <= shift_length) return 0;
+            assert(index - shift_length < extra_num);
+            return index - shift_length;
+        }
+
+    };
+
+    template<typename T>
+    struct global_dynamic_allocator : public std::allocator<T> {
+        auto allocate(size_t n) {
+            return (T *) ALLOCATE_DYNAMIC_MEMORY_N(sizeof(T), n);
+        }
+
+        void deallocate(T *p, size_t n) {
+            DEALLOCATE_DYNAMIC_MEMORY_N((void *) p, sizeof(T), n);
+        }
+    };
+
+#define DYNAMIC_ALLOCATOR(type) \
+    global_dynamic_allocator<type>
+
+    template<typename T>
+    using dynamic_vector = std::vector<T, DYNAMIC_ALLOCATOR(T) >;
+
+}
+
+#endif //SOPHIAR2_DYNAMIC_POOL_HPP

+ 0 - 102
src/utility/global_obj_helper.hpp

@@ -1,102 +0,0 @@
-#ifndef SOPHIAR2_GLOBAL_OBJ_HELPER_HPP
-#define SOPHIAR2_GLOBAL_OBJ_HELPER_HPP
-
-#include "core/small_obj.hpp"
-#include "core/sophiar_manager.h"
-#include "utility/coro_signal2.hpp"
-
-namespace sophiar {
-
-    template<typename SmallObjType, bool AutoSync = true>
-    class global_obj_delegate {
-    public:
-
-        using pointer_type = typename SmallObjType::pointer;
-
-        global_obj_delegate(sophiar_manager &_manager,
-                            global_obj_index_type _obj_index)
-                : manager(_manager),
-                  watcher(_manager.request_global_obj_update_watcher(_obj_index)),
-                  obj_index(_obj_index) {}
-
-        global_obj_delegate(sophiar_manager &_manager,
-                            const std::string &obj_name)
-                : global_obj_delegate(_manager,
-                                      _manager.register_global_obj<SmallObjType>(obj_name)) {}
-
-        global_obj_delegate(global_obj_delegate &&other) noexcept = default;
-
-        signal_watcher new_watcher() const {
-            return manager.request_global_obj_update_watcher(obj_index);
-        }
-
-        void set_value(const pointer_type &ptr,
-                       timestamp_type ts = current_timestamp()) {
-            manager.update_global_obj<SmallObjType>(obj_index, ptr, ts);
-        }
-
-        void manual_sync() {
-            if (watcher.try_wait()) {// new value available
-                obj_ptr = manager.get_global_obj<SmallObjType>(obj_index);
-            }
-        }
-
-        pointer_type get_value() {
-            if constexpr (AutoSync) {
-                manual_sync();
-            }
-            return obj_ptr;
-        }
-
-        bool empty() {
-            return get_value() == nullptr;
-        }
-
-        timestamp_type get_last_update_ts() const {
-            return watcher.get_last_update_ts();
-        }
-
-        global_obj_delegate &operator=(const pointer_type &ptr) {
-            set_value(ptr);
-            return *this;
-        }
-
-        const SmallObjType &operator*() {
-            assert(!empty());
-            return *get_value();
-        }
-
-        const SmallObjType *operator->() {
-            assert(!empty());
-            return get_value().get();
-        }
-
-        boost::asio::awaitable<void> coro_wait_update(bool auto_sync = true) {
-            co_await watcher.coro_wait(auto_sync);
-            obj_ptr = manager.get_global_obj<SmallObjType>(obj_index);
-            co_return;
-        }
-
-    private:
-        global_obj_index_type obj_index;
-        pointer_type obj_ptr;
-        signal_watcher watcher;
-        sophiar_manager &manager;
-
-    };
-
-    template<typename SmallObjType>
-    using global_obj_auto_sync_delegate = global_obj_delegate<SmallObjType, true>;
-
-    template<typename SmallObjType>
-    using global_obj_manual_sync_delegate = global_obj_delegate<SmallObjType, false>;
-
-#define GLOBAL_OBJ_AUTO_DELEGATE(obj_type, obj_index) \
-    global_obj_auto_sync_delegate<obj_type>(global_sophiar_manager, obj_index)
-
-#define GLOBAL_OBJ_MANUAL_DELEGATE(obj_type, obj_index) \
-    global_obj_manual_sync_delegate<obj_type>(global_sophiar_manager, obj_index)
-
-}
-
-#endif //SOPHIAR2_GLOBAL_OBJ_HELPER_HPP

+ 9 - 7
src/utility/name_translator.hpp

@@ -1,6 +1,8 @@
 #ifndef SOPHIAR2_NAME_TRANSLATOR_HPP
 #define SOPHIAR2_NAME_TRANSLATOR_HPP
 
+#include "utility/string_map.hpp"
+
 #include <cassert>
 #include <string>
 #include <unordered_map>
@@ -11,26 +13,26 @@ namespace sophiar {
     class name_translator {
     public:
 
-        void register_item(const std::string &name, IndexType index) {
+        void register_item(std::string_view name, IndexType index) {
             assert(!name_map.contains(name));
             assert(!index_map.contains(index));
-            name_map[name] = index;
-            index_map[index] = name;
+            name_map.insert(name, index);
+            index_map.emplace(index, name);
         }
 
-        IndexType translate(const std::string &name) const {
+        IndexType translate(std::string_view name) const {
             assert(name_map.contains(name));
-            return name_map.at(name);
+            return name_map.query(name);
         }
 
-        std::string translate(IndexType index) const {
+        std::string_view translate(IndexType index) const {
             assert(index_map.contains(index));
             return index_map.at(index);
         }
 
     private:
 
-        std::unordered_map<std::string, IndexType> name_map;
+        string_map<IndexType> name_map;
         std::unordered_map<IndexType, std::string> index_map;
 
     };

+ 15 - 14
src/utility/named_vector.hpp

@@ -1,9 +1,10 @@
 #ifndef SOPHIAR2_NAMED_VECTOR_HPP
 #define SOPHIAR2_NAMED_VECTOR_HPP
 
+#include "utility/string_map.hpp"
+
 #include <cassert>
 #include <climits>
-#include <unordered_map>
 #include <vector>
 
 namespace sophiar {
@@ -12,12 +13,12 @@ namespace sophiar {
     class named_vector {
     public:
 
-        IndexType new_elem(const std::string &name) {
-            assert(!name_map.contains(name));
+        IndexType new_elem(std::string_view name) {
+            assert(!contains(name));
             assert(name_list.size() <= std::numeric_limits<IndexType>::max());
             IndexType new_index = name_list.size();
-            name_map[name] = new_index;
-            name_list.push_back(name);
+            name_map.insert(name, new_index);
+            name_list.emplace_back(name);
             elem_list.resize(new_index + 1);
             return new_index;
         }
@@ -27,26 +28,26 @@ namespace sophiar {
             return name_list[index];
         }
 
-        IndexType to_index_by_name(const std::string &name) const {
-            assert(name_map.contains(name));
-            return name_map.at(name);
+        IndexType to_index_by_name(std::string_view name) const {
+            assert(contains(name));
+            return name_map.query(name);
         }
 
         ElemType &operator[](IndexType index) {
-            assert(index >= 0 && index < name_list.size());
+            assert(contains(index));
             return elem_list[index];
         }
 
         const ElemType &operator[](IndexType index) const {
-            assert(index >= 0 && index < name_list.size());
+            assert(contains(index));
             return elem_list[index];
         }
 
-        ElemType &operator[](const std::string &name) {
+        ElemType &operator[](std::string_view name) {
             return operator[](to_index_by_name(name));
         }
 
-        const ElemType &operator[](const std::string &name) const {
+        const ElemType &operator[](std::string_view name) const {
             return operator[](to_index_by_name(name));
         }
 
@@ -54,7 +55,7 @@ namespace sophiar {
             return name_list.size();
         }
 
-        bool contains(const std::string &name) const {
+        bool contains(std::string_view name) const {
             return name_map.contains(name);
         }
 
@@ -70,7 +71,7 @@ namespace sophiar {
 
     private:
 
-        std::unordered_map<std::string, IndexType> name_map;
+        string_map<IndexType> name_map;
         std::vector<std::string> name_list;
         std::vector<ElemType> elem_list;
 

+ 0 - 42
src/utility/signal_muxer.hpp

@@ -1,42 +0,0 @@
-#ifndef SOPHIAR2_SIGNAL_MUXER_HPP
-#define SOPHIAR2_SIGNAL_MUXER_HPP
-
-#include "utility/named_vector.hpp"
-#include "utility/tiny_signal.hpp"
-
-namespace sophiar {
-
-    template<typename... Args>
-    class [[deprecated]] signal_muxer : public tiny_signal<Args...> {
-    public:
-
-        using signal_type = tiny_signal<Args...>;
-        using slot_type = typename signal_type::slot_type;
-
-        struct proxy_slot : public slot_type {
-
-            signal_type *signal = nullptr;
-
-            virtual void on_signal_received(Args... args) {
-                assert(signal != nullptr);
-                signal->emit(std::move(args)...);
-            }
-
-        };
-
-        void add_upstream_signal(const std::string &name, signal_type &signal) {
-            slot_pool.new_elem(name);
-            auto slot = new proxy_slot; // 只调用有限次,无视内存泄漏
-            slot->signal = this;
-            signal.add_slot_base(slot);
-        }
-
-    private:
-
-        named_vector<uint8_t> slot_pool;
-
-    };
-
-}
-
-#endif //SOPHIAR2_SIGNAL_MUXER_HPP

+ 5 - 5
src/utility/simple_tristate_obj.hpp

@@ -24,28 +24,28 @@ namespace sophiar {
 
     protected:
 
-        boost::asio::awaitable<bool> on_init(const nlohmann::json &config) override {
+        boost::asio::awaitable<bool> on_init(const nlohmann::json &config) noexcept override {
             if constexpr (!Flag) {
                 create_worker(config);
             }
             co_return true;
         }
 
-        boost::asio::awaitable<bool> on_start(const nlohmann::json &config) override {
+        boost::asio::awaitable<bool> on_start(const nlohmann::json &config) noexcept override {
             if constexpr (Flag) {
                 create_worker(config);
             }
             co_return true;
         }
 
-        boost::asio::awaitable<void> on_stop() override {
+        boost::asio::awaitable<void> on_stop() noexcept override {
             if constexpr (Flag) {
                 co_await stop_worker();
             }
             co_return;
         }
 
-        boost::asio::awaitable<void> on_reset() override {
+        boost::asio::awaitable<void> on_reset() noexcept override {
             if constexpr (!Flag) {
                 co_await stop_worker();
             }
@@ -65,7 +65,7 @@ namespace sophiar {
                 }
                 co_return;
             };
-            boost::asio::co_spawn(global_context, std::move(watchdog_func), boost::asio::detached);
+            boost::asio::co_spawn(*global_context, std::move(watchdog_func), boost::asio::detached);
         }
 
         void create_worker(const nlohmann::json &config) {

+ 0 - 52
src/utility/slot_demuxer.hpp

@@ -1,52 +0,0 @@
-#ifndef SOPHIAR2_SLOT_DEMUXER_HPP
-#define SOPHIAR2_SLOT_DEMUXER_HPP
-
-#include "utility/named_vector.hpp"
-#include "utility/tiny_signal.hpp"
-
-namespace sophiar {
-
-    struct slot_demuxer_base {
-
-        virtual ~slot_demuxer_base() = default;
-
-        virtual tiny_slot_base *new_slot() = 0;
-
-    };
-
-    template<typename... Args>
-    class [[deprecated]] slot_demuxer : public slot_demuxer_base {
-    public:
-
-        using signal_type = tiny_signal<Args...>;
-        using slot_type = typename signal_type::slot_type;
-
-        struct proxy_slot : public slot_type {
-
-            slot_type *upstream_slot = nullptr;
-
-            virtual void on_signal_received(Args... args) {
-                assert(upstream_slot != nullptr);
-                upstream_slot->signal_received(std::move(args)...);
-            }
-
-        };
-
-        explicit slot_demuxer(slot_type &slot)
-                : upstream_slot(&slot) {}
-
-        tiny_slot_base *new_slot() override {
-            auto slot = new proxy_slot; // 只调用有限次,无视内存泄漏
-            slot->upstream_slot = this->upstream_slot;
-            return slot;
-        }
-
-    private:
-
-        slot_type *upstream_slot = nullptr;
-
-    };
-
-}
-
-#endif //SOPHIAR2_SLOT_DEMUXER_HPP

+ 54 - 0
src/utility/string_map.hpp

@@ -0,0 +1,54 @@
+#ifndef SOPHIAR2_STRING_MAP_HPP
+#define SOPHIAR2_STRING_MAP_HPP
+
+#include <cassert>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <unordered_map>
+
+namespace sophiar {
+
+    template<typename T>
+    class string_map {
+    public:
+        template<typename ...Args>
+        void insert(std::string_view str, Args... args) {
+            insert(hash_sv(str), std::forward<Args>(args)...);
+        }
+
+        bool contains(std::string_view str) const {
+            return m.contains(hash_sv(str));
+        }
+
+        T &query(std::string_view str) {
+            assert(contains(str));
+            return m.at(hash_sv(str));
+        }
+
+        const T &query(std::string_view str) const {
+            assert(contains(str));
+            return m.at(hash_sv(str));
+        }
+
+        void clear() {
+            m.clear();
+        }
+
+    private:
+        static constexpr auto hash_sv = std::hash<std::string_view>{};
+        std::unordered_map<std::size_t, T> m;
+
+        template<typename ...Args>
+        void insert(size_t index, Args... args) {
+            static_assert(std::is_constructible_v<T, Args...>);
+            assert(!m.contains(index));
+            m.emplace(std::piecewise_construct,
+                      std::forward_as_tuple(index),
+                      std::forward_as_tuple(std::forward<Args>(args)...));
+        }
+    };
+
+}
+
+#endif //SOPHIAR2_STRING_MAP_HPP

+ 0 - 87
src/utility/tiny_signal.hpp

@@ -1,87 +0,0 @@
-#ifndef SOPHIAR2_TINY_SIGNAL_HPP
-#define SOPHIAR2_TINY_SIGNAL_HPP
-
-#include <boost/intrusive/list.hpp>
-
-#include <spdlog/spdlog.h>
-
-#include <cassert>
-#include <type_traits>
-#include <utility>
-
-namespace sophiar {
-
-    using slot_hook_type = boost::intrusive::list_base_hook<
-            boost::intrusive::link_mode<
-                    boost::intrusive::auto_unlink>>;
-
-    struct [[deprecated]] tiny_slot_base : public slot_hook_type {
-
-        bool is_enabled = true;
-
-        virtual ~tiny_slot_base() = default;
-
-        void disconnect() {
-            assert(this->is_linked());
-            this->unlink();
-            SPDLOG_TRACE("Slot {} is disconnected.", fmt::ptr(this));
-        }
-    };
-
-    struct tiny_signal_base {
-
-        virtual ~tiny_signal_base() = default;
-
-        virtual void add_slot_base(tiny_slot_base *slot_base) = 0;
-    };
-
-    template<typename... Args>
-    class [[deprecated]] tiny_signal : public tiny_signal_base {
-    public:
-
-        class slot_type : public tiny_slot_base {
-        public:
-
-            void signal_received(Args... args) {
-                if (!is_enabled) return;
-                on_signal_received(std::move(args)...);
-            }
-
-        protected:
-
-            virtual void on_signal_received(Args... args) = 0;
-
-        };
-
-        void add_slot_base(tiny_slot_base *slot_base) override {
-            auto slot_ptr = dynamic_cast<slot_type *>(slot_base);
-            assert(slot_ptr != nullptr);
-            slot_list.push_back(*slot_ptr);
-            SPDLOG_TRACE("Slot {} is connected to signal {}.",
-                         fmt::ptr(slot_ptr), fmt::ptr(this));
-        }
-
-        void emit(Args... args) {
-            auto iter = slot_list.begin(), iter_end = slot_list.end();
-            while (iter != iter_end) {
-                auto this_iter = iter++;
-                if (iter == iter_end) {
-                    this_iter->signal_received(std::move(args)...);
-                } else {
-                    this_iter->signal_received(args...);
-                }
-            }
-        }
-
-    private:
-
-        using slot_list_type = boost::intrusive::list<
-                slot_type, boost::intrusive::constant_time_size<false>>;
-
-        slot_list_type slot_list;
-
-    };
-
-}
-
-#endif //SOPHIAR2_TINY_SIGNAL_HPP

+ 106 - 0
src/utility/variable_helper.hpp

@@ -0,0 +1,106 @@
+#ifndef SOPHIAR2_VARIABLE_HELPER_HPP
+#define SOPHIAR2_VARIABLE_HELPER_HPP
+
+#include "core/global_defs.h"
+#include "core/small_obj.hpp"
+#include "core/sophiar_pool.h"
+#include "utility/coro_signal2.hpp"
+
+namespace sophiar {
+
+    template<typename SmallObjType, bool AutoSync = true>
+    class variable_delegate {
+    public:
+
+        using pointer_type = typename SmallObjType::pointer;
+
+        explicit variable_delegate(variable_index_type _var_index)
+                : watcher(REQUIRE_VARIABLE_WATCHER(_var_index)),
+                  var_index(_var_index) {}
+
+        explicit variable_delegate(const std::string &obj_name)
+                : variable_delegate(REQUIRE_VARIABLE<SmallObjType>(obj_name)) {}
+
+        variable_delegate(variable_delegate &&other) noexcept = default;
+
+        signal_watcher new_watcher() const {
+            return REQUIRE_VARIABLE_WATCHER(var_index);
+        }
+
+        void set_value(const pointer_type &ptr,
+                       timestamp_type ts = current_timestamp()) {
+            UPDATE_VARIABLE_WITH_TS(SmallObjType, var_index, ptr, ts);
+        }
+
+        template<typename OtherSmallObjType = SmallObjType,
+                typename ValueType = OtherSmallObjType::value_type>
+        void set_value(const ValueType &val,
+                       timestamp_type ts = current_timestamp()) {
+            UPDATE_VARIABLE_VAL_WITH_TS(SmallObjType, var_index, val, ts);
+        }
+
+        void manual_sync() {
+            if (watcher.try_wait()) { // new value available
+                val_ptr = QUERY_VARIABLE(SmallObjType, var_index);
+            }
+        }
+
+        pointer_type get_value() {
+            if constexpr (AutoSync) {
+                manual_sync();
+            }
+            return val_ptr;
+        }
+
+        timestamp_type get_last_update_ts() const {
+            return watcher.get_last_update_ts();
+        }
+
+        bool empty() {
+            return get_value() == nullptr;
+        }
+
+        template<typename T>
+        variable_delegate &operator=(T &&val) {
+            set_value(std::forward<T>(val));
+            return *this;
+        }
+
+        const SmallObjType &operator*() {
+            assert(!empty());
+            return *get_value();
+        }
+
+        const SmallObjType *operator->() {
+            assert(!empty());
+            return get_value().get();
+        }
+
+        boost::asio::awaitable<void> coro_wait_update(bool auto_sync = true) {
+            co_await watcher.coro_wait(auto_sync);
+            val_ptr = QUERY_VARIABLE(SmallObjType, var_index);
+            co_return;
+        }
+
+    private:
+        variable_index_type var_index;
+        pointer_type val_ptr;
+        signal_watcher watcher;
+
+    };
+
+    template<typename SmallObjType>
+    using variable_auto_sync_delegate = variable_delegate<SmallObjType, true>;
+
+    template<typename SmallObjType>
+    using variable_manual_sync_delegate = variable_delegate<SmallObjType, false>;
+
+#define VARIABLE_AUTO_DELEGATE(var_type, var_index) \
+    variable_auto_sync_delegate<var_type>(var_index)
+
+#define VARIABLE_MANUAL_DELEGATE(var_type, var_index) \
+    variable_manual_sync_delegate<var_type>(var_index)
+
+}
+
+#endif //SOPHIAR2_VARIABLE_HELPER_HPP

+ 184 - 0
src/utility/variable_utility.hpp

@@ -0,0 +1,184 @@
+#ifndef SOPHIAR2_VARIABLE_UTILITY_HPP
+#define SOPHIAR2_VARIABLE_UTILITY_HPP
+
+#include "core/basic_obj_types.hpp"
+#include "core/timestamp_helper.hpp"
+#include "utility/config_utility.hpp"
+#include "utility/simple_tristate_obj.hpp"
+#include "utility/variable_helper.hpp"
+
+#include <boost/asio/awaitable.hpp>
+
+#include <spdlog/spdlog.h>
+
+#include <chrono>
+#include <coroutine>
+#include <cstring>
+#include <fstream>
+
+namespace sophiar {
+
+    using boost::asio::awaitable;
+
+    class string_writer {
+    public:
+
+        explicit string_writer(std::string _sep = ", ")
+                : sep(std::move(_sep)) {}
+
+        template<typename T>
+        string_writer &operator<<(const T &val) {
+            if (!is_empty) {
+                ss << sep;
+            }
+            ss << val;
+            is_empty = false;
+            return *this;
+        }
+
+        std::string get_string_and_reset() {
+            auto ret = ss.str();
+            ss.str("");
+            ss.clear();
+            is_empty = true;
+            return ret;
+        }
+
+    private:
+        std::stringstream ss;
+        std::string sep;
+        bool is_empty = true;
+    };
+
+    class string_reader {
+    public:
+
+        explicit string_reader(int _sep_length = 1)
+                : sep_length(_sep_length) {}
+
+        void set_string(std::string str) {
+            ss.clear();
+            ss.str(std::move(str));
+            ss.seekg(0, std::ios::beg);
+        }
+
+        template<typename T>
+        T read_value() {
+            T val;
+            *this >> val;
+            return val;
+        }
+
+        template<typename T>
+        string_reader &operator>>(T &val) {
+            assert(!empty());
+            ss >> val;
+            if (!empty()) {
+                ss.ignore(sep_length);
+            }
+            return *this;
+        }
+
+        bool empty() const {
+            return ss.rdbuf()->in_avail() == 0;
+        }
+
+    private:
+        std::stringstream ss;
+        int sep_length;
+    };
+
+    template<typename SmallObjType>
+    inline coro_worker::pointer variable_debug_watcher_func(const nlohmann::json &config) {
+        std::string var_name;
+        auto var_index = LOAD_VARIABLE_INDEX_WITH_NAME(SmallObjType, "variable_name", var_name);
+        auto worker = make_infinite_coro_worker(
+                [=,
+                        buffer = string_writer(),
+                        var_helper = VARIABLE_AUTO_DELEGATE(SmallObjType, var_index)]() mutable
+                        -> awaitable<bool> {
+                    co_await var_helper.coro_wait_update();
+                    if (var_helper.empty()) {
+                        SPDLOG_DEBUG("{} is empty.", var_name);
+                    } else {
+                        var_helper->write_to(buffer);
+                        SPDLOG_DEBUG("{} = {}", var_name, buffer.get_string_and_reset());
+                    }
+                    co_return true;
+                });
+        return std::move(worker);
+    }
+
+    template<typename SmallObjType>
+    inline coro_worker::pointer variable_recorder_func(const nlohmann::json &config) {
+        auto var_index = LOAD_VARIABLE_INDEX(SmallObjType, "variable_name");
+        auto save_file_path = LOAD_STRING_ITEM("save_file");
+        auto ofs = std::ofstream(save_file_path, std::ofstream::out);
+        assert(ofs.is_open());
+        // create worker
+        auto worker = make_infinite_coro_worker(
+                [=,
+                        buffer = string_writer(","), ofs = std::move(ofs),
+                        var_helper = VARIABLE_AUTO_DELEGATE(SmallObjType, var_index)]() mutable
+                        -> awaitable<bool> {
+                    co_await var_helper.coro_wait_update();
+                    auto ts = var_helper.get_last_update_ts();
+                    buffer << (static_cast<double>(ts) / 1000.0); // us -> ms
+                    if (!var_helper.empty()) {
+                        var_helper->write_to(buffer);
+                    }
+                    ofs << buffer.get_string_and_reset() << std::endl;
+                    co_return true;
+                });
+        return std::move(worker);
+    }
+
+    template<typename SmallObjType>
+    inline coro_worker::pointer variable_replayer_func(const nlohmann::json &config) {
+        auto var_index = LOAD_VARIABLE_INDEX(SmallObjType, "variable_name");
+        auto record_file_path = LOAD_STRING_ITEM("record_file");
+        auto ifs = std::ifstream(record_file_path, std::ifstream::in);
+        assert(ifs.is_open());
+        // create worker
+        auto worker = make_infinite_coro_worker(
+                [=,
+                        timer = boost::asio::high_resolution_timer(*global_context),
+                        buffer = string_reader(), ifs = std::move(ifs)]() mutable
+                        -> awaitable<bool> {
+                    std::string str_buf;
+                    if (!std::getline(ifs, str_buf)) co_return false; // EOF
+                    buffer.set_string(std::move(str_buf));
+                    auto next_ts_ms = buffer.read_value<double>();
+                    auto next_ts = static_cast<timestamp_type>(next_ts_ms * 1000); // ms -> us
+                    auto cur_ts = current_timestamp();
+                    if (next_ts > cur_ts) {
+                        timer.expires_at(get_time_from_timestamp(next_ts));
+                        co_await timer.async_wait(boost::asio::use_awaitable);
+                    } else if (next_ts != cur_ts) { // if time is missed, simply ignore
+                        co_return true;
+                    }
+                    if (buffer.empty()) { // empty obj
+                        UPDATE_VARIABLE(SmallObjType, var_index, nullptr);
+                    } else {
+                        auto new_var = SmallObjType::new_instance();
+                        new_var->fill_from(buffer);
+                        assert(buffer.empty());
+                        UPDATE_VARIABLE(SmallObjType, var_index, std::move(new_var));
+                    }
+                    co_return true;
+                });
+        return std::move(worker);
+    }
+
+    template<typename SmallObjType>
+    using variable_debug_watcher = simple_tristate_obj_wrapper<variable_debug_watcher_func<SmallObjType>>;
+
+    template<typename SmallObjType>
+    using variable_recorder = simple_tristate_obj_wrapper<variable_recorder_func<SmallObjType>>;
+
+    template<typename SmallObjType>
+    using variable_replayer = simple_tristate_obj_wrapper<variable_replayer_func<SmallObjType>>;
+
+}
+
+#endif //SOPHIAR2_VARIABLE_UTILITY_HPP

+ 0 - 225
src/utility/versatile_buffer.hpp

@@ -1,225 +0,0 @@
-#ifndef SOPHIAR2_VERSATILE_BUFFER_HPP
-#define SOPHIAR2_VERSATILE_BUFFER_HPP
-
-#include "utility/bit_operations.hpp"
-
-#include <boost/asio/awaitable.hpp>
-#include <boost/asio/read.hpp>
-#include <boost/asio/use_awaitable.hpp>
-#include <boost/asio/write.hpp>
-
-#include <Eigen/Core>
-
-#include <cassert>
-#include <string>
-#include <type_traits>
-
-namespace sophiar {
-
-    static constexpr size_t VERSATILE_BUFFER_MAX_SIZE = 1024;
-
-    // handle endian properly
-    class [[deprecated("This version is poorly designed.")]]
-    versatile_buffer {
-    public:
-
-        template<typename T>
-        std::enable_if_t<std::is_arithmetic_v<T>>
-        write_value(T val) {
-            assert(end_pos + sizeof(T) <= VERSATILE_BUFFER_MAX_SIZE);
-
-            swap_net_loc_endian(val);
-            std::memcpy(buffer + end_pos, &val, sizeof(T));
-
-            end_pos += sizeof(T);
-        }
-
-        void write_value(const std::string &str) {
-            assert(end_pos + str.length() <= VERSATILE_BUFFER_MAX_SIZE);
-            std::memcpy(buffer + end_pos, str.data(), str.length());
-            end_pos += str.length();
-        }
-
-        template<typename Derived>
-        void write_value(const Eigen::MatrixBase<Derived> &matrix) {
-            for (Eigen::Index i = 0; i < matrix.size(); ++i) {
-                write_value(matrix(i));
-            }
-        }
-
-        template<typename T>
-        std::enable_if_t<std::is_void_v<
-                decltype(std::declval<const T>().write_to_buffer(
-                        std::declval<versatile_buffer &>()))>>
-        write_value(const T &val) {
-            val.write_to_buffer(*this);
-        }
-
-        template<typename T>
-        versatile_buffer &operator<<(const T &val) {
-            write_value(val);
-            return *this;
-        }
-
-        template<typename T>
-        std::enable_if_t<std::is_arithmetic_v<T>, T>
-        read_value() {
-            assert(start_pos + sizeof(T) <= end_pos);
-
-            T tmp_val;
-            std::memcpy(&tmp_val, buffer + start_pos, sizeof(T));
-            swap_net_loc_endian(tmp_val);
-
-            start_pos += sizeof(T);
-            return tmp_val;
-        }
-
-        template<typename T>
-        std::enable_if_t<std::is_arithmetic_v<T>>
-        read_value(T &val) {
-            val = read_value<T>();
-        }
-
-        template<typename Derived>
-        void read_value(Eigen::MatrixBase<Derived> &matrix) {
-            for (Eigen::Index i = 0; i < matrix.size(); ++i) {
-                read_value(matrix(i));
-            }
-        }
-
-        template<typename T>
-        std::enable_if_t<std::is_void_v<
-                decltype(std::declval<T>().fill_from_buffer(
-                        std::declval<versatile_buffer &>()))>>
-        read_value(T &val) {
-            val.fill_from_buffer(*this);
-        }
-
-        std::string read_string(size_t length) {
-            assert(start_pos + length <= end_pos);
-            auto ret = std::string{buffer + start_pos, buffer + start_pos + length};
-            start_pos += length;
-            return ret;
-        }
-
-        void read_string(std::string &str, size_t length) {
-            str = read_string(length);
-        }
-
-        std::string read_string_until(char sep) {
-            auto cur_pos = start_pos;
-            while (cur_pos != end_pos && buffer[cur_pos] != sep) {
-                ++cur_pos;
-            }
-            auto ret = std::string{buffer + start_pos, buffer + cur_pos};
-            if (cur_pos != end_pos) {
-                start_pos = cur_pos + 1;
-            } else {
-                start_pos = cur_pos;
-            }
-            return ret;
-        }
-
-        void read_string_until(std::string &str, char sep) {
-            str = read_string_until(sep);
-        }
-
-        template<typename T>
-        versatile_buffer &operator>>(T &val) {
-            read_value(val);
-            return *this;
-        }
-
-        // will cause reset
-        template<typename AsyncReadStream>
-        boost::asio::awaitable<void> async_read_from(AsyncReadStream &s, size_t length) {
-            assert(empty());
-            reset();
-            assert(length <= VERSATILE_BUFFER_MAX_SIZE);
-            co_await boost::asio::async_read(s,
-                                             boost::asio::buffer(buffer, length),
-                                             boost::asio::use_awaitable);
-            end_pos = length;
-            co_return;
-        }
-
-        // will cause reset
-//        template<typename AsyncReadStream>
-//        boost::asio::awaitable<void> async_read_until(AsyncReadStream &s, char delimiter = '\r') {
-//            assert(empty());
-//            reset();
-//            assert(length <= VERSATILE_BUFFER_MAX_SIZE);
-//            co_await boost::asio::async_read(s,
-//                                             boost::asio::buffer(buffer, length),
-//                                             boost::asio::use_awaitable);
-//            end_pos = length;
-//            co_return;
-//        }
-
-        // will cause reset
-        template<typename AsyncWriteStream>
-        boost::asio::awaitable<void> async_write_to(AsyncWriteStream &s) {
-            co_await boost::asio::async_write(s,
-                                              boost::asio::buffer(buffer + start_pos, end_pos - start_pos),
-                                              boost::asio::use_awaitable);
-            reset();
-            co_return;
-        }
-
-        bool empty() const {
-            return start_pos == end_pos;
-        }
-
-        explicit operator bool() const {
-            return !empty();
-        }
-
-        void reset() {
-            start_pos = 0;
-            end_pos = 0;
-        }
-
-        size_t size() const {
-            return end_pos - start_pos;
-        }
-
-    private:
-        char buffer[VERSATILE_BUFFER_MAX_SIZE];
-        size_t start_pos = 0, end_pos = 0;
-
-    };
-
-    template<typename AsyncWriteStream, typename T>
-    [[deprecated]]
-    std::enable_if_t<std::is_arithmetic_v<T>, boost::asio::awaitable<void>>
-    inline async_write_value(AsyncWriteStream &s, T val) {
-        swap_net_loc_endian(val);
-        co_await boost::asio::async_write(s,
-                                          boost::asio::buffer(&val, sizeof(T)),
-                                          boost::asio::use_awaitable);
-        co_return;
-    }
-
-    template<typename T, typename AsyncReadStream>
-    [[deprecated]]
-    std::enable_if_t<std::is_arithmetic_v<T>, boost::asio::awaitable<T>>
-    inline async_read_value(AsyncReadStream &s) {
-        T tmp_val;
-        co_await boost::asio::async_read(s,
-                                         boost::asio::buffer(&tmp_val, sizeof(T)),
-                                         boost::asio::use_awaitable);
-        swap_net_loc_endian(tmp_val);
-        co_return tmp_val;
-    }
-
-    template<typename T, typename AsyncReadStream>
-    [[deprecated]]
-    std::enable_if_t<std::is_arithmetic_v<T>, boost::asio::awaitable<void>>
-    inline async_read_value(AsyncReadStream &s, T &val) {
-        val = co_await async_read_value<T>(s);
-        co_return;
-    }
-
-}
-
-#endif //SOPHIAR2_VERSATILE_BUFFER_HPP

+ 210 - 153
src/utility/versatile_buffer2.hpp

@@ -2,7 +2,9 @@
 #define SOPHIAR2_VERSATILE_BUFFER2_HPP
 
 #include "utility/bit_operations.hpp"
+#include "utility/dynamic_pool.hpp"
 
+#include <boost/noncopyable.hpp>
 #include <boost/asio/awaitable.hpp>
 #include <boost/asio/read.hpp>
 #include <boost/asio/use_awaitable.hpp>
@@ -10,59 +12,81 @@
 
 #include <Eigen/Core>
 
+#include <algorithm>
 #include <cassert>
 #include <concepts>
+#include <span>
 #include <string>
+#include <string_view>
 #include <type_traits>
 
 namespace sophiar {
 
-    template<size_t length>
-    struct static_memory {
+    template<size_t Length>
+    struct static_memory : private boost::noncopyable {
 
-        char *data() { return buf; }
+        char *data() { return buf.data(); }
 
-        const char *data() const { return buf; }
+        const char *data() const { return buf.data(); }
+
+        constexpr size_t size() { return Length; }
 
-        constexpr size_t max_length() { return length; }
+        static auto new_instance() {
+            using this_type = static_memory<Length>;
+            return std::make_unique<this_type>();
+        }
 
     private:
-        char buf[length];
+        std::array<char, Length> buf;
     };
 
-    struct dynamic_memory {
+    struct dynamic_memory : private boost::noncopyable {
 
-        explicit dynamic_memory(size_t buf_length) {
-            length = buf_length;
-            buf = new char[length];
-        }
+        static constexpr size_t DYNAMIC_MEMORY_DEFAULT_SIZE = 32;
 
-        dynamic_memory(dynamic_memory &&other) noexcept {
-            buf = other.buf;
-            length = other.length;
-            other.buf = nullptr;
-        }
+        using pointer = std::unique_ptr<dynamic_memory>;
 
-        ~dynamic_memory() { delete[]buf; }
+        ~dynamic_memory() {
+            DEALLOCATE_DYNAMIC_MEMORY((void *) buf, capacity_length);
+        }
 
         char *data() { return buf; }
 
         const char *data() const { return buf; }
 
-        size_t max_length() const { return length; }
+        size_t size() const { return used_length; }
 
-        void ensure_length(size_t ext_length) {
-            if (ext_length <= length) return;
-            auto ext_buf = new char[ext_length];
-            memcpy(ext_buf, buf, length);
-            delete[] buf;
-            buf = ext_buf;
-            length = ext_length;
+        void resize(size_t req_size) {
+            if (req_size > capacity_length) [[unlikely]] {
+                auto ext_buf = (char *) ALLOCATE_DYNAMIC_MEMORY(req_size);
+                memcpy(ext_buf, buf, used_length);
+                DEALLOCATE_DYNAMIC_MEMORY((void *) buf, capacity_length);
+                buf = ext_buf;
+                capacity_length = ACTUAL_DYNAMIC_MEMORY_SIZE(req_size);
+            }
+            used_length = req_size;
+        }
+
+        void increase_size(ptrdiff_t extra_size) {
+            assert(extra_size >= 0 || size() > -extra_size);
+            resize(size() + extra_size);
+        }
+
+        static auto new_instance(size_t req_size = DYNAMIC_MEMORY_DEFAULT_SIZE) {
+            return pointer{new dynamic_memory{req_size}};
         }
 
     private:
         char *buf;
-        size_t length;
+        size_t used_length;
+        size_t capacity_length;
+
+        explicit dynamic_memory(size_t req_size) {
+            buf = (char *) ALLOCATE_DYNAMIC_MEMORY(req_size);
+            used_length = req_size;
+            capacity_length = ACTUAL_DYNAMIC_MEMORY_SIZE(req_size);
+        }
+
     };
 
     struct extern_memory {
@@ -70,20 +94,13 @@ namespace sophiar {
         extern_memory(char *extern_buf, size_t buf_length)
                 : buf(extern_buf), length(buf_length) {}
 
-        extern_memory(extern_memory &&other) = default; // to suppress some warning
-
-        explicit extern_memory(dynamic_memory &other)
-                : buf(other.data()), length(other.max_length()) {}
-
-        template<size_t length>
-        explicit extern_memory(static_memory<length> &other)
-                :buf(other.data()), length(other.max_length()) {}
+        extern_memory(const extern_memory &) = default;
 
         char *data() { return buf; }
 
         const char *data() const { return buf; }
 
-        size_t max_length() const { return length; }
+        size_t size() const { return length; }
 
     private:
         char *buf;
@@ -91,12 +108,18 @@ namespace sophiar {
     };
 
     struct const_extern_memory {
+
         const_extern_memory(const char *extern_buf, size_t buf_length)
                 : buf(extern_buf), length(buf_length) {}
 
+        explicit const_extern_memory(const extern_memory &other)
+                : buf(other.data()), length(other.size()) {}
+
+        const_extern_memory(const const_extern_memory &) = default;
+
         const char *data() const { return buf; }
 
-        size_t max_length() const { return length; }
+        size_t size() const { return length; }
 
     private:
         const char *buf;
@@ -104,42 +127,36 @@ namespace sophiar {
     };
 
     template<typename T>
-    concept FixedLengthMemory = requires(T a) {
-        { a.data() } -> std::convertible_to<const char *>; // 如果需要 char *, 就让它报错
-        { a.max_length() }->std::convertible_to<size_t>;
+    concept ReadableMemory = requires(T a) {
+        { a.data() } -> std::convertible_to<const char *>;
+        { a.size() } -> std::convertible_to<size_t>;
     };
 
-    static constexpr size_t VERSATILE_BUFFER_DEFAULT_SIZE = 1024;
+    template<typename T>
+    concept WriteableMemory = requires(T a) {
+        { a.data() } -> std::convertible_to<char *>;
+        { a.size() } -> std::convertible_to<size_t>;
+    };
 
-    // 一次性写入数据,分多次读取
-    template<boost::endian::order net_order = boost::endian::order::big,
-            FixedLengthMemory buffer_type = static_memory<VERSATILE_BUFFER_DEFAULT_SIZE>>
-    class versatile_readable_buffer {
+    // 分多次读取数据
+    template<boost::endian::order net_order>
+    class versatile_reader : private boost::noncopyable {
     public:
 
-        template<typename other_buffer_type = buffer_type,
-                typename  = std::enable_if_t<std::is_default_constructible_v<other_buffer_type>>>
-        versatile_readable_buffer() {
-            cur_pos = 0;
-            cur_length = 0;
-        }
-
-        explicit versatile_readable_buffer(buffer_type &&other_buffer,
-                                           size_t initial_length = 0,
-                                           size_t initial_pos = 0)
-                : buffer(std::move(other_buffer)) {
-            cur_pos = initial_pos;
-            cur_length = initial_length != 0 ? initial_length : buffer.max_length();
+        template<ReadableMemory MemoryType>
+        explicit versatile_reader(const MemoryType &mem, size_t offset = 0)
+                :buf(mem.data(), mem.size()),
+                 pos(buf.begin() + offset) {
         }
 
         template<typename T>
         std::enable_if_t<std::is_arithmetic_v<T>, T>
         read_value() {
-            assert(cur_pos + sizeof(T) <= cur_length);
+            assert(pos + sizeof(T) <= buf.end());
             T tmp_val;
-            std::memcpy(&tmp_val, cur_data(), sizeof(T));
+            std::copy_n(pos, sizeof(T), (char *) &tmp_val);
             swap_net_loc_endian<net_order>(tmp_val);
-            cur_pos += sizeof(T);
+            pos += sizeof(T);
             return tmp_val;
         }
 
@@ -163,10 +180,11 @@ namespace sophiar {
             }
         }
 
-        std::string read_string(size_t read_length) {
-            assert(cur_pos + read_length <= cur_length);
-            auto ret = std::string{cur_data(), cur_data() + read_length};
-            cur_pos += read_length;
+        std::string_view read_string(size_t read_length) {
+            auto end_pos = pos + read_length;
+            assert(end_pos <= buf.end());
+            auto ret = std::string_view{pos, end_pos};
+            pos = end_pos;
             return ret;
         }
 
@@ -174,14 +192,13 @@ namespace sophiar {
             str = read_string(length);
         }
 
-        std::string read_string_until(char sep) {
-            auto start_pos = cur_pos;
-            while (cur_pos != cur_length && *cur_data() != sep) {
-                ++cur_pos;
-            }
-            // solve char * vs const char * problem
-            const char *start_ptr = buffer.data() + start_pos;
-            return std::string{start_ptr, cur_data()};
+        std::string_view read_string_until(char sep) {
+            auto end_pos = pos;
+            while (*end_pos != sep && end_pos != buf.end()) ++end_pos;
+            auto ret = std::string_view{pos, end_pos};
+            pos = end_pos;
+            if (pos != buf.end()) ++pos; // skip the separator
+            return ret;
         }
 
         void read_string_until(std::string &str, char sep) {
@@ -196,82 +213,50 @@ namespace sophiar {
 
         // 从当前位置开始调整 offset
         void manual_offset(ptrdiff_t offset) {
-            assert(offset < 0 && cur_pos >= -offset);
-            assert(offset > 0 && cur_pos + offset <= cur_length);
-            cur_pos += offset;
-        }
-
-        const buffer_type &get_buffer() const {
-            return buffer;
-        }
-
-        size_t get_cur_pos() const {
-            return cur_pos;
+            pos += offset;
+            assert(pos >= buf.begin());
+            assert(pos <= buf.end());
         }
 
-        const char *cur_data() const {
-            return buffer.data() + cur_pos;
-        }
-
-        // 一次性写入数据,原先的数据会直接被覆盖
-        template<typename AsyncReadStream>
-        boost::asio::awaitable<void> async_read_from(AsyncReadStream &s, size_t read_length) {
-            assert(empty());
-            assert(read_length <= buffer.max_length());
-
-            co_await boost::asio::async_read(s,
-                                             boost::asio::buffer(buffer.data(), read_length),
-                                             boost::asio::use_awaitable);
-            cur_length = read_length;
-            cur_pos = 0;
-            co_return;
+        auto current_offset() const {
+            return pos - buf.begin();
         }
 
         bool empty() const {
-            return cur_pos == cur_length;
-        }
-
-        explicit operator bool() const {
-            return !empty();
+            return pos == buf.end();
         }
 
     private:
-        buffer_type buffer;
-        size_t cur_pos, cur_length;
-
+        using buffer_type = std::span<const char>;
+        using iterator_type = buffer_type::iterator;
+        buffer_type buf;
+        iterator_type pos;
     };
 
-    // 分多次写入数据,一次性读取
-    template<boost::endian::order net_order = boost::endian::order::big,
-            FixedLengthMemory buffer_type = static_memory<VERSATILE_BUFFER_DEFAULT_SIZE>>
-    class versatile_writable_buffer {
+    // 分多次写入数据
+    template<boost::endian::order net_order>
+    class versatile_writer {
     public:
 
-        template<typename other_buffer_type = buffer_type,
-                typename  = std::enable_if_t<std::is_default_constructible_v<other_buffer_type>>>
-        versatile_writable_buffer() {
-            cur_pos = 0;
-        }
-
-        explicit versatile_writable_buffer(buffer_type &&other_buffer,
-                                           size_t initial_pos = 0)
-                : buffer(std::move(other_buffer)) {
-            cur_pos = initial_pos;
+        template<WriteableMemory MemoryType>
+        explicit versatile_writer(MemoryType &mem, size_t offset = 0)
+                :buf(mem.data(), mem.size()),
+                 pos(buf.begin() + offset) {
         }
 
         template<typename T>
         std::enable_if_t<std::is_arithmetic_v<T>>
         write_value(T val) {
-            assert(cur_pos + sizeof(T) <= buffer.max_length());
+            assert(pos + sizeof(T) <= buf.end());
             swap_net_loc_endian<net_order>(val);
-            std::memcpy(cur_data(), &val, sizeof(T));
-            cur_pos += sizeof(T);
+            std::copy_n((char *) &val, sizeof(T), pos);
+            pos += sizeof(T);
         }
 
-        void write_value(const std::string &str) {
-            assert(cur_pos + str.length() <= buffer.max_length());
-            std::memcpy(cur_data(), str.data(), str.length());
-            cur_pos += str.length();
+        void write_value(std::string_view str) {
+            assert(pos + str.length() <= buf.end());
+            std::copy(str.begin(), str.end(), pos);
+            pos += str.length();
         }
 
         template<typename Derived>
@@ -296,47 +281,119 @@ namespace sophiar {
 
         // 从当前位置开始调整 offset
         void manual_offset(ptrdiff_t offset) {
-            assert(offset > 0 || cur_pos >= -offset);
-            assert(offset < 0 || cur_pos + offset <= buffer.max_length());
-            cur_pos += offset;
+            pos += offset;
+            assert(pos >= buf.begin());
+            assert(pos <= buf.end());
         }
 
-        const buffer_type &get_buffer() const {
-            return buffer;
+        auto current_offset() const {
+            return pos - buf.begin();
         }
 
-        size_t get_cur_pos() const {
-            return cur_pos;
+        auto remaining_bytes() const {
+            return buf.end() - pos;
         }
 
-        char *cur_data() {
-            return buffer.data() + cur_pos;
+    private:
+        using buffer_type = std::span<char>;
+        using iterator_type = buffer_type::iterator;
+        buffer_type buf;
+        iterator_type pos;
+    };
+
+    template<boost::endian::order net_order>
+    class dynamic_memory_writer {
+    public:
+
+        explicit dynamic_memory_writer(dynamic_memory *mem,
+                                       size_t offset = 0)
+                : buf(mem) {
+            buf->resize(offset);
+        }
+
+        template<typename T>
+        std::enable_if_t<std::is_arithmetic_v<T>>
+        write_value(T val) {
+            swap_net_loc_endian<net_order>(val);
+            auto ptr = end_ptr();
+            buf->increase_size(sizeof(T));
+            memcpy(ptr, &val, sizeof(T));
         }
 
-        template<typename AsyncWriteStream>
-        boost::asio::awaitable<void> async_write_to(AsyncWriteStream &s) {
-            co_await boost::asio::async_write(s,
-                                              boost::asio::buffer(buffer.data(), cur_pos),
-                                              boost::asio::use_awaitable);
-            cur_pos = 0;
-            co_return;
+        void write_value(std::string_view str) {
+            auto ptr = end_ptr();
+            buf->increase_size(str.length());
+            std::copy(str.begin(), str.end(), ptr);
         }
 
-        bool empty() const {
-            return cur_pos == 0;
+        template<typename Derived>
+        void write_value(const Eigen::MatrixBase<Derived> &matrix) {
+            for (Eigen::Index i = 0; i < matrix.size(); ++i) {
+                write_value(matrix(i));
+            }
+        }
+
+        template<typename T, size_t Length>
+        void write_value(const std::array<T, Length> &arr) {
+            for (auto val: arr) {
+                write_value(val);
+            }
         }
 
-        explicit operator bool() const {
-            return !empty();
+        template<typename T>
+        auto &operator<<(const T &val) {
+            write_value(val);
+            return *this;
         }
 
     private:
-        buffer_type buffer;
-        size_t cur_pos;
+        dynamic_memory *buf;
 
+        char *end_ptr() {
+            return buf->data() + buf->size();
+        }
     };
 
-    template<boost::endian::order net_order = boost::endian::order::big,
+    template<boost::endian::order net_order,
+            typename T>
+    std::enable_if_t<std::is_arithmetic_v<T>, T>
+    read_binary_value(const char *data) {
+        T tmp_val;
+        memcpy(&tmp_val, data, sizeof(T));
+        swap_net_loc_endian<net_order>(tmp_val);
+        return tmp_val;
+    }
+
+    template<boost::endian::order net_order,
+            typename T>
+    std::enable_if_t<std::is_arithmetic_v<T>>
+    read_binary_value(const char *data, T &val) {
+        val = read_binary_value<net_order>(data);
+    }
+
+    template<boost::endian::order net_order,
+            typename T>
+    std::enable_if_t<std::is_arithmetic_v<T>>
+    write_binary_value(char *data, T val) {
+        swap_net_loc_endian<net_order>(val);
+        memcpy(data, &val, sizeof(T));
+    }
+
+    template<WriteableMemory MemoryType, typename AsyncReadStream>
+    auto async_fill_memory_from(AsyncReadStream &s, MemoryType &mem) {
+        return boost::asio::async_read(s,
+                                       boost::asio::buffer(mem.data(), mem.size()),
+                                       boost::asio::use_awaitable);
+    }
+
+    template<ReadableMemory MemoryType, typename AsyncWriteStream>
+    auto async_write_memory_to(AsyncWriteStream &s, const MemoryType &mem) {
+        return boost::asio::async_write(s,
+                                        boost::asio::buffer(mem.data(), mem.size()),
+                                        boost::asio::use_awaitable);
+    }
+
+    template<boost::endian::order net_order,
             typename AsyncWriteStream, typename T>
     std::enable_if_t<std::is_arithmetic_v<T>, boost::asio::awaitable<void>>
     inline async_write_value(AsyncWriteStream &s, T val) {
@@ -347,7 +404,7 @@ namespace sophiar {
         co_return;
     }
 
-    template<boost::endian::order net_order = boost::endian::order::big,
+    template<boost::endian::order net_order,
             typename T, typename AsyncReadStream>
     std::enable_if_t<std::is_arithmetic_v<T>, boost::asio::awaitable<T>>
     inline async_read_value(AsyncReadStream &s) {
@@ -359,7 +416,7 @@ namespace sophiar {
         co_return tmp_val;
     }
 
-    template<boost::endian::order net_order = boost::endian::order::big,
+    template<boost::endian::order net_order,
             typename T, typename AsyncReadStream>
     std::enable_if_t<std::is_arithmetic_v<T>, boost::asio::awaitable<void>>
     inline async_read_value(AsyncReadStream &s, T &val) {

+ 16 - 14
tests/CMakeLists.txt

@@ -2,34 +2,36 @@ set (Boost_USE_STATIC_LIBS OFF)
 find_package (Boost REQUIRED COMPONENTS unit_test_framework)
 include_directories (${Boost_INCLUDE_DIRS})
 
+file(GLOB_RECURSE TEST_CORE_SRC_FILES ./core/*.cpp)
 add_executable(test_core
-        core/small_obj.cpp
-        core/sophiar_manager.cpp
-        core/transform_tree.cpp
-        core/tristate_obj.cpp
-        ${EXTERN_DEF_FILES}
+        ${TEST_CORE_SRC_FILES}
         ${CORE_IMPL_FILES})
 target_compile_definitions(test_core PUBLIC SOPHIAR_TEST)
 #target_compile_definitions(test_core PUBLIC CORO_SIGNAL2_USE_TIMER)
 target_link_libraries(test_core ${Boost_LIBRARIES} ${EXTRA_LIBS})
 
+file(GLOB_RECURSE TEST_UTILITY_SRC_FILES ./utility/*.cpp)
 add_executable(test_utility
-        utility/coro_signal2.cpp
-        utility/coro_signal_group.cpp
-        utility/coro_worker.cpp
-        utility/global_obj_helper.cpp
-        utility/global_obj_record_playback.cpp
-        ${EXTERN_DEF_FILES}
+        ${TEST_UTILITY_SRC_FILES}
         ${CORE_IMPL_FILES})
 target_compile_definitions(test_utility PUBLIC SOPHIAR_TEST)
 target_compile_definitions(test_utility PUBLIC CORO_SIGNAL2_USE_TIMER)
 target_link_libraries(test_utility ${Boost_LIBRARIES} ${EXTRA_LIBS})
 
+file(GLOB_RECURSE TEST_ALGORITHM_SRC_FILES ./algorithm/*.cpp)
+add_executable(test_algorithm
+        ${TEST_ALGORITHM_SRC_FILES}
+        ../src/algorithm/transform_tree.cpp
+#        ${ALGORITHM_IMPL_FILES}
+        ${CORE_IMPL_FILES})
+target_compile_definitions(test_algorithm PUBLIC SOPHIAR_TEST SOPHIAR_TEST_ALGORITHM)
+target_compile_definitions(test_algorithm PUBLIC CORO_SIGNAL2_USE_TIMER)
+target_link_libraries(test_algorithm ${Boost_LIBRARIES} ${EXTRA_LIBS})
+
 add_executable(test_ndi
-        interface/ndi.cpp
+        ./interface/ndi.cpp
         ../src/tracker/ndi/ndi_interface.cpp
-        ${EXTERN_DEF_FILES}
         ${CORE_IMPL_FILES})
 target_compile_definitions(test_ndi PUBLIC SOPHIAR_TEST)
-target_compile_definitions(test_ndi PUBLIC CORO_SIGNAL2_USE_TIMER)
+#target_compile_definitions(test_ndi PUBLIC CORO_SIGNAL2_USE_TIMER)
 target_link_libraries(test_ndi ${Boost_LIBRARIES} ${EXTRA_LIBS})

+ 12 - 13
tests/core/transform_tree.cpp → tests/algorithm/transform_tree.cpp

@@ -1,8 +1,10 @@
 #define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MAIN  // in only one cpp file
 
-#include "core/transform_tree.h"
+#include "algorithm/transform_tree.h"
 #include "core/basic_obj_types.hpp"
 #include "utility/debug_utility.hpp"
+#include "utility/coro_worker.hpp"
 
 #include <boost/test/unit_test.hpp>
 #include <boost/asio/awaitable.hpp>
@@ -24,30 +26,27 @@ BOOST_AUTO_TEST_CASE(test_transform_tree) {
 
     spdlog::set_level(spdlog::level::trace);
 
-    REGISTER_TYPE(transform_obj_watcher);
-    REGISTER_TYPE(transform_tree);
-
     std::ifstream config_file("data/transform_tree_config.json");
     BOOST_TEST(config_file.is_open());
+    auto config = nlohmann::json::parse(config_file);
+    BOOST_TEST(initialize(config));
 
     using namespace std::chrono_literals;
-    auto c_in_b_index = global_sophiar_manager.register_global_obj<transform_obj>("C_in_B");
-    auto worker_a = make_interval_coro_worker(global_context, 1s, [=, cur_y = int(0)]() mutable -> awaitable<bool> {
+    auto c_in_b_index = REQUIRE_VARIABLE(transform_obj, "C_in_B");
+    auto worker_a = make_interval_coro_worker(1s, [=, cur_y = int(0)]() mutable -> awaitable<bool> {
         auto new_trans = Eigen::Isometry3d(Eigen::Translation3d(0, ++cur_y, 0));
-        UPDATE_GLOBAL_OBJ_VALUE(transform_obj, c_in_b_index, std::move(new_trans));
+        UPDATE_VARIABLE_VAL(transform_obj, c_in_b_index, std::move(new_trans));
         co_return true;
     });
     worker_a->run();
 
-    auto d_in_root_index = global_sophiar_manager.register_global_obj<transform_obj>("D_in_Root");
-    auto worker_b = make_interval_coro_worker(global_context, 2s, [=, cur_z = int(0)]() mutable -> awaitable<bool> {
+    auto d_in_root_index = REQUIRE_VARIABLE(transform_obj, "D_in_Root");
+    auto worker_b = make_interval_coro_worker(2s, [=, cur_z = int(0)]() mutable -> awaitable<bool> {
         auto new_trans = Eigen::Isometry3d(Eigen::Translation3d(0, 0, --cur_z));
-        UPDATE_GLOBAL_OBJ_VALUE(transform_obj, d_in_root_index, std::move(new_trans));
+        UPDATE_VARIABLE_VAL(transform_obj, d_in_root_index, std::move(new_trans));
         co_return true;
     });
     worker_b->run();
 
-    global_sophiar_manager.load_config_and_start(nlohmann::json::parse(config_file));
-
-    global_context.run();
+    global_context->run();
 }

+ 0 - 48
tests/core/geometry_types.cpp

@@ -1,48 +0,0 @@
-#define BOOST_TEST_DYN_LINK
-
-#include <boost/test/unit_test.hpp>
-
-#include "core/types/geometry_types.hpp"
-
-using namespace sophiar;
-
-BOOST_AUTO_TEST_CASE(test_geometry_types) {
-
-    versatile_data test_data;
-
-    coordinate_pose pose;
-    pose.view_as_base() = Eigen::Translation3d(10, 20, 30) *
-                          Eigen::AngleAxisd(0.5, Eigen::Vector3d::UnitX());
-    test_data << pose;
-    coordinate_pose other_pose;
-    other_pose.setIdentity();
-    test_data >> other_pose;
-    BOOST_TEST(pose.isApprox(other_pose));
-
-    coordinate_pose_compact compact_pose;
-    compact_pose.view_as_base() = Eigen::Translation3d(10, 20, 30) *
-                                  Eigen::AngleAxisd(0.7, Eigen::Vector3d::UnitX());
-    test_data << compact_pose;
-    coordinate_pose_compact other_compact_pose;
-    other_compact_pose.setIdentity();
-    test_data >> other_compact_pose;
-    BOOST_TEST(compact_pose.isApprox(other_compact_pose));
-
-    coordinate_twist twist;
-    twist.angular_part() = Eigen::Vector3d{1, 2, 3};
-    twist.linear_part() = Eigen::Vector3d{4, 5, 6};
-    test_data << twist;
-    coordinate_twist other_twist;
-    other_twist.setZero();
-    test_data >> other_twist;
-    BOOST_TEST(twist.isApprox(other_twist));
-
-    auto some_twist = coordinate_twist::from_rot_axis({0, 0, 1}, {1, 0, 0});
-    BOOST_TEST(some_twist.isApprox(Eigen::Vector<double, 6>{0, 0, 1, 0, -1, 0}));
-    some_twist = coordinate_twist::from_rot_axis({0, 1, 0}, {1, 0, 0});
-    BOOST_TEST(some_twist.isApprox(Eigen::Vector<double, 6>{0, 1, 0, 0, 0, 1}));
-
-    Eigen::Isometry3d some_trans(Eigen::Translation3d(-1, 0, 0));
-    auto new_twist = some_trans * some_twist;
-    BOOST_TEST(new_twist.isApprox(Eigen::Vector<double, 6>{0, 1, 0, 0, 0, 0}));
-}

+ 54 - 66
tests/core/sophiar_manager.cpp

@@ -3,9 +3,11 @@
 #include "core/sophiar_manager.h"
 #include "core/tristate_obj.h"
 #include "core/basic_obj_types.hpp"
+#include "core/external_controller.h"
+#include "utility/config_utility.hpp"
 #include "utility/coro_signal_group.hpp"
 #include "utility/coro_worker.hpp"
-#include "utility/global_obj_helper.hpp"
+#include "utility/variable_helper.hpp"
 
 #include <boost/asio/co_spawn.hpp>
 #include <boost/asio/detached.hpp>
@@ -31,34 +33,29 @@ using namespace std::chrono_literals;
 struct source_node_type : public tristate_obj {
     DEFAULT_NEW_INSTANCE(source_node_type)
 
-    global_obj_index_type output_obj_index;
+    variable_index_type output_var_index;
     coro_worker::pointer source_worker;
 
-    boost::asio::awaitable<bool> on_init(const nlohmann::json &config) override {
-        output_obj_index = get_manager().
-                register_global_obj<u64int_obj>(
-                config["output_obj_name"].get<std::string>());
+    boost::asio::awaitable<bool> on_init(const nlohmann::json &config) noexcept override {
+        output_var_index = LOAD_VARIABLE_INDEX(u64int_obj, "output_var_name");
         co_return true;
     }
 
-    boost::asio::awaitable<bool> on_start(const nlohmann::json &config) override {
+    boost::asio::awaitable<bool> on_start(const nlohmann::json &config) noexcept override {
         source_worker = make_interval_coro_worker(
-                get_context(), std::chrono::seconds(1), [
-                        output_obj = global_obj_auto_sync_delegate<u64int_obj>(
-                                get_manager(), output_obj_index),
-                        start_value = config["start_value"].get<std::uint64_t>()]() mutable
+                std::chrono::seconds(1), [
+                        output_var = VARIABLE_AUTO_DELEGATE(u64int_obj, output_var_index),
+                        start_value = LOAD_UINT_ITEM("start_value")]() mutable
                         -> boost::asio::awaitable<bool> {
-                    auto new_out = u64int_obj::new_instance(start_value);
-                    output_obj.set_value(new_out);
-                    SPDLOG_WARN("New value from source {}", new_out->value);
-                    ++start_value;
+                    output_var = start_value++;
+                    SPDLOG_WARN("New value from source {}", output_var->value);
                     co_return true;
                 });
         source_worker->run();
         co_return true;
     }
 
-    boost::asio::awaitable<void> on_stop() override {
+    boost::asio::awaitable<void> on_stop() noexcept override {
         source_worker->cancel();
         co_await source_worker->coro_wait_stop();
         source_worker.reset();
@@ -70,41 +67,33 @@ struct source_node_type : public tristate_obj {
 struct proxy_node_type : public tristate_obj {
     DEFAULT_NEW_INSTANCE(proxy_node_type);
 
-    global_obj_index_type input_obj_index;
-    global_obj_index_type output_obj_index;
+    variable_index_type input_var_index;
+    variable_index_type output_var_index;
     coro_worker::pointer proxy_worker;
 
-    boost::asio::awaitable<bool> on_init(const nlohmann::json &config) override {
-        input_obj_index = get_manager().
-                register_global_obj<u64int_obj>(
-                config["input_obj_name"].get<std::string>());
-        output_obj_index = get_manager().
-                register_global_obj<u64int_obj>(
-                config["output_obj_name"].get<std::string>());
+    boost::asio::awaitable<bool> on_init(const nlohmann::json &config) noexcept override {
+        input_var_index = LOAD_VARIABLE_INDEX(u64int_obj, "input_var_name");
+        output_var_index = LOAD_VARIABLE_INDEX(u64int_obj, "output_var_name");
         co_return true;
     }
 
-    boost::asio::awaitable<bool> on_start(const nlohmann::json &config) override {
+    boost::asio::awaitable<bool> on_start(const nlohmann::json &config) noexcept override {
         proxy_worker = make_infinite_coro_worker(
-                get_context(), [
-                        input_obj = global_obj_auto_sync_delegate<u64int_obj>(
-                                get_manager(), input_obj_index),
-                        output_obj = global_obj_auto_sync_delegate<u64int_obj>(
-                                get_manager(), output_obj_index),
-                        start_value = config["start_value"].get<std::uint64_t>()]() mutable
+                [
+                        input_var = VARIABLE_AUTO_DELEGATE(u64int_obj, input_var_index),
+                        output_var = VARIABLE_AUTO_DELEGATE(u64int_obj, output_var_index),
+                        start_value = LOAD_UINT_ITEM("start_value")]() mutable
                         -> boost::asio::awaitable<bool> {
-                    co_await input_obj.coro_wait_update();
-                    auto new_out = u64int_obj::new_instance(input_obj->value + start_value);
-                    output_obj.set_value(new_out);
-                    SPDLOG_WARN("New value from proxy {}", new_out->value);
-                    ++start_value;
+                    co_await input_var.coro_wait_update();
+                    output_var = input_var->value + (start_value++);
+                    SPDLOG_WARN("New value from proxy {}", output_var->value);
                     co_return true;
                 });
         proxy_worker->run();
         co_return true;
     }
 
-    boost::asio::awaitable<void> on_stop() override {
+    boost::asio::awaitable<void> on_stop() noexcept override {
         proxy_worker->cancel();
         co_await proxy_worker->coro_wait_stop();
         proxy_worker.reset();
@@ -116,43 +105,39 @@ struct proxy_node_type : public tristate_obj {
 struct target_node_type : public tristate_obj {
     DEFAULT_NEW_INSTANCE(target_node_type);
 
-    global_obj_index_type source_obj_index;
-    global_obj_index_type proxy_obj_index;
+    variable_index_type source_var_index;
+    variable_index_type proxy_var_index;
     coro_worker::pointer target_worker;
     coro_signal_any_group::pointer watch_group;
 
-    boost::asio::awaitable<bool> on_init(const nlohmann::json &config) override {
-        source_obj_index = get_manager().
-                register_global_obj<u64int_obj>(
-                config["source_obj_name"].get<std::string>());
-        proxy_obj_index = get_manager().
-                register_global_obj<u64int_obj>(
-                config["proxy_obj_name"].get<std::string>());
+    boost::asio::awaitable<bool> on_init(const nlohmann::json &config) noexcept override {
+        source_var_index = LOAD_VARIABLE_INDEX(u64int_obj, "source_var_name");
+        proxy_var_index = LOAD_VARIABLE_INDEX(u64int_obj, "proxy_var_name");
         co_return true;
     }
 
-    boost::asio::awaitable<bool> on_start(const nlohmann::json &config) override {
-        watch_group = std::make_unique<coro_signal_any_group>(get_manager());
-        watch_group->add_watcher(get_manager().request_global_obj_update_watcher(source_obj_index));
-        watch_group->add_watcher(get_manager().request_global_obj_update_watcher(proxy_obj_index));
-        watch_group->start(get_context());
+    boost::asio::awaitable<bool> on_start(const nlohmann::json &config) noexcept override {
+        watch_group = std::make_unique<coro_signal_any_group>();
+        watch_group->add_watcher(REQUIRE_VARIABLE_WATCHER(source_var_index));
+        watch_group->add_watcher(REQUIRE_VARIABLE_WATCHER(proxy_var_index));
+        watch_group->start();
         target_worker = make_infinite_coro_worker(
-                get_context(), [
-                        watcher = watch_group->new_watcher(get_context()),
-                        source_obj = global_obj_auto_sync_delegate<u64int_obj>(
-                                get_manager(), source_obj_index),
-                        proxy_obj = global_obj_auto_sync_delegate<u64int_obj>(
-                                get_manager(), proxy_obj_index)]() mutable
+                [
+                        watcher = watch_group->new_watcher(),
+                        source_var = VARIABLE_AUTO_DELEGATE(u64int_obj, source_var_index),
+                        proxy_var = VARIABLE_AUTO_DELEGATE(u64int_obj, proxy_var_index)]() mutable
                         -> boost::asio::awaitable<bool> {
                     co_await watcher.coro_wait();
-                    SPDLOG_ERROR("source: {}, proxy: {}", source_obj->value, proxy_obj->value);
+                    SPDLOG_ERROR("source: {}, proxy: {}",
+                                 source_var.empty() ? 0 : source_var->value,
+                                 proxy_var.empty() ? 0 : proxy_var->value);
                     co_return true;
                 });
         target_worker->run();
         co_return true;
     }
 
-    boost::asio::awaitable<void> on_stop() override {
+    boost::asio::awaitable<void> on_stop() noexcept override {
         co_await watch_group->stop();
         target_worker->cancel();
         co_await target_worker->coro_wait_stop();
@@ -166,15 +151,18 @@ BOOST_AUTO_TEST_CASE(test_sophiar_manager) {
 
     spdlog::set_level(spdlog::level::trace);
 
-    REGISTER_TYPE(source_node_type);
-    REGISTER_TYPE(proxy_node_type);
-    REGISTER_TYPE(target_node_type);
-
     std::ifstream config_file("data/sophiar_manager_config.json");
     BOOST_TEST(config_file.is_open());
+    auto config = nlohmann::json::parse(config_file);
+    initialize({});
 
-    global_sophiar_manager.load_config_and_start(nlohmann::json::parse(config_file));
+    REGISTER_TYPE(source_node_type);
+    REGISTER_TYPE(proxy_node_type);
+    REGISTER_TYPE(target_node_type);
 
-    global_context.run();
+    global_sophiar_pool->load_config(config);
+    global_sophiar_manager->load_config_and_start(config);
+    global_external_controller->load_config_and_start(config);
+    global_context->run();
 
 }

+ 19 - 18
tests/core/tristate_obj.cpp

@@ -25,22 +25,22 @@ awaitable<void> test_tristate_obj_1() {
     struct type_a : public tristate_obj {
         unsigned int cnt = 0;
 
-        awaitable<bool> on_init(const nlohmann::json &) {
+        awaitable<bool> on_init(const nlohmann::json &) noexcept {
             set_bit(cnt, 0);
             co_return true;
         }
 
-        awaitable<bool> on_start(const nlohmann::json &) {
+        awaitable<bool> on_start(const nlohmann::json &) noexcept {
             set_bit(cnt, 1);
             co_return true;
         }
 
-        awaitable<void> on_stop() {
+        awaitable<void> on_stop() noexcept {
             set_bit(cnt, 2);
             co_return;
         }
 
-        awaitable<void> on_reset() {
+        awaitable<void> on_reset() noexcept {
             set_bit(cnt, 3);
             co_return;
         }
@@ -80,30 +80,30 @@ awaitable<void> test_tristate_obj_2() {
         boost::asio::high_resolution_timer timer;
 
         type_a()
-                : timer(get_context()) {}
+                : timer(*global_context) {}
 
-        awaitable<bool> on_init(const nlohmann::json &) {
+        awaitable<bool> on_init(const nlohmann::json &) noexcept {
             timer.expires_from_now(100ms);
             co_await timer.async_wait(use_awaitable);
             set_bit(cnt, 0);
             co_return true;
         }
 
-        awaitable<bool> on_start(const nlohmann::json &) {
+        awaitable<bool> on_start(const nlohmann::json &) noexcept {
             timer.expires_from_now(100ms);
             co_await timer.async_wait(use_awaitable);
             set_bit(cnt, 1);
             co_return true;
         }
 
-        awaitable<void> on_stop() {
+        awaitable<void> on_stop() noexcept {
             timer.expires_from_now(100ms);
             co_await timer.async_wait(use_awaitable);
             set_bit(cnt, 2);
             co_return;
         }
 
-        awaitable<void> on_reset() {
+        awaitable<void> on_reset() noexcept {
             timer.expires_from_now(100ms);
             co_await timer.async_wait(use_awaitable);
             set_bit(cnt, 3);
@@ -155,30 +155,30 @@ awaitable<void> test_tristate_obj_3() {
         boost::asio::high_resolution_timer timer;
 
         type_a()
-                : timer(get_context()) {}
+                : timer(*global_context) {}
 
-        awaitable<bool> on_init(const nlohmann::json &) {
+        awaitable<bool> on_init(const nlohmann::json &) noexcept {
             timer.expires_from_now(100ms);
             co_await timer.async_wait(use_awaitable);
             ++cnt;
             co_return true;
         }
 
-        awaitable<bool> on_start(const nlohmann::json &) {
+        awaitable<bool> on_start(const nlohmann::json &) noexcept {
             timer.expires_from_now(100ms);
             co_await timer.async_wait(use_awaitable);
             ++cnt;
             co_return true;
         }
 
-        awaitable<void> on_stop() {
+        awaitable<void> on_stop() noexcept {
             timer.expires_from_now(100ms);
             co_await timer.async_wait(use_awaitable);
             ++cnt;
             co_return;
         }
 
-        awaitable<void> on_reset() {
+        awaitable<void> on_reset() noexcept {
             timer.expires_from_now(100ms);
             co_await timer.async_wait(use_awaitable);
             ++cnt;
@@ -253,8 +253,9 @@ awaitable<void> test_tristate_obj_3() {
 }
 
 BOOST_AUTO_TEST_CASE(test_tristate_obj) {
-    co_spawn(global_context, test_tristate_obj_1(), detached);
-    co_spawn(global_context, test_tristate_obj_2(), detached);
-    co_spawn(global_context, test_tristate_obj_3(), detached);
-    global_context.run();
+    initialize({});
+    co_spawn(*global_context, test_tristate_obj_1(), detached);
+    co_spawn(*global_context, test_tristate_obj_2(), detached);
+    co_spawn(*global_context, test_tristate_obj_3(), detached);
+    global_context->run();
 }

+ 0 - 155
tests/data/datanode_base_config.json

@@ -1,155 +0,0 @@
-{
-  "mode_list": [
-    {
-      "name": "mode_a"
-    },
-    {
-      "name": "mode_b"
-    },
-    {
-      "name": "mode_c"
-    },
-    {
-      "name": "mode_d"
-    }
-  ],
-  "object_list": [
-    {
-      "type": "trigger_node_type",
-      "name": "trigger_node",
-      "enabled_modes": "all",
-      "construct_config": {}
-    },
-    {
-      "type": "update_node_type",
-      "name": "update_node",
-      "enabled_modes": [
-        "mode_b"
-      ],
-      "construct_config": {}
-    },
-    {
-      "type": "cnt_node_type",
-      "name": "cnt_node",
-      "enabled_modes": [
-        "mode_a",
-        "mode_b",
-        "mode_c"
-      ],
-      "construct_config": {},
-      "init_configs": [],
-      "start_configs": [
-        {
-          "modes": [
-            "mode_a"
-          ],
-          "config": {
-            "trigger_mode": "manual",
-            "minimal_exec_interval_ms": 10
-          }
-        },
-        {
-          "modes": [
-            "mode_b"
-          ],
-          "config": {
-            "trigger_mode": "input",
-            "input_mask": 1,
-            "minimal_exec_interval_ms": 10
-          }
-        },
-        {
-          "modes": [
-            "mode_c"
-          ],
-          "config": {
-            "trigger_mode": "periodic",
-            "exec_interval_ms": 50
-          }
-        }
-      ]
-    },
-    {
-      "type": "test_node_type",
-      "name": "test_node_a",
-      "enabled_modes": [
-        "mode_d"
-      ],
-      "construct_config": {},
-      "init_configs": [],
-      "start_configs": [
-        {
-          "modes": [
-            "mode_d"
-          ],
-          "config": {
-            "trigger_mode": "input",
-            "input_mask": 1
-          }
-        }
-      ]
-    },
-    {
-      "type": "test_node_type",
-      "name": "test_node_b",
-      "enabled_modes": [
-        "mode_d"
-      ],
-      "construct_config": {},
-      "init_configs": [],
-      "start_configs": [
-        {
-          "modes": [
-            "mode_d"
-          ],
-          "config": {
-            "trigger_mode": "input",
-            "input_mask": 1
-          }
-        }
-      ]
-    }
-  ],
-  "connection_list": [
-    {
-      "modes": [
-        "mode_a",
-        "mode_b"
-      ],
-      "connections": [
-        {
-          "signal_object": "trigger_node",
-          "signal_name": "triggered",
-          "slot_object": "cnt_node",
-          "slot_name": "trigger"
-        }
-      ]
-    },
-    {
-      "modes": [
-        "mode_b"
-      ],
-      "connections": [
-        {
-          "signal_object": "update_node",
-          "signal_name": "triggered",
-          "slot_object": "cnt_node",
-          "slot_name": "input_1"
-        }
-      ]
-    },
-    {
-      "modes": [
-        "mode_d"
-      ],
-      "connections": [
-        {
-          "signal_object": "test_node_a",
-          "signal_name": "output_1",
-          "slot_object": "test_node_b",
-          "slot_name": "input_1"
-        }
-      ]
-    }
-  ]
-}

+ 16 - 6
tests/data/sophiar_manager_config.json

@@ -1,11 +1,21 @@
 {
-  "listen_port": 5277,
+  "controller_port": 5277,
+  "variable_list": [
+    {
+      "name": "out_var",
+      "type": "u64int_obj"
+    },
+    {
+      "name": "proxy_var",
+      "type": "u64int_obj"
+    }
+  ],
   "object_list": [
     {
       "type": "source_node_type",
       "name": "source",
       "init_config": {
-        "output_obj_name": "out_obj"
+        "output_var_name": "out_var"
       },
       "start_config": {
         "start_value": 234
@@ -15,8 +25,8 @@
       "type": "proxy_node_type",
       "name": "proxy",
       "init_config": {
-        "input_obj_name": "out_obj",
-        "output_obj_name": "proxy_obj"
+        "input_var_name": "out_var",
+        "output_var_name": "proxy_var"
       },
       "start_config": {
         "start_value": 10000
@@ -29,8 +39,8 @@
       "type": "target_node_type",
       "name": "target",
       "init_config": {
-        "source_obj_name": "out_obj",
-        "proxy_obj_name": "proxy_obj"
+        "source_var_name": "out_var",
+        "proxy_var_name": "proxy_var"
       },
       "start_config": {}
     }

+ 19 - 5
tests/data/transform_tree_config.json

@@ -1,5 +1,19 @@
 {
-  "listen_port": 5277,
+  "controller_port": 5277,
+  "variable_list": [
+    {
+      "name": "C_in_B",
+      "type": "transform_obj"
+    },
+    {
+      "name": "C_in_D",
+      "type": "transform_obj"
+    },
+    {
+      "name": "D_in_Root",
+      "type": "transform_obj"
+    }
+  ],
   "object_list": [
     {
       "type": "transform_tree",
@@ -25,12 +39,12 @@
           {
             "name": "C",
             "parent": "B",
-            "transform_obj_name": "C_in_B"
+            "transform_var_name": "C_in_B"
           },
           {
             "name": "D",
             "parent": "Root",
-            "transform_obj_name": "D_in_Root"
+            "transform_var_name": "D_in_Root"
           }
         ]
       },
@@ -39,7 +53,7 @@
           {
             "target": "C",
             "observer": "D",
-            "transform_name": "C_in_D"
+            "transform_var_name": "C_in_D"
           }
         ]
       }
@@ -48,7 +62,7 @@
       "type": "transform_obj_watcher",
       "name": "transform_watcher",
       "start_config": {
-        "obj_name": "C_in_D"
+        "variable_name": "C_in_D"
       }
     }
   ]

+ 1 - 2
tests/interface/ndi.cpp

@@ -2,8 +2,7 @@
 #define BOOST_TEST_MAIN  // in only one cpp file
 
 #include "tracker/ndi/ndi_interface.h"
-#include "utility/debug_utility.hpp"
-#include "utility/global_obj_helper.hpp"
+#include "utility/variable_helper.hpp"
 #include "core/basic_obj_types.hpp"
 
 #include <boost/asio/co_spawn.hpp>

+ 19 - 15
tests/utility/coro_signal2.cpp

@@ -1,8 +1,8 @@
 #define BOOST_TEST_DYN_LINK
 #define BOOST_TEST_MAIN  // in only one cpp file
 
-#include "utility/coro_signal2.hpp"
 #include "core/sophiar_obj.hpp"
+#include "utility/coro_signal2.hpp"
 #include "utility/debug_utility.hpp"
 
 #include <boost/asio/co_spawn.hpp>
@@ -23,30 +23,30 @@ using boost::asio::use_awaitable;
 using namespace sophiar;
 using namespace std::chrono_literals;
 
-coro_signal2 sig_a{global_context}, sig_b{global_context};
+coro_signal2 *sig_a, *sig_b;
 
 awaitable<void> coro_a() {
-    auto watcher_b = sig_b.new_watcher(global_context);
+    auto watcher_b = sig_b->new_watcher();
     FILE_LINE_TRACE
     for (int i = 0; i < 1000; ++i) {
-        sig_a.try_notify_all();
+        sig_a->try_notify_all();
         co_await watcher_b.coro_wait();
     }
     FILE_LINE_TRACE
-    sig_a.try_notify_all();
+    sig_a->try_notify_all();
     co_return;
 }
 
 awaitable<void> coro_b() {
     co_await coro_sleep(10ms);
-    auto watcher_a = sig_a.new_watcher(global_context);
+    auto watcher_a = sig_a->new_watcher();
     FILE_LINE_TRACE
     for (int i = 0; i < 1000; ++i) {
-        sig_b.try_notify_all();
+        sig_b->try_notify_all();
         co_await watcher_a.coro_wait();
     }
     FILE_LINE_TRACE
-    sig_b.try_notify_all();
+    sig_b->try_notify_all();
     co_return;
 }
 
@@ -55,12 +55,12 @@ awaitable<void> coro_signal2_bench() {
     std::vector<std::unique_ptr<coro_signal2>> sig_pool;
     std::vector<signal_watcher> watcher_pool;
     for (int i = 0; i < length; ++i) {
-        auto new_sig = std::make_unique<coro_signal2>(global_context);
-        watcher_pool.push_back(new_sig->new_watcher(global_context));
+        auto new_sig = std::make_unique<coro_signal2>();
+        watcher_pool.push_back(new_sig->new_watcher());
         sig_pool.push_back(std::move(new_sig));
     }
     for (int i = 0; i < length - 1; ++i) {
-        co_spawn(global_context, [&]() -> awaitable<void> {
+        co_spawn(*global_context, [&]() -> awaitable<void> {
             int index = i;
             co_await watcher_pool[index].coro_wait();
             sig_pool[index + 1]->try_notify_all();
@@ -75,8 +75,12 @@ awaitable<void> coro_signal2_bench() {
 }
 
 BOOST_AUTO_TEST_CASE(test_coro_signal2) {
-//    co_spawn(global_context, coro_a(), detached);
-//    co_spawn(global_context, coro_b(), detached);
-    co_spawn(global_context, coro_signal2_bench(), detached);
-    global_context.run();
+    initialize({});
+
+    sig_a = new coro_signal2{};
+    sig_b = new coro_signal2{};
+//    co_spawn(*global_context, coro_a(), detached);
+//    co_spawn(*global_context, coro_b(), detached);
+    co_spawn(*global_context, coro_signal2_bench(), detached);
+    global_context->run();
 }

+ 29 - 26
tests/utility/coro_signal_group.cpp

@@ -23,39 +23,40 @@ using namespace sophiar;
 using namespace std::chrono_literals;
 
 awaitable<void> test_coro_signal_group_1() {
-    coro_signal2 sig_a(global_context);
-    coro_signal2 sig_b(global_context);
+    coro_signal2 sig_a, sig_b;
 
-    coro_signal_any_group group_any(global_context);
-    coro_signal_all_group group_all(global_context);
+    coro_signal_any_group group_any;
+    coro_signal_all_group group_all;
 
-    group_any.add_watcher(sig_a.new_watcher(global_context));
-    group_any.add_watcher(sig_b.new_watcher(global_context));
-    group_all.add_watcher(sig_a.new_watcher(global_context));
-    group_all.add_watcher(sig_b.new_watcher(global_context));
+    group_any.add_watcher(sig_a.new_watcher());
+    group_any.add_watcher(sig_b.new_watcher());
+    group_all.add_watcher(sig_a.new_watcher());
+    group_all.add_watcher(sig_b.new_watcher());
 
     int cnt_any = 0, cnt_all = 0;
 
-    auto worker_any = make_infinite_coro_worker(global_context, [
-            &cnt_any,
-            watcher = group_any.new_watcher(global_context)]() mutable
-            -> awaitable<bool> {
-        co_await watcher.coro_wait(false);
-        ++cnt_any;
-    });
-    auto worker_all = make_infinite_coro_worker(global_context, [
-            &cnt_all,
-            watcher = group_all.new_watcher(global_context)]() mutable
-            -> awaitable<bool> {
-        co_await watcher.coro_wait(false);
-        ++cnt_all;
-    });
+    auto worker_any = make_infinite_coro_worker(
+            [&,
+                    watcher = group_any.new_watcher()]() mutable
+                    -> awaitable<bool> {
+                co_await watcher.coro_wait(false);
+                ++cnt_any;
+                co_return true;
+            });
+    auto worker_all = make_infinite_coro_worker(
+            [&,
+                    watcher = group_all.new_watcher()]() mutable
+                    -> awaitable<bool> {
+                co_await watcher.coro_wait(false);
+                ++cnt_all;
+                co_return true;
+            });
 
     worker_any->run();
     worker_all->run();
 
-    group_any.start(global_context);
-    group_all.start(global_context);
+    group_any.start();
+    group_all.start();
 
     sig_a.try_notify_all();
     co_await coro_sleep(10ms);
@@ -84,6 +85,8 @@ awaitable<void> test_coro_signal_group_1() {
 }
 
 BOOST_AUTO_TEST_CASE(test_coro_signal_group) {
-    co_spawn(global_context, test_coro_signal_group_1(), detached);
-    global_context.run();
+    initialize({});
+
+    co_spawn(*global_context, test_coro_signal_group_1(), detached);
+    global_context->run();
 }

+ 15 - 13
tests/utility/coro_worker.cpp

@@ -24,7 +24,7 @@ using namespace std::chrono_literals;
 
 awaitable<void> test_coro_worker_1() {
     int cnt = 0;
-    auto worker = make_infinite_coro_worker(global_context, [&]() -> awaitable<bool> {
+    auto worker = make_infinite_coro_worker([&]() -> awaitable<bool> {
         co_await coro_sleep(50ms);
         ++cnt;
         co_return true;
@@ -39,7 +39,7 @@ awaitable<void> test_coro_worker_1() {
 
 awaitable<void> test_coro_worker_2() {
     int cnt = 0;
-    auto worker = make_infinite_coro_worker(global_context, [&]() -> awaitable<bool> {
+    auto worker = make_infinite_coro_worker([&]() -> awaitable<bool> {
         co_await coro_sleep(50ms);
         ++cnt;
         co_return false;
@@ -54,7 +54,7 @@ awaitable<void> test_coro_worker_2() {
 
 awaitable<void> test_coro_worker_3() {
     int cnt = 0;
-    auto worker = make_interval_coro_worker(global_context, 50ms, [&]() -> awaitable<bool> {
+    auto worker = make_interval_coro_worker(50ms, [&]() -> awaitable<bool> {
         ++cnt;
         co_return true;
     });
@@ -68,7 +68,7 @@ awaitable<void> test_coro_worker_3() {
 
 awaitable<void> test_coro_worker_4() {
     int cnt = 0;
-    auto worker = make_interval_coro_worker(global_context, 50ms, [&]() -> awaitable<bool> {
+    auto worker = make_interval_coro_worker(50ms, [&]() -> awaitable<bool> {
         ++cnt;
         co_return false;
     });
@@ -82,7 +82,7 @@ awaitable<void> test_coro_worker_4() {
 
 awaitable<void> test_coro_worker_5() {
     int cnt = 0;
-    auto worker = make_interval_coro_worker(global_context, 10ms, [&]() -> awaitable<bool> {
+    auto worker = make_interval_coro_worker(10ms, [&]() -> awaitable<bool> {
         co_await coro_sleep(50ms);
         ++cnt;
         co_return true;
@@ -97,7 +97,7 @@ awaitable<void> test_coro_worker_5() {
 
 awaitable<void> test_coro_worker_6() {
     int cnt = 0, exit_cnt = 0;
-    auto worker = make_interval_coro_worker(global_context, 10ms, [&]() -> awaitable<bool> {
+    auto worker = make_interval_coro_worker(10ms, [&]() -> awaitable<bool> {
         co_await coro_sleep(50ms);
         ++cnt;
         co_return true;
@@ -115,11 +115,13 @@ awaitable<void> test_coro_worker_6() {
 }
 
 BOOST_AUTO_TEST_CASE(test_coro_worker) {
-    co_spawn(global_context, test_coro_worker_1(), detached);
-    co_spawn(global_context, test_coro_worker_2(), detached);
-    co_spawn(global_context, test_coro_worker_3(), detached);
-    co_spawn(global_context, test_coro_worker_4(), detached);
-    co_spawn(global_context, test_coro_worker_5(), detached);
-    co_spawn(global_context, test_coro_worker_6(), detached);
-    global_context.run();
+    initialize({});
+
+    co_spawn(*global_context, test_coro_worker_1(), detached);
+    co_spawn(*global_context, test_coro_worker_2(), detached);
+    co_spawn(*global_context, test_coro_worker_3(), detached);
+    co_spawn(*global_context, test_coro_worker_4(), detached);
+    co_spawn(*global_context, test_coro_worker_5(), detached);
+    co_spawn(*global_context, test_coro_worker_6(), detached);
+    global_context->run();
 }

+ 34 - 0
tests/utility/dynamic_pool.cpp

@@ -0,0 +1,34 @@
+#define BOOST_TEST_DYN_LINK
+
+#include "utility/debug_utility.hpp"
+#include "utility/dynamic_pool.hpp"
+
+#include <boost/test/unit_test.hpp>
+
+#include <chrono>
+#include <iostream>
+#include <vector>
+
+using namespace sophiar;
+using namespace std::chrono_literals;
+
+void test_dynamic_pool_func1() {
+    std::vector<int> v1;
+    std::vector<int, global_dynamic_allocator<int>> v2;
+    FILE_LINE_TRACE
+    for (int i = 0; i <= 1000000; ++i) v1.push_back(i);
+    FILE_LINE_TRACE
+    for (int i = 0; i <= 1000000; ++i) v2.push_back(i);
+    FILE_LINE_TRACE
+    auto sum = std::accumulate(v2.begin(), v2.end(), 0LL);
+    BOOST_TEST(sum == 500000500000LL);
+}
+
+BOOST_AUTO_TEST_CASE(test_dynamic_pool) {
+    initialize({});
+
+    test_dynamic_pool_func1();
+    test_dynamic_pool_func1();
+    test_dynamic_pool_func1();
+    test_dynamic_pool_func1();
+}

+ 16 - 20
tests/utility/global_obj_helper.cpp

@@ -1,8 +1,9 @@
 #define BOOST_TEST_DYN_LINK
 
+#include "core/basic_obj_types.hpp"
 #include "core/sophiar_obj.hpp"
-#include "utility/global_obj_helper.hpp"
 #include "utility/debug_utility.hpp"
+#include "utility/variable_helper.hpp"
 
 #include <boost/asio/co_spawn.hpp>
 #include <boost/asio/detached.hpp>
@@ -23,18 +24,13 @@ using namespace sophiar;
 using namespace std::chrono_literals;
 
 awaitable<void> test_global_obj_helper_1() {
-    struct small_a : public small_obj<small_a> {
-        int value;
 
-        explicit small_a(int _value = 0) { value = _value; }
-    };
+    REGISTER_VARIABLE("var_a", "u64int_obj");
+    auto var_a_index = REQUIRE_VARIABLE(u64int_obj, "var_a");
+    UPDATE_VARIABLE_VAL(u64int_obj, var_a_index, 123);
 
-    auto obj_a_index = global_sophiar_manager.register_global_obj<small_a>("obj_a");
-    global_sophiar_manager.update_global_obj<small_a>(obj_a_index, small_a::new_instance(123));
-
-    co_spawn(global_context, [=]() -> awaitable<void> {
-        auto obj_a = global_obj_auto_sync_delegate<small_a>(
-                global_sophiar_manager, obj_a_index);
+    co_spawn(*global_context, [=]() -> awaitable<void> {
+        auto obj_a = VARIABLE_AUTO_DELEGATE(u64int_obj, var_a_index);
         BOOST_TEST(obj_a->value == 123);
         co_await coro_sleep(10ms);
         BOOST_TEST(obj_a->value == 234);
@@ -43,9 +39,9 @@ awaitable<void> test_global_obj_helper_1() {
         co_return;
     }, detached);
 
-    co_spawn(global_context, [=]() -> awaitable<void> {
-        auto obj_a = global_obj_manual_sync_delegate<small_a>(
-                global_sophiar_manager, obj_a_index);
+    co_spawn(*global_context, [=]() -> awaitable<void> {
+        auto obj_a = VARIABLE_MANUAL_DELEGATE(u64int_obj, var_a_index);
+        obj_a.manual_sync();
         BOOST_TEST(obj_a->value == 123);
         co_await coro_sleep(10ms);
         BOOST_TEST(obj_a->value == 123);
@@ -54,17 +50,17 @@ awaitable<void> test_global_obj_helper_1() {
         co_return;
     }, detached);
 
-    global_sophiar_manager.update_global_obj<small_a>(obj_a_index, small_a::new_instance(234));
+    UPDATE_VARIABLE_VAL(u64int_obj, var_a_index, 234);
 
     co_await coro_sleep(15ms);
-    auto obj_a = global_obj_auto_sync_delegate<small_a>(
-            global_sophiar_manager, obj_a_index);
-    obj_a = small_a::new_instance(456);
+    auto var_a = VARIABLE_AUTO_DELEGATE(u64int_obj, var_a_index);
+    var_a = 456;
 
     co_return;
 }
 
 BOOST_AUTO_TEST_CASE(test_global_obj_helper) {
-    co_spawn(global_context, test_global_obj_helper_1(), detached);
-    global_context.run();
+    initialize({});
+    co_spawn(*global_context, test_global_obj_helper_1(), detached);
+    global_context->run();
 }

+ 53 - 54
tests/utility/global_obj_record_playback.cpp

@@ -1,8 +1,7 @@
 #define BOOST_TEST_DYN_LINK
 
-#include "core/transform_tree.h"
+#include "algorithm/transform_tree.h"
 #include "core/basic_obj_types.hpp"
-#include "utility/debug_utility.hpp"
 
 #include <boost/test/unit_test.hpp>
 #include <boost/asio/awaitable.hpp>
@@ -20,55 +19,55 @@ using boost::asio::awaitable;
 using boost::asio::co_spawn;
 using boost::asio::detached;
 
-BOOST_AUTO_TEST_CASE(test_global_obj_recoreder) {
-
-    spdlog::set_level(spdlog::level::trace);
-
-    REGISTER_TYPE(double_obj_recorder);
-    REGISTER_TYPE(scalarxyz_obj_recorder);
-    REGISTER_TYPE(transform_obj_recorder);
-
-    std::ifstream config_file("data/global_obj_record_config.json");
-    BOOST_TEST(config_file.is_open());
-
-    using namespace std::chrono_literals;
-    auto double_sample_index = REGISTER_GLOBAL_OBJ(double_obj, "double_sample");
-    auto xyz_sample_index = REGISTER_GLOBAL_OBJ(scalarxyz_obj, "xyz_sample");
-    auto transform_sample_index = REGISTER_GLOBAL_OBJ(transform_obj, "transform_sample");
-    auto worker_a = make_interval_coro_worker(global_context, 1s, [=]() mutable -> awaitable<bool> {
-        static auto sample_val = 1.1;
-        auto new_double = double_obj::value_type(sample_val);
-        auto new_xyz = scalarxyz_obj::value_type(0.1, 0.2, sample_val);
-        auto new_trans = transform_obj::value_type(Eigen::Translation3d(0.3, 0.4, sample_val));
-        UPDATE_GLOBAL_OBJ_VALUE(double_obj, double_sample_index, new_double);
-        UPDATE_GLOBAL_OBJ_VALUE(scalarxyz_obj, xyz_sample_index, std::move(new_xyz));
-        UPDATE_GLOBAL_OBJ_VALUE(transform_obj, transform_sample_index, std::move(new_trans));
-        sample_val += 0.6;
-        co_return true;
-    });
-    worker_a->run();
-
-    global_sophiar_manager.load_config_and_start(nlohmann::json::parse(config_file));
-
-    global_context.run();
-}
-
-BOOST_AUTO_TEST_CASE(test_global_obj_replayer) {
-
-    spdlog::set_level(spdlog::level::trace);
-
-    REGISTER_TYPE(double_obj_watcher);
-    REGISTER_TYPE(scalarxyz_obj_watcher);
-    REGISTER_TYPE(transform_obj_watcher);
-
-    REGISTER_TYPE(double_obj_replayer);
-    REGISTER_TYPE(scalarxyz_obj_replayer);
-    REGISTER_TYPE(transform_obj_replayer);
-
-    std::ifstream config_file("data/global_obj_replay_config.json");
-    BOOST_TEST(config_file.is_open());
-
-    global_sophiar_manager.load_config_and_start(nlohmann::json::parse(config_file));
-
-    global_context.run();
-}
+//BOOST_AUTO_TEST_CASE(test_global_obj_recoreder) {
+//
+//    spdlog::set_level(spdlog::level::trace);
+//
+//    REGISTER_TYPE(double_obj_recorder);
+//    REGISTER_TYPE(scalarxyz_obj_recorder);
+//    REGISTER_TYPE(transform_obj_recorder);
+//
+//    std::ifstream config_file("data/global_obj_record_config.json");
+//    BOOST_TEST(config_file.is_open());
+//
+//    using namespace std::chrono_literals;
+//    auto double_sample_index = REGISTER_GLOBAL_OBJ(double_obj, "double_sample");
+//    auto xyz_sample_index = REGISTER_GLOBAL_OBJ(scalarxyz_obj, "xyz_sample");
+//    auto transform_sample_index = REGISTER_GLOBAL_OBJ(transform_obj, "transform_sample");
+//    auto worker_a = make_interval_coro_worker(global_context, 1s, [=]() mutable -> awaitable<bool> {
+//        static auto sample_val = 1.1;
+//        auto new_double = double_obj::value_type(sample_val);
+//        auto new_xyz = scalarxyz_obj::value_type(0.1, 0.2, sample_val);
+//        auto new_trans = transform_obj::value_type(Eigen::Translation3d(0.3, 0.4, sample_val));
+//        UPDATE_GLOBAL_OBJ_VALUE(double_obj, double_sample_index, new_double);
+//        UPDATE_GLOBAL_OBJ_VALUE(scalarxyz_obj, xyz_sample_index, std::move(new_xyz));
+//        UPDATE_GLOBAL_OBJ_VALUE(transform_obj, transform_sample_index, std::move(new_trans));
+//        sample_val += 0.6;
+//        co_return true;
+//    });
+//    worker_a->run();
+//
+//    global_sophiar_manager.load_config_and_start(nlohmann::json::parse(config_file));
+//
+//    global_context.run();
+//}
+//
+//BOOST_AUTO_TEST_CASE(test_global_obj_replayer) {
+//
+//    spdlog::set_level(spdlog::level::trace);
+//
+//    REGISTER_TYPE(double_obj_watcher);
+//    REGISTER_TYPE(scalarxyz_obj_watcher);
+//    REGISTER_TYPE(transform_obj_watcher);
+//
+//    REGISTER_TYPE(double_obj_replayer);
+//    REGISTER_TYPE(scalarxyz_obj_replayer);
+//    REGISTER_TYPE(transform_obj_replayer);
+//
+//    std::ifstream config_file("data/global_obj_replay_config.json");
+//    BOOST_TEST(config_file.is_open());
+//
+//    global_sophiar_manager.load_config_and_start(nlohmann::json::parse(config_file));
+//
+//    global_context.run();
+//}