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

mavlink / MAVSDK / 12403100458

18 Dec 2024 11:28PM UTC coverage: 44.626% (-0.001%) from 44.627%
12403100458

push

github

web-flow
examples: don't exit too early (#2478)

Otherwise, we'd have to use attach.

14583 of 32678 relevant lines covered (44.63%)

284.87 hits per line

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

59.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
    _work_thread = new std::thread(&MavsdkImpl::work_thread, this);
87✔
56

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

61
MavsdkImpl::~MavsdkImpl()
87✔
62
{
63
    call_every_handler.remove(_heartbeat_send_cookie);
87✔
64

65
    _should_exit = true;
87✔
66

67
    if (_process_user_callbacks_thread != nullptr) {
87✔
68
        _user_callback_queue.stop();
87✔
69
        _process_user_callbacks_thread->join();
87✔
70
        delete _process_user_callbacks_thread;
87✔
71
        _process_user_callbacks_thread = nullptr;
87✔
72
    }
73

74
    if (_work_thread != nullptr) {
87✔
75
        _work_thread->join();
87✔
76
        delete _work_thread;
87✔
77
        _work_thread = nullptr;
87✔
78
    }
79

80
    {
81
        std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
87✔
82
        _systems.clear();
87✔
83
    }
87✔
84

85
    {
86
        std::lock_guard<std::mutex> lock(_connections_mutex);
87✔
87
        _connections.clear();
87✔
88
    }
87✔
89
}
87✔
90

91
std::string MavsdkImpl::version()
1✔
92
{
93
    static unsigned version_counter = 0;
94

95
    ++version_counter;
1✔
96

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

119
std::vector<std::shared_ptr<System>> MavsdkImpl::systems() const
86✔
120
{
121
    std::vector<std::shared_ptr<System>> systems_result{};
86✔
122

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

134
    return systems_result;
86✔
135
}
86✔
136

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

148
    if (timeout_s == 0.0) {
33✔
149
        // Don't wait at all.
150
        return {};
×
151
    }
152

153
    auto prom = std::promise<std::shared_ptr<System>>();
33✔
154

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

163
    auto fut = prom.get_future();
33✔
164

165
    if (timeout_s > 0.0) {
33✔
166
        if (fut.wait_for(std::chrono::milliseconds(int64_t(timeout_s * 1e3))) ==
33✔
167
            std::future_status::ready) {
168
            unsubscribe_on_new_system(handle);
33✔
169
            return fut.get();
33✔
170

171
        } else {
172
            unsubscribe_on_new_system(handle);
×
173
            return std::nullopt;
×
174
        }
175
    } else {
176
        fut.wait();
×
177
        unsubscribe_on_new_system(handle);
×
178
        return std::optional(fut.get());
×
179
    }
180
}
33✔
181

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

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

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

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

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

252
        default:
×
253
            LogErr() << "Unknown server component type";
×
254
            return {};
×
255
    }
256
}
257

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

265
    std::lock_guard<std::mutex> lock(_server_components_mutex);
176✔
266

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

277
    _server_components.emplace_back(std::pair<uint8_t, std::shared_ptr<ServerComponent>>(
176✔
278
        component_id, std::make_shared<ServerComponent>(*this, component_id)));
176✔
279

280
    return _server_components.back().second;
88✔
281
}
176✔
282

