6#include <QtCore/QDateTime>
7#include <QtCore/QLoggingCategory>
16 bool canCaptureImageInVideoMode,
17 bool canCaptureVideoInImageMode,
19 bool hasTrackingPoint,
20 bool hasTrackingRectangle)
24 uint32_t configuredFlags = 0;
25 if (captureVideo) configuredFlags |= CAMERA_CAP_FLAGS_CAPTURE_VIDEO;
26 if (captureImage) configuredFlags |= CAMERA_CAP_FLAGS_CAPTURE_IMAGE;
27 if (hasModes) configuredFlags |= CAMERA_CAP_FLAGS_HAS_MODES;
28 if (hasVideoStream) configuredFlags |= CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM;
29 if (canCaptureImageInVideoMode) configuredFlags |= CAMERA_CAP_FLAGS_CAN_CAPTURE_IMAGE_IN_VIDEO_MODE;
30 if (canCaptureVideoInImageMode) configuredFlags |= CAMERA_CAP_FLAGS_CAN_CAPTURE_VIDEO_IN_IMAGE_MODE;
31 if (hasBasicZoom) configuredFlags |= CAMERA_CAP_FLAGS_HAS_BASIC_ZOOM;
32 if (hasTrackingPoint) configuredFlags |= CAMERA_CAP_FLAGS_HAS_TRACKING_POINT;
33 if (hasTrackingRectangle) configuredFlags |= CAMERA_CAP_FLAGS_HAS_TRACKING_RECTANGLE;
36 _cameras[0].compId = MAV_COMP_ID_CAMERA;
37 _cameras[0].capFlags = configuredFlags;
38 _cameras[0].cameraMode = CAMERA_MODE_IMAGE;
39 if ((configuredFlags & CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM) && (configuredFlags & CAMERA_CAP_FLAGS_HAS_MODES)) {
40 _cameras[0].cameraMode = CAMERA_MODE_VIDEO;
44 _cameras[1].compId = MAV_COMP_ID_CAMERA2;
45 _cameras[1].capFlags = CAMERA_CAP_FLAGS_CAPTURE_IMAGE;
46 _cameras[1].cameraMode = CAMERA_MODE_IMAGE;
51 for (uint8_t i = 0; i < kNumCameras; i++) {
52 if (_cameras[i].compId == compId) {
59const char *MockLinkCamera::_imageCaptureStatusToString(uint8_t status)
66 default:
return "Unknown";
72 for (uint8_t i = 0; i < kNumCameras; i++) {
74 (void) mavlink_msg_heartbeat_pack_chan(
80 MAV_AUTOPILOT_INVALID,
92 QMutexLocker locker(&_camerasMutex);
93 const qint64 now = QDateTime::currentMSecsSinceEpoch();
95 for (uint8_t i = 0; i < kNumCameras; i++) {
104 qCDebug(MockLinkCameraLog) <<
"Camera" << cam->
compId <<
"single-shot complete, total:" << cam->
imagesCaptured;
105 _sendCameraImageCaptured(cam->
compId);
106 _sendCameraCaptureStatus(cam->
compId);
113 if (msg.msgid != MAVLINK_MSG_ID_COMMAND_LONG) {
117 mavlink_command_long_t request{};
118 mavlink_msg_command_long_decode(&msg, &request);
121 if (request.target_component < MAV_COMP_ID_CAMERA || request.target_component > MAV_COMP_ID_CAMERA6) {
125 const uint8_t targetCompId = request.target_component;
127 if (request.command == MAV_CMD_REQUEST_MESSAGE) {
128 return _handleRequestMessage(request, targetCompId);
131 return _handleCameraCommand(request, targetCompId);
134bool MockLinkCamera::_handleCameraCommand(
const mavlink_command_long_t &request, uint8_t targetCompId)
139 QMutexLocker locker(&_camerasMutex);
140 CameraState *cam = _findCamera(targetCompId);
145 switch (request.command) {
146 case MAV_CMD_REQUEST_CAMERA_INFORMATION:
147 _sendCameraInformation(targetCompId);
148 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
151 case MAV_CMD_REQUEST_CAMERA_SETTINGS:
152 _sendCameraSettings(targetCompId);
153 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
156 case MAV_CMD_REQUEST_STORAGE_INFORMATION:
157 _sendStorageInformation(targetCompId);
158 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
161 case MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS:
162 _sendCameraCaptureStatus(targetCompId);
163 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
166 case MAV_CMD_REQUEST_VIDEO_STREAM_INFORMATION:
167 if (cam->capFlags & CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM) {
168 const uint8_t streamId =
static_cast<uint8_t
>(request.param1);
171 for (uint8_t s = 1; s <= kNumStreams; s++) {
172 _sendVideoStreamInformation(targetCompId, s);
175 _sendVideoStreamInformation(targetCompId, streamId);
177 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
179 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
183 case MAV_CMD_REQUEST_VIDEO_STREAM_STATUS:
184 if (cam->capFlags & CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM) {
185 const uint8_t streamId =
static_cast<uint8_t
>(request.param1);
187 for (uint8_t s = 1; s <= kNumStreams; s++) {
188 _sendVideoStreamStatus(targetCompId, s);
191 _sendVideoStreamStatus(targetCompId, streamId);
193 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
195 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
199 case MAV_CMD_SET_CAMERA_MODE:
200 if (cam->capFlags & CAMERA_CAP_FLAGS_HAS_MODES) {
201 const uint8_t requestedMode =
static_cast<uint8_t
>(request.param2);
203 if ((requestedMode != CAMERA_MODE_IMAGE) && (requestedMode != CAMERA_MODE_VIDEO)) {
204 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
208 const bool supportsImageMode =
209 (cam->capFlags & CAMERA_CAP_FLAGS_CAPTURE_IMAGE) ||
210 (cam->capFlags & CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM);
211 const bool supportsVideoMode =
212 (cam->capFlags & CAMERA_CAP_FLAGS_CAPTURE_VIDEO) ||
213 (cam->capFlags & CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM);
215 if ((requestedMode == CAMERA_MODE_IMAGE && !supportsImageMode) ||
216 (requestedMode == CAMERA_MODE_VIDEO && !supportsVideoMode)) {
217 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
221 cam->cameraMode = requestedMode;
222 qCDebug(MockLinkCameraLog) <<
"Camera" << targetCompId <<
"mode set to" << cam->cameraMode;
223 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
225 _sendCameraSettings(targetCompId);
227 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
231 case MAV_CMD_IMAGE_START_CAPTURE:
232 if (cam->capFlags & CAMERA_CAP_FLAGS_CAPTURE_IMAGE) {
233 if ((cam->capFlags & CAMERA_CAP_FLAGS_HAS_MODES) &&
234 (cam->cameraMode == CAMERA_MODE_VIDEO) &&
235 !(cam->capFlags & CAMERA_CAP_FLAGS_CAN_CAPTURE_IMAGE_IN_VIDEO_MODE)) {
236 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
240 const float interval = request.param2;
241 const int count =
static_cast<int>(request.param3);
247 cam->image_interval = interval;
248 cam->imagesCaptured += (count > 0) ? count : 1;
249 cam->singleShotStartMs = 0;
253 cam->image_interval = 0.0f;
254 cam->singleShotStartMs = QDateTime::currentMSecsSinceEpoch();
257 qCDebug(MockLinkCameraLog) <<
"Camera" << targetCompId <<
"image capture started"
258 <<
"interval:" << interval <<
"count:" << count;
259 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
261 _sendCameraCaptureStatus(targetCompId);
263 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
267 case MAV_CMD_IMAGE_STOP_CAPTURE:
268 if (cam->capFlags & CAMERA_CAP_FLAGS_CAPTURE_IMAGE) {
270 cam->image_interval = 0.0f;
271 cam->singleShotStartMs = 0;
272 qCDebug(MockLinkCameraLog) <<
"Camera" << targetCompId <<
"image capture stopped";
273 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
274 _sendCameraCaptureStatus(targetCompId);
276 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
280 case MAV_CMD_VIDEO_START_CAPTURE:
281 if (cam->capFlags & CAMERA_CAP_FLAGS_CAPTURE_VIDEO) {
282 if ((cam->capFlags & CAMERA_CAP_FLAGS_HAS_MODES) &&
283 (cam->cameraMode == CAMERA_MODE_IMAGE) &&
284 !(cam->capFlags & CAMERA_CAP_FLAGS_CAN_CAPTURE_VIDEO_IN_IMAGE_MODE)) {
285 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
289 cam->recording =
true;
290 qCDebug(MockLinkCameraLog) <<
"Camera" << targetCompId <<
"video recording started";
291 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
292 _sendCameraCaptureStatus(targetCompId);
294 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
298 case MAV_CMD_VIDEO_STOP_CAPTURE:
299 if (cam->capFlags & CAMERA_CAP_FLAGS_CAPTURE_VIDEO) {
300 cam->recording =
false;
301 qCDebug(MockLinkCameraLog) <<
"Camera" << targetCompId <<
"video recording stopped";
302 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
303 _sendCameraCaptureStatus(targetCompId);
305 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
309 case MAV_CMD_STORAGE_FORMAT:
310 qCDebug(MockLinkCameraLog) <<
"Camera" << targetCompId <<
"storage formatted";
311 cam->imagesCaptured = 0;
313 cam->image_interval = 0.0f;
314 cam->singleShotStartMs = 0;
315 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
316 _sendStorageInformation(targetCompId);
319 case MAV_CMD_SET_CAMERA_ZOOM:
320 if (cam->capFlags & CAMERA_CAP_FLAGS_HAS_BASIC_ZOOM) {
321 cam->zoomLevel = request.param2;
322 qCDebug(MockLinkCameraLog) <<
"Camera" << targetCompId <<
"zoom set to" << cam->zoomLevel;
323 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
324 _sendCameraSettings(targetCompId);
326 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
330 case MAV_CMD_SET_CAMERA_FOCUS:
331 _sendCommandAck(targetCompId, request.command, MAV_RESULT_DENIED);
334 case MAV_CMD_RESET_CAMERA_SETTINGS:
335 cam->cameraMode = CAMERA_MODE_IMAGE;
336 cam->zoomLevel = 1.0f;
337 cam->focusLevel = 0.0f;
339 cam->image_interval = 0.0f;
340 cam->singleShotStartMs = 0;
341 qCDebug(MockLinkCameraLog) <<
"Camera" << targetCompId <<
"settings reset";
342 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
343 _sendCameraSettings(targetCompId);
346 case MAV_CMD_CAMERA_TRACK_POINT:
347 case MAV_CMD_CAMERA_TRACK_RECTANGLE:
348 case MAV_CMD_CAMERA_STOP_TRACKING:
349 qCDebug(MockLinkCameraLog) <<
"Camera" << targetCompId <<
"tracking command" << request.command;
350 _sendCommandAck(targetCompId, request.command, MAV_RESULT_ACCEPTED);
360bool MockLinkCamera::_handleRequestMessage(
const mavlink_command_long_t &request, uint8_t targetCompId)
362 const CameraState *cam = _findCamera(targetCompId);
367 const int msgId =
static_cast<int>(request.param1);
370 case MAVLINK_MSG_ID_CAMERA_INFORMATION:
371 _sendCameraInformation(targetCompId);
372 _sendCommandAck(targetCompId, MAV_CMD_REQUEST_MESSAGE, MAV_RESULT_ACCEPTED, msgId);
375 case MAVLINK_MSG_ID_CAMERA_SETTINGS:
376 _sendCameraSettings(targetCompId);
377 _sendCommandAck(targetCompId, MAV_CMD_REQUEST_MESSAGE, MAV_RESULT_ACCEPTED, msgId);
380 case MAVLINK_MSG_ID_STORAGE_INFORMATION:
381 _sendStorageInformation(targetCompId);
382 _sendCommandAck(targetCompId, MAV_CMD_REQUEST_MESSAGE, MAV_RESULT_ACCEPTED, msgId);
385 case MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS:
386 _sendCameraCaptureStatus(targetCompId);
387 _sendCommandAck(targetCompId, MAV_CMD_REQUEST_MESSAGE, MAV_RESULT_ACCEPTED, msgId);
390 case MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION:
392 if (!(cam->capFlags & CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM)) {
393 _sendCommandAck(targetCompId, MAV_CMD_REQUEST_MESSAGE, MAV_RESULT_DENIED, msgId);
396 const uint8_t streamId =
static_cast<uint8_t
>(request.param2);
398 for (uint8_t s = 1; s <= kNumStreams; s++) {
399 _sendVideoStreamInformation(targetCompId, s);
402 _sendVideoStreamInformation(targetCompId, streamId);
404 _sendCommandAck(targetCompId, MAV_CMD_REQUEST_MESSAGE, MAV_RESULT_ACCEPTED, msgId);
408 case MAVLINK_MSG_ID_VIDEO_STREAM_STATUS:
410 if (!(cam->capFlags & CAMERA_CAP_FLAGS_HAS_VIDEO_STREAM)) {
411 _sendCommandAck(targetCompId, MAV_CMD_REQUEST_MESSAGE, MAV_RESULT_DENIED, msgId);
414 const uint8_t streamId =
static_cast<uint8_t
>(request.param2);
416 for (uint8_t s = 1; s <= kNumStreams; s++) {
417 _sendVideoStreamStatus(targetCompId, s);
420 _sendVideoStreamStatus(targetCompId, streamId);
422 _sendCommandAck(targetCompId, MAV_CMD_REQUEST_MESSAGE, MAV_RESULT_ACCEPTED, msgId);
433void MockLinkCamera::_sendCameraInformation(uint8_t compId)
435 const CameraState *cam = _findCamera(compId);
440 const int cameraIndex = compId - MAV_COMP_ID_CAMERA;
442 const uint8_t vendorName[32] =
"MockLink";
443 const char cameraDefinitionUri[MAVLINK_MSG_CAMERA_INFORMATION_FIELD_CAM_DEFINITION_URI_LEN] = {};
444 const QString model = QStringLiteral(
"MockCam %1").arg(cameraIndex + 1);
445 QByteArray modelBA = model.toLocal8Bit();
446 modelBA.resize(MAVLINK_MSG_CAMERA_INFORMATION_FIELD_MODEL_NAME_LEN);
449 (void) mavlink_msg_camera_information_pack_chan(
456 reinterpret_cast<const uint8_t *
>(modelBA.constData()),
471 qCDebug(MockLinkCameraLog) <<
"Sent CAMERA_INFORMATION for compId:" << compId <<
"model:" << model;
474void MockLinkCamera::_sendCameraSettings(uint8_t compId)
476 const CameraState *cam = _findCamera(compId);
482 (void) mavlink_msg_camera_settings_pack_chan(
494 qCDebug(MockLinkCameraLog) <<
"Sent CAMERA_SETTINGS for compId:" << compId
495 <<
"mode:" << cam->cameraMode
496 <<
"zoom:" << cam->zoomLevel
497 <<
"focus:" << cam->focusLevel;
500void MockLinkCamera::_sendStorageInformation(uint8_t compId)
502 const char storageName[MAVLINK_MSG_STORAGE_INFORMATION_FIELD_NAME_LEN] = {};
505 (void) mavlink_msg_storage_information_pack_chan(
513 STORAGE_STATUS_READY,
514 static_cast<float>(kStorageTotalMiB),
515 static_cast<float>(kStorageTotalMiB - kStorageFreeMiB),
516 static_cast<float>(kStorageFreeMiB),
524 qCDebug(MockLinkCameraLog) <<
"Sent STORAGE_INFORMATION for compId:" << compId;
527void MockLinkCamera::_sendCameraCaptureStatus(uint8_t compId)
529 const CameraState *cam = _findCamera(compId);
535 (void) mavlink_msg_camera_capture_status_pack_chan(
542 cam->recording ? 1 : 0,
545 static_cast<float>(kStorageFreeMiB),
550 qCDebug(MockLinkCameraLog) <<
"Sent CAMERA_CAPTURE_STATUS for compId:" << compId
551 <<
"status:" << _imageCaptureStatusToString(cam->image_status)
552 <<
"interval:" << cam->image_interval
553 <<
"recording:" << cam->recording
554 <<
"images:" << cam->imagesCaptured;
557void MockLinkCamera::_sendCameraImageCaptured(uint8_t compId)
559 const CameraState *cam = _findCamera(compId);
566 const int32_t lat =
static_cast<int32_t
>(_mockLink->
vehicleLatitude() * 1e7);
567 const int32_t lon =
static_cast<int32_t
>(_mockLink->
vehicleLongitude() * 1e7);
569 const float q[4] = {1.0f, 0.0f, 0.0f, 0.0f};
570 const char fileUrl[MAVLINK_MSG_CAMERA_IMAGE_CAPTURED_FIELD_FILE_URL_LEN] = {};
573 (void) mavlink_msg_camera_image_captured_pack_chan(
580 (compId - MAV_COMP_ID_CAMERA) + 1,
591 qCDebug(MockLinkCameraLog) <<
"Sent CAMERA_IMAGE_CAPTURED for compId:" << compId
592 <<
"index:" << cam->imagesCaptured;
595void MockLinkCamera::_sendVideoStreamInformation(uint8_t compId, uint8_t streamId)
597 const int cameraIndex = compId - MAV_COMP_ID_CAMERA;
598 const QString name = QStringLiteral(
"Stream %1-%2").arg(cameraIndex + 1).arg(streamId);
599 QByteArray nameBA = name.toLocal8Bit();
600 nameBA.resize(MAVLINK_MSG_VIDEO_STREAM_INFORMATION_FIELD_NAME_LEN);
602 const QString uri = QStringLiteral(
"udp://127.0.0.1:5600");
603 QByteArray uriBA = uri.toLocal8Bit();
604 uriBA.resize(MAVLINK_MSG_VIDEO_STREAM_INFORMATION_FIELD_URI_LEN);
607 (void) mavlink_msg_video_stream_information_pack_chan(
614 VIDEO_STREAM_TYPE_RTPUDP,
615 VIDEO_STREAM_STATUS_FLAGS_RUNNING,
624 VIDEO_STREAM_ENCODING_H264,
628 qCDebug(MockLinkCameraLog) <<
"Sent VIDEO_STREAM_INFORMATION for compId:" << compId <<
"stream:" << streamId;
631void MockLinkCamera::_sendVideoStreamStatus(uint8_t compId, uint8_t streamId)
634 (void) mavlink_msg_video_stream_status_pack_chan(
640 VIDEO_STREAM_STATUS_FLAGS_RUNNING,
650 qCDebug(MockLinkCameraLog) <<
"Sent VIDEO_STREAM_STATUS for compId:" << compId <<
"stream:" << streamId;
653void MockLinkCamera::_sendCommandAck(uint8_t compId, uint16_t command, uint8_t result,
int requestedMsgId)
656 (void) mavlink_msg_command_ack_pack_chan(
670 QString logMsg = QStringLiteral(
"Sent COMMAND_ACK for compId: %1 command: %2 result: %3")
671 .arg(compId).arg(commandName).arg(result);
673 if (command == MAV_CMD_REQUEST_MESSAGE && requestedMsgId >= 0) {
674 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(
static_cast<uint32_t
>(requestedMsgId));
675 QString msgName = info ? info->name : QString::number(requestedMsgId);
676 logMsg += QStringLiteral(
" requestedMsg: %1").arg(msgName);
679 qCDebug(MockLinkCameraLog) << logMsg;
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
uint8_t mavlinkChannel() const
static MissionCommandTree * instance()
QString rawName(MAV_CMD command) const
Returns the raw name for the specified command.
void sendCameraHeartbeats()
Send heartbeats for all simulated camera components (call from 1Hz tasks)
bool handleMavlinkMessage(const mavlink_message_t &msg)
void run10HzTasks()
Update camera states (call from 10Hz tasks)
@ ImageCaptureInProgress
Single image capture in progress.
@ ImageCaptureInterval
Interval capture enabled.
@ ImageCaptureIntervalCapture
Interval capture with capture in progress.
@ ImageCaptureIdle
No capture in progress.
double vehicleAltitudeAMSL() const
void respondWithMavlinkMessage(const mavlink_message_t &msg)
Sends the specified mavlink message to QGC.
double vehicleLatitude() const
double vehicleLongitude() const
Per-camera simulated state.
qint64 singleShotStartMs
Timestamp when single-shot capture started (0 = not active)
uint8_t image_status
ImageCaptureStatus enum.