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

mavlink / MAVSDK / 15058408579

16 May 2025 01:11AM UTC coverage: 44.27% (-0.04%) from 44.305%
15058408579

push

github

web-flow
Merge pull request #2576 from mavlink/pr-windows-nproc

CI: hard-code Windows to 4 threads

14843 of 33528 relevant lines covered (44.27%)

279.2 hits per line

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

58.96
/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 "plugin_base.h"
18
#include "mavlink_channels.h"
19
#include "callback_list.tpp"
20
#include "hostname_to_ip.h"
21

22
namespace mavsdk {
23

24
template class CallbackList<>;
25

26
MavsdkImpl::MavsdkImpl(const Mavsdk::Configuration& configuration) :
87✔
27
    timeout_handler(time),
87✔
28
    call_every_handler(time)
174✔
29
{
30
    LogInfo() << "MAVSDK version: " << mavsdk_version;
87✔
31

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

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

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

53
    set_configuration(configuration);
87✔
54

55
    // Start the user callback thread first, so it is ready for anything generated by
56
    // the work thread.
57

58
    _process_user_callbacks_thread =
174✔
59
        new std::thread(&MavsdkImpl::process_user_callbacks_thread, this);
87✔
60

61
    _work_thread = new std::thread(&MavsdkImpl::work_thread, this);
87✔
62
}
87✔
63

64
MavsdkImpl::~MavsdkImpl()
87✔
65
{
66
    _should_exit = true;
87✔
67

68
    // Stop work first because we don't want to trigger anything that would
69
    // potentially want to call into user code.
70

71
    if (_work_thread != nullptr) {
87✔
72
        _work_thread->join();
87✔
73
        delete _work_thread;
87✔
74
        _work_thread = nullptr;
87✔
75
    }
76

77
    if (_process_user_callbacks_thread != nullptr) {
87✔
78
        _user_callback_queue.stop();
87✔
79
        _process_user_callbacks_thread->join();
87✔
80
        delete _process_user_callbacks_thread;
87✔
81
        _process_user_callbacks_thread = nullptr;
87✔
82
    }
83

84
    {
85
        std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
87✔
86
        _systems.clear();
87✔
87
    }
87✔
88

89
    {
90
        std::lock_guard<std::mutex> lock(_connections_mutex);
87✔
91
        _connections.clear();
87✔
92
    }
87✔
93
}
87✔
94

95
std::string MavsdkImpl::version()
1✔
96
{
97
    static unsigned version_counter = 0;
98

99
    ++version_counter;
1✔
100

101
    switch (version_counter) {
1✔
102
        case 10:
×
103
            return "You were wondering about the name of this library?";
×
104
        case 11:
×
105
            return "Let's look at the history:";
×
106
        case 12:
×
107
            return "DroneLink";
×
108
        case 13:
×
109
            return "DroneCore";
×
110
        case 14:
×
111
            return "DronecodeSDK";
×
112
        case 15:
×
113
            return "MAVSDK";
×
114
        case 16:
×
115
            return "And that's it...";
×
116
        case 17:
×
117
            return "At least for now ¯\\_(ツ)_/¯.";
×
118
        default:
1✔
119
            return mavsdk_version;
1✔
120
    }
121
}
122

123
std::vector<std::shared_ptr<System>> MavsdkImpl::systems() const
86✔
124
{
125
    std::vector<std::shared_ptr<System>> systems_result{};
86✔
126

127
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
86✔
128
    for (auto& system : _systems) {
129✔
129
        // We ignore the 0 entry because it's just a null system.
130
        // It's only created because the older, deprecated API needs a
131
        // reference.
132
        if (system.first == 0) {
43✔
133
            continue;
×
134
        }
135
        systems_result.push_back(system.second);
43✔
136
    }
137

138
    return systems_result;
86✔
139
}
86✔
140

141
std::optional<std::shared_ptr<System>> MavsdkImpl::first_autopilot(double timeout_s)
33✔
142
{
143
    {
144
        std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
33✔
145
        for (auto system : _systems) {
33✔
146
            if (system.second->is_connected() && system.second->has_autopilot()) {
×
147
                return system.second;
×
148
            }
149
        }
×
150
    }
33✔
151

152
    if (timeout_s == 0.0) {
33✔
153
        // Don't wait at all.
154
        return {};
×
155
    }
156

157
    auto prom = std::promise<std::shared_ptr<System>>();
33✔
158

159
    std::once_flag flag;
33✔
160
    auto handle = subscribe_on_new_system([this, &prom, &flag]() {
66✔
161
        const auto system = systems().at(0);
66✔
162
        if (system->is_connected() && system->has_autopilot()) {
33✔
163
            std::call_once(flag, [&prom, &system]() { prom.set_value(system); });
66✔
164
        }
165
    });
66✔
166

167
    auto fut = prom.get_future();
33✔
168

169
    if (timeout_s > 0.0) {
33✔
170
        if (fut.wait_for(std::chrono::milliseconds(int64_t(timeout_s * 1e3))) ==
33✔
171
            std::future_status::ready) {
172
            unsubscribe_on_new_system(handle);
33✔
173
            return fut.get();
33✔
174

175
        } else {
176
            unsubscribe_on_new_system(handle);
×
177
            return std::nullopt;
×
178
        }
179
    } else {
180
        fut.wait();
×
181
        unsubscribe_on_new_system(handle);
×
182
        return std::optional(fut.get());
×
183
    }
184
}
33✔
185

186
std::shared_ptr<ServerComponent> MavsdkImpl::server_component(unsigned instance)
47✔
187
{
188
    auto component_type = _configuration.get_component_type();
47✔
189
    switch (component_type) {
47✔
190
        case ComponentType::Autopilot:
47✔
191
        case ComponentType::GroundStation:
192
        case ComponentType::CompanionComputer:
193
        case ComponentType::Camera:
194
        case ComponentType::Gimbal:
195
        case ComponentType::RemoteId:
196
        case ComponentType::Custom:
197
            return server_component_by_type(component_type, instance);
47✔
198
        default:
×
199
            LogErr() << "Unknown component type";
×
200
            return {};
×
201
    }
202
}
203

204
std::shared_ptr<ServerComponent>
205
MavsdkImpl::server_component_by_type(ComponentType server_component_type, unsigned instance)
47✔
206
{
207
    switch (server_component_type) {
47✔
208
        case ComponentType::Autopilot:
33✔
209
            if (instance == 0) {
33✔
210
                return server_component_by_id(MAV_COMP_ID_AUTOPILOT1);
33✔
211
            } else {
212
                LogErr() << "Only autopilot instance 0 is valid";
×
213
                return {};
×
214
            }
215

216
        case ComponentType::GroundStation:
×
217
            if (instance == 0) {
×
218
                return server_component_by_id(MAV_COMP_ID_MISSIONPLANNER);
×
219
            } else {
220
                LogErr() << "Only one ground station supported at this time";
×
221
                return {};
×
222
            }
223

224
        case ComponentType::CompanionComputer:
1✔
225
            if (instance == 0) {
1✔
226
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER);
1✔
227
            } else if (instance == 1) {
×
228
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER2);
×
229
            } else if (instance == 2) {
×
230
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER3);
×
231
            } else if (instance == 3) {
×
232
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER4);
×
233
            } else {
234
                LogErr() << "Only companion computer 0..3 are supported";
×
235
                return {};
×
236
            }
