Skyward boardcore
Loading...
Searching...
No Matches
MavlinkDriver.h
Go to the documentation of this file.
1/* Copyright (c) 2018-2019 Skyward Experimental Rocketry
2 * Author: Alvise de'Faveri Tron
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#pragma once
24
25#include <vector>
26
32#ifndef MAVLINK_H
33#error \
34 "[MavlinkDriver] Mavlink header not found! Please include your mavlink.h \
35implementation before including MavlinkDriver.h"
36#endif
37
41#include <mavlink_lib/mavlink_types.h>
42#include <radio/Transceiver.h>
44
45#include <functional>
46
47#include "MavlinkStatus.h"
48
49namespace Boardcore
50{
51
64template <unsigned int PktLength, unsigned int OutQueueSize,
65 unsigned int MavMsgLength = MAVLINK_MAX_PAYLOAD_LEN>
67{
69 using MavHandler = std::function<void(MavlinkDriver* channel,
70 const mavlink_message_t& msg)>;
71
72public:
82 MavlinkDriver(Transceiver* device, MavHandler onReceive = nullptr,
83 uint16_t sleepAfterSend = 0, size_t outBufferMaxAge = 1000);
84
90 bool start();
91
95 bool isStarted();
96
100 void stop();
101
109 bool enqueueMsg(const mavlink_message_t& msg);
110
118 bool enqueueRaw(uint8_t* msg, size_t size);
119
124
128 void setSleepAfterSend(uint16_t newSleepTime);
129
130private:
138 void runReceiver();
139
147 void runSender();
148
154 static void rcvLauncher(void* arg)
155 {
156 reinterpret_cast<MavlinkDriver*>(arg)->runReceiver();
157 }
158
164 static void sndLauncher(void* arg)
165 {
166 reinterpret_cast<MavlinkDriver*>(arg)->runSender();
167 }
168
169 void updateQueueStats(bool appended);
170
171 void updateSenderStats(size_t msgCount, bool sent);
172
173 Transceiver* device;
174 MavHandler onReceive;
175
176 // Tweakable params
177 uint16_t sleepAfterSend;
178 size_t outBufferMaxAge;
179 uint16_t pollingTime = 100; // ms
180
181 // Buffers (equal to MTU for ethernet)
182 static constexpr size_t MAV_IN_BUFFER_SIZE = 1500;
183
184 SyncPacketQueue<PktLength, OutQueueSize> outQueue;
185 std::unique_ptr<uint8_t[]> rcvBuffer =
186 std::make_unique<uint8_t[]>(MAV_IN_BUFFER_SIZE);
187
188 // Status
189 MavlinkStatus status;
190 miosix::FastMutex mtxStatus;
191
192 // Threads
193 bool stopFlag = false;
194 bool sndStarted = false;
195 bool rcvStarted = false;
196
197 miosix::Thread* sndThread = nullptr;
198 miosix::Thread* rcvThread = nullptr;
199
200 PrintLogger logger = Logging::getLogger("mavlinkdriver");
201};
202
203template <unsigned int PktLength, unsigned int OutQueueSize,
204 unsigned int MavMsgLength>
206 Transceiver* device, MavHandler onReceive, uint16_t sleepAfterSend,
207 size_t outBufferMaxAge)
208 : device(device), onReceive(onReceive), sleepAfterSend(sleepAfterSend),
209 outBufferMaxAge(outBufferMaxAge)
210{
211 memset(&status, 0, sizeof(MavlinkStatus));
212}
213
214template <unsigned int PktLength, unsigned int OutQueueSize,
215 unsigned int MavMsgLength>
217{
218 stopFlag = false;
219
220 // Start sender (joinable thread)
221 if (!sndStarted)
222 {
223 sndThread = miosix::Thread::create(
224 sndLauncher, skywardStack(4 * 1024), miosix::MAIN_PRIORITY,
225 reinterpret_cast<void*>(this), miosix::Thread::JOINABLE);
226
227 if (sndThread != nullptr)
228 sndStarted = true;
229 else
230 LOG_ERR(logger, "Could not start sender!");
231 }
232
233 // Start receiver
234 if (!rcvStarted)
235 {
236 rcvThread = miosix::Thread::create(rcvLauncher, skywardStack(4 * 1024),
237 miosix::MAIN_PRIORITY,
238 reinterpret_cast<void*>(this));
239
240 if (rcvThread != nullptr)
241 rcvStarted = true;
242 else
243 LOG_ERR(logger, "Could not start receiver!");
244 }
245
246 if (sndStarted && rcvStarted)
247 LOG_DEBUG(logger, "Sender and receiver started");
248
249 return sndStarted && rcvStarted;
250}
251
252template <unsigned int PktLength, unsigned int OutQueueSize,
253 unsigned int MavMsgLength>
255{
256 return sndStarted && rcvStarted;
257}
258
259template <unsigned int PktLength, unsigned int OutQueueSize,
260 unsigned int MavMsgLength>
262{
263 stopFlag = true;
264
265 // Wait for sender to stop
266 sndThread->join();
267}
268
269template <unsigned int PktLength, unsigned int OutQueueSize,
270 unsigned int MavMsgLength>
272 const mavlink_message_t& msg)
273{
274 // Convert mavlink message to a byte array
275 uint8_t msgTempBuf[MAVLINK_NUM_NON_PAYLOAD_BYTES + MavMsgLength];
276 int msgLen = mavlink_msg_to_send_buffer(msgTempBuf, &msg);
277
278 // Append the message to the queue
279 bool appended = outQueue.put(msgTempBuf, msgLen);
280
281 // Update stats
282 updateQueueStats(appended);
283
284 // Return ok even if a packet was discarded
285 return appended;
286}
287
288template <unsigned int PktLength, unsigned int OutQueueSize,
289 unsigned int MavMsgLength>
291 uint8_t* msg, size_t size)
292{
293 // Append message to the queue
294 bool appended = outQueue.put(msg, size);
295
296 // Update stats
297 updateQueueStats(appended);
298
299 // Return ok even if a packet was discarded
300 return appended;
301}
302
303template <unsigned int PktLength, unsigned int OutQueueSize,
304 unsigned int MavMsgLength>
306 bool appended)
307{
308 miosix::Lock<miosix::FastMutex> l(mtxStatus);
309
310 if (!appended)
311 {
312 LOG_ERR(logger, "Buffer full, the oldest message has been discarded");
313 status.nDroppedPackets++;
314 }
315
316 status.nSendQueue++;
317
318 if (status.nSendQueue > status.maxSendQueue)
319 status.maxSendQueue = status.nSendQueue;
320}
321
322template <unsigned int PktLength, unsigned int OutQueueSize,
323 unsigned int MavMsgLength>
324void MavlinkDriver<PktLength, OutQueueSize, MavMsgLength>::runReceiver()
325{
326 mavlink_message_t msg;
327 ssize_t rcvSize;
328 uint8_t parseResult = 0;
329
330 while (!stopFlag)
331 {
332 // Check for a new message on the device
333 rcvSize = device->receive(rcvBuffer.get(), MAV_IN_BUFFER_SIZE);
334
335 // If there's a new message ...
336 if (rcvSize > 0)
337 {
338 parseResult = 0;
339 miosix::Lock<miosix::FastMutex> l(mtxStatus);
340
341 for (ssize_t i = 0; i < rcvSize; i++)
342 {
343 // ... parse received bytes
344 parseResult =
345 mavlink_parse_char(MAVLINK_COMM_0,
346 rcvBuffer[i], // byte to parse
347 &msg, // where to parse it
348 &(status.mavStats)); // stats to update
349
350 // When a valid message is found ...
351 if (parseResult == 1)
352 {
353 // Unlock mutex before calling the callback, no one knows
354 // what could happen.
355 miosix::Unlock<miosix::FastMutex> unlock(l);
356
357 LOG_DEBUG(logger,
358 "Received message with ID {}, sequence: {} from "
359 "component {} of system {}",
360 msg.msgid, msg.seq, msg.compid, msg.sysid);
361
362 // ... handle the command
363 if (onReceive != nullptr)
364 onReceive(this, msg);
365
367 }
368 }
369 }
370 }
371}
372
373template <unsigned int PktLength, unsigned int OutQueueSize,
374 unsigned int MavMsgLength>
375void MavlinkDriver<PktLength, OutQueueSize, MavMsgLength>::runSender()
376{
377 LOG_DEBUG(logger, "Sender is running");
378 Packet<PktLength> pkt;
379
380 while (!stopFlag)
381 {
382 outQueue.waitUntilNotEmpty();
383
384 if (!outQueue.isEmpty())
385 {
386 // Get the first packet in the queue, without removing it
387 pkt = outQueue.get();
388
389 // If the packet is ready or too old, send it
390 uint64_t age = TimestampTimer::getTimestamp() - pkt.getTimestamp();
391 if (pkt.isReady() || age >= outBufferMaxAge * 1e3)
392 {
393 outQueue.pop(); // Remove the packet from queue
394
395 LOG_DEBUG(logger, "Sending packet. Size: {} (age: {})",
396 pkt.size(), age);
397
398 bool sent = device->send(pkt.content.data(), pkt.size());
399 updateSenderStats(pkt.getMsgCount(), sent);
400
401 miosix::Thread::sleep(sleepAfterSend);
402 }
403 else
404 {
405 // Wait before sending something else
406 miosix::Thread::sleep(50);
407 }
408 }
409 }
410}
411
412template <unsigned int PktLength, unsigned int OutQueueSize,
413 unsigned int MavMsgLength>
414void MavlinkDriver<PktLength, OutQueueSize, MavMsgLength>::updateSenderStats(
415 size_t msgCount, bool sent)
416{
417 {
418 miosix::Lock<miosix::FastMutex> l(mtxStatus);
419 status.nSendQueue -= msgCount;
420
421 if (!sent)
422 {
423 status.nSendErrors++;
424 LOG_ERR(logger, "Could not send message");
425 }
426 }
427
429}
430
431template <unsigned int PktLength, unsigned int OutQueueSize,
432 unsigned int MavMsgLength>
434{
435 miosix::Lock<miosix::FastMutex> l(mtxStatus);
436 status.timestamp = TimestampTimer::getTimestamp();
437 return status;
438}
439
440template <unsigned int PktLength, unsigned int OutQueueSize,
441 unsigned int MavMsgLength>
443 uint16_t newSleepTime)
444{
445 sleepAfterSend = newSleepTime;
446}
447
448} // namespace Boardcore
#define LOG_ERR(logger,...)
#define LOG_DEBUG(logger,...)
static PrintLogger getLogger(const string &name)
static StackLogger & getInstance()
Definition Singleton.h:52
PrintLogger l
Definition CanDriver.cpp:41
uint64_t getTimestamp()
Returns the current timer value in microseconds.
This file includes all the types the logdecoder script will decode.
unsigned int skywardStack(unsigned int stack)
@ THID_MAV_SENDER
Definition StackData.h:36
@ THID_MAV_RECEIVER
Definition StackData.h:35