Руслан Урядинский / libuavcan

Dependents:   UAVCAN UAVCAN_Subscriber

Committer:
RuslanUrya
Date:
Sat Apr 14 10:25:32 2018 +0000
Revision:
0:dfe6edabb8ec
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
RuslanUrya 0:dfe6edabb8ec 1 /*
RuslanUrya 0:dfe6edabb8ec 2 * Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
RuslanUrya 0:dfe6edabb8ec 3 */
RuslanUrya 0:dfe6edabb8ec 4
RuslanUrya 0:dfe6edabb8ec 5 #ifndef UAVCAN_PROTOCOL_NODE_INFO_RETRIEVER_HPP_INCLUDED
RuslanUrya 0:dfe6edabb8ec 6 #define UAVCAN_PROTOCOL_NODE_INFO_RETRIEVER_HPP_INCLUDED
RuslanUrya 0:dfe6edabb8ec 7
RuslanUrya 0:dfe6edabb8ec 8 #include <uavcan/build_config.hpp>
RuslanUrya 0:dfe6edabb8ec 9 #include <uavcan/debug.hpp>
RuslanUrya 0:dfe6edabb8ec 10 #include <uavcan/util/multiset.hpp>
RuslanUrya 0:dfe6edabb8ec 11 #include <uavcan/node/service_client.hpp>
RuslanUrya 0:dfe6edabb8ec 12 #include <uavcan/node/timer.hpp>
RuslanUrya 0:dfe6edabb8ec 13 #include <uavcan/protocol/node_status_monitor.hpp>
RuslanUrya 0:dfe6edabb8ec 14 #include <uavcan/protocol/GetNodeInfo.hpp>
RuslanUrya 0:dfe6edabb8ec 15
RuslanUrya 0:dfe6edabb8ec 16 namespace uavcan
RuslanUrya 0:dfe6edabb8ec 17 {
RuslanUrya 0:dfe6edabb8ec 18 /**
RuslanUrya 0:dfe6edabb8ec 19 * Classes that need to receive GetNodeInfo responses should implement this interface.
RuslanUrya 0:dfe6edabb8ec 20 */
RuslanUrya 0:dfe6edabb8ec 21 class UAVCAN_EXPORT INodeInfoListener
RuslanUrya 0:dfe6edabb8ec 22 {
RuslanUrya 0:dfe6edabb8ec 23 public:
RuslanUrya 0:dfe6edabb8ec 24 /**
RuslanUrya 0:dfe6edabb8ec 25 * Called when a response to GetNodeInfo request is received. This happens shortly after the node restarts or
RuslanUrya 0:dfe6edabb8ec 26 * becomes online for the first time.
RuslanUrya 0:dfe6edabb8ec 27 * @param node_id Node ID of the node
RuslanUrya 0:dfe6edabb8ec 28 * @param response Node info struct
RuslanUrya 0:dfe6edabb8ec 29 */
RuslanUrya 0:dfe6edabb8ec 30 virtual void handleNodeInfoRetrieved(NodeID node_id, const protocol::GetNodeInfo::Response& node_info) = 0;
RuslanUrya 0:dfe6edabb8ec 31
RuslanUrya 0:dfe6edabb8ec 32 /**
RuslanUrya 0:dfe6edabb8ec 33 * Called when the retriever decides that the node does not support the GetNodeInfo service.
RuslanUrya 0:dfe6edabb8ec 34 * This method will never be called if the number of attempts is unlimited.
RuslanUrya 0:dfe6edabb8ec 35 */
RuslanUrya 0:dfe6edabb8ec 36 virtual void handleNodeInfoUnavailable(NodeID node_id) = 0;
RuslanUrya 0:dfe6edabb8ec 37
RuslanUrya 0:dfe6edabb8ec 38 /**
RuslanUrya 0:dfe6edabb8ec 39 * This call is routed directly from @ref NodeStatusMonitor.
RuslanUrya 0:dfe6edabb8ec 40 * Default implementation does nothing.
RuslanUrya 0:dfe6edabb8ec 41 * @param event Node status change event
RuslanUrya 0:dfe6edabb8ec 42 */
RuslanUrya 0:dfe6edabb8ec 43 virtual void handleNodeStatusChange(const NodeStatusMonitor::NodeStatusChangeEvent& event)
RuslanUrya 0:dfe6edabb8ec 44 {
RuslanUrya 0:dfe6edabb8ec 45 (void)event;
RuslanUrya 0:dfe6edabb8ec 46 }
RuslanUrya 0:dfe6edabb8ec 47
RuslanUrya 0:dfe6edabb8ec 48 /**
RuslanUrya 0:dfe6edabb8ec 49 * This call is routed directly from @ref NodeStatusMonitor.
RuslanUrya 0:dfe6edabb8ec 50 * Default implementation does nothing.
RuslanUrya 0:dfe6edabb8ec 51 * @param msg Node status message
RuslanUrya 0:dfe6edabb8ec 52 */
RuslanUrya 0:dfe6edabb8ec 53 virtual void handleNodeStatusMessage(const ReceivedDataStructure<protocol::NodeStatus>& msg)
RuslanUrya 0:dfe6edabb8ec 54 {
RuslanUrya 0:dfe6edabb8ec 55 (void)msg;
RuslanUrya 0:dfe6edabb8ec 56 }
RuslanUrya 0:dfe6edabb8ec 57
RuslanUrya 0:dfe6edabb8ec 58 virtual ~INodeInfoListener() { }
RuslanUrya 0:dfe6edabb8ec 59 };
RuslanUrya 0:dfe6edabb8ec 60
RuslanUrya 0:dfe6edabb8ec 61 /**
RuslanUrya 0:dfe6edabb8ec 62 * This class automatically retrieves a response to GetNodeInfo once a node appears online or restarts.
RuslanUrya 0:dfe6edabb8ec 63 * It does a number of attempts in case if there's a communication failure before assuming that the node does not
RuslanUrya 0:dfe6edabb8ec 64 * implement the GetNodeInfo service. All parameters are pre-configured with sensible default values that should fit
RuslanUrya 0:dfe6edabb8ec 65 * virtually any use case, but they can be overriden if needed - refer to the setter methods below for details.
RuslanUrya 0:dfe6edabb8ec 66 *
RuslanUrya 0:dfe6edabb8ec 67 * Defaults are pre-configured so that the class is able to query 123 nodes (node ID 1..125, where 1 is our local
RuslanUrya 0:dfe6edabb8ec 68 * node and 1 is one node that implements GetNodeInfo service, hence 123) of which none implements GetNodeInfo
RuslanUrya 0:dfe6edabb8ec 69 * service in under 5 seconds. The 5 second limitation is imposed by UAVCAN-compatible bootloaders, which are
RuslanUrya 0:dfe6edabb8ec 70 * unlikely to wait for more than that before continuing to boot. In case if this default value is not appropriate
RuslanUrya 0:dfe6edabb8ec 71 * for the end application, the request interval can be overriden via @ref setRequestInterval().
RuslanUrya 0:dfe6edabb8ec 72 *
RuslanUrya 0:dfe6edabb8ec 73 * Following the above explained requirements, the default request interval is defined as follows:
RuslanUrya 0:dfe6edabb8ec 74 * request interval [ms] = floor(5000 [ms] bootloader timeout / 123 nodes)
RuslanUrya 0:dfe6edabb8ec 75 * Which yields 40 ms.
RuslanUrya 0:dfe6edabb8ec 76 *
RuslanUrya 0:dfe6edabb8ec 77 * Given default service timeout 1000 ms and the defined above request frequency 40 ms, the maximum number of
RuslanUrya 0:dfe6edabb8ec 78 * concurrent requests will be:
RuslanUrya 0:dfe6edabb8ec 79 * max concurrent requests = ceil(1000 [ms] timeout / 40 [ms] request interval)
RuslanUrya 0:dfe6edabb8ec 80 * Which yields 25 requests.
RuslanUrya 0:dfe6edabb8ec 81 *
RuslanUrya 0:dfe6edabb8ec 82 * Keep the above equations in mind when changing the default request interval.
RuslanUrya 0:dfe6edabb8ec 83 *
RuslanUrya 0:dfe6edabb8ec 84 * Obviously, if all calls are completing in under (request interval), the number of concurrent requests will never
RuslanUrya 0:dfe6edabb8ec 85 * exceed one. This is actually the most likely scenario.
RuslanUrya 0:dfe6edabb8ec 86 *
RuslanUrya 0:dfe6edabb8ec 87 * Note that all nodes are queried in a round-robin fashion, regardless of their uptime, number of requests made, etc.
RuslanUrya 0:dfe6edabb8ec 88 *
RuslanUrya 0:dfe6edabb8ec 89 * Events from this class can be routed to many listeners, @ref INodeInfoListener.
RuslanUrya 0:dfe6edabb8ec 90 */
RuslanUrya 0:dfe6edabb8ec 91 class UAVCAN_EXPORT NodeInfoRetriever : public NodeStatusMonitor
RuslanUrya 0:dfe6edabb8ec 92 , TimerBase
RuslanUrya 0:dfe6edabb8ec 93 {
RuslanUrya 0:dfe6edabb8ec 94 public:
RuslanUrya 0:dfe6edabb8ec 95 enum { MaxNumRequestAttempts = 254 };
RuslanUrya 0:dfe6edabb8ec 96 enum { UnlimitedRequestAttempts = 0 };
RuslanUrya 0:dfe6edabb8ec 97
RuslanUrya 0:dfe6edabb8ec 98 private:
RuslanUrya 0:dfe6edabb8ec 99 typedef MethodBinder<NodeInfoRetriever*,
RuslanUrya 0:dfe6edabb8ec 100 void (NodeInfoRetriever::*)(const ServiceCallResult<protocol::GetNodeInfo>&)>
RuslanUrya 0:dfe6edabb8ec 101 GetNodeInfoResponseCallback;
RuslanUrya 0:dfe6edabb8ec 102
RuslanUrya 0:dfe6edabb8ec 103 struct Entry
RuslanUrya 0:dfe6edabb8ec 104 {
RuslanUrya 0:dfe6edabb8ec 105 uint32_t uptime_sec;
RuslanUrya 0:dfe6edabb8ec 106 uint8_t num_attempts_made;
RuslanUrya 0:dfe6edabb8ec 107 bool request_needed; ///< Always false for unknown nodes
RuslanUrya 0:dfe6edabb8ec 108 bool updated_since_last_attempt; ///< Always false for unknown nodes
RuslanUrya 0:dfe6edabb8ec 109
RuslanUrya 0:dfe6edabb8ec 110 Entry()
RuslanUrya 0:dfe6edabb8ec 111 : uptime_sec(0)
RuslanUrya 0:dfe6edabb8ec 112 , num_attempts_made(0)
RuslanUrya 0:dfe6edabb8ec 113 , request_needed(false)
RuslanUrya 0:dfe6edabb8ec 114 , updated_since_last_attempt(false)
RuslanUrya 0:dfe6edabb8ec 115 {
RuslanUrya 0:dfe6edabb8ec 116 #if UAVCAN_DEBUG
RuslanUrya 0:dfe6edabb8ec 117 StaticAssert<sizeof(Entry) <= 8>::check();
RuslanUrya 0:dfe6edabb8ec 118 #endif
RuslanUrya 0:dfe6edabb8ec 119 }
RuslanUrya 0:dfe6edabb8ec 120 };
RuslanUrya 0:dfe6edabb8ec 121
RuslanUrya 0:dfe6edabb8ec 122 struct NodeInfoRetrievedHandlerCaller
RuslanUrya 0:dfe6edabb8ec 123 {
RuslanUrya 0:dfe6edabb8ec 124 const NodeID node_id;
RuslanUrya 0:dfe6edabb8ec 125 const protocol::GetNodeInfo::Response& node_info;
RuslanUrya 0:dfe6edabb8ec 126
RuslanUrya 0:dfe6edabb8ec 127 NodeInfoRetrievedHandlerCaller(NodeID arg_node_id, const protocol::GetNodeInfo::Response& arg_node_info)
RuslanUrya 0:dfe6edabb8ec 128 : node_id(arg_node_id)
RuslanUrya 0:dfe6edabb8ec 129 , node_info(arg_node_info)
RuslanUrya 0:dfe6edabb8ec 130 { }
RuslanUrya 0:dfe6edabb8ec 131
RuslanUrya 0:dfe6edabb8ec 132 bool operator()(INodeInfoListener* key)
RuslanUrya 0:dfe6edabb8ec 133 {
RuslanUrya 0:dfe6edabb8ec 134 UAVCAN_ASSERT(key != UAVCAN_NULLPTR);
RuslanUrya 0:dfe6edabb8ec 135 key->handleNodeInfoRetrieved(node_id, node_info);
RuslanUrya 0:dfe6edabb8ec 136 return false;
RuslanUrya 0:dfe6edabb8ec 137 }
RuslanUrya 0:dfe6edabb8ec 138 };
RuslanUrya 0:dfe6edabb8ec 139
RuslanUrya 0:dfe6edabb8ec 140 template <typename Event>
RuslanUrya 0:dfe6edabb8ec 141 struct GenericHandlerCaller
RuslanUrya 0:dfe6edabb8ec 142 {
RuslanUrya 0:dfe6edabb8ec 143 void (INodeInfoListener::* const method)(Event);
RuslanUrya 0:dfe6edabb8ec 144 Event event;
RuslanUrya 0:dfe6edabb8ec 145
RuslanUrya 0:dfe6edabb8ec 146 GenericHandlerCaller(void (INodeInfoListener::*arg_method)(Event), Event arg_event)
RuslanUrya 0:dfe6edabb8ec 147 : method(arg_method)
RuslanUrya 0:dfe6edabb8ec 148 , event(arg_event)
RuslanUrya 0:dfe6edabb8ec 149 { }
RuslanUrya 0:dfe6edabb8ec 150
RuslanUrya 0:dfe6edabb8ec 151 bool operator()(INodeInfoListener* key)
RuslanUrya 0:dfe6edabb8ec 152 {
RuslanUrya 0:dfe6edabb8ec 153 UAVCAN_ASSERT(key != UAVCAN_NULLPTR);
RuslanUrya 0:dfe6edabb8ec 154 (key->*method)(event);
RuslanUrya 0:dfe6edabb8ec 155 return false;
RuslanUrya 0:dfe6edabb8ec 156 }
RuslanUrya 0:dfe6edabb8ec 157 };
RuslanUrya 0:dfe6edabb8ec 158
RuslanUrya 0:dfe6edabb8ec 159 enum { DefaultNumRequestAttempts = 16 };
RuslanUrya 0:dfe6edabb8ec 160 enum { DefaultTimerIntervalMSec = 40 }; ///< Read explanation in the class documentation
RuslanUrya 0:dfe6edabb8ec 161
RuslanUrya 0:dfe6edabb8ec 162 /*
RuslanUrya 0:dfe6edabb8ec 163 * State
RuslanUrya 0:dfe6edabb8ec 164 */
RuslanUrya 0:dfe6edabb8ec 165 Entry entries_[NodeID::Max]; // [1, NodeID::Max]
RuslanUrya 0:dfe6edabb8ec 166
RuslanUrya 0:dfe6edabb8ec 167 Multiset<INodeInfoListener*> listeners_;
RuslanUrya 0:dfe6edabb8ec 168
RuslanUrya 0:dfe6edabb8ec 169 ServiceClient<protocol::GetNodeInfo, GetNodeInfoResponseCallback> get_node_info_client_;
RuslanUrya 0:dfe6edabb8ec 170
RuslanUrya 0:dfe6edabb8ec 171 MonotonicDuration request_interval_;
RuslanUrya 0:dfe6edabb8ec 172
RuslanUrya 0:dfe6edabb8ec 173 mutable uint8_t last_picked_node_;
RuslanUrya 0:dfe6edabb8ec 174
RuslanUrya 0:dfe6edabb8ec 175 uint8_t num_attempts_;
RuslanUrya 0:dfe6edabb8ec 176
RuslanUrya 0:dfe6edabb8ec 177 /*
RuslanUrya 0:dfe6edabb8ec 178 * Methods
RuslanUrya 0:dfe6edabb8ec 179 */
RuslanUrya 0:dfe6edabb8ec 180 const Entry& getEntry(NodeID node_id) const { return const_cast<NodeInfoRetriever*>(this)->getEntry(node_id); }
RuslanUrya 0:dfe6edabb8ec 181 Entry& getEntry(NodeID node_id)
RuslanUrya 0:dfe6edabb8ec 182 {
RuslanUrya 0:dfe6edabb8ec 183 if (node_id.get() < 1 || node_id.get() > NodeID::Max)
RuslanUrya 0:dfe6edabb8ec 184 {
RuslanUrya 0:dfe6edabb8ec 185 handleFatalError("NodeInfoRetriever NodeID");
RuslanUrya 0:dfe6edabb8ec 186 }
RuslanUrya 0:dfe6edabb8ec 187 return entries_[node_id.get() - 1];
RuslanUrya 0:dfe6edabb8ec 188 }
RuslanUrya 0:dfe6edabb8ec 189
RuslanUrya 0:dfe6edabb8ec 190 void startTimerIfNotRunning()
RuslanUrya 0:dfe6edabb8ec 191 {
RuslanUrya 0:dfe6edabb8ec 192 if (!TimerBase::isRunning())
RuslanUrya 0:dfe6edabb8ec 193 {
RuslanUrya 0:dfe6edabb8ec 194 TimerBase::startPeriodic(request_interval_);
RuslanUrya 0:dfe6edabb8ec 195 UAVCAN_TRACE("NodeInfoRetriever", "Timer started, interval %s sec", request_interval_.toString().c_str());
RuslanUrya 0:dfe6edabb8ec 196 }
RuslanUrya 0:dfe6edabb8ec 197 }
RuslanUrya 0:dfe6edabb8ec 198
RuslanUrya 0:dfe6edabb8ec 199 NodeID pickNextNodeToQuery(bool& out_at_least_one_request_needed) const
RuslanUrya 0:dfe6edabb8ec 200 {
RuslanUrya 0:dfe6edabb8ec 201 out_at_least_one_request_needed = false;
RuslanUrya 0:dfe6edabb8ec 202
RuslanUrya 0:dfe6edabb8ec 203 for (unsigned iter_cnt_ = 0; iter_cnt_ < (sizeof(entries_) / sizeof(entries_[0])); iter_cnt_++) // Round-robin
RuslanUrya 0:dfe6edabb8ec 204 {
RuslanUrya 0:dfe6edabb8ec 205 last_picked_node_++;
RuslanUrya 0:dfe6edabb8ec 206 if (last_picked_node_ > NodeID::Max)
RuslanUrya 0:dfe6edabb8ec 207 {
RuslanUrya 0:dfe6edabb8ec 208 last_picked_node_ = 1;
RuslanUrya 0:dfe6edabb8ec 209 }
RuslanUrya 0:dfe6edabb8ec 210 UAVCAN_ASSERT((last_picked_node_ >= 1) &&
RuslanUrya 0:dfe6edabb8ec 211 (last_picked_node_ <= NodeID::Max));
RuslanUrya 0:dfe6edabb8ec 212
RuslanUrya 0:dfe6edabb8ec 213 const Entry& entry = getEntry(last_picked_node_);
RuslanUrya 0:dfe6edabb8ec 214
RuslanUrya 0:dfe6edabb8ec 215 if (entry.request_needed)
RuslanUrya 0:dfe6edabb8ec 216 {
RuslanUrya 0:dfe6edabb8ec 217 out_at_least_one_request_needed = true;
RuslanUrya 0:dfe6edabb8ec 218
RuslanUrya 0:dfe6edabb8ec 219 if (entry.updated_since_last_attempt &&
RuslanUrya 0:dfe6edabb8ec 220 !get_node_info_client_.hasPendingCallToServer(last_picked_node_))
RuslanUrya 0:dfe6edabb8ec 221 {
RuslanUrya 0:dfe6edabb8ec 222 UAVCAN_TRACE("NodeInfoRetriever", "Next node to query: %d", int(last_picked_node_));
RuslanUrya 0:dfe6edabb8ec 223 return NodeID(last_picked_node_);
RuslanUrya 0:dfe6edabb8ec 224 }
RuslanUrya 0:dfe6edabb8ec 225 }
RuslanUrya 0:dfe6edabb8ec 226 }
RuslanUrya 0:dfe6edabb8ec 227
RuslanUrya 0:dfe6edabb8ec 228 return NodeID(); // No node could be found
RuslanUrya 0:dfe6edabb8ec 229 }
RuslanUrya 0:dfe6edabb8ec 230
RuslanUrya 0:dfe6edabb8ec 231 virtual void handleTimerEvent(const TimerEvent&)
RuslanUrya 0:dfe6edabb8ec 232 {
RuslanUrya 0:dfe6edabb8ec 233 bool at_least_one_request_needed = false;
RuslanUrya 0:dfe6edabb8ec 234 const NodeID next = pickNextNodeToQuery(at_least_one_request_needed);
RuslanUrya 0:dfe6edabb8ec 235
RuslanUrya 0:dfe6edabb8ec 236 if (next.isUnicast())
RuslanUrya 0:dfe6edabb8ec 237 {
RuslanUrya 0:dfe6edabb8ec 238 UAVCAN_ASSERT(at_least_one_request_needed);
RuslanUrya 0:dfe6edabb8ec 239 getEntry(next).updated_since_last_attempt = false;
RuslanUrya 0:dfe6edabb8ec 240 const int res = get_node_info_client_.call(next, protocol::GetNodeInfo::Request());
RuslanUrya 0:dfe6edabb8ec 241 if (res < 0)
RuslanUrya 0:dfe6edabb8ec 242 {
RuslanUrya 0:dfe6edabb8ec 243 get_node_info_client_.getNode().registerInternalFailure("NodeInfoRetriever GetNodeInfo call");
RuslanUrya 0:dfe6edabb8ec 244 }
RuslanUrya 0:dfe6edabb8ec 245 }
RuslanUrya 0:dfe6edabb8ec 246 else
RuslanUrya 0:dfe6edabb8ec 247 {
RuslanUrya 0:dfe6edabb8ec 248 if (!at_least_one_request_needed)
RuslanUrya 0:dfe6edabb8ec 249 {
RuslanUrya 0:dfe6edabb8ec 250 TimerBase::stop();
RuslanUrya 0:dfe6edabb8ec 251 UAVCAN_TRACE("NodeInfoRetriever", "Timer stopped");
RuslanUrya 0:dfe6edabb8ec 252 }
RuslanUrya 0:dfe6edabb8ec 253 }
RuslanUrya 0:dfe6edabb8ec 254 }
RuslanUrya 0:dfe6edabb8ec 255
RuslanUrya 0:dfe6edabb8ec 256 virtual void handleNodeStatusChange(const NodeStatusChangeEvent& event)
RuslanUrya 0:dfe6edabb8ec 257 {
RuslanUrya 0:dfe6edabb8ec 258 const bool was_offline = !event.was_known ||
RuslanUrya 0:dfe6edabb8ec 259 (event.old_status.mode == protocol::NodeStatus::MODE_OFFLINE);
RuslanUrya 0:dfe6edabb8ec 260
RuslanUrya 0:dfe6edabb8ec 261 const bool offline_now = event.status.mode == protocol::NodeStatus::MODE_OFFLINE;
RuslanUrya 0:dfe6edabb8ec 262
RuslanUrya 0:dfe6edabb8ec 263 if (was_offline || offline_now)
RuslanUrya 0:dfe6edabb8ec 264 {
RuslanUrya 0:dfe6edabb8ec 265 Entry& entry = getEntry(event.node_id);
RuslanUrya 0:dfe6edabb8ec 266
RuslanUrya 0:dfe6edabb8ec 267 entry.request_needed = !offline_now;
RuslanUrya 0:dfe6edabb8ec 268 entry.num_attempts_made = 0;
RuslanUrya 0:dfe6edabb8ec 269
RuslanUrya 0:dfe6edabb8ec 270 UAVCAN_TRACE("NodeInfoRetriever", "Offline status change: node ID %d, request needed: %d",
RuslanUrya 0:dfe6edabb8ec 271 int(event.node_id.get()), int(entry.request_needed));
RuslanUrya 0:dfe6edabb8ec 272
RuslanUrya 0:dfe6edabb8ec 273 if (entry.request_needed)
RuslanUrya 0:dfe6edabb8ec 274 {
RuslanUrya 0:dfe6edabb8ec 275 startTimerIfNotRunning();
RuslanUrya 0:dfe6edabb8ec 276 }
RuslanUrya 0:dfe6edabb8ec 277 }
RuslanUrya 0:dfe6edabb8ec 278
RuslanUrya 0:dfe6edabb8ec 279 listeners_.forEach(
RuslanUrya 0:dfe6edabb8ec 280 GenericHandlerCaller<const NodeStatusChangeEvent&>(&INodeInfoListener::handleNodeStatusChange, event));
RuslanUrya 0:dfe6edabb8ec 281 }
RuslanUrya 0:dfe6edabb8ec 282
RuslanUrya 0:dfe6edabb8ec 283 virtual void handleNodeStatusMessage(const ReceivedDataStructure<protocol::NodeStatus>& msg)
RuslanUrya 0:dfe6edabb8ec 284 {
RuslanUrya 0:dfe6edabb8ec 285 Entry& entry = getEntry(msg.getSrcNodeID());
RuslanUrya 0:dfe6edabb8ec 286
RuslanUrya 0:dfe6edabb8ec 287 if (msg.uptime_sec < entry.uptime_sec)
RuslanUrya 0:dfe6edabb8ec 288 {
RuslanUrya 0:dfe6edabb8ec 289 entry.request_needed = true;
RuslanUrya 0:dfe6edabb8ec 290 entry.num_attempts_made = 0;
RuslanUrya 0:dfe6edabb8ec 291
RuslanUrya 0:dfe6edabb8ec 292 startTimerIfNotRunning();
RuslanUrya 0:dfe6edabb8ec 293 }
RuslanUrya 0:dfe6edabb8ec 294 entry.uptime_sec = msg.uptime_sec;
RuslanUrya 0:dfe6edabb8ec 295 entry.updated_since_last_attempt = true;
RuslanUrya 0:dfe6edabb8ec 296
RuslanUrya 0:dfe6edabb8ec 297 listeners_.forEach(GenericHandlerCaller<const ReceivedDataStructure<protocol::NodeStatus>&>(
RuslanUrya 0:dfe6edabb8ec 298 &INodeInfoListener::handleNodeStatusMessage, msg));
RuslanUrya 0:dfe6edabb8ec 299 }
RuslanUrya 0:dfe6edabb8ec 300
RuslanUrya 0:dfe6edabb8ec 301 void handleGetNodeInfoResponse(const ServiceCallResult<protocol::GetNodeInfo>& result)
RuslanUrya 0:dfe6edabb8ec 302 {
RuslanUrya 0:dfe6edabb8ec 303 Entry& entry = getEntry(result.getCallID().server_node_id);
RuslanUrya 0:dfe6edabb8ec 304
RuslanUrya 0:dfe6edabb8ec 305 if (result.isSuccessful())
RuslanUrya 0:dfe6edabb8ec 306 {
RuslanUrya 0:dfe6edabb8ec 307 /*
RuslanUrya 0:dfe6edabb8ec 308 * Updating the uptime here allows to properly handle a corner case where the service response arrives
RuslanUrya 0:dfe6edabb8ec 309 * after the device has restarted and published its new NodeStatus (although it's unlikely to happen).
RuslanUrya 0:dfe6edabb8ec 310 */
RuslanUrya 0:dfe6edabb8ec 311 entry.uptime_sec = result.getResponse().status.uptime_sec;
RuslanUrya 0:dfe6edabb8ec 312 entry.request_needed = false;
RuslanUrya 0:dfe6edabb8ec 313 listeners_.forEach(NodeInfoRetrievedHandlerCaller(result.getCallID().server_node_id,
RuslanUrya 0:dfe6edabb8ec 314 result.getResponse()));
RuslanUrya 0:dfe6edabb8ec 315 }
RuslanUrya 0:dfe6edabb8ec 316 else
RuslanUrya 0:dfe6edabb8ec 317 {
RuslanUrya 0:dfe6edabb8ec 318 if (num_attempts_ != UnlimitedRequestAttempts)
RuslanUrya 0:dfe6edabb8ec 319 {
RuslanUrya 0:dfe6edabb8ec 320 entry.num_attempts_made++;
RuslanUrya 0:dfe6edabb8ec 321 if (entry.num_attempts_made >= num_attempts_)
RuslanUrya 0:dfe6edabb8ec 322 {
RuslanUrya 0:dfe6edabb8ec 323 entry.request_needed = false;
RuslanUrya 0:dfe6edabb8ec 324 listeners_.forEach(GenericHandlerCaller<NodeID>(&INodeInfoListener::handleNodeInfoUnavailable,
RuslanUrya 0:dfe6edabb8ec 325 result.getCallID().server_node_id));
RuslanUrya 0:dfe6edabb8ec 326 }
RuslanUrya 0:dfe6edabb8ec 327 }
RuslanUrya 0:dfe6edabb8ec 328 }
RuslanUrya 0:dfe6edabb8ec 329 }
RuslanUrya 0:dfe6edabb8ec 330
RuslanUrya 0:dfe6edabb8ec 331 public:
RuslanUrya 0:dfe6edabb8ec 332 NodeInfoRetriever(INode& node)
RuslanUrya 0:dfe6edabb8ec 333 : NodeStatusMonitor(node)
RuslanUrya 0:dfe6edabb8ec 334 , TimerBase(node)
RuslanUrya 0:dfe6edabb8ec 335 , listeners_(node.getAllocator())
RuslanUrya 0:dfe6edabb8ec 336 , get_node_info_client_(node)
RuslanUrya 0:dfe6edabb8ec 337 , request_interval_(MonotonicDuration::fromMSec(DefaultTimerIntervalMSec))
RuslanUrya 0:dfe6edabb8ec 338 , last_picked_node_(1)
RuslanUrya 0:dfe6edabb8ec 339 , num_attempts_(DefaultNumRequestAttempts)
RuslanUrya 0:dfe6edabb8ec 340 { }
RuslanUrya 0:dfe6edabb8ec 341
RuslanUrya 0:dfe6edabb8ec 342 /**
RuslanUrya 0:dfe6edabb8ec 343 * Starts the retriever.
RuslanUrya 0:dfe6edabb8ec 344 * Destroy the object to stop it.
RuslanUrya 0:dfe6edabb8ec 345 * Returns negative error code.
RuslanUrya 0:dfe6edabb8ec 346 */
RuslanUrya 0:dfe6edabb8ec 347 int start(const TransferPriority priority = TransferPriority::OneHigherThanLowest)
RuslanUrya 0:dfe6edabb8ec 348 {
RuslanUrya 0:dfe6edabb8ec 349 int res = NodeStatusMonitor::start();
RuslanUrya 0:dfe6edabb8ec 350 if (res < 0)
RuslanUrya 0:dfe6edabb8ec 351 {
RuslanUrya 0:dfe6edabb8ec 352 return res;
RuslanUrya 0:dfe6edabb8ec 353 }
RuslanUrya 0:dfe6edabb8ec 354
RuslanUrya 0:dfe6edabb8ec 355 res = get_node_info_client_.init(priority);
RuslanUrya 0:dfe6edabb8ec 356 if (res < 0)
RuslanUrya 0:dfe6edabb8ec 357 {
RuslanUrya 0:dfe6edabb8ec 358 return res;
RuslanUrya 0:dfe6edabb8ec 359 }
RuslanUrya 0:dfe6edabb8ec 360 get_node_info_client_.setCallback(GetNodeInfoResponseCallback(this,
RuslanUrya 0:dfe6edabb8ec 361 &NodeInfoRetriever::handleGetNodeInfoResponse));
RuslanUrya 0:dfe6edabb8ec 362 // Note: the timer will be started ad-hoc
RuslanUrya 0:dfe6edabb8ec 363 return 0;
RuslanUrya 0:dfe6edabb8ec 364 }
RuslanUrya 0:dfe6edabb8ec 365
RuslanUrya 0:dfe6edabb8ec 366 /**
RuslanUrya 0:dfe6edabb8ec 367 * This method forces the class to re-request uavcan.protocol.GetNodeInfo from all nodes as if they
RuslanUrya 0:dfe6edabb8ec 368 * have just appeared in the network.
RuslanUrya 0:dfe6edabb8ec 369 */
RuslanUrya 0:dfe6edabb8ec 370 void invalidateAll()
RuslanUrya 0:dfe6edabb8ec 371 {
RuslanUrya 0:dfe6edabb8ec 372 NodeStatusMonitor::forgetAllNodes();
RuslanUrya 0:dfe6edabb8ec 373 get_node_info_client_.cancelAllCalls();
RuslanUrya 0:dfe6edabb8ec 374
RuslanUrya 0:dfe6edabb8ec 375 for (unsigned i = 0; i < (sizeof(entries_) / sizeof(entries_[0])); i++)
RuslanUrya 0:dfe6edabb8ec 376 {
RuslanUrya 0:dfe6edabb8ec 377 entries_[i] = Entry();
RuslanUrya 0:dfe6edabb8ec 378 }
RuslanUrya 0:dfe6edabb8ec 379 // It is not necessary to reset the last picked node index
RuslanUrya 0:dfe6edabb8ec 380 }
RuslanUrya 0:dfe6edabb8ec 381
RuslanUrya 0:dfe6edabb8ec 382 /**
RuslanUrya 0:dfe6edabb8ec 383 * Adds one listener. Does nothing if such listener already exists.
RuslanUrya 0:dfe6edabb8ec 384 * May return -ErrMemory if there's no space to add the listener.
RuslanUrya 0:dfe6edabb8ec 385 */
RuslanUrya 0:dfe6edabb8ec 386 int addListener(INodeInfoListener* listener)
RuslanUrya 0:dfe6edabb8ec 387 {
RuslanUrya 0:dfe6edabb8ec 388 if (listener != UAVCAN_NULLPTR)
RuslanUrya 0:dfe6edabb8ec 389 {
RuslanUrya 0:dfe6edabb8ec 390 removeListener(listener);
RuslanUrya 0:dfe6edabb8ec 391 return (UAVCAN_NULLPTR == listeners_.emplace(listener)) ? -ErrMemory : 0;
RuslanUrya 0:dfe6edabb8ec 392 }
RuslanUrya 0:dfe6edabb8ec 393 else
RuslanUrya 0:dfe6edabb8ec 394 {
RuslanUrya 0:dfe6edabb8ec 395 return -ErrInvalidParam;
RuslanUrya 0:dfe6edabb8ec 396 }
RuslanUrya 0:dfe6edabb8ec 397 }
RuslanUrya 0:dfe6edabb8ec 398
RuslanUrya 0:dfe6edabb8ec 399 /**
RuslanUrya 0:dfe6edabb8ec 400 * Removes the listener.
RuslanUrya 0:dfe6edabb8ec 401 * If the listener was not registered, nothing will be done.
RuslanUrya 0:dfe6edabb8ec 402 */
RuslanUrya 0:dfe6edabb8ec 403 void removeListener(INodeInfoListener* listener)
RuslanUrya 0:dfe6edabb8ec 404 {
RuslanUrya 0:dfe6edabb8ec 405 if (listener != UAVCAN_NULLPTR)
RuslanUrya 0:dfe6edabb8ec 406 {
RuslanUrya 0:dfe6edabb8ec 407 listeners_.removeAll(listener);
RuslanUrya 0:dfe6edabb8ec 408 }
RuslanUrya 0:dfe6edabb8ec 409 else
RuslanUrya 0:dfe6edabb8ec 410 {
RuslanUrya 0:dfe6edabb8ec 411 UAVCAN_ASSERT(0);
RuslanUrya 0:dfe6edabb8ec 412 }
RuslanUrya 0:dfe6edabb8ec 413 }
RuslanUrya 0:dfe6edabb8ec 414
RuslanUrya 0:dfe6edabb8ec 415 unsigned getNumListeners() const { return listeners_.getSize(); }
RuslanUrya 0:dfe6edabb8ec 416
RuslanUrya 0:dfe6edabb8ec 417 /**
RuslanUrya 0:dfe6edabb8ec 418 * Number of attempts to retrieve GetNodeInfo response before giving up on the assumption that the service is
RuslanUrya 0:dfe6edabb8ec 419 * not implemented.
RuslanUrya 0:dfe6edabb8ec 420 * Zero is a special value that can be used to set unlimited number of attempts, @ref UnlimitedRequestAttempts.
RuslanUrya 0:dfe6edabb8ec 421 */
RuslanUrya 0:dfe6edabb8ec 422 uint8_t getNumRequestAttempts() const { return num_attempts_; }
RuslanUrya 0:dfe6edabb8ec 423 void setNumRequestAttempts(const uint8_t num)
RuslanUrya 0:dfe6edabb8ec 424 {
RuslanUrya 0:dfe6edabb8ec 425 num_attempts_ = min(static_cast<uint8_t>(MaxNumRequestAttempts), num);
RuslanUrya 0:dfe6edabb8ec 426 }
RuslanUrya 0:dfe6edabb8ec 427
RuslanUrya 0:dfe6edabb8ec 428 /**
RuslanUrya 0:dfe6edabb8ec 429 * Request interval also implicitly defines the maximum number of concurrent requests.
RuslanUrya 0:dfe6edabb8ec 430 * Read the class documentation for details.
RuslanUrya 0:dfe6edabb8ec 431 */
RuslanUrya 0:dfe6edabb8ec 432 MonotonicDuration getRequestInterval() const { return request_interval_; }
RuslanUrya 0:dfe6edabb8ec 433 void setRequestInterval(const MonotonicDuration interval)
RuslanUrya 0:dfe6edabb8ec 434 {
RuslanUrya 0:dfe6edabb8ec 435 if (interval.isPositive())
RuslanUrya 0:dfe6edabb8ec 436 {
RuslanUrya 0:dfe6edabb8ec 437 request_interval_ = interval;
RuslanUrya 0:dfe6edabb8ec 438 if (TimerBase::isRunning())
RuslanUrya 0:dfe6edabb8ec 439 {
RuslanUrya 0:dfe6edabb8ec 440 TimerBase::startPeriodic(request_interval_);
RuslanUrya 0:dfe6edabb8ec 441 }
RuslanUrya 0:dfe6edabb8ec 442 }
RuslanUrya 0:dfe6edabb8ec 443 else
RuslanUrya 0:dfe6edabb8ec 444 {
RuslanUrya 0:dfe6edabb8ec 445 UAVCAN_ASSERT(0);
RuslanUrya 0:dfe6edabb8ec 446 }
RuslanUrya 0:dfe6edabb8ec 447 }
RuslanUrya 0:dfe6edabb8ec 448
RuslanUrya 0:dfe6edabb8ec 449 /**
RuslanUrya 0:dfe6edabb8ec 450 * These methods are needed mostly for testing.
RuslanUrya 0:dfe6edabb8ec 451 */
RuslanUrya 0:dfe6edabb8ec 452 bool isRetrievingInProgress() const { return TimerBase::isRunning(); }
RuslanUrya 0:dfe6edabb8ec 453
RuslanUrya 0:dfe6edabb8ec 454 uint8_t getNumPendingRequests() const
RuslanUrya 0:dfe6edabb8ec 455 {
RuslanUrya 0:dfe6edabb8ec 456 const unsigned num = get_node_info_client_.getNumPendingCalls();
RuslanUrya 0:dfe6edabb8ec 457 UAVCAN_ASSERT(num <= 0xFF);
RuslanUrya 0:dfe6edabb8ec 458 return static_cast<uint8_t>(num);
RuslanUrya 0:dfe6edabb8ec 459 }
RuslanUrya 0:dfe6edabb8ec 460 };
RuslanUrya 0:dfe6edabb8ec 461
RuslanUrya 0:dfe6edabb8ec 462 }
RuslanUrya 0:dfe6edabb8ec 463
RuslanUrya 0:dfe6edabb8ec 464 #endif // Include guard