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

mavlink / MAVSDK / 14871761335

06 May 2025 11:34PM UTC coverage: 44.198% (+0.005%) from 44.193%
14871761335

Pull #2564

github

web-flow
Merge 617290084 into 809aeb424
Pull Request #2564: Fix manual control inverted r (yaw) axis

0 of 1 new or added line in 1 file covered. (0.0%)

2 existing lines in 2 files now uncovered.

14801 of 33488 relevant lines covered (44.2%)

277.2 hits per line

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

55.24
/src/mavsdk/plugins/camera/camera_impl.cpp
1
#include "camera_impl.h"
2
#include "camera_definition.h"
3
#include "system.h"
4
#include "unused.h"
5
#include "callback_list.tpp"
6
#include "fs_utils.h"
7
#include "string_utils.h"
8
#include "math_utils.h"
9

10
#if BUILD_WITHOUT_CURL != 1
11
#include "http_loader.h"
12
#endif
13

14
#include <algorithm>
15
#include <cassert>
16
#include <cmath>
17
#include <chrono>
18
#include <iterator>
19
#include <filesystem>
20
#include <functional>
21
#include <string>
22
#include <thread>
23

24
namespace mavsdk {
25

26
template class CallbackList<Camera::Mode>;
27
template class CallbackList<std::vector<Camera::Setting>>;
28
template class CallbackList<std::vector<Camera::SettingOptions>>;
29
template class CallbackList<Camera::CaptureInfo>;
30
template class CallbackList<Camera::VideoStreamInfo>;
31
template class CallbackList<Camera::Storage>;
32

33
CameraImpl::CameraImpl(System& system) : PluginImplBase(system)
×
34
{
35
    _system_impl->register_plugin(this);
×
36
}
×
37

38
CameraImpl::CameraImpl(std::shared_ptr<System> system) : PluginImplBase(std::move(system))
9✔
39
{
40
    _system_impl->register_plugin(this);
9✔
41
}
9✔
42

43
CameraImpl::~CameraImpl()
18✔
44
{
45
    _system_impl->unregister_plugin(this);
9✔
46
}
18✔
47

48
void CameraImpl::init()
9✔
49
{
50
    if (const char* env_p = std::getenv("MAVSDK_CAMERA_DEBUGGING")) {
9✔
51
        if (std::string(env_p) == "1") {
×
52
            LogDebug() << "Camera debugging is on.";
×
53
            _debugging = true;
×
54
        }
55
    }
56

57
    const auto cache_dir_option = get_cache_directory();
9✔
58
    if (cache_dir_option) {
9✔
59
        _file_cache.emplace(cache_dir_option.value() / "camera", 50, true);
9✔
60
    } else {
61
        LogErr() << "Failed to get cache directory";
×
62
    }
63

64
    const auto tmp_option = create_tmp_directory("mavsdk-component-metadata");
27✔
65
    if (tmp_option) {
9✔
66
        _tmp_download_path = tmp_option.value();
9✔
67
    } else {
68
        _tmp_download_path = "./mavsdk-component-metadata";
×
69
        std::error_code err;
×
70
        std::filesystem::create_directory(_tmp_download_path, err);
×
71
    }
72

73
    _system_impl->register_mavlink_message_handler(
9✔
74
        MAVLINK_MSG_ID_HEARTBEAT,
75
        [this](const mavlink_message_t& message) { process_heartbeat(message); },
35✔
76
        this);
77

78
    _system_impl->register_mavlink_message_handler(
9✔
79
        MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS,
80
        [this](const mavlink_message_t& message) { process_camera_capture_status(message); },
4✔
81
        this);
82

83
    _system_impl->register_mavlink_message_handler(
9✔
84
        MAVLINK_MSG_ID_STORAGE_INFORMATION,
85
        [this](const mavlink_message_t& message) { process_storage_information(message); },
1✔
86
        this);
87

88
    _system_impl->register_mavlink_message_handler(
9✔
89
        MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED,
90
        [this](const mavlink_message_t& message) { process_camera_image_captured(message); },
4✔
91
        this);
92

93
    _system_impl->register_mavlink_message_handler(
9✔
94
        MAVLINK_MSG_ID_CAMERA_SETTINGS,
95
        [this](const mavlink_message_t& message) { process_camera_settings(message); },
4✔
96
        this);
97

98
    _system_impl->register_mavlink_message_handler(
9✔
99
        MAVLINK_MSG_ID_CAMERA_INFORMATION,
100
        [this](const mavlink_message_t& message) { process_camera_information(message); },
9✔
101
        this);
102

103
    _system_impl->register_mavlink_message_handler(
9✔
104
        MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION,
105
        [this](const mavlink_message_t& message) { process_video_information(message); },
1✔
106
        this);
107

108
    _system_impl->register_mavlink_message_handler(
9✔
109
        MAVLINK_MSG_ID_VIDEO_STREAM_STATUS,
110
        [this](const mavlink_message_t& message) { process_video_stream_status(message); },
3✔
111
        this);
112

113
    _request_missing_capture_info_cookie =
9✔
114
        _system_impl->add_call_every([this]() { request_missing_capture_info(); }, 0.5);
87✔
115

116
    // TODO: use SET_INTERVAL commands instead and fall back to request commands when needed.
117
    _request_slower_call_every_cookie =
9✔
118
        _system_impl->add_call_every([this]() { request_slower(); }, 20.0);
28✔
119
    _request_faster_call_every_cookie =
9✔
120
        _system_impl->add_call_every([this]() { request_faster(); }, 5.0);
32✔
121
}
9✔
122

123
void CameraImpl::deinit()
9✔
124
{
125
    _system_impl->unregister_all_mavlink_message_handlers(this);
9✔
126

127
    _system_impl->cancel_all_param(this);
9✔
128

129
    _system_impl->remove_call_every(_request_missing_capture_info_cookie);
9✔
130
    _system_impl->remove_call_every(_request_slower_call_every_cookie);
9✔
131
    _system_impl->remove_call_every(_request_faster_call_every_cookie);
9✔
132

133
    // FIXME: There is a race condition here.
134
    // We need to wait until all call every calls are done before we go
135
    // out of scope.
136
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
9✔
137

138
    std::lock_guard lock(_mutex);
9✔
139
    _storage_subscription_callbacks.clear();
9✔
140
    _mode_subscription_callbacks.clear();
9✔
141
    _capture_info_callbacks.clear();
9✔
142
    _video_stream_info_subscription_callbacks.clear();
9✔
143
    _camera_list_subscription_callbacks.clear();
9✔
144
    _subscribe_current_settings_callbacks.clear();
9✔
145
    _subscribe_possible_setting_options_callbacks.clear();
9✔
146
}
9✔
147

148
void CameraImpl::enable() {}
9✔
149
void CameraImpl::disable() {}
9✔
150

151
MavlinkCommandSender::CommandLong
152
CameraImpl::make_command_take_photo(int32_t component_id, float interval_s, float no_of_photos)
2✔
153
{
154
    MavlinkCommandSender::CommandLong cmd{};
2✔
155

156
    cmd.command = MAV_CMD_IMAGE_START_CAPTURE;
2✔
157
    cmd.params.maybe_param1 = 0.0f; // Reserved, set to 0
2✔
158
    cmd.params.maybe_param2 = interval_s;
2✔
159
    cmd.params.maybe_param3 = no_of_photos;
2✔
160
    cmd.params.maybe_param4 = get_and_increment_capture_sequence(component_id);
2✔
161
    cmd.target_component_id = fixup_component_target(component_id);
2✔
162

163
    return cmd;
2✔
164
}
165

166
MavlinkCommandSender::CommandLong CameraImpl::make_command_zoom_out(int32_t component_id)
×
167
{
168
    MavlinkCommandSender::CommandLong cmd{};
×
169
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
170
    cmd.params.maybe_param1 = static_cast<float>(ZOOM_TYPE_CONTINUOUS);
×
171
    cmd.params.maybe_param2 = -1.f;
×
172
    cmd.target_component_id = fixup_component_target(component_id);
×
173

174
    return cmd;
×
175
}
176

177
MavlinkCommandSender::CommandLong CameraImpl::make_command_zoom_in(int32_t component_id)
×
178
{
179
    MavlinkCommandSender::CommandLong cmd{};
×
180
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
181
    cmd.params.maybe_param1 = static_cast<float>(ZOOM_TYPE_CONTINUOUS);
×
182
    cmd.params.maybe_param2 = 1.f;
×
183
    cmd.target_component_id = fixup_component_target(component_id);
×
184

185
    return cmd;
×
186
}
187

188
MavlinkCommandSender::CommandLong CameraImpl::make_command_zoom_stop(int32_t component_id)
×
189
{
190
    MavlinkCommandSender::CommandLong cmd{};
×
191
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
192
    cmd.params.maybe_param1 = static_cast<float>(ZOOM_TYPE_CONTINUOUS);
×
193
    cmd.params.maybe_param2 = 0.f;
×
194
    cmd.target_component_id = fixup_component_target(component_id);
×
195

196
    return cmd;
×
197
}
198

199
MavlinkCommandSender::CommandLong
200
CameraImpl::make_command_zoom_range(int32_t component_id, float range)
×
201
{
202
    // Clip to safe range.
203
    range = std::max(0.f, std::min(range, 100.f));
×
204

205
    MavlinkCommandSender::CommandLong cmd{};
×
206
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
207
    cmd.params.maybe_param1 = static_cast<float>(ZOOM_TYPE_RANGE);
×
208
    cmd.params.maybe_param2 = range;
×
209
    cmd.target_component_id = fixup_component_target(component_id);
×
210

211
    return cmd;
×
212
}
213

214
MavlinkCommandSender::CommandLong CameraImpl::make_command_track_point(
×
215
    int32_t component_id, float point_x, float point_y, float radius)
216
{
217
    MavlinkCommandSender::CommandLong cmd{};
×
218
    cmd.command = MAV_CMD_CAMERA_TRACK_POINT;
×
219
    cmd.params.maybe_param1 = point_x;
×
220
    cmd.params.maybe_param2 = point_y;
×
221
    cmd.params.maybe_param3 = radius;
×
222
    cmd.target_component_id = fixup_component_target(component_id);
×
223

224
    return cmd;
×
225
}
226

227
MavlinkCommandSender::CommandLong CameraImpl::make_command_track_rectangle(
×
228
    int32_t component_id,
229
    float top_left_x,
230
    float top_left_y,
231
    float bottom_right_x,
232
    float bottom_right_y)
233
{
234
    MavlinkCommandSender::CommandLong cmd{};
×
235
    cmd.command = MAV_CMD_CAMERA_TRACK_RECTANGLE;
×
236
    cmd.params.maybe_param1 = top_left_x;
×
237
    cmd.params.maybe_param2 = top_left_y;
×
238
    cmd.params.maybe_param3 = bottom_right_x;
×
239
    cmd.params.maybe_param4 = bottom_right_y;
×
240
    cmd.target_component_id = fixup_component_target(component_id);
×
241

242
    return cmd;
×
243
}
244

245
MavlinkCommandSender::CommandLong CameraImpl::make_command_track_stop(int32_t component_id)
×
246
{
247
    MavlinkCommandSender::CommandLong cmd{};
×
248
    cmd.command = MAV_CMD_CAMERA_STOP_TRACKING;
×
249
    cmd.target_component_id = fixup_component_target(component_id);
×
250

251
    return cmd;
×
252
}
253

254
MavlinkCommandSender::CommandLong CameraImpl::make_command_focus_in(int32_t component_id)
×
255
{
256
    MavlinkCommandSender::CommandLong cmd{};
×
257
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
258
    cmd.params.maybe_param1 = static_cast<float>(FOCUS_TYPE_CONTINUOUS);
×
259
    cmd.params.maybe_param2 = -1.f;
×
260
    cmd.target_component_id = fixup_component_target(component_id);
×
261

262
    return cmd;
×
263
}
264

265
MavlinkCommandSender::CommandLong CameraImpl::make_command_focus_out(int32_t component_id)
×
266
{
267
    MavlinkCommandSender::CommandLong cmd{};
×
268
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
269
    cmd.params.maybe_param1 = static_cast<float>(FOCUS_TYPE_CONTINUOUS);
×
270
    cmd.params.maybe_param2 = 1.f;
×
271
    cmd.target_component_id = fixup_component_target(component_id);
×
272

273
    return cmd;
×
274
}
275

276
MavlinkCommandSender::CommandLong CameraImpl::make_command_focus_stop(int32_t component_id)
×
277
{
278
    MavlinkCommandSender::CommandLong cmd{};
×
279
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
280
    cmd.params.maybe_param1 = static_cast<float>(FOCUS_TYPE_CONTINUOUS);
×
281
    cmd.params.maybe_param2 = 0.f;
×
282
    cmd.target_component_id = fixup_component_target(component_id);
×
283

284
    return cmd;
×
285
}
286

287
MavlinkCommandSender::CommandLong
288
CameraImpl::make_command_focus_range(int32_t component_id, float range)
×
289
{
290
    // Clip to safe range.
291
    range = std::max(0.f, std::min(range, 100.f));
×
292

293
    MavlinkCommandSender::CommandLong cmd{};
×
294
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
295
    cmd.params.maybe_param1 = static_cast<float>(FOCUS_TYPE_RANGE);
×
296
    cmd.params.maybe_param2 = range;
×
297
    cmd.target_component_id = fixup_component_target(component_id);
×
298

299
    return cmd;
×
300
}
301

302
MavlinkCommandSender::CommandLong CameraImpl::make_command_stop_photo(int32_t component_id)
1✔
303
{
304
    MavlinkCommandSender::CommandLong cmd_stop_photo{};
1✔
305

306
    cmd_stop_photo.command = MAV_CMD_IMAGE_STOP_CAPTURE;
1✔
307
    cmd_stop_photo.target_component_id = fixup_component_target(component_id);
1✔
308

309
    return cmd_stop_photo;
1✔
310
}
311

312
MavlinkCommandSender::CommandLong
313
CameraImpl::make_command_start_video(int32_t component_id, float capture_status_rate_hz)
×
314
{
315
    MavlinkCommandSender::CommandLong cmd_start_video{};
×
316

317
    cmd_start_video.command = MAV_CMD_VIDEO_START_CAPTURE;
×
318
    cmd_start_video.params.maybe_param1 = 0.f; // Reserved, set to 0
×
319
    cmd_start_video.params.maybe_param2 = capture_status_rate_hz;
×
320
    cmd_start_video.target_component_id = fixup_component_target(component_id);
×
321

322
    return cmd_start_video;
×
323
}
324

325
MavlinkCommandSender::CommandLong CameraImpl::make_command_stop_video(int32_t component_id)
×
326
{
327
    MavlinkCommandSender::CommandLong cmd_stop_video{};
×
328

329
    cmd_stop_video.command = MAV_CMD_VIDEO_STOP_CAPTURE;
×
330
    cmd_stop_video.params.maybe_param1 = 0.f; // Reserved, set to 0
×
331
    cmd_stop_video.target_component_id = fixup_component_target(component_id);
×
332

333
    return cmd_stop_video;
×
334
}
335

336
MavlinkCommandSender::CommandLong
337
CameraImpl::make_command_set_camera_mode(int32_t component_id, float mavlink_mode)
2✔
338
{
339
    MavlinkCommandSender::CommandLong cmd_set_camera_mode{};
2✔
340

341
    cmd_set_camera_mode.command = MAV_CMD_SET_CAMERA_MODE;
2✔
342
    cmd_set_camera_mode.params.maybe_param1 = 0.0f; // Reserved, set to 0
2✔
343
    cmd_set_camera_mode.params.maybe_param2 = mavlink_mode;
2✔
344
    cmd_set_camera_mode.target_component_id = fixup_component_target(component_id);
2✔
345

346
    return cmd_set_camera_mode;
2✔
347
}
348

349
MavlinkCommandSender::CommandLong
350
CameraImpl::make_command_start_video_streaming(int32_t component_id, int32_t stream_id)
×
351
{
352
    MavlinkCommandSender::CommandLong cmd_start_video_streaming{};
×
353

354
    cmd_start_video_streaming.command = MAV_CMD_VIDEO_START_STREAMING;
×
355
    cmd_start_video_streaming.params.maybe_param1 = static_cast<float>(stream_id);
×
356
    cmd_start_video_streaming.target_component_id = fixup_component_target(component_id);
×
357

358
    return cmd_start_video_streaming;
×
359
}
360

361
MavlinkCommandSender::CommandLong
362
CameraImpl::make_command_stop_video_streaming(int32_t component_id, int32_t stream_id)
×
363
{
364
    MavlinkCommandSender::CommandLong cmd_stop_video_streaming{};
×
365

366
    cmd_stop_video_streaming.command = MAV_CMD_VIDEO_STOP_STREAMING;
×
367
    cmd_stop_video_streaming.params.maybe_param1 = static_cast<float>(stream_id);
×
368
    cmd_stop_video_streaming.target_component_id = fixup_component_target(component_id);
×
369

370
    return cmd_stop_video_streaming;
×
371
}
372

373
Camera::Result CameraImpl::take_photo(int32_t component_id)
1✔
374
{
375
    // Take 1 photo only with no interval
376
    auto cmd_take_photo = make_command_take_photo(component_id, 0.f, 1.0f);
1✔
377

378
    return camera_result_from_command_result(_system_impl->send_command(cmd_take_photo));
1✔
379
}
380

381
Camera::Result CameraImpl::zoom_out_start(int32_t component_id)
×
382
{
383
    auto cmd = make_command_zoom_out(component_id);
×
384

385
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
386
}
387

388
Camera::Result CameraImpl::zoom_in_start(int32_t component_id)
×
389
{
390
    auto cmd = make_command_zoom_in(component_id);
×
391

392
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
393
}
394

395
Camera::Result CameraImpl::zoom_stop(int32_t component_id)
×
396
{
397
    auto cmd = make_command_zoom_stop(component_id);
×
398

399
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
400
}
401

402
Camera::Result CameraImpl::zoom_range(int32_t component_id, float range)
×
403
{
404
    auto cmd = make_command_zoom_range(component_id, range);
×
405

406
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
407
}
408

409
Camera::Result
410
CameraImpl::track_point(int32_t component_id, float point_x, float point_y, float radius)
×
411
{
412
    auto cmd = make_command_track_point(component_id, point_x, point_y, radius);
×
413

414
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
415
}
416

417
Camera::Result CameraImpl::track_rectangle(
×
418
    int32_t component_id,
419
    float top_left_x,
420
    float top_left_y,
421
    float bottom_right_x,
422
    float bottom_right_y)
423
{
424
    auto cmd = make_command_track_rectangle(
×
425
        component_id, top_left_x, top_left_y, bottom_right_x, bottom_right_y);
426

427
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
428
}
429

430
Camera::Result CameraImpl::track_stop(int32_t component_id)
×
431
{
432
    auto cmd = make_command_track_stop(component_id);
×
433

434
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
435
}
436

437
Camera::Result CameraImpl::focus_in_start(int32_t component_id)
×
438
{
439
    auto cmd = make_command_focus_in(component_id);
×
440

441
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
442
}
443

444
Camera::Result CameraImpl::focus_out_start(int32_t component_id)
×
445
{
446
    auto cmd = make_command_focus_out(component_id);
×
447

448
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
449
}
450

451
Camera::Result CameraImpl::focus_stop(int32_t component_id)
×
452
{
453
    auto cmd = make_command_focus_stop(component_id);
×
454

455
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
456
}
457

458
Camera::Result CameraImpl::focus_range(int32_t component_id, float range)
×
459
{
460
    auto cmd = make_command_focus_range(component_id, range);
×
461

462
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
463
}
464

465
Camera::Result CameraImpl::start_photo_interval(int32_t component_id, float interval_s)
1✔
466
{
467
    auto cmd_take_photo_time_lapse = make_command_take_photo(component_id, interval_s, 0.f);
1✔
468

469
    return camera_result_from_command_result(_system_impl->send_command(cmd_take_photo_time_lapse));
1✔
470
}
471

472
Camera::Result CameraImpl::stop_photo_interval(int32_t component_id)
1✔
473
{
474
    auto cmd_stop_photo_interval = make_command_stop_photo(component_id);
1✔
475

476
    return camera_result_from_command_result(_system_impl->send_command(cmd_stop_photo_interval));
1✔
477
}
478

479
Camera::Result CameraImpl::start_video(int32_t component_id)
×
480
{
481
    // Capture status rate is not set
482
    auto cmd_start_video = make_command_start_video(component_id, 0.f);
×
483

484
    return camera_result_from_command_result(_system_impl->send_command(cmd_start_video));
×
485
}
486

487
Camera::Result CameraImpl::stop_video(int32_t component_id)
×
488
{
489
    auto cmd_stop_video = make_command_stop_video(component_id);
×
490

491
    return camera_result_from_command_result(_system_impl->send_command(cmd_stop_video));
×
492
}
493

494
void CameraImpl::zoom_in_start_async(int32_t component_id, const Camera::ResultCallback& callback)
×
495
{
496
    auto cmd = make_command_zoom_in(component_id);
×
497

498
    _system_impl->send_command_async(
×
499
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
500
            receive_command_result(result, callback);
×
501
        });
×
502
}
×
503