237

238
        case ComponentType::Camera:
13✔
239
            if (instance == 0) {
13✔
240
                return server_component_by_id(MAV_COMP_ID_CAMERA);
13✔
241
            } else if (instance == 1) {
×
242
                return server_component_by_id(MAV_COMP_ID_CAMERA2);
×
243
            } else if (instance == 2) {
×
244
                return server_component_by_id(MAV_COMP_ID_CAMERA3);
×
245
            } else if (instance == 3) {
×
246
                return server_component_by_id(MAV_COMP_ID_CAMERA4);
×
247
            } else if (instance == 4) {
×
248
                return server_component_by_id(MAV_COMP_ID_CAMERA5);
×
249
            } else if (instance == 5) {
×
250
                return server_component_by_id(MAV_COMP_ID_CAMERA6);
×
251
            } else {
252
                LogErr() << "Only camera 0..5 are supported";
×
253
                return {};
×
254
            }
255

256
        default:
×
257
            LogErr() << "Unknown server component type";
×
258
            return {};
×
259
    }
260
}
261

262
std::shared_ptr<ServerComponent> MavsdkImpl::server_component_by_id(uint8_t component_id)
176✔
263
{
264
    if (component_id == 0) {
176✔
265
        LogErr() << "Server component with component ID 0 not allowed";
×
266
        return nullptr;
×
267
    }
268

269
    std::lock_guard<std::mutex> lock(_server_components_mutex);
176✔
270

271
    for (auto& it : _server_components) {
177✔
272
        if (it.first == component_id) {
89✔
273
            if (it.second != nullptr) {
88✔
274
                return it.second;
88✔
275
            } else {
276
                it.second = std::make_shared<ServerComponent>(*this, component_id);
×
277
            }
278
        }
279
    }
280

281
    _server_components.emplace_back(std::pair<uint8_t, std::shared_ptr<ServerComponent>>(
176✔
282
        component_id, std::make_shared<ServerComponent>(*this, component_id)));
176✔
283

284
    return _server_components.back().second;
88✔
285
}
176✔
286

