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

mavlink / MAVSDK / 16710230981

03 Aug 2025 10:44PM UTC coverage: 46.564% (+0.5%) from 46.108%
16710230981

Pull #2637

github

web-flow
Merge 48f8b59d7 into 501170eb2
Pull Request #2637: Enable forwarding unknown messages

140 of 178 new or added lines in 6 files covered. (78.65%)

43 existing lines in 5 files now uncovered.

16316 of 35040 relevant lines covered (46.56%)

369.1 hits per line

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

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

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

55
    set_configuration(configuration);
107✔
56

57
    // Initialize MessageSet with embedded XML content in dependency order
58
    // This happens at startup before any connections are created, so no synchronization needed
59
    _message_set = std::make_unique<mav::MessageSet>();
107✔
60
    _message_set->addFromXMLString(mav_embedded::MINIMAL_XML);
107✔
61
    _message_set->addFromXMLString(mav_embedded::STANDARD_XML);
107✔
62
    _message_set->addFromXMLString(mav_embedded::COMMON_XML);
107✔
63
    _message_set->addFromXMLString(mav_embedded::ARDUPILOTMEGA_XML);
107✔
64

65
    // Initialize BufferParser for thread-safe parsing
66
    _buffer_parser = std::make_unique<mav::BufferParser>(*_message_set);
107✔
67

68
    // Start the user callback thread first, so it is ready for anything generated by
69
    // the work thread.
70

71
    _process_user_callbacks_thread =
214✔
72
        new std::thread(&MavsdkImpl::process_user_callbacks_thread, this);
107✔
73

74
    _work_thread = new std::thread(&MavsdkImpl::work_thread, this);
107✔
75
}
107✔
76

77
MavsdkImpl::~MavsdkImpl()
107✔
78
{
79
    {
80
        std::lock_guard<std::mutex> lock(_heartbeat_mutex);
107✔
81
        call_every_handler.remove(_heartbeat_send_cookie);
107✔
82
    }
107✔
83

84
    _should_exit = true;
107✔
85

86
    // Stop work first because we don't want to trigger anything that would
87
    // potentially want to call into user code.
88

89
    if (_work_thread != nullptr) {
107✔
90
        _work_thread->join();
107✔
91
        delete _work_thread;
107✔
92
        _work_thread = nullptr;
107✔
93
    }
94

95
    if (_process_user_callbacks_thread != nullptr) {
107✔
96
        _user_callback_queue.stop();
107✔
97
        _process_user_callbacks_thread->join();
107✔
98
        delete _process_user_callbacks_thread;
107✔
99
        _process_user_callbacks_thread = nullptr;
107✔
100
    }
101

102
    std::lock_guard lock(_mutex);
107✔
103

104
    _systems.clear();
107✔
105
    _connections.clear();
107✔
106
}
107✔
107

108
std::string MavsdkImpl::version()
1✔
109
{
110
    static unsigned version_counter = 0;
111

112
    ++version_counter;
1✔
113

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

136
std::vector<std::shared_ptr<System>> MavsdkImpl::systems() const
123✔
137
{
138
    std::vector<std::shared_ptr<System>> systems_result{};
123✔
139

140
    std::lock_guard lock(_mutex);
123✔
141
    for (auto& system : _systems) {
191✔
142
        // We ignore the 0 entry because it's just a null system.
143
        // It's only created because the older, deprecated API needs a
144
        // reference.
145
        if (system.first == 0) {
68✔
146
            continue;
×
147
        }
148
        systems_result.push_back(system.second);
68✔
149
    }
150

151
    return systems_result;
123✔
152
}
123✔
153

154
std::optional<std::shared_ptr<System>> MavsdkImpl::first_autopilot(double timeout_s)
42✔
155
{
156
    {
157
        std::lock_guard lock(_mutex);
42✔
158
        for (auto system : _systems) {
44✔
159
            if (system.second->is_connected() && system.second->has_autopilot()) {
4✔
160
                return system.second;
2✔
161
            }
162
        }
4✔
163
    }
42✔
164

165
    if (timeout_s == 0.0) {
40✔
166
        // Don't wait at all.
167
        return {};
×
168
    }
169

170
    auto prom = std::make_shared<std::promise<std::shared_ptr<System>>>();
40✔
171
    auto fut = prom->get_future();
40✔
172

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

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

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

202
std::shared_ptr<ServerComponent> MavsdkImpl::server_component(unsigned instance)
48✔
203
{
204
    std::lock_guard lock(_mutex);
48✔
205

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

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

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

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

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

274
        default:
×
275
            LogErr() << "Unknown server component type";
×
276
            return {};
×
277
    }
278
}
279

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

287
    std::lock_guard lock(_server_components_mutex);
48✔
288

289
    return server_component_by_id_with_lock(component_id);
48✔
290
}
48✔
291

