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

mavlink / MAVSDK / 12060541516

28 Nov 2024 12:52AM UTC coverage: 43.553% (+4.9%) from 38.691%
12060541516

Pull #2386

github

web-flow
Merge cc93a465c into 5a124d9bc
Pull Request #2386: camera: support multiple cameras within one instance

1353 of 2024 new or added lines in 47 files covered. (66.85%)

118 existing lines in 8 files now uncovered.

13859 of 31821 relevant lines covered (43.55%)

289.55 hits per line

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

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

11
#include <algorithm>
12
#include <cassert>
13
#include <cmath>
14
#include <iterator>
15
#include <filesystem>
16
#include <fstream>
17
#include <functional>
18
#include <string>
19

20
namespace mavsdk {
21

22
template class CallbackList<Camera::Mode>;
23
template class CallbackList<std::vector<Camera::Setting>>;
24
template class CallbackList<std::vector<Camera::SettingOptions>>;
25
template class CallbackList<Camera::CaptureInfo>;
26
template class CallbackList<Camera::VideoStreamInfo>;
27
template class CallbackList<Camera::Storage>;
28

29
CameraImpl::CameraImpl(System& system) : PluginImplBase(system)
×
30
{
31
    _system_impl->register_plugin(this);
×
32
}
×
33

34
CameraImpl::CameraImpl(std::shared_ptr<System> system) : PluginImplBase(std::move(system))
9✔
35
{
36
    _system_impl->register_plugin(this);
9✔
37
}
9✔
38

39
CameraImpl::~CameraImpl()
18✔
40
{
41
    _system_impl->unregister_plugin(this);
9✔
42
}
18✔
43

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

53
    const auto cache_dir_option = get_cache_directory();
9✔
54
    if (cache_dir_option) {
9✔
55
        _file_cache.emplace(cache_dir_option.value() / "camera", 50, true);
9✔
56
    } else {
NEW
57
        LogErr() << "Failed to get cache directory";
×
58
    }
59

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

69
    _system_impl->register_mavlink_message_handler(
9✔
70
        MAVLINK_MSG_ID_HEARTBEAT,
71
        [this](const mavlink_message_t& message) { process_heartbeat(message); },
37✔
72
        this);
73

74
    _system_impl->register_mavlink_message_handler(
9✔
75
        MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS,
76
        [this](const mavlink_message_t& message) { process_camera_capture_status(message); },
5✔
77
        this);
78

79
    _system_impl->register_mavlink_message_handler(
9✔
80
        MAVLINK_MSG_ID_STORAGE_INFORMATION,
81
        [this](const mavlink_message_t& message) { process_storage_information(message); },
1✔
82
        this);
83

84
    _system_impl->register_mavlink_message_handler(
9✔
85
        MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED,
86
        [this](const mavlink_message_t& message) { process_camera_image_captured(message); },
4✔
87
        this);
88

89
    _system_impl->register_mavlink_message_handler(
9✔
90
        MAVLINK_MSG_ID_CAMERA_SETTINGS,
91
        [this](const mavlink_message_t& message) { process_camera_settings(message); },
5✔
92
        this);
93

94
    _system_impl->register_mavlink_message_handler(
9✔
95
        MAVLINK_MSG_ID_CAMERA_INFORMATION,
96
        [this](const mavlink_message_t& message) { process_camera_information(message); },
9✔
97
        this);
98

99
    _system_impl->register_mavlink_message_handler(
9✔
100
        MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION,
101
        [this](const mavlink_message_t& message) { process_video_information(message); },
1✔
102
        this);
103

104
    _system_impl->register_mavlink_message_handler(
9✔
105
        MAVLINK_MSG_ID_VIDEO_STREAM_STATUS,
106
        [this](const mavlink_message_t& message) { process_video_stream_status(message); },
4✔
107
        this);
108

109
    //_request_missing_capture_info_cookie =
110
    //    _system_impl->add_call_every([this]() { request_missing_capture_info(); }, 0.5);
111

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

119
void CameraImpl::deinit()
9✔
120
{
121
    _system_impl->unregister_all_mavlink_message_handlers(this);
9✔
122

123
    _system_impl->cancel_all_param(this);
9✔
124

125
    std::lock_guard lock(_mutex);
9✔
126
    //_system_impl->remove_call_every(_request_missing_capture_info_cookie);
127
    _system_impl->remove_call_every(_request_slower_call_every_cookie);
9✔
128
    _system_impl->remove_call_every(_request_faster_call_every_cookie);
9✔
129

130
    _storage_subscription_callbacks.clear();
9✔
131
    _mode_subscription_callbacks.clear();
9✔
132
    _capture_info_callbacks.clear();
9✔
133
    _video_stream_info_subscription_callbacks.clear();
9✔
134
    _camera_list_subscription_callbacks.clear();
9✔
135
    _subscribe_current_settings_callbacks.clear();
9✔
136
    _subscribe_possible_setting_options_callbacks.clear();
9✔
137
}
9✔
138

139
void CameraImpl::enable() {}
9✔
140
void CameraImpl::disable() {}
9✔
141

142
MavlinkCommandSender::CommandLong
143
CameraImpl::make_command_take_photo(int32_t component_id, float interval_s, float no_of_photos)
2✔
144
{
145
    MavlinkCommandSender::CommandLong cmd{};
2✔
146

147
    cmd.command = MAV_CMD_IMAGE_START_CAPTURE;
2✔
148
    cmd.params.maybe_param1 = 0.0f; // Reserved, set to 0
2✔
149
    cmd.params.maybe_param2 = interval_s;
2✔
150
    cmd.params.maybe_param3 = no_of_photos;
2✔
151
    cmd.params.maybe_param4 = get_and_increment_capture_sequence(component_id);
2✔
152
    cmd.target_component_id = component_id;
2✔
153

154
    return cmd;
2✔
155
}
156

NEW
157
MavlinkCommandSender::CommandLong CameraImpl::make_command_zoom_out(int32_t component_id)
×
158
{
159
    MavlinkCommandSender::CommandLong cmd{};
×
160
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
NEW
161
    cmd.params.maybe_param1 = static_cast<float>(ZOOM_TYPE_CONTINUOUS);
×
162
    cmd.params.maybe_param2 = -1.f;
×
NEW
163
    cmd.target_component_id = component_id;
×
164

165
    return cmd;
×
166
}
167

NEW
168
MavlinkCommandSender::CommandLong CameraImpl::make_command_zoom_in(int32_t component_id)
×
169
{
170
    MavlinkCommandSender::CommandLong cmd{};
×
171
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
NEW
172
    cmd.params.maybe_param1 = static_cast<float>(ZOOM_TYPE_CONTINUOUS);
×
173
    cmd.params.maybe_param2 = 1.f;
×
NEW
174
    cmd.target_component_id = component_id;
×
175

176
    return cmd;
×
177
}
178

NEW
179
MavlinkCommandSender::CommandLong CameraImpl::make_command_zoom_stop(int32_t component_id)
×
180
{
181
    MavlinkCommandSender::CommandLong cmd{};
×
182
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
NEW
183
    cmd.params.maybe_param1 = static_cast<float>(ZOOM_TYPE_CONTINUOUS);
×
184
    cmd.params.maybe_param2 = 0.f;
×
NEW
185
    cmd.target_component_id = component_id;
×
186

187
    return cmd;
×
188
}
189

190
MavlinkCommandSender::CommandLong
NEW
191
CameraImpl::make_command_zoom_range(int32_t component_id, float range)
×
192
{
193
    // Clip to safe range.
194
    range = std::max(0.f, std::min(range, 100.f));
×
195

196
    MavlinkCommandSender::CommandLong cmd{};
×
197
    cmd.command = MAV_CMD_SET_CAMERA_ZOOM;
×
NEW
198
    cmd.params.maybe_param1 = static_cast<float>(ZOOM_TYPE_RANGE);
×
199
    cmd.params.maybe_param2 = range;
×
NEW
200
    cmd.target_component_id = component_id;
×
201

202
    return cmd;
×
203
}
204

NEW
205
MavlinkCommandSender::CommandLong CameraImpl::make_command_track_point(
×
206
    int32_t component_id, float point_x, float point_y, float radius)
207
{
208
    MavlinkCommandSender::CommandLong cmd{};
×
209
    cmd.command = MAV_CMD_CAMERA_TRACK_POINT;
×
NEW
210
    cmd.params.maybe_param1 = point_x;
×
NEW
211
    cmd.params.maybe_param2 = point_y;
×
NEW
212
    cmd.params.maybe_param3 = radius;
×
NEW
213
    cmd.target_component_id = component_id;
×
214

215
    return cmd;
×
216
}
217

218
MavlinkCommandSender::CommandLong CameraImpl::make_command_track_rectangle(
×
219
    int32_t component_id,
220
    float top_left_x,
221
    float top_left_y,
222
    float bottom_right_x,
223
    float bottom_right_y)
224
{
225
    MavlinkCommandSender::CommandLong cmd{};
×
226
    cmd.command = MAV_CMD_CAMERA_TRACK_RECTANGLE;
×
227
    cmd.params.maybe_param1 = top_left_x;
×
228
    cmd.params.maybe_param2 = top_left_y;
×
229
    cmd.params.maybe_param3 = bottom_right_x;
×
230
    cmd.params.maybe_param4 = bottom_right_y;
×
NEW
231
    cmd.target_component_id = component_id;
×
232

233
    return cmd;
×
234
}
235

NEW
236
MavlinkCommandSender::CommandLong CameraImpl::make_command_track_stop(int32_t component_id)
×
237
{
238
    MavlinkCommandSender::CommandLong cmd{};
×
239
    cmd.command = MAV_CMD_CAMERA_STOP_TRACKING;
×
NEW
240
    cmd.target_component_id = component_id;
×
241

242
    return cmd;
×
243
}
244

NEW
245
MavlinkCommandSender::CommandLong CameraImpl::make_command_focus_in(int32_t component_id)
×
246
{
247
    MavlinkCommandSender::CommandLong cmd{};
×
248
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
NEW
249
    cmd.params.maybe_param1 = static_cast<float>(FOCUS_TYPE_CONTINUOUS);
×
250
    cmd.params.maybe_param2 = -1.f;
×
NEW
251
    cmd.target_component_id = component_id;
×
252

253
    return cmd;
×
254
}
255

NEW
256
MavlinkCommandSender::CommandLong CameraImpl::make_command_focus_out(int32_t component_id)
×
257
{
258
    MavlinkCommandSender::CommandLong cmd{};
×
259
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
NEW
260
    cmd.params.maybe_param1 = static_cast<float>(FOCUS_TYPE_CONTINUOUS);
×
261
    cmd.params.maybe_param2 = 1.f;
×
NEW
262
    cmd.target_component_id = component_id;
×
263

264
    return cmd;
×
265
}
266

NEW
267
MavlinkCommandSender::CommandLong CameraImpl::make_command_focus_stop(int32_t component_id)
×
268
{
269
    MavlinkCommandSender::CommandLong cmd{};
×
270
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
NEW
271
    cmd.params.maybe_param1 = static_cast<float>(FOCUS_TYPE_CONTINUOUS);
×
272
    cmd.params.maybe_param2 = 0.f;
×
NEW
273
    cmd.target_component_id = component_id;
×
274

275
    return cmd;
×
276
}
277

278
MavlinkCommandSender::CommandLong
NEW
279
CameraImpl::make_command_focus_range(int32_t component_id, float range)
×
280
{
281
    // Clip to safe range.
282
    range = std::max(0.f, std::min(range, 100.f));
×
283

284
    MavlinkCommandSender::CommandLong cmd{};
×
285
    cmd.command = MAV_CMD_SET_CAMERA_FOCUS;
×
NEW
286
    cmd.params.maybe_param1 = static_cast<float>(FOCUS_TYPE_RANGE);
×
287
    cmd.params.maybe_param2 = range;
×
NEW
288
    cmd.target_component_id = component_id;
×
289

290
    return cmd;
×
291
}
292

293
MavlinkCommandSender::CommandLong CameraImpl::make_command_stop_photo(int32_t component_id)
1✔
294
{
295
    MavlinkCommandSender::CommandLong cmd_stop_photo{};
1✔
296

297
    cmd_stop_photo.command = MAV_CMD_IMAGE_STOP_CAPTURE;
1✔
298
    cmd_stop_photo.target_component_id = component_id;
1✔
299

300
    return cmd_stop_photo;
1✔
301
}
302

303
MavlinkCommandSender::CommandLong
NEW
304
CameraImpl::make_command_start_video(int32_t component_id, float capture_status_rate_hz)
×
305
{
306
    MavlinkCommandSender::CommandLong cmd_start_video{};
×
307

308
    cmd_start_video.command = MAV_CMD_VIDEO_START_CAPTURE;
×
309
    cmd_start_video.params.maybe_param1 = 0.f; // Reserved, set to 0
×
310
    cmd_start_video.params.maybe_param2 = capture_status_rate_hz;
×
NEW
311
    cmd_start_video.target_component_id = component_id;
×
312

313
    return cmd_start_video;
×
314
}
315

NEW
316
MavlinkCommandSender::CommandLong CameraImpl::make_command_stop_video(int32_t component_id)
×
317
{
318
    MavlinkCommandSender::CommandLong cmd_stop_video{};
×
319

320
    cmd_stop_video.command = MAV_CMD_VIDEO_STOP_CAPTURE;
×
321
    cmd_stop_video.params.maybe_param1 = 0.f; // Reserved, set to 0
×
NEW
322
    cmd_stop_video.target_component_id = component_id;
×
323

324
    return cmd_stop_video;
×
325
}
326

327
MavlinkCommandSender::CommandLong
328
CameraImpl::make_command_set_camera_mode(int32_t component_id, float mavlink_mode)
2✔
329
{
330
    MavlinkCommandSender::CommandLong cmd_set_camera_mode{};
2✔
331

332
    cmd_set_camera_mode.command = MAV_CMD_SET_CAMERA_MODE;
2✔
333
    cmd_set_camera_mode.params.maybe_param1 = 0.0f; // Reserved, set to 0
2✔
334
    cmd_set_camera_mode.params.maybe_param2 = mavlink_mode;
2✔
335
    cmd_set_camera_mode.target_component_id = component_id;
2✔
336

337
    return cmd_set_camera_mode;
2✔
338
}
339

340
MavlinkCommandSender::CommandLong
NEW
341
CameraImpl::make_command_start_video_streaming(int32_t component_id, int32_t stream_id)
×
342
{
343
    MavlinkCommandSender::CommandLong cmd_start_video_streaming{};
×
344

345
    cmd_start_video_streaming.command = MAV_CMD_VIDEO_START_STREAMING;
×
346
    cmd_start_video_streaming.params.maybe_param1 = static_cast<float>(stream_id);
×
NEW
347
    cmd_start_video_streaming.target_component_id = component_id;
×
348

349
    return cmd_start_video_streaming;
×
350
}
351

352
MavlinkCommandSender::CommandLong
NEW
353
CameraImpl::make_command_stop_video_streaming(int32_t component_id, int32_t stream_id)
×
354
{
355
    MavlinkCommandSender::CommandLong cmd_stop_video_streaming{};
×
356

357
    cmd_stop_video_streaming.command = MAV_CMD_VIDEO_STOP_STREAMING;
×
358
    cmd_stop_video_streaming.params.maybe_param1 = static_cast<float>(stream_id);
×
NEW
359
    cmd_stop_video_streaming.target_component_id = component_id;
×
360

361
    return cmd_stop_video_streaming;
×
362
}
363

364
Camera::Result CameraImpl::take_photo(int32_t component_id)
1✔
365
{
366
    // Take 1 photo only with no interval
367
    auto cmd_take_photo = make_command_take_photo(component_id, 0.f, 1.0f);
1✔
368

369
    return camera_result_from_command_result(_system_impl->send_command(cmd_take_photo));
1✔
370
}
371

NEW
372
Camera::Result CameraImpl::zoom_out_start(int32_t component_id)
×
373
{
NEW
374
    auto cmd = make_command_zoom_out(component_id);
×
375

376
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
377
}
378

NEW
379
Camera::Result CameraImpl::zoom_in_start(int32_t component_id)
×
380
{
NEW
381
    auto cmd = make_command_zoom_in(component_id);
×
382

383
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
384
}
385

NEW
386
Camera::Result CameraImpl::zoom_stop(int32_t component_id)
×
387
{
NEW
388
    auto cmd = make_command_zoom_stop(component_id);
×
389

390
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
391
}
392

NEW
393
Camera::Result CameraImpl::zoom_range(int32_t component_id, float range)
×
394
{
NEW
395
    auto cmd = make_command_zoom_range(component_id, range);
×
396

397
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
398
}
399

400
Camera::Result
NEW
401
CameraImpl::track_point(int32_t component_id, float point_x, float point_y, float radius)
×
402
{
NEW
403
    auto cmd = make_command_track_point(component_id, point_x, point_y, radius);
×
404

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

408
Camera::Result CameraImpl::track_rectangle(
×
409
    int32_t component_id,
410
    float top_left_x,
411
    float top_left_y,
412
    float bottom_right_x,
413
    float bottom_right_y)
414
{
NEW
415
    auto cmd = make_command_track_rectangle(
×
416
        component_id, top_left_x, top_left_y, bottom_right_x, bottom_right_y);
417

418
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
419
}
420

NEW
421
Camera::Result CameraImpl::track_stop(int32_t component_id)
×
422
{
NEW
423
    auto cmd = make_command_track_stop(component_id);
×
424

425
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
426
}
427

NEW
428
Camera::Result CameraImpl::focus_in_start(int32_t component_id)
×
429
{
NEW
430
    auto cmd = make_command_focus_in(component_id);
×
431

432
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
433
}
434

NEW
435
Camera::Result CameraImpl::focus_out_start(int32_t component_id)
×
436
{
NEW
437
    auto cmd = make_command_focus_out(component_id);
×
438

439
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
440
}
441

NEW
442
Camera::Result CameraImpl::focus_stop(int32_t component_id)
×
443
{
NEW
444
    auto cmd = make_command_focus_stop(component_id);
×
445

446
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
447
}
448

NEW
449
Camera::Result CameraImpl::focus_range(int32_t component_id, float range)
×
450
{
NEW
451
    auto cmd = make_command_focus_range(component_id, range);
×
452

453
    return camera_result_from_command_result(_system_impl->send_command(cmd));
×
454
}
455

456
Camera::Result CameraImpl::start_photo_interval(int32_t component_id, float interval_s)
1✔
457
{
458
    auto cmd_take_photo_time_lapse = make_command_take_photo(component_id, interval_s, 0.f);
1✔
459

460
    return camera_result_from_command_result(_system_impl->send_command(cmd_take_photo_time_lapse));
1✔
461
}
462

463
Camera::Result CameraImpl::stop_photo_interval(int32_t component_id)
1✔
464
{
465
    auto cmd_stop_photo_interval = make_command_stop_photo(component_id);
1✔
466

467
    return camera_result_from_command_result(_system_impl->send_command(cmd_stop_photo_interval));
1✔
468
}
469

NEW
470
Camera::Result CameraImpl::start_video(int32_t component_id)
×
471
{
472
    // Capture status rate is not set
NEW
473
    auto cmd_start_video = make_command_start_video(component_id, 0.f);
×
474

475
    return camera_result_from_command_result(_system_impl->send_command(cmd_start_video));
×
476
}
477

NEW
478
Camera::Result CameraImpl::stop_video(int32_t component_id)
×
479
{
NEW
480
    auto cmd_stop_video = make_command_stop_video(component_id);
×
481

482
    return camera_result_from_command_result(_system_impl->send_command(cmd_stop_video));
×
483
}
484

NEW
485
void CameraImpl::zoom_in_start_async(int32_t component_id, const Camera::ResultCallback& callback)
×
486
{
NEW
487
    auto cmd = make_command_zoom_in(component_id);
×
488

489
    _system_impl->send_command_async(
×
490
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
491
            receive_command_result(result, callback);
×
492
        });
×
493
}
×
494

NEW
495
void CameraImpl::zoom_out_start_async(int32_t component_id, const Camera::ResultCallback& callback)
×
496
{
NEW
497
    auto cmd = make_command_zoom_out(component_id);
×
498

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

NEW
505
void CameraImpl::zoom_stop_async(int32_t component_id, const Camera::ResultCallback& callback)
×
506
{
NEW
507
    auto cmd = make_command_zoom_stop(component_id);
×
508

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

NEW
515
void CameraImpl::zoom_range_async(
×
516
    int32_t component_id, float range, const Camera::ResultCallback& callback)
517
{
NEW
518
    auto cmd = make_command_zoom_range(component_id, range);
×
519

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

526
void CameraImpl::track_point_async(
×
527
    int32_t component_id,
528
    float point_x,
529
    float point_y,
530
    float radius,
531
    const Camera::ResultCallback& callback)
532
{
NEW
533
    auto cmd = make_command_track_point(component_id, point_x, point_y, radius);
×
534

535
    _system_impl->send_command_async(
×
536
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
537
            receive_command_result(result, callback);
×
538
        });
×
539
}
×
540

541
void CameraImpl::track_rectangle_async(
×
542
    int32_t component_id,
543
    float top_left_x,
544
    float top_left_y,
545
    float bottom_right_x,
546
    float bottom_right_y,
547
    const Camera::ResultCallback& callback)
548
{
NEW
549
    auto cmd = make_command_track_rectangle(
×
550
        component_id, top_left_x, top_left_y, bottom_right_x, bottom_right_y);
551

552
    _system_impl->send_command_async(
×
553
        cmd, [this, callback](MavlinkCommandSender::Result result, float) {
×
554
            receive_command_result(result, callback);
×
555
        });
×
556
}
×
557

NEW
558
void CameraImpl::track_stop_async(int32_t component_id, const Camera::ResultCallback& callback)
×
559
{
NEW
560
    auto cmd = make_command_track_stop(component_id);
×
561

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

NEW
568
void CameraImpl::focus_in_start_async(int32_t component_id, const Camera::ResultCallback& callback)
×
569
{
NEW
570
    auto cmd = make_command_focus_in(component_id);
×
571

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

NEW
578
void CameraImpl::focus_out_start_async(int32_t component_id, const Camera::ResultCallback& callback)
×
579
{
NEW
580
    auto cmd = make_command_focus_out(component_id);
×
581

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

NEW
588
void CameraImpl::focus_stop_async(int32_t component_id, const Camera::ResultCallback& callback)
×
589
{
NEW
590
    auto cmd = make_command_focus_stop(component_id);
×
591

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

NEW
598
void CameraImpl::focus_range_async(
×
599
    int32_t component_id, float range, const Camera::ResultCallback& callback)
600
{
NEW
601
    auto cmd = make_command_focus_range(component_id, range);
×
602

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

NEW
609
void CameraImpl::take_photo_async(int32_t component_id, const Camera::ResultCallback& callback)
×
610
{
611
    // Take 1 photo only with no interval
NEW
612
    auto cmd_take_photo = make_command_take_photo(component_id, 0.f, 1.0f);
×
613

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

620
void CameraImpl::start_photo_interval_async(
×
621
    int32_t component_id, float interval_s, const Camera::ResultCallback& callback)
622
{
NEW
623
    auto cmd_take_photo_time_lapse = make_command_take_photo(component_id, interval_s, 0.f);
×
624

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

NEW
631
void CameraImpl::stop_photo_interval_async(
×
632
    int32_t component_id, const Camera::ResultCallback& callback)
633
{
NEW
634
    auto cmd_stop_photo_interval = make_command_stop_photo(component_id);
×
635

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

NEW
642
void CameraImpl::start_video_async(int32_t component_id, const Camera::ResultCallback& callback)
×
643
{
644
    // Capture status rate is not set
NEW
645
    auto cmd_start_video = make_command_start_video(component_id, 0.f);
×
646

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

NEW
653
void CameraImpl::stop_video_async(int32_t component_id, const Camera::ResultCallback& callback)
×
654
{
NEW
655
    auto cmd_stop_video = make_command_stop_video(component_id);
×
656

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

663
Camera::CameraList CameraImpl::camera_list()
86✔
664
{
665
    std::lock_guard lock(_mutex);
86✔
666

667
    return camera_list_with_lock();
86✔
668
}
86✔
669

670
Camera::CameraList CameraImpl::camera_list_with_lock()
103✔
671
{
672
    Camera::CameraList result{};
103✔
673

674
    for (auto& potential_camera : potential_cameras_with_lock()) {
205✔
675
        if (!potential_camera->maybe_information) {
102✔
NEW
676
            continue;
×
677
        }
678
        result.cameras.push_back(potential_camera->maybe_information.value());
102✔
679
    }
103✔
680

681
    return result;
103✔
682
}
683

684
Camera::CameraListHandle
685
CameraImpl::subscribe_camera_list(const Camera::CameraListCallback& callback)
1✔
686
{
687
    std::lock_guard lock(_mutex);
1✔
688
    auto handle = _camera_list_subscription_callbacks.subscribe(callback);
1✔
689

690
    notify_camera_list_with_lock();
1✔
691

692
    return handle;
2✔
693
}
1✔
694

NEW
695
void CameraImpl::unsubscribe_camera_list(Camera::CameraListHandle handle)
×
696
{
NEW
697
    std::lock_guard lock(_mutex);
×
NEW
698
    _camera_list_subscription_callbacks.unsubscribe(handle);
×
UNCOV
699
}
×
700

701
void CameraImpl::notify_camera_list_with_lock()
17✔
702
{
703
    _system_impl->call_user_callback([&]() {
37✔
704
        _camera_list_subscription_callbacks.queue(
705
            camera_list_with_lock(), [this](const auto& func) { func(); });
706
    });
707
}
17✔
708

NEW
709
Camera::Result CameraImpl::start_video_streaming(int32_t component_id, int32_t stream_id)
×
710
{
NEW
711
    auto command = make_command_start_video_streaming(component_id, stream_id);
×
712

NEW
713
    return camera_result_from_command_result(_system_impl->send_command(command));
×
714
}
715

NEW
716
Camera::Result CameraImpl::stop_video_streaming(int32_t component_id, int32_t stream_id)
×
717
{
NEW
718
    auto command = make_command_stop_video_streaming(component_id, stream_id);
×
719

NEW
720
    return camera_result_from_command_result(_system_impl->send_command(command));
×
721
}
722

723
void CameraImpl::request_slower()
10✔
724
{
725
    std::lock_guard lock(_mutex);
10✔
726

727
    for (auto& camera : _potential_cameras) {
11✔
728
        _system_impl->mavlink_request_message().request(
2✔
729
            MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION, camera.component_id, nullptr);
1✔
730
    }
731
}
10✔
732

733
void CameraImpl::request_faster()
14✔
734
{
735
    std::lock_guard lock(_mutex);
14✔
736

737
    for (auto& camera : _potential_cameras) {
19✔
738
        _system_impl->mavlink_request_message().request(
10✔
739
            MAVLINK_MSG_ID_VIDEO_STREAM_STATUS, camera.component_id, nullptr);
5✔
740

741
        _system_impl->mavlink_request_message().request(
10✔
742
            MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS, camera.component_id, nullptr);
5✔
743

744
        _system_impl->mavlink_request_message().request(
10✔
745
            MAVLINK_MSG_ID_STORAGE_INFORMATION, camera.component_id, nullptr);
5✔
746

747
        _system_impl->mavlink_request_message().request(
10✔
748
            MAVLINK_MSG_ID_CAMERA_SETTINGS, camera.component_id, nullptr);
5✔
749
    }
750
}
14✔
751

752
Camera::VideoStreamInfoHandle
753
CameraImpl::subscribe_video_stream_info(const Camera::VideoStreamInfoCallback& callback)
1✔
754
{
755
    std::lock_guard lock(_mutex);
1✔
756

757
    auto handle = _video_stream_info_subscription_callbacks.subscribe(callback);
1✔
758

759
    notify_video_stream_info_for_all_with_lock();
1✔
760

761
    return handle;
2✔
762
}
1✔
763

764
void CameraImpl::unsubscribe_video_stream_info(Camera::VideoStreamInfoHandle handle)
×
765
{
NEW
766
    std::lock_guard lock(_mutex);
×
NEW
767
    _video_stream_info_subscription_callbacks.unsubscribe(handle);
×
UNCOV
768
}
×
769

770
Camera::Result
771
CameraImpl::camera_result_from_command_result(const MavlinkCommandSender::Result command_result)
7✔
772
{
773
    switch (command_result) {
7✔
774
        case MavlinkCommandSender::Result::Success:
7✔
775
            return Camera::Result::Success;
7✔
776
        case MavlinkCommandSender::Result::NoSystem:
×
777
            // FALLTHROUGH
778
        case MavlinkCommandSender::Result::ConnectionError:
779
            // FALLTHROUGH
780
        case MavlinkCommandSender::Result::Busy:
781
            // FALLTHROUGH
782
        case MavlinkCommandSender::Result::Failed:
783
            return Camera::Result::Error;
×
784
        case MavlinkCommandSender::Result::Denied:
×
785
            // FALLTHROUGH
786
        case MavlinkCommandSender::Result::TemporarilyRejected:
787
            return Camera::Result::Denied;
×
788
        case MavlinkCommandSender::Result::Timeout:
×
789
            return Camera::Result::Timeout;
×
NEW
790
        case MavlinkCommandSender::Result::Unsupported:
×
NEW
791
            return Camera::Result::ActionUnsupported;
×
NEW
792
        case MavlinkCommandSender::Result::Cancelled:
×
793
        default:
794
            return Camera::Result::Unknown;
×
795
    }
796
}
797

798
Camera::Result CameraImpl::camera_result_from_parameter_result(
×
799
    const MavlinkParameterClient::Result parameter_result)
800
{
801
    switch (parameter_result) {
×
802
        case MavlinkParameterClient::Result::Success:
×
803
            return Camera::Result::Success;
×
804
        case MavlinkParameterClient::Result::Timeout:
×
805
            return Camera::Result::Timeout;
×
806
        case MavlinkParameterClient::Result::WrongType:
×
807
            // FALLTHROUGH
808
        case MavlinkParameterClient::Result::ParamNameTooLong:
809
            // FALLTHROUGH
810
        case MavlinkParameterClient::Result::NotFound:
811
            // FALLTHROUGH
812
        case MavlinkParameterClient::Result::ValueUnsupported:
813
            return Camera::Result::WrongArgument;
×
NEW
814
        case MavlinkParameterClient::Result::ConnectionError:
×
815
            // FALLTHROUGH
816
        case MavlinkParameterClient::Result::Failed:
817
            // FALLTHROUGH
818
        case MavlinkParameterClient::Result::UnknownError:
819
            return Camera::Result::Error;
×
820
        default:
×
821
            return Camera::Result::Unknown;
×
822
    }
823
}
824

825
Camera::Result CameraImpl::set_mode(int32_t component_id, const Camera::Mode mode)
2✔
826
{
827
    const float mavlink_mode = to_mavlink_camera_mode(mode);
2✔
828
    auto cmd_set_camera_mode = make_command_set_camera_mode(component_id, mavlink_mode);
2✔
829
    const auto command_result = _system_impl->send_command(cmd_set_camera_mode);
2✔
830
    const auto camera_result = camera_result_from_command_result(command_result);
2✔
831

832
    if (camera_result == Camera::Result::Success) {
2✔
833
        std::lock_guard lock(_mutex);
2✔
834
        auto maybe_potential_camera =
835
            maybe_potential_camera_for_component_id_with_lock(component_id);
2✔
836
        if (maybe_potential_camera != nullptr) {
2✔
837
            save_camera_mode_with_lock(*maybe_potential_camera, mode);
2✔
838
        }
839
    }
2✔
840

841
    return camera_result;
2✔
842
}
843

844
void CameraImpl::save_camera_mode_with_lock(PotentialCamera& potential_camera, Camera::Mode mode)
7✔
845
{
846
    potential_camera.mode = mode;
7✔
847

848
    // If there is a camera definition which is the case if we are in this
849
    // function, and if CAM_MODE is defined there, then we reuse that type.
850
    // Otherwise, we hardcode it to `uint32_t`.
851

852
    // Note that it could be that the camera definition defines options
853
    // different from {PHOTO, VIDEO}, in which case the mode received from
854
    // CAMERA_SETTINGS will be wrong.
855

856
    if (!potential_camera.camera_definition) {
7✔
857
        return;
7✔
858
    }
859

UNCOV
860
    ParamValue value;
×
NEW
861
    if (potential_camera.camera_definition->get_setting("CAM_MODE", value)) {
×
862
        if (value.is<uint8_t>()) {
×
NEW
863
            value.set<uint8_t>(static_cast<uint8_t>(mode));
×
864
        } else if (value.is<int8_t>()) {
×
NEW
865
            value.set<int8_t>(static_cast<int8_t>(mode));
×
866
        } else if (value.is<uint16_t>()) {
×
NEW
867
            value.set<uint16_t>(static_cast<uint16_t>(mode));
×
868
        } else if (value.is<int16_t>()) {
×
NEW
869
            value.set<int16_t>(static_cast<int16_t>(mode));
×
870
        } else if (value.is<uint32_t>()) {
×
NEW
871
            value.set<uint32_t>(static_cast<uint32_t>(mode));
×
872
        } else if (value.is<int32_t>()) {
×
NEW
873
            value.set<int32_t>(static_cast<int32_t>(mode));
×
874
        } else if (value.is<uint64_t>()) {
×
NEW
875
            value.set<uint64_t>(static_cast<uint64_t>(mode));
×
876
        } else if (value.is<int64_t>()) {
×
NEW
877
            value.set<int64_t>(static_cast<int64_t>(mode));
×
878
        } else if (value.is<float>()) {
×
NEW
879
            value.set<float>(static_cast<float>(mode));
×
880
        } else if (value.is<double>()) {
×
NEW
881
            value.set<double>(static_cast<double>(mode));
×
882
        }
883
    } else {
NEW
884
        value.set<uint32_t>(static_cast<uint32_t>(mode));
×
885
    }
886

NEW
887
    potential_camera.camera_definition->set_setting("CAM_MODE", value);
×
NEW
888
    refresh_params_with_lock(potential_camera, false);
×
NEW
889
    notify_mode_with_lock(potential_camera);
×
UNCOV
890
}
×
891

892
float CameraImpl::to_mavlink_camera_mode(const Camera::Mode mode)
2✔
893
{
894
    switch (mode) {
2✔
895
        case Camera::Mode::Photo:
1✔
896
            return CAMERA_MODE_IMAGE;
1✔
897
        case Camera::Mode::Video:
1✔
898
            return CAMERA_MODE_VIDEO;
1✔
899
        default:
×
900
        case Camera::Mode::Unknown:
901
            return NAN;
×
902
    }
903
}
904

NEW
905
void CameraImpl::set_mode_async(
×
906
    int32_t component_id, const Camera::Mode mode, const Camera::ResultCallback& callback)
907
{
908
    const auto mavlink_mode = to_mavlink_camera_mode(mode);
×
NEW
909
    auto cmd_set_camera_mode = make_command_set_camera_mode(component_id, mavlink_mode);
×
910

911
    _system_impl->send_command_async(
×
912
        cmd_set_camera_mode,
NEW
913
        [this, callback, mode, component_id](MavlinkCommandSender::Result result, float progress) {
×
914
            UNUSED(progress);
×
NEW
915
            receive_set_mode_command_result(result, callback, mode, component_id);
×
916
        });
×
917
}
×
918

919
Camera::ModeHandle CameraImpl::subscribe_mode(const Camera::ModeCallback& callback)
×
920
{
NEW
921
    std::lock_guard lock(_mutex);
×
NEW
922
    auto handle = _mode_subscription_callbacks.subscribe(callback);
×
923

NEW
924
    notify_mode_for_all_with_lock();
×
925

926
    return handle;
×
927
}
×
928

929
void CameraImpl::unsubscribe_mode(Camera::ModeHandle handle)
×
930
{
NEW
931
    std::lock_guard lock(_mutex);
×
NEW
932
    _mode_subscription_callbacks.unsubscribe(handle);
×
UNCOV
933
}
×
934

935
Camera::StorageHandle CameraImpl::subscribe_storage(const Camera::StorageCallback& callback)
1✔
936
{
937
    std::lock_guard lock(_mutex);
1✔
938
    auto handle = _storage_subscription_callbacks.subscribe(callback);
1✔
939

940
    notify_storage_for_all_with_lock();
1✔
941

942
    return handle;
2✔
943
}
1✔
944

NEW
945
void CameraImpl::unsubscribe_storage(Camera::StorageHandle handle)
×
946
{
NEW
947
    std::lock_guard lock(_mutex);
×
NEW
948
    _storage_subscription_callbacks.unsubscribe(handle);
×
UNCOV
949
}
×
950

951
Camera::CaptureInfoHandle
952
CameraImpl::subscribe_capture_info(const Camera::CaptureInfoCallback& callback)
2✔
953
{
954
    std::lock_guard lock(_mutex);
2✔
955
    return _capture_info_callbacks.subscribe(callback);
2✔
956
}
2✔
957

958
void CameraImpl::unsubscribe_capture_info(Camera::CaptureInfoHandle handle)
1✔
959
{
960
    std::lock_guard lock(_mutex);
1✔
961
    _capture_info_callbacks.unsubscribe(handle);
1✔
962
}
1✔
963

964
void CameraImpl::process_heartbeat(const mavlink_message_t& message)
37✔
965
{
966
    // Check for potential camera
967
    std::lock_guard lock(_mutex);
37✔
968
    auto found =
969
        std::any_of(_potential_cameras.begin(), _potential_cameras.end(), [&](const auto& item) {
37✔
970
            return item.component_id == message.compid;
28✔
971
        });
972

973
    if (!found) {
37✔
974
        _potential_cameras.emplace_back(message.compid);
9✔
975
        check_potential_cameras_with_lock();
9✔
976
    }
977
}
37✔
978

979
void CameraImpl::check_potential_cameras_with_lock()
9✔
980
{
981
    for (auto& potential_camera : _potential_cameras) {
18✔
982
        // First step, get information if we don't already have it.
983
        if (!potential_camera.maybe_information) {
9✔
984
            request_camera_information(potential_camera.component_id);
9✔
985
            potential_camera.information_requested = true;
9✔
986
        }
987
    }
988
}
9✔
989

990
void CameraImpl::process_camera_capture_status(const mavlink_message_t& message)
5✔
991
{
992
    mavlink_camera_capture_status_t camera_capture_status;
5✔
993
    mavlink_msg_camera_capture_status_decode(&message, &camera_capture_status);
5✔
994

995
    std::lock_guard lock(_mutex);
5✔
996
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(message.compid);
5✔
997

998
    if (maybe_potential_camera == nullptr) {
5✔
NEW
999
        return;
×
1000
    }
1001

1002
    // auto& camera = *maybe_potential_camera;
1003

1004
    // If image_count got smaller, consider that the storage was formatted.
1005
    // if (camera_capture_status.image_count < camera.capture_status.image_count) {
1006
    //    LogDebug() << "Seems like storage was formatted, setting state accordingly";
1007
    //    reset_following_format_storage_with_lock(camera);
1008
    //}
1009

1010
    // TODO: enable again
1011
    //{
1012
    //    std::lock_guard lock(_status.mutex);
1013

1014
    //    _status.data.video_on = (camera_capture_status.video_status == 1);
1015
    //    _status.data.photo_interval_on =
1016
    //        (camera_capture_status.image_status == 2 || camera_capture_status.image_status == 3);
1017
    //    _status.received_camera_capture_status = true;
1018
    //    _status.data.recording_time_s =
1019
    //        static_cast<float>(camera_capture_status.recording_time_ms) / 1e3f;
1020

1021
    //    _status.image_count = camera_capture_status.image_count;
1022

1023
    //    if (_status.image_count_at_connection == -1) {
1024
    //        _status.image_count_at_connection = camera_capture_status.image_count;
1025
    //    }
1026
    //}
1027
}
5✔
1028

1029
void CameraImpl::process_storage_information(const mavlink_message_t& message)
1✔
1030
{
1031
    mavlink_storage_information_t storage_information;
1✔
1032
    mavlink_msg_storage_information_decode(&message, &storage_information);
1✔
1033

1034
    // I don't think we should still ignore this as it can be confusing.
1035
    // if (storage_information.total_capacity == 0.0f) {
1036
    //    // Some MAVLink systems happen to send the STORAGE_INFORMATION message
1037
    //    // to indicate that the camera has a slot for a storage even if there
1038
    //    // is no way to know anything about that storage (e.g. regardless of
1039
    //    // whether there is a sdcard in the slot).
1040
    //    //
1041
    //    // We consider that a total capacity of 0 means that this is such a
1042
    //    // message, and we don't expect MAVSDK users to leverage it, which is
1043
    //    // why it is ignored.
1044
    //    return;
1045
    //}
1046

1047
    std::lock_guard lock(_mutex);
1✔
1048
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(message.compid);
1✔
1049

1050
    if (maybe_potential_camera == nullptr) {
1✔
UNCOV
1051
        return;
×
1052
    }
1053

1054
    auto& camera = *maybe_potential_camera;
1✔
1055

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

1064
    notify_storage_with_lock(camera);
1✔
1065
}
1✔
1066

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

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

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

1111
    std::lock_guard lock(_mutex);
4✔
1112
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(message.compid);
4✔
1113

1114
    if (maybe_potential_camera == nullptr) {
4✔
NEW
1115
        return;
×
1116
    }
1117

1118
    auto& camera = *maybe_potential_camera;
4✔
1119

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

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

1146
    _capture_info_callbacks.queue(
4✔
1147
        capture_info, [this](const auto& func) { _system_impl->call_user_callback(func); });
4✔
1148

1149
    // TODO: enable again
1150
#if 0
1151
    _status.photo_list.insert(std::make_pair(image_captured.image_index, capture_info));
1152

1153
    _captured_request_cv.notify_all();
1154

1155
    std::lock_guard lock(_capture_info.mutex);
1156
    // Notify user if a new image has been captured.
1157
    if (_capture_info.last_advertised_image_index < capture_info.index) {
1158
        _capture_info.callbacks.queue(
1159
            capture_info, [this](const auto& func) { _system_impl->call_user_callback(func); });
1160

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

1174
        _capture_info.last_advertised_image_index = capture_info.index;
1175
    }
1176

1177
    else if (auto it = _capture_info.missing_image_retries.find(capture_info.index);
1178
             it != _capture_info.missing_image_retries.end()) {
1179
        _capture_info.callbacks.queue(
1180
            capture_info, [this](const auto& func) { _system_impl->call_user_callback(func); });
1181
        _capture_info.missing_image_retries.erase(it);
1182
    }
1183
#endif
1184
}
4✔
1185

1186
void CameraImpl::request_missing_capture_info()
×
1187
{
1188
#if 0
1189
    std::lock_guard lock(_capture_info.mutex);
1190

1191
    for (auto it = _capture_info.missing_image_retries.begin();
1192
         it != _capture_info.missing_image_retries.end();
1193
         /* ++it */) {
1194
        if (it->second > 3) {
1195
            it = _capture_info.missing_image_retries.erase(it);
1196
        } else {
1197
            ++it;
1198
        }
1199
    }
1200
#endif
1201

1202
    /* TODO: implement again
1203
    if (!_capture_info.missing_image_retries.empty()) {
1204
        auto it_lowest_retries = std::min_element(
1205
            _capture_info.missing_image_retries.begin(), _capture_info.missing_image_retries.end());
1206
        _system_impl->mavlink_request_message().request(
1207
            MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED,
1208
            _component_id + MAV_COMP_ID_CAMERA,
1209
            nullptr,
1210
            it_lowest_retries->first);
1211
        it_lowest_retries->second += 1;
1212
    }
1213
    */
UNCOV
1214
}
×
1215

1216
void CameraImpl::process_camera_settings(const mavlink_message_t& message)
5✔
1217
{
1218
    mavlink_camera_settings_t camera_settings;
5✔
1219
    mavlink_msg_camera_settings_decode(&message, &camera_settings);
5✔
1220

1221
    std::lock_guard lock(_mutex);
5✔
1222
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(message.compid);
5✔
1223

1224
    if (maybe_potential_camera == nullptr) {
5✔
NEW
1225
        return;
×
1226
    }
1227

1228
    save_camera_mode_with_lock(*maybe_potential_camera, to_camera_mode(camera_settings.mode_id));
5✔
1229
}
5✔
1230

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

NEW
1328
            auto component_id = potential_camera.component_id;
×
1329

NEW
1330
            auto download_path = _tmp_download_path / file_cache_tag;
×
1331

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

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

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

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

1374
            auto component_id = potential_camera.component_id;
1✔
1375

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

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

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

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

1410
                    auto downloaded_filepath = _tmp_download_path / downloaded_filename;
2✔
1411

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

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

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

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

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

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

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

1462
    if (maybe_potential_camera == nullptr) {
1✔
NEW
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)
4✔
1493
{
1494
    mavlink_video_stream_status_t received_video_stream_status;
4✔
1495
    mavlink_msg_video_stream_status_decode(&message, &received_video_stream_status);
4✔
1496

1497
    std::lock_guard lock(_mutex);
4✔
1498
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(message.compid);
4✔
1499

1500
    if (maybe_potential_camera == nullptr) {
4✔
NEW
1501
        return;
×
1502
    }
1503

1504
    auto& camera = *maybe_potential_camera;
4✔
1505

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

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

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

1526
    notify_video_stream_info_with_lock(camera);
4✔
1527
}
4✔
1528

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1636
    return !options.empty();
98✔
1637
}
98✔
1638

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

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

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

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

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

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

1673
    auto& camera = *maybe_potential_camera;
3✔
1674

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

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

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

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

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

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

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

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

1753
            auto& camera_later = *maybe_potential_camera_later;
3✔
1754

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

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

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

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

1800
        auto& camera = *maybe_potential_camera;
2✔
1801

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

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

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

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

1835
    return ret.get();
2✔
1836
}
2✔
1837

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

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

1854
    auto status = ret.wait_for(std::chrono::seconds(1));
×
1855

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

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

1878
    auto& camera = *maybe_potential_camera;
2✔
1879

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

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

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

1920
    notify_current_settings_for_all_with_lock();
1✔
1921
    return handle;
2✔
1922
}
1✔
1923

1924
void CameraImpl::unsubscribe_current_settings(Camera::CurrentSettingsHandle handle)
×
1925
{
NEW
1926
    std::lock_guard lock(_mutex);
×
NEW
1927
    _subscribe_current_settings_callbacks.unsubscribe(handle);
×
UNCOV
1928
}
×
1929

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

1936
    notify_possible_setting_options_for_all_with_lock();
1✔
1937
    return handle;
2✔
1938
}
1✔
1939

1940
void CameraImpl::unsubscribe_possible_setting_options(Camera::PossibleSettingOptionsHandle handle)
×
1941
{
NEW
1942
    _subscribe_possible_setting_options_callbacks.unsubscribe(handle);
×
1943
}
×
1944

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

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

1963
void CameraImpl::notify_current_settings_with_lock(PotentialCamera& potential_camera)
4✔
1964
{
1965
    assert(potential_camera.camera_definition != nullptr);
4✔
1966

1967
    if (_subscribe_current_settings_callbacks.empty()) {
4✔
1968
        return;
1✔
1969
    }
1970

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

1977
    auto& camera = potential_camera;
3✔
1978

1979
    Camera::CurrentSettingsUpdate update{};
3✔
1980
    update.component_id = potential_camera.component_id;
3✔
1981

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

2003
    _subscribe_current_settings_callbacks.queue(
3✔
2004
        update, [this](const auto& func) { _system_impl->call_user_callback(func); });
3✔
2005
}
3✔
2006

2007
void CameraImpl::notify_possible_setting_options_with_lock(PotentialCamera& potential_camera)
4✔
2008
{
2009
    assert(potential_camera.camera_definition != nullptr);
4✔
2010

2011
    if (_subscribe_possible_setting_options_callbacks.empty()) {
4✔
2012
        return;
2✔
2013
    }
2014

2015
    Camera::PossibleSettingOptionsUpdate update{};
2✔
2016
    update.component_id = potential_camera.component_id;
2✔
2017

2018
    auto setting_options = get_possible_setting_options_with_lock(potential_camera);
2✔
2019
    if (setting_options.second.empty()) {
2✔
UNCOV
2020
        return;
×
2021
    }
2022

2023
    update.setting_options = setting_options.second;
2✔
2024

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

2029
std::pair<Camera::Result, std::vector<Camera::SettingOptions>>
2030
CameraImpl::get_possible_setting_options(int32_t component_id)
2✔
2031
{
2032
    std::pair<Camera::Result, std::vector<Camera::SettingOptions>> result;
2✔
2033

2034
    std::lock_guard lock(_mutex);
2✔
2035
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(component_id);
2✔
2036
    if (maybe_potential_camera == nullptr) {
2✔
NEW
2037
        result.first = Camera::Result::CameraIdInvalid;
×
NEW
2038
        return result;
×
2039
    }
2040

2041
    auto& camera = *maybe_potential_camera;
2✔
2042
    if (camera.is_fetching_camera_definition || camera.camera_definition == nullptr) {
2✔
NEW
2043
        result.first = Camera::Result::Unavailable;
×
NEW
2044
        return result;
×
2045
    }
2046

2047
    return get_possible_setting_options_with_lock(*maybe_potential_camera);
2✔
2048
}
2✔
2049

2050
std::pair<Camera::Result, std::vector<Camera::SettingOptions>>
2051
CameraImpl::get_possible_setting_options_with_lock(PotentialCamera& potential_camera)
9✔
2052
{
2053
    std::pair<Camera::Result, std::vector<Camera::SettingOptions>> result;
9✔
2054

2055
    std::unordered_map<std::string, ParamValue> possible_settings;
9✔
2056
    assert(potential_camera.camera_definition != nullptr);
9✔
2057

2058
    auto& camera = potential_camera;
9✔
2059

2060
    camera.camera_definition->get_possible_settings(possible_settings);
9✔
2061

2062
    for (auto& possible_setting : possible_settings) {
107✔
2063
        Camera::SettingOptions setting_options{};
98✔
2064
        setting_options.setting_id = possible_setting.first;
98✔
2065
        setting_options.is_range =
98✔
2066
            camera.camera_definition->is_setting_range(possible_setting.first);
98✔
2067
        get_setting_str_with_lock(
98✔
2068
            camera, setting_options.setting_id, setting_options.setting_description);
2069
        get_possible_options_with_lock(camera, possible_setting.first, setting_options.options);
98✔
2070
        result.second.push_back(setting_options);
98✔
2071
    }
98✔
2072

2073
    result.first = Camera::Result::Success;
9✔
2074
    return result;
9✔
2075
}
9✔
2076

2077
void CameraImpl::refresh_params_with_lock(PotentialCamera& potential_camera, bool initial_load)
5✔
2078
{
2079
    assert(potential_camera.camera_definition != nullptr);
5✔
2080

2081
    std::vector<std::pair<std::string, ParamValue>> params;
5✔
2082
    potential_camera.camera_definition->get_unknown_params(params);
5✔
2083

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

2093
    auto component_id = potential_camera.component_id;
2✔
2094

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

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

2120
                    auto& camera_later = *maybe_potential_camera_later;
26✔
2121

2122
                    if (camera_later.camera_definition->set_setting(param_name, value)) {
26✔
2123
                        if (_debugging) {
26✔
NEW
2124
                            LogDebug() << "Got setting for " << param_name << ": " << value;
×
2125
                        }
2126
                        return;
26✔
2127
                    }
2128

NEW
2129
                    if (is_last) {
×
NEW
2130
                        notify_current_settings_with_lock(camera_later);
×
NEW
2131
                        notify_possible_setting_options_with_lock(camera_later);
×
2132
                    }
2133
                },
26✔
2134
                this);
2135

2136
        if (initial_load) {
26✔
2137
            subscribe_to_param_changes_with_lock(potential_camera, param_name, param_value_type);
26✔
2138
        }
2139
        ++count;
26✔
2140
    }
2141
}
5✔
2142

2143
void CameraImpl::subscribe_to_param_changes_with_lock(
26✔
2144
    PotentialCamera& camera, std::string param_name, const ParamValue& param_value_type)
2145
{
2146
    auto component_id = camera.component_id;
26✔
2147
    auto changed = [this, component_id, param_name](auto new_param) {
62✔
2148
        if (_debugging) {
12✔
NEW
2149
            LogDebug() << "Got changing param: " << param_name << " -> " << new_param;
×
2150
        }
2151

2152
        std::lock_guard lock_later(_mutex);
12✔
2153

2154
        auto maybe_potential_camera_later =
2155
            maybe_potential_camera_for_component_id_with_lock(component_id);
12✔
2156
        // We already checked these fields earlier, so we don't check again.
2157
        assert(maybe_potential_camera_later != nullptr);
12✔
2158
        assert(maybe_potential_camera_later->camera_definition != nullptr);
12✔
2159
        auto& camera_later = *maybe_potential_camera_later;
12✔
2160

2161
        ParamValue param_value;
12✔
2162
        param_value.set<decltype(new_param)>(new_param);
12✔
2163
        camera_later.camera_definition->set_setting(param_name, param_value);
12✔
2164
    };
38✔
2165

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

2204
bool CameraImpl::get_setting_str_with_lock(
152✔
2205
    PotentialCamera& potential_camera, const std::string& setting_id, std::string& description)
2206
{
2207
    if (potential_camera.camera_definition == nullptr) {
152✔
2208
        return false;
×
2209
    }
2210

2211
    return potential_camera.camera_definition->get_setting_str(setting_id, description);
152✔
2212
}
2213

2214
bool CameraImpl::get_option_str_with_lock(
125✔
2215
    PotentialCamera& potential_camera,
2216
    const std::string& setting_id,
2217
    const std::string& option_id,
2218
    std::string& description)
2219
{
2220
    if (potential_camera.camera_definition == nullptr) {
125✔
2221
        return false;
×
2222
    }
2223

2224
    return potential_camera.camera_definition->get_option_str(setting_id, option_id, description);
125✔
2225
}
2226

2227
void CameraImpl::request_camera_information(uint8_t component_id)
9✔
2228
{
2229
    _system_impl->mavlink_request_message().request(
9✔
2230
        MAVLINK_MSG_ID_CAMERA_INFORMATION, component_id, nullptr);
2231
}
9✔
2232

2233
Camera::Result CameraImpl::format_storage(int32_t component_id, int32_t storage_id)
1✔
2234
{
2235
    auto prom = std::make_shared<std::promise<Camera::Result>>();
1✔
2236
    auto ret = prom->get_future();
1✔
2237

2238
    format_storage_async(
1✔
2239
        component_id, storage_id, [prom](Camera::Result result) { prom->set_value(result); });
1✔
2240

2241
    return ret.get();
1✔
2242
}
1✔
2243

2244
void CameraImpl::format_storage_async(
1✔
2245
    int32_t component_id, int32_t storage_id, const Camera::ResultCallback& callback)
2246
{
2247
    MavlinkCommandSender::CommandLong cmd_format{};
1✔
2248

2249
    cmd_format.command = MAV_CMD_STORAGE_FORMAT;
1✔
2250
    cmd_format.params.maybe_param1 = static_cast<float>(storage_id); // storage ID
1✔
2251
    cmd_format.params.maybe_param2 = 1.0f; // format
1✔
2252
    cmd_format.params.maybe_param3 = 1.0f; // clear
1✔
2253
    cmd_format.target_component_id = component_id;
1✔
2254

2255
    _system_impl->send_command_async(
1✔
2256
        cmd_format, [this, callback](MavlinkCommandSender::Result result, float progress) {
3✔
2257
            UNUSED(progress);
1✔
2258

2259
            receive_command_result(result, [this, callback](Camera::Result camera_result) {
2✔
2260
                if (camera_result == Camera::Result::Success) {
2261
                    // TODO: add in again
2262
                    // reset_following_format_storage();
2263
                }
2264

2265
                callback(camera_result);
1✔
2266
            });
1✔
2267
        });
1✔
2268
}
1✔
2269

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

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

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

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

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

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

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

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

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

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

2321
    return ret.get();
×
2322
}
×
2323

2324
void CameraImpl::list_photos_async(
×
2325
    int32_t component_id,
2326
    Camera::PhotosRange photos_range,
2327
    const Camera::ListPhotosCallback& callback)
2328
{
NEW
2329
    UNUSED(component_id);
×
NEW
2330
    UNUSED(photos_range);
×
2331

2332
    if (!callback) {
×
2333
        LogWarn() << "Trying to get a photo list with a null callback, ignoring...";
×
2334
        return;
×
2335
    }
2336

2337
    // TODO: enable again with a camera to test against.
NEW
2338
    _system_impl->call_user_callback([callback]() {
×
2339
        LogErr() << "Photo list not implemented.";
2340
        callback(Camera::Result::ProtocolUnsupported, std::vector<Camera::CaptureInfo>{});
2341
    });
2342

2343
#if 0
2344
    std::lock_guard lock(_mutex);
2345

2346
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(component_id);
2347
    if (maybe_potential_camera == nullptr) {
2348
        LogWarn() << "Invalid camera ID: " << component_id;
2349
        return;
2350
    }
2351
    auto& camera = *maybe_potential_camera;
2352

2353
    {
2354
        std::lock_guard status_lock(_status.mutex);
2355

2356
        if (_status.is_fetching_photos) {
2357
            _system_impl->call_user_callback([callback]() {
2358
                callback(Camera::Result::Busy, std::vector<Camera::CaptureInfo>{});
2359
            });
2360
            return;
2361
        } else {
2362
            _status.is_fetching_photos = true;
2363
        }
2364

2365
        if (_status.image_count == -1) {
2366
            LogErr() << "Cannot list photos: camera status has not been received yet!";
2367
            _status.is_fetching_photos = false;
2368
            _system_impl->call_user_callback([callback]() {
2369
                callback(Camera::Result::Error, std::vector<Camera::CaptureInfo>{});
2370
            });
2371
            return;
2372
        }
2373
    }
2374

2375
    const int start_index = [this, photos_range]() {
2376
        switch (photos_range) {
2377
            case Camera::PhotosRange::SinceConnection:
2378
                return _status.image_count_at_connection;
2379
            case Camera::PhotosRange::All:
2380
            // FALLTHROUGH
2381
            default:
2382
                return 0;
2383
        }
2384
    }();
2385

2386
    std::thread([this, start_index, callback, component_id]() {
2387
        std::unique_lock<std::mutex> capture_request_lock(_captured_request_mutex);
2388

2389
        for (int i = start_index; i < _status.image_count; i++) {
2390
            // In case the vehicle sends capture info, but not those we are asking, we do not
2391
            // want to loop infinitely. The safety_count is here to abort if this happens.
2392
            auto safety_count = 0;
2393
            constexpr auto safety_count_boundary = 10;
2394

2395
            while (_status.photo_list.find(i) == _status.photo_list.end() &&
2396
                   safety_count < safety_count_boundary) {
2397
                safety_count++;
2398

2399
                auto request_try_number = 0;
2400
                auto cv_status = std::cv_status::timeout;
2401

2402
                while (cv_status == std::cv_status::timeout) {
2403
                    request_try_number++;
2404

2405
                    // Timeout if the request times out that many times
2406
                    const auto request_try_limit = 10;
2407
                    if (request_try_number >= request_try_limit) {
2408
                        std::lock_guard status_lock(_status.mutex);
2409
                        _status.is_fetching_photos = false;
2410
                        _system_impl->call_user_callback([callback]() {
2411
                            callback(Camera::Result::Timeout, std::vector<Camera::CaptureInfo>{});
2412
                        });
2413
                        return;
2414
                    }
2415

2416
                    _system_impl->mavlink_request_message().request(
2417
                        MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED,
2418
                        component_id;
2419
                        nullptr,
2420
                        i);
2421
                    cv_status = _captured_request_cv.wait_for(
2422
                        capture_request_lock, std::chrono::seconds(1));
2423
                }
2424
            }
2425

2426
            if (safety_count == safety_count_boundary) {
2427
                std::lock_guard status_lock(_status.mutex);
2428
                _status.is_fetching_photos = false;
2429
                _system_impl->call_user_callback([callback]() {
2430
                    callback(Camera::Result::Error, std::vector<Camera::CaptureInfo>{});
2431
                });
2432
                return;
2433
            }
2434
        }
2435

2436
        std::vector<Camera::CaptureInfo> photo_list;
2437
        {
2438
            std::lock_guard status_lock(_status.mutex);
2439

2440
            for (const auto& capture_info : _status.photo_list) {
2441
                if (capture_info.first >= start_index) {
2442
                    photo_list.push_back(capture_info.second);
2443
                }
2444
            }
2445

2446
            _status.is_fetching_photos = false;
2447

2448
            const auto temp_callback = callback;
2449
            _system_impl->call_user_callback([temp_callback, photo_list]() {
2450
                temp_callback(Camera::Result::Success, photo_list);
2451
            });
2452
        }
2453
    }).detach();
2454
#endif
2455
}
2456

2457
std::pair<Camera::Result, Camera::Mode> CameraImpl::get_mode(int32_t component_id)
1✔
2458
{
2459
    std::pair<Camera::Result, Camera::Mode> result{};
1✔
2460

2461
    std::lock_guard lock(_mutex);
1✔
2462

2463
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(component_id);
1✔
2464
    if (maybe_potential_camera == nullptr) {
1✔
NEW
2465
        result.first = Camera::Result::CameraIdInvalid;
×
2466
    } else {
2467
        result.first = Camera::Result::Success;
1✔
2468
        result.second = maybe_potential_camera->mode;
1✔
2469
    }
2470

2471
    return result;
2✔
2472
}
1✔
2473

2474
std::pair<Camera::Result, Camera::Storage> CameraImpl::get_storage(int32_t component_id)
60✔
2475
{
2476
    std::pair<Camera::Result, Camera::Storage> result{};
60✔
2477

2478
    std::lock_guard lock(_mutex);
60✔
2479

2480
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(component_id);
60✔
2481
    if (maybe_potential_camera == nullptr) {
60✔
NEW
2482
        result.first = Camera::Result::CameraIdInvalid;
×
2483
    } else {
2484
        if (maybe_potential_camera->received_storage) {
60✔
2485
            result.first = Camera::Result::Success;
20✔
2486
            result.second = maybe_potential_camera->storage;
20✔
2487
        } else {
2488
            result.first = Camera::Result::Unavailable;
40✔
2489
        }
2490
    }
2491

2492
    return result;
60✔
2493
}
60✔
2494

2495
std::pair<Camera::Result, Camera::VideoStreamInfo>
NEW
2496
CameraImpl::get_video_stream_info(int32_t component_id)
×
2497
{
NEW
2498
    std::pair<Camera::Result, Camera::VideoStreamInfo> result{};
×
2499

NEW
2500
    std::lock_guard lock(_mutex);
×
2501

NEW
2502
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(component_id);
×
NEW
2503
    if (maybe_potential_camera == nullptr) {
×
NEW
2504
        result.first = Camera::Result::CameraIdInvalid;
×
2505
    } else {
NEW
2506
        result.first = Camera::Result::Success;
×
NEW
2507
        result.second = maybe_potential_camera->video_stream_info;
×
2508
    }
2509

NEW
2510
    return result;
×
NEW
2511
}
×
2512

2513
std::pair<Camera::Result, std::vector<Camera::Setting>>
2514
CameraImpl::get_current_settings(int32_t component_id)
2✔
2515
{
2516
    std::pair<Camera::Result, std::vector<Camera::Setting>> result;
2✔
2517

2518
    std::lock_guard lock(_mutex);
2✔
2519
    auto maybe_potential_camera = maybe_potential_camera_for_component_id_with_lock(component_id);
2✔
2520
    if (maybe_potential_camera == nullptr) {
2✔
NEW
2521
        result.first = Camera::Result::CameraIdInvalid;
×
NEW
2522
        return result;
×
2523
    }
2524

2525
    auto& camera = *maybe_potential_camera;
2✔
2526
    if (camera.is_fetching_camera_definition || camera.camera_definition == nullptr) {
2✔
NEW
2527
        result.first = Camera::Result::Unavailable;
×
NEW
2528
        return result;
×
2529
    }
2530

2531
    auto possible_setting_options = get_possible_setting_options_with_lock(camera);
2✔
2532
    if (possible_setting_options.first != Camera::Result::Success) {
2✔
NEW
2533
        result.first = possible_setting_options.first;
×
NEW
2534
        return result;
×
2535
    }
2536

2537
    for (auto& possible_setting : possible_setting_options.second) {
23✔
2538
        // use the cache for this, presumably we updated it right before.
2539
        ParamValue value;
21✔
2540
        if (camera.camera_definition->get_setting(possible_setting.setting_id, value)) {
21✔
2541
            Camera::Setting setting{};
21✔
2542
            setting.setting_id = possible_setting.setting_id;
21✔
2543
            setting.is_range =
21✔
2544
                camera.camera_definition->is_setting_range(possible_setting.setting_id);
21✔
2545
            get_setting_str_with_lock(camera, setting.setting_id, setting.setting_description);
21✔
2546
            setting.option.option_id = value.get_string();
21✔
2547
            if (!camera.camera_definition->is_setting_range(possible_setting.setting_id)) {
21✔
2548
                get_option_str_with_lock(
10✔
2549
                    camera,
2550
                    setting.setting_id,
2551
                    setting.option.option_id,
2552
                    setting.option.option_description);
2553
            }
2554
            result.second.push_back(setting);
21✔
2555
        }
21✔
2556
    }
21✔
2557

2558
    result.first = Camera::Result::Success;
2✔
2559
    return result;
2✔
2560
}
2✔
2561

2562
uint16_t CameraImpl::get_and_increment_capture_sequence(int32_t component_id)
2✔
2563
{
2564
    if (component_id == 0) {
2✔
2565
        // All cameras
NEW
2566
        return 0;
×
2567
    }
2568

2569
    std::lock_guard lock(_mutex);
2✔
2570

2571
    for (auto& potential_camera : _potential_cameras) {
2✔
2572
        if (potential_camera.component_id == component_id) {
2✔
2573
            return potential_camera.capture_sequence++;
2✔
2574
        }
2575
    }
2576

NEW
2577
    return 0;
×
2578
}
2✔
2579

2580
std::vector<CameraImpl::PotentialCamera*> CameraImpl::potential_cameras_with_lock()
106✔
2581
{
2582
    std::vector<CameraImpl::PotentialCamera*> result;
106✔
2583

2584
    for (auto& potential_camera : _potential_cameras) {
211✔
2585
        if (!potential_camera.maybe_information) {
105✔
NEW
2586
            continue;
×
2587
        }
2588
        result.push_back(&potential_camera);
105✔
2589
    }
2590

2591
    return result;
106✔
2592
}
2593

2594
CameraImpl::PotentialCamera*
2595
CameraImpl::maybe_potential_camera_for_component_id_with_lock(uint8_t component_id)
136✔
2596
{
2597
    const auto it = std::find_if(
136✔
2598
        _potential_cameras.begin(), _potential_cameras.end(), [&](auto& potential_camera) {
136✔
2599
            return potential_camera.component_id = component_id;
136✔
2600
        });
2601

2602
    if (it == _potential_cameras.end()) {
136✔
NEW
2603
        return nullptr;
×
2604
    }
2605

2606
    // How to get pointer from iterator?
2607
    return &(*it);
136✔
2608
}
2609

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