FirmwareUpgradeController.cc 42.7 KB
Newer Older
1 2 3 4 5 6 7 8
/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * 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"
Gus Grubba's avatar
Gus Grubba committed
12
//-- TODO: #include "QGCQFileDialog.h"
13
#include "QGCApplication.h"
14
#include "QGCFileDownload.h"
15 16
#include "QGCOptions.h"
#include "QGCCorePlugin.h"
17 18
#include "FirmwareUpgradeSettings.h"
#include "SettingsManager.h"
Pritam Ghanghas's avatar
Pritam Ghanghas committed
19

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

27 28 29 30 31 32 33 34 35 36 37 38 39
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
40
struct FirmwareToUrlElement_t {
41 42 43 44
    FirmwareUpgradeController::AutoPilotStackType_t     stackType;
    FirmwareUpgradeController::FirmwareBuildType_t      firmwareType;
    FirmwareUpgradeController::FirmwareVehicleType_t    vehicleType;
    QString                                             url;
Pritam Ghanghas's avatar
Pritam Ghanghas committed
45 46
};

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

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

78 79 80
    _threadController = new PX4FirmwareUpgradeThreadController(this);
    Q_CHECK_PTR(_threadController);

81 82 83 84 85 86 87 88 89 90 91 92 93
    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);
    
94
    connect(&_eraseTimer, &QTimer::timeout, this, &FirmwareUpgradeController::_eraseProgressTick);
95

96 97 98
    connect(_apmChibiOSSetting,     &Fact::rawValueChanged, this, &FirmwareUpgradeController::_buildAPMFirmwareNames);
    connect(_apmVehicleTypeSetting, &Fact::rawValueChanged, this, &FirmwareUpgradeController::_buildAPMFirmwareNames);

99
    _initFirmwareHash();
Don Gagne's avatar
Don Gagne committed
100
    _determinePX4StableVersion();
101
    _downloadArduPilotManifest();
102 103
}

Don Gagne's avatar
Don Gagne committed
104 105 106 107 108
FirmwareUpgradeController::~FirmwareUpgradeController()
{
    qgcApp()->toolbox()->linkManager()->setConnectionsAllowed();
}

109
void FirmwareUpgradeController::startBoardSearch(void)
110
{
Don Gagne's avatar
Don Gagne committed
111 112 113 114
    LinkManager* linkMgr = qgcApp()->toolbox()->linkManager();

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

115 116
    // FIXME: Why did we get here with active vehicle?
    if (!qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()) {
Don Gagne's avatar
Don Gagne committed
117 118 119
        // We have to disconnect any inactive links
        linkMgr->disconnectAll();
    }
120

121 122 123
    _bootloaderFound = false;
    _startFlashWhenBootloaderFound = false;
    _threadController->startFindBoardLoop();
124 125
}

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

153 154 155 156 157
void FirmwareUpgradeController::flash(const FirmwareIdentifier& firmwareId)
{
    flash(firmwareId.autopilotStackType, firmwareId.firmwareType, firmwareId.firmwareVehicleType);
}

158
void FirmwareUpgradeController::flashSingleFirmwareMode(FirmwareBuildType_t firmwareType)
159
{
Gus Grubba's avatar
Gus Grubba committed
160
    flash(SingleFirmwareMode, firmwareType, DefaultVehicleFirmware);
161 162
}

163
void FirmwareUpgradeController::cancel(void)
164
{
165 166 167
    _eraseTimer.stop();
    _threadController->cancel();
}
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
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;
}

186
void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPortInfo& info, int boardType, QString boardName)
187
{
188 189 190 191 192 193
    _foundBoardInfo =       info;
    _foundBoardType =       static_cast<QGCSerialPortInfo::BoardType_t>(boardType);
    _foundBoardTypeName =   boardName;

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

194
    _startFlashWhenBootloaderFound = false;
195

196
    if (_foundBoardType == QGCSerialPortInfo::BoardTypeSiKRadio) {
197 198 199 200 201 202 203 204
        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);
        }
205
    }
206
    
207
    qCDebug(FirmwareUpgradeLog) << _foundBoardType << _foundBoardTypeName;
208
    emit boardFound();
209 210
}

211 212

void FirmwareUpgradeController::_noBoardFound(void)
213
{
214 215 216 217 218 219
    emit noBoardFound();
}

void FirmwareUpgradeController::_boardGone(void)
{
    emit boardGone();
220 221 222 223 224 225
}