292
std::shared_ptr<ServerComponent> MavsdkImpl::server_component_by_id_with_lock(uint8_t component_id)
208✔
293
{
294
    for (auto& it : _server_components) {
209✔
295
        if (it.first == component_id) {
101✔
296
            if (it.second != nullptr) {
100✔
297
                return it.second;
100✔
298
            } else {
299
                it.second = std::make_shared<ServerComponent>(*this, component_id);
×
300
            }
301
        }
302
    }
303

304
    _server_components.emplace_back(std::pair<uint8_t, std::shared_ptr<ServerComponent>>(
108✔
305
        component_id, std::make_shared<ServerComponent>(*this, component_id)));
216✔
306

307
    return _server_components.back().second;
108✔
308
}
309

310
void MavsdkImpl::forward_message(mavlink_message_t& message, Connection* connection)
33✔
311
{
312
    // Forward_message Function implementing Mavlink routing rules.
313
    // See https://mavlink.io/en/guide/routing.html
314

315
    bool forward_heartbeats_enabled = true;
33✔
316
    const uint8_t target_system_id = get_target_system_id(message);
33✔
317
    const uint8_t target_component_id = get_target_component_id(message);
33✔
318

319
    // If it's a message only for us, we keep it, otherwise, we forward it.
320
    const bool targeted_only_at_us =
321
        (target_system_id == get_own_system_id() && target_component_id == get_own_component_id());
33✔
322

323
    // We don't forward heartbeats unless it's specifically enabled.
324
    const bool heartbeat_check_ok =
33✔
325
        (message.msgid != MAVLINK_MSG_ID_HEARTBEAT || forward_heartbeats_enabled);
33✔
326

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

351
void MavsdkImpl::receive_message(mavlink_message_t& message, Connection* connection)
2,229✔
352
{
353
    {
354
        std::lock_guard lock(_received_messages_mutex);
2,229✔
355
        _received_messages.emplace(ReceivedMessage{std::move(message), connection});
2,235✔
356
    }
2,236✔
357
    _received_messages_cv.notify_one();
2,233✔
358
}
2,237✔
359

360
void MavsdkImpl::receive_libmav_message(const LibmavMessage& message, Connection* connection)
2,233✔
361
{
362
    {
363
        std::lock_guard lock(_received_libmav_messages_mutex);
2,233✔
364
        _received_libmav_messages.emplace(ReceivedLibmavMessage{message, connection});
2,226✔
365
    }
2,229✔
366
    _received_libmav_messages_cv.notify_one();
2,229✔
367
}
2,230✔
368

369
void MavsdkImpl::process_messages()
22,394✔
370
{
371
    std::lock_guard lock(_received_messages_mutex);
22,394✔
372
    while (!_received_messages.empty()) {
24,454✔
373
        auto message_copied = _received_messages.front();
2,215✔
374
        process_message(message_copied.message, message_copied.connection_ptr);
2,212✔
375
        _received_messages.pop();
2,219✔
376
    }
377
}
21,424✔
378

379
void MavsdkImpl::process_libmav_messages()
22,337✔
380
{
381
    std::lock_guard lock(_received_libmav_messages_mutex);
22,337✔
382
    while (!_received_libmav_messages.empty()) {
24,081✔
383
        auto message_copied = _received_libmav_messages.front();
2,212✔
384
        process_libmav_message(message_copied.message, message_copied.connection_ptr);
2,210✔
385
        _received_libmav_messages.pop();
2,214✔
386
    }
2,212✔
387
}
21,733✔
388

389
void MavsdkImpl::process_message(mavlink_message_t& message, Connection* connection)
2,214✔
390
{
391
    // Assumes _received_messages_mutex
392

393
    if (_message_logging_on) {
2,214✔
394
        LogDebug() << "Processing message " << message.msgid << " from "
×
395
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid);
×
396
    }
397

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

403
    {
404
        std::lock_guard lock(_mutex);
2,217✔
405

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

417
            if (!keep) {
2,212✔
418
                LogDebug() << "Dropped incoming message: " << int(message.msgid);
34✔
419
                return;
34✔
420
            }
421
        }
422

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

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

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

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

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

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

475
        bool found_system = false;
2,182✔
476
        for (auto& system : _systems) {
2,204✔
477
            if (system.first == message.sysid) {
2,103✔
478
                system.second->system_impl()->add_new_component(message.compid);
2,081✔
479
                found_system = true;
2,076✔
480
                break;
2,076✔
481
            }
482
        }
483

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

498
            // We now better talk back.
499
            start_sending_heartbeats();
104✔
500
        }
