firmware_update_trigger.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_FIRMWARE_UPDATE_TRIGGER_HPP_INCLUDED
6 #define UAVCAN_PROTOCOL_FIRMWARE_UPDATE_TRIGGER_HPP_INCLUDED
7 
9 #include <uavcan/debug.hpp>
12 #include <uavcan/util/map.hpp>
14 // UAVCAN types
15 #include <uavcan/protocol/file/BeginFirmwareUpdate.hpp>
16 
17 namespace uavcan
18 {
24 {
25 public:
31 
38 
53  virtual bool shouldRequestFirmwareUpdate(NodeID node_id, const protocol::GetNodeInfo::Response& node_info,
54  FirmwareFilePath& out_firmware_file_path) = 0;
55 
75  virtual bool shouldRetryFirmwareUpdate(NodeID node_id,
76  const protocol::file::BeginFirmwareUpdate::Response& error_response,
77  FirmwareFilePath& out_firmware_file_path) = 0;
78 
90  const protocol::file::BeginFirmwareUpdate::Response& response)
91  {
92  (void)node_id;
93  (void)response;
94  }
95 
97 };
98 
137  private TimerBase
138 {
140  void (FirmwareUpdateTrigger::*)(const ServiceCallResult<protocol::file::BeginFirmwareUpdate>&)>
142 
144 
145  enum { DefaultRequestIntervalMs = 1000 };
146 
148  {
149  enum { DefaultOutput = 0xFFU };
150 
151  const FirmwareUpdateTrigger& owner;
153 
154  NextNodeIDSearchPredicate(const FirmwareUpdateTrigger& arg_owner)
155  : owner(arg_owner)
156  , output(DefaultOutput)
157  { }
158 
159  bool operator()(const NodeID node_id, const FirmwareFilePath&)
160  {
161  if (node_id.get() > owner.last_queried_node_id_ &&
162  !owner.begin_fw_update_client_.hasPendingCallToServer(node_id))
163  {
164  output = min(output, node_id.get());
165  }
166  return false;
167  }
168  };
169 
170  /*
171  * State
172  */
174 
176 
178 
180 
182 
183  FirmwareFilePath common_path_prefix_;
184 
186 
187  /*
188  * Methods of INodeInfoListener
189  */
190  virtual void handleNodeInfoUnavailable(NodeID node_id)
191  {
192  UAVCAN_TRACE("FirmwareUpdateTrigger", "Node ID %d could not provide GetNodeInfo response", int(node_id.get()));
193  pending_nodes_.remove(node_id); // For extra paranoia
194  }
195 
196  virtual void handleNodeInfoRetrieved(const NodeID node_id, const protocol::GetNodeInfo::Response& node_info)
197  {
198  FirmwareFilePath firmware_file_path;
199  const bool update_needed = checker_.shouldRequestFirmwareUpdate(node_id, node_info, firmware_file_path);
200  if (update_needed)
201  {
202  UAVCAN_TRACE("FirmwareUpdateTrigger", "Node ID %d requires update; file path: %s",
203  int(node_id.get()), firmware_file_path.c_str());
204  trySetPendingNode(node_id, firmware_file_path);
205  }
206  else
207  {
208  UAVCAN_TRACE("FirmwareUpdateTrigger", "Node ID %d does not need update", int(node_id.get()));
209  pending_nodes_.remove(node_id);
210  }
211  }
212 
214  {
215  if (event.status.mode == protocol::NodeStatus::MODE_OFFLINE)
216  {
217  pending_nodes_.remove(event.node_id);
218  UAVCAN_TRACE("FirmwareUpdateTrigger", "Node ID %d is offline hence forgotten", int(event.node_id.get()));
219  }
220  }
221 
222  /*
223  * Own methods
224  */
225  INode& getNode() { return begin_fw_update_client_.getNode(); }
226 
227  void trySetPendingNode(const NodeID node_id, const FirmwareFilePath& path)
228  {
229  if (UAVCAN_NULLPTR != pending_nodes_.insert(node_id, path))
230  {
231  if (!TimerBase::isRunning())
232  {
233  TimerBase::startPeriodic(request_interval_);
234  UAVCAN_TRACE("FirmwareUpdateTrigger", "Timer started");
235  }
236  }
237  else
238  {
239  getNode().registerInternalFailure("FirmwareUpdateTrigger OOM");
240  }
241  }
242 
244  {
245  // We can't do index search because indices are unstable in Map<>
246  // First try - from the current node up
247  NextNodeIDSearchPredicate s1(*this);
248  (void)pending_nodes_.find<NextNodeIDSearchPredicate&>(s1);
249 
250  if (s1.output != NextNodeIDSearchPredicate::DefaultOutput)
251  {
252  last_queried_node_id_ = s1.output;
253  }
254  else if (last_queried_node_id_ != 0)
255  {
256  // Nothing was found, resetting the selector and trying again
257  UAVCAN_TRACE("FirmwareUpdateTrigger", "Node selector reset, last value: %d", int(last_queried_node_id_));
258  last_queried_node_id_ = 0;
259 
260  NextNodeIDSearchPredicate s2(*this);
261  (void)pending_nodes_.find<NextNodeIDSearchPredicate&>(s2);
262 
263  if (s2.output != NextNodeIDSearchPredicate::DefaultOutput)
264  {
265  last_queried_node_id_ = s2.output;
266  }
267  }
268  else
269  {
270  ; // Hopeless
271  }
272  UAVCAN_TRACE("FirmwareUpdateTrigger", "Next node ID to query: %d, pending nodes: %u, pending calls: %u",
273  int(last_queried_node_id_), pending_nodes_.getSize(),
274  begin_fw_update_client_.getNumPendingCalls());
275  return last_queried_node_id_;
276  }
277 
279  {
280  if (!result.isSuccessful())
281  {
282  UAVCAN_TRACE("FirmwareUpdateTrigger", "Request to %d has timed out, will retry",
283  int(result.getCallID().server_node_id.get()));
284  return;
285  }
286 
287  FirmwareFilePath* const old_path = pending_nodes_.access(result.getCallID().server_node_id);
288  if (old_path == UAVCAN_NULLPTR)
289  {
290  // The entry has been removed, assuming that it's not needed anymore
291  return;
292  }
293 
294  const bool confirmed =
295  result.getResponse().error == protocol::file::BeginFirmwareUpdate::Response::ERROR_OK ||
296  result.getResponse().error == protocol::file::BeginFirmwareUpdate::Response::ERROR_IN_PROGRESS;
297 
298  if (confirmed)
299  {
300  UAVCAN_TRACE("FirmwareUpdateTrigger", "Node %d confirmed the update request",
301  int(result.getCallID().server_node_id.get()));
302  pending_nodes_.remove(result.getCallID().server_node_id);
303  checker_.handleFirmwareUpdateConfirmation(result.getCallID().server_node_id, result.getResponse());
304  }
305  else
306  {
307  UAVCAN_ASSERT(old_path != UAVCAN_NULLPTR);
309  // We won't have to call trySetPendingNode(), because we'll directly update the old path via the pointer
310 
311  const bool update_needed =
312  checker_.shouldRetryFirmwareUpdate(result.getCallID().server_node_id, result.getResponse(), *old_path);
313 
314  if (!update_needed)
315  {
316  UAVCAN_TRACE("FirmwareUpdateTrigger", "Node %d does not need retry",
317  int(result.getCallID().server_node_id.get()));
318  pending_nodes_.remove(result.getCallID().server_node_id);
319  }
320  }
321  }
322 
323  virtual void handleTimerEvent(const TimerEvent&)
324  {
325  if (pending_nodes_.isEmpty())
326  {
327  TimerBase::stop();
328  UAVCAN_TRACE("FirmwareUpdateTrigger", "Timer stopped");
329  return;
330  }
331 
332  const NodeID node_id = pickNextNodeID();
333  if (!node_id.isUnicast())
334  {
335  return;
336  }
337 
338  FirmwareFilePath* const path = pending_nodes_.access(node_id);
339  if (path == UAVCAN_NULLPTR)
340  {
341  UAVCAN_ASSERT(0); // pickNextNodeID() returned a node ID that is not present in the map
342  return;
343  }
344 
345  protocol::file::BeginFirmwareUpdate::Request req;
346 
347  req.source_node_id = getNode().getNodeID().get();
348  if (!common_path_prefix_.empty())
349  {
350  req.image_file_remote_path.path += common_path_prefix_.c_str();
351  req.image_file_remote_path.path.push_back(protocol::file::Path::SEPARATOR);
352  }
353  req.image_file_remote_path.path += path->c_str();
354 
355  UAVCAN_TRACE("FirmwareUpdateTrigger", "Request to %d with path: %s",
356  int(node_id.get()), req.image_file_remote_path.path.c_str());
357 
358  const int call_res = begin_fw_update_client_.call(node_id, req);
359  if (call_res < 0)
360  {
361  getNode().registerInternalFailure("FirmwareUpdateTrigger call");
362  }
363  }
364 
365 public:
367  : TimerBase(node)
368  , begin_fw_update_client_(node)
369  , checker_(checker)
370  , node_info_retriever_(UAVCAN_NULLPTR)
371  , pending_nodes_(node.getAllocator())
372  , request_interval_(MonotonicDuration::fromMSec(DefaultRequestIntervalMs))
373  , last_queried_node_id_(0)
374  { }
375 
377  {
378  if (node_info_retriever_ != UAVCAN_NULLPTR)
379  {
380  node_info_retriever_->removeListener(this);
381  }
382  }
383 
397  int start(NodeInfoRetriever& node_info_retriever,
398  const FirmwareFilePath& arg_common_path_prefix = FirmwareFilePath(),
400  {
401  /*
402  * Configuring the node info retriever
403  */
404  node_info_retriever_ = &node_info_retriever;
405 
406  int res = node_info_retriever_->addListener(this);
407  if (res < 0)
408  {
409  return res;
410  }
411 
412  /*
413  * Initializing the prefix, removing only the last trailing path separator.
414  */
415  common_path_prefix_ = arg_common_path_prefix;
416 
417  if (!common_path_prefix_.empty() &&
418  *(common_path_prefix_.end() - 1) == protocol::file::Path::SEPARATOR)
419  {
420  common_path_prefix_.resize(uint8_t(common_path_prefix_.size() - 1U));
421  }
422 
423  /*
424  * Initializing the client
425  */
426  res = begin_fw_update_client_.init(priority);
427  if (res < 0)
428  {
429  return res;
430  }
431  begin_fw_update_client_.setCallback(
433 
434  // The timer will be started ad-hoc
435  return 0;
436  }
437 
442  MonotonicDuration getRequestInterval() const { return request_interval_; }
444  {
445  if (interval.isPositive())
446  {
447  request_interval_ = interval;
448  if (TimerBase::isRunning()) // Restarting with new interval
449  {
450  TimerBase::startPeriodic(request_interval_);
451  }
452  }
453  else
454  {
455  UAVCAN_ASSERT(0);
456  }
457  }
458 
463  bool isTimerRunning() const { return TimerBase::isRunning(); }
464 
465  unsigned getNumPendingNodes() const
466  {
467  const unsigned ret = pending_nodes_.getSize();
468  UAVCAN_ASSERT((ret > 0) ? isTimerRunning() : true);
469  return ret;
470  }
471 
472 };
473 
474 }
475 
476 #endif // Include guard
void remove(const Key &key)
Definition: map.hpp:256
std::uint8_t uint8_t
Definition: std.hpp:24
void handleBeginFirmwareUpdateResponse(const ServiceCallResult< protocol::file::BeginFirmwareUpdate > &result)
bool isEmpty() const
Definition: map.hpp:170
MonotonicDuration getRequestInterval() const
bool isPositive() const
Definition: time.hpp:48
IFirmwareVersionChecker::FirmwareFilePath FirmwareFilePath
const char * c_str() const
Definition: array.hpp:321
ServiceCallID getCallID() const
Map< NodeID, FirmwareFilePath > pending_nodes_
EnableIf< sizeof((reinterpret_cast< const R * >0)) -> size()) &&sizeof((*(reinterpret_cast< const R *>(0)))[0])
int start(NodeInfoRetriever &node_info_retriever, const FirmwareFilePath &arg_common_path_prefix=FirmwareFilePath(), const TransferPriority priority=TransferPriority::OneHigherThanLowest)
virtual void handleNodeInfoUnavailable(NodeID node_id)
virtual void handleNodeInfoRetrieved(const NodeID node_id, const protocol::GetNodeInfo::Response &node_info)
void startPeriodic(MonotonicDuration period)
Definition: uc_timer.cpp:42
virtual bool shouldRetryFirmwareUpdate(NodeID node_id, const protocol::file::BeginFirmwareUpdate::Response &error_response, FirmwareFilePath &out_firmware_file_path)=0
ValueType * end()
Definition: array.hpp:351
int addListener(INodeInfoListener *listener)
bool isUnicast() const
Definition: transfer.hpp:136
bool empty() const
Definition: array.hpp:712
Value * insert(const Key &key, const Value &value)
Definition: map.hpp:233
ServiceClient< protocol::file::BeginFirmwareUpdate, BeginFirmwareUpdateResponseCallback > begin_fw_update_client_
uint8_t get() const
Definition: transfer.hpp:132
unsigned getSize() const
Definition: map.hpp:367
void removeListener(INodeInfoListener *listener)
virtual bool shouldRequestFirmwareUpdate(NodeID node_id, const protocol::GetNodeInfo::Response &node_info, FirmwareFilePath &out_firmware_file_path)=0
const Key * find(Predicate predicate) const
Definition: map.hpp:302
void resize(SizeType new_size, const ValueType &filler)
Definition: array.hpp:727
UAVCAN_EXPORT const T & min(const T &a, const T &b)
Definition: templates.hpp:281
const ResponseFieldType & getResponse() const
virtual void handleFirmwareUpdateConfirmation(NodeID node_id, const protocol::file::BeginFirmwareUpdate::Response &response)
void setCallback(const Callback &cb)
FirmwareUpdateTrigger(INode &node, IFirmwareVersionChecker &checker)
void setRequestInterval(const MonotonicDuration interval)
Value * access(const Key &key)
Definition: map.hpp:225
unsigned getNumPendingCalls() const
bool operator()(const NodeID node_id, const FirmwareFilePath &)
virtual void handleNodeStatusChange(const NodeStatusMonitor::NodeStatusChangeEvent &event)
MakeString< MaxFirmwareFilePathLength >::Type FirmwareFilePath
NextNodeIDSearchPredicate(const FirmwareUpdateTrigger &arg_owner)
void trySetPendingNode(const NodeID node_id, const FirmwareFilePath &path)
static const TransferPriority OneHigherThanLowest
Definition: transfer.hpp:40
int call(NodeID server_node_id, const RequestType &request)
virtual void handleTimerEvent(const TimerEvent &)


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