283
void MavsdkImpl::forward_message(mavlink_message_t& message, Connection* connection)
×
284
{
285
    // Forward_message Function implementing Mavlink routing rules.
286
    // See https://mavlink.io/en/guide/routing.html
287

288
    bool forward_heartbeats_enabled = true;
×
289
    const uint8_t target_system_id = get_target_system_id(message);
×
290
    const uint8_t target_component_id = get_target_component_id(message);
×
291

292
    // If it's a message only for us, we keep it, otherwise, we forward it.
293
    const bool targeted_only_at_us =
294
        (target_system_id == get_own_system_id() && target_component_id == get_own_component_id());
×
295

296
    // We don't forward heartbeats unless it's specifically enabled.
297
    const bool heartbeat_check_ok =
×
298
        (message.msgid != MAVLINK_MSG_ID_HEARTBEAT || forward_heartbeats_enabled);
×
299

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

324
void MavsdkImpl::receive_message(mavlink_message_t& message, Connection* connection)
1,933✔
325
{
326
    if (_message_logging_on) {
1,933✔
327
        LogDebug() << "Processing message " << message.msgid << " from "
×
328
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid);
×
329
    }
330

331
    if (_should_exit) {
1,933✔
332
        // If we're meant to clean up, let's not try to acquire any more locks but bail.
333
        return;
2✔
334
    }
335

336
    // This is a low level interface where incoming messages can be tampered
337
    // with or even dropped.
338
    {
339
        std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
1,932✔
340
        if (_intercept_incoming_messages_callback != nullptr) {
1,931✔
341
            bool keep = _intercept_incoming_messages_callback(message);
247✔
342
            if (!keep) {
247✔
343
                LogDebug() << "Dropped incoming message: " << int(message.msgid);
41✔
344
                return;
42✔
345
            }
346
        }
347
    }
1,931✔
348

349
    if (_should_exit) {
1,888✔
350
        // If we're meant to clean up, let's not try to acquire any more locks but bail.
351
        return;
×
352
    }
353

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

365
    {
366
        std::lock_guard<std::mutex> lock(_connections_mutex);
1,887✔
367

368
        if (_connections.size() > 1 && mavsdk::Connection::forwarding_connections_count() > 0 &&
1,890✔
369
            (mavsdk::Connection::forwarding_connections_count() > 1 ||
×
370
             !connection->should_forward_messages())) {
×
371
            if (_message_logging_on) {
×
372
                LogDebug() << "Forwarding message " << message.msgid << " from "
×
373
                           << static_cast<int>(message.sysid) << "/"
×
374
                           << static_cast<int>(message.compid);
×
375
            }
376
            forward_message(message, connection);
×
377
        }
378
    }
1,889✔
379

380
    // Don't ever create a system with sysid 0.
381
    if (message.sysid == 0) {
1,890✔
382
        if (_message_logging_on) {
×
383
            LogDebug() << "Ignoring message with sysid == 0";
×
384
        }
385
        return;
×
386
    }
387

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

404
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
1,890✔
405

406
    bool found_system = false;
1,889✔
407
    for (auto& system : _systems) {
1,888✔
408
        if (system.first == message.sysid) {
1,803✔
409
            system.second->system_impl()->add_new_component(message.compid);
1,804✔
410
            found_system = true;
1,803✔
411
            break;
1,803✔
412
        }
413
    }
414

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

429
    if (_should_exit) {
1,888✔
430
        // Don't try to call at() if systems have already been destroyed
431
        // in destructor.
432
        return;
×
433
    }
434

435
    mavlink_message_handler.process_message(message);
1,889✔
436

437
    for (auto& system : _systems) {
1,890✔
438
        if (system.first == message.sysid) {
1,890✔
439
            system.second->system_impl()->process_mavlink_message(message);
1,890✔
440
            break;
1,890✔
441
        }
442
    }
443
}
1,890✔
444

445
bool MavsdkImpl::send_message(mavlink_message_t& message)
2,056✔
446
{
447
    if (_message_logging_on) {
2,056✔
448
        LogDebug() << "Sending message " << message.msgid << " from "
×
449
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid)
×
450
                   << " to " << static_cast<int>(get_target_system_id(message)) << "/"
×
451
                   << static_cast<int>(get_target_component_id(message));
×
452
    }
453

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

467
    std::lock_guard<std::mutex> lock(_connections_mutex);
1,976✔
468

469
    if (_connections.empty()) {
1,976✔
470
        // We obviously can't send any messages without a connection added, so
471
        // we silently ignore this.
472
        return true;
43✔
473
    }
474

475
    uint8_t successful_emissions = 0;
1,933✔
476
    for (auto& _connection : _connections) {
3,867✔
477
        const uint8_t target_system_id = get_target_system_id(message);
1,934✔
478

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

492
    if (successful_emissions == 0) {
1,934✔
493
        LogErr() << "Sending message failed";
×
494
        return false;
×
495
    }
496

497
    return true;
1,934✔
498
}
1,977✔
499

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

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

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

539
    if (!new_conn) {
84✔
540
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
541
    }
542

543
    ConnectionResult ret = new_conn->start();
84✔
544

545
    if (ret != ConnectionResult::Success) {
84✔
546
        return {ret, Mavsdk::ConnectionHandle{}};
×
547
    }
548

549
    auto handle = add_connection(new_conn);
84✔
550

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

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

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

