• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mavlink / MAVSDK / 16485391475

24 Jul 2025 01:13AM UTC coverage: 46.33% (+1.2%) from 45.096%
16485391475

push

github

web-flow
Merge pull request #2610 from mavlink/pr-add-libmavlike

Integrate parts of libmav into MAVSDK and add MavlinkDirect plugin

764 of 987 new or added lines in 14 files covered. (77.41%)

10 existing lines in 1 file now uncovered.

16272 of 35122 relevant lines covered (46.33%)

359.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.39
/src/mavsdk/core/mavsdk_impl.cpp
1
#include "mavsdk_impl.h"
2

3
#include <algorithm>
4
#include <mutex>
5
#include <tcp_server_connection.h>
6

7
#include "connection.h"
8
#include "log.h"
9
#include "tcp_client_connection.h"
10
#include "tcp_server_connection.h"
11
#include "udp_connection.h"
12
#include "system.h"
13
#include "system_impl.h"
14
#include "serial_connection.h"
15
#include "version.h"
16
#include "server_component_impl.h"
17
#include "overloaded.h"
18
#include "mavlink_channels.h"
19
#include "callback_list.tpp"
20
#include "hostname_to_ip.h"
21
#include "embedded_mavlink_xml.h"
22
#include <mav/MessageSet.h>
23

24
namespace mavsdk {
25

26
template class CallbackList<>;
27

28
MavsdkImpl::MavsdkImpl(const Mavsdk::Configuration& configuration) :
101✔
29
    timeout_handler(time),
101✔
30
    call_every_handler(time)
202✔
31
{
32
    LogInfo() << "MAVSDK version: " << mavsdk_version;
101✔
33

34
    if (const char* env_p = std::getenv("MAVSDK_CALLBACK_DEBUGGING")) {
101✔
35
        if (std::string(env_p) == "1") {
×
36
            LogDebug() << "Callback debugging is on.";
×
37
            _callback_debugging = true;
×
38
        }
39
    }
40

41
    if (const char* env_p = std::getenv("MAVSDK_MESSAGE_DEBUGGING")) {
101✔
42
        if (std::string(env_p) == "1") {
×
43
            LogDebug() << "Message debugging is on.";
×
44
            _message_logging_on = true;
×
45
        }
46
    }
47

48
    if (const char* env_p = std::getenv("MAVSDK_SYSTEM_DEBUGGING")) {
101✔
49
        if (std::string(env_p) == "1") {
×
50
            LogDebug() << "System debugging is on.";
×
51
            _system_debugging = true;
×
52
        }
53
    }
54

55
    set_configuration(configuration);
101✔
56

57
    // Initialize MessageSet with embedded XML content in dependency order
58
    _message_set = std::make_unique<mav::MessageSet>();
101✔
59
    _message_set->addFromXMLString(mav_embedded::MINIMAL_XML);
101✔
60
    _message_set->addFromXMLString(mav_embedded::STANDARD_XML);
101✔
61
    _message_set->addFromXMLString(mav_embedded::COMMON_XML);
101✔
62
    _message_set->addFromXMLString(mav_embedded::ARDUPILOTMEGA_XML);
101✔
63

64
    // Start the user callback thread first, so it is ready for anything generated by
65
    // the work thread.
66

67
    _process_user_callbacks_thread =
202✔
68
        new std::thread(&MavsdkImpl::process_user_callbacks_thread, this);
101✔
69

70
    _work_thread = new std::thread(&MavsdkImpl::work_thread, this);
101✔
71
}
101✔
72

73
MavsdkImpl::~MavsdkImpl()
101✔
74
{
75
    {
76
        std::lock_guard<std::mutex> lock(_heartbeat_mutex);
101✔
77
        call_every_handler.remove(_heartbeat_send_cookie);
101✔
78
    }
101✔
79

80
    _should_exit = true;
101✔
81

82
    // Stop work first because we don't want to trigger anything that would
83
    // potentially want to call into user code.
84

85
    if (_work_thread != nullptr) {
101✔
86
        _work_thread->join();
101✔
87
        delete _work_thread;
101✔
88
        _work_thread = nullptr;
101✔
89
    }
90

91
    if (_process_user_callbacks_thread != nullptr) {
101✔
92
        _user_callback_queue.stop();
101✔
93
        _process_user_callbacks_thread->join();
101✔
94
        delete _process_user_callbacks_thread;
101✔
95
        _process_user_callbacks_thread = nullptr;
101✔
96
    }
97

98
    std::lock_guard lock(_mutex);
101✔
99

100
    _systems.clear();
101✔
101
    _connections.clear();
101✔
102
}
202✔
103

104
std::string MavsdkImpl::version()
1✔
105
{
106
    static unsigned version_counter = 0;
107

108
    ++version_counter;
1✔
109

110
    switch (version_counter) {
1✔
111
        case 10:
×
112
            return "You were wondering about the name of this library?";
×
113
        case 11:
×
114
            return "Let's look at the history:";
×
115
        case 12:
×
116
            return "DroneLink";
×
117
        case 13:
×
118
            return "DroneCore";
×
119
        case 14:
×
120
            return "DronecodeSDK";
×
121
        case 15:
×
122
            return "MAVSDK";
×
123
        case 16:
×
124
            return "And that's it...";
×
125
        case 17:
×
126
            return "At least for now ¯\\_(ツ)_/¯.";
×
127
        default:
1✔
128
            return mavsdk_version;
1✔
129
    }
130
}
131

132
std::vector<std::shared_ptr<System>> MavsdkImpl::systems() const
121✔
133
{
134
    std::vector<std::shared_ptr<System>> systems_result{};
121✔
135

136
    std::lock_guard lock(_mutex);
121✔
137
    for (auto& system : _systems) {
185✔
138
        // We ignore the 0 entry because it's just a null system.
139
        // It's only created because the older, deprecated API needs a
140
        // reference.
141
        if (system.first == 0) {
64✔
142
            continue;
×
143
        }
144
        systems_result.push_back(system.second);
64✔
145
    }
146

147
    return systems_result;
121✔
148
}
121✔
149

150
std::optional<std::shared_ptr<System>> MavsdkImpl::first_autopilot(double timeout_s)
40✔
151
{
152
    {
153
        std::lock_guard lock(_mutex);
40✔
154
        for (auto system : _systems) {
40✔
155
            if (system.second->is_connected() && system.second->has_autopilot()) {
×
156
                return system.second;
×
157
            }
158
        }
×
159
    }
40✔
160

161
    if (timeout_s == 0.0) {
40✔
162
        // Don't wait at all.
163
        return {};
×
164
    }
165

166
    auto prom = std::promise<std::shared_ptr<System>>();
40✔
167

168
    std::once_flag flag;
40✔
169
    auto handle = subscribe_on_new_system([this, &prom, &flag]() {
80✔
170
        // Check all systems, not just the first one
171
        auto all_systems = systems();
40✔
172
        for (auto& system : all_systems) {
40✔
173
            if (system->is_connected() && system->has_autopilot()) {
40✔
174
                std::call_once(flag, [&prom, &system]() { prom.set_value(system); });
80✔
175
                break;
40✔
176
            }
177
        }
178
    });
80✔
179

180
    auto fut = prom.get_future();
40✔
181

182
    if (timeout_s > 0.0) {
40✔
183
        if (fut.wait_for(std::chrono::milliseconds(int64_t(timeout_s * 1e3))) ==
40✔
184
            std::future_status::ready) {
185
            unsubscribe_on_new_system(handle);
40✔
186
            return fut.get();
40✔
187

188
        } else {
189
            unsubscribe_on_new_system(handle);
×
190
            return std::nullopt;
×
191
        }
192
    } else {
193
        fut.wait();
×
194
        unsubscribe_on_new_system(handle);
×
195
        return std::optional(fut.get());
×
196
    }
197
}
40✔
198

199
std::shared_ptr<ServerComponent> MavsdkImpl::server_component(unsigned instance)
48✔
200
{
201
    std::lock_guard lock(_mutex);
48✔
202

203
    auto component_type = _configuration.get_component_type();
48✔
204
    switch (component_type) {
48✔
205
        case ComponentType::Autopilot:
48✔
206
        case ComponentType::GroundStation:
207
        case ComponentType::CompanionComputer:
208
        case ComponentType::Camera:
209
        case ComponentType::Gimbal:
210
        case ComponentType::RemoteId:
211
        case ComponentType::Custom:
212
            return server_component_by_type(component_type, instance);
48✔
213
        default:
×
214
            LogErr() << "Unknown component type";
×
215
            return {};
×
216
    }
217
}
48✔
218

219
std::shared_ptr<ServerComponent>
220
MavsdkImpl::server_component_by_type(ComponentType server_component_type, unsigned instance)
48✔
221
{
222
    switch (server_component_type) {
48✔
223
        case ComponentType::Autopilot:
34✔
224
            if (instance == 0) {
34✔
225
                return server_component_by_id(MAV_COMP_ID_AUTOPILOT1);
34✔
226
            } else {
227
                LogErr() << "Only autopilot instance 0 is valid";
×
228
                return {};
×
229
            }
230

231
        case ComponentType::GroundStation:
×
232
            if (instance == 0) {
×
233
                return server_component_by_id(MAV_COMP_ID_MISSIONPLANNER);
×
234
            } else {
235
                LogErr() << "Only one ground station supported at this time";
×
236
                return {};
×
237
            }
238

239
        case ComponentType::CompanionComputer:
1✔
240
            if (instance == 0) {
1✔
241
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER);
1✔
242
            } else if (instance == 1) {
×
243
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER2);
×
244
            } else if (instance == 2) {
×
245
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER3);
×
246
            } else if (instance == 3) {
×
247
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER4);
×
248
            } else {
249
                LogErr() << "Only companion computer 0..3 are supported";
×
250
                return {};
×
251
            }
