/**************************************************************************** * * (c) 2009-2020 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ #include "FirmwareUpgradeController.h" #include "Bootloader.h" #include "QGCApplication.h" #include "QGCFileDownload.h" #include "QGCOptions.h" #include "QGCCorePlugin.h" #include "FirmwareUpgradeSettings.h" #include "SettingsManager.h" #include "QGCZlib.h" #include "JsonHelper.h" #include "LinkManager.h" #include #include #include #include #include #include #include "zlib.h" const char* FirmwareUpgradeController::_manifestFirmwareJsonKey = "firmware"; const char* FirmwareUpgradeController::_manifestBoardIdJsonKey = "board_id"; const char* FirmwareUpgradeController::_manifestMavTypeJsonKey = "mav-type"; const char* FirmwareUpgradeController::_manifestFormatJsonKey = "format"; const char* FirmwareUpgradeController::_manifestUrlJsonKey = "url"; const char* FirmwareUpgradeController::_manifestMavFirmwareVersionTypeJsonKey = "mav-firmware-version-type"; const char* FirmwareUpgradeController::_manifestUSBIDJsonKey = "USBID"; const char* FirmwareUpgradeController::_manifestMavFirmwareVersionJsonKey = "mav-firmware-version"; const char* FirmwareUpgradeController::_manifestBootloaderStrJsonKey = "bootloader_str"; const char* FirmwareUpgradeController::_manifestLatestKey = "latest"; const char* FirmwareUpgradeController::_manifestPlatformKey = "platform"; const char* FirmwareUpgradeController::_manifestBrandNameKey = "brand_name"; struct FirmwareToUrlElement_t { FirmwareUpgradeController::AutoPilotStackType_t stackType; FirmwareUpgradeController::FirmwareBuildType_t firmwareType; FirmwareUpgradeController::FirmwareVehicleType_t vehicleType; QString url; }; // See PX4 Bootloader board_types.txt - https://raw.githubusercontent.com/PX4/PX4-Bootloader/master/board_types.txt static QMap px4_board_name_map { {9, "px4_fmu-v2_default"}, {255, "px4_fmu-v3_default"}, // Simulated board id for V3 which is a V2 board which supports larger flash space {11, "px4_fmu-v4_default"}, {13, "px4_fmu-v4pro_default"}, {20, "uvify_core_default"}, {50, "px4_fmu-v5_default"}, {51, "px4_fmu-v5x_default"}, {52, "px4_fmu-v6_default"}, {53, "px4_fmu-v6x_default"}, {54, "px4_fmu-v6u_default"}, {88, "airmind_mindpx-v2_default"}, {12, "bitcraze_crazyflie_default"}, {14, "bitcraze_crazyflie21_default"}, {42, "omnibus_f4sd_default"}, {33, "mro_x21_default"}, {65, "intel_aerofc-v1_default"}, {123, "holybro_kakutef7_default"}, {41775, "modalai_fc-v1_default"}, {78, "holybro_pix32v5_default"}, {28, "nxp_fmuk66-v3_default"}, {30, "nxp_fmuk66-e_default"}, {31, "nxp_fmurt1062-v1_default"}, {120, "cubepilot_cubeyellow_default"}, {136, "mro_x21-777_default"}, {139, "holybro_durandal-v1_default"}, {140, "cubepilot_cubeorange_default"}, {141, "mro_ctrl-zero-f7_default"}, {142, "mro_ctrl-zero-f7-oem_default"}, {1009, "cuav_nora_default"}, {1010, "cuav_x7pro_default"}, {1017, "mro_pixracerpro_default"}, {1023, "mro_ctrl-zero-h7_default"}, {1024, "mro_ctrl-zero-h7-oem_default"}, }; uint qHash(const FirmwareUpgradeController::FirmwareIdentifier& firmwareId) { return static_cast(( firmwareId.autopilotStackType | (firmwareId.firmwareType << 8) | (firmwareId.firmwareVehicleType << 16) )); } /// @Brief Constructs a new FirmwareUpgradeController Widget. This widget is used within the PX4VehicleConfig set of screens. FirmwareUpgradeController::FirmwareUpgradeController(void) : _singleFirmwareURL (qgcApp()->toolbox()->corePlugin()->options()->firmwareUpgradeSingleURL()) , _singleFirmwareMode (!_singleFirmwareURL.isEmpty()) , _downloadingFirmwareList (false) , _downloadManager (nullptr) , _downloadNetworkReply (nullptr) , _statusLog (nullptr) , _selectedFirmwareBuildType (StableFirmware) , _image (nullptr) , _apmBoardDescriptionReplaceText ("") , _apmChibiOSSetting (qgcApp()->toolbox()->settingsManager()->firmwareUpgradeSettings()->apmChibiOS()) , _apmVehicleTypeSetting (qgcApp()->toolbox()->settingsManager()->firmwareUpgradeSettings()->apmVehicleType()) { _manifestMavFirmwareVersionTypeToFirmwareBuildTypeMap["OFFICIAL"] = StableFirmware; _manifestMavFirmwareVersionTypeToFirmwareBuildTypeMap["BETA"] = BetaFirmware; _manifestMavFirmwareVersionTypeToFirmwareBuildTypeMap["DEV"] = DeveloperFirmware; _manifestMavTypeToFirmwareVehicleTypeMap["Copter"] = CopterFirmware; _manifestMavTypeToFirmwareVehicleTypeMap["HELICOPTER"] = HeliFirmware; _manifestMavTypeToFirmwareVehicleTypeMap["FIXED_WING"] = PlaneFirmware; _manifestMavTypeToFirmwareVehicleTypeMap["GROUND_ROVER"] = RoverFirmware; _manifestMavTypeToFirmwareVehicleTypeMap["SUBMARINE"] = SubFirmware; _threadController = new PX4FirmwareUpgradeThreadController(this); Q_CHECK_PTR(_threadController); connect(_threadController, &PX4FirmwareUpgradeThreadController::foundBoard, this, &FirmwareUpgradeController::_foundBoard); connect(_threadController, &PX4FirmwareUpgradeThreadController::noBoardFound, this, &FirmwareUpgradeController::_noBoardFound); connect(_threadController, &PX4FirmwareUpgradeThreadController::boardGone, this, &FirmwareUpgradeController::_boardGone); connect(_threadController, &PX4FirmwareUpgradeThreadController::foundBoardInfo, this, &FirmwareUpgradeController::_foundBoardInfo); connect(_threadController, &PX4FirmwareUpgradeThreadController::error, this, &FirmwareUpgradeController::_error); connect(_threadController, &PX4FirmwareUpgradeThreadController::updateProgress, this, &FirmwareUpgradeController::_updateProgress); connect(_threadController, &PX4FirmwareUpgradeThreadController::status, this, &FirmwareUpgradeController::_status); connect(_threadController, &PX4FirmwareUpgradeThreadController::eraseStarted, this, &FirmwareUpgradeController::_eraseStarted); connect(_threadController, &PX4FirmwareUpgradeThreadController::eraseComplete, this, &FirmwareUpgradeController::_eraseComplete); connect(_threadController, &PX4FirmwareUpgradeThreadController::flashComplete, this, &FirmwareUpgradeController::_flashComplete); connect(_threadController, &PX4FirmwareUpgradeThreadController::updateProgress, this, &FirmwareUpgradeController::_updateProgress); connect(&_eraseTimer, &QTimer::timeout, this, &FirmwareUpgradeController::_eraseProgressTick); #if !defined(NO_ARDUPILOT_DIALECT) connect(_apmChibiOSSetting, &Fact::rawValueChanged, this, &FirmwareUpgradeController::_buildAPMFirmwareNames); connect(_apmVehicleTypeSetting, &Fact::rawValueChanged, this, &FirmwareUpgradeController::_buildAPMFirmwareNames); #endif _initFirmwareHash(); _determinePX4StableVersion(); #if !defined(NO_ARDUPILOT_DIALECT) _downloadArduPilotManifest(); #endif } FirmwareUpgradeController::~FirmwareUpgradeController() { qgcApp()->toolbox()->linkManager()->setConnectionsAllowed(); } void FirmwareUpgradeController::startBoardSearch(void) { LinkManager* linkMgr = qgcApp()->toolbox()->linkManager(); linkMgr->setConnectionsSuspended(tr("Connect not allowed during Firmware Upgrade.")); // FIXME: Why did we get here with active vehicle? if (!qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()) { // We have to disconnect any inactive links linkMgr->disconnectAll(); } _bootloaderFound = false; _startFlashWhenBootloaderFound = false; _threadController->startFindBoardLoop(); } void FirmwareUpgradeController::flash(AutoPilotStackType_t stackType, FirmwareBuildType_t firmwareType, FirmwareVehicleType_t vehicleType) { qCDebug(FirmwareUpgradeLog) << "FirmwareUpgradeController::flash stackType:firmwareType:vehicleType" << stackType << firmwareType << vehicleType; FirmwareIdentifier firmwareId = FirmwareIdentifier(stackType, firmwareType, vehicleType); if (_bootloaderFound) { _getFirmwareFile(firmwareId); } else { // We haven't found the bootloader yet. Need to wait until then to flash _startFlashWhenBootloaderFound = true; _startFlashWhenBootloaderFoundFirmwareIdentity = firmwareId; _firmwareFilename.clear(); } } void FirmwareUpgradeController::flashFirmwareUrl(QString firmwareFlashUrl) { _firmwareFilename = firmwareFlashUrl; if (_bootloaderFound) { _downloadFirmware(); } else { // We haven't found the bootloader yet. Need to wait until then to flash _startFlashWhenBootloaderFound = true; } } void FirmwareUpgradeController::flash(const FirmwareIdentifier& firmwareId) { flash(firmwareId.autopilotStackType, firmwareId.firmwareType, firmwareId.firmwareVehicleType); } void FirmwareUpgradeController::flashSingleFirmwareMode(FirmwareBuildType_t firmwareType) { flash(SingleFirmwareMode, firmwareType, DefaultVehicleFirmware); } void FirmwareUpgradeController::cancel(void) { _eraseTimer.stop(); _threadController->cancel(); } QStringList FirmwareUpgradeController::availableBoardsName(void) { QGCSerialPortInfo::BoardType_t boardType; QString boardName; QStringList names; auto ports = QGCSerialPortInfo::availablePorts(); for (const auto& info : ports) { if(info.canFlash()) { info.getBoardInfo(boardType, boardName); names.append(boardName); } } return names; } void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPortInfo& info, int boardType, QString boardName) { _boardInfo = info; _boardType = static_cast(boardType); _boardTypeName = boardName; qDebug() << info.manufacturer() << info.description(); _startFlashWhenBootloaderFound = false; if (_boardType == QGCSerialPortInfo::BoardTypeSiKRadio) { 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); } } qCDebug(FirmwareUpgradeLog) << _boardType << _boardTypeName; emit boardFound(); } void FirmwareUpgradeController::_noBoardFound(void) { emit noBoardFound(); } void FirmwareUpgradeController::_boardGone(void) { emit boardGone(); } /// @brief Called when the bootloader is connected to by the findBootloader process. Moves the state machine /// to the next step. void FirmwareUpgradeController::_foundBoardInfo(int bootloaderVersion, int boardID, int flashSize) { _bootloaderFound = true; _bootloaderVersion = static_cast(bootloaderVersion); _bootloaderBoardID = static_cast(boardID); _bootloaderBoardFlashSize = static_cast(flashSize); _appendStatusLog(tr("Connected to bootloader:")); _appendStatusLog(tr(" Version: %1").arg(_bootloaderVersion)); _appendStatusLog(tr(" Board ID: %1").arg(_bootloaderBoardID)); _appendStatusLog(tr(" Flash size: %1").arg(_bootloaderBoardFlashSize)); if (_startFlashWhenBootloaderFound) { flash(_startFlashWhenBootloaderFoundFirmwareIdentity); } else { if (_rgManifestFirmwareInfo.count()) { _buildAPMFirmwareNames(); } emit showFirmwareSelectDlg(); } } /// @brief intializes the firmware hashes with proper urls. /// This happens only once for a class instance first time when it is needed. void FirmwareUpgradeController::_initFirmwareHash() { // indirect check whether this function has been called before or not // may have to be modified if _rgPX4FMUV2Firmware disappears if (!_rgPX4FLowFirmware.isEmpty()) { return; } /////////////////////////////// px4flow firmwares /////////////////////////////////////// FirmwareToUrlElement_t rgPX4FLowFirmwareArray[] = { { PX4FlowPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Flow/master/px4flow.px4" }, #if !defined(NO_ARDUPILOT_DIALECT) { PX4FlowAPM, StableFirmware, DefaultVehicleFirmware, "http://firmware.ardupilot.org/Tools/PX4Flow/px4flow-klt-latest.px4" }, #endif }; /////////////////////////////// 3dr radio firmwares /////////////////////////////////////// FirmwareToUrlElement_t rg3DRRadioFirmwareArray[] = { { ThreeDRRadio, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/SiK/stable/radio~hm_trp.ihx"} }; // We build the maps for PX4 firmwares dynamically using the data below for (auto& element : rgPX4FLowFirmwareArray) { _rgPX4FLowFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } for (auto& element : rg3DRRadioFirmwareArray) { _rg3DRRadioFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } } /// @brief Called when the findBootloader process is unable to sync to the bootloader. Moves the state /// machine to the appropriate error state. void FirmwareUpgradeController::_bootloaderSyncFailed(void) { _errorCancel("Unable to sync with bootloader."); } QHash* FirmwareUpgradeController::_firmwareHashForBoardId(int boardId) { _rgFirmwareDynamic.clear(); switch (boardId) { case Bootloader::boardIDPX4Flow: _rgFirmwareDynamic = _rgPX4FLowFirmware; break; case Bootloader::boardID3DRRadio: _rgFirmwareDynamic = _rg3DRRadioFirmware; break; default: if (px4_board_name_map.contains(boardId)) { const QString px4Url{"http://px4-travis.s3.amazonaws.com/Firmware/%1/%2.px4"}; _rgFirmwareDynamic.insert(FirmwareIdentifier(AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware), px4Url.arg("stable").arg(px4_board_name_map.value(boardId))); _rgFirmwareDynamic.insert(FirmwareIdentifier(AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware), px4Url.arg("beta").arg(px4_board_name_map.value(boardId))); _rgFirmwareDynamic.insert(FirmwareIdentifier(AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware), px4Url.arg("master").arg(px4_board_name_map.value(boardId))); } break; } return &_rgFirmwareDynamic; } void FirmwareUpgradeController::_getFirmwareFile(FirmwareIdentifier firmwareId) { QHash* prgFirmware = _firmwareHashForBoardId(static_cast(_bootloaderBoardID)); if (firmwareId.firmwareType == CustomFirmware) { _firmwareFilename = QString(); _errorCancel(tr("Custom firmware selected but no filename given.")); } else { if (prgFirmware->contains(firmwareId)) { _firmwareFilename = prgFirmware->value(firmwareId); } else { _errorCancel(tr("Unable to find specified firmware for board type")); return; } } if (_firmwareFilename.isEmpty()) { _errorCancel(tr("No firmware file selected")); } else { _downloadFirmware(); } } /// @brief Begins the process of downloading the selected firmware file. void FirmwareUpgradeController::_downloadFirmware(void) { Q_ASSERT(!_firmwareFilename.isEmpty()); _appendStatusLog(tr("Downloading firmware...")); _appendStatusLog(tr(" From: %1").arg(_firmwareFilename)); QGCFileDownload* downloader = new QGCFileDownload(this); connect(downloader, &QGCFileDownload::downloadComplete, this, &FirmwareUpgradeController::_firmwareDownloadComplete); connect(downloader, &QGCFileDownload::downloadProgress, this, &FirmwareUpgradeController::_firmwareDownloadProgress); downloader->download(_firmwareFilename); } /// @brief Updates the progress indicator while downloading void FirmwareUpgradeController::_firmwareDownloadProgress(qint64 curr, qint64 total) { // Take care of cases where 0 / 0 is emitted as error return value if (total > 0) { _progressBar->setProperty("value", static_cast(curr) / static_cast(total)); } } /// @brief Called when the firmware download completes. void FirmwareUpgradeController::_firmwareDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg) { if (errorMsg.isEmpty()) { _appendStatusLog(tr("Download complete")); FirmwareImage* image = new FirmwareImage(this); connect(image, &FirmwareImage::statusMessage, this, &FirmwareUpgradeController::_status); connect(image, &FirmwareImage::errorMessage, this, &FirmwareUpgradeController::_error); if (!image->load(localFile, _bootloaderBoardID)) { _errorCancel(tr("Image load failed")); return; } // We can't proceed unless we have the bootloader if (!_bootloaderFound) { _errorCancel(tr("Bootloader not found")); return; } if (_bootloaderBoardFlashSize != 0 && image->imageSize() > _bootloaderBoardFlashSize) { _errorCancel(tr("Image size of %1 is too large for board flash size %2").arg(image->imageSize()).arg(_bootloaderBoardFlashSize)); return; } _threadController->flash(image); } else { _errorCancel(errorMsg); } } /// @brief returns firmware type as a string QString FirmwareUpgradeController::firmwareTypeAsString(FirmwareBuildType_t type) const { switch (type) { case StableFirmware: return "stable"; case DeveloperFirmware: return "developer"; case BetaFirmware: return "beta"; default: return "custom"; } } /// @brief Signals completion of one of the specified bootloader commands. Moves the state machine to the /// appropriate next step. void FirmwareUpgradeController::_flashComplete(void) { delete _image; _image = nullptr; _appendStatusLog(tr("Upgrade complete"), true); _appendStatusLog("------------------------------------------", false); emit flashComplete(); qgcApp()->toolbox()->linkManager()->setConnectionsAllowed(); } void FirmwareUpgradeController::_error(const QString& errorString) { delete _image; _image = nullptr; _errorCancel(QString("Error: %1").arg(errorString)); } void FirmwareUpgradeController::_status(const QString& statusString) { _appendStatusLog(statusString); } /// @brief Updates the progress bar from long running bootloader commands void FirmwareUpgradeController::_updateProgress(int curr, int total) { // Take care of cases where 0 / 0 is emitted as error return value if (total > 0) { _progressBar->setProperty("value", static_cast(curr) / static_cast(total)); } } /// @brief Moves the progress bar ahead on tick while erasing the board void FirmwareUpgradeController::_eraseProgressTick(void) { _eraseTickCount++; _progressBar->setProperty("value", static_cast(_eraseTickCount*_eraseTickMsec) / static_cast(_eraseTotalMsec)); } /// Appends the specified text to the status log area in the ui void FirmwareUpgradeController::_appendStatusLog(const QString& text, bool critical) { Q_ASSERT(_statusLog); QVariant returnedValue; QVariant varText; if (critical) { varText = QString("%1").arg(text); } else { varText = text; } QMetaObject::invokeMethod(_statusLog, "append", Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, varText)); } void FirmwareUpgradeController::_errorCancel(const QString& msg) { _appendStatusLog(msg, false); _appendStatusLog(tr("Upgrade cancelled"), true); _appendStatusLog("------------------------------------------", false); emit error(); cancel(); qgcApp()->toolbox()->linkManager()->setConnectionsAllowed(); } void FirmwareUpgradeController::_eraseStarted(void) { // We set up our own progress bar for erase since the erase command does not provide one _eraseTickCount = 0; _eraseTimer.start(_eraseTickMsec); } void FirmwareUpgradeController::_eraseComplete(void) { _eraseTimer.stop(); } void FirmwareUpgradeController::setSelectedFirmwareBuildType(FirmwareBuildType_t firmwareType) { _selectedFirmwareBuildType = firmwareType; emit selectedFirmwareBuildTypeChanged(_selectedFirmwareBuildType); _buildAPMFirmwareNames(); } void FirmwareUpgradeController::_buildAPMFirmwareNames(void) { #if !defined(NO_ARDUPILOT_DIALECT) bool chibios = _apmChibiOSSetting->rawValue().toInt() == 0; FirmwareVehicleType_t vehicleType = static_cast(_apmVehicleTypeSetting->rawValue().toInt()); QString boardDescription = _boardInfo.description(); quint16 boardVID = _boardInfo.vendorIdentifier(); quint16 boardPID = _boardInfo.productIdentifier(); uint32_t rawBoardId = _bootloaderBoardID == Bootloader::boardIDPX4FMUV3 ? Bootloader::boardIDPX4FMUV2 : _bootloaderBoardID; if (_boardType == QGCSerialPortInfo::BoardTypePX4Flow) { return; } qCDebug(FirmwareUpgradeLog) << QStringLiteral("_buildAPMFirmwareNames description(%1) vid(%2/0x%3) pid(%4/0x%5)").arg(boardDescription).arg(boardVID).arg(boardVID, 1, 16).arg(boardPID).arg(boardPID, 1, 16); _apmFirmwareNames.clear(); _apmFirmwareNamesBestIndex = -1; _apmFirmwareUrls.clear(); QString apmDescriptionSuffix("-BL"); QString boardDescriptionPrefix; bool bootloaderMatch = boardDescription.endsWith(apmDescriptionSuffix); int currentIndex = 0; for (const ManifestFirmwareInfo_t& firmwareInfo: _rgManifestFirmwareInfo) { bool match = false; if (firmwareInfo.firmwareBuildType == _selectedFirmwareBuildType && firmwareInfo.chibios == chibios && firmwareInfo.vehicleType == vehicleType && firmwareInfo.boardId == rawBoardId) { if (firmwareInfo.fmuv2 && _bootloaderBoardID == Bootloader::boardIDPX4FMUV3) { qCDebug(FirmwareUpgradeLog) << "Skipping fmuv2 manifest entry for fmuv3 board:" << firmwareInfo.friendlyName << boardDescription << firmwareInfo.rgBootloaderPortString << firmwareInfo.url << firmwareInfo.vehicleType; } else { qCDebug(FirmwareUpgradeLog) << "Board id match:" << firmwareInfo.friendlyName << boardDescription << firmwareInfo.rgBootloaderPortString << firmwareInfo.url << firmwareInfo.vehicleType; match = true; if (bootloaderMatch && _apmFirmwareNamesBestIndex == -1 && firmwareInfo.rgBootloaderPortString.contains(boardDescription)) { _apmFirmwareNamesBestIndex = currentIndex; qCDebug(FirmwareUpgradeLog) << "Bootloader best match:" << firmwareInfo.friendlyName << boardDescription << firmwareInfo.rgBootloaderPortString << firmwareInfo.url << firmwareInfo.vehicleType; } } } if (match) { _apmFirmwareNames.append(firmwareInfo.friendlyName); _apmFirmwareUrls.append(firmwareInfo.url); currentIndex++; } } if (_apmFirmwareNamesBestIndex == -1) { _apmFirmwareNamesBestIndex++; if (_apmFirmwareNames.count() > 1) { _apmFirmwareNames.prepend(tr("Choose board type")); _apmFirmwareUrls.prepend(QString()); } } emit apmFirmwareNamesChanged(); #endif } FirmwareUpgradeController::FirmwareVehicleType_t FirmwareUpgradeController::vehicleTypeFromFirmwareSelectionIndex(int index) { if (index < 0 || index >= _apmVehicleTypeFromCurrentVersionList.count()) { qWarning() << "Invalid index, index:count" << index << _apmVehicleTypeFromCurrentVersionList.count(); return CopterFirmware; } return _apmVehicleTypeFromCurrentVersionList[index]; } void FirmwareUpgradeController::_determinePX4StableVersion(void) { QGCFileDownload* downloader = new QGCFileDownload(this); connect(downloader, &QGCFileDownload::downloadComplete, this, &FirmwareUpgradeController::_px4ReleasesGithubDownloadComplete); downloader->download(QStringLiteral("https://api.github.com/repos/PX4/Firmware/releases")); } void FirmwareUpgradeController::_px4ReleasesGithubDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg) { if (errorMsg.isEmpty()) { QFile jsonFile(localFile); if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(FirmwareUpgradeLog) << "Unable to open github px4 releases json file" << localFile << jsonFile.errorString(); return; } QByteArray bytes = jsonFile.readAll(); jsonFile.close(); QJsonParseError jsonParseError; QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qCWarning(FirmwareUpgradeLog) << "Unable to open px4 releases json document" << localFile << jsonParseError.errorString(); return; } // Json should be an array of release objects if (!doc.isArray()) { qCWarning(FirmwareUpgradeLog) << "px4 releases json document is not an array" << localFile; return; } QJsonArray releases = doc.array(); // The first release marked prerelease=false is stable // The first release marked prerelease=true is beta bool foundStable = false; bool foundBeta = false; for (int i=0; idownload(QStringLiteral("http://firmware.ardupilot.org/manifest.json.gz")); } void FirmwareUpgradeController::_ardupilotManifestDownloadComplete(QString remoteFile, QString localFile, QString errorMsg) { if (errorMsg.isEmpty()) { // Delete the QGCFileDownload object sender()->deleteLater(); qCDebug(FirmwareUpgradeLog) << "_ardupilotManifestDownloadFinished" << remoteFile << localFile; QString jsonFileName(QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation)).absoluteFilePath("ArduPilot.Manifest.json")); if (!QGCZlib::inflateGzipFile(localFile, jsonFileName)) { qCWarning(FirmwareUpgradeLog) << "Inflate of compressed manifest failed" << localFile; return; } QString errorString; QJsonDocument doc; if (!JsonHelper::isJsonFile(jsonFileName, doc, errorString)) { qCWarning(FirmwareUpgradeLog) << "Json file read failed" << errorString; return; } QJsonObject json = doc.object(); QJsonArray rgFirmware = json[_manifestFirmwareJsonKey].toArray(); for (int i=0; i(firmwareJson[_manifestBoardIdJsonKey].toInt()); firmwareInfo.firmwareBuildType = firmwareBuildType; firmwareInfo.vehicleType = firmwareVehicleType; firmwareInfo.url = firmwareJson[_manifestUrlJsonKey].toString(); firmwareInfo.version = firmwareJson[_manifestMavFirmwareVersionJsonKey].toString(); firmwareInfo.chibios = format == QStringLiteral("apj"); firmwareInfo.fmuv2 = platform.contains(QStringLiteral("fmuv2")); QJsonArray bootloaderArray = firmwareJson[_manifestBootloaderStrJsonKey].toArray(); for (int j=0; j