All Classes Namespaces Files Functions Variables Enumerations Enumerator
programs/ScipPlayer/ScipPlayerWindow.cpp
Go to the documentation of this file.
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             // 拡張子が CSV だったら、専用のモードでファイルを処理する
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         // 最初の描画、次データの取得、と2回のデータ更新を行う
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         // ディレクトリ中の CSV ファイルを、名前順に読み出す
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         // CSV データを配置する
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         // !!! Top-URG の場合は 23 なのを考慮すべき
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                 // Gx データの読み出し
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                     // MD を RawManualCapture で無理矢理に再生させるための処理
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                     // Mx データの読み出し
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             //return 100;
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                           "&lt;s-kamimura@hokuyo-aut.co.jp&gt;</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 }