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

mavlink / MAVSDK / 11044081504

26 Sep 2024 01:46AM UTC coverage: 37.368%. First build
11044081504

push

github

web-flow
Merge pull request #2407 from mavlink/pr-v2.12-system-debugging

[v2.12 BACKPORT] Add some system debugging

2 of 14 new or added lines in 2 files covered. (14.29%)

11105 of 29718 relevant lines covered (37.37%)

255.14 hits per line

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

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

3
#include <algorithm>
4
#include <mutex>
5

6
#include "connection.h"
7
#include "tcp_connection.h"
8
#include "udp_connection.h"
9
#include "system.h"
10
#include "system_impl.h"
11
#include "serial_connection.h"
12
#include "cli_arg.h"
13
#include "version.h"
14
#include "server_component_impl.h"
15
#include "mavlink_channels.h"
16
#include "callback_list.tpp"
17

18
namespace mavsdk {
19

20
template class CallbackList<>;
21

22
MavsdkImpl::MavsdkImpl(const Mavsdk::Configuration& configuration) :
71✔
23
    timeout_handler(time),
71✔
24
    call_every_handler(time)
71✔
25
{
26
    LogInfo() << "MAVSDK version: " << mavsdk_version;
71✔
27

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

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

42
    if (const char* env_p = std::getenv("MAVSDK_SYSTEM_DEBUGGING")) {
71✔
NEW
43
        if (std::string(env_p) == "1") {
×
NEW
44
            LogDebug() << "System debugging is on.";
×
NEW
45
            _system_debugging = true;
×
46
        }
47
    }
48

49
    set_configuration(configuration);
71✔
50

51
    _work_thread = new std::thread(&MavsdkImpl::work_thread, this);
71✔
52

53
    _process_user_callbacks_thread =
142✔
54
        new std::thread(&MavsdkImpl::process_user_callbacks_thread, this);
71✔
55
}
71✔
56

57
MavsdkImpl::~MavsdkImpl()
71✔
58
{
59
    call_every_handler.remove(_heartbeat_send_cookie);
71✔
60

61
    _should_exit = true;
71✔
62

63
    if (_process_user_callbacks_thread != nullptr) {
71✔
64
        _user_callback_queue.stop();
71✔
65
        _process_user_callbacks_thread->join();
71✔
66
        delete _process_user_callbacks_thread;
71✔
67
        _process_user_callbacks_thread = nullptr;
71✔
68
    }
69

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

76
    {
77
        std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
142✔
78
        _systems.clear();
71✔
79
    }
80

81
    {
82
        std::lock_guard<std::mutex> lock(_connections_mutex);
142✔
83
        _connections.clear();
71✔
84
    }
85
}
71✔
86

87
std::string MavsdkImpl::version()
1✔
88
{
89
    static unsigned version_counter = 0;
90

91
    ++version_counter;
1✔
92

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

115
std::vector<std::shared_ptr<System>> MavsdkImpl::systems() const
70✔
116
{
117
    std::vector<std::shared_ptr<System>> systems_result{};
70✔
118

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

130
    return systems_result;
70✔
131
}
132

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

144
    if (timeout_s == 0.0) {
33✔
145
        // Don't wait at all.
146
        return {};
×
147
    }
148

149
    auto prom = std::promise<std::shared_ptr<System>>();
66✔
150

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

159
    auto fut = prom.get_future();
66✔
160

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

167
        } else {
168
            unsubscribe_on_new_system(handle);
×
169
            return std::nullopt;
×
170
        }
171
    } else {
172
        fut.wait();
×
173
        unsubscribe_on_new_system(handle);
×
174
        return std::optional(fut.get());
×
175
    }
176
}
177

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