252

253
        case ComponentType::Camera:
13✔
254
            if (instance == 0) {
13✔
255
                return server_component_by_id(MAV_COMP_ID_CAMERA);
13✔
256
            } else if (instance == 1) {
×
257
                return server_component_by_id(MAV_COMP_ID_CAMERA2);
×
258
            } else if (instance == 2) {
×
259
                return server_component_by_id(MAV_COMP_ID_CAMERA3);
×
260
            } else if (instance == 3) {
×
261
                return server_component_by_id(MAV_COMP_ID_CAMERA4);
×
262
            } else if (instance == 4) {
×
263
                return server_component_by_id(MAV_COMP_ID_CAMERA5);
×
264
            } else if (instance == 5) {
×
265
                return server_component_by_id(MAV_COMP_ID_CAMERA6);
×
266
            } else {
267
                LogErr() << "Only camera 0..5 are supported";
×
268
                return {};
×
269
            }
270

271
        default:
×
272
            LogErr() << "Unknown server component type";
×
273
            return {};
×
274
    }
275
}
276

277
std::shared_ptr<ServerComponent> MavsdkImpl::server_component_by_id(uint8_t component_id)
48✔
278
{
279
    if (component_id == 0) {
48✔
280
        LogErr() << "Server component with component ID 0 not allowed";
×
281
        return nullptr;
×
282
    }
283

284
    std::lock_guard lock(_server_components_mutex);
48✔
285

286
    return server_component_by_id_with_lock(component_id);
48✔
287
}
48✔
288

289
std::shared_ptr<ServerComponent> MavsdkImpl::server_component_by_id_with_lock(uint8_t component_id)
198✔
290
{
291
    for (auto& it : _server_components) {
199✔
292
        if (it.first == component_id) {
97✔
293
            if (it.second != nullptr) {
96✔
294
                return it.second;
96✔
295
            } else {
296
                it.second = std::make_shared<ServerComponent>(*this, component_id);
×
297
            }
298
        }
299
    }
300

301
    _server_components.emplace_back(std::pair<uint8_t, std::shared_ptr<ServerComponent>>(
204✔
302
        component_id, std::make_shared<ServerComponent>(*this, component_id)));
204✔
303

304
    return _server_components.back().second;
102✔
305
}
306

