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

mavlink / MAVSDK / 12113610048

02 Dec 2024 06:03AM UTC coverage: 43.572% (+4.9%) from 38.69%
12113610048

Pull #2386

github

web-flow
Merge e92382a9b into 68856f27a
Pull Request #2386: camera: support multiple cameras within one instance

1368 of 2039 new or added lines in 47 files covered. (67.09%)

50 existing lines in 8 files now uncovered.

13872 of 31837 relevant lines covered (43.57%)

289.73 hits per line

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

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

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

7
#include "connection.h"
8
#include "tcp_client_connection.h"
9
#include "tcp_server_connection.h"
10
#include "udp_connection.h"
11
#include "system.h"
12
#include "system_impl.h"
13
#include "serial_connection.h"
14
#include "version.h"
15
#include "server_component_impl.h"
16
#include "plugin_base.h"
17
#include "mavlink_channels.h"
18
#include "callback_list.tpp"
19
#include "hostname_to_ip.h"
20

21
namespace mavsdk {
22

23
template class CallbackList<>;
24

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

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

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

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

52
    set_configuration(configuration);
87✔
53

54
    _work_thread = new std::thread(&MavsdkImpl::work_thread, this);
87✔
55

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

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

64
    _should_exit = true;
87✔
65

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

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

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

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

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

94
    ++version_counter;
1✔
95

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

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

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

133
    return systems_result;
86✔
134
}
86✔
135

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

299
    if (!targeted_only_at_us && heartbeat_check_ok) {
×
300
        unsigned successful_emissions = 0;
×
301
        for (auto& entry : _connections) {
×
302
            // Check whether the connection is not the one from which we received the message.
303
            // And also check if the connection was set to forward messages.
304
            if (entry.connection.get() == connection ||
×
305
                !entry.connection->should_forward_messages()) {
×
306
                continue;
×
307
            }
308
            if ((*entry.connection).send_message(message)) {
×
309
                successful_emissions++;
×
310
            }
311
        }
312
        if (successful_emissions == 0) {
×
313
            LogErr() << "Message forwarding failed";
×
314
        }
315
    }
316
}
×
317

318
void MavsdkImpl::receive_message(mavlink_message_t& message, Connection* connection)
1,928✔
319
{
320
    if (_message_logging_on) {
1,928✔
321
        LogDebug() << "Processing message " << message.msgid << " from "
×
322
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid);
×
323
    }
324

325
    if (_should_exit) {
1,928✔
326
        // If we're meant to clean up, let's not try to acquire any more locks but bail.
327
        return;
2✔
328
    }
329

330
    // This is a low level interface where incoming messages can be tampered
331
    // with or even dropped.
332
    {
333
        std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
1,927✔
334
        if (_intercept_incoming_messages_callback != nullptr) {
1,927✔
335
            bool keep = _intercept_incoming_messages_callback(message);
244✔
336
            if (!keep) {
244✔
337
                LogDebug() << "Dropped incoming message: " << int(message.msgid);
40✔
338
                return;
40✔
339
            }
340
        }
341
    }
1,927✔
342

343
    if (_should_exit) {
1,887✔
344
        // If we're meant to clean up, let's not try to acquire any more locks but bail.
345
        return;
×
346
    }
347

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

359
    {
360
        std::lock_guard<std::mutex> lock(_connections_mutex);
1,887✔
361

362
        if (_connections.size() > 1 && mavsdk::Connection::forwarding_connections_count() > 0 &&
1,887✔
363
            (mavsdk::Connection::forwarding_connections_count() > 1 ||
×
364
             !connection->should_forward_messages())) {
×
365
            if (_message_logging_on) {
×
366
                LogDebug() << "Forwarding message " << message.msgid << " from "
×
367
                           << static_cast<int>(message.sysid) << "/"
×
368
                           << static_cast<int>(message.compid);
×
369
            }
370
            forward_message(message, connection);
×
371
        }
372
    }