194
std::shared_ptr<ServerComponent>
195
MavsdkImpl::server_component_by_type(Mavsdk::ComponentType server_component_type, unsigned instance)
35✔
196
{
197
    switch (server_component_type) {
35✔
198
        case Mavsdk::ComponentType::Autopilot:
33✔
199
            if (instance == 0) {
33✔
200
                return server_component_by_id(MAV_COMP_ID_AUTOPILOT1);
33✔
201
            } else {
202
                LogErr() << "Only autopilot instance 0 is valid";
×
203
                return {};
×
204
            }
205

206
        case Mavsdk::ComponentType::GroundStation:
×
207
            if (instance == 0) {
×
208
                return server_component_by_id(MAV_COMP_ID_MISSIONPLANNER);
×
209
            } else {
210
                LogErr() << "Only one ground station supported at this time";
×
211
                return {};
×
212
            }
213

214
        case Mavsdk::ComponentType::CompanionComputer:
1✔
215
            if (instance == 0) {
1✔
216
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER);
1✔
217
            } else if (instance == 1) {
×
218
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER2);
×
219
            } else if (instance == 2) {
×
220
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER3);
×
221
            } else if (instance == 3) {
×
222
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER4);
×
223
            } else {
224
                LogErr() << "Only companion computer 0..3 are supported";
×
225
                return {};
×
226
            }
227

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

246
        default:
×
247
            LogErr() << "Unknown server component type";
×
248
            return {};
×
249
    }
250
}
251

252
std::shared_ptr<ServerComponent> MavsdkImpl::server_component_by_id(uint8_t component_id)
141✔
253
{
254
    if (component_id == 0) {
141✔
255
        LogErr() << "Server component with component ID 0 not allowed";
×
256
        return nullptr;
×
257
    }
258

259
    std::lock_guard<std::mutex> lock(_server_components_mutex);
282✔
260

261
    for (auto& it : _server_components) {
142✔
262
        if (it.first == component_id) {
70✔
263
            if (it.second != nullptr) {
69✔
264
                return it.second;
69✔
265
            } else {
266
                it.second = std::make_shared<ServerComponent>(*this, component_id);
×
267
            }
268
        }
269
    }
270

271
    _server_components.emplace_back(std::pair<uint8_t, std::shared_ptr<ServerComponent>>(
216✔
272
        component_id, std::make_shared<ServerComponent>(*this, component_id)));
288✔
273

274
    return _server_components.back().second;
72✔
275
}
276