287
void MavsdkImpl::forward_message(mavlink_message_t& message, Connection* connection)
×
288
{
289
    // Forward_message Function implementing Mavlink routing rules.
290
    // See https://mavlink.io/en/guide/routing.html
291

292
    bool forward_heartbeats_enabled = true;
×
293
    const uint8_t target_system_id = get_target_system_id(message);
×
294
    const uint8_t target_component_id = get_target_component_id(message);
×
295

296
    // If it's a message only for us, we keep it, otherwise, we forward it.
297
    const bool targeted_only_at_us =
298
        (target_system_id == get_own_system_id() && target_component_id == get_own_component_id());
×
299

300
    // We don't forward heartbeats unless it's specifically enabled.
301
    const bool heartbeat_check_ok =
×
302
        (message.msgid != MAVLINK_MSG_ID_HEARTBEAT || forward_heartbeats_enabled);
×
303

304
    if (!targeted_only_at_us && heartbeat_check_ok) {
×
305
        unsigned successful_emissions = 0;
×
306
        for (auto& entry : _connections) {
×
307
            // Check whether the connection is not the one from which we received the message.
308
            // And also check if the connection was set to forward messages.
309
            if (entry.connection.get() == connection ||
×
310
                !entry.connection->should_forward_messages()) {
×
311
                continue;
×
312
            }
313
            auto result = (*entry.connection).send_message(message);
×
314
            if (result.first) {
×
315
                successful_emissions++;
×
316
            } else {
317
                _connections_errors_subscriptions.queue(
×
318
                    Mavsdk::ConnectionError{result.second, entry.handle},
×
319
                    [this](const auto& func) { call_user_callback(func); });
×
320
            }
321
        }
×
322
        if (successful_emissions == 0) {
×
323
            LogErr() << "Message forwarding failed";
×
324
        }
325
    }
326
}
×
327

328
void MavsdkImpl::receive_message(mavlink_message_t& message, Connection* connection)
1,944✔
329
{
330
    if (_message_logging_on) {
1,944✔
331
        LogDebug() << "Processing message " << message.msgid << " from "
×
332
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid);
×
333
    }
334

335
    if (_should_exit) {
1,944✔
336
        // If we're meant to clean up, let's not try to acquire any more locks but bail.
337
        return;
7✔
338
    }
339

340
    // This is a low level interface where incoming messages can be tampered
341
    // with or even dropped.
342
    {
343
        std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
1,937✔
344
        if (_intercept_incoming_messages_callback != nullptr) {
1,937✔
345
            bool keep = _intercept_incoming_messages_callback(message);
245✔
346
            if (!keep) {
245✔
347
                LogDebug() << "Dropped incoming message: " << int(message.msgid);
40✔
348
                return;
40✔
349
            }
350
        }
351
    }
1,936✔
352

353
    if (_should_exit) {
1,896✔
354
        // If we're meant to clean up, let's not try to acquire any more locks but bail.
355
        return;
×
356
    }
357

358
    /** @note: Forward message if option is enabled and multiple interfaces are connected.
359
     *  Performs message forwarding checks for every messages if message forwarding
360
     *  is enabled on at least one connection, and in case of a single forwarding connection,
361
     *  we check that it is not the one which received the current message.
362
     *
363
     * Conditions:
364
     * 1. At least 2 connections.
365
     * 2. At least 1 forwarding connection.
366
     * 3. At least 2 forwarding connections or current connection is not forwarding.
367
     */
368

369
    {
370
        std::lock_guard<std::mutex> lock(_connections_mutex);
1,896✔
371

372
        if (_connections.size() > 1 && mavsdk::Connection::forwarding_connections_count() > 0 &&
1,897✔
373
            (mavsdk::Connection::forwarding_connections_count() > 1 ||
×
374
             !connection->should_forward_messages())) {
×
375
            if (_message_logging_on) {
×
376
                LogDebug() << "Forwarding message " << message.msgid << " from "
×
377
                           << static_cast<int>(message.sysid) << "/"
×
378
                           << static_cast<int>(message.compid);
×
379
            }
380
            forward_message(message, connection);
×
381
        }
382
    }
