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

mavlink / MAVSDK / 11452150858

22 Oct 2024 02:32AM CUT coverage: 37.403% (+0.02%) from 37.381%
11452150858

push

github

web-flow
camera_server: prevent double ack+message (#2430)

It turns out we were sending the ack and message for storage information
as well as capture status twice, once directly in the request handler
callback, and once the MAVSDK user would call the respond function.

We should only call it in the respond function, not in the callback.

11105 of 29690 relevant lines covered (37.4%)

255.6 hits per line

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

55.45
/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✔
43
        if (std::string(env_p) == "1") {
×
44
            LogDebug() << "System debugging is on.";
×
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
        unsigned successful_emissions = 0;
×
296
        for (auto& entry : _connections) {
×
297
            // Check whether the connection is not the one from which we received the message.
298
            // And also check if the connection was set to forward messages.
299
            if (entry.connection.get() == connection ||
×
300
                !entry.connection->should_forward_messages()) {
×
301
                continue;
×
302
            }
303
            if ((*entry.connection).send_message(message)) {
×
304
                successful_emissions++;
×
305
            }
306
        }
307
        if (successful_emissions == 0) {
×
308
            LogErr() << "Message forwarding failed";
×
309
        }
310
    }
311
}
×
312

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

320
    if (_should_exit) {
1,553✔
321
        // If we're meant to clean up, let's not try to acquire any more locks but bail.
322
        return;
1✔
323
    }
324

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

338
    if (_should_exit) {
1,508✔
339
        // If we're meant to clean up, let's not try to acquire any more locks but bail.
340
        return;
×
341
    }
342

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

354
    {
355
        std::lock_guard<std::mutex> lock(_connections_mutex);
3,015✔
356

357
        if (_connections.size() > 1 && mavsdk::Connection::forwarding_connections_count() > 0 &&
1,507✔
358
            (mavsdk::Connection::forwarding_connections_count() > 1 ||
×
359
             !connection->should_forward_messages())) {
×
360
            if (_message_logging_on) {
×
361
                LogDebug() << "Forwarding message " << message.msgid << " from "
×
362
                           << static_cast<int>(message.sysid) << "/"
×
363
                           << static_cast<int>(message.compid);
×
364
            }
365
            forward_message(message, connection);
×
366
        }
367
    }
368

369
    // Don't ever create a system with sysid 0.
370
    if (message.sysid == 0) {
1,507✔
371
        if (_message_logging_on) {
×
372
            LogDebug() << "Ignoring message with sysid == 0";
×
373
        }
374
        return;
×
375
    }
376

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

393
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
1,508✔
394

395
    bool found_system = false;
1,508✔
396
    for (auto& system : _systems) {
1,507✔
397
        if (system.first == message.sysid) {
1,438✔
398
            system.second->system_impl()->add_new_component(message.compid);
1,439✔
399
            found_system = true;
1,437✔
400
            break;
1,437✔
401
        }
402
    }
403

404
    if (!found_system && message.compid == MAV_COMP_ID_TELEMETRY_RADIO) {
1,506✔
405
        if (_message_logging_on) {
×
406
            LogDebug() << "Don't create new system just for telemetry radio";
×
407
        }
408
        return;
×
409
    }
410

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

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

431
    mavlink_message_handler.process_message(message);
1,506✔
432

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

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

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

463
    std::lock_guard<std::mutex> lock(_connections_mutex);
3,176✔
464

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

471
    uint8_t successful_emissions = 0;
1,553✔
472
    for (auto& _connection : _connections) {
3,106✔
473
        const uint8_t target_system_id = get_target_system_id(message);
1,553✔
474

475
        if (target_system_id != 0 && !(*_connection.connection).has_system_id(target_system_id)) {
1,553✔
476
            continue;
×
477
        }
478

479
        if ((*_connection.connection).send_message(message)) {
1,553✔
480
            successful_emissions++;
1,553✔
481
        }
482
    }
483

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

489
    return true;
1,553✔
490
}
491

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

