OP-1900 have path_progress updated correctly for leg_remaining and error_below end...
[librepilot.git] / ground / openpilotgcs / src / plugins / uploader / devicewidget.cpp
blobaf120d9b328fdbf04e72cb2a4874bddb7c84277d
1 /**
2 ******************************************************************************
4 * @file devicewidget.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * @addtogroup GCSPlugins GCS Plugins
7 * @{
8 * @addtogroup Uploader Serial and USB Uploader Plugin
9 * @{
10 * @brief The USB and Serial protocol uploader plugin
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "devicewidget.h"
29 DeviceWidget::DeviceWidget(QWidget *parent) :
30 QWidget(parent)
32 myDevice = new Ui_deviceWidget();
33 myDevice->setupUi(this);
35 // Initialization of the Device icon display
36 myDevice->verticalGroupBox_loaded->setVisible(false);
37 myDevice->groupCustom->setVisible(false);
38 myDevice->confirmCheckBox->setVisible(false);
39 myDevice->gVDevice->setScene(new QGraphicsScene(this));
40 connect(myDevice->retrieveButton, SIGNAL(clicked()), this, SLOT(downloadFirmware()));
41 connect(myDevice->updateButton, SIGNAL(clicked()), this, SLOT(uploadFirmware()));
42 connect(myDevice->pbLoad, SIGNAL(clicked()), this, SLOT(loadFirmware()));
43 connect(myDevice->confirmCheckBox, SIGNAL(stateChanged(int)), this, SLOT(confirmCB(int)));
44 myDevice->statusIcon->setPixmap(QPixmap(":uploader/images/view-refresh.svg"));
46 myDevice->lblCertified->setText("");
49 void DeviceWidget::showEvent(QShowEvent *event)
51 Q_UNUSED(event)
52 // Thit fitInView method should only be called now, once the
53 // widget is shown, otherwise it cannot compute its values and
54 // the result is usually a ahrsbargraph that is way too small
55 myDevice->gVDevice->fitInView(devicePic.rect(), Qt::KeepAspectRatio);
58 void DeviceWidget::resizeEvent(QResizeEvent *event)
60 Q_UNUSED(event);
61 myDevice->gVDevice->fitInView(devicePic.rect(), Qt::KeepAspectRatio);
64 void DeviceWidget::setDeviceID(int devID)
66 deviceID = devID;
69 void DeviceWidget::setDfu(DFUObject *dfu)
71 m_dfu = dfu;
74 /**
75 Fills the various fields for the device
77 void DeviceWidget::populate()
79 int id = m_dfu->devices[deviceID].ID;
81 // Exclude CC/CC3D
82 if ((id == 0x0401) || (id == 0x0402)) {
83 return;
86 myDevice->lbldevID->setText(tr("Device ID: ") + QString::number(id, 16));
87 // DeviceID tells us what sort of HW we have detected:
88 // display a nice icon:
89 myDevice->gVDevice->scene()->clear();
90 myDevice->lblDevName->setText(deviceDescriptorStruct::idToBoardName(id));
91 myDevice->lblHWRev->setText(tr("HW Revision: ") + QString::number(id & 0x00FF, 16));
93 switch (id) {
94 case 0x0301:
95 devicePic.load(":/uploader/images/gcs-board-oplink.png");
96 break;
97 case 0x0903:
98 case 0x0904:
99 devicePic.load(":/uploader/images/gcs-board-revo.png");
100 break;
101 case 0x0905:
102 devicePic.load(":/uploader/images/gcs-board-nano.png");
103 break;
104 default:
105 // Clear
106 devicePic.load("");
107 break;
109 myDevice->gVDevice->scene()->addPixmap(devicePic);
110 myDevice->gVDevice->setSceneRect(devicePic.rect());
111 myDevice->gVDevice->fitInView(devicePic.rect(), Qt::KeepAspectRatio);
113 bool r = m_dfu->devices[deviceID].Readable;
114 bool w = m_dfu->devices[deviceID].Writable;
116 myDevice->lblAccess->setText(tr("Flash access: ") + QString(r ? "R" : "-") + QString(w ? "W" : "-"));
117 myDevice->lblMaxCode->setText(tr("Max code size: ") + QString::number(m_dfu->devices[deviceID].SizeOfCode));
118 myDevice->lblCRC->setText(QString::number(m_dfu->devices[deviceID].FW_CRC));
119 myDevice->lblBLVer->setText(tr("BL version: ") + QString::number(m_dfu->devices[deviceID].BL_Version));
120 int size = ((OP_DFU::device)m_dfu->devices[deviceID]).SizeOfDesc;
121 m_dfu->enterDFU(deviceID);
122 QByteArray desc = m_dfu->DownloadDescriptionAsBA(size);
124 if (!populateBoardStructuredDescription(desc)) {
125 // desc was not a structured description
126 QString str = m_dfu->DownloadDescription(size);
127 myDevice->lblDescription->setText((!str.isEmpty()) ? str : tr("Unknown"));
128 myDevice->lblCertified->setPixmap(QPixmap(":uploader/images/warning.svg"));
129 myDevice->lblCertified->setToolTip(tr("Custom Firmware Build"));
130 myDevice->lblBuildDate->setText(tr("Unknown"));
131 myDevice->lblGitTag->setText(tr("Unknown"));
132 myDevice->lblBrdName->setText(tr("Unknown"));
134 myDevice->filenameLabel->setText(tr("No file loaded"));
135 status("Ready...", STATUSICON_INFO);
136 QString fwFileName = getDevFirmwarePath();
137 QFile fwFile(fwFileName);
138 if (fwFile.exists()) {
139 loadFirmware(fwFileName);
144 Freezes the contents of the widget so that a user cannot
145 try to modify the contents
147 void DeviceWidget::freeze()
149 updateButtons(false);
152 void DeviceWidget::updateButtons(bool enabled)
154 if (!enabled) {
155 myDevice->description->setEnabled(false);
156 myDevice->pbLoad->setEnabled(false);
157 myDevice->confirmCheckBox->setEnabled(false);
158 myDevice->updateButton->setEnabled(false);
159 myDevice->retrieveButton->setEnabled(false);
160 } else {
161 myDevice->description->setEnabled(true);
162 // Load button (i.e. choose file) is always enabled
163 myDevice->pbLoad->setEnabled(true);
164 myDevice->confirmCheckBox->setEnabled(true);
165 // Update/Upload button is enabled if the "I know what I'm doing!" check box is checked
166 myDevice->updateButton->setEnabled(myDevice->confirmCheckBox->checkState() == Qt::Checked);
167 // Retreive/Download button is always enabled
168 myDevice->retrieveButton->setEnabled(true);
173 Populates the widget field with the description in case
174 it is structured properly
176 bool DeviceWidget::populateBoardStructuredDescription(QByteArray desc)
178 if (UAVObjectUtilManager::descriptionToStructure(desc, onBoardDescription)) {
179 myDevice->lblGitTag->setText(onBoardDescription.gitHash);
180 myDevice->lblBuildDate->setText(onBoardDescription.gitDate.insert(4, "-").insert(7, "-"));
181 if (onBoardDescription.gitTag.startsWith("RELEASE", Qt::CaseSensitive)) {
182 myDevice->lblDescription->setText(onBoardDescription.gitTag);
183 myDevice->lblCertified->setPixmap(QPixmap(":uploader/images/application-certificate.svg"));
184 myDevice->lblCertified->setToolTip(tr("Tagged officially released firmware build"));
185 } else {
186 myDevice->lblDescription->setText(onBoardDescription.gitTag);
187 myDevice->lblCertified->setPixmap(QPixmap(":uploader/images/warning.svg"));
188 myDevice->lblCertified->setToolTip(tr("Untagged or custom firmware build"));
191 myDevice->lblBrdName->setText(deviceDescriptorStruct::idToBoardName(onBoardDescription.boardType << 8 | onBoardDescription.boardRevision));
193 return true;
196 return false;
199 bool DeviceWidget::populateLoadedStructuredDescription(QByteArray desc)
201 if (UAVObjectUtilManager::descriptionToStructure(desc, LoadedDescription)) {
202 myDevice->lblGitTagL->setText(LoadedDescription.gitHash);
203 myDevice->lblBuildDateL->setText(LoadedDescription.gitDate.insert(4, "-").insert(7, "-"));
204 if (LoadedDescription.gitTag.startsWith("RELEASE", Qt::CaseSensitive)) {
205 myDevice->lblDescritpionL->setText(LoadedDescription.gitTag);
206 myDevice->description->setText(LoadedDescription.gitTag);
207 myDevice->lblCertifiedL->setPixmap(QPixmap(":uploader/images/application-certificate.svg"));
208 myDevice->lblCertifiedL->setToolTip(tr("Tagged officially released firmware build"));
209 } else {
210 myDevice->lblDescritpionL->setText(LoadedDescription.gitTag);
211 myDevice->description->setText(LoadedDescription.gitTag);
212 myDevice->lblCertifiedL->setPixmap(QPixmap(":uploader/images/warning.svg"));
213 myDevice->lblCertifiedL->setToolTip(tr("Untagged or custom firmware build"));
215 myDevice->lblBrdNameL->setText(deviceDescriptorStruct::idToBoardName(LoadedDescription.boardType << 8 | LoadedDescription.boardRevision));
217 return true;
219 return false;
223 Updates status message for messages coming from DFU
225 void DeviceWidget::dfuStatus(QString str)
227 status(str, STATUSICON_RUNNING);
230 void DeviceWidget::confirmCB(int value)
232 Q_UNUSED(value);
233 updateButtons(true);
237 Updates status message
239 void DeviceWidget::status(QString str, StatusIcon ic)
241 QPixmap px;
243 myDevice->statusLabel->setText(str);
244 switch (ic) {
245 case STATUSICON_RUNNING:
246 px.load(QString(":/uploader/images/system-run.svg"));
247 break;
248 case STATUSICON_OK:
249 px.load(QString(":/uploader/images/dialog-apply.svg"));
250 break;
251 case STATUSICON_FAIL:
252 px.load(QString(":/uploader/images/process-stop.svg"));
253 break;
254 default:
255 px.load(QString(":/uploader/images/gtk-info.svg"));
257 myDevice->statusIcon->setPixmap(px);
260 void DeviceWidget::loadFirmware()
262 QString file = setOpenFileName();
264 loadFirmware(file);
267 void DeviceWidget::loadFirmware(QString fwfilename)
269 myDevice->verticalGroupBox_loaded->setVisible(false);
270 myDevice->groupCustom->setVisible(false);
272 filename = fwfilename;
274 myDevice->confirmCheckBox->setVisible(false);
275 myDevice->confirmCheckBox->setChecked(false);
277 if (filename.isEmpty()) {
278 status("Empty filename", STATUSICON_FAIL);
279 return;
282 QFile file(filename);
283 if (!file.open(QIODevice::ReadOnly)) {
284 status("Can't open file", STATUSICON_FAIL);
285 return;
288 loadedFW = file.readAll();
290 QByteArray desc = loadedFW.right(100);
291 QPixmap px;
292 if (loadedFW.length() > (int)m_dfu->devices[deviceID].SizeOfCode) {
293 myDevice->lblCRCL->setText(tr("Can't calculate, file too big for device"));
294 } else {
295 myDevice->lblCRCL->setText(QString::number(DFUObject::CRCFromQBArray(loadedFW, m_dfu->devices[deviceID].SizeOfCode)));
298 // myDevice->lblFirmwareSizeL->setText(QString("Firmware size: ")+QVariant(loadedFW.length()).toString()+ QString(" bytes"));
299 if (populateLoadedStructuredDescription(desc)) {
300 myDevice->confirmCheckBox->setChecked(true);
301 myDevice->verticalGroupBox_loaded->setVisible(true);
302 myDevice->groupCustom->setVisible(false);
303 if (myDevice->lblCRC->text() == myDevice->lblCRCL->text()) {
304 myDevice->statusLabel->setText(tr("The board has the same firmware as loaded. No need to update."));
305 px.load(QString(":/uploader/images/warning.svg"));
306 } else if (myDevice->lblDevName->text() != myDevice->lblBrdNameL->text()) {
307 myDevice->statusLabel->setText(tr("WARNING: the loaded firmware is for different hardware. Do not update!"));
308 px.load(QString(":/uploader/images/error.svg"));
309 } else if (QDateTime::fromString(onBoardDescription.gitDate) > QDateTime::fromString(LoadedDescription.gitDate)) {
310 myDevice->statusLabel->setText(tr("The board has newer firmware than loaded. Are you sure you want to update?"));
311 px.load(QString(":/uploader/images/warning.svg"));
312 } else if (!LoadedDescription.gitTag.startsWith("RELEASE", Qt::CaseSensitive)) {
313 myDevice->statusLabel->setText(tr("The loaded firmware is untagged or custom build. Update only if it was received from a trusted source (official website or your own build)."));
314 px.load(QString(":/uploader/images/warning.svg"));
315 } else {
316 myDevice->statusLabel->setText(tr("This is the tagged officially released OpenPilot firmware."));
317 px.load(QString(":/uploader/images/gtk-info.svg"));
319 } else {
320 myDevice->statusLabel->setText(tr("WARNING: the loaded firmware was not packaged with the OpenPilot format. Do not update unless you know what you are doing."));
321 px.load(QString(":/uploader/images/error.svg"));
322 myDevice->confirmCheckBox->setChecked(false);
323 myDevice->confirmCheckBox->setVisible(true);
324 myDevice->verticalGroupBox_loaded->setVisible(false);
325 myDevice->groupCustom->setVisible(true);
327 myDevice->filenameLabel->setText(tr("Firmware loaded: ") + filename);
328 myDevice->statusIcon->setPixmap(px);
332 Sends a firmware to the device
334 void DeviceWidget::uploadFirmware()
336 // clear progress bar now
337 // this avoids displaying an error message and the progress at 100% at the same time
338 setProgress(0);
339 updateButtons(false);
341 if (!m_dfu->devices[deviceID].Writable) {
342 status("Device not writable!", STATUSICON_FAIL);
343 updateButtons(true);
344 return;
347 bool verify = false;
348 /* TODO: does not work properly on current Bootloader!
349 if (m_dfu->devices[deviceID].Readable)
350 verify = true;
353 QByteArray desc = loadedFW.right(100);
354 if (desc.startsWith("OpFw")) {
355 descriptionArray = desc;
356 // Now do sanity checking:
357 // - Check whether board type matches firmware:
358 int board = m_dfu->devices[deviceID].ID;
359 int firmwareBoard = ((desc.at(12) & 0xff) << 8) + (desc.at(13) & 0xff);
360 if ((board == 0x901 && firmwareBoard == 0x902) || // L3GD20 revo supports Revolution firmware
361 (board == 0x902 && firmwareBoard == 0x903)) { // RevoMini1 supporetd by RevoMini2 firmware
362 // These firmwares are designed to be backwards compatible
363 } else if (firmwareBoard != board) {
364 status("Error: firmware does not match board", STATUSICON_FAIL);
365 updateButtons(true);
366 return;
368 // Check the firmware embedded in the file:
369 QByteArray firmwareHash = desc.mid(40, 20);
370 QByteArray fileHash = QCryptographicHash::hash(loadedFW.left(loadedFW.length() - 100), QCryptographicHash::Sha1);
371 if (firmwareHash != fileHash) {
372 status("Error: firmware file corrupt", STATUSICON_FAIL);
373 updateButtons(true);
374 return;
376 } else {
377 // The firmware is not packaged, just upload the text in the description field
378 // if it is there.
379 descriptionArray.clear();
382 status("Starting firmware upload", STATUSICON_RUNNING);
383 emit uploadStarted();
385 // We don't know which device was used previously, so we
386 // are cautious and reenter DFU for this deviceID:
387 if (!m_dfu->enterDFU(deviceID)) {
388 emit uploadEnded(false);
389 status("Error:Could not enter DFU mode", STATUSICON_FAIL);
390 updateButtons(true);
391 return;
393 OP_DFU::Status ret = m_dfu->StatusRequest();
394 qDebug() << m_dfu->StatusToString(ret);
395 m_dfu->AbortOperation(); // Necessary, otherwise I get random failures.
397 connect(m_dfu, SIGNAL(progressUpdated(int)), this, SLOT(setProgress(int)));
398 connect(m_dfu, SIGNAL(operationProgress(QString)), this, SLOT(dfuStatus(QString)));
399 connect(m_dfu, SIGNAL(uploadFinished(OP_DFU::Status)), this, SLOT(uploadFinished(OP_DFU::Status)));
400 bool retstatus = m_dfu->UploadFirmware(filename, verify, deviceID);
401 if (!retstatus) {
402 emit uploadEnded(false);
403 status("Could not start upload!", STATUSICON_FAIL);
404 updateButtons(true);
405 return;
408 status("Uploading, please wait...", STATUSICON_RUNNING);
412 Retrieves the firmware from the device
414 void DeviceWidget::downloadFirmware()
416 // clear progress bar now
417 // this avoids displaying an error message and the progress at 100% at the same time
418 setProgress(0);
419 updateButtons(false);
421 if (!m_dfu->devices[deviceID].Readable) {
422 myDevice->statusLabel->setText(QString("Device not readable!"));
423 status("Device not readable!", STATUSICON_FAIL);
424 updateButtons(true);
425 return;
428 filename = setSaveFileName();
429 if (filename.isEmpty()) {
430 status("Empty filename", STATUSICON_FAIL);
431 updateButtons(true);
432 return;
435 status("Starting firmware download", STATUSICON_RUNNING);
436 emit downloadStarted();
438 connect(m_dfu, SIGNAL(progressUpdated(int)), this, SLOT(setProgress(int)));
439 connect(m_dfu, SIGNAL(downloadFinished()), this, SLOT(downloadFinished()));
441 downloadedFirmware.clear(); // Empty the byte array
442 bool ret = m_dfu->DownloadFirmware(&downloadedFirmware, deviceID);
444 if (!ret) {
445 emit downloadEnded(false);
446 status("Could not start download!", STATUSICON_FAIL);
447 updateButtons(true);
448 return;
451 status("Downloading, please wait...", STATUSICON_RUNNING);
455 Callback for the firmware download result
457 void DeviceWidget::downloadFinished()
459 disconnect(m_dfu, SIGNAL(downloadFinished()), this, SLOT(downloadFinished()));
460 disconnect(m_dfu, SIGNAL(progressUpdated(int)), this, SLOT(setProgress(int)));
462 // Now save the result (use the utility function from OP_DFU)
463 m_dfu->SaveByteArrayToFile(filename, downloadedFirmware);
465 emit downloadEnded(true);
466 status("Download successful", STATUSICON_OK);
467 updateButtons(true);
471 Callback for the firmware upload result
473 void DeviceWidget::uploadFinished(OP_DFU::Status retstatus)
475 disconnect(m_dfu, SIGNAL(uploadFinished(OP_DFU::Status)), this, SLOT(uploadFinished(OP_DFU::Status)));
476 disconnect(m_dfu, SIGNAL(progressUpdated(int)), this, SLOT(setProgress(int)));
477 disconnect(m_dfu, SIGNAL(operationProgress(QString)), this, SLOT(dfuStatus(QString)));
479 if (retstatus != OP_DFU::Last_operation_Success) {
480 emit uploadEnded(false);
481 status(QString("Upload failed with code: ") + m_dfu->StatusToString(retstatus).toLatin1().data(), STATUSICON_FAIL);
482 updateButtons(true);
483 return;
484 } else if (!descriptionArray.isEmpty()) {
485 // We have a structured array to save
486 status(QString("Updating description"), STATUSICON_RUNNING);
487 repaint(); // Make sure the text above shows right away
488 retstatus = m_dfu->UploadDescription(descriptionArray);
489 if (retstatus != OP_DFU::Last_operation_Success) {
490 emit uploadEnded(false);
491 status(QString("Upload failed with code: ") + m_dfu->StatusToString(retstatus).toLatin1().data(), STATUSICON_FAIL);
492 updateButtons(true);
493 return;
495 } else if (!myDevice->description->text().isEmpty()) {
496 // Fallback: we save the description field:
497 status(QString("Updating description"), STATUSICON_RUNNING);
498 repaint(); // Make sure the text above shows right away
499 retstatus = m_dfu->UploadDescription(myDevice->description->text());
500 if (retstatus != OP_DFU::Last_operation_Success) {
501 emit uploadEnded(false);
502 status(QString("Upload failed with code: ") + m_dfu->StatusToString(retstatus).toLatin1().data(), STATUSICON_FAIL);
503 updateButtons(true);
504 return;
508 populate();
510 emit uploadEnded(true);
511 status("Upload successful", STATUSICON_OK);
512 updateButtons(true);
516 Slot to update the progress bar
518 void DeviceWidget::setProgress(int percent)
520 myDevice->progressBar->setValue(percent);
523 QString DeviceWidget::getDevFirmwarePath()
525 QDir fwDirectory;
526 QString fwDirectoryStr;
528 fwDirectoryStr = QCoreApplication::applicationDirPath();
529 fwDirectory = QDir(fwDirectoryStr);
530 #ifdef Q_OS_WIN
531 fwDirectory.cd("../..");
532 fwDirectoryStr = fwDirectory.absolutePath();
533 #elif defined Q_OS_LINUX
534 fwDirectory.cd("../..");
535 fwDirectoryStr = fwDirectory.absolutePath();
536 #elif defined Q_OS_MAC
537 fwDirectory.cd("../../../../../..");
538 fwDirectoryStr = fwDirectory.absolutePath();
539 #endif
540 fwDirectoryStr = fwDirectoryStr + "/fw_" + myDevice->lblBrdName->text().toLower() + "/fw_" + myDevice->lblBrdName->text().toLower() + ".opfw";
541 return fwDirectoryStr;
545 * Opens an open file dialog.
547 QString DeviceWidget::setOpenFileName()
549 QString fwDirectoryStr = getDevFirmwarePath();
551 // Format filename for file chooser
552 QString fileName = QFileDialog::getOpenFileName(this,
553 tr("Select firmware file"),
554 fwDirectoryStr,
555 tr("Firmware Files (*.opfw *.bin)"));
557 return fileName;
561 * Set the save file name
563 QString DeviceWidget::setSaveFileName()
565 QFileDialog::Options options;
566 QString selectedFilter;
567 QString fileName = QFileDialog::getSaveFileName(this,
568 tr("Select firmware file"),
570 tr("Firmware Files (*.bin)"),
571 &selectedFilter,
572 options);
574 return fileName;