/// @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)
{
226
    _bootloaderFound = true;
227 228 229
    _bootloaderVersion = static_cast<uint32_t>(bootloaderVersion);
    _bootloaderBoardID = static_cast<uint32_t>(boardID);
    _bootloaderBoardFlashSize = static_cast<uint32_t>(flashSize);
230
    
231 232 233 234
    _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));
235
    
236
    if (_startFlashWhenBootloaderFound) {
237
        flash(_startFlashWhenBootloaderFoundFirmwareIdentity);
238
    }
239

240 241 242
    if (_rgManifestFirmwareInfo.count()) {
        _buildAPMFirmwareNames();
    }
243 244 245
}


Pritam Ghanghas's avatar
Pritam Ghanghas committed
246 247 248
/// @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()
249
{
Pritam Ghanghas's avatar
Pritam Ghanghas committed
250 251 252 253 254
    // indirect check whether this function has been called before or not
    // may have to be modified if _rgPX4FMUV2Firmware disappears
    if (!_rgPX4FMUV2Firmware.isEmpty()) {
        return;
    }
255

Pritam Ghanghas's avatar
Pritam Ghanghas committed
256 257 258 259 260 261
    //////////////////////////////////// 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"},
    };
262

263 264 265 266 267 268
    //////////////////////////////////// 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
269 270 271 272 273 274
    //////////////////////////////////// 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"},
    };
275 276 277 278 279
    //////////////////////////////////// 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"},
280
        { SingleFirmwareMode,StableFirmware,    DefaultVehicleFirmware, _singleFirmwareURL},
281 282 283 284 285 286 287
    };
    //////////////////////////////////// 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"},
    };
288 289 290 291 292 293 294 295

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

296 297 298 299 300
    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"},
    };
301 302 303 304 305 306 307
    
    //////////////////////////////////// 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"},
    };
308

Pritam Ghanghas's avatar
Pritam Ghanghas committed
309 310
    /////////////////////////////// px4flow firmwares ///////////////////////////////////////
    FirmwareToUrlElement_t rgPX4FLowFirmwareArray[] = {
311
        { PX4FlowPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Flow/master/px4flow.px4" },
312
    #if !defined(NO_ARDUPILOT_DIALECT)
DonLakeFlyer's avatar
DonLakeFlyer committed
313
        { PX4FlowAPM, StableFirmware, DefaultVehicleFirmware, "http://firmware.ardupilot.org/Tools/PX4Flow/px4flow-klt-latest.px4" },
314
    #endif
Pritam Ghanghas's avatar
Pritam Ghanghas committed
315 316 317 318
    };

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

322
    // We build the maps for PX4 firmwares dynamically using the data below
Don Gagne's avatar
Don Gagne committed
323 324 325 326 327 328 329 330 331 332

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

333
    QMap<FirmwareBuildType_t, QString> px4MapFirmwareTypeToDir;
Don Gagne's avatar
Don Gagne committed
334 335 336
    px4MapFirmwareTypeToDir[StableFirmware] =       QStringLiteral("stable");
    px4MapFirmwareTypeToDir[BetaFirmware] =         QStringLiteral("beta");
    px4MapFirmwareTypeToDir[DeveloperFirmware] =    QStringLiteral("master");
Don Gagne's avatar
Don Gagne committed
337 338

    // PX4 Firmwares
339
    for (const FirmwareBuildType_t& firmwareType: px4MapFirmwareTypeToDir.keys()) {
Don Gagne's avatar
Don Gagne committed
340
        QString dir = px4MapFirmwareTypeToDir[firmwareType];
341 342 343 344 345
        _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
346 347 348
    }

    int size = sizeof(rgAeroCoreFirmwareArray)/sizeof(rgAeroCoreFirmwareArray[0]);
Pritam Ghanghas's avatar
Pritam Ghanghas committed
349 350 351 352
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgAeroCoreFirmwareArray[i];
        _rgAeroCoreFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }
353

354 355 356 357 358 359
    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
360 361 362 363 364 365
    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);
    }

366 367 368 369 370 371 372 373 374 375 376 377
    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);
    }

378 379 380 381 382 383
    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);
    }

384 385 386 387 388
    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);
    }
389 390 391 392 393 394
    
    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);
    }
395

Pritam Ghanghas's avatar
Pritam Ghanghas committed
396 397 398 399 400
    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);
    }
401