1,896✔
383

384
    // Don't ever create a system with sysid 0.
385
    if (message.sysid == 0) {
1,897✔
386
        if (_message_logging_on) {
×
387
            LogDebug() << "Ignoring message with sysid == 0";
×
388
        }
389
        return;
×
390
    }
391

392
    // Filter out messages by QGroundControl, however, only do that if MAVSDK
393
    // is also implementing a ground station and not if it is used in another
394
    // configuration, e.g. on a companion.
395
    //
396
    // This is a workaround because PX4 started forwarding messages between
397
    // mavlink instances which leads to existing implementations (including
398
    // examples and integration tests) to connect to QGroundControl by accident
399
    // instead of PX4 because the check `has_autopilot()` is not used.
400
    if (_configuration.get_component_type() == ComponentType::GroundStation &&
1,897✔
401
        message.sysid == 255 && message.compid == MAV_COMP_ID_MISSIONPLANNER) {
1,896✔
402
        if (_message_logging_on) {
×
403
            LogDebug() << "Ignoring messages from QGC as we are also a ground station";
×
404
        }
405
        return;
×
406
    }
407

408
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
1,896✔
409

410
    bool found_system = false;
1,897✔
411
    for (auto& system : _systems) {
1,897✔
412
        if (system.first == message.sysid) {
1,811✔
413
            system.second->system_impl()->add_new_component(message.compid);
1,811✔
414
            found_system = true;
1,810✔
415
            break;
1,810✔
416
        }
417
    }
418

419
    if (!found_system) {
1,895✔
420
        if (_system_debugging) {
86✔
421
            LogWarn() << "Create new system/component " << (int)message.sysid << "/"
×
422
                      << (int)message.compid;
×
423
            LogWarn() << "From message " << (int)message.msgid << " with len " << (int)message.len;
×
424
            std::string bytes = "";
×
425
            for (unsigned i = 0; i < 12 + message.len; ++i) {
×
426
                bytes += std::to_string(reinterpret_cast<uint8_t*>(&message)[i]) + ' ';
×
427
            }
428
            LogWarn() << "Bytes: " << bytes;
×
429
        }
×
430
        make_system_with_component(message.sysid, message.compid);
86✔
431
    }
432

433
    if (_should_exit) {
1,895✔
434
        // Don't try to call at() if systems have already been destroyed
435
        // in destructor.
436
        return;
×
437
    }
438

439
    mavlink_message_handler.process_message(message);
1,897✔
440

441
    for (auto& system : _systems) {
1,897✔
442
        if (system.first == message.sysid) {
1,896✔
443
            system.second->system_impl()->process_mavlink_message(message);
1,896✔
444
            break;
1,897✔
445
        }
446
    }
447
}
1,897✔
448

449
bool MavsdkImpl::send_message(mavlink_message_t& message)
2,068✔
450
{
451
    if (_message_logging_on) {
2,068✔
452
        LogDebug() << "Sending message " << message.msgid << " from "
×
453
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid)
×
454
                   << " to " << static_cast<int>(get_target_system_id(message)) << "/"
×
455
                   << static_cast<int>(get_target_component_id(message));
×
456
    }
457

458
    // This is a low level interface where outgoing messages can be tampered
459
    // with or even dropped.
460
    if (_intercept_outgoing_messages_callback != nullptr) {
2,068✔
461
        const bool keep = _intercept_outgoing_messages_callback(message);
221✔
462
        if (!keep) {
221✔
463
            // We fake that everything was sent as instructed because
464
            // a potential loss would happen later, and we would not be informed
465
            // about it.
466
            LogDebug() << "Dropped outgoing message: " << int(message.msgid);
81✔
467
            return true;
81✔
468
        }
469
    }
470

471
    std::lock_guard<std::mutex> lock(_connections_mutex);
1,987✔
472

473
    if (_connections.empty()) {
1,986✔
474
        // We obviously can't send any messages without a connection added, so
475
        // we silently ignore this.
476
        return true;
43✔
477
    }
478

479
    uint8_t successful_emissions = 0;
1,944✔
480
    for (auto& _connection : _connections) {
3,887✔
481
        const uint8_t target_system_id = get_target_system_id(message);
1,944✔
482

483
        if (target_system_id != 0 && !(*_connection.connection).has_system_id(target_system_id)) {
1,943✔
484
            continue;
×
485
        }
486
        const auto result = (*_connection.connection).send_message(message);
1,944✔
487
        if (result.first) {
1,944✔
488
            successful_emissions++;
1,944✔
489
        } else {
490
            _connections_errors_subscriptions.queue(
×
491
                Mavsdk::ConnectionError{result.second, _connection.handle},
×
492
                [this](const auto& func) { call_user_callback(func); });
×
493
        }
494
    }