1,887✔
373

374
    // Don't ever create a system with sysid 0.
375
    if (message.sysid == 0) {
1,887✔
376
        if (_message_logging_on) {
×
377
            LogDebug() << "Ignoring message with sysid == 0";
×
378
        }
379
        return;
×
380
    }
381

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

398
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
1,887✔
399

400
    bool found_system = false;
1,887✔
401
    for (auto& system : _systems) {
1,887✔
402
        if (system.first == message.sysid) {
1,801✔
403
            system.second->system_impl()->add_new_component(message.compid);
1,801✔
404
            found_system = true;
1,801✔
405
            break;
1,801✔
406
        }
407
    }
408

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

423
    if (_should_exit) {
1,887✔
424
        // Don't try to call at() if systems have already been destroyed
425
        // in destructor.
426
        return;
×
427
    }
428

429
    mavlink_message_handler.process_message(message);
1,887✔
430

431
    for (auto& system : _systems) {
1,887✔
432
        if (system.first == message.sysid) {
1,887✔
433
            system.second->system_impl()->process_mavlink_message(message);
1,887✔
434
            break;
1,887✔
435
        }
436
    }
437
}
1,887✔
438

439
bool MavsdkImpl::send_message(mavlink_message_t& message)
2,053✔
440
{
441
    if (_message_logging_on) {
2,053✔
442
        LogDebug() << "Sending message " << message.msgid << " from "
×
443
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid)
×
444
                   << " to " << static_cast<int>(get_target_system_id(message)) << "/"
×
445
                   << static_cast<int>(get_target_component_id(message));
×
446
    }
447

448
    // This is a low level interface where outgoing messages can be tampered
449
    // with or even dropped.
450
    if (_intercept_outgoing_messages_callback != nullptr) {
2,053✔
451
        const bool keep = _intercept_outgoing_messages_callback(message);
220✔
452
        if (!keep) {
220✔
453
            // We fake that everything was sent as instructed because
454
            // a potential loss would happen later, and we would not be informed
455
            // about it.
456
            LogDebug() << "Dropped outgoing message: " << int(message.msgid);
81✔
457
            return true;
81✔
458
        }
459
    }
460

461
    std::lock_guard<std::mutex> lock(_connections_mutex);
1,972✔
462

463
    if (_connections.empty()) {
1,972✔
464
        // We obviously can't send any messages without a connection added, so
465
        // we silently ignore this.
466
        return true;
43✔
467
    }
468

469
    uint8_t successful_emissions = 0;
1,929✔
470
    for (auto& _connection : _connections) {
3,858✔
471
        const uint8_t target_system_id = get_target_system_id(message);
1,929✔
472

473
        if (target_system_id != 0 && !(*_connection.connection).has_system_id(target_system_id)) {
1,929✔
474
            continue;
×
475
        }
476

477
        if ((*_connection.connection).send_message(message)) {
1,929✔
478
            successful_emissions++;
1,929✔
479
        }
480
    }
481

482
    if (successful_emissions == 0) {
1,929✔
483
        LogErr() << "Sending message failed";
×
484
        return false;
×
485
    }
486

487
    return true;
1,929✔
488
}
1,972✔
489

490
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_any_connection(
86✔
491
    const std::string& connection_url, ForwardingOption forwarding_option)
