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

mavlink / MAVSDK / 9247876068

27 May 2024 01:35AM UTC coverage: 37.127% (+0.1%) from 37.012%
9247876068

push

github

web-flow
Merge pull request #2312 from mavlink/pr-cookies

Use uint64_t instead of void * as cookies

120 of 207 new or added lines in 25 files covered. (57.97%)

6 existing lines in 3 files now uncovered.

10931 of 29442 relevant lines covered (37.13%)

120.7 hits per line

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

4.13
/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 "unused.h"
7
#include "callback_list.tpp"
8

9
#include <algorithm>
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_with_compid(
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_with_compid(
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_with_compid(
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_with_compid(
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_with_compid(
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_with_compid(
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_with_compid(
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
    _check_connection_status_call_every_cookie =
1✔
83
        _system_impl->add_call_every([this]() { check_connection_status(); }, 0.5);
2✔
84

85
    _request_missing_capture_info_cookie =
1✔
86
        _system_impl->add_call_every([this]() { request_missing_capture_info(); }, 0.5);
2✔
87
}
1✔
88

89
void CameraImpl::deinit()
1✔
90
{
91
    _system_impl->remove_call_every(_request_missing_capture_info_cookie);
1✔
92
    _system_impl->remove_call_every(_check_connection_status_call_every_cookie);
1✔
93
    _system_impl->remove_call_every(_status.call_every_cookie);
1✔
94
    _system_impl->remove_call_every(_camera_information_call_every_cookie);
1✔
95
    _system_impl->remove_call_every(_mode.call_every_cookie);
1✔
96
    _system_impl->remove_call_every(_video_stream_info.call_every_cookie);
1✔
97
    _system_impl->unregister_all_mavlink_message_handlers(this);
1✔
98
    _system_impl->cancel_all_param(this);
1✔
99

100
    {
101
        std::lock_guard<std::mutex> lock(_status.mutex);
2✔
102
        _status.subscription_callbacks.clear();
1✔
103
    }
104

105
    {
106
        std::lock_guard<std::mutex> lock(_mode.mutex);
2✔
107
        _mode.subscription_callbacks.clear();
1✔
108
    }
109

110
    {
111
        std::lock_guard<std::mutex> lock(_capture_info.mutex);
2✔
112
        _capture_info.callbacks.clear();
1✔
113
    }
114

115
    {
116
        std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
2✔
117
        _video_stream_info.subscription_callbacks.clear();
1✔
118
    }
119

120
    {
121
        std::lock_guard<std::mutex> lock(_information.mutex);
2✔
122
        _information.subscription_callbacks.clear();
1✔
123
    }
124

125
    {
126
        std::lock_guard<std::mutex> lock(_subscribe_current_settings.mutex);
2✔
127
        _subscribe_current_settings.callbacks.clear();
1✔
128
    }
129

130
    {
131
        std::lock_guard<std::mutex> lock(_subscribe_possible_setting_options.mutex);
2✔
132
        _subscribe_possible_setting_options.callbacks.clear();
1✔
133
    }
134

135
    _camera_found = false;
1✔
136
}
1✔
137

138
Camera::Result CameraImpl::prepare()
×
139
{
140
    auto prom = std::make_shared<std::promise<Camera::Result>>();
×
141
    auto ret = prom->get_future();
×
142

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

145
    return ret.get();
×
146
}
147

148
void CameraImpl::prepare_async(const Camera::ResultCallback& callback)
×
149
{
150
    auto temp_callback = callback;
×
151

152
    std::lock_guard<std::mutex> lock(_information.mutex);
×
153

154
    if (_camera_definition) {
×
155
        _system_impl->call_user_callback(
×
156
            [temp_callback]() { temp_callback(Camera::Result::Success); });
157
    } else {
158
        _camera_definition_callback = [this, temp_callback](Camera::Result result) {
×
159
            temp_callback(result);
×
160
            _camera_definition_callback = nullptr;
×
161
        };
×
162

163
        if (_has_camera_definition_timed_out) {
×
164
            // Try to download the camera_definition again
165
            _has_camera_definition_timed_out = false;
×
166
            request_camera_information();
×
167
        }
168
    }
169
}
×
170

171
void CameraImpl::check_connection_status()
×
172
{
173
    // FIXME: This is a workaround because we don't want to be tied to the
174
    // discovery of an autopilot which triggers enable() and disable() but
175
    // we are interested if a camera is connected or not.
176
    if (_system_impl->has_camera(_camera_id)) {
×
177
        if (!_camera_found) {
×
178
            _camera_found = true;
×
179
            manual_enable();
×
180
        }
181
    }
182
}
×
183

184
void CameraImpl::enable()
1✔
185
{
186
    // FIXME: We check for the connection status manually because
187
    // we're not interested in the connection state of the autopilot
188
    // but only the camera.
189
}
1✔
190

191
void CameraImpl::manual_enable()
×
192
{
193
    refresh_params();
×
194

195
    request_status();
×
196
    request_camera_information();
×
197

NEW
198
    _camera_information_call_every_cookie =
×
NEW
199
        _system_impl->add_call_every([this]() { request_camera_information(); }, 10.0);
×
200

NEW
201
    _status.call_every_cookie = _system_impl->add_call_every([this]() { request_status(); }, 5.0);
×
202
}
×
203

204
void CameraImpl::disable()
1✔
205
{
206
    // FIXME: We check for the connection status manually because
207
    // we're not interested in the connection state of the autopilot
208
    // but only the camera.
209
}
1✔
210

211
void CameraImpl::manual_disable()
×
212
{
213
    invalidate_params();
×
214
    _system_impl->remove_call_every(_camera_information_call_every_cookie);
×
215
    _system_impl->remove_call_every(_status.call_every_cookie);
×
216

217
    _camera_found = false;
×
218
}
×
219

220
void CameraImpl::update_component()
×
221
{
222
    uint8_t cmp_id = _camera_id + MAV_COMP_ID_CAMERA;
×
223
    _system_impl->update_component_id_messages_handler(
×
224
        MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS, cmp_id, this);
225

226
    _system_impl->update_component_id_messages_handler(
×
227
        MAVLINK_MSG_ID_STORAGE_INFORMATION, cmp_id, this);
228

229
    _system_impl->update_component_id_messages_handler(
×
230
        MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED, cmp_id, this);
231

232
    _system_impl->update_component_id_messages_handler(
×
233
        MAVLINK_MSG_ID_CAMERA_SETTINGS, cmp_id, this);
234

235
    _system_impl->update_component_id_messages_handler(
×
236
        MAVLINK_MSG_ID_CAMERA_INFORMATION, cmp_id, this);
237

238
    _system_impl->update_component_id_messages_handler(
×
239
        MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION, cmp_id, this);
240

241
    _system_impl->update_component_id_messages_handler(
×
242
        MAVLINK_MSG_ID_VIDEO_STREAM_STATUS, cmp_id, this);
243
}
×
244

245
Camera::Result CameraImpl::select_camera(const size_t id)
×
246
{
247
    static constexpr std::size_t MAX_SUPPORTED_ID = 5;
248

249
    if (id > MAX_SUPPORTED_ID) {
×
250
        return Camera::Result::WrongArgument;
×
251
    }
252

253
    // camera component IDs go from 100 to 105.
254
    _camera_id = id;
×
255

256
    // We should probably reload everything to make sure the
257
    // correct  camera is initialized.
258
    update_component();
×
259
    manual_disable();
×
260
    manual_enable();
×
261

262
    return Camera::Result::Success;
×
263
}
264

265
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_camera_info()
×
266
{
267
    MavlinkCommandSender::CommandLong command_camera_info{};
×
268

269
    command_camera_info.command = MAV_CMD_REQUEST_CAMERA_INFORMATION;
×
270
    command_camera_info.params.maybe_param1 = 1.0f; // Request it
×
271
    command_camera_info.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
272

273
    return command_camera_info;
×
274
}
275

276
MavlinkCommandSender::CommandLong
277
CameraImpl::make_command_take_photo(float interval_s, float no_of_photos)
×
278
{
279
    MavlinkCommandSender::CommandLong cmd_take_photo{};
×
280

281
    cmd_take_photo.command = MAV_CMD_IMAGE_START_CAPTURE;
×
282
    cmd_take_photo.params.maybe_param1 = 0.0f; // Reserved, set to 0
×
283
    cmd_take_photo.params.maybe_param2 = interval_s;
×
284
    cmd_take_photo.params.maybe_param3 = no_of_photos;
×
285
    cmd_take_photo.params.maybe_param4 = static_cast<float>(_capture.sequence++);
×
286
    cmd_take_photo.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
287

288
    return cmd_take_photo;
×
289
}
290

291
MavlinkCommandSender::CommandLong CameraImpl::make_command_zoom_out()
×
292
{
293
    MavlinkCommandSender::CommandLong cmd{};
×
294
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
295
    cmd.params.maybe_param1 = (float)ZOOM_TYPE_CONTINUOUS;
×
296
    cmd.params.maybe_param2 = -1.f;
×
297
    cmd.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
298

299
    return cmd;
×
300
}
301

302
MavlinkCommandSender::CommandLong CameraImpl::make_command_zoom_in()
×
303
{
304
    MavlinkCommandSender::CommandLong cmd{};
×
305
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
306
    cmd.params.maybe_param1 = (float)ZOOM_TYPE_CONTINUOUS;
×
307
    cmd.params.maybe_param2 = 1.f;
×
308
    cmd.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
309

310
    return cmd;
×
311
}
312

313
MavlinkCommandSender::CommandLong CameraImpl::make_command_zoom_stop()
×
314
{
315
    MavlinkCommandSender::CommandLong cmd{};
×
316
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
317
    cmd.params.maybe_param1 = (float)ZOOM_TYPE_CONTINUOUS;
×
318
    cmd.params.maybe_param2 = 0.f;
×
319
    cmd.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
320

321
    return cmd;
×
322
}
323

324
MavlinkCommandSender::CommandLong CameraImpl::make_command_zoom_range(float range)
×
325
{
326
    // Clip to safe range.
327
    range = std::max(0.f, std::min(range, 100.f));
×
328

329
    MavlinkCommandSender::CommandLong cmd{};
×
330
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
331
    cmd.params.maybe_param1 = (float)ZOOM_TYPE_RANGE;
×
332
    cmd.params.maybe_param2 = range;
×
333
    cmd.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
334

335
    return cmd;
×
336
}
337

338
MavlinkCommandSender::CommandLong
339
CameraImpl::make_command_track_point(float point_x, float point_y, float radius)
×
340
{
341
    MavlinkCommandSender::CommandLong cmd{};
×
342
    cmd.command = MAV_CMD_CAMERA_TRACK_POINT;
×
343
    cmd.params.maybe_param1 = (float)point_x;
×
344
    cmd.params.maybe_param2 = (float)point_y;
×
345
    cmd.params.maybe_param3 = (float)radius;
×
346
    cmd.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
347

348
    return cmd;
×
349
}
350

351
MavlinkCommandSender::CommandLong CameraImpl::make_command_track_rectangle(
×
352
    float top_left_x, float top_left_y, float bottom_right_x, float bottom_right_y)
353
{
354
    MavlinkCommandSender::CommandLong cmd{};
×
355
    cmd.command = MAV_CMD_CAMERA_TRACK_RECTANGLE;
×
356
    cmd.params.maybe_param1 = top_left_x;
×
357
    cmd.params.maybe_param2 = top_left_y;
×
358
    cmd.params.maybe_param3 = bottom_right_x;
×
359
    cmd.params.maybe_param4 = bottom_right_y;
×
360
    cmd.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
361

362
    return cmd;
×
363
}
364
MavlinkCommandSender::CommandLong CameraImpl::make_command_track_stop()
×
365
{
366
    MavlinkCommandSender::CommandLong cmd{};
×
367
    cmd.command = MAV_CMD_CAMERA_STOP_TRACKING;
×
368
    cmd.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
369

370
    return cmd;
×
371
}
372
MavlinkCommandSender::CommandLong CameraImpl::make_command_focus_in()
×
373
{
374
    MavlinkCommandSender::CommandLong cmd{};
×
375
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
376
    cmd.params.maybe_param1 = (float)FOCUS_TYPE_CONTINUOUS;
×
377
    cmd.params.maybe_param2 = -1.f;
×
378
    cmd.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
379

380
    return cmd;
×
381
}
382
MavlinkCommandSender::CommandLong CameraImpl::make_command_focus_out()
×
383
{
384
    MavlinkCommandSender::CommandLong cmd{};
×
385
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
386
    cmd.params.maybe_param1 = (float)FOCUS_TYPE_CONTINUOUS;
×
387
    cmd.params.maybe_param2 = 1.f;
×
388
    cmd.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
389

390
    return cmd;
×
391
}
392
MavlinkCommandSender::CommandLong CameraImpl::make_command_focus_stop()
×
393
{
394
    MavlinkCommandSender::CommandLong cmd{};
×
395
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
396
    cmd.params.maybe_param1 = (float)FOCUS_TYPE_CONTINUOUS;
×
397
    cmd.params.maybe_param2 = 0.f;
×
398
    cmd.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
399

400
    return cmd;
×
401
}
402
MavlinkCommandSender::CommandLong CameraImpl::make_command_focus_range(float range)
×
403
{
404
    // Clip to safe range.
405
    range = std::max(0.f, std::min(range, 100.f));
×
406

407
    MavlinkCommandSender::CommandLong cmd{};
×
408
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
409
    cmd.params.maybe_param1 = (float)FOCUS_TYPE_RANGE;
×
410
    cmd.params.maybe_param2 = range;
×
411
    cmd.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
412

413
    return cmd;
×
414
}
415

416
MavlinkCommandSender::CommandLong CameraImpl::make_command_stop_photo()
×
417
{
418
    MavlinkCommandSender::CommandLong cmd_stop_photo{};
×
419

420
    cmd_stop_photo.command = MAV_CMD_IMAGE_STOP_CAPTURE;
×
421
    cmd_stop_photo.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
422

423
    return cmd_stop_photo;
×
424
}
425

426
MavlinkCommandSender::CommandLong CameraImpl::make_command_start_video(float capture_status_rate_hz)
×
427
{
428
    MavlinkCommandSender::CommandLong cmd_start_video{};
×
429

430
    cmd_start_video.command = MAV_CMD_VIDEO_START_CAPTURE;
×
431
    cmd_start_video.params.maybe_param1 = 0.f; // Reserved, set to 0
×
432
    cmd_start_video.params.maybe_param2 = capture_status_rate_hz;
×
433
    cmd_start_video.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
434

435
    return cmd_start_video;
×
436
}
437

438
MavlinkCommandSender::CommandLong CameraImpl::make_command_stop_video()
×
439
{
440
    MavlinkCommandSender::CommandLong cmd_stop_video{};
×
441

442
    cmd_stop_video.command = MAV_CMD_VIDEO_STOP_CAPTURE;
×
443
    cmd_stop_video.params.maybe_param1 = 0.f; // Reserved, set to 0
×
444
    cmd_stop_video.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
445

446
    return cmd_stop_video;
×
447
}
448

449
MavlinkCommandSender::CommandLong CameraImpl::make_command_set_camera_mode(float mavlink_mode)
×
450
{
451
    MavlinkCommandSender::CommandLong cmd_set_camera_mode{};
×
452

453
    cmd_set_camera_mode.command = MAV_CMD_SET_CAMERA_MODE;
×
454
    cmd_set_camera_mode.params.maybe_param1 = 0.0f; // Reserved, set to 0
×
455
    cmd_set_camera_mode.params.maybe_param2 = mavlink_mode;
×
456
    cmd_set_camera_mode.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
457

458
    return cmd_set_camera_mode;
×
459
}
460

461
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_camera_settings()
×
462
{
463
    MavlinkCommandSender::CommandLong cmd_req_camera_settings{};
×
464

465
    cmd_req_camera_settings.command = MAV_CMD_REQUEST_CAMERA_SETTINGS;
×
466
    cmd_req_camera_settings.params.maybe_param1 = 1.f; // Request it
×
467
    cmd_req_camera_settings.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
468

469
    return cmd_req_camera_settings;
×
470
}
471

472
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_camera_capture_status()
×
473
{
474
    MavlinkCommandSender::CommandLong cmd_req_camera_cap_stat{};
×
475

476
    cmd_req_camera_cap_stat.command = MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS;
×
477
    cmd_req_camera_cap_stat.params.maybe_param1 = 1.0f; // Request it
×
478
    cmd_req_camera_cap_stat.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
479

480
    return cmd_req_camera_cap_stat;
×
481
}
482

483
MavlinkCommandSender::CommandLong
484
CameraImpl::make_command_request_camera_image_captured(const size_t photo_id)
×
485
{
486
    MavlinkCommandSender::CommandLong cmd_req_camera_image_captured{};
×
487

488
    cmd_req_camera_image_captured.command = MAV_CMD_REQUEST_MESSAGE;
×
489
    cmd_req_camera_image_captured.params.maybe_param1 =
×
490
        static_cast<float>(MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED);
×
491
    cmd_req_camera_image_captured.params.maybe_param2 = static_cast<float>(photo_id);
×
492
    cmd_req_camera_image_captured.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
493

494
    return cmd_req_camera_image_captured;
×
495
}
496

497
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_storage_info()
×
498
{
499
    MavlinkCommandSender::CommandLong cmd_req_storage_info{};
×
500

501
    cmd_req_storage_info.command = MAV_CMD_REQUEST_STORAGE_INFORMATION;
×
502
    cmd_req_storage_info.params.maybe_param1 = 0.f; // Reserved, set to 0
×
503
    cmd_req_storage_info.params.maybe_param2 = 1.f; // Request it
×
504
    cmd_req_storage_info.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
505

506
    return cmd_req_storage_info;
×
507
}
508

509
MavlinkCommandSender::CommandLong CameraImpl::make_command_start_video_streaming(int32_t stream_id)
×
510
{
511
    MavlinkCommandSender::CommandLong cmd_start_video_streaming{};
×
512

513
    cmd_start_video_streaming.command = MAV_CMD_VIDEO_START_STREAMING;
×
514
    cmd_start_video_streaming.params.maybe_param1 = static_cast<float>(stream_id);
×
515
    cmd_start_video_streaming.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
516

517
    return cmd_start_video_streaming;
×
518
}
519

520
MavlinkCommandSender::CommandLong CameraImpl::make_command_stop_video_streaming(int32_t stream_id)
×
521
{
522
    MavlinkCommandSender::CommandLong cmd_stop_video_streaming{};
×
523

524
    cmd_stop_video_streaming.command = MAV_CMD_VIDEO_STOP_STREAMING;
×
525
    cmd_stop_video_streaming.params.maybe_param1 = static_cast<float>(stream_id);
×
526
    cmd_stop_video_streaming.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
527

528
    return cmd_stop_video_streaming;
×
529
}
530

531
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_video_stream_info()
×
532
{
533
    MavlinkCommandSender::CommandLong cmd_req_video_stream_info{};
×
534

535
    cmd_req_video_stream_info.command = MAV_CMD_REQUEST_VIDEO_STREAM_INFORMATION;
×
536
    cmd_req_video_stream_info.params.maybe_param2 = 1.0f;
×
537
    cmd_req_video_stream_info.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
538

539
    return cmd_req_video_stream_info;
×
540
}
541

542
MavlinkCommandSender::CommandLong CameraImpl::make_command_request_video_stream_status()
×
543
{
544
    MavlinkCommandSender::CommandLong cmd_req_video_stream_status{};
×
545

546
    cmd_req_video_stream_status.command = MAV_CMD_REQUEST_VIDEO_STREAM_STATUS;
×
547
    cmd_req_video_stream_status.params.maybe_param2 = 1.0f;
×
548
    cmd_req_video_stream_status.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
549

550
    return cmd_req_video_stream_status;
×
551
}
552

553
Camera::Result CameraImpl::take_photo()
×
554
{
555
    // TODO: check whether we are in photo mode.
556

557
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
558

559
    // Take 1 photo only with no interval
560
    auto cmd_take_photo = make_command_take_photo(0.f, 1.0f);
×
561

562
    return camera_result_from_command_result(_system_impl->send_command(cmd_take_photo));
×
563
}
564

565
Camera::Result CameraImpl::zoom_out_start()
×
566
{
567
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
568

569
    auto cmd = make_command_zoom_out();
×
570

571
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
572
}
573

574
Camera::Result CameraImpl::zoom_in_start()
×
575
{
576
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
577

578
    auto cmd = make_command_zoom_in();
×
579

580
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
581
}
582

583
Camera::Result CameraImpl::zoom_stop()
×
584
{
585
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
586

587
    auto cmd = make_command_zoom_stop();
×
588

589
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
590
}
591

592
Camera::Result CameraImpl::zoom_range(float range)
×
593
{
594
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
595

596
    auto cmd = make_command_zoom_range(range);
×
597

598
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
599
}
600

601
Camera::Result CameraImpl::track_point(float point_x, float point_y, float radius)
×
602
{
603
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
604

605
    auto cmd = make_command_track_point(point_x, point_y, radius);
×
606

607
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
608
}
609

610
Camera::Result CameraImpl::track_rectangle(
×
611
    float top_left_x, float top_left_y, float bottom_right_x, float bottom_right_y)
612
{
613
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
614

615
    auto cmd = make_command_track_rectangle(top_left_x, top_left_y, bottom_right_x, bottom_right_y);
×
616

617
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
618
}
619

620
Camera::Result CameraImpl::track_stop()
×
621
{
622
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
623

624
    auto cmd = make_command_track_stop();
×
625

626
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
627
}
628

629
Camera::Result CameraImpl::focus_in_start()
×
630
{
631
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
632

633
    auto cmd = make_command_focus_in();
×
634

635
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
636
}
637

638
Camera::Result CameraImpl::focus_out_start()
×
639
{
640
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
641

642
    auto cmd = make_command_focus_out();
×
643

644
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
645
}
646

647
Camera::Result CameraImpl::focus_stop()
×
648
{
649
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
650

651
    auto cmd = make_command_focus_stop();
×
652

653
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
654
}
655

656
Camera::Result CameraImpl::focus_range(float range)
×
657
{
658
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
659

660
    auto cmd = make_command_focus_range(range);
×
661

662
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
663
}
664

665
Camera::Result CameraImpl::start_photo_interval(float interval_s)
×
666
{
667
    if (!interval_valid(interval_s)) {
×
668
        return Camera::Result::WrongArgument;
×
669
    }
670

671
    // TODO: check whether we are in photo mode.
672

673
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
674

675
    auto cmd_take_photo_time_lapse = make_command_take_photo(interval_s, 0.f);
×
676

677
    return camera_result_from_command_result(_system_impl->send_command(cmd_take_photo_time_lapse));
×
678
}
679

680
Camera::Result CameraImpl::stop_photo_interval()
×
681
{
682
    auto cmd_stop_photo_interval = make_command_stop_photo();
×
683

684
    return camera_result_from_command_result(_system_impl->send_command(cmd_stop_photo_interval));
×
685
}
686

687
Camera::Result CameraImpl::start_video()
×
688
{
689
    // TODO: check whether video capture is already in progress.
690
    // TODO: check whether we are in video mode.
691

692
    // Capture status rate is not set
693
    auto cmd_start_video = make_command_start_video(0.f);
×
694

695
    return camera_result_from_command_result(_system_impl->send_command(cmd_start_video));
×
696
}
697

698
Camera::Result CameraImpl::stop_video()
×
699
{
700
    auto cmd_stop_video = make_command_stop_video();
×
701

702
    {
703
        std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
704
        _video_stream_info.data.status = Camera::VideoStreamInfo::VideoStreamStatus::NotRunning;
×
705
    }
706

707
    return camera_result_from_command_result(_system_impl->send_command(cmd_stop_video));
×
708
}
709

710
void CameraImpl::zoom_in_start_async(const Camera::ResultCallback& callback)
×
711
{
712
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
713

714
    auto cmd = make_command_zoom_in();
×
715

716
    _system_impl->send_command_async(
×
717
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
718
            receive_command_result(result, callback);
×
719
        });
×
720
}
×
721

722
void CameraImpl::zoom_out_start_async(const Camera::ResultCallback& callback)
×
723
{
724
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
725

726
    auto cmd = make_command_zoom_out();
×
727

728
    _system_impl->send_command_async(
×
729
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
730
            receive_command_result(result, callback);
×
731
        });
×
732
}
×
733

734
void CameraImpl::zoom_stop_async(const Camera::ResultCallback& callback)
×
735
{
736
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
737

738
    auto cmd = make_command_zoom_stop();
×
739

740
    _system_impl->send_command_async(
×
741
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
742
            receive_command_result(result, callback);
×
743
        });
×
744
}
×
745

746
void CameraImpl::zoom_range_async(float range, const Camera::ResultCallback& callback)
×
747
{
748
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
749

750
    auto cmd = make_command_zoom_range(range);
×
751

752
    _system_impl->send_command_async(
×
753
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
754
            receive_command_result(result, callback);
×
755
        });
×
756
}
×
757

758
void CameraImpl::track_point_async(
×
759
    float point_x, float point_y, float radius, const Camera::ResultCallback& callback)
760
{
761
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
762

763
    auto cmd = make_command_track_point(point_x, point_y, radius);
×
764

765
    _system_impl->send_command_async(
×
766
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
767
            receive_command_result(result, callback);
×
768
        });
×
769
}
×
770

771
void CameraImpl::track_rectangle_async(
×
772
    float top_left_x,
773
    float top_left_y,
774
    float bottom_right_x,
775
    float bottom_right_y,
776
    const Camera::ResultCallback& callback)
777
{
778
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
779

780
    auto cmd = make_command_track_rectangle(top_left_x, top_left_y, bottom_right_x, bottom_right_y);
×
781

782
    _system_impl->send_command_async(
×
783
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
784
            receive_command_result(result, callback);
×
785
        });
×
786
}
×
787