277
void MavsdkImpl::forward_message(mavlink_message_t& message, Connection* connection)
×
278
{
279
    // Forward_message Function implementing Mavlink routing rules.
280
    // See https://mavlink.io/en/guide/routing.html
281

282
    bool forward_heartbeats_enabled = true;
×
283
    const uint8_t target_system_id = get_target_system_id(message);
×
284
    const uint8_t target_component_id = get_target_component_id(message);
×
285

286
    // If it's a message only for us, we keep it, otherwise, we forward it.
287
    const bool targeted_only_at_us =
288
        (target_system_id == get_own_system_id() && target_component_id == get_own_component_id());
×
289

290
    // We don't forward heartbeats unless it's specifically enabled.
291
    const bool heartbeat_check_ok =
×
292
        (message.msgid != MAVLINK_MSG_ID_HEARTBEAT || forward_heartbeats_enabled);
×
293

294
    if (!targeted_only_at_us && heartbeat_check_ok) {
×
295
        std::lock_guard<std::mutex> lock(_connections_mutex);
×
296

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

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

322
    // This is a low level interface where incoming messages can be tampered
323
    // with or even dropped.
324
    {
325
        std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
1,551✔
326
        if (_intercept_incoming_messages_callback != nullptr) {
1,551✔
327
            bool keep = _intercept_incoming_messages_callback(message);
244✔
328
            if (!keep) {
244✔
329
                LogDebug() << "Dropped incoming message: " << int(message.msgid);
41✔
330
                return;
42✔
331
            }
332
        }
333
    }
334

335
    /** @note: Forward message if option is enabled and multiple interfaces are connected.
336
     *  Performs message forwarding checks for every messages if message forwarding
337
     *  is enabled on at least one connection, and in case of a single forwarding connection,
338
     *  we check that it is not the one which received the current message.
339
     *
340
     * Conditions:
341
     * 1. At least 2 connections.
342
     * 2. At least 1 forwarding connection.
343
     * 3. At least 2 forwarding connections or current connection is not forwarding.
344
     */
345
    if (_connections.size() > 1 && mavsdk::Connection::forwarding_connections_count() > 0 &&
1,509✔
346
        (mavsdk::Connection::forwarding_connections_count() > 1 ||
×
347
         !connection->should_forward_messages())) {
×
348
        if (_message_logging_on) {
×
349
            LogDebug() << "Forwarding message " << message.msgid << " from "
×
350
                       << static_cast<int>(message.sysid) << "/"
×
351
                       << static_cast<int>(message.compid);
×
352
        }
353
        forward_message(message, connection);
×
354
    }
355

356
    // Don't ever create a system with sysid 0.
357
    if (message.sysid == 0) {
1,509✔
358
        if (_message_logging_on) {
×
359
            LogDebug() << "Ignoring message with sysid == 0";
×
360
        }
361
        return;
×
362
    }
363

364
    // Filter out messages by QGroundControl, however, only do that if MAVSDK
365
    // is also implementing a ground station and not if it is used in another
366
    // configuration, e.g. on a companion.
367
    //
368
    // This is a workaround because PX4 started forwarding messages between
369
    // mavlink instances which leads to existing implementations (including
370
    // examples and integration tests) to connect to QGroundControl by accident
371
    // instead of PX4 because the check `has_autopilot()` is not used.
372
    if (_configuration.get_component_type() == Mavsdk::ComponentType::GroundStation &&
1,509✔
373
        message.sysid == 255 && message.compid == MAV_COMP_ID_MISSIONPLANNER) {
1,509✔
374
        if (_message_logging_on) {
×
375
            LogDebug() << "Ignoring messages from QGC as we are also a ground station";
×
376
        }
377
        return;
×
378
    }
379

380
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
1,509✔
381

382
    bool found_system = false;
1,509✔
383
    for (auto& system : _systems) {
1,508✔
384
        if (system.first == message.sysid) {
1,438✔
385
            system.second->system_impl()->add_new_component(message.compid);
1,439✔
386
            found_system = true;
1,439✔
387
            break;
1,439✔
388
        }
389
    }
390

391
    if (!found_system && message.compid == MAV_COMP_ID_TELEMETRY_RADIO) {
1,509✔
392
        if (_message_logging_on) {
×
393
            LogDebug() << "Don't create new system just for telemetry radio";
×
394
        }
395
        return;
×
396
    }
397

398
    if (!found_system) {
1,509✔
399
        if (_system_debugging) {
70✔
NEW
400
            LogWarn() << "Create new system/component " << (int)message.sysid << "/"
×
NEW
401
                      << (int)message.compid;
×
NEW
402
            LogWarn() << "From message " << (int)message.msgid << " with len " << (int)message.len;
×
NEW
403
            std::string bytes = "";
×
NEW
404
            for (unsigned i = 0; i < 12 + message.len; ++i) {
×
NEW
405
                bytes += std::to_string(reinterpret_cast<uint8_t*>(&message)[i]) + ' ';
×
406
            }
NEW
407
            LogWarn() << "Bytes: " << bytes;
×
408
        }
409
        make_system_with_component(message.sysid, message.compid);
70✔
410
    }
411

412
    if (_should_exit) {
1,509✔
413
        // Don't try to call at() if systems have already been destroyed
414
        // in destructor.
415
        return;
1✔
416
    }
417

418
    mavlink_message_handler.process_message(message);
1,508✔
419

420
    for (auto& system : _systems) {
1,508✔
421
        if (system.first == message.sysid) {
1,508✔
422
            system.second->system_impl()->process_mavlink_message(message);
1,508✔
423
            break;
1,508✔
424
        }
425
    }
426
}
427

428
bool MavsdkImpl::send_message(mavlink_message_t& message)
1,663✔
429
{
430
    if (_message_logging_on) {
1,663✔
431
        LogDebug() << "Sending message " << message.msgid << " from "
×
432
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid)
×
433
                   << " to " << static_cast<int>(get_target_system_id(message)) << "/"
×
434
                   << static_cast<int>(get_target_component_id(message));
×
435
    }
436

437
    // This is a low level interface where outgoing messages can be tampered
438
    // with or even dropped.
439
    if (_intercept_outgoing_messages_callback != nullptr) {
1,663✔
440
        const bool keep = _intercept_outgoing_messages_callback(message);
218✔
441
        if (!keep) {
218✔
442
            // We fake that everything was sent as instructed because
443
            // a potential loss would happen later, and we would not be informed
444
            // about it.
445
            LogDebug() << "Dropped outgoing message: " << int(message.msgid);
77✔
446
            return true;
77✔
447
        }
448
    }
449

450
    std::lock_guard<std::mutex> lock(_connections_mutex);
3,172✔
451

452
    if (_connections.empty()) {
1,586✔
453
        // We obviously can't send any messages without a connection added, so
454
        // we silently ignore this.
455
        return true;
35✔
456
    }
457

458
    uint8_t successful_emissions = 0;
1,551✔
459
    for (auto& _connection : _connections) {
3,102✔
460
        const uint8_t target_system_id = get_target_system_id(message);
1,551✔
461

462
        if (target_system_id != 0 && !(*_connection.connection).has_system_id(target_system_id)) {
1,551✔
463
            continue;
×
464
        }
465

466
        if ((*_connection.connection).send_message(message)) {
1,551✔
467
            successful_emissions++;
1,551✔
468
        }
469
    }
470

471
    if (successful_emissions == 0) {
1,551✔
472
        LogErr() << "Sending message failed";
×
473
        return false;
×
474
    }
475

476
    return true;
1,551✔
477
}
478