492
{
493
    CliArg cli_arg;
86✔
494
    if (!cli_arg.parse(connection_url)) {
86✔
495
        return {ConnectionResult::ConnectionUrlInvalid, Mavsdk::ConnectionHandle{}};
×
496
    }
497

498
    return std::visit(
86✔
499
        overloaded{
172✔
500
            [](std::monostate) {
×
501
                // Should not happen anyway.
502
                return std::pair<ConnectionResult, Mavsdk::ConnectionHandle>{
×
503
                    ConnectionResult::ConnectionUrlInvalid, Mavsdk::ConnectionHandle()};
×
504
            },
505
            [this, forwarding_option](const CliArg::Udp& udp) {
84✔
506
                return add_udp_connection(udp, forwarding_option);
84✔
507
            },
508
            [this, forwarding_option](const CliArg::Tcp& tcp) {
2✔
509
                return add_tcp_connection(tcp, forwarding_option);
2✔
510
            },
511
            [this, forwarding_option](const CliArg::Serial& serial) {
×
512
                return add_serial_connection(
×
513
                    serial.path, serial.baudrate, serial.flow_control_enabled, forwarding_option);
×
514
            }},
515
        cli_arg.protocol);
86✔
516
}
86✔
517

518
std::pair<ConnectionResult, Mavsdk::ConnectionHandle>
519
MavsdkImpl::add_udp_connection(const CliArg::Udp& udp, ForwardingOption forwarding_option)
84✔
520
{
521
    auto new_conn = std::make_shared<UdpConnection>(
84✔
522
        [this](mavlink_message_t& message, Connection* connection) {
1,919✔
523
            receive_message(message, connection);
1,919✔
524
        },
1,919✔
525
        udp.mode == CliArg::Udp::Mode::In ? udp.host : "0.0.0.0",
210✔
526
        udp.mode == CliArg::Udp::Mode::In ? udp.port : 0,
168✔
527
        forwarding_option);
252✔
528

529
    if (!new_conn) {
84✔
530
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
531
    }
532

533
    ConnectionResult ret = new_conn->start();
84✔
534

535
    if (ret != ConnectionResult::Success) {
84✔
536
        return {ret, Mavsdk::ConnectionHandle{}};
×
537
    }
538

539
    auto handle = add_connection(new_conn);
84✔
540

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

546
        if (!remote_ip) {
42✔
547
            return {ConnectionResult::DestinationIpUnknown, Mavsdk::ConnectionHandle{}};
×
548
        }
549

550
        new_conn->add_remote(remote_ip.value(), udp.port);
42✔
551
        std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
42✔
552

553
        // With a UDP remote, we need to initiate the connection by sending heartbeats.
554
        auto new_configuration = get_configuration();
42✔
555
        new_configuration.set_always_send_heartbeats(true);
42✔
556
        set_configuration(new_configuration);
42✔
557
    }
42✔
558
    return {ConnectionResult::Success, handle};
84✔
559
}
84✔
560

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

601
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_serial_connection(
×
602
    const std::string& dev_path,
603
    int baudrate,
604
    bool flow_control,
605
    ForwardingOption forwarding_option)
606
{
607
    auto new_conn = std::make_shared<SerialConnection>(
×
608
        [this](mavlink_message_t& message, Connection* connection) {
×
609
            receive_message(message, connection);
×
610
        },
×
611
        dev_path,
612
        baudrate,
613
        flow_control,
614
        forwarding_option);
×
615
    if (!new_conn) {
×
616
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
617
    }
618
    ConnectionResult ret = new_conn->start();
×
619
    if (ret == ConnectionResult::Success) {
×
620
        auto handle = add_connection(new_conn);
×
621

622
        auto new_configuration = get_configuration();
×
623

624
        // PX4 starting with v1.13 does not send heartbeats by default, so we need
625
        // to initiate the MAVLink connection by sending heartbeats.
626
        // Therefore, we override the default here and enable sending heartbeats.
627
        new_configuration.set_always_send_heartbeats(true);
×
628
        set_configuration(new_configuration);
×
629

630
        return {ret, handle};
×
631

632
    } else {
633
        return {ret, Mavsdk::ConnectionHandle{}};
×
634
    }
635
}
×
636

