FirmwareUpgradeController.cc 29.5 KB
Newer Older
Don Gagne's avatar
Don Gagne committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*=====================================================================
 
 QGroundControl Open Source Ground Control Station
 
 (c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 
 This file is part of the QGROUNDCONTROL project
 
 QGROUNDCONTROL is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 QGROUNDCONTROL is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
 
 ======================================================================*/

/// @file
///     @brief PX4 Firmware Upgrade UI
///     @author Don Gagne <don@thegagnes.com>

#include "FirmwareUpgradeController.h"
Don Gagne's avatar
Don Gagne committed
29
#include "Bootloader.h"
Don Gagne's avatar
Don Gagne committed
30 31 32
#include "QGCFileDialog.h"
#include "QGCMessageBox.h"

Pritam Ghanghas's avatar
Pritam Ghanghas committed
33 34 35 36 37 38 39 40

struct FirmwareToUrlElement_t {
    FirmwareUpgradeController::AutoPilotStackType_t    stackType;
    FirmwareUpgradeController::FirmwareType_t          firmwareType;
    FirmwareUpgradeController::FirmwareVehicleType_t   vehicleType;
    QString                                            url;
};

41
uint qHash(const FirmwareUpgradeController::FirmwareIdentifier& firmwareId)
42
{
43 44 45
    return ( firmwareId.autopilotStackType |
             (firmwareId.firmwareType << 8) |
             (firmwareId.firmwareVehicleType << 16) );
46 47
}

Don Gagne's avatar
Don Gagne committed
48 49 50 51
/// @Brief Constructs a new FirmwareUpgradeController Widget. This widget is used within the PX4VehicleConfig set of screens.
FirmwareUpgradeController::FirmwareUpgradeController(void) :
    _downloadManager(NULL),
    _downloadNetworkReply(NULL),
Don Gagne's avatar
Don Gagne committed
52 53
    _statusLog(NULL),
    _image(NULL)
Don Gagne's avatar
Don Gagne committed
54 55 56 57
{
    _threadController = new PX4FirmwareUpgradeThreadController(this);
    Q_CHECK_PTR(_threadController);

Don Gagne's avatar
Don Gagne committed
58 59 60 61 62 63 64 65 66 67 68 69 70
    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);
    
71
    connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkDisconnected, this, &FirmwareUpgradeController::_linkDisconnected);
Don Gagne's avatar
Don Gagne committed
72

Don Gagne's avatar
Don Gagne committed
73 74 75
    connect(&_eraseTimer, &QTimer::timeout, this, &FirmwareUpgradeController::_eraseProgressTick);
}

Don Gagne's avatar
Don Gagne committed
76
void FirmwareUpgradeController::startBoardSearch(void)
Don Gagne's avatar
Don Gagne committed
77
{
Don Gagne's avatar
Don Gagne committed
78 79 80
    _bootloaderFound = false;
    _startFlashWhenBootloaderFound = false;
    _threadController->startFindBoardLoop();
Don Gagne's avatar
Don Gagne committed
81 82
}

83 84 85
void FirmwareUpgradeController::flash(AutoPilotStackType_t stackType,
                                      FirmwareType_t firmwareType,
                                      FirmwareVehicleType_t vehicleType)
Don Gagne's avatar
Don Gagne committed
86
{
87
    FirmwareIdentifier firmwareId = FirmwareIdentifier(stackType, firmwareType, vehicleType);
Don Gagne's avatar
Don Gagne committed
88
    if (_bootloaderFound) {
89
        _getFirmwareFile(firmwareId);
Don Gagne's avatar
Don Gagne committed
90 91 92
    } else {
        // We haven't found the bootloader yet. Need to wait until then to flash
        _startFlashWhenBootloaderFound = true;
93
        _startFlashWhenBootloaderFoundFirmwareIdentity = firmwareId;
Don Gagne's avatar
Don Gagne committed
94
    }
Don Gagne's avatar
Don Gagne committed
95 96
}

