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

mavlink / MAVSDK / 29564081841

17 Jul 2026 07:09AM UTC coverage: 50.512% (+0.01%) from 50.498%
29564081841

push

github

web-flow
Merge pull request #2936 from mavlink/v3-backport

Backport various bugfixes to v3

162 of 257 new or added lines in 24 files covered. (63.04%)

56 existing lines in 15 files now uncovered.

19343 of 38294 relevant lines covered (50.51%)

669.75 hits per line

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

67.72
/src/mavsdk/core/mavlink_request_message.cpp
1

2
#include "log.h"
3
#include "mavlink_request_message.h"
4
#include "system_impl.h"
5
#include <algorithm>
6
#include <cstdint>
7
#include <string>
8

9
namespace mavsdk {
10

11
MavlinkRequestMessage::MavlinkRequestMessage(
172✔
12
    SystemImpl& system_impl,
13
    MavlinkCommandSender& command_sender,
14
    MavlinkMessageHandler& message_handler,
15
    TimeoutHandler& timeout_handler) :
172✔
16
    _system_impl(system_impl),
172✔
17
    _command_sender(command_sender),
172✔
18
    _message_handler(message_handler),
172✔
19
    _timeout_handler(timeout_handler)
172✔
20
{
21
    if (const char* env_p = std::getenv("MAVSDK_COMMAND_DEBUGGING")) {
172✔
22
        if (std::string(env_p) == "1") {
×
23
            LogDebug() << "Command debugging is on.";
×
24
            _debugging = true;
×
25
        }
26
    }
27
}
172✔
28

29
MavlinkRequestMessage::~MavlinkRequestMessage()
172✔
30
{
31
    // The message handler holds callbacks capturing 'this', so make sure none of
32
    // them can fire after we are gone.
33
    _message_handler.unregister_all_blocking(this);
172✔
34

35
    // In-flight requests schedule timeouts that also capture 'this'. Cancel any that
36
    // are still pending, otherwise TimeoutHandler could fire handle_timeout on us
37
    // after destruction (use-after-free).
38
    std::lock_guard<std::mutex> lock(_mutex);
172✔
39
    for (const auto& item : _work_items) {
183✔
40
        _timeout_handler.remove(item.timeout_cookie);
11✔
41
    }
42
}
172✔
43

44
void MavlinkRequestMessage::request(
148✔
45
    uint32_t message_id,
46
    uint8_t target_component,
47
    MavlinkRequestMessageCallback callback,
48
    uint32_t param2)
49
{
50
    std::unique_lock<std::mutex> lock(_mutex);
148✔
51

52
    // Respond with 'Busy' if already in progress.
53
    for (auto& item : _work_items) {
300✔
54
        if (item.message_id == message_id && item.param2 == param2 &&
152✔
55
            item.target_component == target_component) {
×
56
            lock.unlock();
×
57
            if (callback) {
×
58
                callback(MavlinkCommandSender::Result::Busy, {});
×
59
            }
60
            if (_debugging) {
×
61
                LogDebug() << "Message " << item.message_id << " already requested";
×
62
            }
63
            return;
×
64
        }
65
    }
66

67
    // Otherwise, schedule it.
68
    _work_items.emplace_back(WorkItem{message_id, target_component, callback, param2});
148✔
69

70
    // Register a handler for this message id if we don't have one yet. We keep
71
    // it registered for our lifetime; handle_any_message is a no-op when no work
72
    // item is waiting, and this avoids the races of registering and
73
    // unregistering per request.
74
    if (std::find(_registered_message_ids.begin(), _registered_message_ids.end(), message_id) ==
148✔
75
        _registered_message_ids.end()) {
296✔
76
        _registered_message_ids.push_back(message_id);
141✔
77
        _message_handler.register_one(
141✔
78
            message_id,
79
            [this](const mavlink_message_t& message) { handle_any_message(message); },
123✔
80
            this);
81
    }
82

83
    // And send off command
84
    send_request(_work_items.back());
148✔
85
}
148✔
86

87
void MavlinkRequestMessage::send_request(WorkItem& item)
268✔
88
{
89
    // Every second time, starting with first retry, we try the old request commands.
90
    // If no old commands exist, we of course just send the new one again.
91
    if (item.retries % 2 != 0) {
268✔
92
        if (!try_sending_request_using_old_command(item)) {
68✔
93
            send_request_using_new_command(item);
4✔
94
        }
95
    } else {
96
        send_request_using_new_command(item);
200✔
97
    }
98
}
268✔
99

100
void MavlinkRequestMessage::send_request_using_new_command(WorkItem& item)
204✔
101
{
102
    if (_debugging) {
204✔
103
        if (item.retries > 0) {
×
104
            LogDebug() << "Request message " << item.message_id
×
105
                       << " again using REQUEST_MESSAGE (retries: " << item.retries << ")";
×
106
        } else {
107
            LogDebug() << "Request message " << item.message_id << " using REQUEST_MESSAGE";
×
108
        }
109
    }
110

111
    MavlinkCommandSender::CommandLong command_request_message{};
204✔
112
    command_request_message.command = MAV_CMD_REQUEST_MESSAGE;
204✔
113
    command_request_message.target_system_id = _system_impl.get_system_id();
204✔
114
    command_request_message.target_component_id = item.target_component;
204✔
115
    command_request_message.params.maybe_param1 = {static_cast<float>(item.message_id)};
204✔
116
    _command_sender.queue_command_async(
204✔
117
        command_request_message,
118
        [this, message_id = item.message_id](MavlinkCommandSender::Result result, float) {
204✔
119
            if (result != MavlinkCommandSender::Result::InProgress) {
192✔
120
                handle_command_result(message_id, result);
192✔
121
            }
122
        },
192✔
123
        1);
124
}
204✔
125

126
bool MavlinkRequestMessage::try_sending_request_using_old_command(WorkItem& item)
68✔
127
{
128
    MavlinkCommandSender::CommandLong command_request_message{};
68✔
129
    command_request_message.target_system_id = _system_impl.get_system_id();
68✔
130
    command_request_message.target_component_id = item.target_component;
68✔
131

132
    std::string command_name;
68✔
133

134
    switch (item.message_id) {
68✔
UNCOV
135
        case MAVLINK_MSG_ID_AUTOPILOT_VERSION:
×
UNCOV
136
            command_request_message.command = MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES;
×
UNCOV
137
            command_request_message.params.maybe_param1 = {1.f};
×
UNCOV
138
            command_name = "REQUEST_AUTOPILOT_CAPABILITIES";
×
UNCOV
139
            break;
×
140

141
        case MAVLINK_MSG_ID_CAMERA_INFORMATION:
×
142
            command_request_message.command = MAV_CMD_REQUEST_CAMERA_INFORMATION;
×
143
            command_request_message.params.maybe_param1 = {1.0f};
×
144
            command_name = "REQUEST_CAMERA_INFORMATION";
×
145
            break;
×
146

147
        case MAVLINK_MSG_ID_CAMERA_SETTINGS:
10✔
148
            command_request_message.command = MAV_CMD_REQUEST_CAMERA_SETTINGS;
10✔
149
            command_request_message.params.maybe_param1 = {1.0f};
10✔
150
            command_name = "REQUEST_CAMERA_SETTINGS";
10✔
151

152
            break;
10✔
153

154
        case MAVLINK_MSG_ID_STORAGE_INFORMATION:
18✔
155
            command_request_message.command = MAV_CMD_REQUEST_STORAGE_INFORMATION;
18✔
156
            command_request_message.params.maybe_param1 = {
36✔
157
                static_cast<float>(item.param2)}; // storage ID
18✔
158
            command_request_message.params.maybe_param2 = {1.0f};
18✔
159
            command_name = "REQUEST_STORAGE_INFORMATION";
18✔
160
            break;
18✔
161

162
        case MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS:
×
163
            command_request_message.command = MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS;
×
164
            command_request_message.params.maybe_param1 = {1.0f};
×
165
            command_name = "REQUEST_CAMERA_CAPTURE_STATUS";
×
166
            break;
×
167

168
        case MAVLINK_MSG_ID_FLIGHT_INFORMATION:
×
169
            command_request_message.command = MAV_CMD_REQUEST_FLIGHT_INFORMATION;
×
170
            command_request_message.params.maybe_param1 = {1.0f};
×
171
            command_name = "REQUEST_FLIGHT_INFORMATION";
×
172
            break;
×
173

174
        case MAVLINK_MSG_ID_VIDEO_STREAM_STATUS:
19✔
175
            command_request_message.command = MAV_CMD_REQUEST_VIDEO_STREAM_STATUS;
19✔
176
            command_request_message.params.maybe_param1 = {
38✔
177
                static_cast<float>(item.param2)}; // stream ID
19✔
178
            command_name = "REQUEST_VIDEO_STREAM_STATUS";
19✔
179
            break;
19✔
180

181
        case MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION:
17✔
182
            command_request_message.command = MAV_CMD_REQUEST_VIDEO_STREAM_INFORMATION;
17✔
183
            command_request_message.params.maybe_param1 = {
34✔
184
                static_cast<float>(item.param2)}; // stream ID
17✔
185
            command_name = "REQUEST_VIDEO_STREAM_INFORMATION";
17✔
186
            break;
17✔
187

188
        case MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED:
×
189
            command_request_message.command = MAV_CMD_REQUEST_CAMERA_IMAGE_CAPTURE;
×
190
            command_request_message.params.maybe_param1 = {
×
191
                static_cast<float>(item.param2)}; // sequence number
×
192
            command_name = "REQUEST_CAMERA_IMAGE_CAPTURE";
×
193
            break;
×
194

195
        default:
4✔
196
            // No alternative command for this message.
197
            return false;
4✔
198
    }
199

200
    if (_debugging) {
64✔
201
        LogDebug() << "Request message " << item.message_id << " again using " << command_name
×
202
                   << " (retries: " << item.retries << ")";
×
203
    }
204

205
    _command_sender.queue_command_async(
64✔
206
        command_request_message,
207
        [this, message_id = item.message_id](MavlinkCommandSender::Result result, float) {
64✔
208
            if (result != MavlinkCommandSender::Result::InProgress) {
64✔
209
                handle_command_result(message_id, result);
64✔
210
            }
211
        },
64✔
212
        1);
213

214
    return true;
64✔
215
}
68✔
216

217
void MavlinkRequestMessage::handle_any_message(const mavlink_message_t& message)
123✔
218
{
219
    std::unique_lock<std::mutex> lock(_mutex);
123✔
220

221
    for (auto it = _work_items.begin(); it != _work_items.end(); ++it) {
173✔
222
        // Check if we're waiting for this message. Skip unless both the message id and
223
        // the component it came from match what this work item requested.
224
        // TODO: check if params are correct.
225
        if (it->message_id != message.msgid || it->target_component != message.compid) {
161✔
226
            continue;
50✔
227
        }
228

229
        _timeout_handler.remove(it->timeout_cookie);
111✔
230
        // We have at least the message which is what we care about most, so we
231
        // tell the user about it and call it a day. If we receive the result
232
        // of the command later, we ignore it, and we fake the result to be successful
233
        // anyway.
234
        auto temp_callback = it->callback;
111✔
235
        // We can now get rid of the entry now. We keep the message handler
236
        // registered for next time.
237
        _work_items.erase(it);
111✔
238
        lock.unlock();
111✔
239

240
        if (temp_callback) {
111✔
241
            temp_callback(MavlinkCommandSender::Result::Success, message);
1✔
242
        }
243
        return;
111✔
244
    }
111✔
245
}
123✔
246

247
void MavlinkRequestMessage::handle_command_result(
256✔
248
    uint32_t message_id, MavlinkCommandSender::Result result)
249
{
250
    std::unique_lock<std::mutex> lock(_mutex);
256✔
251

252
    for (auto it = _work_items.begin(); it != _work_items.end(); ++it) {
445✔
253
        // Check if we're waiting for this result
254
        if (it->message_id != message_id) {
356✔
255
            continue;
189✔
256
        }
257

258
        switch (result) {
167✔
259
            case MavlinkCommandSender::Result::Success:
22✔
260
                // This is promising, let's hope the message will actually arrive.
261
                // We'll set a timeout in case we need to retry.
262
                it->timeout_cookie = _timeout_handler.add(
22✔
263
                    [this, message_id, target_component = it->target_component]() {
22✔
264
                        handle_timeout(message_id, target_component);
1✔
265
                    },
1✔
266
                    1.0);
267
                return;
167✔
268

269
            case MavlinkCommandSender::Result::Busy:
145✔
270
                // FALLTHROUGH
271
            case MavlinkCommandSender::Result::Denied:
272
                // FALLTHROUGH
273
            case MavlinkCommandSender::Result::Unsupported:
274
                // FALLTHROUGH
275
            case MavlinkCommandSender::Result::Timeout:
276
                // FALLTHROUGH
277
            case MavlinkCommandSender::Result::TemporarilyRejected:
278
                // It looks like we should try again
279
                if (++it->retries > RETRIES) {
145✔
280
                    // We have already retried, let's give up.
281
                    auto temp_callback = it->callback;
26✔
282
                    _work_items.erase(it);
26✔
283
                    lock.unlock();
26✔
284
                    if (temp_callback) {
26✔
285
                        temp_callback(MavlinkCommandSender::Result::Timeout, {});
×
286
                    }
287
                } else {
26✔
288
                    send_request(*it);
119✔
289
                }
290
                return;
145✔
291

292
            case MavlinkCommandSender::Result::NoSystem:
×
293
                // FALLTHROUGH
294
            case MavlinkCommandSender::Result::ConnectionError:
295
                // FALLTHROUGH
296
            case MavlinkCommandSender::Result::Failed:
297
                // FALLTHROUGH
298
            case MavlinkCommandSender::Result::Cancelled:
299
                // FALLTHROUGH
300
            case MavlinkCommandSender::Result::UnknownError: {
301
                // It looks like this did not work, and we can report the error
302
                // No need to try again.
303
                auto temp_callback = it->callback;
×
304
                _work_items.erase(it);
×
305
                lock.unlock();
×
306
                if (temp_callback) {
×
307
                    temp_callback(result, {});
×
308
                }
309
                return;
×
310
            }
×
311

312
            case MavlinkCommandSender::Result::InProgress:
×
313
                // Should not happen, as it is already filtered out.
314
                return;
×
315
        }
316
    }
317
}
256✔
318

319
void MavlinkRequestMessage::handle_timeout(uint32_t message_id, uint8_t target_component)
1✔
320
{
321
    std::unique_lock<std::mutex> lock(_mutex);
1✔
322

323
    for (auto it = _work_items.begin(); it != _work_items.end(); ++it) {
2✔
324
        // Check if we're waiting for this result
325
        if (it->message_id != message_id || it->target_component != target_component) {
1✔
326
            continue;
×
327
        }
328

329
        if (++it->retries > RETRIES) {
1✔
330
            // We have already retried, let's give up.
331
            auto temp_callback = it->callback;
×
332
            _work_items.erase(it);
×
333
            lock.unlock();
×
334
            if (temp_callback) {
×
335
                temp_callback(MavlinkCommandSender::Result::Timeout, {});
×
336
            }
337
            return;
×
338
        }
×
339

340
        send_request(*it);
1✔
341
    }
342
}
1✔
343

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