uc_can_io.cpp
Go to the documentation of this file.
1 /*
2  * CAN bus IO logic.
3  * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
4  * Copyright (C) 2019 Theodoros Ntakouris <zarkopafilis@gmail.com>
5  */
6 
7 #include <cassert>
8 #include <functional>
10 #include <uavcan/debug.hpp>
11 
12 namespace uavcan
13 {
14 /*
15  * CanRxFrame
16  */
17 #if UAVCAN_TOSTRING
18 std::string CanRxFrame::toString(StringRepresentation mode) const
19 {
20  std::string out = CanFrame::toString(mode);
21  out.reserve(128);
22  out += " ts_m=" + ts_mono.toString();
23  out += " ts_utc=" + ts_utc.toString();
24  out += " iface=";
25  out += char('0' + iface_index);
26  return out;
27 }
28 #endif
29 
30 /*
31  * CanTxQueueEntry
32  */
33  #if UAVCAN_TOSTRING
34 std::string CanTxQueueEntry::toString() const
35 {
36  return frame.toString();
37 }
38 #endif
39 
41  if (obj != UAVCAN_NULLPTR) {
42  obj->~CanTxQueueEntry();
43  allocator.deallocate(obj);
44  obj = UAVCAN_NULLPTR;
45  }
46 }
47 
48 /*
49  * CanTxQueue
50  */
52  // Remove all nodes & node contents of the tree without performing re-balancing steps
53  postOrderTraverseEntryCleanup(root_);
54  // Step 2: AvlTree destructor is called to remove all the Node* (automatically after)
55 }
56 
58  if (n == UAVCAN_NULLPTR) {
59  return;
60  }
61 
62  postOrderTraverseEntryCleanup(n->left);
63  postOrderTraverseEntryCleanup(n->right);
64  CanTxQueueEntry::destroy(n->data, allocator_);
65 }
66 
67 bool CanTxQueue::contains(const CanFrame& frame) const {
68  Node* n = root_;
69 
70  while (n != UAVCAN_NULLPTR) {
71  if (frame.priorityHigherThan(n->data->frame)) {
72  n = n->right;
73  continue;
74  }
75 
76  if (frame.priorityLowerThan(n->data->frame)) {
77  n = n->left;
78  continue;
79  }
80 
81  return linkedListContains(n, frame);
82  }
83  return false;
84 }
85 
87  Node* next = head;
88  while(next != UAVCAN_NULLPTR) {
89  if (next->data->frame == frame) {
90  return true;
91  }
92 
93  next = head->equal_keys;
94  }
95  return false;
96 }
97 
99  if (rejected_frames_cnt_ < NumericTraits<uint32_t>::max()) {
100  rejected_frames_cnt_++;
101  }
102 }
103 
104 void CanTxQueue::push(const CanFrame &frame, MonotonicTime tx_deadline, CanIOFlags flags) {
105  const MonotonicTime timestamp = sysclock_.getMonotonic();
106 
107  if (timestamp >= tx_deadline) {
108  UAVCAN_TRACE("CanTxQueue", "Push rejected: already expired");
109  safeIncrementRejectedFrames();
110  return;
111  }
112 
113  void* praw = allocator_.allocate(sizeof(CanTxQueueEntry));
114  if (praw == UAVCAN_NULLPTR) {
115  UAVCAN_TRACE("CanTxQueue", "Push rejected: OOM (CanTxQueueEntry)");
116  safeIncrementRejectedFrames();
117  return;
118  }
119 
120  CanTxQueueEntry* entry = new(praw) CanTxQueueEntry(frame, tx_deadline, flags);
121  UAVCAN_ASSERT(entry);
122  bool result = AvlTree::insert(entry);
123 
124  if (!result) {
125  /* AVL Tree could not allocate a new node */
126  UAVCAN_TRACE("CanTxQueue", "Push rejected: OOM (AvlTree::Node)");
127  safeIncrementRejectedFrames();
128  CanTxQueueEntry::destroy(entry, allocator_);
129  }
130 }
131 
133  if (entry == UAVCAN_NULLPTR) {
134  return;
135  }
136 
137  // Make the AvlTree remove the specific entry deleting it's Node *
138  this->AvlTree::removeEntry(entry);
139  // Then let the entry destroy it's own contents
140  CanTxQueueEntry::destroy(entry, allocator_);
141 }
142 
144  Node* maxNode = searchForNonExpiredMax(root_);
145 
146  if (maxNode == UAVCAN_NULLPTR) {
147  return UAVCAN_NULLPTR;
148  }
149 
150  return maxNode->data;
151 }
152 
154  CanTxQueueEntry* peek_entry = peek();
155  if (peek_entry == UAVCAN_NULLPTR) {
156  return false;
157  }
158  return !rhs_frame.priorityHigherThan(peek_entry->frame);
159 }
160 
162  if (n == UAVCAN_NULLPTR) {
163  return UAVCAN_NULLPTR;
164  }
165 
166  const MonotonicTime timestamp = sysclock_.getMonotonic();
167 
168  while(n->data->isExpired(timestamp)) {
169  remove(n->data);
170  return searchForNonExpiredMax(root_);
171  }
172 
173  while(n->right != UAVCAN_NULLPTR && n->right->data->isExpired(timestamp)) {
174  CanTxQueueEntry* expiredEntry = n->data;
175  n->right = this->AvlTree::removeNode(n, n->data);
176  CanTxQueueEntry::destroy(expiredEntry, allocator_);
177  }
178 
179  Node* r = searchForNonExpiredMax(n->right);
180 
181  if (r != UAVCAN_NULLPTR) {
182  return r;
183  }
184 
185  return n;
186 }
187 
188 /*
189  * CanIOManager
190  */
191 int
193 {
194  UAVCAN_ASSERT(iface_index < MaxCanIfaces);
195  ICanIface *const iface = driver_.getIface(iface_index);
196  if (iface == UAVCAN_NULLPTR)
197  {
198  UAVCAN_ASSERT(0); // Nonexistent interface
199  return -ErrLogic;
200  }
201  const int res = iface->send(frame, tx_deadline, flags);
202  if (res != 1)
203  {
204  UAVCAN_TRACE("CanIOManager", "Send failed: code %i, iface %i, frame %s",
205  res, iface_index, frame.toString().c_str());
206  }
207  if (res > 0)
208  {
209  counters_[iface_index].frames_tx += unsigned(res);
210  }
211  return res;
212 }
213 
215 {
216  UAVCAN_ASSERT(iface_index < MaxCanIfaces);
217  CanTxQueueEntry* entry = tx_queues_[iface_index]->peek();
218  if (entry == UAVCAN_NULLPTR)
219  {
220  return 0;
221  }
222  const int res = sendToIface(iface_index, entry->frame, entry->deadline, entry->flags);
223  if (res > 0)
224  {
225  tx_queues_[iface_index]->remove(entry);
226  }
227  return res;
228 }
229 
230 int CanIOManager::callSelect(CanSelectMasks &inout_masks, const CanFrame *(&pending_tx)[MaxCanIfaces],
231  MonotonicTime blocking_deadline)
232 {
233  const CanSelectMasks in_masks = inout_masks;
234 
235  const int res = driver_.select(inout_masks, pending_tx, blocking_deadline);
236  if (res < 0)
237  {
238  return -ErrDriver;
239  }
240 
241  inout_masks.read &= in_masks.read; // Driver is not required to clean the masks
242  inout_masks.write &= in_masks.write;
243  return res;
244 }
245 
247  std::size_t mem_blocks_per_iface)
248  : driver_(driver), sysclock_(sysclock), num_ifaces_(driver.getNumIfaces())
249 {
250  if (num_ifaces_ < 1 || num_ifaces_ > MaxCanIfaces)
251  {
252  handleFatalError("Num ifaces");
253  }
254 
255  if (mem_blocks_per_iface == 0)
256  {
257  mem_blocks_per_iface = allocator.getBlockCapacity() / (num_ifaces_ + 1U) + 1U;
258  }
259  UAVCAN_TRACE("CanIOManager", "Memory blocks per iface: %u, total: %u",
260  unsigned(mem_blocks_per_iface), unsigned(allocator.getBlockCapacity()));
261 
262  for (int i = 0; i < num_ifaces_; i++)
263  {
265  (allocator, sysclock, mem_blocks_per_iface);
266  }
267 }
268 
270 {
271  uint8_t write_mask = 0;
272  for (uint8_t i = 0; i < getNumIfaces(); i++)
273  {
274  if (!tx_queues_[i]->isEmpty())
275  {
276  write_mask = uint8_t(write_mask | (1 << i));
277  }
278  }
279  return write_mask;
280 }
281 
283 {
284  ICanIface *const iface = driver_.getIface(iface_index);
285  if (iface == UAVCAN_NULLPTR || iface_index >= MaxCanIfaces)
286  {
287  UAVCAN_ASSERT(0);
288  return CanIfacePerfCounters();
289  }
291  cnt.errors = iface->getErrorCount() + tx_queues_[iface_index]->getRejectedFrameCount();
292  cnt.frames_rx = counters_[iface_index].frames_rx;
293  cnt.frames_tx = counters_[iface_index].frames_tx;
294  return cnt;
295 }
296 
297 int CanIOManager::send(const CanFrame &frame, MonotonicTime tx_deadline, MonotonicTime blocking_deadline,
298  uint8_t iface_mask, CanIOFlags flags)
299 {
300  const uint8_t num_ifaces = getNumIfaces();
301  const uint8_t all_ifaces_mask = uint8_t((1U << num_ifaces) - 1);
302  iface_mask &= all_ifaces_mask;
303 
304  if (blocking_deadline > tx_deadline)
305  {
306  blocking_deadline = tx_deadline;
307  }
308 
309  int retval = 0;
310 
311  while (true) // Somebody please refactor this.
312  {
313  if (iface_mask == 0)
314  {
315  break;
316  }
317 
318  CanSelectMasks masks;
319  masks.write = iface_mask | makePendingTxMask();
320  {
321  // Building the list of next pending frames per iface.
322  // This is needed to avoid inner priority inversion in the TX queue.
323  // This is explained in the section 4.4.3.3 of the spec.
324  const CanFrame* pending_tx[MaxCanIfaces] = {};
325  for (int i = 0; i < num_ifaces; i++)
326  {
327  CanTxQueue &q = *tx_queues_[i];
328  CanTxQueueEntry* peek_entry = q.peek();
329  const CanFrame *peek_frame = peek_entry == UAVCAN_NULLPTR ? UAVCAN_NULLPTR : &peek_entry->frame;
330 
331  if (iface_mask & (1 << i)) // I hate myself so much right now.
332  {
333  bool has_priority = false;
334 
335  // This may seem duplicate of topPriorityHigherOrEqual but we want to avoid traversing the queue again
336  if (peek_entry != UAVCAN_NULLPTR)
337  {
338  has_priority = !frame.priorityHigherThan(*peek_frame);
339  }
340 
341  pending_tx[i] = has_priority ? peek_frame : &frame;
342  }
343  else
344  {
345  pending_tx[i] = peek_frame;
346  }
347  }
348 
349  const int select_res = callSelect(masks, pending_tx, blocking_deadline);
350  if (select_res < 0)
351  {
352  return -ErrDriver;
353  }
354  UAVCAN_ASSERT(masks.read == 0);
355  }
356 
357  // Transmission
358  for (uint8_t i = 0; i < num_ifaces; i++)
359  {
360  if (masks.write & (1 << i))
361  {
362  int res = 0;
363  if (iface_mask & (1 << i))
364  {
365  if (tx_queues_[i]->topPriorityHigherOrEqual(frame))
366  {
367  res = sendFromTxQueue(
368  i); // May return 0 if nothing to transmit (e.g. expired)
369  }
370  if (res <= 0)
371  {
372  res = sendToIface(i, frame, tx_deadline, flags);
373  if (res > 0)
374  {
375  iface_mask &= uint8_t(~(1 << i)); // Mark transmitted
376  }
377  }
378  }
379  else
380  {
381  res = sendFromTxQueue(i);
382  }
383  if (res > 0)
384  {
385  retval++;
386  }
387  }
388  }
389 
390  // Timeout. Enqueue the frame if wasn't transmitted and leave.
391  const bool timed_out = sysclock_.getMonotonic() >= blocking_deadline;
392  if (masks.write == 0 || timed_out)
393  {
394  if (!timed_out)
395  {
396  UAVCAN_TRACE("CanIOManager", "Send: Premature timeout in select(), will try again");
397  continue;
398  }
399  for (uint8_t i = 0; i < num_ifaces; i++)
400  {
401  if (iface_mask & (1 << i))
402  {
403  tx_queues_[i]->push(frame, tx_deadline, flags);
404  }
405  }
406  break;
407  }
408  }
409  return retval;
410 }
411 
412 int CanIOManager::receive(CanRxFrame &out_frame, MonotonicTime blocking_deadline, CanIOFlags &out_flags)
413 {
414  const uint8_t num_ifaces = getNumIfaces();
415 
416  while (true)
417  {
418  CanSelectMasks masks;
419  masks.write = makePendingTxMask();
420  masks.read = uint8_t((1 << num_ifaces) - 1);
421  {
422  const CanFrame* pending_tx[MaxCanIfaces] = {};
423  for (int i = 0; i < num_ifaces; i++) // Dear compiler, kindly unroll this. Thanks.
424  {
425  CanTxQueueEntry* entry = tx_queues_[i]->peek();
426  pending_tx[i] = (entry == UAVCAN_NULLPTR) ? UAVCAN_NULLPTR : &entry->frame;
427  }
428 
429  const int select_res = callSelect(masks, pending_tx, blocking_deadline);
430  if (select_res < 0)
431  {
432  return -ErrDriver;
433  }
434  }
435 
436  // Write - if buffers are not empty, one frame will be sent for each iface per one receive() call
437  for (uint8_t i = 0; i < num_ifaces; i++)
438  {
439  if (masks.write & (1 << i))
440  {
441  (void) sendFromTxQueue(
442  i); // It may fail, we don't care. Requested operation was receive, not send.
443  }
444  }
445 
446  // Read
447  for (uint8_t i = 0; i < num_ifaces; i++)
448  {
449  if (masks.read & (1 << i))
450  {
451  ICanIface *const iface = driver_.getIface(i);
452  if (iface == UAVCAN_NULLPTR)
453  {
454  UAVCAN_ASSERT(0); // Nonexistent interface
455  continue;
456  }
457 
458  const int res = iface->receive(out_frame, out_frame.ts_mono, out_frame.ts_utc, out_flags);
459  if (res == 0)
460  {
461  UAVCAN_ASSERT(0); // select() reported that iface has pending RX frames, but receive() returned none
462  continue;
463  }
464  out_frame.iface_index = i;
465 
466  if ((res > 0) && !(out_flags & CanIOFlagLoopback))
467  {
468  counters_[i].frames_rx += 1;
469  }
470  return (res < 0) ? -ErrDriver : res;
471  }
472  }
473 
474  // Timeout checked in the last order - this way we can operate with expired deadline:
475  if (sysclock_.getMonotonic() >= blocking_deadline)
476  {
477  break;
478  }
479  }
480  return 0;
481 }
482 
483 }
CanIfacePerfCounters getIfacePerfCounters(uint8_t iface_index) const
Definition: uc_can_io.cpp:282
bool contains(const CanFrame &frame) const
Definition: uc_can_io.cpp:67
std::uint8_t uint8_t
Definition: std.hpp:24
uint8_t getNumIfaces() const
Definition: can_io.hpp:158
void postOrderTraverseEntryCleanup(Node *n)
Definition: uc_can_io.cpp:57
virtual MonotonicTime getMonotonic() const =0
LazyConstructor< CanTxQueue > tx_queues_[MaxCanIfaces]
Definition: can_io.hpp:144
int receive(CanRxFrame &out_frame, MonotonicTime blocking_deadline, CanIOFlags &out_flags)
Definition: uc_can_io.cpp:412
UtcTime ts_utc
Definition: can_io.hpp:28
virtual void deallocate(const void *ptr)=0
virtual int16_t send(const CanFrame &frame, MonotonicTime tx_deadline, CanIOFlags flags)=0
CanTxQueueEntry * peek()
Definition: uc_can_io.cpp:143
int sendToIface(uint8_t iface_index, const CanFrame &frame, MonotonicTime tx_deadline, CanIOFlags flags)
Definition: uc_can_io.cpp:192
void push(const CanFrame &frame, MonotonicTime tx_deadline, CanIOFlags flags)
Definition: uc_can_io.cpp:104
bool insert(T *data)
Definition: avl_tree.hpp:328
MonotonicTime ts_mono
Definition: can_io.hpp:27
MonotonicTime deadline
Definition: can_io.hpp:42
virtual int16_t receive(CanFrame &out_frame, MonotonicTime &out_ts_monotonic, UtcTime &out_ts_utc, CanIOFlags &out_flags)=0
const CanFrame frame
Definition: can_io.hpp:43
Node * removeNode(Node *node, T *data)
Definition: avl_tree.hpp:233
const uint8_t num_ifaces_
Definition: can_io.hpp:147
UAVCAN_EXPORT void handleFatalError(const char *msg)
Definition: uc_error.cpp:20
uint8_t iface_index
Definition: can_io.hpp:29
AvlTree::Node * searchForNonExpiredMax(Node *n)
Definition: uc_can_io.cpp:161
IfaceFrameCounters counters_[MaxCanIfaces]
Definition: can_io.hpp:145
unsigned long size_t
void remove(CanTxQueueEntry *entry)
Definition: uc_can_io.cpp:132
static std::string toString(long x)
Definition: multiset.cpp:16
CanIOManager(ICanDriver &driver, IPoolAllocator &allocator, ISystemClock &sysclock, std::size_t mem_blocks_per_iface=0)
Definition: uc_can_io.cpp:246
ISystemClock & sysclock_
Definition: can_io.hpp:142
UAVCAN_EXPORT const T & max(const T &a, const T &b)
Definition: templates.hpp:291
void toString(char buf[StringBufSize]) const
Prints time in seconds with microsecond resolution.
Definition: time.hpp:238
bool topPriorityHigherOrEqual(const CanFrame &rhs_frame)
Definition: uc_can_io.cpp:153
int callSelect(CanSelectMasks &inout_masks, const CanFrame *(&pending_tx)[MaxCanIfaces], MonotonicTime blocking_deadline)
Definition: uc_can_io.cpp:230
bool linkedListContains(Node *head, const CanFrame &frame) const
Definition: uc_can_io.cpp:86
bool priorityHigherThan(const CanFrame &rhs) const
Definition: uc_can.cpp:19
bool priorityLowerThan(const CanFrame &rhs) const
uint8_t makePendingTxMask() const
Definition: uc_can_io.cpp:269
static void destroy(CanTxQueueEntry *&obj, IPoolAllocator &allocator)
Definition: uc_can_io.cpp:40
void safeIncrementRejectedFrames()
Definition: uc_can_io.cpp:98
virtual ICanIface * getIface(uint8_t iface_index)=0
uavcan::CanFrame frame
Definition: can.cpp:78
virtual uint64_t getErrorCount() const =0
static const CanIOFlags CanIOFlagLoopback
virtual uint16_t getBlockCapacity() const =0
int sendFromTxQueue(uint8_t iface_index)
Definition: uc_can_io.cpp:214
void removeEntry(T *data)
Definition: avl_tree.hpp:324
~CanTxQueue() override
Definition: uc_can_io.cpp:51
int send(const CanFrame &frame, MonotonicTime tx_deadline, MonotonicTime blocking_deadline, uint8_t iface_mask, CanIOFlags flags)
Definition: uc_can_io.cpp:297
ICanDriver & driver_
Definition: can_io.hpp:141


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