97 98 99 100 101
void FirmwareUpgradeController::flash(const FirmwareIdentifier& firmwareId)
{
    flash(firmwareId.autopilotStackType, firmwareId.firmwareType, firmwareId.firmwareVehicleType);
}

Don Gagne's avatar
Don Gagne committed
102
void FirmwareUpgradeController::cancel(void)
Don Gagne's avatar
Don Gagne committed
103
{
Don Gagne's avatar
Don Gagne committed
104 105 106
    _eraseTimer.stop();
    _threadController->cancel();
}
Don Gagne's avatar
Don Gagne committed
107

Don Gagne's avatar
Don Gagne committed
108 109 110 111 112 113 114 115 116 117 118 119
void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPortInfo& info, int type)
{
    _foundBoardInfo = info;
    switch (type) {
        case FoundBoardPX4FMUV1:
            _foundBoardType = "PX4 FMU V1";
            _startFlashWhenBootloaderFound = false;
            break;
        case FoundBoardPX4FMUV2:
            _foundBoardType = "Pixhawk";
            _startFlashWhenBootloaderFound = false;
            break;
120
        case FoundBoardAeroCore:
121 122 123
            _foundBoardType = "AeroCore";
            _startFlashWhenBootloaderFound = false;
            break;
Don Gagne's avatar
Don Gagne committed
124
        case FoundBoardPX4Flow:
125 126 127
            _foundBoardType = "PX4 Flow";
            _startFlashWhenBootloaderFound = false;
            break;
Don Gagne's avatar
Don Gagne committed
128
        case FoundBoard3drRadio:
129
            _foundBoardType = "3DR Radio";
Don Gagne's avatar
Don Gagne committed
130
            if (!firstAttempt) {
131
                // Radio always flashes latest firmware, so we can start right away without
Don Gagne's avatar
Don Gagne committed
132 133
                // any further user input.
                _startFlashWhenBootloaderFound = true;
134
                _startFlashWhenBootloaderFoundFirmwareIdentity = FirmwareIdentifier(ThreeDRRadio,
135 136
                                                                                    StableFirmware,
                                                                                    DefaultVehicleFirmware);
Don Gagne's avatar
Don Gagne committed
137 138
            }
            break;
Don Gagne's avatar
Don Gagne committed
139
    }
Don Gagne's avatar
Don Gagne committed
140 141 142
    
    qCDebug(FirmwareUpgradeLog) << _foundBoardType;
    emit boardFound();
Don Gagne's avatar
Don Gagne committed
143 144
}

Don Gagne's avatar
Don Gagne committed
145 146

void FirmwareUpgradeController::_noBoardFound(void)
Don Gagne's avatar
Don Gagne committed
147
{
Don Gagne's avatar
Don Gagne committed
148 149 150 151 152 153
    emit noBoardFound();
}

void FirmwareUpgradeController::_boardGone(void)
{
    emit boardGone();
Don Gagne's avatar
Don Gagne committed
154 155 156 157 158 159
}

/// @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)
{
Don Gagne's avatar
Don Gagne committed
160
    _bootloaderFound = true;
Don Gagne's avatar
Don Gagne committed
161
    _bootloaderVersion = bootloaderVersion;
Don Gagne's avatar
Don Gagne committed
162 163
    _bootloaderBoardID = boardID;
    _bootloaderBoardFlashSize = flashSize;
Don Gagne's avatar
Don Gagne committed
164
    
Don Gagne's avatar
Don Gagne committed
165 166 167 168
    _appendStatusLog("Connected to bootloader:");
    _appendStatusLog(QString("  Version: %1").arg(_bootloaderVersion));
    _appendStatusLog(QString("  Board ID: %1").arg(_bootloaderBoardID));
    _appendStatusLog(QString("  Flash size: %1").arg(_bootloaderBoardFlashSize));
Don Gagne's avatar
Don Gagne committed
169
    
Don Gagne's avatar
Don Gagne committed
170
    if (_startFlashWhenBootloaderFound) {
171
        flash(_startFlashWhenBootloaderFoundFirmwareIdentity);
Don Gagne's avatar
Don Gagne committed
172
    }
Don Gagne's avatar
Don Gagne committed
173 174 175
}


