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

mavlink / MAVSDK / 16634671887

30 Jul 2025 09:59PM UTC coverage: 44.638% (-1.7%) from 46.31%
16634671887

Pull #2626

github

web-flow
Merge d1ccc2564 into c0a7c02a0
Pull Request #2626: core: flush after each Log* line

234 of 347 new or added lines in 32 files covered. (67.44%)

432 existing lines in 19 files now uncovered.

15447 of 34605 relevant lines covered (44.64%)

306.88 hits per line

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

57.37
/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) :
87✔
29
    timeout_handler(time),
87✔
30
    call_every_handler(time)
174✔
31
{
32
    LogInfo() << "MAVSDK version: " << mavsdk_version;
87✔
33

34
    if (const char* env_p = std::getenv("MAVSDK_CALLBACK_DEBUGGING")) {
87✔
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")) {
87✔
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")) {
87✔
49
        if (std::string(env_p) == "1") {
×
50
            LogDebug() << "System debugging is on.";
×
51
            _system_debugging = true;
×
52
        }
53
    }
54

55
    set_configuration(configuration);
87✔
56

57
    // Initialize MessageSet with embedded XML content in dependency order
58
    _message_set = std::make_unique<mav::MessageSet>();
87✔
59
    //_message_set->addFromXMLString(mav_embedded::MINIMAL_XML);
60
    //_message_set->addFromXMLString(mav_embedded::STANDARD_XML);
61
    //_message_set->addFromXMLString(mav_embedded::COMMON_XML);
62
    //_message_set->addFromXMLString(mav_embedded::ARDUPILOTMEGA_XML);
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 =
174✔
68
        new std::thread(&MavsdkImpl::process_user_callbacks_thread, this);
87✔
69

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

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

80
    _should_exit = true;
87✔
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) {
87✔
86
        _work_thread->join();
87✔
87
        delete _work_thread;
87✔
88
        _work_thread = nullptr;
87✔
89
    }
90

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

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

100
    _systems.clear();
87✔
101
    _connections.clear();
87✔
102
}
174✔
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
86✔
133
{
134
    std::vector<std::shared_ptr<System>> systems_result{};
86✔
135

136
    std::lock_guard lock(_mutex);
86✔
137
    for (auto& system : _systems) {
129✔
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) {
43✔
142
            continue;
×
143
        }
144
        systems_result.push_back(system.second);
43✔
145
    }
146

147
    return systems_result;
86✔
148
}
86✔
149

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

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

166
    auto prom = std::make_shared<std::promise<std::shared_ptr<System>>>();
33✔
167
    auto fut = prom->get_future();
33✔
168

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

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

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

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

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

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

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

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

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

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

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

283
    std::lock_guard lock(_server_components_mutex);
47✔
284

285
    return server_component_by_id_with_lock(component_id);
47✔
286
}
47✔
287

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

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

303
    return _server_components.back().second;
88✔
304
}
305

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

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

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

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

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

347
void MavsdkImpl::receive_message(mavlink_message_t& message, Connection* connection)
2,170✔
348
{
349
    {
350
        std::lock_guard lock(_received_messages_mutex);
2,170✔
351
        _received_messages.emplace(ReceivedMessage{std::move(message), connection});
2,171✔
352
    }
2,171✔
353
    _received_messages_cv.notify_one();
2,172✔
354
}
2,172✔
355

UNCOV
356
void MavsdkImpl::receive_libmav_message(const LibmavMessage& message, Connection* connection)
×
357
{
358
    {
UNCOV
359
        std::lock_guard lock(_received_libmav_messages_mutex);
×
UNCOV
360
        _received_libmav_messages.emplace(ReceivedLibmavMessage{message, connection});
×
UNCOV
361
    }
×
UNCOV
362
    _received_libmav_messages_cv.notify_one();
×
UNCOV
363
}
×
364

365
void MavsdkImpl::process_messages()
21,409✔
366
{
367
    std::lock_guard lock(_received_messages_mutex);
21,409✔
368
    while (!_received_messages.empty()) {
23,193✔
369
        auto message_copied = _received_messages.front();
2,147✔
370
        process_message(message_copied.message, message_copied.connection_ptr);
2,149✔
371
        _received_messages.pop();
2,152✔
372
    }
373
}
20,562✔
374

375
void MavsdkImpl::process_libmav_messages()
21,329✔
376
{
377
    std::lock_guard lock(_received_libmav_messages_mutex);
21,329✔
378
    while (!_received_libmav_messages.empty()) {
21,160✔
UNCOV
379
        auto message_copied = _received_libmav_messages.front();
×
UNCOV
380
        process_libmav_message(message_copied.message, message_copied.connection_ptr);
×
UNCOV
381
        _received_libmav_messages.pop();
×
UNCOV
382
    }
×
383
}
21,332✔
384

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

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

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