307
void MavsdkImpl::forward_message(mavlink_message_t& message, Connection* connection)
×
308
{
309
    // Forward_message Function implementing Mavlink routing rules.
310
    // See https://mavlink.io/en/guide/routing.html
311

312
    bool forward_heartbeats_enabled = true;
×
313
    const uint8_t target_system_id = get_target_system_id(message);
×
314
    const uint8_t target_component_id = get_target_component_id(message);
×
315

316
    // If it's a message only for us, we keep it, otherwise, we forward it.
317
    const bool targeted_only_at_us =
318
        (target_system_id == get_own_system_id() && target_component_id == get_own_component_id());
×
319

320
    // We don't forward heartbeats unless it's specifically enabled.
321
    const bool heartbeat_check_ok =
×
322
        (message.msgid != MAVLINK_MSG_ID_HEARTBEAT || forward_heartbeats_enabled);
×
323

324
    if (!targeted_only_at_us && heartbeat_check_ok) {
×
325
        unsigned successful_emissions = 0;
×
326
        for (auto& entry : _connections) {
×
327
            // Check whether the connection is not the one from which we received the message.
328
            // And also check if the connection was set to forward messages.
329
            if (entry.connection.get() == connection ||
×
330
                !entry.connection->should_forward_messages()) {
×
331
                continue;
×
332
            }
333
            auto result = (*entry.connection).send_message(message);
×
334
            if (result.first) {
×
335
                successful_emissions++;
×
336
            } else {
337
                _connections_errors_subscriptions.queue(
×
338
                    Mavsdk::ConnectionError{result.second, entry.handle},
×
339
                    [this](const auto& func) { call_user_callback(func); });
×
340
            }
341
        }
×
342
        if (successful_emissions == 0) {
×
343
            LogErr() << "Message forwarding failed";
×
344
        }
345
    }
346
}
×
347

348
void MavsdkImpl::receive_message(mavlink_message_t& message, Connection* connection)
2,133✔
349
{
350
    {
351
        std::lock_guard lock(_received_messages_mutex);
2,133✔
352
        _received_messages.emplace(ReceivedMessage{std::move(message), connection});
2,140✔
353
    }
2,141✔
354
    _received_messages_cv.notify_one();
2,142✔
355
}
2,143✔
356

357
void MavsdkImpl::receive_libmav_message(const LibmavMessage& message, Connection* connection)
2,126✔
358
{
359
    {
360
        std::lock_guard lock(_received_libmav_messages_mutex);
2,126✔
361
        _received_libmav_messages.emplace(ReceivedLibmavMessage{message, connection});
2,125✔
362
    }
2,125✔
363
    _received_libmav_messages_cv.notify_one();
2,124✔
364
}
2,126✔
365

366
void MavsdkImpl::process_messages()
20,794✔
367
{
368
    std::lock_guard lock(_received_messages_mutex);
20,794✔
369
    while (!_received_messages.empty()) {
22,898✔
370
        auto message_copied = _received_messages.front();
2,130✔
371
        process_message(message_copied.message, message_copied.connection_ptr);
2,131✔
372
        _received_messages.pop();
2,132✔
373
    }
374
}
20,704✔
375

376
void MavsdkImpl::process_libmav_messages()
20,768✔
377
{
378
    std::lock_guard lock(_received_libmav_messages_mutex);
20,768✔
379
    while (!_received_libmav_messages.empty()) {
22,819✔
380
        auto message_copied = _received_libmav_messages.front();
2,115✔
381
        process_libmav_message(message_copied.message, message_copied.connection_ptr);
2,115✔
382
        _received_libmav_messages.pop();
2,115✔
383
    }
2,114✔
384
}
20,764✔
385

386
void MavsdkImpl::process_message(mavlink_message_t& message, Connection* connection)
2,132✔
387
{
388
    // Assumes _received_messages_mutex
389

390
    if (_message_logging_on) {
2,132✔
391
        LogDebug() << "Processing message " << message.msgid << " from "
×
392
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid);
×
393
    }
394

395
    if (_should_exit) {
2,132✔
396
        // If we're meant to clean up, let's not try to acquire any more locks but bail.
397
        return;
×
398
    }
399

400
    {
401
        std::lock_guard lock(_mutex);
2,132✔
402

403
        // This is a low level interface where incoming messages can be tampered
404
        // with or even dropped.
405
        {
406
            bool keep = true;
2,132✔
407
            {
408
                std::lock_guard<std::mutex> intercept_lock(_intercept_callbacks_mutex);
2,132✔
409
                if (_intercept_incoming_messages_callback != nullptr) {
2,131✔
410
                    keep = _intercept_incoming_messages_callback(message);
228✔
411
                }
412
            }
2,132✔
413

414
            if (!keep) {
2,131✔
415
                LogDebug() << "Dropped incoming message: " << int(message.msgid);
30✔
416
                return;
30✔
417
            }
418
        }
419

420
        if (_should_exit) {
2,101✔
421
            // If we're meant to clean up, let's not try to acquire any more locks but bail.
422
            return;
×
423
        }
424

425
        /** @note: Forward message if option is enabled and multiple interfaces are connected.
426
         *  Performs message forwarding checks for every messages if message forwarding
427
         *  is enabled on at least one connection, and in case of a single forwarding connection,
428
         *  we check that it is not the one which received the current message.
429
         *
430
         * Conditions:
431
         * 1. At least 2 connections.
432
         * 2. At least 1 forwarding connection.
433
         * 3. At least 2 forwarding connections or current connection is not forwarding.
434
         */
435

436
        if (_connections.size() > 1 && mavsdk::Connection::forwarding_connections_count() > 0 &&
2,101✔
437
            (mavsdk::Connection::forwarding_connections_count() > 1 ||
×
438
             !connection->should_forward_messages())) {
439
            if (_message_logging_on) {
×
440
                LogDebug() << "Forwarding message " << message.msgid << " from "
×
441
                           << static_cast<int>(message.sysid) << "/"
×
442
                           << static_cast<int>(message.compid);
×
443
            }
444
            forward_message(message, connection);
×
445
        }
446

447
        // Don't ever create a system with sysid 0.
448
        if (message.sysid == 0) {
2,100✔
449
            if (_message_logging_on) {
×
450
                LogDebug() << "Ignoring message with sysid == 0";
×
451
            }
452
            return;
×
453
        }
454

455
        // Filter out messages by QGroundControl, however, only do that if MAVSDK
456
        // is also implementing a ground station and not if it is used in another
457
        // configuration, e.g. on a companion.
458
        //
459
        // This is a workaround because PX4 started forwarding messages between
460
        // mavlink instances which leads to existing implementations (including
461
        // examples and integration tests) to connect to QGroundControl by accident
462
        // instead of PX4 because the check `has_autopilot()` is not used.
463

464
        if (_configuration.get_component_type() == ComponentType::GroundStation &&
2,100✔
465
            message.sysid == 255 && message.compid == MAV_COMP_ID_MISSIONPLANNER) {
2,098✔
466
            if (_message_logging_on) {
×
467
                LogDebug() << "Ignoring messages from QGC as we are also a ground station";
×
468
            }
469
            return;
×
470
        }
471

472
        bool found_system = false;
2,098✔
473
        for (auto& system : _systems) {
2,096✔
474
            if (system.first == message.sysid) {
2,004✔
475
                system.second->system_impl()->add_new_component(message.compid);
2,006✔
476
                found_system = true;
2,007✔
477
                break;
2,007✔
478
            }
479
        }
480

481
        if (!found_system) {
2,100✔
482
            if (_system_debugging) {
94✔
483
                LogWarn() << "Create new system/component " << (int)message.sysid << "/"
×
484
                          << (int)message.compid;
×
485
                LogWarn() << "From message " << (int)message.msgid << " with len "
×
486
                          << (int)message.len;
×
487
                std::string bytes = "";
×
488
                for (unsigned i = 0; i < 12 + message.len; ++i) {
×
489
                    bytes += std::to_string(reinterpret_cast<uint8_t*>(&message)[i]) + ' ';
×
490
                }
491
                LogWarn() << "Bytes: " << bytes;
×
492
            }
×
493
            make_system_with_component(message.sysid, message.compid);
94✔
494

495
            // We now better talk back.
496
            start_sending_heartbeats();
94✔
497
        }
498

499
        if (_should_exit) {
2,100✔
500
            // Don't try to call at() if systems have already been destroyed
501
            // in destructor.
502
            return;
×
503
        }
504
    }
