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..cf61e8771 --- /dev/null +++ b/libuavcan/include/uavcan/protocol/dynamic_node_id_server/abstract_server.hpp @@ -0,0 +1,114 @@ +/* + * 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_; + MonotonicTime started_at_; + +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; + } + + 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(Allocation::MAX_REQUEST_PERIOD_MS * 2), + const MonotonicDuration& min_uptime = MonotonicDuration::fromMSec(6000)) 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 < max(allocation_activity_timeout, min_uptime)) + { + 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_; } +}; + +} +} + +#endif // Include guard 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..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 @@ -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()) { @@ -266,9 +268,16 @@ 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); } + + /** + * 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_; } }; } 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_; } }; /** 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();