Pritam Ghanghas's avatar
Pritam Ghanghas committed
176 177 178
/// @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()
Don Gagne's avatar
Don Gagne committed
179
{
Pritam Ghanghas's avatar
Pritam Ghanghas committed
180 181 182 183 184
    // indirect check whether this function has been called before or not
    // may have to be modified if _rgPX4FMUV2Firmware disappears
    if (!_rgPX4FMUV2Firmware.isEmpty()) {
        return;
    }
185

Pritam Ghanghas's avatar
Pritam Ghanghas committed
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
    //////////////////////////////////// PX4FMUV2 firmwares //////////////////////////////////////////////////
    FirmwareToUrlElement_t rgPX4FMV2FirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/px4fmu-v2_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/px4fmu-v2_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/px4fmu-v2_default.px4"},
        { AutoPilotStackAPM, StableFirmware,    QuadFirmware,           "http://firmware.diydrones.com/Copter/stable/PX4-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    X8Firmware,             "http://firmware.diydrones.com/Copter/stable/PX4-octa-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    HexaFirmware,           "http://firmware.diydrones.com/Copter/stable/PX4-hexa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    OctoFirmware,           "http://firmware.diydrones.com/Copter/stable/PX4-octa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    YFirmware,              "http://firmware.diydrones.com/Copter/stable/PX4-tri/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    Y6Firmware,             "http://firmware.diydrones.com/Copter/stable/PX4-y6/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    HeliFirmware,           "http://firmware.diydrones.com/Copter/stable/PX4-heli/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    PlaneFirmware,          "http://firmware.diydrones.com/Plane/stable/PX4/ArduPlane-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    RoverFirmware,          "http://firmware.diydrones.com/Rover/stable/PX4/APMrover2-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      QuadFirmware,           "http://firmware.diydrones.com/Copter/beta/PX4-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      X8Firmware,             "http://firmware.diydrones.com/Copter/beta/PX4-octa-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      HexaFirmware,           "http://firmware.diydrones.com/Copter/beta/PX4-hexa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      OctoFirmware,           "http://firmware.diydrones.com/Copter/beta/PX4-octa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      YFirmware,              "http://firmware.diydrones.com/Copter/beta/PX4-tri/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      Y6Firmware,             "http://firmware.diydrones.com/Copter/beta/PX4-y6/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      HeliFirmware,           "http://firmware.diydrones.com/Copter/beta/PX4-heli/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      PlaneFirmware,          "http://firmware.diydrones.com/Plane/beta/PX4/ArduPlane-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      RoverFirmware,          "http://firmware.diydrones.com/Rover/beta/PX4/APMrover2-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, QuadFirmware,           "http://firmware.diydrones.com/Copter/latest/PX4-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, X8Firmware,             "http://firmware.diydrones.com/Copter/latest/PX4-octa-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, HexaFirmware,           "http://firmware.diydrones.com/Copter/latest/PX4-hexa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, OctoFirmware,           "http://firmware.diydrones.com/Copter/latest/PX4-octa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, YFirmware,              "http://firmware.diydrones.com/Copter/latest/PX4-tri/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, Y6Firmware,             "http://firmware.diydrones.com/Copter/latest/PX4-y6/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, HeliFirmware,           "http://firmware.diydrones.com/Copter/latest/PX4-heli/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, PlaneFirmware,          "http://firmware.diydrones.com/Plane/latest/PX4/ArduPlane-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware,          "http://firmware.diydrones.com/Rover/latest/PX4/APMrover2-v2.px4"}
    };

    //////////////////////////////////// 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"},
        { AutoPilotStackAPM, StableFirmware,    QuadFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/APM/Copter/stable/PX4-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    X8Firmware,             "http://gumstix-aerocore.s3.amazonaws.com/Copter/stable/PX4-octa-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    HexaFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/Copter/stable/PX4-hexa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    OctoFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/Copter/stable/PX4-octa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    YFirmware,              "http://gumstix-aerocore.s3.amazonaws.com/Copter/stable/PX4-tri/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    Y6Firmware,             "http://gumstix-aerocore.s3.amazonaws.com/Copter/stable/PX4-y6/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    HeliFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/Copter/stable/PX4-heli/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    PlaneFirmware,          "http://gumstix-aerocore.s3.amazonaws.com/Plane/stable/PX4/ArduPlane-v2.px4"},
        { AutoPilotStackAPM, StableFirmware,    RoverFirmware,          "http://gumstix-aerocore.s3.amazonaws.com/Rover/stable/PX4/APMrover2-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      QuadFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      X8Firmware,             "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-octa-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      HexaFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-hexa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      OctoFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-octa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      YFirmware,              "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-tri/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      Y6Firmware,             "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-y6/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      HeliFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-heli/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      PlaneFirmware,          "http://gumstix-aerocore.s3.amazonaws.com/Plane/beta/PX4/ArduPlane-v2.px4"},
        { AutoPilotStackAPM, BetaFirmware,      RoverFirmware,          "http://gumstix-aerocore.s3.amazonaws.com/Rover/beta/PX4/APMrover2-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, QuadFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/Copter/latest/PX4-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, X8Firmware,             "http://gumstix-aerocore.s3.amazonaws.com/Copter/latest/PX4-octa-quad/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, HexaFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/Copter/latest/PX4-hexa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, OctoFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/Copter/latest/PX4-octa/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, YFirmware,              "http://gumstix-aerocore.s3.amazonaws.com/Copter/latest/PX4-tri/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, Y6Firmware,             "http://gumstix-aerocore.s3.amazonaws.com/Copter/latest/PX4-y6/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, HeliFirmware,           "http://gumstix-aerocore.s3.amazonaws.com/Copter/latest/PX4-heli/ArduCopter-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, PlaneFirmware,          "http://gumstix-aerocore.s3.amazonaws.com/Plane/latest/PX4/ArduPlane-v2.px4"},
        { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware,          "http://gumstix-aerocore.s3.amazonaws.com/Rover/latest/PX4/APMrover2-v2.px4"}
    };
253 254

    /////////////////////////////// FMUV1 firmwares ///////////////////////////////////////////
Pritam Ghanghas's avatar
Pritam Ghanghas committed
255 256 257 258 259
    FirmwareToUrlElement_t rgPX4FMUV1FirmwareArray[] = {
        { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/latest/px4fmu-v1_default.px4"},
        { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/px4fmu-v1_default.px4"},
        { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/px4fmu-v1_default.px4"},
    };
260

Pritam Ghanghas's avatar
Pritam Ghanghas committed
261 262 263 264 265 266 267
    /////////////////////////////// px4flow firmwares ///////////////////////////////////////
    FirmwareToUrlElement_t rgPX4FLowFirmwareArray[] = {
        { PX4Flow, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Flow/master/px4flow.px4" },
    };

    /////////////////////////////// 3dr radio firmwares ///////////////////////////////////////
    FirmwareToUrlElement_t rg3DRRadioFirmwareArray[] = {
268
        { ThreeDRRadio, StableFirmware, DefaultVehicleFirmware, "http://firmware.diydrones.com/SiK/stable/radio~hm_trp.ihx"}
Pritam Ghanghas's avatar
Pritam Ghanghas committed
269 270 271 272 273 274 275 276
    };

    // populate hashes now
    int size = sizeof(rgPX4FMV2FirmwareArray)/sizeof(rgPX4FMV2FirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgPX4FMV2FirmwareArray[i];
        _rgPX4FMUV2Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }
277

Pritam Ghanghas's avatar
Pritam Ghanghas committed
278 279 280 281 282
    size = sizeof(rgAeroCoreFirmwareArray)/sizeof(rgAeroCoreFirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgAeroCoreFirmwareArray[i];
        _rgAeroCoreFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }
283

Pritam Ghanghas's avatar
Pritam Ghanghas committed
284 285 286 287 288
    size = sizeof(rgPX4FMUV1FirmwareArray)/sizeof(rgPX4FMUV1FirmwareArray[0]);
    for (int i = 0; i < size; i++) {
        const FirmwareToUrlElement_t& element = rgPX4FMUV1FirmwareArray[i];
        _rgPX4FMUV1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
    }
289

Pritam Ghanghas's avatar
Pritam Ghanghas committed
290 291 292 293 294
    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);
    }
295

Pritam Ghanghas's avatar
Pritam Ghanghas committed
296 297 298 299 300 301
    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);
    }
}
302

Pritam Ghanghas's avatar
Pritam Ghanghas committed
303 304 305 306 307 308
/// @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.");
}
309