2,130✔
505

506
    mavlink_message_handler.process_message(message);
2,102✔
507

508
    for (auto& system : _systems) {
2,101✔
509
        if (system.first == message.sysid) {
2,100✔
510
            system.second->system_impl()->process_mavlink_message(message);
2,100✔
511
            break;
2,102✔
512
        }
513
    }
514
}
515

516
void MavsdkImpl::process_libmav_message(const LibmavMessage& message, Connection* /* connection */)
2,115✔
517
{
518
    // Assumes _received_libmav_messages_mutex
519

520
    if (_message_logging_on) {
2,115✔
NEW
521
        LogDebug() << "MavsdkImpl::process_libmav_message: " << message.message_name << " from "
×
NEW
522
                   << static_cast<int>(message.system_id) << "/"
×
NEW
523
                   << static_cast<int>(message.component_id);
×
524
    }
525

526
    if (_message_logging_on) {
2,115✔
NEW
527
        LogDebug() << "Processing libmav message " << message.message_name << " from "
×
NEW
528
                   << static_cast<int>(message.system_id) << "/"
×
NEW
529
                   << static_cast<int>(message.component_id);
×
530
    }
531

532
    if (_should_exit) {
2,115✔
533
        // If we're meant to clean up, let's not try to acquire any more locks but bail.
NEW
534
        return;
×
535
    }
536

537
    {
538
        std::lock_guard lock(_mutex);
2,115✔
539

540
        // Don't ever create a system with sysid 0.
541
        if (message.system_id == 0) {
2,115✔
NEW
542
            if (_message_logging_on) {
×
NEW
543
                LogDebug() << "Ignoring libmav message with sysid == 0";
×
544
            }
NEW
545
            return;
×
546
        }
547

548
        // Filter out QGroundControl messages similar to regular mavlink processing
549
        if (_configuration.get_component_type() == ComponentType::GroundStation &&
2,115✔
550
            message.system_id == 255 && message.component_id == MAV_COMP_ID_MISSIONPLANNER) {
2,114✔
NEW
551
            if (_message_logging_on) {
×
NEW
552
                LogDebug() << "Ignoring libmav messages from QGC as we are also a ground station";
×
553
            }
NEW
554
            return;
×
555
        }
556

557
        bool found_system = false;
2,114✔
558
        for (auto& system : _systems) {
2,113✔
559
            if (system.first == message.system_id) {
2,107✔
560
                system.second->system_impl()->add_new_component(message.component_id);
2,108✔
561
                found_system = true;
2,109✔
562
                break;
2,109✔
563
            }
564
        }
565

566
        if (!found_system) {
2,115✔
567
            if (_system_debugging) {
6✔
NEW
568
                LogWarn() << "Create new system/component from libmav " << (int)message.system_id
×
NEW
569
                          << "/" << (int)message.component_id;
×
570
            }
571
            make_system_with_component(message.system_id, message.component_id);
6✔
572

573
            // We now better talk back.
574
            start_sending_heartbeats();
6✔
575
        }
576

577
        if (_should_exit) {
2,115✔
578
            // Don't try to call at() if systems have already been destroyed
579
            // in destructor.
NEW
580
            return;
×
581
        }
582
    }
2,114✔
583

584
    // Distribute libmav message to systems for libmav-specific handling
585
    bool found_system = false;
2,114✔
586
    for (auto& system : _systems) {
4,229✔
587
        if (system.first == message.system_id) {
2,115✔
588
            if (_message_logging_on) {
2,115✔
NEW
589
                LogDebug() << "Distributing libmav message " << message.message_name
×
NEW
590
                           << " to SystemImpl for system " << system.first;
×
591
            }
592
            system.second->system_impl()->process_libmav_message(message);
2,115✔
593
            found_system = true;
2,115✔
594
            // Don't break - distribute to all matching system instances
595
        }
596
    }
597

598
    if (!found_system) {
2,115✔
NEW
599
        LogWarn() << "No system found for libmav message " << message.message_name
×
NEW
600
                  << " from system " << message.system_id;
×
601
    }
602
}
603

604
bool MavsdkImpl::send_message(mavlink_message_t& message)
2,284✔
605
{
606
    // Create a copy of the message to avoid reference issues
607
    mavlink_message_t message_copy = message;
2,284✔
608

609
    {
610
        std::lock_guard lock(_messages_to_send_mutex);
2,284✔
611
        _messages_to_send.push(std::move(message_copy));
2,283✔
612
    }
2,282✔
613

614
    // For heartbeat messages, we want to process them immediately to speed up system discovery
615
    if (message.msgid == MAVLINK_MSG_ID_HEARTBEAT) {
2,284✔
616
        // Trigger message processing in the work thread
617
        // This is a hint to process messages sooner, but doesn't block
618
        std::this_thread::yield();
265✔
619
    }
620

621
    return true;
2,284✔
622
}
623

