Forráskód Böngészése

修复了切换模式时不会更新配置的bug

jcsyshc 3 éve
szülő
commit
395cb8b1eb

+ 1 - 0
CMakeLists.txt

@@ -16,6 +16,7 @@ list(APPEND EXTRA_LIBS fmt::fmt)
 
 find_package(spdlog REQUIRED)
 list(APPEND EXTRA_LIBS spdlog::spdlog)
+add_compile_definitions(SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE)
 
 find_package(Eigen3 REQUIRED)
 list(APPEND EXTRA_LIBS Eigen3::Eigen)

+ 83 - 30
src/core/sophiar_manager.cpp

@@ -8,6 +8,7 @@
 #include <boost/iterator/counting_iterator.hpp>
 
 #include <fmt/format.h>
+#include <spdlog/spdlog.h>
 
 #include <algorithm>
 #include <coroutine>
@@ -25,22 +26,27 @@ namespace sophiar {
 
     struct sophiar_manager::impl {
 
-        using mode_index_type = uint8_t;
+        using config_index_type = uint16_t;
+        config_index_type next_config_index = 0;
 
         struct config_info {
             nlohmann::json config;
-            timestamp_type update_ts;
+            int16_t ref_count = 0;
         };
 
-        using mode_config_pool_type = std::unordered_map<mode_index_type, config_info>;
+        using config_pool_type = std::unordered_map<config_index_type, config_info>;
+        config_pool_type config_pool;
+
+        using mode_index_type = uint8_t;
+        using mode_config_pool_type = std::unordered_map<mode_index_type, config_index_type>;
 
         using obj_type_index_type = uint8_t;
         using obj_factory_func_pool_type = named_vector<obj_type_index_type, obj_factory_func_type>;
 
         struct obj_info {
             sophiar_obj *ptr;
-            timestamp_type last_init_config_ts;
-            timestamp_type last_start_config_ts;
+            config_index_type last_init_config_index;
+            config_index_type last_start_config_index;
             mode_config_pool_type init_config_pool;
             mode_config_pool_type start_config_pool;
         };
@@ -99,6 +105,11 @@ namespace sophiar {
         mode_index_type current_mode = 0; // all_down
         mode_pool_type mode_pool;
 
+        impl() {
+            config_pool[next_config_index++] =
+                    config_info{.config={}, .ref_count=1}; // default empty config
+        }
+
         std::string get_obj_name_by_ptr(sophiar_obj *obj) const {
             assert(obj_ptr_index_map.contains(obj));
             auto obj_index = obj_ptr_index_map.at(obj);
@@ -119,6 +130,26 @@ namespace sophiar {
             return fmt::format("slot://{}/{}", slot_obj_name, slot_name);
         }
 
+        static auto get_connection_uri(const signal_index_type signal_index,
+                                       const slot_index_type slot_index) {
+            return fmt::format("connection://{}/{}", signal_index, slot_index);
+        }
+
+        void acquire_config(config_index_type config_index) {
+            assert(config_pool.contains(config_index));
+            ++config_pool.at(config_index).ref_count;
+        }
+
+        void release_config(config_index_type config_index) {
+            assert(config_pool.contains(config_index));
+            auto ref_count_after = --config_pool.at(config_index).ref_count;
+            assert(ref_count_after >= 0);
+            if (ref_count_after == 0) { // delete config
+                config_pool.erase(config_index);
+                assert(!config_pool.contains(config_index));
+            }
+        }
+
         void enable_connection(connection_index_type conn_index) {
             auto &conn_info = connection_pool[conn_index];
             assert(!conn_info.is_connected);
@@ -139,6 +170,8 @@ namespace sophiar {
                 conn_info.is_direct_connected = false;
                 conn_info.slot_iter = slot_info.used_slot_pool.begin();
             }
+            SPDLOG_DEBUG("Connection [uri = {}] is enabled.",
+                         get_connection_uri(conn_info.signal_index, conn_info.slot_index));
             conn_info.is_connected = true;
         }
 
@@ -147,13 +180,15 @@ namespace sophiar {
             assert(conn_info.is_connected);
             auto &slot_info = slot_pool[conn_info.slot_index];
             if (conn_info.is_direct_connected) {
-                slot_info.direct_slot->unlink();
+                slot_info.direct_slot->disconnect();
             } else {
                 auto target_slot = *(conn_info.slot_iter);
-                target_slot->unlink();
+                target_slot->disconnect();
                 slot_info.used_slot_pool.erase(conn_info.slot_iter);
                 slot_info.free_slot_pool.push(target_slot);
             }
+            SPDLOG_DEBUG("Connection [uri = {}] is disabled.",
+                         get_connection_uri(conn_info.signal_index, conn_info.slot_index));
             conn_info.is_connected = false;
         }
 
@@ -161,6 +196,9 @@ namespace sophiar {
             auto &mode_info = mode_pool[mode_index];
             using state_type = tristate_obj::state_type;
 
+            SPDLOG_INFO("Try switch to mode {}...",
+                        mode_pool.to_name_by_index(mode_index));
+
             // 停止不需要的对象
             for (obj_index_type obj_index = 0; obj_index < obj_pool.size(); ++obj_index) {
                 auto tristate_ptr = get_tristate_ptr(obj_index);
@@ -207,13 +245,14 @@ namespace sophiar {
 
                 assert(obj_info.init_config_pool.contains(mode_index));
                 assert(obj_info.start_config_pool.contains(mode_index));
-                auto &init_config = obj_info.init_config_pool.at(mode_index);
-                auto &start_config = obj_info.start_config_pool.at(mode_index);
+                auto init_config_index = obj_info.init_config_pool.at(mode_index);
+                auto start_config_index = obj_info.start_config_pool.at(mode_index);
 
                 // check if start config is updated
                 if (tristate_ptr->get_state() == state_type::RUNNING) {
-                    assert(obj_info.last_start_config_ts <= start_config.update_ts);
-                    if (obj_info.last_start_config_ts < start_config.update_ts) {
+                    if (obj_info.last_start_config_index != start_config_index) {
+                        SPDLOG_DEBUG("New start config found for object [name = {}].",
+                                     obj_pool.to_name_by_index(obj_index));
                         co_await tristate_ptr->stop();
                         assert(tristate_ptr->is_stable());
                     }
@@ -223,8 +262,9 @@ namespace sophiar {
                 if (tristate_ptr->get_state() != state_type::INITIAL) {
                     assert(tristate_ptr->get_state() == state_type::PENDING ||
                            tristate_ptr->get_state() == state_type::RUNNING);
-                    assert(obj_info.last_init_config_ts <= init_config.update_ts);
-                    if (obj_info.last_init_config_ts < init_config.update_ts) {
+                    if (obj_info.last_init_config_index != init_config_index) {
+                        SPDLOG_DEBUG("New init config found for object [name = {}].",
+                                     obj_pool.to_name_by_index(obj_index));
                         co_await tristate_ptr->reset();
                         assert(tristate_ptr->is_stable());
                     }
@@ -235,13 +275,15 @@ namespace sophiar {
 
                 // if not initialized, make it to be pending
                 if (tristate_ptr->get_state() == state_type::INITIAL) {
-                    CO_ENSURE(tristate_ptr->init(init_config.config))
-                    obj_info.last_init_config_ts = init_config.update_ts;
+                    assert(config_pool.contains(init_config_index));
+                    CO_ENSURE(tristate_ptr->init(config_pool.at(init_config_index).config))
+                    obj_info.last_init_config_index = init_config_index;
                 }
 
                 assert(tristate_ptr->get_state() == state_type::PENDING);
-                CO_ENSURE(tristate_ptr->start(start_config.config))
-                obj_info.last_start_config_ts = start_config.update_ts;
+                assert(config_pool.contains(start_config_index));
+                CO_ENSURE(tristate_ptr->start(config_pool.at(start_config_index).config))
+                obj_info.last_start_config_index = start_config_index;
                 assert(tristate_ptr->get_state() == state_type::RUNNING);
             }
 
@@ -251,9 +293,13 @@ namespace sophiar {
         awaitable<bool> switch_mode_impl(mode_index_type target_mode) { // 尝试切换模式,如果失败就降级
             bool ok = co_await try_switch_mode(target_mode);
             if (ok) co_return true;
+            SPDLOG_ERROR("Switch to target mode {} failed, degrading...",
+                         mode_pool.to_name_by_index(target_mode));
             for (auto degrade_mode: mode_pool[target_mode].degrade_list) {
                 ok = co_await try_switch_mode(degrade_mode);
                 if (ok) break;
+                SPDLOG_ERROR("Switch to degrade mode {} failed, degrading...",
+                             mode_pool.to_name_by_index(degrade_mode));
             }
             co_return false;
         }
@@ -376,10 +422,12 @@ namespace sophiar {
                 }
                 assert(config_json.contains("config"));
                 auto &init_config = config_json["config"];
+                auto cur_config_index = next_config_index++;
+                auto &config_info = config_pool[cur_config_index];
+                config_info.config = init_config;
                 for (auto mode_index: mode_list) {
-                    auto &config_info = obj_info.init_config_pool[mode_index];
-                    config_info.update_ts = current_ts;
-                    config_info.config = init_config;
+                    obj_info.init_config_pool[mode_index] = cur_config_index;
+                    acquire_config(cur_config_index);
                 }
             }
 
@@ -394,10 +442,12 @@ namespace sophiar {
                 }
                 assert(config_json.contains("config"));
                 auto &start_config = config_json["config"];
+                auto cur_config_index = next_config_index++;
+                auto &config_info = config_pool[cur_config_index];
+                config_info.config = start_config;
                 for (auto mode_index: mode_list) {
-                    auto &config_info = obj_info.start_config_pool[mode_index];
-                    config_info.update_ts = current_ts;
-                    config_info.config = start_config;
+                    obj_info.start_config_pool[mode_index] = cur_config_index;
+                    acquire_config(cur_config_index);
                 }
             }
 
@@ -405,15 +455,13 @@ namespace sophiar {
             for (auto mode_index: enabled_modes) {
                 if (!obj_info.init_config_pool.contains(mode_index)) {
                     // TODO show log
-                    auto &config_info = obj_info.init_config_pool[mode_index];
-                    config_info.update_ts = current_ts;
-                    config_info.config = {};
+                    obj_info.init_config_pool[mode_index] = 0; // empty config
+                    acquire_config(0);
                 }
                 if (!obj_info.start_config_pool.contains(mode_index)) {
                     // TODO show log
-                    auto &config_info = obj_info.start_config_pool[mode_index];
-                    config_info.update_ts = current_ts;
-                    config_info.config = {};
+                    obj_info.start_config_pool[mode_index] = 0; // empty config
+                    acquire_config(0);
                 }
             }
         }
@@ -458,7 +506,7 @@ namespace sophiar {
                     auto slot_uri = get_slot_uri(slot_obj_name, slot_name);
                     auto signal_index = signal_pool.to_index_by_name(signal_uri);
                     auto slot_index = slot_pool.to_index_by_name(slot_uri);
-                    auto conn_uri = fmt::format("connection://{}/{}", signal_index, slot_index);
+                    auto conn_uri = get_connection_uri(signal_index, slot_index);
                     auto conn_index = connection_pool.contains(conn_uri) ?
                                       connection_pool.to_index_by_name(conn_uri) :
                                       connection_pool.new_elem(conn_uri);
@@ -511,6 +559,11 @@ namespace sophiar {
         co_return co_await pimpl->switch_mode_impl(mode_index);
     }
 
+    std::string sophiar_manager::get_object_name(sophiar_obj *obj) const {
+        auto obj_index = pimpl->obj_ptr_index_map.at(obj);
+        return pimpl->obj_pool.to_name_by_index(obj_index);
+    }
+
     sophiar_manager::~sophiar_manager() = default;
 
 }

+ 1 - 1
src/core/sophiar_manager.h

@@ -30,7 +30,7 @@ namespace sophiar {
 
         boost::asio::awaitable<bool> switch_mode(const std::string &mode_name);
 
-//        object_index_type get_object_index(sophiar_obj *obj);
+        std::string get_object_name(sophiar_obj *obj) const;
 
         template<typename Derived>
         void register_object_type(const std::string &type_name) {

+ 44 - 0
src/core/tristate_obj.cpp

@@ -1,6 +1,10 @@
 #include "tristate_obj.h"
 
+#include "third_party/static_block.hpp"
 #include "utility/coro_signal.hpp"
+#include "utility/name_translator.hpp"
+
+#include <spdlog/spdlog.h>
 
 #include <boost/asio/experimental/awaitable_operators.hpp>
 
@@ -8,6 +12,19 @@
 
 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);
+
+    };
+
     using boost::asio::awaitable;
 
     using namespace boost::asio::experimental::awaitable_operators;
@@ -32,6 +49,13 @@ namespace sophiar {
                   stop_finished_signal(get_context()),
                   reset_finished_signal(get_context()) {}
 
+        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));
+        }
+
         awaitable<bool> init(const nlohmann::json &config) {
             if (state == state_type::INITIALIZING) {
                 auto result = co_await (init_finished_signal.coro_wait() ||
@@ -44,14 +68,21 @@ namespace sophiar {
             }
             if (state != state_type::INITIAL) co_return true; // >= PENDING
             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 result = co_await (q_this->on_init(config) || init_cancel_signal.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));
                 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));
                 reset_finished_signal.try_notify_all();
                 co_return false;
             }
@@ -70,14 +101,21 @@ namespace sophiar {
             if (state == state_type::RUNNING) co_return true;
             if (state != state_type::PENDING) co_return false; // INITIAL, INITIALIZING, RESETTING
             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 result = co_await (q_this->on_start(config) || start_cancel_signal.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));
                 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));
                 stop_finished_signal.try_notify_all();
                 co_return false;
             }
@@ -86,8 +124,11 @@ namespace sophiar {
         awaitable<void> stop() {
             if (state == state_type::RUNNING) {
                 state = state_type::STOPPING;
+                log_state_change(state_type::RUNNING, state_type::STOPPING);
                 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));
                 stop_finished_signal.try_notify_all();
             } else if (state == state_type::STOPPING) {
                 co_await stop_finished_signal.coro_wait();
@@ -109,8 +150,11 @@ namespace sophiar {
 
             if (state == state_type::PENDING) {
                 state = state_type::RESETTING;
+                log_state_change(state_type::PENDING, state_type::RESETTING);
                 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));
                 reset_finished_signal.try_notify_all();
             } else if (state == state_type::RESETTING) {
                 co_await reset_finished_signal.coro_wait();

+ 8 - 0
src/utility/name_translator.hpp

@@ -13,7 +13,9 @@ namespace sophiar {
 
         void register_item(const std::string &name, IndexType index) {
             assert(!name_map.contains(name));
+            assert(!index_map.contains(index));
             name_map[name] = index;
+            index_map[index] = name;
         }
 
         IndexType translate(const std::string &name) const {
@@ -21,9 +23,15 @@ namespace sophiar {
             return name_map.at(name);
         }
 
+        std::string translate(IndexType index) const {
+            assert(index_map.contains(index));
+            return index_map.at(index);
+        }
+
     private:
 
         std::unordered_map<std::string, IndexType> name_map;
+        std::unordered_map<IndexType, std::string> index_map;
 
     };
 

+ 5 - 0
src/utility/tiny_signal.hpp

@@ -3,6 +3,8 @@
 
 #include <boost/intrusive/list.hpp>
 
+#include <spdlog/spdlog.h>
+
 #include <cassert>
 #include <type_traits>
 #include <utility>
@@ -22,6 +24,7 @@ namespace sophiar {
         void disconnect() {
             assert(this->is_linked());
             this->unlink();
+            SPDLOG_TRACE("Slot {} is disconnected.", fmt::ptr(this));
         }
     };
 
@@ -54,6 +57,8 @@ namespace sophiar {
             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) {

+ 4 - 0
tests/core/sophiar_manager.cpp

@@ -125,6 +125,8 @@ struct target_node_type : public tristate_obj {
 
 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);
@@ -138,6 +140,8 @@ BOOST_AUTO_TEST_CASE(test_sophiar_manager) {
         co_await global_sophiar_manager.switch_mode("mode_a");
         co_await coro_sleep(100ms);
         co_await global_sophiar_manager.switch_mode("mode_b");
+        co_await coro_sleep(100ms);
+        co_await global_sophiar_manager.switch_mode("mode_a");
     }, detached);
 
     global_context.run();