QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
VehicleCameraControl.cc
Go to the documentation of this file.
2#include "QGCCameraIO.h"
3#include "AppMessages.h"
4#include "QGCFormat.h"
5#include "SettingsManager.h"
6#include "AppSettings.h"
7#include "VideoManager.h"
8#include "QGCCameraManager.h"
9#include "FTPManager.h"
10#include "QGCCompression.h"
11#include "QGCCorePlugin.h"
12#include "QGCFileHelper.h"
13#include "AppMessages.h"
14#include "QGCFormat.h"
15#include "Vehicle.h"
16#include "VehicleLinkManager.h"
17#include "LinkInterface.h"
18#include "MAVLinkProtocol.h"
19#include "QGCVideoStreamInfo.h"
20#include "MissionCommandTree.h"
21
22#include <QtNetwork/QNetworkAccessManager>
23#include <QtCore/QDir>
24
25#include <algorithm>
26#include <QtCore/QSettings>
27#include <QtXml/QDomDocument>
28#include <QtXml/QDomNodeList>
29#include <QtQml/QQmlEngine>
30#include <QtNetwork/QNetworkReply>
31
32#include "QGCNetworkHelper.h"
33#include "QGCLoggingCategory.h"
34
35QGC_LOGGING_CATEGORY(VehicleCameraControlLog, "Camera.VehicleCameraControl")
36QGC_LOGGING_CATEGORY(VehicleCameraControlVerboseLog, "Camera.VehicleCameraControl.Verbose")
37
38QGCCameraOptionExclusion::QGCCameraOptionExclusion(QObject* parent, QString param_, QString value_, QStringList exclusions_)
39 : QObject(parent)
40 , param(param_)
41 , value(value_)
42 , exclusions(exclusions_)
43{
44}
45
46QGCCameraOptionRange::QGCCameraOptionRange(QObject* parent, QString param_, QString value_, QString targetParam_, QString condition_, QStringList optNames_, QStringList optValues_)
47 : QObject(parent)
48 , param(param_)
49 , value(value_)
50 , targetParam(targetParam_)
51 , condition(condition_)
52 , optNames(optNames_)
53 , optValues(optValues_)
54{
55}
56
57static bool read_attribute(QDomNode& node, const char* tagName, bool& target)
58{
59 QDomNamedNodeMap attrs = node.attributes();
60 if(!attrs.count()) {
61 return false;
62 }
63 QDomNode subNode = attrs.namedItem(tagName);
64 if(subNode.isNull()) {
65 return false;
66 }
67 target = subNode.nodeValue() != "0";
68 return true;
69}
70
71static bool read_attribute(QDomNode& node, const char* tagName, int& target)
72{
73 QDomNamedNodeMap attrs = node.attributes();
74 if(!attrs.count()) {
75 return false;
76 }
77 QDomNode subNode = attrs.namedItem(tagName);
78 if(subNode.isNull()) {
79 return false;
80 }
81 target = subNode.nodeValue().toInt();
82 return true;
83}
84
85static bool read_attribute(QDomNode& node, const char* tagName, QString& target)
86{
87 QDomNamedNodeMap attrs = node.attributes();
88 if(!attrs.count()) {
89 return false;
90 }
91 QDomNode subNode = attrs.namedItem(tagName);
92 if(subNode.isNull()) {
93 return false;
94 }
95 target = subNode.nodeValue();
96 return true;
97}
98
99static bool read_value(QDomNode& element, const char* tagName, QString& target)
100{
101 QDomElement de = element.firstChildElement(tagName);
102 if(de.isNull()) {
103 return false;
104 }
105 target = de.text();
106 return true;
107}
108
109VehicleCameraControl::VehicleCameraControl(const mavlink_camera_information_t *info, Vehicle* vehicle, int compID, QObject* parent)
110 : MavlinkCameraControlInterface(vehicle, parent)
111 , _compID(compID)
112{
113 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
114
115 memcpy(&_mavlinkCameraInfo, info, sizeof(mavlink_camera_information_t));
116
117 _vendor = QString(reinterpret_cast<const char*>(info->vendor_name));
118 _modelName = QString(reinterpret_cast<const char*>(info->model_name));
119 _cacheFile = QString::asprintf("%s/%s_%s_%03d.xml",
120 SettingsManager::instance()->appSettings()->parameterSavePath().toStdString().c_str(),
121 _vendor.toStdString().c_str(),
122 _modelName.toStdString().c_str(),
123 static_cast<int>(_mavlinkCameraInfo.cam_definition_version));
124
126
127 if(info->cam_definition_uri[0] != 0) {
128 //-- Process camera definition file
129 _handleDefinitionFile(info->cam_definition_uri);
130 } else {
132 }
133
134 QSettings settings;
135 _photoCaptureMode = static_cast<PhotoCaptureMode>(settings.value(kPhotoMode, static_cast<int>(PHOTO_CAPTURE_SINGLE)).toInt());
136 _photoLapse = settings.value(kPhotoLapse, 1.0).toDouble();
137 _photoLapseCount = settings.value(kPhotoLapseCount, 0).toInt();
138 _thermalOpacity = settings.value(kThermalOpacity, 85.0).toDouble();
139 _thermalMode = static_cast<ThermalViewMode>(settings.value(kThermalMode, static_cast<uint32_t>(THERMAL_BLEND)).toUInt());
140
141 _videoRecordTimeUpdateTimer.setSingleShot(false);
142 _videoRecordTimeUpdateTimer.setInterval(333);
144
145 // Cameras are not required to broadcast CAMERA_SETTINGS when zoom/focus changes,
146 // so re-request it after an accepted zoom/focus command to keep zoom level and
147 // FOV current. The delay also coalesces bursts of zoom/focus commands into a
148 // single request.
149 constexpr int kCameraSettingsRefreshDelayMsecs = 1000;
150 _cameraSettingsRefreshTimer.setSingleShot(true);
151 _cameraSettingsRefreshTimer.setInterval(kCameraSettingsRefreshDelayMsecs);
152 connect(&_cameraSettingsRefreshTimer, &QTimer::timeout, this, [this]() {
153 _cameraSettingsRetries = 0; // Start a fresh request/retry cycle
155 });
156
157 //-- Tracking capabilities
158 _hasTrackingRectCapability = _mavlinkCameraInfo.flags & CAMERA_CAP_FLAGS_HAS_TRACKING_RECTANGLE;
159 _hasTrackingPointCapability = _mavlinkCameraInfo.flags & CAMERA_CAP_FLAGS_HAS_TRACKING_POINT;
160
161 qCDebug(VehicleCameraControlLog) << "Camera Info:";
162 qCDebug(VehicleCameraControlLog) << " vendor:" << vendor();
163 qCDebug(VehicleCameraControlLog) << " model:" << modelName();
164 qCDebug(VehicleCameraControlLog) << " version:" << version();
165 qCDebug(VehicleCameraControlLog) << " firmware:" << firmwareVersion();
166 qCDebug(VehicleCameraControlLog) << " focal length:" << focalLength();
167 qCDebug(VehicleCameraControlLog) << " sensor size:" << sensorSize();
168 qCDebug(VehicleCameraControlLog) << " resolution:" << resolution();
169 qCDebug(VehicleCameraControlLog) << " captures video:" << capturesVideo();
170 qCDebug(VehicleCameraControlLog) << " captures photos:" << capturesPhotos();
171 qCDebug(VehicleCameraControlLog) << " has modes:" << hasModes();
172 qCDebug(VehicleCameraControlLog) << " has zoom:" << hasZoom();
173 qCDebug(VehicleCameraControlLog) << " has focus:" << hasFocus();
174 qCDebug(VehicleCameraControlLog) << " has tracking:" << hasTracking();
175 qCDebug(VehicleCameraControlLog) << " has video stream:" << hasVideoStream();
176 qCDebug(VehicleCameraControlLog) << " photos in video mode:" << photosInVideoMode();
177 qCDebug(VehicleCameraControlLog) << " video in photo mode:" << videoInPhotoMode();
178}
179
181{
182 // Stop all timers to prevent them from firing during or after destruction
183 _captureStatusTimer.stop();
185 _streamInfoTimer.stop();
186 _streamStatusTimer.stop();
189 _storageInfoTimer.stop();
190
191 delete _netManager;
192 _netManager = nullptr;
193}
194
196{
197 qCDebug(VehicleCameraControlLog) << "_initWhenReady()";
198 if(isBasic()) {
199 qCDebug(VehicleCameraControlLog) << "Basic, MAVLink only messages, no parameters.";
200 //-- Basic cameras have no parameters
201 _paramComplete = true;
202 emit parametersReady();
203 } else {
205 }
206
207 QTimer::singleShot(500, this, &VehicleCameraControl::_requestCameraSettings);
208 connect(&_cameraSettingsTimer, &QTimer::timeout, this, &VehicleCameraControl::_cameraSettingsTimeout);
209
210 QTimer::singleShot(1000, this, &VehicleCameraControl::_checkForVideoStreams);
211
213
214 connect(&_captureStatusTimer, &QTimer::timeout, this, &VehicleCameraControl::_requestCaptureStatus);
215 _captureStatusTimer.setSingleShot(true);
216 _captureStatusTimer.start(1500);
217
218 connect(&_storageInfoTimer, &QTimer::timeout, this, &VehicleCameraControl::_storageInfoTimeout);
219 QTimer::singleShot(2000, this, &VehicleCameraControl::_requestStorageInfo);
220
228
229 emit infoChanged();
230
231 delete _netManager;
232 _netManager = nullptr;
233}
234
236{
237 // Even if the camera itself does not report video capture capability
238 // we can always save locally from a video stream, or use onboard recording
239 // if the camera reports video capture capability.
240 return _mavlinkCameraInfo.flags & (CAMERA_CAP_FLAGS_CAPTURE_VIDEO | CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM);
241}
242
244{
245 // If we have a video stream we can always screen grab from it,
246 //even if the camera itself does not report still capture capability.
247 return _mavlinkCameraInfo.flags & (CAMERA_CAP_FLAGS_CAPTURE_IMAGE | CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM);
248}
249
251{
254 } else if (_photoCaptureStatus() != PHOTO_CAPTURE_IDLE) {
257 // The ui is not set up to support recording video while in photo/survey mode, even if the camera technically supports it.
259 } else if (_mavlinkCameraInfo.flags & CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM || _mavlinkCameraInfo.flags & CAMERA_CAP_FLAGS_CAPTURE_VIDEO) {
261 }
262
264}
265
267{
272 } else if (_photoCaptureStatus() == PHOTO_CAPTURE_IDLE) {
273 // We can always do at least a screen grab from the video stream, even if camera doesn't report still capture capability
274 if (_mavlinkCameraInfo.flags & CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM || _mavlinkCameraInfo.flags & CAMERA_CAP_FLAGS_CAPTURE_IMAGE) {
276 }
277 }
278
280}
281
283{
284 if (_mavlinkCameraInfo.firmware_version == 0) {
285 return {};
286 }
287 int major = (_mavlinkCameraInfo.firmware_version) & 0xFF;
288 int minor = (_mavlinkCameraInfo.firmware_version >> 8) & 0xFF;
289 int patch = (_mavlinkCameraInfo.firmware_version >> 16) & 0xFF;
290 int dev = (_mavlinkCameraInfo.firmware_version >> 24) & 0xFF;
291 if (dev != 0) {
292 return QString::asprintf("%d.%d.%d.%d", major, minor, patch, dev);
293 }
294 return QString::asprintf("%d.%d.%d", major, minor, patch);
295}
296
298{
299 return QTime(0, 0).addMSecs(static_cast<int>(recordTime())).toString("hh:mm:ss");
300}
301
303{
304 return QGC::bigSizeMBToString(static_cast<quint64>(_storageFree));
305}
306
308{
309 if(_batteryRemaining >= 0) {
310 return QGC::numberToString(static_cast<quint64>(_batteryRemaining)) + " %";
311 }
312 return "";
313}
314
316{
317 if (_resetting) {
318 return;
319 }
320 if (!hasModes()) {
321 qCWarning(VehicleCameraControlLog) << "Camera does not support modes";
322 return;
323 }
324
325 qCDebug(VehicleCameraControlLog) << "Camera set to video mode";
327}
328
330{
331 if (_resetting) {
332 return;
333 }
334 if (!hasModes()) {
335 qCWarning(VehicleCameraControlLog) << "Camera does not support modes";
336 return;
337 }
338
339 qCDebug(VehicleCameraControlLog) << "Camera set to photo mode";
341}
342
344{
345 if (_resetting) {
346 return;
347 }
348 if (!hasModes()) {
349 qCWarning(VehicleCameraControlLog) << "Camera does not support modes";
350 return;
351 }
353 qCWarning(VehicleCameraControlLog) << "Invalid camera mode" << cameraMode;
354 return;
355 }
356 if (_cameraMode == cameraMode) {
357 return;
358 }
359
360 qCDebug(VehicleCameraControlLog) << "Camera mode set to" << cameraModeToStr(cameraMode);
361
362 //-- Does it have a mode parameter?
363 Fact* pMode = mode();
364 if(pMode) {
365 pMode->setRawValue(cameraMode);
367 } else {
368 //-- Use MAVLink Command
370 _compID, // Target component
371 MAV_CMD_SET_CAMERA_MODE, // Command id
372 true, // ShowError
373 0, // Reserved (Set to 0)
374 cameraMode); // Camera mode (0: photo, 1: video)
376 }
377}
378
380{
381 if(!_resetting) {
383 QSettings settings;
384 settings.setValue(kPhotoMode, static_cast<int>(mode));
386 }
387}
388
390{
391 _photoLapse = interval;
392 QSettings settings;
393 settings.setValue(kPhotoLapse, interval);
394 emit photoLapseChanged();
395}
396
398{
399 _photoLapseCount = count;
400 QSettings settings;
401 settings.setValue(kPhotoLapseCount, count);
403}
404
406{
407 if(_cameraMode != mode) {
409 emit cameraModeChanged();
410 //-- Update stream status
411 _streamStatusTimer.start(1000);
412 }
413}
414
425
427{
428 if(_resetting) {
429 return false;
430 }
431
433 return stopVideoRecording();
434 } else {
435 return startVideoRecording();
436 }
437
438 return false;
439}
440
442{
443 if (_resetting) {
444 return false;
445 }
447 qCWarning(VehicleCameraControlLog) << "Take photo denied - photo capture is disabled";
448 return false;
449 }
451 qCWarning(VehicleCameraControlLog) << "Take photo denied - already capturing";
452 return false;
453 }
454
455 qCDebug(VehicleCameraControlLog) << "takePhoto()";
456
457 const bool canUseMavlinkImageCapture =
458 (_mavlinkCameraInfo.flags & CAMERA_CAP_FLAGS_CAPTURE_IMAGE) &&
460
461 if (canUseMavlinkImageCapture) {
463 _compID,
464 MAV_CMD_IMAGE_START_CAPTURE,
465 true, // ShowError
466 0, // All cameras
467 static_cast<float>(_photoCaptureMode == PHOTO_CAPTURE_SINGLE ? 0 : _photoLapse), // Duration between two consecutive pictures (in seconds--ignored if single image)
468 _photoCaptureMode == PHOTO_CAPTURE_SINGLE ? 1 : _photoLapseCount); // Number of images to capture total - 0 for unlimited capture
471 return true;
472 } else {
476 QTimer::singleShot(500, this, [this]() {
478 });
479 return true;
480 } else {
481 QGC::showAppMessage(tr("Timelapse photo capture is not supported on cameras without still capture capability"));
482 }
483 }
484
485 return false;
486}
487
489{
490 if (_resetting) {
491 return false;
492 }
494 qCWarning(VehicleCameraControlLog) << "Stop taking photos requested - not currently capturing multiple photos";
495 return false;
496 }
497
498 qCDebug(VehicleCameraControlLog) << "Camera stop taking photos";
499
500 // Interval capture is only supported directly by cameras
502 _compID, // Target component
503 MAV_CMD_IMAGE_STOP_CAPTURE,
504 true, // ShowError
505 0); // All cameras
508
509 return true;
510}
511
513{
514 if (_resetting) {
515 return false;
516 }
518 qCWarning(VehicleCameraControlLog) << "Start video denied - already recording";
519 return false;
520 }
522 qCWarning(VehicleCameraControlLog) << "Start video denied - video capture is disabled";
523 return false;
524 }
525
526 bool useMavlinkCommand = _mavlinkCameraInfo.flags & CAMERA_CAP_FLAGS_CAPTURE_VIDEO;
527
528 qCDebug(VehicleCameraControlLog) << "Start video recording:" << (useMavlinkCommand ? "MAVLink command" : "VideoManager");
529
530 if (useMavlinkCommand) {
532 _compID, // Target component
533 MAV_CMD_VIDEO_START_CAPTURE,
534 true, // Show error on failure
535 0, // All streams
536 0, // CAMERA_CAPTURE_STATUS streaming frequency
537 0); // All cameras
538 } else {
540 }
541
542 return true;
543}
544
546{
547 if (_resetting) {
548 return false;
549 }
551 qCWarning(VehicleCameraControlLog) << "Stop video recording requested - already idle";
552 return true;
553 }
554
555 bool useMavlinkCommand = _mavlinkCameraInfo.flags & CAMERA_CAP_FLAGS_CAPTURE_VIDEO;
556
557 qCDebug(VehicleCameraControlLog) << "Camera stop video recording" << (useMavlinkCommand ? "MAVLink command" : "VideoManager");
558
559 if (useMavlinkCommand) {
561 _compID, // Target component
562 MAV_CMD_VIDEO_STOP_CAPTURE,
563 true, // Show error on failure
564 0, // All streams
565 0); // All cameras
566 } else {
568 }
569
570 return true;
571}
572
574{
575 QSettings settings;
576 settings.setValue(kThermalMode, static_cast<uint32_t>(mode));
578 emit thermalModeChanged();
579}
580
582{
583 if(val < 0.0) val = 0.0;
584 if(val > 100.0) val = 100.0;
585 if(fabs(_thermalOpacity - val) > 0.1) {
586 _thermalOpacity = val;
587 QSettings settings;
588 settings.setValue(kThermalOpacity, val);
590 }
591}
592
594{
595 qCDebug(VehicleCameraControlLog) << "Camera set zoom level to" << level;
596 if(hasZoom()) {
597 //-- Limit
598 level = std::min(std::max(level, 0.0), 100.0);
599 if(_vehicle) {
601 _compID, // Target component
602 MAV_CMD_SET_CAMERA_ZOOM, // Command id
603 false, // ShowError
604 ZOOM_TYPE_RANGE, // Zoom type
605 static_cast<float>(level)); // Level
606 }
607 }
608}
609
611{
612 qCDebug(VehicleCameraControlLog) << "Camera set focus level to" << level;
613 if(hasFocus()) {
614 //-- Limit
615 level = std::min(std::max(level, 0.0), 100.0);
616 if(_vehicle) {
618 _compID, // Target component
619 MAV_CMD_SET_CAMERA_FOCUS, // Command id
620 false, // ShowError
621 FOCUS_TYPE_RANGE, // Focus type
622 static_cast<float>(level)); // Level
623 }
624 }
625}
626
628{
629 if(!_resetting) {
630 qCDebug(VehicleCameraControlLog) << "resetSettings()";
631 _resetting = true;
633 _compID, // Target component
634 MAV_CMD_RESET_CAMERA_SETTINGS, // Command id
635 true, // ShowError
636 1); // Do Reset
637 }
638}
639
641{
642 if(!_resetting) {
643 qCDebug(VehicleCameraControlLog) << "formatCard()";
644 if(_vehicle) {
646 _compID, // Target component
647 MAV_CMD_STORAGE_FORMAT, // Command id
648 true, // ShowError
649 id, // Storage ID (1 for first, 2 for second, etc.)
650 1); // Do Format
651 }
652 }
653}
654
656{
657 qCDebug(VehicleCameraControlLog) << "Camera step zoom" << direction;
658 if(_vehicle && hasZoom()) {
660 _compID, // Target component
661 MAV_CMD_SET_CAMERA_ZOOM, // Command id
662 false, // ShowError
663 ZOOM_TYPE_STEP, // Zoom type
664 direction); // Direction (-1 wide, 1 tele)
665 }
666}
667
669{
670 qCDebug(VehicleCameraControlLog) << "Camera start zoom" << direction;
671 if(_vehicle && hasZoom()) {
673 _compID, // Target component
674 MAV_CMD_SET_CAMERA_ZOOM, // Command id
675 false, // ShowError
676 ZOOM_TYPE_CONTINUOUS, // Zoom type
677 direction); // Direction (-1 wide, 1 tele)
678 }
679}
680
682{
683 qCDebug(VehicleCameraControlLog) << "Camera stop zoom";
684 if(_vehicle && hasZoom()) {
686 _compID, // Target component
687 MAV_CMD_SET_CAMERA_ZOOM, // Command id
688 false, // ShowError
689 ZOOM_TYPE_CONTINUOUS, // Zoom type
690 0); // Direction (-1 wide, 1 tele)
691 }
692}
693
695{
696 qCDebug(VehicleCameraControlLog) << "Camera step focus" << direction;
697 if(_vehicle && hasFocus()) {
699 _compID, // Target component
700 MAV_CMD_SET_CAMERA_FOCUS, // Command id
701 false, // ShowError
702 FOCUS_TYPE_STEP, // Focus type
703 direction); // Direction (-1 in, 1 out)
704 }
705}
706
708{
709 qCDebug(VehicleCameraControlLog) << "Camera start focus" << direction;
710 if(_vehicle && hasFocus()) {
712 _compID, // Target component
713 MAV_CMD_SET_CAMERA_FOCUS, // Command id
714 false, // ShowError
715 FOCUS_TYPE_CONTINUOUS, // Focus type
716 direction); // Direction (-1 in, 1 out)
717 }
718}
719
721{
722 qCDebug(VehicleCameraControlLog) << "Camera stop focus";
723 if(_vehicle && hasFocus()) {
725 _compID, // Target component
726 MAV_CMD_SET_CAMERA_FOCUS, // Command id
727 false, // ShowError
728 FOCUS_TYPE_CONTINUOUS, // Focus type
729 0); // Direction (-1 in, 1 out)
730 }
731}
732
734{
735 qCDebug(VehicleCameraControlLog) << "Camera request capture status - retries:" << _cameraCaptureStatusRetries;
736
737 if(_cameraCaptureStatusRetries++ % 2 == 0) {
738 qCDebug(VehicleCameraControlLog) << " Sending REQUEST_MESSAGE:MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS";
740 _compID, // target component
741 MAV_CMD_REQUEST_MESSAGE, // command id
742 false, // showError
743 MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS); // msgid
744 } else {
745 qCDebug(VehicleCameraControlLog) << " Sending MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS (legacy)";
747 _compID, // target component
748 MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS, // command id
749 false, // showError
750 1); // Do Request
751 }
752}
753
755{
756 _updateActiveList();
757 _updateRanges(pFact);
758}
759
760void VehicleCameraControl::_mavCommandResult(int vehicleId, int component, int command, int result, int failureCode)
761{
762 Q_UNUSED(failureCode);
763
764 //-- Is this ours?
765 if (_vehicle->id() != vehicleId || compID() != component) {
766 return;
767 }
768 if (result == MAV_RESULT_IN_PROGRESS) {
769 //-- Do Nothing
770 qCDebug(VehicleCameraControlLog) << "In progress response for" << command;
771 } else if(result == MAV_RESULT_ACCEPTED) {
772 switch(command) {
773 case MAV_CMD_RESET_CAMERA_SETTINGS:
774 _resetting = false;
775 if(isBasic()) {
777 } else {
778 QTimer::singleShot(500, this, &VehicleCameraControl::_requestAllParameters);
779 QTimer::singleShot(2500, this, &VehicleCameraControl::_requestCameraSettings);
780 }
781 break;
782 case MAV_CMD_VIDEO_START_CAPTURE:
784 _captureStatusTimer.start(1000);
785 break;
786 case MAV_CMD_VIDEO_STOP_CAPTURE:
788 _captureStatusTimer.start(1000);
789 break;
790 case MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS:
792 break;
793 case MAV_CMD_REQUEST_STORAGE_INFORMATION:
795 break;
796 case MAV_CMD_IMAGE_START_CAPTURE:
797 _captureStatusTimer.start(1000);
798 break;
799 case MAV_CMD_SET_CAMERA_ZOOM:
800 case MAV_CMD_SET_CAMERA_FOCUS:
802 break;
803 }
804 } else {
805 QString commandStr = MissionCommandTree::instance()->rawName(static_cast<MAV_CMD>(command));
806 if ((result == MAV_RESULT_TEMPORARILY_REJECTED) || (result == MAV_RESULT_FAILED)) {
807 if (result == MAV_RESULT_TEMPORARILY_REJECTED) {
808 qCDebug(VehicleCameraControlLog) << "Command temporarily rejected (MAV_RESULT_TEMPORARILY_REJECTED) for" << commandStr;
809 } else {
810 qCDebug(VehicleCameraControlLog) << "Command failed (MAV_RESULT_FAILED) for" << commandStr;
811 }
812 switch(command) {
813 case MAV_CMD_RESET_CAMERA_SETTINGS:
814 _resetting = false;
815 qCDebug(VehicleCameraControlLog) << "Failed to reset camera settings";
816 break;
817 case MAV_CMD_IMAGE_START_CAPTURE:
818 case MAV_CMD_IMAGE_STOP_CAPTURE:
819 if(++_captureInfoRetries <= 5) {
820 _captureStatusTimer.start(1000);
821 } else {
822 qCDebug(VehicleCameraControlLog) << "Giving up start/stop image capture";
824 }
825 break;
826 case MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS:
827 if(++_cameraCaptureStatusRetries <= 5) {
828 _captureStatusTimer.start(1000);
829 } else {
830 qCDebug(VehicleCameraControlLog) << "Giving up requesting capture status";
831 }
832 break;
833 case MAV_CMD_REQUEST_STORAGE_INFORMATION:
834 if(++_storageInfoRetries <= 5) {
835 QTimer::singleShot(1000, this, &VehicleCameraControl::_requestStorageInfo);
836 } else {
837 qCDebug(VehicleCameraControlLog) << "Giving up requesting storage status";
838 }
839 break;
840 }
841 } else {
842 qCDebug(VehicleCameraControlLog) << "Bad response for" << commandStr << QGCMAVLink::mavResultToString(result);
843 }
844 }
845}
846
848{
849 if(_videoCaptureStatusValue != captureStatus) {
850 _videoCaptureStatusValue = captureStatus;
852 if(captureStatus == VIDEO_CAPTURE_STATUS_RUNNING) {
853 _recordTime = 0;
854 _recTime = QTime::currentTime();
856 } else {
858 _recordTime = 0;
859 emit recordTimeChanged();
860 }
861 }
862}
863
865{
866 _recordTime = static_cast<uint32_t>(_recTime.msecsTo(QTime::currentTime()));
867 emit recordTimeChanged();
868}
869
871{
872 // Only track time here when not using MAVLink video capture (to avoid double-tracking)
874 return;
875 }
876
877 if (recording) {
878 _recordTime = 0;
879 _recTime = QTime::currentTime();
881 } else {
883 _recordTime = 0;
884 emit recordTimeChanged();
885 }
886}
887
889{
890 if(_photoCaptureStatusValue != captureStatus) {
891 qCDebug(VehicleCameraControlLog) << "Set Photo Status:" << captureStatus;
892 _photoCaptureStatusValue = captureStatus;
894 }
895}
896
897bool VehicleCameraControl::_loadCameraDefinitionFile(QByteArray& bytes)
898{
899 QByteArray originalData(bytes);
900 //-- Handle localization
901 if(!_handleLocalization(bytes)) {
902 return false;
903 }
904
905 QDomDocument doc;
906 const QDomDocument::ParseResult result = doc.setContent(bytes, QDomDocument::ParseOption::Default);
907 if (!result) {
908 qCCritical(VehicleCameraControlLog) << "Unable to parse camera definition file on line:" << result.errorLine;
909 qCCritical(VehicleCameraControlLog) << result.errorMessage;
910 return false;
911 }
912 //-- Load camera constants
913 QDomNodeList defElements = doc.elementsByTagName(kDefnition);
914 if(!defElements.size() || !_loadConstants(defElements)) {
915 qCWarning(VehicleCameraControlLog) << "Unable to load camera constants from camera definition";
916 return false;
917 }
918 //-- Load camera parameters
919 QDomNodeList paramElements = doc.elementsByTagName(kParameters);
920 if(!paramElements.size()) {
921 qCDebug(VehicleCameraControlLog) << "No parameters to load from camera";
922 return false;
923 }
924 if(!_loadSettings(paramElements)) {
925 qCWarning(VehicleCameraControlLog) << "Unable to load camera parameters from camera definition";
926 return false;
927 }
928 //-- If this is new, cache it
929 if(!_cached) {
930 qCDebug(VehicleCameraControlLog) << "Saving camera definition file" << _cacheFile;
931 QFile file(_cacheFile);
932 if (!file.open(QIODevice::WriteOnly)) {
933 qWarning() << QString("Could not save cache file %1. Error: %2").arg(_cacheFile).arg(file.errorString());
934 } else {
935 file.write(originalData);
936 }
937 }
938 return true;
939}
940
941bool VehicleCameraControl::_loadConstants(const QDomNodeList nodeList)
942{
943 QDomNode node = nodeList.item(0);
944 if(!read_attribute(node, kVersion, _version)) {
945 return false;
946 }
947 if(!read_value(node, kModel, _modelName)) {
948 return false;
949 }
950 if(!read_value(node, kVendor, _vendor)) {
951 return false;
952 }
953 return true;
954}
955
956bool VehicleCameraControl::_loadSettings(const QDomNodeList nodeList)
957{
958 QDomNode node = nodeList.item(0);
959 QDomElement elem = node.toElement();
960 QDomNodeList parameters = elem.elementsByTagName(kParameter);
961 //-- Pre-process settings (maintain order and skip non-controls)
962 for(int i = 0; i < parameters.size(); i++) {
963 QDomNode parameterNode = parameters.item(i);
964 QString name;
965 if(read_attribute(parameterNode, kName, name)) {
966 bool control = true;
967 read_attribute(parameterNode, kControl, control);
968 if(control) {
969 _settings << name;
970 }
971 } else {
972 qCritical() << "Parameter entry missing parameter name";
973 return false;
974 }
975 }
976 //-- Load parameters
977 for(int i = 0; i < parameters.size(); i++) {
978 QDomNode parameterNode = parameters.item(i);
979 QString factName;
980 read_attribute(parameterNode, kName, factName);
981 QString type;
982 if(!read_attribute(parameterNode, kType, type)) {
983 qCritical() << QString("Parameter %1 missing parameter type").arg(factName);
984 return false;
985 }
986 //-- Does it have a control?
987 bool control = true;
988 read_attribute(parameterNode, kControl, control);
989 //-- Is it read only?
990 bool readOnly = false;
991 read_attribute(parameterNode, kReadOnly, readOnly);
992 //-- Is it write only?
993 bool writeOnly = false;
994 read_attribute(parameterNode, kWriteOnly, writeOnly);
995 //-- It can't be both
996 if(readOnly && writeOnly) {
997 qCritical() << QString("Parameter %1 cannot be both read only and write only").arg(factName);
998 }
999 //-- Param type
1000 bool unknownType;
1001 FactMetaData::ValueType_t factType = FactMetaData::stringToType(type, unknownType);
1002 if (unknownType) {
1003 qCritical() << QString("Unknown type for parameter %1").arg(factName);
1004 return false;
1005 }
1006 //-- By definition, custom types do not have control
1007 if(factType == FactMetaData::valueTypeCustom) {
1008 control = false;
1009 }
1010 //-- Description
1011 QString description;
1012 if(!read_value(parameterNode, kDescription, description)) {
1013 qCritical() << QString("Parameter %1 missing parameter description").arg(factName);
1014 return false;
1015 }
1016 //-- Check for updates
1017 QStringList updates = _loadUpdates(parameterNode);
1018 if(updates.size()) {
1019 qCDebug(VehicleCameraControlVerboseLog) << "Parameter" << factName << "requires updates for:" << updates;
1020 _requestUpdates[factName] = updates;
1021 }
1022 //-- Build metadata
1023 FactMetaData* metaData = new FactMetaData(factType, factName, this);
1024 QQmlEngine::setObjectOwnership(metaData, QQmlEngine::CppOwnership);
1025 metaData->setShortDescription(description);
1026 metaData->setLongDescription(description);
1027 metaData->setHasControl(control);
1028 metaData->setReadOnly(readOnly);
1029 metaData->setWriteOnly(writeOnly);
1030 //-- Options (enums)
1031 QDomElement optionElem = parameterNode.toElement();
1032 QDomNodeList optionsRoot = optionElem.elementsByTagName(kOptions);
1033 if(optionsRoot.size()) {
1034 //-- Iterate options
1035 QDomNode optionsNode = optionsRoot.item(0);
1036 QDomElement optionsElem = optionsNode.toElement();
1037 QDomNodeList options = optionsElem.elementsByTagName(kOption);
1038 for(int optionIndex = 0; optionIndex < options.size(); optionIndex++) {
1039 QDomNode option = options.item(optionIndex);
1040 QString optName;
1041 QString optValue;
1042 QVariant optVariant;
1043 if(!_loadNameValue(option, factName, metaData, optName, optValue, optVariant)) {
1044 delete metaData;
1045 return false;
1046 }
1047 metaData->addEnumInfo(optName, optVariant);
1048 _originalOptNames[factName] << optName;
1049 _originalOptValues[factName] << optVariant;
1050 //-- Check for exclusions
1051 QStringList exclusions = _loadExclusions(option);
1052 if(exclusions.size()) {
1053 qCDebug(VehicleCameraControlVerboseLog) << "New exclusions:" << factName << optValue << exclusions;
1054 QGCCameraOptionExclusion* pExc = new QGCCameraOptionExclusion(this, factName, optValue, exclusions);
1055 QQmlEngine::setObjectOwnership(pExc, QQmlEngine::CppOwnership);
1056 _valueExclusions.append(pExc);
1057 }
1058 //-- Check for range rules
1059 if(!_loadRanges(option, factName, optValue)) {
1060 delete metaData;
1061 return false;
1062 }
1063 }
1064 }
1065 QString defaultValue;
1066 if(read_attribute(parameterNode, kDefault, defaultValue)) {
1067 QVariant defaultVariant;
1068 QString errorString;
1069 if (metaData->convertAndValidateRaw(defaultValue, false, defaultVariant, errorString)) {
1070 metaData->setRawDefaultValue(defaultVariant);
1071 } else {
1072 qWarning() << "Invalid default value for" << factName
1073 << " type:" << metaData->type()
1074 << " value:" << defaultValue
1075 << " error:" << errorString;
1076 }
1077 }
1078 //-- Set metadata and Fact
1079 if (_nameToFactMetaDataMap.contains(factName)) {
1080 qWarning() << QStringLiteral("Duplicate fact name:") << factName;
1081 delete metaData;
1082 } else {
1083 {
1084 //-- Check for Min Value
1085 QString attr;
1086 if(read_attribute(parameterNode, kMin, attr)) {
1087 QVariant typedValue;
1088 QString errorString;
1089 if (metaData->convertAndValidateRaw(attr, true /* convertOnly */, typedValue, errorString)) {
1090 metaData->setRawMin(typedValue);
1091 } else {
1092 qWarning() << "Invalid min value for" << factName
1093 << " type:" << metaData->type()
1094 << " value:" << attr
1095 << " error:" << errorString;
1096 }
1097 }
1098 }
1099 {
1100 //-- Check for Max Value
1101 QString attr;
1102 if(read_attribute(parameterNode, kMax, attr)) {
1103 QVariant typedValue;
1104 QString errorString;
1105 if (metaData->convertAndValidateRaw(attr, true /* convertOnly */, typedValue, errorString)) {
1106 metaData->setRawMax(typedValue);
1107 } else {
1108 qWarning() << "Invalid max value for" << factName
1109 << " type:" << metaData->type()
1110 << " value:" << attr
1111 << " error:" << errorString;
1112 }
1113 }
1114 }
1115 {
1116 //-- Check for Step Value
1117 QString attr;
1118 if(read_attribute(parameterNode, kStep, attr)) {
1119 QVariant typedValue;
1120 QString errorString;
1121 if (metaData->convertAndValidateRaw(attr, true /* convertOnly */, typedValue, errorString)) {
1122 metaData->setRawIncrement(typedValue.toDouble());
1123 } else {
1124 qWarning() << "Invalid step value for" << factName
1125 << " type:" << metaData->type()
1126 << " value:" << attr
1127 << " error:" << errorString;
1128 }
1129 }
1130 }
1131 {
1132 //-- Check for Decimal Places
1133 QString attr;
1134 if(read_attribute(parameterNode, kDecimalPlaces, attr)) {
1135 QVariant typedValue;
1136 QString errorString;
1137 if (metaData->convertAndValidateRaw(attr, true /* convertOnly */, typedValue, errorString)) {
1138 metaData->setDecimalPlaces(typedValue.toInt());
1139 } else {
1140 qWarning() << "Invalid decimal places value for" << factName
1141 << " type:" << metaData->type()
1142 << " value:" << attr
1143 << " error:" << errorString;
1144 }
1145 }
1146 }
1147 {
1148 //-- Check for Units
1149 QString attr;
1150 if(read_attribute(parameterNode, kUnit, attr)) {
1151 metaData->setRawUnits(attr);
1152 }
1153 }
1154 qCDebug(VehicleCameraControlLog) << "New parameter:" << factName << (readOnly ? "ReadOnly" : "Writable") << (writeOnly ? "WriteOnly" : "Readable");
1155 _nameToFactMetaDataMap[factName] = metaData;
1156 Fact* pFact = new Fact(_compID, factName, factType, this);
1157 QQmlEngine::setObjectOwnership(pFact, QQmlEngine::CppOwnership);
1158 pFact->setMetaData(metaData);
1159 pFact->containerSetRawValue(metaData->rawDefaultValue());
1160 QGCCameraParamIO* pIO = new QGCCameraParamIO(this, pFact, _vehicle);
1161 QQmlEngine::setObjectOwnership(pIO, QQmlEngine::CppOwnership);
1162 _paramIO[factName] = pIO;
1163 _addFact(pFact, factName);
1164 }
1165 }
1166 if(_nameToFactMetaDataMap.size() > 0) {
1167 _addFactGroup(this, "camera");
1168 _processRanges();
1170 emit activeSettingsChanged();
1171 return true;
1172 }
1173 return false;
1174}
1175
1176bool VehicleCameraControl::_handleLocalization(QByteArray& bytes)
1177{
1178 QDomDocument doc;
1179 const QDomDocument::ParseResult result = doc.setContent(bytes, QDomDocument::ParseOption::Default);
1180 if (!result) {
1181 qCritical() << "Unable to parse camera definition file on line:" << result.errorLine;
1182 qCritical() << result.errorMessage;
1183 return false;
1184 }
1185 //-- Find out where we are
1186 QLocale locale = QLocale::system();
1187#if defined (Q_OS_MACOS)
1188 locale = QLocale(locale.name());
1189#endif
1190 QString localeName = locale.name().toLower().replace("-", "_");
1191 qCDebug(VehicleCameraControlLog) << "Current locale:" << localeName;
1192 if(localeName == "en_us") {
1193 // Nothing to do
1194 return true;
1195 }
1196 QDomNodeList locRoot = doc.elementsByTagName(kLocalization);
1197 if(!locRoot.size()) {
1198 // Nothing to do
1199 return true;
1200 }
1201 //-- Iterate locales
1202 QDomNode node = locRoot.item(0);
1203 QDomElement elem = node.toElement();
1204 QDomNodeList locales = elem.elementsByTagName(kLocale);
1205 for(int i = 0; i < locales.size(); i++) {
1206 QDomNode localeNode = locales.item(i);
1207 QString name;
1208 if(!read_attribute(localeNode, kName, name)) {
1209 qWarning() << "Localization entry is missing its name attribute";
1210 continue;
1211 }
1212 // If we found a direct match, deal with it now
1213 if(localeName == name.toLower().replace("-", "_")) {
1214 return _replaceLocaleStrings(localeNode, bytes);
1215 }
1216 }
1217 //-- No direct match. Pick first matching language (if any)
1218 localeName = localeName.left(3);
1219 for(int i = 0; i < locales.size(); i++) {
1220 QDomNode localeNode = locales.item(i);
1221 QString name;
1222 read_attribute(localeNode, kName, name);
1223 if(name.toLower().startsWith(localeName)) {
1224 return _replaceLocaleStrings(localeNode, bytes);
1225 }
1226 }
1227 //-- Could not find a language to use
1228 qWarning() << "No match for" << QLocale::system().name() << "in camera definition file";
1229 //-- Just use default, en_US
1230 return true;
1231}
1232
1233bool VehicleCameraControl::_replaceLocaleStrings(const QDomNode node, QByteArray& bytes)
1234{
1235 QDomElement stringElem = node.toElement();
1236 QDomNodeList strings = stringElem.elementsByTagName(kStrings);
1237 for(int i = 0; i < strings.size(); i++) {
1238 QDomNode stringNode = strings.item(i);
1239 QString original;
1240 QString translated;
1241 if(read_attribute(stringNode, kOriginal, original)) {
1242 if(read_attribute(stringNode, kTranslated, translated)) {
1243 QString o; o = "\"" + original + "\"";
1244 QString t; t = "\"" + translated + "\"";
1245 bytes.replace(o.toUtf8(), t.toUtf8());
1246 o = ">" + original + "<";
1247 t = ">" + translated + "<";
1248 bytes.replace(o.toUtf8(), t.toUtf8());
1249 }
1250 }
1251 }
1252 return true;
1253}
1254
1256{
1257 //-- Reset receive list
1258 for(const QString& paramName: _paramIO.keys()) {
1259 if(_paramIO[paramName]) {
1260 _paramIO[paramName]->setParamRequest();
1261 } else {
1262 qCritical() << "QGCParamIO is NULL" << paramName;
1263 }
1264 }
1266 if (sharedLink) {
1268 mavlink_msg_param_ext_request_list_pack_chan(
1269 static_cast<uint8_t>(MAVLinkProtocol::instance()->getSystemId()),
1270 static_cast<uint8_t>(MAVLinkProtocol::getComponentId()),
1271 sharedLink->mavlinkChannel(),
1272 &msg,
1273 static_cast<uint8_t>(_vehicle->id()),
1274 static_cast<uint8_t>(compID()));
1275 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
1276 }
1277 qCDebug(VehicleCameraControlVerboseLog) << "Request all parameters";
1278}
1279
1280QString VehicleCameraControl::_getParamName(const char* param_id)
1281{
1282 // This will null terminate the name string
1283 char parameterNameWithNull[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN + 1] = {};
1284 (void) strncpy(parameterNameWithNull, param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
1285 const QString parameterName(parameterNameWithNull);
1286 return parameterName;
1287}
1288
1289void VehicleCameraControl::handleParamExtAck(const mavlink_param_ext_ack_t& paramExtAck)
1290{
1291 QString paramName = _getParamName(paramExtAck.param_id);
1292 qCDebug(VehicleCameraControlLog).noquote() << "Received PARAM_EXT_ACK:"
1293 << "\n\tParam name:" << paramName
1294 << "\n\tResult:" << static_cast<int>(paramExtAck.param_result)
1295 << "\n\tType:" << static_cast<int>(paramExtAck.param_type);
1296
1297 if(!_paramIO.contains(paramName)) {
1298 qCWarning(VehicleCameraControlLog) << "Received PARAM_EXT_ACK for unknown param:" << paramName;
1299 return;
1300 }
1301 if(_paramIO[paramName]) {
1302 _paramIO[paramName]->handleParamAck(paramExtAck);
1303 } else {
1304 qCritical() << "QGCParamIO is NULL" << paramName;
1305 }
1306}
1307
1308void VehicleCameraControl::handleParamExtValue(const mavlink_param_ext_value_t& paramExtValue)
1309{
1310 QString paramName = _getParamName(paramExtValue.param_id);
1311 qCDebug(VehicleCameraControlLog).noquote() << "Received PARAM_EXT_VALUE:"
1312 << "\n\tParam name:" << paramName
1313 << "\n\tType:" << static_cast<int>(paramExtValue.param_type)
1314 << "\n\tIndex:" << static_cast<int>(paramExtValue.param_index)
1315 << "\n\tCount:" << static_cast<int>(paramExtValue.param_count);
1316
1317 if(!_paramIO.contains(paramName)) {
1318 qCWarning(VehicleCameraControlLog) << "Received PARAM_EXT_VALUE for unknown param:" << paramName;
1319 return;
1320 }
1321 if(_paramIO[paramName]) {
1322 _paramIO[paramName]->handleParamValue(paramExtValue);
1323 } else {
1324 qCritical() << "QGCParamIO is NULL" << paramName;
1325 }
1326}
1327
1328void VehicleCameraControl::_updateActiveList()
1329{
1330 //-- Clear out excluded parameters based on exclusion rules
1331 QStringList exclusionList;
1333 Fact* pFact = getFact(param->param);
1334 if(pFact) {
1335 QString option = pFact->rawValueString();
1336 if(param->value == option) {
1337 exclusionList << param->exclusions;
1338 }
1339 }
1340 }
1341 QStringList active;
1342 for(QString key: _settings) {
1343 if(!exclusionList.contains(key)) {
1344 active.append(key);
1345 }
1346 }
1347 if(active != _activeSettings) {
1348 qCDebug(VehicleCameraControlVerboseLog) << "Excluding" << exclusionList;
1349 _activeSettings = active;
1350 emit activeSettingsChanged();
1351 //-- Force validity of "Facts" based on active set
1352 if(_paramComplete) {
1353 emit parametersReady();
1354 }
1355 }
1356}
1357
1358bool VehicleCameraControl::_processConditionTest(const QString conditionTest)
1359{
1360 enum {
1361 TEST_NONE,
1362 TEST_EQUAL,
1363 TEST_NOT_EQUAL,
1364 TEST_GREATER,
1365 TEST_SMALLER
1366 };
1367 qCDebug(VehicleCameraControlVerboseLog) << "_processConditionTest(" << conditionTest << ")";
1368 int op = TEST_NONE;
1369 QStringList test;
1370
1371 auto split = [&conditionTest](const QString& sep ) {
1372 return conditionTest.split(sep, Qt::SkipEmptyParts);
1373 };
1374
1375 if(conditionTest.contains("!=")) {
1376 test = split("!=");
1377 op = TEST_NOT_EQUAL;
1378 } else if(conditionTest.contains("=")) {
1379 test = split("=");
1380 op = TEST_EQUAL;
1381 } else if(conditionTest.contains(">")) {
1382 test = split(">");
1383 op = TEST_GREATER;
1384 } else if(conditionTest.contains("<")) {
1385 test = split("<");
1386 op = TEST_SMALLER;
1387 }
1388 if(test.size() == 2) {
1389 Fact* pFact = getFact(test[0]);
1390 if(pFact) {
1391 switch(op) {
1392 case TEST_EQUAL:
1393 return pFact->rawValueString() == test[1];
1394 case TEST_NOT_EQUAL:
1395 return pFact->rawValueString() != test[1];
1396 case TEST_GREATER:
1397 return pFact->rawValueString() > test[1];
1398 case TEST_SMALLER:
1399 return pFact->rawValueString() < test[1];
1400 case TEST_NONE:
1401 break;
1402 }
1403 } else {
1404 qWarning() << "Invalid condition parameter:" << test[0] << "in" << conditionTest;
1405 return false;
1406 }
1407 }
1408 qWarning() << "Invalid condition" << conditionTest;
1409 return false;
1410}
1411
1412bool VehicleCameraControl::_processCondition(const QString condition)
1413{
1414 qCDebug(VehicleCameraControlVerboseLog) << "_processCondition(" << condition << ")";
1415 bool result = true;
1416 bool andOp = true;
1417 if(!condition.isEmpty()) {
1418 QStringList scond = condition.split(" ", Qt::SkipEmptyParts);
1419 while(scond.size()) {
1420 QString test = scond.first();
1421 scond.removeFirst();
1422 if(andOp) {
1423 result = result && _processConditionTest(test);
1424 } else {
1425 result = result || _processConditionTest(test);
1426 }
1427 if(!scond.size()) {
1428 return result;
1429 }
1430 andOp = scond.first().toUpper() == "AND";
1431 scond.removeFirst();
1432 }
1433 }
1434 return result;
1435}
1436
1437void VehicleCameraControl::_updateRanges(Fact* pFact)
1438{
1439 QMap<Fact*, QGCCameraOptionRange*> rangesSet;
1440 QMap<Fact*, QString> rangesReset;
1441 QStringList changedList;
1442 QStringList resetList;
1443 QStringList updates;
1444 //-- Iterate range sets looking for limited ranges
1445 for(QGCCameraOptionRange* pRange: _optionRanges) {
1446 //-- If this fact or one of its conditions is part of this range set
1447 if(!changedList.contains(pRange->targetParam) && (pRange->param == pFact->name() || pRange->condition.contains(pFact->name()))) {
1448 Fact* pRFact = getFact(pRange->param); //-- This parameter
1449 Fact* pTFact = getFact(pRange->targetParam); //-- The target parameter (the one its range is to change)
1450 if(pRFact && pTFact) {
1451 //qCDebug(VehicleCameraControlVerboseLog) << "Check new set of options for" << pTFact->name();
1452 QString option = pRFact->rawValueString(); //-- This parameter value
1453 //-- If this value (and condition) triggers a change in the target range
1454 //qCDebug(VehicleCameraControlVerboseLog) << "Range value:" << pRange->value << "Current value:" << option << "Condition:" << pRange->condition;
1455 if(pRange->value == option && _processCondition(pRange->condition)) {
1456 if(pTFact->enumStrings() != pRange->optNames) {
1457 //-- Set limited range set
1458 rangesSet[pTFact] = pRange;
1459 }
1460 changedList << pRange->targetParam;
1461 }
1462 }
1463 }
1464 }
1465 //-- Iterate range sets again looking for resets
1466 for(QGCCameraOptionRange* pRange: _optionRanges) {
1467 if(!changedList.contains(pRange->targetParam) && (pRange->param == pFact->name() || pRange->condition.contains(pFact->name()))) {
1468 Fact* pTFact = getFact(pRange->targetParam); //-- The target parameter (the one its range is to change)
1469 if(!resetList.contains(pRange->targetParam)) {
1470 if(pTFact->enumStrings() != _originalOptNames[pRange->targetParam]) {
1471 //-- Restore full option set
1472 rangesReset[pTFact] = pRange->targetParam;
1473 }
1474 resetList << pRange->targetParam;
1475 }
1476 }
1477 }
1478 //-- Update limited range set
1479 for (Fact* f: rangesSet.keys()) {
1480 f->setEnumInfo(rangesSet[f]->optNames, rangesSet[f]->optVariants);
1481 if(!updates.contains(f->name())) {
1482 emit f->enumsChanged();
1483 qCDebug(VehicleCameraControlVerboseLog) << "Limited set of options for:" << f->name() << rangesSet[f]->optNames;;
1484 updates << f->name();
1485 }
1486 }
1487 //-- Restore full range set
1488 for (Fact* f: rangesReset.keys()) {
1489 f->setEnumInfo(_originalOptNames[rangesReset[f]], _originalOptValues[rangesReset[f]]);
1490 if(!updates.contains(f->name())) {
1491 emit f->enumsChanged();
1492 qCDebug(VehicleCameraControlVerboseLog) << "Restore full set of options for:" << f->name() << _originalOptNames[f->name()];
1493 updates << f->name();
1494 }
1495 }
1496 //-- Parameter update requests
1497 if(_requestUpdates.contains(pFact->name())) {
1498 for(const QString& param: _requestUpdates[pFact->name()]) {
1499 if(!_updatesToRequest.contains(param)) {
1500 _updatesToRequest << param;
1501 }
1502 }
1503 }
1504 if(_updatesToRequest.size()) {
1505 QTimer::singleShot(500, this, &VehicleCameraControl::_requestParamUpdates);
1506 }
1507}
1508
1510{
1511 for(const QString& param: _updatesToRequest) {
1512 _paramIO[param]->paramRequest();
1513 }
1514 _updatesToRequest.clear();
1515}
1516
1518{
1519 qCDebug(VehicleCameraControlLog) << "_requestCameraSettings() - retries:" << _cameraSettingsRetries << "timer active:" << _cameraSettingsTimer.isActive();
1520 if(_vehicle) {
1521 // Use REQUEST_MESSAGE instead of deprecated REQUEST_CAMERA_SETTINGS
1522 // first time and every other time after that.
1523
1524 if(_cameraSettingsRetries % 2 == 0) {
1525 qCDebug(VehicleCameraControlLog) << " Sending REQUEST_MESSAGE:MAVLINK_MSG_ID_CAMERA_SETTINGS";
1527 _compID, // target component
1528 MAV_CMD_REQUEST_MESSAGE, // command id
1529 false, // showError
1530 MAVLINK_MSG_ID_CAMERA_SETTINGS); // msgid
1531 } else {
1532 qCDebug(VehicleCameraControlLog) << " Sending MAV_CMD_REQUEST_CAMERA_SETTINGS (legacy)";
1534 _compID, // Target component
1535 MAV_CMD_REQUEST_CAMERA_SETTINGS, // command id
1536 false, // showError
1537 1); // Do Request
1538 }
1539 if(_cameraSettingsTimer.isActive()) {
1540 qCDebug(VehicleCameraControlLog) << "_requestCameraSettings() - RESTARTING already active timer";
1541 } else {
1542 qCDebug(VehicleCameraControlLog) << "_requestCameraSettings() - starting timer";
1543 }
1544 _cameraSettingsTimer.start(1000); // Wait up to a second for it
1545 }
1546
1547}
1548
1550{
1551 qCDebug(VehicleCameraControlLog) << "_requestStorageInfo() - retries:" << _storageInfoRetries << "timer active:" << _storageInfoTimer.isActive();
1552 if(_vehicle) {
1553 // Use REQUEST_MESSAGE instead of deprecated REQUEST_STORAGE_INFORMATION
1554 // first time and every other time after that.
1555 if(_storageInfoRetries % 2 == 0) {
1556 qCDebug(VehicleCameraControlLog) << " Sending REQUEST_MESSAGE:MAVLINK_MSG_ID_STORAGE_INFORMATION";
1558 _compID, // target component
1559 MAV_CMD_REQUEST_MESSAGE, // command id
1560 false, // showError
1561 MAVLINK_MSG_ID_STORAGE_INFORMATION, // msgid
1562 0); // storage ID
1563 } else {
1564 qCDebug(VehicleCameraControlLog) << " Sending MAV_CMD_REQUEST_STORAGE_INFORMATION (legacy)";
1566 _compID, // Target component
1567 MAV_CMD_REQUEST_STORAGE_INFORMATION, // command id
1568 false, // showError
1569 0, // Storage ID (0 for all, 1 for first, 2 for second, etc.)
1570 1); // Do Request
1571 }
1572 qCDebug(VehicleCameraControlLog) << "_requestStorageInfo() - starting timer";
1573 _storageInfoTimer.start(1000); // Wait up to a second for it
1574 }
1575}
1576
1577void VehicleCameraControl::handleCameraSettings(const mavlink_camera_settings_t& settings)
1578{
1579 qCDebug(VehicleCameraControlLog).noquote() << "Received CAMERA_SETTINGS - stopping timer, resetting retries:"
1580 << "\n\tMode:" << settings.mode_id
1581 << "\n\tZoom level:" << settings.zoomLevel
1582 << "\n\tFocus level:" << settings.focusLevel;
1583
1584 _cameraSettingsTimer.stop();
1586
1587 _setCameraMode(static_cast<CameraMode>(settings.mode_id));
1588 qreal z = static_cast<qreal>(settings.zoomLevel);
1589 qreal f = static_cast<qreal>(settings.focusLevel);
1590 if(std::isfinite(z) && z != _zoomLevel) {
1591 _zoomLevel = z;
1592 emit zoomLevelChanged();
1593 }
1594 if(std::isfinite(f) && f != _focusLevel) {
1595 _focusLevel = f;
1596 emit focusLevelChanged();
1597 }
1598}
1599
1600void VehicleCameraControl::handleStorageInformation(const mavlink_storage_information_t& storageInformation)
1601{
1602 qCDebug(VehicleCameraControlLog) << "Received STORAGE_INFORMATION - stopping timer, resetting retries:"
1603 << "\n\tStorage id:" << storageInformation.storage_id
1604 << "\n\tStorage count:" << storageInformation.storage_count
1605 << "\n\tStatus:"<< storageStatusToStr(storageInformation.status)
1606 << "\n\tTotal capacity:" << storageInformation.total_capacity
1607 << "\n\tUsed capacity:" << storageInformation.used_capacity
1608 << "\n\tAvailable capacity:" << storageInformation.available_capacity;
1609
1610 _storageInfoTimer.stop();
1612
1613 if(storageInformation.status == STORAGE_STATUS_READY) {
1614 uint32_t t = static_cast<uint32_t>(storageInformation.total_capacity);
1615 if(_storageTotal != t) {
1616 _storageTotal = t;
1617 emit storageTotalChanged();
1618 }
1619 uint32_t a = static_cast<uint32_t>(storageInformation.available_capacity);
1620 if(_storageFree != a) {
1621 _storageFree = a;
1622 emit storageFreeChanged();
1623 }
1624 }
1625 if(_storageStatus != static_cast<StorageStatus>(storageInformation.status)) {
1626 _storageStatus = static_cast<StorageStatus>(storageInformation.status);
1627 emit storageStatusChanged();
1628 }
1629}
1630
1631void VehicleCameraControl::handleBatteryStatus(const mavlink_battery_status_t& bs)
1632{
1633 qCDebug(VehicleCameraControlLog).noquote() << "Received BATTERY_STATUS:"
1634 << "\n\tBattery remaining (%):" << bs.battery_remaining;
1635
1636 if(bs.battery_remaining >= 0 && _batteryRemaining != static_cast<int>(bs.battery_remaining)) {
1637 _batteryRemaining = static_cast<int>(bs.battery_remaining);
1639 }
1640}
1641
1642void VehicleCameraControl::handleCameraCaptureStatus(const mavlink_camera_capture_status_t& cameraCaptureStatus)
1643{
1644 qCDebug(VehicleCameraControlLog).noquote() << "Received CAMERA_CAPTURE_STATUS - stopping timer, resetting retries:"
1645 << "\n\tImage status:" << captureImageStatusToStr(cameraCaptureStatus.image_status)
1646 << "\n\tVideo status:" << captureVideoStatusToStr(cameraCaptureStatus.video_status)
1647 << "\n\tInterval:" << cameraCaptureStatus.image_interval
1648 << "\n\tRecording time (ms):" << cameraCaptureStatus.recording_time_ms
1649 << "\n\tCapacity:" << cameraCaptureStatus.available_capacity;
1650
1651 _captureStatusTimer.stop();
1653
1654 //-- Disk Free Space
1655 uint32_t a = static_cast<uint32_t>(cameraCaptureStatus.available_capacity);
1656 if(_storageFree != a) {
1657 _storageFree = a;
1658 emit storageFreeChanged();
1659 }
1660 //-- Do we have recording time?
1661 if(cameraCaptureStatus.recording_time_ms) {
1662 // Resync our _recTime timer to the time info received from the camera component
1663 _recordTime = cameraCaptureStatus.recording_time_ms;
1664 _recTime = _recTime.addMSecs(_recTime.msecsTo(QTime::currentTime()) - static_cast<int>(cameraCaptureStatus.recording_time_ms));
1665 emit recordTimeChanged();
1666 }
1667 //-- Video/Image Capture Status
1668 uint8_t vs = cameraCaptureStatus.video_status < static_cast<uint8_t>(VIDEO_CAPTURE_STATUS_LAST) ? cameraCaptureStatus.video_status : static_cast<uint8_t>(VIDEO_CAPTURE_STATUS_UNDEFINED);
1669 uint8_t ps = cameraCaptureStatus.image_status < static_cast<uint8_t>(PHOTO_CAPTURE_LAST) ? cameraCaptureStatus.image_status : static_cast<uint8_t>(PHOTO_CAPTURE_STATUS_UNDEFINED);
1672 //-- Keep asking for it once in a while when recording
1674 _captureStatusTimer.start(5000);
1675 //-- Same while (single) image capture is busy
1677 _captureStatusTimer.start(1000);
1678 }
1679 //-- Time Lapse
1681 //-- Capture local image as well
1682 const QString photoDir = SettingsManager::instance()->appSettings()->savePath()->rawValue().toString() + QStringLiteral("/Photo");
1684 const QString photoPath = photoDir + "/" + QDateTime::currentDateTime().toString("yyyy-MM-dd_hh.mm.ss.zzz") + ".jpg";
1685 VideoManager::instance()->grabImage(photoPath);
1686 }
1687}
1688
1689void VehicleCameraControl::handleVideoStreamInformation(const mavlink_video_stream_information_t& videoStreamInformation)
1690{
1691 qCDebug(VehicleCameraControlLog).noquote() << "Received VIDEO_STREAM_INFORMATION:"
1692 << "\n\tStream ID:" << videoStreamInformation.stream_id
1693 << "\n\tStream count:" << videoStreamInformation.count
1694 << "\n\tType:" << static_cast<int>(videoStreamInformation.type)
1695 << "\n\tFlags:" << Qt::hex << Qt::showbase << videoStreamInformation.flags << Qt::dec << Qt::noshowbase
1696 << "\n\tBitrate (bits/s):" << videoStreamInformation.bitrate
1697 << "\n\tFramerate (fps):" << videoStreamInformation.framerate
1698 << "\n\tResolution:" << videoStreamInformation.resolution_h << "x" << videoStreamInformation.resolution_v
1699 << "\n\tRotation (deg):" << videoStreamInformation.rotation
1700 << "\n\tHFOV (deg):" << videoStreamInformation.hfov
1701 << "\n\tURI:" << videoStreamInformation.uri;
1702
1703 _expectedCount = videoStreamInformation.count;
1704 if(!_findStream(videoStreamInformation.stream_id, false)) {
1705 qCDebug(VehicleCameraControlLog) << "Create stream handler for stream ID:" << videoStreamInformation.stream_id;
1706 QGCVideoStreamInfo* pStream = new QGCVideoStreamInfo(videoStreamInformation, this);
1707 QQmlEngine::setObjectOwnership(pStream, QQmlEngine::CppOwnership);
1708 _streams.append(pStream);
1709 //-- Thermal is handled separately and not listed
1710 if(!pStream->isThermal()) {
1711 _streamLabels.append(pStream->name());
1712 emit streamsChanged();
1713 emit streamLabelsChanged();
1714 } else {
1715 emit thermalStreamChanged();
1716 }
1717 }
1718 //-- Check for missing count
1719 if(_streams.count() < _expectedCount) {
1720 _streamInfoTimer.start(1000);
1721 } else if (_streamInfoTimer.isActive()) {
1722 //-- Done
1723 qCDebug(VehicleCameraControlLog) << "All stream handlers done";
1724 _streamInfoTimer.stop();
1726 emit autoStreamChanged();
1728 }
1729}
1730
1731void VehicleCameraControl::handleVideoStreamStatus(const mavlink_video_stream_status_t& videoStreamStatus)
1732{
1733 qCDebug(VehicleCameraControlLog) << "Received VIDEO_STREAM_STATUS - stopping timer, resetting retries:"
1734 << "\n\tStream ID:" << videoStreamStatus.stream_id
1735 << "\n\tFlags:" << Qt::hex << Qt::showbase << videoStreamStatus.flags << Qt::dec << Qt::noshowbase
1736 << "\n\tBitrate (bits/s):" << videoStreamStatus.bitrate
1737 << "\n\tFramerate (fps):" << videoStreamStatus.framerate
1738 << "\n\tResolution: " << videoStreamStatus.resolution_h << "x" << videoStreamStatus.resolution_v
1739 << "\n\tRotation (deg):" << videoStreamStatus.rotation
1740 << "\n\tHFOV (deg):" << videoStreamStatus.hfov;
1741
1742 _streamStatusTimer.stop();
1744
1745 QGCVideoStreamInfo* pInfo = _findStream(videoStreamStatus.stream_id);
1746 if(pInfo) {
1747 pInfo->update(videoStreamStatus);
1748 }
1749}
1750
1751void VehicleCameraControl::handleTrackingImageStatus(const mavlink_camera_tracking_image_status_t& trackingImageStatus)
1752{
1753 qCDebug(VehicleCameraControlLog).noquote() << "Received CAMERA_TRACKING_IMAGE_STATUS:"
1754 << "\n\tTracking status:" << static_cast<int>(trackingImageStatus.tracking_status)
1755 << "\n\tTracking mode:" << static_cast<int>(trackingImageStatus.tracking_mode)
1756 << "\n\tPoint:" << trackingImageStatus.point_x << "," << trackingImageStatus.point_y
1757 << "\n\tRectangle:" << trackingImageStatus.rec_top_x << "," << trackingImageStatus.rec_top_y
1758 << " -> " << trackingImageStatus.rec_bottom_x << "," << trackingImageStatus.rec_bottom_y
1759 << "\n\tRadius:" << trackingImageStatus.radius;
1760
1761 _trackingImageStatus = trackingImageStatus;
1762
1763 const bool active = ((_trackingImageStatus.tracking_status & CAMERA_TRACKING_STATUS_FLAGS_ACTIVE) != 0) && trackingEnabled();
1764 const bool isPoint = active && (_trackingImageStatus.tracking_mode == CAMERA_TRACKING_MODE_POINT);
1765
1766 if (!active) {
1767 qCDebug(VehicleCameraControlLog) << "Tracking off";
1768 _trackingImageRect = {};
1771 } else if (isPoint) {
1772 const QPointF point(std::clamp(static_cast<qreal>(_trackingImageStatus.point_x), 0.0, 1.0),
1773 std::clamp(static_cast<qreal>(_trackingImageStatus.point_y), 0.0, 1.0));
1774 qreal radius = static_cast<qreal>(_trackingImageStatus.radius);
1775 if (qIsNaN(radius) || radius <= 0) {
1776 radius = 0.05;
1777 } else {
1778 radius = std::clamp(radius, 0.0, 1.0);
1779 }
1780 qCDebug(VehicleCameraControlLog) << "Tracking Point [" << point << "] radius:" << radius;
1781 _trackingImageRect = {};
1782 if (_trackingImagePoint != point) {
1783 _trackingImagePoint = point;
1785 }
1786 if (!qFuzzyCompare(_trackingImageRadius, radius)) {
1787 _trackingImageRadius = radius;
1789 }
1790 } else {
1791 // Rectangle tracking
1792 const QRectF rect = QRectF(QPointF(std::clamp(static_cast<qreal>(_trackingImageStatus.rec_top_x), 0.0, 1.0),
1793 std::clamp(static_cast<qreal>(_trackingImageStatus.rec_top_y), 0.0, 1.0)),
1794 QPointF(std::clamp(static_cast<qreal>(_trackingImageStatus.rec_bottom_x), 0.0, 1.0),
1795 std::clamp(static_cast<qreal>(_trackingImageStatus.rec_bottom_y), 0.0, 1.0))).normalized();
1796 qCDebug(VehicleCameraControlLog) << "Tracking Rect [" << rect << "]";
1799 if (_trackingImageRect != rect) {
1800 _trackingImageRect = rect;
1802 }
1803 }
1804
1805 if (_trackingImageIsActive != active) {
1806 _trackingImageIsActive = active;
1808 }
1809 if (_trackingImageIsPoint != isPoint) {
1810 _trackingImageIsPoint = isPoint;
1812 }
1813}
1814
1816{
1817 if (stream != _currentStream && stream >= 0 && stream < _streamLabels.count()) {
1819 if(pInfo) {
1820 qCDebug(VehicleCameraControlLog) << "Stopping stream:" << pInfo->uri();
1821 //-- Stop current stream
1823 _compID, // Target component
1824 MAV_CMD_VIDEO_STOP_STREAMING, // Command id
1825 false, // ShowError
1826 pInfo->streamID()); // Stream ID
1827 }
1828 _currentStream = stream;
1829 pInfo = currentStreamInstance();
1830 if(pInfo) {
1831 //-- Start new stream
1832 qCDebug(VehicleCameraControlLog) << "Starting stream:" << pInfo->uri();
1834 _compID, // Target component
1835 MAV_CMD_VIDEO_START_STREAMING, // Command id
1836 false, // ShowError
1837 pInfo->streamID()); // Stream ID
1838 //-- Update stream status
1839 _requestStreamStatus(static_cast<uint8_t>(pInfo->streamID()));
1840 }
1841 emit currentStreamChanged();
1843 }
1844}
1845
1847{
1849 if(pInfo) {
1850 //-- Stop current stream
1852 _compID, // Target component
1853 MAV_CMD_VIDEO_STOP_STREAMING, // Command id
1854 false, // ShowError
1855 pInfo->streamID()); // Stream ID
1856 }
1857}
1858
1860{
1862 if(pInfo) {
1863 //-- Start new stream
1865 _compID, // Target component
1866 MAV_CMD_VIDEO_START_STREAMING, // Command id
1867 false, // ShowError
1868 pInfo->streamID()); // Stream ID
1869 }
1870}
1871
1873{
1874 if(hasVideoStream()) {
1875 return _streams.count() > 0;
1876 }
1877 return false;
1878}
1879
1882{
1883 if(_currentStream < _streamLabels.count() && _streamLabels.count()) {
1885 return pStream;
1886 }
1887 return nullptr;
1888}
1889
1892{
1893 //-- For now, it will return the first thermal listed (if any)
1894 for(int i = 0; i < _streams.count(); i++) {
1895 if(_streams[i]) {
1896 QGCVideoStreamInfo* pStream = qobject_cast<QGCVideoStreamInfo*>(_streams[i]);
1897 if(pStream) {
1898 if(pStream->isThermal()) {
1899 return pStream;
1900 }
1901 }
1902 }
1903 }
1904 return nullptr;
1905}
1906
1908{
1909 qCDebug(VehicleCameraControlLog) << "_requestStreamInfo() - stream:" << streamID << "retries:" << _videoStreamInfoRetries;
1910 // By default, try to use new REQUEST_MESSAGE command instead of
1911 // deprecated MAV_CMD_REQUEST_VIDEO_STREAM_INFORMATION.
1912 if (_videoStreamInfoRetries % 2 == 0) {
1913 qCDebug(VehicleCameraControlLog) << " Sending REQUEST_MESSAGE:MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION";
1915 _compID, // target component
1916 MAV_CMD_REQUEST_MESSAGE, // command id
1917 false, // showError
1918 MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION, // msgid
1919 streamID); // stream ID
1920 } else {
1921 qCDebug(VehicleCameraControlLog) << " Sending MAV_CMD_REQUEST_VIDEO_STREAM_INFORMATION (legacy)";
1923 _compID, // Target component
1924 MAV_CMD_REQUEST_VIDEO_STREAM_INFORMATION, // Command id
1925 false, // ShowError
1926 streamID); // Stream ID
1927 }
1928 _streamInfoTimer.start(1000); // Wait up to a second for it
1929}
1930
1932{
1933 qCDebug(VehicleCameraControlLog) << "_requestStreamStatus() - stream:" << streamID << "retries:" << _videoStreamStatusRetries;
1934 // By default, try to use new REQUEST_MESSAGE command instead of
1935 // deprecated MAV_CMD_REQUEST_VIDEO_STREAM_STATUS.
1936 if (_videoStreamStatusRetries % 2 == 0) {
1937 qCDebug(VehicleCameraControlLog) << " Sending REQUEST_MESSAGE:MAVLINK_MSG_ID_VIDEO_STREAM_STATUS";
1939 _compID, // target component
1940 MAV_CMD_REQUEST_MESSAGE, // command id
1941 false, // showError
1942 MAVLINK_MSG_ID_VIDEO_STREAM_STATUS, // msgid
1943 streamID); // stream id
1944 } else {
1945 qCDebug(VehicleCameraControlLog) << " Sending MAV_CMD_REQUEST_VIDEO_STREAM_STATUS (legacy)";
1947 _compID, // Target component
1948 MAV_CMD_REQUEST_VIDEO_STREAM_STATUS, // Command id
1949 false, // ShowError
1950 streamID); // Stream ID
1951 }
1952 _streamStatusTimer.start(1000); // Wait up to a second for it
1953}
1954
1957{
1958 for(int i = 0; i < _streams.count(); i++) {
1959 if(_streams[i]) {
1960 QGCVideoStreamInfo* pStream = qobject_cast<QGCVideoStreamInfo*>(_streams[i]);
1961 if(pStream) {
1962 if(pStream->streamID() == id) {
1963 return pStream;
1964 }
1965 } else {
1966 qCritical() << "Null QGCVideoStreamInfo instance";
1967 }
1968 }
1969 }
1970 if(report) {
1971 qWarning() << "Stream id not found:" << id;
1972 }
1973 return nullptr;
1974}
1975
1978{
1979 for(int i = 0; i < _streams.count(); i++) {
1980 if(_streams[i]) {
1981 QGCVideoStreamInfo* pStream = qobject_cast<QGCVideoStreamInfo*>(_streams[i]);
1982 if(pStream) {
1983 if(pStream->name() == name) {
1984 return pStream;
1985 }
1986 }
1987 }
1988 }
1989 return nullptr;
1990}
1991
1993{
1995 int count = _expectedCount * 6;
1996 if(_videoStreamInfoRetries > count) {
1997 qCWarning(VehicleCameraControlLog) << "Giving up requesting video stream info";
1998 _streamInfoTimer.stop();
1999 //-- If we have at least one stream, work with what we have.
2000 if(_streams.count()) {
2001 emit autoStreamChanged();
2003 }
2004 return;
2005 }
2006 for(uint8_t i = 0; i < _expectedCount; i++) {
2007 //-- Stream ID starts at 1
2008 if(!_findStream(i+1, false)) {
2009 _requestStreamInfo(i+1);
2010 return;
2011 }
2012 }
2013}
2014
2016{
2019 qCWarning(VehicleCameraControlLog) << "Giving up requesting video stream status";
2020 _streamStatusTimer.stop();
2021 return;
2022 }
2024 if(pStream) {
2025 _requestStreamStatus(static_cast<uint8_t>(pStream->streamID()));
2026 }
2027}
2028
2030{
2032 qCDebug(VehicleCameraControlLog) << "_cameraSettingsTimeout() - retries now:" << _cameraSettingsRetries;
2033 if(_cameraSettingsRetries > 5) {
2034 qCWarning(VehicleCameraControlLog) << "Giving up requesting camera settings after" << _cameraSettingsRetries << "retries";
2035 _cameraSettingsTimer.stop();
2036 return;
2037 }
2038 qCDebug(VehicleCameraControlLog) << "_cameraSettingsTimeout() - calling _requestCameraSettings()";
2040}
2041
2043{
2045 qCDebug(VehicleCameraControlLog) << "_storageInfoTimeout() - retries now:" << _storageInfoRetries;
2046 if(_storageInfoRetries > 5) {
2047 qCWarning(VehicleCameraControlLog) << "Giving up requesting storage info after" << _storageInfoRetries << "retries";
2048 _storageInfoTimer.stop();
2049 return;
2050 }
2051 qCDebug(VehicleCameraControlLog) << "_storageInfoTimeout() - calling _requestStorageInfo()";
2053}
2054
2055QStringList
2056VehicleCameraControl::_loadExclusions(QDomNode option)
2057{
2058 QStringList exclusionList;
2059 QDomElement optionElem = option.toElement();
2060 QDomNodeList excRoot = optionElem.elementsByTagName(kExclusions);
2061 if(excRoot.size()) {
2062 //-- Iterate exclusions
2063 QDomNode node = excRoot.item(0);
2064 QDomElement elem = node.toElement();
2065 QDomNodeList exclusions = elem.elementsByTagName(kExclusion);
2066 for(int i = 0; i < exclusions.size(); i++) {
2067 QString exclude = exclusions.item(i).toElement().text();
2068 if(!exclude.isEmpty()) {
2069 exclusionList << exclude;
2070 }
2071 }
2072 }
2073 return exclusionList;
2074}
2075
2076QStringList
2077VehicleCameraControl::_loadUpdates(QDomNode option)
2078{
2079 QStringList updateList;
2080 QDomElement optionElem = option.toElement();
2081 QDomNodeList updateRoot = optionElem.elementsByTagName(kUpdates);
2082 if(updateRoot.size()) {
2083 //-- Iterate updates
2084 QDomNode node = updateRoot.item(0);
2085 QDomElement elem = node.toElement();
2086 QDomNodeList updates = elem.elementsByTagName(kUpdate);
2087 for(int i = 0; i < updates.size(); i++) {
2088 QString update = updates.item(i).toElement().text();
2089 if(!update.isEmpty()) {
2090 updateList << update;
2091 }
2092 }
2093 }
2094 return updateList;
2095}
2096
2097bool VehicleCameraControl::_loadRanges(QDomNode option, const QString factName, QString paramValue)
2098{
2099 QDomElement optionElem = option.toElement();
2100 QDomNodeList rangeRoot = optionElem.elementsByTagName(kParameterranges);
2101 if(rangeRoot.size()) {
2102 QDomNode node = rangeRoot.item(0);
2103 QDomElement elem = node.toElement();
2104 QDomNodeList parameterRanges = elem.elementsByTagName(kParameterrange);
2105 //-- Iterate parameter ranges
2106 for(int i = 0; i < parameterRanges.size(); i++) {
2107 QString param;
2108 QString condition;
2109 QDomNode paramRange = parameterRanges.item(i);
2110 if(!read_attribute(paramRange, kParameter, param)) {
2111 qCritical() << QString("Malformed option range for parameter %1").arg(factName);
2112 return false;
2113 }
2114 read_attribute(paramRange, kCondition, condition);
2115 QDomElement pelem = paramRange.toElement();
2116 QDomNodeList rangeOptions = pelem.elementsByTagName(kRoption);
2117 QStringList optNames;
2118 QStringList optValues;
2119 //-- Iterate options
2120 for(int rangeOptionIndex = 0; rangeOptionIndex < rangeOptions.size(); rangeOptionIndex++) {
2121 QString optName;
2122 QString optValue;
2123 QDomNode roption = rangeOptions.item(rangeOptionIndex);
2124 if(!read_attribute(roption, kName, optName)) {
2125 qCritical() << QString("Malformed roption for parameter %1").arg(factName);
2126 return false;
2127 }
2128 if(!read_attribute(roption, kValue, optValue)) {
2129 qCritical() << QString("Malformed rvalue for parameter %1").arg(factName);
2130 return false;
2131 }
2132 optNames << optName;
2133 optValues << optValue;
2134 }
2135 if(optNames.size()) {
2136 QGCCameraOptionRange* pRange = new QGCCameraOptionRange(this, factName, paramValue, param, condition, optNames, optValues);
2137 _optionRanges.append(pRange);
2138 qCDebug(VehicleCameraControlVerboseLog) << "New range limit:" << factName << paramValue << param << condition << optNames << optValues;
2139 }
2140 }
2141 }
2142 return true;
2143}
2144
2145void VehicleCameraControl::_processRanges()
2146{
2147 //-- After all parameter are loaded, process parameter ranges
2148 for(QGCCameraOptionRange* pRange: _optionRanges) {
2149 Fact* pRFact = getFact(pRange->targetParam);
2150 if(pRFact) {
2151 for(int i = 0; i < pRange->optNames.size(); i++) {
2152 QVariant optVariant;
2153 QString errorString;
2154 if (!pRFact->metaData()->convertAndValidateRaw(pRange->optValues[i], false, optVariant, errorString)) {
2155 qWarning() << "Invalid roption value, name:" << pRange->targetParam
2156 << " type:" << pRFact->metaData()->type()
2157 << " value:" << pRange->optValues[i]
2158 << " error:" << errorString;
2159 } else {
2160 pRange->optVariants << optVariant;
2161 }
2162 }
2163 }
2164 }
2165}
2166
2167bool VehicleCameraControl::_loadNameValue(QDomNode option, const QString factName, FactMetaData* metaData, QString& optName, QString& optValue, QVariant& optVariant)
2168{
2169 if(!read_attribute(option, kName, optName)) {
2170 qCritical() << QString("Malformed option for parameter %1").arg(factName);
2171 return false;
2172 }
2173 if(!read_attribute(option, kValue, optValue)) {
2174 qCritical() << QString("Malformed value for parameter %1").arg(factName);
2175 return false;
2176 }
2177 QString errorString;
2178 if (!metaData->convertAndValidateRaw(optValue, false, optVariant, errorString)) {
2179 qWarning() << "Invalid option value, name:" << factName
2180 << " type:" << metaData->type()
2181 << " value:" << optValue
2182 << " error:" << errorString;
2183 }
2184 return true;
2185}
2186
2187void VehicleCameraControl::_handleDefinitionFile(const QString &url)
2188{
2189 //-- First check and see if we have it cached
2190 QFile xmlFile(_cacheFile);
2191
2192 QString ftpPrefix(QStringLiteral("%1://").arg(FTPManager::mavlinkFTPScheme));
2193 if (!xmlFile.exists() && url.startsWith(ftpPrefix, Qt::CaseInsensitive)) {
2194 qCDebug(VehicleCameraControlLog) << "No camera definition file cached, attempt ftp download";
2195 int ver = static_cast<int>(_mavlinkCameraInfo.cam_definition_version);
2196 QString ext = "";
2197 if (url.endsWith(".lzma", Qt::CaseInsensitive)) { ext = ".lzma"; }
2198 if (url.endsWith(".xz", Qt::CaseInsensitive)) { ext = ".xz"; }
2199 QString fileName = QString::asprintf("%s_%s_%03d.xml%s",
2200 _vendor.toStdString().c_str(),
2201 _modelName.toStdString().c_str(),
2202 ver,
2203 ext.toStdString().c_str());
2204 connect(_vehicle->ftpManager(), &FTPManager::downloadComplete, this, &VehicleCameraControl::_ftpDownloadComplete);
2206 SettingsManager::instance()->appSettings()->parameterSavePath().toStdString().c_str(),
2207 fileName);
2208 return;
2209 }
2210
2211 if (!xmlFile.exists()) {
2212 qCDebug(VehicleCameraControlLog) << "No camera definition file cached, attempt http download";
2213 _httpRequest(url);
2214 return;
2215 }
2216 if (!xmlFile.open(QIODevice::ReadOnly)) {
2217 qWarning() << "Could not read cached camera definition file:" << _cacheFile;
2218 _httpRequest(url);
2219 return;
2220 }
2221 QByteArray bytes = xmlFile.readAll();
2222 QDomDocument doc;
2223 const QDomDocument::ParseResult result = doc.setContent(bytes, QDomDocument::ParseOption::Default);
2224 if (!result) {
2225 qWarning() << "Could not parse cached camera definition file:" << _cacheFile;
2226 _httpRequest(url);
2227 return;
2228 }
2229 //-- We have it
2230 qCDebug(VehicleCameraControlLog) << "Using cached camera definition file:" << _cacheFile;
2231 _cached = true;
2232 emit dataReady(bytes);
2233}
2234
2235void VehicleCameraControl::_httpRequest(const QString &url)
2236{
2237 qCDebug(VehicleCameraControlLog) << "Request camera definition:" << url;
2238 if(!_netManager) {
2239 _netManager = new QNetworkAccessManager(this);
2240 }
2242 QNetworkRequest request(QUrl::fromUserInput(url));
2243 request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, true);
2244 QSslConfiguration conf = request.sslConfiguration();
2245 conf.setPeerVerifyMode(QSslSocket::VerifyNone);
2246 request.setSslConfiguration(conf);
2247 QNetworkReply* reply = _netManager->get(request);
2248 connect(reply, &QNetworkReply::finished, this, &VehicleCameraControl::_downloadFinished);
2249}
2250
2252{
2253 QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
2254 if(!reply) {
2255 return;
2256 }
2257 int err = reply->error();
2258 int http_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
2259 QByteArray data = reply->readAll();
2260 if(err == QNetworkReply::NoError && http_code == 200) {
2261 data.append("\n");
2262 } else {
2263 data.clear();
2264 qWarning() << QString("Camera Definition (%1) download error: %2 status: %3").arg(
2265 reply->url().toDisplayString(),
2266 reply->errorString(),
2267 reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString()
2268 );
2269 }
2270 emit dataReady(data);
2271 //reply->deleteLater();
2272}
2273
2274void VehicleCameraControl::_ftpDownloadComplete(const QString& fileName, const QString& errorMsg)
2275{
2276 qCDebug(VehicleCameraControlLog) << "FTP Download completed: " << fileName << ", " << errorMsg;
2277
2278 disconnect(_vehicle->ftpManager(), &FTPManager::downloadComplete, this, &VehicleCameraControl::_ftpDownloadComplete);
2279
2280 QString outputFileName = QGCCompression::decompressIfNeeded(fileName);
2281 if (outputFileName.isEmpty()) {
2282 qCWarning(VehicleCameraControlLog) << "Inflate of compressed xml failed" << fileName;
2283 }
2284
2285 QFile xmlFile(outputFileName);
2286
2287 if (!xmlFile.exists()) {
2288 qCDebug(VehicleCameraControlLog) << "No camera definition file present after ftp download completed";
2289 return;
2290 }
2291 if (!xmlFile.open(QIODevice::ReadOnly)) {
2292 qWarning() << "Could not read downloaded camera definition file: " << fileName;
2293 return;
2294 }
2295
2296 _cached = true;
2297 QByteArray bytes = xmlFile.readAll();
2298 emit dataReady(bytes);
2299}
2300
2302{
2303 if(data.size()) {
2304 qCDebug(VehicleCameraControlLog) << "Parsing camera definition";
2305 _loadCameraDefinitionFile(data);
2306 } else {
2307 qCDebug(VehicleCameraControlLog) << "No camera definition received, trying to search on our own...";
2308 QFile definitionFile;
2309 if(QGCCorePlugin::instance()->getOfflineCameraDefinitionFile(_modelName, definitionFile)) {
2310 qCDebug(VehicleCameraControlLog) << "Found offline definition file for: " << _modelName << ", loading: " << definitionFile.fileName();
2311 if (definitionFile.open(QIODevice::ReadOnly)) {
2312 QByteArray newData = definitionFile.readAll();
2313 _loadCameraDefinitionFile(newData);
2314 } else {
2315 qCDebug(VehicleCameraControlLog) << "error opening offline definition file for: " << _modelName;
2316 }
2317 } else {
2318 qCDebug(VehicleCameraControlLog) << "No offline camera definition file found";
2319 }
2320 }
2322}
2323
2325{
2326 for(const QString& param: _paramIO.keys()) {
2327 if(!_paramIO[param]->paramDone()) {
2328 return;
2329 }
2330 }
2331 //-- All parameters loaded (or timed out)
2332 _paramComplete = true;
2333 emit parametersReady();
2334}
2335
2337{
2338 if(_mavlinkCameraInfo.flags & CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM) {
2339 connect(&_streamInfoTimer, &QTimer::timeout, this, &VehicleCameraControl::_streamInfoTimeout);
2340 _streamInfoTimer.setSingleShot(false);
2341 connect(&_streamStatusTimer, &QTimer::timeout, this, &VehicleCameraControl::_streamStatusTimeout);
2342 _streamStatusTimer.setSingleShot(true);
2343 //-- Request all streams
2345 _streamInfoTimer.start(2000);
2346 }
2347}
2348
2349bool VehicleCameraControl::incomingParameter(Fact* pFact, QVariant& newValue)
2350{
2351 Q_UNUSED(pFact);
2352 Q_UNUSED(newValue);
2353 return true;
2354}
2355
2356bool VehicleCameraControl::validateParameter(Fact* pFact, QVariant& newValue)
2357{
2358 Q_UNUSED(pFact);
2359 Q_UNUSED(newValue);
2360 return true;
2361}
2362
2363QStringList
2365{
2366 qCDebug(VehicleCameraControlLog) << "Active:" << _activeSettings;
2367 return _activeSettings;
2368}
2369
2370Fact*
2375
2376Fact*
2378{
2379 return (_paramComplete && _activeSettings.contains(kCAM_EV)) ? getFact(kCAM_EV) : nullptr;
2380}
2381
2382Fact*
2384{
2385 return (_paramComplete && _activeSettings.contains(kCAM_ISO)) ? getFact(kCAM_ISO) : nullptr;
2386}
2387
2388Fact*
2393
2394Fact*
2399
2400Fact*
2402{
2403 return (_paramComplete && _activeSettings.contains(kCAM_WBMODE)) ? getFact(kCAM_WBMODE) : nullptr;
2404}
2405
2406Fact*
2411
2413{
2414 if (_trackingEnabled == set) {
2415 return;
2416 }
2417 _trackingEnabled = set;
2418 if (!set) {
2419 stopTracking();
2420 }
2422}
2423
2425{
2427 qCCritical(VehicleCameraControlLog) << "startTrackingRect called but camera does not have rectangle tracking capability";
2428 return;
2429 }
2430
2431 qCDebug(VehicleCameraControlLog) << "Start Tracking (Rectangle: ["
2432 << static_cast<float>(rec.x()) << ", "
2433 << static_cast<float>(rec.y()) << "] - ["
2434 << static_cast<float>(rec.x() + rec.width()) << ", "
2435 << static_cast<float>(rec.y() + rec.height()) << "]";
2436
2438 MAV_CMD_CAMERA_TRACK_RECTANGLE,
2439 true,
2440 static_cast<float>(rec.x()),
2441 static_cast<float>(rec.y()),
2442 static_cast<float>(rec.x() + rec.width()),
2443 static_cast<float>(rec.y() + rec.height()));
2444
2446}
2447
2448void VehicleCameraControl::startTrackingPoint(QPointF point, double radius)
2449{
2451 qCCritical(VehicleCameraControlLog) << "startTrackingPoint called but camera does not have point tracking capability";
2452 return;
2453 }
2454
2455 qCDebug(VehicleCameraControlLog) << "Start Tracking (Point: ["
2456 << static_cast<float>(point.x()) << ", "
2457 << static_cast<float>(point.y()) << "], Radius: "
2458 << static_cast<float>(radius);
2459
2461 MAV_CMD_CAMERA_TRACK_POINT,
2462 true,
2463 static_cast<float>(point.x()),
2464 static_cast<float>(point.y()),
2465 static_cast<float>(radius));
2466
2468}
2469
2471{
2472 qCDebug(VehicleCameraControlLog) << "Stop Tracking";
2473
2474 //-- Stop Tracking
2476 MAV_CMD_CAMERA_STOP_TRACKING,
2477 true);
2478
2479 //-- Stop Sending Tracking Status
2481 MAV_CMD_SET_MESSAGE_INTERVAL,
2482 true,
2483 MAVLINK_MSG_ID_CAMERA_TRACKING_IMAGE_STATUS,
2484 -1);
2485
2486 // reset tracking state
2487 _trackingImageRect = {};
2491 _trackingImageIsActive = false;
2493 }
2495 _trackingImageIsPoint = false;
2497 }
2498}
2499
2501{
2503 MAV_CMD_SET_MESSAGE_INTERVAL,
2504 true,
2505 MAVLINK_MSG_ID_CAMERA_TRACKING_IMAGE_STATUS,
2506 500000); // Interval (us)
2507}
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
QString errorString
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
struct __mavlink_camera_information_t mavlink_camera_information_t
static bool read_value(QDomNode &element, const char *tagName, QString &target)
static bool read_attribute(QDomNode &node, const char *tagName, bool &target)
static constexpr const char * mavlinkFTPScheme
Definition FTPManager.h:77
void downloadComplete(const QString &file, const QString &errorMsg)
bool download(uint8_t fromCompId, const QString &fromURI, const QString &toDir, const QString &fileName="", bool checksize=true)
Definition FTPManager.cc:30
QMap< QString, FactMetaData * > _nameToFactMetaDataMap
Definition FactGroup.h:71
Q_INVOKABLE Fact * getFact(const QString &name) const
Definition FactGroup.cc:72
void _addFactGroup(FactGroup *factGroup, const QString &name)
Definition FactGroup.cc:133
Q_INVOKABLE bool factExists(const QString &name) const
@ return true: if the fact exists in the group
Definition FactGroup.cc:49
void _addFact(Fact *fact, const QString &name)
Definition FactGroup.cc:116
Holds the meta data associated with a Fact.
void setRawUnits(const QString &rawUnits)
void setDecimalPlaces(int decimalPlaces)
void setHasControl(bool bValue)
void setShortDescription(const QString &shortDescription)
void setLongDescription(const QString &longDescription)
void setRawMin(const QVariant &rawMin)
bool convertAndValidateRaw(const QVariant &rawValue, bool convertOnly, QVariant &typedValue, QString &errorString) const
void setWriteOnly(bool bValue)
void setRawDefaultValue(const QVariant &rawDefaultValue)
void setRawMax(const QVariant &rawMax)
void setRawIncrement(double increment)
void addEnumInfo(const QString &name, const QVariant &value)
Used to add new values to the enum lists after the meta data has been loaded.
QVariant rawDefaultValue() const
ValueType_t type() const
static ValueType_t stringToType(const QString &typeString, bool &unknownType)
void setReadOnly(bool bValue)
A Fact is used to hold a single value within the system.
Definition Fact.h:17
void setMetaData(FactMetaData *metaData, bool setDefaultFromMetaData=false)
Definition Fact.cc:741
void containerSetRawValue(const QVariant &value)
Value coming from Vehicle. This does NOT send a _containerRawValueChanged signal.
Definition Fact.cc:198
FactMetaData * metaData()
Definition Fact.h:177
void setRawValue(const QVariant &value)
Definition Fact.cc:134
QString rawValueString() const
Definition Fact.cc:483
QStringList enumStrings() const
Definition Fact.cc:293
QString name() const
Definition Fact.h:127
static int getComponentId()
static MAVLinkProtocol * instance()
Abstract base class for all camera controls: real and simulated.
PhotoCaptureStatus _photoCaptureStatus() const
virtual PhotoCaptureMode photoCaptureMode() const
QString captureImageStatusToStr(uint8_t image_status)
VideoCaptureStatus _videoCaptureStatus() const
QString captureVideoStatusToStr(uint8_t video_status)
void dataReady(const QByteArray &data)
static MissionCommandTree * instance()
QString rawName(MAV_CMD command) const
Returns the raw name for the specified command.
Camera option exclusions.
Camera option ranges.
QGCCameraOptionRange(QObject *parent, QString param_, QString value_, QString targetParam_, QString condition_, QStringList optNames_, QStringList optValues_)
Camera parameter handler.
Definition QGCCameraIO.h:14
static QGCCorePlugin * instance()
Encapsulates the contents of a VIDEO_STREAM_INFORMATION message.
quint8 streamID() const
bool update(const mavlink_video_stream_status_t &info)
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
int count() const override final
static SettingsManager * instance()
AppSettings * appSettings() const
MAVLink Camera API controller - connected to a real mavlink v2 camera.
void handleParamExtValue(const mavlink_param_ext_value_t &paramExtValue) override
static constexpr const char * kCAM_ISO
void setPhotoLapse(qreal interval) override
static constexpr const char * kRoption
QSize resolution() const override
bool incomingParameter(Fact *pFact, QVariant &newValue) override
Allow controller to modify or invalidate incoming parameter.
QString storageFreeStr() const override
bool capturesPhotos() const override
Q_INVOKABLE void startZoom(int direction) override
static constexpr const char * kName
void setTrackingEnabled(bool set) override
QStringList activeSettings() const override
QList< QGCCameraOptionRange * > _optionRanges
static constexpr const char * kType
static constexpr const char * kModel
void setCameraMode(CameraMode cameraMode) override
Q_INVOKABLE void startTrackingRect(QRectF rec) override
Q_INVOKABLE void startTrackingPoint(QPointF point, double radius) override
bool validateParameter(Fact *pFact, QVariant &newValue) override
Allow controller to modify or invalidate parameter change.
mavlink_camera_tracking_image_status_t _trackingImageStatus
QGCVideoStreamInfo * thermalStreamInstance() override
static constexpr const char * kDescription
QString recordTimeStr() const override
Q_INVOKABLE void formatCard(int id=1) override
bool hasVideoStream() const override
virtual void _onVideoManagerRecordingChanged(bool recording)
int compID() const override
static constexpr const char * kUpdate
void setPhotoCaptureMode(PhotoCaptureMode mode) override
QmlObjectListModel _streams
CapturePhotosState capturePhotosState() const override
Q_INVOKABLE void toggleCameraMode() override
void setFocusLevel(qreal level) override
Q_INVOKABLE void stepFocus(int direction) override
Q_INVOKABLE void stepZoom(int direction) override
static constexpr const char * kDecimalPlaces
static constexpr const char * kOptions
bool hasTracking() const override
Q_INVOKABLE bool toggleVideoRecording() override
virtual void _dataReady(QByteArray data)
Q_INVOKABLE bool takePhoto() override
static constexpr const char * kControl
static constexpr const char * kVersion
void setZoomLevel(qreal level) override
void handleVideoStreamInformation(const mavlink_video_stream_information_t &videoStreamInformation) override
QString vendor() const override
bool capturesVideo() const override
static constexpr const char * kWriteOnly
QString batteryRemainingStr() const override
VehicleCameraControl(const mavlink_camera_information_t *info, Vehicle *vehicle, int compID, QObject *parent=nullptr)
static constexpr const char * kCAM_EXPMODE
virtual void _cameraSettingsTimeout()
static constexpr const char * kPhotoLapse
void handleCameraCaptureStatus(const mavlink_camera_capture_status_t &cameraCaptureStatus) override
static constexpr const char * kCAM_APERTURE
static constexpr const char * kTranslated
Q_INVOKABLE void startFocus(int direction) override
bool autoStream() const override
static constexpr const char * kParameterranges
void setThermalOpacity(double val) override
void handleBatteryStatus(const mavlink_battery_status_t &bs) override
static constexpr const char * kUnit
Q_INVOKABLE void stopFocus() override
static constexpr const char * kDefnition
static constexpr const char * kVendor
static constexpr const char * kOriginal
void setPhotoLapseCount(int count) override
QList< QGCCameraOptionExclusion * > _valueExclusions
QSizeF sensorSize() const override
Q_INVOKABLE void setCameraModePhoto() override
static constexpr const char * kCAM_SHUTTERSPD
virtual void _requestTrackingStatus()
static constexpr const char * kDefault
static constexpr const char * kMin
qreal focalLength() const override
virtual void _setCameraMode(CameraMode mode)
virtual void _requestStreamInfo(uint8_t streamID)
Q_INVOKABLE bool stopVideoRecording() override
void factChanged(Fact *pFact) override
Notify controller a parameter has changed.
void handleVideoStreamStatus(const mavlink_video_stream_status_t &videoStreamStatus) override
QMap< QString, QGCCameraParamIO * > _paramIO
static constexpr const char * kPhotoLapseCount
static constexpr const char * kExclusions
static constexpr const char * kLocale
int version() const override
static constexpr const char * kCondition
static constexpr const char * kThermalMode
bool hasFocus() const override
Q_INVOKABLE void stopStream() override
static constexpr const char * kMax
QString firmwareVersion() const override
virtual void _mavCommandResult(int vehicleId, int component, int command, int result, int failureCode)
static constexpr const char * kStep
Q_INVOKABLE void resetSettings() override
virtual void _requestStreamStatus(uint8_t streamID)
Q_INVOKABLE bool startVideoRecording() override
Q_INVOKABLE void setCameraModeVideo() override
void handleParamExtAck(const mavlink_param_ext_ack_t &paramExtAck) override
static constexpr const char * kOption
void handleTrackingImageStatus(const mavlink_camera_tracking_image_status_t &trackingImageStatus) override
static constexpr const char * kExclusion
void setThermalMode(ThermalViewMode mode) override
static constexpr const char * kStrings
QMap< QString, QVariantList > _originalOptValues
QMap< QString, QStringList > _originalOptNames
static constexpr const char * kPhotoMode
static constexpr const char * kReadOnly
virtual void _requestCaptureStatus()
void handleStorageInformation(const mavlink_storage_information_t &storageInformation) override
bool hasModes() const override
bool trackingEnabled() const override
static constexpr const char * kParameter
static constexpr const char * kValue
virtual QGCVideoStreamInfo * _findStream(uint8_t streamID, bool report=true)
CaptureVideoState captureVideoState() const override
static constexpr const char * kUpdates
void handleCameraSettings(const mavlink_camera_settings_t &settings) override
QMap< QString, QStringList > _requestUpdates
Q_INVOKABLE void stopTracking() override
static constexpr const char * kParameterrange
static constexpr const char * kParameters
Q_INVOKABLE void resumeStream() override
bool photosInVideoMode() const override
quint32 recordTime() const override
virtual void _requestCameraSettings()
static constexpr const char * kCAM_EV
bool isBasic() const override
bool hasZoom() const override
Q_INVOKABLE bool stopTakePhoto() override
void setCurrentStream(int stream) override
QNetworkAccessManager * _netManager
bool videoInPhotoMode() const override
Q_INVOKABLE void stopZoom() override
static constexpr const char * kLocalization
QString modelName() const override
virtual void _setPhotoCaptureStatus(PhotoCaptureStatus captureStatus)
static constexpr const char * kCAM_WBMODE
QGCVideoStreamInfo * currentStreamInstance() override
virtual void _setVideoCaptureStatus(VideoCaptureStatus captureStatus)
mavlink_camera_information_t _mavlinkCameraInfo
static constexpr const char * kCAM_MODE
static constexpr const char * kThermalOpacity
WeakLinkInterfacePtr primaryLink() const
QGCCameraManager * cameraManager()
Definition Vehicle.h:1298
void sendMavCommand(int compId, MAV_CMD command, bool showError, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
Definition Vehicle.cc:2140
VehicleLinkManager * vehicleLinkManager()
Definition Vehicle.h:579
int id() const
Definition Vehicle.h:429
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
Definition Vehicle.cc:1386
FTPManager * ftpManager()
Definition Vehicle.h:580
void mavCommandResult(int vehicleId, int targetComponent, int command, int ackResult, int failureCode)
Q_INVOKABLE void stopRecording()
Q_INVOKABLE void startRecording(const QString &videoFile=QString())
static VideoManager * instance()
void recordingChanged(bool recording)
Q_INVOKABLE void grabImage(const QString &imageFile=QString())
QString decompressIfNeeded(const QString &filePath, const QString &outputPath, bool removeOriginal)
bool ensureDirectoryExists(const QString &path)
void configureProxy(QNetworkAccessManager *manager)
Set up default proxy configuration on a network manager.
QString numberToString(quint64 number)
Decimal integer (e.g. "1,234,567").
Definition QGCFormat.cc:15
QString bigSizeMBToString(quint64 sizeMB)
MB-scaled size, output in MB/GB/TB. Input is in MB.
Definition QGCFormat.cc:38
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
Definition AppMessages.cc:9