624
void MavsdkImpl::deliver_messages()
23,077✔
625
{
626
    // Process messages one at a time to avoid holding the mutex while delivering
627
    while (true) {
628
        mavlink_message_t message;
23,077✔
629
        {
630
            std::lock_guard lock(_messages_to_send_mutex);
23,034✔
631
            if (_messages_to_send.empty()) {
22,979✔
632
                break;
20,720✔
633
            }
634
            message = _messages_to_send.front();
2,284✔
635
            _messages_to_send.pop();
2,281✔
636
        }
23,004✔
637
        deliver_message(message);
2,282✔
638
    }
2,284✔
639
}
20,693✔
640

641
void MavsdkImpl::deliver_message(mavlink_message_t& message)
2,284✔
642
{
643
    if (_message_logging_on) {
2,284✔
644
        LogDebug() << "Sending message " << message.msgid << " from "
×
645
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid)
×
646
                   << " to " << static_cast<int>(get_target_system_id(message)) << "/"
×
647
                   << static_cast<int>(get_target_component_id(message));
×
648
    }
649

650
    // This is a low level interface where outgoing messages can be tampered
651
    // with or even dropped.
652
    bool keep = true;
2,284✔
653
    {
654
        std::lock_guard<std::mutex> lock(_intercept_callbacks_mutex);
2,284✔
655
        if (_intercept_outgoing_messages_callback != nullptr) {
2,284✔
656
            keep = _intercept_outgoing_messages_callback(message);
224✔
657
        }
658
    }
2,284✔
659

660
    if (!keep) {
2,284✔
661
        // We fake that everything was sent as instructed because
662
        // a potential loss would happen later, and we would not be informed
663
        // about it.
664
        LogDebug() << "Dropped outgoing message: " << int(message.msgid);
89✔
665
        return;
89✔
666
    }
667

668
    std::lock_guard lock(_mutex);
2,195✔
669

670
    if (_connections.empty()) {
2,195✔
671
        // We obviously can't send any messages without a connection added, so
672
        // we silently ignore this.
673
        return;
50✔
674
    }
675

676
    uint8_t successful_emissions = 0;
2,144✔
677
    for (auto& _connection : _connections) {
4,289✔
678
        const uint8_t target_system_id = get_target_system_id(message);
2,144✔
679

680
        if (target_system_id != 0 && !(*_connection.connection).has_system_id(target_system_id)) {
2,145✔
681
            continue;
×
682
        }
683
        const auto result = (*_connection.connection).send_message(message);
2,145✔
684
        if (result.first) {
2,145✔
685
            successful_emissions++;
2,145✔
686
        } else {
687
            _connections_errors_subscriptions.queue(
×
688
                Mavsdk::ConnectionError{result.second, _connection.handle},
×
689
                [this](const auto& func) { call_user_callback(func); });
×
690
        }
691
    }
2,145✔
692

693
    if (successful_emissions == 0) {
2,144✔
694
        LogErr() << "Sending message failed";
×
695
    }
696
}
2,194✔
697

698
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_any_connection(
100✔
699
    const std::string& connection_url, ForwardingOption forwarding_option)
700
{
701
    CliArg cli_arg;
100✔
702
    if (!cli_arg.parse(connection_url)) {
100✔
703
        return {ConnectionResult::ConnectionUrlInvalid, Mavsdk::ConnectionHandle{}};
×
704
    }
705

706
    return std::visit(
100✔
707
        overloaded{
200✔
708
            [](std::monostate) {
×
709
                // Should not happen anyway.
710
                return std::pair<ConnectionResult, Mavsdk::ConnectionHandle>{
×
711
                    ConnectionResult::ConnectionUrlInvalid, Mavsdk::ConnectionHandle()};
×
712
            },
713
            [this, forwarding_option](const CliArg::Udp& udp) {
98✔
714
                return add_udp_connection(udp, forwarding_option);
98✔
715
            },
716
            [this, forwarding_option](const CliArg::Tcp& tcp) {
2✔
717
                return add_tcp_connection(tcp, forwarding_option);
2✔
718
            },
719
            [this, forwarding_option](const CliArg::Serial& serial) {
×
720
                return add_serial_connection(
×
721
                    serial.path, serial.baudrate, serial.flow_control_enabled, forwarding_option);
×
722
            }},
723
        cli_arg.protocol);
100✔
724
}
100✔
725

726
std::pair<ConnectionResult, Mavsdk::ConnectionHandle>
727
MavsdkImpl::add_udp_connection(const CliArg::Udp& udp, ForwardingOption forwarding_option)
98✔
728
{
729
    auto new_conn = std::make_unique<UdpConnection>(
98✔
730
        [this](mavlink_message_t& message, Connection* connection) {
2,120✔
731
            receive_message(message, connection);
2,120✔
732
        },
2,124✔
733
        [this](const LibmavMessage& message, Connection* connection) {
2,123✔
734
            receive_libmav_message(message, connection);
2,123✔
735
        },
2,126✔
736
        *_message_set,
98✔
737
        udp.mode == CliArg::Udp::Mode::In ? udp.host : "0.0.0.0",
245✔
738
        udp.mode == CliArg::Udp::Mode::In ? udp.port : 0,
196✔
739
        forwarding_option);
294✔
740

741
    if (!new_conn) {
98✔
742
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
743
    }
744

745
    ConnectionResult ret = new_conn->start();
98✔
746

747
    if (ret != ConnectionResult::Success) {
98✔
748
        return {ret, Mavsdk::ConnectionHandle{}};
×
749
    }
750

751
    if (udp.mode == CliArg::Udp::Mode::Out) {
98✔
752
        // We need to add the IP rather than a hostname, otherwise we end up with two remotes:
753
        // one for the IP, and one for a hostname.
754
        auto remote_ip = resolve_hostname_to_ip(udp.host);
49✔
755

756
        if (!remote_ip) {
49✔
757
            return {ConnectionResult::DestinationIpUnknown, Mavsdk::ConnectionHandle{}};
×
758
        }
759

760
        new_conn->add_remote_to_keep(remote_ip.value(), udp.port);
49✔
761
        std::lock_guard lock(_mutex);
49✔
762

763
        // With a UDP remote, we need to initiate the connection by sending heartbeats.
764
        auto new_configuration = get_configuration();
49✔
765
        new_configuration.set_always_send_heartbeats(true);
49✔
766
        set_configuration(new_configuration);
49✔
767
    }
49✔
768

769
    auto handle = add_connection(std::move(new_conn));
98✔
770

771
    return {ConnectionResult::Success, handle};
98✔
772
}
98✔
773

