VSM C++ SDK
Vehicle Specific Modules SDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
mavlink_decoder.h
Go to the documentation of this file.
1 // Copyright (c) 2018, Smart Projects Holdings Ltd
2 // All rights reserved.
3 // See LICENSE file for license details.
4 
10 #include <ugcs/vsm/callback.h>
11 #include <ugcs/vsm/mavlink.h>
12 
13 #include <unordered_map>
14 
15 #ifndef _UGCS_VSM_MAVLINK_DECODER_H_
16 #define _UGCS_VSM_MAVLINK_DECODER_H_
17 
18 namespace ugcs {
19 namespace vsm {
20 
23 private:
25  enum class State {
27  STX,
29  VER1,
31  VER2
32  };
33 
34 public:
42  uint8_t, uint8_t, uint32_t> Handler;
43 
46 
49  Make_decoder_handler,
50  (Io_buffer::Ptr, mavlink::MESSAGE_ID_TYPE, uint8_t, uint8_t, uint32_t),
51  (nullptr, mavlink::MESSAGE_ID::DEBUG_VALUE, mavlink::SYSTEM_ID_NONE, 0, 0))
52 
53 
54  DEFINE_CALLBACK_BUILDER(Make_raw_data_handler, (Io_buffer::Ptr), (nullptr))
55 
57  struct Stats {
59  uint64_t handled = 0;
61  uint64_t no_handler = 0;
63  uint64_t bad_checksum = 0;
65  uint64_t bad_length = 0;
69  uint64_t unknown_id = 0;
71  uint64_t bytes_received = 0;
74  uint64_t stx_syncs = 0;
75  };
76 
77 
80  packet_buf(ugcs::vsm::Io_buffer::Create())
81  {
82  }
83 
85  Mavlink_decoder(const Mavlink_decoder&) = delete;
86 
88  void
90  {
91  handler = Handler();
92  data_handler = Raw_data_handler();
93  }
94 
98  void
100  {
101  this->handler = handler;
102  }
103 
104  void
105  Register_raw_data_handler(Raw_data_handler handler)
106  {
107  data_handler = handler;
108  }
109 
111  void
113  {
114  // LOG_DBG("read: %s", buffer->Get_hex().c_str());
115  if (data_handler) {
116  data_handler(buffer);
117  }
118  packet_buf = packet_buf->Concatenate(buffer);
119  size_t packet_len;
120  const uint8_t* data;
121  next_read_len = 0;
122 
123  while (true) {
124  size_t buffer_len = packet_buf->Get_length();
125  { // lock scope
126  std::lock_guard<std::mutex> stats_lock(stats_mutex);
127  stats[mavlink::SYSTEM_ID_ANY].bytes_received += buffer_len;
128  if (state == State::STX) {
129  size_t len_skipped = 0;
130  if (buffer_len < mavlink::MAVLINK_1_MIN_FRAME_LEN) {
131  // need at least minimum frame length of data.
132  next_read_len = mavlink::MAVLINK_1_MIN_FRAME_LEN - buffer_len;
133  break;
134  }
135  // look for signature in received data.
136  data = static_cast<const uint8_t*>(packet_buf->Get_data());
137  for (; len_skipped < buffer_len; len_skipped++, data++) {
138  if (*data == mavlink::START_SIGN) {
139  // found preamble. Start receiving payload.
140  state = State::VER1;
141  stats[mavlink::SYSTEM_ID_ANY].stx_syncs++;
142  // slice off the preamble.
143  packet_buf = packet_buf->Slice(1);
144  break;
145  }
146  if (*data == mavlink::START_SIGN2) {
147  // found preamble. Start receiving payload.
148  state = State::VER2;
149  stats[mavlink::SYSTEM_ID_ANY].stx_syncs++;
150  // slice off the preamble.
151  packet_buf = packet_buf->Slice(1);
152  break;
153  }
154  }
155  if (len_skipped) {
156  // slice off the skipped bytes.
157  packet_buf = packet_buf->Slice(len_skipped);
158  }
159  }
160  }
161  if (state == State::VER1 || state == State::VER2) {
162  size_t wrapper_len; // non-payload data len excluding signature.
163  if (state == State::VER1) {
164  wrapper_len = mavlink::MAVLINK_1_HEADER_LEN - 1 + 2;
165  } else {
166  wrapper_len = mavlink::MAVLINK_2_HEADER_LEN - 1 + 2;
167  }
168  buffer_len = packet_buf->Get_length();
169  if (buffer_len == 0) {
170  // need at least the minimum packet len. Initiate next read.
171  next_read_len = wrapper_len;
172  break;
173  }
174  data = static_cast<const uint8_t*>(packet_buf->Get_data());
175  packet_len = wrapper_len + static_cast<size_t>(*data);
176  if (packet_len > buffer_len) {
177  // need the whole packet. Initiate next read.
178  next_read_len = packet_len - buffer_len;
179  break;
180  }
181  if (Decode_packet(packet_buf)) {
182  // decoder suceeded. Slice off the decoded packet.
183  packet_buf = packet_buf->Slice(packet_len);
184  }
185  // if decoder failed, we restart the search for
186  // next preamble in existing data otherwise
187  // continue with next byte after the decoded packet.
188  state = State::STX;
189  }
190  }
191  }
192 
197  size_t
199  {
200  return next_read_len;
201  }
202 
208  const Mavlink_decoder::Stats
209  Get_stats(int system_id)
210  {
211  std::lock_guard<std::mutex> lock(stats_mutex);
212  return stats[system_id];
213  }
214 
216  const Mavlink_decoder::Stats
218  {
219  std::lock_guard<std::mutex> lock(stats_mutex);
220  return stats[mavlink::SYSTEM_ID_ANY];
221  }
222 
223 private:
224  bool
225  Decode_packet(Io_buffer::Ptr buffer)
226  {
227  auto data = static_cast<const uint8_t*>(buffer->Get_data());
228  uint16_t payload_len = data[0];
229  uint8_t system_id;
230  uint8_t component_id;
231  uint8_t seq;
232  uint8_t header_len;
234 
235  if (state == State::VER2) {
236  seq = data[3];
237  system_id = data[4];
238  component_id = data[5];
239  msg_id =
240  static_cast<int>(data[6]) +
241  (static_cast<int>(data[7]) << 8) +
242  (static_cast<int>(data[8]) << 16);
243  header_len = mavlink::MAVLINK_2_HEADER_LEN - 1;
244  } else {
245  seq = data[1];
246  system_id = data[2];
247  component_id = data[3];
248  msg_id = data[4];
249  header_len = mavlink::MAVLINK_1_HEADER_LEN - 1;
250  }
251 
252  mavlink::Checksum sum(data, header_len);
253 
254  mavlink::Extra_byte_length_pair crc_byte_len_pair;
255  // Try all extensions.
256  if ( !sum.Get_extra_byte_length_pair(msg_id, crc_byte_len_pair, mavlink::Extension::Get())
257  && !sum.Get_extra_byte_length_pair(msg_id, crc_byte_len_pair, mavlink::apm::Extension::Get())
258  && !sum.Get_extra_byte_length_pair(msg_id, crc_byte_len_pair, mavlink::sph::Extension::Get())) {
259  std::lock_guard<std::mutex> stats_lock(stats_mutex);
260  stats[mavlink::SYSTEM_ID_ANY].unknown_id++;
261  // LOG_DEBUG("Unknown Mavlink message id: %d (%X)", msg_id, msg_id);
262  return false;
263  }
264 
265  sum.Accumulate(data + header_len, payload_len);
266 
267  uint16_t sum_calc = sum.Accumulate(crc_byte_len_pair.first);
268  /* Convert checksum in Mavlink byte order to a host byte order
269  * compatible type. */
270  auto sum_recv = reinterpret_cast<const mavlink::Uint16*>(data + header_len + payload_len);
271 
272  bool cksum_ok = sum_calc == *sum_recv;
273  bool length_ok = crc_byte_len_pair.second == payload_len;
274 
275  std::unique_lock<std::mutex> stats_lock(stats_mutex);
276  if (cksum_ok && (length_ok || state == State::VER2)) {
277  /*
278  * Fully valid packet received.
279  */
280  if (handler) {
281  stats[system_id].handled++;
282  stats[mavlink::SYSTEM_ID_ANY].handled++;
283  stats_lock.unlock();
284  handler(Io_buffer::Create(*buffer, header_len, payload_len), msg_id, system_id, component_id, seq);
285  } else {
286  stats[system_id].no_handler++;
287  stats[mavlink::SYSTEM_ID_ANY].no_handler++;
288  LOG_DEBUG("Mavlink message %d handler not registered.", msg_id);
289  }
290  return true;
291  } else {
292  if (cksum_ok) {
293  stats[system_id].bad_length++;
294  stats[mavlink::SYSTEM_ID_ANY].bad_length++;
295  LOG_DEBUG("Mavlink payload length mismatch, recv=%d wanted=%d.",
296  payload_len, crc_byte_len_pair.second);
297  } else {
298  stats[mavlink::SYSTEM_ID_ANY].bad_checksum++;
299  }
300  return false;
301  }
302  }
303 
304 
306  State state = State::STX;
307 
309  Handler handler;
310 
312  Raw_data_handler data_handler;
313 
315  std::unordered_map<int, Stats> stats;
316  std::mutex stats_mutex;
317 
319  ugcs::vsm::Io_buffer::Ptr packet_buf;
320 
321  size_t next_read_len = mavlink::MAVLINK_1_MIN_FRAME_LEN;
322 };
323 
324 } /* namespace vsm */
325 } /* namespace ugcs */
326 
327 #endif /* _UGCS_VSM_MAVLINK_DECODER_H_ */
#define LOG_DEBUG(msg,...)
Write debug message to the application log.
Definition: log.h:323
#define DEFINE_CALLBACK_BUILDER(__name, __types, __values)
Define callback builder function.
Definition: callback.h:42
static Ptr Create(Args &&...args)
Create an instance.
Definition: io_buffer.h:34
Generic callback which can be used to define and create an instance of an abstract callable operation...
Helper class for proxying callback invocation.
Definition: callback.h:699
T Get() const
Get the value of underlying type.
Definition: endian.h:344
Generic I/O buffer.
Definition: io_buffer.h:33
std::shared_ptr< Io_buffer > Ptr
Pointer type.
Definition: io_buffer.h:34