Pritam Ghanghas's avatar
Pritam Ghanghas committed
310
/// @brief Prompts the user to select a firmware file if needed and moves the state machine to the next state.
311
void FirmwareUpgradeController::_getFirmwareFile(FirmwareIdentifier firmwareId)
Pritam Ghanghas's avatar
Pritam Ghanghas committed
312 313 314
{
    // make sure the firmware hashes are populated
    _initFirmwareHash();
315

Don Gagne's avatar
Don Gagne committed
316
    // Select the firmware set based on board type
Don Gagne's avatar
Don Gagne committed
317
    
318
    QHash<FirmwareIdentifier, QString> prgFirmware;
Don Gagne's avatar
Don Gagne committed
319 320 321
    
    switch (_bootloaderBoardID) {
        case Bootloader::boardIDPX4FMUV1:
Pritam Ghanghas's avatar
Pritam Ghanghas committed
322
            prgFirmware = _rgPX4FMUV1Firmware;
Don Gagne's avatar
Don Gagne committed
323 324
            break;
            
Don Gagne's avatar
Don Gagne committed
325
        case Bootloader::boardIDPX4Flow:
Pritam Ghanghas's avatar
Pritam Ghanghas committed
326
            prgFirmware = _rgPX4FLowFirmware;
Don Gagne's avatar
Don Gagne committed
327 328
            break;
            
Don Gagne's avatar
Don Gagne committed
329
        case Bootloader::boardIDPX4FMUV2:
Pritam Ghanghas's avatar
Pritam Ghanghas committed
330
            prgFirmware = _rgPX4FMUV2Firmware;
Don Gagne's avatar
Don Gagne committed
331
            break;
Don Gagne's avatar
Don Gagne committed
332 333
            
        case Bootloader::boardIDAeroCore:
Pritam Ghanghas's avatar
Pritam Ghanghas committed
334
            prgFirmware = _rgAeroCoreFirmware;
335
            break;
Don Gagne's avatar
Don Gagne committed
336 337
            
        case Bootloader::boardID3DRRadio:
Pritam Ghanghas's avatar
Pritam Ghanghas committed
338
            prgFirmware = _rg3DRRadioFirmware;
Don Gagne's avatar
Don Gagne committed
339 340
            break;
            
Don Gagne's avatar
Don Gagne committed
341 342 343
        default:
            break;
    }
Don Gagne's avatar
Don Gagne committed
344
    
345
    if (prgFirmware.isEmpty() && firmwareId.firmwareType != CustomFirmware) {
Don Gagne's avatar
Don Gagne committed
346 347
        _errorCancel("Attempting to flash an unknown board type, you must select 'Custom firmware file'");
        return;
Don Gagne's avatar
Don Gagne committed
348 349
    }
    
350
    if (firmwareId.firmwareType == CustomFirmware) {
Don Gagne's avatar
Don Gagne committed
351
        _firmwareFilename = QGCFileDialog::getOpenFileName(NULL,                                                                // Parent to main window
Don Gagne's avatar
Don Gagne committed
352
                                                           "Select Firmware File",                                              // Dialog Caption
Don Gagne's avatar
Don Gagne committed
353
                                                           QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), // Initial directory
Don Gagne's avatar
Don Gagne committed
354
                                                           "Firmware Files (*.px4 *.bin *.ihx)");                               // File filter
Don Gagne's avatar
Don Gagne committed
355
    } else {
356

357 358
        if (prgFirmware.contains(firmwareId)) {
            _firmwareFilename = prgFirmware.value(firmwareId);
Don Gagne's avatar
Don Gagne committed
359 360 361 362
        } else {
            _errorCancel("Unable to find specified firmware download location");
            return;
        }
Don Gagne's avatar
Don Gagne committed
363 364 365
    }
    
    if (_firmwareFilename.isEmpty()) {
Don Gagne's avatar
Don Gagne committed
366
        _errorCancel("No firmware file selected");
Don Gagne's avatar
Don Gagne committed
367 368 369 370 371 372 373 374 375 376
    } else {
        _downloadFirmware();
    }
}