774
std::pair<ConnectionResult, Mavsdk::ConnectionHandle>
775
MavsdkImpl::add_tcp_connection(const CliArg::Tcp& tcp, ForwardingOption forwarding_option)
2✔
776
{
777
    if (tcp.mode == CliArg::Tcp::Mode::Out) {
2✔
778
        auto new_conn = std::make_unique<TcpClientConnection>(
1✔
779
            [this](mavlink_message_t& message, Connection* connection) {
7✔
780
                receive_message(message, connection);
7✔
781
            },
7✔
NEW
782
            [this](const LibmavMessage& message, Connection* connection) {
×
NEW
783
                receive_libmav_message(message, connection);
×
NEW
784
            },
×
785
            *_message_set,
1✔
786
            tcp.host,
1✔
787
            tcp.port,
1✔
788
            forwarding_option);
1✔
789
        if (!new_conn) {
1✔
790
            return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
791
        }
792
        ConnectionResult ret = new_conn->start();
1✔
793
        if (ret == ConnectionResult::Success) {
1✔
794
            return {ret, add_connection(std::move(new_conn))};
1✔
795
        } else {
796
            return {ret, Mavsdk::ConnectionHandle{}};
×
797
        }
798
    } else {
1✔
799
        auto new_conn = std::make_unique<TcpServerConnection>(
1✔
800
            [this](mavlink_message_t& message, Connection* connection) {
12✔
801
                receive_message(message, connection);
12✔
802
            },
12✔
NEW
803
            [this](const LibmavMessage& message, Connection* connection) {
×
NEW
804
                receive_libmav_message(message, connection);
×
NEW
805
            },
×
806
            *_message_set,
1✔
807
            tcp.host,
1✔
808
            tcp.port,
1✔
809
            forwarding_option);
1✔
810
        if (!new_conn) {
1✔
811
            return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
812
        }
813
        ConnectionResult ret = new_conn->start();
1✔
814
        if (ret == ConnectionResult::Success) {
1✔
815
            return {ret, add_connection(std::move(new_conn))};
1✔
816
        } else {
817
            return {ret, Mavsdk::ConnectionHandle{}};
×
818
        }
819
    }
1✔
820
}
821

822
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_serial_connection(
×
823
    const std::string& dev_path,
824
    int baudrate,
825
    bool flow_control,
826
    ForwardingOption forwarding_option)
827
{
828
    auto new_conn = std::make_unique<SerialConnection>(
×
829
        [this](mavlink_message_t& message, Connection* connection) {
×
830
            receive_message(message, connection);
×
831
        },
×
NEW
832
        [this](const LibmavMessage& message, Connection* connection) {
×
NEW
833
            receive_libmav_message(message, connection);
×
NEW
834
        },
×
NEW
835
        *_message_set,
×
836
        dev_path,
837
        baudrate,
838
        flow_control,
839
        forwarding_option);
×
840
    if (!new_conn) {
×
841
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
842
    }
843
    ConnectionResult ret = new_conn->start();
×
844
    if (ret == ConnectionResult::Success) {
×
845
        auto handle = add_connection(std::move(new_conn));
×
846

847
        auto new_configuration = get_configuration();
×
848

849
        // PX4 starting with v1.13 does not send heartbeats by default, so we need
850
        // to initiate the MAVLink connection by sending heartbeats.
851
        // Therefore, we override the default here and enable sending heartbeats.
852
        new_configuration.set_always_send_heartbeats(true);
×
853
        set_configuration(new_configuration);
×
854

855
        return {ret, handle};
×
856

857
    } else {
858
        return {ret, Mavsdk::ConnectionHandle{}};
×
859
    }
860
}
×
861

862
Mavsdk::ConnectionHandle MavsdkImpl::add_connection(std::unique_ptr<Connection>&& new_connection)
100✔
863
{
864
    std::lock_guard lock(_mutex);
100✔
865
    auto handle = _connections_handle_factory.create();
100✔
866
    _connections.emplace_back(ConnectionEntry{std::move(new_connection), handle});
100✔
867

868
    return handle;
200✔
869
}
100✔
870

871
void MavsdkImpl::remove_connection(Mavsdk::ConnectionHandle handle)
×
872
{
873
    std::lock_guard lock(_mutex);
×
874

875
    _connections.erase(std::remove_if(_connections.begin(), _connections.end(), [&](auto&& entry) {
×
876
        return (entry.handle == handle);
×
877
    }));
878
}
×
879

880
Mavsdk::Configuration MavsdkImpl::get_configuration() const
49✔
881
{
882
    std::lock_guard configuration_lock(_mutex);
49✔
883
    return _configuration;
98✔
884
}
49✔
885

886
void MavsdkImpl::set_configuration(Mavsdk::Configuration new_configuration)
150✔
887
{
888
    std::lock_guard server_components_lock(_server_components_mutex);
150✔
889
    // We just point the default to the newly created component. This means
890
    // that the previous default component will be deleted if it is not
891
    // used/referenced anywhere.
892
    _default_server_component =
150✔
893
        server_component_by_id_with_lock(new_configuration.get_component_id());
150✔
894

895
    if (new_configuration.get_always_send_heartbeats() &&
249✔
896
        !_configuration.get_always_send_heartbeats()) {
99✔
897
        start_sending_heartbeats();
50✔
898
    } else if (
100✔
899
        !new_configuration.get_always_send_heartbeats() &&
151✔
900
        _configuration.get_always_send_heartbeats() && !is_any_system_connected()) {
151✔
901
        stop_sending_heartbeats();
×
902
    }
903

904
    _configuration = new_configuration;
150✔
905
    // We cache these values as atomic to avoid having to lock any mutex for them.
906
    _our_system_id = new_configuration.get_system_id();
150✔
907
    _our_component_id = new_configuration.get_component_id();
150✔
908
}
150✔
909

910
uint8_t MavsdkImpl::get_own_system_id() const
5,234✔
911
{
912
    return _our_system_id;
5,234✔
913
}
914

915
uint8_t MavsdkImpl::get_own_component_id() const
1,388✔
916
{
917
    return _our_component_id;
1,388✔
918
}
919

