32 #include <QFileDialog>
33 #include <QMessageBox>
34 #include <QDesktopServices>
36 #include <QHttpMultiPart>
37 #include <QNetworkAccessManager>
38 #include <QNetworkRequest>
39 #include <QNetworkReply>
43 #include "firmwareiapobj.h"
49 #include "ui_uploader.h"
52 using namespace uploader;
60 , telemetryConnected(false)
63 m_widget =
new Ui_UploaderWidget();
64 m_widget->setupUi(
this);
66 m_widget->partitionBrowserTW->setSelectionMode(QAbstractItemView::SingleSelection);
67 m_widget->partitionBrowserTW->setSelectionBehavior(QAbstractItemView::SelectRows);
71 foreach (QWidget *widget, hideable) {
72 QSizePolicy sp = widget->sizePolicy();
73 sp.setRetainSizeWhenHidden(
true);
74 widget->setSizePolicy(sp);
78 QAction *action =
new QAction(
"Save to file",
this);
79 connect(action, &QAction::triggered,
this, &UploaderGadgetWidget::onPartitionSave);
80 m_widget->partitionBrowserTW->addAction(action);
81 action =
new QAction(
"Flash from file",
this);
82 connect(action, &QAction::triggered,
this, &UploaderGadgetWidget::onPartitionFlash);
83 m_widget->partitionBrowserTW->addAction(action);
84 action =
new QAction(
"Erase",
this);
85 connect(action, &QAction::triggered,
this, &UploaderGadgetWidget::onPartitionErase);
86 m_widget->partitionBrowserTW->addAction(action);
87 m_widget->partitionBrowserTW->setContextMenuPolicy(Qt::ActionsContextMenu);
90 FirmwareOnDeviceClear(
true);
91 FirmwareLoadedClear(
true);
93 PartitionBrowserClear();
94 DeviceInformationClear();
96 pm = ExtensionSystem::PluginManager::instance();
101 netMngr =
new QNetworkAccessManager(
this);
110 Qt::QueuedConnection);
112 &UploaderGadgetWidget::onAutopilotDisconnect, Qt::QueuedConnection);
113 firmwareIap = FirmwareIAPObj::GetInstance(obm);
116 Qt::QueuedConnection);
119 connect(m_widget->openButton, &QAbstractButton::clicked,
this,
120 &UploaderGadgetWidget::onLoadFirmwareButtonClick);
121 connect(m_widget->rescueButton, &QAbstractButton::clicked,
this,
122 &UploaderGadgetWidget::onRescueButtonClick);
123 connect(m_widget->flashButton, &QAbstractButton::clicked,
this,
124 &UploaderGadgetWidget::onFlashButtonClick);
125 connect(m_widget->bootButton, &QAbstractButton::clicked,
this,
126 &UploaderGadgetWidget::onBootButtonClick);
127 connect(m_widget->safeBootButton, &QAbstractButton::clicked,
this,
128 &UploaderGadgetWidget::onBootButtonClick);
129 connect(m_widget->exportConfigButton, &QAbstractButton::clicked,
this,
130 &UploaderGadgetWidget::onExportButtonClick);
132 connect(m_widget->pbHelp, &QAbstractButton::clicked,
this, &UploaderGadgetWidget::openHelp);
140 &UploaderGadgetWidget::onBootloaderRemoved, Qt::QueuedConnection);
143 &UploaderGadgetWidget::onBootloaderDetected, Qt::QueuedConnection);
146 &UploaderGadgetWidget::onBootloaderRemoved, Qt::QueuedConnection);
149 &UploaderGadgetWidget::onBootloaderDetected, Qt::QueuedConnection);
152 &UploaderGadgetWidget::onStatusUpdate);
158 onBootloaderDetected();
173 ExtensionSystem::PluginManager::instance()->removeObject(
this);
181 void UploaderGadgetWidget::FirmwareOnDeviceClear(
bool clear)
183 QRegExp rx(
"*OD_lbl");
184 rx.setPatternSyntax(QRegExp::Wildcard);
185 foreach (QLabel *l, findChildren<QLabel *>(rx)) {
188 l->setVisible(!clear);
190 m_widget->firmwareOnDevicePic->setVisible(!clear);
191 m_widget->noInfoOnDevice->setVisible(clear);
192 m_widget->onDeviceGB->setEnabled(!clear);
200 void UploaderGadgetWidget::FirmwareLoadedClear(
bool clear)
202 QRegExp rx(
"*LD_lbl");
203 rx.setPatternSyntax(QRegExp::Wildcard);
204 foreach (QLabel *l, findChildren<QLabel *>(rx)) {
207 l->setVisible(!clear);
209 m_widget->firmwareLoadedPic->setVisible(!clear);
210 m_widget->noFirmwareLoaded->setVisible(clear);
211 m_widget->loadedGB->setEnabled(!clear);
213 m_widget->userDefined_LD_lbl->clear();
214 m_widget->userDefined_LD_lbl->setVisible(!clear);
220 void UploaderGadgetWidget::PartitionBrowserClear()
222 m_widget->partitionBrowserTW->clearContents();
223 m_widget->partitionBrowserGB->setEnabled(
false);
226 void UploaderGadgetWidget::DeviceInformationClear()
228 m_widget->deviceInformationMainLayout->setVisible(
false);
229 m_widget->deviceInformationNoInfo->setVisible(
true);
230 m_widget->boardName_lbl->setVisible(
false);
231 currentBoard.
board = NULL;
238 void UploaderGadgetWidget::DeviceInformationUpdate(
deviceInfo board)
242 currentBoard = board;
243 m_widget->boardName_lbl->setText(board.
board->boardDescription());
244 m_widget->devName_lbl->setText(
245 board.
board->getBoardNameFromID(board.
board->getBoardType() << 8));
247 m_widget->blVer_lbl->setText(board.
bl_version);
249 int bl_version = board.
bl_version.toInt(&bl_version_ok, 16);
250 m_widget->blVer_lbl->setStyleSheet(
251 bl_version_ok && bl_version < board.board->minBootLoaderVersion() ?
"color: red;" :
"");
253 m_widget->deviceInformationMainLayout->setVisible(
true);
254 m_widget->deviceInformationNoInfo->setVisible(
false);
255 m_widget->boardPic->setPixmap(board.
board->getBoardPicture());
256 FirmwareLoadedUpdate(loadedFile);
267 m_widget->crcOD_lbl->setText(crc);
268 m_widget->gitHashOD_lbl->setText(firmware.
gitHash);
269 m_widget->ancestorHashOD_lbl->setText(firmware.
nextAncestor);
270 m_widget->firmwareDateOD_lbl->setText(firmware.
gitDate);
271 m_widget->firmwareTagOD_lbl->setText(firmware.
gitTag);
272 m_widget->uavosSHA_OD_lbl->setText(firmware.
uavoHash.toHex().toUpper());
273 m_widget->userDefined_OD_lbl->setText(firmware.
userDefined);
276 QPixmap pix = QPixmap(QString(
":uploader/images/application-certificate.svg"));
277 m_widget->firmwareOnDevicePic->setPixmap(pix);
278 m_widget->firmwareOnDevicePic->setToolTip(tr(
"Tagged officially released firmware build"));
280 QPixmap pix = QPixmap(QString(
":uploader/images/warning.svg"));
281 m_widget->firmwareOnDevicePic->setPixmap(pix);
282 m_widget->firmwareOnDevicePic->setToolTip(tr(
"Custom Firmware Build"));
284 FirmwareOnDeviceClear(
false);
291 void UploaderGadgetWidget::FirmwareLoadedUpdate(QByteArray firmwareArray)
293 if (firmwareArray.isEmpty())
298 QPixmap pix = QPixmap(QString(
":uploader/images/error.svg"));
299 m_widget->firmwareLoadedPic->setPixmap(pix);
300 m_widget->firmwareLoadedPic->setToolTip(
301 tr(
"Error unrecognized file format, proceed at your own risk"));
302 m_widget->gitHashLD_lbl->setText(
303 tr(
"Error unrecognized file format, proceed at your own risk"));
304 FirmwareLoadedClear(
false);
305 m_widget->userDefined_LD_lbl->setVisible(
false);
313 m_widget->crcLD_lbl->setText(
"Not Available");
314 else if (firmwareArray.length() > currentBoard.
max_code_size.toLong()) {
315 m_widget->crcLD_lbl->setText(
"Not Available");
317 quint32 crc = dfu.CRCFromQBArray(firmwareArray, currentBoard.
max_code_size.toLong());
318 m_widget->crcLD_lbl->setText(QString::number(crc));
320 m_widget->gitHashLD_lbl->setText(firmware.
gitHash);
321 m_widget->ancestorHashLD_lbl->setText(firmware.
nextAncestor);
322 m_widget->firmwareDateLD_lbl->setText(firmware.
gitDate);
323 m_widget->firmwareTagLD_lbl->setText(firmware.
gitTag);
324 m_widget->uavosSHA_LD_lbl->setText(firmware.
uavoHash.toHex().toUpper());
325 m_widget->userDefined_LD_lbl->setText(firmware.
userDefined);
327 QPixmap pix = QPixmap(QString(
":uploader/images/error.svg"));
328 m_widget->firmwareLoadedPic->setPixmap(pix);
329 m_widget->firmwareLoadedPic->setToolTip(
330 tr(
"Error firmware loaded firmware doesn't match connected board"));
332 QPixmap pix = QPixmap(QString(
":uploader/images/application-certificate.svg"));
333 m_widget->firmwareLoadedPic->setPixmap(pix);
334 m_widget->firmwareLoadedPic->setToolTip(tr(
"Tagged officially released firmware build"));
336 QPixmap pix = QPixmap(QString(
":uploader/images/warning.svg"));
337 m_widget->firmwareLoadedPic->setPixmap(pix);
338 m_widget->firmwareLoadedPic->setToolTip(tr(
"Custom Firmware Build"));
340 FirmwareLoadedClear(
false);
346 void UploaderGadgetWidget::onAutopilotConnect()
348 telemetryConnected =
true;
349 CheckAutopilotReady();
355 void UploaderGadgetWidget::onAutopilotDisconnect()
357 FirmwareOnDeviceClear(
true);
358 DeviceInformationClear();
359 PartitionBrowserClear();
360 telemetryConnected =
false;
374 void UploaderGadgetWidget::onAutopilotReady()
384 DeviceInformationUpdate(board);
387 FirmwareOnDeviceUpdate(device, QString::number(utilMngr->
getFirmwareCRC()));
388 if (FirmwareCheckForUpdate(device)) {
389 onRescueButtonClick();
403 void UploaderGadgetWidget::onIAPUpdated()
409 CheckAutopilotReady();
416 void UploaderGadgetWidget::onLoadFirmwareButtonClick()
419 if (currentBoard.
board)
420 board = currentBoard.
board->shortName().toLower();
421 QString filename = LoadFirmwareFileDialog(board);
422 if (filename.isEmpty()) {
426 QFile
file(filename);
427 if (!
file.open(QIODevice::ReadOnly)) {
431 loadedFile =
file.readAll();
433 FirmwareLoadedClear(
true);
434 FirmwareLoadedUpdate(loadedFile);
435 setUploaderStatus(getUploaderStatus());
439 bool UploaderGadgetWidget::flashFirmware(QByteArray &firmwareImage)
445 connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
447 timeout.setSingleShot(
true);
448 timeout.start(120000);
456 onStatusUpdate(QString(
"Starting upload..."), 0);
461 connect(&dfu, &DFUObject::uploadFinished, &loop, [&](
tl_dfu::Status status) {
462 operationSuccess = status;
479 if ((!firmwareImage.right(100).startsWith(
"TlFw"))
480 && (!firmwareImage.right(100).startsWith(
"OpFw"))) {
487 tempArray.append(firmwareImage.right(100));
490 user = user.replace(0, m_widget->userDefined_LD_lbl->text().length(),
491 m_widget->userDefined_LD_lbl->text());
492 tempArray.append(user.toLatin1());
498 timeout.start(10000);
516 quint32 crc = dfu.CRCFromQBArray(firmwareImage, currentBoard.
max_code_size.toLong());
517 FirmwareOnDeviceUpdate(descStructure, QString::number(crc));
528 void UploaderGadgetWidget::onFlashButtonClick()
530 if (flashFirmware(loadedFile)) {
531 this->activateWindow();
532 m_widget->bootButton->setFocus();
536 void UploaderGadgetWidget::haltOrReset(
bool halting)
538 if (!firmwareIap->getIsPresentOnHardware())
549 timeout.setSingleShot(
true);
550 firmwareIap->setBoardRevision(0);
551 firmwareIap->setBoardType(0);
552 connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
553 connect(firmwareIap, QOverload<UAVObject *, bool>::of(&FirmwareIAPObj::transactionCompleted),
554 &loop, &QEventLoop::quit);
555 int magicValue = 1122;
556 int magicStep = 1111;
557 for (
int i = 0;
i < 3; ++
i) {
562 firmwareIap->setCommand(magicValue);
563 magicValue += magicStep;
564 if ((!halting) && (magicValue == 3344))
568 firmwareIap->updated();
571 if (!timeout.isActive()) {
572 setStatusInfo(QString(tr(
"Sending IAP Step %0 failed").arg(
i + 1)),
580 if (conn && conn->
shortName() ==
"USB") {
597 void UploaderGadgetWidget::onRescueButtonClick()
609 setStatusInfo(tr(
"Please connect the board with USB with no external power applied"),
614 bool UploaderGadgetWidget::downloadSettings()
619 timeout.setSingleShot(
true);
621 bool operationSuccess =
false;
623 connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
626 connect(&dfu, &DFUObject::downloadFinished, &loop, [&](
bool status) {
627 qDebug() <<
"downloadFinished in dSettings: " << status;
628 operationSuccess = status;
632 timeout.start(100000);
640 return operationSuccess;
643 bool UploaderGadgetWidget::askIfShouldContinue()
645 QMessageBox msgBox(
this);
646 msgBox.setText(tr(
"Proceed without saving a configuration backup?"));
647 msgBox.setInformativeText(tr(
"It is strongly encouraged that you save a backup of the "
648 "configuration before proceeding with upgrade."));
649 msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
650 msgBox.setDefaultButton(QMessageBox::No);
652 int val = msgBox.exec();
654 if (val == QMessageBox::Yes) {
661 bool UploaderGadgetWidget::saveSettings(
const QByteArray &settingsDump)
664 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Settings Backup"),
665 "cloud_exported.uav",
"*.uav");
666 if (filename.isEmpty()) {
672 if (!filename.endsWith(
".uav", Qt::CaseInsensitive))
673 filename.append(
".uav");
675 QFile
file(filename);
676 if (!
file.open(QIODevice::WriteOnly)) {
683 file.write(settingsDump);
694 int UploaderGadgetWidget::isCloudReleaseAvailable(QString srcRelease)
696 QUrl url(hasRevUrl.arg(srcRelease));
697 QNetworkRequest request(url);
699 QNetworkReply *reply = netMngr->head(request);
704 timeout.setSingleShot(
true);
706 connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
707 connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
708 timeout.start(64000);
713 if (!reply->isFinished()) {
718 QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
720 int code = statusCode.toInt();
731 bool UploaderGadgetWidget::tradeSettingsWithCloud(QString srcRelease, QString ancestor,
732 bool upgrading, QByteArray *settingsOut)
736 QNetworkRequest request(url);
738 QHttpMultiPart *multiPart =
new QHttpMultiPart(QHttpMultiPart::FormDataType);
740 if (settingsOut != NULL) {
741 settingsOut->clear();
745 QByteArray compressed = qCompress(tempArray, 8);
749 compressed.remove(0, 4);
751 QHttpPart githash, ancestorPart, adaptTo, datafile;
753 githash.setHeader(QNetworkRequest::ContentDispositionHeader,
754 QVariant(
"form-data; name=\"githash\""));
755 githash.setBody(srcRelease.toLatin1());
757 ancestorPart.setHeader(QNetworkRequest::ContentDispositionHeader,
758 QVariant(
"form-data; name=\"ancestor\""));
759 ancestorPart.setBody(ancestor.toLatin1());
762 adaptTo.setHeader(QNetworkRequest::ContentDispositionHeader,
763 QVariant(
"form-data; name=\"adaptTo\""));
764 adaptTo.setBody(gcsRev.toLatin1());
766 datafile.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(
"application/octet-stream"));
767 datafile.setHeader(QNetworkRequest::ContentDispositionHeader,
768 QVariant(
"form-data; filename=\"datafile\"; name=\"datafile\""));
769 datafile.setBody(compressed);
771 multiPart->append(githash);
772 multiPart->append(adaptTo);
773 multiPart->append(ancestorPart);
774 multiPart->append(datafile);
776 QNetworkReply *reply = netMngr->post(request, multiPart);
781 timeout.setSingleShot(
true);
783 connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
784 connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
785 timeout.start(20000);
790 if (!reply->isFinished()) {
795 QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
797 int code = statusCode.toInt();
800 setStatusInfo(tr(
"Received status code %1 from cloud").arg(code),
807 settingsDump = reply->readAll();
809 if (settingsOut != NULL) {
810 *settingsOut = settingsDump;
817 return saveSettings(settingsDump);
820 void UploaderGadgetWidget::upgradeError(QString why)
822 upgraderActive =
false;
828 qDebug() <<
"Upgrade assistant failed: " + why;
833 QMessageBox msgBox(
this);
835 msgBox.setIcon(QMessageBox::Critical);
836 msgBox.setText(tr(
"Automatic upgrade failed."));
837 msgBox.setInformativeText(why);
839 msgBox.setStandardButtons(QMessageBox::Ok);
843 void UploaderGadgetWidget::stepChangeAndDelay(QEventLoop &loop,
int delayMs,
850 delay.setSingleShot(
true);
851 connect(&delay, &QTimer::timeout, &loop, &QEventLoop::quit);
853 delay.start(delayMs);
858 void UploaderGadgetWidget::doUpgradeOperation(
bool blankFC,
tl_dfu::device &dev)
860 upgraderActive =
true;
870 timeout.setSingleShot(
true);
872 bool aborted =
false;
873 bool entLoader =
false;
879 connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
880 connect(&m_dialog, &UpgradeAssistantDialog::finished, &loop, [&](
int result) {
894 upgradeError(tr(
"Not in expected bootloader state!"));
899 QString upgradingFrom = m_widget->gitHashOD_lbl->text();
900 QString ancestor = m_widget->ancestorHashOD_lbl->text();
902 qDebug() <<
"Upgrading from " << upgradingFrom;
906 bool upgradingLoader =
false;
909 upgradingLoader = !haveSettingsPart();
911 int requiredLoader = board.
board->minBootLoaderVersion();
914 upgradingLoader =
true;
922 int available = isCloudReleaseAvailable(upgradingFrom);
924 if (available == 0) {
925 available = isCloudReleaseAvailable(ancestor);
933 QMessageBox::critical(
this, tr(
"Cloud upgrade service not available"),
934 tr(
"The cloud upgrade service is not reachable. "
935 "You may continue without migrating settings, "
936 "or cancel the upgrade."),
937 QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel);
939 if (val != QMessageBox::Ok) {
944 }
else if (available == 0) {
948 int val = QMessageBox::critical(
this, tr(
"Cloud upgrade service: release unknown"),
949 tr(
"The cloud upgrade service does not know about "
950 "the firmware on the flight controller. "
951 "You may discard the existing settings, "
952 "cancel the upgrade, or ignore and attempt to "
953 "proceed with migration anyways."),
954 QMessageBox::Discard | QMessageBox::Cancel
955 | QMessageBox::Ignore,
956 QMessageBox::Cancel);
958 if (val == QMessageBox::Discard) {
960 }
else if (val != QMessageBox::Ignore) {
970 upgradeError(tr(
"Aborted!"));
978 QByteArray bootUpdateFile, firmwareFile;
980 if (upgradingLoader) {
981 if (!FirmwareLoadFromFile(getImagePath(board.
board->shortName(),
"bu"), &bootUpdateFile)) {
982 upgradeError(tr(
"Unable to load bootloader update image for board!"));
988 if (!FirmwareLoadFromFile(getImagePath(board.
board->shortName(),
"fw"), &firmwareFile)) {
989 upgradeError(tr(
"Unable to load firmware image for board!"));
994 if (upgradingLoader) {
998 upgradeError(tr(
"Aborted!"));
1004 FirmwareLoadedClear(
true);
1005 FirmwareLoadedUpdate(bootUpdateFile);
1006 setUploaderStatus(getUploaderStatus());
1008 if (!flashFirmware(bootUpdateFile)) {
1009 upgradeError(tr(
"Unable to flash upgrader image to board!"));
1016 dfu.JumpToApp(
false);
1017 dfu.CloseBootloaderComs();
1020 timeout.start(25000);
1025 upgradeError(tr(
"Unable to enter bootloader after bootloader upgrade!"));
1035 upgradeError(tr(
"Aborted!"));
1041 if (!downloadSettings()) {
1042 upgradeError(tr(
"Unable to pull settings partition!"));
1050 upgradeError(tr(
"Aborted!"));
1056 if (!tradeSettingsWithCloud(upgradingFrom, ancestor,
true, &xmlDump)) {
1057 upgradeError(tr(
"Unable to use cloud services to translate settings!"));
1066 upgradeError(tr(
"Aborted!"));
1071 ret = m_dialog.
PromptUser(tr(
"Obtained settings from cloud."),
1072 tr(
"It is recommended that you save a backup of the settings "
1073 "before proceeding with upgrade."),
1074 QStringList() << tr(
"Continue without saving")
1075 << tr(
"Save settings backup"));
1083 done = saveSettings(xmlDump);
1094 upgradeError(tr(
"Aborted!"));
1101 upgradeError(tr(
"Failed to erase settings partition!"));
1107 upgradeError(tr(
"Aborted!"));
1113 FirmwareLoadedClear(
true);
1114 FirmwareLoadedUpdate(firmwareFile);
1115 setUploaderStatus(getUploaderStatus());
1117 loop.processEvents();
1119 upgradeError(tr(
"Aborted!"));
1124 if (!flashFirmware(firmwareFile)) {
1125 upgradeError(tr(
"Unable to flash firmware image to board!"));
1131 upgradeError(tr(
"Aborted!"));
1139 ignoredRev = m_widget->gitHashOD_lbl->text();
1144 upgradeError(tr(
"Aborted!"));
1149 bool firmwareConnected =
false;
1153 firmwareConnected =
true;
1157 dfu.JumpToApp(
false);
1158 dfu.CloseBootloaderComs();
1161 for (
int tries = 0; tries < 2; tries++) {
1163 upgradeError(tr(
"Aborted!"));
1168 timeout.start(25000);
1170 if (!firmwareConnected) {
1177 upgradeError(tr(
"Aborted!"));
1182 if (!firmwareConnected) {
1184 ret = m_dialog.
PromptUser(tr(
"Unable to automatically reset board"),
1185 tr(
"Please remove power and USB from the flight board, "
1186 " plug it back in, and select \"Continue.\""),
1187 QStringList() << tr(
"Continue"));
1189 upgradeError(tr(
"Unable to connect to new firmware!"));
1203 upgradeError(tr(
"Aborted!"));
1208 ret = m_dialog.
PromptUser(tr(
"Please review the imported settings and save to board."),
1209 tr(
"No settings are currently saved. Select the "
1210 "\"Review and Save\" button to import the settings."),
1211 QStringList() << tr(
"Review and Save"));
1227 upgraderActive =
false;
1235 void UploaderGadgetWidget::onExportButtonClick()
1242 qWarning() <<
"Could not get ActionManager instance!";
1249 cmd->
action()->trigger();
1251 qWarning() <<
"Could find UAVSettingsExport action!";
1257 if (!haveSettingsPart()) {
1258 QMessageBox msgBox(
this);
1260 msgBox.setText(tr(
"No settings partition accessible; can't export."));
1261 msgBox.setInformativeText(tr(
"Please upgrade your bootloader."));
1262 msgBox.setStandardButtons(QMessageBox::Ok);
1263 msgBox.setDefaultButton(QMessageBox::Ok);
1271 QMessageBox msgBox(
this);
1272 msgBox.setText(tr(
"Do you wish to export the settings partition as an XML settings file?"));
1273 msgBox.setInformativeText(tr(
"This will send the raw configuration information to a dRonin "
1274 "cloud service for translation."));
1275 msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1276 msgBox.setDefaultButton(QMessageBox::Yes);
1278 int val = msgBox.exec();
1280 if (val != QMessageBox::Yes) {
1285 if (!downloadSettings()) {
1293 QString upgradingFrom = m_widget->gitHashOD_lbl->text();
1294 QString ancestor = m_widget->ancestorHashOD_lbl->text();
1296 tradeSettingsWithCloud(upgradingFrom, ancestor);
1303 void UploaderGadgetWidget::onBootloaderDetected()
1320 if (devices.length() > 1) {
1321 setStatusInfo(tr(
"More than one device was detected in bootloader state"),
1324 }
else if (devices.length() == 0) {
1331 bool triggerUpgrading =
false;
1332 bool inUpgrader =
false;
1333 bool validDescription =
false;
1334 bool blankFC =
false;
1340 if (dfu.OpenBootloaderComs(device)) {
1343 QByteArray description = dfu.DownloadDescriptionAsByteArray(dev.
SizeOfDesc);
1348 FirmwareOnDeviceUpdate(descStructure, QString::number((dev.
FW_CRC)));
1349 validDescription =
true;
1353 switch (uploaderStatus) {
1355 triggerUpgrading =
true;
1361 if (QString(description.left(4)) ==
"Tl")
1364 if (validDescription) {
1365 if (FirmwareCheckForUpdate(descStructure)) {
1366 triggerUpgrading =
true;
1370 QMessageBox msgBox(
this);
1371 msgBox.setText(tr(
"There appears to be no valid firmware on your device.."));
1372 msgBox.setInformativeText(tr(
"Do you want to install firmware?"));
1373 msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1374 msgBox.setDefaultButton(QMessageBox::Yes);
1376 int val = msgBox.exec();
1378 if (val == QMessageBox::Yes) {
1379 triggerUpgrading =
true;
1388 dfu.JumpToApp(
false);
1389 dfu.CloseBootloaderComs();
1396 QTableWidgetItem *label;
1397 QTableWidgetItem *size;
1421 name =
"Bootloader";
1436 name = QString::number(index);
1439 label =
new QTableWidgetItem(name);
1440 size =
new QTableWidgetItem(QString::number(i));
1442 label->setData(Qt::UserRole, index);
1443 size->setData(Qt::UserRole, index);
1445 size->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
1447 m_widget->partitionBrowserTW->setRowCount(row_count + 1);
1449 m_widget->partitionBrowserTW->setItem(row_count, 0, label);
1450 m_widget->partitionBrowserTW->setItem(row_count, 1, size);
1455 m_widget->partitionBrowserGB->setEnabled(
true);
1457 m_widget->partitionBrowserTW->setRowCount(0);
1470 DeviceInformationUpdate(info);
1477 if (FirmwareLoadFromFile(getImagePath(info.
board->shortName()), &loadedFile)) {
1478 FirmwareLoadedClear(
true);
1479 FirmwareLoadedUpdate(loadedFile);
1481 this->activateWindow();
1482 m_widget->flashButton->setFocus();
1491 if (triggerUpgrading) {
1492 if (currentBoard.
board) {
1493 bool canBeUpgraded = currentBoard.
board->queryCapabilities(
1496 if (canBeUpgraded) {
1497 doUpgradeOperation(blankFC, dev);
1515 void UploaderGadgetWidget::onBootloaderRemoved()
1519 DeviceInformationClear();
1520 FirmwareOnDeviceClear(
true);
1521 PartitionBrowserClear();
1528 void UploaderGadgetWidget::startRescue()
1531 m_widget->progressBar->setValue(100);
1533 connect(&rescueTimer, &QTimer::timeout,
this, &UploaderGadgetWidget::onRescueTimer,
1534 Qt::UniqueConnection);
1535 rescueTimer.start(200);
1538 &UploaderGadgetWidget::onDeviceDiscovered, Qt::UniqueConnection);
1540 &UploaderGadgetWidget::onDeviceDiscovered, Qt::UniqueConnection);
1547 void UploaderGadgetWidget::onRescueTimer()
1549 m_widget->progressBar->setValue(m_widget->progressBar->value() - 1);
1551 if (m_widget->progressBar->value() <= 0) {
1553 &UploaderGadgetWidget::onDeviceDiscovered);
1555 &UploaderGadgetWidget::onDeviceDiscovered);
1556 rescueTimer.disconnect();
1563 void UploaderGadgetWidget::onDeviceDiscovered()
1566 m_widget->progressBar->setValue(0);
1568 &UploaderGadgetWidget::onDeviceDiscovered);
1570 &UploaderGadgetWidget::onDeviceDiscovered);
1578 void UploaderGadgetWidget::onStatusUpdate(QString text,
int progress)
1580 if (!text.isEmpty())
1583 m_widget->progressBar->setValue(progress);
1586 void UploaderGadgetWidget::triggerPartitionDownload(
int index)
1588 if (!CheckInBootloaderState())
1603 void UploaderGadgetWidget::onPartitionSave()
1605 int index = m_widget->partitionBrowserTW->selectedItems().first()->data(Qt::UserRole).toInt();
1609 bool operationSuccess =
false;
1612 connect(&dfu, &DFUObject::downloadFinished, &loop, [&](
bool status) {
1613 operationSuccess = status;
1617 triggerPartitionDownload(index);
1621 if (operationSuccess) {
1624 QFileDialog::getSaveFileName(
this, tr(
"Save File"), QDir::homePath(),
"*.bin");
1625 if (filename.isEmpty()) {
1631 if (!filename.endsWith(
".bin", Qt::CaseInsensitive))
1632 filename.append(
".bin");
1633 QFile
file(filename);
1634 if (
file.open(QIODevice::WriteOnly)) {
1635 file.write(tempArray);
1650 void UploaderGadgetWidget::onPartitionFlash()
1652 if (!CheckInBootloaderState())
1655 QFileDialog::getOpenFileName(
this, tr(
"Open File"), QDir::homePath(),
"*.bin");
1656 if (filename.isEmpty()) {
1660 QFile
file(filename);
1661 if (!
file.open(QIODevice::ReadOnly)) {
1666 tempArray =
file.readAll();
1668 int index = m_widget->partitionBrowserTW->selectedItems().first()->data(Qt::UserRole).toInt();
1672 if (tempArray.length() > size) {
1675 if (QMessageBox::warning(
this, tr(
"Warning"),
1676 tr(
"Are you sure you want to flash the selected partition?"),
1677 QMessageBox::Yes, QMessageBox::No)
1678 != QMessageBox::Yes)
1688 connect(&dfu, &DFUObject::uploadFinished, &loop, [&](
tl_dfu::Status status) {
1689 operationSuccess = status;
1707 void UploaderGadgetWidget::onPartitionErase()
1709 int index = m_widget->partitionBrowserTW->selectedItems().first()->data(Qt::UserRole).toInt();
1711 if (QMessageBox::warning(
this, tr(
"Warning"),
1712 tr(
"Are you sure you want erase the selected partition?"),
1713 QMessageBox::Yes, QMessageBox::No)
1714 != QMessageBox::Yes)
1726 void UploaderGadgetWidget::onBootButtonClick()
1728 bool safeboot = (sender() == m_widget->safeBootButton);
1735 if (!CheckInBootloaderState())
1738 dfu.JumpToApp(safeboot);
1739 dfu.CloseBootloaderComs();
1745 void UploaderGadgetWidget::CheckAutopilotReady()
1747 if (telemetryConnected && iapUpdated) {
1756 bool UploaderGadgetWidget::CheckInBootloaderState()
1759 setStatusInfo(tr(
"Cannot perform the selected operation if not in bootloader state"),
1770 QString UploaderGadgetWidget::LoadFirmwareFileDialog(QString boardName)
1772 QFileDialog::Options options;
1773 QString selectedFilter;
1774 boardName = boardName.toLower();
1776 QString fwDirectoryStr = getImagePath(boardName);
1778 QString fileName = QFileDialog::getOpenFileName(
1779 this, tr(
"Select firmware file"), fwDirectoryStr, tr(
"Firmware (fw_*.tlfw);;"
1780 "Bootloader Update (bu_*.tlfw);;"
1781 "All (*.tlfw *.opfw *.bin)"),
1782 &selectedFilter, options);
1796 m_widget->statusLabel->setText(str);
1799 px.load(QString(
":/uploader/images/system-run.svg"));
1802 px.load(QString(
":/uploader/images/dialog-apply.svg"));
1805 px.load(QString(
":/uploader/images/process-stop.svg"));
1808 px.load(QString(
":/uploader/images/gtk-info.svg"));
1810 m_widget->statusPic->setPixmap(px);
1818 return uploaderStatus;
1821 bool UploaderGadgetWidget::haveSettingsPart()
const
1838 uploaderStatus = value;
1839 switch (uploaderStatus) {
1841 m_widget->progressBar->setVisible(
false);
1843 m_widget->rescueButton->setEnabled(
true);
1844 m_widget->rescueButton->setText(tr(
"Rescue"));
1845 m_widget->bootButton->setText(tr(
"Boot"));
1847 m_widget->openButton->setEnabled(
true);
1848 m_widget->bootButton->setEnabled(
false);
1849 m_widget->safeBootButton->setEnabled(
false);
1850 m_widget->flashButton->setEnabled(
false);
1851 m_widget->exportConfigButton->setEnabled(
false);
1852 m_widget->partitionBrowserTW->setContextMenuPolicy(Qt::NoContextMenu);
1855 m_widget->progressBar->setVisible(
true);
1857 m_widget->rescueButton->setText(tr(
"Rescue"));
1858 m_widget->bootButton->setText(tr(
"Boot"));
1860 m_widget->rescueButton->setEnabled(
false);
1861 m_widget->openButton->setEnabled(
true);
1862 m_widget->bootButton->setEnabled(
false);
1863 m_widget->safeBootButton->setEnabled(
false);
1864 m_widget->flashButton->setEnabled(
false);
1865 m_widget->exportConfigButton->setEnabled(
false);
1866 m_widget->partitionBrowserTW->setContextMenuPolicy(Qt::NoContextMenu);
1869 m_widget->progressBar->setVisible(
false);
1871 m_widget->rescueButton->setText(tr(
"Rescue"));
1872 m_widget->bootButton->setText(tr(
"Boot"));
1874 m_widget->rescueButton->setEnabled(
false);
1875 m_widget->openButton->setEnabled(
true);
1876 m_widget->bootButton->setEnabled(
true);
1877 m_widget->safeBootButton->setEnabled(
true);
1878 if (!loadedFile.isEmpty())
1879 m_widget->flashButton->setEnabled(
true);
1881 m_widget->flashButton->setEnabled(
false);
1883 m_widget->exportConfigButton->setEnabled(haveSettingsPart());
1885 m_widget->partitionBrowserTW->setContextMenuPolicy(Qt::ActionsContextMenu);
1888 m_widget->progressBar->setVisible(
false);
1890 m_widget->rescueButton->setText(tr(
"Enter bootloader"));
1891 m_widget->bootButton->setText(tr(
"Reboot"));
1893 m_widget->rescueButton->setEnabled(
true);
1894 m_widget->openButton->setEnabled(
true);
1895 m_widget->bootButton->setEnabled(
true);
1896 m_widget->safeBootButton->setEnabled(
false);
1897 m_widget->flashButton->setEnabled(
false);
1898 m_widget->exportConfigButton->setEnabled(
true);
1900 m_widget->partitionBrowserTW->setContextMenuPolicy(Qt::NoContextMenu);
1905 m_widget->progressBar->setVisible(
true);
1907 m_widget->rescueButton->setEnabled(
false);
1908 m_widget->openButton->setEnabled(
false);
1909 m_widget->bootButton->setEnabled(
false);
1910 m_widget->safeBootButton->setEnabled(
false);
1911 m_widget->flashButton->setEnabled(
false);
1912 m_widget->exportConfigButton->setEnabled(
false);
1913 m_widget->partitionBrowserTW->setContextMenuPolicy(Qt::NoContextMenu);
1923 void UploaderGadgetWidget::openHelp()
1925 QDesktopServices::openUrl(QUrl(
1926 "https://github.com/d-ronin/dRonin/wiki/OnlineHelp:-Uploader-Plugin", QUrl::StrictMode));
1929 QString UploaderGadgetWidget::getImagePath(QString boardName, QString imageType)
1931 QString imageName = QString(
"%1_%2").arg(imageType).arg(boardName.toLower());
1932 QString imageNameWithSuffix = QString(
"%1.tlfw").arg(imageName);
1934 QStringList paths = QStringList()
1940 <<
"../../../../build"
1941 <<
"../../../../../../../build"
1942 <<
"../Resources/firmware"
1943 <<
"/usr/local/" GCS_PROJECT_BRANDING_PRETTY
"/firmware";
1945 foreach (QString path, paths) {
1946 QDir pathDir = QDir(QCoreApplication::applicationDirPath());
1948 if (pathDir.cd(path)) {
1950 QString perTargetPath = QString(
"%1/%2/%3")
1951 .arg(pathDir.absolutePath())
1953 .arg(imageNameWithSuffix);
1955 if (QFile::exists(perTargetPath)) {
1956 return perTargetPath;
1959 QString directPath =
1960 QString(
"%1/%2").arg(pathDir.absolutePath()).arg(imageNameWithSuffix);
1962 if (QFile::exists(directPath)) {
1972 bool UploaderGadgetWidget::FirmwareLoadFromFile(QString filename, QByteArray *contents)
1974 QFileInfo fileinfo = QFileInfo(filename);
1976 if (!fileinfo.exists())
1979 QFile
file(fileinfo.filePath());
1980 if (!
file.open(QIODevice::ReadOnly))
1982 *contents =
file.readAll();
1989 if (currentBoard.
board) {
1990 bool canBeUpgraded =
1993 if (!canBeUpgraded) {
1998 const QString gcsRev(GCS_REVISION);
1999 if (gcsRev.contains(
':')) {
2000 QString gcsShort = gcsRev.mid(gcsRev.indexOf(
':') + 1, 8);
2001 if ((gcsShort != device.
gitHash) && (ignoredRev != device.
gitHash)) {
2004 tr(
"The firmware version on your board does not match this version of GCS."));
2005 msgBox.setInformativeText(
2006 tr(
"Do you want to upgrade the firmware to a compatible version?"));
2007 msgBox.setDetailedText(QString(
"Firmware git hash: %1\nGCS git hash: %2")
2010 msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Ignore);
2011 msgBox.setDefaultButton(QMessageBox::Yes);
2013 int val = msgBox.exec();
2015 if (val == QMessageBox::Yes)
2017 else if (val == QMessageBox::Ignore)
2026 switch (uploaderStatus) {
2029 return upgraderActive;
bool importUAVSettings(const QByteArray &settings, bool quiet=false)
int PromptUser(QString promptText, QString detailText, QStringList buttonText)
void newBoardSeen(deviceInfo board, deviceDescriptorStruct device)
void operationProgress(QString status, int progress)
static ModeManager * instance()
virtual QAction * action() const =0
QList< int > getKnownVendorIDs()
getKnownVendorIDs Get all USB VendorIDs known by the board manager. This can be used by any plugin wh...
virtual ActionManager * actionManager() const =0
Returns the application's action manager.
DevListItem getCurrentDevice()
~UploaderGadgetWidget()
Class destructor, nothing needs to be destructed ATM.
virtual ConnectionManager * connectionManager() const =0
void objectUpdated(UAVObject *obj)
Signal sent whenever any field of the object is updated.
bool active() const
active
virtual Command * command(const QString &id) const =0
Returns the Command object that is known to the system under the given string id. ...
void activateModeByWorkspaceName(const QString &id)
void setOperatingMode(bool upgradingBootloader, bool blankFC)
static ICore * instance()
UploaderGadgetWidget(QWidget *parent=nullptr)
Class constructor, sets signal to slot connections, creates actions, creates utility classes instance...
static bool descriptionToStructure(QByteArray desc, deviceDescriptorStruct &struc)
virtual BoardManager * boardManager() const =0
IBoardType * getBoard(int type)
Find a board from it's type.
const char *const GCS_REVISION_STR
static USBMonitor * instance()
The action manager is responsible for registration of menus and menu items and keyboard shortcuts...
QVector< quint32 > PartitionSizes
QPointer< Core::IBoardType > board
static QString getBoardNameFromID(int id)
unsigned char getRunState()
bool getBoardDescriptionStruct(deviceDescriptorStruct &device)
Core::IBoardType * getBoardType()
Get the IBoardType corresponding to the connected board.
void onStepChanged(UpgradeAssistantStep step)
virtual QString shortName()
QByteArray getBoardCPUSerial()
The class Command represents an action like a menu item, tool button, or shortcut. You don't create Command objects directly, instead use {ActionManager::registerAction()} to register an action and retrieve a Command. The Command object represents the user visible action and its properties. If multiple actions are registered with the same ID (but different contexts) the returned Command is the shared one between these actions.
int getBoardRevision()
Get the connected board hardware revision.