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

mavlink / MAVSDK / 29462275360

16 Jul 2026 12:44AM UTC coverage: 50.438%. First build
29462275360

Pull #2936

github

web-flow
Merge a1505e5d2 into 23fd28534
Pull Request #2936: Backport various bugfixes to v3

161 of 239 new or added lines in 22 files covered. (67.36%)

19309 of 38283 relevant lines covered (50.44%)

671.19 hits per line

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

70.89
/src/mavsdk/core/mavlink_parameter_server.cpp
1
#include "mavlink_parameter_server.h"
2
#include "mavlink_address.h"
3
#include "mavlink_parameter_helper.h"
4
#include "overloaded.h"
5
#include <cassert>
6
#include <limits>
7

8
namespace mavsdk {
9

10
MavlinkParameterServer::MavlinkParameterServer(
169✔
11
    Sender& sender,
12
    MavlinkMessageHandler& message_handler,
13
    std::optional<std::map<std::string, ParamValue>> optional_param_values) :
169✔
14
    _sender(sender),
169✔
15
    _message_handler(message_handler)
169✔
16
{
17
    if (const char* env_p = std::getenv("MAVSDK_PARAMETER_DEBUGGING")) {
169✔
18
        if (std::string(env_p) == "1") {
×
19
            LogDebug() << "Parameter debugging is on.";
×
20
            _parameter_debugging = true;
×
21
        }
22
    }
23

24
    // Populate the parameter set before the first communication, if provided by the user.
25
    if (optional_param_values.has_value()) {
169✔
26
        const auto& param_values = optional_param_values.value();
×
27
        for (const auto& [key, value] : param_values) {
×
28
            const auto result = provide_server_param(key, value);
×
29
            if (result != Result::Ok) {
×
30
                LogDebug() << "Cannot add parameter:" << key << ":" << value << " " << result;
×
31
            }
32
        }
33
    }
34

35
    _message_handler.register_one(
169✔
36
        MAVLINK_MSG_ID_PARAM_SET,
37
        [this](const mavlink_message_t& message) { process_param_set(message); },
6✔
38
        this);
39

40
    _message_handler.register_one(
169✔
41
        MAVLINK_MSG_ID_PARAM_EXT_SET,
42
        [this](const mavlink_message_t& message) { process_param_ext_set(message); },
5✔
43
        this);
44

45
    _message_handler.register_one(
169✔
46
        MAVLINK_MSG_ID_PARAM_REQUEST_READ,
47
        [this](const mavlink_message_t& message) { process_param_request_read(message); },
22✔
48
        this);
49

50
    _message_handler.register_one(
169✔
51
        MAVLINK_MSG_ID_PARAM_REQUEST_LIST,
52
        [this](const mavlink_message_t& message) { process_param_request_list(message); },
3✔
53
        this);
54

55
    _message_handler.register_one(
169✔
56
        MAVLINK_MSG_ID_PARAM_EXT_REQUEST_READ,
57
        [this](const mavlink_message_t& message) { process_param_ext_request_read(message); },
49✔
58
        this);
59

60
    _message_handler.register_one(
169✔
61
        MAVLINK_MSG_ID_PARAM_EXT_REQUEST_LIST,
62
        [this](const mavlink_message_t& message) { process_param_ext_request_list(message); },
2✔
63
        this);
64
}
169✔
65

66
MavlinkParameterServer::~MavlinkParameterServer()
169✔
67
{
68
    _message_handler.unregister_all(this);
169✔
69
}
169✔
70

71
MavlinkParameterServer::Result
72
MavlinkParameterServer::provide_server_param(const std::string& name, const ParamValue& param_value)
72✔
73
{
74
    if (name.size() > PARAM_ID_LEN) {
72✔
75
        LogErr() << "Error: param name too long";
×
76
        return Result::ParamNameTooLong;
×
77
    }
78
    if (param_value.is<std::string>()) {
72✔
79
        const auto s = param_value.get<std::string>();
8✔
80
        if (s.size() > sizeof(mavlink_param_ext_set_t::param_value)) {
8✔
81
            LogErr() << "Error: param value too long";
×
82
            return Result::ParamValueTooLong;
×
83
        }
84
    }
8✔
85
    std::lock_guard<std::mutex> lock(_all_params_mutex);
72✔
86

87
    // Updating the value of an already-provided parameter is always allowed. Adding a new
88
    // parameter is only possible until the set has been locked down, i.e. until a client has
89
    // enumerated the parameters and thereby fixed the indices and count.
90
    const bool already_exists = _param_cache.param_by_id(name, true).has_value();
72✔
91
    if (!already_exists && _params_locked_down) {
72✔
92
        return Result::ParamProvidedTooLate;
1✔
93
    }
94

95
    // first we try to add it as a new parameter
96
    switch (_param_cache.add_new_param(name, param_value)) {
71✔
97
        case MavlinkParameterCache::AddNewParamResult::Ok:
54✔
98
            return Result::Ok;
54✔
99
        case MavlinkParameterCache::AddNewParamResult::AlreadyExists:
17✔
100
            // then, to not change the public api behaviour, try updating its value.
101
            switch (_param_cache.update_existing_param(name, param_value)) {
17✔
102
                case MavlinkParameterCache::UpdateExistingParamResult::Ok:
17✔
103
                    find_and_call_subscriptions_value_changed(name, param_value);
17✔
104
                    // Notify clients of the changed value on the protocol(s) that can carry it and
105
                    // that a client is actually using. The extended protocol can represent any
106
                    // parameter; the non-extended protocol only those that don't need_extended.
107
                    if (!_seen_extended && !_seen_non_extended) {
17✔
108
                        // No request seen yet, so we can't tell which protocol a client uses.
109
                        // Fall back to notifying on every protocol the parameter supports to avoid
110
                        // silently dropping the change.
111
                        enqueue_value_broadcast(name, param_value, true);
2✔
112
                        if (!param_value.needs_extended()) {
2✔
113
                            enqueue_value_broadcast(name, param_value, false);
2✔
114
                        }
115
                    } else {
116
                        if (_seen_extended) {
15✔
117
                            enqueue_value_broadcast(name, param_value, true);
13✔
118
                        }
119
                        if (_seen_non_extended && !param_value.needs_extended()) {
15✔
120
                            enqueue_value_broadcast(name, param_value, false);
2✔
121
                        }
122
                    }
123
                    return Result::OkExistsAlready;
17✔
124
                case MavlinkParameterCache::UpdateExistingParamResult::MissingParam:
×
125
                    return Result::ParamNotFound;
×
126
                case MavlinkParameterCache::UpdateExistingParamResult::WrongType:
×
127
                    return Result::WrongType;
×
128
                default:
×
129
                    LogErr() << "Unknown update_existing_param result";
×
130
                    assert(false);
×
131
                    return Result::Unknown;
132
            }
133
        case MavlinkParameterCache::AddNewParamResult::TooManyParams:
×
134
            return Result::TooManyParams;
×
135
        default:
×
136
            LogErr() << "Unknown add_new_param result";
×
137
            assert(false);
×
138
            return Result::Unknown;
139
    }
140
}
72✔
141

142
MavlinkParameterServer::Result
143
MavlinkParameterServer::provide_server_param_float(const std::string& name, float value)
11✔
144
{
145
    ParamValue param_value;
11✔
146
    param_value.set(value);
11✔
147
    return provide_server_param(name, param_value);
11✔
148
}
11✔
149

150
MavlinkParameterServer::Result
151
MavlinkParameterServer::provide_server_param_int(const std::string& name, int32_t value)
53✔
152
{
153
    ParamValue param_value;
53✔
154
    param_value.set(value);
53✔
155
    return provide_server_param(name, param_value);
53✔
156
}
53✔
157

158
MavlinkParameterServer::Result MavlinkParameterServer::provide_server_param_custom(
8✔
159
    const std::string& name, const std::string& value)
160
{
161
    ParamValue param_value;
8✔
162
    param_value.set(value);
8✔
163
    return provide_server_param(name, param_value);
8✔
164
}
8✔
165

166
std::map<std::string, ParamValue> MavlinkParameterServer::retrieve_all_server_params()
2✔
167
{
168
    std::lock_guard<std::mutex> lock(_all_params_mutex);
2✔
169
    std::map<std::string, ParamValue> map_copy;
2✔
170
    for (auto entry : _param_cache.all_parameters(true)) {
6✔
171
        map_copy[entry.id] = entry.value;
4✔
172
    }
6✔
173
    return map_copy;
2✔
174
}
2✔
175

176
template<class T>
177
std::pair<MavlinkParameterServer::Result, T>
178
MavlinkParameterServer::retrieve_server_param(const std::string& name)
6✔
179
{
180
    std::lock_guard<std::mutex> lock(_all_params_mutex);
6✔
181
    const auto param_opt = _param_cache.param_by_id(name, true);
6✔
182
    if (!param_opt.has_value()) {
6✔
183
        return {Result::NotFound, {}};
×
184
    }
185
    // This parameter exists, check its type
186
    const auto& param = param_opt.value();
6✔
187
    if (param.value.is<T>()) {
6✔
188
        return {Result::Ok, param.value.get<T>()};
6✔
189
    }
190
    return {Result::WrongType, {}};
×
191
}
6✔
192

193
std::pair<MavlinkParameterServer::Result, float>
194
MavlinkParameterServer::retrieve_server_param_float(const std::string& name)
2✔
195
{
196
    return retrieve_server_param<float>(name);
2✔
197
}
198

199
std::pair<MavlinkParameterServer::Result, std::string>
200
MavlinkParameterServer::retrieve_server_param_custom(const std::string& name)
2✔
201
{
202
    return retrieve_server_param<std::string>(name);
2✔
203
}
204

205
std::pair<MavlinkParameterServer::Result, int32_t>
206
MavlinkParameterServer::retrieve_server_param_int(const std::string& name)
2✔
207
{
208
    return retrieve_server_param<int32_t>(name);
2✔
209
}
210

211
void MavlinkParameterServer::process_param_set_internally(
11✔
212
    const std::string& param_id, const ParamValue& value_to_set, bool extended)
213
{
214
    if (_parameter_debugging) {
11✔
215
        LogDebug() << "Param set request" << (extended ? " extended" : "") << ": " << param_id
×
216
                   << " with " << value_to_set;
×
217
    }
218

219
    std::string error_param_id;
11✔
220
    int16_t error_param_index = -1;
11✔
221
    uint8_t error_code = 0;
11✔
222
    bool send_error = false;
11✔
223

224
    bool notify_value_changed = false;
11✔
225
    std::string changed_param_id;
11✔
226
    ParamValue changed_param_value;
11✔
227

228
    {
229
        std::lock_guard<std::mutex> lock(_all_params_mutex);
11✔
230
        mark_protocol_seen(extended);
11✔
231
        // for checking if the update actually changed the value
232
        const auto opt_before_update = _param_cache.param_by_id(param_id, extended);
11✔
233
        const auto result = _param_cache.update_existing_param(param_id, value_to_set);
11✔
234
        const auto param_count = _param_cache.count(extended);
11✔
235

236
        switch (result) {
11✔
237
            case MavlinkParameterCache::UpdateExistingParamResult::MissingParam: {
×
238
                // We do not allow clients to add a new parameter to the parameter set, only to
239
                // update existing parameters. Send PARAM_ERROR for standard protocol.
240
                LogErr() << "Got param_set for non-existing parameter:" << param_id;
×
241

242
                if (!extended) {
×
243
                    // Prepare to send PARAM_ERROR outside the lock
244
                    error_param_id = param_id;
×
245
                    error_param_index = -1;
×
246
                    error_code = 1; // MAV_PARAM_ERROR_DOES_NOT_EXIST
×
247
                    send_error = true;
×
248
                }
NEW
249
                break;
×
250
            }
251
            case MavlinkParameterCache::UpdateExistingParamResult::WrongType: {
×
252
                // Non-extended: send PARAM_ERROR with TYPE_MISMATCH
253
                // Extended: we nack with failed.
254

255
                LogErr() << "Got param_set with wrong type for parameter: " << param_id;
×
256

257
                const auto curr_param = _param_cache.param_by_id(param_id, extended).value();
×
258

259
                if (extended) {
×
260
                    auto new_work = std::make_shared<WorkItem>(
261
                        curr_param.id, curr_param.value, WorkItemAck{PARAM_ACK_FAILED});
×
262
                    _work_queue.push_back(new_work);
×
263

264
                } else {
×
265
                    // Prepare to send PARAM_ERROR outside the lock
266
                    error_param_id = curr_param.id;
×
267
                    error_param_index = static_cast<int16_t>(curr_param.index);
×
268
                    error_code = 7; // MAV_PARAM_ERROR_TYPE_MISMATCH
×
269
                    send_error = true;
×
270
                }
NEW
271
                break;
×
272
            }
×
273
            case MavlinkParameterCache::UpdateExistingParamResult::Ok: {
11✔
274
                LogWarn() << "Update existing params!";
11✔
275
                const auto updated_parameter = _param_cache.param_by_id(param_id, extended).value();
11✔
276
                // The param set doesn't differentiate between an update that actually changed the
277
                // value e.g. 0 to 1 and an update that had no effect e.g. 0 to 0.
278
                if (opt_before_update.has_value() &&
22✔
279
                    opt_before_update.value().value == updated_parameter.value) {
11✔
280
                    LogDebug() << "Update had no effect: " << updated_parameter.value;
2✔
281
                } else {
282
                    LogDebug() << "Updated param to :" << updated_parameter.value;
9✔
283
                    // Don't call the (user) subscription callbacks while holding
284
                    // _all_params_mutex: a callback re-entering a server API that takes the
285
                    // same mutex would self-deadlock. Defer until the lock is released.
286
                    notify_value_changed = true;
9✔
287
                    changed_param_id = updated_parameter.id;
9✔
288
                    changed_param_value = updated_parameter.value;
9✔
289
                }
290
                if (extended) {
11✔
291
                    auto new_work = std::make_shared<WorkItem>(
292
                        updated_parameter.id,
293
                        updated_parameter.value,
294
                        WorkItemAck{PARAM_ACK_ACCEPTED});
5✔
295
                    _work_queue.push_back(new_work);
5✔
296
                } else {
5✔
297
                    auto new_work = std::make_shared<WorkItem>(
298
                        updated_parameter.id,
299
                        updated_parameter.value,
300
                        WorkItemValue{updated_parameter.index, param_count, extended});
6✔
301
                    _work_queue.push_back(new_work);
6✔
302
                }
6✔
303
            } break;
11✔
304
        }
305
    }
11✔
306

307
    // Call the (user) subscription callbacks outside the lock if needed.
308
    if (notify_value_changed) {
11✔
309
        find_and_call_subscriptions_value_changed(changed_param_id, changed_param_value);
9✔
310
    }
311

312
    // Send PARAM_ERROR outside the lock if needed
313
    if (send_error) {
11✔
314
        send_param_error(error_param_id, error_param_index, error_code);
×
315
    }
316
}
11✔
317

318
void MavlinkParameterServer::process_param_set(const mavlink_message_t& message)
6✔
319
{
320
    mavlink_param_set_t set_request{};
6✔
321
    mavlink_msg_param_set_decode(&message, &set_request);
6✔
322
    if (!target_matches(set_request.target_system, set_request.target_component, false)) {
6✔
323
        log_target_mismatch(set_request.target_system, set_request.target_component);
×
324
        return;
×
325
    }
326
    const std::string safe_param_id = extract_safe_param_id(set_request.param_id);
6✔
327

328
    if (safe_param_id.empty()) {
6✔
329
        LogWarn() << "Got ill-formed param_set message (param_id empty)";
×
330
        return;
×
331
    }
332

333
    ParamValue value_to_set;
6✔
334
    if (!value_to_set.set_from_mavlink_param_set_bytewise(set_request)) {
6✔
335
        // This should never happen, the type enum in the message is unknown.
336
        LogWarn() << "Invalid Param Set Request: " << safe_param_id;
×
337
        return;
×
338
    }
339
    process_param_set_internally(safe_param_id, value_to_set, false);
6✔
340
}
6✔
341

342
void MavlinkParameterServer::process_param_ext_set(const mavlink_message_t& message)
5✔
343
{
344
    mavlink_param_ext_set_t set_request{};
5✔
345
    mavlink_msg_param_ext_set_decode(&message, &set_request);
5✔
346
    if (!target_matches(set_request.target_system, set_request.target_component, false)) {
5✔
347
        log_target_mismatch(set_request.target_system, set_request.target_component);
×
348
        return;
×
349
    }
350
    const std::string safe_param_id = extract_safe_param_id(set_request.param_id);
5✔
351

352
    if (safe_param_id.empty()) {
5✔
353
        LogWarn() << "Got ill-formed param_ext_set message (param_id empty)";
×
354
        return;
×
355
    }
356

357
    ParamValue value_to_set;
5✔
358
    if (!value_to_set.set_from_mavlink_param_ext_set(set_request)) {
5✔
359
        // This should never happen, the type enum in the message is unknown.
360
        LogWarn() << "Invalid Param Set ext Request: " << safe_param_id;
×
361
        return;
×
362
    }
363

364
    process_param_set_internally(safe_param_id, value_to_set, true);
5✔
365
}
5✔
366

367
void MavlinkParameterServer::process_param_request_read(const mavlink_message_t& message)
22✔
368
{
369
    if (_parameter_debugging) {
22✔
370
        LogDebug() << "process param_request_read";
×
371
    }
372
    mavlink_param_request_read_t read_request{};
22✔
373
    mavlink_msg_param_request_read_decode(&message, &read_request);
22✔
374
    if (!target_matches(read_request.target_system, read_request.target_component, true)) {
22✔
375
        log_target_mismatch(read_request.target_system, read_request.target_component);
×
376
        return;
×
377
    }
378

379
    const auto param_id_or_index =
380
        extract_request_read_param_identifier(read_request.param_index, read_request.param_id);
22✔
381

382
    std::visit(
22✔
383
        overloaded{
22✔
384
            [&](std::monostate) { LogWarn() << "Ill-formed param_request_read message"; },
×
385
            [&](std::uint16_t index) {
1✔
386
                if (_parameter_debugging) {
1✔
387
                    LogDebug() << "found index: " << index;
×
388
                }
389
                internal_process_param_request_read_by_index(index, false);
1✔
390
            },
1✔
391
            [&](const std::string& id) {
21✔
392
                if (_parameter_debugging) {
21✔
393
                    LogDebug() << "found id: " << id;
×
394
                }
395
                internal_process_param_request_read_by_id(id, false);
21✔
396
            }},
21✔
397
        param_id_or_index);
398
}
22✔
399

400
void MavlinkParameterServer::process_param_ext_request_read(const mavlink_message_t& message)
49✔
401
{
402
    mavlink_param_ext_request_read_t read_request{};
49✔
403
    mavlink_msg_param_ext_request_read_decode(&message, &read_request);
49✔
404
    if (!target_matches(read_request.target_system, read_request.target_component, true)) {
49✔
405
        log_target_mismatch(read_request.target_system, read_request.target_component);
×
406
        return;
×
407
    }
408
    const auto param_id_or_index =
409
        extract_request_read_param_identifier(read_request.param_index, read_request.param_id);
49✔
410

411
    std::visit(
49✔
412
        overloaded{
49✔
413
            [&](std::monostate) { LogWarn() << "Ill-formed param_request_read message"; },
×
414
            [&](std::uint16_t index) {
2✔
415
                if (_parameter_debugging) {
2✔
416
                    LogDebug() << "found index: " << index;
×
417
                }
418
                internal_process_param_request_read_by_index(index, true);
2✔
419
            },
2✔
420
            [&](const std::string& id) {
47✔
421
                if (_parameter_debugging) {
47✔
422
                    LogDebug() << "found id: " << id;
×
423
                }
424
                internal_process_param_request_read_by_id(id, true);
47✔
425
            }},
47✔
426
        param_id_or_index);
427
}
49✔
428

429
void MavlinkParameterServer::internal_process_param_request_read_by_id(
68✔
430
    const std::string& id, const bool extended)
431
{
432
    {
433
        std::lock_guard<std::mutex> lock(_all_params_mutex);
68✔
434
        mark_protocol_seen(extended);
68✔
435
        const auto param_opt = _param_cache.param_by_id(id, extended);
68✔
436

437
        if (!param_opt.has_value()) {
68✔
438
            LogWarn() << "Ignoring request_read message " << (extended ? "extended " : "")
12✔
439
                      << "- param name not found: " << id;
6✔
440
            // Release lock before sending PARAM_ERROR
441
        } else {
442
            const auto& param = param_opt.value();
62✔
443
            const auto param_count = _param_cache.count(extended);
62✔
444
            assert(param.index < param_count);
62✔
445
            auto new_work = std::make_shared<WorkItem>(
446
                param.id, param.value, WorkItemValue{param.index, param_count, extended});
62✔
447
            _work_queue.push_back(new_work);
62✔
448

449
            return;
62✔
450
        }
62✔
451
    }
130✔
452

453
    // Send PARAM_ERROR outside the lock
454
    if (!extended) {
6✔
455
        send_param_error(id, -1, 1); // MAV_PARAM_ERROR_DOES_NOT_EXIST = 1
4✔
456
    }
457
}
458

459
void MavlinkParameterServer::internal_process_param_request_read_by_index(
3✔
460
    std::uint16_t index, bool extended)
461
{
462
    {
463
        std::lock_guard<std::mutex> lock(_all_params_mutex);
3✔
464
        mark_protocol_seen(extended);
3✔
465
        const auto param_opt = _param_cache.param_by_index(index, extended);
3✔
466

467
        if (!param_opt.has_value()) {
3✔
468
            LogWarn() << "Ignoring request_read message " << (extended ? "extended " : "")
×
469
                      << "- param index not found: " << index;
×
470
            // Release lock before sending PARAM_ERROR
471
        } else {
472
            const auto& param = param_opt.value();
3✔
473
            const auto param_count = _param_cache.count(extended);
3✔
474

475
            assert(param.index < param_count);
3✔
476
            auto new_work = std::make_shared<WorkItem>(
477
                param.id, param.value, WorkItemValue{param.index, param_count, extended});
3✔
478
            _work_queue.push_back(new_work);
3✔
479
            return;
3✔
480
        }
3✔
481
    }
6✔
482

483
    // Send PARAM_ERROR outside the lock
484
    if (!extended) {
×
485
        send_param_error("", static_cast<int16_t>(index), 1); // MAV_PARAM_ERROR_DOES_NOT_EXIST = 1
×
486
    }
487
}
488

489
void MavlinkParameterServer::process_param_request_list(const mavlink_message_t& message)
3✔
490
{
491
    mavlink_param_request_list_t list_request{};
3✔
492
    mavlink_msg_param_request_list_decode(&message, &list_request);
3✔
493
    if (!target_matches(list_request.target_system, list_request.target_component, true)) {
3✔
494
        log_target_mismatch(list_request.target_system, list_request.target_component);
×
495
        return;
×
496
    }
497
    broadcast_all_parameters(false);
3✔
498
}
499

500
void MavlinkParameterServer::process_param_ext_request_list(const mavlink_message_t& message)
2✔
501
{
502
    if (_parameter_debugging) {
2✔
503
        LogDebug() << "process param_ext_request_list";
×
504
    }
505

506
    mavlink_param_ext_request_list_t ext_list_request{};
2✔
507
    mavlink_msg_param_ext_request_list_decode(&message, &ext_list_request);
2✔
508
    if (!target_matches(ext_list_request.target_system, ext_list_request.target_component, true)) {
2✔
509
        log_target_mismatch(ext_list_request.target_system, ext_list_request.target_component);
×
510
        return;
×
511
    }
512
    broadcast_all_parameters(true);
2✔
513
}
514

515
void MavlinkParameterServer::broadcast_all_parameters(const bool extended)
5✔
516
{
517
    std::lock_guard<std::mutex> lock(_all_params_mutex);
5✔
518
    mark_protocol_seen(extended);
5✔
519

520
    // Param used with index, we should no longer change the index
521
    _params_locked_down = true;
5✔
522

523
    const auto all_params = _param_cache.all_parameters(extended);
5✔
524
    if (_parameter_debugging) {
5✔
525
        LogDebug() << "broadcast_all_parameters " << (extended ? "extended" : "") << ": "
×
526
                   << all_params.size();
×
527
    }
528
    for (const auto& parameter : all_params) {
37✔
529
        if (_parameter_debugging) {
32✔
530
            LogDebug() << "sending param:" << parameter.id;
×
531
        }
532
        auto new_work = std::make_shared<WorkItem>(
533
            parameter.id,
32✔
534
            parameter.value,
32✔
535
            WorkItemValue{parameter.index, static_cast<uint16_t>(all_params.size()), extended});
32✔
536
        _work_queue.push_back(new_work);
32✔
537
    }
32✔
538
}
5✔
539

540
void MavlinkParameterServer::do_work()
44,028✔
541
{
542
    LockedQueue<WorkItem>::Guard work_queue_guard(_work_queue);
44,028✔
543
    auto work = work_queue_guard.get_front();
43,408✔
544
    if (!work) {
43,084✔
545
        return;
42,925✔
546
    }
547
    const auto param_id_message_buffer = param_id_to_message_buffer(work->param_id);
127✔
548

549
    std::visit(
127✔
550
        overloaded{
254✔
551
            [&](const WorkItemValue& specific) {
122✔
552
                if (specific.extended) {
122✔
553
                    const auto buf = work->param_value.get_128_bytes();
80✔
554
                    if (!_sender.queue_message(
80✔
555
                            [&](MavlinkAddress mavlink_address, uint8_t channel) {
80✔
556
                                mavlink_message_t message;
557
                                mavlink_msg_param_ext_value_pack_chan(
320✔
558
                                    mavlink_address.system_id,
80✔
559
                                    mavlink_address.component_id,
80✔
560
                                    channel,
561
                                    &message,
562
                                    param_id_message_buffer.data(),
160✔
563
                                    buf.data(),
80✔
564
                                    work->param_value.get_mav_param_ext_type(),
80✔
565
                                    specific.param_count,
80✔
566
                                    specific.param_index);
80✔
567
                                return message;
80✔
568
                            })) {
569
                        LogErr() << "Error: Send message failed";
×
570
                        work_queue_guard.pop_front();
×
571
                        return;
×
572
                    }
573
                } else {
574
                    LogWarn() << "sending not extended message";
42✔
575
                    float param_value;
576
                    if (_sender.compatibility_mode() == CompatibilityMode::ArduPilot) {
42✔
577
                        param_value = work->param_value.get_4_float_bytes_cast();
×
578
                    } else {
579
                        param_value = work->param_value.get_4_float_bytes_bytewise();
42✔
580
                    }
581
                    if (!_sender.queue_message(
42✔
582
                            [&](MavlinkAddress mavlink_address, uint8_t channel) {
42✔
583
                                mavlink_message_t message;
584
                                mavlink_msg_param_value_pack_chan(
126✔
585
                                    mavlink_address.system_id,
42✔
586
                                    mavlink_address.component_id,
42✔
587
                                    channel,
588
                                    &message,
589
                                    param_id_message_buffer.data(),
42✔
590
                                    param_value,
42✔
591
                                    work->param_value.get_mav_param_type(),
42✔
592
                                    specific.param_count,
42✔
593
                                    specific.param_index);
42✔
594
                                return message;
42✔
595
                            })) {
596
                        LogErr() << "Error: Send message failed";
×
597
                        work_queue_guard.pop_front();
×
598
                        return;
×
599
                    }
600
                }
601
                work_queue_guard.pop_front();
122✔
602
            },
603
            [&](const WorkItemAck& specific) {
5✔
604
                auto buf = work->param_value.get_128_bytes();
5✔
605
                if (!_sender.queue_message([&](MavlinkAddress mavlink_address, uint8_t channel) {
5✔
606
                        mavlink_message_t message;
607
                        mavlink_msg_param_ext_ack_pack_chan(
20✔
608
                            mavlink_address.system_id,
5✔
609
                            mavlink_address.component_id,
5✔
610
                            channel,
611
                            &message,
612
                            param_id_message_buffer.data(),
10✔
613
                            buf.data(),
5✔
614
                            work->param_value.get_mav_param_ext_type(),
5✔
615
                            specific.param_ack);
5✔
616
                        return message;
5✔
617
                    })) {
618
                    LogErr() << "Error: Send message failed";
×
619
                    work_queue_guard.pop_front();
×
620
                    return;
×
621
                }
622
                work_queue_guard.pop_front();
5✔
623
            }},
624
        work->work_item_variant);
127✔
625
}
85,604✔
626

627
void MavlinkParameterServer::send_param_error(
4✔
628
    const std::string& param_id, int16_t param_index, uint8_t error_code)
629
{
630
    if (_parameter_debugging) {
4✔
631
        LogDebug() << "Sending PARAM_ERROR for " << param_id << " (index: " << param_index
×
632
                   << ") with error code: " << (int)error_code;
×
633
    }
634

635
    const auto param_id_buffer = param_id_to_message_buffer(param_id);
4✔
636

637
    if (!_sender.queue_message([&](MavlinkAddress mavlink_address, uint8_t channel) {
4✔
638
            mavlink_message_t message;
639
            mavlink_msg_param_error_pack_chan(
8✔
640
                mavlink_address.system_id,
4✔
641
                mavlink_address.component_id,
4✔
642
                channel,
643
                &message,
644
                0, // target_system - 0 for broadcast
645
                0, // target_component - 0 for broadcast
646
                param_id_buffer.data(),
4✔
647
                param_index,
4✔
648
                error_code);
4✔
649
            return message;
4✔
650
        })) {
651
        LogErr() << "Error: Send PARAM_ERROR message failed";
×
652
    }
653
}
4✔
654

655
void MavlinkParameterServer::mark_protocol_seen(bool extended)
87✔
656
{
657
    if (extended) {
87✔
658
        _seen_extended = true;
56✔
659
    } else {
660
        _seen_non_extended = true;
31✔
661
    }
662
}
87✔
663

664
void MavlinkParameterServer::enqueue_value_broadcast(
19✔
665
    const std::string& name, const ParamValue& param_value, bool extended)
666
{
667
    auto new_work = std::make_shared<WorkItem>(
668
        name,
669
        param_value,
670
        WorkItemValue{
38✔
671
            std::numeric_limits<std::uint16_t>::max(),
672
            std::numeric_limits<std::uint16_t>::max(),
673
            extended});
19✔
674
    _work_queue.push_back(new_work);
19✔
675
}
19✔
676

677
std::ostream& operator<<(std::ostream& str, const MavlinkParameterServer::Result& result)
×
678
{
679
    switch (result) {
×
680
        case MavlinkParameterServer::Result::Ok:
×
681
            return str << "Ok";
×
682
        case MavlinkParameterServer::Result::OkExistsAlready:
×
683
            return str << "OkExistsAlready";
×
684
        case MavlinkParameterServer::Result::WrongType:
×
685
            return str << "WrongType";
×
686
        case MavlinkParameterServer::Result::ParamNameTooLong:
×
687
            return str << "ParamNameTooLong";
×
688
        case MavlinkParameterServer::Result::NotFound:
×
689
            return str << "NotFound";
×
690
        case MavlinkParameterServer::Result::ParamValueTooLong:
×
691
            return str << ":ParamValueTooLong";
×
NEW
692
        case MavlinkParameterServer::Result::ParamProvidedTooLate:
×
NEW
693
            return str << "ParamProvidedTooLate";
×
694
        default:
×
695
            return str << "UnknownError";
×
696
    }
697
}
698

699
bool MavlinkParameterServer::target_matches(
87✔
700
    const uint16_t target_sys_id, const uint16_t target_comp_id, bool is_request)
701
{
702
    // See: https://mavlink.io/en/services/parameter.html#multi-system-and-multi-component-support
703

704
    if (target_sys_id != _sender.get_own_system_id()) {
87✔
705
        return false;
×
706
    }
707
    if (is_request) {
87✔
708
        return target_comp_id == _sender.get_own_component_id() ||
76✔
709
               target_comp_id == MAV_COMP_ID_ALL;
76✔
710
    }
711
    return target_comp_id == _sender.get_own_component_id();
11✔
712
}
713

714
void MavlinkParameterServer::log_target_mismatch(uint16_t target_sys_id, uint16_t target_comp_id)
×
715
{
716
    if (!_parameter_debugging) {
×
717
        return;
×
718
    }
719

720
    LogDebug() << "Ignoring message - wrong target id. Got:" << (int)target_sys_id << ":"
×
721
               << (int)target_comp_id << " Wanted:" << (int)_sender.get_own_system_id() << ":"
×
722
               << (int)_sender.get_own_component_id();
×
723
}
724

725
std::variant<std::monostate, std::string, std::uint16_t>
726
MavlinkParameterServer::extract_request_read_param_identifier(
71✔
727
    int16_t param_index, const char* param_id)
728
{
729
    // Helper for safely handling a request_read or ext_request_read message (which have the exact
730
    // same layout). returns the identifier that should be used or nothing if the message is
731
    // ill-formed. See https://mavlink.io/en/messages/common.html#PARAM_REQUEST_READ and
732
    // https://mavlink.io/en/messages/common.html#PARAM_EXT_REQUEST_READ
733

734
    if (param_index == -1) {
71✔
735
        // use param_id if index == -1
736
        const auto safe_param_id = extract_safe_param_id(param_id);
68✔
737
        if (safe_param_id.empty()) {
68✔
738
            LogErr() << "Message with param_index=-1 but no empty param id";
×
739
            return std::monostate{};
×
740
        }
741
        return {safe_param_id};
68✔
742
    } else {
68✔
743
        // if index is not -1, it should be a valid parameter index (>=0)
744
        if (param_index < 0) {
3✔
745
            LogErr() << "Param_index " << param_index << " is not a valid param index";
×
746
            return std::monostate{};
×
747
        }
748
        return {static_cast<std::uint16_t>(param_index)};
3✔
749
    }
750
}
751

752
bool MavlinkParameterServer::params_locked_down() const
×
753
{
754
    std::lock_guard<std::mutex> lock(_all_params_mutex);
×
755

756
    return _params_locked_down;
×
757
}
×
758

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