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

mavlink / MAVSDK / 6568658631

19 Oct 2023 01:31AM UTC coverage: 31.23% (+0.02%) from 31.215%
6568658631

push

github

web-flow
Merge pull request #2155 from mavlink/pr-static-fixes

Threading fixes, MAVLink sequence number cleanup

1386 of 1386 new or added lines in 46 files covered. (100.0%)

7906 of 25315 relevant lines covered (31.23%)

23.54 hits per line

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

0.0
/src/mavsdk/plugins/mission/mission_impl.cpp
1
#include "mission_impl.h"
2
#include "system.h"
3
#include "unused.h"
4
#include "callback_list.tpp"
5
#include <algorithm>
6
#include <cmath>
7

8
namespace mavsdk {
9

10
template class CallbackList<Mission::MissionProgress>;
11

12
using MissionItem = Mission::MissionItem;
13
using CameraAction = Mission::MissionItem::CameraAction;
14
using VehicleAction = Mission::MissionItem::VehicleAction;
15

16
MissionImpl::MissionImpl(System& system) : PluginImplBase(system)
×
17
{
18
    _system_impl->register_plugin(this);
×
19
}
×
20

21
MissionImpl::MissionImpl(std::shared_ptr<System> system) : PluginImplBase(std::move(system))
×
22
{
23
    _system_impl->register_plugin(this);
×
24
}
×
25

26
MissionImpl::~MissionImpl()
×
27
{
28
    _system_impl->unregister_plugin(this);
×
29
}
×
30

31
void MissionImpl::init()
×
32
{
33
    _system_impl->register_mavlink_message_handler(
×
34
        MAVLINK_MSG_ID_MISSION_CURRENT,
35
        [this](const mavlink_message_t& message) { process_mission_current(message); },
×
36
        this);
37

38
    _system_impl->register_mavlink_message_handler(
×
39
        MAVLINK_MSG_ID_MISSION_ITEM_REACHED,
40
        [this](const mavlink_message_t& message) { process_mission_item_reached(message); },
×
41
        this);
42

43
    _system_impl->register_mavlink_message_handler(
×
44
        MAVLINK_MSG_ID_GIMBAL_MANAGER_INFORMATION,
45
        [this](const mavlink_message_t& message) { process_gimbal_manager_information(message); },
×
46
        this);
47
}
×
48

49
void MissionImpl::enable()
×
50
{
51
    _system_impl->register_timeout_handler(
×
52
        [this]() { receive_protocol_timeout(); }, 1.0, &_gimbal_protocol_cookie);
×
53

54
    MavlinkCommandSender::CommandLong command{};
×
55
    command.command = MAV_CMD_REQUEST_MESSAGE;
×
56
    command.params.maybe_param1 = static_cast<float>(MAVLINK_MSG_ID_GIMBAL_MANAGER_INFORMATION);
×
57
    command.target_component_id = 0; // any component
×
58
    _system_impl->send_command_async(command, nullptr);
×
59
}
×
60

61
void MissionImpl::disable()
×
62
{
63
    reset_mission_progress();
×
64
    _gimbal_protocol = GimbalProtocol::Unknown;
×
65
}
×
66

67
void MissionImpl::deinit()
×
68
{
69
    _system_impl->unregister_timeout_handler(_gimbal_protocol_cookie);
×
70
    _system_impl->unregister_timeout_handler(_timeout_cookie);
×
71
    _system_impl->unregister_all_mavlink_message_handlers(this);
×
72
}
×
73

74
void MissionImpl::reset_mission_progress()
×
75
{
76
    std::lock_guard<std::mutex> lock(_mission_data.mutex);
×
77
    _mission_data.last_current_mavlink_mission_item = -1;
×
78
    _mission_data.last_reached_mavlink_mission_item = -1;
×
79
    _mission_data.last_current_reported_mission_item = -1;
×
80
    _mission_data.last_total_reported_mission_item = -1;
×
81
}
×
82

83
void MissionImpl::process_mission_current(const mavlink_message_t& message)
×
84
{
85
    mavlink_mission_current_t mission_current;
×
86
    mavlink_msg_mission_current_decode(&message, &mission_current);
×
87

88
    std::lock_guard<std::mutex> lock(_mission_data.mutex);
×
89
    _mission_data.last_current_mavlink_mission_item = mission_current.seq;
×
90
    report_progress_locked();
×
91
}
×
92

93
void MissionImpl::process_mission_item_reached(const mavlink_message_t& message)
×
94
{
95
    mavlink_mission_item_reached_t mission_item_reached;
×
96
    mavlink_msg_mission_item_reached_decode(&message, &mission_item_reached);
×
97

98
    std::lock_guard<std::mutex> lock(_mission_data.mutex);
×
99
    _mission_data.last_reached_mavlink_mission_item = mission_item_reached.seq;
×
100
    report_progress_locked();
×
101
}
×
102

103
void MissionImpl::process_gimbal_manager_information(const mavlink_message_t& message)
×
104
{
105
    UNUSED(message);
×
106
    if (_gimbal_protocol_cookie != nullptr) {
×
107
        LogDebug() << "Using gimbal protocol v2";
×
108
        _gimbal_protocol = GimbalProtocol::V2;
×
109
        _system_impl->unregister_timeout_handler(_gimbal_protocol_cookie);
×
110
    }
111
}
×
112

113
void MissionImpl::wait_for_protocol()
×
114
{
115
    while (_gimbal_protocol == GimbalProtocol::Unknown) {
×
116
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
×
117
    }
118
}
×
119

120
void MissionImpl::wait_for_protocol_async(std::function<void()> callback)
×
121
{
122
    wait_for_protocol();
×
123
    callback();
×
124
}
×
125

126
void MissionImpl::receive_protocol_timeout()
×
127
{
128
    LogDebug() << "Falling back to gimbal protocol v1";
×
129
    _gimbal_protocol = GimbalProtocol::V1;
×
130
    _gimbal_protocol_cookie = nullptr;
×
131
}
×
132

133
Mission::Result MissionImpl::upload_mission(const Mission::MissionPlan& mission_plan)
×
134
{
135
    auto prom = std::promise<Mission::Result>();
×
136
    auto fut = prom.get_future();
×
137

138
    upload_mission_async(mission_plan, [&prom](Mission::Result result) { prom.set_value(result); });
×
139
    return fut.get();
×
140
}
141

142
void MissionImpl::upload_mission_async(
×
143
    const Mission::MissionPlan& mission_plan, const Mission::ResultCallback& callback)
144
{
145
    if (_mission_data.last_upload.lock()) {
×
146
        _system_impl->call_user_callback([callback]() {
×
147
            if (callback) {
148
                callback(Mission::Result::Busy);
149
            }
150
        });
151
        return;
×
152
    }
153

154
    reset_mission_progress();
×
155

156
    wait_for_protocol_async([callback, mission_plan, this]() {
×
157
        const auto int_items = convert_to_int_items(mission_plan.mission_items);
×
158

159
        _mission_data.last_upload = _system_impl->mission_transfer().upload_items_async(
×
160
            MAV_MISSION_TYPE_MISSION,
161
            _system_impl->get_system_id(),
×
162
            int_items,
163
            [this, callback](MavlinkMissionTransfer::Result result) {
×
164
                auto converted_result = convert_result(result);
×
165
                _system_impl->call_user_callback([callback, converted_result]() {
×
166
                    if (callback) {
167
                        callback(converted_result);
168
                    }
169
                });
170
            });
×
171
    });
×
172
}
173

174
void MissionImpl::upload_mission_with_progress_async(
×
175
    const Mission::MissionPlan& mission_plan,
176
    const Mission::UploadMissionWithProgressCallback callback)
177
{
178
    if (_mission_data.last_upload.lock()) {
×
179
        _system_impl->call_user_callback([callback]() {
×
180
            if (callback) {
181
                callback(Mission::Result::Busy, Mission::ProgressData{});
182
            }
183
        });
184
        return;
×
185
    }
186

187
    reset_mission_progress();
×
188

189
    wait_for_protocol_async([callback, mission_plan, this]() {
×
190
        const auto int_items = convert_to_int_items(mission_plan.mission_items);
×
191

192
        _mission_data.last_upload = _system_impl->mission_transfer().upload_items_async(
×
193
            MAV_MISSION_TYPE_MISSION,
194
            _system_impl->get_system_id(),
×
195
            int_items,
196
            [this, callback](MavlinkMissionTransfer::Result result) {
×
197
                auto converted_result = convert_result(result);
×
198
                _system_impl->call_user_callback([callback, converted_result]() {
×
199
                    if (callback) {
200
                        callback(converted_result, Mission::ProgressData{});
201
                    }
202
                });
203
            },
×
204
            [this, callback](float progress) {
×
205
                _system_impl->call_user_callback([callback, progress]() {
×
206
                    if (callback) {
207
                        callback(Mission::Result::Next, Mission::ProgressData{progress});
208
                    }
209
                });
210
            });
×
211
    });
×
212
}
213

214
Mission::Result MissionImpl::cancel_mission_upload() const
×
215
{
216
    auto ptr = _mission_data.last_upload.lock();
×
217
    if (ptr) {
×
218
        ptr->cancel();
×
219
    } else {
220
        LogWarn() << "No mission upload to cancel... ignoring";
×
221
    }
222

223
    return Mission::Result::Success;
×
224
}
225

226
std::pair<Mission::Result, Mission::MissionPlan> MissionImpl::download_mission()
×
227
{
228
    auto prom = std::promise<std::pair<Mission::Result, Mission::MissionPlan>>();
×
229
    auto fut = prom.get_future();
×
230

231
    download_mission_async(
×
232
        [&prom](Mission::Result result, const Mission::MissionPlan& mission_plan) {
×
233
            prom.set_value(std::make_pair<>(result, mission_plan));
×
234
        });
×
235
    return fut.get();
×
236
}
237

238
void MissionImpl::download_mission_async(const Mission::DownloadMissionCallback& callback)
×
239
{
240
    if (_mission_data.last_download.lock()) {
×
241
        _system_impl->call_user_callback([callback]() {
×
242
            if (callback) {
243
                Mission::MissionPlan mission_plan{};
244
                callback(Mission::Result::Busy, mission_plan);
245
            }
246
        });
247
        return;
×
248
    }
249

250
    _mission_data.last_download = _system_impl->mission_transfer().download_items_async(
×
251
        MAV_MISSION_TYPE_MISSION,
252
        _system_impl->get_system_id(),
×
253
        [this, callback](
×
254
            MavlinkMissionTransfer::Result result,
255
            std::vector<MavlinkMissionTransfer::ItemInt> items) {
×
256
            auto result_and_items = convert_to_result_and_mission_items(result, items);
×
257
            _system_impl->call_user_callback([callback, result_and_items]() {
×
258
                callback(result_and_items.first, result_and_items.second);
259
            });
260
        });
×
261
}
262

263
void MissionImpl::download_mission_with_progress_async(
×
264
    const Mission::DownloadMissionWithProgressCallback callback)
265
{
266
    if (_mission_data.last_download.lock()) {
×
267
        _system_impl->call_user_callback([callback]() {
×
268
            if (callback) {
269
                Mission::ProgressDataOrMission progress_data_or_mission{};
270
                progress_data_or_mission.has_mission = false;
271
                progress_data_or_mission.has_progress = false;
272
                callback(Mission::Result::Busy, progress_data_or_mission);
273
            }
274
        });
275
        return;
×
276
    }
277

278
    _mission_data.last_download = _system_impl->mission_transfer().download_items_async(
×
279
        MAV_MISSION_TYPE_MISSION,
280
        _system_impl->get_system_id(),
×
281
        [this, callback](
×
282
            MavlinkMissionTransfer::Result result,
283
            const std::vector<MavlinkMissionTransfer::ItemInt>& items) {
×
284
            auto result_and_items = convert_to_result_and_mission_items(result, items);
×
285
            _system_impl->call_user_callback([callback, result_and_items]() {
×
286
                if (result_and_items.first == Mission::Result::Success) {
287
                    Mission::ProgressDataOrMission progress_data_or_mission{};
288
                    progress_data_or_mission.has_mission = true;
289
                    progress_data_or_mission.mission_plan = result_and_items.second;
290
                    callback(Mission::Result::Next, progress_data_or_mission);
291
                }
292

293
                callback(result_and_items.first, Mission::ProgressDataOrMission{});
294
            });
295
        },
×
296
        [this, callback](float progress) {
×
297
            _system_impl->call_user_callback([callback, progress]() {
×
298
                Mission::ProgressDataOrMission progress_data_or_mission{};
299
                progress_data_or_mission.has_progress = true;
300
                progress_data_or_mission.progress = progress;
301
                callback(Mission::Result::Next, progress_data_or_mission);
302
            });
303
        });
×
304
}
305

306
Mission::Result MissionImpl::cancel_mission_download() const
×
307
{
308
    auto ptr = _mission_data.last_download.lock();
×
309
    if (ptr) {
×
310
        ptr->cancel();
×
311
    } else {
312
        LogWarn() << "No mission download to cancel... ignoring";
×
313
    }
314

315
    return Mission::Result::Success;
×
316
}
317

318
Mission::Result MissionImpl::set_return_to_launch_after_mission(bool enable_rtl)
×
319
{
320
    _enable_return_to_launch_after_mission = enable_rtl;
×
321
    return Mission::Result::Success;
×
322
}
323

324
std::pair<Mission::Result, bool> MissionImpl::get_return_to_launch_after_mission()
×
325
{
326
    return std::make_pair<>(Mission::Result::Success, _enable_return_to_launch_after_mission);
×
327
}
328

329
bool MissionImpl::has_valid_position(const MissionItem& item)
×
330
{
331
    return std::isfinite(item.latitude_deg) && std::isfinite(item.longitude_deg) &&
×
332
           std::isfinite(item.relative_altitude_m);
×
333
}
334

335
float MissionImpl::hold_time(const MissionItem& item)
×
336
{
337
    float hold_time_s;
338
    if (item.is_fly_through) {
×
339
        hold_time_s = 0.0f;
×
340
    } else {
341
        hold_time_s = 0.5f;
×
342
    }
343

344
    return hold_time_s;
×
345
}
346

347
float MissionImpl::acceptance_radius(const MissionItem& item)
×
348
{
349
    float acceptance_radius_m;
350
    if (std::isfinite(item.acceptance_radius_m)) {
×
351
        acceptance_radius_m = item.acceptance_radius_m;
×
352
    } else if (item.is_fly_through) {
×
353
        // _acceptance_radius_m is 0, determine the radius using fly_through
354
        acceptance_radius_m = 3.0f;
×
355
    } else {
356
        // _acceptance_radius_m is 0, determine the radius using fly_through
357
        acceptance_radius_m = 1.0f;
×
358
    }
359

360
    return acceptance_radius_m;
×
361
}
362

363
std::vector<MavlinkMissionTransfer::ItemInt>
364
MissionImpl::convert_to_int_items(const std::vector<MissionItem>& mission_items)
×
365
{
366
    std::vector<MavlinkMissionTransfer::ItemInt> int_items;
×
367

368
    bool last_position_valid = false; // This flag is to protect us from using an invalid x/y.
×
369

370
    unsigned item_i = 0;
×
371
    _mission_data.mavlink_mission_item_to_mission_item_indices.clear();
×
372
    _mission_data.gimbal_v2_in_control = false;
×
373

374
    for (const auto& item : mission_items) {
×
375
        if (item.vehicle_action == VehicleAction::Takeoff) {
×
376
            // There is a vehicle action that we need to send.
377

378
            // Current is the 0th waypoint
379
            uint8_t current = ((int_items.size() == 0) ? 1 : 0);
×
380

381
            uint8_t autocontinue = 1;
×
382

383
            const int32_t x = int32_t(std::round(item.latitude_deg * 1e7));
×
384
            const int32_t y = int32_t(std::round(item.longitude_deg * 1e7));
×
385
            float z = item.relative_altitude_m;
×
386
            MAV_FRAME frame = MAV_FRAME_GLOBAL_RELATIVE_ALT_INT;
×
387

388
            MavlinkMissionTransfer::ItemInt next_item{
×
389
                static_cast<uint16_t>(int_items.size()),
×
390
                (uint8_t)frame,
391
                MAV_CMD_NAV_TAKEOFF,
392
                current,
393
                autocontinue,
394
                NAN,
395
                NAN,
396
                NAN,
397
                NAN,
398
                x,
399
                y,
400
                z,
401
                MAV_MISSION_TYPE_MISSION};
×
402

403
            _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
404
            int_items.push_back(next_item);
×
405
        } else {
406
            if (has_valid_position(item)) {
×
407
                // Current is the 0th waypoint
408
                const uint8_t current = ((int_items.size() == 0) ? 1 : 0);
×
409

410
                const int32_t x = int32_t(std::round(item.latitude_deg * 1e7));
×
411
                const int32_t y = int32_t(std::round(item.longitude_deg * 1e7));
×
412
                const float z = item.relative_altitude_m;
×
413

414
                MavlinkMissionTransfer::ItemInt next_item{
×
415
                    static_cast<uint16_t>(int_items.size()),
×
416
                    static_cast<uint8_t>(MAV_FRAME_GLOBAL_RELATIVE_ALT_INT),
417
                    static_cast<uint8_t>(MAV_CMD_NAV_WAYPOINT),
418
                    current,
419
                    1, // autocontinue
420
                    hold_time(item),
×
421
                    acceptance_radius(item),
×
422
                    0.0f,
423
                    item.yaw_deg,
×
424
                    x,
425
                    y,
426
                    z,
427
                    MAV_MISSION_TYPE_MISSION};
×
428

429
                last_position_valid = true; // because we checked has_valid_position
×
430

431
                _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
432
                int_items.push_back(next_item);
×
433
            }
434
        }
435

436
        if (std::isfinite(item.speed_m_s)) {
×
437
            // The speed has changed, we need to add a speed command.
438

439
            // Current is the 0th waypoint
440
            uint8_t current = ((int_items.size() == 0) ? 1 : 0);
×
441

442
            uint8_t autocontinue = 1;
×
443

444
            MavlinkMissionTransfer::ItemInt next_item{
×
445
                static_cast<uint16_t>(int_items.size()),
×
446
                MAV_FRAME_MISSION,
447
                MAV_CMD_DO_CHANGE_SPEED,
448
                current,
449
                autocontinue,
450
                1.0f, // ground speed
451
                item.speed_m_s,
×
452
                -1.0f, // no throttle change
453
                0.0f, // absolute
454
                0,
455
                0,
456
                NAN,
457
                MAV_MISSION_TYPE_MISSION};
×
458

459
            _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
460
            int_items.push_back(next_item);
×
461
        }
462

463
        if (std::isfinite(item.gimbal_yaw_deg) || std::isfinite(item.gimbal_pitch_deg)) {
×
464
            const auto temp_gimbal_protocol = _gimbal_protocol.load();
×
465
            switch (temp_gimbal_protocol) {
×
466
                case GimbalProtocol::V1:
×
467
                    add_gimbal_items_v1(
×
468
                        int_items, item_i, item.gimbal_pitch_deg, item.gimbal_yaw_deg);
×
469
                    break;
×
470

471
                case GimbalProtocol::V2:
×
472
                    if (!_mission_data.gimbal_v2_in_control) {
×
473
                        acquire_gimbal_control_v2(int_items, item_i);
×
474
                        _mission_data.gimbal_v2_in_control = true;
×
475
                    }
476
                    add_gimbal_items_v2(
×
477
                        int_items, item_i, item.gimbal_pitch_deg, item.gimbal_yaw_deg);
×
478
                    break;
×
479
                case GimbalProtocol::Unknown:
×
480
                    // This should not happen because we wait until we know the protocol version.
481
                    LogErr() << "Unknown gimbal protocol, skipping gimbal commands.";
×
482
                    break;
×
483
            }
484
        }
485

486
        // A loiter time of NAN is ignored but also a loiter time of 0 doesn't
487
        // make any sense and should be discarded.
488
        if (std::isfinite(item.loiter_time_s) && item.loiter_time_s > 0.0f) {
×
489
            if (!last_position_valid) {
×
490
                // In the case where we get a delay without a previous position, we will have to
491
                // ignore it.
492
                LogErr() << "Can't set camera action delay without previous position set.";
×
493

494
            } else {
495
                // Current is the 0th waypoint
496
                uint8_t current = ((int_items.size() == 0) ? 1 : 0);
×
497

498
                uint8_t autocontinue = 1;
×
499

500
                MavlinkMissionTransfer::ItemInt next_item{
×
501
                    static_cast<uint16_t>(int_items.size()),
×
502
                    MAV_FRAME_MISSION,
503
                    MAV_CMD_NAV_DELAY,
504
                    current,
505
                    autocontinue,
506
                    item.loiter_time_s, // loiter time in seconds
×
507
                    -1, // no specified hour
508
                    -1, // no specified minute
509
                    -1, // no specified second
510
                    0,
511
                    0,
512
                    0,
513
                    MAV_MISSION_TYPE_MISSION};
×
514

515
                _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
516
                int_items.push_back(next_item);
×
517
            }
518

519
            if (item.is_fly_through) {
×
520
                LogWarn() << "Conflicting options set: fly_through=true and loiter_time>0.";
×
521
            }
522
        }
523

524
        if (item.camera_action != CameraAction::None) {
×
525
            // Current is the 0th waypoint
526
            uint8_t current = ((int_items.size() == 0) ? 1 : 0);
×
527
            uint8_t autocontinue = 1;
×
528

529
            uint16_t command = 0;
×
530
            float param1 = NAN;
×
531
            float param2 = NAN;
×
532
            float param3 = NAN;
×
533
            float param4 = NAN;
×
534
            switch (item.camera_action) {
×
535
                case CameraAction::TakePhoto:
×
536
                    command = MAV_CMD_IMAGE_START_CAPTURE;
×
537
                    param1 = 0.0f; // all camera IDs
×
538
                    param2 = 0.0f; // no duration, take only one picture
×
539
                    param3 = 1.0f; // only take one picture
×
540
                    param4 = 0.0f; // no capture sequence
×
541
                    break;
×
542
                case CameraAction::StartPhotoInterval:
×
543
                    command = MAV_CMD_IMAGE_START_CAPTURE;
×
544
                    param1 = 0.0f; // all camera IDs
×
545
                    param2 = item.camera_photo_interval_s;
×
546
                    param3 = 0.0f; // unlimited photos
×
547
                    param4 = 0.0f; // no capture sequence
×
548
                    break;
×
549
                case CameraAction::StopPhotoInterval:
×
550
                    command = MAV_CMD_IMAGE_STOP_CAPTURE;
×
551
                    param1 = 0.0f; // all camera IDs
×
552
                    break;
×
553
                case CameraAction::StartVideo:
×
554
                    command = MAV_CMD_VIDEO_START_CAPTURE;
×
555
                    param1 = 0.0f; // all camera IDs
×
556
                    param2 = 0.0f; // no status frequency messages to send
×
557
                    break;
×
558
                case CameraAction::StopVideo:
×
559
                    command = MAV_CMD_VIDEO_STOP_CAPTURE;
×
560
                    param1 = 0.0f; // all camera IDs
×
561
                    break;
×
562
                case CameraAction::StartPhotoDistance:
×
563
                    command = MAV_CMD_DO_SET_CAM_TRIGG_DIST;
×
564
                    if (std::isfinite(item.camera_photo_distance_m)) {
×
565
                        LogErr() << "No photo distance specified";
×
566
                    }
567
                    param1 = item.camera_photo_distance_m; // enable with distance
×
568
                    param2 = 0.0f; // ignore
×
569
                    param3 = 1.0f; // trigger
×
570
                    break;
×
571
                case CameraAction::StopPhotoDistance:
×
572
                    command = MAV_CMD_DO_SET_CAM_TRIGG_DIST;
×
573
                    param1 = 0.0f; // stop
×
574
                    param2 = 0.0f; // ignore
×
575
                    param3 = 0.0f; // no trigger
×
576
                    break;
×
577
                default:
×
578
                    LogErr() << "Camera action not supported";
×
579
                    break;
×
580
            }
581

582
            MavlinkMissionTransfer::ItemInt next_item{
×
583
                static_cast<uint16_t>(int_items.size()),
×
584
                MAV_FRAME_MISSION,
585
                command,
586
                current,
587
                autocontinue,
588
                param1,
589
                param2,
590
                param3,
591
                param4,
592
                0,
593
                0,
594
                NAN,
595
                MAV_MISSION_TYPE_MISSION};
×
596

597
            _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
598
            int_items.push_back(next_item);
×
599
        }
600

601
        if (item.vehicle_action != VehicleAction::None &&
×
602
            item.vehicle_action != VehicleAction::Takeoff) {
×
603
            // There is a vehicle action that we need to send.
604

605
            // Current is the 0th waypoint
606
            uint8_t current = ((int_items.size() == 0) ? 1 : 0);
×
607

608
            uint8_t autocontinue = 1;
×
609

610
            uint16_t command = 0;
×
611
            float param1 = NAN;
×
612
            float param2 = NAN;
×
613
            float param3 = NAN;
×
614
            switch (item.vehicle_action) {
×
615
                case VehicleAction::Land:
×
616
                    command = MAV_CMD_NAV_LAND; // Land at current position with same heading
×
617
                    break;
×
618
                case VehicleAction::TransitionToFw:
×
619
                    command = MAV_CMD_DO_VTOL_TRANSITION; // Do transition
×
620
                    param1 = MAV_VTOL_STATE_FW; // Target state is Fixed-Wing
×
621
                    param2 = 0; // Normal transition
×
622
                    break;
×
623
                case VehicleAction::TransitionToMc:
×
624
                    command = MAV_CMD_DO_VTOL_TRANSITION;
×
625
                    param1 = MAV_VTOL_STATE_MC; // Target state is Multi-Copter
×
626
                    param2 = 0; // Normal transition
×
627
                    break;
×
628
                default:
×
629
                    LogErr() << "Error: vehicle action not supported";
×
630
                    break;
×
631
            }
632

633
            const int32_t x = int32_t(std::round(item.latitude_deg * 1e7));
×
634
            const int32_t y = int32_t(std::round(item.longitude_deg * 1e7));
×
635
            float z = item.relative_altitude_m;
×
636
            MAV_FRAME frame = MAV_FRAME_GLOBAL_RELATIVE_ALT_INT;
×
637

638
            if (command == MAV_CMD_NAV_LAND) {
×
639
                z = 0;
×
640
            }
641

642
            if (command == MAV_CMD_DO_VTOL_TRANSITION) {
×
643
                frame = MAV_FRAME_MISSION;
×
644
            }
645

646
            MavlinkMissionTransfer::ItemInt next_item{
×
647
                static_cast<uint16_t>(int_items.size()),
×
648
                (uint8_t)frame,
649
                command,
650
                current,
651
                autocontinue,
652
                param1,
653
                param2,
654
                param3,
655
                NAN,
656
                x,
657
                y,
658
                z,
659
                MAV_MISSION_TYPE_MISSION};
×
660

661
            _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
662
            int_items.push_back(next_item);
×
663
        }
664

665
        ++item_i;
×
666
    }
667

668
    // We need to decrement the item_i again because it was increased in the loop above
669
    // but the last items below still belong to the last mission item.
670
    --item_i;
×
671

672
    if (_mission_data.gimbal_v2_in_control) {
×
673
        release_gimbal_control_v2(int_items, item_i);
×
674
        _mission_data.gimbal_v2_in_control = false;
×
675
    }
676

677
    if (_enable_return_to_launch_after_mission) {
×
678
        MavlinkMissionTransfer::ItemInt next_item{
×
679
            static_cast<uint16_t>(int_items.size()),
×
680
            MAV_FRAME_MISSION,
681
            MAV_CMD_NAV_RETURN_TO_LAUNCH,
682
            0, // current
683
            1, // autocontinue
684
            NAN, // loiter time in seconds
685
            NAN, // empty
686
            NAN, // radius around waypoint in meters ?
687
            NAN, // loiter at center of waypoint
688
            0,
689
            0,
690
            0,
691
            MAV_MISSION_TYPE_MISSION};
×
692

693
        _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
694
        int_items.push_back(next_item);
×
695
    }
696
    return int_items;
×
697
}
698

699
std::pair<Mission::Result, Mission::MissionPlan> MissionImpl::convert_to_result_and_mission_items(
×
700
    MavlinkMissionTransfer::Result result,
701
    const std::vector<MavlinkMissionTransfer::ItemInt>& int_items)
702
{
703
    std::pair<Mission::Result, Mission::MissionPlan> result_pair;
×
704

705
    result_pair.first = convert_result(result);
×
706
    if (result_pair.first != Mission::Result::Success) {
×
707
        return result_pair;
×
708
    }
709

710
    _mission_data.mavlink_mission_item_to_mission_item_indices.clear();
×
711

712
    Mission::DownloadMissionCallback callback;
×
713
    {
714
        _enable_return_to_launch_after_mission = false;
×
715

716
        MissionItem new_mission_item{};
×
717
        bool have_set_position = false;
×
718

719
        for (const auto& int_item : int_items) {
×
720
            LogDebug() << "Assembling Message: " << int(int_item.seq);
×
721

722
            if (int_item.command == MAV_CMD_NAV_WAYPOINT ||
×
723
                int_item.command == MAV_CMD_NAV_TAKEOFF) {
×
724
                if (int_item.frame != MAV_FRAME_GLOBAL_RELATIVE_ALT_INT) {
×
725
                    LogErr() << "Waypoint frame not supported unsupported";
×
726
                    result_pair.first = Mission::Result::Unsupported;
×
727
                    break;
×
728
                }
729

730
                if (have_set_position) {
×
731
                    // When a new position comes in, create next mission item.
732
                    result_pair.second.mission_items.push_back(new_mission_item);
×
733
                    new_mission_item = {};
×
734
                    have_set_position = false;
×
735
                }
736

737
                new_mission_item.latitude_deg = double(int_item.x) * 1e-7;
×
738
                new_mission_item.longitude_deg = double(int_item.y) * 1e-7;
×
739
                new_mission_item.relative_altitude_m = int_item.z;
×
740
                new_mission_item.yaw_deg = int_item.param4;
×
741

742
                if (int_item.command == MAV_CMD_NAV_TAKEOFF) {
×
743
                    new_mission_item.acceptance_radius_m = 1;
×
744
                    new_mission_item.is_fly_through = false;
×
745
                    new_mission_item.vehicle_action = VehicleAction::Takeoff;
×
746
                } else {
747
                    new_mission_item.acceptance_radius_m = int_item.param2;
×
748
                    new_mission_item.is_fly_through = !(int_item.param1 > 0);
×
749
                }
750

751
                have_set_position = true;
×
752

753
            } else if (int_item.command == MAV_CMD_DO_MOUNT_CONTROL) {
×
754
                if (int(int_item.z) != MAV_MOUNT_MODE_MAVLINK_TARGETING) {
×
755
                    LogErr() << "Gimbal mount control mode unsupported";
×
756
                    result_pair.first = Mission::Result::Unsupported;
×
757
                    break;
×
758
                }
759

760
                new_mission_item.gimbal_pitch_deg = int_item.param1;
×
761
                new_mission_item.gimbal_yaw_deg = int_item.param3;
×
762

763
            } else if (int_item.command == MAV_CMD_DO_GIMBAL_MANAGER_PITCHYAW) {
×
764
                if (int_item.x !=
×
765
                    (GIMBAL_MANAGER_FLAGS_ROLL_LOCK | GIMBAL_MANAGER_FLAGS_PITCH_LOCK)) {
766
                    LogErr() << "Gimbal do pitchyaw flags unsupported";
×
767
                    result_pair.first = Mission::Result::Unsupported;
×
768
                    break;
×
769
                }
770

771
                new_mission_item.gimbal_pitch_deg = int_item.param1;
×
772
                new_mission_item.gimbal_yaw_deg = int_item.param2;
×
773

774
            } else if (int_item.command == MAV_CMD_DO_GIMBAL_MANAGER_CONFIGURE) {
×
775
                // We need to ignore it in order to not throw an "Unsupported" error
776
                continue;
×
777

778
            } else if (int_item.command == MAV_CMD_DO_MOUNT_CONFIGURE) {
×
779
                if (int(int_item.param1) != MAV_MOUNT_MODE_MAVLINK_TARGETING) {
×
780
                    LogErr() << "Gimbal mount configure mode unsupported";
×
781
                    result_pair.first = Mission::Result::Unsupported;
×
782
                    break;
×
783
                }
784

785
                // FIXME: ultimately param4 doesn't count anymore and
786
                //        param7 holds the truth.
787
                if (int(int_item.param4) == 1 || int(int_item.z) == 2) {
×
788
                    _enable_absolute_gimbal_yaw_angle = true;
×
789
                } else {
790
                    _enable_absolute_gimbal_yaw_angle = false;
×
791
                }
792

793
            } else if (int_item.command == MAV_CMD_IMAGE_START_CAPTURE) {
×
794
                if (int_item.param2 > 0 && int(int_item.param3) == 0) {
×
795
                    new_mission_item.camera_action = CameraAction::StartPhotoInterval;
×
796
                    new_mission_item.camera_photo_interval_s = double(int_item.param2);
×
797
                } else if (int(int_item.param2) == 0 && int(int_item.param3) == 1) {
×
798
                    new_mission_item.camera_action = CameraAction::TakePhoto;
×
799
                } else {
800
                    LogErr() << "Mission item START_CAPTURE params unsupported.";
×
801
                    result_pair.first = Mission::Result::Unsupported;
×
802
                    break;
×
803
                }
804

805
            } else if (int_item.command == MAV_CMD_IMAGE_STOP_CAPTURE) {
×
806
                new_mission_item.camera_action = CameraAction::StopPhotoInterval;
×
807

808
            } else if (int_item.command == MAV_CMD_VIDEO_START_CAPTURE) {
×
809
                new_mission_item.camera_action = CameraAction::StartVideo;
×
810

811
            } else if (int_item.command == MAV_CMD_VIDEO_STOP_CAPTURE) {
×
812
                new_mission_item.camera_action = CameraAction::StopVideo;
×
813

814
            } else if (int_item.command == MAV_CMD_DO_SET_CAM_TRIGG_DIST) {
×
815
                if (!std::isfinite(int_item.param1)) {
×
816
                    LogErr() << "Trigger distance in SET_CAM_TRIGG_DIST not finite.";
×
817
                    result_pair.first = Mission::Result::Unsupported;
×
818
                    break;
×
819
                } else if (static_cast<int>(int_item.param1) > 0.0f) {
×
820
                    new_mission_item.camera_action = CameraAction::StartPhotoDistance;
×
821
                    new_mission_item.camera_photo_distance_m = int_item.param1;
×
822
                } else {
823
                    new_mission_item.camera_action = CameraAction::StopPhotoDistance;
×
824
                }
825

826
            } else if (int_item.command == MAV_CMD_NAV_TAKEOFF) {
×
827
                new_mission_item.vehicle_action = VehicleAction::Takeoff;
×
828
            } else if (int_item.command == MAV_CMD_NAV_LAND) {
×
829
                new_mission_item.vehicle_action = VehicleAction::Land;
×
830
            } else if (int_item.command == MAV_CMD_DO_VTOL_TRANSITION) {
×
831
                if (int_item.param1 == MAV_VTOL_STATE_FW) {
×
832
                    new_mission_item.vehicle_action = VehicleAction::TransitionToFw;
×
833
                } else {
834
                    new_mission_item.vehicle_action = VehicleAction::TransitionToMc;
×
835
                }
836

837
            } else if (int_item.command == MAV_CMD_DO_CHANGE_SPEED) {
×
838
                if (int(int_item.param1) == 1 && int_item.param3 < 0 && int(int_item.param4) == 0) {
×
839
                    new_mission_item.speed_m_s = int_item.param2;
×
840
                } else {
841
                    LogErr() << "Mission item DO_CHANGE_SPEED params unsupported";
×
842
                    result_pair.first = Mission::Result::Unsupported;
×
843
                    break;
×
844
                }
845

846
            } else if (int_item.command == MAV_CMD_NAV_LOITER_TIME) {
×
847
                // MAVSDK doesn't use LOITER_TIME anymore, but it is possible
848
                // a mission still uses it
849
                new_mission_item.loiter_time_s = int_item.param1;
×
850

851
            } else if (int_item.command == MAV_CMD_NAV_DELAY) {
×
852
                if (int_item.param1 != -1) {
×
853
                    // use delay in seconds directly
854
                    new_mission_item.loiter_time_s = int_item.param1;
×
855
                } else {
856
                    // TODO: we should support this by converting
857
                    // time of day data to delay in seconds
858
                    // leaving it out for now because a portable implementation
859
                    // is not trivial
860
                    LogErr() << "Mission item NAV_DELAY params unsupported";
×
861
                    result_pair.first = Mission::Result::Unsupported;
×
862
                    break;
×
863
                }
864

865
            } else if (int_item.command == MAV_CMD_NAV_RETURN_TO_LAUNCH) {
×
866
                _enable_return_to_launch_after_mission = true;
×
867

868
            } else {
869
                LogErr() << "UNSUPPORTED mission item command (" << int_item.command << ")";
×
870
                result_pair.first = Mission::Result::Unsupported;
×
871
                break;
×
872
            }
873

874
            _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(
×
875
                result_pair.second.mission_items.size());
×
876
        }
877

878
        // Don't forget to add last mission item.
879
        result_pair.second.mission_items.push_back(new_mission_item);
×
880
    }
881
    return result_pair;
882
}
883

884
Mission::Result MissionImpl::start_mission()
×
885
{
886
    auto prom = std::promise<Mission::Result>();
×
887
    auto fut = prom.get_future();
×
888

889
    start_mission_async([&prom](Mission::Result result) { prom.set_value(result); });
×
890
    return fut.get();
×
891
}
892

893
void MissionImpl::start_mission_async(const Mission::ResultCallback& callback)
×
894
{
895
    _system_impl->set_flight_mode_async(
×
896
        FlightMode::Mission, [this, callback](MavlinkCommandSender::Result result, float) {
×
897
            report_flight_mode_change(callback, result);
×
898
        });
×
899
}
×
900

901
Mission::Result MissionImpl::pause_mission()
×
902
{
903
    auto prom = std::promise<Mission::Result>();
×
904
    auto fut = prom.get_future();
×
905

906
    pause_mission_async([&prom](Mission::Result result) { prom.set_value(result); });
×
907
    return fut.get();
×
908
}
909

910
void MissionImpl::pause_mission_async(const Mission::ResultCallback& callback)
×
911
{
912
    _system_impl->set_flight_mode_async(
×
913
        FlightMode::Hold, [this, callback](MavlinkCommandSender::Result result, float) {
×
914
            report_flight_mode_change(callback, result);
×
915
        });
×
916
}
×
917

918
void MissionImpl::report_flight_mode_change(
×
919
    Mission::ResultCallback callback, MavlinkCommandSender::Result result)
920
{
921
    if (!callback) {
×
922
        return;
×
923
    }
924

925
    _system_impl->call_user_callback(
×
926
        [callback, result]() { callback(command_result_to_mission_result(result)); });
927
}
928

929
Mission::Result MissionImpl::command_result_to_mission_result(MavlinkCommandSender::Result result)
×
930
{
931
    switch (result) {
×
932
        case MavlinkCommandSender::Result::Success:
×
933
            return Mission::Result::Success;
×
934
        case MavlinkCommandSender::Result::NoSystem:
×
935
            return Mission::Result::NoSystem;
×
936
        case MavlinkCommandSender::Result::ConnectionError:
×
937
            return Mission::Result::Error;
×
938
        case MavlinkCommandSender::Result::Busy:
×
939
            return Mission::Result::Busy;
×
940
        case MavlinkCommandSender::Result::TemporarilyRejected:
×
941
            // FALLTHROUGH
942
        case MavlinkCommandSender::Result::Denied:
943
            return Mission::Result::Denied;
×
944
        case MavlinkCommandSender::Result::Timeout:
×
945
            return Mission::Result::Timeout;
×
946
        case MavlinkCommandSender::Result::InProgress:
×
947
            return Mission::Result::Busy;
×
948
        case MavlinkCommandSender::Result::UnknownError:
×
949
            return Mission::Result::Unknown;
×
950
        default:
×
951
            return Mission::Result::Unknown;
×
952
    }
953
}
954

955
Mission::Result MissionImpl::clear_mission()
×
956
{
957
    auto prom = std::promise<Mission::Result>();
×
958
    auto fut = prom.get_future();
×
959

960
    clear_mission_async([&prom](Mission::Result result) { prom.set_value(result); });
×
961
    return fut.get();
×
962
}
963

964
void MissionImpl::clear_mission_async(const Mission::ResultCallback& callback)
×
965
{
966
    reset_mission_progress();
×
967

968
    _system_impl->mission_transfer().clear_items_async(
×
969
        MAV_MISSION_TYPE_MISSION,
970
        _system_impl->get_system_id(),
×
971
        [this, callback](MavlinkMissionTransfer::Result result) {
×
972
            auto converted_result = convert_result(result);
×
973
            _system_impl->call_user_callback([callback, converted_result]() {
×
974
                if (callback) {
975
                    callback(converted_result);
976
                }
977
            });
978
        });
×
979
}
×
980

981
Mission::Result MissionImpl::set_current_mission_item(int current)
×
982
{
983
    auto prom = std::promise<Mission::Result>();
×
984
    auto fut = prom.get_future();
×
985

986
    set_current_mission_item_async(
×
987
        current, [&prom](Mission::Result result) { prom.set_value(result); });
×
988
    return fut.get();
×
989
}
990

991
void MissionImpl::set_current_mission_item_async(
×
992
    int current, const Mission::ResultCallback& callback)
993
{
994
    int mavlink_index = -1;
×
995
    {
996
        std::lock_guard<std::mutex> lock(_mission_data.mutex);
×
997
        // We need to find the first mavlink item which maps to the current mission item.
998
        int i = 0;
×
999
        for (auto index : _mission_data.mavlink_mission_item_to_mission_item_indices) {
×
1000
            if (index == current) {
×
1001
                mavlink_index = i;
×
1002
                break;
×
1003
            }
1004
            ++i;
×
1005
        }
1006
    }
1007

1008
    // If we don't have _mission_data cached from an upload or download,
1009
    // we have to complain. The exception is current set to 0 because it
1010
    // means to reset to the beginning.
1011

1012
    if (mavlink_index == -1 && current != 0) {
×
1013
        _system_impl->call_user_callback([callback]() {
×
1014
            if (callback) {
1015
                // FIXME: come up with better error code.
1016
                callback(Mission::Result::InvalidArgument);
1017
                return;
1018
            }
1019
        });
1020
    }
1021

1022
    _system_impl->mission_transfer().set_current_item_async(
×
1023
        mavlink_index,
1024
        _system_impl->get_system_id(),
×
1025
        [this, callback](MavlinkMissionTransfer::Result result) {
×
1026
            auto converted_result = convert_result(result);
×
1027
            _system_impl->call_user_callback([callback, converted_result]() {
×
1028
                if (callback) {
1029
                    callback(converted_result);
1030
                }
1031
            });
1032
        });
×
1033
}
×
1034

1035
void MissionImpl::report_progress_locked()
×
1036
{
1037
    if (_mission_data.mission_progress_callbacks.empty()) {
×
1038
        return;
×
1039
    }
1040

1041
    int current = current_mission_item_locked();
×
1042
    int total = total_mission_items_locked();
×
1043

1044
    // Do not report -1 as a current mission item
1045
    if (current == -1) {
×
1046
        return;
×
1047
    }
1048

1049
    bool should_report = false;
×
1050
    if (_mission_data.last_current_reported_mission_item != current) {
×
1051
        _mission_data.last_current_reported_mission_item = current;
×
1052
        should_report = true;
×
1053
    }
1054
    if (_mission_data.last_total_reported_mission_item != total) {
×
1055
        _mission_data.last_total_reported_mission_item = total;
×
1056
        should_report = true;
×
1057
    }
1058

1059
    if (should_report) {
×
1060
        _mission_data.mission_progress_callbacks.queue(
×
1061
            {current, total}, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1062
        LogDebug() << "current: " << current << ", total: " << total;
×
1063
    }
1064
}
1065

1066
std::pair<Mission::Result, bool> MissionImpl::is_mission_finished() const
×
1067
{
1068
    std::lock_guard<std::mutex> lock(_mission_data.mutex);
×
1069

1070
    return is_mission_finished_locked();
×
1071
}
1072

1073
std::pair<Mission::Result, bool> MissionImpl::is_mission_finished_locked() const
×
1074
{
1075
    if (_mission_data.last_current_mavlink_mission_item < 0) {
×
1076
        return std::make_pair<Mission::Result, bool>(Mission::Result::Success, false);
×
1077
    }
1078

1079
    if (_mission_data.last_reached_mavlink_mission_item < 0) {
×
1080
        return std::make_pair<Mission::Result, bool>(Mission::Result::Success, false);
×
1081
    }
1082

1083
    if (_mission_data.mavlink_mission_item_to_mission_item_indices.size() == 0) {
×
1084
        return std::make_pair<Mission::Result, bool>(Mission::Result::Success, false);
×
1085
    }
1086

1087
    // It is not straightforward to look at "current" because it jumps to 0
1088
    // once the last item has been done. Therefore we have to lo decide using
1089
    // "reached" here.
1090
    // It seems that we never receive a reached when RTL is initiated after
1091
    // a mission, and we need to account for that.
1092
    const unsigned rtl_correction = _enable_return_to_launch_after_mission ? 2 : 1;
×
1093

1094
    return std::make_pair<Mission::Result, bool>(
1095
        Mission::Result::Success,
×
1096
        unsigned(_mission_data.last_reached_mavlink_mission_item + rtl_correction) ==
×
1097
            _mission_data.mavlink_mission_item_to_mission_item_indices.size());
×
1098
}
1099

1100
int MissionImpl::current_mission_item() const
×
1101
{
1102
    std::lock_guard<std::mutex> lock(_mission_data.mutex);
×
1103
    return current_mission_item_locked();
×
1104
}
1105

1106
int MissionImpl::current_mission_item_locked() const
×
1107
{
1108
    // If the mission is finished, let's return the total as the current
1109
    // to signal this.
1110
    if (is_mission_finished_locked().second) {
×
1111
        return total_mission_items_locked();
×
1112
    }
1113

1114
    // We want to return the current mission item and not the underlying
1115
    // mavlink mission item.
1116
    if (_mission_data.last_current_mavlink_mission_item >=
×
1117
            static_cast<int>(_mission_data.mavlink_mission_item_to_mission_item_indices.size()) ||
×
1118
        _mission_data.last_current_mavlink_mission_item < 0) {
×
1119
        return -1;
×
1120
    }
1121

1122
    return _mission_data.mavlink_mission_item_to_mission_item_indices[static_cast<unsigned>(
×
1123
        _mission_data.last_current_mavlink_mission_item)];
×
1124
}
1125

1126
int MissionImpl::total_mission_items() const
×
1127
{
1128
    std::lock_guard<std::mutex> lock(_mission_data.mutex);
×
1129
    return total_mission_items_locked();
×
1130
}
1131

1132
int MissionImpl::total_mission_items_locked() const
×
1133
{
1134
    if (_mission_data.mavlink_mission_item_to_mission_item_indices.size() == 0) {
×
1135
        return 0;
×
1136
    }
1137
    return _mission_data.mavlink_mission_item_to_mission_item_indices.back() + 1;
×
1138
}
1139

1140
Mission::MissionProgress MissionImpl::mission_progress()
×
1141
{
1142
    Mission::MissionProgress mission_progress;
×
1143
    mission_progress.current = current_mission_item();
×
1144
    mission_progress.total = total_mission_items();
×
1145

1146
    return mission_progress;
×
1147
}
1148

1149
Mission::MissionProgressHandle
1150
MissionImpl::subscribe_mission_progress(const Mission::MissionProgressCallback& callback)
×
1151
{
1152
    std::lock_guard<std::mutex> lock(_mission_data.mutex);
×
1153
    return _mission_data.mission_progress_callbacks.subscribe(callback);
×
1154
}
1155

1156
void MissionImpl::unsubscribe_mission_progress(Mission::MissionProgressHandle handle)
×
1157
{
1158
    std::lock_guard<std::mutex> lock(_mission_data.mutex);
×
1159
    _mission_data.mission_progress_callbacks.unsubscribe(handle);
×
1160
}
×
1161

1162
Mission::Result MissionImpl::convert_result(MavlinkMissionTransfer::Result result)
×
1163
{
1164
    switch (result) {
×
1165
        case MavlinkMissionTransfer::Result::Success:
×
1166
            return Mission::Result::Success;
×
1167
        case MavlinkMissionTransfer::Result::ConnectionError:
×
1168
            return Mission::Result::Error;
×
1169
        case MavlinkMissionTransfer::Result::Denied:
×
1170
            return Mission::Result::Denied;
×
1171
        case MavlinkMissionTransfer::Result::TooManyMissionItems:
×
1172
            return Mission::Result::TooManyMissionItems;
×
1173
        case MavlinkMissionTransfer::Result::Timeout:
×
1174
            return Mission::Result::Timeout;
×
1175
        case MavlinkMissionTransfer::Result::Unsupported:
×
1176
            return Mission::Result::Unsupported;
×
1177
        case MavlinkMissionTransfer::Result::UnsupportedFrame:
×
1178
            return Mission::Result::Unsupported;
×
1179
        case MavlinkMissionTransfer::Result::NoMissionAvailable:
×
1180
            return Mission::Result::NoMissionAvailable;
×
1181
        case MavlinkMissionTransfer::Result::Cancelled:
×
1182
            return Mission::Result::TransferCancelled;
×
1183
        case MavlinkMissionTransfer::Result::MissionTypeNotConsistent:
×
1184
            return Mission::Result::Error; // should not happen
×
1185
        case MavlinkMissionTransfer::Result::InvalidSequence:
×
1186
            return Mission::Result::Error; // should not happen
×
1187
        case MavlinkMissionTransfer::Result::CurrentInvalid:
×
1188
            return Mission::Result::Error; // should not happen
×
1189
        case MavlinkMissionTransfer::Result::ProtocolError:
×
1190
            return Mission::Result::ProtocolError;
×
1191
        case MavlinkMissionTransfer::Result::InvalidParam:
×
1192
            return Mission::Result::InvalidArgument;
×
1193
        case MavlinkMissionTransfer::Result::IntMessagesNotSupported:
×
1194
            return Mission::Result::IntMessagesNotSupported;
×
1195
        default:
×
1196
            return Mission::Result::Unknown;
×
1197
    }
1198
}
1199

1200
void MissionImpl::add_gimbal_items_v1(
×
1201
    std::vector<MavlinkMissionTransfer::ItemInt>& int_items,
1202
    unsigned item_i,
1203
    float pitch_deg,
1204
    float yaw_deg)
1205
{
1206
    if (_enable_absolute_gimbal_yaw_angle) {
×
1207
        // We need to configure the gimbal to use an absolute angle.
1208

1209
        // Current is the 0th waypoint
1210
        uint8_t current = ((int_items.size() == 0) ? 1 : 0);
×
1211

1212
        uint8_t autocontinue = 1;
×
1213

1214
        MavlinkMissionTransfer::ItemInt next_item{
×
1215
            static_cast<uint16_t>(int_items.size()),
×
1216
            MAV_FRAME_MISSION,
1217
            MAV_CMD_DO_MOUNT_CONFIGURE,
1218
            current,
1219
            autocontinue,
1220
            MAV_MOUNT_MODE_MAVLINK_TARGETING,
1221
            0.0f, // stabilize roll
1222
            0.0f, // stabilize pitch
1223
            1.0f, // stabilize yaw, FIXME: for now we use this for an absolute yaw angle,
1224
                  // because it works.
1225
            0,
1226
            0,
1227
            2.0f, // eventually this is the correct flag to set absolute yaw angle.
1228
            MAV_MISSION_TYPE_MISSION};
×
1229

1230
        _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
1231
        int_items.push_back(next_item);
×
1232
    }
1233

1234
    // The gimbal has changed, we need to add a gimbal command.
1235

1236
    // Current is the 0th waypoint
1237
    uint8_t current = ((int_items.size() == 0) ? 1 : 0);
×
1238

1239
    uint8_t autocontinue = 1;
×
1240

1241
    MavlinkMissionTransfer::ItemInt next_item{
×
1242
        static_cast<uint16_t>(int_items.size()),
×
1243
        MAV_FRAME_MISSION,
1244
        MAV_CMD_DO_MOUNT_CONTROL,
1245
        current,
1246
        autocontinue,
1247
        pitch_deg, // pitch
1248
        0.0f, // roll (yes it is a weird order)
1249
        yaw_deg, // yaw
1250
        NAN,
1251
        0,
1252
        0,
1253
        MAV_MOUNT_MODE_MAVLINK_TARGETING,
1254
        MAV_MISSION_TYPE_MISSION};
×
1255

1256
    _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
1257
    int_items.push_back(next_item);
×
1258
}
×
1259

1260
void MissionImpl::add_gimbal_items_v2(
×
1261
    std::vector<MavlinkMissionTransfer::ItemInt>& int_items,
1262
    unsigned item_i,
1263
    float pitch_deg,
1264
    float yaw_deg)
1265
{
1266
    uint8_t current = ((int_items.size() == 0) ? 1 : 0);
×
1267

1268
    uint8_t autocontinue = 1;
×
1269

1270
    // We don't set YAW_LOCK because we probably just want to face forward.
1271
    uint32_t flags = GIMBAL_MANAGER_FLAGS_ROLL_LOCK | GIMBAL_MANAGER_FLAGS_PITCH_LOCK;
×
1272

1273
    // Pitch should be between -180 and 180 degrees
1274
    pitch_deg = fmod(pitch_deg, 360.f);
×
1275
    pitch_deg = pitch_deg > 180.f ? pitch_deg - 360.f : pitch_deg;
×
1276

1277
    // Yaw should be between -180 and 180 degrees
1278
    yaw_deg = fmod(yaw_deg, 360.f);
×
1279
    yaw_deg = yaw_deg > 180.f ? yaw_deg - 360.f : yaw_deg;
×
1280

1281
    MavlinkMissionTransfer::ItemInt next_item{
×
1282
        static_cast<uint16_t>(int_items.size()),
×
1283
        MAV_FRAME_MISSION,
1284
        MAV_CMD_DO_GIMBAL_MANAGER_PITCHYAW,
1285
        current,
1286
        autocontinue,
1287
        pitch_deg, // pitch
1288
        yaw_deg, // yaw
1289
        NAN, // pitch rate
1290
        NAN, // yaw rate
1291
        static_cast<int32_t>(flags),
×
1292
        0, // reserved
1293
        0, // all devices
1294
        MAV_MISSION_TYPE_MISSION};
×
1295

1296
    _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
1297
    int_items.push_back(next_item);
×
1298
}
×
1299

1300
void MissionImpl::acquire_gimbal_control_v2(
×
1301
    std::vector<MavlinkMissionTransfer::ItemInt>& int_items, unsigned item_i)
1302
{
1303
    uint8_t current = ((int_items.size() == 0) ? 1 : 0);
×
1304

1305
    uint8_t autocontinue = 1;
×
1306

1307
    MavlinkMissionTransfer::ItemInt next_item{
×
1308
        static_cast<uint16_t>(int_items.size()),
×
1309
        MAV_FRAME_MISSION,
1310
        MAV_CMD_DO_GIMBAL_MANAGER_CONFIGURE,
1311
        current,
1312
        autocontinue,
1313
        -2.0f, // primary control sysid: set itself (autopilot) when running mission
1314
        -2.0f, // primary control compid: itself (autopilot) when running mission
1315
        -1.0f, // secondary control sysid: unchanged
1316
        -1.0f, // secondary control compid: unchanged
1317
        0, // reserved
1318
        0, // reserved
1319
        0, // all devices
1320
        MAV_MISSION_TYPE_MISSION};
×
1321

1322
    _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
1323
    int_items.push_back(next_item);
×
1324
}
×
1325

1326
void MissionImpl::release_gimbal_control_v2(
×
1327
    std::vector<MavlinkMissionTransfer::ItemInt>& int_items, unsigned item_i)
1328
{
1329
    uint8_t current = ((int_items.size() == 0) ? 1 : 0);
×
1330

1331
    uint8_t autocontinue = 1;
×
1332

1333
    MavlinkMissionTransfer::ItemInt next_item{
×
1334
        static_cast<uint16_t>(int_items.size()),
×
1335
        MAV_FRAME_MISSION,
1336
        MAV_CMD_DO_GIMBAL_MANAGER_CONFIGURE,
1337
        current,
1338
        autocontinue,
1339
        -3.0f, // primary control sysid: remove itself (autopilot) if in control
1340
        -3.0f, // primary control compid: remove itself (autopilot) if in control
1341
        -1.0f, // secondary control sysid: unchanged
1342
        -1.0f, // secondary control compid: unchanged
1343
        0, // reserved
1344
        0, // reserved
1345
        0, // all devices
1346
        MAV_MISSION_TYPE_MISSION};
×
1347

1348
    _mission_data.mavlink_mission_item_to_mission_item_indices.push_back(item_i);
×
1349
    int_items.push_back(next_item);
×
1350
}
×
1351

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