FirmwareUpgradeController.cc 46.2 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)
DonLakeFlyer's avatar
DonLakeFlyer committed
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
    connect(_threadController, &PX4FirmwareUpgradeThreadController::foundBoard,             this, &FirmwareUpgradeController::_foundBoard);
    connect(_threadController, &PX4FirmwareUpgradeThreadController::noBoardFound,           this, &FirmwareUpgradeController::_noBoardFound);
    connect(_threadController, &PX4FirmwareUpgradeThreadController::boardGone,              this, &FirmwareUpgradeController::_boardGone);
87
    connect(_threadController, &PX4FirmwareUpgradeThreadController::foundBoardInfo,         this, &FirmwareUpgradeController::_foundBoardInfo);
88 89 90 91 92 93 94 95
    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);
    
96
    connect(&_eraseTimer, &QTimer::timeout, this, &FirmwareUpgradeController::_eraseProgressTick);
97

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

201
    _startFlashWhenBootloaderFound = false;
202

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

218 219

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

void FirmwareUpgradeController::_boardGone(void)
{
    emit boardGone();
227 228 229 230
}

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

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

    emit bootloaderFound();
252 253 254
}


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

Pritam Ghanghas's avatar
Pritam Ghanghas committed
265 266 267 268 269 270
    //////////////////////////////////// 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"},
    };
271

272 273 274 275 276 277
    //////////////////////////////////// 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
278 279 280 281 282 283
    //////////////////////////////////// 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"},
    };
284 285 286 287 288
    //////////////////////////////////// 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"},
289
        { SingleFirmwareMode,StableFirmware,    DefaultVehicleFirmware, _singleFirmwareURL},
290 291 292 293 294 295 296
    };
    //////////////////////////////////// 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"},
    };
297 298 299 300 301 302 303 304

    //////////////////////////////////// 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"},
    };

305
    //////////////////////////////////// Omnibus F4 SD firmwares //////////////////////////////////////////////////
306 307 308 309 310
    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"},
    };
311 312 313 314 315 316 317
    
    //////////////////////////////////// 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"},
    };
318 319 320 321 322 323 324

    //////////////////////////////////// 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"},
    };
325
    
326 327 328 329 330 331
    //////////////////////////////////// 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"},
    };
332 333 334 335 336 337 338

    //////////////////////////////////// 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
339 340 341 342 343 344
    //////////////////////////////////// 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"},
    };
345

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

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

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

#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");

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

    // PX4 Firmwares
376
    for (const FirmwareBuildType_t& firmwareType: px4MapFirmwareTypeToDir.keys()) {
Don Gagne's avatar
Don Gagne committed
377
        QString dir = px4MapFirmwareTypeToDir[firmwareType];
378 379 380 381 382
        _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
383 384 385
    }

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

391 392 393 394 395 396
    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
397 398 399 400 401 402
    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);
    }

403 404 405 406 407 408 409 410 411 412 413 414
    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);
    }

415 416 417 418 419 420
    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);
    }

421 422 423 424 425
    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);
    }
426
    
427 428 429 430 431 432
    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);
    }

433 434 435 436 437 438
    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);
    }

439 440 441 442 443
    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);
    }
444

445 446 447 448 449 450
    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
451 452 453 454 455 456
    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
457 458 459 460 461
    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);
    }
462

Pritam Ghanghas's avatar
Pritam Ghanghas committed
463 464 465 466 467
    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);
    }
468 469 470 471 472 473

    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
474
}
475

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

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

    _rgFirmwareDynamic.clear();
487

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

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

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

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

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

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

    _threadController->flash(image);
627 628 629
    } else {
        _errorCancel(errorMsg);
    }
630 631
}

632
/// @brief returns firmware type as a string
633
QString FirmwareUpgradeController::firmwareTypeAsString(FirmwareBuildType_t type) const
634 635 636 637 638 639 640 641 642 643 644 645 646
{
    switch (type) {
    case StableFirmware:
        return "stable";
    case DeveloperFirmware:
        return "developer";
    case BetaFirmware:
        return "beta";
    default:
        return "custom";
    }
}

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