788
void CameraImpl::track_stop_async(const Camera::ResultCallback& callback)
×
789
{
790
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
791

792
    auto cmd = make_command_track_stop();
×
793

794
    _system_impl->send_command_async(
×
795
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
796
            receive_command_result(result, callback);
×
797
        });
×
798
}
×
799

800
void CameraImpl::focus_in_start_async(const Camera::ResultCallback& callback)
×
801
{
802
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
803

804
    auto cmd = make_command_focus_in();
×
805

806
    _system_impl->send_command_async(
×
807
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
808
            receive_command_result(result, callback);
×
809
        });
×
810
}
×
811

812
void CameraImpl::focus_out_start_async(const Camera::ResultCallback& callback)
×
813
{
814
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
815

816
    auto cmd = make_command_focus_out();
×
817

818
    _system_impl->send_command_async(
×
819
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
820
            receive_command_result(result, callback);
×
821
        });
×
822
}
×
823

824
void CameraImpl::focus_stop_async(const Camera::ResultCallback& callback)
×
825
{
826
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
827

828
    auto cmd = make_command_focus_stop();
×
829

830
    _system_impl->send_command_async(
×
831
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
832
            receive_command_result(result, callback);
×
833
        });
×
834
}
×
835

836
void CameraImpl::focus_range_async(float range, const Camera::ResultCallback& callback)
×
837
{
838
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
839

840
    auto cmd = make_command_focus_range(range);
×
841

842
    _system_impl->send_command_async(
×
843
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
844
            receive_command_result(result, callback);
×
845
        });