501

502
        if (_should_exit) {
2,178✔
503
            // Don't try to call at() if systems have already been destroyed
504
            // in destructor.
505
            return;
×
506
        }
507
    }
2,212✔
508

509
    mavlink_message_handler.process_message(message);
2,184✔
510

511
    for (auto& system : _systems) {
2,219✔
512
        if (system.first == message.sysid) {
2,213✔
513
            system.second->system_impl()->process_mavlink_message(message);
2,179✔
514
            break;
2,185✔
515
        }
516
    }
517
}
518

519
void MavsdkImpl::process_libmav_message(const LibmavMessage& message, Connection* /* connection */)
2,214✔
520
{
521
    // Assumes _received_libmav_messages_mutex
522

523
    if (_message_logging_on) {
2,214✔
524
        LogDebug() << "MavsdkImpl::process_libmav_message: " << message.message_name << " from "
×
525
                   << static_cast<int>(message.system_id) << "/"
×
526
                   << static_cast<int>(message.component_id);
×
527
    }
528

529
    if (_message_logging_on) {
2,214✔
530
        LogDebug() << "Processing libmav message " << message.message_name << " from "
×
531
                   << static_cast<int>(message.system_id) << "/"
×
532
                   << static_cast<int>(message.component_id);
×
533
    }
534

535
    if (_should_exit) {
2,214✔
536
        // If we're meant to clean up, let's not try to acquire any more locks but bail.
537
        return;
×
538
    }
539

540
    {
541
        std::lock_guard lock(_mutex);
2,210✔
542

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

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

560
        bool found_system = false;
2,212✔
561
        for (auto& system : _systems) {
2,247✔
562
            if (system.first == message.system_id) {
2,241✔
563
                system.second->system_impl()->add_new_component(message.component_id);
2,206✔
564
                found_system = true;
2,206✔
565
                break;
2,206✔
566
            }
567
        }
568

569
        if (!found_system) {
2,213✔
570
            if (_system_debugging) {
6✔
571
                LogWarn() << "Create new system/component from libmav " << (int)message.system_id
×
572
                          << "/" << (int)message.component_id;
×
573
            }
574
            make_system_with_component(message.system_id, message.component_id);
6✔
575

576
            // We now better talk back.
577
            start_sending_heartbeats();
6✔
578
        }
579

580
        if (_should_exit) {
2,213✔
581
            // Don't try to call at() if systems have already been destroyed
582
            // in destructor.
583
            return;
×
584
        }
585
    }
2,210✔
586

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

601
    if (!found_system) {
2,215✔
602
        LogWarn() << "No system found for libmav message " << message.message_name
×
603
                  << " from system " << message.system_id;
×
604
    }
605
}
606

607
bool MavsdkImpl::send_message(mavlink_message_t& message)
2,350✔
608
{
609
    // Create a copy of the message to avoid reference issues
610
    mavlink_message_t message_copy = message;
2,350✔
611

612
    {
613
        std::lock_guard lock(_messages_to_send_mutex);
2,350✔
614
        _messages_to_send.push(std::move(message_copy));
2,347✔
615
    }
2,349✔
616

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

624
    return true;
2,349✔
625
}
626