Pritam Ghanghas's avatar
Pritam Ghanghas committed
402 403 404 405 406
    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);
    }
407 408 409 410 411 412

    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
413
}
414

Pritam Ghanghas's avatar
Pritam Ghanghas committed
415 416 417 418 419 420
/// @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.");
}
421

422
QHash<FirmwareUpgradeController::FirmwareIdentifier, QString>* FirmwareUpgradeController::_firmwareHashForBoardId(int boardId)
Pritam Ghanghas's avatar
Pritam Ghanghas committed
423
{
424 425

    _rgFirmwareDynamic.clear();
426

427 428
    switch (boardId) {
    case Bootloader::boardIDPX4Flow:
429
        _rgFirmwareDynamic = _rgPX4FLowFirmware;
430
        break;
431
    case Bootloader::boardIDPX4FMUV2:
432
        _rgFirmwareDynamic = _rgPX4FMUV2Firmware;
433
        break;
DonLakeFlyer's avatar
DonLakeFlyer committed
434
    case Bootloader::boardIDPX4FMUV3:
435
        _rgFirmwareDynamic = _rgFMUV3Firmware;
436
        break;
437
    case Bootloader::boardIDPX4FMUV4:
438
        _rgFirmwareDynamic = _rgFMUV4Firmware;
439
        break;
440
    case Bootloader::boardIDPX4FMUV4PRO:
441
        _rgFirmwareDynamic = _rgFMUV4PROFirmware;
442
        break;
443
    case Bootloader::boardIDPX4FMUV5:
444
        _rgFirmwareDynamic = _rgFMUV5Firmware;
445
        break;
446
    case Bootloader::boardIDAeroCore:
447
        _rgFirmwareDynamic = _rgAeroCoreFirmware;
448
        break;
449
    case Bootloader::boardIDAUAVX2_1:
450
        _rgFirmwareDynamic = _rgAUAVX2_1Firmware;
451
        break;
Henry Zhang's avatar
Henry Zhang committed
452
    case Bootloader::boardIDMINDPXFMUV2:
453
        _rgFirmwareDynamic = _rgMindPXFMUV2Firmware;
454
        break;
455
    case Bootloader::boardIDTAPV1:
456
        _rgFirmwareDynamic = _rgTAPV1Firmware;
457
        break;
458
    case Bootloader::boardIDASCV1:
459
        _rgFirmwareDynamic = _rgASCV1Firmware;
460
        break;
461
    case Bootloader::boardIDCrazyflie2:
462
        _rgFirmwareDynamic = _rgCrazyflie2Firmware;
463
        break;
464
    case Bootloader::boardIDOmnibusF4SD:
465
        _rgFirmwareDynamic = _rgOmnibusF4SDFirmware;
466
        break;
467 468
    case Bootloader::boardIDFMUK66V3:
        _rgFirmwareDynamic = _rgFMUK66V3Firmware;
469
        break;
470
    case Bootloader::boardID3DRRadio:
471
        _rgFirmwareDynamic = _rg3DRRadioFirmware;
472
        break;
473
    default:
474 475
        // Unknown board id
        break;
476 477 478
    }

    return &_rgFirmwareDynamic;
479
}
Lorenz Meier's avatar
Lorenz Meier committed
480

481 482 483
/// @brief Prompts the user to select a firmware file if needed and moves the state machine to the next state.
void FirmwareUpgradeController::_getFirmwareFile(FirmwareIdentifier firmwareId)
{
484
    QHash<FirmwareIdentifier, QString>* prgFirmware = _firmwareHashForBoardId(static_cast<int>(_bootloaderBoardID));
485
    
486
    if (firmwareId.firmwareType == CustomFirmware) {
Gus Grubba's avatar
Gus Grubba committed
487 488 489 490
        _firmwareFilename = QString(); //-- TODO: QGCQFileDialog::getOpenFileName(nullptr,                                                                // Parent to main window
                                                  //          tr("Select Firmware File"),                                             // Dialog Caption
                                                  //          QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),    // Initial directory
                                                  //          tr("Firmware Files (*.px4 *.apj *.bin *.ihx)"));                              // File filter
491
    } else {
492 493
        if (prgFirmware->contains(firmwareId)) {
            _firmwareFilename = prgFirmware->value(firmwareId);
494
        } else {
495
            _errorCancel(tr("Unable to find specified firmware for board type"));
496 497
            return;
        }
498 499 500
    }
    
    if (_firmwareFilename.isEmpty()) {
501
        _errorCancel(tr("No firmware file selected"));
502 503 504 505 506 507 508 509 510 511
    } else {
        _downloadFirmware();
    }
}

