2 ******************************************************************************
4 * @file devicewidget.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * @addtogroup GCSPlugins GCS Plugins
8 * @addtogroup Uploader Serial and USB Uploader Plugin
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
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
) :
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
)
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
)
61 myDevice
->gVDevice
->fitInView(devicePic
.rect(), Qt::KeepAspectRatio
);
64 void DeviceWidget::setDeviceID(int devID
)
69 void DeviceWidget::setDfu(DFUObject
*dfu
)
75 Fills the various fields for the device
77 void DeviceWidget::populate()
79 int id
= m_dfu
->devices
[deviceID
].ID
;
82 if ((id
== 0x0401) || (id
== 0x0402)) {
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));
95 devicePic
.load(":/uploader/images/gcs-board-oplink.png");
99 devicePic
.load(":/uploader/images/gcs-board-revo.png");
102 devicePic
.load(":/uploader/images/gcs-board-nano.png");
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
)
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);
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"));
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
));
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"));
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
));
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
)
237 Updates status message
239 void DeviceWidget::status(QString str
, StatusIcon ic
)
243 myDevice
->statusLabel
->setText(str
);
245 case STATUSICON_RUNNING
:
246 px
.load(QString(":/uploader/images/system-run.svg"));
249 px
.load(QString(":/uploader/images/dialog-apply.svg"));
251 case STATUSICON_FAIL
:
252 px
.load(QString(":/uploader/images/process-stop.svg"));
255 px
.load(QString(":/uploader/images/gtk-info.svg"));
257 myDevice
->statusIcon
->setPixmap(px
);
260 void DeviceWidget::loadFirmware()
262 QString file
= setOpenFileName();
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
);
282 QFile
file(filename
);
283 if (!file
.open(QIODevice::ReadOnly
)) {
284 status("Can't open file", STATUSICON_FAIL
);
288 loadedFW
= file
.readAll();
290 QByteArray desc
= loadedFW
.right(100);
292 if (loadedFW
.length() > (int)m_dfu
->devices
[deviceID
].SizeOfCode
) {
293 myDevice
->lblCRCL
->setText(tr("Can't calculate, file too big for device"));
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"));
316 myDevice
->statusLabel
->setText(tr("This is the tagged officially released OpenPilot firmware."));
317 px
.load(QString(":/uploader/images/gtk-info.svg"));
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
339 updateButtons(false);
341 if (!m_dfu
->devices
[deviceID
].Writable
) {
342 status("Device not writable!", STATUSICON_FAIL
);
348 /* TODO: does not work properly on current Bootloader!
349 if (m_dfu->devices[deviceID].Readable)
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
);
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
);
377 // The firmware is not packaged, just upload the text in the description field
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
);
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
);
402 emit
uploadEnded(false);
403 status("Could not start upload!", STATUSICON_FAIL
);
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
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
);
428 filename
= setSaveFileName();
429 if (filename
.isEmpty()) {
430 status("Empty filename", STATUSICON_FAIL
);
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
);
445 emit
downloadEnded(false);
446 status("Could not start download!", STATUSICON_FAIL
);
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
);
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
);
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
);
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
);
510 emit
uploadEnded(true);
511 status("Upload successful", STATUSICON_OK
);
516 Slot to update the progress bar
518 void DeviceWidget::setProgress(int percent
)
520 myDevice
->progressBar
->setValue(percent
);
523 QString
DeviceWidget::getDevFirmwarePath()
526 QString fwDirectoryStr
;
528 fwDirectoryStr
= QCoreApplication::applicationDirPath();
529 fwDirectory
= QDir(fwDirectoryStr
);
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();
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"),
555 tr("Firmware Files (*.opfw *.bin)"));
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)"),