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

mavlink / MAVSDK / 4899817366

pending completion
4899817366

Pull #1772

github

GitHub
Merge c0bb90862 into e4e9c71dd
Pull Request #1772: Refactor MAVLinkParameters into client and server classes

2192 of 2192 new or added lines in 34 files covered. (100.0%)

7708 of 24812 relevant lines covered (31.07%)

19.96 hits per line

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

4.77
/src/mavsdk/plugins/camera/camera_impl.cpp
1
#include "camera_impl.h"
2
#include "camera_definition.h"
3
#include "system.h"
4
#include "mavsdk_math.h"
5
#include "http_loader.h"
6
#include "camera_definition_files.h"
7
#include "unused.h"
8
#include "callback_list.tpp"
9

10
#include <functional>
11
#include <cmath>
12
#include <sstream>
13

14
namespace mavsdk {
15

16
template class CallbackList<Camera::Mode>;
17
template class CallbackList<std::vector<Camera::Setting>>;
18
template class CallbackList<std::vector<Camera::SettingOptions>>;
19
template class CallbackList<Camera::CaptureInfo>;
20
template class CallbackList<Camera::VideoStreamInfo>;
21
template class CallbackList<Camera::Status>;
22

23
CameraImpl::CameraImpl(System& system) : PluginImplBase(system)
×
24
{
25
    _system_impl->register_plugin(this);
×
26
}
×
27

28
CameraImpl::CameraImpl(std::shared_ptr<System> system) : PluginImplBase(std::move(system))
1✔
29
{
30
    _system_impl->register_plugin(this);
1✔
31
}
1✔
32

33
CameraImpl::~CameraImpl()
1✔
34
{
35
    _system_impl->unregister_plugin(this);
1✔
36
}
1✔
37

38
void CameraImpl::init()
1✔
39
{
40
    _system_impl->register_mavlink_message_handler(
2✔
41
        MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS,
42
        _camera_id + MAV_COMP_ID_CAMERA,
1✔
43
        [this](const mavlink_message_t& message) { process_camera_capture_status(message); },
×
44
        this);
45

46
    _system_impl->register_mavlink_message_handler(
2✔
47
        MAVLINK_MSG_ID_STORAGE_INFORMATION,
48
        _camera_id + MAV_COMP_ID_CAMERA,
1✔
49
        [this](const mavlink_message_t& message) { process_storage_information(message); },
×
50
        this);
51

52
    _system_impl->register_mavlink_message_handler(
2✔
53
        MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED,
54
        _camera_id + MAV_COMP_ID_CAMERA,
1✔
55
        [this](const mavlink_message_t& message) { process_camera_image_captured(message); },
×
56
        this);
57

58
    _system_impl->register_mavlink_message_handler(
2✔
59
        MAVLINK_MSG_ID_CAMERA_SETTINGS,
60
        _camera_id + MAV_COMP_ID_CAMERA,
1✔
61
        [this](const mavlink_message_t& message) { process_camera_settings(message); },
×
62
        this);
63

64
    _system_impl->register_mavlink_message_handler(
2✔
65
        MAVLINK_MSG_ID_CAMERA_INFORMATION,
66
        _camera_id + MAV_COMP_ID_CAMERA,
1✔
67
        [this](const mavlink_message_t& message) { process_camera_information(message); },
×
68
        this);
69

70
    _system_impl->register_mavlink_message_handler(
2✔
71
        MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION,
72
        _camera_id + MAV_COMP_ID_CAMERA,
1✔
73
        [this](const mavlink_message_t& message) { process_video_information(message); },
×
74
        this);
75

76
    _system_impl->register_mavlink_message_handler(
2✔
77
        MAVLINK_MSG_ID_VIDEO_STREAM_STATUS,
78
        _camera_id + MAV_COMP_ID_CAMERA,
1✔
79
        [this](const mavlink_message_t& message) { process_video_stream_status(message); },
×
80
        this);
81

82
    if (_system_impl->has_autopilot()) {
1✔
83
        _system_impl->register_mavlink_message_handler(
×
84
            MAVLINK_MSG_ID_FLIGHT_INFORMATION,
85
            [this](const mavlink_message_t& message) { process_flight_information(message); },
×
86
            this);
87
    }
88

89
    _system_impl->add_call_every(
1✔
90
        [this]() { check_connection_status(); }, 0.5, &_check_connection_status_call_every_cookie);
×
91

92
    _system_impl->add_call_every(
1✔
93
        [this]() { request_missing_capture_info(); }, 0.5, &_request_missing_capture_info_cookie);
×
94
}
1✔
95

96
void CameraImpl::deinit()
1✔
97
{
98
    _system_impl->remove_call_every(_request_missing_capture_info_cookie);
1✔
99
    _system_impl->remove_call_every(_check_connection_status_call_every_cookie);
1✔
100
    _system_impl->remove_call_every(_status.call_every_cookie);
1✔
101
    _system_impl->remove_call_every(_camera_information_call_every_cookie);
1✔
102
    _system_impl->remove_call_every(_flight_information_call_every_cookie);
1✔
103
    _system_impl->remove_call_every(_mode.call_every_cookie);
1✔
104
    _system_impl->remove_call_every(_video_stream_info.call_every_cookie);
1✔
105
    _system_impl->unregister_all_mavlink_message_handlers(this);
1✔
106
    _system_impl->cancel_all_param(this);
1✔
107

108
    {
109
        std::lock_guard<std::mutex> lock(_status.mutex);
2✔
110
        _status.subscription_callbacks.clear();
1✔
111
    }
112

113
    {
114
        std::lock_guard<std::mutex> lock(_mode.mutex);
2✔
115
        _mode.subscription_callbacks.clear();
1✔
116
    }
117

118
    {
119
        std::lock_guard<std::mutex> lock(_capture_info.mutex);
2✔
120
        _capture_info.callbacks.clear();
1✔
121
    }
122

123
    {
124
        std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
2✔
125
        _video_stream_info.subscription_callbacks.clear();
1✔
126
    }
127

128
    {
129
        std::lock_guard<std::mutex> lock(_information.mutex);
2✔
130
        _information.subscription_callbacks.clear();
1✔
131
    }
132

133
    {
134
        std::lock_guard<std::mutex> lock(_subscribe_current_settings.mutex);
2✔
135
        _subscribe_current_settings.callbacks.clear();
1✔
136
    }
137

138
    {
139
        std::lock_guard<std::mutex> lock(_subscribe_possible_setting_options.mutex);
2✔
140
        _subscribe_possible_setting_options.callbacks.clear();
1✔
141
    }
142

143
    _camera_found = false;
1✔
144
}
1✔
145

146
Camera::Result CameraImpl::prepare()
×
147
{
148
    auto prom = std::make_shared<std::promise<Camera::Result>>();
×
149
    auto ret = prom->get_future();
×
150

151
    prepare_async([&prom](Camera::Result result) { prom->set_value(result); });
×
152

153
    return ret.get();
×
154
}
155

156
void CameraImpl::prepare_async(const Camera::ResultCallback& callback)
×
157
{
158
    auto temp_callback = callback;
×
159

160
    std::lock_guard<std::mutex> lock(_information.mutex);
×
161

162
    if (_camera_definition) {
×
163
        _system_impl->call_user_callback(
×
164
            [temp_callback]() { temp_callback(Camera::Result::Success); });
165
    } else {
166
        _camera_definition_callback = [this, temp_callback](bool has_succeeded) {
×
167
            if (has_succeeded) {
×
168
                temp_callback(Camera::Result::Success);
×
169
            } else {
170
                temp_callback(Camera::Result::Error);
×
171
            }
172
            _camera_definition_callback = nullptr;
×
173
        };
×
174

175
        if (_has_camera_definition_timed_out) {
×
176
            // Try to download the camera_definition again
177
            _has_camera_definition_timed_out = false;
×
178
            request_camera_information();
×
179
        }
180
    }
181
}
×
182

183
void CameraImpl::check_connection_status()
×
184
{
185
    // FIXME: This is a workaround because we don't want to be tied to the
186
    // discovery of an autopilot which triggers enable() and disable() but
187
    // we are interested if a camera is connected or not.
188
    if (_system_impl->has_camera(_camera_id)) {
×
189
        if (!_camera_found) {
×
190
            _camera_found = true;
×
191
            manual_enable();
×
192
        }
193
    }
194
}
×
195

196
void CameraImpl::enable()
1✔
197
{
198
    // FIXME: We check for the connection status manually because
199
    // we're not interested in the connection state of the autopilot
200
    // but only the camera.
201
}
1✔
202

203
void CameraImpl::manual_enable()
×
204
{
205
    refresh_params();
×
206

207
    request_status();
×
208
    request_camera_information();
×
209

210
    _system_impl->add_call_every(
×
211
        [this]() { request_camera_information(); }, 10.0, &_camera_information_call_every_cookie);
×
212

213
    // for backwards compatibility with Yuneec drones
214
    if (_system_impl->has_autopilot()) {
×
215
        request_flight_information();
×
216

217
        _system_impl->add_call_every(
×
218
            [this]() { request_flight_information(); },
×
219
            10.0,
220
            &_flight_information_call_every_cookie);
221
    }
222
}
×
223

224
void CameraImpl::disable()
1✔
225
{
226
    // FIXME: We check for the connection status manually because
227
    // we're not interested in the connection state of the autopilot
228
    // but only the camera.
229
}
1✔
230

231
void CameraImpl::manual_disable()
×
232
{
233
    invalidate_params();
×
234
    _system_impl->remove_call_every(_camera_information_call_every_cookie);
×
235

236
    if (_flight_information_call_every_cookie) {
×
237
        _system_impl->remove_call_every(_flight_information_call_every_cookie);
×
238
    }
239

240
    _camera_found = false;
×
241
}
×
242

243
void CameraImpl::update_component()
×
244
{
245
    uint8_t cmp_id = _camera_id + MAV_COMP_ID_CAMERA;
×
246
    _system_impl->update_componentid_messages_handler(
×
247
        MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS, cmp_id, this);
248

249
    _system_impl->update_componentid_messages_handler(
×
250
        MAVLINK_MSG_ID_STORAGE_INFORMATION, cmp_id, this);
251

252
    _system_impl->update_componentid_messages_handler(
×
253
        MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED, cmp_id, this);
254

255
    _system_impl->update_componentid_messages_handler(MAVLINK_MSG_ID_CAMERA_SETTINGS, cmp_id, this);
×
256

257
    _system_impl->update_componentid_messages_handler(
×
258
        MAVLINK_MSG_ID_CAMERA_INFORMATION, cmp_id, this);
259

260
    _system_impl->update_componentid_messages_handler(
×
261
        MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION, cmp_id, this);
262

263
    _system_impl->update_componentid_messages_handler(
×
264
        MAVLINK_MSG_ID_VIDEO_STREAM_STATUS, cmp_id, this);
265
}
×
266

267
Camera::Result CameraImpl::select_camera(const size_t id)
×
268
{
269
    static constexpr std::size_t MAX_SUPPORTED_ID = 5;
270

271
    if (id > MAX_SUPPORTED_ID) {
×
272
        return Camera::Result::WrongArgument;
×
273
    }
274

275
    // camera component IDs go from 100 to 105.
276
    _camera_id = id;
×
277

278
    // We should probably reload everything to make sure the
279
    // correct  camera is initialized.
280
    update_component();
×
281
    manual_disable();
×
282
    manual_enable();
×
283

284
    return Camera::Result::Success;
×
285
}
286

287
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_flight_information()
×
288
{
289
    MavlinkCommandSender::CommandLong command_flight_information{};
×
290

291
    command_flight_information.command = MAV_CMD_REQUEST_FLIGHT_INFORMATION;
×
292
    command_flight_information.params.maybe_param1 = 1.0f; // Request it
×
293
    command_flight_information.target_component_id = MAV_COMP_ID_AUTOPILOT1;
×
294

295
    return command_flight_information;
×
296
}
297

298
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_camera_info()
×
299
{
300
    MavlinkCommandSender::CommandLong command_camera_info{};
×
301

302
    command_camera_info.command = MAV_CMD_REQUEST_CAMERA_INFORMATION;
×
303
    command_camera_info.params.maybe_param1 = 1.0f; // Request it
×
304
    command_camera_info.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
305

306
    return command_camera_info;
×
307
}
308

309
MavlinkCommandSender::CommandLong
310
CameraImpl::make_command_take_photo(float interval_s, float no_of_photos)
×
311
{
312
    MavlinkCommandSender::CommandLong cmd_take_photo{};
×
313

314
    cmd_take_photo.command = MAV_CMD_IMAGE_START_CAPTURE;
×
315
    cmd_take_photo.params.maybe_param1 = 0.0f; // Reserved, set to 0
×
316
    cmd_take_photo.params.maybe_param2 = interval_s;
×
317
    cmd_take_photo.params.maybe_param3 = no_of_photos;
×
318
    cmd_take_photo.params.maybe_param4 = static_cast<float>(_capture.sequence++);
×
319
    cmd_take_photo.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
320

321
    return cmd_take_photo;
×
322
}
323

324
MavlinkCommandSender::CommandLong CameraImpl::make_command_stop_photo()
×
325
{
326
    MavlinkCommandSender::CommandLong cmd_stop_photo{};
×
327

328
    cmd_stop_photo.command = MAV_CMD_IMAGE_STOP_CAPTURE;
×
329
    cmd_stop_photo.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
330

331
    return cmd_stop_photo;
×
332
}
333

334
MavlinkCommandSender::CommandLong CameraImpl::make_command_start_video(float capture_status_rate_hz)
×
335
{
336
    MavlinkCommandSender::CommandLong cmd_start_video{};
×
337

338
    cmd_start_video.command = MAV_CMD_VIDEO_START_CAPTURE;
×
339
    cmd_start_video.params.maybe_param1 = 0.f; // Reserved, set to 0
×
340
    cmd_start_video.params.maybe_param2 = capture_status_rate_hz;
×
341
    cmd_start_video.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
342

343
    return cmd_start_video;
×
344
}
345

346
MavlinkCommandSender::CommandLong CameraImpl::make_command_stop_video()
×
347
{
348
    MavlinkCommandSender::CommandLong cmd_stop_video{};
×
349

350
    cmd_stop_video.command = MAV_CMD_VIDEO_STOP_CAPTURE;
×
351
    cmd_stop_video.params.maybe_param1 = 0.f; // Reserved, set to 0
×
352
    cmd_stop_video.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
353

354
    return cmd_stop_video;
×
355
}
356

357
MavlinkCommandSender::CommandLong CameraImpl::make_command_set_camera_mode(float mavlink_mode)
×
358
{
359
    MavlinkCommandSender::CommandLong cmd_set_camera_mode{};
×
360

361
    cmd_set_camera_mode.command = MAV_CMD_SET_CAMERA_MODE;
×
362
    cmd_set_camera_mode.params.maybe_param1 = 0.0f; // Reserved, set to 0
×
363
    cmd_set_camera_mode.params.maybe_param2 = mavlink_mode;
×
364
    cmd_set_camera_mode.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
365

366
    return cmd_set_camera_mode;
×
367
}
368

369
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_camera_settings()
×
370
{
371
    MavlinkCommandSender::CommandLong cmd_req_camera_settings{};
×
372

373
    cmd_req_camera_settings.command = MAV_CMD_REQUEST_CAMERA_SETTINGS;
×
374
    cmd_req_camera_settings.params.maybe_param1 = 1.f; // Request it
×
375
    cmd_req_camera_settings.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
376

377
    return cmd_req_camera_settings;
×
378
}
379

380
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_camera_capture_status()
×
381
{
382
    MavlinkCommandSender::CommandLong cmd_req_camera_cap_stat{};
×
383

384
    cmd_req_camera_cap_stat.command = MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS;
×
385
    cmd_req_camera_cap_stat.params.maybe_param1 = 1.0f; // Request it
×
386
    cmd_req_camera_cap_stat.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
387

388
    return cmd_req_camera_cap_stat;
×
389
}
390

391
MavlinkCommandSender::CommandLong
392
CameraImpl::make_command_request_camera_image_captured(const size_t photo_id)
×
393
{
394
    MavlinkCommandSender::CommandLong cmd_req_camera_image_captured{};
×
395

396
    cmd_req_camera_image_captured.command = MAV_CMD_REQUEST_MESSAGE;
×
397
    cmd_req_camera_image_captured.params.maybe_param1 =
×
398
        static_cast<float>(MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED);
×
399
    cmd_req_camera_image_captured.params.maybe_param2 = static_cast<float>(photo_id);
×
400
    cmd_req_camera_image_captured.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
401

402
    return cmd_req_camera_image_captured;
×
403
}
404

405
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_storage_info()
×
406
{
407
    MavlinkCommandSender::CommandLong cmd_req_storage_info{};
×
408

409
    cmd_req_storage_info.command = MAV_CMD_REQUEST_STORAGE_INFORMATION;
×
410
    cmd_req_storage_info.params.maybe_param1 = 0.f; // Reserved, set to 0
×
411
    cmd_req_storage_info.params.maybe_param2 = 1.f; // Request it
×
412
    cmd_req_storage_info.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
413

414
    return cmd_req_storage_info;
×
415
}
416

417
MavlinkCommandSender::CommandLong CameraImpl::make_command_start_video_streaming()
×
418
{
419
    MavlinkCommandSender::CommandLong cmd_start_video_streaming{};
×
420

421
    cmd_start_video_streaming.command = MAV_CMD_VIDEO_START_STREAMING;
×
422
    cmd_start_video_streaming.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
423

424
    return cmd_start_video_streaming;
×
425
}
426

427
MavlinkCommandSender::CommandLong CameraImpl::make_command_stop_video_streaming()
×
428
{
429
    MavlinkCommandSender::CommandLong cmd_stop_video_streaming{};
×
430

431
    cmd_stop_video_streaming.command = MAV_CMD_VIDEO_STOP_STREAMING;
×
432
    cmd_stop_video_streaming.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
433

434
    return cmd_stop_video_streaming;
×
435
}
436

437
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_video_stream_info()
×
438
{
439
    MavlinkCommandSender::CommandLong cmd_req_video_stream_info{};
×
440

441
    cmd_req_video_stream_info.command = MAV_CMD_REQUEST_VIDEO_STREAM_INFORMATION;
×
442
    cmd_req_video_stream_info.params.maybe_param2 = 1.0f;
×
443
    cmd_req_video_stream_info.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
444

445
    return cmd_req_video_stream_info;
×
446
}
447

448
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_video_stream_status()
×
449
{
450
    MavlinkCommandSender::CommandLong cmd_req_video_stream_status{};
×
451

452
    cmd_req_video_stream_status.command = MAV_CMD_REQUEST_VIDEO_STREAM_STATUS;
×
453
    cmd_req_video_stream_status.params.maybe_param2 = 1.0f;
×
454
    cmd_req_video_stream_status.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
455

456
    return cmd_req_video_stream_status;
×
457
}
458

459
Camera::Result CameraImpl::take_photo()
×
460
{
461
    // TODO: check whether we are in photo mode.
462

463
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
464

465
    // Take 1 photo only with no interval
466
    auto cmd_take_photo = make_command_take_photo(0.f, 1.0f);
×
467

468
    return camera_result_from_command_result(_system_impl->send_command(cmd_take_photo));
×
469
}
470

471
Camera::Result CameraImpl::start_photo_interval(float interval_s)
×
472
{
473
    if (!interval_valid(interval_s)) {
×
474
        return Camera::Result::WrongArgument;
×
475
    }
476

477
    // TODO: check whether we are in photo mode.
478

479
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
480

481
    auto cmd_take_photo_time_lapse = make_command_take_photo(interval_s, 0.f);
×
482

483
    return camera_result_from_command_result(_system_impl->send_command(cmd_take_photo_time_lapse));
×
484
}
485

486
Camera::Result CameraImpl::stop_photo_interval()
×
487
{
488
    auto cmd_stop_photo_interval = make_command_stop_photo();
×
489

490
    return camera_result_from_command_result(_system_impl->send_command(cmd_stop_photo_interval));
×
491
}
492

493
Camera::Result CameraImpl::start_video()
×
494
{
495
    // TODO: check whether video capture is already in progress.
496
    // TODO: check whether we are in video mode.
497

498
    // Capture status rate is not set
499
    auto cmd_start_video = make_command_start_video(0.f);
×
500

501
    return camera_result_from_command_result(_system_impl->send_command(cmd_start_video));
×
502
}
503

504
Camera::Result CameraImpl::stop_video()
×
505
{
506
    auto cmd_stop_video = make_command_stop_video();
×
507

508
    {
509
        std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
510
        _video_stream_info.data.status = Camera::VideoStreamInfo::VideoStreamStatus::NotRunning;
×
511
    }
512

513
    return camera_result_from_command_result(_system_impl->send_command(cmd_stop_video));
×
514
}
515

516
void CameraImpl::take_photo_async(const Camera::ResultCallback& callback)
×
517
{
518
    // TODO: check whether we are in photo mode.
519

520
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
521

522
    // Take 1 photo only with no interval
523
    auto cmd_take_photo = make_command_take_photo(0.f, 1.0f);
×
524

525
    _system_impl->send_command_async(
×
526
        cmd_take_photo, [this, callback](MavlinkCommandSender::Result result, float) {
×
527
            receive_command_result(result, callback);
×
528
        });
×
529
}
×
530

531
void CameraImpl::start_photo_interval_async(
×
532
    float interval_s, const Camera::ResultCallback& callback)
533
{
534
    if (!interval_valid(interval_s)) {
×
535
        const auto temp_callback = callback;
×
536
        _system_impl->call_user_callback(
×
537
            [temp_callback]() { temp_callback(Camera::Result::WrongArgument); });
538
        return;
×
539
    }
540

541
    // TODO: check whether we are in photo mode.
542

543
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
544

545
    auto cmd_take_photo_time_lapse = make_command_take_photo(interval_s, 0.f);
×
546

547
    _system_impl->send_command_async(
×
548
        cmd_take_photo_time_lapse, [this, callback](MavlinkCommandSender::Result result, float) {
×
549
            receive_command_result(result, callback);
×
550
        });
×
551
}
552

553
void CameraImpl::stop_photo_interval_async(const Camera::ResultCallback& callback)
×
554
{
555
    auto cmd_stop_photo_interval = make_command_stop_photo();
×
556

557
    _system_impl->send_command_async(
×
558
        cmd_stop_photo_interval, [this, callback](MavlinkCommandSender::Result result, float) {
×
559
            receive_command_result(result, callback);
×
560
        });
×
561
}
×
562

563
void CameraImpl::start_video_async(const Camera::ResultCallback& callback)
×
564
{
565
    // TODO: check whether video capture is already in progress.
566
    // TODO: check whether we are in video mode.
567

568
    // Capture status rate is not set
569
    auto cmd_start_video = make_command_start_video(0.f);
×
570

571
    _system_impl->send_command_async(
×
572
        cmd_start_video, [this, callback](MavlinkCommandSender::Result result, float) {
×
573
            receive_command_result(result, callback);
×
574
        });
×
575
}
×
576

577
void CameraImpl::stop_video_async(const Camera::ResultCallback& callback)
×
578
{
579
    auto cmd_stop_video = make_command_stop_video();
×
580

581
    _system_impl->send_command_async(
×
582
        cmd_stop_video, [this, callback](MavlinkCommandSender::Result result, float) {
×
583
            receive_command_result(result, callback);
×
584
        });
×
585
}
×
586

587
Camera::Information CameraImpl::information() const
×
588
{
589
    std::lock_guard<std::mutex> lock(_information.mutex);
×
590

591
    return _information.data;
×
592
}
593

594
Camera::InformationHandle
595
CameraImpl::subscribe_information(const Camera::InformationCallback& callback)
×
596
{
597
    std::lock_guard<std::mutex> lock(_information.mutex);
×
598
    auto handle = _information.subscription_callbacks.subscribe(callback);
×
599

600
    // If there was already a subscription, cancel the call
601
    if (_status.call_every_cookie) {
×
602
        _system_impl->remove_call_every(_status.call_every_cookie);
×
603
    }
604

605
    if (callback) {
×
606
        if (_status.call_every_cookie == nullptr) {
×
607
            _system_impl->add_call_every(
×
608
                [this]() { request_status(); }, 5.0, &_status.call_every_cookie);
×
609
        }
610
    } else {
611
        _system_impl->remove_call_every(_status.call_every_cookie);
×
612
        _status.call_every_cookie = nullptr;
×
613
    }
614

615
    return handle;
×
616
}
617

618
void CameraImpl::unsubscribe_information(Camera::InformationHandle handle)
×
619
{
620
    std::lock_guard<std::mutex> lock(_information.mutex);
×
621
    _information.subscription_callbacks.unsubscribe(handle);
×
622
}
×
623

624
Camera::Result CameraImpl::start_video_streaming()
×
625
{
626
    std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
627

628
    if (_video_stream_info.available &&
×
629
        _video_stream_info.data.status == Camera::VideoStreamInfo::VideoStreamStatus::InProgress) {
×
630
        return Camera::Result::InProgress;
×
631
    }
632

633
    // TODO Check whether we're in video mode
634
    auto command = make_command_start_video_streaming();
×
635

636
    auto result = camera_result_from_command_result(_system_impl->send_command(command));
×
637
    // if (result == Camera::Result::Success) {
638
    // Cache video stream info; app may query immediately next.
639
    // TODO: check if we can/should do that.
640
    // auto info = get_video_stream_info();
641
    //}
642
    return result;
×
643
}
644

645
Camera::Result CameraImpl::stop_video_streaming()
×
646
{
647
    // TODO I think we need to maintain current state, whether we issued
648
    // video capture request or video streaming request, etc.We shouldn't
649
    // send stop video streaming if we've not started it!
650
    auto command = make_command_stop_video_streaming();
×
651

652
    auto result = camera_result_from_command_result(_system_impl->send_command(command));
×
653
    {
654
        std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
655
        // TODO: check if we can/should do that.
656
        _video_stream_info.data.status = Camera::VideoStreamInfo::VideoStreamStatus::NotRunning;
×
657
    }
658
    return result;
×
659
}
660

661
void CameraImpl::request_video_stream_info()
×
662
{
663
    _system_impl->send_command_async(make_command_request_video_stream_info(), nullptr);
×
664
    _system_impl->send_command_async(make_command_request_video_stream_status(), nullptr);
×
665
}
×
666

667
Camera::VideoStreamInfo CameraImpl::video_stream_info()
×
668
{
669
    std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
670

671
    return _video_stream_info.data;
×
672
}
673

674
Camera::VideoStreamInfoHandle
675
CameraImpl::subscribe_video_stream_info(const Camera::VideoStreamInfoCallback& callback)
×
676
{
677
    std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
678

679
    auto handle = _video_stream_info.subscription_callbacks.subscribe(callback);
×
680

681
    if (callback) {
×
682
        _system_impl->add_call_every(
×
683
            [this]() { request_video_stream_info(); }, 1.0, &_video_stream_info.call_every_cookie);
×
684
    } else {
685
        _system_impl->remove_call_every(_video_stream_info.call_every_cookie);
×
686
    }
687

688
    return handle;
×
689
}
690

691
void CameraImpl::unsubscribe_video_stream_info(Camera::VideoStreamInfoHandle handle)
×
692
{
693
    std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
694
    _video_stream_info.subscription_callbacks.unsubscribe(handle);
×
695
}
×
696

697
Camera::Result
698
CameraImpl::camera_result_from_command_result(const MavlinkCommandSender::Result command_result)
×
699
{
700
    switch (command_result) {
×
701
        case MavlinkCommandSender::Result::Success:
×
702
            return Camera::Result::Success;
×
703
        case MavlinkCommandSender::Result::NoSystem:
×
704
            // FALLTHROUGH
705
        case MavlinkCommandSender::Result::ConnectionError:
706
            // FALLTHROUGH
707
        case MavlinkCommandSender::Result::Busy:
708
            return Camera::Result::Error;
×
709
        case MavlinkCommandSender::Result::Denied:
×
710
            // FALLTHROUGH
711
        case MavlinkCommandSender::Result::TemporarilyRejected:
712
            return Camera::Result::Denied;
×
713
        case MavlinkCommandSender::Result::Timeout:
×
714
            return Camera::Result::Timeout;
×
715
        default:
×
716
            return Camera::Result::Unknown;
×
717
    }
718
}
719

720
Camera::Result CameraImpl::camera_result_from_parameter_result(
×
721
    const MavlinkParameterSender::Result parameter_result)
722
{
723
    switch (parameter_result) {
×
724
        case MavlinkParameterSender::Result::Success:
×
725
            return Camera::Result::Success;
×
726
        case MavlinkParameterSender::Result::Timeout:
×
727
            return Camera::Result::Timeout;
×
728
        case MavlinkParameterSender::Result::ConnectionError:
×
729
            return Camera::Result::Error;
×
730
        case MavlinkParameterSender::Result::WrongType:
×
731
            return Camera::Result::WrongArgument;
×
732
        case MavlinkParameterSender::Result::ParamNameTooLong:
×
733
            return Camera::Result::WrongArgument;
×
734
        case MavlinkParameterSender::Result::NotFound:
×
735
            return Camera::Result::WrongArgument;
×
736
        case MavlinkParameterSender::Result::ValueUnsupported:
×
737
            return Camera::Result::WrongArgument;
×
738
        case MavlinkParameterSender::Result::Failed:
×
739
            return Camera::Result::Error;
×
740
        case MavlinkParameterSender::Result::UnknownError:
×
741
            return Camera::Result::Error;
×
742
        default:
×
743
            return Camera::Result::Unknown;
×
744
    }
745
}
746

747
Camera::Result CameraImpl::set_mode(const Camera::Mode mode)
×
748
{
749
    const float mavlink_mode = to_mavlink_camera_mode(mode);
×
750
    auto cmd_set_camera_mode = make_command_set_camera_mode(mavlink_mode);
×
751
    const auto command_result = _system_impl->send_command(cmd_set_camera_mode);
×
752
    const auto camera_result = camera_result_from_command_result(command_result);
×
753

754
    if (camera_result == Camera::Result::Success) {
×
755
        {
756
            std::lock_guard<std::mutex> lock(_mode.mutex);
×
757
            _mode.data = mode;
×
758
        }
759
        notify_mode();
×
760
        if (_camera_definition != nullptr) {
×
761
            save_camera_mode(mavlink_mode);
×
762
        }
763
    }
764

765
    return camera_result;
×
766
}
767

768
void CameraImpl::save_camera_mode(const float mavlink_camera_mode)
×
769
{
770
    if (!std::isfinite(mavlink_camera_mode)) {
×
771
        LogWarn() << "Can't save NAN as camera mode";
×
772
        return;
×
773
    }
774

775
    // If there is a camera definition (which is the case if we are
776
    // in this function, and if CAM_MODE is defined there, then
777
    // we reuse that type. Otherwise, we hardcode it to `uint32_t`.
778
    // Note that it could be that the camera definition defines options
779
    // different than {PHOTO, VIDEO}, in which case the mode received
780
    // from CAMERA_SETTINGS will be wrong. Not sure if it means that
781
    // it should be ignored in that case, but that may be tricky to
782
    // maintain (what if the MAVLink CAMERA_MODE enum evolves?), so
783
    // I am assuming here that in such a case, CAMERA_SETTINGS is
784
    // never sent by the camera.
785
    ParamValue value;
×
786
    if (_camera_definition->get_setting("CAM_MODE", value)) {
×
787
        if (value.is<uint8_t>()) {
×
788
            value.set<uint8_t>(static_cast<uint8_t>(mavlink_camera_mode));
×
789
        } else if (value.is<int8_t>()) {
×
790
            value.set<int8_t>(static_cast<int8_t>(mavlink_camera_mode));
×
791
        } else if (value.is<uint16_t>()) {
×
792
            value.set<uint16_t>(static_cast<uint16_t>(mavlink_camera_mode));
×
793
        } else if (value.is<int16_t>()) {
×
794
            value.set<int16_t>(static_cast<int16_t>(mavlink_camera_mode));
×
795
        } else if (value.is<uint32_t>()) {
×
796
            value.set<uint32_t>(static_cast<uint32_t>(mavlink_camera_mode));
×
797
        } else if (value.is<int32_t>()) {
×
798
            value.set<int32_t>(static_cast<int32_t>(mavlink_camera_mode));
×
799
        } else if (value.is<uint64_t>()) {
×
800
            value.set<uint64_t>(static_cast<uint64_t>(mavlink_camera_mode));
×
801
        } else if (value.is<int64_t>()) {
×
802
            value.set<int64_t>(static_cast<int64_t>(mavlink_camera_mode));
×
803
        } else if (value.is<float>()) {
×
804
            value.set<float>(static_cast<float>(mavlink_camera_mode));
×
805
        } else if (value.is<double>()) {
×
806
            value.set<double>(static_cast<double>(mavlink_camera_mode));
×
807
        }
808
    } else {
809
        value.set<uint32_t>(static_cast<uint32_t>(mavlink_camera_mode));
×
810
    }
811

812
    _camera_definition->set_setting("CAM_MODE", value);
×
813
    refresh_params();
×
814
}
815

816
float CameraImpl::to_mavlink_camera_mode(const Camera::Mode mode) const
×
817
{
818
    switch (mode) {
×
819
        case Camera::Mode::Photo:
×
820
            return CAMERA_MODE_IMAGE;
×
821
        case Camera::Mode::Video:
×
822
            return CAMERA_MODE_VIDEO;
×
823
        default:
×
824
        case Camera::Mode::Unknown:
825
            return NAN;
×
826
    }
827
}
828

829
void CameraImpl::set_mode_async(const Camera::Mode mode, const Camera::ResultCallback& callback)
×
830
{
831
    const auto mavlink_mode = to_mavlink_camera_mode(mode);
×
832
    auto cmd_set_camera_mode = make_command_set_camera_mode(mavlink_mode);
×
833

834
    _system_impl->send_command_async(
×
835
        cmd_set_camera_mode,
836
        [this, callback, mode](MavlinkCommandSender::Result result, float progress) {
×
837
            UNUSED(progress);
×
838
            receive_set_mode_command_result(result, callback, mode);
×
839
        });
×
840
}
×
841

842
Camera::Mode CameraImpl::mode()
×
843
{
844
    std::lock_guard<std::mutex> lock(_mode.mutex);
×
845
    return _mode.data;
×
846
}
847

848
Camera::ModeHandle CameraImpl::subscribe_mode(const Camera::ModeCallback& callback)
×
849
{
850
    std::unique_lock<std::mutex> lock(_mode.mutex);
×
851
    auto handle = _mode.subscription_callbacks.subscribe(callback);
×
852
    lock.unlock();
×
853

854
    notify_mode();
×
855

856
    if (callback) {
×
857
        _system_impl->add_call_every(
×
858
            [this]() { request_camera_settings(); }, 5.0, &_mode.call_every_cookie);
×
859
    } else {
860
        _system_impl->remove_call_every(_mode.call_every_cookie);
×
861
    }
862

863
    return handle;
×
864
}
865

866
void CameraImpl::unsubscribe_mode(Camera::ModeHandle handle)
×
867
{
868
    std::lock_guard<std::mutex> lock(_mode.mutex);
×
869
    _mode.subscription_callbacks.unsubscribe(handle);
×
870
}
×
871

872
bool CameraImpl::interval_valid(float interval_s)
×
873
{
874
    // Reject everything faster than 1000 Hz, as well as negative inputs.
875
    if (interval_s < 0.001f) {
×
876
        LogWarn() << "Invalid interval input";
×
877
        return false;
×
878
    } else {
879
        return true;
×
880
    }
881
}
882

883
void CameraImpl::request_status()
×
884
{
885
    _system_impl->send_command_async(make_command_request_camera_capture_status(), nullptr);
×
886
    _system_impl->send_command_async(make_command_request_storage_info(), nullptr);
×
887
}
×
888

889
Camera::StatusHandle CameraImpl::subscribe_status(const Camera::StatusCallback& callback)
×
890
{
891
    std::lock_guard<std::mutex> lock(_status.mutex);
×
892

893
    auto handle = _status.subscription_callbacks.subscribe(callback);
×
894

895
    if (callback) {
×
896
        if (_status.call_every_cookie == nullptr) {
×
897
            _system_impl->add_call_every(
×
898
                [this]() { request_status(); }, 5.0, &_status.call_every_cookie);
×
899
        }
900
    } else {
901
        _system_impl->remove_call_every(_status.call_every_cookie);
×
902
        _status.call_every_cookie = nullptr;
×
903
    }
904

905
    return handle;
×
906
}
907

908
void CameraImpl::unsubscribe_status(Camera::StatusHandle handle)
×
909
{
910
    std::lock_guard<std::mutex> lock(_status.mutex);
×
911
    _status.subscription_callbacks.unsubscribe(handle);
×
912
}
×
913

914
Camera::Status CameraImpl::status()
×
915
{
916
    std::lock_guard<std::mutex> lock(_status.mutex);
×
917
    return _status.data;
×
918
}
919

920
Camera::CaptureInfoHandle
921
CameraImpl::subscribe_capture_info(const Camera::CaptureInfoCallback& callback)
×
922
{
923
    std::lock_guard<std::mutex> lock(_capture_info.mutex);
×
924
    return _capture_info.callbacks.subscribe(callback);
×
925
}
926

927
void CameraImpl::unsubscribe_capture_info(Camera::CaptureInfoHandle handle)
×
928
{
929
    std::lock_guard<std::mutex> lock(_capture_info.mutex);
×
930
    _capture_info.callbacks.unsubscribe(handle);
×
931
}
×
932

933
void CameraImpl::process_camera_capture_status(const mavlink_message_t& message)
×
934
{
935
    mavlink_camera_capture_status_t camera_capture_status;
×
936
    mavlink_msg_camera_capture_status_decode(&message, &camera_capture_status);
×
937

938
    // If image_count got smaller, consider that the storage was formatted.
939
    if (camera_capture_status.image_count < _status.image_count) {
×
940
        LogDebug() << "Seems like storage was formatted, setting state accordingly";
×
941
        reset_following_format_storage();
×
942
    }
943

944
    {
945
        std::lock_guard<std::mutex> lock(_status.mutex);
×
946

947
        _status.data.video_on = (camera_capture_status.video_status == 1);
×
948
        _status.data.photo_interval_on =
×
949
            (camera_capture_status.image_status == 2 || camera_capture_status.image_status == 3);
×
950
        _status.received_camera_capture_status = true;
×
951
        _status.data.recording_time_s = float(camera_capture_status.recording_time_ms) / 1e3f;
×
952

953
        _status.image_count = camera_capture_status.image_count;
×
954

955
        if (_status.image_count_at_connection == -1) {
×
956
            _status.image_count_at_connection = camera_capture_status.image_count;
×
957
        }
958
    }
959

960
    check_status();
×
961
}
×
962

963
void CameraImpl::process_storage_information(const mavlink_message_t& message)
×
964
{
965
    mavlink_storage_information_t storage_information;
×
966
    mavlink_msg_storage_information_decode(&message, &storage_information);
×
967

968
    if (storage_information.total_capacity == 0.0f) {
×
969
        // Some MAVLink systems happen to send the STORAGE_INFORMATION message
970
        // to indicate that the camera has a slot for a storage even if there
971
        // is no way to know anything about that storage (e.g. whether or not
972
        // there is an sdcard in the slot).
973
        //
974
        // We consider that a total capacity of 0 means that this is such a
975
        // message, and we don't expect MAVSDK users to leverage it, which is
976
        // why it is ignored.
977
        return;
×
978
    }
979

980
    {
981
        std::lock_guard<std::mutex> lock(_status.mutex);
×
982
        switch (storage_information.status) {
×
983
            case STORAGE_STATUS_EMPTY:
×
984
                _status.data.storage_status = Camera::Status::StorageStatus::NotAvailable;
×
985
                break;
×
986
            case STORAGE_STATUS_UNFORMATTED:
×
987
                _status.data.storage_status = Camera::Status::StorageStatus::Unformatted;
×
988
                break;
×
989
            case STORAGE_STATUS_READY:
×
990
                _status.data.storage_status = Camera::Status::StorageStatus::Formatted;
×
991
                break;
×
992
            case STORAGE_STATUS_NOT_SUPPORTED:
×
993
                _status.data.storage_status = Camera::Status::StorageStatus::NotSupported;
×
994
                break;
×
995
            default:
×
996
                _status.data.storage_status = Camera::Status::StorageStatus::NotSupported;
×
997
                LogErr() << "Unknown storage status received.";
×
998
                break;
×
999
        }
1000

1001
        _status.data.available_storage_mib = storage_information.available_capacity;
×
1002
        _status.data.used_storage_mib = storage_information.used_capacity;
×
1003
        _status.data.total_storage_mib = storage_information.total_capacity;
×
1004
        _status.data.storage_id = storage_information.storage_id;
×
1005
        _status.data.storage_type =
×
1006
            static_cast<Camera::Status::StorageType>(storage_information.type);
×
1007
        _status.received_storage_information = true;
×
1008
    }
1009

1010
    check_status();
×
1011
}
1012

1013
void CameraImpl::process_camera_image_captured(const mavlink_message_t& message)
×
1014
{
1015
    mavlink_camera_image_captured_t image_captured;
×
1016
    mavlink_msg_camera_image_captured_decode(&message, &image_captured);
×
1017

1018
    {
1019
        Camera::CaptureInfo capture_info = {};
×
1020
        capture_info.position.latitude_deg = image_captured.lat / 1e7;
×
1021
        capture_info.position.longitude_deg = image_captured.lon / 1e7;
×
1022
        capture_info.position.absolute_altitude_m = image_captured.alt / 1e3f;
×
1023
        capture_info.position.relative_altitude_m = image_captured.relative_alt / 1e3f;
×
1024
        capture_info.time_utc_us = image_captured.time_utc;
×
1025
        capture_info.attitude_quaternion.w = image_captured.q[0];
×
1026
        capture_info.attitude_quaternion.x = image_captured.q[1];
×
1027
        capture_info.attitude_quaternion.y = image_captured.q[2];
×
1028
        capture_info.attitude_quaternion.z = image_captured.q[3];
×
1029
        capture_info.attitude_euler_angle =
1030
            to_euler_angle_from_quaternion(capture_info.attitude_quaternion);
×
1031
        capture_info.file_url = std::string(image_captured.file_url);
×
1032
        capture_info.is_success = (image_captured.capture_result == 1);
×
1033
        capture_info.index = image_captured.image_index;
×
1034

1035
        _status.photo_list.insert(std::make_pair(image_captured.image_index, capture_info));
×
1036

1037
        _captured_request_cv.notify_all();
×
1038

1039
        std::lock_guard<std::mutex> lock(_capture_info.mutex);
×
1040
        // Notify user if a new image has been captured.
1041
        if (_capture_info.last_advertised_image_index < capture_info.index) {
×
1042
            _capture_info.callbacks.queue(
×
1043
                capture_info, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1044

1045
            if (_capture_info.last_advertised_image_index != -1) {
×
1046
                // Save captured indices that have been dropped to request later, however, don't
1047
                // do it from the very beginning as there might be many photos from a previous
1048
                // time that we don't want to request.
1049
                for (int i = _capture_info.last_advertised_image_index + 1; i < capture_info.index;
×
1050
                     ++i) {
1051
                    if (_capture_info.missing_image_retries.find(i) ==
×
1052
                        _capture_info.missing_image_retries.end()) {
×
1053
                        _capture_info.missing_image_retries[i] = 0;
×
1054
                    }
1055
                }
1056
            }
1057

1058
            _capture_info.last_advertised_image_index = capture_info.index;
×
1059
        }
1060

1061
        else if (auto it = _capture_info.missing_image_retries.find(capture_info.index);
×
1062
                 it != _capture_info.missing_image_retries.end()) {
×
1063
            _capture_info.callbacks.queue(
×
1064
                capture_info, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1065
            _capture_info.missing_image_retries.erase(it);
×
1066
        }
1067
    }
1068
}
×
1069

1070
void CameraImpl::request_missing_capture_info()
×
1071
{
1072
    std::lock_guard<std::mutex> lock(_capture_info.mutex);
×
1073

1074
    for (auto it = _capture_info.missing_image_retries.begin();
×
1075
         it != _capture_info.missing_image_retries.end();
×
1076
         /* ++it */) {
1077
        if (it->second > 3) {
×
1078
            it = _capture_info.missing_image_retries.erase(it);
×
1079
        } else {
1080
            ++it;
×
1081
        }
1082
    }
1083

1084
    if (!_capture_info.missing_image_retries.empty()) {
×
1085
        auto it_lowest_retries = std::min_element(
×
1086
            _capture_info.missing_image_retries.begin(), _capture_info.missing_image_retries.end());
×
1087
        _system_impl->send_command_async(
×
1088
            CameraImpl::make_command_request_camera_image_captured(it_lowest_retries->first),
×
1089
            nullptr);
1090
        it_lowest_retries->second += 1;
×
1091
    }
1092
}
×
1093

1094
Camera::EulerAngle CameraImpl::to_euler_angle_from_quaternion(Camera::Quaternion quaternion)
×
1095
{
1096
    auto& q = quaternion;
×
1097

1098
    // FIXME: This is duplicated from telemetry/math_conversions.cpp.
1099
    Camera::EulerAngle euler_angle;
×
1100
    euler_angle.roll_deg = to_deg_from_rad(
×
1101
        atan2f(2.0f * (q.w * q.x + q.y * q.z), 1.0f - 2.0f * (q.x * q.x + q.y * q.y)));
×
1102

1103
    euler_angle.pitch_deg = to_deg_from_rad(asinf(2.0f * (q.w * q.y - q.z * q.x)));
×
1104
    euler_angle.yaw_deg = to_deg_from_rad(
×
1105
        atan2f(2.0f * (q.w * q.z + q.x * q.y), 1.0f - 2.0f * (q.y * q.y + q.z * q.z)));
×
1106
    return euler_angle;
×
1107
}
1108

1109
void CameraImpl::process_camera_settings(const mavlink_message_t& message)
×
1110
{
1111
    mavlink_camera_settings_t camera_settings;
×
1112
    mavlink_msg_camera_settings_decode(&message, &camera_settings);
×
1113

1114
    {
1115
        std::lock_guard<std::mutex> lock(_mode.mutex);
×
1116
        _mode.data = to_camera_mode(camera_settings.mode_id);
×
1117
    }
1118
    notify_mode();
×
1119

1120
    if (_camera_definition) {
×
1121
        // This "parameter" needs to be manually set.
1122
        save_camera_mode(camera_settings.mode_id);
×
1123
    }
1124
}
×
1125

1126
Camera::Mode CameraImpl::to_camera_mode(const uint8_t mavlink_camera_mode) const
×
1127
{
1128
    switch (mavlink_camera_mode) {
×
1129
        case CAMERA_MODE_IMAGE:
×
1130
            return Camera::Mode::Photo;
×
1131
        case CAMERA_MODE_VIDEO:
×
1132
            return Camera::Mode::Video;
×
1133
        default:
×
1134
            return Camera::Mode::Unknown;
×
1135
    }
1136
}
1137

1138
void CameraImpl::process_camera_information(const mavlink_message_t& message)
×
1139
{
1140
    mavlink_camera_information_t camera_information;
×
1141
    mavlink_msg_camera_information_decode(&message, &camera_information);
×
1142

1143
    std::lock_guard<std::mutex> lock(_information.mutex);
×
1144

1145
    _information.data.vendor_name = (char*)(camera_information.vendor_name);
×
1146
    _information.data.model_name = (char*)(camera_information.model_name);
×
1147
    _information.data.focal_length_mm = camera_information.focal_length;
×
1148
    _information.data.horizontal_sensor_size_mm = camera_information.sensor_size_h;
×
1149
    _information.data.vertical_sensor_size_mm = camera_information.sensor_size_v;
×
1150
    _information.data.horizontal_resolution_px = camera_information.resolution_h;
×
1151
    _information.data.vertical_resolution_px = camera_information.resolution_v;
×
1152

1153
    _information.subscription_callbacks.queue(
×
1154
        _information.data, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1155

1156
    if (should_fetch_camera_definition(camera_information.cam_definition_uri)) {
×
1157
        _is_fetching_camera_definition = true;
×
1158

1159
        std::thread([this, camera_information]() {
×
1160
            std::string content{};
×
1161
            const auto has_succeeded = fetch_camera_definition(camera_information, content);
×
1162

1163
            if (has_succeeded) {
×
1164
                LogDebug() << "Successfully loaded camera definition";
×
1165

1166
                if (_camera_definition_callback) {
×
1167
                    _system_impl->call_user_callback(
×
1168
                        [this]() { _camera_definition_callback(true); });
1169
                }
1170

1171
                _camera_definition.reset(new CameraDefinition());
×
1172
                _camera_definition->load_string(content);
×
1173
                refresh_params();
×
1174
            } else {
1175
                LogDebug() << "Failed to fetch camera definition!";
×
1176

1177
                if (++_camera_definition_fetch_count >= 3) {
×
1178
                    LogWarn() << "Giving up fetching the camera definition";
×
1179

1180
                    std::lock_guard<std::mutex> thread_lock(_information.mutex);
×
1181
                    _has_camera_definition_timed_out = true;
×
1182

1183
                    if (_camera_definition_callback) {
×
1184
                        _system_impl->call_user_callback(
×
1185
                            [this]() { _camera_definition_callback(false); });
1186
                    }
1187
                }
1188
            }
1189

1190
            std::lock_guard<std::mutex> thread_lock(_information.mutex);
×
1191
            _is_fetching_camera_definition = false;
×
1192
        }).detach();
×
1193
    }
1194
}
×
1195

1196
bool CameraImpl::should_fetch_camera_definition(const std::string& uri) const
×
1197
{
1198
    return !uri.empty() && !_camera_definition && !_is_fetching_camera_definition &&
×
1199
           !_has_camera_definition_timed_out;
×
1200
}
1201

1202
bool CameraImpl::fetch_camera_definition(
×
1203
    const mavlink_camera_information_t& camera_information, std::string& camera_definition_out)
1204
{
1205
    auto download_succeeded =
1206
        download_definition_file(camera_information.cam_definition_uri, camera_definition_out);
×
1207

1208
    if (download_succeeded) {
×
1209
        return true;
×
1210
    }
1211

1212
    return load_stored_definition(camera_information, camera_definition_out);
×
1213
}
1214

1215
bool CameraImpl::download_definition_file(
×
1216
    const std::string& uri, std::string& camera_definition_out)
1217
{
1218
    HttpLoader http_loader;
×
1219
    LogInfo() << "Downloading camera definition from: " << uri;
×
1220
    if (!http_loader.download_text_sync(uri, camera_definition_out)) {
×
1221
        LogErr() << "Failed to download camera definition.";
×
1222
        return false;
×
1223
    }
1224

1225
    return true;
×
1226
}
1227

1228
bool CameraImpl::load_stored_definition(
×
1229
    const mavlink_camera_information_t& camera_information, std::string& camera_definition_out)
1230
{
1231
    // TODO: we might also try to support the correct version of the xml files.
1232
    if (strcmp((const char*)(camera_information.vendor_name), "Yuneec") == 0) {
×
1233
        if (strcmp((const char*)(camera_information.model_name), "E90") == 0) {
×
1234
            LogInfo() << "Using cached file for Yuneec E90.";
×
1235
            camera_definition_out = e90xml;
×
1236
            return true;
×
1237
        } else if (strcmp((const char*)(camera_information.model_name), "E50") == 0) {
×
1238
            LogInfo() << "Using cached file for Yuneec E50.";
×
1239
            camera_definition_out = e50xml;
×
1240
            return true;
×
1241
        } else if (strcmp((const char*)(camera_information.model_name), "CGOET") == 0) {
×
1242
            LogInfo() << "Using cached file for Yuneec ET.";
×
1243
            camera_definition_out = cgoetxml;
×
1244
            return true;
×
1245
        } else if (strcmp((const char*)(camera_information.model_name), "E10T") == 0) {
×
1246
            LogInfo() << "Using cached file for Yuneec E10T.";
×
1247
            camera_definition_out = e10txml;
×
1248
            return true;
×
1249
        } else if (strcmp((const char*)(camera_information.model_name), "E30Z") == 0) {
×
1250
            LogInfo() << "Using cached file for Yuneec E30Z.";
×
1251
            camera_definition_out = e30zxml;
×
1252
            return true;
×
1253
        }
1254
    } else if (strcmp((const char*)(camera_information.vendor_name), "Sony") == 0) {
×
1255
        if (strcmp((const char*)(camera_information.model_name), "ILCE-7RM4") == 0) {
×
1256
            LogInfo() << "Using cached file for Sony ILCE-7RM4.";
×
1257
            camera_definition_out = ILCE7RM4xml;
×
1258
            return true;
×
1259
        }
1260
    }
1261

1262
    return false;
×
1263
}
1264

1265
void CameraImpl::process_video_information(const mavlink_message_t& message)
×
1266
{
1267
    mavlink_video_stream_information_t received_video_info;
×
1268
    mavlink_msg_video_stream_information_decode(&message, &received_video_info);
×
1269

1270
    {
1271
        std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
1272
        // TODO: use stream_id and count
1273
        _video_stream_info.data.status =
×
1274
            (received_video_info.flags & VIDEO_STREAM_STATUS_FLAGS_RUNNING ?
×
1275
                 Camera::VideoStreamInfo::VideoStreamStatus::InProgress :
1276
                 Camera::VideoStreamInfo::VideoStreamStatus::NotRunning);
1277
        _video_stream_info.data.spectrum =
×
1278
            (received_video_info.flags & VIDEO_STREAM_STATUS_FLAGS_THERMAL ?
×
1279
                 Camera::VideoStreamInfo::VideoStreamSpectrum::Infrared :
1280
                 Camera::VideoStreamInfo::VideoStreamSpectrum::VisibleLight);
1281

1282
        auto& video_stream_info = _video_stream_info.data.settings;
×
1283
        video_stream_info.frame_rate_hz = received_video_info.framerate;
×
1284
        video_stream_info.horizontal_resolution_pix = received_video_info.resolution_h;
×
1285
        video_stream_info.vertical_resolution_pix = received_video_info.resolution_v;
×
1286
        video_stream_info.bit_rate_b_s = received_video_info.bitrate;
×
1287
        video_stream_info.rotation_deg = received_video_info.rotation;
×
1288
        video_stream_info.horizontal_fov_deg = static_cast<float>(received_video_info.hfov);
×
1289
        video_stream_info.uri = received_video_info.uri;
×
1290
        _video_stream_info.available = true;
×
1291
    }
1292

1293
    notify_video_stream_info();
×
1294
}
×
1295

1296
void CameraImpl::process_video_stream_status(const mavlink_message_t& message)
×
1297
{
1298
    mavlink_video_stream_status_t received_video_stream_status;
×
1299
    mavlink_msg_video_stream_status_decode(&message, &received_video_stream_status);
×
1300
    {
1301
        std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
1302
        _video_stream_info.data.status =
×
1303
            (received_video_stream_status.flags & VIDEO_STREAM_STATUS_FLAGS_RUNNING ?
×
1304
                 Camera::VideoStreamInfo::VideoStreamStatus::InProgress :
1305
                 Camera::VideoStreamInfo::VideoStreamStatus::NotRunning);
1306
        _video_stream_info.data.spectrum =
×
1307
            (received_video_stream_status.flags & VIDEO_STREAM_STATUS_FLAGS_THERMAL ?
×
1308
                 Camera::VideoStreamInfo::VideoStreamSpectrum::Infrared :
1309
                 Camera::VideoStreamInfo::VideoStreamSpectrum::VisibleLight);
1310

1311
        auto& video_stream_info = _video_stream_info.data.settings;
×
1312
        video_stream_info.frame_rate_hz = received_video_stream_status.framerate;
×
1313
        video_stream_info.horizontal_resolution_pix = received_video_stream_status.resolution_h;
×
1314
        video_stream_info.vertical_resolution_pix = received_video_stream_status.resolution_v;
×
1315
        video_stream_info.bit_rate_b_s = received_video_stream_status.bitrate;
×
1316
        video_stream_info.rotation_deg = received_video_stream_status.rotation;
×
1317
        video_stream_info.horizontal_fov_deg =
×
1318
            static_cast<float>(received_video_stream_status.hfov);
×
1319
        _video_stream_info.available = true;
×
1320
    }
1321

1322
    notify_video_stream_info();
×
1323
}
×
1324

1325
void CameraImpl::process_flight_information(const mavlink_message_t& message)
×
1326
{
1327
    mavlink_flight_information_t flight_information;
×
1328
    mavlink_msg_flight_information_decode(&message, &flight_information);
×
1329

1330
    std::stringstream folder_name_stream;
×
1331
    {
1332
        std::lock_guard<std::mutex> information_lock(_information.mutex);
×
1333

1334
        // For Yuneec cameras, the folder names can be derived from the flight ID,
1335
        // starting at 100 up to 999.
1336
        if (_information.data.vendor_name == "Yuneec" && _information.data.model_name == "E90") {
×
1337
            folder_name_stream << (101 + flight_information.flight_uuid % 899) << "E90HD";
×
1338
        } else if (
×
1339
            _information.data.vendor_name == "Yuneec" && _information.data.model_name == "E50") {
×
1340
            folder_name_stream << (101 + flight_information.flight_uuid % 899) << "E50HD";
×
1341
        } else if (
×
1342
            _information.data.vendor_name == "Yuneec" && _information.data.model_name == "CGOET") {
×
1343
            folder_name_stream << (101 + flight_information.flight_uuid % 899) << "CGOET";
×
1344
        } else if (
×
1345
            _information.data.vendor_name == "Yuneec" && _information.data.model_name == "E10T") {
×
1346
            folder_name_stream << (101 + flight_information.flight_uuid % 899) << "E10T";
×
1347
        } else {
1348
            // Folder name unknown
1349
        }
1350
    }
1351

1352
    {
1353
        std::lock_guard<std::mutex> lock(_status.mutex);
×
1354
        _status.data.media_folder_name = folder_name_stream.str();
×
1355
    }
1356
}
×
1357

1358
void CameraImpl::notify_video_stream_info()
×
1359
{
1360
    std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
1361

1362
    _video_stream_info.subscription_callbacks.queue(
×
1363
        _video_stream_info.data,
×
1364
        [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1365
}
×
1366

1367
void CameraImpl::check_status()
×
1368
{
1369
    std::lock_guard<std::mutex> lock(_status.mutex);
×
1370

1371
    if (_status.received_camera_capture_status && _status.received_storage_information) {
×
1372
        _status.subscription_callbacks.queue(
×
1373
            _status.data, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1374

1375
        _status.received_camera_capture_status = false;
×
1376
        _status.received_storage_information = false;
×
1377
    }
1378
}
×
1379

1380
void CameraImpl::receive_command_result(
×
1381
    MavlinkCommandSender::Result command_result, const Camera::ResultCallback& callback)
1382
{
1383
    Camera::Result camera_result = camera_result_from_command_result(command_result);
×
1384

1385
    if (callback) {
×
1386
        _system_impl->call_user_callback([callback, camera_result]() { callback(camera_result); });
×
1387
    }
1388
}
×
1389

1390
void CameraImpl::receive_set_mode_command_result(
×
1391
    const MavlinkCommandSender::Result command_result,
1392
    const Camera::ResultCallback callback,
1393
    const Camera::Mode mode)
1394
{
1395
    Camera::Result camera_result = camera_result_from_command_result(command_result);
×
1396

1397
    if (callback) {
×
1398
        const auto temp_callback = callback;
×
1399
        _system_impl->call_user_callback(
×
1400
            [temp_callback, camera_result]() { temp_callback(camera_result); });
1401
    }
1402

1403
    if (command_result == MavlinkCommandSender::Result::Success && _camera_definition) {
×
1404
        // This "parameter" needs to be manually set.
1405
        {
1406
            std::lock_guard<std::mutex> lock(_mode.mutex);
×
1407
            _mode.data = mode;
×
1408
        }
1409

1410
        const auto mavlink_mode = to_mavlink_camera_mode(mode);
×
1411

1412
        if (std::isnan(mavlink_mode)) {
×
1413
            LogWarn() << "Unknown camera mode";
×
1414
            return;
×
1415
        }
1416

1417
        notify_mode();
×
1418
        save_camera_mode(mavlink_mode);
×
1419
    }
1420
}
1421

1422
void CameraImpl::notify_mode()
×
1423
{
1424
    std::lock_guard<std::mutex> lock(_mode.mutex);
×
1425

1426
    _mode.subscription_callbacks.queue(
×
1427
        _mode.data, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1428
}
×
1429

1430
bool CameraImpl::get_possible_setting_options(std::vector<std::string>& settings)
×
1431
{
1432
    settings.clear();
×
1433

1434
    if (!_camera_definition) {
×
1435
        LogWarn() << "Error: no camera definition available yet";
×
1436
        return false;
×
1437
    }
1438

1439
    std::unordered_map<std::string, ParamValue> cd_settings{};
×
1440
    _camera_definition->get_possible_settings(cd_settings);
×
1441

1442
    for (const auto& cd_setting : cd_settings) {
×
1443
        if (cd_setting.first == "CAM_MODE") {
×
1444
            // We ignore the mode param.
1445
            continue;
×
1446
        }
1447

1448
        settings.push_back(cd_setting.first);
×
1449
    }
1450

1451
    return settings.size() > 0;
×
1452
}
1453

1454
bool CameraImpl::get_possible_options(
×
1455
    const std::string& setting_id, std::vector<Camera::Option>& options)
1456
{
1457
    options.clear();
×
1458

1459
    if (!_camera_definition) {
×
1460
        LogWarn() << "Error: no camera definition available yet";
×
1461
        return false;
×
1462
    }
1463

1464
    std::vector<ParamValue> values;
×
1465
    if (!_camera_definition->get_possible_options(setting_id, values)) {
×
1466
        return false;
×
1467
    }
1468

1469
    for (const auto& value : values) {
×
1470
        std::stringstream ss{};
×
1471
        ss << value;
×
1472
        Camera::Option option{};
×
1473
        option.option_id = ss.str();
×
1474
        if (!is_setting_range(setting_id)) {
×
1475
            get_option_str(setting_id, option.option_id, option.option_description);
×
1476
        }
1477
        options.push_back(option);
×
1478
    }
1479

1480
    return options.size() > 0;
×
1481
}
1482

1483
bool CameraImpl::is_setting_range(const std::string& setting_id)
×
1484
{
1485
    return _camera_definition->is_setting_range(setting_id);
×
1486
}
1487

1488
Camera::Result CameraImpl::set_setting(Camera::Setting setting)
×
1489
{
1490
    auto prom = std::make_shared<std::promise<Camera::Result>>();
×
1491
    auto ret = prom->get_future();
×
1492

1493
    set_setting_async(setting, [&prom](Camera::Result result) { prom->set_value(result); });
×
1494

1495
    return ret.get();
×
1496
}
1497

1498
void CameraImpl::set_setting_async(Camera::Setting setting, const Camera::ResultCallback callback)
×
1499
{
1500
    set_option_async(setting.setting_id, setting.option, callback);
×
1501
}
×
1502

1503
void CameraImpl::set_option_async(
×
1504
    const std::string& setting_id,
1505
    const Camera::Option& option,
1506
    const Camera::ResultCallback& callback)
1507
{
1508
    if (!_camera_definition) {
×
1509
        LogWarn() << "Error: no camera defnition available yet.";
×
1510
        if (callback) {
×
1511
            const auto temp_callback = callback;
×
1512
            _system_impl->call_user_callback(
×
1513
                [temp_callback]() { temp_callback(Camera::Result::Error); });
1514
        }
1515
        return;
×
1516
    }
1517

1518
    // We get it first so that we have the type of the param value.
1519
    ParamValue value;
×
1520

1521
    if (_camera_definition->is_setting_range(setting_id)) {
×
1522
        // TODO: Get type from minimum.
1523
        std::vector<ParamValue> all_values;
×
1524
        if (!_camera_definition->get_all_options(setting_id, all_values)) {
×
1525
            if (callback) {
×
1526
                LogErr() << "Could not get all options to get type for range param.";
×
1527
                const auto temp_callback = callback;
×
1528
                _system_impl->call_user_callback(
×
1529
                    [temp_callback]() { temp_callback(Camera::Result::Error); });
1530
            }
1531
            return;
×
1532
        }
1533

1534
        if (all_values.size() == 0) {
×
1535
            if (callback) {
×
1536
                LogErr() << "Could not get any options to get type for range param.";
×
1537
                const auto temp_callback = callback;
×
1538
                _system_impl->call_user_callback(
×
1539
                    [temp_callback]() { temp_callback(Camera::Result::Error); });
1540
            }
1541
            return;
×
1542
        }
1543
        value = all_values[0];
×
1544
        // Now re-use that type.
1545
        // FIXME: this is quite ugly, we should do better than that.
1546
        if (!value.set_as_same_type(option.option_id)) {
×
1547
            if (callback) {
×
1548
                LogErr() << "Could not set option value to given type.";
×
1549
                const auto temp_callback = callback;
×
1550
                _system_impl->call_user_callback(
×
1551
                    [temp_callback]() { temp_callback(Camera::Result::Error); });
1552
            }
1553
            return;
×
1554
        }
1555

1556
    } else {
1557
        if (!_camera_definition->get_option_value(setting_id, option.option_id, value)) {
×
1558
            if (callback) {
×
1559
                LogErr() << "Could not get option value.";
×
1560
                const auto temp_callback = callback;
×
1561
                _system_impl->call_user_callback(
×
1562
                    [temp_callback]() { temp_callback(Camera::Result::Error); });
1563
            }
1564
            return;
×
1565
        }
1566

1567
        std::vector<ParamValue> possible_values;
×
1568
        _camera_definition->get_possible_options(setting_id, possible_values);
×
1569
        bool allowed = false;
×
1570
        for (const auto& possible_value : possible_values) {
×
1571
            if (value == possible_value) {
×
1572
                allowed = true;
×
1573
            }
1574
        }
1575
        if (!allowed) {
×
1576
            LogErr() << "Setting " << setting_id << "(" << option.option_id << ") not allowed";
×
1577
            if (callback) {
×
1578
                const auto temp_callback = callback;
×
1579
                _system_impl->call_user_callback(
×
1580
                    [temp_callback]() { temp_callback(Camera::Result::Error); });
1581
            }
1582
            return;
×
1583
        }
1584
    }
1585

1586
    _system_impl->set_param_async(
×
1587
        setting_id,
1588
        value,
1589
        [this, callback, setting_id, value](MavlinkParameterSender::Result result) {
×
1590
            if (result == MavlinkParameterSender::Result::Success) {
×
1591
                if (!this->_camera_definition) {
×
1592
                    if (callback) {
×
1593
                        const auto temp_callback = callback;
×
1594
                        _system_impl->call_user_callback(
×
1595
                            [temp_callback]() { temp_callback(Camera::Result::Error); });
1596
                    }
1597
                    return;
×
1598
                }
1599

1600
                if (!_camera_definition->set_setting(setting_id, value)) {
×
1601
                    if (callback) {
×
1602
                        const auto temp_callback = callback;
×
1603
                        _system_impl->call_user_callback(
×
1604
                            [temp_callback]() { temp_callback(Camera::Result::Error); });
1605
                    }
1606
                    return;
×
1607
                }
1608

1609
                if (callback) {
×
1610
                    const auto temp_callback = callback;
×
1611
                    _system_impl->call_user_callback(
×
1612
                        [temp_callback]() { temp_callback(Camera::Result::Success); });
1613
                }
1614

1615
                // FIXME: We are already holding the lock when this lambda is run and need to
1616
                //        schedule the refresh_params() for later.
1617
                //        We (ab)use the thread pool for the user callbacks for this.
1618
                _system_impl->call_user_callback([this]() { refresh_params(); });
×
1619
            } else {
1620
                if (callback) {
×
1621
                    const auto temp_callback = callback;
×
1622
                    _system_impl->call_user_callback([temp_callback, result]() {
×
1623
                        temp_callback(camera_result_from_parameter_result(result));
1624
                    });
1625
                }
1626
            }
1627
        },
1628
        this,
1629
        static_cast<uint8_t>(_camera_id + MAV_COMP_ID_CAMERA),
×
1630
        true);
1631
}
1632

1633
void CameraImpl::get_setting_async(
×
1634
    Camera::Setting setting, const Camera::GetSettingCallback callback)
1635
{
1636
    get_option_async(
×
1637
        setting.setting_id,
×
1638
        [this, setting, callback](Camera::Result result, const Camera::Option& option) {
×
1639
            Camera::Setting new_setting{};
×
1640
            new_setting.option = option;
×
1641
            if (callback) {
×
1642
                const auto temp_callback = callback;
×
1643
                _system_impl->call_user_callback(
×
1644
                    [temp_callback, result, new_setting]() { temp_callback(result, new_setting); });
1645
            }
1646
        });
×
1647
}
×
1648

1649
std::pair<Camera::Result, Camera::Setting> CameraImpl::get_setting(Camera::Setting setting)
×
1650
{
1651
    auto prom = std::make_shared<std::promise<std::pair<Camera::Result, Camera::Setting>>>();
×
1652
    auto ret = prom->get_future();
×
1653

1654
    get_setting_async(setting, [&prom](Camera::Result result, const Camera::Setting& new_setting) {
×
1655
        prom->set_value(std::make_pair<>(result, new_setting));
×
1656
    });
×
1657

1658
    return ret.get();
×
1659
}
1660

1661
Camera::Result CameraImpl::get_option(const std::string& setting_id, Camera::Option& option)
×
1662
{
1663
    auto prom = std::make_shared<std::promise<Camera::Result>>();
×
1664
    auto ret = prom->get_future();
×
1665

1666
    get_option_async(
×
1667
        setting_id, [prom, &option](Camera::Result result, const Camera::Option& option_gotten) {
×
1668
            prom->set_value(result);
×
1669
            if (result == Camera::Result::Success) {
×
1670
                option = option_gotten;
×
1671
            }
1672
        });
×
1673

1674
    auto status = ret.wait_for(std::chrono::seconds(1));
×
1675

1676
    if (status == std::future_status::ready) {
×
1677
        return Camera::Result::Success;
×
1678
    } else {
1679
        return Camera::Result::Timeout;
×
1680
    }
1681
}
1682

1683
void CameraImpl::get_option_async(
×
1684
    const std::string& setting_id,
1685
    const std::function<void(Camera::Result, const Camera::Option&)>& callback)
1686
{
1687
    if (!_camera_definition) {
×
1688
        LogWarn() << "Error: no camera defnition available yet.";
×
1689
        if (callback) {
×
1690
            Camera::Option empty_option{};
×
1691
            const auto temp_callback = callback;
×
1692
            _system_impl->call_user_callback([temp_callback, empty_option]() {
×
1693
                temp_callback(Camera::Result::Error, empty_option);
1694
            });
1695
        }
1696
        return;
×
1697
    }
1698

1699
    ParamValue value;
×
1700
    // We should have this cached and don't need to get the param.
1701
    if (_camera_definition->get_setting(setting_id, value)) {
×
1702
        if (callback) {
×
1703
            Camera::Option new_option{};
×
1704
            new_option.option_id = value.get_string();
×
1705
            if (!is_setting_range(setting_id)) {
×
1706
                get_option_str(setting_id, new_option.option_id, new_option.option_description);
×
1707
            }
1708
            const auto temp_callback = callback;
×
1709
            _system_impl->call_user_callback([temp_callback, new_option]() {
×
1710
                temp_callback(Camera::Result::Success, new_option);
1711
            });
1712
        }
1713
    } else {
1714
        // If this still happens, we request the param, but also complain.
1715
        LogWarn() << "Setting '" << setting_id << "' not found.";
×
1716
        if (callback) {
×
1717
            Camera::Option no_option{};
×
1718
            const auto temp_callback = callback;
×
1719
            _system_impl->call_user_callback(
×
1720
                [temp_callback, no_option]() { temp_callback(Camera::Result::Error, no_option); });
1721
        }
1722
    }
1723
}
1724

1725
Camera::CurrentSettingsHandle
1726
CameraImpl::subscribe_current_settings(const Camera::CurrentSettingsCallback& callback)
×
1727
{
1728
    std::unique_lock<std::mutex> lock(_subscribe_current_settings.mutex);
×
1729
    auto handle = _subscribe_current_settings.callbacks.subscribe(callback);
×
1730
    lock.unlock();
×
1731

1732
    notify_current_settings();
×
1733
    return handle;
×
1734
}
1735

1736
void CameraImpl::unsubscribe_current_settings(Camera::CurrentSettingsHandle handle)
×
1737
{
1738
    std::lock_guard<std::mutex> lock(_subscribe_current_settings.mutex);
×
1739
    _subscribe_current_settings.callbacks.unsubscribe(handle);
×
1740
}
×
1741

1742
Camera::PossibleSettingOptionsHandle CameraImpl::subscribe_possible_setting_options(
×
1743
    const Camera::PossibleSettingOptionsCallback& callback)
1744
{
1745
    std::unique_lock<std::mutex> lock(_subscribe_possible_setting_options.mutex);
×
1746
    auto handle = _subscribe_possible_setting_options.callbacks.subscribe(callback);
×
1747
    lock.unlock();
×
1748

1749
    notify_possible_setting_options();
×
1750
    return handle;
×
1751
}
1752

1753
void CameraImpl::unsubscribe_possible_setting_options(Camera::PossibleSettingOptionsHandle handle)
×
1754
{
1755
    std::lock_guard<std::mutex> lock(_subscribe_possible_setting_options.mutex);
×
1756
    _subscribe_possible_setting_options.callbacks.unsubscribe(handle);
×
1757
}
×
1758

1759
void CameraImpl::notify_current_settings()
×
1760
{
1761
    std::lock_guard<std::mutex> lock(_subscribe_current_settings.mutex);
×
1762

1763
    if (_subscribe_current_settings.callbacks.empty()) {
×
1764
        return;
×
1765
    }
1766

1767
    if (!_camera_definition) {
×
1768
        LogErr() << "notify_current_settings has no camera definition";
×
1769
        return;
×
1770
    }
1771

1772
    std::vector<Camera::Setting> current_settings{};
×
1773
    std::vector<std::string> possible_setting_options{};
×
1774
    if (!get_possible_setting_options(possible_setting_options)) {
×
1775
        LogErr() << "Could not get possible settings in current options subscription.";
×
1776
        return;
×
1777
    }
1778

1779
    for (auto& possible_setting : possible_setting_options) {
×
1780
        // use the cache for this, presumably we updated it right before.
1781
        ParamValue value;
×
1782
        if (_camera_definition->get_setting(possible_setting, value)) {
×
1783
            Camera::Setting setting{};
×
1784
            setting.setting_id = possible_setting;
×
1785
            setting.is_range = is_setting_range(possible_setting);
×
1786
            get_setting_str(setting.setting_id, setting.setting_description);
×
1787
            setting.option.option_id = value.get_string();
×
1788
            if (!is_setting_range(possible_setting)) {
×
1789
                get_option_str(
×
1790
                    setting.setting_id,
1791
                    setting.option.option_id,
1792
                    setting.option.option_description);
1793
            }
1794
            current_settings.push_back(setting);
×
1795
        }
1796
    }
1797

1798
    _subscribe_current_settings.callbacks.queue(
×
1799
        current_settings, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1800
}
1801

1802
void CameraImpl::notify_possible_setting_options()
×
1803
{
1804
    std::lock_guard<std::mutex> lock(_subscribe_possible_setting_options.mutex);
×
1805

1806
    if (_subscribe_possible_setting_options.callbacks.empty()) {
×
1807
        return;
×
1808
    }
1809

1810
    if (!_camera_definition) {
×
1811
        LogErr() << "notify_possible_setting_options has no camera definition";
×
1812
        return;
×
1813
    }
1814

1815
    auto setting_options = possible_setting_options();
×
1816
    if (setting_options.size() == 0) {
×
1817
        return;
×
1818
    }
1819

1820
    _subscribe_possible_setting_options.callbacks.queue(
×
1821
        setting_options, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1822
}
1823

1824
std::vector<Camera::SettingOptions> CameraImpl::possible_setting_options()
×
1825
{
1826
    std::vector<Camera::SettingOptions> results{};
×
1827

1828
    std::vector<std::string> possible_settings{};
×
1829
    if (!get_possible_setting_options(possible_settings)) {
×
1830
        LogErr() << "Could not get possible settings.";
×
1831
        return results;
×
1832
    }
1833

1834
    for (auto& possible_setting : possible_settings) {
×
1835
        Camera::SettingOptions setting_options{};
×
1836
        setting_options.setting_id = possible_setting;
×
1837
        setting_options.is_range = _camera_definition->is_setting_range(possible_setting);
×
1838
        get_setting_str(setting_options.setting_id, setting_options.setting_description);
×
1839
        get_possible_options(possible_setting, setting_options.options);
×
1840
        results.push_back(setting_options);
×
1841
    }
1842

1843
    return results;
1844
}
1845

1846
void CameraImpl::refresh_params()
×
1847
{
1848
    if (!_camera_definition) {
×
1849
        return;
×
1850
    }
1851

1852
    std::vector<std::pair<std::string, ParamValue>> params;
×
1853
    _camera_definition->get_unknown_params(params);
×
1854
    if (params.size() == 0) {
×
1855
        // We're assuming that we changed one option and this did not cause
1856
        // any other possible settings to change. However, we still would
1857
        // like to notify the current settings with this one change.
1858
        notify_current_settings();
×
1859
        notify_possible_setting_options();
×
1860
        return;
×
1861
    }
1862

1863
    unsigned count = 0;
×
1864
    for (const auto& param : params) {
×
1865
        const std::string& param_name = param.first;
×
1866
        const ParamValue& param_value_type = param.second;
×
1867
        const bool is_last = (count == params.size() - 1);
×
1868
        _system_impl->get_param_async(
×
1869
            param_name,
1870
            param_value_type,
1871
            [param_name, is_last, this](MavlinkParameterSender::Result result, ParamValue value) {
×
1872
                if (result != MavlinkParameterSender::Result::Success) {
×
1873
                    return;
×
1874
                }
1875
                // We need to check again by the time this callback runs
1876
                if (!this->_camera_definition) {
×
1877
                    return;
×
1878
                }
1879

1880
                if (!this->_camera_definition->set_setting(param_name, value)) {
×
1881
                    return;
×
1882
                }
1883

1884
                if (is_last) {
×
1885
                    notify_current_settings();
×
1886
                    notify_possible_setting_options();
×
1887
                }
1888
            },
1889
            this,
1890
            static_cast<uint8_t>(_camera_id + MAV_COMP_ID_CAMERA),
×
1891
            true);
1892
        ++count;
×
1893
    }
1894
}
1895

1896
void CameraImpl::invalidate_params()
×
1897
{
1898
    if (!_camera_definition) {
×
1899
        return;
×
1900
    }
1901

1902
    _camera_definition->set_all_params_unknown();
×
1903
}
1904

1905
bool CameraImpl::get_setting_str(const std::string& setting_id, std::string& description)
×
1906
{
1907
    if (!_camera_definition) {
×
1908
        return false;
×
1909
    }
1910

1911
    return _camera_definition->get_setting_str(setting_id, description);
×
1912
}
1913

1914
bool CameraImpl::get_option_str(
×
1915
    const std::string& setting_id, const std::string& option_id, std::string& description)
1916
{
1917
    if (!_camera_definition) {
×
1918
        return false;
×
1919
    }
1920

1921
    return _camera_definition->get_option_str(setting_id, option_id, description);
×
1922
}
1923

1924
void CameraImpl::request_camera_settings()
×
1925
{
1926
    auto command_camera_settings = make_command_request_camera_settings();
×
1927
    _system_impl->send_command_async(command_camera_settings, nullptr);
×
1928
}
×
1929

1930
void CameraImpl::request_flight_information()
×
1931
{
1932
    auto command_flight_information = make_command_request_flight_information();
×
1933
    _system_impl->send_command_async(command_flight_information, nullptr);
×
1934
}
×
1935

1936
void CameraImpl::request_camera_information()
×
1937
{
1938
    auto command_camera_info = make_command_request_camera_info();
×
1939
    _system_impl->send_command_async(command_camera_info, nullptr);
×
1940
}
×
1941

1942
Camera::Result CameraImpl::format_storage()
×
1943
{
1944
    auto prom = std::make_shared<std::promise<Camera::Result>>();
×
1945
    auto ret = prom->get_future();
×
1946

1947
    format_storage_async([prom](Camera::Result result) { prom->set_value(result); });
×
1948

1949
    return ret.get();
×
1950
}
1951

1952
void CameraImpl::format_storage_async(Camera::ResultCallback callback)
×
1953
{
1954
    MavlinkCommandSender::CommandLong cmd_format{};
×
1955

1956
    cmd_format.command = MAV_CMD_STORAGE_FORMAT;
×
1957
    cmd_format.params.maybe_param1 = 1.0f; // storage ID
×
1958
    cmd_format.params.maybe_param2 = 1.0f; // format
×
1959
    cmd_format.params.maybe_param3 = 1.0f; // clear
×
1960
    cmd_format.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
1961

1962
    _system_impl->send_command_async(
×
1963
        cmd_format, [this, callback](MavlinkCommandSender::Result result, float progress) {
×
1964
            UNUSED(progress);
×
1965

1966
            receive_command_result(result, [this, callback](Camera::Result camera_result) {
×
1967
                if (camera_result == Camera::Result::Success) {
×
1968
                    reset_following_format_storage();
×
1969
                }
1970

1971
                callback(camera_result);
×
1972
            });
×
1973
        });
×
1974
}
×
1975

1976
void CameraImpl::reset_following_format_storage()
×
1977
{
1978
    {
1979
        std::lock_guard<std::mutex> status_lock(_status.mutex);
×
1980
        _status.photo_list.clear();
×
1981
        _status.image_count = 0;
×
1982
        _status.image_count_at_connection = 0;
×
1983
    }
1984
    {
1985
        std::lock_guard<std::mutex> lock(_capture_info.mutex);
×
1986
        _capture_info.last_advertised_image_index = -1;
×
1987
        _capture_info.missing_image_retries.clear();
×
1988
    }
1989
}
×
1990

1991
std::pair<Camera::Result, std::vector<Camera::CaptureInfo>>
1992
CameraImpl::list_photos(Camera::PhotosRange photos_range)
×
1993
{
1994
    std::promise<std::pair<Camera::Result, std::vector<Camera::CaptureInfo>>> prom;
×
1995
    auto ret = prom.get_future();
×
1996

1997
    list_photos_async(
×
1998
        photos_range, [&prom](Camera::Result result, std::vector<Camera::CaptureInfo> photo_list) {
×
1999
            prom.set_value(std::make_pair(result, photo_list));
×
2000
        });
×
2001

2002
    return ret.get();
×
2003
}
2004

2005
void CameraImpl::list_photos_async(
×
2006
    Camera::PhotosRange photos_range, const Camera::ListPhotosCallback callback)
2007
{
2008
    if (!callback) {
×
2009
        LogWarn() << "Trying to get a photo list with a null callback, ignoring...";
×
2010
        return;
×
2011
    }
2012

2013
    {
2014
        std::lock_guard<std::mutex> status_lock(_status.mutex);
×
2015

2016
        if (_status.is_fetching_photos) {
×
2017
            _system_impl->call_user_callback([callback]() {
×
2018
                callback(Camera::Result::Busy, std::vector<Camera::CaptureInfo>{});
2019
            });
2020
            return;
×
2021
        } else {
2022
            _status.is_fetching_photos = true;
×
2023
        }
2024

2025
        if (_status.image_count == -1) {
×
2026
            LogErr() << "Cannot list photos: camera status has not been received yet!";
×
2027
            _status.is_fetching_photos = false;
×
2028
            _system_impl->call_user_callback([callback]() {
×
2029
                callback(Camera::Result::Error, std::vector<Camera::CaptureInfo>{});
2030
            });
2031
            return;
×
2032
        }
2033
    }
2034

2035
    const int start_index = [this, photos_range]() {
×
2036
        switch (photos_range) {
×
2037
            case Camera::PhotosRange::SinceConnection:
×
2038
                return _status.image_count_at_connection;
×
2039
            case Camera::PhotosRange::All:
×
2040
            // FALLTHROUGH
2041
            default:
2042
                return 0;
×
2043
        }
2044
    }();
×
2045

2046
    std::thread([this, start_index, callback]() {
×
2047
        std::unique_lock<std::mutex> capture_request_lock(_captured_request_mutex);
×
2048

2049
        for (int i = start_index; i < _status.image_count; i++) {
×
2050
            // In case the vehicle sends capture info, but not those we are asking, we do not
2051
            // want to loop infinitely. The safety_count is here to abort if this happens.
2052
            auto safety_count = 0;
×
2053
            const auto safety_count_boundary = 10;
×
2054

2055
            while (_status.photo_list.find(i) == _status.photo_list.end() &&
×
2056
                   safety_count < safety_count_boundary) {
2057
                safety_count++;
×
2058

2059
                auto request_try_number = 0;
×
2060
                const auto request_try_limit =
×
2061
                    10; // Timeout if the request times out that many times
2062
                auto cv_status = std::cv_status::timeout;
×
2063

2064
                while (cv_status == std::cv_status::timeout) {
×
2065
                    request_try_number++;
×
2066
                    if (request_try_number >= request_try_limit) {
×
2067
                        std::lock_guard<std::mutex> status_lock(_status.mutex);
×
2068
                        _status.is_fetching_photos = false;
×
2069
                        _system_impl->call_user_callback([callback]() {
×
2070
                            callback(Camera::Result::Timeout, std::vector<Camera::CaptureInfo>{});
2071
                        });
2072
                        return;
×
2073
                    }
2074

2075
                    _system_impl->send_command_async(
×
2076
                        make_command_request_camera_image_captured(i), nullptr);
2077
                    cv_status = _captured_request_cv.wait_for(
×
2078
                        capture_request_lock, std::chrono::seconds(1));
×
2079
                }
2080
            }
2081

2082
            if (safety_count == safety_count_boundary) {
×
2083
                std::lock_guard<std::mutex> status_lock(_status.mutex);
×
2084
                _status.is_fetching_photos = false;
×
2085
                _system_impl->call_user_callback([callback]() {
×
2086
                    callback(Camera::Result::Error, std::vector<Camera::CaptureInfo>{});
2087
                });
2088
                return;
×
2089
            }
2090
        }
2091

2092
        std::vector<Camera::CaptureInfo> photo_list;
×
2093
        {
2094
            std::lock_guard<std::mutex> status_lock(_status.mutex);
×
2095

2096
            for (auto capture_info : _status.photo_list) {
×
2097
                if (capture_info.first >= start_index) {
×
2098
                    photo_list.push_back(capture_info.second);
×
2099
                }
2100
            }
2101

2102
            _status.is_fetching_photos = false;
×
2103

2104
            const auto temp_callback = callback;
×
2105
            _system_impl->call_user_callback([temp_callback, photo_list]() {
×
2106
                temp_callback(Camera::Result::Success, photo_list);
2107
            });
2108
        }
2109
    }).detach();
×
2110
}
2111

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

© 2025 Coveralls, Inc