/// @brief Begins the process of downloading the selected firmware file.
void FirmwareUpgradeController::_downloadFirmware(void)
{
    Q_ASSERT(!_firmwareFilename.isEmpty());
    
512 513
    _appendStatusLog(tr("Downloading firmware..."));
    _appendStatusLog(tr(" From: %1").arg(_firmwareFilename));
514
    
515 516 517 518 519
    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);
520 521 522
}

/// @brief Updates the progress indicator while downloading
523
void FirmwareUpgradeController::_firmwareDownloadProgress(qint64 curr, qint64 total)
524 525 526
{
    // Take care of cases where 0 / 0 is emitted as error return value
    if (total > 0) {
527
        _progressBar->setProperty("value", static_cast<float>(curr) / static_cast<float>(total));
528 529 530 531
    }
}

/// @brief Called when the firmware download completes.
532
void FirmwareUpgradeController::_firmwareDownloadFinished(QString remoteFile, QString localFile)
533
{
534 535
    Q_UNUSED(remoteFile);

536
    _appendStatusLog(tr("Download complete"));
537
    
538
    FirmwareImage* image = new FirmwareImage(this);
539
    
540 541
    connect(image, &FirmwareImage::statusMessage, this, &FirmwareUpgradeController::_status);
    connect(image, &FirmwareImage::errorMessage, this, &FirmwareUpgradeController::_error);
542
    
543
    if (!image->load(localFile, _bootloaderBoardID)) {
544
        _errorCancel(tr("Image load failed"));
545
        return;
546 547
    }
    
548 549
    // We can't proceed unless we have the bootloader
    if (!_bootloaderFound) {
550
        _errorCancel(tr("Bootloader not found"));
551 552
        return;
    }
553
    
554
    if (_bootloaderBoardFlashSize != 0 && image->imageSize() > _bootloaderBoardFlashSize) {
555
        _errorCancel(tr("Image size of %1 is too large for board flash size %2").arg(image->imageSize()).arg(_bootloaderBoardFlashSize));
556
        return;
557
    }
558 559

    _threadController->flash(image);
560 561
}

562
/// @brief Called when an error occurs during download
563
void FirmwareUpgradeController::_firmwareDownloadError(QString errorMsg)
564
{
565
    _errorCancel(errorMsg);
566 567
}

568
/// @brief returns firmware type as a string
569
QString FirmwareUpgradeController::firmwareTypeAsString(FirmwareBuildType_t type) const
570 571 572 573 574 575 576 577 578 579 580 581 582
{
    switch (type) {
    case StableFirmware:
        return "stable";
    case DeveloperFirmware:
        return "developer";
    case BetaFirmware:
        return "beta";
    default:
        return "custom";
    }
}

583 584 585
/// @brief Signals completion of one of the specified bootloader commands. Moves the state machine to the
///         appropriate next step.
void FirmwareUpgradeController::_flashComplete(void)
586
{
587
    delete _image;
588
    _image = nullptr;
589
    
590
    _appendStatusLog(tr("Upgrade complete"), true);
591 592
    _appendStatusLog("------------------------------------------", false);
    emit flashComplete();
593
    qgcApp()->toolbox()->linkManager()->setConnectionsAllowed();
594 595
}

596
void FirmwareUpgradeController::_error(const QString& errorString)
597
{
598
    delete _image;
599
    _image = nullptr;
600 601
    
    _errorCancel(QString("Error: %1").arg(errorString));
602 603
}

604
void FirmwareUpgradeController::_status(const QString& statusString)
605
{
606
    _appendStatusLog(statusString);
607 608 609 610 611
}

/// @brief Updates the progress bar from long running bootloader commands
void FirmwareUpgradeController::_updateProgress(int curr, int total)
{
612 613
    // Take care of cases where 0 / 0 is emitted as error return value
    if (total > 0) {
614
        _progressBar->setProperty("value", static_cast<float>(curr) / static_cast<float>(total));
615
    }
616 617 618 619 620 621
}

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

