ThymioFlasher.cpp
Go to the documentation of this file.
00001 #include <QFileDialog>
00002 #include <QMessageBox>
00003 #include <QDebug>
00004 #include <QPushButton>
00005 #include <QVBoxLayout>
00006 #include <QHBoxLayout>
00007 #include <QProgressBar>
00008 #include <QLineEdit>
00009 #include <QLabel>
00010 #include <QGroupBox>
00011 #include <QListWidget>
00012 #include <QApplication>
00013 #include <QTextCodec>
00014 #include <QTranslator>
00015 #include <QLibraryInfo>
00016 #include <QtConcurrentRun>
00017 #include <memory>
00018 #include <iostream>
00019 
00020 #include "../common/consts.h"
00021 #include "../utils/HexFile.h"
00022 
00023 #include "ThymioFlasher.h"
00024 #include "ThymioFlasher.moc"
00025 
00026 namespace Aseba
00027 {
00028         using namespace Dashel;
00029         using namespace std;
00030         
00031         typedef std::map<int, std::pair<std::string, std::string> > PortsMap;
00032         
00033         QtBootloaderInterface::QtBootloaderInterface(Stream* stream, int dest):
00034                 BootloaderInterface(stream, dest),
00035                 pagesCount(0),
00036                 pagesDoneCount(0)
00037         {}
00038         
00039         void QtBootloaderInterface::writeHexGotDescription(unsigned pagesCount)
00040         {
00041                 this->pagesCount = pagesCount;
00042                 this->pagesDoneCount = 0;
00043         }
00044         
00045         void QtBootloaderInterface::writePageStart(unsigned pageNumber, const uint8* data, bool simple)
00046         {
00047                 pagesDoneCount += 1;
00048                 emit flashProgress((100*pagesDoneCount)/pagesCount);
00049         }
00050         
00051         void QtBootloaderInterface::errorWritePageNonFatal(unsigned pageNumber)
00052         {
00053                 qDebug() << "Warning, error while writing page" << pageNumber << "continuing ...";
00054         }
00055 
00056         
00057         ThymioFlasherDialog::ThymioFlasherDialog(const std::string& target):
00058                 target(target)
00059         {
00060                 // Create the gui ...
00061                 setWindowTitle(tr("Thymio Firmware Updater"));
00062                 
00063                 QVBoxLayout* mainLayout = new QVBoxLayout(this);
00064                 
00065                 QHBoxLayout *imageLayout = new QHBoxLayout();
00066                 QLabel* image = new QLabel(this);
00067                 image->setPixmap(QPixmap(":/images/thymioflasher.png"));
00068                 imageLayout->addStretch();
00069                 imageLayout->addWidget(image);
00070                 imageLayout->addStretch();
00071                 mainLayout->addLayout(imageLayout);
00072                 
00073                 // file selector
00074                 QGroupBox* fileGroupBox = new QGroupBox(tr("Firmware file"));
00075                 QHBoxLayout *fileLayout = new QHBoxLayout();
00076                 lineEdit = new QLineEdit(this);
00077                 fileButton = new QPushButton(tr("Select..."), this);
00078                 fileButton->setIcon(QIcon(":/images/fileopen.svgz"));
00079                 fileLayout->addWidget(fileButton);
00080                 fileLayout->addWidget(lineEdit);
00081                 fileGroupBox->setLayout(fileLayout);
00082                 mainLayout->addWidget(fileGroupBox);
00083 
00084                 // progress bar
00085                 progressBar = new QProgressBar(this);
00086                 progressBar->setValue(0);
00087                 progressBar->setRange(0, 100);
00088                 mainLayout->addWidget(progressBar);
00089 
00090                 // flash and quit buttons
00091                 QHBoxLayout *flashLayout = new QHBoxLayout();
00092                 flashButton = new QPushButton(tr("Update"), this);
00093                 flashButton->setEnabled(false);
00094                 flashLayout->addWidget(flashButton);
00095                 quitButton = new QPushButton(tr("Quit"), this);
00096                 flashLayout->addWidget(quitButton);
00097                 mainLayout->addItem(flashLayout);
00098 
00099                 // connections
00100                 connect(fileButton, SIGNAL(clicked()), SLOT(openFile()));
00101                 connect(flashButton, SIGNAL(clicked()), SLOT(doFlash()));
00102                 connect(quitButton, SIGNAL(clicked()), SLOT(close()));
00103                 connect(&flashFutureWatcher, SIGNAL(finished()), SLOT(flashFinished()));
00104                 
00105                 show();
00106         }
00107         
00108         ThymioFlasherDialog::~ThymioFlasherDialog()
00109         {
00110                 flashFuture.waitForFinished();
00111         }
00112         
00113         void ThymioFlasherDialog::setupFlashButtonState()
00114         {
00115                 flashButton->setEnabled(
00116                         !lineEdit->text().isEmpty()
00117                 );
00118         }
00119         
00120         void ThymioFlasherDialog::openFile(void)
00121         {
00122                 QString name = QFileDialog::getOpenFileName(this, tr("Select hex file"), QString(), tr("Hex files (*.hex)"));
00123                 lineEdit->setText(name);
00124                 setupFlashButtonState();
00125         }
00126 
00127         void ThymioFlasherDialog::doFlash(void) 
00128         {
00129                 // warning message
00130                 const int warnRet = QMessageBox::warning(this, tr("Pre-update warning"), tr("Your are about to write a new firmware to the Thymio II. Make sure that the robot is charged and that the USB cable is properly connected.<p><b>Do not unplug the robot during the update!</b></p>Are you sure you want to proceed?"), QMessageBox::No|QMessageBox::Yes, QMessageBox::No);
00131                 if (warnRet != QMessageBox::Yes)
00132                         return;
00133                 
00134                 // disable buttons while flashing
00135                 quitButton->setEnabled(false);
00136                 flashButton->setEnabled(false);
00137                 fileButton->setEnabled(false);
00138                 lineEdit->setEnabled(false);
00139         
00140                 // start flash thread
00141                 Q_ASSERT(!flashFuture.isRunning());
00142                 const string hexFileName(lineEdit->text().toLocal8Bit().constData());
00143                 flashFuture = QtConcurrent::run(this, &ThymioFlasherDialog::flashThread, target, hexFileName);
00144                 flashFutureWatcher.setFuture(flashFuture);
00145         }
00146         
00147         ThymioFlasherDialog::FlashResult ThymioFlasherDialog::flashThread(const std::string& _target, const std::string& hexFileName) const
00148         {
00149                 // open stream
00150                 Dashel::Hub hub;
00151                 Dashel::Stream* stream(0);
00152                 try
00153                 {
00154                         stream = hub.connect(_target);
00155                 }
00156                 catch (Dashel::DashelException& e)
00157                 {
00158                         return FlashResult(FlashResult::WARNING, tr("Cannot connect to target"), tr("Cannot connect to target: %1").arg(e.what()));
00159                 }
00160                 
00161                 // do flash
00162                 try
00163                 {
00164                         QtBootloaderInterface bootloaderInterface(stream, 1);
00165                         connect(&bootloaderInterface, SIGNAL(flashProgress(int)), this, SLOT(flashProgress(int)), Qt::QueuedConnection);
00166                         bootloaderInterface.writeHex(hexFileName, true, true);
00167                 }
00168                 catch (HexFile::Error& e)
00169                 {
00170                         return FlashResult(FlashResult::WARNING, tr("Update Error"), tr("Unable to read Hex file: %1").arg(e.toString().c_str()));
00171                 }
00172                 catch (BootloaderInterface::Error& e)
00173                 {
00174                         return FlashResult(FlashResult::FAILURE, tr("Update Error"), tr("A bootloader error happened during the update process: %1").arg(e.what()));
00175                 }
00176                 catch (Dashel::DashelException& e)
00177                 {
00178                         return FlashResult(FlashResult::FAILURE, tr("Update Error"), tr("A communication error happened during the update process: %1").arg(e.what()));
00179                 }
00180                 return FlashResult();
00181         }
00182         
00183         void ThymioFlasherDialog::flashProgress(int percentage)
00184         {
00185                 progressBar->setValue(percentage);
00186         }
00187         
00188         void ThymioFlasherDialog::flashFinished()
00189         {
00190                 // re-enable buttons
00191                 quitButton->setEnabled(true);
00192                 flashButton->setEnabled(true);
00193                 fileButton->setEnabled(true);
00194                 lineEdit->setEnabled(true);
00195                 
00196                 // handle flash result
00197                 const FlashResult& result(flashFutureWatcher.result());
00198                 if (result.status == FlashResult::WARNING) 
00199                 {
00200                         QMessageBox::warning(this, result.title, result.text);
00201                 }
00202                 else if (result.status == FlashResult::FAILURE)
00203                 {
00204                         QMessageBox::critical(this, result.title, result.text);
00205                         close();
00206                 }
00207         }
00208 };
00209 
00210 
00211 int main(int argc, char *argv[])
00212 {
00213         QApplication app(argc, argv);
00214         
00215         QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
00216         
00217         QTranslator qtTranslator;
00218         QTranslator translator;
00219         app.installTranslator(&qtTranslator);
00220         app.installTranslator(&translator);
00221         qtTranslator.load(QString("qt_") + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
00222         translator.load(QString(":/thymioflasher_") + QLocale::system().name());
00223         
00224         const Aseba::PortsMap ports = Dashel::SerialPortEnumerator::getPorts();
00225         std::string target("ser:device=");
00226         bool thymioFound(false);
00227         bool thymiosFound(false);
00228         for (Aseba::PortsMap::const_iterator it = ports.begin(); it != ports.end(); ++it)
00229         {
00230                 if (it->second.second.compare(0,9,"Thymio-II") == 0)
00231                 {
00232                         if (thymioFound)
00233                                 thymiosFound = true;
00234                         thymioFound = true;
00235                         target += it->second.first;
00236                         //std::cout << target << std::endl;
00237                 }
00238         }
00239         if (!thymioFound)
00240         {
00241                 QMessageBox::critical(0, QApplication::tr("Thymio II not found"), QApplication::tr("<p><b>Cannot find Thymio II!</b></p><p>Plug Thymio II or use the command-line updater.</p>"));
00242                 return 1;
00243         }
00244         if (thymiosFound)
00245         {
00246                 QMessageBox::critical(0, QApplication::tr("Multiple Thymio II found"), QApplication::tr("<p><b>More than one Thymio II found!</b></p><p>Plug a single Thymio II or use the command-line updater.</p>"));
00247                 return 2;
00248         }
00249         
00250         Aseba::ThymioFlasherDialog flasher(target);
00251         
00252         return app.exec();
00253 }


aseba
Author(s): Stéphane Magnenat
autogenerated on Thu Jan 2 2014 11:17:17