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

mavlink / MAVSDK / 7238188264

17 Dec 2023 11:27AM UTC coverage: 37.138% (+0.05%) from 37.088%
7238188264

push

github

web-flow
Merge pull request #2195 from mavlink/pr-remove-cached-camera-defs

camera: remove cached camera definition files

0 of 1 new or added line in 1 file covered. (0.0%)

2 existing lines in 1 file now uncovered.

10003 of 26935 relevant lines covered (37.14%)

113.49 hits per line

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

55.44
/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() : timeout_handler(time), call_every_handler(time)
69✔
23
{
24
    LogInfo() << "MAVSDK version: " << mavsdk_version;
69✔
25

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

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

40
    _work_thread = new std::thread(&MavsdkImpl::work_thread, this);
69✔
41

42
    _process_user_callbacks_thread =
138✔
43
        new std::thread(&MavsdkImpl::process_user_callbacks_thread, this);
69✔
44
}
69✔
45

46
MavsdkImpl::~MavsdkImpl()
69✔
47
{
48
    call_every_handler.remove(_heartbeat_send_cookie);
69✔
49

50
    _should_exit = true;
69✔
51

52
    if (_process_user_callbacks_thread != nullptr) {
69✔
53
        _user_callback_queue.stop();
69✔
54
        _process_user_callbacks_thread->join();
69✔
55
        delete _process_user_callbacks_thread;
69✔
56
        _process_user_callbacks_thread = nullptr;
69✔
57
    }
58

59
    if (_work_thread != nullptr) {
69✔
60
        _work_thread->join();
69✔
61
        delete _work_thread;
69✔
62
        _work_thread = nullptr;
69✔
63
    }
64

65
    {
66
        std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
138✔
67
        _systems.clear();
69✔
68
    }
69

70
    {
71
        std::lock_guard<std::mutex> lock(_connections_mutex);
138✔
72
        _connections.clear();
69✔
73
    }
74
}
69✔
75

76
std::string MavsdkImpl::version()
1✔
77
{
78
    static unsigned version_counter = 0;
79

80
    ++version_counter;
1✔
81

82
    switch (version_counter) {
1✔
83
        case 10:
×
84
            return "You were wondering about the name of this library?";
×
85
        case 11:
×
86
            return "Let's look at the history:";
×
87
        case 12:
×
88
            return "DroneLink";
×
89
        case 13:
×
90
            return "DroneCore";
×
91
        case 14:
×
92
            return "DronecodeSDK";
×
93
        case 15:
×
94
            return "MAVSDK";
×
95
        case 16:
×
96
            return "And that's it...";
×
97
        case 17:
×
98
            return "At least for now ¯\\_(ツ)_/¯.";
×
99
        default:
1✔
100
            return mavsdk_version;
1✔
101
    }
102
}
103

104
std::vector<std::shared_ptr<System>> MavsdkImpl::systems() const
68✔
105
{
106
    std::vector<std::shared_ptr<System>> systems_result{};
68✔
107

108
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
136✔
109
    for (auto& system : _systems) {
102✔
110
        // We ignore the 0 entry because it's just a null system.
111
        // It's only created because the older, deprecated API needs a
112
        // reference.
113
        if (system.first == 0) {
34✔
114
            continue;
×
115
        }
116
        systems_result.push_back(system.second);
34✔
117
    }
118

119
    return systems_result;
68✔
120
}
121

122
std::optional<std::shared_ptr<System>> MavsdkImpl::first_autopilot(double timeout_s)
33✔
123
{
124
    {
125
        std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
33✔
126
        for (auto system : _systems) {
33✔
127
            if (system.second->is_connected() && system.second->has_autopilot()) {
×
128
                return system.second;
×
129
            }
130
        }
131
    }
132

133
    if (timeout_s == 0.0) {
33✔
134
        // Don't wait at all.
135
        return {};
×
136
    }
137

138
    auto prom = std::promise<std::shared_ptr<System>>();
66✔
139

140
    std::once_flag flag;
33✔
141
    auto handle = subscribe_on_new_system([this, &prom, &flag]() {
33✔
142
        const auto system = systems().at(0);
99✔
143
        if (system->is_connected() && system->has_autopilot()) {
33✔
144
            std::call_once(flag, [&prom, &system]() { prom.set_value(system); });
66✔
145
        }
146
    });
33✔
147

148
    auto fut = prom.get_future();
66✔
149

150
    if (timeout_s > 0.0) {
33✔
151
        if (fut.wait_for(std::chrono::milliseconds(int64_t(timeout_s * 1e3))) ==
33✔
152
            std::future_status::ready) {
153
            unsubscribe_on_new_system(handle);
33✔
154
            return fut.get();
33✔
155

156
        } else {
157
            unsubscribe_on_new_system(handle);
×
158
            return std::nullopt;
×
159
        }
160
    } else {
161
        fut.wait();
×
162
        unsubscribe_on_new_system(handle);
×
163
        return std::optional(fut.get());
×
164
    }
165
}
166