/// Appends the specified text to the status log area in the ui
626
void FirmwareUpgradeController::_appendStatusLog(const QString& text, bool critical)
627 628 629 630
{
    Q_ASSERT(_statusLog);
    
    QVariant returnedValue;
631 632 633 634 635 636 637 638
    QVariant varText;
    
    if (critical) {
        varText = QString("<font color=\"yellow\">%1</font>").arg(text);
    } else {
        varText = text;
    }
    
639 640 641 642
    QMetaObject::invokeMethod(_statusLog,
                              "append",
                              Q_RETURN_ARG(QVariant, returnedValue),
                              Q_ARG(QVariant, varText));
Don Gagne's avatar
Don Gagne committed
643
}
644

645 646 647
void FirmwareUpgradeController::_errorCancel(const QString& msg)
{
    _appendStatusLog(msg, false);
648
    _appendStatusLog(tr("Upgrade cancelled"), true);
649 650 651
    _appendStatusLog("------------------------------------------", false);
    emit error();
    cancel();
652
    qgcApp()->toolbox()->linkManager()->setConnectionsAllowed();
653 654 655 656 657 658 659 660 661 662
}

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)
663
{
664
    _eraseTimer.stop();
665
}
666

667
void FirmwareUpgradeController::setSelectedFirmwareBuildType(FirmwareBuildType_t firmwareType)
668
{
669 670 671
    _selectedFirmwareBuildType = firmwareType;
    emit selectedFirmwareBuildTypeChanged(_selectedFirmwareBuildType);
    _buildAPMFirmwareNames();
672 673
}

674
void FirmwareUpgradeController::_buildAPMFirmwareNames(void)
675
{
Don Gagne's avatar
Don Gagne committed
676
    qCDebug(FirmwareUpgradeLog) << "_buildAPMFirmwareNames";
677

678 679
    bool                    chibios =       _apmChibiOSSetting->rawValue().toInt() == 0;
    FirmwareVehicleType_t   vehicleType =   static_cast<FirmwareVehicleType_t>(_apmVehicleTypeSetting->rawValue().toInt());
680

681 682
    _apmFirmwareNames.clear();
    _apmFirmwareUrls.clear();
683

684 685 686 687 688 689 690 691
    QString apmDescriptionSuffix("-BL");
    bool    bootloaderMatch = _foundBoardInfo.description().endsWith(apmDescriptionSuffix);

    for (const ManifestFirmwareInfo_t& firmwareInfo: _rgManifestFirmwareInfo) {
        bool match = false;
        if (firmwareInfo.firmwareBuildType == _selectedFirmwareBuildType && firmwareInfo.chibios == chibios && firmwareInfo.vehicleType == vehicleType) {
            if (bootloaderMatch) {
                if (firmwareInfo.rgBootloaderPortString.contains(_foundBoardInfo.description())) {
Don Gagne's avatar
Don Gagne committed
692
                    qCDebug(FirmwareUpgradeLog) << "Bootloader match:" << firmwareInfo.friendlyName << _foundBoardInfo.description() << firmwareInfo.rgBootloaderPortString << firmwareInfo.url << firmwareInfo.vehicleType;
693 694 695 696
                    match = true;
                }
            } else {
                if (firmwareInfo.rgVID.contains(_foundBoardInfo.vendorIdentifier()) && firmwareInfo.rgPID.contains(_foundBoardInfo.productIdentifier())) {
Don Gagne's avatar
Don Gagne committed
697
                    qCDebug(FirmwareUpgradeLog) << "Fallback match:" << firmwareInfo.friendlyName << _foundBoardInfo.vendorIdentifier() << _foundBoardInfo.productIdentifier() << _bootloaderBoardID << firmwareInfo.url << firmwareInfo.vehicleType;
698 699
                    match = true;
                }
700
            }
Don Gagne's avatar
Don Gagne committed
701
        }
702

Don Gagne's avatar
Don Gagne committed
703 704 705
        // Do a final filter on fmuv2/fmuv3
        if (match && _bootloaderBoardID == Bootloader::boardIDPX4FMUV3) {
            match = !firmwareInfo.fmuv2;
706
        }
707

708 709 710
        if (match) {
            _apmFirmwareNames.append(firmwareInfo.friendlyName);
            _apmFirmwareUrls.append(firmwareInfo.url);
711 712 713
        }
    }

Don Gagne's avatar
Don Gagne committed
714 715 716
    if (_apmFirmwareNames.count() > 1) {
        _apmFirmwareNames.prepend(tr("Choose board type"));
        _apmFirmwareUrls.prepend(QString());
717 718
    }

719
    emit apmFirmwareNamesChanged();
720 721
}