504
void CameraImpl::zoom_out_start_async(int32_t component_id, const Camera::ResultCallback& callback)
×
505
{
506
    auto cmd = make_command_zoom_out(component_id);
×
507

508
    _system_impl->send_command_async(
×
509
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
510
            receive_command_result(result, callback);
×
511
        });
×
512
}
×
513

514
void CameraImpl::zoom_stop_async(int32_t component_id, const Camera::ResultCallback& callback)
×
515
{
516
    auto cmd = make_command_zoom_stop(component_id);
×
517

518
    _system_impl->send_command_async(
×
519
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
520
            receive_command_result(result, callback);
×
521
        });
×
522
}
×
523

524
void CameraImpl::zoom_range_async(
×
525
    int32_t component_id, float range, const Camera::ResultCallback& callback)
526
{
527
    auto cmd = make_command_zoom_range(component_id, range);
×
528

529
    _system_impl->send_command_async(
×
530
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
531
            receive_command_result(result, callback);
×
532
        });
×
533
}
×
534

535
void CameraImpl::track_point_async(
×
536
    int32_t component_id,
537
    float point_x,
538
    float point_y,
539
    float radius,
540
    const Camera::ResultCallback& callback)
541
{
542
    auto cmd = make_command_track_point(component_id, point_x, point_y, radius);
×
543

544
    _system_impl->send_command_async(
×
545
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
546
            receive_command_result(result, callback);
×
547
        });
×
548
}
×
549

550
void CameraImpl::track_rectangle_async(
×
551
    int32_t component_id,
552
    float top_left_x,
553
    float top_left_y,
554
    float bottom_right_x,
555
    float bottom_right_y,
556
    const Camera::ResultCallback& callback)
557
{
558
    auto cmd = make_command_track_rectangle(
×
559
        component_id, top_left_x, top_left_y, bottom_right_x, bottom_right_y);
560

561
    _system_impl->send_command_async(
×
562
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
563
            receive_command_result(result, callback);
×
564
        });
×
565
}
×
566

567
void CameraImpl::track_stop_async(int32_t component_id, const Camera::ResultCallback& callback)
×
568
{
569
    auto cmd = make_command_track_stop(component_id);
×
570

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

577
void CameraImpl::focus_in_start_async(int32_t component_id, const Camera::ResultCallback& callback)
×
578
{
579
    auto cmd = make_command_focus_in(component_id);
×
580

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

587
void CameraImpl::focus_out_start_async(int32_t component_id, const Camera::ResultCallback& callback)
×
588
{
589
    auto cmd = make_command_focus_out(component_id);
×
590

591
    _system_impl->send_command_async(
×
592
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
593
            receive_command_result(result, callback);
×
594
        });
×
595
}
×
596

597
void CameraImpl::focus_stop_async(int32_t component_id, const Camera::ResultCallback& callback)
×
598
{
599
    auto cmd = make_command_focus_stop(component_id);
×
600

601
    _system_impl->send_command_async(
×
602
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
603
            receive_command_result(result, callback);
×
604
        });
×
605
}
×
606

607
void CameraImpl::focus_range_async(
×
608
    int32_t component_id, float range, const Camera::ResultCallback& callback)
609
{
610
    auto cmd = make_command_focus_range(component_id, range);
×
611

612
    _system_impl->send_command_async(
×
613
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
614
            receive_command_result(result, callback);
×
615
        });
×
616
}
×
617

618
void CameraImpl::take_photo_async(int32_t component_id, const Camera::ResultCallback& callback)
×
619
{
620
    // Take 1 photo only with no interval
621
    auto cmd_take_photo = make_command_take_photo(component_id, 0.f, 1.0f);
×
622

623
    _system_impl->send_command_async(
×
624
        cmd_take_photo, [this, callback](MavlinkCommandSender::Result result, float) {
×
625
            receive_command_result(result, callback);
×
626
        });
×
627
}
×
628

629
void CameraImpl::start_photo_interval_async(
×
630
    int32_t component_id, float interval_s, const Camera::ResultCallback& callback)
631
{
632
    auto cmd_take_photo_time_lapse = make_command_take_photo(component_id, interval_s, 0.f);
×
633

634
    _system_impl->send_command_async(
×
635
        cmd_take_photo_time_lapse, [this, callback](MavlinkCommandSender::Result result, float) {
×
636
            receive_command_result(result, callback);
×
637
        });
×
638
}
×
639

640
void CameraImpl::stop_photo_interval_async(
×
641
    int32_t component_id, const Camera::ResultCallback& callback)
642
{
643
    auto cmd_stop_photo_interval = make_command_stop_photo(component_id);
×
644

645
    _system_impl->send_command_async(
×
646
        cmd_stop_photo_interval, [this, callback](MavlinkCommandSender::Result result, float) {
×
647
            receive_command_result(result, callback);
×
648
        });
×
649
}
×
650

651
void CameraImpl::start_video_async(int32_t component_id, const Camera::ResultCallback& callback)
×
652
{
653
    // Capture status rate is not set
654
    auto cmd_start_video = make_command_start_video(component_id, 0.f);
×
655

656
    _system_impl->send_command_async(
×
657
        cmd_start_video, [this, callback](MavlinkCommandSender::Result result, float) {
×
658
            receive_command_result(result, callback);
×
659
        });
×
660
}
×
661

662
void CameraImpl::stop_video_async(int32_t component_id, const Camera::ResultCallback& callback)
×
663
{
664
    auto cmd_stop_video = make_command_stop_video(component_id);
×
665

666
    _system_impl->send_command_async(
×
667
        cmd_stop_video, [this, callback](MavlinkCommandSender::Result result, float) {
×
668
            receive_command_result(result, callback);
×
669
        });
×
670
}
×
671

672
Camera::CameraList CameraImpl::camera_list()
88✔
673
{
674
    std::lock_guard lock(_mutex);
88✔
675

676
    return camera_list_with_lock();
88✔
677
}
88✔
678

679
Camera::CameraList CameraImpl::camera_list_with_lock()
105✔
680
{
681
    Camera::CameraList result{};
105✔
682

683
    for (auto& potential_camera : potential_cameras_with_lock()) {
209✔
684
        if (!potential_camera->maybe_information) {
104✔
685
            continue;
×
686
        }
687
        result.cameras.push_back(potential_camera->maybe_information.value());
104✔
688
    }
105✔
689

690
    return result;
105✔
691
}
692

693
Camera::CameraListHandle
694
CameraImpl::subscribe_camera_list(const Camera::CameraListCallback& callback)
1✔
695
{
696
    std::lock_guard lock(_mutex);
1✔
697
    auto handle = _camera_list_subscription_callbacks.subscribe(callback);
1✔
698

699
    notify_camera_list_with_lock();
1✔
700

701
    return handle;
2✔
702
}
1✔
703

704
void CameraImpl::unsubscribe_camera_list(Camera::CameraListHandle handle)
1✔
705
{
706
    std::lock_guard lock(_mutex);
1✔
707
    _camera_list_subscription_callbacks.unsubscribe(handle);
1✔
708
}
1✔
709

710
void CameraImpl::notify_camera_list_with_lock()
17✔
711
{
712
    _system_impl->call_user_callback([&]() {
37✔
713
        _camera_list_subscription_callbacks.queue(
714
            camera_list_with_lock(), [this](const auto& func) { func(); });
715
    });
716
}
17✔
717