167
std::shared_ptr<ServerComponent> MavsdkImpl::server_component_by_type(
34✔
168
    Mavsdk::ServerComponentType server_component_type, unsigned instance)
169
{
170
    switch (server_component_type) {
34✔
171
        case Mavsdk::ServerComponentType::Autopilot:
33✔
172
            if (instance == 0) {
33✔
173
                return server_component_by_id(MAV_COMP_ID_AUTOPILOT1);
33✔
174
            } else {
175
                LogErr() << "Only autopilot instance 0 is valid";
×
176
                return {};
×
177
            }
178

179
        case Mavsdk::ServerComponentType::GroundStation:
×
180
            if (instance == 0) {
×
181
                return server_component_by_id(MAV_COMP_ID_MISSIONPLANNER);
×
182
            } else {
183
                LogErr() << "Only one ground station supported at this time";
×
184
                return {};
×
185
            }
186

187
        case Mavsdk::ServerComponentType::CompanionComputer:
×
188
            if (instance == 0) {
×
189
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER);
×
190
            } else if (instance == 1) {
×
191
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER2);
×
192
            } else if (instance == 2) {
×
193
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER3);
×
194
            } else if (instance == 3) {
×
195
                return server_component_by_id(MAV_COMP_ID_ONBOARD_COMPUTER4);
×
196
            } else {
197
                LogErr() << "Only companion computer 0..3 are supported";
×
198
                return {};
×
199
            }
200

201
        case Mavsdk::ServerComponentType::Camera:
1✔
202
            if (instance == 0) {
1✔
203
                return server_component_by_id(MAV_COMP_ID_CAMERA);
1✔
204
            } else if (instance == 1) {
×
205
                return server_component_by_id(MAV_COMP_ID_CAMERA2);
×
206
            } else if (instance == 2) {
×
207
                return server_component_by_id(MAV_COMP_ID_CAMERA3);
×
208
            } else if (instance == 3) {
×
209
                return server_component_by_id(MAV_COMP_ID_CAMERA4);
×
210
            } else if (instance == 4) {
×
211
                return server_component_by_id(MAV_COMP_ID_CAMERA5);
×
212
            } else if (instance == 5) {
×
213
                return server_component_by_id(MAV_COMP_ID_CAMERA6);
×
214
            } else {
215
                LogErr() << "Only camera 0..5 are supported";
×
216
                return {};
×
217
            }
218

219
        default:
×
220
            LogErr() << "Unknown server component type";
×
221
            return {};
×
222
    }
223
}
224

225
std::shared_ptr<ServerComponent> MavsdkImpl::server_component_by_id(uint8_t component_id)
136✔
226
{
227
    if (component_id == 0) {
136✔
228
        LogErr() << "Server component with component ID 0 not allowed";
×
229
        return nullptr;
×
230
    }
231

232
    std::lock_guard<std::mutex> lock(_server_components_mutex);
272✔
233

234
    for (auto& it : _server_components) {
136✔
235
        if (it.first == component_id) {
68✔
236
            if (it.second != nullptr) {
68✔
237
                return it.second;
68✔
238
            } else {
239
                it.second = std::make_shared<ServerComponent>(*this, component_id);
×
240
            }
241
        }
242
    }
243

244
    _server_components.emplace_back(std::pair<uint8_t, std::shared_ptr<ServerComponent>>(
204✔
245
        component_id, std::make_shared<ServerComponent>(*this, component_id)));
272✔
246

247
    return _server_components.back().second;
68✔
248
}
249