637
Mavsdk::ConnectionHandle
638
MavsdkImpl::add_connection(const std::shared_ptr<Connection>& new_connection)
86✔
639
{
640
    std::lock_guard<std::mutex> lock(_connections_mutex);
86✔
641
    auto handle = _connections_handle_factory.create();
86✔
642
    _connections.emplace_back(ConnectionEntry{new_connection, handle});
86✔
643

644
    return handle;
172✔
645
}
86✔
646

647
void MavsdkImpl::remove_connection(Mavsdk::ConnectionHandle handle)
×
648
{
649
    std::lock_guard<std::mutex> lock(_connections_mutex);
×
650

651
    _connections.erase(std::remove_if(_connections.begin(), _connections.end(), [&](auto&& entry) {
×
652
        return (entry.handle == handle);
×
653
    }));
654
}
×
655

656
Mavsdk::Configuration MavsdkImpl::get_configuration() const
42✔
657
{
658
    return _configuration;
42✔
659
}
660

661
void MavsdkImpl::set_configuration(Mavsdk::Configuration new_configuration)
129✔
662
{
663
    // We just point the default to the newly created component. This means
664
    // that the previous default component will be deleted if it is not
665
    // used/referenced anywhere.
666
    _default_server_component = server_component_by_id(new_configuration.get_component_id());
129✔
667

668
    if (new_configuration.get_always_send_heartbeats() &&
214✔
669
        !_configuration.get_always_send_heartbeats()) {
85✔
670
        start_sending_heartbeats();
43✔
671
    } else if (
86✔
672
        !new_configuration.get_always_send_heartbeats() &&
130✔
673
        _configuration.get_always_send_heartbeats() && !is_any_system_connected()) {
130✔
674
        stop_sending_heartbeats();
×
675
    }
676

677
    _configuration = new_configuration;
129✔
678
}
129✔
679

680
uint8_t MavsdkImpl::get_own_system_id() const
4,899✔
681
{
682
    return _configuration.get_system_id();
4,899✔
683
}
684

685
uint8_t MavsdkImpl::get_own_component_id() const
1,292✔
686
{
687
    return _configuration.get_component_id();
1,292✔
688
}
689

690
uint8_t MavsdkImpl::channel() const
×
691
{
692
    // TODO
693
    return 0;
×
694
}
695

696
Autopilot MavsdkImpl::autopilot() const
×
697
{
698
    // TODO
699
    return Autopilot::Px4;
×
700
}
701

702
// FIXME: this should be per component
703
uint8_t MavsdkImpl::get_mav_type() const
254✔
704
{
705
    switch (_configuration.get_component_type()) {
254✔
706
        case ComponentType::Autopilot:
113✔
707
            return MAV_TYPE_GENERIC;
113✔
708

709
        case ComponentType::GroundStation:
81✔
710
            return MAV_TYPE_GCS;
81✔
711

712
        case ComponentType::CompanionComputer:
4✔
713
            return MAV_TYPE_ONBOARD_CONTROLLER;
4✔
714

715
        case ComponentType::Camera:
56✔
716
            return MAV_TYPE_CAMERA;
56✔
717

718
        case ComponentType::Gimbal:
×
719
            return MAV_TYPE_GIMBAL;
×
720

721
        case ComponentType::RemoteId:
×
722
            return MAV_TYPE_ODID;
×
723

724
        case ComponentType::Custom:
×
725
            return MAV_TYPE_GENERIC;
×
726

727
        default:
×
728
            LogErr() << "Unknown configuration";
×
729
            return 0;
×
730
    }
731
}
732

733
void MavsdkImpl::make_system_with_component(uint8_t system_id, uint8_t comp_id)
86✔
734
{
735
    // Needs _systems_lock
736

737
    if (_should_exit) {
86✔
738
        // When the system got destroyed in the destructor, we have to give up.
739
        return;
×
740
    }
741

742
    if (static_cast<int>(system_id) == 0 && static_cast<int>(comp_id) == 0) {
86✔
743
        LogDebug() << "Initializing connection to remote system...";
×
744
    } else {
745
        LogDebug() << "New system ID: " << static_cast<int>(system_id)
258✔
746
                   << " Comp ID: " << static_cast<int>(comp_id);
258✔
747
    }
748

749
    // Make a system with its first component
750
    auto new_system = std::make_shared<System>(*this);
86✔
751
    new_system->init(system_id, comp_id);
86✔
752

753
    _systems.emplace_back(system_id, new_system);
86✔
754
}
86✔
755