627
void MavsdkImpl::deliver_messages()
24,625✔
628
{
629
    // Process messages one at a time to avoid holding the mutex while delivering
630
    while (true) {
631
        mavlink_message_t message;
632
        {
633
            std::lock_guard lock(_messages_to_send_mutex);
24,625✔
634
            if (_messages_to_send.empty()) {
24,243✔
635
                break;
21,960✔
636
            }
637
            message = _messages_to_send.front();
2,347✔
638
            _messages_to_send.pop();
2,348✔
639
        }
24,309✔
640
        deliver_message(message);
2,350✔
641
    }
2,350✔
642
}
21,819✔
643

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

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

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

671
    std::lock_guard lock(_mutex);
2,258✔
672

673
    if (_connections.empty()) {
2,262✔
674
        // We obviously can't send any messages without a connection added, so
675
        // we silently ignore this.
676
        return;
52✔
677
    }
678

679
    uint8_t successful_emissions = 0;
2,206✔
680
    for (auto& _connection : _connections) {
4,427✔
681
        const uint8_t target_system_id = get_target_system_id(message);
2,216✔
682

683
        if (target_system_id != 0 && !(*_connection.connection).has_system_id(target_system_id)) {
2,225✔
684
            continue;
3✔
685
        }
686
        const auto result = (*_connection.connection).send_message(message);
2,218✔
687
        if (result.first) {
2,222✔
688
            successful_emissions++;
2,217✔
689
        } else {
690
            _connections_errors_subscriptions.queue(
10✔
691
                Mavsdk::ConnectionError{result.second, _connection.handle},
5✔
692
                [this](const auto& func) { call_user_callback(func); });
×
693
        }
694
    }
2,222✔
695

696
    if (successful_emissions == 0) {
2,208✔
697
        LogErr() << "Sending message failed";
×
698
    }
699
}
2,261✔
700

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

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

729
std::pair<ConnectionResult, Mavsdk::ConnectionHandle>
730
MavsdkImpl::add_udp_connection(const CliArg::Udp& udp, ForwardingOption forwarding_option)
106✔
731
{
732
    auto new_conn = std::make_unique<UdpConnection>(
733
        [this](mavlink_message_t& message, Connection* connection) {
2,213✔
734
            receive_message(message, connection);
2,213✔
735
        },
2,217✔
736
        [this](const LibmavMessage& message, Connection* connection) {
2,211✔
737
            receive_libmav_message(message, connection);
2,211✔
738
        },
2,214✔
739
        *this, // Pass MavsdkImpl reference for thread-safe MessageSet access
740
        udp.mode == CliArg::Udp::Mode::In ? udp.host : "0.0.0.0",
212✔
741
        udp.mode == CliArg::Udp::Mode::In ? udp.port : 0,
106✔
742
        forwarding_option);
106✔
743

744
    if (!new_conn) {
106✔
745
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
746
    }
747

748
    ConnectionResult ret = new_conn->start();
106✔
749

750
    if (ret != ConnectionResult::Success) {
106✔
751
        return {ret, Mavsdk::ConnectionHandle{}};
×
752
    }
753

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

759
        if (!remote_ip) {
53✔
760
            return {ConnectionResult::DestinationIpUnknown, Mavsdk::ConnectionHandle{}};
×
761
        }
762

763
        new_conn->add_remote_to_keep(remote_ip.value(), udp.port);
53✔
764
        std::lock_guard lock(_mutex);
53✔
765

766
        // With a UDP remote, we need to initiate the connection by sending heartbeats.
767
        auto new_configuration = get_configuration();
53✔
768
        new_configuration.set_always_send_heartbeats(true);
53✔
769
        set_configuration(new_configuration);
53✔
770
    }
53✔
771

772
    auto handle = add_connection(std::move(new_conn));
106✔
773

774
    return {ConnectionResult::Success, handle};
106✔
775
}
106✔
776

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

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