500
    switch (cli_arg.get_protocol()) {
70✔
501
        case CliArg::Protocol::Udp: {
70✔
502
            int port = cli_arg.get_port() ? cli_arg.get_port() : Mavsdk::DEFAULT_UDP_PORT;
70✔
503

504
            if (cli_arg.get_path().empty() || cli_arg.get_path() == Mavsdk::DEFAULT_UDP_BIND_IP) {
70✔
505
                std::string path = Mavsdk::DEFAULT_UDP_BIND_IP;
105✔
506
                return add_udp_connection(path, port, forwarding_option);
35✔
507
            } else {
508
                std::string path = cli_arg.get_path();
70✔
509
                return setup_udp_remote(path, port, forwarding_option);
35✔
510
            }
511
        }
512

513
        case CliArg::Protocol::Tcp: {
×
514
            std::string path = Mavsdk::DEFAULT_TCP_REMOTE_IP;
×
515
            int port = Mavsdk::DEFAULT_TCP_REMOTE_PORT;
×
516
            if (!cli_arg.get_path().empty()) {
×
517
                path = cli_arg.get_path();
×
518
            }
519
            if (cli_arg.get_port()) {
×
520
                port = cli_arg.get_port();
×
521
            }
522
            return add_tcp_connection(path, port, forwarding_option);
×
523
        }
524

525
        case CliArg::Protocol::Serial: {
×
526
            int baudrate = Mavsdk::DEFAULT_SERIAL_BAUDRATE;
×
527
            if (cli_arg.get_baudrate()) {
×
528
                baudrate = cli_arg.get_baudrate();
×
529
            }
530
            bool flow_control = cli_arg.get_flow_control();
×
531
            return add_serial_connection(
532
                cli_arg.get_path(), baudrate, flow_control, forwarding_option);
×
533
        }
534

535
        default:
×
536
            return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
537
    }
538
}
539

540
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_udp_connection(
35✔
541
    const std::string& local_ip, const int local_port, ForwardingOption forwarding_option)
542
{
543
    auto new_conn = std::make_shared<UdpConnection>(
35✔
544
        [this](mavlink_message_t& message, Connection* connection) {
962✔
545
            receive_message(message, connection);
962✔
546
        },
962✔
547
        local_ip,
548
        local_port,
549
        forwarding_option);
70✔
550
    if (!new_conn) {
35✔
551
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
552
    }
553
    ConnectionResult ret = new_conn->start();
35✔
554
    if (ret == ConnectionResult::Success) {
35✔
555
        return {ret, add_connection(new_conn)};
35✔
556
    } else {
557
        return {ret, Mavsdk::ConnectionHandle{}};
×
558
    }
559
}
560

561
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::setup_udp_remote(
35✔
562
    const std::string& remote_ip, int remote_port, ForwardingOption forwarding_option)
563
{
564
    auto new_conn = std::make_shared<UdpConnection>(
35✔
565
        [this](mavlink_message_t& message, Connection* connection) {
591✔
566
            receive_message(message, connection);
591✔
567
        },
591✔
568
        "0.0.0.0",
569
        0,
35✔
570
        forwarding_option);
70✔
571
    if (!new_conn) {
35✔
572
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
573
    }
574
    ConnectionResult ret = new_conn->start();
35✔
575
    if (ret == ConnectionResult::Success) {
35✔
576
        new_conn->add_remote(remote_ip, remote_port);
35✔
577
        auto handle = add_connection(new_conn);
35✔
578
        std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
70✔
579

580
        // With a UDP remote, we need to initiate the connection by sending heartbeats.
581
        auto new_configuration = get_configuration();
35✔
582
        new_configuration.set_always_send_heartbeats(true);
35✔
583
        set_configuration(new_configuration);
35✔
584

585
        return {ret, handle};
35✔
586
    } else {
587
        return {ret, Mavsdk::ConnectionHandle{}};
×
588
    }
589
}
590

591
std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_tcp_connection(
×
592
    const std::string& remote_ip, int remote_port, ForwardingOption forwarding_option)