718
Camera::Result CameraImpl::start_video_streaming(int32_t component_id, int32_t stream_id)
×
719
{
720
    auto command = make_command_start_video_streaming(component_id, stream_id);
×
721

722
    return camera_result_from_command_result(_system_impl->send_command(command));
×
723
}
724

725
Camera::Result CameraImpl::stop_video_streaming(int32_t component_id, int32_t stream_id)
×
726
{
727
    auto command = make_command_stop_video_streaming(component_id, stream_id);
×
728

729
    return camera_result_from_command_result(_system_impl->send_command(command));
×
730
}
731

732
void CameraImpl::request_slower()
10✔
733
{
734
    std::lock_guard lock(_mutex);
10✔
735

736
    for (auto& camera : _potential_cameras) {
11✔
737
        _system_impl->mavlink_request_message().request(
2✔
738
            MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION,
739
            fixup_component_target(camera.component_id),
1✔
740
            nullptr);
741
    }
742
}
10✔
743

744
void CameraImpl::request_faster()
14✔
745
{
746
    std::lock_guard lock(_mutex);
14✔
747

748
    for (auto& camera : _potential_cameras) {
19✔
749
        _system_impl->mavlink_request_message().request(
10✔
750
            MAVLINK_MSG_ID_VIDEO_STREAM_STATUS,
751
            fixup_component_target(camera.component_id),
5✔
752
            nullptr);
753

754
        _system_impl->mavlink_request_message().request(
10✔
755
            MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS,
756
            fixup_component_target(camera.component_id),
5✔
757
            nullptr);
758

759
        _system_impl->mavlink_request_message().request(
10✔
760
            MAVLINK_MSG_ID_STORAGE_INFORMATION,
761
            fixup_component_target(camera.component_id),
5✔
762
            nullptr);
763

764
        _system_impl->mavlink_request_message().request(
10✔
765
            MAVLINK_MSG_ID_CAMERA_SETTINGS, fixup_component_target(camera.component_id), nullptr);
5✔
766
    }
767
}
14✔
768

769
Camera::VideoStreamInfoHandle
770
CameraImpl::subscribe_video_stream_info(const Camera::VideoStreamInfoCallback& callback)
1✔
771
{
772
    std::lock_guard lock(_mutex);
1✔
773

774
    auto handle = _video_stream_info_subscription_callbacks.subscribe(callback);
1✔
775

776
    notify_video_stream_info_for_all_with_lock();
1✔
777

778
    return handle;
2✔
779
}
1✔
780

781
void CameraImpl::unsubscribe_video_stream_info(Camera::VideoStreamInfoHandle handle)
×
782
{
783
    std::lock_guard lock(_mutex);
×
784
    _video_stream_info_subscription_callbacks.unsubscribe(handle);
×
785
}
×
786

787
Camera::Result
788
CameraImpl::camera_result_from_command_result(const MavlinkCommandSender::Result command_result)
7✔
789
{
790
    switch (command_result) {
7✔
791
        case MavlinkCommandSender::Result::Success:
7✔
792
            return Camera::Result::Success;
7✔
793
        case MavlinkCommandSender::Result::NoSystem:
×
794
            // FALLTHROUGH
795
        case MavlinkCommandSender::Result::ConnectionError:
796
            // FALLTHROUGH
797
        case MavlinkCommandSender::Result::Busy:
798
            // FALLTHROUGH
799
        case MavlinkCommandSender::Result::Failed:
800
            return Camera::Result::Error;
×
801
        case MavlinkCommandSender::Result::Denied:
×
802
            // FALLTHROUGH
803
        case MavlinkCommandSender::Result::TemporarilyRejected:
804
            return Camera::Result::Denied;
×
805
        case MavlinkCommandSender::Result::Timeout:
×
806
            return Camera::Result::Timeout;
×
807
        case MavlinkCommandSender::Result::Unsupported:
×
808
            return Camera::Result::ActionUnsupported;
×
809
        case MavlinkCommandSender::Result::Cancelled:
×
810
        default:
811
            return Camera::Result::Unknown;
×
812
    }
813
}
814

815
Camera::Result CameraImpl::camera_result_from_parameter_result(
×
816
    const MavlinkParameterClient::Result parameter_result)
817
{
818
    switch (parameter_result) {
×
819
        case MavlinkParameterClient::Result::Success:
×
820
            return Camera::Result::Success;
×
821
        case MavlinkParameterClient::Result::Timeout:
×
822
            return Camera::Result::Timeout;
×
823
        case MavlinkParameterClient::Result::WrongType:
×
824
            // FALLTHROUGH
825
        case MavlinkParameterClient::Result::ParamNameTooLong:
826
            // FALLTHROUGH
827
        case MavlinkParameterClient::Result::NotFound:
828
            // FALLTHROUGH
829
        case MavlinkParameterClient::Result::ValueUnsupported:
830
            return Camera::Result::WrongArgument;
×
831
        case MavlinkParameterClient::Result::ConnectionError:
×
832
            // FALLTHROUGH
833
        case MavlinkParameterClient::Result::Failed:
834
            // FALLTHROUGH
835
        case MavlinkParameterClient::Result::UnknownError:
836
            return Camera::Result::Error;
×
837
        default:
×
838
            return Camera::Result::Unknown;
×
839
    }
840
}
841

842
Camera::Result CameraImpl::set_mode(int32_t component_id, const Camera::Mode mode)
2✔
843
{
844
    const float mavlink_mode = to_mavlink_camera_mode(mode);
2✔
845
    auto cmd_set_camera_mode = make_command_set_camera_mode(component_id, mavlink_mode);
2✔
846
    const auto command_result = _system_impl->send_command(cmd_set_camera_mode);
2✔
847
    const auto camera_result = camera_result_from_command_result(command_result);
2✔
848

849
    if (camera_result == Camera::Result::Success) {
2✔
850
        std::lock_guard lock(_mutex);
2✔
851
        auto maybe_potential_camera =
852
            maybe_potential_camera_for_component_id_with_lock(component_id, 0);
2✔
853
        if (maybe_potential_camera != nullptr) {
2✔
854
            save_camera_mode_with_lock(*maybe_potential_camera, mode);
2✔
855
        }
856
    }
2✔
857

858
    return camera_result;
2✔
859
}
860

861
void CameraImpl::save_camera_mode_with_lock(PotentialCamera& potential_camera, Camera::Mode mode)
6✔
862
{
863
    potential_camera.mode = mode;
6✔
864

865
    // If there is a camera definition which is the case if we are in this
866
    // function, and if CAM_MODE is defined there, then we reuse that type.
867
    // Otherwise, we hardcode it to `uint32_t`.
868

869
    // Note that it could be that the camera definition defines options
870
    // different from {PHOTO, VIDEO}, in which case the mode received from
871
    // CAMERA_SETTINGS will be wrong.
872

873
    if (!potential_camera.camera_definition) {
6✔
874
        return;
6✔
875
    }
876

877
    ParamValue value;
×
878
    if (potential_camera.camera_definition->get_setting("CAM_MODE", value)) {
×
879
        if (value.is<uint8_t>()) {
×
880
            value.set<uint8_t>(static_cast<uint8_t>(mode));
×
881
        } else if (value.is<int8_t>()) {
×
882
            value.set<int8_t>(static_cast<int8_t>(mode));
×
883
        } else if (value.is<uint16_t>()) {
×
884
            value.set<uint16_t>(static_cast<uint16_t>(mode));
×
885
        } else if (value.is<int16_t>()) {
×
886
            value.set<int16_t>(static_cast<int16_t>(mode));
×
887
        } else if (value.is<uint32_t>()) {
×
888
            value.set<uint32_t>(static_cast<uint32_t>(mode));
×
889
        } else if (value.is<int32_t>()) {
×
890
            value.set<int32_t>(static_cast<int32_t>(mode));
×
891
        } else if (value.is<uint64_t>()) {
×
892
            value.set<uint64_t>(static_cast<uint64_t>(mode));
×
893
        } else if (value.is<int64_t>()) {
×
894
            value.set<int64_t>(static_cast<int64_t>(mode));
×
895
        } else if (value.is<float>()) {
×
896
            value.set<float>(static_cast<float>(mode));
×
897
        } else if (value.is<double>()) {
×
898
            value.set<double>(static_cast<double>(mode));
×
899
        }
900
    } else {
901
        value.set<uint32_t>(static_cast<uint32_t>(mode));
×
902
    }
903

904
    potential_camera.camera_definition->set_setting("CAM_MODE", value);
×
905
    refresh_params_with_lock(potential_camera, false);
×
906
    notify_mode_with_lock(potential_camera);
×
907
}
×
908

909
float CameraImpl::to_mavlink_camera_mode(const Camera::Mode mode)
2✔
910
{
911
    switch (mode) {
2✔
912
        case Camera::Mode::Photo:
1✔
913
            return CAMERA_MODE_IMAGE;
1✔
914
        case Camera::Mode::Video:
1✔
915
            return CAMERA_MODE_VIDEO;
1✔
916
        default:
×
917
        case Camera::Mode::Unknown:
918
            return NAN;
×
919
    }
920
}
921

922
void CameraImpl::set_mode_async(
×
923
    int32_t component_id, const Camera::Mode mode, const Camera::ResultCallback& callback)
924
{
925
    const auto mavlink_mode = to_mavlink_camera_mode(mode);
×
926
    auto cmd_set_camera_mode = make_command_set_camera_mode(component_id, mavlink_mode);
×
927

928
    _system_impl->send_command_async(
×
929
        cmd_set_camera_mode,
930
        [this, callback, mode, component_id](MavlinkCommandSender::Result result, float progress) {
×
931
            UNUSED(progress);
×
932
            receive_set_mode_command_result(result, callback, mode, component_id);
×
933
        });
×
934
}
×
935

936
Camera::ModeHandle CameraImpl::subscribe_mode(const Camera::ModeCallback& callback)
×
937
{
938
    std::lock_guard lock(_mutex);
×
939
    auto handle = _mode_subscription_callbacks.subscribe(callback);
×
940

941
    notify_mode_for_all_with_lock();
×
942

943
    return handle;
×
944
}
×
945

946
void CameraImpl::unsubscribe_mode(Camera::ModeHandle handle)
×
947
{
948
    std::lock_guard lock(_mutex);
×
949
    _mode_subscription_callbacks.unsubscribe(handle);
×
950
}
×
951

952
Camera::StorageHandle CameraImpl::subscribe_storage(const Camera::StorageCallback& callback)
1✔
953
{
954
    std::lock_guard lock(_mutex);
1✔
955
    auto handle = _storage_subscription_callbacks.subscribe(callback);
1✔
956

957
    notify_storage_for_all_with_lock();
1✔
958

959
    return handle;
2✔
960
}
1✔
961

962
void CameraImpl::unsubscribe_storage(Camera::StorageHandle handle)
×
963
{
964
    std::lock_guard lock(_mutex);
×
965
    _storage_subscription_callbacks.unsubscribe(handle);
×
966
}
×
967

968
Camera::CaptureInfoHandle
969
CameraImpl::subscribe_capture_info(const Camera::CaptureInfoCallback& callback)
2✔
970
{
971
    std::lock_guard lock(_mutex);
2✔
972
    return _capture_info_callbacks.subscribe(callback);
2✔
973
}
2✔
974

975
void CameraImpl::unsubscribe_capture_info(Camera::CaptureInfoHandle handle)
2✔
976
{
977
    std::lock_guard lock(_mutex);
2✔
978
    _capture_info_callbacks.unsubscribe(handle);
2✔
979
}
2✔
980

981
void CameraImpl::process_heartbeat(const mavlink_message_t& message)
35✔
982
{
983
    // Check for potential camera
984
    std::lock_guard lock(_mutex);
35✔
985
    auto found =
986
        std::any_of(_potential_cameras.begin(), _potential_cameras.end(), [&](const auto& item) {
35✔
987
            return item.component_id == message.compid;
26✔
988
        });
989

990
    if (!found) {
35✔
991
        _potential_cameras.emplace_back(message.compid);
9✔
992
        check_potential_cameras_with_lock();
9✔
993
    }
994
}
35✔
995

996
void CameraImpl::check_potential_cameras_with_lock()
9✔
997
{
998
    for (auto& potential_camera : _potential_cameras) {
18✔
999
        // First step, get information if we don't already have it.
1000
        if (!potential_camera.maybe_information) {
9✔
1001
            request_camera_information(potential_camera.component_id);
9✔
1002
            potential_camera.information_requested = true;
9✔
1003
        }
1004
    }
1005
}
9✔
1006

1007
void CameraImpl::process_camera_capture_status(const mavlink_message_t& message)
4✔
1008
{
1009
    mavlink_camera_capture_status_t camera_capture_status;
4✔
1010
    mavlink_msg_camera_capture_status_decode(&message, &camera_capture_status);
4✔
1011

1012
    std::lock_guard lock(_mutex);
4✔
1013
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(
4✔
1014
        message.compid, camera_capture_status.camera_device_id);
4✔
1015

1016
    if (maybe_potential_camera == nullptr) {
4✔
1017
        return;
×
1018
    }
1019

1020
    auto& camera = *maybe_potential_camera;
4✔
1021

1022
    // If image_count got smaller, consider that the storage was formatted.
1023
    if (camera_capture_status.image_count < camera.capture_status.image_count) {
4✔
1024
        LogInfo() << "Seems like storage was formatted, setting state accordingly";
×
1025
        reset_following_format_storage_with_lock(camera);
×
1026
    }
1027

1028
    camera.storage.video_on = (camera_capture_status.video_status == 1);
4✔
1029
    camera.storage.photo_interval_on =
4✔
1030
        (camera_capture_status.image_status == 2 || camera_capture_status.image_status == 3);
4✔
1031
    camera.capture_status.received_camera_capture_status = true;
4✔
1032
    camera.storage.recording_time_s =
4✔
1033
        static_cast<float>(camera_capture_status.recording_time_ms) / 1e3f;
4✔
1034

1035
    camera.capture_status.image_count = camera_capture_status.image_count;
4✔
1036

1037
    if (camera.capture_status.image_count_at_connection == -1) {
4✔
1038
        camera.capture_status.image_count_at_connection = camera_capture_status.image_count;
2✔
1039
    }
1040
}
4✔
1041