1,944✔
495

496
    if (successful_emissions == 0) {
1,944✔
497
        LogErr() << "Sending message failed";
×
498
        return false;
×
499
    }
500

501
    return true;
1,944✔
502
}
1,987✔
503

504
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_any_connection(
86✔
505
    const std::string& connection_url, ForwardingOption forwarding_option)
506
{
507
    CliArg cli_arg;
86✔
508
    if (!cli_arg.parse(connection_url)) {
86✔
509
        return {ConnectionResult::ConnectionUrlInvalid, Mavsdk::ConnectionHandle{}};
×
510
    }
511

512
    return std::visit(
86✔
513
        overloaded{
172✔
514
            [](std::monostate) {
×
515
                // Should not happen anyway.
516
                return std::pair<ConnectionResult, Mavsdk::ConnectionHandle>{
×
517
                    ConnectionResult::ConnectionUrlInvalid, Mavsdk::ConnectionHandle()};
×
518
            },
519
            [this, forwarding_option](const CliArg::Udp& udp) {
84✔
520
                return add_udp_connection(udp, forwarding_option);
84✔
521
            },
522
            [this, forwarding_option](const CliArg::Tcp& tcp) {
2✔
523
                return add_tcp_connection(tcp, forwarding_option);
2✔
524
            },
525
            [this, forwarding_option](const CliArg::Serial& serial) {
×
526
                return add_serial_connection(
×
527
                    serial.path, serial.baudrate, serial.flow_control_enabled, forwarding_option);
×
528
            }},
529
        cli_arg.protocol);
86✔
530
}
86✔
531

532
std::pair<ConnectionResult, Mavsdk::ConnectionHandle>
533
MavsdkImpl::add_udp_connection(const CliArg::Udp& udp, ForwardingOption forwarding_option)
84✔
534
{
535
    auto new_conn = std::make_unique<UdpConnection>(
84✔
536
        [this](mavlink_message_t& message, Connection* connection) {
1,925✔
537
            receive_message(message, connection);
1,925✔
538
        },
1,925✔
539
        udp.mode == CliArg::Udp::Mode::In ? udp.host : "0.0.0.0",
210✔
540
        udp.mode == CliArg::Udp::Mode::In ? udp.port : 0,
168✔
541
        forwarding_option);
252✔
542

543
    if (!new_conn) {
84✔
544
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
545
    }
546

547
    ConnectionResult ret = new_conn->start();
84✔
548

549
    if (ret != ConnectionResult::Success) {
84✔
550
        return {ret, Mavsdk::ConnectionHandle{}};
×
551
    }
552

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

558
        if (!remote_ip) {
42✔
559
            return {ConnectionResult::DestinationIpUnknown, Mavsdk::ConnectionHandle{}};
×
560
        }
561

562
        new_conn->add_remote(remote_ip.value(), udp.port);
42✔
563
        std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
42✔
564

565
        // With a UDP remote, we need to initiate the connection by sending heartbeats.
566
        auto new_configuration = get_configuration();
42✔
567
        new_configuration.set_always_send_heartbeats(true);
42✔
568
        set_configuration(new_configuration);
42✔
569
    }
42✔
570

571
    auto handle = add_connection(std::move(new_conn));
84✔
572

573
    return {ConnectionResult::Success, handle};
84✔
574
}
84✔
575

576
std::pair<ConnectionResult, Mavsdk::ConnectionHandle>
577
MavsdkImpl::add_tcp_connection(const CliArg::Tcp& tcp, ForwardingOption forwarding_option)
2✔
578
{
579
    if (tcp.mode == CliArg::Tcp::Mode::Out) {
2✔
580
        auto new_conn = std::make_unique<TcpClientConnection>(
1✔
581
            [this](mavlink_message_t& message, Connection* connection) {
7✔
582
                receive_message(message, connection);
7✔
583
            },
7✔
584
            tcp.host,
1✔
585
            tcp.port,
1✔
586
            forwarding_option);
1✔
587
        if (!new_conn) {
1✔
588
            return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
589
        }
590
        ConnectionResult ret = new_conn->start();
1✔
591
        if (ret == ConnectionResult::Success) {
1✔
592
            return {ret, add_connection(std::move(new_conn))};
1✔
593
        } else {
594
            return {ret, Mavsdk::ConnectionHandle{}};
×
595
        }
596
    } else {
1✔
597
        auto new_conn = std::make_unique<TcpServerConnection>(
1✔
598
            [this](mavlink_message_t& message, Connection* connection) {
12✔
599
                receive_message(message, connection);
12✔
600
            },
12✔
601
            tcp.host,
1✔
602
            tcp.port,
1✔
603
            forwarding_option);
1✔
604
        if (!new_conn) {
1✔
605
            return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
606
        }
607
        ConnectionResult ret = new_conn->start();
1✔
608
        if (ret == ConnectionResult::Success) {
1✔
609
            return {ret, add_connection(std::move(new_conn))};
1✔
610
        } else {
611
            return {ret, Mavsdk::ConnectionHandle{}};
×
612
        }
613
    }
1✔
614
}
615