920
uint8_t MavsdkImpl::channel() const
×
921
{
922
    // TODO
923
    return 0;
×
924
}
925

926
Autopilot MavsdkImpl::autopilot() const
×
927
{
928
    // TODO
929
    return Autopilot::Px4;
×
930
}
931

932
// FIXME: this should be per component
933
uint8_t MavsdkImpl::get_mav_type() const
265✔
934
{
935
    return _configuration.get_mav_type();
265✔
936
}
937

938
void MavsdkImpl::make_system_with_component(uint8_t system_id, uint8_t comp_id)
100✔
939
{
940
    // Needs _systems_lock
941

942
    if (_should_exit) {
100✔
943
        // When the system got destroyed in the destructor, we have to give up.
944
        return;
×
945
    }
946

947
    if (static_cast<int>(system_id) == 0 && static_cast<int>(comp_id) == 0) {
100✔
948
        LogDebug() << "Initializing connection to remote system...";
×
949
    } else {
950
        LogDebug() << "New system ID: " << static_cast<int>(system_id)
300✔
951
                   << " Comp ID: " << static_cast<int>(comp_id);
300✔
952
    }
953

954
    // Make a system with its first component
955
    auto new_system = std::make_shared<System>(*this);
100✔
956
    new_system->init(system_id, comp_id);
100✔
957

958
    _systems.emplace_back(system_id, new_system);
100✔
959
}
100✔
960

961
void MavsdkImpl::notify_on_discover()
100✔
962
{
963
    // Queue the callbacks without holding the mutex to avoid deadlocks
964
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
150✔
965
}
100✔
966

967
void MavsdkImpl::notify_on_timeout()
×
968
{
969
    // Queue the callbacks without holding the mutex to avoid deadlocks
970
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
971
}
×
972

973
Mavsdk::NewSystemHandle
974
MavsdkImpl::subscribe_on_new_system(const Mavsdk::NewSystemCallback& callback)
50✔
975
{
976
    std::lock_guard lock(_mutex);
50✔
977

978
    const auto handle = _new_system_callbacks.subscribe(callback);
50✔
979

980
    if (is_any_system_connected()) {
50✔
981
        _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
982
    }
983

984
    return handle;
100✔
985
}
50✔
986

987
void MavsdkImpl::unsubscribe_on_new_system(Mavsdk::NewSystemHandle handle)
49✔
988
{
989
    _new_system_callbacks.unsubscribe(handle);
49✔
990
}
49✔
991

992
bool MavsdkImpl::is_any_system_connected() const
50✔
993
{
994
    std::vector<std::shared_ptr<System>> connected_systems = systems();
50✔
995
    return std::any_of(connected_systems.cbegin(), connected_systems.cend(), [](auto& system) {
50✔
996
        return system->is_connected();
×
997
    });
50✔
998
}
50✔
999

1000
void MavsdkImpl::work_thread()
101✔
1001
{
1002
    while (!_should_exit) {
20,879✔
1003
        // Process incoming messages
1004
        process_messages();
20,744✔
1005

1006
        // Process incoming libmav messages
1007
        process_libmav_messages();
20,796✔
1008

1009
        // Run timers
1010
        timeout_handler.run_once();
20,724✔
1011
        call_every_handler.run_once();
20,774✔
1012

1013
        // Do component work
1014
        {
1015
            std::lock_guard lock(_server_components_mutex);
20,765✔
1016
            for (auto& it : _server_components) {
41,527✔
1017
                if (it.second != nullptr) {
20,796✔
1018
                    it.second->_impl->do_work();
20,731✔
1019
                }
1020
            }
1021
        }
20,670✔
1022

1023
        // Deliver outgoing messages
1024
        deliver_messages();
20,687✔
1025

1026
        // If no messages to send, check if there are messages to receive
1027
        std::unique_lock lock_received(_received_messages_mutex);
20,781✔
1028
        if (_received_messages.empty()) {
20,701✔
1029
            // No messages to process, wait for a signal or timeout
1030
            _received_messages_cv.wait_for(lock_received, std::chrono::milliseconds(10), [this]() {
20,622✔
1031
                return !_received_messages.empty() || _should_exit;
41,413✔
1032
            });
1033
        }
1034
    }
20,781✔
1035
}
118✔
1036

1037
void MavsdkImpl::call_user_callback_located(
1,074✔
1038
    const std::string& filename, const int linenumber, const std::function<void()>& func)
1039
{
1040
    // Don't enqueue callbacks if we're shutting down
1041
    if (_should_exit) {
1,074✔
1042
        return;
×
1043
    }
1044

1045
    auto callback_size = _user_callback_queue.size();
1,074✔
1046
    if (callback_size == 10) {
1,074✔
1047
        LogWarn()
×
1048
            << "User callback queue too slow.\n"
×
1049
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
1050

1051
    } else if (callback_size == 99) {
1,074✔
1052
        LogErr()
×
1053
            << "User callback queue overflown\n"
×
1054
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
1055

1056
    } else if (callback_size == 100) {
1,074✔
1057
        return;
×
1058
    }
1059

1060
    // We only need to keep track of filename and linenumber if we're actually debugging this.
1061
    UserCallback user_callback =
1,074✔
1062
        _callback_debugging ? UserCallback{func, filename, linenumber} : UserCallback{func};
2,148✔
1063

1064
    _user_callback_queue.push_back(std::make_shared<UserCallback>(user_callback));
1,074✔
1065
}
1,074✔
1066

1067
void MavsdkImpl::process_user_callbacks_thread()
101✔
1068
{
1069
    while (!_should_exit) {
1,276✔
1070
        std::shared_ptr<UserCallback> callback;
1,175✔
1071
        {
1072
            LockedQueue<UserCallback>::Guard guard(_user_callback_queue);
1,175✔
1073
            callback = guard.wait_and_pop_front();
1,175✔
1074
        }
1,175✔
1075

1076
        if (!callback) {
1,175✔
1077
            continue;
101✔
1078
        }
1079

1080
        // Check if we're in the process of shutting down before executing the callback
1081
        if (_should_exit) {
1,074✔
1082
            continue;
×
1083
        }
1084

1085
        const double timeout_s = 1.0;
1,074✔
1086
        auto cookie = timeout_handler.add(
1,074✔
1087
            [&]() {
×
1088
                if (_callback_debugging) {
×
1089
                    LogWarn() << "Callback called from " << callback->filename << ":"
×
1090
                              << callback->linenumber << " took more than " << timeout_s
×
1091
                              << " second to run.";
×
1092
                    fflush(stdout);
×
1093
                    fflush(stderr);
×
1094
                    abort();
×
1095
                } else {
1096
                    LogWarn()
×
1097
                        << "Callback took more than " << timeout_s << " second to run.\n"
×
1098
                        << "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
1099
                }
1100
            },
×
1101
            timeout_s);
1,074✔
1102
        callback->func();
1,074✔
1103
        timeout_handler.remove(cookie);
1,074✔
1104
    }
1,175✔
1105
}
101✔
1106

