14#include <QtCore/QFile>
15#include <QtCore/QMutexLocker>
16#include <QtCore/QRandomGenerator>
17#include <QtCore/QTemporaryFile>
18#include <QtCore/QThread>
19#include <QtCore/QTimer>
26std::atomic<
int>
MockLink::_nextVehicleSystemId{128};
28QList<MockLink::FlightMode_t> MockLink::_availableFlightModes = {
51 , _firmwareType(_mockConfig->firmwareType())
52 , _vehicleType(_mockConfig->vehicleType())
53 , _sendStatusText(_mockConfig->sendStatusText())
54 , _enableCamera(_mockConfig->enableCamera())
55 , _enableGimbal(_mockConfig->enableGimbal())
56 , _failureMode(_mockConfig->failureMode())
57 , _vehicleSystemId(_mockConfig->incrementVehicleId() ? _nextVehicleSystemId++ : static_cast<int>(_nextVehicleSystemId))
58 , _vehicleLatitude(_defaultVehicleLatitude + ((_vehicleSystemId - 128) * 0.0001))
59 , _vehicleLongitude(_defaultVehicleLongitude + ((_vehicleSystemId - 128) * 0.0001))
60 , _boardVendorId(_mockConfig->boardVendorId())
61 , _boardProductId(_mockConfig->boardProductId())
64 _mockConfig->cameraCaptureVideo(),
65 _mockConfig->cameraCaptureImage(),
66 _mockConfig->cameraHasModes(),
67 _mockConfig->cameraHasVideoStream(),
68 _mockConfig->cameraCanCaptureImageInVideoMode(),
69 _mockConfig->cameraCanCaptureVideoInImageMode(),
70 _mockConfig->cameraHasBasicZoom(),
71 _mockConfig->cameraHasTrackingPoint(),
72 _mockConfig->cameraHasTrackingRectangle())
75 _mockConfig->gimbalHasRollAxis(),
76 _mockConfig->gimbalHasPitchAxis(),
77 _mockConfig->gimbalHasYawAxis(),
78 _mockConfig->gimbalHasYawFollow(),
79 _mockConfig->gimbalHasYawLock(),
80 _mockConfig->gimbalHasRetract(),
81 _mockConfig->gimbalHasNeutral())
83 , _mockLinkFTP(new
MockLinkFTP(_vehicleSystemId, _vehicleComponentId, this))
85 qCDebug(MockLinkLog) <<
this;
88 _adsbVehicles.reserve(_numberOfVehicles);
89 for (
int i = 0; i < _numberOfVehicles; ++i) {
90 ADSBVehicle vehicle{};
91 vehicle.angle = i * 72.0;
94 const double latOffset = 0.001 * i;
95 const double lonOffset = 0.001 * (i % 2 == 0 ? i : -i);
96 vehicle.coordinate = QGeoCoordinate(_defaultVehicleLatitude + latOffset, _defaultVehicleLongitude + lonOffset);
99 vehicle.altitude = _defaultVehicleHomeAltitude + (i * 5);
101 _adsbVehicles.append(vehicle);
107 _runningTime.start();
109 _workerThread =
new QThread(
this);
110 _workerThread->setObjectName(QStringLiteral(
"Mock_%1").arg(_mockConfig->name()));
112 _worker->moveToThread(_workerThread);
114 (void) connect(_workerThread, &QThread::finished, _worker, &QObject::deleteLater);
115 _workerThread->start();
122 delete _mockLinkCamera;
123 delete _mockLinkGimbal;
125 if (!_logDownloadFilename.isEmpty()) {
126 QFile::remove(_logDownloadFilename);
130 _workerThread->quit();
131 _workerThread->wait();
134 qCDebug(MockLinkLog) <<
this;
137bool MockLink::_connect()
141 _disconnectedEmitted =
false;
143 mavlinkStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
145 auxStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
160 if (_workerThread && _workerThread->isRunning()) {
161 _workerThread->quit();
162 _workerThread->wait();
167 if (!_disconnectedEmitted.exchange(
true)) {
185 _sendBatteryStatus();
188 if (_vehicleType != MAV_TYPE_SUBMARINE) {
189 _sendRemoteIDArmStatus();
191 _sendAvailableModesMonitor();
201 if (!
qgcApp()->runningUnitTests()) {
206 if (_sendHomePositionDelayCount > 0) {
208 _sendHomePositionDelayCount--;
212 if (_availableModesMonitorSeqNumber == 0) {
213 qCDebug(MockLinkLog) <<
"Bumping sequence number for available modes monitor to trigger requery of modes";
214 _availableModesMonitorSeqNumber = 1;
227 if (_sendGPSPositionDelayCount > 0) {
229 _sendGPSPositionDelayCount--;
231 if (_vehicleType != MAV_TYPE_SUBMARINE) {
233 _sendGlobalPositionInt();
235 _sendExtendedSysState();
251 _paramRequestListWorker();
252 _logDownloadWorker();
253 _availableModesWorker();
259 _sendStatusTextMessages();
262bool MockLink::_allocateMavlinkChannel()
265 Q_ASSERT(!_mavlinkAuxChannelIsSet());
269 qCWarning(MockLinkLog) <<
"LinkInterface::_allocateMavlinkChannel failed";
273 _mavlinkAuxChannel = LinkManager::instance()->allocateMavlinkChannel();
274 if (!_mavlinkAuxChannelIsSet()) {
275 qCWarning(MockLinkLog) <<
"_allocateMavlinkChannel failed";
280 qCDebug(MockLinkLog) <<
"_allocateMavlinkChannel" << _mavlinkAuxChannel;
284void MockLink::_freeMavlinkChannel()
286 qCDebug(MockLinkLog) <<
"_freeMavlinkChannel" << _mavlinkAuxChannel;
287 if (!_mavlinkAuxChannelIsSet()) {
292 LinkManager::instance()->freeMavlinkChannel(_mavlinkAuxChannel);
296bool MockLink::_mavlinkAuxChannelIsSet()
const
298 return (LinkManager::invalidMavlinkChannel() != _mavlinkAuxChannel);
301void MockLink::_loadParams()
304 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
305 if (_vehicleType == MAV_TYPE_FIXED_WING) {
306 paramFile.setFileName(
":/FirmwarePlugin/APM/Plane.OfflineEditing.params");
307 }
else if (_vehicleType == MAV_TYPE_SUBMARINE ) {
308 paramFile.setFileName(
":/FirmwarePlugin/APM/Sub.OfflineEditing.params");
309 }
else if (_vehicleType == MAV_TYPE_GROUND_ROVER ) {
310 paramFile.setFileName(
":/FirmwarePlugin/APM/Rover.OfflineEditing.params");
312 paramFile.setFileName(
":/FirmwarePlugin/APM/Copter.OfflineEditing.params");
315 paramFile.setFileName(
":/MockLink/PX4MockLink.params");
318 const bool success = paramFile.open(QFile::ReadOnly);
322 QTextStream paramStream(¶mFile);
323 while (!paramStream.atEnd()) {
324 const QString line = paramStream.readLine();
326 if (line.startsWith(
"#")) {
330 const QStringList paramData = line.split(
"\t");
331 Q_ASSERT(paramData.count() == 5);
333 const int compId = paramData.at(1).toInt();
334 const QString paramName = paramData.at(2);
335 const QString valStr = paramData.at(3);
336 const uint paramType = paramData.at(4).toUInt();
340 case MAV_PARAM_TYPE_REAL32:
341 paramValue = QVariant(valStr.toFloat());
343 case MAV_PARAM_TYPE_UINT32:
344 paramValue = QVariant(valStr.toUInt());
346 case MAV_PARAM_TYPE_INT32:
347 paramValue = QVariant(valStr.toInt());
349 case MAV_PARAM_TYPE_UINT16:
350 paramValue = QVariant((quint16)valStr.toUInt());
352 case MAV_PARAM_TYPE_INT16:
353 paramValue = QVariant((qint16)valStr.toInt());
355 case MAV_PARAM_TYPE_UINT8:
356 paramValue = QVariant((quint8)valStr.toUInt());
358 case MAV_PARAM_TYPE_INT8:
359 paramValue = QVariant((qint8)valStr.toUInt());
362 qCCritical(MockLinkVerboseLog) <<
"Unknown type" << paramType;
363 paramValue = QVariant(valStr.toInt());
367 qCDebug(MockLinkVerboseLog) <<
"Loading param" << paramName << paramValue;
369 _mapParamName2Value[compId][paramName] = paramValue;
370 _mapParamName2MavParamType[compId][paramName] =
static_cast<MAV_PARAM_TYPE
>(paramType);
374void MockLink::_sendHeartBeat()
377 (void) mavlink_msg_heartbeat_pack_chan(
391void MockLink::_sendHighLatency2()
393 qCDebug(MockLinkLog) <<
"Sending" << _mavCustomMode;
396 px4_cm.
data = _mavCustomMode;
399 (void) mavlink_msg_high_latency2_pack_chan(
407 px4_cm.custom_mode_hl,
408 static_cast<int32_t
>(_vehicleLatitude * 1E7),
409 static_cast<int32_t
>(_vehicleLongitude * 1E7),
410 static_cast<int16_t
>(_vehicleAltitudeAMSL),
411 static_cast<int16_t
>(_vehicleAltitudeAMSL),
433void MockLink::_sendSysStatus()
436 (void) mavlink_msg_sys_status_pack_chan(
441 MAV_SYS_STATUS_SENSOR_GPS,
447 _battery1PctRemaining,
453void MockLink::_sendBatteryStatus()
455 if (_battery1PctRemaining > 1) {
456 _battery1PctRemaining =
static_cast<int8_t
>(100 - (_runningTime.elapsed() / 1000));
457 _battery1TimeRemaining =
static_cast<double>(_batteryMaxTimeRemaining) * (
static_cast<double>(_battery1PctRemaining) / 100.0);
458 if (_battery1PctRemaining > 50) {
459 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_OK;
460 }
else if (_battery1PctRemaining > 30) {
461 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_LOW;
462 }
else if (_battery1PctRemaining > 20) {
463 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_CRITICAL;
465 _battery1ChargeState = MAV_BATTERY_CHARGE_STATE_EMERGENCY;
469 if (_battery2PctRemaining > 1) {
470 _battery2PctRemaining =
static_cast<int8_t
>(100 - ((_runningTime.elapsed() / 1000) / 2));
471 _battery2TimeRemaining =
static_cast<double>(_batteryMaxTimeRemaining) * (
static_cast<double>(_battery2PctRemaining) / 100.0);
472 if (_battery2PctRemaining > 50) {
473 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_OK;
474 }
else if (_battery2PctRemaining > 30) {
475 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_LOW;
476 }
else if (_battery2PctRemaining > 20) {
477 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_CRITICAL;
479 _battery2ChargeState = MAV_BATTERY_CHARGE_STATE_EMERGENCY;
484 uint16_t rgVoltages[10]{};
485 uint16_t rgVoltagesNone[10]{};
486 uint16_t rgVoltagesExtNone[4]{};
488 for (
size_t i = 0; i < std::size(rgVoltages); i++) {
489 rgVoltages[i] = UINT16_MAX;
490 rgVoltagesNone[i] = UINT16_MAX;
492 rgVoltages[0] = rgVoltages[1] = rgVoltages[2] = 4200;
494 (void) mavlink_msg_battery_status_pack_chan(
500 MAV_BATTERY_FUNCTION_ALL,
501 MAV_BATTERY_TYPE_LIPO,
507 _battery1PctRemaining,
508 _battery1TimeRemaining,
509 _battery1ChargeState,
516 (void) mavlink_msg_battery_status_pack_chan(
522 MAV_BATTERY_FUNCTION_ALL,
523 MAV_BATTERY_TYPE_LIPO,
529 _battery2PctRemaining,
530 _battery2TimeRemaining,
531 _battery2ChargeState,
539void MockLink::_sendVibration()
542 (void) mavlink_msg_vibration_pack_chan(
561 uint8_t buffer[MAVLINK_MAX_PACKET_LEN]{};
562 const int cBuffer = mavlink_msg_to_send_buffer(buffer, &msg);
563 const QByteArray bytes(
reinterpret_cast<char*
>(buffer), cBuffer);
568void MockLink::_writeBytes(
const QByteArray &bytes)
574void MockLink::_writeBytesQueued(
const QByteArray &bytes)
577 qCDebug(MockLinkLog) <<
"Dropping queued bytes on disconnected/uninitialized mock link";
582 _handleIncomingNSHBytes(bytes.constData(), bytes.length());
586 if (bytes.startsWith(QByteArrayLiteral(
"\r\r\r"))) {
588 _handleIncomingNSHBytes(&bytes.constData()[3], bytes.length() - 3);
591 _handleIncomingMavlinkBytes(
reinterpret_cast<const uint8_t*
>(bytes.constData()), bytes.length());
594void MockLink::_handleIncomingNSHBytes(
const char *bytes,
int cBytes)
599 if ((cBytes == 4) && (bytes[0] ==
'\r') && (bytes[1] ==
'\r') && (bytes[2] ==
'\r')) {
605 qCDebug(MockLinkLog) <<
"NSH:" << bytes;
608 if (strncmp(bytes,
"sh /etc/init.d/rc.usb\n", cBytes) == 0) {
610 _mavlinkStarted =
true;
616void MockLink::_handleIncomingMavlinkBytes(
const uint8_t *bytes,
int cBytes)
619 mavlink_status_t comm{};
621 QMutexLocker lock(&_mavlinkAuxMutex);
622 for (qint64 i = 0; i < cBytes; i++) {
623 const int parsed = mavlink_parse_char(_getMavlinkAuxChannel(), bytes[i], &msg, &comm);
628 _handleIncomingMavlinkMsg(msg);
635 _receivedMavlinkMessageCountMap[msg.msgid]++;
638 if (msg.msgid == MAVLINK_MSG_ID_COMMAND_LONG) {
639 mavlink_command_long_t request{};
640 mavlink_msg_command_long_decode(&msg, &request);
642 _receivedMavCommandCountMap[
static_cast<MAV_CMD
>(request.command)]++;
643 _receivedMavCommandByCompCountMap[
static_cast<MAV_CMD
>(request.command)][request.target_component]++;
645 if (request.command == MAV_CMD_REQUEST_MESSAGE) {
646 _receivedRequestMessageCountMap[
static_cast<uint32_t
>(request.param1)]++;
647 _receivedRequestMessageByCompAndMsgCountMap[request.target_component][
static_cast<int>(request.param1)]++;
654 _updateIncomingMessageCounts(msg);
669 case MAVLINK_MSG_ID_HEARTBEAT:
670 _handleHeartBeat(msg);
672 case MAVLINK_MSG_ID_PARAM_REQUEST_LIST:
673 _handleParamRequestList(msg);
675 case MAVLINK_MSG_ID_SET_MODE:
678 case MAVLINK_MSG_ID_PARAM_SET:
679 _handleParamSet(msg);
681 case MAVLINK_MSG_ID_PARAM_REQUEST_READ:
682 _handleParamRequestRead(msg);
684 case MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL:
687 case MAVLINK_MSG_ID_COMMAND_LONG:
688 _handleCommandLong(msg);
690 case MAVLINK_MSG_ID_MANUAL_CONTROL:
691 _handleManualControl(msg);
693 case MAVLINK_MSG_ID_LOG_REQUEST_LIST:
694 _handleLogRequestList(msg);
696 case MAVLINK_MSG_ID_LOG_REQUEST_DATA:
697 _handleLogRequestData(msg);
699 case MAVLINK_MSG_ID_PARAM_MAP_RC:
700 _handleParamMapRC(msg);
710 qCDebug(MockLinkLog) <<
"Heartbeat";
715 mavlink_param_map_rc_t paramMapRC{};
716 mavlink_msg_param_map_rc_decode(&msg, ¶mMapRC);
718 const QString paramName(QString::fromLocal8Bit(paramMapRC.param_id,
static_cast<int>(strnlen(paramMapRC.param_id, MAVLINK_MSG_PARAM_MAP_RC_FIELD_PARAM_ID_LEN))));
720 if (paramMapRC.param_index == -1) {
721 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);
722 }
else if (paramMapRC.param_index == -2) {
723 qCDebug(MockLinkLog) <<
"MockLink - PARAM_MAP_RC: Clear tuningID" << paramMapRC.parameter_rc_channel_index;
725 qCWarning(MockLinkLog) <<
"MockLink - PARAM_MAP_RC: Unsupported param_index" << paramMapRC.param_index;
731 mavlink_set_mode_t request{};
732 mavlink_msg_set_mode_decode(&msg, &request);
734 Q_ASSERT(request.target_system == _vehicleSystemId);
736 _mavBaseMode = request.base_mode;
737 _mavCustomMode = request.custom_mode;
742 mavlink_manual_control_t manualControl{};
743 mavlink_msg_manual_control_decode(&msg, &manualControl);
745 qCDebug(MockLinkLog) <<
"MANUAL_CONTROL" << manualControl.x << manualControl.y << manualControl.z << manualControl.r;
748void MockLink::_setParamFloatUnionIntoMap(
int componentId,
const QString ¶mName,
float paramFloat)
750 Q_ASSERT(_mapParamName2Value.contains(componentId));
751 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
752 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
754 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
755 QVariant paramVariant;
756 mavlink_param_union_t valueUnion{};
757 valueUnion.param_float = paramFloat;
759 case MAV_PARAM_TYPE_REAL32:
760 paramVariant = QVariant::fromValue(valueUnion.param_float);
762 case MAV_PARAM_TYPE_UINT32:
763 paramVariant = QVariant::fromValue(valueUnion.param_uint32);
765 case MAV_PARAM_TYPE_INT32:
766 paramVariant = QVariant::fromValue(valueUnion.param_int32);
768 case MAV_PARAM_TYPE_UINT16:
769 paramVariant = QVariant::fromValue(valueUnion.param_uint16);
771 case MAV_PARAM_TYPE_INT16:
772 paramVariant = QVariant::fromValue(valueUnion.param_int16);
774 case MAV_PARAM_TYPE_UINT8:
775 paramVariant = QVariant::fromValue(valueUnion.param_uint8);
777 case MAV_PARAM_TYPE_INT8:
778 paramVariant = QVariant::fromValue(valueUnion.param_int8);
781 qCCritical(MockLinkLog) <<
"Invalid parameter type" << paramType;
782 paramVariant = QVariant::fromValue(valueUnion.param_int32);
786 qCDebug(MockLinkLog) <<
"_setParamFloatUnionIntoMap" << paramName << paramVariant;
787 _mapParamName2Value[componentId][paramName] = paramVariant;
792 mavlink_param_union_t valueUnion{};
793 valueUnion.param_float = value;
794 _setParamFloatUnionIntoMap(componentId, paramName, valueUnion.param_float);
797float MockLink::_floatUnionForParam(
int componentId,
const QString ¶mName)
799 Q_ASSERT(_mapParamName2Value.contains(componentId));
800 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
801 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
803 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
804 const QVariant paramVar = _mapParamName2Value[componentId][paramName];
806 mavlink_param_union_t valueUnion{};
808 case MAV_PARAM_TYPE_REAL32:
809 valueUnion.param_float = paramVar.toFloat();
811 case MAV_PARAM_TYPE_UINT32:
812 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
813 valueUnion.param_float = paramVar.toUInt();
815 valueUnion.param_uint32 = paramVar.toUInt();
818 case MAV_PARAM_TYPE_INT32:
819 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
820 valueUnion.param_float = paramVar.toInt();
822 valueUnion.param_int32 = paramVar.toInt();
825 case MAV_PARAM_TYPE_UINT16:
826 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
827 valueUnion.param_float = paramVar.toUInt();
829 valueUnion.param_uint16 = paramVar.toUInt();
832 case MAV_PARAM_TYPE_INT16:
833 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
834 valueUnion.param_float = paramVar.toInt();
836 valueUnion.param_int16 = paramVar.toInt();
839 case MAV_PARAM_TYPE_UINT8:
840 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
841 valueUnion.param_float = paramVar.toUInt();
843 valueUnion.param_uint8 = paramVar.toUInt();
846 case MAV_PARAM_TYPE_INT8:
847 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
848 valueUnion.param_float = (
unsigned char)paramVar.toChar().toLatin1();
850 valueUnion.param_int8 = (
unsigned char)paramVar.toChar().toLatin1();
854 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
855 valueUnion.param_float = paramVar.toInt();
857 valueUnion.param_int32 = paramVar.toInt();
859 qCCritical(MockLinkLog) <<
"Invalid parameter type" << paramType;
862 return valueUnion.param_float;
865uint32_t MockLink::_computeParamHash(
int componentId)
const
868 static const QStringList volatileParams = {
869 QStringLiteral(
"COM_FLIGHT_UUID"),
870 QStringLiteral(
"EKF2_MAGBIAS_X"),
871 QStringLiteral(
"EKF2_MAGBIAS_Y"),
872 QStringLiteral(
"EKF2_MAGBIAS_Z"),
873 QStringLiteral(
"EKF2_MAG_DECL"),
874 QStringLiteral(
"LND_FLIGHT_T_HI"),
875 QStringLiteral(
"LND_FLIGHT_T_LO"),
876 QStringLiteral(
"SYS_RESTART_TYPE"),
880 const auto ¶ms = _mapParamName2Value[componentId];
881 for (
auto it = params.constBegin(); it != params.constEnd(); ++it) {
882 const QString &name = it.key();
883 if (volatileParams.contains(name)) {
886 const QVariant &value = it.value();
887 const MAV_PARAM_TYPE mavType = _mapParamName2MavParamType[componentId][name];
890 crc =
QGC::crc32(
reinterpret_cast<const uint8_t *
>(qPrintable(name)), name.length(), crc);
898 if (_failureMode == MockConfiguration::FailParamNoResponseToRequestList) {
902 mavlink_param_request_list_t request{};
903 mavlink_msg_param_request_list_decode(&msg, &request);
905 Q_ASSERT(request.target_system == _vehicleSystemId);
906 Q_ASSERT(request.target_component == MAV_COMP_ID_ALL);
910 QMutexLocker locker(&_paramRequestListMutex);
911 _paramRequestListComponentIds = _mapParamName2Value.keys();
912 if (!_paramRequestListComponentIds.isEmpty()) {
913 _paramRequestListParamNames = _mapParamName2Value[_paramRequestListComponentIds.first()].keys();
917 _currentParamRequestListComponentIndex = 0;
918 _currentParamRequestListParamIndex = 0;
919 _paramRequestListHashCheckSent =
false;
922void MockLink::_paramRequestListWorker()
924 if (_currentParamRequestListComponentIndex == -1) {
930 QMutexLocker locker(&_paramRequestListMutex);
933 if (_currentParamRequestListComponentIndex >= _paramRequestListComponentIds.count()) {
934 _currentParamRequestListComponentIndex = -1;
938 const int componentId = _paramRequestListComponentIds.at(_currentParamRequestListComponentIndex);
939 const int cParameters = _paramRequestListParamNames.count();
941 if (_currentParamRequestListParamIndex >= cParameters) {
945 if (_firmwareType == MAV_AUTOPILOT_PX4 && !_paramRequestListHashCheckSent) {
946 _paramRequestListHashCheckSent =
true;
948 mavlink_param_union_t valueUnion{};
949 valueUnion.type = MAV_PARAM_TYPE_UINT32;
950 valueUnion.param_uint32 = _computeParamHash(componentId);
952 char paramId[MAVLINK_MSG_ID_PARAM_VALUE_LEN]{};
953 (void) strncpy(paramId,
"_HASH_CHECK", MAVLINK_MSG_ID_PARAM_VALUE_LEN);
955 qCDebug(MockLinkLog) <<
"Sending _HASH_CHECK in PARAM_REQUEST_LIST stream" << componentId <<
"hash:" << valueUnion.param_uint32;
958 (void) mavlink_msg_param_value_pack_chan(
964 valueUnion.param_float,
965 MAV_PARAM_TYPE_UINT32,
974 if (++_currentParamRequestListComponentIndex >= _paramRequestListComponentIds.count()) {
975 _currentParamRequestListComponentIndex = -1;
976 _paramRequestListComponentIds.clear();
977 _paramRequestListParamNames.clear();
980 _paramRequestListParamNames = _mapParamName2Value[_paramRequestListComponentIds.at(_currentParamRequestListComponentIndex)].keys();
981 _currentParamRequestListParamIndex = 0;
982 _paramRequestListHashCheckSent =
false;
987 const QString ¶mName = _paramRequestListParamNames.at(_currentParamRequestListParamIndex);
989 if (((_failureMode == MockConfiguration::FailMissingParamOnInitialRequest) || (_failureMode == MockConfiguration::FailMissingParamOnAllRequests)) && (paramName == _failParam)) {
990 qCDebug(MockLinkLog) <<
"Skipping param send:" << paramName;
992 char paramId[MAVLINK_MSG_ID_PARAM_VALUE_LEN]{};
995 Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
996 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
998 const MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
1000 Q_ASSERT(paramName.length() <= MAVLINK_MSG_ID_PARAM_VALUE_LEN);
1001 (void) strncpy(paramId, paramName.toLocal8Bit().constData(), MAVLINK_MSG_ID_PARAM_VALUE_LEN);
1003 qCDebug(MockLinkLog) <<
"Sending msg_param_value" << componentId << paramId << paramType << _mapParamName2Value[componentId][paramId];
1005 (void) mavlink_msg_param_value_pack_chan(
1011 _floatUnionForParam(componentId, paramName),
1014 _currentParamRequestListParamIndex
1020 ++_currentParamRequestListParamIndex;
1025 mavlink_param_set_t request{};
1026 mavlink_msg_param_set_decode(&msg, &request);
1028 Q_ASSERT(request.target_system == _vehicleSystemId);
1029 const int componentId = request.target_component;
1032 char paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN + 1]{};
1033 paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN] = 0;
1034 (void) strncpy(paramId, request.param_id, MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN);
1036 qCDebug(MockLinkLog) <<
"_handleParamSet" << componentId << paramId << request.param_type;
1041 if ((_firmwareType == MAV_AUTOPILOT_PX4) && (strncmp(paramId,
"_HASH_CHECK", MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN) == 0)) {
1042 QMutexLocker locker(&_paramRequestListMutex);
1043 _currentParamRequestListComponentIndex = -1;
1044 _paramRequestListComponentIds.clear();
1045 _paramRequestListParamNames.clear();
1046 qCDebug(MockLinkLog) <<
"Received _HASH_CHECK PARAM_SET, stopping parameter stream";
1050 Q_ASSERT(_mapParamName2Value.contains(componentId));
1051 Q_ASSERT(_mapParamName2MavParamType.contains(componentId));
1052 Q_ASSERT(_mapParamName2Value[componentId].contains(paramId));
1053 Q_ASSERT(request.param_type == _mapParamName2MavParamType[componentId][paramId]);
1057 qCDebug(MockLinkLog) <<
"Param set failure: first attempt no ack" << paramId;
1058 _paramSetFailureFirstAttemptPending =
false;
1063 qCDebug(MockLinkLog) <<
"Param set failure: no ack" << paramId;
1068 _setParamFloatUnionIntoMap(componentId, paramId, request.param_value);
1071 mavlink_msg_param_value_pack_chan(
1077 request.param_value,
1079 _mapParamName2Value[componentId].count(),
1080 _mapParamName2Value[componentId].keys().indexOf(paramId)
1088 mavlink_param_request_read_t request{};
1089 mavlink_msg_param_request_read_decode(&msg, &request);
1091 const QString paramName(QString::fromLocal8Bit(request.param_id,
static_cast<int>(strnlen(request.param_id, MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN))));
1092 const int componentId = request.target_component;
1095 if ((_firmwareType == MAV_AUTOPILOT_PX4) && (paramName ==
"_HASH_CHECK")) {
1096 _hashCheckRequestCount++;
1097 if (_hashCheckNoResponse) {
1101 const int hashComponentId = _mapParamName2Value.contains(MAV_COMP_ID_AUTOPILOT1)
1102 ? MAV_COMP_ID_AUTOPILOT1
1103 : _mapParamName2Value.keys().first();
1105 mavlink_param_union_t valueUnion{};
1106 valueUnion.type = MAV_PARAM_TYPE_UINT32;
1107 valueUnion.param_uint32 = _computeParamHash(hashComponentId);
1108 (void) mavlink_msg_param_value_pack_chan(
1114 valueUnion.param_float,
1115 MAV_PARAM_TYPE_UINT32,
1123 Q_ASSERT(_mapParamName2Value.contains(componentId));
1125 char paramId[MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN + 1]{};
1128 Q_ASSERT(request.target_system == _vehicleSystemId);
1130 if (request.param_index == -1) {
1132 (void) strncpy(paramId, request.param_id, MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
1135 Q_ASSERT(request.param_index >= 0 && request.param_index < _mapParamName2Value[componentId].count());
1137 const QString key = _mapParamName2Value[componentId].keys().at(request.param_index);
1138 Q_ASSERT(key.length() <= MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
1139 strcpy(paramId, key.toLocal8Bit().constData());
1142 Q_ASSERT(_mapParamName2Value[componentId].contains(paramId));
1143 Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramId));
1145 if ((_failureMode == MockConfiguration::FailMissingParamOnAllRequests) && (strcmp(paramId, _failParam) == 0)) {
1146 qCDebug(MockLinkLog) <<
"Ignoring request read for " << _failParam;
1152 qCDebug(MockLinkLog) <<
"Param request read failure: first attempt no response" << paramId;
1153 _paramRequestReadFailureFirstAttemptPending =
false;
1158 qCDebug(MockLinkLog) <<
"Param request read failure: no response" << paramId;
1162 (void) mavlink_msg_param_value_pack_chan(
1168 _floatUnionForParam(componentId, paramId),
1169 _mapParamName2MavParamType[componentId][paramId],
1170 _mapParamName2Value[componentId].count(),
1171 _mapParamName2Value[componentId].keys().indexOf(paramId)
1181void MockLink::_handleInProgressCommandLong(
const mavlink_command_long_t &request)
1183 uint8_t commandResult = MAV_RESULT_UNSUPPORTED;
1185 switch (request.command) {
1188 commandResult = MAV_RESULT_ACCEPTED;
1192 commandResult = MAV_RESULT_FAILED;
1200 (void) mavlink_msg_command_ack_pack_chan(
1202 _vehicleComponentId,
1206 MAV_RESULT_IN_PROGRESS,
1215 (void) mavlink_msg_command_ack_pack_chan(
1217 _vehicleComponentId,
1231void MockLink::_handleCommandLongSetMessageInterval(
const mavlink_command_long_t &request,
bool &accepted)
1239 static bool firstCmdUser3 =
true;
1240 static bool firstCmdUser4 =
true;
1242 mavlink_command_long_t request{};
1243 mavlink_msg_command_long_decode(&msg, &request);
1245 uint8_t commandResult = MAV_RESULT_UNSUPPORTED;
1247 switch (request.command) {
1248 case MAV_CMD_COMPONENT_ARM_DISARM:
1249 if (request.param1 == 0.0f) {
1250 _mavBaseMode &= ~MAV_MODE_FLAG_SAFETY_ARMED;
1252 _mavBaseMode |= MAV_MODE_FLAG_SAFETY_ARMED;
1254 commandResult = MAV_RESULT_ACCEPTED;
1256 case MAV_CMD_PREFLIGHT_CALIBRATION:
1257 _handlePreFlightCalibration(request);
1258 commandResult = MAV_RESULT_ACCEPTED;
1260 case MAV_CMD_DO_MOTOR_TEST:
1261 commandResult = MAV_RESULT_ACCEPTED;
1263 case MAV_CMD_CONTROL_HIGH_LATENCY:
1265 _highLatencyTransmissionEnabled =
static_cast<int>(request.param1) != 0;
1267 commandResult = MAV_RESULT_ACCEPTED;
1269 commandResult = MAV_RESULT_FAILED;
1272 case MAV_CMD_PREFLIGHT_STORAGE:
1273 commandResult = MAV_RESULT_ACCEPTED;
1275 case MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES:
1276 commandResult = MAV_RESULT_ACCEPTED;
1277 _respondWithAutopilotVersion();
1279 case MAV_CMD_REQUEST_MESSAGE:
1281 bool accepted =
false;
1283 _handleRequestMessage(request, accepted, noAck);
1289 commandResult = MAV_RESULT_ACCEPTED;
1293 case MAV_CMD_NAV_TAKEOFF:
1294 _handleTakeoff(request);
1295 commandResult = MAV_RESULT_ACCEPTED;
1299 commandResult = MAV_RESULT_ACCEPTED;
1303 commandResult = MAV_RESULT_FAILED;
1307 if (firstCmdUser3) {
1308 firstCmdUser3 =
false;
1311 firstCmdUser3 =
true;
1312 commandResult = MAV_RESULT_ACCEPTED;
1317 if (firstCmdUser4) {
1318 firstCmdUser4 =
false;
1321 firstCmdUser4 =
true;
1322 commandResult = MAV_RESULT_FAILED;
1332 _handleInProgressCommandLong(request);
1334 case MAV_CMD_SET_MESSAGE_INTERVAL:
1336 bool accepted =
false;
1338 _handleCommandLongSetMessageInterval(request, accepted);
1340 commandResult = MAV_RESULT_ACCEPTED;
1347 (void) mavlink_msg_command_ack_pack_chan(
1349 _vehicleComponentId,
1365 (void) mavlink_msg_command_ack_pack_chan(
1367 _vehicleComponentId,
1380void MockLink::_respondWithAutopilotVersion()
1382 union FlightVersion {
1392 FlightVersion(uint32_t version = 0) : raw(version) {}
1394 FlightVersion flightVersion;
1396#ifndef QGC_NO_ARDUPILOT_DIALECT
1397 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
1398 if (_vehicleType == MAV_TYPE_SUBMARINE ) {
1399 flightVersion.parts.major = 4;
1400 flightVersion.parts.minor = 5;
1401 flightVersion.parts.patch = 7;
1403 flightVersion.parts.major = 4;
1404 flightVersion.parts.minor = 6;
1405 flightVersion.parts.patch = 3;
1407 flightVersion.parts.type = FIRMWARE_VERSION_TYPE_OFFICIAL;
1408 }
else if (_firmwareType == MAV_AUTOPILOT_PX4) {
1410 flightVersion.parts.major = 1;
1411 flightVersion.parts.minor = 4;
1412 flightVersion.parts.patch = 1;
1413 flightVersion.parts.type = FIRMWARE_VERSION_TYPE_DEV;
1414#ifndef QGC_NO_ARDUPILOT_DIALECT
1418 const uint8_t customVersion[8]{};
1419 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);
1422 (void) mavlink_msg_autopilot_version_pack_chan(
1424 _vehicleComponentId,
1432 reinterpret_cast<const uint8_t*
>(&customVersion),
1433 reinterpret_cast<const uint8_t*
>(&customVersion),
1434 reinterpret_cast<const uint8_t*
>(&customVersion),
1443void MockLink::_sendHomePosition()
1445 const float bogus[4]{};
1448 (void) mavlink_msg_home_position_pack_chan(
1450 _vehicleComponentId,
1453 static_cast<int32_t
>(_vehicleLatitude * 1E7),
1454 static_cast<int32_t
>(_vehicleLongitude * 1E7),
1455 static_cast<int32_t
>(_defaultVehicleHomeAltitude * 1000),
1464void MockLink::_sendGpsRawInt()
1466 static uint64_t timeTick = 0;
1469 (void) mavlink_msg_gps_raw_int_pack_chan(
1471 _vehicleComponentId,
1475 GPS_FIX_TYPE_3D_FIX,
1476 static_cast<int32_t
>(_vehicleLatitude * 1E7),
1477 static_cast<int32_t
>(_vehicleLongitude * 1E7),
1478 static_cast<int32_t
>(_vehicleAltitudeAMSL * 1000),
1495void MockLink::_sendGlobalPositionInt()
1497 static uint64_t timeTick = 0;
1500 (void) mavlink_msg_global_position_int_pack_chan(
1502 _vehicleComponentId,
1506 static_cast<int32_t
>(_vehicleLatitude * 1E7),
1507 static_cast<int32_t
>(_vehicleLongitude * 1E7),
1508 static_cast<int32_t
>(_vehicleAltitudeAMSL * 1000),
1509 static_cast<int32_t
>((_vehicleAltitudeAMSL - _defaultVehicleHomeAltitude) * 1000),
1516void MockLink::_sendExtendedSysState()
1519 (void) mavlink_msg_extended_sys_state_pack_chan(
1521 _vehicleComponentId,
1524 MAV_VTOL_STATE_UNDEFINED,
1525 (_vehicleAltitudeAMSL > _defaultVehicleHomeAltitude) ? MAV_LANDED_STATE_IN_AIR : MAV_LANDED_STATE_ON_GROUND
1530void MockLink::_sendChunkedStatusText(uint16_t chunkId,
bool missingChunks)
1532 constexpr int cChunks = 4;
1535 for (
int i = 0; i < cChunks; i++) {
1536 if (missingChunks && (i & 1)) {
1540 int cBuf = MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN;
1541 char msgBuf[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN]{};
1543 if (i == cChunks - 1) {
1548 for (
int j = 0; j < cBuf - 1; j++) {
1549 msgBuf[j] =
'0' + num++;
1554 msgBuf[cBuf-1] =
'A' + i;
1557 (void) mavlink_msg_statustext_pack_chan(
1559 _vehicleComponentId,
1571void MockLink::_sendStatusTextMessages()
1573 struct StatusMessage {
1574 MAV_SEVERITY severity;
1578 static constexpr struct StatusMessage rgMessages[] = {
1579 { MAV_SEVERITY_INFO,
"#Testing audio output" },
1580 { MAV_SEVERITY_EMERGENCY,
"Status text emergency" },
1581 { MAV_SEVERITY_ALERT,
"Status text alert" },
1582 { MAV_SEVERITY_CRITICAL,
"Status text critical" },
1583 { MAV_SEVERITY_ERROR,
"Status text error" },
1584 { MAV_SEVERITY_WARNING,
"Status text warning" },
1585 { MAV_SEVERITY_NOTICE,
"Status text notice" },
1586 { MAV_SEVERITY_INFO,
"Status text info" },
1587 { MAV_SEVERITY_DEBUG,
"Status text debug" },
1591 for (
size_t i = 0; i < std::size(rgMessages); i++) {
1592 const struct StatusMessage *status = &rgMessages[i];
1593 char statusText[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN] = {};
1594 (void) std::strncpy(statusText, status->msg,
sizeof(statusText) - 1);
1596 (void) mavlink_msg_statustext_pack_chan(
1598 _vehicleComponentId,
1609 _sendChunkedStatusText(1,
false );
1610 _sendChunkedStatusText(2,
true );
1611 _sendChunkedStatusText(3,
false );
1612 _sendChunkedStatusText(4,
true );
1617 mockConfig->setDynamic(
true);
1620 if (LinkManager::instance()->createConnectedLink(config)) {
1621 return qobject_cast<MockLink*>(config->link());
1627MockLink *MockLink::_startMockLinkWorker(
const QString &configName, MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType,
bool sendStatusText,
bool enableCamera,
bool enableGimbal, MockConfiguration::FailureMode_t failureMode)
1631 mockConfig->setFirmwareType(firmwareType);
1632 mockConfig->setVehicleType(vehicleType);
1633 mockConfig->setSendStatusText(sendStatusText);
1634 mockConfig->setEnableCamera(enableCamera);
1635 mockConfig->setEnableGimbal(enableGimbal);
1636 mockConfig->setFailureMode(failureMode);
1638 return _startMockLink(mockConfig);
1643 return _startMockLinkWorker(QStringLiteral(
"PX4 MultiRotor MockLink"), MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, sendStatusText, enableCamera, enableGimbal, failureMode);
1648 return _startMockLinkWorker(QStringLiteral(
"Generic MockLink"), MAV_AUTOPILOT_GENERIC, MAV_TYPE_QUADROTOR, sendStatusText, enableCamera, enableGimbal, failureMode);
1653 return _startMockLinkWorker(QStringLiteral(
"No Initial Connect MockLink"), MAV_AUTOPILOT_PX4, MAV_TYPE_GENERIC, sendStatusText, enableCamera, enableGimbal, failureMode);
1658 return _startMockLinkWorker(QStringLiteral(
"ArduCopter MockLink"),MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_QUADROTOR, sendStatusText, enableCamera, enableGimbal, failureMode);
1663 return _startMockLinkWorker(QStringLiteral(
"ArduPlane MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_FIXED_WING, sendStatusText, enableCamera, enableGimbal, failureMode);
1668 return _startMockLinkWorker(QStringLiteral(
"ArduSub MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_SUBMARINE, sendStatusText, enableCamera, enableGimbal, failureMode);
1673 return _startMockLinkWorker(QStringLiteral(
"ArduRover MockLink"), MAV_AUTOPILOT_ARDUPILOTMEGA, MAV_TYPE_GROUND_ROVER, sendStatusText, enableCamera, enableGimbal, failureMode);
1676void MockLink::_sendRCChannels()
1679 (void) mavlink_msg_rc_channels_pack_chan(
1681 _vehicleComponentId,
1686 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500,
1687 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500,
1688 UINT16_MAX, UINT16_MAX,
1694void MockLink::_handlePreFlightCalibration(
const mavlink_command_long_t& request)
1696 static constexpr const char *gyroCalResponse =
"[cal] calibration started: 2 gyro";
1697 static constexpr const char *magCalResponse =
"[cal] calibration started: 2 mag";
1698 static constexpr const char *accelCalResponse =
"[cal] calibration started: 2 accel";
1699 const char *pCalMessage;
1701 if (request.param1 == 1) {
1702 pCalMessage = gyroCalResponse;
1703 }
else if (request.param2 == 1) {
1704 pCalMessage = magCalResponse;
1705 }
else if (request.param5 == 1) {
1706 pCalMessage = accelCalResponse;
1711 char statusText[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN] = {};
1712 (void) std::strncpy(statusText, pCalMessage,
sizeof(statusText) - 1);
1715 (void) mavlink_msg_statustext_pack_chan(
1717 _vehicleComponentId,
1728void MockLink::_handleTakeoff(
const mavlink_command_long_t &request)
1730 _vehicleAltitudeAMSL = request.param7 + _defaultVehicleHomeAltitude;
1731 _mavBaseMode |= MAV_MODE_FLAG_SAFETY_ARMED;
1736 mavlink_log_request_list_t request{};
1737 mavlink_msg_log_request_list_decode(&msg, &request);
1739 if ((request.start != 0) && (request.end != 0xffff)) {
1740 qCWarning(MockLinkLog) <<
"_handleLogRequestList cannot handle partial requests";
1745 mavlink_msg_log_entry_pack_chan(
1747 _vehicleComponentId,
1754 _logDownloadFileSize
1759QString MockLink::_createRandomFile(uint32_t byteCount)
1761 QTemporaryFile tempFile;
1762 tempFile.setAutoRemove(
false);
1763 if (!tempFile.open()) {
1764 qCWarning(MockLinkLog) <<
"MockLink::createRandomFile open failed" << tempFile.errorString();
1768 for (uint32_t bytesWritten = 0; bytesWritten < byteCount; bytesWritten++) {
1769 const unsigned char byte = (QRandomGenerator::global()->generate() * 0xFF) / RAND_MAX;
1770 (void) tempFile.write(
reinterpret_cast<const char*
>(&
byte), 1);
1774 return tempFile.fileName();
1779 mavlink_log_request_data_t request{};
1780 mavlink_msg_log_request_data_decode(&msg, &request);
1782#ifdef QGC_UNITTEST_BUILD
1783 if (_logDownloadFilename.isEmpty()) {
1784 _logDownloadFilename = _createRandomFile(_logDownloadFileSize);
1788 if (request.id != 0) {
1789 qCWarning(MockLinkLog) <<
"_handleLogRequestData id must be 0";
1793 if (request.ofs > (_logDownloadFileSize - 1)) {
1794 qCWarning(MockLinkLog) <<
"_handleLogRequestData offset past end of file request.ofs:size" << request.ofs << _logDownloadFileSize;
1801 QMutexLocker locker(&_logDownloadMutex);
1802 _logDownloadCurrentOffset = request.ofs;
1803 if (request.ofs + request.count > _logDownloadFileSize) {
1804 request.count = _logDownloadFileSize - request.ofs;
1806 _logDownloadBytesRemaining = request.count;
1809void MockLink::_logDownloadWorker()
1813 QMutexLocker locker(&_logDownloadMutex);
1814 if (_logDownloadBytesRemaining == 0) {
1818 QFile file(_logDownloadFilename);
1819 if (!file.open(QIODevice::ReadOnly)) {
1820 qCWarning(MockLinkLog) <<
"_logDownloadWorker open failed" << file.errorString();
1824 uint8_t buffer[MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN]{};
1826 const qint64 bytesToRead = qMin(_logDownloadBytesRemaining, (uint32_t)MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN);
1827 Q_ASSERT(file.seek(_logDownloadCurrentOffset));
1828 Q_ASSERT(file.read(
reinterpret_cast<char*
>(buffer), bytesToRead) == bytesToRead);
1830 qCDebug(MockLinkLog) <<
"_logDownloadWorker" << _logDownloadCurrentOffset << _logDownloadBytesRemaining;
1833 (void) mavlink_msg_log_data_pack_chan(
1835 _vehicleComponentId,
1839 _logDownloadCurrentOffset,
1845 _logDownloadCurrentOffset += bytesToRead;
1846 _logDownloadBytesRemaining -= bytesToRead;
1851void MockLink::_sendADSBVehicles()
1853 for (
int i = 0; i < _adsbVehicles.size(); ++i) {
1855 _adsbVehicles[i].angle += (i + 1);
1858 _adsbVehicles[i].coordinate = _adsbVehicles[i].coordinate.atDistanceAndAzimuth(5, _adsbVehicles[i].angle);
1861 _adsbVehicles[i].altitude += (i % 2 == 0 ? 0.5 : -0.5);
1863 QByteArray callsign = QString(
"N12345%1").arg(i, 2, 10, QChar(
'0')).toLatin1();
1864 callsign.resize(MAVLINK_MSG_ADSB_VEHICLE_FIELD_CALLSIGN_LEN);
1868 (void) mavlink_msg_adsb_vehicle_pack_chan(
1870 _vehicleComponentId,
1874 _adsbVehicles[i].coordinate.latitude() * 1e7,
1875 _adsbVehicles[i].coordinate.longitude() * 1e7,
1876 ADSB_ALTITUDE_TYPE_GEOMETRIC,
1877 _adsbVehicles[i].altitude * 1000,
1879 static_cast<uint16_t
>(_adsbVehicles[i].angle * 100),
1881 callsign.constData(),
1882 ADSB_EMITTER_TYPE_ROTOCRAFT,
1884 ADSB_FLAGS_VALID_COORDS | ADSB_FLAGS_VALID_ALTITUDE | ADSB_FLAGS_VALID_HEADING | ADSB_FLAGS_VALID_CALLSIGN | ADSB_FLAGS_SIMULATED,
1891void MockLink::_moveADSBVehicle(
int vehicleIndex)
1893 _adsbAngles[vehicleIndex] += 10;
1894 QGeoCoordinate &coord = _adsbVehicleCoordinates[vehicleIndex];
1897 coord = QGeoCoordinate(coord.latitude(), coord.longitude()).atDistanceAndAzimuth(500, _adsbAngles[vehicleIndex]);
1898 coord.setAltitude(100);
1901void MockLink::_handleRequestMessageAutopilotVersion(
const mavlink_command_long_t &,
bool &accepted)
1905 switch (_failureMode) {
1906 case MockConfiguration::FailNone:
1908 case MockConfiguration::FailInitialConnectRequestMessageAutopilotVersionFailure:
1911 case MockConfiguration::FailInitialConnectRequestMessageAutopilotVersionLost:
1918 _respondWithAutopilotVersion();
1921void MockLink::_handleRequestMessageDebug(
const mavlink_command_long_t &,
bool &accepted,
bool &noAck)
1926 switch (_requestMessageFailureMode) {
1941 (void) mavlink_msg_debug_pack_chan(
1943 _vehicleComponentId,
1951void MockLink::_handleRequestMessageAvailableModes(
const mavlink_command_long_t &request,
bool &accepted)
1958 QMutexLocker locker(&_availableModesWorkerMutex);
1959 if (request.param2 == 0) {
1961 if (_availableModesWorkerNextModeIndex != 0) {
1962 qCWarning(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: _availableModesWorker already running - _availableModesWorkerNextModeIndex:" << _availableModesWorkerNextModeIndex;
1966 qCDebug(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: starting available modes sequence worker";
1967 _availableModesWorkerNextModeIndex = 1;
1970 if (request.param2 > _availableFlightModes.count()) {
1971 qCWarning(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: requested mode index out of range" << request.param2 << _availableFlightModes.count();
1975 qCDebug(MockLinkLog) <<
"MAVLINK_MSG_ID_AVAILABLE_MODES: received specific mode request for index" << request.param2;
1976 _availableModesWorkerNextModeIndex = -request.param2;
1980void MockLink::_handleRequestMessage(
const mavlink_command_long_t &request,
bool &accepted,
bool &noAck)
1985 const uint32_t requestedMessageId =
static_cast<uint32_t
>(request.param1);
1989 QMutexLocker locker(&_requestMessageNoResponseMutex);
1990 if (_requestMessageNoResponseIds.contains(requestedMessageId)) {
1996 switch (
static_cast<int>(request.param1)) {
1997 case MAVLINK_MSG_ID_AUTOPILOT_VERSION:
1998 _handleRequestMessageAutopilotVersion(request, accepted);
2000 case MAVLINK_MSG_ID_COMPONENT_METADATA:
2001 if (_firmwareType == MAV_AUTOPILOT_PX4) {
2002 _sendGeneralMetaData();
2006 case MAVLINK_MSG_ID_DEBUG:
2007 _handleRequestMessageDebug(request, accepted, noAck);
2009 case MAVLINK_MSG_ID_AVAILABLE_MODES:
2010 _handleRequestMessageAvailableModes(request, accepted);
2015void MockLink::_sendGeneralMetaData()
2017 static constexpr const char metaDataURI[MAVLINK_MSG_COMPONENT_METADATA_FIELD_URI_LEN] =
"mftp://[;comp=1]general.json";
2020 (void) mavlink_msg_component_metadata_pack_chan(
2022 _vehicleComponentId,
2032void MockLink::_sendRemoteIDArmStatus()
2034 char armStatusError[MAVLINK_MSG_OPEN_DRONE_ID_ARM_STATUS_FIELD_ERROR_LEN] = {};
2035 std::strncpy(armStatusError,
"No Error",
sizeof(armStatusError) - 1);
2038 (void) mavlink_msg_open_drone_id_arm_status_pack(
2040 MAV_COMP_ID_ODID_TXRX_1,
2042 MAV_ODID_ARM_STATUS_GOOD_TO_ARM,
2056 return _mockLinkFTP;
2059void MockLink::_sendAvailableMode(uint8_t modeIndexOneBased)
2061 if (modeIndexOneBased > _availableModesCount()) {
2062 qCWarning(MockLinkLog) <<
"modeIndexOneBased out of range" << modeIndexOneBased << _availableModesCount();
2066 qCDebug(MockLinkLog) <<
"_sendAvailableMode modeIndexOneBased:" << modeIndexOneBased;
2068 const FlightMode_t &availableMode = _availableFlightModes[modeIndexOneBased - 1];
2069 char modeName[MAVLINK_MSG_AVAILABLE_MODES_FIELD_MODE_NAME_LEN] = {};
2070 std::strncpy(modeName, availableMode.name,
sizeof(modeName) - 1);
2074 (void) mavlink_msg_available_modes_pack_chan(
2076 _vehicleComponentId,
2079 _availableModesCount(),
2081 availableMode.standard_mode,
2082 availableMode.custom_mode,
2083 availableMode.canBeSet ? 0 : MAV_MODE_PROPERTY_NOT_USER_SELECTABLE,
2088void MockLink::_availableModesWorker()
2092 QMutexLocker locker(&_availableModesWorkerMutex);
2093 if (_availableModesWorkerNextModeIndex == 0) {
2098 _sendAvailableMode(qAbs(_availableModesWorkerNextModeIndex));
2100 if (_availableModesWorkerNextModeIndex < 0) {
2102 _availableModesWorkerNextModeIndex = 0;
2103 }
else if (++_availableModesWorkerNextModeIndex > _availableModesCount()) {
2105 _availableModesWorkerNextModeIndex = 0;
2106 qCDebug(MockLinkLog) <<
"_availableModesWorker: all modes sent, stopping worker";
2110void MockLink::_sendAvailableModesMonitor()
2114 (void) mavlink_msg_available_modes_monitor_pack_chan(
2116 _vehicleComponentId,
2119 _availableModesMonitorSeqNumber);
2123int MockLink::_availableModesCount()
const
2125 return _availableFlightModes.count() - (_availableModesMonitorSeqNumber == 0 ? 1 : 0);
std::shared_ptr< LinkConfiguration > SharedLinkConfigurationPtr
mavlink_status_t * mavlink_get_channel_status(uint8_t chan)
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
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()
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.
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)
@ 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
@ 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)
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.
@ FailParamRequestReadNoResponse
Do not respond to PARAM_REQUEST_READ.
static constexpr MAV_CMD MAV_CMD_MOCKLINK_RESULT_IN_PROGRESS_NO_ACK
static constexpr MAV_CMD MAV_CMD_MOCKLINK_NO_RESPONSE_NO_RETRY
void simulateConnectionRemoved()
static FactMetaData::ValueType_t mavTypeToFactType(MAV_PARAM_TYPE mavType)
quint32 crc32(const quint8 *src, unsigned len, unsigned state)