18#include <QtCore/QFile>
19#include <QtCore/QJsonArray>
20#include <QtCore/QJsonDocument>
21#include <QtCore/QJsonObject>
22#include <QtCore/QMutexLocker>
24#include <QtCore/QRandomGenerator>
25#include <QtCore/QTemporaryFile>
26#include <QtCore/QThread>
27#include <QtCore/QTimer>
35std::atomic<
int>
MockLink::_nextVehicleSystemId{128};
37QList<MockLink::FlightMode_t> MockLink::_availableFlightModes = {
60 , _firmwareType(_mockConfig->firmwareType())
61 , _vehicleType(_mockConfig->vehicleType())
62 , _sendStatusText(_mockConfig->sendStatusText())
63 , _enableCamera(_mockConfig->enableCamera())
64 , _enableGimbal(_mockConfig->enableGimbal())
65 , _failureMode(_mockConfig->failureMode())
66 , _vehicleSystemId(_mockConfig->incrementVehicleId() ? _nextVehicleSystemId++ : static_cast<int>(_nextVehicleSystemId))
67 , _vehicleLatitude(_defaultVehicleLatitude + ((_vehicleSystemId - 128) * 0.0001))
68 , _vehicleLongitude(_defaultVehicleLongitude + ((_vehicleSystemId - 128) * 0.0001))
69 , _boardVendorId(_mockConfig->boardVendorId())
70 , _boardProductId(_mockConfig->boardProductId())
73 _mockConfig->cameraCaptureVideo(),
74 _mockConfig->cameraCaptureImage(),
75 _mockConfig->cameraHasModes(),
76 _mockConfig->cameraHasVideoStream(),
77 _mockConfig->cameraCanCaptureImageInVideoMode(),
78 _mockConfig->cameraCanCaptureVideoInImageMode(),
79 _mockConfig->cameraHasBasicZoom(),
80 _mockConfig->cameraHasTrackingPoint(),
81 _mockConfig->cameraHasTrackingRectangle())
84 _mockConfig->gimbalHasRollAxis(),
85 _mockConfig->gimbalHasPitchAxis(),
86 _mockConfig->gimbalHasYawAxis(),
87 _mockConfig->gimbalHasYawFollow(),
88 _mockConfig->gimbalHasYawLock(),
89 _mockConfig->gimbalHasRetract(),
90 _mockConfig->gimbalHasNeutral())
93 , _mockLinkFTP(new
MockLinkFTP(_vehicleSystemId, _vehicleComponentId, this))
95 qCDebug(MockLinkLog) <<
this;
106 _adsbVehicles.reserve(_numberOfVehicles);
107 for (
int i = 0; i < _numberOfVehicles; ++i) {
108 ADSBVehicle vehicle{};
109 vehicle.angle = i * 72.0;
112 const double latOffset = 0.001 * i;
113 const double lonOffset = 0.001 * (i % 2 == 0 ? i : -i);
114 vehicle.coordinate = QGeoCoordinate(_defaultVehicleLatitude + latOffset, _defaultVehicleLongitude + lonOffset);
117 vehicle.altitude = _defaultVehicleHomeAltitude + (i * 5);
119 _adsbVehicles.append(vehicle);
125 _runningTime.start();
127 _workerThread =
new QThread(
this);
128 _workerThread->setObjectName(QStringLiteral(
"Mock_%1").arg(_mockConfig->
name()));
130 _worker->moveToThread(_workerThread);
132 (void) connect(_workerThread, &QThread::finished, _worker, &QObject::deleteLater);
133 _workerThread->start();
140 delete _mockLinkCamera;
141 delete _mockLinkGimbal;
142 delete _mockLinkPX4Calibration;
144 if (!_logDownloadFilename.isEmpty()) {
145 QFile::remove(_logDownloadFilename);
149 _workerThread->quit();
150 _workerThread->wait();
153 qCDebug(MockLinkLog) <<
this;
156bool MockLink::_connect()
160 _disconnectedEmitted =
false;
162 outgoingStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
164 incomingStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
179 if (_workerThread && _workerThread->isRunning()) {
180 _workerThread->quit();
181 _workerThread->wait();
185 if (_outgoingMavlinkChannelIsSet()) {
187 outgoingStatus->signing =
nullptr;
188 outgoingStatus->signing_streams =
nullptr;
189 mavlink_reset_channel_status(_outgoingMavlinkChannel);
194 if (!_disconnectedEmitted.exchange(
true)) {
212 _sendBatteryStatus();
213 _sendNamedValueFloats();
216 if (_vehicleType != MAV_TYPE_SUBMARINE) {
217 _sendRemoteIDArmStatus();
219 _sendAvailableModesMonitor();
238 if (_sendHomePositionDelayCount > 0) {
240 _sendHomePositionDelayCount--;
244 if (_availableModesMonitorSeqNumber == 0) {
245 qCDebug(MockLinkLog) <<
"Bumping sequence number for available modes monitor to trigger requery of modes";
246 _availableModesMonitorSeqNumber = 1;
259 const bool gpsDelayExpired = (_sendGPSPositionDelayCount == 0);
260 if (_sendGPSPositionDelayCount > 0) {
262 _sendGPSPositionDelayCount--;
265 if (_vehicleType != MAV_TYPE_SUBMARINE) {
267 _sendGlobalPositionInt();
269 _sendExtendedSysState();
272 _sendAttitudeQuaternion();
273 _sendAttitudeTarget();
274 _sendLocalPositionNed();
275 _sendPositionTargetLocalNed();
293 for (
int i = 0; i < paramSends; ++i) {
294 _paramRequestListWorker();
296 _logDownloadWorker();
297 _availableModesWorker();
298 _apmCompassCalWorker();
299 _apmAccelCalWorker();
305 _sendStatusTextMessages();
308bool MockLink::_allocateMavlinkChannel()
311 Q_ASSERT(!_incomingMavlinkChannelIsSet());
312 Q_ASSERT(!_outgoingMavlinkChannelIsSet());
316 qCWarning(MockLinkLog) <<
"LinkInterface::_allocateMavlinkChannel failed";
321 if (!_incomingMavlinkChannelIsSet()) {
322 qCWarning(MockLinkLog) <<
"_allocateMavlinkChannel aux failed";
328 if (!_outgoingMavlinkChannelIsSet()) {
329 qCWarning(MockLinkLog) <<
"_allocateMavlinkChannel vehicle failed";
335 qCDebug(MockLinkLog) <<
"_allocateMavlinkChannel aux:" << _incomingMavlinkChannel <<
"vehicle:" << _outgoingMavlinkChannel;
339void MockLink::_freeMavlinkChannel()
341 qCDebug(MockLinkLog) <<
"_freeMavlinkChannel aux:" << _incomingMavlinkChannel <<
"vehicle:" << _outgoingMavlinkChannel;
342 if (!_incomingMavlinkChannelIsSet()) {
343 Q_ASSERT(!_outgoingMavlinkChannelIsSet());
348 if (_outgoingMavlinkChannelIsSet()) {
350 mavlink_reset_channel_status(_outgoingMavlinkChannel);
354 mavlink_reset_channel_status(_incomingMavlinkChannel);
360bool MockLink::_incomingMavlinkChannelIsSet()
const
365bool MockLink::_outgoingMavlinkChannelIsSet()
const
370void MockLink::_loadParams()
373 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
374 if (_vehicleType == MAV_TYPE_FIXED_WING) {
375 paramFile.setFileName(
":/FirmwarePlugin/APM/Plane.OfflineEditing.params");
376 }
else if (_vehicleType == MAV_TYPE_SUBMARINE ) {
377 paramFile.setFileName(
":/FirmwarePlugin/APM/Sub.OfflineEditing.params");
378 }
else if (_vehicleType == MAV_TYPE_GROUND_ROVER ) {
379 paramFile.setFileName(
":/FirmwarePlugin/APM/Rover.OfflineEditing.params");
381 paramFile.setFileName(
":/FirmwarePlugin/APM/Copter.OfflineEditing.params");
384 paramFile.setFileName(
":/MockLink/PX4MockLink.params");
387 const bool success = paramFile.open(QFile::ReadOnly);
391 QTextStream paramStream(¶mFile);
392 while (!paramStream.atEnd()) {
393 const QString line = paramStream.readLine();
395 if (line.startsWith(
"#")) {
399 const QStringList paramData = line.split(
"\t");
400 Q_ASSERT(paramData.count() == 5);
402 const int compId = paramData.at(1).toInt();
403 const QString paramName = paramData.at(2);
404 const QString valStr = paramData.at(3);
405 const uint paramType = paramData.at(4).toUInt();
409 case MAV_PARAM_TYPE_REAL32:
412 case MAV_PARAM_TYPE_UINT32:
415 case MAV_PARAM_TYPE_INT32:
418 case MAV_PARAM_TYPE_UINT16:
419 paramValue = QVariant((quint16)valStr.toUInt());
421 case MAV_PARAM_TYPE_INT16:
422 paramValue = QVariant((qint16)valStr.toInt());
424 case MAV_PARAM_TYPE_UINT8:
425 paramValue = QVariant((quint8)valStr.toUInt());
427 case MAV_PARAM_TYPE_INT8:
428 paramValue = QVariant((qint8)valStr.toUInt());
431 qCCritical(MockLinkVerboseLog) <<
"Unknown type" << paramType;
436 qCDebug(MockLinkVerboseLog) <<
"Loading param" << paramName <<
paramValue;
438 _mapParamName2Value[compId][paramName] =
paramValue;
439 _mapParamName2MavParamType[compId][paramName] =
static_cast<MAV_PARAM_TYPE
>(paramType);
453void MockLink::_resetParamsToDefaults()
455 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
459 for (
auto compIt = _mapParamName2Value.begin(); compIt != _mapParamName2Value.end(); ++compIt) {
460 for (
auto paramIt = compIt.value().begin(); paramIt != compIt.value().end(); ++paramIt) {
461 if (kAPMCalOffsetParams.contains(paramIt.key())) {
462 paramIt.value() = QVariant(0.0f);
469 if (_firmwareType != MAV_AUTOPILOT_PX4) {
470 qCWarning(MockLinkLog) <<
"Param reset to defaults not supported for firmware type" << _firmwareType;
474 QFile metaDataFile(QStringLiteral(
":/MockLink/Parameter.MetaData.json"));
475 if (!metaDataFile.open(QFile::ReadOnly)) {
476 qCWarning(MockLinkLog) <<
"Unable to open parameter metadata for reset" << metaDataFile.fileName();
480 QJsonParseError parseError{};
481 const QJsonDocument doc = QJsonDocument::fromJson(metaDataFile.readAll(), &parseError);
482 if (parseError.error != QJsonParseError::NoError) {
483 qCWarning(MockLinkLog) <<
"Unable to parse parameter metadata for reset:" << parseError.errorString();
487 QHash<QString, QVariant> defaults;
488 const QJsonArray parameters = doc.object().value(QStringLiteral(
"parameters")).toArray();
489 for (
const QJsonValue ¶meter : parameters) {
490 const QJsonObject paramObject = parameter.toObject();
491 if (paramObject.contains(QStringLiteral(
"default"))) {
492 defaults[paramObject.value(QStringLiteral(
"name")).toString()] = paramObject.value(QStringLiteral(
"default")).toVariant();
496 for (
auto compIt = _mapParamName2Value.begin(); compIt != _mapParamName2Value.end(); ++compIt) {
497 const int compId = compIt.key();
498 for (
auto paramIt = compIt.value().begin(); paramIt != compIt.value().end(); ++paramIt) {
499 const QString ¶mName = paramIt.key();
500 if (!_resetSysAutostartOnParamReset && (paramName == QLatin1String(
"SYS_AUTOSTART"))) {
503 const auto defaultIt = defaults.constFind(paramName);
504 if (defaultIt == defaults.constEnd()) {
507 switch (_mapParamName2MavParamType[compId][paramName]) {
508 case MAV_PARAM_TYPE_REAL32:
509 paramIt.value() = QVariant(defaultIt->toFloat());
511 case MAV_PARAM_TYPE_UINT32:
512 paramIt.value() = QVariant(defaultIt->toUInt());
514 case MAV_PARAM_TYPE_INT32:
515 paramIt.value() = QVariant(defaultIt->toInt());
517 case MAV_PARAM_TYPE_UINT16:
518 paramIt.value() = QVariant(
static_cast<quint16
>(defaultIt->toUInt()));
520 case MAV_PARAM_TYPE_INT16:
521 paramIt.value() = QVariant(
static_cast<qint16
>(defaultIt->toInt()));
523 case MAV_PARAM_TYPE_UINT8:
524 paramIt.value() = QVariant(
static_cast<quint8
>(defaultIt->toUInt()));
526 case MAV_PARAM_TYPE_INT8:
527 paramIt.value() = QVariant(
static_cast<qint8
>(defaultIt->toInt()));
530 qCWarning(MockLinkLog) <<
"Param reset skipped unhandled type" << _mapParamName2MavParamType[compId][paramName] << paramName;
537void MockLink::_sendHeartBeat()
540 (void) mavlink_msg_heartbeat_pack_chan(
543 _outgoingMavlinkChannel,
554void MockLink::_sendHighLatency2()
556 qCDebug(MockLinkLog) <<
"Sending" << _mavCustomMode;
559 px4_cm.
data = _mavCustomMode;
562 (void) mavlink_msg_high_latency2_pack_chan(
565 _outgoingMavlinkChannel,
570 px4_cm.custom_mode_hl,
571 static_cast<int32_t
>(_vehicleLatitude * 1E7),
572 static_cast<int32_t
>(_vehicleLongitude * 1E7),
573 static_cast<int16_t
>(_vehicleAltitudeAMSL),
574 static_cast<int16_t
>(_vehicleAltitudeAMSL),
596void MockLink::_sendSysStatus()
599 (void) mavlink_msg_sys_status_pack_chan(
602 _outgoingMavlinkChannel,
604 MAV_SYS_STATUS_SENSOR_GPS,
610 _battery1PctRemaining,
616void MockLink::_sendBatteryStatus()
618 if (_battery1PctRemaining > 1) {
619 _battery1PctRemaining =
static_cast<int8_t
>(100 - (_runningTime.elapsed() / 1000));
620 _battery1TimeRemaining =
static_cast<double>(_batteryMaxTimeRemaining) * (
static_cast<double>(_battery1PctRemaining) / 100.0);
621 if (_battery1PctRemaining > 50) {
622 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_OK;
623 }
else if (_battery1PctRemaining > 30) {
624 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_LOW;
625 }
else if (_battery1PctRemaining > 20) {
626 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_CRITICAL;
628 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_EMERGENCY;
632 if (_battery2PctRemaining > 1) {
633 _battery2PctRemaining =
static_cast<int8_t
>(100 - ((_runningTime.elapsed() / 1000) / 2));
634 _battery2TimeRemaining =
static_cast<double>(_batteryMaxTimeRemaining) * (
static_cast<double>(_battery2PctRemaining) / 100.0);
635 if (_battery2PctRemaining > 50) {
636 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_OK;
637 }
else if (_battery2PctRemaining > 30) {
638 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_LOW;
639 }
else if (_battery2PctRemaining > 20) {
640 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_CRITICAL;
642 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_EMERGENCY;
647 uint16_t rgVoltages[10]{};
648 uint16_t rgVoltagesNone[10]{};
649 uint16_t rgVoltagesExtNone[4]{};
651 for (
size_t i = 0; i < std::size(rgVoltages); i++) {
652 rgVoltages[i] = UINT16_MAX;
653 rgVoltagesNone[i] = UINT16_MAX;
655 rgVoltages[0] = rgVoltages[1] = rgVoltages[2] = 4200;
657 (void) mavlink_msg_battery_status_pack_chan(
660 _outgoingMavlinkChannel,
663 MAV_BATTERY_FUNCTION_ALL,
664 MAV_BATTERY_TYPE_LIPO,
670 _battery1PctRemaining,
671 _battery1TimeRemaining,
672 _battery1ChargeState,
679 (void) mavlink_msg_battery_status_pack_chan(
682 _outgoingMavlinkChannel,
685 MAV_BATTERY_FUNCTION_ALL,
686 MAV_BATTERY_TYPE_LIPO,
692 _battery2PctRemaining,
693 _battery2TimeRemaining,
694 _battery2ChargeState,
702void MockLink::_sendNamedValueFloats()
704 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
707 const float sinVal =
static_cast<float>(std::sin(
static_cast<double>(timeBootMs) / 1000.0));
708 const float cosVal =
static_cast<float>(std::cos(
static_cast<double>(timeBootMs) / 1000.0));
711 static constexpr char kSinName[10] =
"sin_wave";
712 static constexpr char kCosName[10] =
"cos_wave";
715 (void) mavlink_msg_named_value_float_pack_chan(
718 _outgoingMavlinkChannel,
726 (void) mavlink_msg_named_value_float_pack_chan(
729 _outgoingMavlinkChannel,
738void MockLink::_sendVibration()
741 (void) mavlink_msg_vibration_pack_chan(
744 _outgoingMavlinkChannel,
760 uint8_t buffer[MAVLINK_MAX_PACKET_LEN]{};
761 const int cBuffer = mavlink_msg_to_send_buffer(buffer, &msg);
762 const QByteArray bytes(
reinterpret_cast<char*
>(buffer), cBuffer);
767void MockLink::_writeBytes(
const QByteArray &bytes)
773void MockLink::_writeBytesQueued(
const QByteArray &bytes)
776 qCDebug(MockLinkLog) <<
"Dropping queued bytes on disconnected/uninitialized mock link";
781 _handleIncomingNSHBytes(bytes.constData(), bytes.length());
785 if (bytes.startsWith(QByteArrayLiteral(
"\r\r\r"))) {
787 _handleIncomingNSHBytes(&bytes.constData()[3], bytes.length() - 3);
790 _handleIncomingMavlinkBytes(
reinterpret_cast<const uint8_t*
>(bytes.constData()), bytes.length());
793void MockLink::_handleIncomingNSHBytes(
const char *bytes,
int cBytes)
798 if ((cBytes == 4) && (bytes[0] ==
'\r') && (bytes[1] ==
'\r') && (bytes[2] ==
'\r')) {
804 qCDebug(MockLinkLog) <<
"NSH:" << bytes;
807 if (strncmp(bytes,
"sh /etc/init.d/rc.usb\n", cBytes) == 0) {
809 _mavlinkStarted =
true;
815void MockLink::_handleIncomingMavlinkBytes(
const uint8_t *bytes,
int cBytes)
818 mavlink_status_t comm{};
820 QMutexLocker lock(&_incomingMavlinkMutex);
821 for (qint64 i = 0; i < cBytes; i++) {
822 const int parsed = mavlink_parse_char(_incomingMavlinkChannel, bytes[i], &msg, &comm);
827 _handleIncomingMavlinkMsg(msg);
834 _receivedMavlinkMessageCountMap[msg.msgid]++;
837 if (msg.msgid == MAVLINK_MSG_ID_COMMAND_LONG) {
839 mavlink_msg_command_long_decode(&msg, &request);
841 _receivedMavCommandCountMap[
static_cast<MAV_CMD
>(request.command)]++;
842 _receivedMavCommandByCompCountMap[
static_cast<MAV_CMD
>(request.command)][request.target_component]++;
844 if (request.command == MAV_CMD_REQUEST_MESSAGE) {
845 _receivedRequestMessageCountMap[
static_cast<uint32_t
>(request.param1)]++;
846 _receivedRequestMessageByCompAndMsgCountMap[request.target_component][
static_cast<int>(request.param1)]++;
848 }
else if (msg.msgid == MAVLINK_MSG_ID_COMMAND_INT) {
849 mavlink_command_int_t request{};
850 mavlink_msg_command_int_decode(&msg, &request);
852 _receivedMavCommandCountMap[
static_cast<MAV_CMD
>(request.command)]++;
853 _receivedMavCommandByCompCountMap[
static_cast<MAV_CMD
>(request.command)][request.target_component]++;
859 _updateIncomingMessageCounts(msg);
874 case MAVLINK_MSG_ID_HEARTBEAT:
875 _handleHeartBeat(msg);
877 case MAVLINK_MSG_ID_PARAM_REQUEST_LIST:
878 _handleParamRequestList(msg);
880 case MAVLINK_MSG_ID_SET_MODE:
883 case MAVLINK_MSG_ID_PARAM_SET:
884 _handleParamSet(msg);
886 case MAVLINK_MSG_ID_PARAM_REQUEST_READ:
887 _handleParamRequestRead(msg);
889 case MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL:
892 case MAVLINK_MSG_ID_COMMAND_LONG:
893 _handleCommandLong(msg);
895 case MAVLINK_MSG_ID_COMMAND_INT:
896 _handleCommandInt(msg);
898 case MAVLINK_MSG_ID_MANUAL_CONTROL:
899 _handleManualControl(msg);
901 case MAVLINK_MSG_ID_RC_CHANNELS_OVERRIDE:
902 _handleRCChannelsOverride(msg);
904 case MAVLINK_MSG_ID_LOG_REQUEST_LIST:
905 _handleLogRequestList(msg);
907 case MAVLINK_MSG_ID_LOG_REQUEST_DATA:
908 _handleLogRequestData(msg);
910 case MAVLINK_MSG_ID_PARAM_MAP_RC:
911 _handleParamMapRC(msg);
913 case MAVLINK_MSG_ID_SETUP_SIGNING:
914 _handleSetupSigning(msg);
916 case MAVLINK_MSG_ID_COMMAND_ACK:
919 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
921 mavlink_msg_command_ack_decode(&msg, &ack);
922 if (ack.command == 0) {
923 QMutexLocker locker(&_apmAccelCalMutex);
924 if (_apmAccelCalPosIndex >= 0 && _apmAccelCalPosIndex < 6) {
925 _apmAccelCalGotAck =
true;
938 qCDebug(MockLinkVerboseLog) <<
"Heartbeat";
943 mavlink_param_map_rc_t paramMapRC{};
944 mavlink_msg_param_map_rc_decode(&msg, ¶mMapRC);
946 const QString paramName(QString::fromLocal8Bit(paramMapRC.param_id,
static_cast<int>(strnlen(paramMapRC.param_id, MAVLINK_MSG_PARAM_MAP_RC_FIELD_PARAM_ID_LEN))));
948 if (paramMapRC.param_index == -1) {
949 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);
950 }
else if (paramMapRC.param_index == -2) {
951 qCDebug(MockLinkLog) <<
"MockLink - PARAM_MAP_RC: Clear tuningID" << paramMapRC.parameter_rc_channel_index;
953 qCWarning(MockLinkLog) <<
"MockLink - PARAM_MAP_RC: Unsupported param_index" << paramMapRC.param_index;
960 mavlink_msg_setup_signing_decode(&msg, &setupSigning);
962 if (setupSigning.target_system != _vehicleSystemId) {
967 bool allZeroKey =
true;
968 for (
const uint8_t
byte : setupSigning.secret_key) {
975 _signingEnabled = !allZeroKey;
979 if (_signingEnabled) {
980 memcpy(_mockSigning.secret_key, setupSigning.secret_key,
sizeof(_mockSigning.secret_key));
981 _mockSigning.link_id = _outgoingMavlinkChannel;
982 _mockSigning.flags = MAVLINK_SIGNING_FLAG_SIGN_OUTGOING;
985 status->signing = &_mockSigning;
986 status->signing_streams = &_mockSigningStreams;
988 QGC::secureZero(_mockSigning.secret_key,
sizeof(_mockSigning.secret_key));
989 _mockSigning.accept_unsigned_callback =
nullptr;
990 status->signing =
nullptr;
991 status->signing_streams =
nullptr;
994 qCDebug(MockLinkLog) <<
"Signing" << (_signingEnabled ?
"enabled" :
"disabled");
999 mavlink_set_mode_t request{};
1000 mavlink_msg_set_mode_decode(&msg, &request);
1002 Q_ASSERT(request.target_system == _vehicleSystemId);
1004 _mavBaseMode = request.base_mode;
1005 _mavCustomMode = request.custom_mode;
1010 mavlink_manual_control_t manualControl{};
1011 mavlink_msg_manual_control_decode(&msg, &manualControl);
1014 const auto axisStr = [](int16_t v) -> QString {
1015 return (v == INT16_MAX) ? QStringLiteral(
"invalid") : QString::number(v);
1018 const auto extStr = [](int16_t v,
bool enabled) -> QString {
1019 return enabled ? QString::number(v) : QStringLiteral(
"disabled");
1022 const uint8_t ext = manualControl.enabled_extensions;
1024 qCDebug(MockLinkVerboseLog).noquote()
1026 <<
"target:" << manualControl.target
1027 <<
"x:" << axisStr(manualControl.x)
1028 <<
"y:" << axisStr(manualControl.y)
1029 <<
"z:" << axisStr(manualControl.z)
1030 <<
"r:" << axisStr(manualControl.r)
1031 <<
"buttons:" << QStringLiteral(
"0x%1").arg(manualControl.buttons, 4, 16, QLatin1Char(
'0'))
1032 <<
"buttons2:" << QStringLiteral(
"0x%1").arg(manualControl.buttons2, 4, 16, QLatin1Char(
'0'))
1033 <<
"enabled_extensions:" << QStringLiteral(
"0x%1").arg(ext, 2, 16, QLatin1Char(
'0'))
1034 <<
"s(pitch):" << extStr(manualControl.s, ext & (1 << 0))
1035 <<
"t(roll):" << extStr(manualControl.t, ext & (1 << 1))
1036 <<
"aux1:" << extStr(manualControl.aux1, ext & (1 << 2))
1037 <<
"aux2:" << extStr(manualControl.aux2, ext & (1 << 3))
1038 <<
"aux3:" << extStr(manualControl.aux3, ext & (1 << 4))
1039 <<
"aux4:" << extStr(manualControl.aux4, ext & (1 << 5))
1040 <<
"aux5:" << extStr(manualControl.aux5, ext & (1 << 6))
1041 <<
"aux6:" << extStr(manualControl.aux6, ext & (1 << 7));
1046 mavlink_rc_channels_override_t
override{};
1047 mavlink_msg_rc_channels_override_decode(&msg, &
override);
1052 const uint16_t rawValues[18] = {
1053 override.chan1_raw,
override.chan2_raw,
override.chan3_raw,
override.chan4_raw,
1054 override.chan5_raw,
override.chan6_raw,
override.chan7_raw,
override.chan8_raw,
1055 override.chan9_raw,
override.chan10_raw,
override.chan11_raw,
override.chan12_raw,
1056 override.chan13_raw,
override.chan14_raw,
override.chan15_raw,
override.chan16_raw,
1057 override.chan17_raw,
override.chan18_raw,
1060 bool anyChange =
false;
1061 for (
int i = 0; i < kRcChannelOverrideChannelCount; ++i) {
1062 const uint16_t raw = rawValues[i];
1063 const bool isExtended = (i >= 8);
1065 RCChannelOverride::State newState;
1067 if (raw == 0 || raw == UINT16_MAX) {
1069 }
else if (raw ==
static_cast<uint16_t
>(UINT16_MAX - 1)) {
1070 newState = RCChannelOverride::State::Released;
1072 newState = RCChannelOverride::State::Overridden;
1075 if (raw == UINT16_MAX) {
1077 }
else if (raw == 0) {
1078 newState = RCChannelOverride::State::Released;
1080 newState = RCChannelOverride::State::Overridden;
1084 RCChannelOverride &ch = _rcChannelOverrides[i];
1085 if (ch.state == newState) {
1091 const auto stateLabel = [](RCChannelOverride::State s) ->
const char * {
1093 case RCChannelOverride::State::Ignore:
return "ignore";
1094 case RCChannelOverride::State::Released:
return "released";
1095 case RCChannelOverride::State::Overridden:
return "overridden";
1099 qCDebug(MockLinkLog).noquote() << QStringLiteral(
"RC_CHANNELS_OVERRIDE ch%1: %2 -> %3").arg(i + 1).arg(stateLabel(ch.state)).arg(stateLabel(newState));
1101 ch.state = newState;
1102 ch.value = (newState == RCChannelOverride::State::Overridden) ? raw : 0;
1107 for (
int i = 0; i < kRcChannelOverrideChannelCount; ++i) {
1108 if (_rcChannelOverrides[i].state == RCChannelOverride::State::Overridden) {
1109 active << QStringLiteral(
"ch%1").arg(i + 1);
1112 if (active.isEmpty()) {
1113 qCDebug(MockLinkLog) <<
"RC_CHANNELS_OVERRIDE: no channels currently overridden";
1115 qCDebug(MockLinkLog).noquote() <<
"RC_CHANNELS_OVERRIDE active overrides:" << active.join(QStringLiteral(
", "));
1119 for (
int i = 0; i < kRcChannelOverrideChannelCount; ++i) {
1120 if (_rcChannelOverrides[i].state == RCChannelOverride::State::Overridden) {
1121 qCDebug(MockLinkVerboseLog).noquote() << QStringLiteral(
"RC_CHANNELS_OVERRIDE ch%1 value: %2").arg(i + 1).arg(_rcChannelOverrides[i].value);
1126void MockLink::_setParamFloatUnionIntoMap(
int componentId,
const QString ¶mName,
float paramFloat)
1128 Q_ASSERT(_mapParamName2Value.contains(componentId));
1129 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
1130 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
1132 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
1133 QVariant paramVariant;
1135 valueUnion.param_float = paramFloat;
1136 switch (paramType) {
1137 case MAV_PARAM_TYPE_REAL32:
1138 paramVariant = QVariant::fromValue(valueUnion.param_float);
1140 case MAV_PARAM_TYPE_UINT32:
1141 paramVariant = QVariant::fromValue(valueUnion.param_uint32);
1143 case MAV_PARAM_TYPE_INT32:
1144 paramVariant = QVariant::fromValue(valueUnion.param_int32);
1146 case MAV_PARAM_TYPE_UINT16:
1147 paramVariant = QVariant::fromValue(valueUnion.param_uint16);
1149 case MAV_PARAM_TYPE_INT16:
1150 paramVariant = QVariant::fromValue(valueUnion.param_int16);
1152 case MAV_PARAM_TYPE_UINT8:
1153 paramVariant = QVariant::fromValue(valueUnion.param_uint8);
1155 case MAV_PARAM_TYPE_INT8:
1156 paramVariant = QVariant::fromValue(valueUnion.param_int8);
1159 qCCritical(MockLinkLog) <<
"Invalid parameter type" << paramType;
1160 paramVariant = QVariant::fromValue(valueUnion.param_int32);
1164 qCDebug(MockLinkLog) <<
"_setParamFloatUnionIntoMap" << paramName << paramVariant;
1165 _mapParamName2Value[componentId][paramName] = paramVariant;
1171 valueUnion.param_float = value;
1172 _setParamFloatUnionIntoMap(componentId, paramName, valueUnion.param_float);
1175float MockLink::_floatUnionForParam(
int componentId,
const QString ¶mName)
1177 Q_ASSERT(_mapParamName2Value.contains(componentId));
1178 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
1179 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
1181 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
1182 const QVariant paramVar = _mapParamName2Value[componentId][paramName];
1185 switch (paramType) {
1186 case MAV_PARAM_TYPE_REAL32:
1187 valueUnion.param_float = paramVar.toFloat();
1189 case MAV_PARAM_TYPE_UINT32:
1190 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1191 valueUnion.param_float = paramVar.toUInt();
1193 valueUnion.param_uint32 = paramVar.toUInt();
1196 case MAV_PARAM_TYPE_INT32:
1197 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1198 valueUnion.param_float = paramVar.toInt();
1200 valueUnion.param_int32 = paramVar.toInt();
1203 case MAV_PARAM_TYPE_UINT16:
1204 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1205 valueUnion.param_float = paramVar.toUInt();
1207 valueUnion.param_uint16 = paramVar.toUInt();
1210 case MAV_PARAM_TYPE_INT16:
1211 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1212 valueUnion.param_float = paramVar.toInt();
1214 valueUnion.param_int16 = paramVar.toInt();
1217 case MAV_PARAM_TYPE_UINT8:
1218 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1219 valueUnion.param_float = paramVar.toUInt();
1221 valueUnion.param_uint8 = paramVar.toUInt();
1224 case MAV_PARAM_TYPE_INT8:
1225 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1226 valueUnion.param_float = (
unsigned char)paramVar.toChar().toLatin1();
1228 valueUnion.param_int8 = (
unsigned char)paramVar.toChar().toLatin1();
1232 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1233 valueUnion.param_float = paramVar.toInt();
1235 valueUnion.param_int32 = paramVar.toInt();
1237 qCCritical(MockLinkLog) <<
"Invalid parameter type" << paramType;
1240 return valueUnion.param_float;
1243uint32_t MockLink::_computeParamHash(
int componentId)
const
1246 static const QStringList volatileParams = {
1247 QStringLiteral(
"COM_FLIGHT_UUID"),
1248 QStringLiteral(
"EKF2_MAGBIAS_X"),
1249 QStringLiteral(
"EKF2_MAGBIAS_Y"),
1250 QStringLiteral(
"EKF2_MAGBIAS_Z"),
1251 QStringLiteral(
"EKF2_MAG_DECL"),
1252 QStringLiteral(
"LND_FLIGHT_T_HI"),
1253 QStringLiteral(
"LND_FLIGHT_T_LO"),
1254 QStringLiteral(
"SYS_RESTART_TYPE"),
1258 const auto ¶ms = _mapParamName2Value[componentId];
1259 for (
auto it = params.constBegin(); it != params.constEnd(); ++it) {
1260 const QString &name = it.key();
1261 if (volatileParams.contains(name)) {
1264 const QVariant &value = it.value();
1265 const MAV_PARAM_TYPE mavType = _mapParamName2MavParamType[componentId][name];
1268 crc =
QGC::crc32(
reinterpret_cast<const uint8_t *
>(qPrintable(name)), name.length(), crc);
1280 mavlink_param_request_list_t request{};
1281 mavlink_msg_param_request_list_decode(&msg, &request);
1283 Q_ASSERT(request.target_system == _vehicleSystemId);
1284 Q_ASSERT(request.target_component == MAV_COMP_ID_ALL);
1288 QMutexLocker locker(&_paramRequestListMutex);
1289 _paramRequestListComponentIds = _mapParamName2Value.keys();
1290 if (!_paramRequestListComponentIds.isEmpty()) {
1291 _paramRequestListParamNames = _mapParamName2Value[_paramRequestListComponentIds.first()].keys();
1295 _currentParamRequestListComponentIndex = 0;
1296 _currentParamRequestListParamIndex = 0;
1297 _paramRequestListHashCheckSent =
false;
1300void MockLink::_paramRequestListWorker()
1302 if (_currentParamRequestListComponentIndex == -1) {
1308 QMutexLocker locker(&_paramRequestListMutex);
1311 if (_currentParamRequestListComponentIndex >= _paramRequestListComponentIds.count()) {
1312 _currentParamRequestListComponentIndex = -1;
1316 const int componentId = _paramRequestListComponentIds.at(_currentParamRequestListComponentIndex);
1317 const int cParameters = _paramRequestListParamNames.count();
1319 if (_currentParamRequestListParamIndex >= cParameters) {
1323 if (_firmwareType == MAV_AUTOPILOT_PX4 && !_paramRequestListHashCheckSent) {
1324 _paramRequestListHashCheckSent =
true;
1327 valueUnion.type = MAV_PARAM_TYPE_UINT32;
1328 valueUnion.param_uint32 = _computeParamHash(componentId);
1330 char paramId[MAVLINK_MSG_ID_PARAM_VALUE_LEN]{};
1331 (void) strncpy(paramId,
"_HASH_CHECK", MAVLINK_MSG_ID_PARAM_VALUE_LEN);
1333 qCDebug(MockLinkLog) <<
"Sending _HASH_CHECK in PARAM_REQUEST_LIST stream" << componentId <<
"hash:" << valueUnion.param_uint32;
1336 (void) mavlink_msg_param_value_pack_chan(
1339 _outgoingMavlinkChannel,
1342 valueUnion.param_float,
1343 MAV_PARAM_TYPE_UINT32,
1352 if (++_currentParamRequestListComponentIndex >= _paramRequestListComponentIds.count()) {
1353 _currentParamRequestListComponentIndex = -1;
1354 _paramRequestListComponentIds.clear();
1355 _paramRequestListParamNames.clear();
1358 _paramRequestListParamNames = _mapParamName2Value[_paramRequestListComponentIds.at(_currentParamRequestListComponentIndex)].keys();
1359 _currentParamRequestListParamIndex = 0;
1360 _paramRequestListHashCheckSent =
false;
1365 const QString ¶mName = _paramRequestListParamNames.at(_currentParamRequestListParamIndex);
1368 qCDebug(MockLinkLog) <<
"Skipping param send:" << paramName;
1370 char paramId[MAVLINK_MSG_ID_PARAM_VALUE_LEN]{};
1373 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
1374 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
1376 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
1378 Q_ASSERT(paramName.length() <= MAVLINK_MSG_ID_PARAM_VALUE_LEN);
1379 (void) strncpy(paramId, paramName.toLocal8Bit().constData(), MAVLINK_MSG_ID_PARAM_VALUE_LEN);
1381 qCDebug(MockLinkLog) <<
"Sending msg_param_value" << componentId << paramId << paramType << _mapParamName2Value[componentId][paramId];
1383 (void) mavlink_msg_param_value_pack_chan(
1386 _outgoingMavlinkChannel,
1389 _floatUnionForParam(componentId, paramName),
1392 _currentParamRequestListParamIndex
1398 ++_currentParamRequestListParamIndex;
1403 mavlink_param_set_t request{};
1404 mavlink_msg_param_set_decode(&msg, &request);
1406 Q_ASSERT(request.target_system == _vehicleSystemId);
1407 const int componentId = request.target_component;
1410 char paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN + 1]{};
1411 paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN] = 0;
1412 (void) strncpy(paramId, request.param_id, MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN);
1414 qCDebug(MockLinkLog) <<
"_handleParamSet" << componentId << paramId << request.param_type;
1419 if ((_firmwareType == MAV_AUTOPILOT_PX4) && (strncmp(paramId,
"_HASH_CHECK", MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN) == 0)) {
1420 QMutexLocker locker(&_paramRequestListMutex);
1421 _currentParamRequestListComponentIndex = -1;
1422 _paramRequestListComponentIds.clear();
1423 _paramRequestListParamNames.clear();
1424 qCDebug(MockLinkLog) <<
"Received _HASH_CHECK PARAM_SET, stopping parameter stream";
1428 Q_ASSERT(_mapParamName2Value.contains(componentId));
1429 Q_ASSERT(_mapParamName2MavParamType.contains(componentId));
1430 Q_ASSERT(_mapParamName2Value[componentId].contains(paramId));
1431 Q_ASSERT(request.param_type == _mapParamName2MavParamType[componentId][paramId]);
1435 qCDebug(MockLinkLog) <<
"Param set failure: first attempt no ack" << paramId;
1436 _paramSetFailureFirstAttemptPending =
false;
1441 qCDebug(MockLinkLog) <<
"Param set failure: no ack" << paramId;
1446 qCDebug(MockLinkLog) <<
"Param set failure: PARAM_ERROR" << paramId;
1447 _sendParamError(componentId, paramId,
1448 _mapParamName2Value[componentId].keys().indexOf(paramId),
1449 MAV_PARAM_ERROR_VALUE_OUT_OF_RANGE);
1454 _setParamFloatUnionIntoMap(componentId, paramId, request.param_value);
1457 mavlink_msg_param_value_pack_chan(
1460 _outgoingMavlinkChannel,
1463 request.param_value,
1465 _mapParamName2Value[componentId].count(),
1466 _mapParamName2Value[componentId].keys().indexOf(paramId)
1474 mavlink_param_request_read_t request{};
1475 mavlink_msg_param_request_read_decode(&msg, &request);
1477 const QString paramName(QString::fromLocal8Bit(request.param_id,
static_cast<int>(strnlen(request.param_id, MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN))));
1478 const int componentId = request.target_component;
1481 if ((_firmwareType == MAV_AUTOPILOT_PX4) && (paramName ==
"_HASH_CHECK")) {
1482 _hashCheckRequestCount++;
1483 if (_hashCheckNoResponse) {
1487 const int hashComponentId = _mapParamName2Value.contains(MAV_COMP_ID_AUTOPILOT1)
1488 ? MAV_COMP_ID_AUTOPILOT1
1489 : _mapParamName2Value.keys().first();
1492 valueUnion.type = MAV_PARAM_TYPE_UINT32;
1493 valueUnion.param_uint32 = _computeParamHash(hashComponentId);
1494 (void) mavlink_msg_param_value_pack_chan(
1497 _outgoingMavlinkChannel,
1500 valueUnion.param_float,
1501 MAV_PARAM_TYPE_UINT32,
1509 Q_ASSERT(_mapParamName2Value.contains(componentId));
1511 char paramId[MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN + 1]{};
1514 Q_ASSERT(request.target_system == _vehicleSystemId);
1516 if (request.param_index == -1) {
1518 (void) strncpy(paramId, request.param_id, MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
1521 Q_ASSERT(request.param_index >= 0 && request.param_index < _mapParamName2Value[componentId].count());
1523 const QString key = _mapParamName2Value[componentId].keys().at(request.param_index);
1524 Q_ASSERT(key.length() <= MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
1525 strcpy(paramId, key.toLocal8Bit().constData());
1528 Q_ASSERT(_mapParamName2Value[componentId].contains(paramId));
1529 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramId));
1532 qCDebug(MockLinkLog) <<
"Ignoring request read for " << _failParam;
1538 qCDebug(MockLinkLog) <<
"Param request read failure: first attempt no response" << paramId;
1539 _paramRequestReadFailureFirstAttemptPending =
false;
1544 qCDebug(MockLinkLog) <<
"Param request read failure: no response" << paramId;
1549 qCDebug(MockLinkLog) <<
"Param request read failure: PARAM_ERROR" << paramId;
1550 _sendParamError(componentId, paramId, request.param_index, MAV_PARAM_ERROR_DOES_NOT_EXIST);
1554 (void) mavlink_msg_param_value_pack_chan(
1557 _outgoingMavlinkChannel,
1560 _floatUnionForParam(componentId, paramId),
1561 _mapParamName2MavParamType[componentId][paramId],
1562 _mapParamName2Value[componentId].count(),
1563 _mapParamName2Value[componentId].keys().indexOf(paramId)
1568void MockLink::_sendParamError(
int componentId,
const char *paramId, int16_t paramIndex, uint8_t errorCode)
1571 char paramIdBuf[MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN + 1] = {};
1572 (void) strncpy(paramIdBuf, paramId, MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN);
1574 (void) mavlink_msg_param_error_pack_chan(
1576 static_cast<uint8_t
>(componentId),
1577 _outgoingMavlinkChannel,
1595 uint8_t commandResult = MAV_RESULT_UNSUPPORTED;
1597 switch (request.command) {
1600 commandResult = MAV_RESULT_ACCEPTED;
1604 commandResult = MAV_RESULT_FAILED;
1612 (void) mavlink_msg_command_ack_pack_chan(
1614 _vehicleComponentId,
1615 _outgoingMavlinkChannel,
1618 MAV_RESULT_IN_PROGRESS,
1627 (void) mavlink_msg_command_ack_pack_chan(
1629 _vehicleComponentId,
1630 _outgoingMavlinkChannel,
1643void MockLink::_handleCommandLongSetMessageInterval(
const mavlink_command_long_t &request,
bool &accepted)
1647 static const QSet<int> kPidTuningMessageIds = {
1648 MAVLINK_MSG_ID_ATTITUDE_QUATERNION,
1649 MAVLINK_MSG_ID_ATTITUDE_TARGET,
1650 MAVLINK_MSG_ID_LOCAL_POSITION_NED,
1651 MAVLINK_MSG_ID_POSITION_TARGET_LOCAL_NED,
1652 MAVLINK_MSG_ID_NAV_CONTROLLER_OUTPUT,
1653 MAVLINK_MSG_ID_VFR_HUD,
1655 accepted = kPidTuningMessageIds.contains(
static_cast<int>(request.param1));
1660 static bool firstCmdUser3 =
true;
1661 static bool firstCmdUser4 =
true;
1664 mavlink_msg_command_long_decode(&msg, &request);
1666 uint8_t commandResult = MAV_RESULT_UNSUPPORTED;
1668 switch (request.command) {
1669 case MAV_CMD_COMPONENT_ARM_DISARM:
1670 if (request.param1 == 0.0f) {
1671 _mavBaseMode &= ~MAV_MODE_FLAG_SAFETY_ARMED;
1673 _mavBaseMode |= MAV_MODE_FLAG_SAFETY_ARMED;
1675 commandResult = MAV_RESULT_ACCEPTED;
1677 case MAV_CMD_PREFLIGHT_CALIBRATION:
1678 _handlePreFlightCalibration(request);
1679 commandResult = MAV_RESULT_ACCEPTED;
1681 case MAV_CMD_DO_MOTOR_TEST:
1682 commandResult = MAV_RESULT_ACCEPTED;
1684 case MAV_CMD_DO_START_MAG_CAL:
1686 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1687 QMutexLocker locker(&_apmCompassCalMutex);
1688 _apmCompassCalProgress = 0;
1689 _apmCompassCalTickCount = 0;
1690 commandResult = MAV_RESULT_ACCEPTED;
1693 case MAV_CMD_DO_CANCEL_MAG_CAL:
1695 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1696 QMutexLocker locker(&_apmCompassCalMutex);
1697 _apmCompassCalProgress = -1;
1698 commandResult = MAV_RESULT_ACCEPTED;
1701 case MAV_CMD_CONTROL_HIGH_LATENCY:
1703 _highLatencyTransmissionEnabled =
static_cast<int>(request.param1) != 0;
1705 commandResult = MAV_RESULT_ACCEPTED;
1707 commandResult = MAV_RESULT_FAILED;
1710 case MAV_CMD_PREFLIGHT_STORAGE:
1711 if (
static_cast<int>(request.param1) == 2) {
1713 _resetParamsToDefaults();
1715 commandResult = MAV_RESULT_ACCEPTED;
1717 case MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN:
1721 commandResult = MAV_RESULT_ACCEPTED;
1723 case MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES:
1724 commandResult = MAV_RESULT_ACCEPTED;
1725 _respondWithAutopilotVersion();
1727 case MAV_CMD_REQUEST_MESSAGE:
1729 bool accepted =
false;
1731 _handleRequestMessage(request, accepted, noAck);
1737 commandResult = MAV_RESULT_ACCEPTED;
1741 case MAV_CMD_NAV_TAKEOFF:
1742 _handleTakeoff(request);
1743 commandResult = MAV_RESULT_ACCEPTED;
1747 commandResult = MAV_RESULT_ACCEPTED;
1751 commandResult = MAV_RESULT_FAILED;
1755 if (firstCmdUser3) {
1756 firstCmdUser3 =
false;
1759 firstCmdUser3 =
true;
1760 commandResult = MAV_RESULT_ACCEPTED;
1765 if (firstCmdUser4) {
1766 firstCmdUser4 =
false;
1769 firstCmdUser4 =
true;
1770 commandResult = MAV_RESULT_FAILED;
1780 _handleInProgressCommandLong(request);
1782 case MAV_CMD_SET_MESSAGE_INTERVAL:
1784 bool accepted =
false;
1786 _handleCommandLongSetMessageInterval(request, accepted);
1788 commandResult = MAV_RESULT_ACCEPTED;
1795 (void) mavlink_msg_command_ack_pack_chan(
1797 _vehicleComponentId,
1798 _outgoingMavlinkChannel,
1812 mavlink_command_int_t request{};
1813 mavlink_msg_command_int_decode(&msg, &request);
1819 const uint8_t commandResult = MAV_RESULT_UNSUPPORTED;
1822 (void) mavlink_msg_command_ack_pack_chan(
1824 _vehicleComponentId,
1825 _outgoingMavlinkChannel,
1840 (void) mavlink_msg_command_ack_pack_chan(
1842 _vehicleComponentId,
1843 _outgoingMavlinkChannel,
1855void MockLink::_respondWithAutopilotVersion()
1857 union FlightVersion {
1867 FlightVersion(uint32_t version = 0) : raw(version) {}
1869 FlightVersion flightVersion;
1871#ifndef QGC_NO_ARDUPILOT_DIALECT
1872 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1873 flightVersion.parts.major = 4;
1874 flightVersion.parts.minor = 7;
1875 flightVersion.parts.patch = 0;
1876 flightVersion.parts.type = FIRMWARE_VERSION_TYPE_OFFICIAL;
1877 }
else if (_firmwareType == MAV_AUTOPILOT_PX4) {
1879 flightVersion.parts.major = 1;
1880 flightVersion.parts.minor = 17;
1881 flightVersion.parts.patch = 0;
1882 flightVersion.parts.type = FIRMWARE_VERSION_TYPE_OFFICIAL;
1883#ifndef QGC_NO_ARDUPILOT_DIALECT
1887 const uint8_t customVersion[8]{};
1888 const uint64_t capabilities = MAV_PROTOCOL_CAPABILITY_MAVLINK2 | MAV_PROTOCOL_CAPABILITY_MISSION_FENCE | MAV_PROTOCOL_CAPABILITY_MISSION_RALLY | MAV_PROTOCOL_CAPABILITY_MISSION_INT | ((_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) ? MAV_PROTOCOL_CAPABILITY_TERRAIN : 0);
1891 (void) mavlink_msg_autopilot_version_pack_chan(
1893 _vehicleComponentId,
1894 _outgoingMavlinkChannel,
1901 reinterpret_cast<const uint8_t*
>(&customVersion),
1902 reinterpret_cast<const uint8_t*
>(&customVersion),
1903 reinterpret_cast<const uint8_t*
>(&customVersion),
1912void MockLink::_sendHomePosition()
1914 const float bogus[4]{};
1917 (void) mavlink_msg_home_position_pack_chan(
1919 _vehicleComponentId,
1920 _outgoingMavlinkChannel,
1922 static_cast<int32_t
>(_vehicleLatitude * 1E7),
1923 static_cast<int32_t
>(_vehicleLongitude * 1E7),
1924 static_cast<int32_t
>(_defaultVehicleHomeAltitude * 1000),
1933void MockLink::_sendGpsRawInt()
1935 static uint64_t timeTick = 0;
1938 (void) mavlink_msg_gps_raw_int_pack_chan(
1940 _vehicleComponentId,
1941 _outgoingMavlinkChannel,
1944 GPS_FIX_TYPE_3D_FIX,
1945 static_cast<int32_t
>(_vehicleLatitude * 1E7),
1946 static_cast<int32_t
>(_vehicleLongitude * 1E7),
1947 static_cast<int32_t
>(_vehicleAltitudeAMSL * 1000),
1964void MockLink::_sendGlobalPositionInt()
1966 static uint64_t timeTick = 0;
1969 (void) mavlink_msg_global_position_int_pack_chan(
1971 _vehicleComponentId,
1972 _outgoingMavlinkChannel,
1975 static_cast<int32_t
>(_vehicleLatitude * 1E7),
1976 static_cast<int32_t
>(_vehicleLongitude * 1E7),
1977 static_cast<int32_t
>(_vehicleAltitudeAMSL * 1000),
1978 static_cast<int32_t
>((_vehicleAltitudeAMSL - _defaultVehicleHomeAltitude) * 1000),
1985void MockLink::_sendAttitudeQuaternion()
1987 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
1988 const float t = timeBootMs / 1000.0f;
1991 const float roll = 0.20f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.50f * t);
1992 const float pitch = 0.10f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.40f * t);
1993 const float yaw = 0.30f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.10f * t);
1996 const float cr = std::cos(roll / 2.0f), sr = std::sin(roll / 2.0f);
1997 const float cp = std::cos(pitch / 2.0f), sp = std::sin(pitch / 2.0f);
1998 const float cy = std::cos(yaw / 2.0f), sy = std::sin(yaw / 2.0f);
1999 const float q1 = cr * cp * cy + sr * sp * sy;
2000 const float q2 = sr * cp * cy - cr * sp * sy;
2001 const float q3 = cr * sp * cy + sr * cp * sy;
2002 const float q4 = cr * cp * sy - sr * sp * cy;
2005 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);
2006 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);
2007 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);
2009 const float reprOffsetQ[4] = {1.0f, 0.0f, 0.0f, 0.0f};
2012 (void) mavlink_msg_attitude_quaternion_pack_chan(
2014 _vehicleComponentId,
2015 _outgoingMavlinkChannel,
2019 rollspeed, pitchspeed, yawspeed,
2025void MockLink::_sendAttitudeTarget()
2028 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
2029 const float t = timeBootMs / 1000.0f;
2030 static constexpr float kPhase = 0.3f;
2032 const float roll = 0.20f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.50f * t + kPhase);
2033 const float pitch = 0.10f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.40f * t + kPhase);
2034 const float yaw = 0.30f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.10f * t + kPhase);
2036 const float cr = std::cos(roll / 2.0f), sr = std::sin(roll / 2.0f);
2037 const float cp = std::cos(pitch / 2.0f), sp = std::sin(pitch / 2.0f);
2038 const float cy = std::cos(yaw / 2.0f), sy = std::sin(yaw / 2.0f);
2039 const float qSp[4] = {
2040 cr * cp * cy + sr * sp * sy,
2041 sr * cp * cy - cr * sp * sy,
2042 cr * sp * cy + sr * cp * sy,
2043 cr * cp * sy - sr * sp * cy,
2046 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);
2047 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);
2048 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);
2051 (void) mavlink_msg_attitude_target_pack_chan(
2053 _vehicleComponentId,
2054 _outgoingMavlinkChannel,
2059 bodyRollRate, bodyPitchRate, bodyYawRate,
2065void MockLink::_sendLocalPositionNed()
2067 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
2068 const float t = timeBootMs / 1000.0f;
2070 const float x = 5.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.08f * t);
2071 const float y = 5.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.10f * t);
2072 const float z = -10.0f + 1.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.15f * t);
2073 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);
2074 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);
2075 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);
2078 (void) mavlink_msg_local_position_ned_pack_chan(
2080 _vehicleComponentId,
2081 _outgoingMavlinkChannel,
2090void MockLink::_sendPositionTargetLocalNed()
2093 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
2094 const float t = timeBootMs / 1000.0f + 0.5f;
2096 const float x = 5.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.08f * t);
2097 const float y = 5.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.10f * t);
2098 const float z = -10.0f + 1.0f * std::sin(2.0f *
static_cast<float>(M_PI) * 0.15f * t);
2099 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);
2100 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);
2101 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);
2104 (void) mavlink_msg_position_target_local_ned_pack_chan(
2106 _vehicleComponentId,
2107 _outgoingMavlinkChannel,
2110 MAV_FRAME_LOCAL_NED,
2120void MockLink::_sendExtendedSysState()
2123 (void) mavlink_msg_extended_sys_state_pack_chan(
2125 _vehicleComponentId,
2126 _outgoingMavlinkChannel,
2128 MAV_VTOL_STATE_UNDEFINED,
2129 (_vehicleAltitudeAMSL > _defaultVehicleHomeAltitude) ? MAV_LANDED_STATE_IN_AIR : MAV_LANDED_STATE_ON_GROUND
2134void MockLink::_sendChunkedStatusText(uint16_t chunkId,
bool missingChunks)
2136 constexpr int cChunks = 4;
2139 for (
int i = 0; i < cChunks; i++) {
2140 if (missingChunks && (i & 1)) {
2144 int cBuf = MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN;
2145 char msgBuf[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN]{};
2147 if (i == cChunks - 1) {
2152 for (
int j = 0; j < cBuf - 1; j++) {
2153 msgBuf[j] =
'0' + num++;
2158 msgBuf[cBuf-1] =
'A' + i;
2161 (void) mavlink_msg_statustext_pack_chan(
2163 _vehicleComponentId,
2164 _outgoingMavlinkChannel,
2175void MockLink::_sendStatusTextMessages()
2177 struct StatusMessage {
2178 MAV_SEVERITY severity;
2182 static constexpr struct StatusMessage rgMessages[] = {
2183 { MAV_SEVERITY_INFO,
"#Testing audio output" },
2184 { MAV_SEVERITY_EMERGENCY,
"Status text emergency" },
2185 { MAV_SEVERITY_ALERT,
"Status text alert" },
2186 { MAV_SEVERITY_CRITICAL,
"Status text critical" },
2187 { MAV_SEVERITY_ERROR,
"Status text error" },
2188 { MAV_SEVERITY_WARNING,
"Status text warning" },
2189 { MAV_SEVERITY_NOTICE,
"Status text notice" },
2190 { MAV_SEVERITY_INFO,
"Status text info" },
2191 { MAV_SEVERITY_DEBUG,
"Status text debug" },
2195 for (
size_t i = 0; i < std::size(rgMessages); i++) {
2196 const struct StatusMessage *status = &rgMessages[i];
2197 char statusText[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN] = {};
2198 (void) std::strncpy(statusText, status->msg,
sizeof(statusText) - 1);
2200 (void) mavlink_msg_statustext_pack_chan(
2202 _vehicleComponentId,
2203 _outgoingMavlinkChannel,
2213 _sendChunkedStatusText(1,
false );
2214 _sendChunkedStatusText(2,
true );
2215 _sendChunkedStatusText(3,
false );
2216 _sendChunkedStatusText(4,
true );
2225 return qobject_cast<MockLink*>(
config->link());
2231MockLink *MockLink::_startMockLinkWorker(
const QString &configName, MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType,
bool sendStatusText,
bool enableCamera,
bool enableGimbal,
MockConfiguration::FailureMode_t failureMode,
bool preloadMission)
2243 return _startMockLink(mockConfig);
2248 return _startMockLinkWorker(QStringLiteral(
"PX4 MultiRotor MockLink"), MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, sendStatusText, enableCamera, enableGimbal, failureMode);
2253 return _startMockLinkWorker(QStringLiteral(
"PX4 MultiRotor MockLink"), MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, sendStatusText, enableCamera, enableGimbal, failureMode,
true );
2258 return _startMockLinkWorker(QStringLiteral(
"Generic MockLink"), MAV_AUTOPILOT_GENERIC, MAV_TYPE_QUADROTOR, sendStatusText, enableCamera, enableGimbal, failureMode);
2263 return _startMockLinkWorker(QStringLiteral(
"No Initial Connect MockLink"), MAV_AUTOPILOT_PX4, MAV_TYPE_GENERIC, sendStatusText, enableCamera, enableGimbal, failureMode);
2268 return _startMockLinkWorker(QStringLiteral(
"ArduCopter MockLink"),MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_QUADROTOR, sendStatusText, enableCamera, enableGimbal, failureMode);
2273 return _startMockLinkWorker(QStringLiteral(
"ArduPlane MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_FIXED_WING, sendStatusText, enableCamera, enableGimbal, failureMode);
2278 return _startMockLinkWorker(QStringLiteral(
"ArduSub MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_SUBMARINE, sendStatusText, enableCamera, enableGimbal, failureMode);
2283 return _startMockLinkWorker(QStringLiteral(
"ArduRover MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_GROUND_ROVER, sendStatusText, enableCamera, enableGimbal, failureMode);
2286void MockLink::_sendRCChannels()
2289 (void) mavlink_msg_rc_channels_pack_chan(
2291 _vehicleComponentId,
2292 _outgoingMavlinkChannel,
2296 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500,
2297 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500,
2298 UINT16_MAX, UINT16_MAX,
2306 if ((request.param1 == 0) && (request.param2 == 0) && (request.param3 == 0) &&
2307 (request.param4 == 0) && (request.param5 == 0) && (request.param6 == 0) &&
2308 (request.param7 == 0)) {
2310 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
2312 QMutexLocker locker(&_apmAccelCalMutex);
2313 if (_apmAccelCalPosIndex >= 0) {
2316 cmd.target_system = 255;
2317 cmd.target_component = MAV_COMP_ID_MISSIONPLANNER;
2318 cmd.command = MAV_CMD_ACCELCAL_VEHICLE_POS;
2319 cmd.param1 =
static_cast<float>(ACCELCAL_VEHICLE_POS_FAILED);
2320 (void) mavlink_msg_command_long_encode_chan(
2321 _vehicleSystemId, _vehicleComponentId, _outgoingMavlinkChannel, &msg, &cmd);
2323 _apmAccelCalPosIndex = -1;
2327 (void) _mockLinkPX4Calibration->
cancel();
2332 if (request.param2 == 1) {
2338 if (request.param1 == 1) {
2343 if (request.param5 == 1) {
2344 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
2346 QMutexLocker locker(&_apmAccelCalMutex);
2347 _apmAccelCalPosIndex = 0;
2348 _apmAccelCalGotAck =
false;
2349 _apmAccelCalTickCount = 0;
2359 char statusText[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN] = {};
2360 (void) std::strncpy(statusText, text.toUtf8().constData(),
sizeof(statusText) - 1);
2363 (void) mavlink_msg_statustext_pack_chan(
2365 _vehicleComponentId,
2366 _outgoingMavlinkChannel,
2378 _vehicleAltitudeAMSL = request.param7 + _defaultVehicleHomeAltitude;
2379 _mavBaseMode |= MAV_MODE_FLAG_SAFETY_ARMED;
2384 mavlink_log_request_list_t request{};
2385 mavlink_msg_log_request_list_decode(&msg, &request);
2387 if ((request.start != 0) && (request.end != 0xffff)) {
2388 qCWarning(MockLinkLog) <<
"_handleLogRequestList cannot handle partial requests";
2393 mavlink_msg_log_entry_pack_chan(
2395 _vehicleComponentId,
2396 _outgoingMavlinkChannel,
2402 _logDownloadFileSize
2407QString MockLink::_createRandomFile(uint32_t byteCount)
2409 QTemporaryFile tempFile;
2410 tempFile.setAutoRemove(
false);
2411 if (!tempFile.open()) {
2412 qCWarning(MockLinkLog) <<
"MockLink::createRandomFile open failed" << tempFile.errorString();
2416 for (uint32_t bytesWritten = 0; bytesWritten < byteCount; bytesWritten++) {
2417 const unsigned char byte = (QRandomGenerator::global()->generate() * 0xFF) / RAND_MAX;
2418 (void) tempFile.write(
reinterpret_cast<const char*
>(&
byte), 1);
2422 return tempFile.fileName();
2427 mavlink_log_request_data_t request{};
2428 mavlink_msg_log_request_data_decode(&msg, &request);
2430#ifdef QGC_UNITTEST_BUILD
2431 if (_logDownloadFilename.isEmpty()) {
2432 _logDownloadFilename = _createRandomFile(_logDownloadFileSize);
2436 if (request.id != 0) {
2437 qCWarning(MockLinkLog) <<
"_handleLogRequestData id must be 0";
2441 if (request.ofs > (_logDownloadFileSize - 1)) {
2442 qCWarning(MockLinkLog) <<
"_handleLogRequestData offset past end of file request.ofs:size" << request.ofs << _logDownloadFileSize;
2449 QMutexLocker locker(&_logDownloadMutex);
2450 _logDownloadCurrentOffset = request.ofs;
2451 if (request.ofs + request.count > _logDownloadFileSize) {
2452 request.count = _logDownloadFileSize - request.ofs;
2454 _logDownloadBytesRemaining = request.count;
2457void MockLink::_logDownloadWorker()
2461 QMutexLocker locker(&_logDownloadMutex);
2462 if (_logDownloadBytesRemaining == 0) {
2466 QFile file(_logDownloadFilename);
2467 if (!file.open(QIODevice::ReadOnly)) {
2468 qCWarning(MockLinkLog) <<
"_logDownloadWorker open failed" << file.errorString();
2472 uint8_t buffer[MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN]{};
2474 const qint64 bytesToRead = qMin(_logDownloadBytesRemaining, (uint32_t)MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN);
2475 Q_ASSERT(file.seek(_logDownloadCurrentOffset));
2476 Q_ASSERT(file.read(
reinterpret_cast<char*
>(buffer), bytesToRead) == bytesToRead);
2478 qCDebug(MockLinkLog) <<
"_logDownloadWorker" << _logDownloadCurrentOffset << _logDownloadBytesRemaining;
2481 (void) mavlink_msg_log_data_pack_chan(
2483 _vehicleComponentId,
2484 _outgoingMavlinkChannel,
2487 _logDownloadCurrentOffset,
2493 _logDownloadCurrentOffset += bytesToRead;
2494 _logDownloadBytesRemaining -= bytesToRead;
2499void MockLink::_sendADSBVehicles()
2501 for (
int i = 0; i < _adsbVehicles.size(); ++i) {
2503 _adsbVehicles[i].angle += (i + 1);
2506 _adsbVehicles[i].coordinate = _adsbVehicles[i].coordinate.atDistanceAndAzimuth(5, _adsbVehicles[i].angle);
2509 _adsbVehicles[i].altitude += (i % 2 == 0 ? 0.5 : -0.5);
2511 QByteArray callsign = QString(
"N12345%1").arg(i, 2, 10, QChar(
'0')).toLatin1();
2512 callsign.resize(MAVLINK_MSG_ADSB_VEHICLE_FIELD_CALLSIGN_LEN);
2516 (void) mavlink_msg_adsb_vehicle_pack_chan(
2518 _vehicleComponentId,
2519 _outgoingMavlinkChannel,
2522 _adsbVehicles[i].coordinate.latitude() * 1e7,
2523 _adsbVehicles[i].coordinate.longitude() * 1e7,
2524 ADSB_ALTITUDE_TYPE_GEOMETRIC,
2525 _adsbVehicles[i].altitude * 1000,
2527 static_cast<uint16_t
>(_adsbVehicles[i].angle * 100),
2529 callsign.constData(),
2530 ADSB_EMITTER_TYPE_ROTOCRAFT,
2532 ADSB_FLAGS_VALID_COORDS | ADSB_FLAGS_VALID_ALTITUDE | ADSB_FLAGS_VALID_HEADING | ADSB_FLAGS_VALID_CALLSIGN | ADSB_FLAGS_SIMULATED,
2539void MockLink::_moveADSBVehicle(
int vehicleIndex)
2541 _adsbAngles[vehicleIndex] += 10;
2542 QGeoCoordinate &coord = _adsbVehicleCoordinates[vehicleIndex];
2545 coord = QGeoCoordinate(coord.latitude(), coord.longitude()).atDistanceAndAzimuth(500, _adsbAngles[vehicleIndex]);
2546 coord.setAltitude(100);
2553 switch (_failureMode) {
2566 _respondWithAutopilotVersion();
2574 switch (_requestMessageFailureMode) {
2589 (void) mavlink_msg_debug_pack_chan(
2591 _vehicleComponentId,
2592 _outgoingMavlinkChannel,
2606 QMutexLocker locker(&_availableModesWorkerMutex);
2607 if (request.param2 == 0) {
2609 if (_availableModesWorkerNextModeIndex != 0) {
2610 qCWarning(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: _availableModesWorker already running - _availableModesWorkerNextModeIndex:" << _availableModesWorkerNextModeIndex;
2614 qCDebug(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: starting available modes sequence worker";
2615 _availableModesWorkerNextModeIndex = 1;
2618 if (request.param2 > _availableFlightModes.count()) {
2619 qCWarning(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: requested mode index out of range" << request.param2 << _availableFlightModes.count();
2623 qCDebug(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: received specific mode request for index" << request.param2;
2624 _availableModesWorkerNextModeIndex = -request.param2;
2633 const uint32_t requestedMessageId =
static_cast<uint32_t
>(request.param1);
2637 QMutexLocker locker(&_requestMessageNoResponseMutex);
2638 if (_requestMessageNoResponseIds.contains(requestedMessageId)) {
2644 switch (
static_cast<int>(request.param1)) {
2645 case MAVLINK_MSG_ID_AUTOPILOT_VERSION:
2646 _handleRequestMessageAutopilotVersion(request, accepted);
2648 case MAVLINK_MSG_ID_COMPONENT_METADATA:
2649 if (_firmwareType == MAV_AUTOPILOT_PX4) {
2650 _sendGeneralMetaData();
2654 case MAVLINK_MSG_ID_DEBUG:
2655 _handleRequestMessageDebug(request, accepted, noAck);
2657 case MAVLINK_MSG_ID_AVAILABLE_MODES:
2658 _handleRequestMessageAvailableModes(request, accepted);
2663void MockLink::_sendGeneralMetaData()
2665 static constexpr const char metaDataURI[MAVLINK_MSG_COMPONENT_METADATA_FIELD_URI_LEN] =
"mftp://[;comp=1]general.json";
2668 (void) mavlink_msg_component_metadata_pack_chan(
2670 _vehicleComponentId,
2671 _outgoingMavlinkChannel,
2680void MockLink::_sendRemoteIDArmStatus()
2682 char armStatusError[MAVLINK_MSG_OPEN_DRONE_ID_ARM_STATUS_FIELD_ERROR_LEN] = {};
2683 std::strncpy(armStatusError,
"No Error",
sizeof(armStatusError) - 1);
2686 (void) mavlink_msg_open_drone_id_arm_status_pack(
2688 MAV_COMP_ID_ODID_TXRX_1,
2690 MAV_ODID_ARM_STATUS_GOOD_TO_ARM,
2696void MockLink::_sendEscInfo()
2700 static const uint16_t failureFlags[4] = {0, 0, 0, 0};
2701 static const uint32_t errorCount[4] = {0, 0, 0, 0};
2702 static const int16_t temperature[4] = {3000, 3000, 3000, 3000};
2705 (void) mavlink_msg_esc_info_pack_chan(
2707 _vehicleComponentId,
2708 _outgoingMavlinkChannel,
2711 static_cast<uint64_t
>(_runningTime.elapsed()) * 1000,
2714 ESC_CONNECTION_TYPE_DSHOT,
2723void MockLink::_sendEscStatus()
2725 static const int32_t rpm[4] = {5000, 5000, 5000, 5000};
2726 static const float voltage[4] = {16.0f, 16.0f, 16.0f, 16.0f};
2727 static const float current[4] = {5.0f, 5.0f, 5.0f, 5.0f};
2730 (void) mavlink_msg_esc_status_pack_chan(
2732 _vehicleComponentId,
2733 _outgoingMavlinkChannel,
2736 static_cast<uint64_t
>(_runningTime.elapsed()) * 1000,
2744void MockLink::_sendRadioStatus()
2749 (void) mavlink_msg_radio_status_pack_chan(
2751 _vehicleComponentId,
2752 _outgoingMavlinkChannel,
2773 return _mockLinkFTP;
2776void MockLink::_sendAvailableMode(uint8_t modeIndexOneBased)
2778 if (modeIndexOneBased > _availableModesCount()) {
2779 qCWarning(MockLinkLog) <<
"modeIndexOneBased out of range" << modeIndexOneBased << _availableModesCount();
2783 qCDebug(MockLinkLog) <<
"_sendAvailableMode modeIndexOneBased:" << modeIndexOneBased;
2785 const FlightMode_t &availableMode = _availableFlightModes[modeIndexOneBased - 1];
2786 char modeName[MAVLINK_MSG_AVAILABLE_MODES_FIELD_MODE_NAME_LEN] = {};
2787 std::strncpy(modeName, availableMode.name,
sizeof(modeName) - 1);
2791 (void) mavlink_msg_available_modes_pack_chan(
2793 _vehicleComponentId,
2794 _outgoingMavlinkChannel,
2796 _availableModesCount(),
2798 availableMode.standard_mode,
2799 availableMode.custom_mode,
2800 availableMode.canBeSet ? 0 : MAV_MODE_PROPERTY_NOT_USER_SELECTABLE,
2805void MockLink::_availableModesWorker()
2809 QMutexLocker locker(&_availableModesWorkerMutex);
2810 if (_availableModesWorkerNextModeIndex == 0) {
2815 _sendAvailableMode(qAbs(_availableModesWorkerNextModeIndex));
2817 if (_availableModesWorkerNextModeIndex < 0) {
2819 _availableModesWorkerNextModeIndex = 0;
2820 }
else if (++_availableModesWorkerNextModeIndex > _availableModesCount()) {
2822 _availableModesWorkerNextModeIndex = 0;
2823 qCDebug(MockLinkLog) <<
"_availableModesWorker: all modes sent, stopping worker";
2827void MockLink::_sendAvailableModesMonitor()
2831 (void) mavlink_msg_available_modes_monitor_pack_chan(
2833 _vehicleComponentId,
2834 _outgoingMavlinkChannel,
2836 _availableModesMonitorSeqNumber);
2840int MockLink::_availableModesCount()
const
2842 return _availableFlightModes.count() - (_availableModesMonitorSeqNumber == 0 ? 1 : 0);
2853void MockLink::_apmCompassCalWorker()
2855 if (_firmwareType != MAV_AUTOPILOT_ARDUPILOTMEGA) {
2859 QMutexLocker locker(&_apmCompassCalMutex);
2860 if (_apmCompassCalProgress < 0) {
2865 if (++_apmCompassCalTickCount < 50) {
2868 _apmCompassCalTickCount = 0;
2870 const int pct = _apmCompassCalProgress;
2874 for (uint8_t
id = 0;
id < 3; ++id) {
2875 mavlink_mag_cal_progress_t progress{};
2876 progress.compass_id = id;
2877 progress.cal_mask = 0x07;
2878 progress.cal_status = MAG_CAL_RUNNING_STEP_ONE;
2879 progress.completion_pct =
static_cast<uint8_t
>(qMin(pct, 100));
2880 (void) mavlink_msg_mag_cal_progress_encode_chan(
2881 _vehicleSystemId, _vehicleComponentId, _outgoingMavlinkChannel, &msg, &progress);
2887 for (uint8_t
id = 0;
id < 3; ++id) {
2888 mavlink_mag_cal_report_t report{};
2889 report.compass_id = id;
2890 report.cal_mask = 0x07;
2891 report.cal_status = MAG_CAL_SUCCESS;
2892 report.fitness = 0.5f;
2893 (void) mavlink_msg_mag_cal_report_encode_chan(
2894 _vehicleSystemId, _vehicleComponentId, _outgoingMavlinkChannel, &msg, &report);
2901 (void) QMetaObject::invokeMethod(
this, [
this] {
2902 constexpr int compId = MAV_COMP_ID_AUTOPILOT1;
2903 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS_X")] = QVariant(10.0f);
2904 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS_Y")] = QVariant(10.0f);
2905 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS_Z")] = QVariant(10.0f);
2906 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS2_X")] = QVariant(10.0f);
2907 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS2_Y")] = QVariant(10.0f);
2908 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS2_Z")] = QVariant(10.0f);
2909 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS3_X")] = QVariant(10.0f);
2910 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS3_Y")] = QVariant(10.0f);
2911 _mapParamName2Value[compId][QStringLiteral(
"COMPASS_OFS3_Z")] = QVariant(10.0f);
2912 }, Qt::QueuedConnection);
2914 _apmCompassCalProgress = -1;
2916 _apmCompassCalProgress = qMin(pct + 5, 100);
2927void MockLink::_apmAccelCalWorker()
2929 if (_firmwareType != MAV_AUTOPILOT_ARDUPILOTMEGA) {
2933 QMutexLocker locker(&_apmAccelCalMutex);
2934 if (_apmAccelCalPosIndex < 0) {
2938 if (_apmAccelCalPosIndex == 6) {
2942 cmd.target_system = 255;
2943 cmd.target_component = MAV_COMP_ID_MISSIONPLANNER;
2944 cmd.command = MAV_CMD_ACCELCAL_VEHICLE_POS;
2945 cmd.param1 =
static_cast<float>(ACCELCAL_VEHICLE_POS_SUCCESS);
2946 (void) mavlink_msg_command_long_encode_chan(
2947 _vehicleSystemId, _vehicleComponentId, _outgoingMavlinkChannel, &msg, &cmd);
2953 (void) QMetaObject::invokeMethod(
this, [
this] {
2954 constexpr int compId = MAV_COMP_ID_AUTOPILOT1;
2955 _mapParamName2Value[compId][QStringLiteral(
"INS_ACCOFFS_X")] = QVariant(0.1f);
2956 _mapParamName2Value[compId][QStringLiteral(
"INS_ACCOFFS_Y")] = QVariant(0.1f);
2957 _mapParamName2Value[compId][QStringLiteral(
"INS_ACCOFFS_Z")] = QVariant(0.1f);
2958 }, Qt::QueuedConnection);
2960 _apmAccelCalPosIndex = -1;
2965 if (!_apmAccelCalGotAck) {
2967 if (++_apmAccelCalTickCount < 50) {
2970 _apmAccelCalTickCount = 0;
2974 cmd.target_system = 255;
2975 cmd.target_component = MAV_COMP_ID_MISSIONPLANNER;
2976 cmd.command = MAV_CMD_ACCELCAL_VEHICLE_POS;
2977 cmd.param1 =
static_cast<float>(kAPMAccelCalPosSequence[_apmAccelCalPosIndex]);
2978 (void) mavlink_msg_command_long_encode_chan(
2979 _vehicleSystemId, _vehicleComponentId, _outgoingMavlinkChannel, &msg, &cmd);
2983 _apmAccelCalGotAck =
false;
2984 _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
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 setEnableCamera(bool enableCamera)
void setEnableGimbal(bool enableGimbal)
void setSendStatusText(bool sendStatusText)
void setFirmwareType(MAV_AUTOPILOT firmwareType)
void setPreloadMission(bool preloadMission)
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.
void mavlinkMessageReceived(const mavlink_message_t &message)
Called to handle an FTP message.
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.
@ 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)
static MockLink * startGenericMockLink(bool sendStatusText, bool enableCamera, bool enableGimbal, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone)
void sendStatusTextMessage(uint8_t severity, const QString &text)
static constexpr MAV_CMD MAV_CMD_MOCKLINK_ALWAYS_RESULT_ACCEPTED
@ 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
MockLinkFTP * mockLinkFTP() const
static MockLink * startAPMArduSubMockLink(bool sendStatusText, bool enableCamera, bool enableGimbal, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone)
static MockLink * startAPMArduCopterMockLink(bool sendStatusText, bool enableCamera, bool enableGimbal, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone)
static constexpr MAV_CMD MAV_CMD_MOCKLINK_ALWAYS_RESULT_FAILED
QVariant paramValue(int componentId, const QString ¶mName) const
static MockLink * startNoInitialConnectMockLink(bool sendStatusText, bool enableCamera, bool enableGimbal, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone)
static MockLink * startPX4MockLink(bool sendStatusText, bool enableCamera, bool enableGimbal, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone)
static MockLink * startAPMArduRoverMockLink(bool sendStatusText, bool enableCamera, bool enableGimbal, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone)
static constexpr MAV_CMD MAV_CMD_MOCKLINK_SECOND_ATTEMPT_RESULT_FAILED
static MockLink * startPX4MockLinkWithMission(bool sendStatusText, bool enableCamera, bool enableGimbal, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone)
void respondWithMavlinkMessage(const mavlink_message_t &msg)
Sends the specified mavlink message to QGC.
static constexpr MAV_CMD MAV_CMD_MOCKLINK_NO_RESPONSE
MockLink(SharedLinkConfigurationPtr &config, QObject *parent=nullptr)
void setArmed(bool armed)
Set the armed state of the simulated vehicle.
static MockLink * startAPMArduPlaneMockLink(bool sendStatusText, bool enableCamera, bool enableGimbal, MockConfiguration::FailureMode_t failureMode=MockConfiguration::FailNone)
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
Q_INVOKABLE void simulateConnectionRemoved()
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)