399
    {
400
        std::lock_guard lock(_mutex);
2,150✔
401

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

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

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

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

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

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

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

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

471
        bool found_system = false;
2,113✔
472
        for (auto& system : _systems) {
2,114✔
473
            if (system.first == message.sysid) {
2,027✔
474
                system.second->system_impl()->add_new_component(message.compid);
2,026✔
475
                found_system = true;
2,027✔
476
                break;
2,027✔
477
            }
478
        }
479

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

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

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

505
    mavlink_message_handler.process_message(message);
2,108✔
506

507
    for (auto& system : _systems) {
2,112✔
508
        if (system.first == message.sysid) {
2,111✔
509
            system.second->system_impl()->process_mavlink_message(message);
2,113✔
510
            break;
2,114✔
511
        }
512
    }
513
}
514

UNCOV
515
void MavsdkImpl::process_libmav_message(const LibmavMessage& message, Connection* /* connection */)
×
516
{
517
    // Assumes _received_libmav_messages_mutex
518

UNCOV
519
    if (_message_logging_on) {
×
520
        LogDebug() << "MavsdkImpl::process_libmav_message: " << message.message_name << " from "
×
521
                   << static_cast<int>(message.system_id) << "/"
×
522
                   << static_cast<int>(message.component_id);
×
523
    }
524

UNCOV
525
    if (_message_logging_on) {
×
526
        LogDebug() << "Processing libmav message " << message.message_name << " from "
×
527
                   << static_cast<int>(message.system_id) << "/"
×
528
                   << static_cast<int>(message.component_id);
×
529
    }
530

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

536
    {
UNCOV
537
        std::lock_guard lock(_mutex);
×
538

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

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

UNCOV
556
        bool found_system = false;
×
UNCOV
557
        for (auto& system : _systems) {
×
UNCOV
558
            if (system.first == message.system_id) {
×
UNCOV
559
                system.second->system_impl()->add_new_component(message.component_id);
×
UNCOV
560
                found_system = true;
×
UNCOV
561
                break;
×
562
            }
563
        }
564

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

572
            // We now better talk back.
UNCOV
573
            start_sending_heartbeats();
×
574
        }
575

UNCOV
576
        if (_should_exit) {
×
577
            // Don't try to call at() if systems have already been destroyed
578
            // in destructor.
579
            return;
×
580
        }
UNCOV
581
    }
×
582

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

UNCOV
597
    if (!found_system) {
×
598
        LogWarn() << "No system found for libmav message " << message.message_name
×
599
                  << " from system " << message.system_id;
×
600
    }
601
}
602

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

608
    {
609
        std::lock_guard lock(_messages_to_send_mutex);
2,299✔
610
        _messages_to_send.push(std::move(message_copy));
2,296✔
611
    }
2,296✔
612

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

620
    return true;
2,298✔
621
}
622

623
void MavsdkImpl::deliver_messages()
23,720✔
624
{
625
    // Process messages one at a time to avoid holding the mutex while delivering
626
    while (true) {
627
        mavlink_message_t message;
23,720✔
628
        {
629
            std::lock_guard lock(_messages_to_send_mutex);
23,537✔
630
            if (_messages_to_send.empty()) {
23,240✔
631
                break;
21,182✔
632
            }
633
            message = _messages_to_send.front();
2,299✔
634
            _messages_to_send.pop();
2,297✔
635
        }
23,481✔
636
        deliver_message(message);
2,290✔
637
    }
2,298✔
638
}
20,739✔
639

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

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

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

667
    std::lock_guard lock(_mutex);
2,213✔
668

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

675
    uint8_t successful_emissions = 0;
2,168✔
676
    for (auto& _connection : _connections) {
4,339✔
677
        const uint8_t target_system_id = get_target_system_id(message);
2,172✔
678

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

692
    if (successful_emissions == 0) {
2,171✔
693
        LogErr() << "Sending message failed";
×
694
    }
695
}
2,214✔
696

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

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

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

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

744
    ConnectionResult ret = new_conn->start();
84✔
745

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

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

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

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

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

768
    auto handle = add_connection(std::move(new_conn));
84✔
769

770
    return {ConnectionResult::Success, handle};
84✔
771
}
84✔
772

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

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

846
        auto new_configuration = get_configuration();
×
847

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

854
        return {ret, handle};
×
855

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

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

