18#include <QtCore/QFile>
19#include <QtCore/QMutexLocker>
20#include <QtCore/QRandomGenerator>
21#include <QtCore/QTemporaryFile>
22#include <QtCore/QThread>
23#include <QtCore/QTimer>
31std::atomic<
int>
MockLink::_nextVehicleSystemId{128};
33QList<MockLink::FlightMode_t> MockLink::_availableFlightModes = {
56 , _firmwareType(_mockConfig->firmwareType())
57 , _vehicleType(_mockConfig->vehicleType())
58 , _sendStatusText(_mockConfig->sendStatusText())
59 , _enableCamera(_mockConfig->enableCamera())
60 , _enableGimbal(_mockConfig->enableGimbal())
61 , _failureMode(_mockConfig->failureMode())
62 , _vehicleSystemId(_mockConfig->incrementVehicleId() ? _nextVehicleSystemId++ : static_cast<int>(_nextVehicleSystemId))
63 , _vehicleLatitude(_defaultVehicleLatitude + ((_vehicleSystemId - 128) * 0.0001))
64 , _vehicleLongitude(_defaultVehicleLongitude + ((_vehicleSystemId - 128) * 0.0001))
65 , _boardVendorId(_mockConfig->boardVendorId())
66 , _boardProductId(_mockConfig->boardProductId())
69 _mockConfig->cameraCaptureVideo(),
70 _mockConfig->cameraCaptureImage(),
71 _mockConfig->cameraHasModes(),
72 _mockConfig->cameraHasVideoStream(),
73 _mockConfig->cameraCanCaptureImageInVideoMode(),
74 _mockConfig->cameraCanCaptureVideoInImageMode(),
75 _mockConfig->cameraHasBasicZoom(),
76 _mockConfig->cameraHasTrackingPoint(),
77 _mockConfig->cameraHasTrackingRectangle())
80 _mockConfig->gimbalHasRollAxis(),
81 _mockConfig->gimbalHasPitchAxis(),
82 _mockConfig->gimbalHasYawAxis(),
83 _mockConfig->gimbalHasYawFollow(),
84 _mockConfig->gimbalHasYawLock(),
85 _mockConfig->gimbalHasRetract(),
86 _mockConfig->gimbalHasNeutral())
88 , _mockLinkFTP(new
MockLinkFTP(_vehicleSystemId, _vehicleComponentId, this))
90 qCDebug(MockLinkLog) <<
this;
97 _adsbVehicles.reserve(_numberOfVehicles);
98 for (
int i = 0; i < _numberOfVehicles; ++i) {
99 ADSBVehicle vehicle{};
100 vehicle.angle = i * 72.0;
103 const double latOffset = 0.001 * i;
104 const double lonOffset = 0.001 * (i % 2 == 0 ? i : -i);
105 vehicle.coordinate = QGeoCoordinate(_defaultVehicleLatitude + latOffset, _defaultVehicleLongitude + lonOffset);
108 vehicle.altitude = _defaultVehicleHomeAltitude + (i * 5);
110 _adsbVehicles.append(vehicle);
116 _runningTime.start();
118 _workerThread =
new QThread(
this);
119 _workerThread->setObjectName(QStringLiteral(
"Mock_%1").arg(_mockConfig->
name()));
121 _worker->moveToThread(_workerThread);
123 (void) connect(_workerThread, &QThread::finished, _worker, &QObject::deleteLater);
124 _workerThread->start();
131 delete _mockLinkCamera;
132 delete _mockLinkGimbal;
134 if (!_logDownloadFilename.isEmpty()) {
135 QFile::remove(_logDownloadFilename);
139 _workerThread->quit();
140 _workerThread->wait();
143 qCDebug(MockLinkLog) <<
this;
146bool MockLink::_connect()
150 _disconnectedEmitted =
false;
152 vehicleStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
154 auxStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
169 if (_workerThread && _workerThread->isRunning()) {
170 _workerThread->quit();
171 _workerThread->wait();
175 if (_mavlinkVehicleChannelIsSet()) {
177 vehicleStatus->signing =
nullptr;
178 vehicleStatus->signing_streams =
nullptr;
179 mavlink_reset_channel_status(_mavlinkVehicleChannel);
184 if (!_disconnectedEmitted.exchange(
true)) {
202 _sendBatteryStatus();
203 _sendNamedValueFloats();
206 if (_vehicleType != MAV_TYPE_SUBMARINE) {
207 _sendRemoteIDArmStatus();
209 _sendAvailableModesMonitor();
224 if (_sendHomePositionDelayCount > 0) {
226 _sendHomePositionDelayCount--;
230 if (_availableModesMonitorSeqNumber == 0) {
231 qCDebug(MockLinkLog) <<
"Bumping sequence number for available modes monitor to trigger requery of modes";
232 _availableModesMonitorSeqNumber = 1;
245 if (_sendGPSPositionDelayCount > 0) {
247 _sendGPSPositionDelayCount--;
249 if (_vehicleType != MAV_TYPE_SUBMARINE) {
251 _sendGlobalPositionInt();
253 _sendExtendedSysState();
269 _paramRequestListWorker();
270 _logDownloadWorker();
271 _availableModesWorker();
277 _sendStatusTextMessages();
280bool MockLink::_allocateMavlinkChannel()
283 Q_ASSERT(!_mavlinkAuxChannelIsSet());
284 Q_ASSERT(!_mavlinkVehicleChannelIsSet());
288 qCWarning(MockLinkLog) <<
"LinkInterface::_allocateMavlinkChannel failed";
293 if (!_mavlinkAuxChannelIsSet()) {
294 qCWarning(MockLinkLog) <<
"_allocateMavlinkChannel aux failed";
300 if (!_mavlinkVehicleChannelIsSet()) {
301 qCWarning(MockLinkLog) <<
"_allocateMavlinkChannel vehicle failed";
307 qCDebug(MockLinkLog) <<
"_allocateMavlinkChannel aux:" << _mavlinkAuxChannel <<
"vehicle:" << _mavlinkVehicleChannel;
311void MockLink::_freeMavlinkChannel()
313 qCDebug(MockLinkLog) <<
"_freeMavlinkChannel aux:" << _mavlinkAuxChannel <<
"vehicle:" << _mavlinkVehicleChannel;
314 if (!_mavlinkAuxChannelIsSet()) {
315 Q_ASSERT(!_mavlinkVehicleChannelIsSet());
320 if (_mavlinkVehicleChannelIsSet()) {
322 mavlink_reset_channel_status(_mavlinkVehicleChannel);
326 mavlink_reset_channel_status(_mavlinkAuxChannel);
332bool MockLink::_mavlinkAuxChannelIsSet()
const
337bool MockLink::_mavlinkVehicleChannelIsSet()
const
342void MockLink::_loadParams()
345 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
346 if (_vehicleType == MAV_TYPE_FIXED_WING) {
347 paramFile.setFileName(
":/FirmwarePlugin/APM/Plane.OfflineEditing.params");
348 }
else if (_vehicleType == MAV_TYPE_SUBMARINE ) {
349 paramFile.setFileName(
":/FirmwarePlugin/APM/Sub.OfflineEditing.params");
350 }
else if (_vehicleType == MAV_TYPE_GROUND_ROVER ) {
351 paramFile.setFileName(
":/FirmwarePlugin/APM/Rover.OfflineEditing.params");
353 paramFile.setFileName(
":/FirmwarePlugin/APM/Copter.OfflineEditing.params");
356 paramFile.setFileName(
":/MockLink/PX4MockLink.params");
359 const bool success = paramFile.open(QFile::ReadOnly);
363 QTextStream paramStream(¶mFile);
364 while (!paramStream.atEnd()) {
365 const QString line = paramStream.readLine();
367 if (line.startsWith(
"#")) {
371 const QStringList paramData = line.split(
"\t");
372 Q_ASSERT(paramData.count() == 5);
374 const int compId = paramData.at(1).toInt();
375 const QString paramName = paramData.at(2);
376 const QString valStr = paramData.at(3);
377 const uint paramType = paramData.at(4).toUInt();
381 case MAV_PARAM_TYPE_REAL32:
382 paramValue = QVariant(valStr.toFloat());
384 case MAV_PARAM_TYPE_UINT32:
385 paramValue = QVariant(valStr.toUInt());
387 case MAV_PARAM_TYPE_INT32:
388 paramValue = QVariant(valStr.toInt());
390 case MAV_PARAM_TYPE_UINT16:
391 paramValue = QVariant((quint16)valStr.toUInt());
393 case MAV_PARAM_TYPE_INT16:
394 paramValue = QVariant((qint16)valStr.toInt());
396 case MAV_PARAM_TYPE_UINT8:
397 paramValue = QVariant((quint8)valStr.toUInt());
399 case MAV_PARAM_TYPE_INT8:
400 paramValue = QVariant((qint8)valStr.toUInt());
403 qCCritical(MockLinkVerboseLog) <<
"Unknown type" << paramType;
404 paramValue = QVariant(valStr.toInt());
408 qCDebug(MockLinkVerboseLog) <<
"Loading param" << paramName << paramValue;
410 _mapParamName2Value[compId][paramName] = paramValue;
411 _mapParamName2MavParamType[compId][paramName] =
static_cast<MAV_PARAM_TYPE
>(paramType);
415void MockLink::_sendHeartBeat()
418 (void) mavlink_msg_heartbeat_pack_chan(
421 _getMavlinkVehicleChannel(),
432void MockLink::_sendHighLatency2()
434 qCDebug(MockLinkLog) <<
"Sending" << _mavCustomMode;
437 px4_cm.
data = _mavCustomMode;
440 (void) mavlink_msg_high_latency2_pack_chan(
443 _getMavlinkVehicleChannel(),
448 px4_cm.custom_mode_hl,
449 static_cast<int32_t
>(_vehicleLatitude * 1E7),
450 static_cast<int32_t
>(_vehicleLongitude * 1E7),
451 static_cast<int16_t
>(_vehicleAltitudeAMSL),
452 static_cast<int16_t
>(_vehicleAltitudeAMSL),
474void MockLink::_sendSysStatus()
477 (void) mavlink_msg_sys_status_pack_chan(
480 _getMavlinkVehicleChannel(),
482 MAV_SYS_STATUS_SENSOR_GPS,
488 _battery1PctRemaining,
494void MockLink::_sendBatteryStatus()
496 if (_battery1PctRemaining > 1) {
497 _battery1PctRemaining =
static_cast<int8_t
>(100 - (_runningTime.elapsed() / 1000));
498 _battery1TimeRemaining =
static_cast<double>(_batteryMaxTimeRemaining) * (
static_cast<double>(_battery1PctRemaining) / 100.0);
499 if (_battery1PctRemaining > 50) {
500 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_OK;
501 }
else if (_battery1PctRemaining > 30) {
502 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_LOW;
503 }
else if (_battery1PctRemaining > 20) {
504 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_CRITICAL;
506 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_EMERGENCY;
510 if (_battery2PctRemaining > 1) {
511 _battery2PctRemaining =
static_cast<int8_t
>(100 - ((_runningTime.elapsed() / 1000) / 2));
512 _battery2TimeRemaining =
static_cast<double>(_batteryMaxTimeRemaining) * (
static_cast<double>(_battery2PctRemaining) / 100.0);
513 if (_battery2PctRemaining > 50) {
514 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_OK;
515 }
else if (_battery2PctRemaining > 30) {
516 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_LOW;
517 }
else if (_battery2PctRemaining > 20) {
518 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_CRITICAL;
520 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_EMERGENCY;
525 uint16_t rgVoltages[10]{};
526 uint16_t rgVoltagesNone[10]{};
527 uint16_t rgVoltagesExtNone[4]{};
529 for (
size_t i = 0; i < std::size(rgVoltages); i++) {
530 rgVoltages[i] = UINT16_MAX;
531 rgVoltagesNone[i] = UINT16_MAX;
533 rgVoltages[0] = rgVoltages[1] = rgVoltages[2] = 4200;
535 (void) mavlink_msg_battery_status_pack_chan(
538 _getMavlinkVehicleChannel(),
541 MAV_BATTERY_FUNCTION_ALL,
542 MAV_BATTERY_TYPE_LIPO,
548 _battery1PctRemaining,
549 _battery1TimeRemaining,
550 _battery1ChargeState,
557 (void) mavlink_msg_battery_status_pack_chan(
560 _getMavlinkVehicleChannel(),
563 MAV_BATTERY_FUNCTION_ALL,
564 MAV_BATTERY_TYPE_LIPO,
570 _battery2PctRemaining,
571 _battery2TimeRemaining,
572 _battery2ChargeState,
580void MockLink::_sendNamedValueFloats()
582 const uint32_t timeBootMs =
static_cast<uint32_t
>(_runningTime.elapsed());
585 const float sinVal =
static_cast<float>(std::sin(
static_cast<double>(timeBootMs) / 1000.0));
586 const float cosVal =
static_cast<float>(std::cos(
static_cast<double>(timeBootMs) / 1000.0));
589 (void) mavlink_msg_named_value_float_pack_chan(
592 _getMavlinkVehicleChannel(),
600 (void) mavlink_msg_named_value_float_pack_chan(
603 _getMavlinkVehicleChannel(),
612void MockLink::_sendVibration()
615 (void) mavlink_msg_vibration_pack_chan(
618 _getMavlinkVehicleChannel(),
634 uint8_t buffer[MAVLINK_MAX_PACKET_LEN]{};
635 const int cBuffer = mavlink_msg_to_send_buffer(buffer, &msg);
636 const QByteArray bytes(
reinterpret_cast<char*
>(buffer), cBuffer);
641void MockLink::_writeBytes(
const QByteArray &bytes)
647void MockLink::_writeBytesQueued(
const QByteArray &bytes)
650 qCDebug(MockLinkLog) <<
"Dropping queued bytes on disconnected/uninitialized mock link";
655 _handleIncomingNSHBytes(bytes.constData(), bytes.length());
659 if (bytes.startsWith(QByteArrayLiteral(
"\r\r\r"))) {
661 _handleIncomingNSHBytes(&bytes.constData()[3], bytes.length() - 3);
664 _handleIncomingMavlinkBytes(
reinterpret_cast<const uint8_t*
>(bytes.constData()), bytes.length());
667void MockLink::_handleIncomingNSHBytes(
const char *bytes,
int cBytes)
672 if ((cBytes == 4) && (bytes[0] ==
'\r') && (bytes[1] ==
'\r') && (bytes[2] ==
'\r')) {
678 qCDebug(MockLinkLog) <<
"NSH:" << bytes;
681 if (strncmp(bytes,
"sh /etc/init.d/rc.usb\n", cBytes) == 0) {
683 _mavlinkStarted =
true;
689void MockLink::_handleIncomingMavlinkBytes(
const uint8_t *bytes,
int cBytes)
692 mavlink_status_t comm{};
694 QMutexLocker lock(&_mavlinkAuxMutex);
695 for (qint64 i = 0; i < cBytes; i++) {
696 const int parsed = mavlink_parse_char(_getMavlinkAuxChannel(), bytes[i], &msg, &comm);
701 _handleIncomingMavlinkMsg(msg);
708 _receivedMavlinkMessageCountMap[msg.msgid]++;
711 if (msg.msgid == MAVLINK_MSG_ID_COMMAND_LONG) {
713 mavlink_msg_command_long_decode(&msg, &request);
715 _receivedMavCommandCountMap[
static_cast<MAV_CMD
>(request.command)]++;
716 _receivedMavCommandByCompCountMap[
static_cast<MAV_CMD
>(request.command)][request.target_component]++;
718 if (request.command == MAV_CMD_REQUEST_MESSAGE) {
719 _receivedRequestMessageCountMap[
static_cast<uint32_t
>(request.param1)]++;
720 _receivedRequestMessageByCompAndMsgCountMap[request.target_component][
static_cast<int>(request.param1)]++;
727 _updateIncomingMessageCounts(msg);
742 case MAVLINK_MSG_ID_HEARTBEAT:
743 _handleHeartBeat(msg);
745 case MAVLINK_MSG_ID_PARAM_REQUEST_LIST:
746 _handleParamRequestList(msg);
748 case MAVLINK_MSG_ID_SET_MODE:
751 case MAVLINK_MSG_ID_PARAM_SET:
752 _handleParamSet(msg);
754 case MAVLINK_MSG_ID_PARAM_REQUEST_READ:
755 _handleParamRequestRead(msg);
757 case MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL:
760 case MAVLINK_MSG_ID_COMMAND_LONG:
761 _handleCommandLong(msg);
763 case MAVLINK_MSG_ID_MANUAL_CONTROL:
764 _handleManualControl(msg);
766 case MAVLINK_MSG_ID_RC_CHANNELS_OVERRIDE:
767 _handleRCChannelsOverride(msg);
769 case MAVLINK_MSG_ID_LOG_REQUEST_LIST:
770 _handleLogRequestList(msg);
772 case MAVLINK_MSG_ID_LOG_REQUEST_DATA:
773 _handleLogRequestData(msg);
775 case MAVLINK_MSG_ID_PARAM_MAP_RC:
776 _handleParamMapRC(msg);
778 case MAVLINK_MSG_ID_SETUP_SIGNING:
779 _handleSetupSigning(msg);
789 qCDebug(MockLinkVerboseLog) <<
"Heartbeat";
794 mavlink_param_map_rc_t paramMapRC{};
795 mavlink_msg_param_map_rc_decode(&msg, ¶mMapRC);
797 const QString paramName(QString::fromLocal8Bit(paramMapRC.param_id,
static_cast<int>(strnlen(paramMapRC.param_id, MAVLINK_MSG_PARAM_MAP_RC_FIELD_PARAM_ID_LEN))));
799 if (paramMapRC.param_index == -1) {
800 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);
801 }
else if (paramMapRC.param_index == -2) {
802 qCDebug(MockLinkLog) <<
"MockLink - PARAM_MAP_RC: Clear tuningID" << paramMapRC.parameter_rc_channel_index;
804 qCWarning(MockLinkLog) <<
"MockLink - PARAM_MAP_RC: Unsupported param_index" << paramMapRC.param_index;
811 mavlink_msg_setup_signing_decode(&msg, &setupSigning);
813 if (setupSigning.target_system != _vehicleSystemId) {
818 bool allZeroKey =
true;
819 for (
const uint8_t
byte : setupSigning.secret_key) {
826 _signingEnabled = !allZeroKey;
830 if (_signingEnabled) {
831 memcpy(_mockSigning.secret_key, setupSigning.secret_key,
sizeof(_mockSigning.secret_key));
832 _mockSigning.link_id = _getMavlinkVehicleChannel();
833 _mockSigning.flags = MAVLINK_SIGNING_FLAG_SIGN_OUTGOING;
836 status->signing = &_mockSigning;
837 status->signing_streams = &_mockSigningStreams;
839 QGC::secureZero(_mockSigning.secret_key,
sizeof(_mockSigning.secret_key));
840 _mockSigning.accept_unsigned_callback =
nullptr;
841 status->signing =
nullptr;
842 status->signing_streams =
nullptr;
845 qCDebug(MockLinkLog) <<
"Signing" << (_signingEnabled ?
"enabled" :
"disabled");
850 mavlink_set_mode_t request{};
851 mavlink_msg_set_mode_decode(&msg, &request);
853 Q_ASSERT(request.target_system == _vehicleSystemId);
855 _mavBaseMode = request.base_mode;
856 _mavCustomMode = request.custom_mode;
861 mavlink_manual_control_t manualControl{};
862 mavlink_msg_manual_control_decode(&msg, &manualControl);
865 const auto axisStr = [](int16_t v) -> QString {
866 return (v == INT16_MAX) ? QStringLiteral(
"invalid") : QString::number(v);
869 const auto extStr = [](int16_t v,
bool enabled) -> QString {
870 return enabled ? QString::number(v) : QStringLiteral(
"disabled");
873 const uint8_t ext = manualControl.enabled_extensions;
875 qCDebug(MockLinkVerboseLog).noquote()
877 <<
"target:" << manualControl.target
878 <<
"x:" << axisStr(manualControl.x)
879 <<
"y:" << axisStr(manualControl.y)
880 <<
"z:" << axisStr(manualControl.z)
881 <<
"r:" << axisStr(manualControl.r)
882 <<
"buttons:" << QStringLiteral(
"0x%1").arg(manualControl.buttons, 4, 16, QLatin1Char(
'0'))
883 <<
"buttons2:" << QStringLiteral(
"0x%1").arg(manualControl.buttons2, 4, 16, QLatin1Char(
'0'))
884 <<
"enabled_extensions:" << QStringLiteral(
"0x%1").arg(ext, 2, 16, QLatin1Char(
'0'))
885 <<
"s(pitch):" << extStr(manualControl.s, ext & (1 << 0))
886 <<
"t(roll):" << extStr(manualControl.t, ext & (1 << 1))
887 <<
"aux1:" << extStr(manualControl.aux1, ext & (1 << 2))
888 <<
"aux2:" << extStr(manualControl.aux2, ext & (1 << 3))
889 <<
"aux3:" << extStr(manualControl.aux3, ext & (1 << 4))
890 <<
"aux4:" << extStr(manualControl.aux4, ext & (1 << 5))
891 <<
"aux5:" << extStr(manualControl.aux5, ext & (1 << 6))
892 <<
"aux6:" << extStr(manualControl.aux6, ext & (1 << 7));
897 mavlink_rc_channels_override_t
override{};
898 mavlink_msg_rc_channels_override_decode(&msg, &
override);
903 const uint16_t rawValues[18] = {
904 override.chan1_raw,
override.chan2_raw,
override.chan3_raw,
override.chan4_raw,
905 override.chan5_raw,
override.chan6_raw,
override.chan7_raw,
override.chan8_raw,
906 override.chan9_raw,
override.chan10_raw,
override.chan11_raw,
override.chan12_raw,
907 override.chan13_raw,
override.chan14_raw,
override.chan15_raw,
override.chan16_raw,
908 override.chan17_raw,
override.chan18_raw,
911 bool anyChange =
false;
912 for (
int i = 0; i < kRcChannelOverrideChannelCount; ++i) {
913 const uint16_t raw = rawValues[i];
914 const bool isExtended = (i >= 8);
916 RCChannelOverride::State newState;
918 if (raw == 0 || raw == UINT16_MAX) {
920 }
else if (raw ==
static_cast<uint16_t
>(UINT16_MAX - 1)) {
921 newState = RCChannelOverride::State::Released;
923 newState = RCChannelOverride::State::Overridden;
926 if (raw == UINT16_MAX) {
928 }
else if (raw == 0) {
929 newState = RCChannelOverride::State::Released;
931 newState = RCChannelOverride::State::Overridden;
935 RCChannelOverride &ch = _rcChannelOverrides[i];
936 if (ch.state == newState) {
942 const auto stateLabel = [](RCChannelOverride::State s) ->
const char * {
944 case RCChannelOverride::State::Ignore:
return "ignore";
945 case RCChannelOverride::State::Released:
return "released";
946 case RCChannelOverride::State::Overridden:
return "overridden";
950 qCDebug(MockLinkLog).noquote() << QStringLiteral(
"RC_CHANNELS_OVERRIDE ch%1: %2 -> %3").arg(i + 1).arg(stateLabel(ch.state)).arg(stateLabel(newState));
953 ch.value = (newState == RCChannelOverride::State::Overridden) ? raw : 0;
958 for (
int i = 0; i < kRcChannelOverrideChannelCount; ++i) {
959 if (_rcChannelOverrides[i].state == RCChannelOverride::State::Overridden) {
960 active << QStringLiteral(
"ch%1").arg(i + 1);
963 if (active.isEmpty()) {
964 qCDebug(MockLinkLog) <<
"RC_CHANNELS_OVERRIDE: no channels currently overridden";
966 qCDebug(MockLinkLog).noquote() <<
"RC_CHANNELS_OVERRIDE active overrides:" << active.join(QStringLiteral(
", "));
970 for (
int i = 0; i < kRcChannelOverrideChannelCount; ++i) {
971 if (_rcChannelOverrides[i].state == RCChannelOverride::State::Overridden) {
972 qCDebug(MockLinkVerboseLog).noquote() << QStringLiteral(
"RC_CHANNELS_OVERRIDE ch%1 value: %2").arg(i + 1).arg(_rcChannelOverrides[i].value);
977void MockLink::_setParamFloatUnionIntoMap(
int componentId,
const QString ¶mName,
float paramFloat)
979 Q_ASSERT(_mapParamName2Value.contains(componentId));
980 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
981 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
983 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
984 QVariant paramVariant;
986 valueUnion.param_float = paramFloat;
988 case MAV_PARAM_TYPE_REAL32:
989 paramVariant = QVariant::fromValue(valueUnion.param_float);
991 case MAV_PARAM_TYPE_UINT32:
992 paramVariant = QVariant::fromValue(valueUnion.param_uint32);
994 case MAV_PARAM_TYPE_INT32:
995 paramVariant = QVariant::fromValue(valueUnion.param_int32);
997 case MAV_PARAM_TYPE_UINT16:
998 paramVariant = QVariant::fromValue(valueUnion.param_uint16);
1000 case MAV_PARAM_TYPE_INT16:
1001 paramVariant = QVariant::fromValue(valueUnion.param_int16);
1003 case MAV_PARAM_TYPE_UINT8:
1004 paramVariant = QVariant::fromValue(valueUnion.param_uint8);
1006 case MAV_PARAM_TYPE_INT8:
1007 paramVariant = QVariant::fromValue(valueUnion.param_int8);
1010 qCCritical(MockLinkLog) <<
"Invalid parameter type" << paramType;
1011 paramVariant = QVariant::fromValue(valueUnion.param_int32);
1015 qCDebug(MockLinkLog) <<
"_setParamFloatUnionIntoMap" << paramName << paramVariant;
1016 _mapParamName2Value[componentId][paramName] = paramVariant;
1022 valueUnion.param_float = value;
1023 _setParamFloatUnionIntoMap(componentId, paramName, valueUnion.param_float);
1026float MockLink::_floatUnionForParam(
int componentId,
const QString ¶mName)
1028 Q_ASSERT(_mapParamName2Value.contains(componentId));
1029 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
1030 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
1032 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
1033 const QVariant paramVar = _mapParamName2Value[componentId][paramName];
1036 switch (paramType) {
1037 case MAV_PARAM_TYPE_REAL32:
1038 valueUnion.param_float = paramVar.toFloat();
1040 case MAV_PARAM_TYPE_UINT32:
1041 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1042 valueUnion.param_float = paramVar.toUInt();
1044 valueUnion.param_uint32 = paramVar.toUInt();
1047 case MAV_PARAM_TYPE_INT32:
1048 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1049 valueUnion.param_float = paramVar.toInt();
1051 valueUnion.param_int32 = paramVar.toInt();
1054 case MAV_PARAM_TYPE_UINT16:
1055 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1056 valueUnion.param_float = paramVar.toUInt();
1058 valueUnion.param_uint16 = paramVar.toUInt();
1061 case MAV_PARAM_TYPE_INT16:
1062 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1063 valueUnion.param_float = paramVar.toInt();
1065 valueUnion.param_int16 = paramVar.toInt();
1068 case MAV_PARAM_TYPE_UINT8:
1069 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1070 valueUnion.param_float = paramVar.toUInt();
1072 valueUnion.param_uint8 = paramVar.toUInt();
1075 case MAV_PARAM_TYPE_INT8:
1076 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1077 valueUnion.param_float = (
unsigned char)paramVar.toChar().toLatin1();
1079 valueUnion.param_int8 = (
unsigned char)paramVar.toChar().toLatin1();
1083 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1084 valueUnion.param_float = paramVar.toInt();
1086 valueUnion.param_int32 = paramVar.toInt();
1088 qCCritical(MockLinkLog) <<
"Invalid parameter type" << paramType;
1091 return valueUnion.param_float;
1094uint32_t MockLink::_computeParamHash(
int componentId)
const
1097 static const QStringList volatileParams = {
1098 QStringLiteral(
"COM_FLIGHT_UUID"),
1099 QStringLiteral(
"EKF2_MAGBIAS_X"),
1100 QStringLiteral(
"EKF2_MAGBIAS_Y"),
1101 QStringLiteral(
"EKF2_MAGBIAS_Z"),
1102 QStringLiteral(
"EKF2_MAG_DECL"),
1103 QStringLiteral(
"LND_FLIGHT_T_HI"),
1104 QStringLiteral(
"LND_FLIGHT_T_LO"),
1105 QStringLiteral(
"SYS_RESTART_TYPE"),
1109 const auto ¶ms = _mapParamName2Value[componentId];
1110 for (
auto it = params.constBegin(); it != params.constEnd(); ++it) {
1111 const QString &name = it.key();
1112 if (volatileParams.contains(name)) {
1115 const QVariant &value = it.value();
1116 const MAV_PARAM_TYPE mavType = _mapParamName2MavParamType[componentId][name];
1119 crc =
QGC::crc32(
reinterpret_cast<const uint8_t *
>(qPrintable(name)), name.length(), crc);
1131 mavlink_param_request_list_t request{};
1132 mavlink_msg_param_request_list_decode(&msg, &request);
1134 Q_ASSERT(request.target_system == _vehicleSystemId);
1135 Q_ASSERT(request.target_component == MAV_COMP_ID_ALL);
1139 QMutexLocker locker(&_paramRequestListMutex);
1140 _paramRequestListComponentIds = _mapParamName2Value.keys();
1141 if (!_paramRequestListComponentIds.isEmpty()) {
1142 _paramRequestListParamNames = _mapParamName2Value[_paramRequestListComponentIds.first()].keys();
1146 _currentParamRequestListComponentIndex = 0;
1147 _currentParamRequestListParamIndex = 0;
1148 _paramRequestListHashCheckSent =
false;
1151void MockLink::_paramRequestListWorker()
1153 if (_currentParamRequestListComponentIndex == -1) {
1159 QMutexLocker locker(&_paramRequestListMutex);
1162 if (_currentParamRequestListComponentIndex >= _paramRequestListComponentIds.count()) {
1163 _currentParamRequestListComponentIndex = -1;
1167 const int componentId = _paramRequestListComponentIds.at(_currentParamRequestListComponentIndex);
1168 const int cParameters = _paramRequestListParamNames.count();
1170 if (_currentParamRequestListParamIndex >= cParameters) {
1174 if (_firmwareType == MAV_AUTOPILOT_PX4 && !_paramRequestListHashCheckSent) {
1175 _paramRequestListHashCheckSent =
true;
1178 valueUnion.type = MAV_PARAM_TYPE_UINT32;
1179 valueUnion.param_uint32 = _computeParamHash(componentId);
1181 char paramId[MAVLINK_MSG_ID_PARAM_VALUE_LEN]{};
1182 (void) strncpy(paramId,
"_HASH_CHECK", MAVLINK_MSG_ID_PARAM_VALUE_LEN);
1184 qCDebug(MockLinkLog) <<
"Sending _HASH_CHECK in PARAM_REQUEST_LIST stream" << componentId <<
"hash:" << valueUnion.param_uint32;
1187 (void) mavlink_msg_param_value_pack_chan(
1190 _getMavlinkVehicleChannel(),
1193 valueUnion.param_float,
1194 MAV_PARAM_TYPE_UINT32,
1203 if (++_currentParamRequestListComponentIndex >= _paramRequestListComponentIds.count()) {
1204 _currentParamRequestListComponentIndex = -1;
1205 _paramRequestListComponentIds.clear();
1206 _paramRequestListParamNames.clear();
1209 _paramRequestListParamNames = _mapParamName2Value[_paramRequestListComponentIds.at(_currentParamRequestListComponentIndex)].keys();
1210 _currentParamRequestListParamIndex = 0;
1211 _paramRequestListHashCheckSent =
false;
1216 const QString ¶mName = _paramRequestListParamNames.at(_currentParamRequestListParamIndex);
1219 qCDebug(MockLinkLog) <<
"Skipping param send:" << paramName;
1221 char paramId[MAVLINK_MSG_ID_PARAM_VALUE_LEN]{};
1224 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
1225 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
1227 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
1229 Q_ASSERT(paramName.length() <= MAVLINK_MSG_ID_PARAM_VALUE_LEN);
1230 (void) strncpy(paramId, paramName.toLocal8Bit().constData(), MAVLINK_MSG_ID_PARAM_VALUE_LEN);
1232 qCDebug(MockLinkLog) <<
"Sending msg_param_value" << componentId << paramId << paramType << _mapParamName2Value[componentId][paramId];
1234 (void) mavlink_msg_param_value_pack_chan(
1237 _getMavlinkVehicleChannel(),
1240 _floatUnionForParam(componentId, paramName),
1243 _currentParamRequestListParamIndex
1249 ++_currentParamRequestListParamIndex;
1254 mavlink_param_set_t request{};
1255 mavlink_msg_param_set_decode(&msg, &request);
1257 Q_ASSERT(request.target_system == _vehicleSystemId);
1258 const int componentId = request.target_component;
1261 char paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN + 1]{};
1262 paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN] = 0;
1263 (void) strncpy(paramId, request.param_id, MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN);
1265 qCDebug(MockLinkLog) <<
"_handleParamSet" << componentId << paramId << request.param_type;
1270 if ((_firmwareType == MAV_AUTOPILOT_PX4) && (strncmp(paramId,
"_HASH_CHECK", MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN) == 0)) {
1271 QMutexLocker locker(&_paramRequestListMutex);
1272 _currentParamRequestListComponentIndex = -1;
1273 _paramRequestListComponentIds.clear();
1274 _paramRequestListParamNames.clear();
1275 qCDebug(MockLinkLog) <<
"Received _HASH_CHECK PARAM_SET, stopping parameter stream";
1279 Q_ASSERT(_mapParamName2Value.contains(componentId));
1280 Q_ASSERT(_mapParamName2MavParamType.contains(componentId));
1281 Q_ASSERT(_mapParamName2Value[componentId].contains(paramId));
1282 Q_ASSERT(request.param_type == _mapParamName2MavParamType[componentId][paramId]);
1286 qCDebug(MockLinkLog) <<
"Param set failure: first attempt no ack" << paramId;
1287 _paramSetFailureFirstAttemptPending =
false;
1292 qCDebug(MockLinkLog) <<
"Param set failure: no ack" << paramId;
1297 qCDebug(MockLinkLog) <<
"Param set failure: PARAM_ERROR" << paramId;
1298 _sendParamError(componentId, paramId,
1299 _mapParamName2Value[componentId].keys().indexOf(paramId),
1300 MAV_PARAM_ERROR_VALUE_OUT_OF_RANGE);
1305 _setParamFloatUnionIntoMap(componentId, paramId, request.param_value);
1308 mavlink_msg_param_value_pack_chan(
1311 _getMavlinkVehicleChannel(),
1314 request.param_value,
1316 _mapParamName2Value[componentId].count(),
1317 _mapParamName2Value[componentId].keys().indexOf(paramId)
1325 mavlink_param_request_read_t request{};
1326 mavlink_msg_param_request_read_decode(&msg, &request);
1328 const QString paramName(QString::fromLocal8Bit(request.param_id,
static_cast<int>(strnlen(request.param_id, MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN))));
1329 const int componentId = request.target_component;
1332 if ((_firmwareType == MAV_AUTOPILOT_PX4) && (paramName ==
"_HASH_CHECK")) {
1333 _hashCheckRequestCount++;
1334 if (_hashCheckNoResponse) {
1338 const int hashComponentId = _mapParamName2Value.contains(MAV_COMP_ID_AUTOPILOT1)
1339 ? MAV_COMP_ID_AUTOPILOT1
1340 : _mapParamName2Value.keys().first();
1343 valueUnion.type = MAV_PARAM_TYPE_UINT32;
1344 valueUnion.param_uint32 = _computeParamHash(hashComponentId);
1345 (void) mavlink_msg_param_value_pack_chan(
1348 _getMavlinkVehicleChannel(),
1351 valueUnion.param_float,
1352 MAV_PARAM_TYPE_UINT32,
1360 Q_ASSERT(_mapParamName2Value.contains(componentId));
1362 char paramId[MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN + 1]{};
1365 Q_ASSERT(request.target_system == _vehicleSystemId);
1367 if (request.param_index == -1) {
1369 (void) strncpy(paramId, request.param_id, MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
1372 Q_ASSERT(request.param_index >= 0 && request.param_index < _mapParamName2Value[componentId].count());
1374 const QString key = _mapParamName2Value[componentId].keys().at(request.param_index);
1375 Q_ASSERT(key.length() <= MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
1376 strcpy(paramId, key.toLocal8Bit().constData());
1379 Q_ASSERT(_mapParamName2Value[componentId].contains(paramId));
1380 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramId));
1383 qCDebug(MockLinkLog) <<
"Ignoring request read for " << _failParam;
1389 qCDebug(MockLinkLog) <<
"Param request read failure: first attempt no response" << paramId;
1390 _paramRequestReadFailureFirstAttemptPending =
false;
1395 qCDebug(MockLinkLog) <<
"Param request read failure: no response" << paramId;
1400 qCDebug(MockLinkLog) <<
"Param request read failure: PARAM_ERROR" << paramId;
1401 _sendParamError(componentId, paramId, request.param_index, MAV_PARAM_ERROR_DOES_NOT_EXIST);
1405 (void) mavlink_msg_param_value_pack_chan(
1408 _getMavlinkVehicleChannel(),
1411 _floatUnionForParam(componentId, paramId),
1412 _mapParamName2MavParamType[componentId][paramId],
1413 _mapParamName2Value[componentId].count(),
1414 _mapParamName2Value[componentId].keys().indexOf(paramId)
1419void MockLink::_sendParamError(
int componentId,
const char *paramId, int16_t paramIndex, uint8_t errorCode)
1422 char paramIdBuf[MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN + 1] = {};
1423 (void) strncpy(paramIdBuf, paramId, MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN);
1425 (void) mavlink_msg_param_error_pack_chan(
1427 static_cast<uint8_t
>(componentId),
1446 uint8_t commandResult = MAV_RESULT_UNSUPPORTED;
1448 switch (request.command) {
1451 commandResult = MAV_RESULT_ACCEPTED;
1455 commandResult = MAV_RESULT_FAILED;
1463 (void) mavlink_msg_command_ack_pack_chan(
1465 _vehicleComponentId,
1469 MAV_RESULT_IN_PROGRESS,
1478 (void) mavlink_msg_command_ack_pack_chan(
1480 _vehicleComponentId,
1494void MockLink::_handleCommandLongSetMessageInterval(
const mavlink_command_long_t &request,
bool &accepted)
1502 static bool firstCmdUser3 =
true;
1503 static bool firstCmdUser4 =
true;
1506 mavlink_msg_command_long_decode(&msg, &request);
1508 uint8_t commandResult = MAV_RESULT_UNSUPPORTED;
1510 switch (request.command) {
1511 case MAV_CMD_COMPONENT_ARM_DISARM:
1512 if (request.param1 == 0.0f) {
1513 _mavBaseMode &= ~MAV_MODE_FLAG_SAFETY_ARMED;
1515 _mavBaseMode |= MAV_MODE_FLAG_SAFETY_ARMED;
1517 commandResult = MAV_RESULT_ACCEPTED;
1519 case MAV_CMD_PREFLIGHT_CALIBRATION:
1520 _handlePreFlightCalibration(request);
1521 commandResult = MAV_RESULT_ACCEPTED;
1523 case MAV_CMD_DO_MOTOR_TEST:
1524 commandResult = MAV_RESULT_ACCEPTED;
1526 case MAV_CMD_CONTROL_HIGH_LATENCY:
1528 _highLatencyTransmissionEnabled =
static_cast<int>(request.param1) != 0;
1530 commandResult = MAV_RESULT_ACCEPTED;
1532 commandResult = MAV_RESULT_FAILED;
1535 case MAV_CMD_PREFLIGHT_STORAGE:
1536 commandResult = MAV_RESULT_ACCEPTED;
1538 case MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES:
1539 commandResult = MAV_RESULT_ACCEPTED;
1540 _respondWithAutopilotVersion();
1542 case MAV_CMD_REQUEST_MESSAGE:
1544 bool accepted =
false;
1546 _handleRequestMessage(request, accepted, noAck);
1552 commandResult = MAV_RESULT_ACCEPTED;
1556 case MAV_CMD_NAV_TAKEOFF:
1557 _handleTakeoff(request);
1558 commandResult = MAV_RESULT_ACCEPTED;
1562 commandResult = MAV_RESULT_ACCEPTED;
1566 commandResult = MAV_RESULT_FAILED;
1570 if (firstCmdUser3) {
1571 firstCmdUser3 =
false;
1574 firstCmdUser3 =
true;
1575 commandResult = MAV_RESULT_ACCEPTED;
1580 if (firstCmdUser4) {
1581 firstCmdUser4 =
false;
1584 firstCmdUser4 =
true;
1585 commandResult = MAV_RESULT_FAILED;
1595 _handleInProgressCommandLong(request);
1597 case MAV_CMD_SET_MESSAGE_INTERVAL:
1599 bool accepted =
false;
1601 _handleCommandLongSetMessageInterval(request, accepted);
1603 commandResult = MAV_RESULT_ACCEPTED;
1610 (void) mavlink_msg_command_ack_pack_chan(
1612 _vehicleComponentId,
1628 (void) mavlink_msg_command_ack_pack_chan(
1630 _vehicleComponentId,
1643void MockLink::_respondWithAutopilotVersion()
1645 union FlightVersion {
1655 FlightVersion(uint32_t version = 0) : raw(version) {}
1657 FlightVersion flightVersion;
1659#ifndef QGC_NO_ARDUPILOT_DIALECT
1660 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1661 flightVersion.parts.major = 4;
1662 flightVersion.parts.minor = 7;
1663 flightVersion.parts.patch = 0;
1664 flightVersion.parts.type = FIRMWARE_VERSION_TYPE_OFFICIAL;
1665 }
else if (_firmwareType == MAV_AUTOPILOT_PX4) {
1667 flightVersion.parts.major = 1;
1668 flightVersion.parts.minor = 4;
1669 flightVersion.parts.patch = 1;
1670 flightVersion.parts.type = FIRMWARE_VERSION_TYPE_DEV;
1671#ifndef QGC_NO_ARDUPILOT_DIALECT
1675 const uint8_t customVersion[8]{};
1676 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);
1679 (void) mavlink_msg_autopilot_version_pack_chan(
1681 _vehicleComponentId,
1689 reinterpret_cast<const uint8_t*
>(&customVersion),
1690 reinterpret_cast<const uint8_t*
>(&customVersion),
1691 reinterpret_cast<const uint8_t*
>(&customVersion),
1700void MockLink::_sendHomePosition()
1702 const float bogus[4]{};
1705 (void) mavlink_msg_home_position_pack_chan(
1707 _vehicleComponentId,
1710 static_cast<int32_t
>(_vehicleLatitude * 1E7),
1711 static_cast<int32_t
>(_vehicleLongitude * 1E7),
1712 static_cast<int32_t
>(_defaultVehicleHomeAltitude * 1000),
1721void MockLink::_sendGpsRawInt()
1723 static uint64_t timeTick = 0;
1726 (void) mavlink_msg_gps_raw_int_pack_chan(
1728 _vehicleComponentId,
1732 GPS_FIX_TYPE_3D_FIX,
1733 static_cast<int32_t
>(_vehicleLatitude * 1E7),
1734 static_cast<int32_t
>(_vehicleLongitude * 1E7),
1735 static_cast<int32_t
>(_vehicleAltitudeAMSL * 1000),
1752void MockLink::_sendGlobalPositionInt()
1754 static uint64_t timeTick = 0;
1757 (void) mavlink_msg_global_position_int_pack_chan(
1759 _vehicleComponentId,
1763 static_cast<int32_t
>(_vehicleLatitude * 1E7),
1764 static_cast<int32_t
>(_vehicleLongitude * 1E7),
1765 static_cast<int32_t
>(_vehicleAltitudeAMSL * 1000),
1766 static_cast<int32_t
>((_vehicleAltitudeAMSL - _defaultVehicleHomeAltitude) * 1000),
1773void MockLink::_sendExtendedSysState()
1776 (void) mavlink_msg_extended_sys_state_pack_chan(
1778 _vehicleComponentId,
1781 MAV_VTOL_STATE_UNDEFINED,
1782 (_vehicleAltitudeAMSL > _defaultVehicleHomeAltitude) ? MAV_LANDED_STATE_IN_AIR : MAV_LANDED_STATE_ON_GROUND
1787void MockLink::_sendChunkedStatusText(uint16_t chunkId,
bool missingChunks)
1789 constexpr int cChunks = 4;
1792 for (
int i = 0; i < cChunks; i++) {
1793 if (missingChunks && (i & 1)) {
1797 int cBuf = MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN;
1798 char msgBuf[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN]{};
1800 if (i == cChunks - 1) {
1805 for (
int j = 0; j < cBuf - 1; j++) {
1806 msgBuf[j] =
'0' + num++;
1811 msgBuf[cBuf-1] =
'A' + i;
1814 (void) mavlink_msg_statustext_pack_chan(
1816 _vehicleComponentId,
1828void MockLink::_sendStatusTextMessages()
1830 struct StatusMessage {
1831 MAV_SEVERITY severity;
1835 static constexpr struct StatusMessage rgMessages[] = {
1836 { MAV_SEVERITY_INFO,
"#Testing audio output" },
1837 { MAV_SEVERITY_EMERGENCY,
"Status text emergency" },
1838 { MAV_SEVERITY_ALERT,
"Status text alert" },
1839 { MAV_SEVERITY_CRITICAL,
"Status text critical" },
1840 { MAV_SEVERITY_ERROR,
"Status text error" },
1841 { MAV_SEVERITY_WARNING,
"Status text warning" },
1842 { MAV_SEVERITY_NOTICE,
"Status text notice" },
1843 { MAV_SEVERITY_INFO,
"Status text info" },
1844 { MAV_SEVERITY_DEBUG,
"Status text debug" },
1848 for (
size_t i = 0; i < std::size(rgMessages); i++) {
1849 const struct StatusMessage *status = &rgMessages[i];
1850 char statusText[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN] = {};
1851 (void) std::strncpy(statusText, status->msg,
sizeof(statusText) - 1);
1853 (void) mavlink_msg_statustext_pack_chan(
1855 _vehicleComponentId,
1866 _sendChunkedStatusText(1,
false );
1867 _sendChunkedStatusText(2,
true );
1868 _sendChunkedStatusText(3,
false );
1869 _sendChunkedStatusText(4,
true );
1878 return qobject_cast<MockLink*>(config->link());
1884MockLink *MockLink::_startMockLinkWorker(
const QString &configName, MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType,
bool sendStatusText,
bool enableCamera,
bool enableGimbal,
MockConfiguration::FailureMode_t failureMode)
1895 return _startMockLink(mockConfig);
1900 return _startMockLinkWorker(QStringLiteral(
"PX4 MultiRotor MockLink"), MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, sendStatusText, enableCamera, enableGimbal, failureMode);
1905 return _startMockLinkWorker(QStringLiteral(
"Generic MockLink"), MAV_AUTOPILOT_GENERIC, MAV_TYPE_QUADROTOR, sendStatusText, enableCamera, enableGimbal, failureMode);
1910 return _startMockLinkWorker(QStringLiteral(
"No Initial Connect MockLink"), MAV_AUTOPILOT_PX4, MAV_TYPE_GENERIC, sendStatusText, enableCamera, enableGimbal, failureMode);
1915 return _startMockLinkWorker(QStringLiteral(
"ArduCopter MockLink"),MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_QUADROTOR, sendStatusText, enableCamera, enableGimbal, failureMode);
1920 return _startMockLinkWorker(QStringLiteral(
"ArduPlane MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_FIXED_WING, sendStatusText, enableCamera, enableGimbal, failureMode);
1925 return _startMockLinkWorker(QStringLiteral(
"ArduSub MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_SUBMARINE, sendStatusText, enableCamera, enableGimbal, failureMode);
1930 return _startMockLinkWorker(QStringLiteral(
"ArduRover MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_GROUND_ROVER, sendStatusText, enableCamera, enableGimbal, failureMode);
1933void MockLink::_sendRCChannels()
1936 (void) mavlink_msg_rc_channels_pack_chan(
1938 _vehicleComponentId,
1943 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500,
1944 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500,
1945 UINT16_MAX, UINT16_MAX,
1953 static constexpr const char *gyroCalResponse =
"[cal] calibration started: 2 gyro";
1954 static constexpr const char *magCalResponse =
"[cal] calibration started: 2 mag";
1955 static constexpr const char *accelCalResponse =
"[cal] calibration started: 2 accel";
1956 const char *pCalMessage;
1958 if (request.param1 == 1) {
1959 pCalMessage = gyroCalResponse;
1960 }
else if (request.param2 == 1) {
1961 pCalMessage = magCalResponse;
1962 }
else if (request.param5 == 1) {
1963 pCalMessage = accelCalResponse;
1968 char statusText[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN] = {};
1969 (void) std::strncpy(statusText, pCalMessage,
sizeof(statusText) - 1);
1972 (void) mavlink_msg_statustext_pack_chan(
1974 _vehicleComponentId,
1987 _vehicleAltitudeAMSL = request.param7 + _defaultVehicleHomeAltitude;
1988 _mavBaseMode |= MAV_MODE_FLAG_SAFETY_ARMED;
1993 mavlink_log_request_list_t request{};
1994 mavlink_msg_log_request_list_decode(&msg, &request);
1996 if ((request.start != 0) && (request.end != 0xffff)) {
1997 qCWarning(MockLinkLog) <<
"_handleLogRequestList cannot handle partial requests";
2002 mavlink_msg_log_entry_pack_chan(
2004 _vehicleComponentId,
2011 _logDownloadFileSize
2016QString MockLink::_createRandomFile(uint32_t byteCount)
2018 QTemporaryFile tempFile;
2019 tempFile.setAutoRemove(
false);
2020 if (!tempFile.open()) {
2021 qCWarning(MockLinkLog) <<
"MockLink::createRandomFile open failed" << tempFile.errorString();
2025 for (uint32_t bytesWritten = 0; bytesWritten < byteCount; bytesWritten++) {
2026 const unsigned char byte = (QRandomGenerator::global()->generate() * 0xFF) / RAND_MAX;
2027 (void) tempFile.write(
reinterpret_cast<const char*
>(&
byte), 1);
2031 return tempFile.fileName();
2036 mavlink_log_request_data_t request{};
2037 mavlink_msg_log_request_data_decode(&msg, &request);
2039#ifdef QGC_UNITTEST_BUILD
2040 if (_logDownloadFilename.isEmpty()) {
2041 _logDownloadFilename = _createRandomFile(_logDownloadFileSize);
2045 if (request.id != 0) {
2046 qCWarning(MockLinkLog) <<
"_handleLogRequestData id must be 0";
2050 if (request.ofs > (_logDownloadFileSize - 1)) {
2051 qCWarning(MockLinkLog) <<
"_handleLogRequestData offset past end of file request.ofs:size" << request.ofs << _logDownloadFileSize;
2058 QMutexLocker locker(&_logDownloadMutex);
2059 _logDownloadCurrentOffset = request.ofs;
2060 if (request.ofs + request.count > _logDownloadFileSize) {
2061 request.count = _logDownloadFileSize - request.ofs;
2063 _logDownloadBytesRemaining = request.count;
2066void MockLink::_logDownloadWorker()
2070 QMutexLocker locker(&_logDownloadMutex);
2071 if (_logDownloadBytesRemaining == 0) {
2075 QFile file(_logDownloadFilename);
2076 if (!file.open(QIODevice::ReadOnly)) {
2077 qCWarning(MockLinkLog) <<
"_logDownloadWorker open failed" << file.errorString();
2081 uint8_t buffer[MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN]{};
2083 const qint64 bytesToRead = qMin(_logDownloadBytesRemaining, (uint32_t)MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN);
2084 Q_ASSERT(file.seek(_logDownloadCurrentOffset));
2085 Q_ASSERT(file.read(
reinterpret_cast<char*
>(buffer), bytesToRead) == bytesToRead);
2087 qCDebug(MockLinkLog) <<
"_logDownloadWorker" << _logDownloadCurrentOffset << _logDownloadBytesRemaining;
2090 (void) mavlink_msg_log_data_pack_chan(
2092 _vehicleComponentId,
2096 _logDownloadCurrentOffset,
2102 _logDownloadCurrentOffset += bytesToRead;
2103 _logDownloadBytesRemaining -= bytesToRead;
2108void MockLink::_sendADSBVehicles()
2110 for (
int i = 0; i < _adsbVehicles.size(); ++i) {
2112 _adsbVehicles[i].angle += (i + 1);
2115 _adsbVehicles[i].coordinate = _adsbVehicles[i].coordinate.atDistanceAndAzimuth(5, _adsbVehicles[i].angle);
2118 _adsbVehicles[i].altitude += (i % 2 == 0 ? 0.5 : -0.5);
2120 QByteArray callsign = QString(
"N12345%1").arg(i, 2, 10, QChar(
'0')).toLatin1();
2121 callsign.resize(MAVLINK_MSG_ADSB_VEHICLE_FIELD_CALLSIGN_LEN);
2125 (void) mavlink_msg_adsb_vehicle_pack_chan(
2127 _vehicleComponentId,
2131 _adsbVehicles[i].coordinate.latitude() * 1e7,
2132 _adsbVehicles[i].coordinate.longitude() * 1e7,
2133 ADSB_ALTITUDE_TYPE_GEOMETRIC,
2134 _adsbVehicles[i].altitude * 1000,
2136 static_cast<uint16_t
>(_adsbVehicles[i].angle * 100),
2138 callsign.constData(),
2139 ADSB_EMITTER_TYPE_ROTOCRAFT,
2141 ADSB_FLAGS_VALID_COORDS | ADSB_FLAGS_VALID_ALTITUDE | ADSB_FLAGS_VALID_HEADING | ADSB_FLAGS_VALID_CALLSIGN | ADSB_FLAGS_SIMULATED,
2148void MockLink::_moveADSBVehicle(
int vehicleIndex)
2150 _adsbAngles[vehicleIndex] += 10;
2151 QGeoCoordinate &coord = _adsbVehicleCoordinates[vehicleIndex];
2154 coord = QGeoCoordinate(coord.latitude(), coord.longitude()).atDistanceAndAzimuth(500, _adsbAngles[vehicleIndex]);
2155 coord.setAltitude(100);
2162 switch (_failureMode) {
2175 _respondWithAutopilotVersion();
2183 switch (_requestMessageFailureMode) {
2198 (void) mavlink_msg_debug_pack_chan(
2200 _vehicleComponentId,
2215 QMutexLocker locker(&_availableModesWorkerMutex);
2216 if (request.param2 == 0) {
2218 if (_availableModesWorkerNextModeIndex != 0) {
2219 qCWarning(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: _availableModesWorker already running - _availableModesWorkerNextModeIndex:" << _availableModesWorkerNextModeIndex;
2223 qCDebug(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: starting available modes sequence worker";
2224 _availableModesWorkerNextModeIndex = 1;
2227 if (request.param2 > _availableFlightModes.count()) {
2228 qCWarning(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: requested mode index out of range" << request.param2 << _availableFlightModes.count();
2232 qCDebug(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: received specific mode request for index" << request.param2;
2233 _availableModesWorkerNextModeIndex = -request.param2;
2242 const uint32_t requestedMessageId =
static_cast<uint32_t
>(request.param1);
2246 QMutexLocker locker(&_requestMessageNoResponseMutex);
2247 if (_requestMessageNoResponseIds.contains(requestedMessageId)) {
2253 switch (
static_cast<int>(request.param1)) {
2254 case MAVLINK_MSG_ID_AUTOPILOT_VERSION:
2255 _handleRequestMessageAutopilotVersion(request, accepted);
2257 case MAVLINK_MSG_ID_COMPONENT_METADATA:
2258 if (_firmwareType == MAV_AUTOPILOT_PX4) {
2259 _sendGeneralMetaData();
2263 case MAVLINK_MSG_ID_DEBUG:
2264 _handleRequestMessageDebug(request, accepted, noAck);
2266 case MAVLINK_MSG_ID_AVAILABLE_MODES:
2267 _handleRequestMessageAvailableModes(request, accepted);
2272void MockLink::_sendGeneralMetaData()
2274 static constexpr const char metaDataURI[MAVLINK_MSG_COMPONENT_METADATA_FIELD_URI_LEN] =
"mftp://[;comp=1]general.json";
2277 (void) mavlink_msg_component_metadata_pack_chan(
2279 _vehicleComponentId,
2289void MockLink::_sendRemoteIDArmStatus()
2291 char armStatusError[MAVLINK_MSG_OPEN_DRONE_ID_ARM_STATUS_FIELD_ERROR_LEN] = {};
2292 std::strncpy(armStatusError,
"No Error",
sizeof(armStatusError) - 1);
2295 (void) mavlink_msg_open_drone_id_arm_status_pack(
2297 MAV_COMP_ID_ODID_TXRX_1,
2299 MAV_ODID_ARM_STATUS_GOOD_TO_ARM,
2313 return _mockLinkFTP;
2316void MockLink::_sendAvailableMode(uint8_t modeIndexOneBased)
2318 if (modeIndexOneBased > _availableModesCount()) {
2319 qCWarning(MockLinkLog) <<
"modeIndexOneBased out of range" << modeIndexOneBased << _availableModesCount();
2323 qCDebug(MockLinkLog) <<
"_sendAvailableMode modeIndexOneBased:" << modeIndexOneBased;
2325 const FlightMode_t &availableMode = _availableFlightModes[modeIndexOneBased - 1];
2326 char modeName[MAVLINK_MSG_AVAILABLE_MODES_FIELD_MODE_NAME_LEN] = {};
2327 std::strncpy(modeName, availableMode.name,
sizeof(modeName) - 1);
2331 (void) mavlink_msg_available_modes_pack_chan(
2333 _vehicleComponentId,
2336 _availableModesCount(),
2338 availableMode.standard_mode,
2339 availableMode.custom_mode,
2340 availableMode.canBeSet ? 0 : MAV_MODE_PROPERTY_NOT_USER_SELECTABLE,
2345void MockLink::_availableModesWorker()
2349 QMutexLocker locker(&_availableModesWorkerMutex);
2350 if (_availableModesWorkerNextModeIndex == 0) {
2355 _sendAvailableMode(qAbs(_availableModesWorkerNextModeIndex));
2357 if (_availableModesWorkerNextModeIndex < 0) {
2359 _availableModesWorkerNextModeIndex = 0;
2360 }
else if (++_availableModesWorkerNextModeIndex > _availableModesCount()) {
2362 _availableModesWorkerNextModeIndex = 0;
2363 qCDebug(MockLinkLog) <<
"_availableModesWorker: all modes sent, stopping worker";
2367void MockLink::_sendAvailableModesMonitor()
2371 (void) mavlink_msg_available_modes_monitor_pack_chan(
2373 _vehicleComponentId,
2376 _availableModesMonitorSeqNumber);
2380int MockLink::_availableModesCount()
const
2382 return _availableFlightModes.count() - (_availableModesMonitorSeqNumber == 0 ? 1 : 0);
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 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)
uint8_t mavlinkChannel() const
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()
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)
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)
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)
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
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
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)