850
        auto new_configuration = get_configuration();
×
851

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

858
        return {ret, handle};
×
859

860
    } else {
861
        return {ret, Mavsdk::ConnectionHandle{}};
×
862
    }
863
}
×
864

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

871
    return handle;
216✔
872
}
108✔
873

874
void MavsdkImpl::remove_connection(Mavsdk::ConnectionHandle handle)
×
875
{
876
    std::lock_guard lock(_mutex);
×
877

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

883
Mavsdk::Configuration MavsdkImpl::get_configuration() const
53✔
884
{
885
    std::lock_guard configuration_lock(_mutex);
53✔
886
    return _configuration;
106✔
887
}
53✔
888

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

898
    if (new_configuration.get_always_send_heartbeats() &&
265✔
899
        !_configuration.get_always_send_heartbeats()) {
105✔
900
        start_sending_heartbeats();
54✔
901
    } else if (
106✔
902
        !new_configuration.get_always_send_heartbeats() &&
161✔
903
        _configuration.get_always_send_heartbeats() && !is_any_system_connected()) {
161✔
904
        stop_sending_heartbeats();
×
905
    }
906

907
    _configuration = new_configuration;
160✔
908
    // We cache these values as atomic to avoid having to lock any mutex for them.
909
    _our_system_id = new_configuration.get_system_id();
160✔
910
    _our_component_id = new_configuration.get_component_id();
160✔
911
}
160✔
912

913
uint8_t MavsdkImpl::get_own_system_id() const
5,451✔
914
{
915
    return _our_system_id;
5,451✔
916
}
917

918
uint8_t MavsdkImpl::get_own_component_id() const
1,411✔
919
{
920
    return _our_component_id;
1,411✔
921
}
922

923
uint8_t MavsdkImpl::channel() const
×
924
{
925
    // TODO
926
    return 0;
×
927
}
928

929
Autopilot MavsdkImpl::autopilot() const
×
930
{
931
    // TODO
932
    return Autopilot::Px4;
×
933
}
934

935
// FIXME: this should be per component
936
uint8_t MavsdkImpl::get_mav_type() const
322✔
937
{
938
    return _configuration.get_mav_type();
322✔
939
}
940

941
void MavsdkImpl::make_system_with_component(uint8_t system_id, uint8_t comp_id)
110✔
942
{
943
    // Needs _systems_lock
944

945
    if (_should_exit) {
110✔
946
        // When the system got destroyed in the destructor, we have to give up.
947
        return;
×
948
    }
949

950
    if (static_cast<int>(system_id) == 0 && static_cast<int>(comp_id) == 0) {
110✔
951
        LogDebug() << "Initializing connection to remote system...";
×
952
    } else {
953
        LogDebug() << "New system ID: " << static_cast<int>(system_id)
220✔
954
                   << " Comp ID: " << static_cast<int>(comp_id);
110✔
955
    }
956

957
    // Make a system with its first component
958
    auto new_system = std::make_shared<System>(*this);
110✔
959
    new_system->init(system_id, comp_id);
110✔
960

961
    _systems.emplace_back(system_id, new_system);
110✔
962
}
110✔
963

964
void MavsdkImpl::notify_on_discover()
110✔
965
{
966
    // Queue the callbacks without holding the mutex to avoid deadlocks
967
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
160✔
968
}
110✔
969

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

976
Mavsdk::NewSystemHandle
977
MavsdkImpl::subscribe_on_new_system(const Mavsdk::NewSystemCallback& callback)
50✔
978
{
979
    std::lock_guard lock(_mutex);
50✔
980

981
    const auto handle = _new_system_callbacks.subscribe(callback);
50✔
982

983
    if (is_any_system_connected()) {
50✔
984
        _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
985
    }
986

987
    return handle;
100✔
988
}
50✔
989

990
void MavsdkImpl::unsubscribe_on_new_system(Mavsdk::NewSystemHandle handle)
49✔
991
{
992
    _new_system_callbacks.unsubscribe(handle);
49✔
993
}
49✔
994

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