756
void MavsdkImpl::notify_on_discover()
86✔
757
{
758
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
86✔
759
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
129✔
760
}
86✔
761

762
void MavsdkImpl::notify_on_timeout()
×
763
{
764
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
×
765
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
766
}
×
767

768
Mavsdk::NewSystemHandle
769
MavsdkImpl::subscribe_on_new_system(const Mavsdk::NewSystemCallback& callback)
43✔
770
{
771
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
43✔
772

773
    const auto handle = _new_system_callbacks.subscribe(callback);
43✔
774

775
    if (is_any_system_connected()) {
43✔
776
        _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
777
    }
778

779
    return handle;
86✔
780
}
43✔
781

782
void MavsdkImpl::unsubscribe_on_new_system(Mavsdk::NewSystemHandle handle)
42✔
783
{
784
    _new_system_callbacks.unsubscribe(handle);
42✔
785
}
42✔
786

787
bool MavsdkImpl::is_any_system_connected() const
43✔
788
{
789
    std::vector<std::shared_ptr<System>> connected_systems = systems();
43✔
790
    return std::any_of(connected_systems.cbegin(), connected_systems.cend(), [](auto& system) {
43✔
791
        return system->is_connected();
×
792
    });
43✔
793
}
43✔
794

795
void MavsdkImpl::work_thread()
87✔
796
{
797
    while (!_should_exit) {
20,026✔
798
        timeout_handler.run_once();
19,888✔
799
        call_every_handler.run_once();
20,113✔
800

801
        {
802
            std::lock_guard<std::mutex> lock(_server_components_mutex);
20,114✔
803
            for (auto& it : _server_components) {
39,996✔
804
                if (it.second != nullptr) {
20,115✔
805
                    it.second->_impl->do_work();
19,942✔
806
                }
807
            }
808
        }
19,502✔
809

810
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
19,616✔
811
    }
812
}
813

814
void MavsdkImpl::call_user_callback_located(
940✔
815
    const std::string& filename, const int linenumber, const std::function<void()>& func)
816
{
817
    auto callback_size = _user_callback_queue.size();
940✔
818
    if (callback_size == 10) {
940✔
UNCOV
819
        LogWarn()
×
UNCOV
820
            << "User callback queue too slow.\n"
×
UNCOV
821
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
822

823
    } else if (callback_size == 99) {
940✔
824
        LogErr()
×
825
            << "User callback queue overflown\n"
×
826
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
827

828
    } else if (callback_size == 100) {
940✔
829
        return;
×
830
    }
831

832
    // We only need to keep track of filename and linenumber if we're actually debugging this.
833
    UserCallback user_callback =
940✔
834
        _callback_debugging ? UserCallback{func, filename, linenumber} : UserCallback{func};
1,880✔
835

836
    _user_callback_queue.enqueue(user_callback);
940✔
837
}
940✔
838

839
void MavsdkImpl::process_user_callbacks_thread()
87✔
840
{
841
    while (!_should_exit) {
1,114✔
842
        auto callback = _user_callback_queue.dequeue();
1,027✔
843
        if (!callback) {
1,027✔
844
            continue;
87✔
845
        }
846

847
        const double timeout_s = 1.0;
940✔
848
        auto cookie = timeout_handler.add(
940✔
849
            [&]() {
×
850
                if (_callback_debugging) {
×
851
                    LogWarn() << "Callback called from " << callback.value().filename << ":"
×
852
                              << callback.value().linenumber << " took more than " << timeout_s
×
853
                              << " second to run.";
×
854
                    fflush(stdout);
×
855
                    fflush(stderr);
×
856
                    abort();
×
857
                } else {
858
                    LogWarn()
×
859
                        << "Callback took more than " << timeout_s << " second to run.\n"
×
860
                        << "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
861
                }
862
            },
×
863
            timeout_s);
940✔
864
        callback.value().func();
940✔
865
        timeout_handler.remove(cookie);
940✔
866
    }
1,027✔
867
}
87✔
868

