19constexpr double kPi = std::numbers::pi_v<double>;
24 constexpr int kHeartbeatTickMs = 500;
25 constexpr int kSilentTimeoutMs = 5000;
26 constexpr int kMaxRetryCount = 10;
29QVariantList QGCCameraManager::_cameraList;
39 if (result != MAV_RESULT_ACCEPTED) {
40 qCDebug(CameraManagerLog) <<
"CAMERA_FOV_STATUS request failed, result:"
41 << result <<
"failure:" << failureCode;
45 if (message.msgid != MAVLINK_MSG_ID_CAMERA_FOV_STATUS) {
46 qCDebug(CameraManagerLog) <<
"Unexpected msg id:" << message.msgid;
50 mavlink_camera_fov_status_t fov{};
52 mavlink_msg_camera_fov_status_decode(&message, &fov);
64 qCDebug(CameraManagerLog) <<
this;
70 qCDebug(CameraManagerLog) <<
this;
80 qCDebug(CameraManagerLog) <<
this;
82 (void) qRegisterMetaType<CameraMetaData*>(
"CameraMetaData*");
84 _addCameraControlToLists(_simulatedCameraControl);
91 _camerasLostHeartbeatTimer.setSingleShot(
false);
92 _lastZoomChange.start();
93 _lastFocusChange.start();
94 _lastCameraChange.start();
95 _camerasLostHeartbeatTimer.start(kHeartbeatTickMs);
98void QGCCameraManager::_initialConnectCompleted()
100 _initialConnectComplete =
true;
106 for (
auto* cameraInfo : _cameraInfoRequest) {
107 cameraInfo->backoffTimer.stop();
108 QObject::disconnect(&cameraInfo->backoffTimer,
nullptr,
nullptr,
nullptr);
110 qDeleteAll(_cameraInfoRequest);
111 _cameraInfoRequest.clear();
114 _camerasLostHeartbeatTimer.stop();
116 qCDebug(CameraManagerLog) <<
this;
121 if ((sel != _currentCameraIndex) && (sel >= 0) && (sel < _cameras.
count())) {
122 _currentCameraIndex = sel;
130 qCDebug(CameraManagerLog) << ready;
138 _vehicleReadyState =
true;
145 if (!_initialConnectComplete) {
151 const bool fromAutopilot = message.compid == MAV_COMP_ID_AUTOPILOT1;
152 const bool fromCamera = (message.compid >= MAV_COMP_ID_CAMERA) && (message.compid <= MAV_COMP_ID_CAMERA6);
153 if ((message.sysid == _vehicle->
id()) && (fromAutopilot || fromCamera)) {
154 switch (message.msgid) {
155 case MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS:
156 _handleCameraCaptureStatus(message);
158 case MAVLINK_MSG_ID_STORAGE_INFORMATION:
159 _handleStorageInformation(message);
161 case MAVLINK_MSG_ID_HEARTBEAT:
165 _handleHeartbeat(message);
168 case MAVLINK_MSG_ID_CAMERA_INFORMATION:
169 _handleCameraInfo(message);
171 case MAVLINK_MSG_ID_CAMERA_SETTINGS:
172 _handleCameraSettings(message);
174 case MAVLINK_MSG_ID_PARAM_EXT_ACK:
175 _handleParamExtAck(message);
177 case MAVLINK_MSG_ID_PARAM_EXT_VALUE:
178 _handleParamExtValue(message);
180 case MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION:
181 _handleVideoStreamInformation(message);
183 case MAVLINK_MSG_ID_VIDEO_STREAM_STATUS:
184 _handleVideoStreamStatus(message);
186 case MAVLINK_MSG_ID_BATTERY_STATUS:
187 _handleBatteryStatus(message);
189 case MAVLINK_MSG_ID_CAMERA_TRACKING_IMAGE_STATUS:
190 _handleTrackingImageStatus(message);
192 case MAVLINK_MSG_ID_CAMERA_FOV_STATUS:
193 _handleCameraFovStatus(message);
203 const QString sCompID = QString::number(message.compid);
205 if (!_cameraInfoRequest.contains(sCompID)) {
207 CameraStruct *pInfo =
new CameraStruct(
this, message.compid, _vehicle);
208 pInfo->lastHeartbeat.start();
209 _cameraInfoRequest[sCompID] = pInfo;
210 _requestCameraInfo(pInfo);
214 CameraStruct *pInfo = _cameraInfoRequest[sCompID];
216 qCWarning(CameraManagerLog) << sCompID <<
"is null";
220 if (pInfo->infoReceived) {
221 pInfo->lastHeartbeat.start();
225 if (pInfo->lastHeartbeat.elapsed() > kSilentTimeoutMs) {
226 qCDebug(CameraManagerLog) <<
"Camera" <<
QGCMAVLink::compIdToString(message.compid) <<
"reappeared after being silent. Resetting retry count and requesting info.";
227 pInfo->retryCount = 0;
228 pInfo->backoffTimer.stop();
229 pInfo->lastHeartbeat.start();
230 _requestCameraInfo(pInfo);
234 pInfo->lastHeartbeat.start();
239 if ((_currentCameraIndex < _cameras.
count()) && !_cameras.
isEmpty()) {
268 for (
int i = 0; i < _cameras.
count(); i++) {
274 qCCritical(CameraManagerLog) <<
"Invalid MavlinkCameraControlInterface instance";
277 if (pCamera->
compID() ==
id) {
288 if (qobject_cast<SimulatedCameraControl*>(cameraControl)) {
289 qCDebug(CameraManagerLog) <<
"Adding simulated camera to list";
291 qCDebug(CameraManagerLog) <<
"Adding real camera to list - simulated camera will be removed if present";
294 _cameras.
append(cameraControl);
295 _cameraLabels.append(cameraControl->
modelName());
300 if ((_cameras.
count() == 2) && (_cameras[0] == _simulatedCameraControl)) {
302 (void) _cameraLabels.removeAt(0);
311 const QString sCompID = QString::number(message.compid);
312 if (!_cameraInfoRequest.contains(sCompID)) {
313 qCDebug(CameraManagerLog) <<
"Ignoring - Camera info not requested for component" <<
QGCMAVLink::compIdToString(message.compid);
316 if (_cameraInfoRequest[sCompID]->infoReceived) {
317 qCDebug(CameraManagerLog) <<
"Ignoring - Already received camera info for component" <<
QGCMAVLink::compIdToString(message.compid);
322 mavlink_msg_camera_information_decode(&message, &info);
324 <<
"Model:" <<
reinterpret_cast<const char*
>(info.model_name);
325 qCDebug(CameraManagerLog) <<
"Creating MavlinkCameraControlInterface for camera";
329 _addCameraControlToLists(pCamera);
331 _cameraInfoRequest[sCompID]->infoReceived =
true;
332 _cameraInfoRequest[sCompID]->retryCount = 0;
333 _cameraInfoRequest[sCompID]->backoffTimer.stop();
337 double aspect = std::numeric_limits<double>::quiet_NaN();
339 if (info.resolution_h > 0 && info.resolution_v > 0) {
340 aspect = double(info.resolution_v) / double(info.resolution_h);
341 }
else if (info.sensor_size_h > 0.f && info.sensor_size_v > 0.f) {
342 aspect = double(info.sensor_size_v) / double(info.sensor_size_h);
345 _aspectByCompId.insert(message.compid, aspect);
350 QList<QString> stale;
351 for (
auto it = _cameraInfoRequest.cbegin(), end = _cameraInfoRequest.cend(); it != end; ++it) {
352 const auto *info = it.value();
353 if (info && info->infoReceived && (info->lastHeartbeat.elapsed() > kSilentTimeoutMs)) {
354 stale.push_back(it.key());
357 if (stale.isEmpty()) {
361 bool removedAny =
false;
362 for (
const QString& key : std::as_const(stale)) {
370 const int idx = _cameras.
indexOf(pCamera);
374 (void) _cameraLabels.removeAt(idx);
376 pCamera->deleteLater();
388 _addCameraControlToLists(_simulatedCameraControl);
394 if (_currentCameraIndex != 0) {
395 _currentCameraIndex = 0;
401void QGCCameraManager::_handleCameraCaptureStatus(
const mavlink_message_t &message)
405 mavlink_camera_capture_status_t cap{};
406 mavlink_msg_camera_capture_status_decode(&message, &cap);
411void QGCCameraManager::_handleStorageInformation(
const mavlink_message_t &message)
415 mavlink_storage_information_t st{};
416 mavlink_msg_storage_information_decode(&message, &st);
423 auto pCamera = _findCamera(message.compid);
425 mavlink_camera_settings_t settings{};
426 mavlink_msg_camera_settings_decode(&message, &settings);
429 const int newZoom =
static_cast<int>(settings.zoomLevel);
430 if (QThread::currentThread() == thread()) {
431 _setCurrentZoomLevel(newZoom);
433 QMetaObject::invokeMethod(
435 "_setCurrentZoomLevel",
436 Qt::QueuedConnection,
449 mavlink_param_ext_ack_t ack{};
450 mavlink_msg_param_ext_ack_decode(&message, &ack);
459 mavlink_param_ext_value_t value{};
460 mavlink_msg_param_ext_value_decode(&message, &value);
465void QGCCameraManager::_handleVideoStreamInformation(
const mavlink_message_t &message)
469 mavlink_video_stream_information_t streamInfo{};
470 mavlink_msg_video_stream_information_decode(&message, &streamInfo);
480 mavlink_video_stream_status_t streamStatus{};
481 mavlink_msg_video_stream_status_decode(&message, &streamStatus);
490 mavlink_battery_status_t batteryStatus{};
491 mavlink_msg_battery_status_decode(&message, &batteryStatus);
496void QGCCameraManager::_handleTrackingImageStatus(
const mavlink_message_t &message)
500 mavlink_camera_tracking_image_status_t tis{};
501 mavlink_msg_camera_tracking_image_status_decode(&message, &tis);
512 if (ack.result != MAV_RESULT_ACCEPTED) {
513 qCDebug(CameraManagerLog) <<
"MAV_CMD_REQUEST_CAMERA_INFORMATION failed. compId" <<
QGCMAVLink::compIdToString(cameraInfo->compID)
525 if (result != MAV_RESULT_ACCEPTED) {
526 qCDebug(CameraManagerLog) <<
"MAV_CMD_REQUEST_MESSAGE:MAVLINK_MSG_ID_CAMERA_INFORMATION failed. compId" <<
QGCMAVLink::compIdToString(cameraInfo->compID)
551 ackHandlerInfo.resultHandlerData = pInfo;
552 ackHandlerInfo.progressHandler =
nullptr;
553 ackHandlerInfo.progressHandlerData =
nullptr;
575 const int delaySeconds = 1 << (cameraInfo->
retryCount / 2);
576 const int delayMs = delaySeconds * 1000;
581 (void) QObject::disconnect(&cameraInfo->
backoffTimer,
nullptr,
nullptr,
nullptr);
584 const uint8_t compId = cameraInfo->
compID;
585 QPointer<QGCCameraManager> mgrGuard(manager);
587 (void) QObject::connect(&cameraInfo->
backoffTimer, &QTimer::timeout,
589 [mgrGuard, compId]() {
594 auto* info = mgrGuard->findCameraStruct(compId);
606void QGCCameraManager::_requestCameraInfo(CameraStruct *pInfo)
616 qCDebug(CameraManagerLog) <<
"Joystick changed";
617 if (_activeJoystick) {
632 _activeJoystick = joystick;
634 if (_activeJoystick) {
684 if (_lastZoomChange.elapsed() > 40) {
685 _lastZoomChange.start();
686 qCDebug(CameraManagerLog) <<
"Step Camera Zoom" << direction;
696 qCDebug(CameraManagerLog) <<
"Start Camera Zoom" << direction;
705 qCDebug(CameraManagerLog) <<
"Stop Camera Zoom";
714 if (_lastFocusChange.elapsed() > 40) {
715 _lastFocusChange.start();
716 qCDebug(CameraManagerLog) <<
"Step Camera Focus" << direction;
726 qCDebug(CameraManagerLog) <<
"Start Camera Focus" << direction;
735 qCDebug(CameraManagerLog) <<
"Stop Camera Focus";
744 if (_lastCameraChange.elapsed() > 1000) {
745 _lastCameraChange.start();
746 qCDebug(CameraManagerLog) <<
"Step Camera" << direction;
747 int camera = _currentCameraIndex + direction;
749 camera = _cameras.
count() - 1;
750 }
else if (camera >= _cameras.
count()) {
759 if (_lastCameraChange.elapsed() > 1000) {
760 _lastCameraChange.start();
763 qCDebug(CameraManagerLog) <<
"Step Camera Stream" << direction;
777 if (_cameraList.isEmpty()) {
779 _cameraList.reserve(cams.size());
782 _cameraList << QVariant::fromValue(cam);
790 qCWarning(CameraManagerLog) <<
"requestCameraFovForComp: vehicle is null";
794 compId, MAVLINK_MSG_ID_CAMERA_FOV_STATUS);
799 auto it = _aspectByCompId.constFind(compId);
800 return (it == _aspectByCompId.cend())
801 ? std::numeric_limits<double>::quiet_NaN()
809 return std::numeric_limits<double>::quiet_NaN();
813 mavlink_camera_fov_status_t fov{};
814 mavlink_msg_camera_fov_status_decode(&message, &fov);
816 if (!std::isfinite(fov.hfov) || fov.hfov <= 0.0 || fov.hfov >= 180.0) {
821 if (!std::isfinite(aspect) || aspect <= 0.0) {
825 const double hfovRad = fov.hfov *
kPi / 180.0;
826 const double vfovRad = 2.0 * std::atan(std::tan(hfovRad * 0.5) * aspect);
827 const double vfovDeg = vfovRad * 180.0 /
kPi;
829 if (!std::isfinite(vfovDeg) || vfovDeg <= 0.0 || vfovDeg >= 180.0) {
830 qCWarning(CameraManagerLog) <<
"Invalid calculated VFOV:" << vfovDeg
831 <<
"hfov:" << fov.hfov
832 <<
"aspect:" << aspect
833 <<
"compId:" << message.compid;
838 settings->cameraHFov()->setRawValue(fov.hfov);
839 settings->cameraVFov()->setRawValue(vfovDeg);
842void QGCCameraManager::_setCurrentZoomLevel(
int level)
844 if (_zoomValueCurrent == level) {
847 _zoomValueCurrent = level;
853 return _zoomValueCurrent;
static void _requestFovOnZoom_Handler(void *user, MAV_RESULT result, Vehicle::RequestMessageResultHandlerFailureCode_t failureCode, const mavlink_message_t &message)
static void _handleCameraInfoRetry(QGCCameraManager::CameraStruct *cameraInfo)
static void _requestCameraInfoMessageResultHandler(void *resultHandlerData, MAV_RESULT result, Vehicle::RequestMessageResultHandlerFailureCode_t failureCode, const mavlink_message_t &message)
static void _requestCameraInfoCommandResultHandler(void *resultHandlerData, int, const mavlink_command_ack_t &ack, Vehicle::MavCmdResultFailureCode_t failureCode)
static void _requestCameraInfoHelper(QGCCameraManager *manager, QGCCameraManager::CameraStruct *pInfo)
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
struct __mavlink_command_ack_t mavlink_command_ack_t
struct __mavlink_camera_information_t mavlink_camera_information_t
virtual MavlinkCameraControlInterface * createCameraControl(const mavlink_camera_information_t *info, Vehicle *vehicle, int compID, QObject *parent=nullptr) const
Camera control.
static JoystickManager * instance()
void activeJoystickChanged(Joystick *joystick)
void stopContinuousFocus()
void stepZoom(int direction)
void startContinuousZoom(int direction)
void stopContinuousZoom()
void stepCamera(int direction)
void stepStream(int direction)
void stepFocus(int direction)
void startContinuousFocus(int direction)
Abstract base class for all camera controls: real and simulated.
virtual Q_INVOKABLE void stopZoom()=0
virtual Q_INVOKABLE void startZoom(int direction)=0
virtual QGCVideoStreamInfo * thermalStreamInstance()=0
virtual QmlObjectListModel * streams()=0
virtual void handleBatteryStatus(const mavlink_battery_status_t &bs)=0
virtual void handleTrackingImageStatus(const mavlink_camera_tracking_image_status_t &trackingImageStatus)=0
virtual Q_INVOKABLE void stepZoom(int direction)=0
virtual Q_INVOKABLE bool takePhoto()=0
virtual Q_INVOKABLE bool toggleVideoRecording()=0
virtual void handleCameraSettings(const mavlink_camera_settings_t &settings)=0
virtual QString modelName() const =0
virtual Q_INVOKABLE bool stopVideoRecording()=0
virtual void handleVideoStreamInformation(const mavlink_video_stream_information_t &videoStreamInformation)=0
virtual void handleStorageInformation(const mavlink_storage_information_t &storageInformation)=0
virtual Q_INVOKABLE void stepFocus(int direction)=0
virtual Q_INVOKABLE void startFocus(int direction)=0
virtual int compID() const =0
virtual void handleParamExtValue(const mavlink_param_ext_value_t ¶mExtValue)=0
virtual Q_INVOKABLE bool startVideoRecording()=0
virtual QGCVideoStreamInfo * currentStreamInstance()=0
virtual void handleVideoStreamStatus(const mavlink_video_stream_status_t &videoStreamStatus)=0
virtual void setCurrentStream(int stream)=0
virtual int currentStream() const =0
virtual void handleParamExtAck(const mavlink_param_ext_ack_t ¶mExtAck)=0
virtual Q_INVOKABLE void stopFocus()=0
virtual void handleCameraCaptureStatus(const mavlink_camera_capture_status_t &cameraCaptureStatus)=0
static MultiVehicleManager * instance()
void parameterReadyVehicleAvailableChanged(bool parameterReadyVehicleAvailable)
void currentZoomLevelChanged()
void setCurrentCamera(int sel)
void _vehicleReady(bool ready)
void _checkForLostCameras()
const QVariantList & cameraList() const
Q_INVOKABLE void requestCameraFovForComp(int compId)
void cameraLabelsChanged()
MavlinkCameraControlInterface * currentCameraInstance()
QGCCameraManager(Vehicle *vehicle)
QGCVideoStreamInfo * currentStreamInstance()
void _stepZoom(int direction)
void _startZoom(int direction)
void _startVideoRecording()
double aspectForComp(int compId) const
double currentCameraAspect()
QGCVideoStreamInfo * thermalStreamInstance()
Vehicle * vehicle() const
void _toggleVideoRecording()
void _mavlinkMessageReceived(const mavlink_message_t &message)
void currentCameraChanged()
void _stepFocus(int direction)
int currentZoomLevel() const
void _stepCamera(int direction)
void _activeJoystickChanged(Joystick *joystick)
void _stepStream(int direction)
void _startFocus(int direction)
void _stopVideoRecording()
static QString compIdToString(uint8_t compId)
static QString mavResultToString(uint8_t result)
Encapsulates the contents of a VIDEO_STREAM_INFORMATION message.
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
bool isEmpty() const override final
QObject * removeAt(int index)
int count() const override final
int indexOf(const QObject *object)
static SettingsManager * instance()
GimbalControllerSettings * gimbalControllerSettings() const
Creates a simulated Camera Control which supports:
static QString mavCmdResultFailureCodeToString(MavCmdResultFailureCode_t failureCode)
void initialConnectComplete()
FirmwarePlugin * firmwarePlugin()
Provides access to the Firmware Plugin for this Vehicle.
void mavlinkMessageReceived(const mavlink_message_t &message)
static QString requestMessageResultHandlerFailureCodeToString(RequestMessageResultHandlerFailureCode_t failureCode)
void requestMessage(RequestMessageResultHandler resultHandler, void *resultHandlerData, int compId, int messageId, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f)
void sendMavCommandWithHandler(const MavCmdAckHandlerInfo_t *ackHandlerInfo, int compId, MAV_CMD command, 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)
Sends the command and calls the callback with the result.
Vehicle * vehicle
Raw pointer is safe: CameraStruct is owned by QGCCameraManager which is a child of Vehicle.
QPointer< QGCCameraManager > manager
CameraStruct(QGCCameraManager *manager_, uint8_t compID_, Vehicle *vehicle_)
Callback info bundle for sendMavCommandWithHandler.
MavCmdResultHandler resultHandler
nullptr for no handler
MavCmdResultFailureCode_t
RequestMessageResultHandlerFailureCode_t