18#ifdef QGC_GST_STREAMING
22#include <QtCore/QFile>
23#include <QtCore/QJsonArray>
24#include <QtCore/QJsonDocument>
25#include <QtCore/QJsonObject>
26#include <QtCore/QMutexLocker>
28#include <QtCore/QRandomGenerator>
29#include <QtCore/QTemporaryFile>
30#include <QtCore/QThread>
31#include <QtCore/QTimer>
39std::atomic<
int>
MockLink::_nextVehicleSystemId{128};
41QList<MockLink::FlightMode_t> MockLink::_availableFlightModes = {
65 , _firmwareType(_mockConfig->firmwareType())
66 , _vehicleType(_mockConfig->vehicleType())
67 , _sendStatusText(_mockConfig->sendStatusText())
68 , _apmStartFreshParams(_mockConfig->apmStartFreshParams())
69 , _enableCamera(_mockConfig->enableCamera())
70 , _enableGimbal(_mockConfig->enableGimbal())
71 , _enableProximity(_mockConfig->enableProximity())
72 , _failureMode(_mockConfig->failureMode())
73 , _stayMavlinkV1(_mockConfig->stayMavlinkV1())
74 , _ftpCapability(_mockConfig->ftpCapability())
75 , _vehicleSystemId(_mockConfig->incrementVehicleId() ? _nextVehicleSystemId++ : static_cast<int>(_nextVehicleSystemId))
76 , _vehicleLatitude(_defaultVehicleLatitude + ((_vehicleSystemId - 128) * 0.0001))
77 , _vehicleLongitude(_defaultVehicleLongitude + ((_vehicleSystemId - 128) * 0.0001))
78 , _boardVendorId(_mockConfig->boardVendorId())
79 , _boardProductId(_mockConfig->boardProductId())
82 _mockConfig->cameraCaptureVideo(),
83 _mockConfig->cameraCaptureImage(),
84 _mockConfig->cameraHasModes(),
85 _mockConfig->cameraHasVideoStream(),
86 _mockConfig->cameraCanCaptureImageInVideoMode(),
87 _mockConfig->cameraCanCaptureVideoInImageMode(),
88 _mockConfig->cameraHasBasicZoom(),
89 _mockConfig->cameraHasTrackingPoint(),
90 _mockConfig->cameraHasTrackingRectangle())
93 _mockConfig->gimbalHasRollAxis(),
94 _mockConfig->gimbalHasPitchAxis(),
95 _mockConfig->gimbalHasYawAxis(),
96 _mockConfig->gimbalHasYawFollow(),
97 _mockConfig->gimbalHasYawLock(),
98 _mockConfig->gimbalHasRetract(),
99 _mockConfig->gimbalHasNeutral())
102 , _mockLinkFTP(new
MockLinkFTP(_vehicleSystemId, _vehicleComponentId, this))
103 , _requestedVideoStreamType(_mockConfig->videoStreamTypeEnum())
105 qCDebug(MockLinkLog) <<
this;
116 _adsbVehicles.reserve(_numberOfVehicles);
117 for (
int i = 0; i < _numberOfVehicles; ++i) {
118 ADSBVehicle vehicle{};
119 vehicle.angle = i * 72.0;
122 const double latOffset = 0.001 * i;
123 const double lonOffset = 0.001 * (i % 2 == 0 ? i : -i);
124 vehicle.coordinate = QGeoCoordinate(_defaultVehicleLatitude + latOffset, _defaultVehicleLongitude + lonOffset);
127 vehicle.altitude = _defaultVehicleHomeAltitude + (i * 5);
129 _adsbVehicles.append(vehicle);
135 _runningTime.start();
137 _workerThread =
new QThread(
this);
138 _workerThread->setObjectName(QStringLiteral(
"Mock_%1").arg(_mockConfig->
name()));
140 _worker->moveToThread(_workerThread);
142 (void) connect(_workerThread, &QThread::finished, _worker, &QObject::deleteLater);
143 _workerThread->start();
150 delete _mockLinkCamera;
151 delete _mockLinkGimbal;
152 delete _mockLinkPX4Calibration;
154 if (!_logDownloadFilename.isEmpty()) {
155 QFile::remove(_logDownloadFilename);
159 _workerThread->quit();
160 _workerThread->wait();
163 qCDebug(MockLinkLog) <<
this;
166bool MockLink::_connect()
170 _disconnectedEmitted =
false;
177 if (_mavlinkV2Upgraded) {
178 outgoingStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
180 outgoingStatus->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
183 incomingStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
185 _startVideoStreamServer();
201 if (_workerThread && _workerThread->isRunning()) {
202 _workerThread->quit();
203 _workerThread->wait();
207 if (_outgoingMavlinkChannelIsSet()) {
209 outgoingStatus->signing =
nullptr;
210 outgoingStatus->signing_streams =
nullptr;
211 mavlink_reset_channel_status(_outgoingMavlinkChannel);
216 _stopVideoStreamServer();
220 if (!_disconnectedEmitted.exchange(
true)) {
226void MockLink::_startVideoStreamServer()
228#ifdef QGC_GST_STREAMING
237 switch (_requestedVideoStreamType) {
263 if (!server->start(serverType, QStringLiteral(
"127.0.0.1"), port)) {
264 qCWarning(MockLinkLog) <<
"Failed to start mock video stream server for type" << _requestedVideoStreamType;
269 _videoStreamServer = server;
271 QMutexLocker locker(&_videoStreamMutex);
272 _servedVideoStreamType = _requestedVideoStreamType;
273 _videoStreamUri = server->servedUri();
275 qCDebug(MockLinkLog) <<
"Mock video stream server serving" << server->servedUri();
279void MockLink::_stopVideoStreamServer()
281#ifdef QGC_GST_STREAMING
285 QMutexLocker locker(&_videoStreamMutex);
287 _videoStreamUri.clear();
289 if (_videoStreamServer) {
290 delete _videoStreamServer;
291 _videoStreamServer =
nullptr;
298 QMutexLocker locker(&_videoStreamMutex);
299 type = _servedVideoStreamType;
300 uri = _videoStreamUri;
315 _sendBatteryStatus();
316 _sendNamedValueFloats();
319 if (_vehicleType != MAV_TYPE_SUBMARINE) {
320 _sendRemoteIDArmStatus();
322 _sendAvailableModesMonitor();
328 if (_enableProximity) {
329 _sendDistanceSensors();
345 if (_sendHomePositionDelayCount > 0) {
347 _sendHomePositionDelayCount--;
351 if (_availableModesMonitorSeqNumber == 0) {
352 qCDebug(MockLinkLog) <<
"Bumping sequence number for available modes monitor to trigger requery of modes";
353 _availableModesMonitorSeqNumber = 1;
366 const bool gpsDelayExpired = (_sendGPSPositionDelayCount == 0);
367 if (_sendGPSPositionDelayCount > 0) {
369 _sendGPSPositionDelayCount--;
372 if (_vehicleType != MAV_TYPE_SUBMARINE) {
374 _sendGlobalPositionInt();
376 _sendExtendedSysState();
379 _sendAttitudeQuaternion();
380 _sendAttitudeTarget();
381 _sendLocalPositionNed();
382 _sendPositionTargetLocalNed();
400 for (
int i = 0; i < paramSends; ++i) {
401 _paramRequestListWorker();
403 _logDownloadWorker();
404 _availableModesWorker();
405 _apmCompassCalWorker();
406 _apmAccelCalWorker();
412 _sendStatusTextMessages();
415bool MockLink::_allocateMavlinkChannel()
418 Q_ASSERT(!_incomingMavlinkChannelIsSet());
419 Q_ASSERT(!_outgoingMavlinkChannelIsSet());
423 qCWarning(MockLinkLog) <<
"LinkInterface::_allocateMavlinkChannel failed";
428 if (!_incomingMavlinkChannelIsSet()) {
429 qCWarning(MockLinkLog) <<
"_allocateMavlinkChannel aux failed";
435 if (!_outgoingMavlinkChannelIsSet()) {
436 qCWarning(MockLinkLog) <<
"_allocateMavlinkChannel vehicle failed";
442 qCDebug(MockLinkLog) <<
"_allocateMavlinkChannel aux:" << _incomingMavlinkChannel <<
"vehicle:" << _outgoingMavlinkChannel;
446void MockLink::_freeMavlinkChannel()
448 qCDebug(MockLinkLog) <<
"_freeMavlinkChannel aux:" << _incomingMavlinkChannel <<
"vehicle:" << _outgoingMavlinkChannel;
449 if (!_incomingMavlinkChannelIsSet()) {
450 Q_ASSERT(!_outgoingMavlinkChannelIsSet());
455 if (_outgoingMavlinkChannelIsSet()) {
457 mavlink_reset_channel_status(_outgoingMavlinkChannel);
461 mavlink_reset_channel_status(_incomingMavlinkChannel);
467bool MockLink::_incomingMavlinkChannelIsSet()
const
472bool MockLink::_outgoingMavlinkChannelIsSet()
const
477void MockLink::_loadParams()
480 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
481 if (_vehicleType == MAV_TYPE_FIXED_WING) {
482 paramFile.setFileName(
":/FirmwarePlugin/APM/Plane.OfflineEditing.params");
483 }
else if (_vehicleType == MAV_TYPE_SUBMARINE ) {
484 paramFile.setFileName(
":/FirmwarePlugin/APM/Sub.OfflineEditing.params");
485 }
else if (_vehicleType == MAV_TYPE_GROUND_ROVER ) {
486 paramFile.setFileName(
":/FirmwarePlugin/APM/Rover.OfflineEditing.params");
488 paramFile.setFileName(
":/FirmwarePlugin/APM/Copter.OfflineEditing.params");
490 }
else if (_firmwareType == MAV_AUTOPILOT_GENERIC) {
491 paramFile.setFileName(
":/MockLink/GenericMockLink.params");
493 paramFile.setFileName(
":/MockLink/PX4MockLink.params");
496 const bool success = paramFile.open(QFile::ReadOnly);
500 QTextStream paramStream(¶mFile);
501 while (!paramStream.atEnd()) {
502 const QString line = paramStream.readLine();
504 if (line.startsWith(
"#")) {
508 const QStringList paramData = line.split(
"\t");
509 Q_ASSERT(paramData.count() == 5);
511 const int compId = paramData.at(1).toInt();
512 const QString paramName = paramData.at(2);
513 const QString valStr = paramData.at(3);
514 const uint paramType = paramData.at(4).toUInt();
518 case MAV_PARAM_TYPE_REAL32:
521 case MAV_PARAM_TYPE_UINT32:
524 case MAV_PARAM_TYPE_INT32:
527 case MAV_PARAM_TYPE_UINT16:
528 paramValue = QVariant((quint16)valStr.toUInt());
530 case MAV_PARAM_TYPE_INT16:
531 paramValue = QVariant((qint16)valStr.toInt());
533 case MAV_PARAM_TYPE_UINT8:
534 paramValue = QVariant((quint8)valStr.toUInt());
536 case MAV_PARAM_TYPE_INT8:
537 paramValue = QVariant((qint8)valStr.toUInt());
540 qCCritical(MockLinkVerboseLog) <<
"Unknown type" << paramType;
545 qCDebug(MockLinkVerboseLog) <<
"Loading param" << paramName <<
paramValue;
547 _mapParamName2Value[compId][paramName] =
paramValue;
548 _mapParamName2MavParamType[compId][paramName] =
static_cast<MAV_PARAM_TYPE
>(paramType);
551 if ((_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) && _apmStartFreshParams) {
552 _applyAPMFreshFlashState();
566void MockLink::_resetParamsToDefaults()
568 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
572 for (
auto compIt = _mapParamName2Value.begin(); compIt != _mapParamName2Value.end(); ++compIt) {
573 for (
auto paramIt = compIt.value().begin(); paramIt != compIt.value().end(); ++paramIt) {
574 if (kAPMCalOffsetParams.contains(paramIt.key())) {
575 paramIt.value() = QVariant(0.0f);
582 if (_firmwareType != MAV_AUTOPILOT_PX4) {
583 qCWarning(MockLinkLog) <<
"Param reset to defaults not supported for firmware type" << _firmwareType;
587 QFile metaDataFile(QStringLiteral(
":/MockLink/Parameter.MetaData.json"));
588 if (!metaDataFile.open(QFile::ReadOnly)) {
589 qCWarning(MockLinkLog) <<
"Unable to open parameter metadata for reset" << metaDataFile.fileName();
593 QJsonParseError parseError{};
594 const QJsonDocument doc = QJsonDocument::fromJson(metaDataFile.readAll(), &parseError);
595 if (parseError.error != QJsonParseError::NoError) {
596 qCWarning(MockLinkLog) <<
"Unable to parse parameter metadata for reset:" << parseError.errorString();
600 QHash<QString, QVariant> defaults;
601 const QJsonArray parameters = doc.object().value(QStringLiteral(
"parameters")).toArray();
602 for (
const QJsonValue ¶meter : parameters) {
603 const QJsonObject paramObject = parameter.toObject();
604 if (paramObject.contains(QStringLiteral(
"default"))) {
605 defaults[paramObject.value(QStringLiteral(
"name")).toString()] = paramObject.value(QStringLiteral(
"default")).toVariant();
609 for (
auto compIt = _mapParamName2Value.begin(); compIt != _mapParamName2Value.end(); ++compIt) {
610 const int compId = compIt.key();
611 for (
auto paramIt = compIt.value().begin(); paramIt != compIt.value().end(); ++paramIt) {
612 const QString ¶mName = paramIt.key();
613 if (!_resetSysAutostartOnParamReset && (paramName == QLatin1String(
"SYS_AUTOSTART"))) {
616 const auto defaultIt = defaults.constFind(paramName);
617 if (defaultIt == defaults.constEnd()) {
620 switch (_mapParamName2MavParamType[compId][paramName]) {
621 case MAV_PARAM_TYPE_REAL32:
622 paramIt.value() = QVariant(defaultIt->toFloat());
624 case MAV_PARAM_TYPE_UINT32:
625 paramIt.value() = QVariant(defaultIt->toUInt());
627 case MAV_PARAM_TYPE_INT32:
628 paramIt.value() = QVariant(defaultIt->toInt());
630 case MAV_PARAM_TYPE_UINT16:
631 paramIt.value() = QVariant(
static_cast<quint16
>(defaultIt->toUInt()));
633 case MAV_PARAM_TYPE_INT16:
634 paramIt.value() = QVariant(
static_cast<qint16
>(defaultIt->toInt()));
636 case MAV_PARAM_TYPE_UINT8:
637 paramIt.value() = QVariant(
static_cast<quint8
>(defaultIt->toUInt()));
639 case MAV_PARAM_TYPE_INT8:
640 paramIt.value() = QVariant(
static_cast<qint8
>(defaultIt->toInt()));
643 qCWarning(MockLinkLog) <<
"Param reset skipped unhandled type" << _mapParamName2MavParamType[compId][paramName] << paramName;
653void MockLink::_applyAPMFreshFlashState()
656 _resetParamsToDefaults();
658 auto ¶mMap = _mapParamName2Value[MAV_COMP_ID_AUTOPILOT1];
659 const auto ¶mTypeMap = _mapParamName2MavParamType[MAV_COMP_ID_AUTOPILOT1];
662 auto setIntParam = [¶mMap, ¶mTypeMap](
const QString ¶mName,
int value) {
663 if (!paramMap.contains(paramName)) {
666 switch (paramTypeMap.value(paramName)) {
667 case MAV_PARAM_TYPE_UINT32: paramMap[paramName] = QVariant(
static_cast<quint32
>(value));
break;
668 case MAV_PARAM_TYPE_INT32: paramMap[paramName] = QVariant(value);
break;
669 case MAV_PARAM_TYPE_UINT16: paramMap[paramName] = QVariant(
static_cast<quint16
>(value));
break;
670 case MAV_PARAM_TYPE_INT16: paramMap[paramName] = QVariant(
static_cast<qint16
>(value));
break;
671 case MAV_PARAM_TYPE_UINT8: paramMap[paramName] = QVariant(
static_cast<quint8
>(value));
break;
672 case MAV_PARAM_TYPE_INT8: paramMap[paramName] = QVariant(
static_cast<qint8
>(value));
break;
673 default: paramMap[paramName] = QVariant(value);
break;
678 setIntParam(QStringLiteral(
"FRAME_CLASS"), 0);
681 const QStringList rcMapParams = {
682 QStringLiteral(
"RCMAP_ROLL"),
683 QStringLiteral(
"RCMAP_PITCH"),
684 QStringLiteral(
"RCMAP_YAW"),
685 QStringLiteral(
"RCMAP_THROTTLE"),
687 for (
const QString &mapParam : rcMapParams) {
688 if (!paramMap.contains(mapParam)) {
691 const int channel = paramMap[mapParam].toInt();
692 setIntParam(QStringLiteral(
"RC%1_MIN").arg(channel), 1100);
693 setIntParam(QStringLiteral(
"RC%1_MAX").arg(channel), 1900);
694 setIntParam(QStringLiteral(
"RC%1_TRIM").arg(channel), 1500);
698void MockLink::_sendHeartBeat()
701 (void) mavlink_msg_heartbeat_pack_chan(
704 _outgoingMavlinkChannel,
715void MockLink::_sendHighLatency2()
717 qCDebug(MockLinkLog) <<
"Sending" << _mavCustomMode;
720 px4_cm.
data = _mavCustomMode;
723 (void) mavlink_msg_high_latency2_pack_chan(
726 _outgoingMavlinkChannel,
731 px4_cm.custom_mode_hl,
732 static_cast<int32_t
>(_vehicleLatitude * 1E7),
733 static_cast<int32_t
>(_vehicleLongitude * 1E7),
734 static_cast<int16_t
>(_vehicleAltitudeAMSL),
735 static_cast<int16_t
>(_vehicleAltitudeAMSL),
757void MockLink::_sendSysStatus()
760 (void) mavlink_msg_sys_status_pack_chan(
763 _outgoingMavlinkChannel,
765 MAV_SYS_STATUS_SENSOR_GPS,
771 _battery1PctRemaining,
777void MockLink::_sendBatteryStatus()
779 if (_battery1PctRemaining > 1) {
780 _battery1PctRemaining =
static_cast<int8_t
>(100 - (_runningTime.elapsed() / 1000));
781 _battery1TimeRemaining =
static_cast<double>(_batteryMaxTimeRemaining) * (
static_cast<double>(_battery1PctRemaining) / 100.0);
782 if (_battery1PctRemaining > 50) {
783 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_OK;
784 }
else if (_battery1PctRemaining > 30) {
785 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_LOW;
786 }
else if (_battery1PctRemaining > 20) {
787 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_CRITICAL;
789 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_EMERGENCY;
793 if (_battery2PctRemaining > 1) {
794 _battery2PctRemaining =
static_cast<int8_t
>(100 - ((_runningTime.elapsed() / 1000) / 2));
795 _battery2TimeRemaining =
static_cast<double>(_batteryMaxTimeRemaining) * (
static_cast<double>(_battery2PctRemaining) / 100.0);
796 if (_battery2PctRemaining > 50) {
797 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_OK;
798 }
else if (_battery2PctRemaining > 30) {
799 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_LOW;
800 }
else if (_battery2PctRemaining > 20) {
801 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_CRITICAL;
803 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_EMERGENCY;
808 uint16_t rgVoltages[10]{};
809 uint16_t rgVoltagesNone[10]{};
810 uint16_t rgVoltagesExtNone[4]{};
812 for (
size_t i = 0; i < std::size(rgVoltages); i++) {
813 rgVoltages[i] = UINT16_MAX;
814 rgVoltagesNone[i] = UINT16_MAX;
816 rgVoltages[0] = rgVoltages[1] = rgVoltages[2] = 4200;
818 (void) mavlink_msg_battery_status_pack_chan(
821 _outgoingMavlinkChannel,
824 MAV_BATTERY_FUNCTION_ALL,
825 MAV_BATTERY_TYPE_LIPO,
831 _battery1PctRemaining,
832 _battery1TimeRemaining,
833 _battery1ChargeState,
840 (void) mavlink_msg_battery_status_pack_chan(
843 _outgoingMavlinkChannel,
846 MAV_BATTERY_FUNCTION_ALL,
847 MAV_BATTERY_TYPE_LIPO,
853 _battery2PctRemaining,
854 _battery2TimeRemaining,
855 _battery2ChargeState,
863void MockLink::_sendNamedValueFloats()
865 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
868 const float sinVal =
static_cast<float>(std::sin(
static_cast<double>(timeBootMs) / 1000.0));
869 const float cosVal =
static_cast<float>(std::cos(
static_cast<double>(timeBootMs) / 1000.0));
872 static constexpr char kSinName[10] =
"sin_wave";
873 static constexpr char kCosName[10] =
"cos_wave";
876 (void) mavlink_msg_named_value_float_pack_chan(
879 _outgoingMavlinkChannel,
887 (void) mavlink_msg_named_value_float_pack_chan(
890 _outgoingMavlinkChannel,
899void MockLink::_sendVibration()
902 (void) mavlink_msg_vibration_pack_chan(
905 _outgoingMavlinkChannel,
918void MockLink::_sendDistanceSensors()
922 static constexpr MAV_SENSOR_ORIENTATION rgOrientations[] = {
923 MAV_SENSOR_ROTATION_NONE,
924 MAV_SENSOR_ROTATION_YAW_45,
925 MAV_SENSOR_ROTATION_YAW_90,
926 MAV_SENSOR_ROTATION_YAW_180,
927 MAV_SENSOR_ROTATION_YAW_270,
928 MAV_SENSOR_ROTATION_YAW_315,
931 static constexpr uint16_t minDistanceCm = 20;
932 static constexpr uint16_t maxDistanceCm = 4000;
934 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
935 const float quaternion[4]{};
937 for (
size_t i = 0; i < std::size(rgOrientations); i++) {
939 const double sweep = std::sin((timeBootMs / 5000.0) + (i * M_PI / 4));
940 const uint16_t currentDistanceCm =
static_cast<uint16_t
>(2000 + (1500 * sweep));
943 (void) mavlink_msg_distance_sensor_pack_chan(
946 _outgoingMavlinkChannel,
952 MAV_DISTANCE_SENSOR_LASER,
953 static_cast<uint8_t
>(i),
968 uint8_t buffer[MAVLINK_MAX_PACKET_LEN]{};
969 const int cBuffer = mavlink_msg_to_send_buffer(buffer, &msg);
970 const QByteArray bytes(
reinterpret_cast<char*
>(buffer), cBuffer);
975void MockLink::_writeBytes(
const QByteArray &bytes)
981void MockLink::_writeBytesQueued(
const QByteArray &bytes)
984 qCDebug(MockLinkLog) <<
"Dropping queued bytes on disconnected/uninitialized mock link";
989 _handleIncomingNSHBytes(bytes.constData(), bytes.length());
993 if (bytes.startsWith(QByteArrayLiteral(
"\r\r\r"))) {
995 _handleIncomingNSHBytes(&bytes.constData()[3], bytes.length() - 3);
998 _handleIncomingMavlinkBytes(
reinterpret_cast<const uint8_t*
>(bytes.constData()), bytes.length());
1001void MockLink::_handleIncomingNSHBytes(
const char *bytes,
int cBytes)
1006 if ((cBytes == 4) && (bytes[0] ==
'\r') && (bytes[1] ==
'\r') && (bytes[2] ==
'\r')) {
1012 qCDebug(MockLinkLog) <<
"NSH:" << bytes;
1015 if (strncmp(bytes,
"sh /etc/init.d/rc.usb\n", cBytes) == 0) {
1017 _mavlinkStarted =
true;
1023void MockLink::_handleIncomingMavlinkBytes(
const uint8_t *bytes,
int cBytes)
1026 mavlink_status_t comm{};
1028 QMutexLocker lock(&_incomingMavlinkMutex);
1029 for (qint64 i = 0; i < cBytes; i++) {
1030 const int parsed = mavlink_parse_char(_incomingMavlinkChannel, bytes[i], &msg, &comm);
1034 if (!_mavlinkV2Upgraded && !_stayMavlinkV1 && (msg.magic == MAVLINK_STX)) {
1036 _mavlinkV2Upgraded =
true;
1038 outgoingStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
1039 qCDebug(MockLinkLog) <<
"Received MAVLink v2 message from GCS, upgrading outgoing traffic to v2";
1042 _handleIncomingMavlinkMsg(msg);
1049 _receivedMavlinkMessageCountMap[msg.msgid]++;
1050 _lastReceivedMavlinkMessageMap[msg.msgid] = msg;
1053 if (msg.msgid == MAVLINK_MSG_ID_COMMAND_LONG) {
1055 mavlink_msg_command_long_decode(&msg, &request);
1057 _receivedMavCommandCountMap[
static_cast<MAV_CMD
>(request.command)]++;
1058 _receivedMavCommandByCompCountMap[
static_cast<MAV_CMD
>(request.command)][request.target_component]++;
1060 if (request.command == MAV_CMD_REQUEST_MESSAGE) {
1061 _receivedRequestMessageCountMap[
static_cast<uint32_t
>(request.param1)]++;
1062 _receivedRequestMessageByCompAndMsgCountMap[request.target_component][
static_cast<int>(request.param1)]++;
1064 }
else if (msg.msgid == MAVLINK_MSG_ID_COMMAND_INT) {
1065 mavlink_command_int_t request{};
1066 mavlink_msg_command_int_decode(&msg, &request);
1068 _receivedMavCommandCountMap[
static_cast<MAV_CMD
>(request.command)]++;
1069 _receivedMavCommandByCompCountMap[
static_cast<MAV_CMD
>(request.command)][request.target_component]++;
1075 _updateIncomingMessageCounts(msg);
1089 switch (msg.msgid) {
1090 case MAVLINK_MSG_ID_HEARTBEAT:
1091 _handleHeartBeat(msg);
1093 case MAVLINK_MSG_ID_PARAM_REQUEST_LIST:
1094 _handleParamRequestList(msg);
1096 case MAVLINK_MSG_ID_SET_MODE:
1097 _handleSetMode(msg);
1099 case MAVLINK_MSG_ID_PARAM_SET:
1100 _handleParamSet(msg);
1102 case MAVLINK_MSG_ID_PARAM_REQUEST_READ:
1103 _handleParamRequestRead(msg);
1105 case MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL:
1108 case MAVLINK_MSG_ID_COMMAND_LONG:
1109 _handleCommandLong(msg);
1111 case MAVLINK_MSG_ID_COMMAND_INT:
1112 _handleCommandInt(msg);
1114 case MAVLINK_MSG_ID_MANUAL_CONTROL:
1115 _handleManualControl(msg);
1117 case MAVLINK_MSG_ID_RC_CHANNELS_OVERRIDE:
1118 _handleRCChannelsOverride(msg);
1120 case MAVLINK_MSG_ID_LOG_REQUEST_LIST:
1121 _handleLogRequestList(msg);
1123 case MAVLINK_MSG_ID_LOG_REQUEST_DATA:
1124 _handleLogRequestData(msg);
1126 case MAVLINK_MSG_ID_LOG_ERASE:
1127 _handleLogErase(msg);
1129 case MAVLINK_MSG_ID_PARAM_MAP_RC:
1130 _handleParamMapRC(msg);
1132 case MAVLINK_MSG_ID_SETUP_SIGNING:
1133 _handleSetupSigning(msg);
1135 case MAVLINK_MSG_ID_COMMAND_ACK:
1138 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1140 mavlink_msg_command_ack_decode(&msg, &ack);
1141 if (ack.command == 0) {
1142 QMutexLocker locker(&_apmAccelCalMutex);
1143 if (_apmAccelCalPosIndex >= 0 && _apmAccelCalPosIndex < 6) {
1144 _apmAccelCalGotAck =
true;
1157 qCDebug(MockLinkVerboseLog) <<
"Heartbeat";
1162 mavlink_param_map_rc_t paramMapRC{};
1163 mavlink_msg_param_map_rc_decode(&msg, ¶mMapRC);
1165 const QString paramName(QString::fromLocal8Bit(paramMapRC.param_id,
static_cast<int>(strnlen(paramMapRC.param_id, MAVLINK_MSG_PARAM_MAP_RC_FIELD_PARAM_ID_LEN))));
1167 if (paramMapRC.param_index == -1) {
1168 qCDebug(MockLinkLog) << QStringLiteral(
"MockLink - PARAM_MAP_RC: param(%1) tuningID(%2) centerValue(%3) scale(%4) min(%5) max(%6)").arg(paramName).arg(paramMapRC.parameter_rc_channel_index).arg(paramMapRC.param_value0).arg(paramMapRC.scale).arg(paramMapRC.param_value_min).arg(paramMapRC.param_value_max);
1169 }
else if (paramMapRC.param_index == -2) {
1170 qCDebug(MockLinkLog) <<
"MockLink - PARAM_MAP_RC: Clear tuningID" << paramMapRC.parameter_rc_channel_index;
1172 qCWarning(MockLinkLog) <<
"MockLink - PARAM_MAP_RC: Unsupported param_index" << paramMapRC.param_index;
1179 mavlink_msg_setup_signing_decode(&msg, &setupSigning);
1181 if (setupSigning.target_system != _vehicleSystemId) {
1186 bool allZeroKey =
true;
1187 for (
const uint8_t
byte : setupSigning.secret_key) {
1194 _signingEnabled = !allZeroKey;
1198 if (_signingEnabled) {
1199 memcpy(_mockSigning.secret_key, setupSigning.secret_key,
sizeof(_mockSigning.secret_key));
1200 _mockSigning.link_id = _outgoingMavlinkChannel;
1201 _mockSigning.flags = MAVLINK_SIGNING_FLAG_SIGN_OUTGOING;
1204 status->signing = &_mockSigning;
1205 status->signing_streams = &_mockSigningStreams;
1207 QGC::secureZero(_mockSigning.secret_key,
sizeof(_mockSigning.secret_key));
1208 _mockSigning.accept_unsigned_callback =
nullptr;
1209 status->signing =
nullptr;
1210 status->signing_streams =
nullptr;
1213 qCDebug(MockLinkLog) <<
"Signing" << (_signingEnabled ?
"enabled" :
"disabled");
1218 mavlink_set_mode_t request{};
1219 mavlink_msg_set_mode_decode(&msg, &request);
1221 Q_ASSERT(request.target_system == _vehicleSystemId);
1223 _mavBaseMode = request.base_mode;
1224 _mavCustomMode = request.custom_mode;
1229 mavlink_manual_control_t manualControl{};
1230 mavlink_msg_manual_control_decode(&msg, &manualControl);
1233 const auto axisStr = [](int16_t v) -> QString {
1234 return (v == INT16_MAX) ? QStringLiteral(
"invalid") : QString::number(v);
1237 const auto extStr = [](int16_t v,
bool enabled) -> QString {
1238 return enabled ? QString::number(v) : QStringLiteral(
"disabled");
1241 const uint8_t ext = manualControl.enabled_extensions;
1243 qCDebug(MockLinkVerboseLog).noquote()
1245 <<
"target:" << manualControl.target
1246 <<
"x:" << axisStr(manualControl.x)
1247 <<
"y:" << axisStr(manualControl.y)
1248 <<
"z:" << axisStr(manualControl.z)
1249 <<
"r:" << axisStr(manualControl.r)
1250 <<
"buttons:" << QStringLiteral(
"0x%1").arg(manualControl.buttons, 4, 16, QLatin1Char(
'0'))
1251 <<
"buttons2:" << QStringLiteral(
"0x%1").arg(manualControl.buttons2, 4, 16, QLatin1Char(
'0'))
1252 <<
"enabled_extensions:" << QStringLiteral(
"0x%1").arg(ext, 2, 16, QLatin1Char(
'0'))
1253 <<
"s(pitch):" << extStr(manualControl.s, ext & (1 << 0))
1254 <<
"t(roll):" << extStr(manualControl.t, ext & (1 << 1))
1255 <<
"aux1:" << extStr(manualControl.aux1, ext & (1 << 2))
1256 <<
"aux2:" << extStr(manualControl.aux2, ext & (1 << 3))
1257 <<
"aux3:" << extStr(manualControl.aux3, ext & (1 << 4))
1258 <<
"aux4:" << extStr(manualControl.aux4, ext & (1 << 5))
1259 <<
"aux5:" << extStr(manualControl.aux5, ext & (1 << 6))
1260 <<
"aux6:" << extStr(manualControl.aux6, ext & (1 << 7));
1265 mavlink_rc_channels_override_t
override{};
1266 mavlink_msg_rc_channels_override_decode(&msg, &
override);
1271 const uint16_t rawValues[18] = {
1272 override.chan1_raw,
override.chan2_raw,
override.chan3_raw,
override.chan4_raw,
1273 override.chan5_raw,
override.chan6_raw,
override.chan7_raw,
override.chan8_raw,
1274 override.chan9_raw,
override.chan10_raw,
override.chan11_raw,
override.chan12_raw,
1275 override.chan13_raw,
override.chan14_raw,
override.chan15_raw,
override.chan16_raw,
1276 override.chan17_raw,
override.chan18_raw,
1279 bool anyChange =
false;
1280 for (
int i = 0; i < kRcChannelOverrideChannelCount; ++i) {
1281 const uint16_t raw = rawValues[i];
1282 const bool isExtended = (i >= 8);
1284 RCChannelOverride::State newState;
1286 if (raw == 0 || raw == UINT16_MAX) {
1288 }
else if (raw ==
static_cast<uint16_t
>(UINT16_MAX - 1)) {
1289 newState = RCChannelOverride::State::Released;
1291 newState = RCChannelOverride::State::Overridden;
1294 if (raw == UINT16_MAX) {
1296 }
else if (raw == 0) {
1297 newState = RCChannelOverride::State::Released;
1299 newState = RCChannelOverride::State::Overridden;
1303 RCChannelOverride &ch = _rcChannelOverrides[i];
1304 if (ch.state == newState) {
1310 const auto stateLabel = [](RCChannelOverride::State s) ->
const char * {
1312 case RCChannelOverride::State::Ignore:
return "ignore";
1313 case RCChannelOverride::State::Released:
return "released";
1314 case RCChannelOverride::State::Overridden:
return "overridden";
1318 qCDebug(MockLinkLog).noquote() << QStringLiteral(
"RC_CHANNELS_OVERRIDE ch%1: %2 -> %3").arg(i + 1).arg(stateLabel(ch.state)).arg(stateLabel(newState));
1320 ch.state = newState;
1321 ch.value = (newState == RCChannelOverride::State::Overridden) ? raw : 0;
1326 for (
int i = 0; i < kRcChannelOverrideChannelCount; ++i) {
1327 if (_rcChannelOverrides[i].state == RCChannelOverride::State::Overridden) {
1328 active << QStringLiteral(
"ch%1").arg(i + 1);
1331 if (active.isEmpty()) {
1332 qCDebug(MockLinkLog) <<
"RC_CHANNELS_OVERRIDE: no channels currently overridden";
1334 qCDebug(MockLinkLog).noquote() <<
"RC_CHANNELS_OVERRIDE active overrides:" << active.join(QStringLiteral(
", "));
1338 for (
int i = 0; i < kRcChannelOverrideChannelCount; ++i) {
1339 if (_rcChannelOverrides[i].state == RCChannelOverride::State::Overridden) {
1340 qCDebug(MockLinkVerboseLog).noquote() << QStringLiteral(
"RC_CHANNELS_OVERRIDE ch%1 value: %2").arg(i + 1).arg(_rcChannelOverrides[i].value);
1345void MockLink::_setParamFloatUnionIntoMap(
int componentId,
const QString ¶mName,
float paramFloat)
1347 Q_ASSERT(_mapParamName2Value.contains(componentId));
1348 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
1349 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
1351 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
1352 QVariant paramVariant;
1354 valueUnion.param_float = paramFloat;
1355 switch (paramType) {
1356 case MAV_PARAM_TYPE_REAL32:
1357 paramVariant = QVariant::fromValue(valueUnion.param_float);
1359 case MAV_PARAM_TYPE_UINT32:
1360 paramVariant = QVariant::fromValue(valueUnion.param_uint32);
1362 case MAV_PARAM_TYPE_INT32:
1363 paramVariant = QVariant::fromValue(valueUnion.param_int32);
1365 case MAV_PARAM_TYPE_UINT16:
1366 paramVariant = QVariant::fromValue(valueUnion.param_uint16);
1368 case MAV_PARAM_TYPE_INT16:
1369 paramVariant = QVariant::fromValue(valueUnion.param_int16);
1371 case MAV_PARAM_TYPE_UINT8:
1372 paramVariant = QVariant::fromValue(valueUnion.param_uint8);
1374 case MAV_PARAM_TYPE_INT8:
1375 paramVariant = QVariant::fromValue(valueUnion.param_int8);
1378 qCCritical(MockLinkLog) <<
"Invalid parameter type" << paramType;
1379 paramVariant = QVariant::fromValue(valueUnion.param_int32);
1383 qCDebug(MockLinkLog) <<
"_setParamFloatUnionIntoMap" << paramName << paramVariant;
1384 _mapParamName2Value[componentId][paramName] = paramVariant;
1390 valueUnion.param_float = value;
1391 _setParamFloatUnionIntoMap(componentId, paramName, valueUnion.param_float);
1394float MockLink::_floatUnionForParam(
int componentId,
const QString ¶mName)
1396 Q_ASSERT(_mapParamName2Value.contains(componentId));
1397 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
1398 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
1400 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
1401 const QVariant paramVar = _mapParamName2Value[componentId][paramName];
1404 switch (paramType) {
1405 case MAV_PARAM_TYPE_REAL32:
1406 valueUnion.param_float = paramVar.toFloat();
1408 case MAV_PARAM_TYPE_UINT32:
1409 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1410 valueUnion.param_float = paramVar.toUInt();
1412 valueUnion.param_uint32 = paramVar.toUInt();
1415 case MAV_PARAM_TYPE_INT32:
1416 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1417 valueUnion.param_float = paramVar.toInt();
1419 valueUnion.param_int32 = paramVar.toInt();
1422 case MAV_PARAM_TYPE_UINT16:
1423 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1424 valueUnion.param_float = paramVar.toUInt();
1426 valueUnion.param_uint16 = paramVar.toUInt();
1429 case MAV_PARAM_TYPE_INT16:
1430 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1431 valueUnion.param_float = paramVar.toInt();
1433 valueUnion.param_int16 = paramVar.toInt();
1436 case MAV_PARAM_TYPE_UINT8:
1437 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1438 valueUnion.param_float = paramVar.toUInt();
1440 valueUnion.param_uint8 = paramVar.toUInt();
1443 case MAV_PARAM_TYPE_INT8:
1444 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1445 valueUnion.param_float = (
unsigned char)paramVar.toChar().toLatin1();
1447 valueUnion.param_int8 = (
unsigned char)paramVar.toChar().toLatin1();
1451 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1452 valueUnion.param_float = paramVar.toInt();
1454 valueUnion.param_int32 = paramVar.toInt();
1456 qCCritical(MockLinkLog) <<
"Invalid parameter type" << paramType;
1459 return valueUnion.param_float;
1462uint32_t MockLink::_computeParamHash(
int componentId)
const
1467 static const QSet<QString> volatileParams = []() {
1468 QSet<QString> volatiles;
1469 QFile metaDataFile(QStringLiteral(
":/MockLink/Parameter.MetaData.json"));
1470 if (metaDataFile.open(QFile::ReadOnly)) {
1471 QJsonParseError parseError{};
1472 const QJsonDocument doc = QJsonDocument::fromJson(metaDataFile.readAll(), &parseError);
1473 if (parseError.error != QJsonParseError::NoError) {
1474 qCWarning(MockLinkLog) <<
"Unable to parse parameter metadata for volatile param list:" << parseError.errorString();
1476 const QJsonArray parameters = doc.object().value(QStringLiteral(
"parameters")).toArray();
1477 for (
const QJsonValue ¶meter : parameters) {
1478 const QJsonObject paramObject = parameter.toObject();
1479 if (paramObject.value(QStringLiteral(
"volatile")).toBool()) {
1480 volatiles.insert(paramObject.value(QStringLiteral(
"name")).toString());
1484 qCWarning(MockLinkLog) <<
"Unable to open parameter metadata for volatile param list" << metaDataFile.fileName();
1490 const auto ¶ms = _mapParamName2Value[componentId];
1491 for (
auto it = params.constBegin(); it != params.constEnd(); ++it) {
1492 const QString &name = it.key();
1493 if (volatileParams.contains(name)) {
1496 const QVariant &value = it.value();
1497 const MAV_PARAM_TYPE mavType = _mapParamName2MavParamType[componentId][name];
1500 crc =
QGC::crc32(
reinterpret_cast<const uint8_t *
>(qPrintable(name)), name.length(), crc);
1512 mavlink_param_request_list_t request{};
1513 mavlink_msg_param_request_list_decode(&msg, &request);
1515 Q_ASSERT(request.target_system == _vehicleSystemId);
1516 Q_ASSERT(request.target_component == MAV_COMP_ID_ALL);
1520 QMutexLocker locker(&_paramRequestListMutex);
1521 _paramRequestListComponentIds = _mapParamName2Value.keys();
1522 if (!_paramRequestListComponentIds.isEmpty()) {
1523 _paramRequestListParamNames = _mapParamName2Value[_paramRequestListComponentIds.first()].keys();
1527 _currentParamRequestListComponentIndex = 0;
1528 _currentParamRequestListParamIndex = 0;
1529 _paramRequestListHashCheckSent =
false;
1532void MockLink::_paramRequestListWorker()
1534 if (_currentParamRequestListComponentIndex == -1) {
1540 QMutexLocker locker(&_paramRequestListMutex);
1543 if (_currentParamRequestListComponentIndex >= _paramRequestListComponentIds.count()) {
1544 _currentParamRequestListComponentIndex = -1;
1548 const int componentId = _paramRequestListComponentIds.at(_currentParamRequestListComponentIndex);
1549 const int cParameters = _paramRequestListParamNames.count();
1551 if (_currentParamRequestListParamIndex >= cParameters) {
1555 if (_firmwareType == MAV_AUTOPILOT_PX4 && !_paramRequestListHashCheckSent) {
1556 _paramRequestListHashCheckSent =
true;
1559 valueUnion.type = MAV_PARAM_TYPE_UINT32;
1560 valueUnion.param_uint32 = _computeParamHash(componentId);
1562 char paramId[MAVLINK_MSG_ID_PARAM_VALUE_LEN]{};
1563 (void) strncpy(paramId,
"_HASH_CHECK", MAVLINK_MSG_ID_PARAM_VALUE_LEN);
1565 qCDebug(MockLinkLog) <<
"Sending _HASH_CHECK in PARAM_REQUEST_LIST stream" << componentId <<
"hash:" << valueUnion.param_uint32;
1568 (void) mavlink_msg_param_value_pack_chan(
1571 _outgoingMavlinkChannel,
1574 valueUnion.param_float,
1575 MAV_PARAM_TYPE_UINT32,
1584 if (++_currentParamRequestListComponentIndex >= _paramRequestListComponentIds.count()) {
1585 _currentParamRequestListComponentIndex = -1;
1586 _paramRequestListComponentIds.clear();
1587 _paramRequestListParamNames.clear();
1590 _paramRequestListParamNames = _mapParamName2Value[_paramRequestListComponentIds.at(_currentParamRequestListComponentIndex)].keys();
1591 _currentParamRequestListParamIndex = 0;
1592 _paramRequestListHashCheckSent =
false;
1597 const QString ¶mName = _paramRequestListParamNames.at(_currentParamRequestListParamIndex);
1600 qCDebug(MockLinkLog) <<
"Skipping param send:" << paramName;
1602 char paramId[MAVLINK_MSG_ID_PARAM_VALUE_LEN]{};
1605 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
1606 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
1608 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
1610 Q_ASSERT(paramName.length() <= MAVLINK_MSG_ID_PARAM_VALUE_LEN);
1611 (void) strncpy(paramId, paramName.toLocal8Bit().constData(), MAVLINK_MSG_ID_PARAM_VALUE_LEN);
1613 qCDebug(MockLinkLog) <<
"Sending msg_param_value" << componentId << paramId << paramType << _mapParamName2Value[componentId][paramId];
1615 (void) mavlink_msg_param_value_pack_chan(
1618 _outgoingMavlinkChannel,
1621 _floatUnionForParam(componentId, paramName),
1624 _currentParamRequestListParamIndex
1630 ++_currentParamRequestListParamIndex;
1635 mavlink_param_set_t request{};
1636 mavlink_msg_param_set_decode(&msg, &request);
1638 Q_ASSERT(request.target_system == _vehicleSystemId);
1639 const int componentId = request.target_component;
1642 char paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN + 1]{};
1643 paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN] = 0;
1644 (void) strncpy(paramId, request.param_id, MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN);
1646 qCDebug(MockLinkLog) <<
"_handleParamSet" << componentId << paramId << request.param_type;
1651 if ((_firmwareType == MAV_AUTOPILOT_PX4) && (strncmp(paramId,
"_HASH_CHECK", MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN) == 0)) {
1652 QMutexLocker locker(&_paramRequestListMutex);
1653 _currentParamRequestListComponentIndex = -1;
1654 _paramRequestListComponentIds.clear();
1655 _paramRequestListParamNames.clear();
1656 qCDebug(MockLinkLog) <<
"Received _HASH_CHECK PARAM_SET, stopping parameter stream";
1663 if (!_mapParamName2Value.contains(componentId) || !_mapParamName2MavParamType.contains(componentId)) {
1664 qCDebug(MockLinkLog) <<
"_handleParamSet unknown component, rejecting with PARAM_ERROR - componentId:" << componentId <<
"param:" << paramId;
1665 _sendParamError(componentId, paramId, -1, MAV_PARAM_ERROR_COMPONENT_NOT_FOUND);
1668 if (!_mapParamName2Value[componentId].contains(paramId)) {
1669 qCDebug(MockLinkLog) <<
"_handleParamSet unknown param, rejecting with PARAM_ERROR - componentId:" << componentId <<
"param:" << paramId;
1670 _sendParamError(componentId, paramId, -1, MAV_PARAM_ERROR_DOES_NOT_EXIST);
1673 if (request.param_type != _mapParamName2MavParamType[componentId][paramId]) {
1674 qCDebug(MockLinkLog) <<
"_handleParamSet type mismatch, rejecting with PARAM_ERROR - param:" << paramId
1675 <<
"requested type:" << request.param_type <<
"actual type:" << _mapParamName2MavParamType[componentId][paramId];
1676 _sendParamError(componentId, paramId, -1, MAV_PARAM_ERROR_TYPE_MISMATCH);
1682 qCDebug(MockLinkLog) <<
"Param set failure: first attempt no ack" << paramId;
1683 _paramSetFailureFirstAttemptPending =
false;
1688 qCDebug(MockLinkLog) <<
"Param set failure: no ack" << paramId;
1693 qCDebug(MockLinkLog) <<
"Param set failure: PARAM_ERROR" << paramId;
1694 _sendParamError(componentId, paramId,
1695 _mapParamName2Value[componentId].keys().indexOf(paramId),
1696 MAV_PARAM_ERROR_VALUE_OUT_OF_RANGE);
1701 _setParamFloatUnionIntoMap(componentId, paramId, request.param_value);
1704 mavlink_msg_param_value_pack_chan(
1707 _outgoingMavlinkChannel,
1710 request.param_value,
1712 _mapParamName2Value[componentId].count(),
1713 _mapParamName2Value[componentId].keys().indexOf(paramId)
1721 mavlink_param_request_read_t request{};
1722 mavlink_msg_param_request_read_decode(&msg, &request);
1724 const QString paramName(QString::fromLocal8Bit(request.param_id,
static_cast<int>(strnlen(request.param_id, MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN))));
1725 const int componentId = request.target_component;
1728 if ((_firmwareType == MAV_AUTOPILOT_PX4) && (paramName ==
"_HASH_CHECK")) {
1729 _hashCheckRequestCount++;
1730 if (_hashCheckNoResponse) {
1734 const int hashComponentId = _mapParamName2Value.contains(MAV_COMP_ID_AUTOPILOT1)
1735 ? MAV_COMP_ID_AUTOPILOT1
1736 : _mapParamName2Value.keys().first();
1739 valueUnion.type = MAV_PARAM_TYPE_UINT32;
1740 valueUnion.param_uint32 = _computeParamHash(hashComponentId);
1741 (void) mavlink_msg_param_value_pack_chan(
1744 _outgoingMavlinkChannel,
1747 valueUnion.param_float,
1748 MAV_PARAM_TYPE_UINT32,
1756 if (!_mapParamName2Value.contains(componentId)) {
1757 qCDebug(MockLinkLog) <<
"_handleParamRequestRead unknown component, rejecting with PARAM_ERROR - componentId:" << componentId <<
"param:" << paramName;
1758 const QByteArray paramIdBytes = paramName.toLocal8Bit();
1759 _sendParamError(componentId, paramIdBytes.constData(), request.param_index, MAV_PARAM_ERROR_COMPONENT_NOT_FOUND);
1763 char paramId[MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN + 1]{};
1766 Q_ASSERT(request.target_system == _vehicleSystemId);
1768 if (request.param_index == -1) {
1770 (void) strncpy(paramId, request.param_id, MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
1773 Q_ASSERT(request.param_index >= 0 && request.param_index < _mapParamName2Value[componentId].count());
1775 const QString key = _mapParamName2Value[componentId].keys().at(request.param_index);
1776 Q_ASSERT(key.length() <= MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
1777 strcpy(paramId, key.toLocal8Bit().constData());
1780 if (!_mapParamName2Value[componentId].contains(paramId) || !_mapParamName2MavParamType[componentId].contains(paramId)) {
1783 qCDebug(MockLinkLog) <<
"_handleParamRequestRead unknown param, rejecting with PARAM_ERROR - componentId:" << componentId <<
"param:" << paramId;
1784 _sendParamError(componentId, paramId, request.param_index, MAV_PARAM_ERROR_DOES_NOT_EXIST);
1789 qCDebug(MockLinkLog) <<
"Ignoring request read for " << _failParam;
1795 qCDebug(MockLinkLog) <<
"Param request read failure: first attempt no response" << paramId;
1796 _paramRequestReadFailureFirstAttemptPending =
false;
1801 qCDebug(MockLinkLog) <<
"Param request read failure: no response" << paramId;
1806 qCDebug(MockLinkLog) <<
"Param request read failure: PARAM_ERROR" << paramId;
1807 _sendParamError(componentId, paramId, request.param_index, MAV_PARAM_ERROR_DOES_NOT_EXIST);
1811 (void) mavlink_msg_param_value_pack_chan(
1814 _outgoingMavlinkChannel,
1817 _floatUnionForParam(componentId, paramId),
1818 _mapParamName2MavParamType[componentId][paramId],
1819 _mapParamName2Value[componentId].count(),
1820 _mapParamName2Value[componentId].keys().indexOf(paramId)
1825void MockLink::_sendParamError(
int componentId,
const char *paramId, int16_t paramIndex, uint8_t errorCode)
1828 char paramIdBuf[MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN + 1] = {};
1829 (void) strncpy(paramIdBuf, paramId, MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN);
1831 (void) mavlink_msg_param_error_pack_chan(
1833 static_cast<uint8_t
>(componentId),
1834 _outgoingMavlinkChannel,
1852 uint8_t commandResult = MAV_RESULT_UNSUPPORTED;
1854 switch (request.command) {
1857 commandResult = MAV_RESULT_ACCEPTED;
1861 commandResult = MAV_RESULT_FAILED;
1869 (void) mavlink_msg_command_ack_pack_chan(
1871 _vehicleComponentId,
1872 _outgoingMavlinkChannel,
1875 MAV_RESULT_IN_PROGRESS,
1884 (void) mavlink_msg_command_ack_pack_chan(
1886 _vehicleComponentId,
1887 _outgoingMavlinkChannel,
1900void MockLink::_handleCommandLongSetMessageInterval(
const mavlink_command_long_t &request,
bool &accepted)
1904 static const QSet<int> kPidTuningMessageIds = {
1905 MAVLINK_MSG_ID_ATTITUDE_QUATERNION,
1906 MAVLINK_MSG_ID_ATTITUDE_TARGET,
1907 MAVLINK_MSG_ID_LOCAL_POSITION_NED,
1908 MAVLINK_MSG_ID_POSITION_TARGET_LOCAL_NED,
1909 MAVLINK_MSG_ID_NAV_CONTROLLER_OUTPUT,
1910 MAVLINK_MSG_ID_VFR_HUD,
1912 accepted = kPidTuningMessageIds.contains(
static_cast<int>(request.param1));
1917 static bool firstCmdUser3 =
true;
1918 static bool firstCmdUser4 =
true;
1921 mavlink_msg_command_long_decode(&msg, &request);
1923 uint8_t commandResult = MAV_RESULT_UNSUPPORTED;
1925 switch (request.command) {
1926 case MAV_CMD_COMPONENT_ARM_DISARM:
1927 if (request.param1 == 0.0f) {
1928 _mavBaseMode &= ~MAV_MODE_FLAG_SAFETY_ARMED;
1930 _mavBaseMode |= MAV_MODE_FLAG_SAFETY_ARMED;
1932 commandResult = MAV_RESULT_ACCEPTED;
1934 case MAV_CMD_PREFLIGHT_CALIBRATION:
1935 _handlePreFlightCalibration(request);
1936 commandResult = MAV_RESULT_ACCEPTED;
1938 case MAV_CMD_DO_MOTOR_TEST:
1939 commandResult = MAV_RESULT_ACCEPTED;
1941 case MAV_CMD_DO_START_MAG_CAL:
1945 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1946 if (_apmMagCalStartFailureMode) {
1947 commandResult = MAV_RESULT_FAILED;
1949 QMutexLocker locker(&_apmCompassCalMutex);
1950 _apmCompassCalProgress = 0;
1951 _apmCompassCalTickCount = 0;
1952 commandResult = MAV_RESULT_ACCEPTED;
1956 case MAV_CMD_DO_CANCEL_MAG_CAL:
1958 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1959 QMutexLocker locker(&_apmCompassCalMutex);
1960 _apmStaleFailedMagCalReportStreaming =
false;
1961 _apmCompassCalProgress = -1;
1962 commandResult = MAV_RESULT_ACCEPTED;
1965 case MAV_CMD_CONTROL_HIGH_LATENCY:
1967 _highLatencyTransmissionEnabled =
static_cast<int>(request.param1) != 0;
1969 commandResult = MAV_RESULT_ACCEPTED;
1971 commandResult = MAV_RESULT_FAILED;
1974 case MAV_CMD_PREFLIGHT_STORAGE:
1975 if (
static_cast<int>(request.param1) == 2) {
1977 _resetParamsToDefaults();
1979 commandResult = MAV_RESULT_ACCEPTED;
1981 case MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN:
1985 commandResult = MAV_RESULT_ACCEPTED;
1987 case MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES:
1988 commandResult = MAV_RESULT_ACCEPTED;
1989 _respondWithAutopilotVersion();
1991 case MAV_CMD_REQUEST_MESSAGE:
1993 bool accepted =
false;
1995 _handleRequestMessage(request, accepted, noAck);
2001 commandResult = MAV_RESULT_ACCEPTED;
2005 case MAV_CMD_NAV_TAKEOFF:
2006 _handleTakeoff(request);
2007 commandResult = MAV_RESULT_ACCEPTED;
2011 commandResult = MAV_RESULT_ACCEPTED;
2015 commandResult = MAV_RESULT_FAILED;
2019 if (firstCmdUser3) {
2020 firstCmdUser3 =
false;
2023 firstCmdUser3 =
true;
2024 commandResult = MAV_RESULT_ACCEPTED;
2029 if (firstCmdUser4) {
2030 firstCmdUser4 =
false;
2033 firstCmdUser4 =
true;
2034 commandResult = MAV_RESULT_FAILED;
2044 _handleInProgressCommandLong(request);
2046 case MAV_CMD_SET_MESSAGE_INTERVAL:
2048 bool accepted =
false;
2050 _handleCommandLongSetMessageInterval(request, accepted);
2052 commandResult = MAV_RESULT_ACCEPTED;
2059 (void) mavlink_msg_command_ack_pack_chan(
2061 _vehicleComponentId,
2062 _outgoingMavlinkChannel,
2076 mavlink_command_int_t request{};
2077 mavlink_msg_command_int_decode(&msg, &request);
2083 const uint8_t commandResult = MAV_RESULT_UNSUPPORTED;
2086 (void) mavlink_msg_command_ack_pack_chan(
2088 _vehicleComponentId,
2089 _outgoingMavlinkChannel,
2104 (void) mavlink_msg_command_ack_pack_chan(
2106 _vehicleComponentId,
2107 _outgoingMavlinkChannel,
2119void MockLink::_respondWithAutopilotVersion()
2121 union FlightVersion {
2131 FlightVersion(uint32_t version = 0) : raw(version) {}
2133 FlightVersion flightVersion;
2135 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
2136 flightVersion.parts.major = 4;
2137 flightVersion.parts.minor = 7;
2138 flightVersion.parts.patch = 0;
2139 flightVersion.parts.type = FIRMWARE_VERSION_TYPE_OFFICIAL;
2140 }
else if (_firmwareType == MAV_AUTOPILOT_PX4) {
2141 flightVersion.parts.major = 1;
2142 flightVersion.parts.minor = 17;
2143 flightVersion.parts.patch = 0;
2144 flightVersion.parts.type = FIRMWARE_VERSION_TYPE_OFFICIAL;
2147 const uint8_t customVersion[8]{};
2148 const uint64_t capabilities = MAV_PROTOCOL_CAPABILITY_MAVLINK2 | MAV_PROTOCOL_CAPABILITY_MISSION_FENCE | MAV_PROTOCOL_CAPABILITY_MISSION_RALLY | MAV_PROTOCOL_CAPABILITY_MISSION_INT
2149 | ((_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) ? MAV_PROTOCOL_CAPABILITY_TERRAIN : 0)
2150 | (_ftpCapability ? MAV_PROTOCOL_CAPABILITY_FTP : 0);
2153 (void) mavlink_msg_autopilot_version_pack_chan(
2155 _vehicleComponentId,
2156 _outgoingMavlinkChannel,
2163 reinterpret_cast<const uint8_t*
>(&customVersion),
2164 reinterpret_cast<const uint8_t*
>(&customVersion),
2165 reinterpret_cast<const uint8_t*
>(&customVersion),
2174void MockLink::_sendHomePosition()
2176 const float bogus[4]{};
2179 (void) mavlink_msg_home_position_pack_chan(
2181 _vehicleComponentId,
2182 _outgoingMavlinkChannel,
2184 static_cast<int32_t
>(_vehicleLatitude * 1E7),
2185 static_cast<int32_t
>(_vehicleLongitude * 1E7),
2186 static_cast<int32_t
>(_defaultVehicleHomeAltitude * 1000),
2195void MockLink::_sendGpsRawInt()
2197 static uint64_t timeTick = 0;
2200 (void) mavlink_msg_gps_raw_int_pack_chan(
2202 _vehicleComponentId,
2203 _outgoingMavlinkChannel,
2206 GPS_FIX_TYPE_3D_FIX,
2207 static_cast<int32_t
>(_vehicleLatitude * 1E7),
2208 static_cast<int32_t
>(_vehicleLongitude * 1E7),
2209 static_cast<int32_t
>(_vehicleAltitudeAMSL * 1000),
2226void MockLink::_sendGlobalPositionInt()
2228 static uint64_t timeTick = 0;
2231 (void) mavlink_msg_global_position_int_pack_chan(
2233 _vehicleComponentId,
2234 _outgoingMavlinkChannel,
2237 static_cast<int32_t
>(_vehicleLatitude * 1E7),
2238 static_cast<int32_t
>(_vehicleLongitude * 1E7),
2239 static_cast<int32_t
>(_vehicleAltitudeAMSL * 1000),
2240 static_cast<int32_t
>((_vehicleAltitudeAMSL - _defaultVehicleHomeAltitude) * 1000),
2247void MockLink::_sendAttitudeQuaternion()
2249 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
2250 const float t = timeBootMs / 1000.0f;
2253 const float roll = 0.20f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.50f * t);
2254 const float pitch = 0.10f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.40f * t);
2255 const float yaw = 0.30f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.10f * t);
2258 const float cr = std::cos(roll / 2.0f), sr = std::sin(roll / 2.0f);
2259 const float cp = std::cos(pitch / 2.0f), sp = std::sin(pitch / 2.0f);
2260 const float cy = std::cos(yaw / 2.0f), sy = std::sin(yaw / 2.0f);
2261 const float q1 = cr * cp * cy + sr * sp * sy;
2262 const float q2 = sr * cp * cy - cr * sp * sy;
2263 const float q3 = cr * sp * cy + sr * cp * sy;
2264 const float q4 = cr * cp * sy - sr * sp * cy;
2267 const float rollspeed = 0.20f * (2.0f *
static_cast<float>(M_PI) * 0.50f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.50f * t);
2268 const float pitchspeed = 0.10f * (2.0f *
static_cast<float>(M_PI) * 0.40f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.40f * t);
2269 const float yawspeed = 0.30f * (2.0f *
static_cast<float>(M_PI) * 0.10f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.10f * t);
2271 const float reprOffsetQ[4] = {1.0f, 0.0f, 0.0f, 0.0f};
2274 (void) mavlink_msg_attitude_quaternion_pack_chan(
2276 _vehicleComponentId,
2277 _outgoingMavlinkChannel,
2281 rollspeed, pitchspeed, yawspeed,
2287void MockLink::_sendAttitudeTarget()
2290 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
2291 const float t = timeBootMs / 1000.0f;
2292 static constexpr float kPhase = 0.3f;
2294 const float roll = 0.20f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.50f * t + kPhase);
2295 const float pitch = 0.10f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.40f * t + kPhase);
2296 const float yaw = 0.30f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.10f * t + kPhase);
2298 const float cr = std::cos(roll / 2.0f), sr = std::sin(roll / 2.0f);
2299 const float cp = std::cos(pitch / 2.0f), sp = std::sin(pitch / 2.0f);
2300 const float cy = std::cos(yaw / 2.0f), sy = std::sin(yaw / 2.0f);
2301 const float qSp[4] = {
2302 cr * cp * cy + sr * sp * sy,
2303 sr * cp * cy - cr * sp * sy,
2304 cr * sp * cy + sr * cp * sy,
2305 cr * cp * sy - sr * sp * cy,
2308 const float bodyRollRate = 0.20f * (2.0f *
static_cast<float>(M_PI) * 0.50f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.50f * t + kPhase);
2309 const float bodyPitchRate = 0.10f * (2.0f *
static_cast<float>(M_PI) * 0.40f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.40f * t + kPhase);
2310 const float bodyYawRate = 0.30f * (2.0f *
static_cast<float>(M_PI) * 0.10f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.10f * t + kPhase);
2313 (void) mavlink_msg_attitude_target_pack_chan(
2315 _vehicleComponentId,
2316 _outgoingMavlinkChannel,
2321 bodyRollRate, bodyPitchRate, bodyYawRate,
2327void MockLink::_sendLocalPositionNed()
2329 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
2330 const float t = timeBootMs / 1000.0f;
2332 const float x = 5.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.08f * t);
2333 const float y = 5.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.10f * t);
2334 const float z = -10.0f + 1.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.15f * t);
2335 const float vx = 5.0f * (2.0f *
static_cast<float>(M_PI) * 0.08f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.08f * t);
2336 const float vy = 5.0f * (2.0f *
static_cast<float>(M_PI) * 0.10f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.10f * t);
2337 const float vz = 1.0f * (2.0f *
static_cast<float>(M_PI) * 0.15f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.15f * t);
2340 (void) mavlink_msg_local_position_ned_pack_chan(
2342 _vehicleComponentId,
2343 _outgoingMavlinkChannel,
2352void MockLink::_sendPositionTargetLocalNed()
2355 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
2356 const float t = timeBootMs / 1000.0f + 0.5f;
2358 const float x = 5.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.08f * t);
2359 const float y = 5.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.10f * t);
2360 const float z = -10.0f + 1.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.15f * t);
2361 const float vx = 5.0f * (2.0f *
static_cast<float>(M_PI) * 0.08f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.08f * t);
2362 const float vy = 5.0f * (2.0f *
static_cast<float>(M_PI) * 0.10f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.10f * t);
2363 const float vz = 1.0f * (2.0f *
static_cast<float>(M_PI) * 0.15f) * std::cos(2.0f *
static_cast<float>(M_PI) * 0.15f * t);
2366 (void) mavlink_msg_position_target_local_ned_pack_chan(
2368 _vehicleComponentId,
2369 _outgoingMavlinkChannel,
2372 MAV_FRAME_LOCAL_NED,
2382void MockLink::_sendExtendedSysState()
2385 (void) mavlink_msg_extended_sys_state_pack_chan(
2387 _vehicleComponentId,
2388 _outgoingMavlinkChannel,
2390 MAV_VTOL_STATE_UNDEFINED,
2391 (_vehicleAltitudeAMSL > _defaultVehicleHomeAltitude) ? MAV_LANDED_STATE_IN_AIR : MAV_LANDED_STATE_ON_GROUND
2396void MockLink::_sendChunkedStatusText(uint16_t chunkId,
bool missingChunks)
2398 constexpr int cChunks = 4;
2401 for (
int i = 0; i < cChunks; i++) {
2402 if (missingChunks && (i & 1)) {
2406 int cBuf = MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN;
2407 char msgBuf[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN]{};
2409 if (i == cChunks - 1) {
2414 for (
int j = 0; j < cBuf - 1; j++) {
2415 msgBuf[j] =
'0' + num++;
2420 msgBuf[cBuf-1] =
'A' + i;
2423 (void) mavlink_msg_statustext_pack_chan(
2425 _vehicleComponentId,
2426 _outgoingMavlinkChannel,
2437void MockLink::_sendStatusTextMessages()
2439 struct StatusMessage {
2440 MAV_SEVERITY severity;
2444 static constexpr struct StatusMessage rgMessages[] = {
2445 { MAV_SEVERITY_INFO,
"#Testing audio output" },
2446 { MAV_SEVERITY_EMERGENCY,
"Status text emergency" },
2447 { MAV_SEVERITY_ALERT,
"Status text alert" },
2448 { MAV_SEVERITY_CRITICAL,
"Status text critical" },
2449 { MAV_SEVERITY_ERROR,
"Status text error" },
2450 { MAV_SEVERITY_WARNING,
"Status text warning" },
2451 { MAV_SEVERITY_NOTICE,
"Status text notice" },
2452 { MAV_SEVERITY_INFO,
"Status text info" },
2453 { MAV_SEVERITY_DEBUG,
"Status text debug" },
2457 for (
size_t i = 0; i < std::size(rgMessages); i++) {
2458 const struct StatusMessage *status = &rgMessages[i];
2459 char statusText[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN] = {};
2460 (void) std::strncpy(statusText, status->msg,
sizeof(statusText) - 1);
2462 (void) mavlink_msg_statustext_pack_chan(
2464 _vehicleComponentId,
2465 _outgoingMavlinkChannel,
2475 _sendChunkedStatusText(1,
false );
2476 _sendChunkedStatusText(2,
true );
2477 _sendChunkedStatusText(3,
false );
2478 _sendChunkedStatusText(4,
true );
2487 return qobject_cast<MockLink*>(
config->link());
2510 return _startMockLink(mockConfig);
2515 return _startMockLinkWorker(QStringLiteral(
"PX4 MultiRotor MockLink"), MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, options, failureMode, videoStreamType);
2525 return _startMockLinkWorker(QStringLiteral(
"Generic MockLink"), MAV_AUTOPILOT_GENERIC, MAV_TYPE_QUADROTOR, options, failureMode, videoStreamType);
2530 return _startMockLinkWorker(QStringLiteral(
"No Initial Connect MockLink"), MAV_AUTOPILOT_PX4, MAV_TYPE_GENERIC, options, failureMode);
2535 return _startMockLinkWorker(QStringLiteral(
"ArduCopter MockLink"),MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_QUADROTOR, options, failureMode, videoStreamType);
2540 return _startMockLinkWorker(QStringLiteral(
"ArduPlane MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_FIXED_WING, options, failureMode, videoStreamType);
2545 return _startMockLinkWorker(QStringLiteral(
"ArduSub MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_SUBMARINE, options, failureMode, videoStreamType);
2550 return _startMockLinkWorker(QStringLiteral(
"ArduRover MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_GROUND_ROVER, options, failureMode, videoStreamType);
2553void MockLink::_sendRCChannels()
2556 (void) mavlink_msg_rc_channels_pack_chan(
2558 _vehicleComponentId,
2559 _outgoingMavlinkChannel,
2563 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500,
2564 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500,
2565 UINT16_MAX, UINT16_MAX,
2573 if ((request.param1 == 0) && (request.param2 == 0) && (request.param3 == 0) &&
2574 (request.param4 == 0) && (request.param5 == 0) && (request.param6 == 0) &&
2575 (request.param7 == 0)) {
2577 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
2579 QMutexLocker locker(&_apmAccelCalMutex);
2580 if (_apmAccelCalPosIndex >= 0) {
2583 cmd.target_system = 255;
2584 cmd.target_component = MAV_COMP_ID_MISSIONPLANNER;
2585 cmd.command = MAV_CMD_ACCELCAL_VEHICLE_POS;
2586 cmd.param1 =
static_cast<float>(ACCELCAL_VEHICLE_POS_FAILED);
2587 (void) mavlink_msg_command_long_encode_chan(
2588 _vehicleSystemId, _vehicleComponentId, _outgoingMavlinkChannel, &msg, &cmd);
2590 _apmAccelCalPosIndex = -1;
2594 (void) _mockLinkPX4Calibration->
cancel();
2599 if (request.param2 == 1) {
2605 if (request.param1 == 1) {
2610 if (request.param5 == 1) {
2611 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
2613 QMutexLocker locker(&_apmAccelCalMutex);
2614 _apmAccelCalPosIndex = 0;
2615 _apmAccelCalGotAck =
false;
2616 _apmAccelCalTickCount = 0;
2626 char statusText[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN] = {};
2627 (void) std::strncpy(statusText, text.toUtf8().constData(),
sizeof(statusText) - 1);
2630 (void) mavlink_msg_statustext_pack_chan(
2632 _vehicleComponentId,
2633 _outgoingMavlinkChannel,
2645 _vehicleAltitudeAMSL = request.param7 + _defaultVehicleHomeAltitude;
2646 _mavBaseMode |= MAV_MODE_FLAG_SAFETY_ARMED;
2651 mavlink_log_request_list_t request{};
2652 mavlink_msg_log_request_list_decode(&msg, &request);
2654 if ((request.start != 0) && (request.end != 0xffff)) {
2655 qCWarning(MockLinkLog) <<
"_handleLogRequestList cannot handle partial requests";
2661 const QList<MockLinkFTP::LogFile> logFiles = _mockLinkFTP->
logFiles();
2662 if (!_logsErased && !logFiles.isEmpty()) {
2663 const uint16_t numLogs =
static_cast<uint16_t
>(logFiles.count());
2664 for (uint16_t
id = 0;
id < numLogs;
id++) {
2666 (void) mavlink_msg_log_entry_pack_chan(
2668 _vehicleComponentId,
2669 _outgoingMavlinkChannel,
2675 static_cast<uint32_t
>(logFiles[
id].size)
2682 const uint16_t numLogs = _logsErased ? 0 : 1;
2683 const uint16_t logId = _logsErased ? 0 : _logDownloadLogId;
2684 const uint32_t logSize = _logsErased ? 0 : _logDownloadFileSize;
2686 mavlink_msg_log_entry_pack_chan(
2688 _vehicleComponentId,
2689 _outgoingMavlinkChannel,
2702 mavlink_log_erase_t request{};
2703 mavlink_msg_log_erase_decode(&msg, &request);
2705 if ((request.target_system != _vehicleSystemId) || (request.target_component != _vehicleComponentId)) {
2713QString MockLink::_createRandomFile(uint32_t byteCount)
2715 QTemporaryFile tempFile;
2716 tempFile.setAutoRemove(
false);
2717 if (!tempFile.open()) {
2718 qCWarning(MockLinkLog) <<
"MockLink::createRandomFile open failed" << tempFile.errorString();
2722 for (uint32_t bytesWritten = 0; bytesWritten < byteCount; bytesWritten++) {
2723 const unsigned char byte = (QRandomGenerator::global()->generate() * 0xFF) / RAND_MAX;
2724 (void) tempFile.write(
reinterpret_cast<const char*
>(&
byte), 1);
2728 return tempFile.fileName();
2731QString MockLink::_createLogContentsFile(
const QString &logName)
2733 QTemporaryFile tempFile;
2734 tempFile.setAutoRemove(
false);
2735 if (!tempFile.open()) {
2736 qCWarning(MockLinkLog) <<
"_createLogContentsFile open failed" << tempFile.errorString();
2741 return tempFile.fileName();
2746 mavlink_log_request_data_t request{};
2747 mavlink_msg_log_request_data_decode(&msg, &request);
2750 QMutexLocker locker(&_logDownloadMutex);
2752 const QList<MockLinkFTP::LogFile> logFiles = _logsErased ? QList<MockLinkFTP::LogFile>() : _mockLinkFTP->logFiles();
2753 if (!logFiles.isEmpty()) {
2755 if (request.id >= logFiles.count()) {
2756 qCWarning(MockLinkLog) <<
"_handleLogRequestData id out of range:" << request.id;
2759 if (_logDownloadFilename.isEmpty() || (_logDownloadId != request.id)) {
2760 _logDownloadFilename = _createLogContentsFile(logFiles[request.id].name);
2761 _logDownloadId = request.id;
2762 _logDownloadSize =
static_cast<uint32_t
>(logFiles[request.id].size);
2765#ifdef QGC_UNITTEST_BUILD
2766 if (_logDownloadFilename.isEmpty()) {
2767 _logDownloadFilename = _createRandomFile(_logDownloadFileSize);
2770 if (request.id != _logDownloadLogId) {
2771 qCWarning(MockLinkLog) <<
"_handleLogRequestData id must be" << _logDownloadLogId;
2774 _logDownloadId = _logDownloadLogId;
2775 _logDownloadSize = _logDownloadFileSize;
2778 if (request.ofs > (_logDownloadSize - 1)) {
2779 qCWarning(MockLinkLog) <<
"_handleLogRequestData offset past end of file request.ofs:size" << request.ofs << _logDownloadSize;
2784 _logDownloadCurrentOffset = request.ofs;
2785 if (request.ofs + request.count > _logDownloadSize) {
2786 request.count = _logDownloadSize - request.ofs;
2788 _logDownloadBytesRemaining = request.count;
2791void MockLink::_logDownloadWorker()
2795 QMutexLocker locker(&_logDownloadMutex);
2796 if (_logDownloadBytesRemaining == 0) {
2800 QFile file(_logDownloadFilename);
2801 if (!file.open(QIODevice::ReadOnly)) {
2802 qCWarning(MockLinkLog) <<
"_logDownloadWorker open failed" << file.errorString();
2803 _logDownloadBytesRemaining = 0;
2807 uint8_t buffer[MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN]{};
2809 const qint64 bytesToRead = qMin(_logDownloadBytesRemaining, (uint32_t)MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN);
2810 if (!file.seek(_logDownloadCurrentOffset)) {
2811 qCWarning(MockLinkLog) <<
"_logDownloadWorker seek failed - offset:" << _logDownloadCurrentOffset << file.errorString();
2812 _logDownloadBytesRemaining = 0;
2815 if (file.read(
reinterpret_cast<char*
>(buffer), bytesToRead) != bytesToRead) {
2816 qCWarning(MockLinkLog) <<
"_logDownloadWorker read failed - bytesToRead:" << bytesToRead << file.errorString();
2817 _logDownloadBytesRemaining = 0;
2821 qCDebug(MockLinkLog) <<
"_logDownloadWorker" << _logDownloadCurrentOffset << _logDownloadBytesRemaining;
2824 (void) mavlink_msg_log_data_pack_chan(
2826 _vehicleComponentId,
2827 _outgoingMavlinkChannel,
2830 _logDownloadCurrentOffset,
2836 _logDownloadCurrentOffset += bytesToRead;
2837 _logDownloadBytesRemaining -= bytesToRead;
2842void MockLink::_sendADSBVehicles()
2844 for (
int i = 0; i < _adsbVehicles.size(); ++i) {
2846 _adsbVehicles[i].angle += (i + 1);
2849 _adsbVehicles[i].coordinate = _adsbVehicles[i].coordinate.atDistanceAndAzimuth(5, _adsbVehicles[i].angle);
2852 _adsbVehicles[i].altitude += (i % 2 == 0 ? 0.5 : -0.5);
2854 QByteArray callsign = QString(
"N12345%1").arg(i, 2, 10, QChar(
'0')).toLatin1();
2855 callsign.resize(MAVLINK_MSG_ADSB_VEHICLE_FIELD_CALLSIGN_LEN);
2859 (void) mavlink_msg_adsb_vehicle_pack_chan(
2861 _vehicleComponentId,
2862 _outgoingMavlinkChannel,
2865 _adsbVehicles[i].coordinate.latitude() * 1e7,
2866 _adsbVehicles[i].coordinate.longitude() * 1e7,
2867 ADSB_ALTITUDE_TYPE_GEOMETRIC,
2868 _adsbVehicles[i].altitude * 1000,
2870 static_cast<uint16_t
>(_adsbVehicles[i].angle * 100),
2872 callsign.constData(),
2873 ADSB_EMITTER_TYPE_ROTOCRAFT,
2875 ADSB_FLAGS_VALID_COORDS | ADSB_FLAGS_VALID_ALTITUDE | ADSB_FLAGS_VALID_HEADING | ADSB_FLAGS_VALID_CALLSIGN | ADSB_FLAGS_SIMULATED,
2882void MockLink::_moveADSBVehicle(
int vehicleIndex)
2884 _adsbAngles[vehicleIndex] += 10;
2885 QGeoCoordinate &coord = _adsbVehicleCoordinates[vehicleIndex];
2888 coord = QGeoCoordinate(coord.latitude(), coord.longitude()).atDistanceAndAzimuth(500, _adsbAngles[vehicleIndex]);
2889 coord.setAltitude(100);
2896 switch (_failureMode) {
2909 _respondWithAutopilotVersion();
2917 switch (_requestMessageFailureMode) {
2932 (void) mavlink_msg_debug_pack_chan(
2934 _vehicleComponentId,
2935 _outgoingMavlinkChannel,
2949 QMutexLocker locker(&_availableModesWorkerMutex);
2950 if (request.param2 == 0) {
2952 if (_availableModesWorkerNextModeIndex != 0) {
2953 qCWarning(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: _availableModesWorker already running - _availableModesWorkerNextModeIndex:" << _availableModesWorkerNextModeIndex;
2957 qCDebug(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: starting available modes sequence worker";
2958 _availableModesWorkerNextModeIndex = 1;
2961 if (request.param2 > _availableFlightModes.count()) {
2962 qCWarning(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: requested mode index out of range" << request.param2 << _availableFlightModes.count();
2966 qCDebug(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: received specific mode request for index" << request.param2;
2967 _availableModesWorkerNextModeIndex = -request.param2;
2976 const uint32_t requestedMessageId =
static_cast<uint32_t
>(request.param1);
2980 QMutexLocker locker(&_requestMessageNoResponseMutex);
2981 if (_requestMessageNoResponseIds.contains(requestedMessageId)) {
2987 switch (
static_cast<int>(request.param1)) {
2988 case MAVLINK_MSG_ID_AUTOPILOT_VERSION:
2989 _handleRequestMessageAutopilotVersion(request, accepted);
2991 case MAVLINK_MSG_ID_COMPONENT_METADATA:
2992 if (_firmwareType == MAV_AUTOPILOT_PX4) {
2993 _sendGeneralMetaData();
2997 case MAVLINK_MSG_ID_DEBUG:
2998 _handleRequestMessageDebug(request, accepted, noAck);
3000 case MAVLINK_MSG_ID_AVAILABLE_MODES:
3001 _handleRequestMessageAvailableModes(request, accepted);
3006void MockLink::_sendGeneralMetaData()
3008 static constexpr const char metaDataURI[MAVLINK_MSG_COMPONENT_METADATA_FIELD_URI_LEN] =
"mftp://[;comp=1]general.json";
3011 (void) mavlink_msg_component_metadata_pack_chan(
3013 _vehicleComponentId,
3014 _outgoingMavlinkChannel,
3025 QMutexLocker locker(&_remoteIDArmStatusMutex);
3026 _remoteIDArmStatus = status;
3027 _remoteIDArmStatusError =
error;
3030void MockLink::_sendRemoteIDArmStatus()
3033 QByteArray errorUtf8;
3035 QMutexLocker locker(&_remoteIDArmStatusMutex);
3036 armStatus = _remoteIDArmStatus;
3037 errorUtf8 = _remoteIDArmStatusError.toUtf8();
3040 char armStatusError[MAVLINK_MSG_OPEN_DRONE_ID_ARM_STATUS_FIELD_ERROR_LEN] = {};
3041 std::strncpy(armStatusError, errorUtf8.constData(),
sizeof(armStatusError) - 1);
3044 (void) mavlink_msg_open_drone_id_arm_status_pack_chan(
3046 MAV_COMP_ID_ODID_TXRX_1,
3047 static_cast<uint8_t
>(_outgoingMavlinkChannel),
3055void MockLink::_sendEscInfo()
3059 static const uint16_t failureFlags[4] = {0, 0, 0, 0};
3060 static const uint32_t errorCount[4] = {0, 0, 0, 0};
3061 static const int16_t temperature[4] = {3000, 3000, 3000, 3000};
3064 (void) mavlink_msg_esc_info_pack_chan(
3066 _vehicleComponentId,
3067 _outgoingMavlinkChannel,
3070 static_cast<uint64_t
>(_runningTime.elapsed()) * 1000,
3073 ESC_CONNECTION_TYPE_DSHOT,
3082void MockLink::_sendEscStatus()
3084 static const int32_t rpm[4] = {5000, 5000, 5000, 5000};
3085 static const float voltage[4] = {16.0f, 16.0f, 16.0f, 16.0f};
3086 static const float current[4] = {5.0f, 5.0f, 5.0f, 5.0f};
3089 (void) mavlink_msg_esc_status_pack_chan(
3091 _vehicleComponentId,
3092 _outgoingMavlinkChannel,
3095 static_cast<uint64_t
>(_runningTime.elapsed()) * 1000,
3103void MockLink::_sendRadioStatus()
3108 (void) mavlink_msg_radio_status_pack_chan(
3110 _vehicleComponentId,
3111 _outgoingMavlinkChannel,
3132 return _mockLinkFTP;
3135void MockLink::_sendAvailableMode(uint8_t modeIndexOneBased)
3137 if (modeIndexOneBased > _availableModesCount()) {
3138 qCWarning(MockLinkLog) <<
"modeIndexOneBased out of range" << modeIndexOneBased << _availableModesCount();
3142 qCDebug(MockLinkLog) <<
"_sendAvailableMode modeIndexOneBased:" << modeIndexOneBased;
3144 const FlightMode_t &availableMode = _availableFlightModes[modeIndexOneBased - 1];
3145 char modeName[MAVLINK_MSG_AVAILABLE_MODES_FIELD_MODE_NAME_LEN] = {};
3146 std::strncpy(modeName, availableMode.name,
sizeof(modeName) - 1);
3150 (void) mavlink_msg_available_modes_pack_chan(
3152 _vehicleComponentId,
3153 _outgoingMavlinkChannel,
3155 _availableModesCount(),
3157 availableMode.standard_mode,
3158 availableMode.custom_mode,
3159 availableMode.canBeSet ? 0 : MAV_MODE_PROPERTY_NOT_USER_SELECTABLE,
3164void MockLink::_availableModesWorker()
3168 QMutexLocker locker(&_availableModesWorkerMutex);
3169 if (_availableModesWorkerNextModeIndex == 0) {
3174 _sendAvailableMode(qAbs(_availableModesWorkerNextModeIndex));
3176 if (_availableModesWorkerNextModeIndex < 0) {
3178 _availableModesWorkerNextModeIndex = 0;
3179 }
else if (++_availableModesWorkerNextModeIndex > _availableModesCount()) {
3181 _availableModesWorkerNextModeIndex = 0;
3182 qCDebug(MockLinkLog) <<
"_availableModesWorker: all modes sent, stopping worker";
3186void MockLink::_sendAvailableModesMonitor()
3190 (void) mavlink_msg_available_modes_monitor_pack_chan(
3192 _vehicleComponentId,
3193 _outgoingMavlinkChannel,
3195 _availableModesMonitorSeqNumber);
3199int MockLink::_availableModesCount()
const
3201 return _availableFlightModes.count() - (_availableModesMonitorSeqNumber == 0 ? 1 : 0);
3214 QMutexLocker locker(&_apmCompassCalMutex);
3215 _apmStaleFailedMagCalReportStreaming =
true;
3216 _apmCompassCalTickCount = 0;
3221 QMutexLocker locker(&_apmCompassCalMutex);
3222 return _apmStaleFailedMagCalReportStreaming;
3225void MockLink::_apmCompassCalWorker()
3227 if (_firmwareType != MAV_AUTOPILOT_ARDUPILOTMEGA) {
3231 QMutexLocker locker(&_apmCompassCalMutex);
3232 if ((_apmCompassCalProgress < 0) && !_apmStaleFailedMagCalReportStreaming) {
3237 if (++_apmCompassCalTickCount < 50) {
3240 _apmCompassCalTickCount = 0;
3242 if (_apmStaleFailedMagCalReportStreaming) {
3245 mavlink_mag_cal_report_t report{};
3246 report.compass_id = 0;
3247 report.cal_mask = 0x01;
3248 report.cal_status = MAG_CAL_FAILED;
3249 report.fitness = 999.0f;
3250 (void) mavlink_msg_mag_cal_report_encode_chan(
3251 _vehicleSystemId, _vehicleComponentId, _outgoingMavlinkChannel, &msg, &report);
3256 const int pct = _apmCompassCalProgress;
3260 for (uint8_t
id = 0;
id < 3; ++id) {
3261 mavlink_mag_cal_progress_t progress{};
3262 progress.compass_id = id;
3263 progress.cal_mask = 0x07;
3264 progress.cal_status = MAG_CAL_RUNNING_STEP_ONE;
3265 progress.completion_pct =
static_cast<uint8_t
>(qMin(pct, 100));
3266 (void) mavlink_msg_mag_cal_progress_encode_chan(
3267 _vehicleSystemId, _vehicleComponentId, _outgoingMavlinkChannel, &msg, &progress);
3273 for (uint8_t
id = 0;
id < 3; ++id) {
3274 mavlink_mag_cal_report_t report{};
3275 report.compass_id = id;
3276 report.cal_mask = 0x07;
3277 report.cal_status = MAG_CAL_SUCCESS;
3278 report.fitness = 0.5f;
3279 (void) mavlink_msg_mag_cal_report_encode_chan(
3280 _vehicleSystemId, _vehicleComponentId, _outgoingMavlinkChannel, &msg, &report);
3287 (void) QMetaObject::invokeMethod(
this, [
this] {
3288 constexpr int compId = MAV_COMP_ID_AUTOPILOT1;
3289 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS_X")] = QVariant(10.0f);
3290 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS_Y")] = QVariant(10.0f);
3291 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS_Z")] = QVariant(10.0f);
3292 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS2_X")] = QVariant(10.0f);
3293 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS2_Y")] = QVariant(10.0f);
3294 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS2_Z")] = QVariant(10.0f);
3295 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS3_X")] = QVariant(10.0f);
3296 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS3_Y")] = QVariant(10.0f);
3297 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS3_Z")] = QVariant(10.0f);
3298 }, Qt::QueuedConnection);
3300 _apmCompassCalProgress = -1;
3302 _apmCompassCalProgress = qMin(pct + 5, 100);
3313void MockLink::_apmAccelCalWorker()
3315 if (_firmwareType != MAV_AUTOPILOT_ARDUPILOTMEGA) {
3319 QMutexLocker locker(&_apmAccelCalMutex);
3320 if (_apmAccelCalPosIndex < 0) {
3324 if (_apmAccelCalPosIndex == 6) {
3328 cmd.target_system = 255;
3329 cmd.target_component = MAV_COMP_ID_MISSIONPLANNER;
3330 cmd.command = MAV_CMD_ACCELCAL_VEHICLE_POS;
3331 cmd.param1 =
static_cast<float>(ACCELCAL_VEHICLE_POS_SUCCESS);
3332 (void) mavlink_msg_command_long_encode_chan(
3333 _vehicleSystemId, _vehicleComponentId, _outgoingMavlinkChannel, &msg, &cmd);
3339 (void) QMetaObject::invokeMethod(
this, [
this] {
3340 constexpr int compId = MAV_COMP_ID_AUTOPILOT1;
3341 _mapParamName2Value[compId][QStringLiteral(
"INS_ACCOFFS_X")] = QVariant(0.1f);
3342 _mapParamName2Value[compId][QStringLiteral(
"INS_ACCOFFS_Y")] = QVariant(0.1f);
3343 _mapParamName2Value[compId][QStringLiteral(
"INS_ACCOFFS_Z")] = QVariant(0.1f);
3344 }, Qt::QueuedConnection);
3346 _apmAccelCalPosIndex = -1;
3351 if (!_apmAccelCalGotAck) {
3353 if (++_apmAccelCalTickCount < 50) {
3356 _apmAccelCalTickCount = 0;
3360 cmd.target_system = 255;
3361 cmd.target_component = MAV_COMP_ID_MISSIONPLANNER;
3362 cmd.command = MAV_CMD_ACCELCAL_VEHICLE_POS;
3363 cmd.param1 =
static_cast<float>(kAPMAccelCalPosSequence[_apmAccelCalPosIndex]);
3364 (void) mavlink_msg_command_long_encode_chan(
3365 _vehicleSystemId, _vehicleComponentId, _outgoingMavlinkChannel, &msg, &cmd);
3369 _apmAccelCalGotAck =
false;
3370 _apmAccelCalPosIndex++;
std::shared_ptr< LinkConfiguration > SharedLinkConfigurationPtr
mavlink_status_t * mavlink_get_channel_status(uint8_t chan)
struct __mavlink_setup_signing_t mavlink_setup_signing_t
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
struct __mavlink_command_ack_t mavlink_command_ack_t
struct param_union mavlink_param_union_t
struct __mavlink_command_long_t mavlink_command_long_t
void setDynamic(bool dynamic=true)
Set if this is this a dynamic configuration. (decided at runtime)
The link interface defines the interface for all links used to communicate with the ground station ap...
void bytesReceived(LinkInterface *link, const QByteArray &data)
virtual void _freeMavlinkChannel()
void _connectionRemoved()
bool mavlinkChannelIsSet() const
virtual bool _allocateMavlinkChannel()
SharedLinkConfigurationPtr linkConfiguration()
SharedLinkConfigurationPtr addConfiguration(LinkConfiguration *config)
uint8_t allocateMavlinkChannel()
static LinkManager * instance()
void freeMavlinkChannel(uint8_t channel)
static constexpr uint8_t invalidMavlinkChannel()
static int getComponentId()
static MAVLinkProtocol * instance()
bool preloadMission() const
@ VideoStreamRtpUdpH265
RTP/UDP H.265 -> udp265://.
@ VideoStreamNone
No stream served.
@ VideoStreamRtpUdpH264
RTP/UDP H.264 -> udp://.
@ VideoStreamRtspH264
RTSP H.264 -> rtsp://.
@ VideoStreamMpegTsTcp
MPEG-TS over TCP -> tcp://.
@ VideoStreamMpegTsUdp
MPEG-TS over UDP -> mpegts://.
void setVehicleType(MAV_TYPE vehicleType)
@ FailInitialConnectRequestMessageAutopilotVersionLost
REQUEST_MESSAGE:AUTOPILOT_VERSION success, AUTOPILOT_VERSION never sent.
@ FailMissingParamOnInitialRequest
Not all params are sent on initial request, should still succeed since QGC will re-query missing para...
@ FailMissingParamOnAllRequests
Not all params are sent on initial request, QGC retries will fail as well.
@ FailInitialConnectRequestMessageAutopilotVersionFailure
REQUEST_MESSAGE:AUTOPILOT_VERSION returns failure.
@ FailParamNoResponseToRequestList
Do not respond to PARAM_REQUEST_LIST.
void setFailureMode(FailureMode_t failureMode)
void setEnableProximity(bool enableProximity)
void setApmStartFreshParams(bool apmStartFreshParams)
void setEnableCamera(bool enableCamera)
void setEnableGimbal(bool enableGimbal)
void setSendStatusText(bool sendStatusText)
bool cameraHasVideoStream() const
void setFtpCapability(bool ftpCapability)
void setFirmwareType(MAV_AUTOPILOT firmwareType)
@ OptionAPMStartFreshParams
void setVideoStreamType(int value)
void setPreloadMission(bool preloadMission)
void setStayMavlinkV1(bool stayV1)
Simulates MAVLink Camera Protocol v2 components for MockLink.
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)
Mock implementation of Mavlink FTP server.
QList< LogFile > logFiles() const
Returns the log files served from the @MAV_LOG virtual directory.
void setLogFiles(const QList< LogFile > &logFiles)
void mavlinkMessageReceived(const mavlink_message_t &message)
Called to handle an FTP message.
QByteArray logFileContents(const QString &name) const
Simulates MAVLink Gimbal Manager Protocol for MockLink.
void run1HzTasks()
Send periodic gimbal status messages (call from 1Hz tasks)
bool handleMavlinkMessage(const mavlink_message_t &msg)
bool handleMavlinkMessage(const mavlink_message_t &msg)
void loadSimpleMultirotorMission()
Simulates the PX4 commander magnetometer and accelerometer calibration protocols for MockLink.
void startMagCalibration()
void run10HzTasks()
Called by MockLink::run10HzTasks on the worker thread.
void startAccelCalibration()
Worker class that runs periodic tasks for MockLink simulation.
void servedVideoStream(MockConfiguration::VideoStreamType &type, QString &uri) const
@ FailRequestMessageCommandAcceptedMsgNotSent
@ FailRequestMessageCommandNoResponse
@ FailRequestMessageCommandUnsupported
void writeBytesQueuedSignal(const QByteArray &bytes)
void setMockParamValue(int componentId, const QString ¶mName, float value)
Change a float parameter value directly on MockLink (for testing cache invalidation)
void sendStatusTextMessage(uint8_t severity, const QString &text)
static constexpr MAV_CMD MAV_CMD_MOCKLINK_ALWAYS_RESULT_ACCEPTED
static MockLink * startPX4MockLinkWithMission(MockConfiguration::Options options=MockConfiguration::OptionNone, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone)
@ FailParamSetParamError
Respond with PARAM_ERROR (VALUE_OUT_OF_RANGE) instead of PARAM_VALUE.
@ FailParamSetFirstAttemptNoAck
Skip ack on first attempt, respond to retry.
@ FailParamSetNoAck
Do not send PARAM_VALUE ack.
static constexpr MAV_CMD MAV_CMD_MOCKLINK_SECOND_ATTEMPT_RESULT_ACCEPTED
static MockLink * startGenericMockLink(MockConfiguration::Options options=MockConfiguration::OptionNone, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone, MockConfiguration::VideoStreamType videoStreamType=MockConfiguration::VideoStreamNone)
static MockLink * startAPMArduCopterMockLink(MockConfiguration::Options options=MockConfiguration::OptionNone, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone, MockConfiguration::VideoStreamType videoStreamType=MockConfiguration::VideoStreamNone)
MockLinkFTP * mockLinkFTP() const
static MockLink * startPX4MockLink(MockConfiguration::Options options=MockConfiguration::OptionNone, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone, MockConfiguration::VideoStreamType videoStreamType=MockConfiguration::VideoStreamNone)
static constexpr MAV_CMD MAV_CMD_MOCKLINK_ALWAYS_RESULT_FAILED
static MockLink * startNoInitialConnectMockLink(MockConfiguration::Options options=MockConfiguration::OptionNone, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone)
QVariant paramValue(int componentId, const QString ¶mName) const
static constexpr MAV_CMD MAV_CMD_MOCKLINK_SECOND_ATTEMPT_RESULT_FAILED
static MockLink * startAPMArduSubMockLink(MockConfiguration::Options options=MockConfiguration::OptionNone, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone, MockConfiguration::VideoStreamType videoStreamType=MockConfiguration::VideoStreamNone)
static MockLink * startAPMArduPlaneMockLink(MockConfiguration::Options options=MockConfiguration::OptionNone, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone, MockConfiguration::VideoStreamType videoStreamType=MockConfiguration::VideoStreamNone)
void respondWithMavlinkMessage(const mavlink_message_t &msg)
Sends the specified mavlink message to QGC.
static MockLink * startAPMArduRoverMockLink(MockConfiguration::Options options=MockConfiguration::OptionNone, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone, MockConfiguration::VideoStreamType videoStreamType=MockConfiguration::VideoStreamNone)
bool apmStaleFailedMagCalReportStreamingActive() const
Returns true while the stale failed MAG_CAL_REPORT stream is active.
static constexpr MAV_CMD MAV_CMD_MOCKLINK_NO_RESPONSE
void setRemoteIDArmStatus(uint8_t status, const QString &error)
MockLink(SharedLinkConfigurationPtr &config, QObject *parent=nullptr)
void setArmed(bool armed)
Set the armed state of the simulated vehicle.
static constexpr MAV_CMD MAV_CMD_MOCKLINK_RESULT_IN_PROGRESS_ACCEPTED
void highLatencyTransmissionEnabledChanged(bool highLatencyTransmissionEnabled)
static constexpr MAV_CMD MAV_CMD_MOCKLINK_RESULT_IN_PROGRESS_FAILED
void sendUnexpectedCommandAck(MAV_CMD command, MAV_RESULT ackResult)
void sendStatusTextMessages()
@ FailParamRequestReadFirstAttemptNoResponse
Skip response on first attempt, respond to retry.
@ FailParamRequestReadParamError
Respond with PARAM_ERROR (DOES_NOT_EXIST) instead of PARAM_VALUE.
@ FailParamRequestReadNoResponse
Do not respond to PARAM_REQUEST_READ.
static constexpr MAV_CMD MAV_CMD_MOCKLINK_RESULT_IN_PROGRESS_NO_ACK
static constexpr MAV_CMD MAV_CMD_MOCKLINK_NO_RESPONSE_NO_RETRY
void startAPMStaleFailedMagCalReportStreaming()
Q_INVOKABLE void simulateConnectionRemoved()
Serves a synthetic (videotestsrc) live video stream for MockLink so the GStreamer receive pipeline ca...
@ MpegTsTcp
MPEG-TS over TCP -> tcp://.
@ RtpUdpH265
RTP/H.265 over UDP -> udp265://.
@ RtpUdpH264
RTP/H.264 over UDP -> udp://.
@ RtspH264
RTSP (H.264) -> rtsp://.
@ MpegTsUdp
MPEG-TS over UDP -> mpegts://.
static FactMetaData::ValueType_t mavTypeToFactType(MAV_PARAM_TYPE mavType)
uint64_t currentSigningTimestampTicks()
Current signing timestamp in 10µs ticks since 2015-01-01.
bool insecureConnectionAcceptUnsignedCallback(const mavlink_status_t *status, uint32_t message_id)
quint32 crc32(const quint8 *src, unsigned len, unsigned state)
void secureZero(void *data, size_t size)
@ PX4_CUSTOM_MAIN_MODE_AUTO
@ PX4_CUSTOM_SUB_MODE_AUTO_RESERVED_DO_NOT_USE