563
        // With a UDP remote, we need to initiate the connection by sending heartbeats.
564
        auto new_configuration = get_configuration();
42✔
565
        new_configuration.set_always_send_heartbeats(true);
42✔
566
        set_configuration(new_configuration);
42✔
567
    }
42✔
568
    return {ConnectionResult::Success, handle};
84✔
569
}
84✔
570

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

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

632
        auto new_configuration = get_configuration();
×
633

634
        // PX4 starting with v1.13 does not send heartbeats by default, so we need
635
        // to initiate the MAVLink connection by sending heartbeats.
636
        // Therefore, we override the default here and enable sending heartbeats.
637
        new_configuration.set_always_send_heartbeats(true);
×
638
        set_configuration(new_configuration);
×
639

640
        return {ret, handle};
×
641

642
    } else {
643
        return {ret, Mavsdk::ConnectionHandle{}};
×
644
    }
645
}
×
646

647
Mavsdk::ConnectionHandle
648
MavsdkImpl::add_connection(const std::shared_ptr<Connection>& new_connection)
86✔
649
{
650
    std::lock_guard<std::mutex> lock(_connections_mutex);
86✔
651
    auto handle = _connections_handle_factory.create();
86✔
652
    _connections.emplace_back(ConnectionEntry{new_connection, handle});
86✔
653

654
    return handle;
172✔
655
}
86✔
656

657
void MavsdkImpl::remove_connection(Mavsdk::ConnectionHandle handle)
×
658
{
659
    std::lock_guard<std::mutex> lock(_connections_mutex);
×
660

661
    _connections.erase(std::remove_if(_connections.begin(), _connections.end(), [&](auto&& entry) {
×
662
        return (entry.handle == handle);
×
663
    }));
664
}
×
665

666
Mavsdk::Configuration MavsdkImpl::get_configuration() const
42✔
667
{
668
    return _configuration;
42✔
669
}
670

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

678
    if (new_configuration.get_always_send_heartbeats() &&
214✔
679
        !_configuration.get_always_send_heartbeats()) {
85✔
680
        start_sending_heartbeats();
43✔
681
    } else if (
86✔
682
        !new_configuration.get_always_send_heartbeats() &&
130✔
683
        _configuration.get_always_send_heartbeats() && !is_any_system_connected()) {
130✔
684
        stop_sending_heartbeats();
×
685
    }
686

687
    _configuration = new_configuration;
129✔
688
}
129✔
689

690
uint8_t MavsdkImpl::get_own_system_id() const
4,906✔
691
{
692
    return _configuration.get_system_id();
4,906✔
693
}
694

695
uint8_t MavsdkImpl::get_own_component_id() const
1,293✔
696
{
697
    return _configuration.get_component_id();
1,293✔
698
}
699

700
uint8_t MavsdkImpl::channel() const
×
701
{
702
    // TODO
703
    return 0;
×
704
}
705

706
Autopilot MavsdkImpl::autopilot() const
×
707
{
708
    // TODO
709
    return Autopilot::Px4;
×
710
}
711

712
// FIXME: this should be per component
713
uint8_t MavsdkImpl::get_mav_type() const
255✔
714
{
715
    switch (_configuration.get_component_type()) {
255✔
716
        case ComponentType::Autopilot:
114✔
717
            return MAV_TYPE_GENERIC;
114✔
718

719
        case ComponentType::GroundStation:
82✔
720
            return MAV_TYPE_GCS;
82✔
721

722
        case ComponentType::CompanionComputer:
4✔
723
            return MAV_TYPE_ONBOARD_CONTROLLER;
4✔
724

725
        case ComponentType::Camera:
55✔
726
            return MAV_TYPE_CAMERA;
55✔
727

728
        case ComponentType::Gimbal:
×
729
            return MAV_TYPE_GIMBAL;
×
730

731
        case ComponentType::RemoteId:
×
732
            return MAV_TYPE_ODID;
×
733

734
        case ComponentType::Custom:
×
735
            return MAV_TYPE_GENERIC;
×
736

737
        default:
738
            LogErr() << "Unknown configuration";
739
            return 0;
×
740
    }
741
}
742