/// @brief Begins the process of downloading the selected firmware file.
void FirmwareUpgradeController::_downloadFirmware(void)
{
    Q_ASSERT(!_firmwareFilename.isEmpty());
    
Don Gagne's avatar
Don Gagne committed
377 378
    _appendStatusLog("Downloading firmware...");
    _appendStatusLog(QString(" From: %1").arg(_firmwareFilename));
Don Gagne's avatar
Don Gagne committed
379 380 381 382 383 384 385 386 387 388
    
    // Split out filename from path
    QString firmwareFilename = QFileInfo(_firmwareFilename).fileName();
    Q_ASSERT(!firmwareFilename.isEmpty());
    
    // Determine location to download file to
    QString downloadFile = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
    if (downloadFile.isEmpty()) {
        downloadFile = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
        if (downloadFile.isEmpty()) {
Don Gagne's avatar
Don Gagne committed
389
            _errorCancel("Unabled to find writable download location. Tried downloads and temp directory.");
Don Gagne's avatar
Don Gagne committed
390 391 392 393
            return;
        }
    }
    Q_ASSERT(!downloadFile.isEmpty());
Don Gagne's avatar
Don Gagne committed
394
    downloadFile += "/"  + firmwareFilename;
Don Gagne's avatar
Don Gagne committed
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424

    QUrl firmwareUrl;
    if (_firmwareFilename.startsWith("http:")) {
        firmwareUrl.setUrl(_firmwareFilename);
    } else {
        firmwareUrl = QUrl::fromLocalFile(_firmwareFilename);
    }
    Q_ASSERT(firmwareUrl.isValid());
    
    QNetworkRequest networkRequest(firmwareUrl);
    
    // Store download file location in user attribute so we can retrieve when the download finishes
    networkRequest.setAttribute(QNetworkRequest::User, downloadFile);
    
    _downloadManager = new QNetworkAccessManager(this);
    Q_CHECK_PTR(_downloadManager);
    _downloadNetworkReply = _downloadManager->get(networkRequest);
    Q_ASSERT(_downloadNetworkReply);
    connect(_downloadNetworkReply, &QNetworkReply::downloadProgress, this, &FirmwareUpgradeController::_downloadProgress);
    connect(_downloadNetworkReply, &QNetworkReply::finished, this, &FirmwareUpgradeController::_downloadFinished);
    // FIXME
    //connect(_downloadNetworkReply, &QNetworkReply::error, this, &FirmwareUpgradeController::_downloadError);
    connect(_downloadNetworkReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(_downloadError(QNetworkReply::NetworkError)));
}