×
846
}
×
847

848
void CameraImpl::take_photo_async(const Camera::ResultCallback& callback)
×
849
{
850
    // TODO: check whether we are in photo mode.
851

852
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
853

854
    // Take 1 photo only with no interval
855
    auto cmd_take_photo = make_command_take_photo(0.f, 1.0f);
×
856

857
    _system_impl->send_command_async(
×
858
        cmd_take_photo, [this, callback](MavlinkCommandSender::Result result, float) {
×
859
            receive_command_result(result, callback);
×
860
        });
×
861
}
×
862

863
void CameraImpl::start_photo_interval_async(
×
864
    float interval_s, const Camera::ResultCallback& callback)
865
{
866
    if (!interval_valid(interval_s)) {
×
867
        const auto temp_callback = callback;
×
868
        _system_impl->call_user_callback(
×
869
            [temp_callback]() { temp_callback(Camera::Result::WrongArgument); });
870
        return;
×
871
    }
872

873
    // TODO: check whether we are in photo mode.
874

875
    std::lock_guard<std::mutex> lock(_capture.mutex);
×
876

877
    auto cmd_take_photo_time_lapse = make_command_take_photo(interval_s, 0.f);
×
878

879
    _system_impl->send_command_async(
×
880
        cmd_take_photo_time_lapse, [this, callback](MavlinkCommandSender::Result result, float) {
×
881
            receive_command_result(result, callback);
×
882
        });
×
883
}
884

885
void CameraImpl::stop_photo_interval_async(const Camera::ResultCallback& callback)
×
886
{
887
    auto cmd_stop_photo_interval = make_command_stop_photo();
×
888

889
    _system_impl->send_command_async(
×
890
        cmd_stop_photo_interval, [this, callback](MavlinkCommandSender::Result result, float) {
×
891
            receive_command_result(result, callback);
×
892
        });
×
893
}
×
894

895
void CameraImpl::start_video_async(const Camera::ResultCallback& callback)
×
896
{
897
    // TODO: check whether video capture is already in progress.
898
    // TODO: check whether we are in video mode.
899

900
    // Capture status rate is not set
901
    auto cmd_start_video = make_command_start_video(0.f);
×
902

903
    _system_impl->send_command_async(
×
904
        cmd_start_video, [this, callback](MavlinkCommandSender::Result result, float) {
×
905
            receive_command_result(result, callback);
×
906
        });
×
907
}
×
908

909
void CameraImpl::stop_video_async(const Camera::ResultCallback& callback)
×
910
{
911
    auto cmd_stop_video = make_command_stop_video();
×
912

913
    _system_impl->send_command_async(
×
914
        cmd_stop_video, [this, callback](MavlinkCommandSender::Result result, float) {
×
915
            receive_command_result(result, callback);
×
916
        });
×
917
}
×
918

919
Camera::Information CameraImpl::information() const
×
920
{
921
    std::lock_guard<std::mutex> lock(_information.mutex);
×
922

923
    return _information.data;
×
924
}
925

926
Camera::InformationHandle
927
CameraImpl::subscribe_information(const Camera::InformationCallback& callback)
×
928
{
929
    std::lock_guard<std::mutex> lock(_information.mutex);
×
930
    auto handle = _information.subscription_callbacks.subscribe(callback);
×
931

932
    // If there was already a subscription, cancel the call
933
    if (_status.call_every_cookie) {
×
934
        _system_impl->remove_call_every(_status.call_every_cookie);
×
935
    }
936

937
    if (callback) {
×
NEW
938
        _system_impl->remove_call_every(_status.call_every_cookie);
×
NEW
939
        _status.call_every_cookie =
×
NEW
940
            _system_impl->add_call_every([this]() { request_status(); }, 1.0);
×
941
    } else {
942
        _system_impl->remove_call_every(_status.call_every_cookie);
×
943
    }
944

945
    return handle;
×
946
}
947

948
void CameraImpl::unsubscribe_information(Camera::InformationHandle handle)
×
949
{
950
    std::lock_guard<std::mutex> lock(_information.mutex);
×
951
    _information.subscription_callbacks.unsubscribe(handle);
×
952
}
×
953

954
Camera::Result CameraImpl::start_video_streaming(int32_t stream_id)
×
955
{
956
    std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
957

958
    if (_video_stream_info.available &&
×
959
        _video_stream_info.data.status == Camera::VideoStreamInfo::VideoStreamStatus::InProgress) {
×
960
        return Camera::Result::InProgress;
×
961
    }
962

963
    // TODO Check whether we're in video mode
964
    auto command = make_command_start_video_streaming(stream_id);
×
965

966
    auto result = camera_result_from_command_result(_system_impl->send_command(command));
×
967
    // if (result == Camera::Result::Success) {
968
    // Cache video stream info; app may query immediately next.
969
    // TODO: check if we can/should do that.
970
    // auto info = get_video_stream_info();
971
    //}
972
    return result;
×
973
}
974

975
Camera::Result CameraImpl::stop_video_streaming(int32_t stream_id)
×
976
{
977
    // TODO I think we need to maintain current state, whether we issued
978
    // video capture request or video streaming request, etc.We shouldn't
979
    // send stop video streaming if we've not started it!
980
    auto command = make_command_stop_video_streaming(stream_id);
×
981

982
    auto result = camera_result_from_command_result(_system_impl->send_command(command));
×
983
    {
984
        std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
985
        // TODO: check if we can/should do that.
986
        _video_stream_info.data.status = Camera::VideoStreamInfo::VideoStreamStatus::NotRunning;
×
987
    }
988
    return result;
×
989
}
990

991
void CameraImpl::request_video_stream_info()
×
992
{
993
    _system_impl->send_command_async(make_command_request_video_stream_info(), nullptr);
×
994
    _system_impl->send_command_async(make_command_request_video_stream_status(), nullptr);
×
995
}
×
996

997
Camera::VideoStreamInfo CameraImpl::video_stream_info()
×
998
{
999
    std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
1000

1001
    return _video_stream_info.data;
×
1002
}
1003

1004
Camera::VideoStreamInfoHandle
1005
CameraImpl::subscribe_video_stream_info(const Camera::VideoStreamInfoCallback& callback)
×
1006
{
1007
    std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
1008

1009
    auto handle = _video_stream_info.subscription_callbacks.subscribe(callback);
×
1010

1011
    if (callback) {
×
NEW
1012
        _system_impl->remove_call_every(_video_stream_info.call_every_cookie);
×
NEW
1013
        _video_stream_info.call_every_cookie =
×
NEW
1014
            _system_impl->add_call_every([this]() { request_video_stream_info(); }, 1.0);
×
1015
    } else {
1016
        _system_impl->remove_call_every(_video_stream_info.call_every_cookie);
×
1017
    }
1018

1019
    return handle;
×
1020
}
1021

1022
void CameraImpl::unsubscribe_video_stream_info(Camera::VideoStreamInfoHandle handle)
×
1023
{
1024
    std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
1025
    _video_stream_info.subscription_callbacks.unsubscribe(handle);
×
1026
}
×
1027

1028
Camera::Result
1029
CameraImpl::camera_result_from_command_result(const MavlinkCommandSender::Result command_result)
×
1030
{
1031
    switch (command_result) {
×
1032
        case MavlinkCommandSender::Result::Success:
×
1033
            return Camera::Result::Success;
×
1034
        case MavlinkCommandSender::Result::NoSystem:
×
1035
            // FALLTHROUGH
1036
        case MavlinkCommandSender::Result::ConnectionError:
1037
            // FALLTHROUGH
1038
        case MavlinkCommandSender::Result::Busy:
1039
            return Camera::Result::Error;
×
1040
        case MavlinkCommandSender::Result::Failed:
×
1041
            return Camera::Result::Error;
×
1042
        case MavlinkCommandSender::Result::Denied:
×
1043
            // FALLTHROUGH
1044
        case MavlinkCommandSender::Result::TemporarilyRejected:
1045
            return Camera::Result::Denied;
×
1046
        case MavlinkCommandSender::Result::Timeout:
×
1047
            return Camera::Result::Timeout;
×
1048
        default:
×
1049
            return Camera::Result::Unknown;
×
1050
    }
1051
}
1052