616
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_serial_connection(
×
617
    const std::string& dev_path,
618
    int baudrate,
619
    bool flow_control,
620
    ForwardingOption forwarding_option)
621
{
622
    auto new_conn = std::make_unique<SerialConnection>(
×
623
        [this](mavlink_message_t& message, Connection* connection) {
×
624
            receive_message(message, connection);
×
625
        },
×
626
        dev_path,
627
        baudrate,
628
        flow_control,
629
        forwarding_option);
×
630
    if (!new_conn) {
×
631
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
632
    }
633
    ConnectionResult ret = new_conn->start();
×
634
    if (ret == ConnectionResult::Success) {
×
635
        auto handle = add_connection(std::move(new_conn));
×
636

637
        auto new_configuration = get_configuration();
×
638

639
        // PX4 starting with v1.13 does not send heartbeats by default, so we need
640
        // to initiate the MAVLink connection by sending heartbeats.
641
        // Therefore, we override the default here and enable sending heartbeats.
642
        new_configuration.set_always_send_heartbeats(true);
×
643
        set_configuration(new_configuration);
×
644

645
        return {ret, handle};
×
646

647
    } else {
648
        return {ret, Mavsdk::ConnectionHandle{}};
×
649
    }
650
}
×
651

652
Mavsdk::ConnectionHandle MavsdkImpl::add_connection(std::unique_ptr<Connection>&& new_connection)
86✔
653
{
654
    std::lock_guard<std::mutex> lock(_connections_mutex);
86✔
655
    auto handle = _connections_handle_factory.create();
86✔
656
    _connections.emplace_back(ConnectionEntry{std::move(new_connection), handle});
86✔
657

658
    return handle;
172✔
659
}
86✔
660

661
void MavsdkImpl::remove_connection(Mavsdk::ConnectionHandle handle)
×
662
{
663
    std::lock_guard<std::mutex> lock(_connections_mutex);
×
664

665
    _connections.erase(std::remove_if(_connections.begin(), _connections.end(), [&](auto&& entry) {
×
666
        return (entry.handle == handle);
×
667
    }));
668
}
×
669

670
Mavsdk::Configuration MavsdkImpl::get_configuration() const
42✔
671
{
672
    return _configuration;
42✔
673
}
674

675
void MavsdkImpl::set_configuration(Mavsdk::Configuration new_configuration)
129✔
676
{
677
    // We just point the default to the newly created component. This means
678
    // that the previous default component will be deleted if it is not
679
    // used/referenced anywhere.
680
    _default_server_component = server_component_by_id(new_configuration.get_component_id());
129✔
681

682
    if (new_configuration.get_always_send_heartbeats() &&
214✔
683
        !_configuration.get_always_send_heartbeats()) {
85✔
684
        start_sending_heartbeats();
43✔
685
    } else if (
86✔
686
        !new_configuration.get_always_send_heartbeats() &&
130✔
687
        _configuration.get_always_send_heartbeats() && !is_any_system_connected()) {
130✔
688
        stop_sending_heartbeats();
×
689
    }
690

691
    _configuration = new_configuration;
129✔
692
}
129✔
693

694
uint8_t MavsdkImpl::get_own_system_id() const
4,924✔
695
{
696
    return _configuration.get_system_id();
4,924✔
697
}
698

699
uint8_t MavsdkImpl::get_own_component_id() const
1,297✔
700
{
701
    return _configuration.get_component_id();
1,297✔
702
}
703

704
uint8_t MavsdkImpl::channel() const
×
705
{
706
    // TODO
707
    return 0;
×
708
}
709

710
Autopilot MavsdkImpl::autopilot() const
×
711
{
712
    // TODO
713
    return Autopilot::Px4;
×
714
}
715

