2 ******************************************************************************
4 * @file uploadergadgetwidget.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * @addtogroup GCSPlugins GCS Plugins
8 * @addtogroup YModemUploader YModem Serial Uploader Plugin
10 * @brief The YModem protocol serial 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 "uploadergadgetwidget.h"
29 #include "flightstatus.h"
30 #include "oplinkstatus.h"
32 #include "devicewidget.h"
33 #include "runningdevicewidget.h"
35 #include <extensionsystem/pluginmanager.h>
36 #include <coreplugin/icore.h>
37 #include <coreplugin/coreconstants.h>
38 #include <coreplugin/connectionmanager.h>
39 #include <uavtalk/telemetrymanager.h>
41 #include <QDesktopServices>
42 #include <QMessageBox>
43 #include <QProgressBar>
45 #include "rebootdialog.h"
47 #define DFU_DEBUG true
49 const int UploaderGadgetWidget::BOARD_EVENT_TIMEOUT
= 20000;
50 const int UploaderGadgetWidget::AUTOUPDATE_CLOSE_TIMEOUT
= 7000;
51 const int UploaderGadgetWidget::REBOOT_TIMEOUT
= 20000;
52 const int UploaderGadgetWidget::ERASE_TIMEOUT
= 20000;
53 const int UploaderGadgetWidget::BOOTLOADER_TIMEOUT
= 20000;
55 TimedDialog::TimedDialog(const QString
&title
, const QString
&labelText
, int timeout
, QWidget
*parent
, Qt::WindowFlags flags
) :
56 QProgressDialog(labelText
, tr("Cancel"), 0, timeout
, parent
, flags
), bar(new QProgressBar(this))
58 setWindowTitle(title
);
60 // open immediately...
61 setMinimumDuration(0);
63 bar
->setRange(0, timeout
);
64 bar
->setFormat(tr("Timing out in %1 seconds").arg(timeout
));
68 void TimedDialog::perform()
70 setValue(value() + 1);
71 int remaining
= bar
->maximum() - bar
->value();
73 bar
->setFormat(tr("Timing out in %1 seconds").arg(remaining
));
75 bar
->setFormat(tr("Timed out after %1 seconds").arg(bar
->maximum()));
79 ConnectionWaiter::ConnectionWaiter(int targetDeviceCount
, int timeout
, QWidget
*parent
) : QObject(parent
), eventLoop(this), timer(this), timeout(timeout
), elapsed(0), targetDeviceCount(targetDeviceCount
), result(ConnectionWaiter::Ok
)
82 int ConnectionWaiter::exec()
84 connect(USBMonitor::instance(), SIGNAL(deviceDiscovered(USBPortInfo
)), this, SLOT(deviceEvent()));
85 connect(USBMonitor::instance(), SIGNAL(deviceRemoved(USBPortInfo
)), this, SLOT(deviceEvent()));
87 connect(&timer
, SIGNAL(timeout()), this, SLOT(perform()));
96 void ConnectionWaiter::cancel()
99 result
= ConnectionWaiter::Canceled
;
102 void ConnectionWaiter::quit()
104 disconnect(USBMonitor::instance(), SIGNAL(deviceDiscovered(USBPortInfo
)), this, SLOT(deviceEvent()));
105 disconnect(USBMonitor::instance(), SIGNAL(deviceRemoved(USBPortInfo
)), this, SLOT(deviceEvent()));
110 void ConnectionWaiter::perform()
113 emit
timeChanged(elapsed
);
114 int remaining
= timeout
- elapsed
* 1000;
115 if (remaining
<= 0) {
116 result
= ConnectionWaiter::TimedOut
;
121 void ConnectionWaiter::deviceEvent()
123 if (USBMonitor::instance()->availableDevices(0x20a0, -1, -1, -1).length() == targetDeviceCount
) {
128 int ConnectionWaiter::openDialog(const QString
&title
, const QString
&labelText
, int targetDeviceCount
, int timeout
, QWidget
*parent
, Qt::WindowFlags flags
)
130 TimedDialog
dlg(title
, labelText
, timeout
/ 1000, parent
, flags
);
131 ConnectionWaiter
waiter(targetDeviceCount
, timeout
, parent
);
133 connect(&dlg
, SIGNAL(canceled()), &waiter
, SLOT(cancel()));
134 connect(&waiter
, SIGNAL(timeChanged(int)), &dlg
, SLOT(perform()));
135 return waiter
.exec();
138 UploaderGadgetWidget::UploaderGadgetWidget(QWidget
*parent
) : QWidget(parent
)
140 m_config
= new Ui_UploaderWidget();
141 m_config
->setupUi(this);
142 m_currentIAPStep
= IAP_STATE_READY
;
145 m_autoUpdateClosing
= false;
147 // Listen to autopilot connection events
148 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
149 TelemetryManager
*telMngr
= pm
->getObject
<TelemetryManager
>();
150 connect(telMngr
, SIGNAL(connected()), this, SLOT(onAutopilotConnect()));
151 connect(telMngr
, SIGNAL(disconnected()), this, SLOT(onAutopilotDisconnect()));
153 Core::ConnectionManager
*cm
= Core::ICore::instance()->connectionManager();
154 connect(cm
, SIGNAL(deviceConnected(QIODevice
*)), this, SLOT(onPhysicalHWConnect()));
156 connect(m_config
->haltButton
, SIGNAL(clicked()), this, SLOT(systemHalt()));
157 connect(m_config
->resetButton
, SIGNAL(clicked()), this, SLOT(systemReset()));
158 connect(m_config
->bootButton
, SIGNAL(clicked()), this, SLOT(systemBoot()));
159 connect(m_config
->safeBootButton
, SIGNAL(clicked()), this, SLOT(systemSafeBoot()));
160 connect(m_config
->eraseBootButton
, SIGNAL(clicked()), this, SLOT(systemEraseBoot()));
161 connect(m_config
->rescueButton
, SIGNAL(clicked()), this, SLOT(systemRescue()));
165 connect(m_config
->autoUpdateButton
, SIGNAL(clicked()), this, SLOT(startAutoUpdate()));
166 connect(m_config
->autoUpdateEraseButton
, SIGNAL(clicked()), this, SLOT(startAutoUpdateErase()));
167 connect(m_config
->autoUpdateOkButton
, SIGNAL(clicked()), this, SLOT(closeAutoUpdate()));
168 m_config
->autoUpdateButton
->setEnabled(autoUpdateCapable());
169 m_config
->autoUpdateEraseButton
->setEnabled(autoUpdateCapable());
170 m_config
->autoUpdateGroupBox
->setVisible(false);
172 m_config
->refreshPorts
->setIcon(QIcon(":uploader/images/view-refresh.svg"));
174 bootButtonsSetEnable(false);
176 connect(m_config
->refreshPorts
, SIGNAL(clicked()), this, SLOT(getSerialPorts()));
178 connect(m_config
->pbHelp
, SIGNAL(clicked()), this, SLOT(openHelp()));
179 // And check whether by any chance we are not already connected
180 if (telMngr
->isConnected()) {
181 onAutopilotConnect();
185 bool sortPorts(const QSerialPortInfo
&s1
, const QSerialPortInfo
&s2
)
187 return s1
.portName() < s2
.portName();
191 Gets the list of serial ports
193 void UploaderGadgetWidget::getSerialPorts()
197 // Populate the telemetry combo box:
198 m_config
->telemetryLink
->clear();
200 list
.append(QString("USB"));
201 QList
<QSerialPortInfo
> ports
= QSerialPortInfo::availablePorts();
203 // sort the list by port number (nice idea from PT_Dreamer :))
204 qSort(ports
.begin(), ports
.end(), sortPorts
);
205 foreach(QSerialPortInfo port
, ports
) {
206 list
.append(port
.portName());
209 m_config
->telemetryLink
->addItems(list
);
213 QString
UploaderGadgetWidget::getPortDevice(const QString
&friendName
)
215 QList
<QSerialPortInfo
> ports
= QSerialPortInfo::availablePorts();
216 foreach(QSerialPortInfo port
, ports
) {
217 if (port
.portName() == friendName
) {
218 return port
.portName();
224 void UploaderGadgetWidget::connectSignalSlot(QWidget
*widget
)
226 connect(qobject_cast
<DeviceWidget
*>(widget
), SIGNAL(uploadStarted()), this, SLOT(uploadStarted()));
227 connect(qobject_cast
<DeviceWidget
*>(widget
), SIGNAL(uploadEnded(bool)), this, SLOT(uploadEnded(bool)));
228 connect(qobject_cast
<DeviceWidget
*>(widget
), SIGNAL(downloadStarted()), this, SLOT(downloadStarted()));
229 connect(qobject_cast
<DeviceWidget
*>(widget
), SIGNAL(downloadEnded(bool)), this, SLOT(downloadEnded(bool)));
232 FlightStatus
*UploaderGadgetWidget::getFlightStatus()
234 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
237 UAVObjectManager
*objManager
= pm
->getObject
<UAVObjectManager
>();
238 Q_ASSERT(objManager
);
239 FlightStatus
*status
= dynamic_cast<FlightStatus
*>(objManager
->getObject("FlightStatus"));
244 void UploaderGadgetWidget::bootButtonsSetEnable(bool enabled
)
246 m_config
->bootButton
->setEnabled(enabled
);
247 m_config
->safeBootButton
->setEnabled(enabled
);
249 // this feature is supported only on BL revision >= 4
250 bool isBL4
= ((m_dfu
!= NULL
) && (m_dfu
->devices
[0].BL_Version
>= 4));
251 m_config
->eraseBootButton
->setEnabled(isBL4
&& enabled
);
254 void UploaderGadgetWidget::onPhysicalHWConnect()
256 bootButtonsSetEnable(false);
257 m_config
->rescueButton
->setEnabled(false);
258 m_config
->telemetryLink
->setEnabled(false);
262 Enables widget buttons if autopilot connected
264 void UploaderGadgetWidget::onAutopilotConnect()
266 QTimer::singleShot(1000, this, SLOT(populate()));
269 void UploaderGadgetWidget::populate()
271 m_config
->haltButton
->setEnabled(true);
272 m_config
->resetButton
->setEnabled(true);
273 bootButtonsSetEnable(false);
274 m_config
->rescueButton
->setEnabled(false);
275 m_config
->telemetryLink
->setEnabled(false);
277 // Add a very simple widget with Board model & serial number
278 // Delete all previous tabs:
279 while (m_config
->systemElements
->count()) {
280 QWidget
*qw
= m_config
->systemElements
->widget(0);
281 m_config
->systemElements
->removeTab(0);
284 RunningDeviceWidget
*dw
= new RunningDeviceWidget(this);
286 m_config
->systemElements
->addTab(dw
, tr("Connected Device"));
290 Enables widget buttons if autopilot disconnected
292 void UploaderGadgetWidget::onAutopilotDisconnect()
294 m_config
->haltButton
->setEnabled(false);
295 m_config
->resetButton
->setEnabled(false);
296 bootButtonsSetEnable(true);
297 if (m_currentIAPStep
== IAP_STATE_BOOTLOADER
) {
298 m_config
->rescueButton
->setEnabled(false);
299 m_config
->telemetryLink
->setEnabled(false);
301 m_config
->rescueButton
->setEnabled(true);
302 m_config
->telemetryLink
->setEnabled(true);
306 static void sleep(int ms
)
308 QEventLoop eventLoop
;
309 QTimer::singleShot(ms
, &eventLoop
, SLOT(quit()));
315 Tell the mainboard to go to bootloader:
316 - Send the relevant IAP commands
317 - setup callback for MoBo acknowledge
319 void UploaderGadgetWidget::goToBootloader(UAVObject
*callerObj
, bool success
)
323 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
324 UAVObjectManager
*objManager
= pm
->getObject
<UAVObjectManager
>();
325 UAVObject
*fwIAP
= dynamic_cast<UAVDataObject
*>(objManager
->getObject("FirmwareIAPObj"));
327 switch (m_currentIAPStep
) {
328 case IAP_STATE_READY
:
329 m_config
->haltButton
->setEnabled(false);
330 getSerialPorts(); // Useful in case a new serial port appeared since the initial list,
331 // otherwise we won't find it when we stop the board.
332 // The board is running, send the 1st IAP Reset order:
333 fwIAP
->getField("Command")->setValue("1122");
334 fwIAP
->getField("BoardRevision")->setDouble(0);
335 fwIAP
->getField("BoardType")->setDouble(0);
336 connect(fwIAP
, SIGNAL(transactionCompleted(UAVObject
*, bool)), this, SLOT(goToBootloader(UAVObject
*, bool)));
337 m_currentIAPStep
= IAP_STATE_STEP_1
;
341 emit
progressUpdate(JUMP_TO_BL
, QVariant(1));
343 case IAP_STATE_STEP_1
:
345 log("Oops, failure step 1");
346 log("Reset did NOT happen");
347 m_currentIAPStep
= IAP_STATE_READY
;
348 disconnect(fwIAP
, SIGNAL(transactionCompleted(UAVObject
*, bool)), this, SLOT(goToBootloader(UAVObject
*, bool)));
349 m_config
->haltButton
->setEnabled(true);
350 emit
progressUpdate(FAILURE
, QVariant());
351 emit
bootloaderFailed();
355 fwIAP
->getField("Command")->setValue("2233");
356 m_currentIAPStep
= IAP_STATE_STEP_2
;
359 emit
progressUpdate(JUMP_TO_BL
, QVariant(2));
361 case IAP_STATE_STEP_2
:
363 log("Oops, failure step 2");
364 log("Reset did NOT happen");
365 m_currentIAPStep
= IAP_STATE_READY
;
366 disconnect(fwIAP
, SIGNAL(transactionCompleted(UAVObject
*, bool)), this, SLOT(goToBootloader(UAVObject
*, bool)));
367 m_config
->haltButton
->setEnabled(true);
368 emit
progressUpdate(FAILURE
, QVariant());
369 emit
bootloaderFailed();
373 fwIAP
->getField("Command")->setValue("3344");
374 m_currentIAPStep
= IAP_STEP_RESET
;
376 emit
progressUpdate(JUMP_TO_BL
, QVariant(3));
381 m_currentIAPStep
= IAP_STATE_READY
;
383 log("Oops, failure step 3");
384 log("Reset did NOT happen");
385 disconnect(fwIAP
, SIGNAL(transactionCompleted(UAVObject
*, bool)), this, SLOT(goToBootloader(UAVObject
*, bool)));
386 m_config
->haltButton
->setEnabled(true);
387 emit
progressUpdate(FAILURE
, QVariant());
388 emit
bootloaderFailed();
392 // The board is now reset: we have to disconnect telemetry
393 Core::ConnectionManager
*cm
= Core::ICore::instance()->connectionManager();
394 QString dli
= cm
->getCurrentDevice().getConName();
395 QString dlj
= cm
->getCurrentDevice().getConName();
396 cm
->disconnectDevice();
398 // Tell connections to stop their polling threads: otherwise it will mess up DFU
399 cm
->suspendPolling();
402 emit
progressUpdate(JUMP_TO_BL
, QVariant(4));
403 m_config
->boardStatus
->setText(tr("Bootloader"));
404 if (dlj
.startsWith("USB")) {
405 m_config
->telemetryLink
->setCurrentIndex(m_config
->telemetryLink
->findText("USB"));
407 m_config
->telemetryLink
->setCurrentIndex(m_config
->telemetryLink
->findText(dli
));
410 disconnect(fwIAP
, SIGNAL(transactionCompleted(UAVObject
*, bool)), this, SLOT(goToBootloader(UAVObject
*, bool)));
412 m_currentIAPStep
= IAP_STATE_BOOTLOADER
;
414 // Tell the mainboard to get into bootloader state:
415 log("Detecting devices, please wait a few seconds...");
417 if (dlj
.startsWith("USB")) {
418 m_dfu
= new DFUObject(DFU_DEBUG
, false, QString());
420 m_dfu
= new DFUObject(DFU_DEBUG
, true, getPortDevice(dli
));
423 if (!m_dfu
->ready()) {
424 log("Could not enter DFU mode.");
428 m_currentIAPStep
= IAP_STATE_READY
;
429 m_config
->boardStatus
->setText(tr("Bootloader?"));
430 m_config
->haltButton
->setEnabled(true);
431 emit
progressUpdate(FAILURE
, QVariant());
432 emit
bootloaderFailed();
435 m_dfu
->AbortOperation();
436 if (!m_dfu
->enterDFU(0)) {
437 log("Could not enter DFU mode.");
441 m_currentIAPStep
= IAP_STATE_READY
;
442 m_config
->boardStatus
->setText(tr("Bootloader?"));
443 emit
progressUpdate(FAILURE
, QVariant());
444 emit
bootloaderFailed();
449 m_dfu
->findDevices();
450 log(QString("Found %1 device(s).").arg(QString::number(m_dfu
->numberOfDevices
)));
451 if (m_dfu
->numberOfDevices
< 0 || m_dfu
->numberOfDevices
> 3) {
452 log("Inconsistent number of devices! Aborting");
456 emit
progressUpdate(FAILURE
, QVariant());
457 emit
bootloaderFailed();
460 // Delete all previous tabs:
461 while (m_config
->systemElements
->count()) {
462 QWidget
*qw
= m_config
->systemElements
->widget(0);
463 m_config
->systemElements
->removeTab(0);
466 for (int i
= 0; i
< m_dfu
->numberOfDevices
; i
++) {
467 DeviceWidget
*dw
= new DeviceWidget(this);
468 connectSignalSlot(dw
);
472 m_config
->systemElements
->addTab(dw
, tr("Device") + QString::number(i
));
475 QApplication::processEvents();
477 // Need to re-enable in case we were not connected
478 bootButtonsSetEnable(true);
479 emit
progressUpdate(JUMP_TO_BL
, QVariant(5));
480 emit
bootloaderSuccess();
490 case IAP_STATE_BOOTLOADER
:
491 // We should never end up here anyway.
492 emit
progressUpdate(FAILURE
, QVariant());
493 emit
bootloaderFailed();
498 void UploaderGadgetWidget::systemHalt()
500 // The board can not be halted when in armed state.
501 // If board is armed, or arming. Show message with notice.
502 FlightStatus
*status
= getFlightStatus();
504 if (status
->getArmed() == FlightStatus::ARMED_DISARMED
) {
507 cannotHaltMessageBox();
512 Tell the mainboard to reset:
513 - Send the relevant IAP commands
514 - setup callback for MoBo acknowledge
516 void UploaderGadgetWidget::systemReset()
518 FlightStatus
*status
= getFlightStatus();
520 // The board can not be reset when in armed state.
521 // If board is armed, or arming. Show message with notice.
522 if (status
->getArmed() == FlightStatus::ARMED_DISARMED
) {
529 log("Board Reset initiated.");
532 cannotResetMessageBox();
536 void UploaderGadgetWidget::systemBoot()
538 commonSystemBoot(false, false);
541 void UploaderGadgetWidget::systemSafeBoot()
543 commonSystemBoot(true, false);
546 void UploaderGadgetWidget::systemEraseBoot()
548 switch (confirmEraseSettingsMessageBox()) {
549 case QMessageBox::Ok
:
550 commonSystemBoot(true, true);
552 case QMessageBox::Help
:
553 QDesktopServices::openUrl(QUrl(tr("http://wiki.openpilot.org/display/Doc/Erase+board+settings"), QUrl::StrictMode
));
558 void UploaderGadgetWidget::rebootWithDialog()
560 RebootDialog
dialog(this);
565 void UploaderGadgetWidget::systemReboot()
567 ResultEventLoop eventLoop
;
569 connect(this, SIGNAL(bootloaderSuccess()), &eventLoop
, SLOT(success()));
570 connect(this, SIGNAL(bootloaderFailed()), &eventLoop
, SLOT(fail()));
574 if (eventLoop
.run(REBOOT_TIMEOUT
) != 0) {
575 emit
progressUpdate(FAILURE
, QVariant());
579 disconnect(this, SIGNAL(bootloaderSuccess()), &eventLoop
, SLOT(success()));
580 disconnect(this, SIGNAL(bootloaderFailed()), &eventLoop
, SLOT(fail()));
582 commonSystemBoot(false, false);
584 ExtensionSystem::PluginManager
*pluginManager
= ExtensionSystem::PluginManager::instance();
585 Q_ASSERT(pluginManager
);
586 TelemetryManager
*telemetryManager
= pluginManager
->getObject
<TelemetryManager
>();
587 Q_ASSERT(telemetryManager
);
589 if (!telemetryManager
->isConnected()) {
590 progressUpdate(BOOTING
, QVariant());
591 ResultEventLoop eventLoop
;
593 connect(telemetryManager
, SIGNAL(connected()), &eventLoop
, SLOT(success()));
595 if (eventLoop
.run(REBOOT_TIMEOUT
) != 0) {
596 emit
progressUpdate(FAILURE
, QVariant());
600 disconnect(telemetryManager
, SIGNAL(connected()), &eventLoop
, SLOT(success()));
603 emit
progressUpdate(SUCCESS
, QVariant());
607 * Tells the system to boot (from Bootloader state)
608 * @param[in] safeboot Indicates whether the firmware should use the stock HWSettings
610 void UploaderGadgetWidget::commonSystemBoot(bool safeboot
, bool erase
)
613 bootButtonsSetEnable(false);
614 // Suspend telemety & polling in case it is not done yet
615 Core::ConnectionManager
*cm
= Core::ICore::instance()->connectionManager();
616 cm
->disconnectDevice();
617 cm
->suspendPolling();
619 QString devName
= m_config
->telemetryLink
->currentText();
620 log("Attempting to boot the system through " + devName
+ ".");
624 if (devName
== "USB") {
625 m_dfu
= new DFUObject(DFU_DEBUG
, false, QString());
627 m_dfu
= new DFUObject(DFU_DEBUG
, true, getPortDevice(devName
));
630 m_dfu
->AbortOperation();
631 if (!m_dfu
->enterDFU(0)) {
632 log("Could not enter DFU mode.");
635 bootButtonsSetEnable(true);
636 m_config
->rescueButton
->setEnabled(true); // Boot not possible, maybe Rescue OK?
640 log("Booting system...");
641 m_dfu
->JumpToApp(safeboot
, erase
);
642 // Restart the polling thread
644 m_config
->rescueButton
->setEnabled(true);
645 m_config
->telemetryLink
->setEnabled(true);
646 m_config
->boardStatus
->setText(tr("Running"));
647 if (m_currentIAPStep
== IAP_STATE_BOOTLOADER
) {
648 // Freeze the tabs, they are not useful anymore and their buttons
649 // will cause segfaults or weird stuff if we use them.
650 for (int i
= 0; i
< m_config
->systemElements
->count(); i
++) {
651 // OP-682 arriving here too "early" (before the devices are refreshed) was leading to a crash
652 // OP-682 the crash was due to an unchecked cast in the line below that would cast a RunningDeviceGadget to a DeviceGadget
653 DeviceWidget
*qw
= dynamic_cast<DeviceWidget
*>(m_config
->systemElements
->widget(i
));
655 // OP-682 fixed a second crash by disabling *all* buttons in the device widget
656 // disabling the buttons is only half of the solution as even if the buttons are enabled
657 // the app should not crash
662 m_currentIAPStep
= IAP_STATE_READY
;
663 log("You can now reconnect telemetry...");
664 delete m_dfu
; // Frees up the USB/Serial port too
669 bool UploaderGadgetWidget::autoUpdateCapable()
671 return QDir(":/firmware").exists();
674 bool UploaderGadgetWidget::autoUpdate(bool erase
)
676 if (m_oplinkwatchdog
.isConnected() &&
677 m_oplinkwatchdog
.opLinkType() == OPLinkWatchdog::OPLINK_MINI
) {
678 emit
progressUpdate(FAILURE
, QVariant(tr("To upgrade the OPLinkMini board please disconnect it from the USB port, "
679 "press the Upgrade again button and follow instructions on screen.")));
680 emit
autoUpdateFailed();
684 ExtensionSystem::PluginManager
*pluginManager
= ExtensionSystem::PluginManager::instance();
685 Q_ASSERT(pluginManager
);
686 TelemetryManager
*telemetryManager
= pluginManager
->getObject
<TelemetryManager
>();
687 Q_ASSERT(telemetryManager
);
689 if (USBMonitor::instance()->availableDevices(0x20a0, -1, -1, -1).length() > 0 &&
690 telemetryManager
->connectionState() != TelemetryManager::TELEMETRY_CONNECTED
) {
691 // Wait for the board to completely connect
692 ResultEventLoop eventLoop
;
693 connect(telemetryManager
, SIGNAL(connected()), &eventLoop
, SLOT(success()));
695 if (telemetryManager
->connectionState() != TelemetryManager::TELEMETRY_CONNECTED
696 && eventLoop
.run(REBOOT_TIMEOUT
) != 0) {
697 emit
progressUpdate(FAILURE
, QVariant(tr("Timed out while waiting for a board to be fully connected!")));
698 emit
autoUpdateFailed();
702 disconnect(telemetryManager
, SIGNAL(connected()), &eventLoop
, SLOT(success()));
704 if (USBMonitor::instance()->availableDevices(0x20a0, -1, -1, -1).length() == 0) {
705 ConnectionWaiter
waiter(1, BOARD_EVENT_TIMEOUT
);
706 connect(&waiter
, SIGNAL(timeChanged(int)), this, SLOT(autoUpdateConnectProgress(int)));
707 if (waiter
.exec() == ConnectionWaiter::TimedOut
) {
708 emit
progressUpdate(FAILURE
, QVariant(tr("Timed out while waiting for a board to be connected!")));
709 emit
autoUpdateFailed();
713 ResultEventLoop eventLoop
;
714 connect(this, SIGNAL(bootloaderSuccess()), &eventLoop
, SLOT(success()));
715 connect(this, SIGNAL(bootloaderFailed()), &eventLoop
, SLOT(fail()));
719 if (eventLoop
.run(BOOTLOADER_TIMEOUT
) != 0) {
720 emit
progressUpdate(FAILURE
, QVariant(tr("Failed to enter bootloader mode.")));
721 emit
autoUpdateFailed();
725 disconnect(this, SIGNAL(bootloaderSuccess()), &eventLoop
, SLOT(success()));
726 disconnect(this, SIGNAL(bootloaderFailed()), &eventLoop
, SLOT(fail()));
734 Core::ConnectionManager
*connectionManager
= Core::ICore::instance()->connectionManager();
735 m_dfu
= new DFUObject(DFU_DEBUG
, false, QString());
736 m_dfu
->AbortOperation();
737 emit
progressUpdate(JUMP_TO_BL
, QVariant());
739 if (!m_dfu
->enterDFU(0) || !m_dfu
->findDevices() ||
740 (m_dfu
->numberOfDevices
!= 1) || m_dfu
->numberOfDevices
> 5) {
743 connectionManager
->resumePolling();
744 emit
progressUpdate(FAILURE
, QVariant(tr("Failed to enter bootloader mode.")));
745 emit
autoUpdateFailed();
750 emit
progressUpdate(LOADING_FW
, QVariant());
751 switch (m_dfu
->devices
[0].ID
) {
753 filename
= "fw_oplinkmini";
759 filename
= "fw_revoproto";
762 filename
= "fw_revolution";
765 filename
= "fw_discoveryf4bare";
768 filename
= "fw_revonano";
771 emit
progressUpdate(FAILURE
, QVariant(tr("Unknown board id '0x%1'").arg(QString::number(m_dfu
->devices
[0].ID
, 16))));
772 emit
autoUpdateFailed();
775 filename
= ":/firmware/" + filename
+ ".opfw";
777 if (!QFile::exists(filename
)) {
778 emit
progressUpdate(FAILURE
, QVariant(tr("Firmware image not found.")));
779 emit
autoUpdateFailed();
782 QFile
file(filename
);
783 if (!file
.open(QIODevice::ReadOnly
)) {
784 emit
progressUpdate(FAILURE
, QVariant(tr("Could not open firmware image for reading.")));
785 emit
autoUpdateFailed();
788 firmware
= file
.readAll();
789 QEventLoop eventLoop2
;
790 connect(m_dfu
, SIGNAL(progressUpdated(int)), this, SLOT(autoUpdateFlashProgress(int)));
791 connect(m_dfu
, SIGNAL(uploadFinished(OP_DFU::Status
)), &eventLoop2
, SLOT(quit()));
792 emit
progressUpdate(UPLOADING_FW
, QVariant());
793 if (!m_dfu
->enterDFU(0)) {
794 emit
progressUpdate(FAILURE
, QVariant(tr("Could not enter direct firmware upload mode.")));
795 emit
autoUpdateFailed();
798 m_dfu
->AbortOperation();
799 if (!m_dfu
->UploadFirmware(filename
, false, 0)) {
800 emit
progressUpdate(FAILURE
, QVariant(tr("Firmware upload failed.")));
801 emit
autoUpdateFailed();
805 QByteArray desc
= firmware
.right(100);
806 emit
progressUpdate(UPLOADING_DESC
, QVariant());
807 if (m_dfu
->UploadDescription(desc
) != OP_DFU::Last_operation_Success
) {
808 emit
progressUpdate(FAILURE
, QVariant(tr("Failed to upload firmware description.")));
809 emit
autoUpdateFailed();
813 commonSystemBoot(false, erase
);
815 // Wait for board to connect to GCS again after boot and erase
816 // Theres a special case with OPLink
817 if (!telemetryManager
->isConnected() && !m_oplinkwatchdog
.isConnected()) {
818 progressUpdate(erase
? BOOTING_AND_ERASING
: BOOTING
, QVariant());
819 ResultEventLoop eventLoop
;
820 connect(telemetryManager
, SIGNAL(connected()), &eventLoop
, SLOT(success()));
821 connect(&m_oplinkwatchdog
, SIGNAL(opLinkMiniConnected()), &eventLoop
, SLOT(success()));
823 if (eventLoop
.run(REBOOT_TIMEOUT
+ (erase
? ERASE_TIMEOUT
: 0)) != 0) {
824 emit
progressUpdate(FAILURE
, QVariant(tr("Timed out while booting.")));
825 emit
autoUpdateFailed();
829 disconnect(&m_oplinkwatchdog
, SIGNAL(opLinkMiniConnected()), &eventLoop
, SLOT(success()));
830 disconnect(telemetryManager
, SIGNAL(connected()), &eventLoop
, SLOT(success()));
833 emit
progressUpdate(SUCCESS
, QVariant());
834 emit
autoUpdateSuccess();
838 void UploaderGadgetWidget::autoUpdateDisconnectProgress(int value
)
840 emit
progressUpdate(WAITING_DISCONNECT
, value
);
843 void UploaderGadgetWidget::autoUpdateConnectProgress(int value
)
845 emit
progressUpdate(WAITING_CONNECT
, value
);
848 void UploaderGadgetWidget::autoUpdateFlashProgress(int value
)
850 emit
progressUpdate(UPLOADING_FW
, value
);
854 Attempt a guided procedure to put both boards in BL mode when
855 the system is not bootable
857 void UploaderGadgetWidget::systemRescue()
859 Core::ConnectionManager
*cm
= Core::ICore::instance()->connectionManager();
861 cm
->disconnectDevice();
862 // stop the polling thread: otherwise it will mess up DFU
863 cm
->suspendPolling();
865 // Delete all previous tabs:
866 while (m_config
->systemElements
->count()) {
867 QWidget
*qw
= m_config
->systemElements
->widget(0);
868 m_config
->systemElements
->removeTab(0);
872 // Existing DFU objects will have a handle over USB and will
873 // disturb everything for the rescue process:
879 // Avoid users pressing Rescue twice.
880 m_config
->rescueButton
->setEnabled(false);
882 // Now we're good to go
884 log("**********************************************************");
885 log("** Follow those instructions to attempt a system rescue **");
886 log("**********************************************************");
887 log("You will be prompted to first connect USB, then system power");
889 // Check if boards are connected and, if yes, prompt user to disconnect them all
890 if (USBMonitor::instance()->availableDevices(0x20a0, -1, -1, -1).length() > 0) {
891 QString labelText
= QString("<p align=\"left\">%1</p>").arg(tr("Please disconnect your OpenPilot board."));
892 int result
= ConnectionWaiter::openDialog(tr("System Rescue"), labelText
, 0, BOARD_EVENT_TIMEOUT
, this);
894 case ConnectionWaiter::Canceled
:
895 m_config
->rescueButton
->setEnabled(true);
898 case ConnectionWaiter::TimedOut
:
899 QMessageBox::warning(this, tr("System Rescue"), tr("Timed out while waiting for all boards to be disconnected!"));
900 m_config
->rescueButton
->setEnabled(true);
905 // Now prompt user to connect board
906 QString labelText
= QString("<p align=\"left\">%1<br>%2</p>").arg(tr("Please connect your OpenPilot board.")).arg(tr("Board must be connected to the USB port!"));
907 int result
= ConnectionWaiter::openDialog(tr("System Rescue"), labelText
, 1, BOARD_EVENT_TIMEOUT
, this);
909 case ConnectionWaiter::Canceled
:
910 m_config
->rescueButton
->setEnabled(true);
913 case ConnectionWaiter::TimedOut
:
914 QMessageBox::warning(this, tr("System Rescue"), tr("Timed out while waiting for a board to be connected!"));
915 m_config
->rescueButton
->setEnabled(true);
919 log("Detecting first board...");
920 m_dfu
= new DFUObject(DFU_DEBUG
, false, QString());
921 m_dfu
->AbortOperation();
922 if (!m_dfu
->enterDFU(0)) {
923 log("Could not enter DFU mode.");
927 m_config
->rescueButton
->setEnabled(true);
930 if (!m_dfu
->findDevices() || (m_dfu
->numberOfDevices
!= 1)) {
931 log("Could not detect a board, aborting!");
935 m_config
->rescueButton
->setEnabled(true);
938 log(QString("Found %1 device(s).").arg(m_dfu
->numberOfDevices
));
940 if (m_dfu
->numberOfDevices
> 5) {
941 log("Inconsistent number of devices, aborting!");
945 m_config
->rescueButton
->setEnabled(true);
948 for (int i
= 0; i
< m_dfu
->numberOfDevices
; i
++) {
949 DeviceWidget
*dw
= new DeviceWidget(this);
950 connectSignalSlot(dw
);
954 m_config
->systemElements
->addTab(dw
, tr("Device") + QString::number(i
));
956 m_config
->haltButton
->setEnabled(false);
957 m_config
->resetButton
->setEnabled(false);
958 bootButtonsSetEnable(true);
959 m_config
->rescueButton
->setEnabled(false);
961 // So that we can boot from the GUI afterwards.
962 m_currentIAPStep
= IAP_STATE_BOOTLOADER
;
965 void UploaderGadgetWidget::uploadStarted()
967 m_config
->haltButton
->setEnabled(false);
968 bootButtonsSetEnable(false);
969 m_config
->resetButton
->setEnabled(false);
970 m_config
->rescueButton
->setEnabled(false);
973 void UploaderGadgetWidget::uploadEnded(bool succeed
)
976 // device is halted so no halt
977 m_config
->haltButton
->setEnabled(false);
978 bootButtonsSetEnable(true);
979 // device is halted so no reset
980 m_config
->resetButton
->setEnabled(false);
981 m_config
->rescueButton
->setEnabled(true);
984 void UploaderGadgetWidget::downloadStarted()
986 m_config
->haltButton
->setEnabled(false);
987 bootButtonsSetEnable(false);
988 m_config
->resetButton
->setEnabled(false);
989 m_config
->rescueButton
->setEnabled(false);
992 void UploaderGadgetWidget::downloadEnded(bool succeed
)
995 // device is halted so no halt
996 m_config
->haltButton
->setEnabled(false);
997 bootButtonsSetEnable(true);
998 // device is halted so no reset
999 m_config
->resetButton
->setEnabled(false);
1000 m_config
->rescueButton
->setEnabled(true);
1003 void UploaderGadgetWidget::startAutoUpdate()
1005 startAutoUpdate(false);
1008 void UploaderGadgetWidget::startAutoUpdateErase()
1010 startAutoUpdate(true);
1012 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
1013 UAVObjectUtilManager
*utilMngr
= pm
->getObject
<UAVObjectUtilManager
>();
1014 int id
= utilMngr
->getBoardModel();
1021 void UploaderGadgetWidget::startAutoUpdate(bool erase
)
1023 m_config
->autoUpdateProgressBar
->setValue(0);
1024 autoUpdateStatus(uploader::JUMP_TO_BL
, QVariant());
1025 m_config
->buttonFrame
->setEnabled(false);
1026 m_config
->splitter
->setEnabled(false);
1027 m_config
->autoUpdateGroupBox
->setVisible(true);
1028 m_config
->autoUpdateOkButton
->setEnabled(false);
1030 connect(this, SIGNAL(progressUpdate(uploader::ProgressStep
, QVariant
)), this, SLOT(autoUpdateStatus(uploader::ProgressStep
, QVariant
)));
1034 void UploaderGadgetWidget::finishAutoUpdate()
1036 disconnect(this, SIGNAL(progressUpdate(uploader::ProgressStep
, QVariant
)), this, SLOT(autoUpdateStatus(uploader::ProgressStep
, QVariant
)));
1037 m_config
->autoUpdateOkButton
->setEnabled(true);
1038 m_autoUpdateClosing
= true;
1040 // wait a bit and "close" auto update
1041 QTimer::singleShot(AUTOUPDATE_CLOSE_TIMEOUT
, this, SLOT(closeAutoUpdate()));
1044 void UploaderGadgetWidget::closeAutoUpdate()
1046 if (m_autoUpdateClosing
) {
1047 m_config
->autoUpdateGroupBox
->setVisible(false);
1048 m_config
->buttonFrame
->setEnabled(true);
1049 m_config
->splitter
->setEnabled(true);
1051 m_autoUpdateClosing
= false;
1054 void UploaderGadgetWidget::autoUpdateStatus(uploader::ProgressStep status
, QVariant value
)
1060 case uploader::WAITING_DISCONNECT
:
1061 m_config
->autoUpdateLabel
->setText(tr("Waiting for all OpenPilot boards to be disconnected from USB."));
1062 m_config
->autoUpdateProgressBar
->setMaximum(BOARD_EVENT_TIMEOUT
/ 1000);
1063 m_config
->autoUpdateProgressBar
->setValue(value
.toInt());
1064 remaining
= m_config
->autoUpdateProgressBar
->maximum() - m_config
->autoUpdateProgressBar
->value();
1065 m_config
->autoUpdateProgressBar
->setFormat(tr("Timing out in %1 seconds").arg(remaining
));
1067 case uploader::WAITING_CONNECT
:
1068 m_config
->autoUpdateLabel
->setText(tr("Please connect the OpenPilot board to the USB port."));
1069 m_config
->autoUpdateProgressBar
->setMaximum(BOARD_EVENT_TIMEOUT
/ 1000);
1070 m_config
->autoUpdateProgressBar
->setValue(value
.toInt());
1071 remaining
= m_config
->autoUpdateProgressBar
->maximum() - m_config
->autoUpdateProgressBar
->value();
1072 m_config
->autoUpdateProgressBar
->setFormat(tr("Timing out in %1 seconds").arg(remaining
));
1074 case uploader::JUMP_TO_BL
:
1075 m_config
->autoUpdateLabel
->setText(tr("Bringing the board into boot loader mode. Please wait."));
1076 m_config
->autoUpdateProgressBar
->setFormat(tr("Step %1").arg(value
.toInt()));
1077 m_config
->autoUpdateProgressBar
->setMaximum(5);
1078 m_config
->autoUpdateProgressBar
->setValue(value
.toInt());
1080 case uploader::LOADING_FW
:
1081 m_config
->autoUpdateLabel
->setText(tr("Preparing to upload firmware to the board."));
1083 case uploader::UPLOADING_FW
:
1084 m_config
->autoUpdateLabel
->setText(tr("Uploading firmware to the board."));
1085 m_config
->autoUpdateProgressBar
->setFormat("%p%");
1086 m_config
->autoUpdateProgressBar
->setMaximum(100);
1087 m_config
->autoUpdateProgressBar
->setValue(value
.toInt());
1089 case uploader::UPLOADING_DESC
:
1090 m_config
->autoUpdateLabel
->setText(tr("Uploading description of the new firmware to the board."));
1092 case uploader::BOOTING
:
1093 m_config
->autoUpdateLabel
->setText(tr("Rebooting the board. Please wait."));
1095 case uploader::BOOTING_AND_ERASING
:
1096 m_config
->autoUpdateLabel
->setText(tr("Rebooting and erasing the board. Please wait."));
1098 case uploader::SUCCESS
:
1099 m_config
->autoUpdateProgressBar
->setValue(m_config
->autoUpdateProgressBar
->maximum());
1100 msg
= tr("Board was updated successfully. Press OK to finish.");
1101 m_config
->autoUpdateLabel
->setText(QString("<font color='green'>%1</font>").arg(msg
));
1104 case uploader::FAILURE
:
1105 msg
= value
.toString();
1106 if (msg
.isEmpty()) {
1107 msg
= tr("Something went wrong.");
1109 msg
+= tr(" Press OK to finish, you will have to manually upgrade the board.");
1111 m_config
->autoUpdateLabel
->setText(QString("<font color='red'>%1</font>").arg(msg
));
1120 void UploaderGadgetWidget::log(QString str
)
1122 qDebug() << "UploaderGadgetWidget -" << str
;
1123 m_config
->textBrowser
->append(str
);
1126 void UploaderGadgetWidget::clearLog()
1128 m_config
->textBrowser
->clear();
1132 * Remove all the device widgets...
1134 UploaderGadgetWidget::~UploaderGadgetWidget()
1136 while (m_config
->systemElements
->count()) {
1137 QWidget
*qw
= m_config
->systemElements
->widget(0);
1138 m_config
->systemElements
->removeTab(0);
1144 void UploaderGadgetWidget::openHelp()
1146 QDesktopServices::openUrl(QUrl(tr("http://wiki.openpilot.org/x/AoBZ"), QUrl::StrictMode
));
1149 int UploaderGadgetWidget::confirmEraseSettingsMessageBox()
1151 QMessageBox
msgBox(this);
1153 msgBox
.setWindowTitle(tr("Confirm Settings Erase?"));
1154 msgBox
.setIcon(QMessageBox::Question
);
1155 msgBox
.setText(tr("Do you want to erase all settings from the board?"));
1156 msgBox
.setInformativeText(tr("Settings cannot be recovered after this operation.\nThe board will be restarted and all settings erased."));
1157 msgBox
.setStandardButtons(QMessageBox::Ok
| QMessageBox::Cancel
| QMessageBox::Help
);
1158 return msgBox
.exec();
1161 int UploaderGadgetWidget::cannotHaltMessageBox()
1163 QMessageBox
msgBox(this);
1165 msgBox
.setWindowTitle(tr("Cannot Halt Board!"));
1166 msgBox
.setIcon(QMessageBox::Warning
);
1167 msgBox
.setText(tr("The controller board is armed and can not be halted."));
1168 msgBox
.setInformativeText(tr("Please make sure the board is not armed and then press Halt again to proceed or use Rescue to force a firmware upgrade."));
1169 msgBox
.setStandardButtons(QMessageBox::Ok
);
1170 return msgBox
.exec();
1173 int UploaderGadgetWidget::cannotResetMessageBox()
1175 QMessageBox
msgBox(this);
1177 msgBox
.setWindowTitle(tr("Cannot Reset Board!"));
1178 msgBox
.setIcon(QMessageBox::Warning
);
1179 msgBox
.setText(tr("The controller board is armed and can not be reset."));
1180 msgBox
.setInformativeText(tr("Please make sure the board is not armed and then press reset again to proceed or power cycle to force a board reset."));
1181 msgBox
.setStandardButtons(QMessageBox::Ok
);
1182 return msgBox
.exec();
1186 int ResultEventLoop::run(int millisTimout
)
1188 m_timer
.singleShot(millisTimout
, this, SLOT(fail()));
1192 void ResultEventLoop::success()
1198 void ResultEventLoop::fail()