479
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_any_connection(
70✔
480
    const std::string& connection_url, ForwardingOption forwarding_option)
481
{
482
    CliArg cli_arg;
140✔
483
    if (!cli_arg.parse(connection_url)) {
70✔
484
        return {ConnectionResult::ConnectionUrlInvalid, Mavsdk::ConnectionHandle{}};
×
485
    }
486

487
    switch (cli_arg.get_protocol()) {
70✔
488
        case CliArg::Protocol::Udp: {
70✔
489
            int port = cli_arg.get_port() ? cli_arg.get_port() : Mavsdk::DEFAULT_UDP_PORT;
70✔
490

491
            if (cli_arg.get_path().empty() || cli_arg.get_path() == Mavsdk::DEFAULT_UDP_BIND_IP) {
70✔
492
                std::string path = Mavsdk::DEFAULT_UDP_BIND_IP;
105✔
493
                return add_udp_connection(path, port, forwarding_option);
35✔
494
            } else {
495
                std::string path = cli_arg.get_path();
70✔
496
                return setup_udp_remote(path, port, forwarding_option);
35✔
497
            }
498
        }
499

500
        case CliArg::Protocol::Tcp: {
×
501
            std::string path = Mavsdk::DEFAULT_TCP_REMOTE_IP;
×
502
            int port = Mavsdk::DEFAULT_TCP_REMOTE_PORT;
×
503
            if (!cli_arg.get_path().empty()) {
×
504
                path = cli_arg.get_path();
×
505
            }
506
            if (cli_arg.get_port()) {
×
507
                port = cli_arg.get_port();
×
508
            }
509
            return add_tcp_connection(path, port, forwarding_option);
×
510
        }
511

512
        case CliArg::Protocol::Serial: {
×
513
            int baudrate = Mavsdk::DEFAULT_SERIAL_BAUDRATE;
×
514
            if (cli_arg.get_baudrate()) {
×
515
                baudrate = cli_arg.get_baudrate();
×
516
            }
517
            bool flow_control = cli_arg.get_flow_control();
×
518
            return add_serial_connection(
519
                cli_arg.get_path(), baudrate, flow_control, forwarding_option);
×
520
        }
521

522
        default:
×
523
            return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
524
    }
525
}
526