867
    return handle;
172✔
868
}
86✔
869

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

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

879
Mavsdk::Configuration MavsdkImpl::get_configuration() const
42✔
880
{
881
    std::lock_guard configuration_lock(_mutex);
42✔
882
    return _configuration;
84✔
883
}
42✔
884

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

894
    if (new_configuration.get_always_send_heartbeats() &&
214✔
895
        !_configuration.get_always_send_heartbeats()) {
85✔
896
        start_sending_heartbeats();
43✔
897
    } else if (
86✔
898
        !new_configuration.get_always_send_heartbeats() &&
130✔
899
        _configuration.get_always_send_heartbeats() && !is_any_system_connected()) {
130✔
900
        stop_sending_heartbeats();
×
901
    }
902

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

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

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

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

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

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

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

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

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

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

957
    _systems.emplace_back(system_id, new_system);
86✔
958
}
86✔
959

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

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

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

977
    const auto handle = _new_system_callbacks.subscribe(callback);
43✔
978

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

983
    return handle;
86✔
984
}
43✔
985

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

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

999
void MavsdkImpl::work_thread()
87✔
1000
{
1001
    while (!_should_exit) {
21,374✔
1002
        // Process incoming messages
1003
        process_messages();
20,793✔
1004

1005
        // Process incoming libmav messages
1006
        process_libmav_messages();
21,441✔
1007

1008
        // Run timers
1009
        timeout_handler.run_once();
21,107✔
1010
        call_every_handler.run_once();
21,182✔
1011

1012
        // Do component work
1013
        {
1014
            std::lock_guard lock(_server_components_mutex);
21,441✔
1015
            for (auto& it : _server_components) {
42,655✔
1016
                if (it.second != nullptr) {
21,443✔
1017
                    it.second->_impl->do_work();
20,929✔
1018
                }
1019
            }
1020
        }
20,560✔
1021

1022
        // Deliver outgoing messages
1023
        deliver_messages();
20,850✔
1024

1025
        // If no messages to send, check if there are messages to receive
1026
        std::unique_lock lock_received(_received_messages_mutex);
21,145✔
1027
        if (_received_messages.empty()) {
21,309✔
1028
            // No messages to process, wait for a signal or timeout
1029
            _received_messages_cv.wait_for(lock_received, std::chrono::milliseconds(10), [this]() {
20,837✔
1030
                return !_received_messages.empty() || _should_exit;
42,298✔
1031
            });
1032
        }
1033
    }
21,470✔
1034
}
163✔
1035

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

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

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

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

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

1063
    _user_callback_queue.enqueue(user_callback);
1,055✔
1064
}
1,055✔
1065

1066
void MavsdkImpl::process_user_callbacks_thread()
87✔
1067
{
1068
    while (!_should_exit) {
1,229✔
1069
        auto callback = _user_callback_queue.dequeue();
1,142✔
1070
        if (!callback) {
1,142✔
1071
            continue;
87✔
1072
        }
1073

1074
        // Check if we're in the process of shutting down before executing the callback
1075
        if (_should_exit) {
1,055✔
1076
            continue;
×
1077
        }
1078

1079
        const double timeout_s = 1.0;
1,055✔
1080
        auto cookie = timeout_handler.add(
2,110✔
NEW
1081
            [this,
×
1082
             filename = callback.value().filename,
1,055✔
1083
             linenumber = callback.value().linenumber,
1,055✔
NEW
1084
             timeout_s]() {
×
1085
                if (_callback_debugging) {
×
NEW
1086
                    LogWarn() << "Callback called from " << filename << ":" << linenumber
×
NEW
1087
                              << " took more than " << timeout_s << " second to run.";
×
1088
                    fflush(stdout);
×
1089
                    fflush(stderr);
×
1090
                    abort();
×
1091
                } else {
1092
                    LogWarn()
×
1093
                        << "Callback took more than " << timeout_s << " second to run.\n"
×
1094
                        << "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
1095
                }
1096
            },
×
1097
            timeout_s);
1,055✔
1098
        callback.value().func();
1,055✔
1099
        timeout_handler.remove(cookie);
1,055✔
1100
    }
1,142✔
1101
}
87✔
1102

1103
void MavsdkImpl::start_sending_heartbeats()
215✔
1104
{
1105
    // Check if we're in the process of shutting down
1106
    if (_should_exit) {
215✔
1107
        return;
×
1108
    }
1109

1110
    // Before sending out first heartbeats we need to make sure we have a
1111
    // default server component.
1112
    default_server_component_impl();
215✔
1113

1114
    {
1115
        std::lock_guard<std::mutex> lock(_heartbeat_mutex);
215✔
1116
        call_every_handler.remove(_heartbeat_send_cookie);
215✔
1117
        _heartbeat_send_cookie =
430✔
1118
            call_every_handler.add([this]() { send_heartbeats(); }, HEARTBEAT_SEND_INTERVAL_S);
497✔
1119
    }
215✔
1120
}
1121

