node_info_retriever.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
3  */
4 
5 #ifndef UAVCAN_PROTOCOL_NODE_INFO_RETRIEVER_HPP_INCLUDED
6 #define UAVCAN_PROTOCOL_NODE_INFO_RETRIEVER_HPP_INCLUDED
7 
9 #include <uavcan/debug.hpp>
10 #include <uavcan/util/multiset.hpp>
12 #include <uavcan/node/timer.hpp>
14 #include <uavcan/protocol/GetNodeInfo.hpp>
15 
16 namespace uavcan
17 {
22 {
23 public:
30  virtual void handleNodeInfoRetrieved(NodeID node_id, const protocol::GetNodeInfo::Response& node_info) = 0;
31 
36  virtual void handleNodeInfoUnavailable(NodeID node_id) = 0;
37 
44  {
45  (void)event;
46  }
47 
54  {
55  (void)msg;
56  }
57 
58  virtual ~INodeInfoListener() { }
59 };
60 
92  , TimerBase
93 {
94 public:
95  enum { MaxNumRequestAttempts = 254 };
96  enum { UnlimitedRequestAttempts = 0 };
97 
98 private:
100  void (NodeInfoRetriever::*)(const ServiceCallResult<protocol::GetNodeInfo>&)>
102 
103  struct Entry
104  {
109 
111  : uptime_sec(0)
112  , num_attempts_made(0)
113  , request_needed(false)
114  , updated_since_last_attempt(false)
115  {
116 #if UAVCAN_DEBUG
117  StaticAssert<sizeof(Entry) <= 8>::check();
118 #endif
119  }
120  };
121 
123  {
125  const protocol::GetNodeInfo::Response& node_info;
126 
127  NodeInfoRetrievedHandlerCaller(NodeID arg_node_id, const protocol::GetNodeInfo::Response& arg_node_info)
128  : node_id(arg_node_id)
129  , node_info(arg_node_info)
130  { }
131 
133  {
135  key->handleNodeInfoRetrieved(node_id, node_info);
136  return false;
137  }
138  };
139 
140  template <typename Event>
142  {
143  void (INodeInfoListener::* const method)(Event);
144  Event event;
145 
146  GenericHandlerCaller(void (INodeInfoListener::*arg_method)(Event), Event arg_event)
147  : method(arg_method)
148  , event(arg_event)
149  { }
150 
152  {
154  (key->*method)(event);
155  return false;
156  }
157  };
158 
159  enum { DefaultNumRequestAttempts = 16 };
160  enum { DefaultTimerIntervalMSec = 40 };
161 
162  /*
163  * State
164  */
165  Entry entries_[NodeID::Max]; // [1, NodeID::Max]
166 
168 
170 
172 
174 
176 
177  /*
178  * Methods
179  */
180  const Entry& getEntry(NodeID node_id) const { return const_cast<NodeInfoRetriever*>(this)->getEntry(node_id); }
182  {
183  if (node_id.get() < 1 || node_id.get() > NodeID::Max)
184  {
185  handleFatalError("NodeInfoRetriever NodeID");
186  }
187  return entries_[node_id.get() - 1];
188  }
189 
191  {
192  if (!TimerBase::isRunning())
193  {
194  TimerBase::startPeriodic(request_interval_);
195  UAVCAN_TRACE("NodeInfoRetriever", "Timer started, interval %s sec", request_interval_.toString().c_str());
196  }
197  }
198 
199  NodeID pickNextNodeToQuery(bool& out_at_least_one_request_needed) const
200  {
201  out_at_least_one_request_needed = false;
202 
203  for (unsigned iter_cnt_ = 0; iter_cnt_ < (sizeof(entries_) / sizeof(entries_[0])); iter_cnt_++) // Round-robin
204  {
205  last_picked_node_++;
206  if (last_picked_node_ > NodeID::Max)
207  {
208  last_picked_node_ = 1;
209  }
210  UAVCAN_ASSERT((last_picked_node_ >= 1) &&
211  (last_picked_node_ <= NodeID::Max));
212 
213  const Entry& entry = getEntry(last_picked_node_);
214 
215  if (entry.request_needed)
216  {
217  out_at_least_one_request_needed = true;
218 
219  if (entry.updated_since_last_attempt &&
220  !get_node_info_client_.hasPendingCallToServer(last_picked_node_))
221  {
222  UAVCAN_TRACE("NodeInfoRetriever", "Next node to query: %d", int(last_picked_node_));
223  return NodeID(last_picked_node_);
224  }
225  }
226  }
227 
228  return NodeID(); // No node could be found
229  }
230 
231  virtual void handleTimerEvent(const TimerEvent&)
232  {
233  bool at_least_one_request_needed = false;
234  const NodeID next = pickNextNodeToQuery(at_least_one_request_needed);
235 
236  if (next.isUnicast())
237  {
238  UAVCAN_ASSERT(at_least_one_request_needed);
239  getEntry(next).updated_since_last_attempt = false;
240  const int res = get_node_info_client_.call(next, protocol::GetNodeInfo::Request());
241  if (res < 0)
242  {
243  get_node_info_client_.getNode().registerInternalFailure("NodeInfoRetriever GetNodeInfo call");
244  }
245  }
246  else
247  {
248  if (!at_least_one_request_needed)
249  {
250  TimerBase::stop();
251  UAVCAN_TRACE("NodeInfoRetriever", "Timer stopped");
252  }
253  }
254  }
255 
257  {
258  const bool was_offline = !event.was_known ||
259  (event.old_status.mode == protocol::NodeStatus::MODE_OFFLINE);
260 
261  const bool offline_now = event.status.mode == protocol::NodeStatus::MODE_OFFLINE;
262 
263  if (was_offline || offline_now)
264  {
265  Entry& entry = getEntry(event.node_id);
266 
267  entry.request_needed = !offline_now;
268  entry.num_attempts_made = 0;
269 
270  UAVCAN_TRACE("NodeInfoRetriever", "Offline status change: node ID %d, request needed: %d",
271  int(event.node_id.get()), int(entry.request_needed));
272 
273  if (entry.request_needed)
274  {
275  startTimerIfNotRunning();
276  }
277  }
278 
279  listeners_.forEach(
281  }
282 
284  {
285  Entry& entry = getEntry(msg.getSrcNodeID());
286 
287  if (msg.uptime_sec < entry.uptime_sec)
288  {
289  entry.request_needed = true;
290  entry.num_attempts_made = 0;
291 
292  startTimerIfNotRunning();
293  }
294  entry.uptime_sec = msg.uptime_sec;
295  entry.updated_since_last_attempt = true;
296 
299  }
300 
302  {
303  Entry& entry = getEntry(result.getCallID().server_node_id);
304 
305  if (result.isSuccessful())
306  {
307  /*
308  * Updating the uptime here allows to properly handle a corner case where the service response arrives
309  * after the device has restarted and published its new NodeStatus (although it's unlikely to happen).
310  */
311  entry.uptime_sec = result.getResponse().status.uptime_sec;
312  entry.request_needed = false;
313  listeners_.forEach(NodeInfoRetrievedHandlerCaller(result.getCallID().server_node_id,
314  result.getResponse()));
315  }
316  else
317  {
318  if (num_attempts_ != UnlimitedRequestAttempts)
319  {
320  entry.num_attempts_made++;
321  if (entry.num_attempts_made >= num_attempts_)
322  {
323  entry.request_needed = false;
325  result.getCallID().server_node_id));
326  }
327  }
328  }
329  }
330 
331 public:
333  : NodeStatusMonitor(node)
334  , TimerBase(node)
335  , listeners_(node.getAllocator())
336  , get_node_info_client_(node)
337  , request_interval_(MonotonicDuration::fromMSec(DefaultTimerIntervalMSec))
338  , last_picked_node_(1)
339  , num_attempts_(DefaultNumRequestAttempts)
340  { }
341 
348  {
349  int res = NodeStatusMonitor::start();
350  if (res < 0)
351  {
352  return res;
353  }
354 
355  res = get_node_info_client_.init(priority);
356  if (res < 0)
357  {
358  return res;
359  }
360  get_node_info_client_.setCallback(GetNodeInfoResponseCallback(this,
362  // Note: the timer will be started ad-hoc
363  return 0;
364  }
365 
371  {
373  get_node_info_client_.cancelAllCalls();
374 
375  for (unsigned i = 0; i < (sizeof(entries_) / sizeof(entries_[0])); i++)
376  {
377  entries_[i] = Entry();
378  }
379  // It is not necessary to reset the last picked node index
380  }
381 
387  {
388  if (listener != UAVCAN_NULLPTR)
389  {
390  removeListener(listener);
391  return (UAVCAN_NULLPTR == listeners_.emplace(listener)) ? -ErrMemory : 0;
392  }
393  else
394  {
395  return -ErrInvalidParam;
396  }
397  }
398 
404  {
405  if (listener != UAVCAN_NULLPTR)
406  {
407  listeners_.removeAll(listener);
408  }
409  else
410  {
411  UAVCAN_ASSERT(0);
412  }
413  }
414 
415  unsigned getNumListeners() const { return listeners_.getSize(); }
416 
422  uint8_t getNumRequestAttempts() const { return num_attempts_; }
424  {
425  num_attempts_ = min(static_cast<uint8_t>(MaxNumRequestAttempts), num);
426  }
427 
432  MonotonicDuration getRequestInterval() const { return request_interval_; }
434  {
435  if (interval.isPositive())
436  {
437  request_interval_ = interval;
438  if (TimerBase::isRunning())
439  {
440  TimerBase::startPeriodic(request_interval_);
441  }
442  }
443  else
444  {
445  UAVCAN_ASSERT(0);
446  }
447  }
448 
452  bool isRetrievingInProgress() const { return TimerBase::isRunning(); }
453 
455  {
456  const unsigned num = get_node_info_client_.getNumPendingCalls();
457  UAVCAN_ASSERT(num <= 0xFF);
458  return static_cast<uint8_t>(num);
459  }
460 };
461 
462 }
463 
464 #endif // Include guard
std::uint8_t uint8_t
Definition: std.hpp:24
bool isPositive() const
Definition: time.hpp:48
ServiceCallID getCallID() const
virtual void handleNodeStatusChange(const NodeStatusMonitor::NodeStatusChangeEvent &event)
bool updated_since_last_attempt
Always false for unknown nodes.
void startPeriodic(MonotonicDuration period)
Definition: uc_timer.cpp:42
int addListener(INodeInfoListener *listener)
bool isUnicast() const
Definition: transfer.hpp:136
ServiceClient< protocol::GetNodeInfo, GetNodeInfoResponseCallback > get_node_info_client_
MonotonicDuration getRequestInterval() const
UAVCAN_EXPORT void handleFatalError(const char *msg)
Definition: uc_error.cpp:20
virtual void handleNodeStatusChange(const NodeStatusChangeEvent &event)
void handleGetNodeInfoResponse(const ServiceCallResult< protocol::GetNodeInfo > &result)
void toString(char buf[StringBufSize]) const
Prints time in seconds with microsecond resolution.
Definition: time.hpp:224
uint8_t get() const
Definition: transfer.hpp:132
NodeID pickNextNodeToQuery(bool &out_at_least_one_request_needed) const
Entry & getEntry(NodeID node_id)
void setRequestInterval(const MonotonicDuration interval)
static const uint8_t Max
Definition: transfer.hpp:120
Multiset< INodeInfoListener * > listeners_
void removeListener(INodeInfoListener *listener)
void removeAll(const T &ref)
Definition: multiset.hpp:269
NodeInfoRetrievedHandlerCaller(NodeID arg_node_id, const protocol::GetNodeInfo::Response &arg_node_info)
std::uint32_t uint32_t
Definition: std.hpp:26
int start(const TransferPriority priority=TransferPriority::OneHigherThanLowest)
UAVCAN_EXPORT const T & min(const T &a, const T &b)
Definition: templates.hpp:281
virtual void handleTimerEvent(const TimerEvent &)
virtual void handleNodeStatusMessage(const ReceivedDataStructure< protocol::NodeStatus > &msg)
virtual void handleNodeInfoRetrieved(NodeID node_id, const protocol::GetNodeInfo::Response &node_info)=0
const ResponseFieldType & getResponse() const
void setNumRequestAttempts(const uint8_t num)
void setCallback(const Callback &cb)
virtual void handleNodeStatusMessage(const ReceivedDataStructure< protocol::NodeStatus > &msg)
bool hasPendingCallToServer(NodeID server_node_id) const
unsigned getSize() const
Definition: multiset.hpp:461
unsigned getNumPendingCalls() const
bool request_needed
Always false for unknown nodes.
const Entry & getEntry(NodeID node_id) const
void forEach(Operator oper)
Definition: multiset.hpp:294
virtual void handleNodeInfoUnavailable(NodeID node_id)=0
static const TransferPriority OneHigherThanLowest
Definition: transfer.hpp:40
int call(NodeID server_node_id, const RequestType &request)
GenericHandlerCaller(void(INodeInfoListener::*arg_method)(Event), Event arg_event)


uavcan_communicator
Author(s):
autogenerated on Wed Jan 11 2023 03:59:39