660
void FirmwareUpgradeController::_error(const QString& errorString)
661
{
662
    delete _image;
663
    _image = nullptr;
664 665
    
    _errorCancel(QString("Error: %1").arg(errorString));
666 667
}

668
void FirmwareUpgradeController::_status(const QString& statusString)
669
{
670
    _appendStatusLog(statusString);
671 672 673 674 675
}

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

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

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

709 710 711
void FirmwareUpgradeController::_errorCancel(const QString& msg)
{
    _appendStatusLog(msg, false);
712
    _appendStatusLog(tr("Upgrade cancelled"), true);
713 714 715
    _appendStatusLog("------------------------------------------", false);
    emit error();
    cancel();
716
    qgcApp()->toolbox()->linkManager()->setConnectionsAllowed();
717 718 719 720 721 722 723 724 725 726
}

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)
727
{
728
    _eraseTimer.stop();
729
}
730

731
void FirmwareUpgradeController::setSelectedFirmwareBuildType(FirmwareBuildType_t firmwareType)
732
{
733 734 735
    _selectedFirmwareBuildType = firmwareType;
    emit selectedFirmwareBuildTypeChanged(_selectedFirmwareBuildType);
    _buildAPMFirmwareNames();
736 737
}

738
void FirmwareUpgradeController::_buildAPMFirmwareNames(void)
739
{
740
#if !defined(NO_ARDUPILOT_DIALECT)
Don Gagne's avatar
Don Gagne committed
741 742
    bool                    chibios =           _apmChibiOSSetting->rawValue().toInt() == 0;
    FirmwareVehicleType_t   vehicleType =       static_cast<FirmwareVehicleType_t>(_apmVehicleTypeSetting->rawValue().toInt());
743 744 745
    QString                 boardDescription =  _boardInfo.description();
    quint16                 boardVID =          _boardInfo.vendorIdentifier();
    quint16                 boardPID =          _boardInfo.productIdentifier();
746
    uint32_t                rawBoardId =        _bootloaderBoardID == Bootloader::boardIDPX4FMUV3 ? Bootloader::boardIDPX4FMUV2 : _bootloaderBoardID;
Don Gagne's avatar
Don Gagne committed
747

DonLakeFlyer's avatar
DonLakeFlyer committed
748 749 750 751
    if (_boardType == QGCSerialPortInfo::BoardTypePX4Flow) {
        return;
    }

Don Gagne's avatar
Don Gagne committed
752
    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);
753

754
    _apmFirmwareNames.clear();
755
    _apmFirmwareNamesBestIndex = -1;
756
    _apmFirmwareUrls.clear();
757

758
    QString apmDescriptionSuffix("-BL");
759
    QString boardDescriptionPrefix;
Don Gagne's avatar
Don Gagne committed
760
    bool    bootloaderMatch = boardDescription.endsWith(apmDescriptionSuffix);
761

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

778 779 780
        if (match) {
            _apmFirmwareNames.append(firmwareInfo.friendlyName);
            _apmFirmwareUrls.append(firmwareInfo.url);
781
            currentIndex++;
782 783 784
        }
    }

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

793
    emit apmFirmwareNamesChanged();
794
#endif
795 796
}

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

    return _apmVehicleTypeFromCurrentVersionList[index];
}
Don Gagne's avatar
Don Gagne committed
806 807 808 809

void FirmwareUpgradeController::_determinePX4StableVersion(void)
{
    QGCFileDownload* downloader = new QGCFileDownload(this);
810
    connect(downloader, &QGCFileDownload::downloadComplete, this, &FirmwareUpgradeController::_px4ReleasesGithubDownloadComplete);
811
    downloader->download(QStringLiteral("https://api.github.com/repos/PX4/Firmware/releases"));
Don Gagne's avatar
Don Gagne committed
812 813
}

814
void FirmwareUpgradeController::_px4ReleasesGithubDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg)
Don Gagne's avatar
Don Gagne committed
815
{
816 817 818 819 820 821 822 823
    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();
Don Gagne's avatar
Don Gagne committed
824

825 826 827 828 829 830
        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;
        }
