FirmwareUpgradeController.cc 46.4 KB
Newer Older
1 2
/****************************************************************************
 *
Gus Grubba's avatar
Gus Grubba committed
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 5 6 7 8
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
9

10
#include "FirmwareUpgradeController.h"
11
#include "Bootloader.h"
12
#include "QGCApplication.h"
13
#include "QGCFileDownload.h"
14 15
#include "QGCOptions.h"
#include "QGCCorePlugin.h"
16 17
#include "FirmwareUpgradeSettings.h"
#include "SettingsManager.h"
18 19
#include "QGCZlib.h"
#include "JsonHelper.h"
Pritam Ghanghas's avatar
Pritam Ghanghas committed
20

Don Gagne's avatar
Don Gagne committed
21 22
#include <QStandardPaths>
#include <QRegularExpression>
Don Gagne's avatar
Don Gagne committed
23 24
#include <QJsonDocument>
#include <QJsonObject>
25
#include <QJsonArray>
26
#include <QNetworkProxy>
Don Gagne's avatar
Don Gagne committed
27

28 29
#include "zlib.h"

30 31 32 33 34 35 36 37 38 39 40 41 42
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";

Pritam Ghanghas's avatar
Pritam Ghanghas committed
43
struct FirmwareToUrlElement_t {
44 45 46 47
    FirmwareUpgradeController::AutoPilotStackType_t     stackType;
    FirmwareUpgradeController::FirmwareBuildType_t      firmwareType;
    FirmwareUpgradeController::FirmwareVehicleType_t    vehicleType;
    QString                                             url;
Pritam Ghanghas's avatar
Pritam Ghanghas committed
48 49
};

50
uint qHash(const FirmwareUpgradeController::FirmwareIdentifier& firmwareId)
51
{
52
    return static_cast<uint>(( firmwareId.autopilotStackType |
53 54
                               (firmwareId.firmwareType << 8) |
                               (firmwareId.firmwareVehicleType << 16) ));
55 56
}

57
/// @Brief Constructs a new FirmwareUpgradeController Widget. This widget is used within the PX4VehicleConfig set of screens.
58
FirmwareUpgradeController::FirmwareUpgradeController(void)
59 60
    : _singleFirmwareURL                (qgcApp()->toolbox()->corePlugin()->options()->firmwareUpgradeSingleURL())
    , _singleFirmwareMode               (!_singleFirmwareURL.isEmpty())
61
    , _downloadingFirmwareList          (false)
62 63 64
    , _downloadManager                  (nullptr)
    , _downloadNetworkReply             (nullptr)
    , _statusLog                        (nullptr)
65
    , _selectedFirmwareBuildType             (StableFirmware)
66 67
    , _image                            (nullptr)
    , _apmBoardDescriptionReplaceText   ("<APMBoardDescription>")
68 69
    , _apmChibiOSSetting                (qgcApp()->toolbox()->settingsManager()->firmwareUpgradeSettings()->apmChibiOS())
    , _apmVehicleTypeSetting            (qgcApp()->toolbox()->settingsManager()->firmwareUpgradeSettings()->apmVehicleType())
70
{
71 72 73 74 75 76 77 78 79 80
    _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;

81 82 83
    _threadController = new PX4FirmwareUpgradeThreadController(this);
    Q_CHECK_PTR(_threadController);

84 85 86 87 88 89 90 91 92 93 94 95 96
    connect(_threadController, &PX4FirmwareUpgradeThreadController::foundBoard,             this, &FirmwareUpgradeController::_foundBoard);
    connect(_threadController, &PX4FirmwareUpgradeThreadController::noBoardFound,           this, &FirmwareUpgradeController::_noBoardFound);
    connect(_threadController, &PX4FirmwareUpgradeThreadController::boardGone,              this, &FirmwareUpgradeController::_boardGone);
    connect(_threadController, &PX4FirmwareUpgradeThreadController::foundBootloader,        this, &FirmwareUpgradeController::_foundBootloader);
    connect(_threadController, &PX4FirmwareUpgradeThreadController::bootloaderSyncFailed,   this, &FirmwareUpgradeController::_bootloaderSyncFailed);
    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);
    
97
    connect(&_eraseTimer, &QTimer::timeout, this, &FirmwareUpgradeController::_eraseProgressTick);
98

99
#if !defined(NO_ARDUPILOT_DIALECT)
100 101
    connect(_apmChibiOSSetting,     &Fact::rawValueChanged, this, &FirmwareUpgradeController::_buildAPMFirmwareNames);
    connect(_apmVehicleTypeSetting, &Fact::rawValueChanged, this, &FirmwareUpgradeController::_buildAPMFirmwareNames);
102
#endif
103

104
    _initFirmwareHash();
Don Gagne's avatar
Don Gagne committed
105
    _determinePX4StableVersion();
106 107

#if !defined(NO_ARDUPILOT_DIALECT)
108
    _downloadArduPilotManifest();
109
#endif
110 111
}

Don Gagne's avatar
Don Gagne committed
112 113 114 115 116
FirmwareUpgradeController::~FirmwareUpgradeController()
{
    qgcApp()->toolbox()->linkManager()->setConnectionsAllowed();
}

117
void FirmwareUpgradeController::startBoardSearch(void)
118
{
Don Gagne's avatar
Don Gagne committed
119 120 121 122
    LinkManager* linkMgr = qgcApp()->toolbox()->linkManager();

    linkMgr->setConnectionsSuspended(tr("Connect not allowed during Firmware Upgrade."));

123 124
    // FIXME: Why did we get here with active vehicle?
    if (!qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()) {
Don Gagne's avatar
Don Gagne committed
125 126 127
        // We have to disconnect any inactive links
        linkMgr->disconnectAll();
    }
128

129 130 131
    _bootloaderFound = false;
    _startFlashWhenBootloaderFound = false;
    _threadController->startFindBoardLoop();
132 133
}

134
void FirmwareUpgradeController::flash(AutoPilotStackType_t stackType,
135
                                      FirmwareBuildType_t firmwareType,
136
                                      FirmwareVehicleType_t vehicleType)
137
{
138
    qCDebug(FirmwareUpgradeLog) << "_flash stackType:firmwareType:vehicleType" << stackType << firmwareType << vehicleType;
139
    FirmwareIdentifier firmwareId = FirmwareIdentifier(stackType, firmwareType, vehicleType);
140
    if (_bootloaderFound) {
141
        _getFirmwareFile(firmwareId);
142 143 144
    } else {
        // We haven't found the bootloader yet. Need to wait until then to flash
        _startFlashWhenBootloaderFound = true;
145
        _startFlashWhenBootloaderFoundFirmwareIdentity = firmwareId;
146 147 148 149 150 151 152 153 154 155 156 157
        _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;
158
    }
159 160
}

161 162 163 164 165
void FirmwareUpgradeController::flash(const FirmwareIdentifier& firmwareId)
{
    flash(firmwareId.autopilotStackType, firmwareId.firmwareType, firmwareId.firmwareVehicleType);
}

166
void FirmwareUpgradeController::flashSingleFirmwareMode(FirmwareBuildType_t firmwareType)
167
{
Gus Grubba's avatar
Gus Grubba committed
168
    flash(SingleFirmwareMode, firmwareType, DefaultVehicleFirmware);
169 170
}

171
void FirmwareUpgradeController::cancel(void)
172
{
173 174 175
    _eraseTimer.stop();
    _threadController->cancel();
}
176

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
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;
}

194
void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPortInfo& info, int boardType, QString boardName)
195
{
196 197 198 199 200 201
    _foundBoardInfo =       info;
    _foundBoardType =       static_cast<QGCSerialPortInfo::BoardType_t>(boardType);
    _foundBoardTypeName =   boardName;

    qDebug() << info.manufacturer() << info.description();

202
    _startFlashWhenBootloaderFound = false;
203

204
    if (_foundBoardType == QGCSerialPortInfo::BoardTypeSiKRadio) {
205 206 207 208 209 210 211 212
        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);
        }
213
    }
214
    
215
    qCDebug(FirmwareUpgradeLog) << _foundBoardType << _foundBoardTypeName;
216
    emit boardFound();
217 218
}

219 220

void FirmwareUpgradeController::_noBoardFound(void)
221
{
222 223 224 225 226 227
    emit noBoardFound();
}

void FirmwareUpgradeController::_boardGone(void)
{
    emit boardGone();
228 229 230 231 232 233
}

/// @brief Called when the bootloader is connected to by the findBootloader process. Moves the state machine
///         to the next step.
void FirmwareUpgradeController::_foundBootloader(int bootloaderVersion, int boardID, int flashSize)
{
234
    _bootloaderFound = true;
235 236 237
    _bootloaderVersion = static_cast<uint32_t>(bootloaderVersion);
    _bootloaderBoardID = static_cast<uint32_t>(boardID);
    _bootloaderBoardFlashSize = static_cast<uint32_t>(flashSize);
238
    
239 240 241 242
    _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));
243
    
244
    if (_startFlashWhenBootloaderFound) {
245
        flash(_startFlashWhenBootloaderFoundFirmwareIdentity);
246
    }
247

248 249 250
    if (_rgManifestFirmwareInfo.count()) {
        _buildAPMFirmwareNames();
    }
251 252

    emit bootloaderFound();
253 254 255
}


Pritam Ghanghas's avatar
Pritam Ghanghas committed
256 257 258
/// @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()
259
{
Pritam Ghanghas's avatar
Pritam Ghanghas committed
260 261 262 263 264
    // indirect check whether this function has been called before or not
    // may have to be modified if _rgPX4FMUV2Firmware disappears
    if (!_rgPX4FMUV2Firmware.isEmpty()) {
        return;
    }
265

Pritam Ghanghas's avatar
Pritam Ghanghas committed
266 267 268 269 270 271
    //////////////////////////////////// PX4FMU aerocore firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t  rgAeroCoreFirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://gumstix-aerocore.s3.amazonaws.com/PX4/stable/aerocore_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://gumstix-aerocore.s3.amazonaws.com/PX4/beta/aerocore_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://gumstix-aerocore.s3.amazonaws.com/PX4/master/aerocore_default.px4"},
    };
272

273 274 275 276 277 278
    //////////////////////////////////// AUAVX2_1 firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t rgAUAVX2_1FirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/auav-x21_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/auav-x21_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/auav-x21_default.px4"},
    };
Henry Zhang's avatar
Henry Zhang committed
279 280 281 282 283 284
    //////////////////////////////////// MindPXFMUV2 firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t rgMindPXFMUV2FirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/mindpx-v2_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/mindpx-v2_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/mindpx-v2_default.px4"},
    };
285 286 287 288 289
    //////////////////////////////////// TAPV1 firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t rgTAPV1FirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/tap-v1_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/tap-v1_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/tap-v1_default.px4"},
290
        { SingleFirmwareMode,StableFirmware,    DefaultVehicleFirmware, _singleFirmwareURL},
291 292 293 294 295 296 297
    };
    //////////////////////////////////// ASCV1 firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t rgASCV1FirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/asc-v1_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/asc-v1_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/asc-v1_default.px4"},
    };
298 299 300 301 302 303 304 305

    //////////////////////////////////// Crazyflie 2.0 firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t rgCrazyflie2FirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/crazyflie_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/crazyflie_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/crazyflie_default.px4"},
    };

306
    //////////////////////////////////// Omnibus F4 SD firmwares //////////////////////////////////////////////////
307 308 309 310 311
    FirmwareToUrlElement_t rgOmnibusF4SDFirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/omnibus_f4sd_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/omnibus_f4sd_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/omnibus_f4sd_default.px4"},
    };
312 313 314 315 316 317 318
    
    //////////////////////////////////// FMUK66V3 firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t rgFMUK66V3FirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/nxp_fmuk66-v3_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/nxp_fmuk66-v3_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/nxp_fmuk66-v3_default.px4"},
    };
319 320 321 322 323 324 325

    //////////////////////////////////// Kakute F7 firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t rgKakuteF7FirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/holybro_kakutef7_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/holybro_kakutef7_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/holybro_kakutef7_default.px4"},
    };
326
    
327 328 329 330 331 332
    //////////////////////////////////// Durandal firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t rgDurandalV1FirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/holybro_durandal-v1_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/holybro_durandal-v1_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/holybro_durandal-v1_default.px4"},
    };
333 334 335 336 337 338 339

    //////////////////////////////////// ModalAI FC v1 firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t rgModalFCV1FirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/modalai_fc-v1_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/modalai_fc-v1_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/modalai_fc-v1_default.px4"},
    };
junwoo091400's avatar
junwoo091400 committed
340 341 342 343 344 345
    //////////////////////////////////// UVify FC firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t rgUVifyCoreFirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/uvify_core_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/uvify_core_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/uvify_core_default.px4"},
    };
346

Pritam Ghanghas's avatar
Pritam Ghanghas committed
347 348
    /////////////////////////////// px4flow firmwares ///////////////////////////////////////
    FirmwareToUrlElement_t rgPX4FLowFirmwareArray[] = {
349
        { PX4FlowPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Flow/master/px4flow.px4" },
350
    #if !defined(NO_ARDUPILOT_DIALECT)
DonLakeFlyer's avatar
DonLakeFlyer committed
351
        { PX4FlowAPM, StableFirmware, DefaultVehicleFirmware, "http://firmware.ardupilot.org/Tools/PX4Flow/px4flow-klt-latest.px4" },
352
    #endif
Pritam Ghanghas's avatar
Pritam Ghanghas committed
353 354 355 356
    };

    /////////////////////////////// 3dr radio firmwares ///////////////////////////////////////
    FirmwareToUrlElement_t rg3DRRadioFirmwareArray[] = {
357
        { ThreeDRRadio, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/SiK/stable/radio~hm_trp.ihx"}
Pritam Ghanghas's avatar
Pritam Ghanghas committed
358 359
    };

360
    // We build the maps for PX4 firmwares dynamically using the data below
Don Gagne's avatar
Don Gagne committed
361 362 363 364 365 366 367 368 369 370

#if 0
    Example URLs for PX4 and ArduPilot
    { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/px4fmu-v4_default.px4"},
    { AutoPilotStackAPM, StableFirmware,    CopterFirmware,         "http://firmware.ardupilot.org/Copter/stable/PX4/ArduCopter-v4.px4"},
    { AutoPilotStackAPM, DeveloperFirmware, CopterChibiosFirmware,  "http://firmware.ardupilot.org/Copter/latest/fmuv4/arducopter.apj"},
#endif

    QString px4Url          ("http://px4-travis.s3.amazonaws.com/Firmware/%1/px4fmu-%2_default.px4");

371
    QMap<FirmwareBuildType_t, QString> px4MapFirmwareTypeToDir;
Don Gagne's avatar
Don Gagne committed
372 373 374
    px4MapFirmwareTypeToDir[StableFirmware] =       QStringLiteral("stable");
    px4MapFirmwareTypeToDir[BetaFirmware] =         QStringLiteral("beta");
    px4MapFirmwareTypeToDir[DeveloperFirmware] =    QStringLiteral("master");
Don Gagne's avatar
Don Gagne committed
375 376

    // PX4 Firmwares
377
    for (const FirmwareBuildType_t& firmwareType: px4MapFirmwareTypeToDir.keys()) {
Don Gagne's avatar
Don Gagne committed
378
        QString dir = px4MapFirmwareTypeToDir[firmwareType];
379 380 381 382 383
        _rgFMUV5Firmware.insert     (FirmwareIdentifier(AutoPilotStackPX4, firmwareType, DefaultVehicleFirmware), px4Url.arg(dir).arg("v5"));
        _rgFMUV4PROFirmware.insert  (FirmwareIdentifier(AutoPilotStackPX4, firmwareType, DefaultVehicleFirmware), px4Url.arg(dir).arg("v4pro"));
        _rgFMUV4Firmware.insert     (FirmwareIdentifier(AutoPilotStackPX4, firmwareType, DefaultVehicleFirmware), px4Url.arg(dir).arg("v4"));
        _rgFMUV3Firmware.insert     (FirmwareIdentifier(AutoPilotStackPX4, firmwareType, DefaultVehicleFirmware), px4Url.arg(dir).arg("v3"));
        _rgPX4FMUV2Firmware.insert  (FirmwareIdentifier(AutoPilotStackPX4, firmwareType, DefaultVehicleFirmware), px4Url.arg(dir).arg("v2"));
Don Gagne's avatar
Don Gagne committed
384 385 386
    }

    int size = sizeof(rgAeroCoreFirmwareArray)/sizeof(rgAeroCoreFirmwareArray[0]);
Pritam Ghanghas's avatar
Pritam Ghanghas committed
387 388 389 390
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgAeroCoreFirmwareArray[i];
        _rgAeroCoreFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }
391

392 393 394 395 396 397
    size = sizeof(rgAUAVX2_1FirmwareArray)/sizeof(rgAUAVX2_1FirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgAUAVX2_1FirmwareArray[i];
        _rgAUAVX2_1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }

Henry Zhang's avatar
Henry Zhang committed
398 399 400 401 402 403
    size = sizeof(rgMindPXFMUV2FirmwareArray)/sizeof(rgMindPXFMUV2FirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgMindPXFMUV2FirmwareArray[i];
        _rgMindPXFMUV2Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }

404 405 406 407 408 409 410 411 412 413 414 415
    size = sizeof(rgTAPV1FirmwareArray)/sizeof(rgTAPV1FirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgTAPV1FirmwareArray[i];
        _rgTAPV1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }

    size = sizeof(rgASCV1FirmwareArray)/sizeof(rgASCV1FirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgASCV1FirmwareArray[i];
        _rgASCV1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }

416 417 418 419 420 421
    size = sizeof(rgCrazyflie2FirmwareArray)/sizeof(rgCrazyflie2FirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgCrazyflie2FirmwareArray[i];
        _rgCrazyflie2Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }

422 423 424 425 426
    size = sizeof(rgOmnibusF4SDFirmwareArray)/sizeof(rgOmnibusF4SDFirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgOmnibusF4SDFirmwareArray[i];
        _rgOmnibusF4SDFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }
427
    
428 429 430 431 432 433
    size = sizeof(rgKakuteF7FirmwareArray)/sizeof(rgKakuteF7FirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgKakuteF7FirmwareArray[i];
        _rgKakuteF7Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }

434 435 436 437 438 439
    size = sizeof(rgDurandalV1FirmwareArray)/sizeof(rgDurandalV1FirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgDurandalV1FirmwareArray[i];
        _rgDurandalV1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }

440 441 442 443 444
    size = sizeof(rgFMUK66V3FirmwareArray)/sizeof(rgFMUK66V3FirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgFMUK66V3FirmwareArray[i];
        _rgFMUK66V3Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }
445

446 447 448 449 450 451
    size = sizeof(rgModalFCV1FirmwareArray)/sizeof(rgModalFCV1FirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgModalFCV1FirmwareArray[i];
        _rgModalFCV1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }

junwoo091400's avatar
junwoo091400 committed
452 453 454 455 456 457
    size = sizeof(rgUVifyCoreFirmwareArray)/sizeof(rgUVifyCoreFirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgUVifyCoreFirmwareArray[i];
        _rgUVifyCoreFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }

Pritam Ghanghas's avatar
Pritam Ghanghas committed
458 459 460 461 462
    size = sizeof(rgPX4FLowFirmwareArray)/sizeof(rgPX4FLowFirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgPX4FLowFirmwareArray[i];
        _rgPX4FLowFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }
463

Pritam Ghanghas's avatar
Pritam Ghanghas committed
464 465 466 467 468
    size = sizeof(rg3DRRadioFirmwareArray)/sizeof(rg3DRRadioFirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rg3DRRadioFirmwareArray[i];
        _rg3DRRadioFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }
469 470 471 472 473 474

    size = sizeof(rg3DRRadioFirmwareArray)/sizeof(rg3DRRadioFirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rg3DRRadioFirmwareArray[i];
        _rg3DRRadioFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }
Pritam Ghanghas's avatar
Pritam Ghanghas committed
475
}
476

Pritam Ghanghas's avatar
Pritam Ghanghas committed
477 478 479 480 481 482
/// @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.");
}
483

484
QHash<FirmwareUpgradeController::FirmwareIdentifier, QString>* FirmwareUpgradeController::_firmwareHashForBoardId(int boardId)
Pritam Ghanghas's avatar
Pritam Ghanghas committed
485
{
486 487

    _rgFirmwareDynamic.clear();
488

489 490
    switch (boardId) {
    case Bootloader::boardIDPX4Flow:
491
        _rgFirmwareDynamic = _rgPX4FLowFirmware;
492
        break;
493
    case Bootloader::boardIDPX4FMUV2:
494
        _rgFirmwareDynamic = _rgPX4FMUV2Firmware;
495
        break;
DonLakeFlyer's avatar
DonLakeFlyer committed
496
    case Bootloader::boardIDPX4FMUV3:
497
        _rgFirmwareDynamic = _rgFMUV3Firmware;
498
        break;
499
    case Bootloader::boardIDPX4FMUV4:
500
        _rgFirmwareDynamic = _rgFMUV4Firmware;
501
        break;
502
    case Bootloader::boardIDPX4FMUV4PRO:
503
        _rgFirmwareDynamic = _rgFMUV4PROFirmware;
504
        break;
505
    case Bootloader::boardIDPX4FMUV5:
506
        _rgFirmwareDynamic = _rgFMUV5Firmware;
507
        break;
508
    case Bootloader::boardIDAeroCore:
509
        _rgFirmwareDynamic = _rgAeroCoreFirmware;
510
        break;
511
    case Bootloader::boardIDAUAVX2_1:
512
        _rgFirmwareDynamic = _rgAUAVX2_1Firmware;
513
        break;
Henry Zhang's avatar
Henry Zhang committed
514
    case Bootloader::boardIDMINDPXFMUV2:
515
        _rgFirmwareDynamic = _rgMindPXFMUV2Firmware;
516
        break;
517
    case Bootloader::boardIDTAPV1:
518
        _rgFirmwareDynamic = _rgTAPV1Firmware;
519
        break;
520
    case Bootloader::boardIDASCV1:
521
        _rgFirmwareDynamic = _rgASCV1Firmware;
522
        break;
523
    case Bootloader::boardIDCrazyflie2:
524
        _rgFirmwareDynamic = _rgCrazyflie2Firmware;
525
        break;
526
    case Bootloader::boardIDOmnibusF4SD:
527
        _rgFirmwareDynamic = _rgOmnibusF4SDFirmware;
528
        break;
529 530 531
    case Bootloader::boardIDKakuteF7:
        _rgFirmwareDynamic = _rgKakuteF7Firmware;
        break;
532 533 534
    case Bootloader::boardIDDurandalV1:
        _rgFirmwareDynamic = _rgDurandalV1Firmware;
        break;
535 536
    case Bootloader::boardIDFMUK66V3:
        _rgFirmwareDynamic = _rgFMUK66V3Firmware;
537
        break;
538 539 540
    case Bootloader::boardIDModalFCV1:
        _rgFirmwareDynamic = _rgModalFCV1Firmware;
        break;
junwoo091400's avatar
junwoo091400 committed
541 542 543
    case Bootloader::boardIDUVifyCore:
        _rgFirmwareDynamic = _rgUVifyCoreFirmware;
        break;
544
    case Bootloader::boardID3DRRadio:
545
        _rgFirmwareDynamic = _rg3DRRadioFirmware;
546
        break;
547
    default:
548 549
        // Unknown board id
        break;
550 551 552
    }

    return &_rgFirmwareDynamic;
553
}
Lorenz Meier's avatar
Lorenz Meier committed
554

555 556
void FirmwareUpgradeController::_getFirmwareFile(FirmwareIdentifier firmwareId)
{
557
    QHash<FirmwareIdentifier, QString>* prgFirmware = _firmwareHashForBoardId(static_cast<int>(_bootloaderBoardID));
558
    if (firmwareId.firmwareType == CustomFirmware) {
559 560
        _firmwareFilename = QString();
        _errorCancel(tr("Custom firmware selected but no filename given."));
561
    } else {
562 563
        if (prgFirmware->contains(firmwareId)) {
            _firmwareFilename = prgFirmware->value(firmwareId);
564
        } else {
565
            _errorCancel(tr("Unable to find specified firmware for board type"));
566 567
            return;
        }
568 569 570
    }
    
    if (_firmwareFilename.isEmpty()) {
571
        _errorCancel(tr("No firmware file selected"));
572 573 574 575 576 577 578 579 580 581
    } else {
        _downloadFirmware();
    }
}

/// @brief Begins the process of downloading the selected firmware file.
void FirmwareUpgradeController::_downloadFirmware(void)
{
    Q_ASSERT(!_firmwareFilename.isEmpty());
    
582 583
    _appendStatusLog(tr("Downloading firmware..."));
    _appendStatusLog(tr(" From: %1").arg(_firmwareFilename));
584
    
585 586 587 588 589
    QGCFileDownload* downloader = new QGCFileDownload(this);
    connect(downloader, &QGCFileDownload::downloadFinished, this, &FirmwareUpgradeController::_firmwareDownloadFinished);
    connect(downloader, &QGCFileDownload::downloadProgress, this, &FirmwareUpgradeController::_firmwareDownloadProgress);
    connect(downloader, &QGCFileDownload::error,            this, &FirmwareUpgradeController::_firmwareDownloadError);
    downloader->download(_firmwareFilename);
590 591 592
}

/// @brief Updates the progress indicator while downloading
593
void FirmwareUpgradeController::_firmwareDownloadProgress(qint64 curr, qint64 total)
594 595 596
{
    // Take care of cases where 0 / 0 is emitted as error return value
    if (total > 0) {
597
        _progressBar->setProperty("value", static_cast<float>(curr) / static_cast<float>(total));
598 599 600 601
    }
}

/// @brief Called when the firmware download completes.
602
void FirmwareUpgradeController::_firmwareDownloadFinished(QString remoteFile, QString localFile)
603
{
604 605
    Q_UNUSED(remoteFile);

606
    _appendStatusLog(tr("Download complete"));
607
    
608
    FirmwareImage* image = new FirmwareImage(this);
609
    
610 611
    connect(image, &FirmwareImage::statusMessage, this, &FirmwareUpgradeController::_status);
    connect(image, &FirmwareImage::errorMessage, this, &FirmwareUpgradeController::_error);
612
    
613
    if (!image->load(localFile, _bootloaderBoardID)) {
614
        _errorCancel(tr("Image load failed"));
615
        return;
616 617
    }
    
618 619
    // We can't proceed unless we have the bootloader
    if (!_bootloaderFound) {
620
        _errorCancel(tr("Bootloader not found"));
621 622
        return;
    }
623
    
624
    if (_bootloaderBoardFlashSize != 0 && image->imageSize() > _bootloaderBoardFlashSize) {
625
        _errorCancel(tr("Image size of %1 is too large for board flash size %2").arg(image->imageSize()).arg(_bootloaderBoardFlashSize));
626
        return;
627
    }
628 629

    _threadController->flash(image);
630 631
}

632
/// @brief Called when an error occurs during download
633
void FirmwareUpgradeController::_firmwareDownloadError(QString errorMsg)
634
{
635
    _errorCancel(errorMsg);
636 637
}

638
/// @brief returns firmware type as a string
639
QString FirmwareUpgradeController::firmwareTypeAsString(FirmwareBuildType_t type) const
640 641 642 643 644 645 646 647 648 649 650 651 652
{
    switch (type) {
    case StableFirmware:
        return "stable";
    case DeveloperFirmware:
        return "developer";
    case BetaFirmware:
        return "beta";
    default:
        return "custom";
    }
}

653 654 655
/// @brief Signals completion of one of the specified bootloader commands. Moves the state machine to the
///         appropriate next step.
void FirmwareUpgradeController::_flashComplete(void)
656
{
657
    delete _image;
658
    _image = nullptr;
659
    
660
    _appendStatusLog(tr("Upgrade complete"), true);
661 662
    _appendStatusLog("------------------------------------------", false);
    emit flashComplete();
663
    qgcApp()->toolbox()->linkManager()->setConnectionsAllowed();
664 665
}

666
void FirmwareUpgradeController::_error(const QString& errorString)
667
{
668
    delete _image;
669
    _image = nullptr;
670 671
    
    _errorCancel(QString("Error: %1").arg(errorString));
672 673
}

674
void FirmwareUpgradeController::_status(const QString& statusString)
675
{
676
    _appendStatusLog(statusString);
677 678 679 680 681
}

/// @brief Updates the progress bar from long running bootloader commands
void FirmwareUpgradeController::_updateProgress(int curr, int total)
{
682 683
    // Take care of cases where 0 / 0 is emitted as error return value
    if (total > 0) {
684
        _progressBar->setProperty("value", static_cast<float>(curr) / static_cast<float>(total));
685
    }
686 687 688 689 690 691
}

/// @brief Moves the progress bar ahead on tick while erasing the board
void FirmwareUpgradeController::_eraseProgressTick(void)
{
    _eraseTickCount++;
692
    _progressBar->setProperty("value", static_cast<float>(_eraseTickCount*_eraseTickMsec) / static_cast<float>(_eraseTotalMsec));
693 694 695
}

/// Appends the specified text to the status log area in the ui
696
void FirmwareUpgradeController::_appendStatusLog(const QString& text, bool critical)
697 698 699 700
{
    Q_ASSERT(_statusLog);
    
    QVariant returnedValue;
701 702 703 704 705 706 707 708
    QVariant varText;
    
    if (critical) {
        varText = QString("<font color=\"yellow\">%1</font>").arg(text);
    } else {
        varText = text;
    }
    
709 710 711 712
    QMetaObject::invokeMethod(_statusLog,
                              "append",
                              Q_RETURN_ARG(QVariant, returnedValue),
                              Q_ARG(QVariant, varText));
Don Gagne's avatar
Don Gagne committed
713
}
714

715 716 717
void FirmwareUpgradeController::_errorCancel(const QString& msg)
{
    _appendStatusLog(msg, false);
718
    _appendStatusLog(tr("Upgrade cancelled"), true);
719 720 721
    _appendStatusLog("------------------------------------------", false);
    emit error();
    cancel();
722
    qgcApp()->toolbox()->linkManager()->setConnectionsAllowed();
723 724 725 726 727 728 729 730 731 732
}

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)
733
{
734
    _eraseTimer.stop();
735
}
736

737
void FirmwareUpgradeController::setSelectedFirmwareBuildType(FirmwareBuildType_t firmwareType)
738
{
739 740 741
    _selectedFirmwareBuildType = firmwareType;
    emit selectedFirmwareBuildTypeChanged(_selectedFirmwareBuildType);
    _buildAPMFirmwareNames();
742 743
}

744
void FirmwareUpgradeController::_buildAPMFirmwareNames(void)
745
{
746
#if !defined(NO_ARDUPILOT_DIALECT)
747

Don Gagne's avatar
Don Gagne committed
748 749 750 751 752
    bool                    chibios =           _apmChibiOSSetting->rawValue().toInt() == 0;
    FirmwareVehicleType_t   vehicleType =       static_cast<FirmwareVehicleType_t>(_apmVehicleTypeSetting->rawValue().toInt());
    QString                 boardDescription =  _foundBoardInfo.description();
    quint16                 boardVID =          _foundBoardInfo.vendorIdentifier();
    quint16                 boardPID =          _foundBoardInfo.productIdentifier();
753
    uint32_t                rawBoardId =        _bootloaderBoardID == Bootloader::boardIDPX4FMUV3 ? Bootloader::boardIDPX4FMUV2 : _bootloaderBoardID;
Don Gagne's avatar
Don Gagne committed
754 755

    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);
756

757
    _apmFirmwareNames.clear();
758
    _apmFirmwareNamesBestIndex = -1;
759
    _apmFirmwareUrls.clear();
760

761
    QString apmDescriptionSuffix("-BL");
762
    QString boardDescriptionPrefix;
Don Gagne's avatar
Don Gagne committed
763
    bool    bootloaderMatch = boardDescription.endsWith(apmDescriptionSuffix);
764

765
    int currentIndex = 0;
766 767
    for (const ManifestFirmwareInfo_t& firmwareInfo: _rgManifestFirmwareInfo) {
        bool match = false;
768 769 770
        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;
771
            } else {
772 773 774 775 776
                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;
777
                }
778
            }
Don Gagne's avatar
Don Gagne committed
779
        }
780

781 782 783
        if (match) {
            _apmFirmwareNames.append(firmwareInfo.friendlyName);
            _apmFirmwareUrls.append(firmwareInfo.url);
784
            currentIndex++;
785 786 787
        }
    }

788 789 790 791 792 793
    if (_apmFirmwareNamesBestIndex == -1) {
        _apmFirmwareNamesBestIndex++;
        if (_apmFirmwareNames.count() > 1) {
            _apmFirmwareNames.prepend(tr("Choose board type"));
            _apmFirmwareUrls.prepend(QString());
        }
794 795
    }

796
    emit apmFirmwareNamesChanged();
797
#endif
798 799
}

800
FirmwareUpgradeController::FirmwareVehicleType_t FirmwareUpgradeController::vehicleTypeFromFirmwareSelectionIndex(int index)
801 802 803
{
    if (index < 0 || index >= _apmVehicleTypeFromCurrentVersionList.count()) {
        qWarning() << "Invalid index, index:count" << index << _apmVehicleTypeFromCurrentVersionList.count();
Don Gagne's avatar
Don Gagne committed
804
        return CopterFirmware;
805 806 807 808
    }

    return _apmVehicleTypeFromCurrentVersionList[index];
}
Don Gagne's avatar
Don Gagne committed
809 810 811 812

void FirmwareUpgradeController::_determinePX4StableVersion(void)
{
    QGCFileDownload* downloader = new QGCFileDownload(this);
813 814 815
    connect(downloader, &QGCFileDownload::downloadFinished, this, &FirmwareUpgradeController::_px4ReleasesGithubDownloadFinished);
    connect(downloader, &QGCFileDownload::error, this, &FirmwareUpgradeController::_px4ReleasesGithubDownloadError);
    downloader->download(QStringLiteral("https://api.github.com/repos/PX4/Firmware/releases"));
Don Gagne's avatar
Don Gagne committed
816 817
}

818
void FirmwareUpgradeController::_px4ReleasesGithubDownloadFinished(QString remoteFile, QString localFile)
Don Gagne's avatar
Don Gagne committed
819 820 821 822 823
{
    Q_UNUSED(remoteFile);

    QFile jsonFile(localFile);
    if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
824
        qCWarning(FirmwareUpgradeLog) << "Unable to open github px4 releases json file" << localFile << jsonFile.errorString();
Don Gagne's avatar
Don Gagne committed
825 826 827 828 829 830 831 832
        return;
    }
    QByteArray bytes = jsonFile.readAll();
    jsonFile.close();

    QJsonParseError jsonParseError;
    QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError);
    if (jsonParseError.error != QJsonParseError::NoError) {
833
        qCWarning(FirmwareUpgradeLog) <<  "Unable to open px4 releases json document" << localFile << jsonParseError.errorString();
Don Gagne's avatar
Don Gagne committed
834 835 836
        return;
    }

837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
    // 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; i<releases.count() && (!foundStable || !foundBeta); i++) {
        QJsonObject release = releases[i].toObject();
        if (!foundStable && !release["prerelease"].toBool()) {
            _px4StableVersion = release["name"].toString();
            emit px4StableVersionChanged(_px4StableVersion);
            qCDebug(FirmwareUpgradeLog()) << "Found px4 stable version" << _px4StableVersion;
            foundStable = true;
        } else if (!foundBeta && release["prerelease"].toBool()) {
            _px4BetaVersion = release["name"].toString();
            emit px4StableVersionChanged(_px4BetaVersion);
            qCDebug(FirmwareUpgradeLog()) << "Found px4 beta version" << _px4BetaVersion;
            foundBeta = true;
        }
    }

    if (!foundStable) {
        qCDebug(FirmwareUpgradeLog()) << "Unable to find px4 stable version" << localFile;
    }
    if (!foundBeta) {
        qCDebug(FirmwareUpgradeLog()) << "Unable to find px4 beta version" << localFile;
    }
Don Gagne's avatar
Don Gagne committed
869 870
}

871
void FirmwareUpgradeController::_px4ReleasesGithubDownloadError(QString errorMsg)
Don Gagne's avatar
Don Gagne committed
872
{
873
    qCWarning(FirmwareUpgradeLog) << "PX4 releases github download failed" << errorMsg;
Don Gagne's avatar
Don Gagne committed
874
}
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893

void FirmwareUpgradeController::_downloadArduPilotManifest(void)
{
    _downloadingFirmwareList = true;
    emit downloadingFirmwareListChanged(true);

    QGCFileDownload* downloader = new QGCFileDownload(this);
    connect(downloader, &QGCFileDownload::downloadFinished, this, &FirmwareUpgradeController::_ardupilotManifestDownloadFinished);
    connect(downloader, &QGCFileDownload::error,            this, &FirmwareUpgradeController::_ardupilotManifestDownloadError);
    downloader->download(QStringLiteral("http://firmware.ardupilot.org/manifest.json.gz"));
}

void FirmwareUpgradeController::_ardupilotManifestDownloadFinished(QString remoteFile, QString localFile)
{
    Q_UNUSED(remoteFile);

    // Delete the QGCFileDownload object
    sender()->deleteLater();

894
    qCDebug(FirmwareUpgradeLog) << "_ardupilotManifestDownloadFinished" << remoteFile << localFile;
895

896 897 898
    QString jsonFileName(QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation)).absoluteFilePath("ArduPilot.Manifest.json"));
    if (!QGCZlib::inflateGzipFile(localFile, jsonFileName)) {
        qCWarning(FirmwareUpgradeLog) << "Inflate of compressed manifest failed" << localFile;
899 900 901
        return;
    }

902 903 904 905
    QString         errorString;
    QJsonDocument   doc;
    if (JsonHelper::isJsonFile(jsonFileName, doc, errorString)) {
        qCWarning(FirmwareUpgradeLog) << "Json file read failed" << errorString;
906 907
        return;
    }
908

909 910 911 912 913 914 915 916 917
    QJsonObject json =          doc.object();
    QJsonArray  rgFirmware =    json[_manifestFirmwareJsonKey].toArray();

    for (int i=0; i<rgFirmware.count(); i++) {
        const QJsonObject& firmwareJson = rgFirmware[i].toObject();

        FirmwareVehicleType_t   firmwareVehicleType =   _manifestMavTypeToFirmwareVehicleType(firmwareJson[_manifestMavTypeJsonKey].toString());
        FirmwareBuildType_t     firmwareBuildType =     _manifestMavFirmwareVersionTypeToFirmwareBuildType(firmwareJson[_manifestMavFirmwareVersionTypeJsonKey].toString());
        QString                 format =                firmwareJson[_manifestFormatJsonKey].toString();
Don Gagne's avatar
Don Gagne committed
918
        QString                 platform =              firmwareJson[_manifestPlatformKey].toString();
919 920

        if (firmwareVehicleType != DefaultVehicleFirmware && firmwareBuildType != CustomFirmware && (format == QStringLiteral("apj") || format == QStringLiteral("px4"))) {
Don Gagne's avatar
Don Gagne committed
921
            if (platform.contains("-heli") && firmwareVehicleType != HeliFirmware) {
922 923 924 925 926 927 928 929 930 931 932 933
                continue;
            }

            _rgManifestFirmwareInfo.append(ManifestFirmwareInfo_t());
            ManifestFirmwareInfo_t& firmwareInfo = _rgManifestFirmwareInfo.last();

            firmwareInfo.boardId =              static_cast<uint32_t>(firmwareJson[_manifestBoardIdJsonKey].toInt());
            firmwareInfo.firmwareBuildType =    firmwareBuildType;
            firmwareInfo.vehicleType =          firmwareVehicleType;
            firmwareInfo.url =                  firmwareJson[_manifestUrlJsonKey].toString();
            firmwareInfo.version =              firmwareJson[_manifestMavFirmwareVersionJsonKey].toString();
            firmwareInfo.chibios =              format == QStringLiteral("apj");
Don Gagne's avatar
Don Gagne committed
934
            firmwareInfo.fmuv2 =                platform.contains(QStringLiteral("fmuv2"));
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952

            QJsonArray bootloaderArray = firmwareJson[_manifestBootloaderStrJsonKey].toArray();
            for (int j=0; j<bootloaderArray.count(); j++) {
                firmwareInfo.rgBootloaderPortString.append(bootloaderArray[j].toString());
            }

            QJsonArray usbidArray = firmwareJson[_manifestUSBIDJsonKey].toArray();
            for (int j=0; j<usbidArray.count(); j++) {
                QStringList vidpid = usbidArray[j].toString().split('/');
                QString vid = vidpid[0];
                QString pid = vidpid[1];

                bool ok;
                firmwareInfo.rgVID.append(vid.right(vid.count() - 2).toInt(&ok, 16));
                firmwareInfo.rgPID.append(pid.right(pid.count() - 2).toInt(&ok, 16));
            }

            QString brandName = firmwareJson[_manifestBrandNameKey].toString();
Don Gagne's avatar
Don Gagne committed
953
            firmwareInfo.friendlyName = QStringLiteral("%1 - %2").arg(brandName.isEmpty() ? platform : brandName).arg(firmwareInfo.version);
954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
        }
    }

    if (_bootloaderFound) {
        _buildAPMFirmwareNames();
    }

    _downloadingFirmwareList = false;
    emit downloadingFirmwareListChanged(false);
}

void FirmwareUpgradeController::_ardupilotManifestDownloadError(QString errorMsg)
{
    qCWarning(FirmwareUpgradeLog) << "ArduPilot Manifest download failed" << errorMsg;
}

FirmwareUpgradeController::FirmwareBuildType_t FirmwareUpgradeController::_manifestMavFirmwareVersionTypeToFirmwareBuildType(const QString& manifestMavFirmwareVersionType)
{
    if (_manifestMavFirmwareVersionTypeToFirmwareBuildTypeMap.contains(manifestMavFirmwareVersionType)) {
        return _manifestMavFirmwareVersionTypeToFirmwareBuildTypeMap[manifestMavFirmwareVersionType];
    } else {
        return CustomFirmware;
    }
}

FirmwareUpgradeController::FirmwareVehicleType_t FirmwareUpgradeController::_manifestMavTypeToFirmwareVehicleType(const QString& manifestMavType)
{
    if (_manifestMavTypeToFirmwareVehicleTypeMap.contains(manifestMavType)) {
        return _manifestMavTypeToFirmwareVehicleTypeMap[manifestMavType];
    } else {
        return DefaultVehicleFirmware;
    }
}