250
void MavsdkImpl::forward_message(mavlink_message_t& message, Connection* connection)
×
251
{
252
    // Forward_message Function implementing Mavlink routing rules.
253
    // See https://mavlink.io/en/guide/routing.html
254

255
    bool forward_heartbeats_enabled = true;
×
256
    const uint8_t target_system_id = get_target_system_id(message);
×
257
    const uint8_t target_component_id = get_target_component_id(message);
×
258

259
    // If it's a message only for us, we keep it, otherwise, we forward it.
260
    const bool targeted_only_at_us =
261
        (target_system_id == get_own_system_id() && target_component_id == get_own_component_id());
×
262

263
    // We don't forward heartbeats unless it's specifically enabled.
264
    const bool heartbeat_check_ok =
×
265
        (message.msgid != MAVLINK_MSG_ID_HEARTBEAT || forward_heartbeats_enabled);
×
266

267
    if (!targeted_only_at_us && heartbeat_check_ok) {
×
268
        std::lock_guard<std::mutex> lock(_connections_mutex);
×
269

270
        unsigned successful_emissions = 0;
×
271
        for (auto& entry : _connections) {
×
272
            // Check whether the connection is not the one from which we received the message.
273
            // And also check if the connection was set to forward messages.
274
            if (entry.connection.get() == connection ||
×
275
                !entry.connection->should_forward_messages()) {
×
276
                continue;
×
277
            }
278
            if ((*entry.connection).send_message(message)) {
×
279
                successful_emissions++;
×
280
            }
281
        }
282
        if (successful_emissions == 0) {
×
283
            LogErr() << "Message forwarding failed";
×
284
        }
285
    }
286
}
×
287

288
void MavsdkImpl::receive_message(mavlink_message_t& message, Connection* connection)
1,446✔
289
{
290
    if (_message_logging_on) {
1,446✔
291
        LogDebug() << "Processing message " << message.msgid << " from "
×
292
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid);
×
293
    }
294

295
    // This is a low level interface where incoming messages can be tampered
296
    // with or even dropped.
297
    {
298
        std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
1,446✔
299
        if (_intercept_incoming_messages_callback != nullptr) {
1,446✔
300
            bool keep = _intercept_incoming_messages_callback(message);
241✔
301
            if (!keep) {
241✔
302
                LogDebug() << "Dropped incoming message: " << int(message.msgid);
40✔
303
                return;
39✔
304
            }
305
        }
306
    }
307

308
    /** @note: Forward message if option is enabled and multiple interfaces are connected.
309
     *  Performs message forwarding checks for every messages if message forwarding
310
     *  is enabled on at least one connection, and in case of a single forwarding connection,
311
     *  we check that it is not the one which received the current message.
312
     *
313
     * Conditions:
314
     * 1. At least 2 connections.
315
     * 2. At least 1 forwarding connection.
316
     * 3. At least 2 forwarding connections or current connection is not forwarding.
317
     */
318
    if (_connections.size() > 1 && mavsdk::Connection::forwarding_connections_count() > 0 &&
1,407✔
319
        (mavsdk::Connection::forwarding_connections_count() > 1 ||
×
320
         !connection->should_forward_messages())) {
×
321
        if (_message_logging_on) {
×
322
            LogDebug() << "Forwarding message " << message.msgid << " from "
×
323
                       << static_cast<int>(message.sysid) << "/"
×
324
                       << static_cast<int>(message.compid);
×
325
        }
326
        forward_message(message, connection);
×
327
    }
328

329
    // Don't ever create a system with sysid 0.
330
    if (message.sysid == 0) {
1,407✔
331
        if (_message_logging_on) {
×
332
            LogDebug() << "Ignoring message with sysid == 0";
×
333
        }
334
        return;
×
335
    }
336

337
    // Filter out messages by QGroundControl, however, only do that if MAVSDK
338
    // is also implementing a ground station and not if it is used in another
339
    // configuration, e.g. on a companion.
340
    //
341
    // This is a workaround because PX4 started forwarding messages between
342
    // mavlink instances which leads to existing implementations (including
343
    // examples and integration tests) to connect to QGroundControl by accident
344
    // instead of PX4 because the check `has_autopilot()` is not used.
345
    if (_configuration.get_usage_type() == Mavsdk::Configuration::UsageType::GroundStation &&
1,407✔
346
        message.sysid == 255 && message.compid == MAV_COMP_ID_MISSIONPLANNER) {
1,406✔
347
        if (_message_logging_on) {
×
348
            LogDebug() << "Ignoring messages from QGC as we are also a ground station";
×
349
        }
350
        return;
×
351
    }