/// @brief Updates the progress indicator while downloading
void FirmwareUpgradeController::_downloadProgress(qint64 curr, qint64 total)
{
    // Take care of cases where 0 / 0 is emitted as error return value
    if (total > 0) {
Don Gagne's avatar
Don Gagne committed
425
        _progressBar->setProperty("value", (float)curr / (float)total);
Don Gagne's avatar
Don Gagne committed
426 427 428 429 430 431
    }
}

/// @brief Called when the firmware download completes.
void FirmwareUpgradeController::_downloadFinished(void)
{
Don Gagne's avatar
Don Gagne committed
432
    _appendStatusLog("Download complete");
Don Gagne's avatar
Don Gagne committed
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
    
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());
    Q_ASSERT(reply);
    
    Q_ASSERT(_downloadNetworkReply == reply);
    
    _downloadManager->deleteLater();
    _downloadManager = NULL;
    
    // When an error occurs or the user cancels the download, we still end up here. So bail out in
    // those cases.
    if (reply->error() != QNetworkReply::NoError) {
        return;
    }
    
    // Download file location is in user attribute
    QString downloadFilename = reply->request().attribute(QNetworkRequest::User).toString();
    Q_ASSERT(!downloadFilename.isEmpty());
    
    // Store downloaded file in download location
    QFile file(downloadFilename);
    if (!file.open(QIODevice::WriteOnly)) {
Don Gagne's avatar
Don Gagne committed
455
        _errorCancel(QString("Could not save downloaded file to %1. Error: %2").arg(downloadFilename).arg(file.errorString()));
Don Gagne's avatar
Don Gagne committed
456 457 458 459 460
        return;
    }
    
    file.write(reply->readAll());
    file.close();