527
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_udp_connection(
35✔
528
    const std::string& local_ip, const int local_port, ForwardingOption forwarding_option)
529
{
530
    auto new_conn = std::make_shared<UdpConnection>(
35✔
531
        [this](mavlink_message_t& message, Connection* connection) {
961✔
532
            receive_message(message, connection);
961✔
533
        },
961✔
534
        local_ip,
535
        local_port,
536
        forwarding_option);
70✔
537
    if (!new_conn) {
35✔
538
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
539
    }
540
    ConnectionResult ret = new_conn->start();
35✔
541
    if (ret == ConnectionResult::Success) {
35✔
542
        return {ret, add_connection(new_conn)};
35✔
543
    } else {
544
        return {ret, Mavsdk::ConnectionHandle{}};
×
545
    }
546
}
547

548
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::setup_udp_remote(
35✔
549
    const std::string& remote_ip, int remote_port, ForwardingOption forwarding_option)
550
{
551
    auto new_conn = std::make_shared<UdpConnection>(
35✔
552
        [this](mavlink_message_t& message, Connection* connection) {
590✔
553
            receive_message(message, connection);
590✔
554
        },
590✔
555
        "0.0.0.0",
556
        0,
35✔
557
        forwarding_option);
70✔
558
    if (!new_conn) {
35✔
559
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
560
    }
561
    ConnectionResult ret = new_conn->start();
35✔
562
    if (ret == ConnectionResult::Success) {
35✔
563
        new_conn->add_remote(remote_ip, remote_port);
35✔
564
        auto handle = add_connection(new_conn);
35✔
565
        std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
70✔
566

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

572
        return {ret, handle};
35✔
573
    } else {
574
        return {ret, Mavsdk::ConnectionHandle{}};
×
575
    }
576
}
577

578
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_tcp_connection(
×
579
    const std::string& remote_ip, int remote_port, ForwardingOption forwarding_option)
580
{
581
    auto new_conn = std::make_shared<TcpConnection>(
×
582
        [this](mavlink_message_t& message, Connection* connection) {
×
583
            receive_message(message, connection);
×
584
        },
×
585
        remote_ip,
586
        remote_port,
587
        forwarding_option);
×
588
    if (!new_conn) {
×
589
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
590
    }
591
    ConnectionResult ret = new_conn->start();
×
592
    if (ret == ConnectionResult::Success) {
×
593
        return {ret, add_connection(new_conn)};
×
594
    } else {
595
        return {ret, Mavsdk::ConnectionHandle{}};
×
596
    }
597
}
598

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

620
        auto new_configuration = get_configuration();
×
621

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

628
        return {ret, handle};
×
629

630
    } else {
631
        return {ret, Mavsdk::ConnectionHandle{}};
×
632
    }
633
}
634

635
Mavsdk::ConnectionHandle
636
MavsdkImpl::add_connection(const std::shared_ptr<Connection>& new_connection)
70✔
637
{
638
    std::lock_guard<std::mutex> lock(_connections_mutex);
70✔
639
    auto handle = Mavsdk::ConnectionHandle{_connections_handle_id++};
70✔
640
    _connections.emplace_back(ConnectionEntry{new_connection, handle});
70✔
641

642
    return handle;
70✔
643
}
644

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

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