743
void MavsdkImpl::make_system_with_component(uint8_t system_id, uint8_t comp_id)
86✔
744
{
745
    // Needs _systems_lock
746

747
    if (_should_exit) {
86✔
748
        // When the system got destroyed in the destructor, we have to give up.
749
        return;
×
750
    }
751

752
    if (static_cast<int>(system_id) == 0 && static_cast<int>(comp_id) == 0) {
86✔
753
        LogDebug() << "Initializing connection to remote system...";
×
754
    } else {
755
        LogDebug() << "New system ID: " << static_cast<int>(system_id)
258✔
756
                   << " Comp ID: " << static_cast<int>(comp_id);
258✔
757
    }
758

759
    // Make a system with its first component
760
    auto new_system = std::make_shared<System>(*this);
86✔
761
    new_system->init(system_id, comp_id);
86✔
762

763
    _systems.emplace_back(system_id, new_system);
86✔
764
}
86✔
765

766
void MavsdkImpl::notify_on_discover()
86✔
767
{
768
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
86✔
769
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
129✔
770
}
86✔
771

772
void MavsdkImpl::notify_on_timeout()
×
773
{
774
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
×
775
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
776
}
×
777

778
Mavsdk::NewSystemHandle
779
MavsdkImpl::subscribe_on_new_system(const Mavsdk::NewSystemCallback& callback)
43✔
780
{
781
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
43✔
782

783
    const auto handle = _new_system_callbacks.subscribe(callback);
43✔
784

785
    if (is_any_system_connected()) {
43✔
786
        _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
787
    }
788

789
    return handle;
86✔
790
}
43✔
791

792
void MavsdkImpl::unsubscribe_on_new_system(Mavsdk::NewSystemHandle handle)
42✔
793
{
794
    _new_system_callbacks.unsubscribe(handle);
42✔
795
}
42✔
796

797
bool MavsdkImpl::is_any_system_connected() const
43✔
798
{
799
    std::vector<std::shared_ptr<System>> connected_systems = systems();
43✔
800
    return std::any_of(connected_systems.cbegin(), connected_systems.cend(), [](auto& system) {
43✔
801
        return system->is_connected();
1✔
802
    });
43✔
803
}
43✔
804

805
void MavsdkImpl::work_thread()
87✔
806
{
807
    while (!_should_exit) {
20,223✔
808
        timeout_handler.run_once();
19,657✔
809
        call_every_handler.run_once();
20,174✔
810

811
        {
812
            std::lock_guard<std::mutex> lock(_server_components_mutex);
20,162✔
813
            for (auto& it : _server_components) {
40,171✔
814
                if (it.second != nullptr) {
20,056✔
815
                    it.second->_impl->do_work();
19,782✔
816
                }
817
            }
818
        }
19,769✔
819

820
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
19,748✔
821
    }
822
}
823

824
void MavsdkImpl::call_user_callback_located(
939✔
825
    const std::string& filename, const int linenumber, const std::function<void()>& func)
826
{
827
    auto callback_size = _user_callback_queue.size();
939✔
828
    if (callback_size == 10) {
939✔
829
        LogWarn()
4✔
830
            << "User callback queue too slow.\n"
2✔
831
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
2✔
832

833
    } else if (callback_size == 99) {
937✔
834
        LogErr()
×
835
            << "User callback queue overflown\n"
×
836
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
837

838
    } else if (callback_size == 100) {
937✔
839
        return;
×
840
    }
841

842
    // We only need to keep track of filename and linenumber if we're actually debugging this.
843
    UserCallback user_callback =
939✔
844
        _callback_debugging ? UserCallback{func, filename, linenumber} : UserCallback{func};
1,878✔
845

846
    _user_callback_queue.enqueue(user_callback);
939✔
847
}
939✔
848

849
void MavsdkImpl::process_user_callbacks_thread()
87✔
850
{
851
    while (!_should_exit) {
1,113✔
852
        auto callback = _user_callback_queue.dequeue();
1,026✔
853
        if (!callback) {
1,026✔
854
            continue;
87✔
855
        }
856

857
        const double timeout_s = 1.0;
939✔
858
        auto cookie = timeout_handler.add(
939✔
859
            [&]() {
×
860
                if (_callback_debugging) {
×
861
                    LogWarn() << "Callback called from " << callback.value().filename << ":"
×
862
                              << callback.value().linenumber << " took more than " << timeout_s
×
863
                              << " second to run.";
×
864
                    fflush(stdout);
×
865
                    fflush(stderr);
×
866
                    abort();
×
867
                } else {
868
                    LogWarn()
×
869
                        << "Callback took more than " << timeout_s << " second to run.\n"
×
870
                        << "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
871
                }
872
            },
×
873
            timeout_s);
939✔
874
        callback.value().func();
939✔
875
        timeout_handler.remove(cookie);
939✔
876
    }
1,026✔
877
}
87✔
878