1122
void MavsdkImpl::stop_sending_heartbeats()
×
1123
{
1124
    if (!_configuration.get_always_send_heartbeats()) {
×
1125
        std::lock_guard<std::mutex> lock(_heartbeat_mutex);
×
1126
        call_every_handler.remove(_heartbeat_send_cookie);
×
1127
    }
×
1128
}
×
1129

1130
ServerComponentImpl& MavsdkImpl::default_server_component_impl()
1,080✔
1131
{
1132
    std::lock_guard lock(_server_components_mutex);
1,080✔
1133
    return default_server_component_with_lock();
1,080✔
1134
}
1,080✔
1135

1136
ServerComponentImpl& MavsdkImpl::default_server_component_with_lock()
1,080✔
1137
{
1138
    if (_default_server_component == nullptr) {
1,080✔
1139
        _default_server_component = server_component_by_id_with_lock(_our_component_id);
×
1140
    }
1141
    return *_default_server_component->_impl;
1,080✔
1142
}
1143

1144
void MavsdkImpl::send_heartbeats()
282✔
1145
{
1146
    std::lock_guard lock(_server_components_mutex);
282✔
1147

1148
    for (auto& it : _server_components) {
564✔
1149
        if (it.second != nullptr) {
284✔
1150
            it.second->_impl->send_heartbeat();
284✔
1151
        }
1152
    }
1153
}
276✔
1154

1155
void MavsdkImpl::intercept_incoming_messages_async(std::function<bool(mavlink_message_t&)> callback)
22✔
1156
{
1157
    std::lock_guard<std::mutex> lock(_intercept_callbacks_mutex);
22✔
1158
    _intercept_incoming_messages_callback = callback;
22✔
1159
}
22✔
1160

1161
void MavsdkImpl::intercept_outgoing_messages_async(std::function<bool(mavlink_message_t&)> callback)
14✔
1162
{
1163
    std::lock_guard<std::mutex> lock(_intercept_callbacks_mutex);
14✔
1164
    _intercept_outgoing_messages_callback = callback;
14✔
1165
}
14✔
1166

1167
Mavsdk::ConnectionErrorHandle
1168
MavsdkImpl::subscribe_connection_errors(Mavsdk::ConnectionErrorCallback callback)
×
1169
{
1170
    std::lock_guard lock(_mutex);
×
1171

1172
    const auto handle = _connections_errors_subscriptions.subscribe(callback);
×
1173

1174
    return handle;
×
1175
}
×
1176

1177
void MavsdkImpl::unsubscribe_connection_errors(Mavsdk::ConnectionErrorHandle handle)
×
1178
{
1179
    std::lock_guard lock(_mutex);
×
1180
    _connections_errors_subscriptions.unsubscribe(handle);
×
1181
}
×
1182

1183
uint8_t MavsdkImpl::get_target_system_id(const mavlink_message_t& message)
2,172✔
1184
{
1185
    // Checks whether connection knows target system ID by extracting target system if set.
1186
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
2,172✔
1187

1188
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_SYSTEM)) {
2,169✔
1189
        return 0;
426✔
1190
    }
1191

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

1198
    return (_MAV_PAYLOAD(&message))[meta->target_system_ofs];
1,738✔
1199
}
1200

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

1206
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_COMPONENT)) {
×
1207
        return 0;
×
1208
    }
1209

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

1216
    return (_MAV_PAYLOAD(&message))[meta->target_component_ofs];
×
1217
}
1218

1219
Sender& MavsdkImpl::sender()
×
1220
{
1221
    std::lock_guard lock(_server_components_mutex);
×
1222
    return default_server_component_with_lock().sender();
×
1223
}
×
1224

1225
std::vector<Connection*> MavsdkImpl::get_connections() const
×
1226
{
1227
    std::vector<Connection*> connections;
×
1228
    for (const auto& connection_entry : _connections) {
×
1229
        connections.push_back(connection_entry.connection.get());
×
1230
    }
1231
    return connections;
×
1232
}
1233

UNCOV
1234
mav::MessageSet& MavsdkImpl::get_message_set() const
×
1235
{
UNCOV
1236
    return *_message_set;
×
1237
}
1238

1239
} // 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