716
// FIXME: this should be per component
717
uint8_t MavsdkImpl::get_mav_type() const
259✔
718
{
719
    return _configuration.get_mav_type();
259✔
720
}
721

722
void MavsdkImpl::make_system_with_component(uint8_t system_id, uint8_t comp_id)
86✔
723
{
724
    // Needs _systems_lock
725

726
    if (_should_exit) {
86✔
727
        // When the system got destroyed in the destructor, we have to give up.
728
        return;
×
729
    }
730

731
    if (static_cast<int>(system_id) == 0 && static_cast<int>(comp_id) == 0) {
86✔
732
        LogDebug() << "Initializing connection to remote system...";
×
733
    } else {
734
        LogDebug() << "New system ID: " << static_cast<int>(system_id)
258✔
735
                   << " Comp ID: " << static_cast<int>(comp_id);
258✔
736
    }
737

738
    // Make a system with its first component
739
    auto new_system = std::make_shared<System>(*this);
86✔
740
    new_system->init(system_id, comp_id);
86✔
741

742
    _systems.emplace_back(system_id, new_system);
86✔
743
}
86✔
744

745
void MavsdkImpl::notify_on_discover()
86✔
746
{
747
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
86✔
748
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
129✔
749
}
86✔
750

751
void MavsdkImpl::notify_on_timeout()
×
752
{
753
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
×
754
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
755
}
×
756

757
Mavsdk::NewSystemHandle
758
MavsdkImpl::subscribe_on_new_system(const Mavsdk::NewSystemCallback& callback)
43✔
759
{
760
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
43✔
761

762
    const auto handle = _new_system_callbacks.subscribe(callback);
43✔
763

764
    if (is_any_system_connected()) {
43✔
765
        _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
766
    }
767

768
    return handle;
86✔
769
}
43✔
770

771
void MavsdkImpl::unsubscribe_on_new_system(Mavsdk::NewSystemHandle handle)
42✔
772
{
773
    _new_system_callbacks.unsubscribe(handle);
42✔
774
}
42✔
775

776
bool MavsdkImpl::is_any_system_connected() const
43✔
777
{
778
    std::vector<std::shared_ptr<System>> connected_systems = systems();
43✔
779
    return std::any_of(connected_systems.cbegin(), connected_systems.cend(), [](auto& system) {
43✔
780
        return system->is_connected();
×
781
    });
43✔
782
}
43✔
783

784
void MavsdkImpl::work_thread()
87✔
785
{
786
    while (!_should_exit) {
21,565✔
787
        timeout_handler.run_once();
21,356✔
788
        call_every_handler.run_once();
21,492✔
789

790
        {
791
            std::lock_guard<std::mutex> lock(_server_components_mutex);
21,479✔
792
            for (auto& it : _server_components) {
42,927✔
793
                if (it.second != nullptr) {
21,506✔
794
                    it.second->_impl->do_work();
21,336✔
795
                }
796
            }
797
        }
21,312✔
798

799
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
21,370✔
800
    }
801
}
802

803
void MavsdkImpl::call_user_callback_located(
952✔
804
    const std::string& filename, const int linenumber, const std::function<void()>& func)
805
{
806
    auto callback_size = _user_callback_queue.size();
952✔
807
    if (callback_size == 10) {
952✔
808
        LogWarn()
×
809
            << "User callback queue too slow.\n"
×
810
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
811

812
    } else if (callback_size == 99) {
952✔
813
        LogErr()
×
814
            << "User callback queue overflown\n"
×
815
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
816

817
    } else if (callback_size == 100) {
952✔
818
        return;
×
819
    }
820

821
    // We only need to keep track of filename and linenumber if we're actually debugging this.
822
    UserCallback user_callback =
952✔
823
        _callback_debugging ? UserCallback{func, filename, linenumber} : UserCallback{func};
1,904✔
824

825
    _user_callback_queue.enqueue(user_callback);
952✔
826
}
952✔
827

828
void MavsdkImpl::process_user_callbacks_thread()
87✔
829
{
830
    while (!_should_exit) {
1,126✔
831
        auto callback = _user_callback_queue.dequeue();
1,039✔
832
        if (!callback) {
1,039✔
833
            continue;
87✔
834
        }
835

836
        const double timeout_s = 1.0;
952✔
837
        auto cookie = timeout_handler.add(
952✔
838
            [&]() {
×
839
                if (_callback_debugging) {
×
840
                    LogWarn() << "Callback called from " << callback.value().filename << ":"
×
841
                              << callback.value().linenumber << " took more than " << timeout_s
×
842
                              << " second to run.";
×
843
                    fflush(stdout);
×
844
                    fflush(stderr);
×
845
                    abort();
×
846
                } else {
847
                    LogWarn()
×
848
                        << "Callback took more than " << timeout_s << " second to run.\n"
×
849
                        << "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
850
                }
851
            },
×
852
            timeout_s);
952✔
853
        callback.value().func();
952✔
854
        timeout_handler.remove(cookie);
952✔
855
    }
1,039✔
856
}
87✔
857