879
void MavsdkImpl::start_sending_heartbeats()
129✔
880
{
881
    // Before sending out first heartbeats we need to make sure we have a
882
    // default server component.
883
    default_server_component_impl();
129✔
884

885
    call_every_handler.remove(_heartbeat_send_cookie);
129✔
886
    _heartbeat_send_cookie =
258✔
887
        call_every_handler.add([this]() { send_heartbeat(); }, HEARTBEAT_SEND_INTERVAL_S);
383✔
888
}
129✔
889

890
void MavsdkImpl::stop_sending_heartbeats()
×
891
{
892
    if (!_configuration.get_always_send_heartbeats()) {
×
893
        call_every_handler.remove(_heartbeat_send_cookie);
×
894
    }
895
}
×
896

897
ServerComponentImpl& MavsdkImpl::default_server_component_impl()
904✔
898
{
899
    if (_default_server_component == nullptr) {
904✔
900
        _default_server_component = server_component_by_id(_configuration.get_component_id());
×
901
    }
902
    return *_default_server_component->_impl;
904✔
903
}
904

905
void MavsdkImpl::send_heartbeat()
254✔
906
{
907
    std::lock_guard<std::mutex> lock(_server_components_mutex);
254✔
908

909
    for (auto& it : _server_components) {
509✔
910
        if (it.second != nullptr) {
254✔
911
            it.second->_impl->send_heartbeat();
254✔
912
        }
913
    }
914
}
254✔
915

916
void MavsdkImpl::intercept_incoming_messages_async(std::function<bool(mavlink_message_t&)> callback)
22✔
917
{
918
    std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
22✔
919
    _intercept_incoming_messages_callback = callback;
22✔
920
}
22✔
921

922
void MavsdkImpl::intercept_outgoing_messages_async(std::function<bool(mavlink_message_t&)> callback)
14✔
923
{
924
    std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
14✔
925
    _intercept_outgoing_messages_callback = callback;
14✔
926
}
14✔
927

928
Mavsdk::ConnectionErrorHandle
929
MavsdkImpl::subscribe_connection_errors(Mavsdk::ConnectionErrorCallback callback)
×
930
{
931
    std::lock_guard lock(_connections_mutex);
×
932

933
    const auto handle = _connections_errors_subscriptions.subscribe(callback);
×
934

935
    return handle;
×
936
}
×
937

938
void MavsdkImpl::unsubscribe_connection_errors(Mavsdk::ConnectionErrorHandle handle)
×
939
{
940
    std::lock_guard lock(_connections_mutex);
×
941
    _connections_errors_subscriptions.unsubscribe(handle);
×
942
}
×
943

944
uint8_t MavsdkImpl::get_target_system_id(const mavlink_message_t& message)
1,934✔
945
{
946
    // Checks whether connection knows target system ID by extracting target system if set.
947
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
1,934✔
948

949
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_SYSTEM)) {
1,933✔
950
        return 0;
381✔
951
    }
952

953
    // Don't look at the target system offset if it is outside the payload length.
954
    // This can happen if the fields are trimmed.
955
    if (meta->target_system_ofs >= message.len) {
1,552✔
956
        return 0;
13✔
957
    }
958

959
    return (_MAV_PAYLOAD(&message))[meta->target_system_ofs];
1,539✔
960
}
961

962
uint8_t MavsdkImpl::get_target_component_id(const mavlink_message_t& message)
×
963
{
964
    // Checks whether connection knows target system ID by extracting target system if set.
965
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
×
966

967
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_COMPONENT)) {
×
968
        return 0;
×
969
    }
970

971
    // Don't look at the target component offset if it is outside the payload length.
972
    // This can happen if the fields are trimmed.
973
    if (meta->target_component_ofs >= message.len) {
×
974
        return 0;
×
975
    }
976

977
    return (_MAV_PAYLOAD(&message))[meta->target_component_ofs];
×
978
}
979

980
Sender& MavsdkImpl::sender()
×
981
{
982
    return default_server_component_impl().sender();
×
983
}
984

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