352

353
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
1,406✔
354

355
    // The only situation where we create a system with sysid 0 is when we initialize the connection
356
    // to the remote.
357
    if (_systems.size() == 1 && _systems[0].first == 0) {
1,407✔
358
        LogDebug() << "New: System ID: " << static_cast<int>(message.sysid)
102✔
359
                   << " Comp ID: " << static_cast<int>(message.compid);
102✔
360
        _systems[0].first = message.sysid;
34✔
361
        _systems[0].second->system_impl()->set_system_id(message.sysid);
34✔
362

363
        // Even though the fake system was already discovered, we can now
364
        // send a notification, now that it seems to really actually exist.
365
        notify_on_discover();
34✔
366
    }
367

368
    bool found_system = false;
1,407✔
369
    for (auto& system : _systems) {
1,407✔
370
        if (system.first == message.sysid) {
1,373✔
371
            system.second->system_impl()->add_new_component(message.compid);
1,373✔
372
            found_system = true;
1,372✔
373
            break;
1,372✔
374
        }
375
    }
376

377
    if (!found_system && message.compid == MAV_COMP_ID_TELEMETRY_RADIO) {
1,406✔
378
        if (_message_logging_on) {
×
379
            LogDebug() << "Don't create new system just for telemetry radio";
×
380
        }
381
        return;
×
382
    }
383

384
    if (!found_system) {
1,406✔
385
        make_system_with_component(message.sysid, message.compid);
34✔
386
    }
387

388
    if (_should_exit) {
1,406✔
389
        // Don't try to call at() if systems have already been destroyed
390
        // in destructor.
391
        return;
2✔
392
    }
393

394
    mavlink_message_handler.process_message(message);
1,404✔
395

396
    for (auto& system : _systems) {
1,405✔
397
        if (system.first == message.sysid) {
1,405✔
398
            system.second->system_impl()->process_mavlink_message(message);
1,405✔
399
            break;
1,405✔
400
        }
401
    }
402
}
403

404
bool MavsdkImpl::send_message(mavlink_message_t& message)
1,527✔
405
{
406
    if (_message_logging_on) {
1,527✔
407
        LogDebug() << "Sending message " << message.msgid << " from "
×
408
                   << static_cast<int>(message.sysid) << "/" << static_cast<int>(message.compid)
×
409
                   << " to " << static_cast<int>(get_target_system_id(message)) << "/"
×
410
                   << static_cast<int>(get_target_component_id(message));
×
411
    }
412

413
    // This is a low level interface where outgoing messages can be tampered
414
    // with or even dropped.
415
    if (_intercept_outgoing_messages_callback != nullptr) {
1,527✔
416
        const bool keep = _intercept_outgoing_messages_callback(message);
219✔
417
        if (!keep) {
219✔
418
            // We fake that everything was sent as instructed because
419
            // a potential loss would happen later, and we would not be informed
420
            // about it.
421
            LogDebug() << "Dropped outgoing message: " << int(message.msgid);
80✔
422
            return true;
80✔
423
        }
424
    }
425

426
    std::lock_guard<std::mutex> lock(_connections_mutex);
2,894✔
427

428
    if (_connections.empty()) {
1,447✔
429
        // We obviously can't send any messages without a connection added, so
430
        // we silently ignore this.
431
        return true;
×
432
    }
433

434
    uint8_t successful_emissions = 0;
1,447✔
435
    for (auto& _connection : _connections) {
2,894✔
436
        const uint8_t target_system_id = get_target_system_id(message);
1,446✔
437

438
        if (target_system_id != 0 && !(*_connection.connection).has_system_id(target_system_id)) {
1,447✔
439
            continue;
×
440
        }
441

442
        if ((*_connection.connection).send_message(message)) {
1,447✔
443
            successful_emissions++;
1,447✔
444
        }
445
    }
446

447
    if (successful_emissions == 0) {
1,447✔
448
        LogErr() << "Sending message failed";
×
449
        return false;
×
450
    }
451

452
    return true;
1,447✔
453
}
454

455
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_any_connection(
68✔
456
    const std::string& connection_url, ForwardingOption forwarding_option)
