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
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
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
00085 progressBar = new QProgressBar(this);
00086 progressBar->setValue(0);
00087 progressBar->setRange(0, 100);
00088 mainLayout->addWidget(progressBar);
00089
00090
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
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
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
00135 quitButton->setEnabled(false);
00136 flashButton->setEnabled(false);
00137 fileButton->setEnabled(false);
00138 lineEdit->setEnabled(false);
00139
00140
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
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
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
00191 quitButton->setEnabled(true);
00192 flashButton->setEnabled(true);
00193 fileButton->setEnabled(true);
00194 lineEdit->setEnabled(true);
00195
00196
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
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 }