1042
void CameraImpl::process_storage_information(const mavlink_message_t& message)
1✔
1043
{
1044
    mavlink_storage_information_t storage_information;
1✔
1045
    mavlink_msg_storage_information_decode(&message, &storage_information);
1✔
1046

1047
    std::lock_guard lock(_mutex);
1✔
1048
    // TODO: should STORAGE_INFORMATION have a camera_device_id?
1049
    auto maybe_potential_camera =
1050
        maybe_potential_camera_for_component_id_with_lock(message.compid, 0);
1✔
1051

1052
    if (maybe_potential_camera == nullptr) {
1✔
1053
        return;
×
1054
    }
1055

1056
    auto& camera = *maybe_potential_camera;
1✔
1057

1058
    camera.storage.storage_status = storage_status_from_mavlink(storage_information.status);
1✔
1059
    camera.storage.available_storage_mib = storage_information.available_capacity;
1✔
1060
    camera.storage.used_storage_mib = storage_information.used_capacity;
1✔
1061
    camera.storage.total_storage_mib = storage_information.total_capacity;
1✔
1062
    camera.storage.storage_id = storage_information.storage_id;
1✔
1063
    camera.storage.storage_type = storage_type_from_mavlink(storage_information.type);
1✔
1064
    camera.received_storage = true;
1✔
1065

1066
    notify_storage_with_lock(camera);
1✔
1067
}
1✔
1068

1069
Camera::Storage::StorageStatus CameraImpl::storage_status_from_mavlink(const int storage_status)
1✔
1070
{
1071
    switch (storage_status) {
1✔
1072
        case STORAGE_STATUS_EMPTY:
×
1073
            return Camera::Storage::StorageStatus::NotAvailable;
×
1074
        case STORAGE_STATUS_UNFORMATTED:
×
1075
            return Camera::Storage::StorageStatus::Unformatted;
×
1076
        case STORAGE_STATUS_READY:
1✔
1077
            return Camera::Storage::StorageStatus::Formatted;
1✔
1078
            break;
1079
        case STORAGE_STATUS_NOT_SUPPORTED:
×
1080
            return Camera::Storage::StorageStatus::NotSupported;
×
1081
        default:
×
1082
            LogErr() << "Unknown storage status received.";
×
1083
            return Camera::Storage::StorageStatus::NotSupported;
×
1084
    }
1085
}
1086

1087
Camera::Storage::StorageType CameraImpl::storage_type_from_mavlink(const int storage_type)
1✔
1088
{
1089
    switch (storage_type) {
1✔
1090
        default:
×
1091
            LogErr() << "Unknown storage_type enum value: " << storage_type;
×
1092
        // FALLTHROUGH
1093
        case STORAGE_TYPE_UNKNOWN:
1✔
1094
            return mavsdk::Camera::Storage::StorageType::Unknown;
1✔
1095
        case STORAGE_TYPE_USB_STICK:
×
1096
            return mavsdk::Camera::Storage::StorageType::UsbStick;
×
1097
        case STORAGE_TYPE_SD:
×
1098
            return mavsdk::Camera::Storage::StorageType::Sd;
×
1099
        case STORAGE_TYPE_MICROSD:
×
1100
            return mavsdk::Camera::Storage::StorageType::Microsd;
×
1101
        case STORAGE_TYPE_HD:
×
1102
            return mavsdk::Camera::Storage::StorageType::Hd;
×
1103
        case STORAGE_TYPE_OTHER:
×
1104
            return mavsdk::Camera::Storage::StorageType::Other;
×
1105
    }
1106
}
1107