457
{
458
    CliArg cli_arg;
136✔
459
    if (!cli_arg.parse(connection_url)) {
68✔
460
        return {ConnectionResult::ConnectionUrlInvalid, Mavsdk::ConnectionHandle{}};
×
461
    }
462

463
    switch (cli_arg.get_protocol()) {
68✔
464
        case CliArg::Protocol::Udp: {
68✔
465
            int port = cli_arg.get_port() ? cli_arg.get_port() : Mavsdk::DEFAULT_UDP_PORT;
68✔
466

467
            if (cli_arg.get_path().empty() || cli_arg.get_path() == Mavsdk::DEFAULT_UDP_BIND_IP) {
68✔
468
                std::string path = Mavsdk::DEFAULT_UDP_BIND_IP;
102✔
469
                return add_udp_connection(path, port, forwarding_option);
34✔
470
            } else {
471
                std::string path = cli_arg.get_path();
68✔
472
                return setup_udp_remote(path, port, forwarding_option);
34✔
473
            }
474
        }
475

476
        case CliArg::Protocol::Tcp: {
×
477
            std::string path = Mavsdk::DEFAULT_TCP_REMOTE_IP;
×
478
            int port = Mavsdk::DEFAULT_TCP_REMOTE_PORT;
×
479
            if (!cli_arg.get_path().empty()) {
×
480
                path = cli_arg.get_path();
×
481
            }
482
            if (cli_arg.get_port()) {
×
483
                port = cli_arg.get_port();
×
484
            }
485
            return add_tcp_connection(path, port, forwarding_option);
×
486
        }
487

488
        case CliArg::Protocol::Serial: {
×
489
            int baudrate = Mavsdk::DEFAULT_SERIAL_BAUDRATE;
×
490
            if (cli_arg.get_baudrate()) {
×
491
                baudrate = cli_arg.get_baudrate();
×
492
            }
493
            bool flow_control = cli_arg.get_flow_control();
×
494
            return add_serial_connection(
495
                cli_arg.get_path(), baudrate, flow_control, forwarding_option);
×
496
        }
497

498
        default:
×
499
            return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
500
    }
501
}
502

503
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_udp_connection(
34✔
504
    const std::string& local_ip, const int local_port, ForwardingOption forwarding_option)
505
{
506
    auto new_conn = std::make_shared<UdpConnection>(
34✔
507
        [this](mavlink_message_t& message, Connection* connection) {
885✔
508
            receive_message(message, connection);
885✔
509
        },
885✔
510
        local_ip,
511
        local_port,
512
        forwarding_option);
68✔
513
    if (!new_conn) {
34✔
514
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
515
    }
516
    ConnectionResult ret = new_conn->start();
34✔
517
    if (ret == ConnectionResult::Success) {
34✔
518
        return {ret, add_connection(new_conn)};
34✔
519
    } else {
520
        return {ret, Mavsdk::ConnectionHandle{}};
×
521
    }
522
}
523

524
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::setup_udp_remote(
34✔
525
    const std::string& remote_ip, int remote_port, ForwardingOption forwarding_option)
526
{
527
    auto new_conn = std::make_shared<UdpConnection>(
34✔
528
        [this](mavlink_message_t& message, Connection* connection) {
561✔
529
            receive_message(message, connection);
561✔
530
        },
561✔
531
        "0.0.0.0",
532
        0,
34✔
533
        forwarding_option);
68✔
534
    if (!new_conn) {
34✔
535
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
536
    }
537
    ConnectionResult ret = new_conn->start();
34✔
538
    if (ret == ConnectionResult::Success) {
34✔
539
        new_conn->add_remote(remote_ip, remote_port);
34✔
540
        auto handle = add_connection(new_conn);
34✔
541
        std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
68✔
542
        if (_systems.empty()) {
34✔
543
            make_system_with_component(0, 0);
34✔
544
        }
545

546
        // With a UDP remote, we need to initiate the connection by sending
547
        // heartbeats.
548
        auto new_configuration = get_configuration();
34✔
549
        new_configuration.set_always_send_heartbeats(true);
34✔
550
        set_configuration(new_configuration);
34✔
551

552
        return {ret, handle};
34✔
553
    } else {
554
        return {ret, Mavsdk::ConnectionHandle{}};
×
555
    }
556
}
557

558
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_tcp_connection(
×
559
    const std::string& remote_ip, int remote_port, ForwardingOption forwarding_option)