1107
void MavsdkImpl::start_sending_heartbeats()
250✔
1108
{
1109
    // Check if we're in the process of shutting down
1110
    if (_should_exit) {
250✔
1111
        return;
×
1112
    }
1113

1114
    // Before sending out first heartbeats we need to make sure we have a
1115
    // default server component.
1116
    default_server_component_impl();
250✔
1117

1118
    {
1119
        std::lock_guard<std::mutex> lock(_heartbeat_mutex);
250✔
1120
        call_every_handler.remove(_heartbeat_send_cookie);
250✔
1121
        _heartbeat_send_cookie =
500✔
1122
            call_every_handler.add([this]() { send_heartbeats(); }, HEARTBEAT_SEND_INTERVAL_S);
514✔
1123
    }
250✔
1124
}
1125

1126
void MavsdkImpl::stop_sending_heartbeats()
×
1127
{
1128
    if (!_configuration.get_always_send_heartbeats()) {
×
1129
        std::lock_guard<std::mutex> lock(_heartbeat_mutex);
×
1130
        call_every_handler.remove(_heartbeat_send_cookie);
×
1131
    }
×
1132
}
×
1133

1134
ServerComponentImpl& MavsdkImpl::default_server_component_impl()
1,136✔
1135
{
1136
    std::lock_guard lock(_server_components_mutex);
1,136✔
1137
    return default_server_component_with_lock();
1,136✔
1138
}
1,136✔
1139

1140
ServerComponentImpl& MavsdkImpl::default_server_component_with_lock()
1,136✔
1141
{
1142
    if (_default_server_component == nullptr) {
1,136✔
1143
        _default_server_component = server_component_by_id_with_lock(_our_component_id);
×
1144
    }
1145
    return *_default_server_component->_impl;
1,136✔
1146
}
1147

1148
void MavsdkImpl::send_heartbeats()
264✔
1149
{
1150
    std::lock_guard lock(_server_components_mutex);
264✔
1151

1152
    for (auto& it : _server_components) {
528✔
1153
        if (it.second != nullptr) {
265✔
1154
            it.second->_impl->send_heartbeat();
265✔
1155
        }
1156
    }
1157
}
262✔
1158

1159
void MavsdkImpl::intercept_incoming_messages_async(std::function<bool(mavlink_message_t&)> callback)
22✔
1160
{
1161
    std::lock_guard<std::mutex> lock(_intercept_callbacks_mutex);
22✔
1162
    _intercept_incoming_messages_callback = callback;
22✔
1163
}
22✔
1164

1165
void MavsdkImpl::intercept_outgoing_messages_async(std::function<bool(mavlink_message_t&)> callback)
14✔
1166
{
1167
    std::lock_guard<std::mutex> lock(_intercept_callbacks_mutex);
14✔
1168
    _intercept_outgoing_messages_callback = callback;
14✔
1169
}
14✔
1170

1171
Mavsdk::ConnectionErrorHandle
1172
MavsdkImpl::subscribe_connection_errors(Mavsdk::ConnectionErrorCallback callback)
×
1173
{
1174
    std::lock_guard lock(_mutex);
×
1175

1176
    const auto handle = _connections_errors_subscriptions.subscribe(callback);
×
1177

1178
    return handle;
×
1179
}
×
1180

1181
void MavsdkImpl::unsubscribe_connection_errors(Mavsdk::ConnectionErrorHandle handle)
×
1182
{
1183
    std::lock_guard lock(_mutex);
×
1184
    _connections_errors_subscriptions.unsubscribe(handle);
×
1185
}
×
1186

1187
uint8_t MavsdkImpl::get_target_system_id(const mavlink_message_t& message)
2,145✔
1188
{
1189
    // Checks whether connection knows target system ID by extracting target system if set.
1190
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
2,145✔
1191

1192
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_SYSTEM)) {
2,145✔
1193
        return 0;
414✔
1194
    }
1195

1196
    // Don't look at the target system offset if it is outside the payload length.
1197
    // This can happen if the fields are trimmed.
1198
    if (meta->target_system_ofs >= message.len) {
1,731✔
1199
        return 0;
5✔
1200
    }
1201

1202
    return (_MAV_PAYLOAD(&message))[meta->target_system_ofs];
1,726✔
1203
}
1204

1205
uint8_t MavsdkImpl::get_target_component_id(const mavlink_message_t& message)
×
1206
{
1207
    // Checks whether connection knows target system ID by extracting target system if set.
1208
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
×
1209

1210
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_COMPONENT)) {
×
1211
        return 0;
×
1212
    }
1213

1214
    // Don't look at the target component offset if it is outside the payload length.
1215
    // This can happen if the fields are trimmed.
1216
    if (meta->target_component_ofs >= message.len) {
×
1217
        return 0;
×
1218
    }
1219

1220
    return (_MAV_PAYLOAD(&message))[meta->target_component_ofs];
×
1221
}
1222

1223
Sender& MavsdkImpl::sender()
×
1224
{
1225
    std::lock_guard lock(_server_components_mutex);
×
1226
    return default_server_component_with_lock().sender();
×
1227
}
×
1228

NEW
1229
std::vector<Connection*> MavsdkImpl::get_connections() const
×
1230
{
NEW
1231
    std::vector<Connection*> connections;
×
NEW
1232
    for (const auto& connection_entry : _connections) {
×
NEW
1233
        connections.push_back(connection_entry.connection.get());
×
1234
    }
NEW
1235
    return connections;
×
1236
}
1237

1238
mav::MessageSet& MavsdkImpl::get_message_set() const
10✔
1239
{
1240
    return *_message_set;
10✔
1241
}
1242

1243
} // namespace mavsdk
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc