FirmwareUpgradeController.cc 42.2 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
#if !defined(NO_ARDUPILOT_DIALECT)
97 98
    connect(_apmChibiOSSetting,     &Fact::rawValueChanged, this, &FirmwareUpgradeController::_buildAPMFirmwareNames);
    connect(_apmVehicleTypeSetting, &Fact::rawValueChanged, this, &FirmwareUpgradeController::_buildAPMFirmwareNames);
99
#endif
100

101
    _initFirmwareHash();
Don Gagne's avatar
Don Gagne committed
102
    _determinePX4StableVersion();
103 104

#if !defined(NO_ARDUPILOT_DIALECT)
105
    _downloadArduPilotManifest();
106
#endif
107 108
}

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

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

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

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

126 127 128
    _bootloaderFound = false;
    _startFlashWhenBootloaderFound = false;
    _threadController->startFindBoardLoop();
129 130
}

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

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

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

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

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

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

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

199
    _startFlashWhenBootloaderFound = false;
200

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

216 217

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

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

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

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


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

Pritam Ghanghas's avatar
Pritam Ghanghas committed
261 262 263 264 265 266
    //////////////////////////////////// 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"},
    };
267

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

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

301 302 303 304 305
    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"},
    };
306 307 308 309 310 311 312
    
    //////////////////////////////////// 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"},
    };
313

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

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

327
    // We build the maps for PX4 firmwares dynamically using the data below
Don Gagne's avatar
Don Gagne committed
328 329 330 331 332 333 334 335 336 337

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

338
    QMap<FirmwareBuildType_t, QString> px4MapFirmwareTypeToDir;
Don Gagne's avatar
Don Gagne committed
339 340 341
    px4MapFirmwareTypeToDir[StableFirmware] =       QStringLiteral("stable");
    px4MapFirmwareTypeToDir[BetaFirmware] =         QStringLiteral("beta");
    px4MapFirmwareTypeToDir[DeveloperFirmware] =    QStringLiteral("master");
Don Gagne's avatar
Don Gagne committed
342 343

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

    int size = sizeof(rgAeroCoreFirmwareArray)/sizeof(rgAeroCoreFirmwareArray[0]);
Pritam Ghanghas's avatar
Pritam Ghanghas committed
354 355 356 357
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgAeroCoreFirmwareArray[i];
        _rgAeroCoreFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }
358

359 360 361 362 363 364
    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
365 366 367 368 369 370
    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);
    }

371 372 373 374 375 376 377 378 379 380 381 382
    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);
    }

383 384 385 386 387 388
    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);
    }

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

Pritam Ghanghas's avatar
Pritam Ghanghas committed
401 402 403 404 405
    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);
    }
406

Pritam Ghanghas's avatar
Pritam Ghanghas committed
407 408 409 410 411
    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);
    }
412 413 414 415 416 417

    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
418
}
419

Pritam Ghanghas's avatar
Pritam Ghanghas committed
420 421 422 423 424 425
/// @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.");
}
426

427
QHash<FirmwareUpgradeController::FirmwareIdentifier, QString>* FirmwareUpgradeController::_firmwareHashForBoardId(int boardId)
Pritam Ghanghas's avatar
Pritam Ghanghas committed
428
{
429 430

    _rgFirmwareDynamic.clear();
431

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

    return &_rgFirmwareDynamic;
484
}
Lorenz Meier's avatar
Lorenz Meier committed
485

486 487
void FirmwareUpgradeController::_getFirmwareFile(FirmwareIdentifier firmwareId)
{
488
    QHash<FirmwareIdentifier, QString>* prgFirmware = _firmwareHashForBoardId(static_cast<int>(_bootloaderBoardID));
489
    if (firmwareId.firmwareType == CustomFirmware) {
490 491
        _firmwareFilename = QString();
        _errorCancel(tr("Custom firmware selected but no filename given."));
492
    } else {
493 494
        if (prgFirmware->contains(firmwareId)) {
            _firmwareFilename = prgFirmware->value(firmwareId);
495
        } else {
496
            _errorCancel(tr("Unable to find specified firmware for board type"));
497 498
            return;
        }
499 500 501
    }
    
    if (_firmwareFilename.isEmpty()) {
502
        _errorCancel(tr("No firmware file selected"));
503 504 505 506 507 508 509 510 511 512
    } else {
        _downloadFirmware();
    }
}

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

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

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

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

    _threadController->flash(image);
561 562
}

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

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

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

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

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

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

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

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

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

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

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

675
void FirmwareUpgradeController::_buildAPMFirmwareNames(void)
676
{
677
#if !defined(NO_ARDUPILOT_DIALECT)
Don Gagne's avatar
Don Gagne committed
678
    qCDebug(FirmwareUpgradeLog) << "_buildAPMFirmwareNames";
679

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

683 684
    _apmFirmwareNames.clear();
    _apmFirmwareUrls.clear();
685

686 687 688 689 690 691 692 693
    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
694
                    qCDebug(FirmwareUpgradeLog) << "Bootloader match:" << firmwareInfo.friendlyName << _foundBoardInfo.description() << firmwareInfo.rgBootloaderPortString << firmwareInfo.url << firmwareInfo.vehicleType;
695 696 697 698
                    match = true;
                }
            } else {
                if (firmwareInfo.rgVID.contains(_foundBoardInfo.vendorIdentifier()) && firmwareInfo.rgPID.contains(_foundBoardInfo.productIdentifier())) {
Don Gagne's avatar
Don Gagne committed
699
                    qCDebug(FirmwareUpgradeLog) << "Fallback match:" << firmwareInfo.friendlyName << _foundBoardInfo.vendorIdentifier() << _foundBoardInfo.productIdentifier() << _bootloaderBoardID << firmwareInfo.url << firmwareInfo.vehicleType;
700 701
                    match = true;
                }
702
            }
Don Gagne's avatar
Don Gagne committed
703
        }
704

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

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

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

721
    emit apmFirmwareNamesChanged();
722
#endif
723 724
}

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

    return _apmVehicleTypeFromCurrentVersionList[index];
}
Don Gagne's avatar
Don Gagne committed
734 735 736 737

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

743
void FirmwareUpgradeController::_px4ReleasesGithubDownloadFinished(QString remoteFile, QString localFile)
Don Gagne's avatar
Don Gagne committed
744 745 746 747 748
{
    Q_UNUSED(remoteFile);

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

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

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 791 792 793
    // 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
794 795
}

796
void FirmwareUpgradeController::_px4ReleasesGithubDownloadError(QString errorMsg)
Don Gagne's avatar
Don Gagne committed
797
{
798
    qCWarning(FirmwareUpgradeLog) << "PX4 releases github download failed" << errorMsg;
Don Gagne's avatar
Don Gagne committed
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 872 873 874

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
875
        QString                 platform =              firmwareJson[_manifestPlatformKey].toString();
876 877

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

            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
910
            firmwareInfo.friendlyName = QStringLiteral("%1 - %2").arg(brandName.isEmpty() ? platform : brandName).arg(firmwareInfo.version);
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 941 942 943
        }
    }

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