5#include <QtCore/QRegularExpression>
6#include <QtCore/QSettings>
7#include <QtCore/QStandardPaths>
8#include <QtCore/QThread>
37 qCDebug(FirmwarePluginLog) <<
this;
42 qCDebug(FirmwarePluginLog) <<
this;
52 Q_UNUSED(custom_mode);
55 const uint8_t baseModeBit;
59 static constexpr Bit2Name rgBit2Name[] = {
60 { MAV_MODE_FLAG_MANUAL_INPUT_ENABLED,
"Manual" },
61 { MAV_MODE_FLAG_STABILIZE_ENABLED,
"Stabilize" },
62 { MAV_MODE_FLAG_GUIDED_ENABLED,
"Guided" },
63 { MAV_MODE_FLAG_AUTO_ENABLED,
"Auto" },
64 { MAV_MODE_FLAG_TEST_ENABLED,
"Test" },
70 }
else if (base_mode & MAV_MODE_FLAG_CUSTOM_MODE_ENABLED) {
73 for (
size_t i = 0; i < std::size(rgBit2Name); i++) {
74 if (base_mode & rgBit2Name[i].baseModeBit) {
90 Q_UNUSED(custom_mode);
92 qCWarning(FirmwarePluginLog) <<
"FirmwarePlugin::setFlightMode called on base class, not supported";
99 switch (vehicleClass) {
101 return QStringLiteral(
":/json/MavCmdInfoCommon.json");
103 return QStringLiteral(
":/json/MavCmdInfoFixedWing.json");
105 return QStringLiteral(
":/json/MavCmdInfoMultiRotor.json");
107 return QStringLiteral(
":/json/MavCmdInfoVTOL.json");
109 return QStringLiteral(
":/json/MavCmdInfoSub.json");
111 return QStringLiteral(
":/json/MavCmdInfoRover.json");
113 qCWarning(FirmwarePluginLog) <<
"FirmwarePlugin::missionCommandOverrides called with bad VehicleClass_t:" << vehicleClass;
121 Q_UNUSED(guidedMode);
147 Q_UNUSED(takeoffAltRel);
155 Q_UNUSED(forwardFlightLoiterRadius);
185 _sendROICommand(vehicle, roiCenterCoord, MAV_FRAME_GLOBAL_RELATIVE_ALT,
static_cast<float>(relativeAltitudeMeters));
191 qCDebug(FirmwarePluginLog) <<
"_sendROICommand: lat" << coord.latitude() <<
"lon" << coord.longitude()
192 <<
"frame" << frame <<
"altitude" << altitude;
194 if (vehicle->
capabilityBits() & MAV_PROTOCOL_CAPABILITY_COMMAND_INT) {
197 MAV_CMD_DO_SET_ROI_LOCATION,
200 static_cast<float>(qQNaN()),
201 static_cast<float>(qQNaN()),
202 static_cast<float>(qQNaN()),
203 static_cast<float>(qQNaN()),
210 MAV_CMD_DO_SET_ROI_LOCATION,
212 static_cast<float>(qQNaN()),
213 static_cast<float>(qQNaN()),
214 static_cast<float>(qQNaN()),
215 static_cast<float>(qQNaN()),
216 static_cast<float>(coord.latitude()),
217 static_cast<float>(coord.longitude()),
245 QVariant::fromValue(QUrl::fromUserInput(
"qrc:/qml/QGroundControl/Toolbar/TelemetryRSSIIndicator.qml")),
246 QVariant::fromValue(QUrl::fromUserInput(
"qrc:/qml/QGroundControl/Toolbar/RCRSSIIndicator.qml")),
247 QVariant::fromValue(QUrl::fromUserInput(
"qrc:/qml/QGroundControl/Toolbar/BatteryIndicator.qml")),
248 QVariant::fromValue(QUrl::fromUserInput(
"qrc:/qml/QGroundControl/Toolbar/RemoteIDIndicator.qml")),
249 QVariant::fromValue(QUrl::fromUserInput(
"qrc:/qml/QGroundControl/Toolbar/GimbalIndicator.qml")),
250 QVariant::fromValue(QUrl::fromUserInput(
"qrc:/qml/QGroundControl/Toolbar/EscIndicator.qml")),
251 QVariant::fromValue(QUrl::fromUserInput(
"qrc:/qml/QGroundControl/Toolbar/JoystickIndicator.qml")),
252 QVariant::fromValue(QUrl::fromUserInput(
"qrc:/qml/QGroundControl/Toolbar/MultiVehicleSelector.qml")),
255 QVariant::fromValue(QUrl::fromUserInput(
"qrc:/qml/QGroundControl/Toolbar/GCSControlIndicator.qml")),
265 if (vehicle->
armed()) {
269 bool vehicleArmed =
false;
275 for (
int i = 0; i < 15; i++) {
276 if (vehicle->
armed()) {
280 QThread::msleep(100);
281 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
293 bool flightModeChanged =
false;
296 for (
int retries = 0; retries < 3; retries++) {
300 for (
int i = 0; i < 13; i++) {
302 flightModeChanged =
true;
305 QThread::msleep(100);
306 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
309 if (flightModeChanged) {
314 return flightModeChanged;
329 rollSupported =
false;
330 pitchSupported =
false;
331 yawSupported =
false;
349 qCDebug(FirmwarePluginLog) <<
"Skipping version check";
354 qCDebug(FirmwarePluginLog) <<
"Downloading" << versionFile;
356 (void) connect(downloader, &
QGCFileDownload::finished,
this, [vehicle,
this, versionFile](
bool success,
const QString &localFile,
const QString &errorMsg) {
359 }
else if (!errorMsg.isEmpty()) {
360 qCDebug(FirmwarePluginLog) <<
"Failed to download the latest fw version file. Error:" << errorMsg;
362 sender()->deleteLater();
365 if (!downloader->
start(versionFile)) {
366 downloader->deleteLater();
373 qCDebug(FirmwarePluginLog) <<
"Download complete" << remoteFile << localFile;
375 QFile versionFile(localFile);
376 if (!versionFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
377 qCWarning(FirmwarePluginLog) <<
"Error opening downloaded version file.";
381 QTextStream stream(&versionFile);
382 const QString versionFileContents = stream.readAll();
384 const QRegularExpressionMatch match = QRegularExpression(
_versionRegex()).match(versionFileContents);
386 qCDebug(FirmwarePluginLog) <<
"Looking for version number...";
388 if (match.hasMatch()) {
389 version = match.captured(1);
391 qCWarning(FirmwarePluginLog) <<
"Unable to parse version info from file" << remoteFile;
395 qCDebug(FirmwarePluginLog) <<
"Latest stable version = " << version;
400 if ((currType == FIRMWARE_VERSION_TYPE_OFFICIAL) && (vehicle->
versionCompare(version) < 0)) {
401 const QString currentVersionNumber = QStringLiteral(
"%1.%2.%3").arg(vehicle->
firmwareMajorVersion())
407 settings.beginGroup(QStringLiteral(
"LastNotifiedStableFirmware"));
412 settings.setValue(settingsKey, version);
414 QGC::showAppMessage(tr(
"Vehicle is not running latest stable firmware! Running %1, latest stable is %2.").arg(currentVersionNumber, version));
438 if ((currMajor == major) && (currMinor == minor) && (currPatch == patch)) {
442 if ((currMajor > major)
443 || ((currMajor == major) && (currMinor > minor))
444 || ((currMajor == major) && (currMinor == minor) && (currPatch > patch)))
454 const QStringList versionNumbers = compare.split(
".");
455 if (versionNumbers.size() != 3) {
456 qCWarning(FirmwarePluginLog) <<
"Error parsing version number: wrong format";
460 const int major = versionNumbers[0].toInt();
461 const int minor = versionNumbers[1].toInt();
462 const int patch = versionNumbers[2].toInt();
469 const float altitude =
static_cast<float>(motionReport.
altMetersAMSL);
470 if (!vehicle || !qIsFinite(altitude)) {
471 qCDebug(FirmwarePluginLog) <<
"Follow target requires a vehicle and finite altitude";
479 mavlink_follow_target_t follow_target{};
481 follow_target.timestamp =
qgcApp()->msecsSinceBoot();
482 follow_target.est_capabilities = estimationCapabilities;
483 follow_target.position_cov[0] =
static_cast<float>(motionReport.
pos_std_dev[0]);
484 follow_target.position_cov[2] =
static_cast<float>(motionReport.
pos_std_dev[2]);
485 follow_target.alt = altitude;
486 follow_target.lat = motionReport.
lat_int;
487 follow_target.lon = motionReport.
lon_int;
488 follow_target.vel[0] =
static_cast<float>(motionReport.
vxMetersPerSec);
489 follow_target.vel[1] =
static_cast<float>(motionReport.
vyMetersPerSec);
492 mavlink_msg_follow_target_encode_chan(
495 sharedLink->mavlinkChannel(),
519 qCDebug(FirmwarePluginLog) <<
"Flight Mode:" <<
flightMode.mode_name <<
" Custom Mode:" <<
flightMode.custom_mode;
526 if (existingFlightMode.custom_mode == newFlightMode.
custom_mode) {
527 qCDebug(FirmwarePluginLog) <<
"Flight Mode:" << newFlightMode.
mode_name <<
" Custom Mode:" << newFlightMode.
custom_mode
528 <<
" already exists, not adding again.";
541 const QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
542 + QStringLiteral(
"/ParameterMetaData");
554 qCDebug(FirmwarePluginLog) <<
"No parameter metadata parser for firmware plugin" <<
this <<
"(expected for generic firmware)";
556 qCWarning(FirmwarePluginLog) <<
"No parameter metadata parser for firmware plugin" <<
this;
562 if (!metaDataFile.isEmpty()) {
571 if (autopilot == MAV_AUTOPILOT_GENERIC) {
577 if (internalVersion.isNull()) {
584 qCDebug(FirmwarePluginLog) <<
"Using internal parameter metadata:" << internalFile;
588 const int wantedMajorVersion = internalVersion.majorVersion();
592 const QStringList entries = cacheDir.entryList(QStringList(wildcard), QDir::Files);
595 QVersionNumber bestVersion;
596 for (
const QString &entry : entries) {
598 if (ver.isNull() || ver.majorVersion() != wantedMajorVersion) {
601 if (bestVersion.isNull() || ver > bestVersion) {
603 bestFile = cacheDir.filePath(entry);
607 if (bestFile.isEmpty()) {
608 qCDebug(FirmwarePluginLog) <<
"No cached parameter metadata found, using internal:" << internalFile;
612 if (internalVersion.majorVersion() == wantedMajorVersion && bestVersion <= internalVersion) {
613 qCDebug(FirmwarePluginLog) <<
"Internal metadata" << internalVersion.toString() <<
">= cache" << bestVersion.toString()
614 <<
"— using internal:" << internalFile;
617 qCDebug(FirmwarePluginLog) <<
"Using cached parameter metadata" << bestVersion.toString() <<
":" << bestFile;
624 if (autopilot == MAV_AUTOPILOT_GENERIC) {
630 if (data.isEmpty()) {
631 qCWarning(FirmwarePluginLog) <<
"Cannot cache parameter metadata: failed to read" << metaDataFile << readError;
637 bool validJson =
false;
640 qCDebug(FirmwarePluginLog) <<
"Skipping cache of non-JSON parameter metadata:" << metaDataFile;
643 if (newVersion.isNull()) {
647 newVersion = QVersionNumber(1, 0);
650 const int majorVersion = newVersion.majorVersion();
653 const QString wildcard = QStringLiteral(
"%1_%2.%3.*.json").arg(
kCachedMetaDataFilePrefix).arg(autopilot).arg(majorVersion);
654 const QStringList existing = cacheDir.entryList(QStringList(wildcard), QDir::Files);
656 for (
const QString &file : existing) {
661 if (!existingVersion.isNull() && newVersion < existingVersion) {
666 const QString cachePath = cacheDir.filePath(
667 QStringLiteral(
"%1_%2.%3.%4.json").arg(
kCachedMetaDataFilePrefix).arg(autopilot).arg(majorVersion).arg(newVersion.minorVersion()));
669 qCWarning(FirmwarePluginLog) <<
"Failed to cache parameter metadata to" << cachePath;
673 for (
const QString &file : existing) {
674 const QString fullPath = cacheDir.filePath(file);
675 if (fullPath != cachePath && !QFile::remove(fullPath)) {
676 qCWarning(FirmwarePluginLog) <<
"Failed to remove old cache file:" << file;
static constexpr const char * kCachedMetaDataFilePrefix
static const QString guided_mode_not_supported_by_vehicle
static QDir _parameterMetaDataCacheDir(bool ensureExists=false)
QList< FirmwareFlightMode > FlightModeList
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
struct __mavlink_message mavlink_message_t
Unified file download utility with decompression, verification, and QML support.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
struct __mavlink_camera_information_t mavlink_camera_information_t
The AutoPilotPlugin class is an abstract base class which represents the methods and objects which ar...
virtual CommandSupportedResult anyVersionSupportsCommand(MAV_CMD) const
CommandSupportedResult getCommandSupported(MAV_CMD cmd) const
The FirmwarePlugin class represents the methods and objects which are specific to a certain Firmware ...
virtual void guidedModeChangeEquivalentAirspeedMetersSecond(Vehicle *vehicle, double airspeed_equiv) const
virtual void guidedModeChangeAltitude(Vehicle *vehicle, double altitudeChange, bool pauseVehicle)
virtual void startMission(Vehicle *vehicle) const
Command the vehicle to start the mission.
virtual void checkIfIsLatestStable(Vehicle *vehicle) const
Used to check if running firmware is latest stable version.
FlightModeList _flightModeList
virtual const QVariantList & toolIndicators(const Vehicle *vehicle)
virtual void guidedModeChangeGroundSpeedMetersSecond(Vehicle *vehicle, double groundspeed) const
virtual bool setFlightMode(const QString &flightMode, uint8_t *base_mode, uint32_t *custom_mode) const
virtual ~FirmwarePlugin()
virtual void _versionFileDownloadFinished(const QString &remoteFile, const QString &localFile, Vehicle *vehicle) const
Callback to process file with latest release information.
virtual void guidedModeTakeoff(Vehicle *vehicle, double takeoffAltRel) const
Command vehicle to takeoff from current location to the specified height.
void _sendROICommand(Vehicle *vehicle, const QGeoCoordinate &coord, MAV_FRAME frame, float altitude) const
Build + send MAV_CMD_DO_SET_ROI_LOCATION (COMMAND_INT when the vehicle supports it).
void cacheParameterMetaDataFile(const QString &metaDataFile)
virtual QString missionCommandOverrides(QGCMAVLinkTypes::VehicleClass_t vehicleClass) const
bool _setFlightModeAndValidate(Vehicle *vehicle, const QString &flightMode) const
virtual QString _internalParameterMetaDataFile(const Vehicle *) const
virtual QString _getLatestVersionFileUrl(Vehicle *) const
returns url with latest firmware release information.
void _updateFlightModeList(FlightModeList &flightModeList)
virtual void guidedModeChangeHeading(Vehicle *vehicle, const QGeoCoordinate &headingCoord) const
Command vehicle to rotate towards specified location.
virtual bool guidedModeGotoLocation(Vehicle *vehicle, const QGeoCoordinate &gotoCoord, double forwardFlightLoiterRadius=0.0) const
static QString stableFirmwareSettingsKey(const Vehicle *vehicle)
Settings key identifying the stable firmware stream (firmware + vehicle class) for this vehicle.
virtual QString flightMode(uint8_t base_mode, uint32_t custom_mode) const
virtual void startTakeoff(Vehicle *vehicle) const
Command the vehicle to start a takeoff.
void _addNewFlightMode(FirmwareFlightMode &flightMode)
virtual void pauseVehicle(Vehicle *vehicle) const
virtual MAV_AUTOPILOT _autopilotType() const
int versionCompare(const Vehicle *vehicle, const QString &compare) const
virtual Autotune * createAutotune(Vehicle *vehicle) const
Creates Autotune object.
static bool isStableFirmwareVersionUnseen(const QString &latestVersion, const QString &seenVersion)
ParameterMetaData * loadParameterMetaData(const Vehicle *vehicle)
virtual AutoPilotPlugin * autopilotPlugin(Vehicle *vehicle) const
virtual void guidedModeRTL(Vehicle *vehicle, bool smartRTL) const
Command vehicle to return to launch.
virtual ParameterMetaData * _createParameterMetaData()
virtual QString _versionRegex() const
Returns regex QString to extract version information from text.
bool _armVehicleAndValidate(Vehicle *vehicle) const
QVariantList _toolIndicatorList
QString _cachedParameterMetaDataFile(const Vehicle *vehicle) const
virtual void setGuidedMode(Vehicle *vehicle, bool guidedMode) const
Set guided flight mode.
FlightModeCustomModeMap _modeEnumToString
virtual bool guidedModeROI(Vehicle *vehicle, const QGeoCoordinate &roiCenterCoord, double relativeAltitudeMeters) const
virtual void batteryConsumptionData(Vehicle *vehicle, int &mAhBattery, double &hoverAmps, double &cruiseAmps) const
virtual QGCCameraManager * createCameraManager(Vehicle *vehicle) const
Creates vehicle camera manager.
virtual void sendGCSMotionReport(Vehicle *vehicle, const FollowMe::GCSMotionReport &motionReport, uint8_t estimationCapabilities) const
Sends the appropriate mavlink message for follow me support.
QMap< int, remapParamNameMinorVersionRemapMap_t > remapParamNameMajorVersionMap_t
virtual const remapParamNameMajorVersionMap_t & paramNameRemapMajorVersionMap() const
virtual void guidedModeLand(Vehicle *vehicle) const
Command vehicle to land at current location.
virtual MavlinkCameraControlInterface * createCameraControl(const mavlink_camera_information_t *info, Vehicle *vehicle, int compID, QObject *parent=nullptr) const
Camera control.
virtual bool hasGimbal(Vehicle *vehicle, bool &rollSupported, bool &pitchSupported, bool &yawSupported) const
This is the generic implementation of the AutoPilotPlugin class for mavs we do not have a specific Au...
static int getComponentId()
static MAVLinkProtocol * instance()
Abstract base class for all camera controls: real and simulated.
File download with progress, decompression, and hash verification.
void finished(bool success, const QString &localPath, const QString &errorMessage)
bool start(const QString &remoteUrl)
static VehicleClass_t vehicleClass(MAV_TYPE mavType)
static const char * vehicleClassToCanonicalString(VehicleClass_t vehicleClass)
static constexpr const VehicleClass_t VehicleClassSub
static constexpr const VehicleClass_t VehicleClassFixedWing
static constexpr const VehicleClass_t VehicleClassMultiRotor
static constexpr const VehicleClass_t VehicleClassRoverBoat
static FirmwareClass_t firmwareClass(MAV_AUTOPILOT autopilot)
static const char * firmwareClassToCanonicalString(FirmwareClass_t firmwareClass)
static constexpr const VehicleClass_t VehicleClassVTOL
static bool shouldNotify(const QString &latestVersion, const QString &lastNotifiedVersion)
MAVLink Camera API controller - connected to a real mavlink v2 camera.
WeakLinkInterfacePtr primaryLink() const
void sendMavCommand(int compId, MAV_CMD command, bool showError, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
QString flightMode() const
void setNewStableFirmwareVersion(const QString &version)
uint64_t capabilityBits() const
MAV_TYPE vehicleType() const
VehicleLinkManager * vehicleLinkManager()
void sendMavCommandInt(int compId, MAV_CMD command, MAV_FRAME frame, bool showError, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
int firmwareMinorVersion() const
MAV_AUTOPILOT firmwareType() const
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
void setFlightMode(const QString &flightMode)
int firmwareVersionType() const
int defaultComponentId() const
int firmwarePatchVersion() const
Q_INVOKABLE int versionCompare(const QString &compare) const
Used to check if running current version is equal or higher than the one being compared.
int firmwareMajorVersion() const
void setArmed(bool armed, bool showError)
QByteArray readFile(const QString &filePath, QString *errorString, qint64 maxBytes)
Read file contents, transparently decompressing .gz/.xz/.zst/.bz2/.lz4 files.
bool atomicWrite(const QString &filePath, const QByteArray &data)
bool ensureDirectoryExists(const QString &path)
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
static constexpr VehicleClass_t VehicleClassGeneric