All Classes Namespaces Files Functions Variables Enumerations Enumerator
programs/UrgScanner/UrgScannerWindow.cpp
Go to the documentation of this file.
00001 
00010 #include <QShortcut>
00011 #include <QTimer>
00012 #include <QSettings>
00013 #include <QFileDialog>
00014 #include <QWheelEvent>
00015 #include <cmath>
00016 #include "UrgScannerWindow.h"
00017 #include "UrgDataDraw.h"
00018 #include "WiiJoystick.h"
00019 #include "FindComPorts.h"
00020 #include "UrgUsbCom.h"
00021 #include "UrgDevice.h"
00022 #include "ConvertStdStringPath.h"
00023 
00024 using namespace qrk;
00025 using namespace std;
00026 
00027 
00028 struct UrgScannerWindow::pImpl
00029 {
00030   WiiJoystick wii_;
00031   UrgDevice urg_;
00032   long* urg_data_;
00033   size_t urg_data_max_;
00034   UrgDataDraw* data_draw_widget_;
00035   QTimer* redraw_timer_;
00036   bool save_ok_;
00037   UrgUsbCom urg_usb_;
00038   FindComPorts urg_finder_;
00039 
00040 
00041   pImpl(UrgScannerWindow* parent)
00042     : urg_data_(NULL), urg_data_max_(1),
00043       data_draw_widget_(new UrgDataDraw(parent)),
00044       redraw_timer_(new QTimer(parent)), save_ok_(false)
00045   {
00046     urg_finder_.addBaseName("/dev/ttyACM");
00047     urg_finder_.addBaseName("/dev/tty.usbmodem");
00048 
00049     urg_finder_.addDriverName("URG Series USB Device Driver");
00050     urg_finder_.addDriverName("URG-X002 USB Device Driver");
00051   }
00052 
00053 
00054   void loadSettings(UrgScannerWindow* parent)
00055   {
00056     QSettings settings("Hokuyo LTD.", "URG Scanner");
00057 
00058 #ifdef WINDOWS_OS
00059     // ポート選択用のコンボボックスの設定を読み出し
00060     parent->com_combobox_->
00061       setCurrentIndex(settings.value("port_index", 0).toInt());
00062 #endif
00063 
00064     // H 型 URG かどうかの選択を読み出し
00065     bool h_type = settings.value("h_type", false).toBool();
00066     parent->mirror_checkbox_->setChecked(h_type);
00067     data_draw_widget_->setTypeH(h_type);
00068 
00069     // 全面を表示するかの選択を読み出し
00070     bool front_only = settings.value("front_only", false).toBool();
00071     parent->front_only_checkbox_->setChecked(front_only);
00072     data_draw_widget_->setFrontOnly(front_only);
00073 
00074     // 高感度モードの選択を読み出し
00075     bool hs_mode = settings.value("hs_mode", false).toBool();
00076     parent->hs_mode_checkbox_->setChecked(hs_mode);
00077 
00078     // 強度データ表示の選択を読み出し
00079     bool intensity_mode = settings.value("intensity_mode", false).toBool();
00080     parent->intensity_checkbox_->setChecked(intensity_mode);
00081   }
00082 
00083 
00084   void saveSettings(UrgScannerWindow* parent) {
00085 
00086     QSettings settings("Hokuyo LTD.", "URG Scanner");
00087 
00088 #ifdef WINDOWS_OS
00089     settings.setValue("port_index",
00090                       parent->com_combobox_->currentIndex());
00091 #endif
00092     settings.setValue("h_type", parent->mirror_checkbox_->checkState());
00093     settings.setValue("front_only", parent->front_only_checkbox_->checkState());
00094     settings.setValue("hs_mode", parent->hs_mode_checkbox_->checkState());
00095     settings.setValue("intensity_mode",
00096                       parent->intensity_checkbox_->checkState());
00097   }
00098 
00099   // フォームの初期化
00100   void initializeForm(UrgScannerWindow* parent)
00101   {
00102     parent->com_combobox_->clear();
00103     vector<string> devices;
00104     urg_finder_.find(devices);
00105     for (vector<string>::iterator it = devices.begin();
00106          it != devices.end(); ++it) {
00107       if (urg_usb_.isUsbCom(it->c_str())) {
00108         *it = *it + " [URG]";
00109       }
00110       parent->com_combobox_->addItem(it->c_str());
00111     }
00112 
00113     // 描画ウィジットの配置
00114     parent->verticalLayout_tools_->addWidget(data_draw_widget_);
00115 
00116     // 接続ボタンの処理
00117     connect(parent->connect_button_, SIGNAL(clicked(bool)),
00118             parent, SLOT(connectHandler(bool)));
00119 
00120     // URG データの再描画
00121     connect(redraw_timer_, SIGNAL(timeout()), parent, SLOT(redrawHandler()));
00122 
00123     // H 型かどうか
00124     connect(parent->mirror_checkbox_, SIGNAL(clicked(bool)),
00125             data_draw_widget_, SLOT(setTypeH(bool)));
00126     data_draw_widget_->
00127       setTypeH((parent->mirror_checkbox_->checkState() != Qt::Checked)
00128                ? false : true);
00129 
00130     // 前方のみの取得
00131     connect(parent->front_only_checkbox_, SIGNAL(clicked(bool)),
00132             data_draw_widget_, SLOT(setFrontOnly(bool)));
00133     data_draw_widget_->
00134       setFrontOnly((parent->front_only_checkbox_->checkState() != Qt::Checked)
00135                    ? false : true);
00136 
00137     parent->hs_mode_checkbox_->hide();
00138 
00139 
00140     // 強度データ
00141     connect(parent->intensity_checkbox_, SIGNAL(clicked(bool)),
00142             parent, SLOT(meHandler(bool)));
00143 
00144     // 拡大率の変更
00145     connect(parent->magnify_slidebar_, SIGNAL(valueChanged(int)),
00146             data_draw_widget_, SLOT(magnifyChanged(int)));
00147 
00148     // 拡大率について、フォームの初期値を代入
00149     data_draw_widget_->magnifyChanged(parent->magnify_slidebar_->value());
00150 
00151     // RETURN キーで、視点を書記化
00152     (void) new QShortcut(Qt::Key_Return, data_draw_widget_, SLOT(resetView()));
00153 
00154     // VRML の保存、読み込みイベント
00155     connect(parent->load_button_, SIGNAL(clicked()),
00156             parent, SLOT(loadVrml()));
00157     connect(parent->save_button_, SIGNAL(clicked()),
00158             parent, SLOT(saveVrml()));
00159   }
00160 
00161   void clearForm(UrgScannerWindow* parent) {
00162     static_cast<void>(parent);
00163 
00164     // !!! Window のメッセージ欄に表示する
00165     //parent->battery_label_->setText(tr("battery:  --[%]"));
00166 
00167     // データ描画ウィンドウから、測定中データを消去
00168     data_draw_widget_->clearCaptureData();
00169   }
00170 
00171   // Z 軸, X 軸 の順に回転させることを想定している
00172   void getWiiRotate(Point3d<int>& wii_rotate)
00173   {
00174     if (wii_.isConnected()) {
00175 
00176       // 加速度情報の取得
00177       Point3d<double> acc;
00178       wii_.acceleration(acc);
00179 
00180       // 回転角度の計算
00181       double length = sqrt((acc.x * acc.x) + (acc.z * acc.z));
00182       double x_rad = atan2(-acc.y, length);
00183       double z_rad = atan2(acc.z, acc.x);
00184 
00185       wii_rotate.x = -static_cast<int>(180 * z_rad / M_PI) + 90;
00186       wii_rotate.y = -static_cast<int>(180 * x_rad / M_PI);
00187       wii_rotate.z = 0;
00188 
00189     } else {
00190       wii_rotate.x = 0;
00191     }
00192   }
00193 };
00194 
00195 
00196 UrgScannerWindow::UrgScannerWindow(void) : pimpl(new pImpl(this))
00197 {
00198   setupUi(this);
00199 
00200   pimpl->initializeForm(this);
00201   pimpl->clearForm(this);
00202 
00203   pimpl->loadSettings(this);
00204 
00205   // Ctrl-q で終了させる
00206   (void) new QShortcut(Qt::CTRL + Qt::Key_Q, this, SLOT(close()));
00207 }
00208 
00209 
00210 UrgScannerWindow::~UrgScannerWindow(void)
00211 {
00212   pimpl->saveSettings(this);
00213 }
00214 
00215 
00216 void UrgScannerWindow::connectHandler(bool checked)
00217 {
00218   // !!! 接続時には「Press wiimote 1 + 2 buttons.」と表示させる
00219 
00220   if (checked) {
00221     if (! pimpl->wii_.connect()) {
00222       // !!! メッセージボックスにエラーを表示する
00223       fprintf(stderr, "WiiJoystick::connect: %s\n", pimpl->wii_.what());
00224       // return;
00225 
00226       // !!! Wii がなくても表示はできるようにする
00227     }
00228     enum { MovingAverageSize = 12 };
00229     pimpl->wii_.setAccelerationAverageSize(MovingAverageSize);
00230 
00231     // URG への接続処理
00232     if (com_combobox_->count() == 0) {
00233       return;
00234     }
00235     string device_name = com_combobox_->currentText().toStdString();
00236     string device = device_name.substr(0, device_name.find(' '));
00237 
00238     if (! pimpl->urg_.connect(device.c_str(), 115200)) {
00239       // !!! メッセージボックスにエラーを表示する
00240       fprintf(stderr, "UrgDevice::connect: %s\n", pimpl->urg_.what());
00241       return;
00242     }
00243     pimpl->urg_.setCaptureMode(AutoCapture);
00244 
00245     // Intensity モードに設定
00246     bool me_on = intensity_checkbox_->isChecked() ? true : false;
00247     if (! meHandler(me_on)) {
00248       // 設定に失敗したら、Intensity のチェックボックスを無効にする
00249       intensity_checkbox_->setEnabled(false);
00250     }
00251 
00252     // 表示名の変更
00253     connect_button_->setText(tr("Disconnect"));
00254 
00255     if (! pimpl->urg_data_) {
00256       // 受信バッファの初期化
00257       pimpl->urg_data_max_ = pimpl->urg_.maxScanLines();
00258       pimpl->urg_data_ = new long[pimpl->urg_data_max_];
00259       //pimpl->urg_.setCaptureRange(0, pimpl->urg_data_max_);
00260     }
00261 
00262     // インターバル間隔は、URG の更新周期とする
00263     size_t scan_msec = pimpl->urg_.scanMsec();
00264     pimpl->redraw_timer_->start(scan_msec);
00265 
00266   } else {
00267     // 離されたとき disconnect()
00268     connect_button_->setText(tr("Connect"));
00269     pimpl->redraw_timer_->stop();
00270 
00271     if (pimpl->wii_.isConnected()) {
00272       pimpl->wii_.disconnect();
00273     }
00274     pimpl->urg_.disconnect();
00275     intensity_checkbox_->setEnabled(true);
00276 
00277     pimpl->clearForm(this);
00278   }
00279 }
00280 
00281 
00282 // 再描画
00283 void UrgScannerWindow::redrawHandler(void)
00284 {
00285   // Wii 姿勢の取得
00286   Point3d<int> wii_rotate;
00287   pimpl->getWiiRotate(wii_rotate);
00288 
00289   // 記録するかどうか、の取得
00290   bool record = pimpl->wii_.isButtonPressed(WiiJoystick::BUTTON_B);
00291   if (record && (! pimpl->save_ok_)) {
00292     pimpl->save_ok_ = true;
00293     save_button_->setEnabled(true);
00294   }
00295 
00296   // 現在のデータを表示するかどうか
00297   bool no_plot =
00298     pimpl->wii_.isButtonPressed(WiiJoystick::BUTTON_1) ||
00299     pimpl->wii_.isButtonPressed(WiiJoystick::BUTTON_2);
00300 
00301   // データの再描画
00302   pimpl->data_draw_widget_->redraw(pimpl->urg_, wii_rotate, record, no_plot);
00303 }
00304 
00305 
00306 // Intensity モードの設定
00307 bool UrgScannerWindow::meHandler(bool checked)
00308 {
00309   if (! pimpl->urg_.isConnected()) {
00310     // 接続されていなければ、戻る
00311     return true;
00312   }
00313 
00314   // Intensity モードを変更する
00315   pimpl->redraw_timer_->stop();
00316   RangeCaptureMode mode = checked ? IntensityCapture : AutoCapture;
00317   pimpl->urg_.setCaptureMode(mode);
00318   pimpl->data_draw_widget_->setIntensityMode(checked);
00319   pimpl->redraw_timer_->start();
00320 
00321   return true;
00322 }
00323 
00324 
00325 void UrgScannerWindow::loadVrml(void)
00326 {
00327   QString fileName =
00328     QFileDialog::getOpenFileName(this, tr("Open VRML file."), ".",
00329                                  tr("VRML file (*.wrl)"));
00330 
00331   if (! fileName.isEmpty()) {
00332     // 日本語を含むパスへの対処
00333     string std_path = qrk::toStdStringPath(fileName);
00334 
00335     pimpl->data_draw_widget_->loadVrml(std_path);
00336   }
00337 }
00338 
00339 
00340 void UrgScannerWindow::saveVrml(void)
00341 {
00342   // !!! 全ての保存を SaveAs として扱う
00343 
00344   QString fileName =
00345     QFileDialog::getSaveFileName(this, tr("Save VRML file."), ".",
00346                                  tr("VRML file (*.wrl)"));
00347 
00348   if (! fileName.isEmpty()) {
00349     // 日本語を含むパスへの対処
00350     string std_path = qrk::toStdStringPath(fileName);
00351 
00352     pimpl->data_draw_widget_->saveVrml(std_path);
00353   }
00354 }
00355 
00356 
00357 // ホイールによる拡大縮小の操作
00358 void UrgScannerWindow::wheelEvent(QWheelEvent* event)
00359 {
00360   int degrees = event->delta() / 8;
00361   int steps = degrees / 15;
00362 
00363   int add_value = ((steps > 0) ? +1 : -1) * magnify_slidebar_->singleStep();
00364   magnify_slidebar_->setValue(magnify_slidebar_->value() + add_value);
00365 
00366   event->accept();
00367 }