1003
void MavsdkImpl::work_thread()
107✔
1004
{
1005
    while (!_should_exit) {
22,188✔
1006
        // Process incoming messages
1007
        process_messages();
21,606✔
1008

1009
        // Process incoming libmav messages
1010
        process_libmav_messages();
21,488✔
1011

1012
        // Run timers
1013
        timeout_handler.run_once();
21,636✔
1014
        call_every_handler.run_once();
22,025✔
1015

1016
        // Do component work
1017
        {
1018
            std::lock_guard lock(_server_components_mutex);
22,292✔
1019
            for (auto& it : _server_components) {
44,443✔
1020
                if (it.second != nullptr) {
22,382✔
1021
                    it.second->_impl->do_work();
22,249✔
1022
                }
1023
            }
1024
        }
21,703✔
1025

1026
        // Deliver outgoing messages
1027
        deliver_messages();
21,831✔
1028

1029
        // If no messages to send, check if there are messages to receive
1030
        std::unique_lock lock_received(_received_messages_mutex);
21,815✔
1031
        if (_received_messages.empty()) {
21,918✔
1032
            // No messages to process, wait for a signal or timeout
1033
            _received_messages_cv.wait_for(lock_received, std::chrono::milliseconds(10), [this]() {
21,702✔
1034
                return !_received_messages.empty() || _should_exit;
43,939✔
1035
            });
1036
        }
1037
    }
22,364✔
1038
}
125✔
1039

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

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

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

1059
    } else if (callback_size == 100) {
1,083✔
1060
        return;
×
1061
    }
1062

1063
    // We only need to keep track of filename and linenumber if we're actually debugging this.
1064
    UserCallback user_callback =
1065
        _callback_debugging ? UserCallback{func, filename, linenumber} : UserCallback{func};
2,166✔
1066

1067
    _user_callback_queue.push_back(std::make_shared<UserCallback>(user_callback));
1,083✔
1068
}
1,083✔
1069

