From f8ca385db246f6e75c3c8949e38d40d70303eb50 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 18 Dec 2015 11:29:47 -0800 Subject: [PATCH] Show APM firmware versions when flashing --- qgroundcontrol.pro | 2 + src/VehicleSetup/FirmwareUpgrade.qml | 75 +---- src/VehicleSetup/FirmwareUpgradeController.cc | 294 +++++++++++++----- src/VehicleSetup/FirmwareUpgradeController.h | 39 ++- 4 files changed, 263 insertions(+), 147 deletions(-) diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 1b7934639..589b3e343 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -262,6 +262,7 @@ HEADERS += \ src/QGCComboBox.h \ src/QGCConfig.h \ src/QGCDockWidget.h \ + src/QGCFileDownload.h \ src/QGCGeo.h \ src/QGCLoggingCategory.h \ src/QGCMapPalette.h \ @@ -382,6 +383,7 @@ SOURCES += \ src/QGCApplication.cc \ src/QGCComboBox.cc \ src/QGCDockWidget.cc \ + src/QGCFileDownload.cc \ src/QGCLoggingCategory.cc \ src/QGCMapPalette.cc \ src/QGCPalette.cc \ diff --git a/src/VehicleSetup/FirmwareUpgrade.qml b/src/VehicleSetup/FirmwareUpgrade.qml index 683daee78..62fcd6470 100644 --- a/src/VehicleSetup/FirmwareUpgrade.qml +++ b/src/VehicleSetup/FirmwareUpgrade.qml @@ -164,7 +164,7 @@ QGCView { var firmwareType = firmwareVersionCombo.model.get(firmwareVersionCombo.currentIndex).firmwareType var vehicleType = FirmwareUpgradeController.DefaultVehicleFirmware if (apmFlightStack.checked) { - vehicleType = vehicleTypeSelectionCombo.model.get(vehicleTypeSelectionCombo.currentIndex).vehicleType + vehicleType = controller.vehicleTypeFromVersionIndex(vehicleTypeSelectionCombo.currentIndex) } controller.flash(stack, firmwareType, vehicleType) } @@ -199,47 +199,6 @@ QGCView { } } - ListModel { - id: vehicleTypeList - - ListElement { - text: "Quad" - vehicleType: FirmwareUpgradeController.QuadFirmware - } - ListElement { - text: "X8" - vehicleType: FirmwareUpgradeController.X8Firmware - } - ListElement { - text: "Hexa" - vehicleType: FirmwareUpgradeController.HexaFirmware - } - ListElement { - text: "Octo" - vehicleType: FirmwareUpgradeController.OctoFirmware - } - ListElement { - text: "Y" - vehicleType: FirmwareUpgradeController.YFirmware - } - ListElement { - text: "Y6" - vehicleType: FirmwareUpgradeController.Y6Firmware - } - ListElement { - text: "Heli" - vehicleType: FirmwareUpgradeController.HeliFirmware - } - ListElement { - text: "Plane" - vehicleType: FirmwareUpgradeController.PlaneFirmware - } - ListElement { - text: "Rover" - vehicleType: FirmwareUpgradeController.RoverFirmware - } - } - ListModel { id: px4FlowTypeList @@ -274,17 +233,6 @@ QGCView { firmwareVersionCombo.currentIndex = 0 } - function vehicleTypeChanged(model) { - vehicleTypeSelectionCombo.model = null - // All of this bizarre, setting model to null and index to 1 and then to 0 is to work around - // strangeness in the combo box implementation. This sequence of steps correctly changes the combo model - // without generating any warnings and correctly updates the combo text with the new selection. - vehicleTypeSelectionCombo.model = null - vehicleTypeSelectionCombo.model = model - vehicleTypeSelectionCombo.currentIndex = 1 - vehicleTypeSelectionCombo.currentIndex = 0 - } - QGCRadioButton { id: px4FlightStack checked: true @@ -301,10 +249,7 @@ QGCView { text: "APM Flight Stack" visible: !px4Flow - onClicked: { - parent.firmwareVersionChanged(firmwareTypeList) - parent.vehicleTypeChanged(vehicleTypeList) - } + onClicked: parent.firmwareVersionChanged(firmwareTypeList) } QGCLabel { @@ -317,19 +262,21 @@ QGCView { Row { spacing: 10 QGCComboBox { - id: firmwareVersionCombo - width: 200 - visible: showFirmwareTypeSelection - model: px4Flow ? px4FlowTypeList : firmwareTypeList + id: firmwareVersionCombo + width: 200 + visible: showFirmwareTypeSelection + model: px4Flow ? px4FlowTypeList : firmwareTypeList + currentIndex: controller.selectedFirmwareType onActivated: { - if (model.get(index).firmwareType == FirmwareUpgradeController.PX4BetaFirmware || FirmwareUpgradeController.APMBetaFirmware ) { + controller.selectedFirmwareType = index + if (model.get(index).firmwareType == FirmwareUpgradeController.BetaFirmware) { firmwareVersionWarningLabel.visible = true firmwareVersionWarningLabel.text = "WARNING: BETA FIRMWARE. " + "This firmware version is ONLY intended for beta testers. " + "Although it has received FLIGHT TESTING, it represents actively changed code. " + "Do NOT use for normal operation." - } else if (model.get(index).firmwareType == FirmwareUpgradeController.PX4DeveloperFirmware || FirmwareUpgradeController.APMDeveloperFirmware) { + } else if (model.get(index).firmwareType == FirmwareUpgradeController.DeveloperFirmware) { firmwareVersionWarningLabel.visible = true firmwareVersionWarningLabel.text = "WARNING: CONTINUOUS BUILD FIRMWARE. " + "This firmware has NOT BEEN FLIGHT TESTED. " + @@ -347,7 +294,7 @@ QGCView { id: vehicleTypeSelectionCombo width: 200 visible: apmFlightStack.checked - model: vehicleTypeList + model: controller.apmAvailableVersions } } diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc index d5a2585fe..cd0e2af1d 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.cc +++ b/src/VehicleSetup/FirmwareUpgradeController.cc @@ -1,5 +1,5 @@ /*===================================================================== - + QGroundControl Open Source Ground Control Station (c) 2009, 2015 QGROUNDCONTROL PROJECT @@ -29,6 +29,7 @@ #include "Bootloader.h" #include "QGCFileDialog.h" #include "QGCApplication.h" +#include "QGCFileDownload.h" struct FirmwareToUrlElement_t { FirmwareUpgradeController::AutoPilotStackType_t stackType; @@ -45,11 +46,12 @@ uint qHash(const FirmwareUpgradeController::FirmwareIdentifier& firmwareId) } /// @Brief Constructs a new FirmwareUpgradeController Widget. This widget is used within the PX4VehicleConfig set of screens. -FirmwareUpgradeController::FirmwareUpgradeController(void) : - _downloadManager(NULL), - _downloadNetworkReply(NULL), - _statusLog(NULL), - _image(NULL) +FirmwareUpgradeController::FirmwareUpgradeController(void) + : _downloadManager(NULL) + , _downloadNetworkReply(NULL) + , _statusLog(NULL) + , _selectedFirmwareType(StableFirmware) + , _image(NULL) { _threadController = new PX4FirmwareUpgradeThreadController(this); Q_CHECK_PTR(_threadController); @@ -68,6 +70,8 @@ FirmwareUpgradeController::FirmwareUpgradeController(void) : connect(_threadController, &PX4FirmwareUpgradeThreadController::updateProgress, this, &FirmwareUpgradeController::_updateProgress); connect(&_eraseTimer, &QTimer::timeout, this, &FirmwareUpgradeController::_eraseProgressTick); + + _initFirmwareHash(); } FirmwareUpgradeController::~FirmwareUpgradeController() @@ -115,42 +119,45 @@ void FirmwareUpgradeController::cancel(void) _threadController->cancel(); } -void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPortInfo& info, int type) +void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPortInfo& info, int boardType) { _foundBoardInfo = info; - switch (type) { - case QGCSerialPortInfo::BoardTypePX4FMUV1: - _foundBoardType = "PX4 FMU V1"; - _startFlashWhenBootloaderFound = false; - break; - case QGCSerialPortInfo::BoardTypePX4FMUV2: - case QGCSerialPortInfo::BoardTypePX4FMUV4: - _foundBoardType = "Pixhawk"; - _startFlashWhenBootloaderFound = false; - break; - case QGCSerialPortInfo::BoardTypeAeroCore: - _foundBoardType = "AeroCore"; - _startFlashWhenBootloaderFound = false; - break; - case QGCSerialPortInfo::BoardTypePX4Flow: - _foundBoardType = "PX4 Flow"; - _startFlashWhenBootloaderFound = false; - break; - case QGCSerialPortInfo::BoardType3drRadio: - _foundBoardType = "3DR Radio"; - if (!firstAttempt) { - // Radio always flashes latest firmware, so we can start right away without - // any further user input. - _startFlashWhenBootloaderFound = true; - _startFlashWhenBootloaderFoundFirmwareIdentity = FirmwareIdentifier(ThreeDRRadio, - StableFirmware, - DefaultVehicleFirmware); - } - break; + _foundBoardType = (QGCSerialPortInfo::BoardType_t)boardType; + + switch (boardType) { + case QGCSerialPortInfo::BoardTypePX4FMUV1: + _foundBoardTypeName = "PX4 FMU V1"; + _startFlashWhenBootloaderFound = false; + break; + case QGCSerialPortInfo::BoardTypePX4FMUV2: + case QGCSerialPortInfo::BoardTypePX4FMUV4: + _foundBoardTypeName = "Pixhawk"; + _startFlashWhenBootloaderFound = false; + break; + case QGCSerialPortInfo::BoardTypeAeroCore: + _foundBoardTypeName = "AeroCore"; + _startFlashWhenBootloaderFound = false; + break; + case QGCSerialPortInfo::BoardTypePX4Flow: + _foundBoardTypeName = "PX4 Flow"; + _startFlashWhenBootloaderFound = false; + break; + case QGCSerialPortInfo::BoardType3drRadio: + _foundBoardTypeName = "3DR Radio"; + if (!firstAttempt) { + // Radio always flashes latest firmware, so we can start right away without + // any further user input. + _startFlashWhenBootloaderFound = true; + _startFlashWhenBootloaderFoundFirmwareIdentity = FirmwareIdentifier(ThreeDRRadio, + StableFirmware, + DefaultVehicleFirmware); + } + break; } qCDebug(FirmwareUpgradeLog) << _foundBoardType; emit boardFound(); + _loadAPMVersions(_foundBoardType); } @@ -334,46 +341,65 @@ void FirmwareUpgradeController::_bootloaderSyncFailed(void) _errorCancel("Unable to sync with bootloader."); } -/// @brief Prompts the user to select a firmware file if needed and moves the state machine to the next state. -void FirmwareUpgradeController::_getFirmwareFile(FirmwareIdentifier firmwareId) +QHash* FirmwareUpgradeController::_firmwareHashForBoardId(int boardId) { - // make sure the firmware hashes are populated - _initFirmwareHash(); - - // Select the firmware set based on board type - - QHash prgFirmware; - - switch (_bootloaderBoardID) { - case Bootloader::boardIDPX4FMUV1: - prgFirmware = _rgPX4FMUV1Firmware; - break; - - case Bootloader::boardIDPX4Flow: - prgFirmware = _rgPX4FLowFirmware; - break; - - case Bootloader::boardIDPX4FMUV2: - prgFirmware = _rgPX4FMUV2Firmware; - break; + switch (boardId) { + case Bootloader::boardIDPX4FMUV1: + return &_rgPX4FMUV1Firmware; + case Bootloader::boardIDPX4Flow: + return &_rgPX4FLowFirmware; + case Bootloader::boardIDPX4FMUV2: + return &_rgPX4FMUV2Firmware; + case Bootloader::boardIDPX4FMUV4: + return &_rgPX4FMUV4Firmware; + case Bootloader::boardIDAeroCore: + return &_rgAeroCoreFirmware; + case Bootloader::boardID3DRRadio: + return &_rg3DRRadioFirmware; + default: + return NULL; + } +} - case Bootloader::boardIDPX4FMUV4: - prgFirmware = _rgPX4FMUV4Firmware; - break; - - case Bootloader::boardIDAeroCore: - prgFirmware = _rgAeroCoreFirmware; - break; - - case Bootloader::boardID3DRRadio: - prgFirmware = _rg3DRRadioFirmware; - break; - - default: - break; +QHash* FirmwareUpgradeController::_firmwareHashForBoardType(QGCSerialPortInfo::BoardType_t boardType) +{ + int boardId; + + switch (boardType) { + case QGCSerialPortInfo::BoardTypePX4FMUV1: + boardId = Bootloader::boardIDPX4FMUV1; + break; + case QGCSerialPortInfo::BoardTypePX4FMUV2: + boardId = Bootloader::boardIDPX4FMUV2; + break; + case QGCSerialPortInfo::BoardTypePX4FMUV4: + boardId = Bootloader::boardIDPX4FMUV4; + break; + case QGCSerialPortInfo::BoardTypeAeroCore: + boardId = Bootloader::boardIDAeroCore; + break; + case QGCSerialPortInfo::BoardTypePX4Flow: + boardId = Bootloader::boardIDPX4Flow; + break; + case QGCSerialPortInfo::BoardType3drRadio: + boardId = Bootloader::boardID3DRRadio; + break; + case QGCSerialPortInfo::BoardTypeUnknown: + qWarning() << "Internal error"; + boardId = Bootloader::boardIDPX4FMUV2; + break; } + + return _firmwareHashForBoardId(boardId); +} + + +/// @brief Prompts the user to select a firmware file if needed and moves the state machine to the next state. +void FirmwareUpgradeController::_getFirmwareFile(FirmwareIdentifier firmwareId) +{ + QHash* prgFirmware = _firmwareHashForBoardId(_bootloaderBoardID); - if (prgFirmware.isEmpty() && firmwareId.firmwareType != CustomFirmware) { + if (!prgFirmware && firmwareId.firmwareType != CustomFirmware) { _errorCancel("Attempting to flash an unknown board type, you must select 'Custom firmware file'"); return; } @@ -385,8 +411,8 @@ void FirmwareUpgradeController::_getFirmwareFile(FirmwareIdentifier firmwareId) "Firmware Files (*.px4 *.bin *.ihx)"); // File filter } else { - if (prgFirmware.contains(firmwareId)) { - _firmwareFilename = prgFirmware.value(firmwareId); + if (prgFirmware->contains(firmwareId)) { + _firmwareFilename = prgFirmware->value(firmwareId); } else { _errorCancel("Unable to find specified firmware download location"); return; @@ -609,3 +635,123 @@ void FirmwareUpgradeController::_eraseComplete(void) { _eraseTimer.stop(); } + +void FirmwareUpgradeController::_loadAPMVersions(QGCSerialPortInfo::BoardType_t boardType) +{ + _apmVersionMap.clear(); + + QHash* prgFirmware = _firmwareHashForBoardType(boardType); + + foreach (FirmwareIdentifier firmwareId, prgFirmware->keys()) { + if (firmwareId.autopilotStackType == AutoPilotStackAPM) { + QString versionFile = QFileInfo(prgFirmware->value(firmwareId)).path() + "/git-version.txt"; + + qCDebug(FirmwareUpgradeLog) << "Downloading" << versionFile; + QGCFileDownload* downloader = new QGCFileDownload(this); + connect(downloader, &QGCFileDownload::downloadFinished, this, &FirmwareUpgradeController::_apmVersionDownloadFinished); + downloader->download(versionFile); + } + } +} + + +void FirmwareUpgradeController::_apmVersionDownloadFinished(QString remoteFile, QString localFile) +{ + qCDebug(FirmwareUpgradeLog) << "Download complete" << remoteFile << localFile; + + // Now read the version file and pull out the version string + + QFile versionFile(localFile); + versionFile.open(QIODevice::ReadOnly | QIODevice::Text); + QTextStream stream(&versionFile); + QString versionContents = stream.readAll(); + + QString version; + QRegularExpression re("APMVERSION: (.*)$"); + QRegularExpressionMatch match = re.match(versionContents); + if (match.hasMatch()) { + version = match.captured(1); + } + + if (version.isEmpty()) { + qWarning() << "Unable to parse version info from file" << remoteFile; + return; + } + + // In order to determine the firmware and vehicle type for this file we find the matching entry in the firmware list + + QHash* prgFirmware = _firmwareHashForBoardType(_foundBoardType); + + QString remotePath = QFileInfo(remoteFile).path(); + foreach (FirmwareIdentifier firmwareId, prgFirmware->keys()) { + if (remotePath == QFileInfo((*prgFirmware)[firmwareId]).path()) { + qCDebug(FirmwareUpgradeLog) << "Adding version to map, version:firwmareType:vehicleType" << version << firmwareId.firmwareType << firmwareId.firmwareVehicleType; + _apmVersionMap[firmwareId.firmwareType][firmwareId.firmwareVehicleType] = version; + } + } + + emit apmAvailableVersionsChanged(); +} + +void FirmwareUpgradeController::setSelectedFirmwareType(FirmwareType_t firmwareType) +{ + _selectedFirmwareType = firmwareType; + emit selectedFirmwareTypeChanged(_selectedFirmwareType); + emit apmAvailableVersionsChanged(); +} + +QStringList FirmwareUpgradeController::apmAvailableVersions(void) +{ + QStringList list; + + _apmVehicleTypeFromCurrentVersionList.clear(); + + foreach (FirmwareVehicleType_t vehicleType, _apmVersionMap[_selectedFirmwareType].keys()) { + QString version; + + switch (vehicleType) { + case QuadFirmware: + version = "Quad - "; + break; + case X8Firmware: + version = "X8 - "; + break; + case HexaFirmware: + version = "Hexa - "; + break; + case OctoFirmware: + version = "Octo - "; + break; + case YFirmware: + version = "Y - "; + break; + case Y6Firmware: + version = "Y6 - "; + break; + case HeliFirmware: + version = "Heli - "; + break; + case PlaneFirmware: + case RoverFirmware: + case DefaultVehicleFirmware: + break; + } + + version += _apmVersionMap[_selectedFirmwareType][vehicleType]; + _apmVehicleTypeFromCurrentVersionList.append(vehicleType); + + list << version; + } + + return list; +} + +FirmwareUpgradeController::FirmwareVehicleType_t FirmwareUpgradeController::vehicleTypeFromVersionIndex(int index) +{ + if (index < 0 || index >= _apmVehicleTypeFromCurrentVersionList.count()) { + qWarning() << "Invalid index, index:count" << index << _apmVehicleTypeFromCurrentVersionList.count(); + return QuadFirmware; + } + + return _apmVehicleTypeFromCurrentVersionList[index]; +} diff --git a/src/VehicleSetup/FirmwareUpgradeController.h b/src/VehicleSetup/FirmwareUpgradeController.h index b65b09eb0..adbf54324 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.h +++ b/src/VehicleSetup/FirmwareUpgradeController.h @@ -106,10 +106,12 @@ public: FirmwareUpgradeController(void); ~FirmwareUpgradeController(); - Q_PROPERTY(QString boardPort READ boardPort NOTIFY boardFound) - Q_PROPERTY(QString boardDescription READ boardDescription NOTIFY boardFound) - Q_PROPERTY(QString boardType MEMBER _foundBoardType NOTIFY boardFound) - + Q_PROPERTY(QString boardPort READ boardPort NOTIFY boardFound) + Q_PROPERTY(QString boardDescription READ boardDescription NOTIFY boardFound) + Q_PROPERTY(QString boardType MEMBER _foundBoardTypeName NOTIFY boardFound) + Q_PROPERTY(FirmwareType_t selectedFirmwareType READ selectedFirmwareType WRITE setSelectedFirmwareType NOTIFY selectedFirmwareTypeChanged) + Q_PROPERTY(QStringList apmAvailableVersions READ apmAvailableVersions NOTIFY apmAvailableVersionsChanged) + /// TextArea for log output Q_PROPERTY(QQuickItem* statusLog READ statusLog WRITE setStatusLog) @@ -126,6 +128,8 @@ public: Q_INVOKABLE void flash(AutoPilotStackType_t stackType, FirmwareType_t firmwareType = StableFirmware, FirmwareVehicleType_t vehicleType = DefaultVehicleFirmware ); + + Q_INVOKABLE FirmwareVehicleType_t vehicleTypeFromVersionIndex(int index); // overload, not exposed to qml side void flash(const FirmwareIdentifier& firmwareId); @@ -140,6 +144,11 @@ public: QString boardPort(void) { return _foundBoardInfo.portName(); } QString boardDescription(void) { return _foundBoardInfo.description(); } + + FirmwareType_t selectedFirmwareType(void) { return _selectedFirmwareType; } + void setSelectedFirmwareType(FirmwareType_t firmwareType); + + QStringList apmAvailableVersions(void); signals: void boardFound(void); @@ -148,12 +157,14 @@ signals: void flashComplete(void); void flashCancelled(void); void error(void); + void selectedFirmwareTypeChanged(FirmwareType_t firmwareType); + void apmAvailableVersionsChanged(void); private slots: void _downloadProgress(qint64 curr, qint64 total); void _downloadFinished(void); void _downloadError(QNetworkReply::NetworkError code); - void _foundBoard(bool firstAttempt, const QSerialPortInfo& portInfo, int type); + void _foundBoard(bool firstAttempt, const QSerialPortInfo& portInfo, int boardType); void _noBoardFound(void); void _boardGone(); void _foundBootloader(int bootloaderVersion, int boardID, int flashSize); @@ -165,6 +176,7 @@ private slots: void _eraseStarted(void); void _eraseComplete(void); void _eraseProgressTick(void); + void _apmVersionDownloadFinished(QString remoteFile, QString localFile); private: void _getFirmwareFile(FirmwareIdentifier firmwareId); @@ -172,7 +184,10 @@ private: void _downloadFirmware(void); void _appendStatusLog(const QString& text, bool critical = false); void _errorCancel(const QString& msg); - + void _loadAPMVersions(QGCSerialPortInfo::BoardType_t boardType); + QHash* _firmwareHashForBoardId(int boardId); + QHash* _firmwareHashForBoardType(QGCSerialPortInfo::BoardType_t boardType); + QString _portName; QString _portDescription; @@ -184,6 +199,9 @@ private: QHash _rgPX4FLowFirmware; QHash _rg3DRRadioFirmware; + QMap > _apmVersionMap; + QList _apmVehicleTypeFromCurrentVersionList; + /// Information which comes back from the bootloader bool _bootloaderFound; ///< true: we have received the foundBootloader signals uint32_t _bootloaderVersion; ///< Bootloader version @@ -216,9 +234,12 @@ private: bool _searchingForBoard; ///< true: searching for board, false: search for bootloader - QSerialPortInfo _foundBoardInfo; - QString _foundBoardType; - + QSerialPortInfo _foundBoardInfo; + QGCSerialPortInfo::BoardType_t _foundBoardType; + QString _foundBoardTypeName; + + FirmwareType_t _selectedFirmwareType; + FirmwareImage* _image; }; -- 2.22.0