1053
Camera::Result CameraImpl::camera_result_from_parameter_result(
×
1054
    const MavlinkParameterClient::Result parameter_result)
1055
{
1056
    switch (parameter_result) {
×
1057
        case MavlinkParameterClient::Result::Success:
×
1058
            return Camera::Result::Success;
×
1059
        case MavlinkParameterClient::Result::Timeout:
×
1060
            return Camera::Result::Timeout;
×
1061
        case MavlinkParameterClient::Result::ConnectionError:
×
1062
            return Camera::Result::Error;
×
1063
        case MavlinkParameterClient::Result::WrongType:
×
1064
            return Camera::Result::WrongArgument;
×
1065
        case MavlinkParameterClient::Result::ParamNameTooLong:
×
1066
            return Camera::Result::WrongArgument;
×
1067
        case MavlinkParameterClient::Result::NotFound:
×
1068
            return Camera::Result::WrongArgument;
×
1069
        case MavlinkParameterClient::Result::ValueUnsupported:
×
1070
            return Camera::Result::WrongArgument;
×
1071
        case MavlinkParameterClient::Result::Failed:
×
1072
            return Camera::Result::Error;
×
1073
        case MavlinkParameterClient::Result::UnknownError:
×
1074
            return Camera::Result::Error;
×
1075
        default:
×
1076
            return Camera::Result::Unknown;
×
1077
    }
1078
}
1079

1080
Camera::Result CameraImpl::set_mode(const Camera::Mode mode)
×
1081
{
1082
    const float mavlink_mode = to_mavlink_camera_mode(mode);
×
1083
    auto cmd_set_camera_mode = make_command_set_camera_mode(mavlink_mode);
×
1084
    const auto command_result = _system_impl->send_command(cmd_set_camera_mode);
×
1085
    const auto camera_result = camera_result_from_command_result(command_result);
×
1086

1087
    if (camera_result == Camera::Result::Success) {
×
1088
        {
1089
            std::lock_guard<std::mutex> lock(_mode.mutex);
×
1090
            _mode.data = mode;
×
1091
        }
1092
        notify_mode();
×
1093
        if (_camera_definition != nullptr) {
×
1094
            save_camera_mode(mavlink_mode);
×
1095
        }
1096
    }
1097

1098
    return camera_result;
×
1099
}
1100

1101
void CameraImpl::save_camera_mode(const float mavlink_camera_mode)
×
1102
{
1103
    if (!std::isfinite(mavlink_camera_mode)) {
×
1104
        LogWarn() << "Can't save NAN as camera mode";
×
1105
        return;
×
1106
    }
1107

1108
    // If there is a camera definition (which is the case if we are
1109
    // in this function, and if CAM_MODE is defined there, then
1110
    // we reuse that type. Otherwise, we hardcode it to `uint32_t`.
1111
    // Note that it could be that the camera definition defines options
1112
    // different than {PHOTO, VIDEO}, in which case the mode received
1113
    // from CAMERA_SETTINGS will be wrong. Not sure if it means that
1114
    // it should be ignored in that case, but that may be tricky to
1115
    // maintain (what if the MAVLink CAMERA_MODE enum evolves?), so
1116
    // I am assuming here that in such a case, CAMERA_SETTINGS is
1117
    // never sent by the camera.
1118
    ParamValue value;
×
1119
    if (_camera_definition->get_setting("CAM_MODE", value)) {
×
1120
        if (value.is<uint8_t>()) {
×
1121
            value.set<uint8_t>(static_cast<uint8_t>(mavlink_camera_mode));
×
1122
        } else if (value.is<int8_t>()) {
×
1123
            value.set<int8_t>(static_cast<int8_t>(mavlink_camera_mode));
×
1124
        } else if (value.is<uint16_t>()) {
×
1125
            value.set<uint16_t>(static_cast<uint16_t>(mavlink_camera_mode));
×
1126
        } else if (value.is<int16_t>()) {
×
1127
            value.set<int16_t>(static_cast<int16_t>(mavlink_camera_mode));
×
1128
        } else if (value.is<uint32_t>()) {
×
1129
            value.set<uint32_t>(static_cast<uint32_t>(mavlink_camera_mode));
×
1130
        } else if (value.is<int32_t>()) {
×
1131
            value.set<int32_t>(static_cast<int32_t>(mavlink_camera_mode));
×
1132
        } else if (value.is<uint64_t>()) {
×
1133
            value.set<uint64_t>(static_cast<uint64_t>(mavlink_camera_mode));
×
1134
        } else if (value.is<int64_t>()) {
×
1135
            value.set<int64_t>(static_cast<int64_t>(mavlink_camera_mode));
×
1136
        } else if (value.is<float>()) {
×
1137
            value.set<float>(static_cast<float>(mavlink_camera_mode));
×
1138
        } else if (value.is<double>()) {
×
1139
            value.set<double>(static_cast<double>(mavlink_camera_mode));
×
1140
        }
1141
    } else {
1142
        value.set<uint32_t>(static_cast<uint32_t>(mavlink_camera_mode));
×
1143
    }
1144

1145
    _camera_definition->set_setting("CAM_MODE", value);
×
1146
    refresh_params();
×
1147
}
1148

1149
float CameraImpl::to_mavlink_camera_mode(const Camera::Mode mode) const
×
1150
{
1151
    switch (mode) {
×
1152
        case Camera::Mode::Photo:
×
1153
            return CAMERA_MODE_IMAGE;
×
1154
        case Camera::Mode::Video:
×
1155
            return CAMERA_MODE_VIDEO;
×
1156
        default:
×
1157
        case Camera::Mode::Unknown:
1158
            return NAN;
×
1159
    }
1160
}
1161

1162
void CameraImpl::set_mode_async(const Camera::Mode mode, const Camera::ResultCallback& callback)
×
1163
{
1164
    const auto mavlink_mode = to_mavlink_camera_mode(mode);
×
1165
    auto cmd_set_camera_mode = make_command_set_camera_mode(mavlink_mode);
×
1166

1167
    _system_impl->send_command_async(
×
1168
        cmd_set_camera_mode,
1169
        [this, callback, mode](MavlinkCommandSender::Result result, float progress) {
×
1170
            UNUSED(progress);
×
1171
            receive_set_mode_command_result(result, callback, mode);
×
1172
        });
×
1173
}
×
1174

1175
Camera::Mode CameraImpl::mode()
×
1176
{
1177
    std::lock_guard<std::mutex> lock(_mode.mutex);
×
1178
    return _mode.data;
×
1179
}
1180

1181
Camera::ModeHandle CameraImpl::subscribe_mode(const Camera::ModeCallback& callback)
×
1182
{
1183
    std::unique_lock<std::mutex> lock(_mode.mutex);
×
1184
    auto handle = _mode.subscription_callbacks.subscribe(callback);
×
1185
    lock.unlock();
×
1186

1187
    notify_mode();
×
1188

1189
    if (callback) {
×
NEW
1190
        _system_impl->remove_call_every(_mode.call_every_cookie);
×
NEW
1191
        _mode.call_every_cookie =
×
NEW
1192
            _system_impl->add_call_every([this]() { request_camera_settings(); }, 5.0);
×
1193
    } else {
1194
        _system_impl->remove_call_every(_mode.call_every_cookie);
×
1195
    }
1196

1197
    return handle;
×
1198
}
1199

1200
void CameraImpl::unsubscribe_mode(Camera::ModeHandle handle)
×
1201
{
1202
    std::lock_guard<std::mutex> lock(_mode.mutex);
×
1203
    _mode.subscription_callbacks.unsubscribe(handle);
×
1204
}
×
1205

1206
bool CameraImpl::interval_valid(float interval_s)
×
1207
{
1208
    // Reject everything faster than 1000 Hz, as well as negative inputs.
1209
    if (interval_s < 0.001f) {
×
1210
        LogWarn() << "Invalid interval input";
×
1211
        return false;
×
1212
    } else {
1213
        return true;
×
1214
    }
1215
}
1216

1217
void CameraImpl::request_status()
×
1218
{
1219
    _system_impl->send_command_async(make_command_request_camera_capture_status(), nullptr);
×
1220
    _system_impl->send_command_async(make_command_request_storage_info(), nullptr);
×
1221
}
×
1222

1223
Camera::StatusHandle CameraImpl::subscribe_status(const Camera::StatusCallback& callback)
×
1224
{
1225
    std::lock_guard<std::mutex> lock(_status.mutex);
×
1226
    auto handle = _status.subscription_callbacks.subscribe(callback);
×
1227

1228
    return handle;
×
1229
}
1230

1231
void CameraImpl::unsubscribe_status(Camera::StatusHandle handle)
×
1232
{
1233
    std::lock_guard<std::mutex> lock(_status.mutex);
×
1234
    _status.subscription_callbacks.unsubscribe(handle);
×
1235
}
×
1236

1237
Camera::Status CameraImpl::status()
×
1238
{
1239
    std::lock_guard<std::mutex> lock(_status.mutex);
×
1240
    return _status.data;
×
1241
}
1242

1243
Camera::CaptureInfoHandle
1244
CameraImpl::subscribe_capture_info(const Camera::CaptureInfoCallback& callback)
×
1245
{
1246
    std::lock_guard<std::mutex> lock(_capture_info.mutex);
×
1247
    return _capture_info.callbacks.subscribe(callback);
×
1248
}
1249

1250
void CameraImpl::unsubscribe_capture_info(Camera::CaptureInfoHandle handle)
×
1251
{
1252
    std::lock_guard<std::mutex> lock(_capture_info.mutex);
×
1253
    _capture_info.callbacks.unsubscribe(handle);
×
1254
}
×
1255

1256
void CameraImpl::process_camera_capture_status(const mavlink_message_t& message)
×
1257
{
1258
    mavlink_camera_capture_status_t camera_capture_status;
×
1259
    mavlink_msg_camera_capture_status_decode(&message, &camera_capture_status);
×
1260

1261
    // If image_count got smaller, consider that the storage was formatted.
1262
    if (camera_capture_status.image_count < _status.image_count) {
×
1263
        LogDebug() << "Seems like storage was formatted, setting state accordingly";
×
1264
        reset_following_format_storage();
×
1265
    }
1266

1267
    {
1268
        std::lock_guard<std::mutex> lock(_status.mutex);
×
1269

1270
        _status.data.video_on = (camera_capture_status.video_status == 1);
×
1271
        _status.data.photo_interval_on =
×
1272
            (camera_capture_status.image_status == 2 || camera_capture_status.image_status == 3);
×
1273
        _status.received_camera_capture_status = true;
×
1274
        _status.data.recording_time_s = float(camera_capture_status.recording_time_ms) / 1e3f;
×
1275

1276
        _status.image_count = camera_capture_status.image_count;
×
1277

1278
        if (_status.image_count_at_connection == -1) {
×
1279
            _status.image_count_at_connection = camera_capture_status.image_count;
×
1280
        }
1281
    }
1282

1283
    check_status();
×
1284
}
×
1285

1286
void CameraImpl::process_storage_information(const mavlink_message_t& message)
×
1287
{
1288
    mavlink_storage_information_t storage_information;
×
1289
    mavlink_msg_storage_information_decode(&message, &storage_information);
×
1290

1291
    if (storage_information.total_capacity == 0.0f) {
×
1292
        // Some MAVLink systems happen to send the STORAGE_INFORMATION message
1293
        // to indicate that the camera has a slot for a storage even if there
1294
        // is no way to know anything about that storage (e.g. whether or not
1295
        // there is an sdcard in the slot).
1296
        //
1297
        // We consider that a total capacity of 0 means that this is such a
1298
        // message, and we don't expect MAVSDK users to leverage it, which is
1299
        // why it is ignored.
1300
        return;
×
1301
    }
1302

1303
    {
1304
        std::lock_guard<std::mutex> lock(_status.mutex);
×
1305
        _status.data.storage_status = storage_status_from_mavlink(storage_information.status);
×
1306
        _status.data.available_storage_mib = storage_information.available_capacity;
×
1307
        _status.data.used_storage_mib = storage_information.used_capacity;
×
1308
        _status.data.total_storage_mib = storage_information.total_capacity;
×
1309
        _status.data.storage_id = storage_information.storage_id;
×
1310
        _status.data.storage_type = storage_type_from_mavlink(storage_information.type);
×
1311
        _status.received_storage_information = true;
×
1312
    }
1313

1314
    check_status();
×
1315
}
1316