Don Gagne's avatar
Don Gagne committed
461
    FirmwareImage* image = new FirmwareImage(this);
Don Gagne's avatar
Don Gagne committed
462
    
Don Gagne's avatar
Don Gagne committed
463 464
    connect(image, &FirmwareImage::statusMessage, this, &FirmwareUpgradeController::_status);
    connect(image, &FirmwareImage::errorMessage, this, &FirmwareUpgradeController::_error);
Don Gagne's avatar
Don Gagne committed
465
    
Don Gagne's avatar
Don Gagne committed
466 467 468
    if (!image->load(downloadFilename, _bootloaderBoardID)) {
        _errorCancel("Image load failed");
        return;
Don Gagne's avatar
Don Gagne committed
469 470
    }
    
Don Gagne's avatar
Don Gagne committed
471 472
    // We can't proceed unless we have the bootloader
    if (!_bootloaderFound) {
473
        _errorCancel("Bootloader not found");
Don Gagne's avatar
Don Gagne committed
474 475
        return;
    }
476
    
477 478
    if (_bootloaderBoardFlashSize != 0 && image->imageSize() > _bootloaderBoardFlashSize) {
        _errorCancel(QString("Image size of %1 is too large for board flash size %2").arg(image->imageSize()).arg(_bootloaderBoardFlashSize));
Don Gagne's avatar
Don Gagne committed
479
        return;
480
    }
Don Gagne's avatar
Don Gagne committed
481 482

    _threadController->flash(image);
483 484
}

Don Gagne's avatar
Don Gagne committed
485 486 487
/// @brief Called when an error occurs during download
void FirmwareUpgradeController::_downloadError(QNetworkReply::NetworkError code)
{
Don Gagne's avatar
Don Gagne committed
488 489
    QString errorMsg;
    
Don Gagne's avatar
Don Gagne committed
490
    if (code == QNetworkReply::OperationCanceledError) {
Don Gagne's avatar
Don Gagne committed
491
        errorMsg = "Download cancelled";
Don Gagne's avatar
Don Gagne committed
492
    } else {
Don Gagne's avatar
Don Gagne committed
493
        errorMsg = QString("Error during download. Error: %1").arg(code);
Don Gagne's avatar
Don Gagne committed
494
    }
Don Gagne's avatar
Don Gagne committed
495
    _errorCancel(errorMsg);
Don Gagne's avatar
Don Gagne committed
496 497
}