858
void MavsdkImpl::start_sending_heartbeats()
129✔
859
{
860
    // Before sending out first heartbeats we need to make sure we have a
861
    // default server component.
862
    default_server_component_impl();
129✔
863

864
    call_every_handler.remove(_heartbeat_send_cookie);
129✔
865
    _heartbeat_send_cookie =
258✔
866
        call_every_handler.add([this]() { send_heartbeat(); }, HEARTBEAT_SEND_INTERVAL_S);
387✔
867
}
129✔
868

869
void MavsdkImpl::stop_sending_heartbeats()
×
870
{
871
    if (!_configuration.get_always_send_heartbeats()) {
×
872
        call_every_handler.remove(_heartbeat_send_cookie);
×
873
    }
874
}
×
875

876
ServerComponentImpl& MavsdkImpl::default_server_component_impl()
909✔
877
{
878
    if (_default_server_component == nullptr) {
909✔
879
        _default_server_component = server_component_by_id(_configuration.get_component_id());
×
880
    }
881
    return *_default_server_component->_impl;
909✔
882
}
883

884
void MavsdkImpl::send_heartbeat()
258✔
885
{
886
    std::lock_guard<std::mutex> lock(_server_components_mutex);
258✔
887

888
    for (auto& it : _server_components) {
517✔
889
        if (it.second != nullptr) {
259✔
890
            it.second->_impl->send_heartbeat();
259✔
891
        }
892
    }
893
}
258✔
894

895
void MavsdkImpl::intercept_incoming_messages_async(std::function<bool(mavlink_message_t&)> callback)
22✔
896
{
897
    std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
22✔
898
    _intercept_incoming_messages_callback = callback;
22✔
899
}
22✔
900

901
void MavsdkImpl::intercept_outgoing_messages_async(std::function<bool(mavlink_message_t&)> callback)
14✔
902
{
903
    std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
14✔
904
    _intercept_outgoing_messages_callback = callback;
14✔
905
}
14✔
906

907
Mavsdk::ConnectionErrorHandle
908
MavsdkImpl::subscribe_connection_errors(Mavsdk::ConnectionErrorCallback callback)
×
909
{
910
    std::lock_guard lock(_connections_mutex);
×
911

912
    const auto handle = _connections_errors_subscriptions.subscribe(callback);
×
913

914
    return handle;
×
915
}
×
916

917
void MavsdkImpl::unsubscribe_connection_errors(Mavsdk::ConnectionErrorHandle handle)
×
918
{
919
    std::lock_guard lock(_connections_mutex);
×
920
    _connections_errors_subscriptions.unsubscribe(handle);
×
921
}
×
922

923
uint8_t MavsdkImpl::get_target_system_id(const mavlink_message_t& message)
1,944✔
924
{
925
    // Checks whether connection knows target system ID by extracting target system if set.
926
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
1,944✔
927

928
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_SYSTEM)) {
1,943✔
929
        return 0;
385✔
930
    }
931

932
    // Don't look at the target system offset if it is outside the payload length.
933
    // This can happen if the fields are trimmed.
934
    if (meta->target_system_ofs >= message.len) {
1,558✔
935
        return 0;
13✔
936
    }
937

938
    return (_MAV_PAYLOAD(&message))[meta->target_system_ofs];
1,545✔
939
}
940

941
uint8_t MavsdkImpl::get_target_component_id(const mavlink_message_t& message)
×
942
{
943
    // Checks whether connection knows target system ID by extracting target system if set.
944
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
×
945

946
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_COMPONENT)) {
×
947
        return 0;
×
948
    }
949

950
    // Don't look at the target component offset if it is outside the payload length.
951
    // This can happen if the fields are trimmed.
952
    if (meta->target_component_ofs >= message.len) {
×
953
        return 0;
×
954
    }
955

956
    return (_MAV_PAYLOAD(&message))[meta->target_component_ofs];
×
957
}
958

959
Sender& MavsdkImpl::sender()
×
960
{
961
    return default_server_component_impl().sender();
×
962
}
963

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