654
Mavsdk::Configuration MavsdkImpl::get_configuration() const
35✔
655
{
656
    return _configuration;
35✔
657
}
658

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

666
    if (new_configuration.get_always_send_heartbeats() &&
176✔
667
        !_configuration.get_always_send_heartbeats()) {
70✔
668
        start_sending_heartbeats();
35✔
669
    } else if (
71✔
670
        !new_configuration.get_always_send_heartbeats() &&
107✔
671
        _configuration.get_always_send_heartbeats() && !is_any_system_connected()) {
107✔
672
        stop_sending_heartbeats();
×
673
    }
674

675
    _configuration = new_configuration;
106✔
676
}
106✔
677

678
uint8_t MavsdkImpl::get_own_system_id() const
4,259✔
679
{
680
    return _configuration.get_system_id();
4,259✔
681
}
682

683
uint8_t MavsdkImpl::get_own_component_id() const
1,210✔
684
{
685
    return _configuration.get_component_id();
1,210✔
686
}
687

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

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

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

707
        case Mavsdk::ComponentType::GroundStation:
43✔
708
            return MAV_TYPE_GCS;
43✔
709

710
        case Mavsdk::ComponentType::CompanionComputer:
4✔
711
            return MAV_TYPE_ONBOARD_CONTROLLER;
4✔
712

713
        case Mavsdk::ComponentType::Camera:
2✔
714
            return MAV_TYPE_CAMERA;
2✔
715

716
        case Mavsdk::ComponentType::Custom:
×
717
            return MAV_TYPE_GENERIC;
×
718

719
        default:
×
720
            LogErr() << "Unknown configuration";
×
721
            return 0;
×
722
    }
723
}
724

725
void MavsdkImpl::make_system_with_component(uint8_t system_id, uint8_t comp_id)
70✔
726
{
727
    // Needs _systems_lock
728

729
    if (_should_exit) {
70✔
730
        // When the system got destroyed in the destructor, we have to give up.
731
        return;
1✔
732
    }
733

734
    if (static_cast<int>(system_id) == 0 && static_cast<int>(comp_id) == 0) {
69✔
735
        LogDebug() << "Initializing connection to remote system...";
×
736
    } else {
737
        LogDebug() << "New system ID: " << static_cast<int>(system_id)
207✔
738
                   << " Comp ID: " << static_cast<int>(comp_id);
207✔
739
    }
740

741
    // Make a system with its first component
742
    auto new_system = std::make_shared<System>(*this);
138✔
743
    new_system->init(system_id, comp_id);
69✔
744

745
    _systems.emplace_back(system_id, new_system);
69✔
746
}
747

748
void MavsdkImpl::notify_on_discover()
69✔
749
{
750
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
138✔
751
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
104✔
752
}
69✔
753

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

760
Mavsdk::NewSystemHandle
761
MavsdkImpl::subscribe_on_new_system(const Mavsdk::NewSystemCallback& callback)
35✔
762
{
763
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
35✔
764

765
    const auto handle = _new_system_callbacks.subscribe(callback);
35✔
766

767
    if (is_any_system_connected()) {
35✔
768
        _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
769
    }
770

771
    return handle;
35✔
772
}
773

774
void MavsdkImpl::unsubscribe_on_new_system(Mavsdk::NewSystemHandle handle)
34✔
775
{
776
    _new_system_callbacks.unsubscribe(handle);
34✔
777
}
34✔
778

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

787
void MavsdkImpl::work_thread()
11,707✔
788
{
789
    while (!_should_exit) {
11,707✔
790
        timeout_handler.run_once();
11,475✔
791
        call_every_handler.run_once();
11,647✔
792

793
        {
794
            std::lock_guard<std::mutex> lock(_server_components_mutex);
23,218✔
795
            for (auto& it : _server_components) {
23,303✔
796
                if (it.second != nullptr) {
11,711✔
797
                    it.second->_impl->do_work();
11,677✔
798
                }
799
            }
800
        }
801

802
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
11,626✔
803
    }
804
}
71✔
805

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