1070
void MavsdkImpl::process_user_callbacks_thread()
107✔
1071
{
1072
    while (!_should_exit) {
1,297✔
1073
        UserCallback callback;
1,190✔
1074
        {
1075
            LockedQueue<UserCallback>::Guard guard(_user_callback_queue);
1,190✔
1076
            auto ptr = guard.wait_and_pop_front();
1,190✔
1077
            if (!ptr) {
1,190✔
1078
                continue;
107✔
1079
            }
1080
            // We need to get a copy instead of just a shared_ptr because the queue might
1081
            // be invalidated when the lock is released.
1082
            callback = *ptr;
1,083✔
1083
        }
1,297✔
1084

1085
        // Check if we're in the process of shutting down before executing the callback
1086
        if (_should_exit) {
1,083✔
1087
            continue;
×
1088
        }
1089

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

1112
void MavsdkImpl::start_sending_heartbeats()
274✔
1113
{
1114
    // Check if we're in the process of shutting down
1115
    if (_should_exit) {
274✔
1116
        return;
×
1117
    }
1118

1119
    // Before sending out first heartbeats we need to make sure we have a
1120
    // default server component.
1121
    default_server_component_impl();
274✔
1122

1123
    {
1124
        std::lock_guard<std::mutex> lock(_heartbeat_mutex);
274✔
1125
        call_every_handler.remove(_heartbeat_send_cookie);
274✔
1126
        _heartbeat_send_cookie =
274✔
1127
            call_every_handler.add([this]() { send_heartbeats(); }, HEARTBEAT_SEND_INTERVAL_S);
596✔
1128
    }
274✔
1129
}
1130

1131
void MavsdkImpl::stop_sending_heartbeats()
×
1132
{
1133
    if (!_configuration.get_always_send_heartbeats()) {
×
1134
        std::lock_guard<std::mutex> lock(_heartbeat_mutex);
×
1135
        call_every_handler.remove(_heartbeat_send_cookie);
×
1136
    }
×
1137
}
×
1138

1139
ServerComponentImpl& MavsdkImpl::default_server_component_impl()
1,175✔
1140
{
1141
    std::lock_guard lock(_server_components_mutex);
1,175✔
1142
    return default_server_component_with_lock();
1,174✔
1143
}
1,174✔
1144

1145
ServerComponentImpl& MavsdkImpl::default_server_component_with_lock()
1,174✔
1146
{
1147
    if (_default_server_component == nullptr) {
1,174✔
1148
        _default_server_component = server_component_by_id_with_lock(_our_component_id);
×
1149
    }
1150
    return *_default_server_component->_impl;
1,173✔
1151
}
1152

1153
void MavsdkImpl::send_heartbeats()
322✔
1154
{
1155
    std::lock_guard lock(_server_components_mutex);
322✔
1156

1157
    for (auto& it : _server_components) {
641✔
1158
        if (it.second != nullptr) {
324✔
1159
            it.second->_impl->send_heartbeat();
324✔
1160
        }
1161
    }
1162
}
320✔
1163

1164
void MavsdkImpl::intercept_incoming_messages_async(std::function<bool(mavlink_message_t&)> callback)
22✔
1165
{
1166
    std::lock_guard<std::mutex> lock(_intercept_callbacks_mutex);
22✔
1167
    _intercept_incoming_messages_callback = callback;
22✔
1168
}
22✔
1169

1170
void MavsdkImpl::intercept_outgoing_messages_async(std::function<bool(mavlink_message_t&)> callback)
14✔
1171
{
1172
    std::lock_guard<std::mutex> lock(_intercept_callbacks_mutex);
14✔
1173
    _intercept_outgoing_messages_callback = callback;
14✔
1174
}
14✔
1175

1176
Mavsdk::ConnectionErrorHandle
1177
MavsdkImpl::subscribe_connection_errors(Mavsdk::ConnectionErrorCallback callback)
×
1178
{
1179
    std::lock_guard lock(_mutex);
×
1180

1181
    const auto handle = _connections_errors_subscriptions.subscribe(callback);
×
1182

1183
    return handle;
×
1184
}
×
1185

1186
void MavsdkImpl::unsubscribe_connection_errors(Mavsdk::ConnectionErrorHandle handle)
×
1187
{
1188
    std::lock_guard lock(_mutex);
×
1189
    _connections_errors_subscriptions.unsubscribe(handle);
×
1190
}
×
1191

1192
uint8_t MavsdkImpl::get_target_system_id(const mavlink_message_t& message)
2,258✔
1193
{
1194
    // Checks whether connection knows target system ID by extracting target system if set.
1195
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
2,258✔
1196

1197
    if (meta == nullptr) {
2,256✔
1198
        // For unknown messages, assume broadcast (target_system = 0)
1199
        // This ensures unknown messages are forwarded to all connections
1200
        if (_message_logging_on) {
5✔
UNCOV
1201
            LogDebug() << "Unknown message ID " << message.msgid
×
UNCOV
1202
                       << " - assuming broadcast forwarding";
×
1203
        }
1204
        return 0;
4✔
1205
    }
1206

1207
    if (!(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_SYSTEM)) {
2,251✔
1208
        return 0;
499✔
1209
    }
1210

1211
    // Don't look at the target system offset if it is outside the payload length.
1212
    // This can happen if the fields are trimmed.
1213
    if (meta->target_system_ofs >= message.len) {
1,752✔
1214
        return 0;
13✔
1215
    }
1216

1217
    return (_MAV_PAYLOAD(&message))[meta->target_system_ofs];
1,739✔
1218
}
1219

1220
uint8_t MavsdkImpl::get_target_component_id(const mavlink_message_t& message)
33✔
1221
{
1222
    // Checks whether connection knows target component ID by extracting target component if set.
1223
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
33✔
1224

1225
    if (meta == nullptr) {
33✔
1226
        // For unknown messages, assume broadcast (target_component = 0)
1227
        // This ensures unknown messages are forwarded to all connections
1228
        if (_message_logging_on) {
1✔
UNCOV
1229
            LogDebug() << "Unknown message ID " << message.msgid
×
1230
                       << " - assuming broadcast forwarding";
×
1231
        }
1232
        return 0;
1✔
1233
    }
1234

1235
    if (!(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_COMPONENT)) {
32✔
1236
        return 0;
17✔
1237
    }
1238

1239
    // Don't look at the target component offset if it is outside the payload length.
1240
    // This can happen if the fields are trimmed.
1241
    if (meta->target_component_ofs >= message.len) {
15✔
1242
        return 0;
1✔
1243
    }
1244

1245
    return (_MAV_PAYLOAD(&message))[meta->target_component_ofs];
14✔
1246
}
1247

UNCOV
1248
Sender& MavsdkImpl::sender()
×
1249
{
UNCOV
1250
    std::lock_guard lock(_server_components_mutex);
×
UNCOV
1251
    return default_server_component_with_lock().sender();
×
UNCOV
1252
}
×
1253

UNCOV
1254
std::vector<Connection*> MavsdkImpl::get_connections() const
×
1255
{
UNCOV
1256
    std::vector<Connection*> connections;
×
UNCOV
1257
    for (const auto& connection_entry : _connections) {
×
UNCOV
1258
        connections.push_back(connection_entry.connection.get());
×
1259
    }
UNCOV
1260
    return connections;
×
1261
}
1262

1263
mav::MessageSet& MavsdkImpl::get_message_set() const
10✔
1264
{
1265
    // Note: This returns a reference to MessageSet without locking.
1266
    // Thread safety for MessageSet operations must be ensured by:
1267
    // 1. Using load_custom_xml_to_message_set() for write operations (XML loading)
1268
    // 2. libmav MessageSet should be internally thread-safe for read operations
1269
    // 3. If race conditions persist, consider implementing a thread-safe MessageSet wrapper
1270
    return *_message_set;
10✔
1271
}
1272

1273
bool MavsdkImpl::load_custom_xml_to_message_set(const std::string& xml_content)
4✔
1274
{
1275
    std::lock_guard<std::mutex> lock(_message_set_mutex);
4✔
1276
    auto result = _message_set->addFromXMLString(xml_content, false /* recursive_open_includes */);
4✔
1277
    return result == ::mav::MessageSetResult::Success;
8✔
1278
}
4✔
1279

1280
// Thread-safe MessageSet read operations
1281
std::optional<std::string> MavsdkImpl::message_id_to_name_safe(uint32_t id) const
×
1282
{
UNCOV
1283
    std::lock_guard<std::mutex> lock(_message_set_mutex);
×
UNCOV
1284
    auto message_def = _message_set->getMessageDefinition(static_cast<int>(id));
×
UNCOV
1285
    if (message_def) {
×
UNCOV
1286
        return message_def.get().name();
×
1287
    }
UNCOV
1288
    return std::nullopt;
×
UNCOV
1289
}
×
1290

UNCOV
1291
std::optional<int> MavsdkImpl::message_name_to_id_safe(const std::string& name) const
×
1292
{
UNCOV
1293
    std::lock_guard<std::mutex> lock(_message_set_mutex);
×
UNCOV
1294
    return _message_set->idForMessage(name);
×
UNCOV
1295
}
×
1296

UNCOV
1297
std::optional<mav::Message> MavsdkImpl::create_message_safe(const std::string& message_name) const
×
1298
{
UNCOV
1299
    std::lock_guard<std::mutex> lock(_message_set_mutex);
×
UNCOV
1300
    return _message_set->create(message_name);
×
UNCOV
1301
}
×
1302

1303
// Thread-safe parsing for LibmavReceiver
1304
std::optional<mav::Message> MavsdkImpl::parse_message_safe(
2,228✔
1305
    const uint8_t* buffer, size_t buffer_len, size_t& bytes_consumed) const
1306
{
1307
    std::lock_guard<std::mutex> lock(_message_set_mutex);
2,228✔
1308
    return _buffer_parser->parseMessage(buffer, buffer_len, bytes_consumed);
2,233✔
1309
}
2,227✔
1310

1311
mav::OptionalReference<const mav::MessageDefinition>
1312
MavsdkImpl::get_message_definition_safe(int message_id) const
2,229✔
1313
{
1314
    std::lock_guard<std::mutex> lock(_message_set_mutex);
2,229✔
1315
    return _message_set->getMessageDefinition(message_id);
2,231✔
1316
}
2,231✔
1317

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