560
{
561
    auto new_conn = std::make_shared<TcpConnection>(
×
562
        [this](mavlink_message_t& message, Connection* connection) {
×
563
            receive_message(message, connection);
×
564
        },
×
565
        remote_ip,
566
        remote_port,
567
        forwarding_option);
×
568
    if (!new_conn) {
×
569
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
570
    }
571
    ConnectionResult ret = new_conn->start();
×
572
    if (ret == ConnectionResult::Success) {
×
573
        return {ret, add_connection(new_conn)};
×
574
    } else {
575
        return {ret, Mavsdk::ConnectionHandle{}};
×
576
    }
577
}
578

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

600
        auto new_configuration = get_configuration();
×
601

602
        // PX4 starting with v1.13 does not send heartbeats by default, so we need
603
        // to initiate the MAVLink connection by sending heartbeats.
604
        // Therefore, we override the default here and enable sending heartbeats.
605
        new_configuration.set_always_send_heartbeats(true);
×
606
        set_configuration(new_configuration);
×
607

608
        return {ret, handle};
×
609

610
    } else {
611
        return {ret, Mavsdk::ConnectionHandle{}};
×
612
    }
613
}
614

615
Mavsdk::ConnectionHandle
616
MavsdkImpl::add_connection(const std::shared_ptr<Connection>& new_connection)
68✔
617
{
618
    std::lock_guard<std::mutex> lock(_connections_mutex);
68✔
619
    auto handle = Mavsdk::ConnectionHandle{_connections_handle_id++};
68✔
620
    _connections.emplace_back(ConnectionEntry{new_connection, handle});
68✔
621

622
    return handle;
68✔
623
}
624

625
void MavsdkImpl::remove_connection(Mavsdk::ConnectionHandle handle)
×
626
{
627
    std::lock_guard<std::mutex> lock(_connections_mutex);
×
628

629
    _connections.erase(std::remove_if(_connections.begin(), _connections.end(), [&](auto&& entry) {
×
630
        return (entry.handle == handle);
×
631
    }));
×
632
}
×
633

634
Mavsdk::Configuration MavsdkImpl::get_configuration() const
34✔
635
{
636
    return _configuration;
34✔
637
}
638

639
void MavsdkImpl::set_configuration(Mavsdk::Configuration new_configuration)
102✔
640
{
641
    // We just point the default to the newly created component. This means
642
    // that the previous default component will be deleted if it is not
643
    // used/referenced anywhere.
644
    _default_server_component = server_component_by_id(new_configuration.get_component_id());
102✔
645

646
    if (new_configuration.get_always_send_heartbeats() &&
170✔
647
        !_configuration.get_always_send_heartbeats()) {
68✔
648
        start_sending_heartbeats();
34✔
649
    } else if (
68✔
650
        !new_configuration.get_always_send_heartbeats() &&
102✔
651
        _configuration.get_always_send_heartbeats() && !is_any_system_connected()) {
102✔
652
        stop_sending_heartbeats();
×
653
    }
654

655
    _configuration = new_configuration;
102✔
656
}
102✔
657

658
uint8_t MavsdkImpl::get_own_system_id() const
3,899✔
659
{
660
    return _configuration.get_system_id();
3,899✔
661
}
662

663
uint8_t MavsdkImpl::get_own_component_id() const
1,135✔
664
{
665
    return _configuration.get_component_id();
1,135✔
666
}
667

668
uint8_t MavsdkImpl::channel() const
×
669
{
670
    // TODO
671
    return 0;
×
672
}
673

674
Autopilot MavsdkImpl::autopilot() const
×
675
{
676
    // TODO
677
    return Autopilot::Px4;
×
678
}
679

680
// FIXME: this should be per component
681
uint8_t MavsdkImpl::get_mav_type() const
96✔
682
{
683
    switch (_configuration.get_usage_type()) {
96✔
684
        case Mavsdk::Configuration::UsageType::Autopilot:
50✔
685
            return MAV_TYPE_GENERIC;
50✔
686

687
        case Mavsdk::Configuration::UsageType::GroundStation:
45✔
688
            return MAV_TYPE_GCS;
45✔
689

690
        case Mavsdk::Configuration::UsageType::CompanionComputer:
×
691
            return MAV_TYPE_ONBOARD_CONTROLLER;
×
692

693
        case Mavsdk::Configuration::UsageType::Camera:
1✔
694
            return MAV_TYPE_CAMERA;
1✔
695

696
        case Mavsdk::Configuration::UsageType::Custom:
×
697
            return MAV_TYPE_GENERIC;
×
698

UNCOV
699
        default:
×
UNCOV
700
            LogErr() << "Unknown configuration";
×
701
            return 0;
×
702
    }
703
}
704