1108
void CameraImpl::process_camera_image_captured(const mavlink_message_t& message)
4✔
1109
{
1110
    mavlink_camera_image_captured_t image_captured;
4✔
1111
    mavlink_msg_camera_image_captured_decode(&message, &image_captured);
4✔
1112

1113
    std::lock_guard lock(_mutex);
4✔
1114
    auto maybe_potential_camera =
1115
        maybe_potential_camera_for_component_id_with_lock(message.compid, image_captured.camera_id);
4✔
1116

1117
    if (maybe_potential_camera == nullptr) {
4✔
1118
        return;
×
1119
    }
1120

1121
    auto& camera = *maybe_potential_camera;
4✔
1122

1123
    mavsdk::Quaternion quaternion{};
4✔
1124
    quaternion.w = image_captured.q[0];
4✔
1125
    quaternion.x = image_captured.q[1];
4✔
1126
    quaternion.y = image_captured.q[2];
4✔
1127
    quaternion.z = image_captured.q[3];
4✔
1128
    auto euler = to_euler_angle_from_quaternion(quaternion);
4✔
1129

1130
    Camera::CaptureInfo capture_info = {};
4✔
1131
    capture_info.component_id = camera.component_id;
4✔
1132
    capture_info.position.latitude_deg = image_captured.lat / 1e7;
4✔
1133
    capture_info.position.longitude_deg = image_captured.lon / 1e7;
4✔
1134
    capture_info.position.absolute_altitude_m = static_cast<float>(image_captured.alt) / 1e3f;
4✔
1135
    capture_info.position.relative_altitude_m =
4✔
1136
        static_cast<float>(image_captured.relative_alt) / 1e3f;
4✔
1137
    capture_info.time_utc_us = image_captured.time_utc;
4✔
1138
    capture_info.attitude_quaternion.w = image_captured.q[0];
4✔
1139
    capture_info.attitude_quaternion.x = image_captured.q[1];
4✔
1140
    capture_info.attitude_quaternion.y = image_captured.q[2];
4✔
1141
    capture_info.attitude_quaternion.z = image_captured.q[3];
4✔
1142
    capture_info.attitude_euler_angle.roll_deg = euler.roll_deg;
4✔
1143
    capture_info.attitude_euler_angle.pitch_deg = euler.pitch_deg;
4✔
1144
    capture_info.attitude_euler_angle.yaw_deg = euler.yaw_deg;
4✔
1145
    capture_info.file_url = std::string(image_captured.file_url);
4✔
1146
    capture_info.is_success = (image_captured.capture_result == 1);
4✔
1147
    capture_info.index = image_captured.image_index;
4✔
1148

1149
    camera.capture_status.photo_list.insert(
8✔
1150
        std::make_pair(image_captured.image_index, capture_info));
8✔
1151

1152
    // Notify user if a new image has been captured.
1153
    if (camera.capture_info.last_advertised_image_index < capture_info.index) {
4✔
1154
        _capture_info_callbacks.queue(
4✔
1155
            capture_info, [this](const auto& func) { _system_impl->call_user_callback(func); });
4✔
1156

1157
        if (camera.capture_info.last_advertised_image_index != -1) {
4✔
1158
            // Save captured indices that have been dropped to request later, however, don't
1159
            // do it from the very beginning as there might be many photos from a previous
1160
            // time that we don't want to request.
1161
            for (int i = camera.capture_info.last_advertised_image_index + 1;
2✔
1162
                 i < capture_info.index;
2✔
1163
                 ++i) {
1164
                if (camera.capture_info.missing_image_retries.find(i) ==
×
1165
                    camera.capture_info.missing_image_retries.end()) {
×
1166
                    camera.capture_info.missing_image_retries[i] = 0;
×
1167
                }
1168
            }
1169
        }
1170

1171
        camera.capture_info.last_advertised_image_index = capture_info.index;
4✔
1172
    }
1173

1174
    else if (auto it = camera.capture_info.missing_image_retries.find(capture_info.index);
×
1175
             it != camera.capture_info.missing_image_retries.end()) {
×
1176
        _capture_info_callbacks.queue(
×
1177
            capture_info, [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1178
        camera.capture_info.missing_image_retries.erase(it);
×
1179
    }
1180
}
4✔
1181

1182
void CameraImpl::request_missing_capture_info()
69✔
1183
{
1184
    std::lock_guard lock(_mutex);
69✔
1185
    for (auto& potential_camera : _potential_cameras) {
129✔
1186
        // Clean out entries once we have tried 3 times.
1187
        for (auto it = potential_camera.capture_info.missing_image_retries.begin();
60✔
1188
             it != potential_camera.capture_info.missing_image_retries.end();
60✔
1189
             /* ++it */) {
1190
            if (it->second > 3) {
×
1191
                it = potential_camera.capture_info.missing_image_retries.erase(it);
×
1192
            } else {
1193
                ++it;
×
1194
            }
1195
        }
1196

1197
        // Request a few entries, 3 each time.
1198
        for (unsigned i = 0; i < 3; ++i) {
240✔
1199
            if (!potential_camera.capture_info.missing_image_retries.empty()) {
180✔
1200
                auto it_lowest_retries = std::min_element(
×
1201
                    potential_camera.capture_info.missing_image_retries.begin(),
1202
                    potential_camera.capture_info.missing_image_retries.end());
1203
                _system_impl->mavlink_request_message().request(
×
1204
                    MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED,
1205
                    potential_camera.component_id,
×
1206
                    nullptr,
1207
                    it_lowest_retries->first);
×
1208
                it_lowest_retries->second += 1;
×
1209
            }
1210
        }
1211
    }
1212
}
69✔
1213

1214
void CameraImpl::process_camera_settings(const mavlink_message_t& message)
4✔
1215
{
1216
    mavlink_camera_settings_t camera_settings;
4✔
1217
    mavlink_msg_camera_settings_decode(&message, &camera_settings);
4✔
1218

1219
    std::lock_guard lock(_mutex);
4✔
1220
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(
4✔
1221
        message.compid, camera_settings.camera_device_id);
4✔
1222

1223
    if (maybe_potential_camera == nullptr) {
4✔
1224
        return;
×
1225
    }
1226

1227
    save_camera_mode_with_lock(*maybe_potential_camera, to_camera_mode(camera_settings.mode_id));
4✔
1228
}
4✔
1229

1230
Camera::Mode CameraImpl::to_camera_mode(const uint8_t mavlink_camera_mode)
4✔
1231
{
1232
    switch (mavlink_camera_mode) {
4✔
1233
        case CAMERA_MODE_IMAGE:
4✔
1234
            return Camera::Mode::Photo;
4✔
1235
        case CAMERA_MODE_VIDEO:
×
1236
            return Camera::Mode::Video;
×
1237
        default:
×
1238
            return Camera::Mode::Unknown;
×
1239
    }
1240
}
1241

1242
void CameraImpl::process_camera_information(const mavlink_message_t& message)
9✔
1243
{
1244
    mavlink_camera_information_t camera_information;
9✔
1245
    mavlink_msg_camera_information_decode(&message, &camera_information);
9✔
1246

1247
    // Make sure all strings are zero terminated, so we don't overrun anywhere.
1248
    camera_information.vendor_name[sizeof(camera_information.vendor_name) - 1] = '\0';
9✔
1249
    camera_information.model_name[sizeof(camera_information.model_name) - 1] = '\0';
9✔
1250
    camera_information.cam_definition_uri[sizeof(camera_information.cam_definition_uri) - 1] = '\0';
9✔
1251

1252
    Camera::Information new_information{};
9✔
1253
    // TODO: Check the case for 1-6.
1254
    new_information.component_id = message.compid;
9✔
1255
    new_information.vendor_name = reinterpret_cast<char*>(camera_information.vendor_name);
9✔
1256
    new_information.model_name = reinterpret_cast<char*>(camera_information.model_name);
9✔
1257
    new_information.focal_length_mm = camera_information.focal_length;
9✔
1258
    new_information.horizontal_sensor_size_mm = camera_information.sensor_size_h;
9✔
1259
    new_information.vertical_sensor_size_mm = camera_information.sensor_size_v;
9✔
1260
    new_information.horizontal_resolution_px = camera_information.resolution_h;
9✔
1261
    new_information.vertical_resolution_px = camera_information.resolution_v;
9✔
1262

1263
    std::lock_guard lock(_mutex);
9✔
1264

1265
    auto potential_camera =
9✔
1266
        std::find_if(_potential_cameras.begin(), _potential_cameras.end(), [&](auto& item) {
9✔
1267
            return item.component_id == message.compid;
9✔
1268
        });
1269

1270
    if (potential_camera == _potential_cameras.end()) {
9✔
1271
        _potential_cameras.emplace_back(message.compid);
×
1272
        potential_camera = std::prev(_potential_cameras.end());
×
1273
    }
1274

1275
    // We need a copy of the component ID inside the information.
1276
    potential_camera->component_id = new_information.component_id;
9✔
1277
    potential_camera->maybe_information = new_information;
9✔
1278
    potential_camera->camera_definition_url = camera_information.cam_definition_uri;
9✔
1279
    potential_camera->camera_definition_version = camera_information.cam_definition_version;
9✔
1280
    check_camera_definition_with_lock(*potential_camera);
9✔
1281
}
9✔
1282

1283
void CameraImpl::check_camera_definition_with_lock(PotentialCamera& potential_camera)
9✔
1284
{
1285
    const std::string url = potential_camera.camera_definition_url;
9✔
1286

1287
    if (potential_camera.camera_definition_url.empty()) {
9✔
1288
        potential_camera.camera_definition_result = Camera::Result::Unavailable;
7✔
1289
        notify_camera_list_with_lock();
7✔
1290
    }
1291

1292
    const auto& info = potential_camera.maybe_information.value();
9✔
1293
    auto file_cache_tag = replace_non_ascii_and_whitespace(
9✔
1294
        std::string("camera_definition-") + info.model_name + "_" + info.vendor_name + "-" +
81✔
1295
        std::to_string(potential_camera.camera_definition_version) + ".xml");
45✔
1296

1297
    std::optional<std::filesystem::path> cached_file_option{};
9✔
1298
    if (_file_cache) {
9✔
1299
        cached_file_option = _file_cache->access(file_cache_tag);
9✔
1300
    }
1301

1302
    if (cached_file_option) {
9✔
1303
        LogInfo() << "Using cached file " << cached_file_option.value();
1✔
1304
        load_camera_definition_with_lock(potential_camera, cached_file_option.value());
1✔
1305
        potential_camera.is_fetching_camera_definition = false;
1✔
1306
        potential_camera.camera_definition_result = Camera::Result::Success;
1✔
1307
        notify_camera_list_with_lock();
1✔
1308

1309
    } else {
1310
        potential_camera.is_fetching_camera_definition = true;
8✔
1311

1312
        if (url.empty()) {
8✔
1313
            LogInfo() << "No camera definition URL available";
7✔
1314
            potential_camera.camera_definition_result = Camera::Result::ProtocolUnsupported;
7✔
1315
            notify_camera_list_with_lock();
7✔
1316

1317
        } else if (starts_with(url, "http://") || starts_with(url, "https://")) {
1✔
1318
#if BUILD_WITHOUT_CURL == 1
1319
            potential_camera.camera_definition_result = Camera::Result::ProtocolUnsupported;
1320
            notify_camera_list_with_lock();
1321
#else
1322
            if (_http_loader == nullptr) {
×
1323
                _http_loader = std::make_unique<HttpLoader>();
×
1324
            }
1325
            LogInfo() << "Downloading camera definition from: " << url;
×
1326

1327
            auto component_id = potential_camera.component_id;
×
1328

1329
            auto download_path = _tmp_download_path / file_cache_tag;
×
1330

1331
            _http_loader->download_async(
×
1332
                url,
1333
                download_path.string(),
×
1334
                [download_path, file_cache_tag, component_id, this](
×
1335
                    int progress, HttpStatus status, CURLcode curl_code) mutable {
×
1336
                    // TODO: check if we still exist
1337
                    LogDebug() << "Download progress: " << progress
×
1338
                               << ", status: " << static_cast<int>(status)
×
1339
                               << ", curl_code: " << std::to_string(curl_code);
×
1340

1341
                    std::lock_guard lock(_mutex);
×
1342
                    auto maybe_potential_camera =
1343
                        maybe_potential_camera_for_component_id_with_lock(component_id, 0);
×
1344
                    if (maybe_potential_camera == nullptr) {
×
1345
                        LogErr() << "Failed to find camera.";
×
1346
                    }
1347

1348
                    if (status == HttpStatus::Error) {
×
1349
                        LogErr() << "File download failed with result "
×
1350
                                 << std::to_string(curl_code);
×
1351
                        maybe_potential_camera->is_fetching_camera_definition = false;
×
1352
                        maybe_potential_camera->camera_definition_result = Camera::Result::Error;
×
1353
                        notify_camera_list_with_lock();
×
1354

1355
                    } else if (status == HttpStatus::Finished) {
×
1356
                        LogDebug() << "File download finished " << download_path;
×
1357
                        if (_file_cache) {
×
1358
                            // Cache the file (this will move/remove the temp file as well)
1359
                            download_path = _file_cache->insert(file_cache_tag, download_path)
×
1360
                                                .value_or(download_path);
×
1361
                            LogDebug() << "Cached path: " << download_path;
×
1362
                        }
1363
                        load_camera_definition_with_lock(*maybe_potential_camera, download_path);
×
1364
                        maybe_potential_camera->is_fetching_camera_definition = false;
×
1365
                        maybe_potential_camera->camera_definition_result = Camera::Result::Success;
×
1366
                        notify_camera_list_with_lock();
×
1367
                    }
1368
                });
×
1369
#endif
1370
        } else if (starts_with(url, "mftp://") || starts_with(url, "mavlinkftp://")) {
1✔
1371
            LogInfo() << "Download file: " << url << " using MAVLink FTP...";
1✔
1372

1373
            auto component_id = potential_camera.component_id;
1✔
1374

1375
            auto downloaded_filename = strip_prefix(strip_prefix(url, "mavlinkftp://"), "mftp://");
6✔
1376

1377
            _system_impl->mavlink_ftp_client().download_async(
3✔
1378
                downloaded_filename,
1379
                _tmp_download_path.string(),
2✔
1380
                false,
1381
                [file_cache_tag, downloaded_filename, component_id, this](
17✔
1382
                    MavlinkFtpClient::ClientResult client_result,
1383
                    MavlinkFtpClient::ProgressData progress_data) mutable {
7✔
1384
                    // TODO: check if we still exist
1385
                    if (client_result == MavlinkFtpClient::ClientResult::Next) {
17✔
1386
                        LogDebug()
32✔
1387
                            << "Mavlink FTP download progress: "
16✔
1388
                            << 100 * progress_data.bytes_transferred / progress_data.total_bytes
32✔
1389
                            << " %";
48✔
1390
                        return;
16✔
1391
                    }
1392

1393
                    std::lock_guard lock(_mutex);
1✔
1394
                    auto maybe_potential_camera =
1395
                        maybe_potential_camera_for_component_id_with_lock(component_id, 0);
1✔
1396
                    if (maybe_potential_camera == nullptr) {
1✔
1397
                        LogErr() << "Failed to find camera with ID " << component_id;
×
1398
                        return;
×
1399
                    }
1400

1401
                    if (client_result != MavlinkFtpClient::ClientResult::Success) {
1✔
1402
                        LogErr() << "File download failed with result " << client_result;
×
1403
                        maybe_potential_camera->is_fetching_camera_definition = false;
×
1404
                        maybe_potential_camera->camera_definition_result = Camera::Result::Error;
×
1405
                        notify_camera_list_with_lock();
×
1406
                        return;
×
1407
                    }
1408

1409
                    auto downloaded_filepath = _tmp_download_path / downloaded_filename;
2✔
1410

1411
                    LogDebug() << "File download finished to " << downloaded_filepath;
1✔
1412
                    if (_file_cache) {
1✔
1413
                        // Cache the file (this will move/remove the temp file as well)
1414
                        downloaded_filepath =
1✔
1415
                            _file_cache->insert(file_cache_tag, downloaded_filepath)
2✔
1416
                                .value_or(downloaded_filepath);
2✔
1417
                        LogDebug() << "Cached path: " << downloaded_filepath;
1✔
1418
                    }
1419
                    load_camera_definition_with_lock(*maybe_potential_camera, downloaded_filepath);
1✔
1420
                    maybe_potential_camera->is_fetching_camera_definition = false;
1✔
1421
                    maybe_potential_camera->camera_definition_result = Camera::Result::Success;
1✔
1422
                    notify_camera_list_with_lock();
1✔
1423
                });
1✔
1424
        } else {
1✔
1425
            LogErr() << "Unknown protocol for URL: " << url;
×
1426
            potential_camera.camera_definition_result = Camera::Result::ProtocolUnsupported;
×
1427
            notify_camera_list_with_lock();
×
1428
        }
1429
    }
1430
}
9✔
1431

1432
void CameraImpl::load_camera_definition_with_lock(
2✔
1433
    PotentialCamera& potential_camera, const std::filesystem::path& path)
1434
{
1435
    if (potential_camera.camera_definition == nullptr) {
2✔
1436
        potential_camera.camera_definition = std::make_unique<CameraDefinition>();
2✔
1437
    }
1438

1439
    if (!potential_camera.camera_definition->load_file(path.string())) {
2✔
1440
        LogErr() << "Failed to load camera definition: " << path;
×
1441
        // We can't keep something around that's not loaded correctly.
1442
        potential_camera.camera_definition = nullptr;
×
1443
        return;
×
1444
    }
1445

1446
    // We assume default settings initially and then load the params one by one.
1447
    // This way we can start using it straightaway.
1448
    potential_camera.camera_definition->reset_to_default_settings(true);
2✔
1449

1450
    refresh_params_with_lock(potential_camera, true);
2✔
1451
}
1452

1453
void CameraImpl::process_video_information(const mavlink_message_t& message)
1✔
1454
{
1455
    mavlink_video_stream_information_t received_video_info;
1✔
1456
    mavlink_msg_video_stream_information_decode(&message, &received_video_info);
1✔
1457

1458
    std::lock_guard lock(_mutex);
1✔
1459
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(
1✔
1460
        message.compid, received_video_info.camera_device_id);
1✔
1461

1462
    if (maybe_potential_camera == nullptr) {
1✔
1463
        return;
×
1464
    }
1465

1466
    auto& camera = *maybe_potential_camera;
1✔
1467

1468
    // TODO: use stream_id and count
1469
    camera.video_stream_info.status =
1✔
1470
        (received_video_info.flags & VIDEO_STREAM_STATUS_FLAGS_RUNNING ?
1✔
1471
             Camera::VideoStreamInfo::VideoStreamStatus::InProgress :
1472
             Camera::VideoStreamInfo::VideoStreamStatus::NotRunning);
1473
    camera.video_stream_info.spectrum =
1✔
1474
        (received_video_info.flags & VIDEO_STREAM_STATUS_FLAGS_THERMAL ?
1✔
1475
             Camera::VideoStreamInfo::VideoStreamSpectrum::Infrared :
1476
             Camera::VideoStreamInfo::VideoStreamSpectrum::VisibleLight);
1477

1478
    auto& video_stream_info = camera.video_stream_info.settings;
1✔
1479
    video_stream_info.frame_rate_hz = received_video_info.framerate;
1✔
1480
    video_stream_info.horizontal_resolution_pix = received_video_info.resolution_h;
1✔
1481
    video_stream_info.vertical_resolution_pix = received_video_info.resolution_v;
1✔
1482
    video_stream_info.bit_rate_b_s = received_video_info.bitrate;
1✔
1483
    video_stream_info.rotation_deg = received_video_info.rotation;
1✔
1484
    video_stream_info.horizontal_fov_deg = static_cast<float>(received_video_info.hfov);
1✔
1485
    video_stream_info.uri = received_video_info.uri;
1✔
1486
    camera.video_stream_info.stream_id = received_video_info.stream_id;
1✔
1487
    camera.received_video_stream_info = true;
1✔
1488

1489
    notify_video_stream_info_with_lock(camera);
1✔
1490
}
1✔
1491

1492
void CameraImpl::process_video_stream_status(const mavlink_message_t& message)
3✔
1493
{
1494
    mavlink_video_stream_status_t received_video_stream_status;
3✔
1495
    mavlink_msg_video_stream_status_decode(&message, &received_video_stream_status);
3✔
1496

1497
    std::lock_guard lock(_mutex);
3✔
1498
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(
3✔
1499
        message.compid, received_video_stream_status.camera_device_id);
3✔
1500

1501
    if (maybe_potential_camera == nullptr) {
3✔
1502
        return;
×
1503
    }
1504

1505
    auto& camera = *maybe_potential_camera;
3✔
1506

1507
    camera.video_stream_info.status =
3✔
1508
        (received_video_stream_status.flags & VIDEO_STREAM_STATUS_FLAGS_RUNNING ?
3✔
1509
             Camera::VideoStreamInfo::VideoStreamStatus::InProgress :
1510
             Camera::VideoStreamInfo::VideoStreamStatus::NotRunning);
1511
    camera.video_stream_info.spectrum =
3✔
1512
        (received_video_stream_status.flags & VIDEO_STREAM_STATUS_FLAGS_THERMAL ?
3✔
1513
             Camera::VideoStreamInfo::VideoStreamSpectrum::Infrared :
1514
             Camera::VideoStreamInfo::VideoStreamSpectrum::VisibleLight);
1515

1516
    auto& video_stream_info = camera.video_stream_info.settings;
3✔
1517
    video_stream_info.frame_rate_hz = received_video_stream_status.framerate;
3✔
1518
    video_stream_info.horizontal_resolution_pix = received_video_stream_status.resolution_h;
3✔
1519
    video_stream_info.vertical_resolution_pix = received_video_stream_status.resolution_v;
3✔
1520
    video_stream_info.bit_rate_b_s = received_video_stream_status.bitrate;
3✔
1521
    video_stream_info.rotation_deg = received_video_stream_status.rotation;
3✔
1522
    video_stream_info.horizontal_fov_deg = static_cast<float>(received_video_stream_status.hfov);
3✔
1523

1524
    // We only set this when we get the static information rather than just the status.
1525
    // camera.received_video_stream_info = true;
1526

1527
    notify_video_stream_info_with_lock(camera);
3✔
1528
}
3✔
1529

1530
void CameraImpl::notify_video_stream_info_for_all_with_lock()
1✔
1531
{
1532
    for (auto& camera : _potential_cameras) {
2✔
1533
        notify_video_stream_info_with_lock(camera);
1✔
1534
    }
1535
}
1✔
1536

1537
void CameraImpl::notify_video_stream_info_with_lock(PotentialCamera& camera)
5✔
1538
{
1539
    if (!camera.received_video_stream_info) {
5✔
1540
        return;
4✔
1541
    }
1542

1543
    _video_stream_info_subscription_callbacks.queue(
3✔
1544
        Camera::VideoStreamUpdate{camera.component_id, camera.video_stream_info},
2✔
1545
        [this](const auto& func) { _system_impl->call_user_callback(func); });
1✔
1546
}
1547

1548
void CameraImpl::notify_storage_for_all_with_lock()
1✔
1549
{
1550
    for (auto& potential_camera : potential_cameras_with_lock()) {
2✔
1551
        notify_storage_with_lock(*potential_camera);
1✔
1552
    }
1✔
1553
}
1✔
1554

1555
void CameraImpl::notify_storage_with_lock(PotentialCamera& camera)
2✔
1556
{
1557
    if (!camera.received_storage) {
2✔
1558
        return;
1✔
1559
    }
1560

1561
    _storage_subscription_callbacks.queue(
3✔
1562
        Camera::StorageUpdate{camera.component_id, camera.storage},
2✔
1563
        [this](const auto& func) { _system_impl->call_user_callback(func); });
1✔
1564
}
1565

1566
void CameraImpl::receive_command_result(
2✔
1567
    MavlinkCommandSender::Result command_result, const Camera::ResultCallback& callback) const
1568
{
1569
    Camera::Result camera_result = camera_result_from_command_result(command_result);
2✔
1570

1571
    if (callback) {
2✔
1572
        _system_impl->call_user_callback([callback, camera_result]() { callback(camera_result); });
4✔
1573
    }
1574
}
2✔
1575

1576
void CameraImpl::receive_set_mode_command_result(
×
1577
    const MavlinkCommandSender::Result command_result,
1578
    const Camera::ResultCallback& callback,
1579
    const Camera::Mode mode,
1580
    int32_t component_id)
1581
{
1582
    Camera::Result camera_result = camera_result_from_command_result(command_result);
×
1583

1584
    if (callback) {
×
1585
        _system_impl->call_user_callback([callback, camera_result]() { callback(camera_result); });
×
1586
    }
1587

1588
    if (camera_result == Camera::Result::Success) {
×
1589
        std::lock_guard lock(_mutex);
×
1590
        auto maybe_potential_camera =
1591
            maybe_potential_camera_for_component_id_with_lock(component_id, 0);
×
1592
        if (maybe_potential_camera != nullptr) {
×
1593
            save_camera_mode_with_lock(*maybe_potential_camera, mode);
×
1594
        }
1595
    }
×
1596
}
×
1597

1598
void CameraImpl::notify_mode_for_all_with_lock()
×
1599
{
1600
    for (auto& camera : potential_cameras_with_lock()) {
×
1601
        notify_mode_with_lock(*camera);
×
1602
    }
×
1603
}
×
1604

1605
void CameraImpl::notify_mode_with_lock(PotentialCamera& camera)
×
1606
{
1607
    _mode_subscription_callbacks.queue(
×
1608
        Camera::ModeUpdate{camera.component_id, camera.mode},
×
1609
        [this](const auto& func) { _system_impl->call_user_callback(func); });
×
1610
}
×
1611

1612
bool CameraImpl::get_possible_options_with_lock(
86✔
1613
    PotentialCamera& camera, const std::string& setting_id, std::vector<Camera::Option>& options)
1614
{
1615
    options.clear();
86✔
1616

1617
    if (!camera.camera_definition) {
86✔
1618
        LogWarn() << "Error: no camera definition available yet";
×
1619
        return false;
×
1620
    }
1621

1622
    std::vector<ParamValue> values;
86✔
1623
    if (!camera.camera_definition->get_possible_options(setting_id, values)) {
86✔
1624
        return false;
×
1625
    }
1626

1627
    for (const auto& value : values) {
312✔
1628
        Camera::Option option{};
226✔
1629
        option.option_id = value.get_string();
226✔
1630
        if (!camera.camera_definition->is_setting_range(setting_id)) {
226✔
1631
            get_option_str_with_lock(
88✔
1632
                camera, setting_id, option.option_id, option.option_description);
1633
        }
1634
        options.push_back(option);
226✔
1635
    }
226✔
1636

1637
    return !options.empty();
86✔
1638
}
86✔
1639

1640
Camera::Result CameraImpl::set_setting(int32_t component_id, const Camera::Setting& setting)
3✔
1641

1642
{
1643
    auto prom = std::make_shared<std::promise<Camera::Result>>();
3✔
1644
    auto ret = prom->get_future();
3✔
1645

1646
    set_setting_async(
3✔
1647
        component_id, setting, [&prom](Camera::Result result) { prom->set_value(result); });
3✔
1648

1649
    return ret.get();
3✔
1650
}
3✔
1651

1652
void CameraImpl::set_setting_async(
3✔
1653
    int32_t component_id, const Camera::Setting& setting, const Camera::ResultCallback& callback)
1654
{
1655
    set_option_async(component_id, setting.setting_id, setting.option, callback);
3✔
1656
}
3✔
1657

1658
void CameraImpl::set_option_async(
3✔
1659
    int32_t component_id,
1660
    const std::string& setting_id,
1661
    const Camera::Option& option,
1662
    const Camera::ResultCallback& callback)
1663
{
1664
    std::lock_guard lock(_mutex);
3✔
1665
    auto maybe_potential_camera =
1666
        maybe_potential_camera_for_component_id_with_lock(component_id, 0);
3✔
1667
    if (maybe_potential_camera == nullptr) {
3✔
1668
        if (callback != nullptr) {
×
1669
            _system_impl->call_user_callback(
×
1670
                [callback]() { callback(Camera::Result::CameraIdInvalid); });
1671
        }
1672
        return;
×
1673
    }
1674

1675
    auto& camera = *maybe_potential_camera;
3✔
1676

1677
    if (camera.camera_definition == nullptr) {
3✔
1678
        if (callback != nullptr) {
×
1679
            _system_impl->call_user_callback(
×
1680
                [callback]() { callback(Camera::Result::Unavailable); });
1681
        }
1682
        return;
×
1683
    }
1684

1685
    // We get it first so that we have the type of the param value.
1686
    ParamValue value;
3✔
1687

1688
    if (camera.camera_definition->is_setting_range(setting_id)) {
3✔
1689
        // TODO: Get type from minimum.
1690
        std::vector<ParamValue> all_values;
×
1691
        if (!camera.camera_definition->get_all_options(setting_id, all_values)) {
×
1692
            if (callback) {
×
1693
                LogErr() << "Could not get all options to get type for range param.";
×
1694
                _system_impl->call_user_callback([callback]() { callback(Camera::Result::Error); });
×
1695
            }
1696
            return;
×
1697
        }
1698

1699
        if (all_values.empty()) {
×
1700
            if (callback) {
×
1701
                LogErr() << "Could not get any options to get type for range param.";
×
1702
                _system_impl->call_user_callback([callback]() { callback(Camera::Result::Error); });
×
1703
            }
1704
            return;
×
1705
        }
1706
        value = all_values[0];
×
1707

1708
        // Now re-use that type. This is quite ugly, but I don't know how we could do better than
1709
        // that.
1710
        if (!value.set_as_same_type(option.option_id)) {
×
1711
            if (callback) {
×
1712
                LogErr() << "Could not set option value to given type.";
×
1713
                _system_impl->call_user_callback([callback]() { callback(Camera::Result::Error); });
×
1714
            }
1715
            return;
×
1716
        }
1717

1718
    } else {
×
1719
        if (!camera.camera_definition->get_option_value(setting_id, option.option_id, value)) {
3✔
1720
            if (callback) {
×
1721
                LogErr() << "Could not get option value.";
×
1722
                _system_impl->call_user_callback([callback]() { callback(Camera::Result::Error); });
×
1723
            }
1724
            return;
×
1725
        }
1726

1727
        std::vector<ParamValue> possible_values;
3✔
1728
        camera.camera_definition->get_possible_options(setting_id, possible_values);
3✔
1729
        bool allowed = false;
3✔
1730
        for (const auto& possible_value : possible_values) {
9✔
1731
            if (value == possible_value) {
6✔
1732
                allowed = true;
3✔
1733
            }
1734
        }
1735
        if (!allowed) {
3✔
1736
            LogErr() << "Setting " << setting_id << "(" << option.option_id << ") not allowed";
×
1737
            if (callback) {
×
1738
                _system_impl->call_user_callback([callback]() { callback(Camera::Result::Error); });
×
1739
            }
1740
            return;
×
1741
        }
1742
    }
3✔
1743

1744
    _system_impl->set_param_async(
6✔
1745
        setting_id,
1746
        value,
1747
        [this, component_id, callback, setting_id, value](MavlinkParameterClient::Result result) {
15✔
1748
            std::lock_guard lock_later(_mutex);
3✔
1749
            auto maybe_potential_camera_later =
1750
                maybe_potential_camera_for_component_id_with_lock(component_id, 0);
3✔
1751
            // We already checked these fields earlier, so we don't check again.
1752
            assert(maybe_potential_camera_later != nullptr);
3✔
1753
            assert(maybe_potential_camera_later->camera_definition != nullptr);
3✔
1754

1755
            auto& camera_later = *maybe_potential_camera_later;
3✔
1756

1757
            if (result != MavlinkParameterClient::Result::Success) {
3✔
1758
                if (callback) {
×
1759
                    _system_impl->call_user_callback([callback, result]() {
×
1760
                        callback(camera_result_from_parameter_result(result));
1761
                    });
1762
                }
1763
                return;
×
1764
            }
1765

1766
            if (!camera_later.camera_definition->set_setting(setting_id, value)) {
3✔
1767
                if (callback) {
×
1768
                    _system_impl->call_user_callback(
×
1769
                        [callback]() { callback(Camera::Result::Error); });
1770
                }
1771
                return;
×
1772
            }
1773

1774
            if (callback) {
3✔
1775
                _system_impl->call_user_callback(
6✔
1776
                    [callback]() { callback(Camera::Result::Success); });
1777
            }
1778
            refresh_params_with_lock(camera_later, false);
3✔
1779
        },
3✔
1780
        this,
1781
        camera.component_id,
3✔
1782
        true);
1783
}
3✔
1784

1785
void CameraImpl::get_setting_async(
2✔
1786
    int32_t component_id,
1787
    const Camera::Setting& setting,
1788
    const Camera::GetSettingCallback& callback)
1789
{
1790
    {
1791
        std::lock_guard lock(_mutex);
2✔
1792
        auto maybe_potential_camera =
1793
            maybe_potential_camera_for_component_id_with_lock(component_id, 0);
2✔
1794
        if (maybe_potential_camera == nullptr) {
2✔
1795
            if (callback != nullptr) {
×
1796
                _system_impl->call_user_callback(
×
1797
                    [callback]() { callback(Camera::Result::CameraIdInvalid, {}); });
1798
            }
1799
            return;
×
1800
        }
1801

1802
        auto& camera = *maybe_potential_camera;
2✔
1803

1804
        if (camera.camera_definition == nullptr) {
2✔
1805
            if (callback != nullptr) {
×
1806
                _system_impl->call_user_callback(
×
1807
                    [callback]() { callback(Camera::Result::Unavailable, {}); });
1808
            }
1809
            return;
×
1810
        }
1811
    }
2✔
1812

1813
    get_option_async(
4✔
1814
        component_id,
1815
        setting.setting_id,
2✔
1816
        [this, callback](Camera::Result result, const Camera::Option& option) {
4✔
1817
            Camera::Setting new_setting{};
2✔
1818
            new_setting.option = option;
2✔
1819
            if (callback) {
2✔
1820
                _system_impl->call_user_callback(
4✔
1821
                    [callback, result, new_setting]() { callback(result, new_setting); });
1822
            }
1823
        });
2✔
1824
}
1825

1826
std::pair<Camera::Result, Camera::Setting>
1827
CameraImpl::get_setting(int32_t component_id, const Camera::Setting& setting)
2✔
1828
{
1829
    auto prom = std::make_shared<std::promise<std::pair<Camera::Result, Camera::Setting>>>();
2✔
1830
    auto ret = prom->get_future();
2✔
1831

1832
    get_setting_async(
2✔
1833
        component_id, setting, [&prom](Camera::Result result, const Camera::Setting& new_setting) {
2✔
1834
            prom->set_value(std::make_pair<>(result, new_setting));
2✔
1835
        });
2✔
1836

1837
    return ret.get();
2✔
1838
}
2✔
1839

1840
Camera::Result
1841
CameraImpl::get_option(int32_t component_id, const std::string& setting_id, Camera::Option& option)
×
1842
{
1843
    auto prom = std::make_shared<std::promise<Camera::Result>>();
×
1844
    auto ret = prom->get_future();
×
1845

1846
    get_option_async(
×
1847
        component_id,
1848
        setting_id,
1849
        [prom, &option](Camera::Result result, const Camera::Option& option_gotten) {
×
1850
            prom->set_value(result);
×
1851
            if (result == Camera::Result::Success) {
×
1852
                option = option_gotten;
×
1853
            }
1854
        });
×
1855

1856
    auto status = ret.wait_for(std::chrono::seconds(1));
×
1857

1858
    if (status == std::future_status::ready) {
×
1859
        return Camera::Result::Success;
×
1860
    } else {
1861
        return Camera::Result::Timeout;
×
1862
    }
1863
}
×
1864

1865
void CameraImpl::get_option_async(
2✔
1866
    int32_t component_id,
1867
    const std::string& setting_id,
1868
    const std::function<void(Camera::Result, const Camera::Option&)>& callback)
1869
{
1870
    std::lock_guard lock(_mutex);
2✔
1871
    auto maybe_potential_camera =
1872
        maybe_potential_camera_for_component_id_with_lock(component_id, 0);
2✔
1873
    if (maybe_potential_camera == nullptr) {
2✔
1874
        if (callback != nullptr) {
×
1875
            _system_impl->call_user_callback(
×
1876
                [callback]() { callback(Camera::Result::CameraIdInvalid, {}); });
1877
        }
1878
        return;
×
1879
    }
1880

1881
    auto& camera = *maybe_potential_camera;
2✔
1882

1883
    if (camera.camera_definition == nullptr) {
2✔
1884
        if (callback != nullptr) {
×
1885
            _system_impl->call_user_callback(
×
1886
                [callback]() { callback(Camera::Result::Unavailable, {}); });
1887
        }
1888
        return;
×
1889
    }
1890

1891
    ParamValue value;
2✔
1892
    // We should have this cached and don't need to get the param.
1893
    if (camera.camera_definition->get_setting(setting_id, value)) {
2✔
1894
        if (callback) {
2✔
1895
            Camera::Option new_option{};
2✔
1896
            new_option.option_id = value.get_string();
2✔
1897
            if (!camera.camera_definition->is_setting_range(setting_id)) {
2✔
1898
                get_option_str_with_lock(
1✔
1899
                    camera, setting_id, new_option.option_id, new_option.option_description);
1900
            }
1901
            const auto temp_callback = callback;
2✔
1902
            _system_impl->call_user_callback([temp_callback, new_option]() {
4✔
1903
                temp_callback(Camera::Result::Success, new_option);
1904
            });
1905
        }
2✔
1906
    } else {
1907
        // If this still happens, we request the param, but also complain.
1908
        LogWarn() << "Setting '" << setting_id << "' not found.";
×
1909
        if (callback) {
×
1910
            const auto temp_callback = callback;
×
1911
            _system_impl->call_user_callback(
×
1912
                [temp_callback]() { temp_callback(Camera::Result::Error, {}); });
1913
        }
×
1914
    }
1915
}
2✔
1916

1917
Camera::CurrentSettingsHandle
1918
CameraImpl::subscribe_current_settings(const Camera::CurrentSettingsCallback& callback)
1✔
1919
{
1920
    std::lock_guard lock(_mutex);
1✔
1921
    auto handle = _subscribe_current_settings_callbacks.subscribe(callback);
1✔
1922

1923
    notify_current_settings_for_all_with_lock();
1✔
1924
    return handle;
2✔
1925
}
1✔
1926

1927
void CameraImpl::unsubscribe_current_settings(Camera::CurrentSettingsHandle handle)
1✔
1928
{
1929
    std::lock_guard lock(_mutex);
1✔
1930
    _subscribe_current_settings_callbacks.unsubscribe(handle);
1✔
1931
}
1✔
1932

1933
Camera::PossibleSettingOptionsHandle CameraImpl::subscribe_possible_setting_options(
1✔
1934
    const Camera::PossibleSettingOptionsCallback& callback)
1935
{
1936
    std::lock_guard lock(_mutex);
1✔
1937
    auto handle = _subscribe_possible_setting_options_callbacks.subscribe(callback);
1✔
1938

1939
    notify_possible_setting_options_for_all_with_lock();
1✔
1940
    return handle;
2✔
1941
}
1✔
1942

1943
void CameraImpl::unsubscribe_possible_setting_options(Camera::PossibleSettingOptionsHandle handle)
×
1944
{
1945
    _subscribe_possible_setting_options_callbacks.unsubscribe(handle);
×
1946
}
×
1947

1948
void CameraImpl::notify_current_settings_for_all_with_lock()
1✔
1949
{
1950
    for (auto& potential_camera : potential_cameras_with_lock()) {
2✔
1951
        if (potential_camera->camera_definition != nullptr) {
1✔
1952
            notify_current_settings_with_lock(*potential_camera);
1✔
1953
        }
1954
    }
1✔
1955
}
1✔
1956

1957
void CameraImpl::notify_possible_setting_options_for_all_with_lock()
1✔
1958
{
1959
    for (auto& potential_camera : potential_cameras_with_lock()) {
2✔
1960
        if (potential_camera->camera_definition != nullptr) {
1✔
1961
            notify_possible_setting_options_with_lock(*potential_camera);
1✔
1962
        }
1963
    }
1✔
1964
}
1✔
1965

1966
void CameraImpl::notify_current_settings_with_lock(PotentialCamera& potential_camera)
4✔
1967
{
1968
    assert(potential_camera.camera_definition != nullptr);
4✔
1969

1970
    if (_subscribe_current_settings_callbacks.empty()) {
4✔
1971
        return;
2✔
1972
    }
1973

1974
    auto possible_setting_options = get_possible_setting_options_with_lock(potential_camera);
2✔
1975
    if (possible_setting_options.first != Camera::Result::Success) {
2✔
1976
        LogErr() << "Could not get possible settings in current options subscription.";
×
1977
        return;
×
1978
    }
1979

1980
    auto& camera = potential_camera;
2✔
1981

1982
    Camera::CurrentSettingsUpdate update{};
2✔
1983
    update.component_id = potential_camera.component_id;
2✔
1984

1985
    for (auto& possible_setting : possible_setting_options.second) {
23✔
1986
        // use the cache for this, presumably we updated it right before.
1987
        ParamValue value;
21✔
1988
        if (camera.camera_definition->get_setting(possible_setting.setting_id, value)) {
21✔
1989
            Camera::Setting setting{};
11✔
1990
            setting.setting_id = possible_setting.setting_id;
11✔
1991
            setting.is_range =
11✔
1992
                camera.camera_definition->is_setting_range(possible_setting.setting_id);
11✔
1993
            get_setting_str_with_lock(camera, setting.setting_id, setting.setting_description);
11✔
1994
            setting.option.option_id = value.get_string();
11✔
1995
            if (!camera.camera_definition->is_setting_range(possible_setting.setting_id)) {
11✔
1996
                get_option_str_with_lock(
5✔
1997
                    camera,
1998
                    setting.setting_id,
1999
                    setting.option.option_id,
2000
                    setting.option.option_description);
2001
            }
2002
            update.current_settings.push_back(setting);
11✔
2003
        }
11✔
2004
    }
21✔
2005

2006
    _subscribe_current_settings_callbacks.queue(
2✔
2007
        update, [this](const auto& func) { _system_impl->call_user_callback(func); });
2✔
2008
}
2✔
2009

2010
void CameraImpl::notify_possible_setting_options_with_lock(PotentialCamera& potential_camera)
4✔
2011
{
2012
    assert(potential_camera.camera_definition != nullptr);
4✔
2013

2014
    if (_subscribe_possible_setting_options_callbacks.empty()) {
4✔
2015
        return;
2✔
2016
    }
2017

2018
    Camera::PossibleSettingOptionsUpdate update{};
2✔
2019
    update.component_id = potential_camera.component_id;
2✔
2020

2021
    auto setting_options = get_possible_setting_options_with_lock(potential_camera);
2✔
2022
    if (setting_options.second.empty()) {
2✔
2023
        return;
×
2024
    }
2025

2026
    update.setting_options = setting_options.second;
2✔
2027

2028
    _subscribe_possible_setting_options_callbacks.queue(
2✔
2029
        update, [this](const auto& func) { _system_impl->call_user_callback(func); });
2✔
2030
}
2✔
2031

2032
std::pair<Camera::Result, std::vector<Camera::SettingOptions>>
2033
CameraImpl::get_possible_setting_options(int32_t component_id)
2✔
2034
{
2035
    std::pair<Camera::Result, std::vector<Camera::SettingOptions>> result;
2✔
2036

2037
    std::lock_guard lock(_mutex);
2✔
2038
    auto maybe_potential_camera =
2039
        maybe_potential_camera_for_component_id_with_lock(component_id, 0);
2✔
2040
    if (maybe_potential_camera == nullptr) {
2✔
2041
        result.first = Camera::Result::CameraIdInvalid;
×
2042
        return result;
×
2043
    }
2044

2045
    auto& camera = *maybe_potential_camera;
2✔
2046
    if (camera.is_fetching_camera_definition || camera.camera_definition == nullptr) {
2✔
2047
        result.first = Camera::Result::Unavailable;
×
2048
        return result;
×
2049
    }
2050

2051
    return get_possible_setting_options_with_lock(*maybe_potential_camera);
2✔
2052
}
2✔
2053

2054
std::pair<Camera::Result, std::vector<Camera::SettingOptions>>
2055
CameraImpl::get_possible_setting_options_with_lock(PotentialCamera& potential_camera)
8✔
2056
{
2057
    std::pair<Camera::Result, std::vector<Camera::SettingOptions>> result;
8✔
2058

2059
    std::unordered_map<std::string, ParamValue> possible_settings;
8✔
2060
    assert(potential_camera.camera_definition != nullptr);
8✔
2061

2062
    auto& camera = potential_camera;
8✔
2063

2064
    camera.camera_definition->get_possible_settings(possible_settings);
8✔
2065

2066
    for (auto& possible_setting : possible_settings) {
94✔
2067
        Camera::SettingOptions setting_options{};
86✔
2068
        setting_options.setting_id = possible_setting.first;
86✔
2069
        setting_options.is_range =
86✔
2070
            camera.camera_definition->is_setting_range(possible_setting.first);
86✔
2071
        get_setting_str_with_lock(
86✔
2072
            camera, setting_options.setting_id, setting_options.setting_description);
2073
        get_possible_options_with_lock(camera, possible_setting.first, setting_options.options);
86✔
2074
        result.second.push_back(setting_options);
86✔
2075
    }
86✔
2076

2077
    result.first = Camera::Result::Success;
8✔
2078
    return result;
8✔
2079
}
8✔
2080

2081
void CameraImpl::refresh_params_with_lock(PotentialCamera& potential_camera, bool initial_load)
5✔
2082
{
2083
    assert(potential_camera.camera_definition != nullptr);
5✔
2084

2085
    std::vector<std::pair<std::string, ParamValue>> params;
5✔
2086
    potential_camera.camera_definition->get_unknown_params(params);
5✔
2087

2088
    if (params.empty()) {
5✔
2089
        // We're assuming that we changed one option and this did not cause
2090
        // any other possible settings to change. However, we still would
2091
        // like to notify the current settings with this one change.
2092
        notify_current_settings_with_lock(potential_camera);
3✔
2093
        notify_possible_setting_options_with_lock(potential_camera);
3✔
2094
        return;
3✔
2095
    }
2096

2097
    auto component_id = potential_camera.component_id;
2✔
2098

2099
    unsigned count = 0;
2✔
2100
    for (const auto& param : params) {
28✔
2101
        const std::string& param_name = param.first;
26✔
2102
        const ParamValue& param_value_type = param.second;
26✔
2103
        const bool is_last = (count == params.size() - 1);
26✔
2104
        if (_debugging) {
26✔
2105
            LogDebug() << "Trying to get param: " << param_name;
×
2106
        }
2107
        _system_impl->param_sender(potential_camera.component_id, true)
52✔
2108
            ->get_param_async(
26✔
2109
                param_name,
2110
                param_value_type,
2111
                [component_id, param_name, is_last, this](
26✔
2112
                    MavlinkParameterClient::Result result, const ParamValue& value) {
78✔
2113
                    if (result != MavlinkParameterClient::Result::Success) {
26✔
2114
                        return;
×
2115
                    }
2116

2117
                    std::lock_guard lock_later(_mutex);
26✔
2118
                    auto maybe_potential_camera_later =
2119
                        maybe_potential_camera_for_component_id_with_lock(component_id, 0);
26✔
2120
                    // We already checked these fields earlier, so we don't check again.
2121
                    assert(maybe_potential_camera_later != nullptr);
26✔
2122
                    assert(maybe_potential_camera_later->camera_definition != nullptr);
26✔
2123

2124
                    auto& camera_later = *maybe_potential_camera_later;
26✔
2125

2126
                    if (camera_later.camera_definition->set_setting(param_name, value)) {
26✔
2127
                        if (_debugging) {
26✔
2128
                            LogDebug() << "Got setting for " << param_name << ": " << value;
×
2129
                        }
2130
                        return;
26✔
2131
                    }
2132

2133
                    if (is_last) {
×
2134
                        notify_current_settings_with_lock(camera_later);
×
2135
                        notify_possible_setting_options_with_lock(camera_later);
×
2136
                    }
2137
                },
26✔
2138
                this);
2139

2140
        if (initial_load) {
26✔
2141
            subscribe_to_param_changes_with_lock(potential_camera, param_name, param_value_type);
26✔
2142
        }
2143
        ++count;
26✔
2144
    }
2145
}
5✔
2146

2147
void CameraImpl::subscribe_to_param_changes_with_lock(
26✔
2148
    PotentialCamera& camera, std::string param_name, const ParamValue& param_value_type)
2149
{
2150
    auto component_id = camera.component_id;
26✔
2151
    auto changed = [this, component_id, param_name](auto new_param) {
65✔
2152
        if (_debugging) {
13✔
2153
            LogDebug() << "Got changing param: " << param_name << " -> " << new_param;
×
2154
        }
2155

2156
        std::lock_guard lock_later(_mutex);
13✔
2157

2158
        auto maybe_potential_camera_later =
2159
            maybe_potential_camera_for_component_id_with_lock(component_id, 0);
13✔
2160
        // We already checked these fields earlier, so we don't check again.
2161
        assert(maybe_potential_camera_later != nullptr);
13✔
2162
        assert(maybe_potential_camera_later->camera_definition != nullptr);
13✔
2163
        auto& camera_later = *maybe_potential_camera_later;
13✔
2164

2165
        ParamValue param_value;
13✔
2166
        param_value.set<decltype(new_param)>(new_param);
13✔
2167
        camera_later.camera_definition->set_setting(param_name, param_value);
13✔
2168
    };
39✔
2169

2170
    if (param_value_type.is<uint8_t>()) {
26✔
2171
        _system_impl->param_sender(camera.component_id, true)
×
2172
            ->subscribe_param_changed<uint8_t>(param_name, changed, this);
×
2173
    } else if (param_value_type.is<uint8_t>()) {
26✔
2174
        _system_impl->param_sender(camera.component_id, true)
×
2175
            ->subscribe_param_changed<uint16_t>(param_name, changed, this);
×
2176
    } else if (param_value_type.is<uint32_t>()) {
26✔
2177
        _system_impl->param_sender(camera.component_id, true)
×
2178
            ->subscribe_param_changed<uint32_t>(param_name, changed, this);
×
2179
    } else if (param_value_type.is<uint64_t>()) {
26✔
2180
        _system_impl->param_sender(camera.component_id, true)
×
2181
            ->subscribe_param_changed<uint64_t>(param_name, changed, this);
×
2182
    } else if (param_value_type.is<int8_t>()) {
26✔
2183
        _system_impl->param_sender(camera.component_id, true)
×
2184
            ->subscribe_param_changed<int8_t>(param_name, changed, this);
×
2185
    } else if (param_value_type.is<int16_t>()) {
26✔
2186
        _system_impl->param_sender(camera.component_id, true)
×
2187
            ->subscribe_param_changed<int16_t>(param_name, changed, this);
×
2188
    } else if (param_value_type.is<int32_t>()) {
26✔
2189
        _system_impl->param_sender(camera.component_id, true)
26✔
2190
            ->subscribe_param_changed<int32_t>(param_name, changed, this);
26✔
2191
    } else if (param_value_type.is<int64_t>()) {
×
2192
        _system_impl->param_sender(camera.component_id, true)
×
2193
            ->subscribe_param_changed<int64_t>(param_name, changed, this);
×
2194
    } else if (param_value_type.is<float>()) {
×
2195
        _system_impl->param_sender(camera.component_id, true)
×
2196
            ->subscribe_param_changed<float>(param_name, changed, this);
×
2197
    } else if (param_value_type.is<double>()) {
×
2198
        _system_impl->param_sender(camera.component_id, true)
×
2199
            ->subscribe_param_changed<double>(param_name, changed, this);
×
2200
    } else if (param_value_type.is<std::string>()) {
×
2201
        _system_impl->param_sender(camera.component_id, true)
×
2202
            ->subscribe_param_changed<std::string>(param_name, changed, this);
×
2203
    } else {
2204
        LogErr() << "Unknown type for param " << param_name;
×
2205
    }
2206
}
26✔
2207

2208
bool CameraImpl::get_setting_str_with_lock(
118✔
2209
    PotentialCamera& potential_camera, const std::string& setting_id, std::string& description)
2210
{
2211
    if (potential_camera.camera_definition == nullptr) {
118✔
2212
        return false;
×
2213
    }
2214

2215
    return potential_camera.camera_definition->get_setting_str(setting_id, description);
118✔
2216
}
2217

2218
bool CameraImpl::get_option_str_with_lock(
104✔
2219
    PotentialCamera& potential_camera,
2220
    const std::string& setting_id,
2221
    const std::string& option_id,
2222
    std::string& description)
2223
{
2224
    if (potential_camera.camera_definition == nullptr) {
104✔
2225
        return false;
×
2226
    }
2227

2228
    return potential_camera.camera_definition->get_option_str(setting_id, option_id, description);
104✔
2229
}
2230

2231
void CameraImpl::request_camera_information(uint8_t component_id)
9✔
2232
{
2233
    _system_impl->mavlink_request_message().request(
18✔
2234
        MAVLINK_MSG_ID_CAMERA_INFORMATION, fixup_component_target(component_id), nullptr);
9✔
2235
}
9✔
2236

2237
Camera::Result CameraImpl::format_storage(int32_t component_id, int32_t storage_id)
1✔
2238
{
2239
    auto prom = std::make_shared<std::promise<Camera::Result>>();
1✔
2240
    auto ret = prom->get_future();
1✔
2241

2242
    format_storage_async(
1✔
2243
        component_id, storage_id, [prom](Camera::Result result) { prom->set_value(result); });
1✔
2244

2245
    return ret.get();
1✔
2246
}
1✔
2247

2248
void CameraImpl::format_storage_async(
1✔
2249
    int32_t component_id, int32_t storage_id, const Camera::ResultCallback& callback)
2250
{
2251
    MavlinkCommandSender::CommandLong cmd_format{};
1✔
2252

2253
    cmd_format.command = MAV_CMD_STORAGE_FORMAT;
1✔
2254
    cmd_format.params.maybe_param1 = static_cast<float>(storage_id); // storage ID
1✔
2255
    cmd_format.params.maybe_param2 = 1.0f; // format
1✔
2256
    cmd_format.params.maybe_param3 = 1.0f; // clear
1✔
2257
    cmd_format.target_component_id = component_id;
1✔
2258

2259
    _system_impl->send_command_async(
1✔
2260
        cmd_format, [this, callback](MavlinkCommandSender::Result result, float progress) {
3✔
2261
            UNUSED(progress);
1✔
2262

2263
            receive_command_result(result, [this, callback](Camera::Result camera_result) {
1✔
2264
                callback(camera_result);
1✔
2265
            });
1✔
2266
        });
1✔
2267
}
1✔
2268

2269
Camera::Result CameraImpl::reset_settings(int32_t component_id)
1✔
2270
{
2271
    auto prom = std::make_shared<std::promise<Camera::Result>>();
1✔
2272
    auto ret = prom->get_future();
1✔
2273

2274
    reset_settings_async(component_id, [prom](Camera::Result result) { prom->set_value(result); });
2✔
2275

2276
    return ret.get();
1✔
2277
}
1✔
2278

2279
void CameraImpl::reset_settings_async(int32_t component_id, const Camera::ResultCallback& callback)
1✔
2280
{
2281
    MavlinkCommandSender::CommandLong cmd_format{};
1✔
2282

2283
    cmd_format.command = MAV_CMD_RESET_CAMERA_SETTINGS;
1✔
2284
    cmd_format.params.maybe_param1 = 1.0f; // reset
1✔
2285
    cmd_format.target_component_id = component_id;
1✔
2286

2287
    _system_impl->send_command_async(
1✔
2288
        cmd_format, [this, callback](MavlinkCommandSender::Result result, float progress) {
3✔
2289
            UNUSED(progress);
1✔
2290

2291
            receive_command_result(result, [this, callback](Camera::Result camera_result) {
1✔
2292
                callback(camera_result);
1✔
2293
            });
1✔
2294
        });
1✔
2295
}
1✔
2296

2297
void CameraImpl::reset_following_format_storage_with_lock(PotentialCamera& camera)
×
2298
{
2299
    camera.capture_status.photo_list.clear();
×
2300
    camera.capture_status.image_count = 0;
×
2301
    camera.capture_status.image_count_at_connection = 0;
×
2302
    camera.capture_info.last_advertised_image_index = -1;
×
2303
    camera.capture_info.missing_image_retries.clear();
×
2304
}
×
2305

2306
std::pair<Camera::Result, std::vector<Camera::CaptureInfo>>
2307
CameraImpl::list_photos(int32_t component_id, Camera::PhotosRange photos_range)
×
2308
{
2309
    std::promise<std::pair<Camera::Result, std::vector<Camera::CaptureInfo>>> prom;
×
2310
    auto ret = prom.get_future();
×
2311

2312
    list_photos_async(
×
2313
        component_id,
2314
        photos_range,
2315
        [&prom](Camera::Result result, const std::vector<Camera::CaptureInfo>& photo_list) {
×
2316
            prom.set_value(std::make_pair(result, photo_list));
×
2317
        });
×
2318

2319
    return ret.get();
×
2320
}
×
2321

2322
void CameraImpl::list_photos_async(
×
2323
    int32_t component_id,
2324
    Camera::PhotosRange photos_range,
2325
    const Camera::ListPhotosCallback& callback)
2326
{
2327
    if (!callback) {
×
2328
        LogWarn() << "Trying to get a photo list with a null callback, ignoring...";
×
2329
        return;
×
2330
    }
2331

2332
    std::lock_guard lock(_mutex);
×
2333

2334
    auto maybe_potential_camera =
2335
        maybe_potential_camera_for_component_id_with_lock(component_id, 0);
×
2336
    if (maybe_potential_camera == nullptr) {
×
2337
        LogWarn() << "Invalid camera ID: " << component_id;
×
2338
        return;
×
2339
    }
2340
    auto& camera = *maybe_potential_camera;
×
2341

2342
    if (camera.capture_status.image_count == -1) {
×
2343
        LogErr() << "Cannot list photos: camera status has not been received yet!";
×
2344
        _system_impl->call_user_callback(
×
2345
            [callback]() { callback(Camera::Result::Error, std::vector<Camera::CaptureInfo>{}); });
2346
        return;
×
2347
    }
2348

2349
    // By default we just try to keep track of photos captured while we were connected.
2350
    // If the API user asks for all, we will start requesting all previous ones.
2351

2352
    if (camera.capture_status.photos_range == Camera::PhotosRange::SinceConnection &&
×
2353
        photos_range == Camera::PhotosRange::All) {
×
2354
        camera.capture_status.photos_range = photos_range;
×
2355

2356
        auto oldest_photo_it = std::min_element(
×
2357
            camera.capture_status.photo_list.begin(),
2358
            camera.capture_status.photo_list.end(),
2359
            [](auto& a, auto& b) { return a.first < b.first; });
×
2360

2361
        if (oldest_photo_it != camera.capture_status.photo_list.end()) {
×
2362
            for (int i = 0; i < oldest_photo_it->first; ++i) {
×
2363
                if (camera.capture_info.missing_image_retries.find(i) ==
×
2364
                    camera.capture_info.missing_image_retries.end()) {
×
2365
                    camera.capture_info.missing_image_retries[i] = 0;
×
2366
                }
2367
            }
2368
        }
2369
    }
2370

2371
    const int start_index = [&, this]() {
×
2372
        switch (photos_range) {
×
2373
            case Camera::PhotosRange::SinceConnection:
×
2374
                return camera.capture_status.image_count_at_connection;
×
2375
            case Camera::PhotosRange::All:
×
2376
            // FALLTHROUGH
2377
            default:
2378
                return 0;
×
2379
        }
2380
    }();
×
2381

2382
    std::vector<Camera::CaptureInfo> photo_list;
×
2383

2384
    for (const auto& capture_info : camera.capture_status.photo_list) {
×
2385
        if (capture_info.first >= start_index) {
×
2386
            photo_list.push_back(capture_info.second);
×
2387
        }
2388
    }
2389

2390
    _system_impl->call_user_callback(
×
2391
        [callback, photo_list]() { callback(Camera::Result::Success, photo_list); });
2392
}
×
2393

2394
std::pair<Camera::Result, Camera::Mode> CameraImpl::get_mode(int32_t component_id)
1✔
2395
{
2396
    std::pair<Camera::Result, Camera::Mode> result{};
1✔
2397

2398
    std::lock_guard lock(_mutex);
1✔
2399

2400
    auto maybe_potential_camera =
2401
        maybe_potential_camera_for_component_id_with_lock(component_id, 0);
1✔
2402
    if (maybe_potential_camera == nullptr) {
1✔
2403
        result.first = Camera::Result::CameraIdInvalid;
×
2404
    } else {
2405
        result.first = Camera::Result::Success;
1✔
2406
        result.second = maybe_potential_camera->mode;
1✔
2407
    }
2408

2409
    return result;
2✔
2410
}
1✔
2411

2412
std::pair<Camera::Result, Camera::Storage> CameraImpl::get_storage(int32_t component_id)
60✔
2413
{
2414
    std::pair<Camera::Result, Camera::Storage> result{};
60✔
2415

2416
    std::lock_guard lock(_mutex);
60✔
2417

2418
    auto maybe_potential_camera =
2419
        maybe_potential_camera_for_component_id_with_lock(component_id, 0);
60✔
2420
    if (maybe_potential_camera == nullptr) {
60✔
2421
        result.first = Camera::Result::CameraIdInvalid;
×
2422
    } else {
2423
        if (maybe_potential_camera->received_storage) {
60✔
2424
            result.first = Camera::Result::Success;
20✔
2425
            result.second = maybe_potential_camera->storage;
20✔
2426
        } else {
2427
            result.first = Camera::Result::Unavailable;
40✔
2428
        }
2429
    }
2430

2431
    return result;
60✔
2432
}
60✔
2433

2434
std::pair<Camera::Result, Camera::VideoStreamInfo>
2435
CameraImpl::get_video_stream_info(int32_t component_id)
×
2436
{
2437
    std::pair<Camera::Result, Camera::VideoStreamInfo> result{};
×
2438

2439
    std::lock_guard lock(_mutex);
×
2440

2441
    auto maybe_potential_camera =
2442
        maybe_potential_camera_for_component_id_with_lock(component_id, 0);
×
2443
    if (maybe_potential_camera == nullptr) {
×
2444
        result.first = Camera::Result::CameraIdInvalid;
×
2445
    } else {
2446
        result.first = Camera::Result::Success;
×
2447
        result.second = maybe_potential_camera->video_stream_info;
×
2448
    }
2449

2450
    return result;
×
2451
}
×
2452

2453
std::pair<Camera::Result, std::vector<Camera::Setting>>
2454
CameraImpl::get_current_settings(int32_t component_id)
2✔
2455
{
2456
    std::pair<Camera::Result, std::vector<Camera::Setting>> result;
2✔
2457

2458
    std::lock_guard lock(_mutex);
2✔
2459
    auto maybe_potential_camera =
2460
        maybe_potential_camera_for_component_id_with_lock(component_id, 0);
2✔
2461
    if (maybe_potential_camera == nullptr) {
2✔
2462
        result.first = Camera::Result::CameraIdInvalid;
×
2463
        return result;
×
2464
    }
2465

2466
    auto& camera = *maybe_potential_camera;
2✔
2467
    if (camera.is_fetching_camera_definition || camera.camera_definition == nullptr) {
2✔
2468
        result.first = Camera::Result::Unavailable;
×
2469
        return result;
×
2470
    }
2471

2472
    auto possible_setting_options = get_possible_setting_options_with_lock(camera);
2✔
2473
    if (possible_setting_options.first != Camera::Result::Success) {
2✔
2474
        result.first = possible_setting_options.first;
×
2475
        return result;
×
2476
    }
2477

2478
    for (auto& possible_setting : possible_setting_options.second) {
23✔
2479
        // use the cache for this, presumably we updated it right before.
2480
        ParamValue value;
21✔
2481
        if (camera.camera_definition->get_setting(possible_setting.setting_id, value)) {
21✔
2482
            Camera::Setting setting{};
21✔
2483
            setting.setting_id = possible_setting.setting_id;
21✔
2484
            setting.is_range =
21✔
2485
                camera.camera_definition->is_setting_range(possible_setting.setting_id);
21✔
2486
            get_setting_str_with_lock(camera, setting.setting_id, setting.setting_description);
21✔
2487
            setting.option.option_id = value.get_string();
21✔
2488
            if (!camera.camera_definition->is_setting_range(possible_setting.setting_id)) {
21✔
2489
                get_option_str_with_lock(
10✔
2490
                    camera,
2491
                    setting.setting_id,
2492
                    setting.option.option_id,
2493
                    setting.option.option_description);
2494
            }
2495
            result.second.push_back(setting);
21✔
2496
        }
21✔
2497
    }
21✔
2498

2499
    result.first = Camera::Result::Success;
2✔
2500
    return result;
2✔
2501
}
2✔
2502

2503
uint16_t CameraImpl::get_and_increment_capture_sequence(int32_t component_id)
2✔
2504
{
2505
    if (component_id == 0) {
2✔
2506
        // All cameras
2507
        return 0;
×
2508
    }
2509

2510
    std::lock_guard lock(_mutex);
2✔
2511

2512
    for (auto& potential_camera : _potential_cameras) {
2✔
2513
        if (potential_camera.component_id == component_id) {
2✔
2514
            return potential_camera.capture_sequence++;
2✔
2515
        }
2516
    }
2517

2518
    return 0;
×
2519
}
2✔
2520

2521
std::vector<CameraImpl::PotentialCamera*> CameraImpl::potential_cameras_with_lock()
108✔
2522
{
2523
    std::vector<CameraImpl::PotentialCamera*> result;
108✔
2524

2525
    for (auto& potential_camera : _potential_cameras) {
215✔
2526
        if (!potential_camera.maybe_information) {
107✔
UNCOV
2527
            continue;
×
2528
        }
2529
        result.push_back(&potential_camera);
107✔
2530
    }
2531

2532
    return result;
108✔
2533
}
2534

2535
CameraImpl::PotentialCamera* CameraImpl::maybe_potential_camera_for_component_id_with_lock(
134✔
2536
    uint8_t component_id, uint8_t camera_device_id)
2537
{
2538
    // Component Ids 1-6 means the camera is connected to the autopilot and the
2539
    // ID is set by the camera_device_id instead.
2540
    if (component_id == 1 && camera_device_id != 0) {
134✔
2541
        component_id = camera_device_id;
×
2542
    }
2543

2544
    const auto it = std::find_if(
134✔
2545
        _potential_cameras.begin(), _potential_cameras.end(), [&](auto& potential_camera) {
134✔
2546
            return potential_camera.component_id = component_id;
134✔
2547
        });
2548

2549
    if (it == _potential_cameras.end()) {
134✔
2550
        return nullptr;
×
2551
    }
2552

2553
    // How to get pointer from iterator?
2554
    return &(*it);
134✔
2555
}
2556

2557
uint8_t CameraImpl::fixup_component_target(uint8_t component_id)
35✔
2558
{
2559
    // Component Ids 1-6 means the camera is connected to the autopilot.
2560
    if (component_id >= 1 && component_id <= 6) {
35✔
2561
        return 1;
×
2562
    }
2563

2564
    return component_id;
35✔
2565
}
2566

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

© 2025 Coveralls, Inc