1317
Camera::Status::StorageStatus
1318
CameraImpl::storage_status_from_mavlink(const int storage_status) const
×
1319
{
1320
    switch (storage_status) {
×
1321
        case STORAGE_STATUS_EMPTY:
×
1322
            return Camera::Status::StorageStatus::NotAvailable;
×
1323
        case STORAGE_STATUS_UNFORMATTED:
×
1324
            return Camera::Status::StorageStatus::Unformatted;
×
1325
        case STORAGE_STATUS_READY:
×
1326
            return Camera::Status::StorageStatus::Formatted;
×
1327
            break;
1328
        case STORAGE_STATUS_NOT_SUPPORTED:
×
1329
            return Camera::Status::StorageStatus::NotSupported;
×
1330
        default:
×
1331
            LogErr() << "Unknown storage status received.";
×
1332
            return Camera::Status::StorageStatus::NotSupported;
×
1333
    }
1334
}
1335

1336
Camera::Status::StorageType CameraImpl::storage_type_from_mavlink(const int storage_type) const
×
1337
{
1338
    switch (storage_type) {
×
1339
        default:
×
1340
            LogErr() << "Unknown storage_type enum value: " << storage_type;
×
1341
        // FALLTHROUGH
1342
        case STORAGE_TYPE_UNKNOWN:
×
1343
            return mavsdk::Camera::Status::StorageType::Unknown;
×
1344
        case STORAGE_TYPE_USB_STICK:
×
1345
            return mavsdk::Camera::Status::StorageType::UsbStick;
×
1346
        case STORAGE_TYPE_SD:
×
1347
            return mavsdk::Camera::Status::StorageType::Sd;
×
1348
        case STORAGE_TYPE_MICROSD:
×
1349
            return mavsdk::Camera::Status::StorageType::Microsd;
×
1350
        case STORAGE_TYPE_HD:
×
1351
            return mavsdk::Camera::Status::StorageType::Hd;
×
1352
        case STORAGE_TYPE_OTHER:
×
1353
            return mavsdk::Camera::Status::StorageType::Other;
×
1354
    }
1355
}
1356

