diff --git a/ground/openpilotgcs/src/plugins/setupwizard/pages/autoupdatepage.cpp b/ground/openpilotgcs/src/plugins/setupwizard/pages/autoupdatepage.cpp index 57a6b7723..7c3b9fc63 100644 --- a/ground/openpilotgcs/src/plugins/setupwizard/pages/autoupdatepage.cpp +++ b/ground/openpilotgcs/src/plugins/setupwizard/pages/autoupdatepage.cpp @@ -40,8 +40,13 @@ void AutoUpdatePage::updateStatus(uploader::AutoUpdateStep status, QVariant valu switch (status) { case uploader::WAITING_DISCONNECT: getWizard()->setWindowFlags(getWizard()->windowFlags() & ~Qt::WindowStaysOnTopHint); + getWizard()->setWindowIcon(qApp->windowIcon()); disableButtons(); + getWizard()->show(); ui->statusLabel->setText("Waiting for all OP boards to be disconnected"); + // TODO get rid of magic number 20s + ui->levellinProgressBar->setMaximum(20); + ui->levellinProgressBar->setValue(value.toInt()); break; case uploader::WAITING_CONNECT: getWizard()->setWindowFlags(getWizard()->windowFlags() | Qt::WindowStaysOnTopHint); @@ -49,6 +54,8 @@ void AutoUpdatePage::updateStatus(uploader::AutoUpdateStep status, QVariant valu disableButtons(); getWizard()->show(); ui->statusLabel->setText("Please connect the board to the USB port (don't use external supply)"); + // TODO get rid of magic number 20s + ui->levellinProgressBar->setMaximum(20); ui->levellinProgressBar->setValue(value.toInt()); break; case uploader::JUMP_TO_BL: @@ -60,6 +67,7 @@ void AutoUpdatePage::updateStatus(uploader::AutoUpdateStep status, QVariant valu break; case uploader::UPLOADING_FW: ui->statusLabel->setText("Uploading firmware"); + ui->levellinProgressBar->setMaximum(100); ui->levellinProgressBar->setValue(value.toInt()); break; case uploader::UPLOADING_DESC: @@ -77,7 +85,11 @@ void AutoUpdatePage::updateStatus(uploader::AutoUpdateStep status, QVariant valu getWizard()->setWindowIcon(qApp->windowIcon()); enableButtons(true); getWizard()->show(); - ui->statusLabel->setText("Something went wrong, you will have to manually upgrade the board using the uploader plugin"); + QString msg = value.toString(); + if (msg.isEmpty()) { + msg = "Something went wrong, you will have to manually upgrade the board using the uploader plugin"; + } + ui->statusLabel->setText(msg); break; } } diff --git a/ground/openpilotgcs/src/plugins/uploader/uploader.ui b/ground/openpilotgcs/src/plugins/uploader/uploader.ui index 1143f5e1f..f0423a68a 100644 --- a/ground/openpilotgcs/src/plugins/uploader/uploader.ui +++ b/ground/openpilotgcs/src/plugins/uploader/uploader.ui @@ -47,7 +47,16 @@ 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -277,7 +286,7 @@ Rescue is possible in USB mode only. 0 - false + true diff --git a/ground/openpilotgcs/src/plugins/uploader/uploadergadgetwidget.cpp b/ground/openpilotgcs/src/plugins/uploader/uploadergadgetwidget.cpp index 63858419d..3e922afcd 100644 --- a/ground/openpilotgcs/src/plugins/uploader/uploadergadgetwidget.cpp +++ b/ground/openpilotgcs/src/plugins/uploader/uploadergadgetwidget.cpp @@ -35,6 +35,7 @@ #define DFU_DEBUG true +const int UploaderGadgetWidget::BOARD_EVENT_TIMEOUT = 20000; const int UploaderGadgetWidget::AUTOUPDATE_CLOSE_TIMEOUT = 7000; TimedDialog::TimedDialog(const QString &title, const QString &labelText, int timeout, QWidget *parent, Qt::WindowFlags flags) : @@ -75,6 +76,48 @@ void TimedDialog::perform() } } +ConnectionWaiter::ConnectionWaiter(int targetDeviceCount, int timeout, QWidget *parent) : QObject(parent), eventLoop(this), timer(this), timeout(timeout), elapsed(0), targetDeviceCount(targetDeviceCount), result(ConnectionWaiter::Ok) +{ +} + +int ConnectionWaiter::exec() { + connect(USBMonitor::instance(), SIGNAL(deviceDiscovered(USBPortInfo)), this, SLOT(deviceEvent())); + connect(USBMonitor::instance(), SIGNAL(deviceRemoved(USBPortInfo)), this, SLOT(deviceEvent())); + + connect(&timer, SIGNAL(timeout()), this, SLOT(perform())); + timer.start(1000); + + emit timeChanged(0); + eventLoop.exec(); + + return result; +} + +void ConnectionWaiter::quit() { + disconnect(USBMonitor::instance(), SIGNAL(deviceDiscovered(USBPortInfo)), this, SLOT(deviceEvent())); + disconnect(USBMonitor::instance(), SIGNAL(deviceRemoved(USBPortInfo)), this, SLOT(deviceEvent())); + timer.stop(); + eventLoop.exit(); +} + +void ConnectionWaiter::perform() +{ + ++elapsed; + emit timeChanged(elapsed); + int remaining = timeout - elapsed * 1000; + if (remaining <= 0) { + result = ConnectionWaiter::TimedOut; + quit(); + } +} + +void ConnectionWaiter::deviceEvent() +{ + if (USBMonitor::instance()->availableDevices(0x20a0, -1, -1, -1).length() == targetDeviceCount) { + quit(); + } +} + UploaderGadgetWidget::UploaderGadgetWidget(QWidget *parent) : QWidget(parent) { m_config = new Ui_UploaderWidget(); @@ -82,9 +125,8 @@ UploaderGadgetWidget::UploaderGadgetWidget(QWidget *parent) : QWidget(parent) currentStep = IAP_STATE_READY; resetOnly = false; dfu = NULL; - m_timer = 0; - m_progress = 0; msg = new QErrorMessage(this); + // Listen to autopilot connection events ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); TelemetryManager *telMngr = pm->getObject(); @@ -124,7 +166,6 @@ UploaderGadgetWidget::UploaderGadgetWidget(QWidget *parent) : QWidget(parent) } } - bool sortPorts(const QSerialPortInfo &s1, const QSerialPortInfo &s2) { return s1.portName() < s2.portName(); @@ -569,31 +610,25 @@ bool UploaderGadgetWidget::autoUpdate() delete dfu; dfu = NULL; } - QEventLoop loop; - QTimer timer; - timer.setSingleShot(true); - connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); - while (USBMonitor::instance()->availableDevices(0x20a0, -1, -1, -1).length() > 0) { - emit autoUpdateSignal(WAITING_DISCONNECT, QVariant()); - if (QMessageBox::warning(this, tr("OpenPilot Uploader"), tr("Please disconnect your OpenPilot board"), QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Cancel) { - emit autoUpdateSignal(FAILURE, QVariant()); + + if (USBMonitor::instance()->availableDevices(0x20a0, -1, -1, -1).length() > 0) { + // wait for all boards to be disconnected + ConnectionWaiter waiter(0, BOARD_EVENT_TIMEOUT); + connect(&waiter, SIGNAL(timeChanged(int)), this, SLOT(autoUpdateDisconnectProgress(int))); + if (waiter.exec() == ConnectionWaiter::TimedOut) { + emit autoUpdateSignal(FAILURE, QVariant(tr("Timed out while waiting for all boards to be disconnected!"))); return false; } - sleep(500); } - emit autoUpdateSignal(WAITING_CONNECT, 0); - autoUpdateConnectTimeout = 0; - m_timer = new QTimer(this); - connect(m_timer, SIGNAL(timeout()), this, SLOT(performAuto())); - m_timer->start(1000); - connect(USBMonitor::instance(), SIGNAL(deviceDiscovered(USBPortInfo)), &m_eventloop, SLOT(quit())); - m_eventloop.exec(); - if (!m_timer->isActive()) { - m_timer->stop(); - emit autoUpdateSignal(FAILURE, QVariant()); + + // wait for a board to connect + ConnectionWaiter waiter(1, BOARD_EVENT_TIMEOUT); + connect(&waiter, SIGNAL(timeChanged(int)), this, SLOT(autoUpdateConnectProgress(int))); + if (waiter.exec() == ConnectionWaiter::TimedOut) { + emit autoUpdateSignal(FAILURE, QVariant(tr("Timed out while waiting for a board to be connected!"))); return false; } - m_timer->stop(); + dfu = new DFUObject(DFU_DEBUG, false, QString()); dfu->AbortOperation(); emit autoUpdateSignal(JUMP_TO_BL, QVariant()); @@ -618,6 +653,7 @@ bool UploaderGadgetWidget::autoUpdate() emit autoUpdateSignal(FAILURE, QVariant()); return false; } + QString filename; emit autoUpdateSignal(LOADING_FW, QVariant()); switch (dfu->devices[0].ID) { @@ -654,9 +690,10 @@ bool UploaderGadgetWidget::autoUpdate() emit autoUpdateSignal(FAILURE, QVariant()); return false; } + QEventLoop eventLoop; firmware = file.readAll(); connect(dfu, SIGNAL(progressUpdated(int)), this, SLOT(autoUpdateProgress(int))); - connect(dfu, SIGNAL(uploadFinished(OP_DFU::Status)), &m_eventloop, SLOT(quit())); + connect(dfu, SIGNAL(uploadFinished(OP_DFU::Status)), &eventLoop, SLOT(quit())); emit autoUpdateSignal(UPLOADING_FW, QVariant()); if (!dfu->enterDFU(0)) { emit autoUpdateSignal(FAILURE, QVariant()); @@ -667,7 +704,7 @@ bool UploaderGadgetWidget::autoUpdate() emit autoUpdateSignal(FAILURE, QVariant()); return false; } - m_eventloop.exec(); + eventLoop.exec(); QByteArray desc = firmware.right(100); emit autoUpdateSignal(UPLOADING_DESC, QVariant()); if (dfu->UploadDescription(desc) != OP_DFU::Last_operation_Success) { @@ -679,11 +716,26 @@ bool UploaderGadgetWidget::autoUpdate() return true; } -void UploaderGadgetWidget::autoUpdateProgress(int value) +void UploaderGadgetWidget::autoUpdateDisconnectProgress(int value) +{ + emit autoUpdateSignal(WAITING_DISCONNECT, value); +} + +void UploaderGadgetWidget::autoUpdateConnectProgress(int value) +{ + emit autoUpdateSignal(WAITING_CONNECT, value); +} + +void UploaderGadgetWidget::autoUpdateFlashProgress(int value) { emit autoUpdateSignal(UPLOADING_FW, value); } +void UploaderGadgetWidget::autoUpdateProgress(int value) +{ + autoUpdateFlashProgress(value); +} + /** Attempt a guided procedure to put both boards in BL mode when the system is not bootable @@ -798,16 +850,6 @@ void UploaderGadgetWidget::systemRescue() currentStep = IAP_STATE_BOOTLOADER; } -void UploaderGadgetWidget::performAuto() -{ - ++autoUpdateConnectTimeout; - emit autoUpdateSignal(WAITING_CONNECT, autoUpdateConnectTimeout * 5); - if (autoUpdateConnectTimeout == 20) { - m_timer->stop(); - m_eventloop.exit(); - } -} - void UploaderGadgetWidget::uploadStarted() { m_config->haltButton->setEnabled(false); @@ -852,6 +894,7 @@ void UploaderGadgetWidget::startAutoUpdate() m_config->splitter->setEnabled(false); m_config->autoUpdateGroupBox->setVisible(true); m_config->autoUpdateOkButton->setEnabled(false); + connect(this, SIGNAL(autoUpdateSignal(uploader::AutoUpdateStep, QVariant)), this, SLOT(autoUpdateStatus(uploader::AutoUpdateStep, QVariant))); autoUpdate(); } @@ -860,14 +903,13 @@ void UploaderGadgetWidget::finishAutoUpdate() { disconnect(this, SIGNAL(autoUpdateSignal(uploader::AutoUpdateStep, QVariant)), this, SLOT(autoUpdateStatus(uploader::AutoUpdateStep, QVariant))); m_config->autoUpdateOkButton->setEnabled(true); - connect(&autoUpdateCloseTimer, SIGNAL(timeout()), this, SLOT(closeAutoUpdate())); - autoUpdateCloseTimer.start(AUTOUPDATE_CLOSE_TIMEOUT); + + // wait a bit and "close" auto update + QTimer::singleShot(AUTOUPDATE_CLOSE_TIMEOUT, this, SLOT(closeAutoUpdate())); } void UploaderGadgetWidget::closeAutoUpdate() { - autoUpdateCloseTimer.stop(); - disconnect(&autoUpdateCloseTimer, SIGNAL(timeout()), this, SLOT(closeAutoUpdate())); m_config->autoUpdateGroupBox->setVisible(false); m_config->buttonFrame->setEnabled(true); m_config->splitter->setEnabled(true); @@ -875,13 +917,22 @@ void UploaderGadgetWidget::closeAutoUpdate() void UploaderGadgetWidget::autoUpdateStatus(uploader::AutoUpdateStep status, QVariant value) { + QString msg; + int remaining; switch (status) { case uploader::WAITING_DISCONNECT: m_config->autoUpdateLabel->setText("Waiting for all OpenPilot boards to be disconnected from USB."); + m_config->autoUpdateProgressBar->setMaximum(BOARD_EVENT_TIMEOUT / 1000); + m_config->autoUpdateProgressBar->setValue(value.toInt()); + remaining = m_config->autoUpdateProgressBar->maximum() - m_config->autoUpdateProgressBar->value(); + m_config->autoUpdateProgressBar->setFormat(tr("Timing out in %1 seconds").arg(remaining)); break; case uploader::WAITING_CONNECT: m_config->autoUpdateLabel->setText("Please connect the OpenPilot board to the USB port."); + m_config->autoUpdateProgressBar->setMaximum(BOARD_EVENT_TIMEOUT / 1000); m_config->autoUpdateProgressBar->setValue(value.toInt()); + remaining = m_config->autoUpdateProgressBar->maximum() - m_config->autoUpdateProgressBar->value(); + m_config->autoUpdateProgressBar->setFormat(tr("Timing out in %1 seconds").arg(remaining)); break; case uploader::JUMP_TO_BL: m_config->autoUpdateProgressBar->setValue(0); @@ -892,6 +943,8 @@ void UploaderGadgetWidget::autoUpdateStatus(uploader::AutoUpdateStep status, QVa break; case uploader::UPLOADING_FW: m_config->autoUpdateLabel->setText("Uploading firmware to the board."); + m_config->autoUpdateProgressBar->setFormat("%p%"); + m_config->autoUpdateProgressBar->setMaximum(100); m_config->autoUpdateProgressBar->setValue(value.toInt()); break; case uploader::UPLOADING_DESC: @@ -901,11 +954,18 @@ void UploaderGadgetWidget::autoUpdateStatus(uploader::AutoUpdateStep status, QVa m_config->autoUpdateLabel->setText("Rebooting the board."); break; case uploader::SUCCESS: - m_config->autoUpdateLabel->setText("Board was updated successfully, press OK to finish."); + m_config->autoUpdateProgressBar->setValue(m_config->autoUpdateProgressBar->maximum()); + msg = tr("Board was updated successfully.") + " " + tr("Press OK to finish."); + m_config->autoUpdateLabel->setText(QString("%1").arg(msg)); finishAutoUpdate(); break; case uploader::FAILURE: - m_config->autoUpdateLabel->setText("Something went wrong, you will have to manually upgrade the board. Press OK to continue."); + QString msg = value.toString(); + if (msg.isEmpty()) { + msg = "Something went wrong, you will have to manually upgrade the board."; + } + msg += " " + tr("Press OK to finish."); + m_config->autoUpdateLabel->setText(QString("%1").arg(msg)); finishAutoUpdate(); break; } @@ -936,17 +996,8 @@ UploaderGadgetWidget::~UploaderGadgetWidget() delete qw; qw = 0; } - if (m_progress) { - delete m_progress; - m_progress = 0; - } - if (m_timer) { - delete m_timer; - m_timer = 0; - } } - /** Shows a message box with an error string. diff --git a/ground/openpilotgcs/src/plugins/uploader/uploadergadgetwidget.h b/ground/openpilotgcs/src/plugins/uploader/uploadergadgetwidget.h index baccc5ae7..f5335ef16 100644 --- a/ground/openpilotgcs/src/plugins/uploader/uploadergadgetwidget.h +++ b/ground/openpilotgcs/src/plugins/uploader/uploadergadgetwidget.h @@ -81,6 +81,38 @@ private: QProgressBar *bar; }; +// A helper class to wait for board connection and disconnection events +// until a the desired number of connected boards is found +// or until a timeout is reached +class ConnectionWaiter: public QObject { + Q_OBJECT + +public: + ConnectionWaiter(int targetDeviceCount, int timeout, QWidget *parent = 0); + + enum DialogCode { Ok, TimedOut }; + +public slots: + int exec(); + void quit(); + +signals: + void timeChanged(int elapsed); + +private slots: + void perform(); + void deviceEvent(); + +private: + QEventLoop eventLoop; + QTimer timer; + // timeour in ms + int timeout; + // elapsed time in seconds + int elapsed; + int targetDeviceCount; + int result; +}; class UPLOADER_EXPORT UploaderGadgetWidget : public QWidget { Q_OBJECT @@ -88,6 +120,9 @@ class UPLOADER_EXPORT UploaderGadgetWidget : public QWidget { public: UploaderGadgetWidget(QWidget *parent = 0); ~UploaderGadgetWidget(); + + static const int BOARD_EVENT_TIMEOUT; + void log(QString str); bool autoUpdateCapable(); @@ -97,7 +132,11 @@ public slots: void populate(); void openHelp(); bool autoUpdate(); + void autoUpdateDisconnectProgress(int); + void autoUpdateConnectProgress(int); + // autoUpdateProgress is deprecated: use autoUpdateFlashProgress instead void autoUpdateProgress(int); + void autoUpdateFlashProgress(int); signals: void autoUpdateSignal(uploader::AutoUpdateStep, QVariant); @@ -109,17 +148,13 @@ private: bool resetOnly; void clearLog(); QString getPortDevice(const QString &friendName); - QProgressDialog *m_progress; - QTimer *m_timer; QLineEdit *openFileNameLE; - QEventLoop m_eventloop; QErrorMessage *msg; void connectSignalSlot(QWidget *widget); - int autoUpdateConnectTimeout; FlightStatus *getFlightStatus(); void bootButtonsSetEnable(bool enabled); static const int AUTOUPDATE_CLOSE_TIMEOUT; - QTimer autoUpdateCloseTimer; + private slots: void onPhysicalHWConnect(); void versionMatchCheck(); @@ -134,7 +169,6 @@ private slots: void commonSystemBoot(bool safeboot = false, bool erase = false); void systemRescue(); void getSerialPorts(); - void performAuto(); void uploadStarted(); void uploadEnded(bool succeed); void downloadStarted();