705
void MavsdkImpl::make_system_with_component(uint8_t system_id, uint8_t comp_id)
68✔
706
{
707
    // Needs _systems_lock
708

709
    if (_should_exit) {
68✔
710
        // When the system got destroyed in the destructor, we have to give up.
711
        return;
×
712
    }
713

714
    if (static_cast<int>(system_id) == 0 && static_cast<int>(comp_id) == 0) {
68✔
715
        LogDebug() << "Initializing connection to remote system...";
34✔
716
    } else {
717
        LogDebug() << "New system ID: " << static_cast<int>(system_id)
102✔
718
                   << " Comp ID: " << static_cast<int>(comp_id);
102✔
719
    }
720

721
    // Make a system with its first component
722
    auto new_system = std::make_shared<System>(*this);
136✔
723
    new_system->init(system_id, comp_id);
68✔
724

725
    _systems.emplace_back(system_id, new_system);
68✔
726
}
727

728
void MavsdkImpl::notify_on_discover()
100✔
729
{
730
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
200✔
731
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
134✔
732
}
100✔
733

734
void MavsdkImpl::notify_on_timeout()
×
735
{
736
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
×
737
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
738
}
×
739

740
Mavsdk::NewSystemHandle
741
MavsdkImpl::subscribe_on_new_system(const Mavsdk::NewSystemCallback& callback)
34✔
742
{
743
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
34✔
744

745
    const auto handle = _new_system_callbacks.subscribe(callback);
34✔
746

747
    if (is_any_system_connected()) {
34✔
748
        _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
749
    }
750

751
    return handle;
34✔
752
}
753

754
void MavsdkImpl::unsubscribe_on_new_system(Mavsdk::NewSystemHandle handle)
34✔
755
{
756
    _new_system_callbacks.unsubscribe(handle);
34✔
757
}
34✔
758

759
bool MavsdkImpl::is_any_system_connected() const
34✔
760
{
761
    std::vector<std::shared_ptr<System>> connected_systems = systems();
68✔
762
    return std::any_of(connected_systems.cbegin(), connected_systems.cend(), [](auto& system) {
34✔
763
        return system->is_connected();
×
764
    });
34✔
765
}
766

767
void MavsdkImpl::work_thread()
4,736✔
768
{
769
    while (!_should_exit) {
4,736✔
770
        timeout_handler.run_once();
4,662✔
771
        call_every_handler.run_once();
4,670✔
772

773
        {
774
            std::lock_guard<std::mutex> lock(_server_components_mutex);
9,334✔
775
            for (auto& it : _server_components) {
9,264✔
776
                if (it.second != nullptr) {
4,599✔
777
                    it.second->_impl->do_work();
4,599✔
778
                }
779
            }
780
        }
781

782
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
4,666✔
783
    }
784
}
69✔
785

786
void MavsdkImpl::call_user_callback_located(
116✔
787
    const std::string& filename, const int linenumber, const std::function<void()>& func)
788
{
789
    auto callback_size = _user_callback_queue.size();
116✔
790
    if (callback_size == 10) {
116✔
791
        LogWarn()
×
792
            << "User callback queue too slow.\n"
×
793
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
794

795
    } else if (callback_size == 99) {
116✔
796
        LogErr()
×
797
            << "User callback queue overflown\n"
×
798
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
799

800
    } else if (callback_size == 100) {
116✔
801
        return;
×
802
    }
803

804
    // We only need to keep track of filename and linenumber if we're actually debugging this.
805
    UserCallback user_callback =
116✔
806
        _callback_debugging ? UserCallback{func, filename, linenumber} : UserCallback{func};
348✔
807

808
    _user_callback_queue.enqueue(user_callback);
116✔
809
}
810