Don Gagne's avatar
Don Gagne committed
831

832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
        // 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;
            }
856 857
        }

858 859 860 861 862 863 864 865
        if (!foundStable) {
            qCDebug(FirmwareUpgradeLog()) << "Unable to find px4 stable version" << localFile;
        }
        if (!foundBeta) {
            qCDebug(FirmwareUpgradeLog()) << "Unable to find px4 beta version" << localFile;
        }
    } else {
        qCWarning(FirmwareUpgradeLog) << "PX4 releases github download failed" << errorMsg;
866
    }
Don Gagne's avatar
Don Gagne committed
867 868
}

869 870 871 872 873 874
void FirmwareUpgradeController::_downloadArduPilotManifest(void)
{
    _downloadingFirmwareList = true;
    emit downloadingFirmwareListChanged(true);

    QGCFileDownload* downloader = new QGCFileDownload(this);
875
    connect(downloader, &QGCFileDownload::downloadComplete, this, &FirmwareUpgradeController::_ardupilotManifestDownloadComplete);
876 877 878
    downloader->download(QStringLiteral("http://firmware.ardupilot.org/manifest.json.gz"));
}

879
void FirmwareUpgradeController::_ardupilotManifestDownloadComplete(QString remoteFile, QString localFile, QString errorMsg)
880
{
881 882 883
    if (errorMsg.isEmpty()) {
        // Delete the QGCFileDownload object
        sender()->deleteLater();
884

885
        qCDebug(FirmwareUpgradeLog) << "_ardupilotManifestDownloadFinished" << remoteFile << localFile;
886

887 888 889 890 891
        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;
        }
892

893 894 895 896 897 898
        QString         errorString;
        QJsonDocument   doc;
        if (!JsonHelper::isJsonFile(jsonFileName, doc, errorString)) {
            qCWarning(FirmwareUpgradeLog) << "Json file read failed" << errorString;
            return;
        }
899

900 901
        QJsonObject json =          doc.object();
        QJsonArray  rgFirmware =    json[_manifestFirmwareJsonKey].toArray();
902

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

906 907 908 909
            FirmwareVehicleType_t   firmwareVehicleType =   _manifestMavTypeToFirmwareVehicleType(firmwareJson[_manifestMavTypeJsonKey].toString());
            FirmwareBuildType_t     firmwareBuildType =     _manifestMavFirmwareVersionTypeToFirmwareBuildType(firmwareJson[_manifestMavFirmwareVersionTypeJsonKey].toString());
            QString                 format =                firmwareJson[_manifestFormatJsonKey].toString();
            QString                 platform =              firmwareJson[_manifestPlatformKey].toString();
910

911 912 913 914
            if (firmwareVehicleType != DefaultVehicleFirmware && firmwareBuildType != CustomFirmware && (format == QStringLiteral("apj") || format == QStringLiteral("px4"))) {
                if (platform.contains("-heli") && firmwareVehicleType != HeliFirmware) {
                    continue;
                }
915

916 917
                _rgManifestFirmwareInfo.append(ManifestFirmwareInfo_t());
                ManifestFirmwareInfo_t& firmwareInfo = _rgManifestFirmwareInfo.last();
918

919 920 921 922 923 924
                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");                firmwareInfo.fmuv2 =                platform.contains(QStringLiteral("fmuv2"));
925

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

931 932 933 934 935
                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];
936

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

942 943
                QString brandName = firmwareJson[_manifestBrandNameKey].toString();
                firmwareInfo.friendlyName = QStringLiteral("%1 - %2").arg(brandName.isEmpty() ? platform : brandName).arg(firmwareInfo.version);
944
            }
945
        }
946

947 948
        if (_bootloaderFound) {
            _buildAPMFirmwareNames();
949 950
        }

951 952 953 954
        _downloadingFirmwareList = false;
        emit downloadingFirmwareListChanged(false);
    } else {
        qCWarning(FirmwareUpgradeLog) << "ArduPilot Manifest download failed" << errorMsg;
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
    }
}

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;
    }
}