Don Gagne's avatar
Don Gagne committed
498 499 500
/// @brief Signals completion of one of the specified bootloader commands. Moves the state machine to the
///         appropriate next step.
void FirmwareUpgradeController::_flashComplete(void)
Don Gagne's avatar
Don Gagne committed
501
{
Don Gagne's avatar
Don Gagne committed
502 503
    delete _image;
    _image = NULL;
Don Gagne's avatar
Don Gagne committed
504
    
Don Gagne's avatar
Don Gagne committed
505 506 507
    _appendStatusLog("Upgrade complete", true);
    _appendStatusLog("------------------------------------------", false);
    emit flashComplete();
Don Gagne's avatar
Don Gagne committed
508 509
}

Don Gagne's avatar
Don Gagne committed
510
void FirmwareUpgradeController::_error(const QString& errorString)
Don Gagne's avatar
Don Gagne committed
511
{
Don Gagne's avatar
Don Gagne committed
512 513 514 515
    delete _image;
    _image = NULL;
    
    _errorCancel(QString("Error: %1").arg(errorString));
Don Gagne's avatar
Don Gagne committed
516 517
}

Don Gagne's avatar
Don Gagne committed
518
void FirmwareUpgradeController::_status(const QString& statusString)
Don Gagne's avatar
Don Gagne committed
519
{
Don Gagne's avatar
Don Gagne committed
520
    _appendStatusLog(statusString);
Don Gagne's avatar
Don Gagne committed
521 522 523 524 525
}

/// @brief Updates the progress bar from long running bootloader commands
void FirmwareUpgradeController::_updateProgress(int curr, int total)
{
Don Gagne's avatar
Don Gagne committed
526 527 528 529
    // Take care of cases where 0 / 0 is emitted as error return value
    if (total > 0) {
        _progressBar->setProperty("value", (float)curr / (float)total);
    }
Don Gagne's avatar
Don Gagne committed
530 531 532 533 534 535
}

/// @brief Moves the progress bar ahead on tick while erasing the board
void FirmwareUpgradeController::_eraseProgressTick(void)
{
    _eraseTickCount++;
Don Gagne's avatar
Don Gagne committed
536
    _progressBar->setProperty("value", (float)(_eraseTickCount*_eraseTickMsec) / (float)_eraseTotalMsec);
Don Gagne's avatar
Don Gagne committed
537 538 539
}

/// Appends the specified text to the status log area in the ui
Don Gagne's avatar
Don Gagne committed
540
void FirmwareUpgradeController::_appendStatusLog(const QString& text, bool critical)
Don Gagne's avatar
Don Gagne committed
541 542 543 544
{
    Q_ASSERT(_statusLog);
    
    QVariant returnedValue;
Don Gagne's avatar
Don Gagne committed
545 546 547 548 549 550 551 552
    QVariant varText;
    
    if (critical) {
        varText = QString("<font color=\"yellow\">%1</font>").arg(text);
    } else {
        varText = text;
    }
    
Don Gagne's avatar
Don Gagne committed
553 554 555 556
    QMetaObject::invokeMethod(_statusLog,
                              "append",
                              Q_RETURN_ARG(QVariant, returnedValue),
                              Q_ARG(QVariant, varText));
Don Gagne's avatar
Don Gagne committed
557
}
558

Don Gagne's avatar
Don Gagne committed
559
bool FirmwareUpgradeController::qgcConnections(void)
560
{
561
    return qgcApp()->toolbox()->linkManager()->anyConnectedLinks();
562 563
}

Don Gagne's avatar
Don Gagne committed
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
void FirmwareUpgradeController::_linkDisconnected(LinkInterface* link)
{
    Q_UNUSED(link);
    emit qgcConnectionsChanged(qgcConnections());
}

void FirmwareUpgradeController::_errorCancel(const QString& msg)
{
    _appendStatusLog(msg, false);
    _appendStatusLog("Upgrade cancelled", true);
    _appendStatusLog("------------------------------------------", false);
    emit error();
    cancel();
}

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)
587
{
Don Gagne's avatar
Don Gagne committed
588
    _eraseTimer.stop();
589
}