815
    } else if (callback_size == 99) {
1,458✔
816
        LogErr()
×
817
            << "User callback queue overflown\n"
×
818
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
819

820
    } else if (callback_size == 100) {
1,458✔
821
        return;
×
822
    }
823

824
    // We only need to keep track of filename and linenumber if we're actually debugging this.
825
    UserCallback user_callback =
1,474✔
826
        _callback_debugging ? UserCallback{func, filename, linenumber} : UserCallback{func};
4,421✔
827

828
    _user_callback_queue.enqueue(user_callback);
1,473✔
829
}
830

831
void MavsdkImpl::process_user_callbacks_thread()
1,616✔
832
{
833
    while (!_should_exit) {
1,616✔
834
        auto callback = _user_callback_queue.dequeue();
1,545✔
835
        if (!callback) {
1,545✔
836
            continue;
71✔
837
        }
838

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

861
void MavsdkImpl::start_sending_heartbeats()
104✔
862
{
863
    // Before sending out first heartbeats we need to make sure we have a
864
    // default server component.
865
    default_server_component_impl();
104✔
866

867
    call_every_handler.remove(_heartbeat_send_cookie);
104✔
868
    _heartbeat_send_cookie =
208✔
869
        call_every_handler.add([this]() { send_heartbeat(); }, HEARTBEAT_SEND_INTERVAL_S);
265✔
870
}
104✔
871

872
void MavsdkImpl::stop_sending_heartbeats()
×
873
{
874
    if (!_configuration.get_always_send_heartbeats()) {
×
875
        call_every_handler.remove(_heartbeat_send_cookie);
×
876
    }
877
}
×
878

879
ServerComponentImpl& MavsdkImpl::default_server_component_impl()
753✔
880
{
881
    if (_default_server_component == nullptr) {
753✔
882
        _default_server_component = server_component_by_id(_configuration.get_component_id());
×
883
    }
884
    return *_default_server_component->_impl;
753✔
885
}
886

887
void MavsdkImpl::send_heartbeat()
161✔
888
{
889
    std::lock_guard<std::mutex> lock(_server_components_mutex);
322✔
890

891
    for (auto& it : _server_components) {
323✔
892
        if (it.second != nullptr) {
162✔
893
            it.second->_impl->send_heartbeat();
162✔
894
        }
895
    }
896
}
161✔
897

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

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

910
uint8_t MavsdkImpl::get_target_system_id(const mavlink_message_t& message)
1,551✔
911
{
912
    // Checks whether connection knows target system ID by extracting target system if set.
913
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
1,551✔
914

915
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_SYSTEM)) {
1,551✔
916
        return 0;
222✔
917
    }
918

919
    // Don't look at the target system offset if it is outside the payload length.
920
    // This can happen if the fields are trimmed.
921
    if (meta->target_system_ofs >= message.len) {
1,329✔
922
        return 0;
×
923
    }
924

925
    return (_MAV_PAYLOAD(&message))[meta->target_system_ofs];
1,329✔
926
}
927

928
uint8_t MavsdkImpl::get_target_component_id(const mavlink_message_t& message)
×
929
{
930
    // Checks whether connection knows target system ID by extracting target system if set.
931
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
×
932

933
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_COMPONENT)) {
×
934
        return 0;
×
935
    }
936

937
    // Don't look at the target component offset if it is outside the payload length.
938
    // This can happen if the fields are trimmed.
939
    if (meta->target_component_ofs >= message.len) {
×
940
        return 0;
×
941
    }
942

943
    return (_MAV_PAYLOAD(&message))[meta->target_component_ofs];
×
944
}
945

946
Sender& MavsdkImpl::sender()
×
947
{
948
    return default_server_component_impl().sender();
×
949
}
950

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

© 2025 Coveralls, Inc