811
void MavsdkImpl::process_user_callbacks_thread()
254✔
812
{
813
    while (!_should_exit) {
254✔
814
        auto callback = _user_callback_queue.dequeue();
185✔
815
        if (!callback) {
185✔
816
            continue;
69✔
817
        }
818

819
        void* cookie{nullptr};
116✔
820

821
        const double timeout_s = 1.0;
116✔
822
        timeout_handler.add(
116✔
823
            [&]() {
×
824
                if (_callback_debugging) {
×
825
                    LogWarn() << "Callback called from " << callback.value().filename << ":"
×
826
                              << callback.value().linenumber << " took more than " << timeout_s
×
827
                              << " second to run.";
×
828
                    fflush(stdout);
×
829
                    fflush(stderr);
×
830
                    abort();
×
831
                } else {
832
                    LogWarn()
×
833
                        << "Callback took more than " << timeout_s << " second to run.\n"
×
834
                        << "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
835
                }
836
            },
×
837
            timeout_s,
838
            &cookie);
839
        callback.value().func();
116✔
840
        timeout_handler.remove(cookie);
116✔
841
    }
842
}
69✔
843

844
void MavsdkImpl::start_sending_heartbeats()
100✔
845
{
846
    // Before sending out first heartbeats we need to make sure we have a
847
    // default server component.
848
    default_server_component_impl();
100✔
849

850
    if (_heartbeat_send_cookie == nullptr) {
100✔
851
        call_every_handler.add(
68✔
852
            [this]() { send_heartbeat(); }, HEARTBEAT_SEND_INTERVAL_S, &_heartbeat_send_cookie);
96✔
853
    }
854
}
100✔
855

856
void MavsdkImpl::stop_sending_heartbeats()
×
857
{
858
    if (!_configuration.get_always_send_heartbeats()) {
×
859
        call_every_handler.remove(_heartbeat_send_cookie);
×
860
        _heartbeat_send_cookie = nullptr;
×
861
    }
862
}
×
863

864
ServerComponentImpl& MavsdkImpl::default_server_component_impl()
723✔
865
{
866
    if (_default_server_component == nullptr) {
723✔
867
        _default_server_component = server_component_by_id(_configuration.get_component_id());
×
868
    }
869
    return *_default_server_component->_impl;
723✔
870
}
871

872
void MavsdkImpl::send_heartbeat()
96✔
873
{
874
    std::lock_guard<std::mutex> lock(_server_components_mutex);
192✔
875

876
    for (auto& it : _server_components) {
192✔
877
        if (it.second != nullptr) {
96✔
878
            it.second->_impl->send_heartbeat();
96✔
879
        }
880
    }
881
}
96✔
882

883
void MavsdkImpl::intercept_incoming_messages_async(std::function<bool(mavlink_message_t&)> callback)
22✔
884
{
885
    std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
44✔
886
    _intercept_incoming_messages_callback = callback;
22✔
887
}
22✔
888

889
void MavsdkImpl::intercept_outgoing_messages_async(std::function<bool(mavlink_message_t&)> callback)
14✔
890
{
891
    std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
28✔
892
    _intercept_outgoing_messages_callback = callback;
14✔
893
}
14✔
894

895
uint8_t MavsdkImpl::get_target_system_id(const mavlink_message_t& message)
1,447✔
896
{
897
    // Checks whether connection knows target system ID by extracting target system if set.
898
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
1,447✔
899

900
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_SYSTEM)) {
1,447✔
901
        return 0;
193✔
902
    }
903

904
    // Don't look at the target system offset if it is outside the payload length.
905
    // This can happen if the fields are trimmed.
906
    if (meta->target_system_ofs >= message.len) {
1,254✔
907
        return 0;
×
908
    }
909

910
    return (_MAV_PAYLOAD(&message))[meta->target_system_ofs];
1,254✔
911
}
912

913
uint8_t MavsdkImpl::get_target_component_id(const mavlink_message_t& message)
×
914
{
915
    // Checks whether connection knows target system ID by extracting target system if set.
916
    const mavlink_msg_entry_t* meta = mavlink_get_msg_entry(message.msgid);
×
917

918
    if (meta == nullptr || !(meta->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_COMPONENT)) {
×
919
        return 0;
×
920
    }
921

922
    // Don't look at the target component offset if it is outside the payload length.
923
    // This can happen if the fields are trimmed.
924
    if (meta->target_component_ofs >= message.len) {
×
925
        return 0;
×
926
    }
927

928
    return (_MAV_PAYLOAD(&message))[meta->target_component_ofs];
×
929
}
930

931
Sender& MavsdkImpl::sender()
×
932
{
933
    return default_server_component_impl().sender();
×
934
}
935

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