1357
void CameraImpl::process_camera_image_captured(const mavlink_message_t& message)
×
1358
{
1359
    mavlink_camera_image_captured_t image_captured;
×
1360
    mavlink_msg_camera_image_captured_decode(&message, &image_captured);
×
1361

1362
    {
1363
        Camera::CaptureInfo capture_info = {};
×
1364
        capture_info.position.latitude_deg = image_captured.lat / 1e7;
×
1365
        capture_info.position.longitude_deg = image_captured.lon / 1e7;
×
1366
        capture_info.position.absolute_altitude_m = image_captured.alt / 1e3f;
×
1367
        capture_info.position.relative_altitude_m = image_captured.relative_alt / 1e3f;
×
1368
        capture_info.time_utc_us = image_captured.time_utc;
×
1369
        capture_info.attitude_quaternion.w = image_captured.q[0];
×
1370
        capture_info.attitude_quaternion.x = image_captured.q[1];
×
1371
        capture_info.attitude_quaternion.y = image_captured.q[2];
×
1372
        capture_info.attitude_quaternion.z = image_captured.q[3];
×
1373
        capture_info.attitude_euler_angle =
1374
            to_euler_angle_from_quaternion(capture_info.attitude_quaternion);
×
1375
        capture_info.file_url = std::string(image_captured.file_url);
×
1376
        capture_info.is_success = (image_captured.capture_result == 1);
×
1377
        capture_info.index = image_captured.image_index;
×
1378

1379
        _status.photo_list.insert(std::make_pair(image_captured.image_index, capture_info));
×
1380

1381
        _captured_request_cv.notify_all();
×
1382

1383
        std::lock_guard<std::mutex> lock(_capture_info.mutex);
×
1384
        // Notify user if a new image has been captured.
1385
        if (_capture_info.last_advertised_image_index < capture_info.index) {
×
1386
            _capture_info.callbacks.queue(
×
1387
                capture_info, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1388

1389
            if (_capture_info.last_advertised_image_index != -1) {
×
1390
                // Save captured indices that have been dropped to request later, however, don't
1391
                // do it from the very beginning as there might be many photos from a previous
1392
                // time that we don't want to request.
1393
                for (int i = _capture_info.last_advertised_image_index + 1; i < capture_info.index;
×
1394
                     ++i) {
1395
                    if (_capture_info.missing_image_retries.find(i) ==
×
1396
                        _capture_info.missing_image_retries.end()) {
×
1397
                        _capture_info.missing_image_retries[i] = 0;
×
1398
                    }
1399
                }
1400
            }
1401

1402
            _capture_info.last_advertised_image_index = capture_info.index;
×
1403
        }
1404

1405
        else if (auto it = _capture_info.missing_image_retries.find(capture_info.index);
×
1406
                 it != _capture_info.missing_image_retries.end()) {
×
1407
            _capture_info.callbacks.queue(
×
1408
                capture_info, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1409
            _capture_info.missing_image_retries.erase(it);
×
1410
        }
1411
    }
1412
}
×
1413

1414
void CameraImpl::request_missing_capture_info()
×
1415
{
1416
    std::lock_guard<std::mutex> lock(_capture_info.mutex);
×
1417

1418
    for (auto it = _capture_info.missing_image_retries.begin();
×
1419
         it != _capture_info.missing_image_retries.end();
×
1420
         /* ++it */) {
1421
        if (it->second > 3) {
×
1422
            it = _capture_info.missing_image_retries.erase(it);
×
1423
        } else {
1424
            ++it;
×
1425
        }
1426
    }
1427

1428
    if (!_capture_info.missing_image_retries.empty()) {
×
1429
        auto it_lowest_retries = std::min_element(
×
1430
            _capture_info.missing_image_retries.begin(), _capture_info.missing_image_retries.end());
×
1431
        _system_impl->send_command_async(
×
1432
            CameraImpl::make_command_request_camera_image_captured(it_lowest_retries->first),
×
1433
            nullptr);
1434
        it_lowest_retries->second += 1;
×
1435
    }
1436
}
×
1437

1438
Camera::EulerAngle CameraImpl::to_euler_angle_from_quaternion(Camera::Quaternion quaternion)
×
1439
{
1440
    auto& q = quaternion;
×
1441

1442
    // FIXME: This is duplicated from telemetry/math_conversions.cpp.
1443
    Camera::EulerAngle euler_angle;
×
1444
    euler_angle.roll_deg = to_deg_from_rad(
×
1445
        atan2f(2.0f * (q.w * q.x + q.y * q.z), 1.0f - 2.0f * (q.x * q.x + q.y * q.y)));
×
1446

1447
    euler_angle.pitch_deg = to_deg_from_rad(asinf(2.0f * (q.w * q.y - q.z * q.x)));
×
1448
    euler_angle.yaw_deg = to_deg_from_rad(
×
1449
        atan2f(2.0f * (q.w * q.z + q.x * q.y), 1.0f - 2.0f * (q.y * q.y + q.z * q.z)));
×
1450
    return euler_angle;
×
1451
}
1452

1453
void CameraImpl::process_camera_settings(const mavlink_message_t& message)
×
1454
{
1455
    mavlink_camera_settings_t camera_settings;
×
1456
    mavlink_msg_camera_settings_decode(&message, &camera_settings);
×
1457

1458
    {
1459
        std::lock_guard<std::mutex> lock(_mode.mutex);
×
1460
        _mode.data = to_camera_mode(camera_settings.mode_id);
×
1461
    }
1462
    notify_mode();
×
1463

1464
    if (_camera_definition) {
×
1465
        // This "parameter" needs to be manually set.
1466
        save_camera_mode(camera_settings.mode_id);
×
1467
    }
1468
}
×
1469

1470
Camera::Mode CameraImpl::to_camera_mode(const uint8_t mavlink_camera_mode) const
×
1471
{
1472
    switch (mavlink_camera_mode) {
×
1473
        case CAMERA_MODE_IMAGE:
×
1474
            return Camera::Mode::Photo;
×
1475
        case CAMERA_MODE_VIDEO:
×
1476
            return Camera::Mode::Video;
×
1477
        default:
×
1478
            return Camera::Mode::Unknown;
×
1479
    }
1480
}
1481

1482
void CameraImpl::process_camera_information(const mavlink_message_t& message)
×
1483
{
1484
    mavlink_camera_information_t camera_information;
×
1485
    mavlink_msg_camera_information_decode(&message, &camera_information);
×
1486

1487
    // Make sure all strings are zero terminated, so we don't overrun anywhere.
1488
    camera_information.vendor_name[sizeof(camera_information.vendor_name) - 1] = '\0';
×
1489
    camera_information.model_name[sizeof(camera_information.model_name) - 1] = '\0';
×
1490
    camera_information.cam_definition_uri[sizeof(camera_information.cam_definition_uri) - 1] = '\0';
×
1491

1492
    std::lock_guard<std::mutex> lock(_information.mutex);
×
1493

1494
    _information.data.vendor_name = (char*)(camera_information.vendor_name);
×
1495
    _information.data.model_name = (char*)(camera_information.model_name);
×
1496
    _information.data.focal_length_mm = camera_information.focal_length;
×
1497
    _information.data.horizontal_sensor_size_mm = camera_information.sensor_size_h;
×
1498
    _information.data.vertical_sensor_size_mm = camera_information.sensor_size_v;
×
1499
    _information.data.horizontal_resolution_px = camera_information.resolution_h;
×
1500
    _information.data.vertical_resolution_px = camera_information.resolution_v;
×
1501

1502
    _information.subscription_callbacks.queue(
×
1503
        _information.data, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1504

1505
    if (should_fetch_camera_definition(camera_information.cam_definition_uri)) {
×
1506
        _is_fetching_camera_definition = true;
×
1507

1508
        std::thread([this, camera_information]() {
×
1509
            std::string content{};
×
1510
            const auto result = fetch_camera_definition(camera_information, content);
×
1511

1512
            if (result == Camera::Result::Success) {
×
1513
                LogDebug() << "Successfully loaded camera definition";
×
1514

1515
                if (_camera_definition_callback) {
×
1516
                    _system_impl->call_user_callback(
×
1517
                        [this, result]() { _camera_definition_callback(result); });
1518
                }
1519

1520
                _camera_definition.reset(new CameraDefinition());
×
1521
                _camera_definition->load_string(content);
×
1522
                refresh_params();
×
1523

1524
            } else if (result == Camera::Result::ProtocolUnsupported) {
×
1525
                LogWarn() << "Protocol for " << camera_information.cam_definition_uri
×
1526
                          << " not supported";
×
1527
                if (_camera_definition_callback) {
×
1528
                    _system_impl->call_user_callback(
×
1529
                        [this, result]() { _camera_definition_callback(result); });
1530
                }
1531

1532
            } else {
1533
                LogDebug() << "Failed to fetch camera definition!";
×
1534

1535
                if (++_camera_definition_fetch_count >= 3) {
×
1536
                    LogWarn() << "Giving up fetching the camera definition";
×
1537

1538
                    std::lock_guard<std::mutex> thread_lock(_information.mutex);
×
1539
                    _has_camera_definition_timed_out = true;
×
1540

1541
                    if (_camera_definition_callback) {
×
1542
                        _system_impl->call_user_callback(
×
1543
                            [this, result]() { _camera_definition_callback(result); });
1544
                    }
1545
                }
1546
            }
1547

1548
            std::lock_guard<std::mutex> thread_lock(_information.mutex);
×
1549
            _is_fetching_camera_definition = false;
×
1550
        }).detach();
×
1551
    }
1552
}
×
1553

1554
bool CameraImpl::should_fetch_camera_definition(const std::string& uri) const
×
1555
{
1556
    return !uri.empty() && !_camera_definition && !_is_fetching_camera_definition &&
×
1557
           !_has_camera_definition_timed_out;
×
1558
}
1559

1560
Camera::Result CameraImpl::fetch_camera_definition(
×
1561
    const mavlink_camera_information_t& camera_information, std::string& camera_definition_out)
1562
{
1563
    auto result =
1564
        download_definition_file(camera_information.cam_definition_uri, camera_definition_out);
×
1565

1566
    return result;
×
1567
}
1568

1569
Camera::Result
1570
CameraImpl::download_definition_file(const std::string& uri, std::string& camera_definition_out)
×
1571
{
1572
#if BUILD_WITHOUT_CURL == 1
1573
    UNUSED(uri);
1574
    UNUSED(camera_definition_out);
1575
    return Camera::Result::ProtocolUnsupported;
1576
#else
1577
    HttpLoader http_loader;
×
1578
    LogInfo() << "Downloading camera definition from: " << uri;
×
1579
    if (!http_loader.download_text_sync(uri, camera_definition_out)) {
×
1580
        LogErr() << "Failed to download camera definition.";
×
1581
        return Camera::Result::Error;
×
1582
    }
1583
#endif
1584

1585
    return Camera::Result::Success;
×
1586
}
1587

1588
void CameraImpl::process_video_information(const mavlink_message_t& message)
×
1589
{
1590
    mavlink_video_stream_information_t received_video_info;
×
1591
    mavlink_msg_video_stream_information_decode(&message, &received_video_info);
×
1592

1593
    {
1594
        std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
1595
        // TODO: use stream_id and count
1596
        _video_stream_info.data.status =
×
1597
            (received_video_info.flags & VIDEO_STREAM_STATUS_FLAGS_RUNNING ?
×
1598
                 Camera::VideoStreamInfo::VideoStreamStatus::InProgress :
1599
                 Camera::VideoStreamInfo::VideoStreamStatus::NotRunning);
1600
        _video_stream_info.data.spectrum =
×
1601
            (received_video_info.flags & VIDEO_STREAM_STATUS_FLAGS_THERMAL ?
×
1602
                 Camera::VideoStreamInfo::VideoStreamSpectrum::Infrared :
1603
                 Camera::VideoStreamInfo::VideoStreamSpectrum::VisibleLight);
1604

1605
        auto& video_stream_info = _video_stream_info.data.settings;
×
1606
        video_stream_info.frame_rate_hz = received_video_info.framerate;
×
1607
        video_stream_info.horizontal_resolution_pix = received_video_info.resolution_h;
×
1608
        video_stream_info.vertical_resolution_pix = received_video_info.resolution_v;
×
1609
        video_stream_info.bit_rate_b_s = received_video_info.bitrate;
×
1610
        video_stream_info.rotation_deg = received_video_info.rotation;
×
1611
        video_stream_info.horizontal_fov_deg = static_cast<float>(received_video_info.hfov);
×
1612
        video_stream_info.uri = received_video_info.uri;
×
1613
        _video_stream_info.available = true;
×
1614
    }
1615

1616
    notify_video_stream_info();
×
1617
}
×
1618

1619
void CameraImpl::process_video_stream_status(const mavlink_message_t& message)
×
1620
{
1621
    mavlink_video_stream_status_t received_video_stream_status;
×
1622
    mavlink_msg_video_stream_status_decode(&message, &received_video_stream_status);
×
1623
    {
1624
        std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
1625
        _video_stream_info.data.status =
×
1626
            (received_video_stream_status.flags & VIDEO_STREAM_STATUS_FLAGS_RUNNING ?
×
1627
                 Camera::VideoStreamInfo::VideoStreamStatus::InProgress :
1628
                 Camera::VideoStreamInfo::VideoStreamStatus::NotRunning);
1629
        _video_stream_info.data.spectrum =
×
1630
            (received_video_stream_status.flags & VIDEO_STREAM_STATUS_FLAGS_THERMAL ?
×
1631
                 Camera::VideoStreamInfo::VideoStreamSpectrum::Infrared :
1632
                 Camera::VideoStreamInfo::VideoStreamSpectrum::VisibleLight);
1633

1634
        auto& video_stream_info = _video_stream_info.data.settings;
×
1635
        video_stream_info.frame_rate_hz = received_video_stream_status.framerate;
×
1636
        video_stream_info.horizontal_resolution_pix = received_video_stream_status.resolution_h;
×
1637
        video_stream_info.vertical_resolution_pix = received_video_stream_status.resolution_v;
×
1638
        video_stream_info.bit_rate_b_s = received_video_stream_status.bitrate;
×
1639
        video_stream_info.rotation_deg = received_video_stream_status.rotation;
×
1640
        video_stream_info.horizontal_fov_deg =
×
1641
            static_cast<float>(received_video_stream_status.hfov);
×
1642
        _video_stream_info.available = true;
×
1643
    }
1644

1645
    notify_video_stream_info();
×
1646
}
×
1647

1648
void CameraImpl::notify_video_stream_info()
×
1649
{
1650
    std::lock_guard<std::mutex> lock(_video_stream_info.mutex);
×
1651

1652
    _video_stream_info.subscription_callbacks.queue(
×
1653
        _video_stream_info.data,
×
1654
        [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1655
}
×
1656

1657
void CameraImpl::check_status()
×
1658
{
1659
    std::lock_guard<std::mutex> lock(_status.mutex);
×
1660

1661
    if (_status.received_camera_capture_status && _status.received_storage_information) {
×
1662
        _status.subscription_callbacks.queue(
×
1663
            _status.data, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1664

1665
        _status.received_camera_capture_status = false;
×
1666
        _status.received_storage_information = false;
×
1667
    }
1668
}
×
1669

1670
void CameraImpl::receive_command_result(
×
1671
    MavlinkCommandSender::Result command_result, const Camera::ResultCallback& callback)
1672
{
1673
    Camera::Result camera_result = camera_result_from_command_result(command_result);
×
1674

1675
    if (callback) {
×
1676
        _system_impl->call_user_callback([callback, camera_result]() { callback(camera_result); });
×
1677
    }
1678
}
×
1679

1680
void CameraImpl::receive_set_mode_command_result(
×
1681
    const MavlinkCommandSender::Result command_result,
1682
    const Camera::ResultCallback callback,
1683
    const Camera::Mode mode)
1684
{
1685
    Camera::Result camera_result = camera_result_from_command_result(command_result);
×
1686

1687
    if (callback) {
×
1688
        const auto temp_callback = callback;
×
1689
        _system_impl->call_user_callback(
×
1690
            [temp_callback, camera_result]() { temp_callback(camera_result); });
1691
    }
1692

1693
    if (command_result == MavlinkCommandSender::Result::Success && _camera_definition) {
×
1694
        // This "parameter" needs to be manually set.
1695
        {
1696
            std::lock_guard<std::mutex> lock(_mode.mutex);
×
1697
            _mode.data = mode;
×
1698
        }
1699

1700
        const auto mavlink_mode = to_mavlink_camera_mode(mode);
×
1701

1702
        if (std::isnan(mavlink_mode)) {
×
1703
            LogWarn() << "Unknown camera mode";
×
1704
            return;
×
1705
        }
1706

1707
        notify_mode();
×
1708
        save_camera_mode(mavlink_mode);
×
1709
    }
1710
}
1711

1712
void CameraImpl::notify_mode()
×
1713
{
1714
    std::lock_guard<std::mutex> lock(_mode.mutex);
×
1715

1716
    _mode.subscription_callbacks.queue(
×
1717
        _mode.data, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1718
}
×
1719

1720
bool CameraImpl::get_possible_setting_options(std::vector<std::string>& settings)
×
1721
{
1722
    settings.clear();
×
1723

1724
    if (!_camera_definition) {
×
1725
        LogWarn() << "Error: no camera definition available yet";
×
1726
        return false;
×
1727
    }
1728

1729
    std::unordered_map<std::string, ParamValue> cd_settings{};
×
1730
    _camera_definition->get_possible_settings(cd_settings);
×
1731

1732
    for (const auto& cd_setting : cd_settings) {
×
1733
        if (cd_setting.first == "CAM_MODE") {
×
1734
            // We ignore the mode param.
1735
            continue;
×
1736
        }
1737

1738
        settings.push_back(cd_setting.first);
×
1739
    }
1740

1741
    return settings.size() > 0;
×
1742
}
1743

1744
bool CameraImpl::get_possible_options(
×
1745
    const std::string& setting_id, std::vector<Camera::Option>& options)
1746
{
1747
    options.clear();
×
1748

1749
    if (!_camera_definition) {
×
1750
        LogWarn() << "Error: no camera definition available yet";
×
1751
        return false;
×
1752
    }
1753

1754
    std::vector<ParamValue> values;
×
1755
    if (!_camera_definition->get_possible_options(setting_id, values)) {
×
1756
        return false;
×
1757
    }
1758

1759
    for (const auto& value : values) {
×
1760
        std::stringstream ss{};
×
1761
        ss << value;
×
1762
        Camera::Option option{};
×
1763
        option.option_id = ss.str();
×
1764
        if (!is_setting_range(setting_id)) {
×
1765
            get_option_str(setting_id, option.option_id, option.option_description);
×
1766
        }
1767
        options.push_back(option);
×
1768
    }
1769

1770
    return options.size() > 0;
×
1771
}
1772

1773
bool CameraImpl::is_setting_range(const std::string& setting_id)
×
1774
{
1775
    return _camera_definition->is_setting_range(setting_id);
×
1776
}
1777

1778
Camera::Result CameraImpl::set_setting(Camera::Setting setting)
×
1779
{
1780
    auto prom = std::make_shared<std::promise<Camera::Result>>();
×
1781
    auto ret = prom->get_future();
×
1782

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

1785
    return ret.get();
×
1786
}
1787

1788
void CameraImpl::set_setting_async(Camera::Setting setting, const Camera::ResultCallback callback)
×
1789
{
1790
    set_option_async(setting.setting_id, setting.option, callback);
×
1791
}
×
1792

1793
void CameraImpl::set_option_async(
×
1794
    const std::string& setting_id,
1795
    const Camera::Option& option,
1796
    const Camera::ResultCallback& callback)
1797
{
1798
    if (!_camera_definition) {
×
1799
        LogWarn() << "Error: no camera defnition available yet.";
×
1800
        if (callback) {
×
1801
            const auto temp_callback = callback;
×
1802
            _system_impl->call_user_callback(
×
1803
                [temp_callback]() { temp_callback(Camera::Result::Error); });
1804
        }
1805
        return;
×
1806
    }
1807

1808
    // We get it first so that we have the type of the param value.
1809
    ParamValue value;
×
1810

1811
    if (_camera_definition->is_setting_range(setting_id)) {
×
1812
        // TODO: Get type from minimum.
1813
        std::vector<ParamValue> all_values;
×
1814
        if (!_camera_definition->get_all_options(setting_id, all_values)) {
×
1815
            if (callback) {
×
1816
                LogErr() << "Could not get all options to get type for range param.";
×
1817
                const auto temp_callback = callback;
×
1818
                _system_impl->call_user_callback(
×
1819
                    [temp_callback]() { temp_callback(Camera::Result::Error); });
1820
            }
1821
            return;
×
1822
        }
1823

1824
        if (all_values.size() == 0) {
×
1825
            if (callback) {
×
1826
                LogErr() << "Could not get any options to get type for range param.";
×
1827
                const auto temp_callback = callback;
×
1828
                _system_impl->call_user_callback(
×
1829
                    [temp_callback]() { temp_callback(Camera::Result::Error); });
1830
            }
1831
            return;
×
1832
        }
1833
        value = all_values[0];
×
1834
        // Now re-use that type.
1835
        // FIXME: this is quite ugly, we should do better than that.
1836
        if (!value.set_as_same_type(option.option_id)) {
×
1837
            if (callback) {
×
1838
                LogErr() << "Could not set option value to given type.";
×
1839
                const auto temp_callback = callback;
×
1840
                _system_impl->call_user_callback(
×
1841
                    [temp_callback]() { temp_callback(Camera::Result::Error); });
1842
            }
1843
            return;
×
1844
        }
1845

1846
    } else {
1847
        if (!_camera_definition->get_option_value(setting_id, option.option_id, value)) {
×
1848
            if (callback) {
×
1849
                LogErr() << "Could not get option value.";
×
1850
                const auto temp_callback = callback;
×
1851
                _system_impl->call_user_callback(
×
1852
                    [temp_callback]() { temp_callback(Camera::Result::Error); });
1853
            }
1854
            return;
×
1855
        }
1856

1857
        std::vector<ParamValue> possible_values;
×
1858
        _camera_definition->get_possible_options(setting_id, possible_values);
×
1859
        bool allowed = false;
×
1860
        for (const auto& possible_value : possible_values) {
×
1861
            if (value == possible_value) {
×
1862
                allowed = true;
×
1863
            }
1864
        }
1865
        if (!allowed) {
×
1866
            LogErr() << "Setting " << setting_id << "(" << option.option_id << ") not allowed";
×
1867
            if (callback) {
×
1868
                const auto temp_callback = callback;
×
1869
                _system_impl->call_user_callback(
×
1870
                    [temp_callback]() { temp_callback(Camera::Result::Error); });
1871
            }
1872
            return;
×
1873
        }
1874
    }
1875

1876
    _system_impl->set_param_async(
×
1877
        setting_id,
1878
        value,
1879
        [this, callback, setting_id, value](MavlinkParameterClient::Result result) {
×
1880
            if (result == MavlinkParameterClient::Result::Success) {
×
1881
                if (!this->_camera_definition) {
×
1882
                    if (callback) {
×
1883
                        const auto temp_callback = callback;
×
1884
                        _system_impl->call_user_callback(
×
1885
                            [temp_callback]() { temp_callback(Camera::Result::Error); });
1886
                    }
1887
                    return;
×
1888
                }
1889

1890
                if (!_camera_definition->set_setting(setting_id, value)) {
×
1891
                    if (callback) {
×
1892
                        const auto temp_callback = callback;
×
1893
                        _system_impl->call_user_callback(
×
1894
                            [temp_callback]() { temp_callback(Camera::Result::Error); });
1895
                    }
1896
                    return;
×
1897
                }
1898

1899
                if (callback) {
×
1900
                    const auto temp_callback = callback;
×
1901
                    _system_impl->call_user_callback(
×
1902
                        [temp_callback]() { temp_callback(Camera::Result::Success); });
1903
                }
1904

1905
                // FIXME: We are already holding the lock when this lambda is run and need to
1906
                //        schedule the refresh_params() for later.
1907
                //        We (ab)use the thread pool for the user callbacks for this.
1908
                _system_impl->call_user_callback([this]() { refresh_params(); });
×
1909
            } else {
1910
                if (callback) {
×
1911
                    const auto temp_callback = callback;
×
1912
                    _system_impl->call_user_callback([temp_callback, result]() {
×
1913
                        temp_callback(camera_result_from_parameter_result(result));
1914
                    });
1915
                }
1916
            }
1917
        },
1918
        this,
1919
        static_cast<uint8_t>(_camera_id + MAV_COMP_ID_CAMERA),
×
1920
        true);
1921
}
1922

1923
void CameraImpl::get_setting_async(
×
1924
    Camera::Setting setting, const Camera::GetSettingCallback callback)
1925
{
1926
    get_option_async(
×
1927
        setting.setting_id,
×
1928
        [this, setting, callback](Camera::Result result, const Camera::Option& option) {
×
1929
            Camera::Setting new_setting{};
×
1930
            new_setting.option = option;
×
1931
            if (callback) {
×
1932
                const auto temp_callback = callback;
×
1933
                _system_impl->call_user_callback(
×
1934
                    [temp_callback, result, new_setting]() { temp_callback(result, new_setting); });
1935
            }
1936
        });
×
1937
}
×
1938

1939
std::pair<Camera::Result, Camera::Setting> CameraImpl::get_setting(Camera::Setting setting)
×
1940
{
1941
    auto prom = std::make_shared<std::promise<std::pair<Camera::Result, Camera::Setting>>>();
×
1942
    auto ret = prom->get_future();
×
1943

1944
    get_setting_async(setting, [&prom](Camera::Result result, const Camera::Setting& new_setting) {
×
1945
        prom->set_value(std::make_pair<>(result, new_setting));
×
1946
    });
×
1947

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

1951
Camera::Result CameraImpl::get_option(const std::string& setting_id, Camera::Option& option)
×
1952
{
1953
    auto prom = std::make_shared<std::promise<Camera::Result>>();
×
1954
    auto ret = prom->get_future();
×
1955

1956
    get_option_async(
×
1957
        setting_id, [prom, &option](Camera::Result result, const Camera::Option& option_gotten) {
×
1958
            prom->set_value(result);
×
1959
            if (result == Camera::Result::Success) {
×
1960
                option = option_gotten;
×
1961
            }
1962
        });
×
1963

1964
    auto status = ret.wait_for(std::chrono::seconds(1));
×
1965

1966
    if (status == std::future_status::ready) {
×
1967
        return Camera::Result::Success;
×
1968
    } else {
1969
        return Camera::Result::Timeout;
×
1970
    }
1971
}
1972

1973
void CameraImpl::get_option_async(
×
1974
    const std::string& setting_id,
1975
    const std::function<void(Camera::Result, const Camera::Option&)>& callback)
1976
{
1977
    if (!_camera_definition) {
×
1978
        LogWarn() << "Error: no camera defnition available yet.";
×
1979
        if (callback) {
×
1980
            Camera::Option empty_option{};
×
1981
            const auto temp_callback = callback;
×
1982
            _system_impl->call_user_callback([temp_callback, empty_option]() {
×
1983
                temp_callback(Camera::Result::Error, empty_option);
1984
            });
1985
        }
1986
        return;
×
1987
    }
1988

1989
    ParamValue value;
×
1990
    // We should have this cached and don't need to get the param.
1991
    if (_camera_definition->get_setting(setting_id, value)) {
×
1992
        if (callback) {
×
1993
            Camera::Option new_option{};
×
1994
            new_option.option_id = value.get_string();
×
1995
            if (!is_setting_range(setting_id)) {
×
1996
                get_option_str(setting_id, new_option.option_id, new_option.option_description);
×
1997
            }
1998
            const auto temp_callback = callback;
×
1999
            _system_impl->call_user_callback([temp_callback, new_option]() {
×
2000
                temp_callback(Camera::Result::Success, new_option);
2001
            });
2002
        }
2003
    } else {
2004
        // If this still happens, we request the param, but also complain.
2005
        LogWarn() << "Setting '" << setting_id << "' not found.";
×
2006
        if (callback) {
×
2007
            Camera::Option no_option{};
×
2008
            const auto temp_callback = callback;
×
2009
            _system_impl->call_user_callback(
×
2010
                [temp_callback, no_option]() { temp_callback(Camera::Result::Error, no_option); });
2011
        }
2012
    }
2013
}
2014

2015
Camera::CurrentSettingsHandle
2016
CameraImpl::subscribe_current_settings(const Camera::CurrentSettingsCallback& callback)
×
2017
{
2018
    std::unique_lock<std::mutex> lock(_subscribe_current_settings.mutex);
×
2019
    auto handle = _subscribe_current_settings.callbacks.subscribe(callback);
×
2020
    lock.unlock();
×
2021

2022
    notify_current_settings();
×
2023
    return handle;
×
2024
}
2025

2026
void CameraImpl::unsubscribe_current_settings(Camera::CurrentSettingsHandle handle)
×
2027
{
2028
    std::lock_guard<std::mutex> lock(_subscribe_current_settings.mutex);
×
2029
    _subscribe_current_settings.callbacks.unsubscribe(handle);
×
2030
}
×
2031

2032
Camera::PossibleSettingOptionsHandle CameraImpl::subscribe_possible_setting_options(
×
2033
    const Camera::PossibleSettingOptionsCallback& callback)
2034
{
2035
    std::unique_lock<std::mutex> lock(_subscribe_possible_setting_options.mutex);
×
2036
    auto handle = _subscribe_possible_setting_options.callbacks.subscribe(callback);
×
2037
    lock.unlock();
×
2038

2039
    notify_possible_setting_options();
×
2040
    return handle;
×
2041
}
2042

2043
void CameraImpl::unsubscribe_possible_setting_options(Camera::PossibleSettingOptionsHandle handle)
×
2044
{
2045
    std::lock_guard<std::mutex> lock(_subscribe_possible_setting_options.mutex);
×
2046
    _subscribe_possible_setting_options.callbacks.unsubscribe(handle);
×
2047
}
×
2048

2049
void CameraImpl::notify_current_settings()
×
2050
{
2051
    std::lock_guard<std::mutex> lock(_subscribe_current_settings.mutex);
×
2052

2053
    if (_subscribe_current_settings.callbacks.empty()) {
×
2054
        return;
×
2055
    }
2056

2057
    if (!_camera_definition) {
×
2058
        LogErr() << "notify_current_settings has no camera definition";
×
2059
        return;
×
2060
    }
2061

2062
    std::vector<Camera::Setting> current_settings{};
×
2063
    std::vector<std::string> possible_setting_options{};
×
2064
    if (!get_possible_setting_options(possible_setting_options)) {
×
2065
        LogErr() << "Could not get possible settings in current options subscription.";
×
2066
        return;
×
2067
    }
2068

2069
    for (auto& possible_setting : possible_setting_options) {
×
2070
        // use the cache for this, presumably we updated it right before.
2071
        ParamValue value;
×
2072
        if (_camera_definition->get_setting(possible_setting, value)) {
×
2073
            Camera::Setting setting{};
×
2074
            setting.setting_id = possible_setting;
×
2075
            setting.is_range = is_setting_range(possible_setting);
×
2076
            get_setting_str(setting.setting_id, setting.setting_description);
×
2077
            setting.option.option_id = value.get_string();
×
2078
            if (!is_setting_range(possible_setting)) {
×
2079
                get_option_str(
×
2080
                    setting.setting_id,
2081
                    setting.option.option_id,
2082
                    setting.option.option_description);
2083
            }
2084
            current_settings.push_back(setting);
×
2085
        }
2086
    }
2087

2088
    _subscribe_current_settings.callbacks.queue(
×
2089
        current_settings, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
2090
}
2091

2092
void CameraImpl::notify_possible_setting_options()
×
2093
{
2094
    std::lock_guard<std::mutex> lock(_subscribe_possible_setting_options.mutex);
×
2095

2096
    if (_subscribe_possible_setting_options.callbacks.empty()) {
×
2097
        return;
×
2098
    }
2099

2100
    if (!_camera_definition) {
×
2101
        LogErr() << "notify_possible_setting_options has no camera definition";
×
2102
        return;
×
2103
    }
2104

2105
    auto setting_options = possible_setting_options();
×
2106
    if (setting_options.size() == 0) {
×
2107
        return;
×
2108
    }
2109

2110
    _subscribe_possible_setting_options.callbacks.queue(
×
2111
        setting_options, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
2112
}
2113

2114
std::vector<Camera::SettingOptions> CameraImpl::possible_setting_options()
×
2115
{
2116
    std::vector<Camera::SettingOptions> results{};
×
2117

2118
    std::vector<std::string> possible_settings{};
×
2119
    if (!get_possible_setting_options(possible_settings)) {
×
2120
        LogErr() << "Could not get possible settings.";
×
2121
        return results;
×
2122
    }
2123

2124
    for (auto& possible_setting : possible_settings) {
×
2125
        Camera::SettingOptions setting_options{};
×
2126
        setting_options.setting_id = possible_setting;
×
2127
        setting_options.is_range = _camera_definition->is_setting_range(possible_setting);
×
2128
        get_setting_str(setting_options.setting_id, setting_options.setting_description);
×
2129
        get_possible_options(possible_setting, setting_options.options);
×
2130
        results.push_back(setting_options);
×
2131
    }
2132

2133
    return results;
2134
}
2135

2136
void CameraImpl::refresh_params()
×
2137
{
2138
    if (!_camera_definition) {
×
2139
        return;
×
2140
    }
2141

2142
    std::vector<std::pair<std::string, ParamValue>> params;
×
2143
    _camera_definition->get_unknown_params(params);
×
2144
    if (params.size() == 0) {
×
2145
        // We're assuming that we changed one option and this did not cause
2146
        // any other possible settings to change. However, we still would
2147
        // like to notify the current settings with this one change.
2148
        notify_current_settings();
×
2149
        notify_possible_setting_options();
×
2150
        return;
×
2151
    }
2152

2153
    unsigned count = 0;
×
2154
    for (const auto& param : params) {
×
2155
        const std::string& param_name = param.first;
×
2156
        const ParamValue& param_value_type = param.second;
×
2157
        const bool is_last = (count == params.size() - 1);
×
2158
        _system_impl->get_param_async(
×
2159
            param_name,
2160
            param_value_type,
2161
            [param_name, is_last, this](MavlinkParameterClient::Result result, ParamValue value) {
×
2162
                if (result != MavlinkParameterClient::Result::Success) {
×
2163
                    return;
×
2164
                }
2165
                // We need to check again by the time this callback runs
2166
                if (!this->_camera_definition) {
×
2167
                    return;
×
2168
                }
2169

2170
                if (!this->_camera_definition->set_setting(param_name, value)) {
×
2171
                    return;
×
2172
                }
2173

2174
                if (is_last) {
×
2175
                    notify_current_settings();
×
2176
                    notify_possible_setting_options();
×
2177
                }
2178
            },
2179
            this,
2180
            static_cast<uint8_t>(_camera_id + MAV_COMP_ID_CAMERA),
×
2181
            true);
2182
        ++count;
×
2183
    }
2184
}
2185

2186
void CameraImpl::invalidate_params()
×
2187
{
2188
    if (!_camera_definition) {
×
2189
        return;
×
2190
    }
2191

2192
    _camera_definition->set_all_params_unknown();
×
2193
}
2194

2195
bool CameraImpl::get_setting_str(const std::string& setting_id, std::string& description)
×
2196
{
2197
    if (!_camera_definition) {
×
2198
        return false;
×
2199
    }
2200

2201
    return _camera_definition->get_setting_str(setting_id, description);
×
2202
}
2203

2204
bool CameraImpl::get_option_str(
×
2205
    const std::string& setting_id, const std::string& option_id, std::string& description)
2206
{
2207
    if (!_camera_definition) {
×
2208
        return false;
×
2209
    }
2210

2211
    return _camera_definition->get_option_str(setting_id, option_id, description);
×
2212
}
2213

2214
void CameraImpl::request_camera_settings()
×
2215
{
2216
    auto command_camera_settings = make_command_request_camera_settings();
×
2217
    _system_impl->send_command_async(command_camera_settings, nullptr);
×
2218
}
×
2219

2220
void CameraImpl::request_camera_information()
×
2221
{
2222
    auto command_camera_info = make_command_request_camera_info();
×
2223
    _system_impl->send_command_async(command_camera_info, nullptr);
×
2224
}
×
2225

2226
Camera::Result CameraImpl::format_storage(int32_t storage_id)
×
2227
{
2228
    auto prom = std::make_shared<std::promise<Camera::Result>>();
×
2229
    auto ret = prom->get_future();
×
2230

2231
    format_storage_async(storage_id, [prom](Camera::Result result) { prom->set_value(result); });
×
2232

2233
    return ret.get();
×
2234
}
2235

2236
void CameraImpl::format_storage_async(int32_t storage_id, Camera::ResultCallback callback)
×
2237
{
2238
    MavlinkCommandSender::CommandLong cmd_format{};
×
2239

2240
    cmd_format.command = MAV_CMD_STORAGE_FORMAT;
×
2241
    cmd_format.params.maybe_param1 = static_cast<float>(storage_id); // storage ID
×
2242
    cmd_format.params.maybe_param2 = 1.0f; // format
×
2243
    cmd_format.params.maybe_param3 = 1.0f; // clear
×
2244
    cmd_format.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
2245

2246
    _system_impl->send_command_async(
×
2247
        cmd_format, [this, callback](MavlinkCommandSender::Result result, float progress) {
×
2248
            UNUSED(progress);
×
2249

2250
            receive_command_result(result, [this, callback](Camera::Result camera_result) {
×
2251
                if (camera_result == Camera::Result::Success) {
×
2252
                    reset_following_format_storage();
×
2253
                }
2254

2255
                callback(camera_result);
×
2256
            });
×
2257
        });
×
2258
}
×
2259

2260
Camera::Result CameraImpl::reset_settings()
×
2261
{
2262
    auto prom = std::make_shared<std::promise<Camera::Result>>();
×
2263
    auto ret = prom->get_future();
×
2264

2265
    reset_settings_async([prom](Camera::Result result) { prom->set_value(result); });
×
2266

2267
    return ret.get();
×
2268
}
2269
void CameraImpl::reset_settings_async(const Camera::ResultCallback callback)
×
2270
{
2271
    MavlinkCommandSender::CommandLong cmd_format{};
×
2272

2273
    cmd_format.command = MAV_CMD_RESET_CAMERA_SETTINGS;
×
2274
    cmd_format.params.maybe_param1 = 1.0f; // reset
×
2275
    cmd_format.target_component_id = _camera_id + MAV_COMP_ID_CAMERA;
×
2276

2277
    _system_impl->send_command_async(
×
2278
        cmd_format, [this, callback](MavlinkCommandSender::Result result, float progress) {
×
2279
            UNUSED(progress);
×
2280

2281
            receive_command_result(result, [this, callback](Camera::Result camera_result) {
×
2282
                callback(camera_result);
×
2283
            });
×
2284
        });
×
2285
}
×
2286

2287
void CameraImpl::reset_following_format_storage()
×
2288
{
2289
    {
2290
        std::lock_guard<std::mutex> status_lock(_status.mutex);
×
2291
        _status.photo_list.clear();
×
2292
        _status.image_count = 0;
×
2293
        _status.image_count_at_connection = 0;
×
2294
    }
2295
    {
2296
        std::lock_guard<std::mutex> lock(_capture_info.mutex);
×
2297
        _capture_info.last_advertised_image_index = -1;
×
2298
        _capture_info.missing_image_retries.clear();
×
2299
    }
2300
}
×
2301

2302
std::pair<Camera::Result, std::vector<Camera::CaptureInfo>>
2303
CameraImpl::list_photos(Camera::PhotosRange photos_range)
×
2304
{
2305
    std::promise<std::pair<Camera::Result, std::vector<Camera::CaptureInfo>>> prom;
×
2306
    auto ret = prom.get_future();
×
2307

2308
    list_photos_async(
×
2309
        photos_range, [&prom](Camera::Result result, std::vector<Camera::CaptureInfo> photo_list) {
×
2310
            prom.set_value(std::make_pair(result, photo_list));
×
2311
        });
×
2312

2313
    return ret.get();
×
2314
}
2315

2316
void CameraImpl::list_photos_async(
×
2317
    Camera::PhotosRange photos_range, const Camera::ListPhotosCallback callback)
2318
{
2319
    if (!callback) {
×
2320
        LogWarn() << "Trying to get a photo list with a null callback, ignoring...";
×
2321
        return;
×
2322
    }
2323

2324
    {
2325
        std::lock_guard<std::mutex> status_lock(_status.mutex);
×
2326

2327
        if (_status.is_fetching_photos) {
×
2328
            _system_impl->call_user_callback([callback]() {
×
2329
                callback(Camera::Result::Busy, std::vector<Camera::CaptureInfo>{});
2330
            });
2331
            return;
×
2332
        } else {
2333
            _status.is_fetching_photos = true;
×
2334
        }
2335

2336
        if (_status.image_count == -1) {
×
2337
            LogErr() << "Cannot list photos: camera status has not been received yet!";
×
2338
            _status.is_fetching_photos = false;
×
2339
            _system_impl->call_user_callback([callback]() {
×
2340
                callback(Camera::Result::Error, std::vector<Camera::CaptureInfo>{});
2341
            });
2342
            return;
×
2343
        }
2344
    }
2345

2346
    const int start_index = [this, photos_range]() {
×
2347
        switch (photos_range) {
×
2348
            case Camera::PhotosRange::SinceConnection:
×
2349
                return _status.image_count_at_connection;
×
2350
            case Camera::PhotosRange::All:
×
2351
            // FALLTHROUGH
2352
            default:
2353
                return 0;
×
2354
        }
2355
    }();
×
2356

2357
    std::thread([this, start_index, callback]() {
×
2358
        std::unique_lock<std::mutex> capture_request_lock(_captured_request_mutex);
×
2359

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

2366
            while (_status.photo_list.find(i) == _status.photo_list.end() &&
×
2367
                   safety_count < safety_count_boundary) {
2368
                safety_count++;
×
2369

2370
                auto request_try_number = 0;
×
2371
                const auto request_try_limit =
×
2372
                    10; // Timeout if the request times out that many times
2373
                auto cv_status = std::cv_status::timeout;
×
2374

2375
                while (cv_status == std::cv_status::timeout) {
×
2376
                    request_try_number++;
×
2377
                    if (request_try_number >= request_try_limit) {
×
2378
                        std::lock_guard<std::mutex> status_lock(_status.mutex);
×
2379
                        _status.is_fetching_photos = false;
×
2380
                        _system_impl->call_user_callback([callback]() {
×
2381
                            callback(Camera::Result::Timeout, std::vector<Camera::CaptureInfo>{});
2382
                        });
2383
                        return;
×
2384
                    }
2385

2386
                    _system_impl->send_command_async(
×
2387
                        make_command_request_camera_image_captured(i), nullptr);
2388
                    cv_status = _captured_request_cv.wait_for(
×
2389
                        capture_request_lock, std::chrono::seconds(1));
×
2390
                }
2391
            }
2392

2393
            if (safety_count == safety_count_boundary) {
×
2394
                std::lock_guard<std::mutex> status_lock(_status.mutex);
×
2395
                _status.is_fetching_photos = false;
×
2396
                _system_impl->call_user_callback([callback]() {
×
2397
                    callback(Camera::Result::Error, std::vector<Camera::CaptureInfo>{});
2398
                });
2399
                return;
×
2400
            }
2401
        }
2402

2403
        std::vector<Camera::CaptureInfo> photo_list;
×
2404
        {
2405
            std::lock_guard<std::mutex> status_lock(_status.mutex);
×
2406

2407
            for (auto capture_info : _status.photo_list) {
×
2408
                if (capture_info.first >= start_index) {
×
2409
                    photo_list.push_back(capture_info.second);
×
2410
                }
2411
            }
2412

2413
            _status.is_fetching_photos = false;
×
2414

2415
            const auto temp_callback = callback;
×
2416
            _system_impl->call_user_callback([temp_callback, photo_list]() {
×
2417
                temp_callback(Camera::Result::Success, photo_list);
2418
            });
2419
        }
2420
    }).detach();
×
2421
}
2422

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