869
void MavsdkImpl::start_sending_heartbeats()
129✔
870
{
871
    // Before sending out first heartbeats we need to make sure we have a
872
    // default server component.
873
    default_server_component_impl();
129✔
874

875
    call_every_handler.remove(_heartbeat_send_cookie);
129✔
876
    _heartbeat_send_cookie =
258✔
877
        call_every_handler.add([this]() { send_heartbeat(); }, HEARTBEAT_SEND_INTERVAL_S);
382✔
878
}
129✔
879

880
void MavsdkImpl::stop_sending_heartbeats()
×
881
{
882
    if (!_configuration.get_always_send_heartbeats()) {
×
883
        call_every_handler.remove(_heartbeat_send_cookie);
×
884
    }
885
}
×
886

887
ServerComponentImpl& MavsdkImpl::default_server_component_impl()
904✔
888
{
889
    if (_default_server_component == nullptr) {
904✔
890
        _default_server_component = server_component_by_id(_configuration.get_component_id());
×
891
    }
892
    return *_default_server_component->_impl;
904✔
893
}
894

895
void MavsdkImpl::send_heartbeat()
253✔
896
{
897
    std::lock_guard<std::mutex> lock(_server_components_mutex);
253✔
898

899
    for (auto& it : _server_components) {
507✔
900
        if (it.second != nullptr) {
254✔
901
            it.second->_impl->send_heartbeat();
254✔
902
        }
903
    }
904
}
253✔
905

906
void MavsdkImpl::intercept_incoming_messages_async(std::function<bool(mavlink_message_t&)> callback)
22✔
907
{
908
    std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
22✔
909
    _intercept_incoming_messages_callback = callback;
22✔
910
}
22✔
911

912
void MavsdkImpl::intercept_outgoing_messages_async(std::function<bool(mavlink_message_t&)> callback)
14✔
913
{
914
    std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
14✔
915
    _intercept_outgoing_messages_callback = callback;
14✔
916
}
14✔
917

918
uint8_t MavsdkImpl::get_target_system_id(const mavlink_message_t& message)
1,929✔
919
{
920
    // Checks whether connection knows target system ID by extracting target system if set.
921
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
1,929✔
922

923
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_SYSTEM)) {
1,929✔
924
        return 0;
381✔
925
    }
926

927
    // Don't look at the target system offset if it is outside the payload length.
928
    // This can happen if the fields are trimmed.
929
    if (meta->target_system_ofs >= message.len) {
1,548✔
930
        return 0;
13✔
931
    }
932

933
    return (_MAV_PAYLOAD(&message))[meta->target_system_ofs];
1,535✔
934
}
935

936
uint8_t MavsdkImpl::get_target_component_id(const mavlink_message_t& message)
×
937
{
938
    // Checks whether connection knows target system ID by extracting target system if set.
939
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
×
940

941
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_COMPONENT)) {
×
942
        return 0;
×
943
    }
944

945
    // Don't look at the target component offset if it is outside the payload length.
946
    // This can happen if the fields are trimmed.
947
    if (meta->target_component_ofs >= message.len) {
×
948
        return 0;
×
949
    }
950

951
    return (_MAV_PAYLOAD(&message))[meta->target_component_ofs];
×
952
}
953

954
Sender& MavsdkImpl::sender()
×
955
{
956
    return default_server_component_impl().sender();
×
957
}
958

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