593
{
594
    auto new_conn = std::make_shared<TcpConnection>(
×
595
        [this](mavlink_message_t& message, Connection* connection) {
×
596
            receive_message(message, connection);
×
597
        },
×
598
        remote_ip,
599
        remote_port,
600
        forwarding_option);
×
601
    if (!new_conn) {
×
602
        return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
×
603
    }
604
    ConnectionResult ret = new_conn->start();
×
605
    if (ret == ConnectionResult::Success) {
×
606
        return {ret, add_connection(new_conn)};
×
607
    } else {
608
        return {ret, Mavsdk::ConnectionHandle{}};
×
609
    }
610
}
611

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

633
        auto new_configuration = get_configuration();
×
634

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

641
        return {ret, handle};
×
642

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

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

655
    return handle;
70✔
656
}
657

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

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

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

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

679
    if (new_configuration.get_always_send_heartbeats() &&
176✔
680
        !_configuration.get_always_send_heartbeats()) {
70✔
681
        start_sending_heartbeats();
35✔
682
    } else if (
71✔
683
        !new_configuration.get_always_send_heartbeats() &&
107✔
684
        _configuration.get_always_send_heartbeats() && !is_any_system_connected()) {
107✔
685
        stop_sending_heartbeats();
×
686
    }
687

688
    _configuration = new_configuration;
106✔
689
}
106✔
690

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

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

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

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

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

720
        case Mavsdk::ComponentType::GroundStation:
43✔
721
            return MAV_TYPE_GCS;
43✔
722

723
        case Mavsdk::ComponentType::CompanionComputer:
4✔
724
            return MAV_TYPE_ONBOARD_CONTROLLER;
4✔
725

726
        case Mavsdk::ComponentType::Camera:
2✔
727
            return MAV_TYPE_CAMERA;
2✔
728

729
        case Mavsdk::ComponentType::Custom:
×
730
            return MAV_TYPE_GENERIC;
×
731

732
        default:
×
733
            LogErr() << "Unknown configuration";
×
734
            return 0;
×
735
    }
736
}
737

738
void MavsdkImpl::make_system_with_component(uint8_t system_id, uint8_t comp_id)
69✔
739
{
740
    // Needs _systems_lock
741

742
    if (_should_exit) {
69✔
743
        // When the system got destroyed in the destructor, we have to give up.
744
        return;
×
745
    }
746

747
    if (static_cast<int>(system_id) == 0 && static_cast<int>(comp_id) == 0) {
69✔
748
        LogDebug() << "Initializing connection to remote system...";
×
749
    } else {
750
        LogDebug() << "New system ID: " << static_cast<int>(system_id)
207✔
751
                   << " Comp ID: " << static_cast<int>(comp_id);
207✔
752
    }
753

754
    // Make a system with its first component
755
    auto new_system = std::make_shared<System>(*this);
138✔
756
    new_system->init(system_id, comp_id);
69✔
757

758
    _systems.emplace_back(system_id, new_system);
69✔
759
}
760

761
void MavsdkImpl::notify_on_discover()
69✔
762
{
763
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
138✔
764
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
104✔
765
}
69✔
766

767
void MavsdkImpl::notify_on_timeout()
×
768
{
769
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
×
770
    _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
771
}
×
772

773
Mavsdk::NewSystemHandle
774
MavsdkImpl::subscribe_on_new_system(const Mavsdk::NewSystemCallback& callback)
35✔
775
{
776
    std::lock_guard<std::recursive_mutex> lock(_systems_mutex);
35✔
777

778
    const auto handle = _new_system_callbacks.subscribe(callback);
35✔
779

780
    if (is_any_system_connected()) {
35✔
781
        _new_system_callbacks.queue([this](const auto& func) { call_user_callback(func); });
×
782
    }
783

784
    return handle;
35✔
785
}
786

787
void MavsdkImpl::unsubscribe_on_new_system(Mavsdk::NewSystemHandle handle)
34✔
788
{
789
    _new_system_callbacks.unsubscribe(handle);
34✔
790
}
34✔
791

