00001
00012 #include "ScipPlayerWindow.h"
00013 #include "ScipDataReader.h"
00014 #include "UrgDrawWidget.h"
00015 #include "UrgDevice.h"
00016 #include "ScipHandler.h"
00017 #include "CustomConnection.h"
00018 #include "ConvertStdStringPath.h"
00019 #include <QTimer>
00020 #include <QUrl>
00021 #include <QDragEnterEvent>
00022 #include <QDropEvent>
00023 #include <QFileDialog>
00024 #include <QShortcut>
00025 #include <QSettings>
00026 #include <QMessageBox>
00027
00028 using namespace qrk;
00029 using namespace std;
00030
00031
00032 namespace
00033 {
00034 const char* Organization = "Hokuyo LTD.";
00035 const char* Application = "Scip Player";
00036
00037 typedef vector<string> ScipData;
00038 }
00039
00040
00041 struct ScipPlayerWindow::pImpl
00042 {
00043 ScipPlayerWindow* widget_;
00044 UrgDrawWidget urg_draw_widget_;
00045 ScipDataReader* data_reader_;
00046
00047 ScipData scip_data_;
00048 long current_timestamp_;
00049 long last_timestamp_;
00050
00051 CustomConnection con_;
00052 UrgDevice urg_;
00053
00054 QTimer continuous_timer_;
00055 int play_ratio_;
00056 bool led_on_;
00057
00058 QStringList csv_files_;
00059 QString csv_files_dir_;
00060 bool csv_mode_;
00061 int csv_index_;
00062
00063
00064 pImpl(ScipPlayerWindow* parent)
00065 : widget_(parent), data_reader_(NULL),
00066 current_timestamp_(0), last_timestamp_(0),
00067 play_ratio_(0), led_on_(false), csv_mode_(false), csv_index_(0)
00068 {
00069 urg_.setConnection(&con_);
00070 continuous_timer_.setSingleShot(true);
00071 urg_draw_widget_.setDrawPeriod(1);
00072 }
00073
00074
00075 ~pImpl(void)
00076 {
00077 delete data_reader_;
00078 }
00079
00080
00081 void initializeForm(void)
00082 {
00083
00084 widget_->dummy_label_->hide();
00085 widget_->main_layout_->addWidget(&urg_draw_widget_);
00086
00087 widget_->path_edit_->setAcceptDrops(false);
00088 widget_->setAcceptDrops(true);
00089
00090
00091 connect(widget_->path_button_, SIGNAL(clicked()),
00092 widget_, SLOT(pathPressed()));
00093 connect(widget_->reload_button_, SIGNAL(clicked()),
00094 widget_, SLOT(reloadPressed()));
00095 connect(widget_->next_button_, SIGNAL(clicked()),
00096 widget_, SLOT(nextPressed()));
00097 connect(widget_->continuous_button_, SIGNAL(clicked()),
00098 widget_, SLOT(continuousPressed()));
00099 connect(&continuous_timer_, SIGNAL(timeout()),
00100 widget_, SLOT(timerTimeout()));
00101
00102
00103 connect(widget_->action_about_, SIGNAL(triggered()),
00104 widget_, SLOT(aboutApplication()));
00105 connect(widget_->action_quit_, SIGNAL(triggered()),
00106 widget_, SLOT(close()));
00107
00108
00109 (void) new QShortcut(Qt::Key_Return, widget_, SLOT(initializeView()));
00110 (void) new QShortcut(Qt::Key_F5, widget_, SLOT(reloadPressed()));
00111 (void) new QShortcut(Qt::Key_Less, widget_, SLOT(zoomSmaller()));
00112 (void) new QShortcut(Qt::Key_Comma, widget_, SLOT(zoomSmaller()));
00113 (void) new QShortcut(Qt::Key_Greater, widget_, SLOT(zoomLarger()));
00114 (void) new QShortcut(Qt::Key_Period, widget_, SLOT(zoomLarger()));
00115 }
00116
00117
00118 void saveSettings(void)
00119 {
00120 QSettings settings(Organization, Application);
00121
00122 settings.setValue("geometry", widget_->saveGeometry());
00123 settings.setValue("file_path", widget_->path_edit_->text());
00124 }
00125
00126
00127 void loadSettings(void)
00128 {
00129 QSettings settings(Organization, Application);
00130
00131 widget_->restoreGeometry(settings.value("geometry").toByteArray());
00132 QString log_file = settings.value("file_path", "").toString();
00133 widget_->path_edit_->setText(log_file);
00134 }
00135
00136
00137 void reload(void)
00138 {
00139
00140 widget_->setWindowTitle("Scip Player");
00141 continuous_timer_.stop();
00142 scip_data_.clear();
00143 con_.clear();
00144 urg_draw_widget_.clear();
00145 urg_draw_widget_.redraw();
00146 current_timestamp_ = 0;
00147 play_ratio_ = 0;
00148 led_on_ = false;
00149
00150 QString log_file = widget_->path_edit_->text();
00151
00152 QFileInfo file_info(log_file);
00153 QString suffix = file_info.suffix().toLower();
00154 csv_mode_ = (! suffix.compare("csv")) ? true : false;
00155 if (csv_mode_) {
00156
00157 handleCsvFile(log_file);
00158
00159 } else {
00160 handleRawFile(log_file);
00161 }
00162 }
00163
00164
00165 void handleRawFile(const QString& log_file)
00166 {
00167 string std_log_file = qrk::toStdStringPath(log_file);
00168 delete data_reader_;
00169 data_reader_ = new ScipDataReader(std_log_file);
00170
00171 prepareUrgReply();
00172
00173 if (! urg_.connect("dummy")) {
00174 fprintf(stderr, "UrgDevice::connect: %s\n", urg_.what());
00175 }
00176
00177
00178 for (int i = 0; i < 2; ++i) {
00179 while ((! data_reader_->isEmpty()) && (! updateDrawData())) {
00180 ;
00181 }
00182 }
00183
00184 #if 0
00185
00186 updateDrawData();
00187 #endif
00188 }
00189
00190
00191 void handleCsvFile(const QString& file_name)
00192 {
00193
00194 QFileInfo file_info(file_name);
00195 QDir dir = file_info.dir();
00196 csv_files_dir_ = file_info.absolutePath();
00197 QStringList filters;
00198 filters << "*.csv" << "*.CSV";
00199 dir.setNameFilters(filters);
00200 csv_files_ =
00201 dir.entryList(QDir::Files | QDir::NoSymLinks |
00202 QDir::NoDotAndDotDot | QDir::Readable, QDir::Name);
00203
00204
00205 csv_index_ = 0;
00206 current_timestamp_ = 0;
00207
00208
00209 updateCsvFile();
00210 }
00211
00212
00213 bool updateCsvFile(void)
00214 {
00215 if (csv_index_ >= csv_files_.size()) {
00216 return false;
00217 }
00218
00219
00220 QString file_name = csv_files_[csv_index_];
00221 QFile file(csv_files_dir_ + "/" + file_name);
00222
00223 widget_->setWindowTitle("Scip Player: " + file_name);
00224
00225 file.open(QIODevice::ReadOnly | QIODevice::Text);
00226
00227
00228 vector<Point<long> > points;
00229 while (! file.atEnd()) {
00230 QByteArray line = file.readLine();
00231 setLine(points, line);
00232 }
00233
00234 urg_draw_widget_.clear();
00235 urg_draw_widget_.setUrgData(points, 0);
00236 urg_draw_widget_.redraw();
00237
00238 ++csv_index_;
00239 return true;
00240 }
00241
00242
00243 void setLine(vector<Point<long> >& points, const QByteArray& line)
00244 {
00245 QList<QByteArray> tokens = line.split(',');
00246 if (tokens.size() < 5) {
00247
00248 return;
00249 }
00250
00251
00252 if (atoi(tokens[1]) < 20) {
00253
00254 return;
00255 }
00256 long x = atoi(tokens[3]);
00257 long y = atoi(tokens[4]);
00258 points.push_back(Point<long>(-y, x));
00259 }
00260
00261
00262 void prepareUrgReply(void)
00263 {
00264 con_.clear();
00265 con_.setReadData("QT\r00P\r\r");
00266
00267
00268 con_.setReadData("PP\r"
00269 "00P\r"
00270 "MODL:URG-04LX(Hokuyo Automatic Co.,Ltd.);N\r"
00271 "DMIN:20;4\r"
00272 "DMAX:5600;_\r"
00273 "ARES:1024;\r"
00274 "AMIN:44;7\r"
00275 "AMAX:725;o\r"
00276 "AFRT:384;6\r"
00277 "SCAN:600;e\r"
00278 "\r");
00279 }
00280
00281
00282 bool updateDrawData(void)
00283 {
00284 if (csv_mode_) {
00285 return updateCsvFile();
00286 } else {
00287 return updateRawDraw();
00288 }
00289 }
00290
00291
00292
00293 bool updateRawDraw(void)
00294 {
00295 if (! scip_data_.empty()) {
00296
00297 current_timestamp_ = timestamp(scip_data_);
00298 setScipData(scip_data_);
00299 urg_draw_widget_.setUrgData(&urg_);
00300 urg_draw_widget_.redraw();
00301 scip_data_.clear();
00302
00303 QString message = QString("timestamp: %1 [msec] (%2)")
00304 .arg(current_timestamp_)
00305 .arg(current_timestamp_ - last_timestamp_);
00306 widget_->statusBar()->showMessage(message, 60 * 1000);
00307 last_timestamp_ = current_timestamp_;
00308 }
00309
00310 ScipData line_block;
00311 while (data_reader_->readReplyLines(line_block)) {
00312
00313 string& first_line = line_block[0];
00314 if (! first_line.compare(0, 2, "BM")) {
00315 con_.setReadData("BM\r00P\r\r");
00316
00317 } else if (! first_line.compare(0, 2, "PP")) {
00318
00319 setScipData(line_block);
00320 urg_.loadParameter();
00321
00322 } else if (! first_line.compare(0, 1, "G")) {
00323
00324 swap(scip_data_, line_block);
00325 setPlayEnabled(true);
00326 return true;
00327
00328 } else if (! first_line.compare(0, 1, "M")) {
00329
00330 if (line_block.size() <= 2) {
00331
00332
00333 if (! led_on_) {
00334 con_.setReadData("BM\r00P\r\r");
00335 led_on_ = true;
00336 }
00337 if (! first_line.compare(1, 1, "E")) {
00338 fprintf(stderr, "set intensity mode.\n");
00339 urg_.setCaptureMode(IntensityCapture);
00340 }
00341
00342 } else {
00343
00344
00345 swap(scip_data_, line_block);
00346 setPlayEnabled(true);
00347 return true;
00348 }
00349 }
00350 }
00351
00352 setPlayEnabled(false);
00353 return false;
00354 }
00355
00356
00357 void setPlayEnabled(bool enable)
00358 {
00359 widget_->next_button_->setEnabled(enable);
00360 widget_->continuous_button_->setEnabled(enable);
00361 }
00362
00363
00364 void setScipData(const ScipData& line_block)
00365 {
00366 for (ScipData::const_iterator it = line_block.begin();
00367 it != line_block.end(); ++it) {
00368 con_.setReadData(*it + "\r");
00369 }
00370 }
00371
00372
00373 void continuousPlay(void)
00374 {
00375 if (updateDrawData()) {
00376 long delay = timestamp(scip_data_) - current_timestamp_;
00377 if (play_ratio_ > 0) {
00378 delay /= play_ratio_;
00379 }
00380 continuous_timer_.start(max(delay, 1L));
00381 }
00382 }
00383
00384
00385 long timestamp(const ScipData& data)
00386 {
00387 if (data.size() <= 2) {
00388
00389 return 25;
00390 }
00391 return ScipHandler::decode(data[2].c_str(), 4);
00392 }
00393 };
00394
00395
00396 ScipPlayerWindow::ScipPlayerWindow(void) : pimpl(new pImpl(this))
00397 {
00398 setupUi(this);
00399 pimpl->initializeForm();
00400 pimpl->loadSettings();
00401
00402 pimpl->reload();
00403 }
00404
00405
00406 ScipPlayerWindow::~ScipPlayerWindow(void)
00407 {
00408 pimpl->saveSettings();
00409 }
00410
00411
00412 void ScipPlayerWindow::aboutApplication(void)
00413 {
00414 QMessageBox::about(this, tr("About Scip Player"),
00415 tr("<h2>Scip Player ($Rev: 1957 $)</h2>"
00416 "<p>Demo Application for URG sensor</p>"
00417 "<p>Report bugs to "
00418 "<s-kamimura@hokuyo-aut.co.jp></p>"));
00419 }
00420
00421
00422 void ScipPlayerWindow::pathPressed(void)
00423 {
00424 QString file_name =
00425 QFileDialog::getOpenFileName(this, tr("SCIP Log file"),
00426 path_edit_->text(),
00427 tr("Log files (*.txt *.log *.csv)"));
00428 if (file_name.isEmpty()) {
00429 return;
00430 }
00431
00432 path_edit_->setText(file_name);
00433 pimpl->reload();
00434 }
00435
00436
00437 void ScipPlayerWindow::reloadPressed(void)
00438 {
00439 pimpl->reload();
00440 }
00441
00442
00443 void ScipPlayerWindow::nextPressed(void)
00444 {
00445 pimpl->continuous_timer_.stop();
00446 if (pimpl->play_ratio_ > 0) {
00447 --pimpl->play_ratio_;
00448 }
00449 pimpl->updateDrawData();
00450 }
00451
00452
00453 void ScipPlayerWindow::timerTimeout(void)
00454 {
00455 pimpl->continuousPlay();
00456 }
00457
00458
00459 void ScipPlayerWindow::continuousPressed(void)
00460 {
00461 ++pimpl->play_ratio_;
00462 pimpl->continuousPlay();
00463 }
00464
00465
00466 void ScipPlayerWindow::dragEnterEvent(QDragEnterEvent* event)
00467 {
00468 if (event->mimeData()->hasFormat("text/uri-list")) {
00469 event->acceptProposedAction();
00470 }
00471 }
00472
00473
00474 void ScipPlayerWindow::dropEvent(QDropEvent* event)
00475 {
00476 QList<QUrl> urls = event->mimeData()->urls();
00477 if (urls.isEmpty()) {
00478 return;
00479 }
00480
00481 QString file_name = urls.first().toLocalFile();
00482 if (file_name.isEmpty()) {
00483 return;
00484 }
00485
00486 path_edit_->setText(file_name);
00487 pimpl->reload();
00488 }
00489
00490
00491 void ScipPlayerWindow::zoomSmaller(void)
00492 {
00493 pimpl->urg_draw_widget_.updateZoomRatio(+1);
00494 }
00495
00496
00497 void ScipPlayerWindow::zoomLarger(void)
00498 {
00499 pimpl->urg_draw_widget_.updateZoomRatio(-1);
00500 }
00501
00502
00503 void ScipPlayerWindow::initializeView(void)
00504 {
00505 pimpl->urg_draw_widget_.initializeView();
00506 }