722
FirmwareUpgradeController::FirmwareVehicleType_t FirmwareUpgradeController::vehicleTypeFromFirmwareSelectionIndex(int index)
723 724 725
{
    if (index < 0 || index >= _apmVehicleTypeFromCurrentVersionList.count()) {
        qWarning() << "Invalid index, index:count" << index << _apmVehicleTypeFromCurrentVersionList.count();
Don Gagne's avatar
Don Gagne committed
726
        return CopterFirmware;
727 728 729 730
    }

    return _apmVehicleTypeFromCurrentVersionList[index];
}
Don Gagne's avatar
Don Gagne committed
731 732 733 734

void FirmwareUpgradeController::_determinePX4StableVersion(void)
{
    QGCFileDownload* downloader = new QGCFileDownload(this);
735 736 737
    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
738 739
}

740
void FirmwareUpgradeController::_px4ReleasesGithubDownloadFinished(QString remoteFile, QString localFile)
Don Gagne's avatar
Don Gagne committed
741 742 743 744 745
{
    Q_UNUSED(remoteFile);

    QFile jsonFile(localFile);
    if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
746
        qCWarning(FirmwareUpgradeLog) << "Unable to open github px4 releases json file" << localFile << jsonFile.errorString();
Don Gagne's avatar
Don Gagne committed
747 748 749 750 751 752 753 754
        return;
    }
    QByteArray bytes = jsonFile.readAll();
    jsonFile.close();

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

759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
    // 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
791 792
}

793
void FirmwareUpgradeController::_px4ReleasesGithubDownloadError(QString errorMsg)
Don Gagne's avatar
Don Gagne committed
794
{
795
    qCWarning(FirmwareUpgradeLog) << "PX4 releases github download failed" << errorMsg;
Don Gagne's avatar
Don Gagne committed
796
}
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 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 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871

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);
#if 0
    downloader->download(QStringLiteral("http://firmware.ardupilot.org/manifest.json.gz"));
#else
    downloader->download(QStringLiteral("http://firmware.ardupilot.org/manifest.json"));
#endif
}

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

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

    qDebug() << "_ardupilotManifestDownloadFinished" << remoteFile << localFile;

#if 0
    QFile gzipFile(localFile);
    if (!gzipFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qCWarning(FirmwareUpgradeLog) << "Unable to open ArduPilot firmware manifest file" << localFile << gzipFile.errorString();
        QFile::remove(localFile);
        return;
    }

    // Store decompressed size as first four bytes. This is required by qUncompress routine.
    QByteArray raw;
    int decompressedSize = 3073444;
    raw.append((unsigned char)((decompressedSize >> 24) & 0xFF));
    raw.append((unsigned char)((decompressedSize >> 16) & 0xFF));
    raw.append((unsigned char)((decompressedSize >> 8) & 0xFF));
    raw.append((unsigned char)((decompressedSize >> 0) & 0xFF));

    raw.append(gzipFile.readAll());
    QByteArray bytes = qUncompress(raw);
#else


    QFile jsonFile(localFile);
    if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qCWarning(FirmwareUpgradeLog) << "Unable to open ArduPilot firmware manifest file" << localFile << jsonFile.errorString();
        QFile::remove(localFile);
        return;
    }
    QByteArray bytes = jsonFile.readAll();
    jsonFile.close();
#endif
    QFile::remove(localFile);

    QJsonParseError jsonParseError;
    QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError);
    if (jsonParseError.error != QJsonParseError::NoError) {
        qCWarning(FirmwareUpgradeLog) <<  "Unable to open ArduPilot manifest json document" << localFile << jsonParseError.errorString();
    }




    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
872
        QString                 platform =              firmwareJson[_manifestPlatformKey].toString();
873 874

        if (firmwareVehicleType != DefaultVehicleFirmware && firmwareBuildType != CustomFirmware && (format == QStringLiteral("apj") || format == QStringLiteral("px4"))) {
Don Gagne's avatar
Don Gagne committed
875
            if (platform.contains("-heli") && firmwareVehicleType != HeliFirmware) {
876 877 878 879 880 881 882 883 884 885 886 887
                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
888
            firmwareInfo.fmuv2 =                platform.contains(QStringLiteral("fmuv2"));
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906

            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
907
            firmwareInfo.friendlyName = QStringLiteral("%1 - %2").arg(brandName.isEmpty() ? platform : brandName).arg(firmwareInfo.version);
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
        }
    }

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