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();
113 qDeleteAll(_cameraInfoContexts);
114 _cameraInfoContexts.clear();
117 _camerasLostHeartbeatTimer.stop();
119 qCDebug(CameraManagerLog) <<
this;
133 if ((sel != _currentCameraIndex) && (sel >= 0) && (sel < _cameras.
count())) {
134 _currentCameraIndex = sel;
142 qCDebug(CameraManagerLog) << ready;
150 _vehicleReadyState =
true;
157 if (!_initialConnectComplete) {
163 const bool fromAutopilot = message.compid == MAV_COMP_ID_AUTOPILOT1;
164 const bool fromCamera = (message.compid >= MAV_COMP_ID_CAMERA) && (message.compid <= MAV_COMP_ID_CAMERA6);
165 if ((message.sysid == _vehicle->
id()) && (fromAutopilot || fromCamera)) {
166 switch (message.msgid) {
167 case MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS:
168 _handleCameraCaptureStatus(message);
170 case MAVLINK_MSG_ID_STORAGE_INFORMATION:
171 _handleStorageInformation(message);
173 case MAVLINK_MSG_ID_HEARTBEAT:
177 _handleHeartbeat(message);
180 case MAVLINK_MSG_ID_CAMERA_INFORMATION:
181 _handleCameraInfo(message);
183 case MAVLINK_MSG_ID_CAMERA_SETTINGS:
184 _handleCameraSettings(message);
186 case MAVLINK_MSG_ID_PARAM_EXT_ACK:
187 _handleParamExtAck(message);
189 case MAVLINK_MSG_ID_PARAM_EXT_VALUE:
190 _handleParamExtValue(message);
192 case MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION:
193 _handleVideoStreamInformation(message);
195 case MAVLINK_MSG_ID_VIDEO_STREAM_STATUS:
196 _handleVideoStreamStatus(message);
198 case MAVLINK_MSG_ID_BATTERY_STATUS:
199 _handleBatteryStatus(message);
201 case MAVLINK_MSG_ID_CAMERA_TRACKING_IMAGE_STATUS:
202 _handleTrackingImageStatus(message);
204 case MAVLINK_MSG_ID_CAMERA_FOV_STATUS:
205 _handleCameraFovStatus(message);
215 const QString sCompID = QString::number(message.compid);
217 if (!_cameraInfoRequest.contains(sCompID)) {
219 CameraStruct *pInfo =
new CameraStruct(
this, message.compid, _vehicle);
220 pInfo->lastHeartbeat.start();
221 _cameraInfoRequest[sCompID] = pInfo;
222 _requestCameraInfo(pInfo);
226 CameraStruct *pInfo = _cameraInfoRequest[sCompID];
228 qCWarning(CameraManagerLog) << sCompID <<
"is null";
232 if (pInfo->infoReceived) {
233 pInfo->lastHeartbeat.start();
237 if (pInfo->lastHeartbeat.elapsed() > kSilentTimeoutMs) {
238 qCDebug(CameraManagerLog) <<
"Camera" <<
QGCMAVLink::compIdToString(message.compid) <<
"reappeared after being silent. Resetting retry count and requesting info.";
239 pInfo->retryCount = 0;
240 pInfo->backoffTimer.stop();
241 pInfo->lastHeartbeat.start();
242 _requestCameraInfo(pInfo);
246 pInfo->lastHeartbeat.start();
251 if ((_currentCameraIndex < _cameras.
count()) && !_cameras.
isEmpty()) {
280 for (
int i = 0; i < _cameras.
count(); i++) {
286 qCCritical(CameraManagerLog) <<
"Invalid MavlinkCameraControlInterface instance";
289 if (pCamera->
compID() ==
id) {
300 if (qobject_cast<SimulatedCameraControl*>(cameraControl)) {
301 qCDebug(CameraManagerLog) <<
"Adding simulated camera to list";
303 qCDebug(CameraManagerLog) <<
"Adding real camera to list - simulated camera will be removed if present";
306 _cameras.
append(cameraControl);
307 _cameraLabels.append(cameraControl->
modelName());
312 if ((_cameras.
count() == 2) && (_cameras[0] == _simulatedCameraControl)) {
314 (void) _cameraLabels.removeAt(0);
323 const QString sCompID = QString::number(message.compid);
324 if (!_cameraInfoRequest.contains(sCompID)) {
325 qCDebug(CameraManagerLog) <<
"Ignoring - Camera info not requested for component" <<
QGCMAVLink::compIdToString(message.compid);
328 if (_cameraInfoRequest[sCompID]->infoReceived) {
329 qCDebug(CameraManagerLog) <<
"Ignoring - Already received camera info for component" <<
QGCMAVLink::compIdToString(message.compid);
334 mavlink_msg_camera_information_decode(&message, &info);
336 <<
"Model:" <<
reinterpret_cast<const char*
>(info.model_name);
337 qCDebug(CameraManagerLog) <<
"Creating MavlinkCameraControlInterface for camera";
341 _addCameraControlToLists(pCamera);
343 _cameraInfoRequest[sCompID]->infoReceived =
true;
344 _cameraInfoRequest[sCompID]->retryCount = 0;
345 _cameraInfoRequest[sCompID]->backoffTimer.stop();
349 double aspect = std::numeric_limits<double>::quiet_NaN();
351 if (info.resolution_h > 0 && info.resolution_v > 0) {
352 aspect = double(info.resolution_v) / double(info.resolution_h);
353 }
else if (info.sensor_size_h > 0.f && info.sensor_size_v > 0.f) {
354 aspect = double(info.sensor_size_v) / double(info.sensor_size_h);
357 _aspectByCompId.insert(message.compid, aspect);
362 QList<QString> stale;
363 for (
auto it = _cameraInfoRequest.cbegin(), end = _cameraInfoRequest.cend(); it != end; ++it) {
364 const auto *info = it.value();
365 if (info && info->infoReceived && (info->lastHeartbeat.elapsed() > kSilentTimeoutMs)) {
366 stale.push_back(it.key());
369 if (stale.isEmpty()) {
373 bool removedAny =
false;
374 for (
const QString& key : std::as_const(stale)) {
382 const int idx = _cameras.
indexOf(pCamera);
386 (void) _cameraLabels.removeAt(idx);
388 pCamera->deleteLater();
400 _addCameraControlToLists(_simulatedCameraControl);
406 if (_currentCameraIndex != 0) {
407 _currentCameraIndex = 0;
413void QGCCameraManager::_handleCameraCaptureStatus(
const mavlink_message_t &message)
417 mavlink_camera_capture_status_t cap{};
418 mavlink_msg_camera_capture_status_decode(&message, &cap);
423void QGCCameraManager::_handleStorageInformation(
const mavlink_message_t &message)
427 mavlink_storage_information_t st{};
428 mavlink_msg_storage_information_decode(&message, &st);
435 auto pCamera = _findCamera(message.compid);
437 mavlink_camera_settings_t settings{};
438 mavlink_msg_camera_settings_decode(&message, &settings);
441 const int newZoom =
static_cast<int>(settings.zoomLevel);
442 if (QThread::currentThread() == thread()) {
443 _setCurrentZoomLevel(newZoom);
445 QMetaObject::invokeMethod(
447 "_setCurrentZoomLevel",
448 Qt::QueuedConnection,
461 mavlink_param_ext_ack_t ack{};
462 mavlink_msg_param_ext_ack_decode(&message, &ack);
471 mavlink_param_ext_value_t value{};
472 mavlink_msg_param_ext_value_decode(&message, &value);
477void QGCCameraManager::_handleVideoStreamInformation(
const mavlink_message_t &message)
481 mavlink_video_stream_information_t streamInfo{};
482 mavlink_msg_video_stream_information_decode(&message, &streamInfo);
492 mavlink_video_stream_status_t streamStatus{};
493 mavlink_msg_video_stream_status_decode(&message, &streamStatus);
502 mavlink_battery_status_t batteryStatus{};
503 mavlink_msg_battery_status_decode(&message, &batteryStatus);
508void QGCCameraManager::_handleTrackingImageStatus(
const mavlink_message_t &message)
512 mavlink_camera_tracking_image_status_t tis{};
513 mavlink_msg_camera_tracking_image_status_decode(&message, &tis);
525 if (!context->manager) {
531 qCDebug(CameraManagerLog) <<
"Camera info request callback for removed camera. compId" <<
QGCMAVLink::compIdToString(context->compID);
543 if (ack.result != MAV_RESULT_ACCEPTED) {
559 if (result != MAV_RESULT_ACCEPTED) {
586 ackHandlerInfo.progressHandler =
nullptr;
587 ackHandlerInfo.progressHandlerData =
nullptr;
609 const int delaySeconds = 1 << (cameraInfo->
retryCount / 2);
610 const int delayMs = delaySeconds * 1000;
615 (void) QObject::disconnect(&cameraInfo->
backoffTimer,
nullptr,
nullptr,
nullptr);
618 const uint8_t compId = cameraInfo->
compID;
619 QPointer<QGCCameraManager> mgrGuard(manager);
621 (void) QObject::connect(&cameraInfo->
backoffTimer, &QTimer::timeout,
623 [mgrGuard, compId]() {
628 auto* info = mgrGuard->findCameraStruct(compId);
640void QGCCameraManager::_requestCameraInfo(CameraStruct *pInfo)
650 qCDebug(CameraManagerLog) <<
"Joystick changed";
651 if (_activeJoystick) {
666 _activeJoystick = joystick;
668 if (_activeJoystick) {
718 if (_lastZoomChange.elapsed() > 40) {
719 _lastZoomChange.start();
720 qCDebug(CameraManagerLog) <<
"Step Camera Zoom" << direction;
730 qCDebug(CameraManagerLog) <<
"Start Camera Zoom" << direction;
739 qCDebug(CameraManagerLog) <<
"Stop Camera Zoom";
748 if (_lastFocusChange.elapsed() > 40) {
749 _lastFocusChange.start();
750 qCDebug(CameraManagerLog) <<
"Step Camera Focus" << direction;
760 qCDebug(CameraManagerLog) <<
"Start Camera Focus" << direction;
769 qCDebug(CameraManagerLog) <<
"Stop Camera Focus";
778 if (_lastCameraChange.elapsed() > 1000) {
779 _lastCameraChange.start();
780 qCDebug(CameraManagerLog) <<
"Step Camera" << direction;
781 int camera = _currentCameraIndex + direction;
783 camera = _cameras.
count() - 1;
784 }
else if (camera >= _cameras.
count()) {
793 if (_lastCameraChange.elapsed() > 1000) {
794 _lastCameraChange.start();
797 qCDebug(CameraManagerLog) <<
"Step Camera Stream" << direction;
811 if (_cameraList.isEmpty()) {
813 _cameraList.reserve(cams.size());
816 _cameraList << QVariant::fromValue(cam);
824 qCWarning(CameraManagerLog) <<
"requestCameraFovForComp: vehicle is null";
828 compId, MAVLINK_MSG_ID_CAMERA_FOV_STATUS);
833 auto it = _aspectByCompId.constFind(compId);
834 return (it == _aspectByCompId.cend())
835 ? std::numeric_limits<double>::quiet_NaN()
843 return std::numeric_limits<double>::quiet_NaN();
847 mavlink_camera_fov_status_t fov{};
848 mavlink_msg_camera_fov_status_decode(&message, &fov);
850 if (!std::isfinite(fov.hfov) || fov.hfov <= 0.0 || fov.hfov >= 180.0) {
855 if (!std::isfinite(aspect) || aspect <= 0.0) {
859 const double hfovRad = fov.hfov *
kPi / 180.0;
860 const double vfovRad = 2.0 * std::atan(std::tan(hfovRad * 0.5) * aspect);
861 const double vfovDeg = vfovRad * 180.0 /
kPi;
863 if (!std::isfinite(vfovDeg) || vfovDeg <= 0.0 || vfovDeg >= 180.0) {
864 qCWarning(CameraManagerLog) <<
"Invalid calculated VFOV:" << vfovDeg
865 <<
"hfov:" << fov.hfov
866 <<
"aspect:" << aspect
867 <<
"compId:" << message.compid;
872 settings->cameraHFov()->setRawValue(fov.hfov);
873 settings->cameraVFov()->setRawValue(vfovDeg);
876void QGCCameraManager::_setCurrentZoomLevel(
int level)
878 if (_zoomValueCurrent == level) {
881 _zoomValueCurrent = level;
887 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 QGCCameraManager::CameraStruct * _cameraStructFromContext(void *resultHandlerData)
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()
CameraInfoRequestContext * cameraInfoContext(uint8_t compId)
Returns the lazily created, manager-lifetime context for compId.
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