From e13d6dbfd694840f49c84dacdc8f5fd651a656dd Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 19 Feb 2016 11:00:18 +0300 Subject: [PATCH 1/6] AllocationRequestManager::getTimeOfLastAllocationActivity() --- .../dynamic_node_id_server/allocation_request_manager.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/allocation_request_manager.hpp b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/allocation_request_manager.hpp index b68a7005d..ae85348d2 100644 --- a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/allocation_request_manager.hpp +++ b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/allocation_request_manager.hpp @@ -51,6 +51,7 @@ class AllocationRequestManager const MonotonicDuration stage_timeout_; MonotonicTime last_message_timestamp_; + MonotonicTime last_activity_timestamp_; Allocation::FieldTypes::unique_id current_unique_id_; IAllocationRequestHandler& handler_; @@ -127,6 +128,7 @@ class AllocationRequestManager void handleAllocation(const ReceivedDataStructure& msg) { trace(TraceAllocationActivity, msg.getSrcNodeID().get()); + last_activity_timestamp_ = msg.getMonotonicTimestamp(); if (!msg.isAnonymousTransfer()) { @@ -269,6 +271,12 @@ class AllocationRequestManager return allocation_pub_.broadcast(msg); } + + /** + * When the last allocation activity was registered. + * This value can be used to heuristically determine whether there are any unallocated nodes left in the network. + */ + MonotonicTime getTimeOfLastAllocationActivity() const { return last_activity_timestamp_; } }; } From ab2b952432afd73a6d6c6ea7502ab11ce54028f2 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 19 Feb 2016 11:25:50 +0300 Subject: [PATCH 2/6] Refactored the dynamic node ID allocation server: added a new class AbstractServer, which is inherited by CentralizedServer and DistributedServer. This change allowed to move the data and logic that is common to both types of servers to a single location. In the next step this will be used to add more complex common logic. --- .../abstract_server.hpp | 68 +++++++++++++++++++ .../centralized/server.hpp | 47 ++++--------- .../distributed/server.hpp | 51 ++++---------- 3 files changed, 96 insertions(+), 70 deletions(-) create mode 100644 libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp diff --git a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp new file mode 100644 index 000000000..c465f4207 --- /dev/null +++ b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 Pavel Kirienko + */ + +#ifndef UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_SERVER_SERVER_HPP_INCLUDED +#define UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_SERVER_SERVER_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace uavcan +{ +namespace dynamic_node_id_server +{ + +class AbstractServer : protected IAllocationRequestHandler + , protected INodeDiscoveryHandler +{ + UniqueID own_unique_id_; + +protected: + INode& node_; + IEventTracer& tracer_; + AllocationRequestManager allocation_request_manager_; + NodeDiscoverer node_discoverer_; + + AbstractServer(INode& node, + IEventTracer& tracer) : + node_(node), + tracer_(tracer), + allocation_request_manager_(node, tracer, *this), + node_discoverer_(node, tracer, *this) + { } + + const UniqueID& getOwnUniqueID() const { return own_unique_id_; } + + int init(const UniqueID& own_unique_id, const TransferPriority priority) + { + int res = 0; + + own_unique_id_ = own_unique_id; + + res = allocation_request_manager_.init(priority); + if (res < 0) + { + return res; + } + + res = node_discoverer_.init(priority); + if (res < 0) + { + return res; + } + + return 0; + } + +public: + const NodeDiscoverer& getNodeDiscoverer() const { return node_discoverer_; } +}; + +} +} + +#endif // Include guard diff --git a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/centralized/server.hpp b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/centralized/server.hpp index 31daf63c6..abbddcd8c 100644 --- a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/centralized/server.hpp +++ b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/centralized/server.hpp @@ -7,8 +7,7 @@ #include #include -#include -#include +#include #include #include #include @@ -26,15 +25,8 @@ namespace centralized * * This version is suitable only for simple non-critical systems. */ -class Server : IAllocationRequestHandler - , INodeDiscoveryHandler +class Server : public AbstractServer { - UniqueID own_unique_id_; - - INode& node_; - IEventTracer& tracer_; - AllocationRequestManager allocation_request_manager_; - NodeDiscoverer node_discoverer_; Storage storage_; /* @@ -128,10 +120,7 @@ class Server : IAllocationRequestHandler Server(INode& node, IStorageBackend& storage, IEventTracer& tracer) - : node_(node) - , tracer_(tracer) - , allocation_request_manager_(node, tracer, *this) - , node_discoverer_(node, tracer, *this) + : AbstractServer(node, tracer) , storage_(storage) { } @@ -148,12 +137,19 @@ class Server : IAllocationRequestHandler } /* - * Making sure that the server is started with the same node ID + * Common logic */ - own_unique_id_ = own_unique_id; + res = AbstractServer::init(own_unique_id, priority); + if (res < 0) + { + return res; + } + /* + * Making sure that the server is started with the same node ID + */ { - const NodeID stored_own_node_id = storage_.getNodeIDForUniqueID(own_unique_id_); + const NodeID stored_own_node_id = storage_.getNodeIDForUniqueID(getOwnUniqueID()); if (stored_own_node_id.isValid()) { if (stored_own_node_id != node_.getNodeID()) @@ -163,7 +159,7 @@ class Server : IAllocationRequestHandler } else { - res = storage_.add(node_.getNodeID(), own_unique_id_); + res = storage_.add(node_.getNodeID(), getOwnUniqueID()); if (res < 0) { return res; @@ -171,21 +167,6 @@ class Server : IAllocationRequestHandler } } - /* - * Misc - */ - res = allocation_request_manager_.init(priority); - if (res < 0) - { - return res; - } - - res = node_discoverer_.init(priority); - if (res < 0) - { - return res; - } - return 0; } diff --git a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/distributed/server.hpp b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/distributed/server.hpp index e36fb323c..cd72825e7 100644 --- a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/distributed/server.hpp +++ b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/distributed/server.hpp @@ -9,9 +9,8 @@ #include #include #include -#include +#include #include -#include #include namespace uavcan @@ -23,8 +22,7 @@ namespace distributed /** * This class implements the top-level allocation logic and server API. */ -class UAVCAN_EXPORT Server : IAllocationRequestHandler - , INodeDiscoveryHandler +class UAVCAN_EXPORT Server : public AbstractServer , IRaftLeaderMonitor { struct UniqueIDLogPredicate @@ -55,19 +53,10 @@ class UAVCAN_EXPORT Server : IAllocationRequestHandler } }; - /* - * Constants - */ - UniqueID own_unique_id_; - /* * States */ - INode& node_; - IEventTracer& tracer_; RaftCore raft_core_; - AllocationRequestManager allocation_request_manager_; - NodeDiscoverer node_discoverer_; /* * Methods of IAllocationRequestHandler @@ -196,7 +185,7 @@ class UAVCAN_EXPORT Server : IAllocationRequestHandler if (!result.isConstructed()) { - raft_core_.appendLog(own_unique_id_, node_.getNodeID()); + raft_core_.appendLog(getOwnUniqueID(), node_.getNodeID()); } } @@ -239,11 +228,8 @@ class UAVCAN_EXPORT Server : IAllocationRequestHandler Server(INode& node, IStorageBackend& storage, IEventTracer& tracer) - : node_(node) - , tracer_(tracer) + : AbstractServer(node, tracer) , raft_core_(node, storage, tracer, *this) - , allocation_request_manager_(node, tracer, *this) - , node_discoverer_(node, tracer, *this) { } int init(const UniqueID& own_unique_id, @@ -260,36 +246,28 @@ class UAVCAN_EXPORT Server : IAllocationRequestHandler } /* - * Making sure that the server is started with the same node ID + * Common logic */ - own_unique_id_ = own_unique_id; + res = AbstractServer::init(own_unique_id, priority); + if (res < 0) + { + return res; + } + /* + * Making sure that the server is started with the same node ID + */ const LazyConstructor own_log_entry = raft_core_.traverseLogFromEndUntil(NodeIDLogPredicate(node_.getNodeID())); if (own_log_entry.isConstructed()) { - if (own_log_entry->entry.unique_id != own_unique_id_) + if (own_log_entry->entry.unique_id != getOwnUniqueID()) { return -ErrInvalidConfiguration; } } - /* - * Misc - */ - res = allocation_request_manager_.init(priority); - if (res < 0) - { - return res; - } - - res = node_discoverer_.init(priority); - if (res < 0) - { - return res; - } - return 0; } @@ -299,7 +277,6 @@ class UAVCAN_EXPORT Server : IAllocationRequestHandler * These accessors are needed for debugging, visualization and testing. */ const RaftCore& getRaftCore() const { return raft_core_; } - const NodeDiscoverer& getNodeDiscoverer() const { return node_discoverer_; } }; /** From f72e082846a82368503c267b11c86fc3d3a776ec Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 19 Feb 2016 11:40:48 +0300 Subject: [PATCH 3/6] AbstractServer::guessIfAllDynamicNodesAreAllocated() --- .../abstract_server.hpp | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp index c465f4207..505642c6f 100644 --- a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp +++ b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp @@ -20,6 +20,7 @@ class AbstractServer : protected IAllocationRequestHandler , protected INodeDiscoveryHandler { UniqueID own_unique_id_; + MonotonicTime started_at_; protected: INode& node_; @@ -55,10 +56,54 @@ class AbstractServer : protected IAllocationRequestHandler return res; } + started_at_ = node_.getMonotonicTime(); + return 0; } public: + /** + * This can be used to guess if there are any un-allocated dynamic nodes left in the network. + */ + bool guessIfAllDynamicNodesAreAllocated( + const MonotonicDuration& allocation_activity_timeout = + MonotonicDuration::fromMSec(protocol::NodeStatus::OFFLINE_TIMEOUT_MS)) const + { + const MonotonicTime ts = node_.getMonotonicTime(); + + /* + * If uptime is not large enough, the allocator may be unaware about some nodes yet. + */ + const MonotonicDuration uptime = ts - started_at_; + if (uptime < allocation_activity_timeout) + { + return false; + } + + /* + * If there are any undiscovered nodes, assume that allocation is still happening. + */ + if (node_discoverer_.hasUnknownNodes()) + { + return false; + } + + /* + * Lastly, check if there wasn't any allocation messages detected on the bus in the specified amount of time. + */ + const MonotonicDuration since_allocation_activity = + ts - allocation_request_manager_.getTimeOfLastAllocationActivity(); + if (since_allocation_activity < allocation_activity_timeout) + { + return false; + } + + return true; + } + + /** + * This is useful for debugging/testing/monitoring. + */ const NodeDiscoverer& getNodeDiscoverer() const { return node_discoverer_; } }; From eb4532aefea21319132d4183ec8a5383f570a6da Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 19 Feb 2016 11:51:40 +0300 Subject: [PATCH 4/6] Registering outgoing Allocation messages as activity as well --- .../dynamic_node_id_server/allocation_request_manager.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/allocation_request_manager.hpp b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/allocation_request_manager.hpp index ae85348d2..a16346b9a 100644 --- a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/allocation_request_manager.hpp +++ b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/allocation_request_manager.hpp @@ -268,6 +268,7 @@ class AllocationRequestManager msg.node_id = allocated_node_id.get(); trace(TraceAllocationResponse, msg.node_id); + last_activity_timestamp_ = allocation_pub_.getNode().getMonotonicTime(); return allocation_pub_.broadcast(msg); } From b7515646bbedb3e3905e547f241a580ee8c97630 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 19 Feb 2016 11:52:51 +0300 Subject: [PATCH 5/6] New field in dynamic allocator app for Linux: 'All allocated' --- .../linux/apps/uavcan_dynamic_node_id_server.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libuavcan_drivers/linux/apps/uavcan_dynamic_node_id_server.cpp b/libuavcan_drivers/linux/apps/uavcan_dynamic_node_id_server.cpp index 1be941ccd..5aa4d6d26 100644 --- a/libuavcan_drivers/linux/apps/uavcan_dynamic_node_id_server.cpp +++ b/libuavcan_drivers/linux/apps/uavcan_dynamic_node_id_server.cpp @@ -256,7 +256,7 @@ void redraw(const uavcan_linux::NodePtr& node, /* * Constants that are permanent for the designed UI layout */ - constexpr unsigned NumRelevantEvents = 16; + constexpr unsigned NumRelevantEvents = 17; constexpr unsigned NumRowsWithoutEvents = 3; /* @@ -390,6 +390,11 @@ void redraw(const uavcan_linux::NodePtr& node, node->getInternalFailureCount(), colorize_if(node->getInternalFailureCount() != 0, CLIColor::Magenta)); + const bool all_allocated = server.guessIfAllDynamicNodesAreAllocated(); + render_top_str("All allocated", + all_allocated ? "Yes": "No", + colorize_if(!all_allocated, CLIColor::Magenta)); + // Empty line before the next block std::printf(" "); render_next_event_counter(); From 38f5591dda9ac01fe26924c30e3262cea298404b Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 19 Feb 2016 12:35:44 +0300 Subject: [PATCH 6/6] Optimized default timeouts --- .../protocol/dynamic_node_id_server/abstract_server.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp index 505642c6f..cf61e8771 100644 --- a/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp +++ b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp @@ -67,7 +67,8 @@ class AbstractServer : protected IAllocationRequestHandler */ bool guessIfAllDynamicNodesAreAllocated( const MonotonicDuration& allocation_activity_timeout = - MonotonicDuration::fromMSec(protocol::NodeStatus::OFFLINE_TIMEOUT_MS)) const + MonotonicDuration::fromMSec(Allocation::MAX_REQUEST_PERIOD_MS * 2), + const MonotonicDuration& min_uptime = MonotonicDuration::fromMSec(6000)) const { const MonotonicTime ts = node_.getMonotonicTime(); @@ -75,7 +76,7 @@ class AbstractServer : protected IAllocationRequestHandler * If uptime is not large enough, the allocator may be unaware about some nodes yet. */ const MonotonicDuration uptime = ts - started_at_; - if (uptime < allocation_activity_timeout) + if (uptime < max(allocation_activity_timeout, min_uptime)) { return false; }