792
bool MavsdkImpl::is_any_system_connected() const
35✔
793
{
794
    std::vector<std::shared_ptr<System>> connected_systems = systems();
70✔
795
    return std::any_of(connected_systems.cbegin(), connected_systems.cend(), [](auto& system) {
35✔
796
        return system->is_connected();
×
797
    });
35✔
798
}
799

800
void MavsdkImpl::work_thread()
11,754✔
801
{
802
    while (!_should_exit) {
11,754✔
803
        timeout_handler.run_once();
11,577✔
804
        call_every_handler.run_once();
11,680✔
805

806
        {
807
            std::lock_guard<std::mutex> lock(_server_components_mutex);
23,301✔
808
            for (auto& it : _server_components) {
23,392✔
809
                if (it.second != nullptr) {
11,729✔
810
                    it.second->_impl->do_work();
11,698✔
811
                }
812
            }
813
        }
814

815
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
11,667✔
816
    }
817
}
70✔
818

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

828
    } else if (callback_size == 99) {
1,474✔
829
        LogErr()
×
830
            << "User callback queue overflown\n"
×
831
               "See: https://mavsdk.mavlink.io/main/en/cpp/troubleshooting.html#user_callbacks";
×
832

833
    } else if (callback_size == 100) {
1,474✔
834
        return;
×
835
    }
836

837
    // We only need to keep track of filename and linenumber if we're actually debugging this.
838
    UserCallback user_callback =
1,474✔
839
        _callback_debugging ? UserCallback{func, filename, linenumber} : UserCallback{func};
4,422✔
840

841
    _user_callback_queue.enqueue(user_callback);
1,474✔
842
}
843

844
void MavsdkImpl::process_user_callbacks_thread()
1,616✔
845
{
846
    while (!_should_exit) {
1,616✔
847
        auto callback = _user_callback_queue.dequeue();
1,545✔
848
        if (!callback) {
1,545✔
849
            continue;
71✔
850
        }
851

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

874
void MavsdkImpl::start_sending_heartbeats()
104✔
875
{
876
    // Before sending out first heartbeats we need to make sure we have a
877
    // default server component.
878
    default_server_component_impl();
104✔
879

880
    call_every_handler.remove(_heartbeat_send_cookie);
104✔
881
    _heartbeat_send_cookie =
208✔
882
        call_every_handler.add([this]() { send_heartbeat(); }, HEARTBEAT_SEND_INTERVAL_S);
265✔
883
}
104✔
884

885
void MavsdkImpl::stop_sending_heartbeats()
×
886
{
887
    if (!_configuration.get_always_send_heartbeats()) {
×
888
        call_every_handler.remove(_heartbeat_send_cookie);
×
889
    }
890
}
×
891

892
ServerComponentImpl& MavsdkImpl::default_server_component_impl()
752✔
893
{
894
    if (_default_server_component == nullptr) {
752✔
895
        _default_server_component = server_component_by_id(_configuration.get_component_id());
×
896
    }
897
    return *_default_server_component->_impl;
752✔
898
}
899

900
void MavsdkImpl::send_heartbeat()
161✔
901
{
902
    std::lock_guard<std::mutex> lock(_server_components_mutex);
322✔
903

904
    for (auto& it : _server_components) {
323✔
905
        if (it.second != nullptr) {
162✔
906
            it.second->_impl->send_heartbeat();
162✔
907
        }
908
    }
909
}
161✔
910

911
void MavsdkImpl::intercept_incoming_messages_async(std::function<bool(mavlink_message_t&)> callback)
22✔
912
{
913
    std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
44✔
914
    _intercept_incoming_messages_callback = callback;
22✔
915
}
22✔
916

917
void MavsdkImpl::intercept_outgoing_messages_async(std::function<bool(mavlink_message_t&)> callback)
14✔
918
{
919
    std::lock_guard<std::mutex> lock(_intercept_callback_mutex);
28✔
920
    _intercept_outgoing_messages_callback = callback;
14✔
921
}
14✔
922

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

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

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

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

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

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

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

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

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

964
} // namespace mavsdk
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc