tab_states_and_goals.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2012, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  *
00029  * Author: Mario Prats, Ioan Sucan
00030  */
00031 
00032 #include <main_window.h>
00033 #include <job_processing.h>
00034 
00035 #include <moveit/kinematic_constraints/utils.h>
00036 #include <moveit/robot_state/conversions.h>
00037 #include <moveit/warehouse/constraints_storage.h>
00038 #include <moveit/warehouse/state_storage.h>
00039 
00040 #include <rviz/display_context.h>
00041 #include <rviz/window_manager_interface.h>
00042 
00043 #include <eigen_conversions/eigen_msg.h>
00044 
00045 #include <QMessageBox>
00046 #include <QInputDialog>
00047 #include <QFileDialog>
00048 
00049 
00050 #include <boost/math/constants/constants.hpp>
00051 
00052 namespace benchmark_tool
00053 {
00054 
00055 void MainWindow::createGoalPoseButtonClicked(void)
00056 {
00057   std::stringstream ss;
00058 
00059   {
00060     const planning_scene_monitor::LockedPlanningSceneRO &ps = scene_display_->getPlanningSceneRO();
00061     if ( ! ps || robot_interaction_->getActiveEndEffectors().empty() )
00062     {
00063       if ( ! ps ) ROS_ERROR("Not planning scene");
00064       if ( robot_interaction_->getActiveEndEffectors().empty() ) ROS_ERROR("No end effector");
00065       return;
00066     }
00067     else
00068       ss << ps->getName().c_str() << "_pose_" << std::setfill('0') << std::setw(4) << goal_poses_.size();
00069   }
00070 
00071   bool ok = false;
00072   QString text = QInputDialog::getText(this, tr("Choose a name"),
00073                                        tr("Goal pose name:"), QLineEdit::Normal,
00074                                        QString(ss.str().c_str()), &ok);
00075 
00076   std::string name;
00077   if (ok)
00078   {
00079     if ( ! text.isEmpty() )
00080     {
00081       name = text.toStdString();
00082       if (goal_poses_.find(name) != goal_poses_.end())
00083         QMessageBox::warning(this, "Name already exists", QString("The name '").append(name.c_str()).
00084                              append("' already exists. Not creating goal."));
00085       else
00086       {
00087         //Create the new goal pose at the current eef pose, and attach an interactive marker to it
00088         Eigen::Affine3d tip_pose = scene_display_->getPlanningSceneRO()->getCurrentState().getLinkState(robot_interaction_->getActiveEndEffectors()[0].parent_link)->getGlobalLinkTransform();
00089         goals_initial_pose_.insert(std::pair<std::string, Eigen::Affine3d>(name, tip_pose));
00090 
00091         geometry_msgs::Pose marker_pose;
00092         tf::poseEigenToMsg(tip_pose * goal_offset_, marker_pose);
00093         static const float marker_scale = 0.15;
00094 
00095         GripperMarkerPtr goal_pose(new GripperMarker(scene_display_->getPlanningSceneRO()->getCurrentState(), scene_display_->getSceneNode(), visualization_manager_, name, scene_display_->getRobotModel()->getModelFrame(),
00096                                 robot_interaction_->getActiveEndEffectors()[0], marker_pose, marker_scale, GripperMarker::NOT_TESTED));
00097         goal_poses_.insert(GoalPosePair(name,  goal_pose));
00098 
00099         // Connect signals
00100         goal_pose->connect(this, SLOT( goalPoseFeedback(visualization_msgs::InteractiveMarkerFeedback &) ));
00101 
00102         //If connected to a database, store the constraint
00103         if (constraints_storage_)
00104         {
00105           moveit_msgs::Constraints c;
00106           c.name = name;
00107 
00108           shape_msgs::SolidPrimitive sp;
00109           sp.type = sp.BOX;
00110           sp.dimensions.resize(3, std::numeric_limits<float>::epsilon() * 10.0);
00111 
00112           moveit_msgs::PositionConstraint pc;
00113           pc.constraint_region.primitives.push_back(sp);
00114           geometry_msgs::Pose posemsg;
00115           posemsg.position.x = goal_pose->imarker->getPosition().x;
00116           posemsg.position.y = goal_pose->imarker->getPosition().y;
00117           posemsg.position.z = goal_pose->imarker->getPosition().z;
00118           posemsg.orientation.x = 0.0;
00119           posemsg.orientation.y = 0.0;
00120           posemsg.orientation.z = 0.0;
00121           posemsg.orientation.w = 1.0;
00122           pc.constraint_region.primitive_poses.push_back(posemsg);
00123           pc.weight = 1.0;
00124           c.position_constraints.push_back(pc);
00125 
00126           moveit_msgs::OrientationConstraint oc;
00127           oc.orientation.x = goal_pose->imarker->getOrientation().x;
00128           oc.orientation.y = goal_pose->imarker->getOrientation().y;
00129           oc.orientation.z = goal_pose->imarker->getOrientation().z;
00130           oc.orientation.w = goal_pose->imarker->getOrientation().w;
00131           oc.absolute_x_axis_tolerance = oc.absolute_y_axis_tolerance =
00132             oc.absolute_z_axis_tolerance = std::numeric_limits<float>::epsilon() * 10.0;
00133           oc.weight = 1.0;
00134           c.orientation_constraints.push_back(oc);
00135 
00136           try
00137           {
00138             constraints_storage_->addConstraints(c);
00139           }
00140           catch (std::runtime_error &ex)
00141           {
00142             ROS_ERROR("Cannot save constraint on database: %s", ex.what());
00143           }
00144         }
00145       }
00146     }
00147     else
00148       QMessageBox::warning(this, "Goal not created", "Cannot use an empty name for a new goal pose.");
00149   }
00150 
00151   populateGoalPosesList();
00152 }
00153 
00154 void MainWindow::removeSelectedGoalsButtonClicked(void)
00155 {
00156   QList<QListWidgetItem*> found_items = ui_.goal_poses_list->selectedItems();
00157   for ( unsigned int i = 0 ; i < found_items.size() ; i++ )
00158   {
00159     goal_poses_.erase(found_items[i]->text().toStdString());
00160   }
00161   populateGoalPosesList();
00162 }
00163 
00164 void MainWindow::removeAllGoalsButtonClicked(void)
00165 {
00166   goal_poses_.clear();
00167   goals_initial_pose_.clear();
00168   populateGoalPosesList();
00169 }
00170 
00171 void MainWindow::loadGoalsFromDBButtonClicked(void)
00172 {
00173   //Get all the constraints from the database, convert to goal pose markers
00174   if (constraints_storage_ && !robot_interaction_->getActiveEndEffectors().empty())
00175   {
00176     //First clear the current list
00177     removeAllGoalsButtonClicked();
00178 
00179     std::vector<std::string> names;
00180     try
00181     {
00182       constraints_storage_->getKnownConstraints(ui_.load_poses_filter_text->text().toStdString(), names);
00183     }
00184     catch (std::runtime_error &ex)
00185     {
00186       QMessageBox::warning(this, "Cannot query the database", QString("Wrongly formatted regular expression for goal poses: ").append(ex.what()));
00187       return;
00188     }
00189 
00190     for (unsigned int i = 0 ; i < names.size() ; i++)
00191     {
00192       //Create a goal pose marker
00193       moveit_warehouse::ConstraintsWithMetadata c;
00194       bool got_constraint = false;
00195       try
00196       {
00197         got_constraint = constraints_storage_->getConstraints(c, names[i]);
00198       }
00199       catch (std::runtime_error &ex)
00200       {
00201         ROS_ERROR("%s", ex.what());
00202       }
00203       if (!got_constraint)
00204         continue;
00205 
00206       if ( c->position_constraints.size() > 0 && c->position_constraints[0].constraint_region.primitive_poses.size() > 0 && c->orientation_constraints.size() > 0 )
00207       {
00208         //Overwrite if exists. TODO: Ask the user before overwriting? copy the existing one with another name before?
00209         if ( goal_poses_.find(c->name) != goal_poses_.end() )
00210         {
00211           goal_poses_.erase(c->name);
00212         }
00213         geometry_msgs::Pose shape_pose;
00214         shape_pose.position = c->position_constraints[0].constraint_region.primitive_poses[0].position;
00215         shape_pose.orientation = c->orientation_constraints[0].orientation;
00216 
00217         Eigen::Affine3d shape_pose_eigen;
00218         tf::poseMsgToEigen(shape_pose, shape_pose_eigen);
00219         goals_initial_pose_.insert(std::pair<std::string, Eigen::Affine3d>(c->name, shape_pose_eigen));
00220 
00221         tf::poseEigenToMsg(shape_pose_eigen * goal_offset_, shape_pose);
00222 
00223         static const float marker_scale = 0.15;
00224         GripperMarkerPtr goal_pose(new GripperMarker(scene_display_->getPlanningSceneRO()->getCurrentState(), scene_display_->getSceneNode(), visualization_manager_, c->name, scene_display_->getRobotModel()->getModelFrame(),
00225                                 robot_interaction_->getActiveEndEffectors()[0], shape_pose, marker_scale, GripperMarker::NOT_TESTED, false,
00226                                 ui_.show_x_checkbox->isChecked(), ui_.show_y_checkbox->isChecked(), ui_.show_z_checkbox->isChecked()));
00227         // Connect signals
00228         goal_pose->connect(this, SLOT( goalPoseFeedback(visualization_msgs::InteractiveMarkerFeedback &) ));
00229 
00230         goal_poses_.insert(GoalPosePair(c->name, goal_pose));
00231       }
00232     }
00233     populateGoalPosesList();
00234    }
00235   else
00236   {
00237     if (!constraints_storage_)
00238       QMessageBox::warning(this, "Warning", "Not connected to a database.");
00239   }
00240 }
00241 
00242 void MainWindow::saveGoalsOnDBButtonClicked(void)
00243 {
00244   if (constraints_storage_)
00245   {
00246     //Convert all goal pose markers into constraints and store them
00247     for (GoalPoseMap::iterator it = goal_poses_.begin(); it != goal_poses_.end(); ++it)
00248     {
00249       moveit_msgs::Constraints c;
00250       c.name = it->first;
00251 
00252       shape_msgs::SolidPrimitive sp;
00253       sp.type = sp.BOX;
00254       sp.dimensions.resize(3, std::numeric_limits<float>::epsilon() * 10.0);
00255 
00256       moveit_msgs::PositionConstraint pc;
00257       pc.constraint_region.primitives.push_back(sp);
00258       geometry_msgs::Pose posemsg;
00259       it->second->getPosition(posemsg.position);
00260       posemsg.orientation.x = 0.0;
00261       posemsg.orientation.y = 0.0;
00262       posemsg.orientation.z = 0.0;
00263       posemsg.orientation.w = 1.0;
00264       pc.constraint_region.primitive_poses.push_back(posemsg);
00265       pc.weight = 1.0;
00266       c.position_constraints.push_back(pc);
00267 
00268       moveit_msgs::OrientationConstraint oc;
00269       it->second->getOrientation(oc.orientation);
00270       oc.absolute_x_axis_tolerance = oc.absolute_y_axis_tolerance =
00271         oc.absolute_z_axis_tolerance = std::numeric_limits<float>::epsilon() * 10.0;
00272       oc.weight = 1.0;
00273       c.orientation_constraints.push_back(oc);
00274 
00275       try
00276       {
00277         constraints_storage_->addConstraints(c);
00278       }
00279       catch (std::runtime_error &ex)
00280       {
00281         ROS_ERROR("Cannot save constraint: %s", ex.what());
00282       }
00283     }
00284   }
00285   else
00286   {
00287     QMessageBox::warning(this, "Warning", "Not connected to a database.");
00288   }
00289 }
00290 
00291 void MainWindow::deleteGoalsOnDBButtonClicked(void)
00292 {
00293   if (constraints_storage_)
00294   {
00295     //Warn the user
00296     QMessageBox msgBox;
00297     msgBox.setText("All the selected items will be removed from the database");
00298     msgBox.setInformativeText("Do you want to continue?");
00299     msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
00300     msgBox.setDefaultButton(QMessageBox::No);
00301     int ret = msgBox.exec();
00302 
00303     switch (ret)
00304     {
00305       case QMessageBox::Yes:
00306       {
00307         //Go through the list of goal poses, and delete those selected
00308         QList<QListWidgetItem*> found_items = ui_.goal_poses_list->selectedItems();
00309         for ( std::size_t i = 0 ; i < found_items.size() ; i++ )
00310         {
00311           try
00312           {
00313             constraints_storage_->removeConstraints(found_items[i]->text().toStdString());
00314           }
00315           catch (std::runtime_error &ex)
00316           {
00317             ROS_ERROR("%s", ex.what());
00318           }
00319         }
00320         break;
00321       }
00322     }
00323   }
00324   removeSelectedGoalsButtonClicked();
00325 }
00326 
00327 
00328 void MainWindow::loadStatesFromDBButtonClicked(void)
00329 {
00330   //Get all the start states from the database
00331   if (robot_state_storage_)
00332   {
00333     //First clear the current list
00334     removeAllStatesButtonClicked();
00335 
00336     std::vector<std::string> names;
00337     try
00338     {
00339       robot_state_storage_->getKnownRobotStates(ui_.load_states_filter_text->text().toStdString(), names);
00340     }
00341     catch (std::runtime_error &ex)
00342     {
00343       QMessageBox::warning(this, "Cannot query the database", QString("Wrongly formatted regular expression for goal poses: ").append(ex.what()));
00344       return;
00345     }
00346 
00347     for ( unsigned int i = 0 ; i < names.size() ; i++ )
00348     {
00349       moveit_warehouse::RobotStateWithMetadata rs;
00350       bool got_state = false;
00351       try
00352       {
00353         got_state = robot_state_storage_->getRobotState(rs, names[i]);
00354       }
00355       catch(std::runtime_error &ex)
00356       {
00357         ROS_ERROR("%s", ex.what());
00358       }
00359       if (!got_state)
00360         continue;
00361 
00362       //Overwrite if exists.
00363       if (start_states_.find(names[i]) != start_states_.end())
00364       {
00365         start_states_.erase(names[i]);
00366       }
00367 
00368       //Store the current start state
00369       start_states_.insert(StartStatePair(names[i], StartStatePtr(new StartState(*rs))));
00370     }
00371     populateStartStatesList();
00372   }
00373   else
00374   {
00375     QMessageBox::warning(this, "Warning", "Not connected to a database.");
00376   }
00377 }
00378 
00379 void MainWindow::saveStatesOnDBButtonClicked(void)
00380 {
00381   if (robot_state_storage_)
00382   {
00383     //Store all start states
00384     for (StartStateMap::iterator it = start_states_.begin(); it != start_states_.end(); ++it)
00385       try
00386       {
00387         robot_state_storage_->addRobotState(it->second->state_msg, it->first);
00388       }
00389       catch (std::runtime_error &ex)
00390       {
00391         ROS_ERROR("Cannot save robot state: %s", ex.what());
00392       }
00393   }
00394   else
00395   {
00396     QMessageBox::warning(this, "Warning", "Not connected to a database.");
00397   }
00398 }
00399 
00400 void MainWindow::deleteStatesOnDBButtonClicked(void)
00401 {
00402   if (robot_state_storage_)
00403   {
00404     //Warn the user
00405     QMessageBox msgBox;
00406     msgBox.setText("All the selected items will be removed from the database");
00407     msgBox.setInformativeText("Do you want to continue?");
00408     msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
00409     msgBox.setDefaultButton(QMessageBox::No);
00410     int ret = msgBox.exec();
00411 
00412     switch (ret)
00413     {
00414       case QMessageBox::Yes:
00415       {
00416         QList<QListWidgetItem*> found_items =  ui_.start_states_list->selectedItems();
00417         for (unsigned int i = 0; i < found_items.size() ; ++i)
00418         {
00419           try
00420           {
00421             robot_state_storage_->removeRobotState(found_items[i]->text().toStdString());
00422           }
00423           catch (std::runtime_error &ex)
00424           {
00425             ROS_ERROR("%s", ex.what());
00426           }
00427         }
00428         break;
00429       }
00430     }
00431   }
00432   removeSelectedStatesButtonClicked();
00433 }
00434 
00435 void MainWindow::visibleAxisChanged(int state)
00436 {
00437   if ( ! robot_interaction_->getActiveEndEffectors().empty() )
00438   {
00439     for (GoalPoseMap::iterator it = goal_poses_.begin(); it != goal_poses_.end(); ++it)
00440     {
00441       if (it->second->isVisible())
00442       {
00443         it->second->setAxisVisibility(ui_.show_x_checkbox->isChecked(), ui_.show_y_checkbox->isChecked(), ui_.show_z_checkbox->isChecked());
00444       }
00445     }
00446   }
00447 }
00448 
00449 void MainWindow::populateGoalPosesList(void)
00450 {
00451   ui_.goal_poses_list->clear();
00452   for (GoalPoseMap::iterator it = goal_poses_.begin(); it != goal_poses_.end(); ++it)
00453   {
00454     QListWidgetItem *item = new QListWidgetItem(QString(it->first.c_str()));
00455     ui_.goal_poses_list->addItem(item);
00456     if (! it->second->isVisible())
00457     {
00458       item->setBackground(QBrush(Qt::Dense4Pattern));
00459     }
00460     else if (it->second->isSelected())
00461     {
00462       //If selected, highlight in the list
00463       item->setSelected(true);
00464     }
00465   }
00466 }
00467 
00468 void MainWindow::switchGoalVisibilityButtonClicked(void)
00469 {
00470   QList<QListWidgetItem*> selection = ui_.goal_poses_list->selectedItems();
00471   for (std::size_t i = 0; i < selection.size() ; ++i)
00472   {
00473     std::string name = selection[i]->text().toStdString();
00474     if (goal_poses_[name]->isVisible())
00475     {
00476       //Set invisible
00477       goal_poses_[name]->hide();
00478       selection[i]->setBackground(QBrush(Qt::Dense4Pattern));
00479     }
00480     else
00481     {
00482       //Set visible
00483       goal_poses_[name]->show(scene_display_->getSceneNode(), visualization_manager_);
00484       selection[i]->setBackground(QBrush(Qt::NoBrush));
00485     }
00486   }
00487 }
00488 
00489 void MainWindow::goalPoseSelectionChanged(void)
00490 {
00491   for (unsigned int i = 0; i < ui_.goal_poses_list->count() ; ++i)
00492   {
00493     QListWidgetItem *item = ui_.goal_poses_list->item(i);
00494     std::string name = item->text().toStdString();
00495     if ( goal_poses_.find(name) != goal_poses_.end() &&
00496         ( (item->isSelected() && ! goal_poses_[name]->isSelected() )
00497             || ( ! item->isSelected() && goal_poses_[name]->isSelected() )))
00498       switchGoalPoseMarkerSelection(name);
00499   }
00500 }
00501 
00502 void MainWindow::goalPoseDoubleClicked(QListWidgetItem * item)
00503 {
00504   JobProcessing::addBackgroundJob(boost::bind(&MainWindow::computeGoalPoseDoubleClicked, this, item));
00505 }
00506 
00507 void MainWindow::computeGoalPoseDoubleClicked(QListWidgetItem * item)
00508 {
00509   if ( ! robot_interaction_ || robot_interaction_->getActiveEndEffectors().empty() )
00510     return;
00511 
00512   std::string item_text = item->text().toStdString();
00513 
00514   //Switch the marker color to processing color while processing
00515   JobProcessing::addMainLoopJob(boost::bind(&MainWindow::updateGoalMarkerStateFromName, this, item_text, GripperMarker::PROCESSING));
00516 
00517   checkIfGoalReachable(item_text, true);
00518 }
00519 
00520 /* Receives feedback from the interactive marker attached to a goal pose */
00521 void MainWindow::goalPoseFeedback(visualization_msgs::InteractiveMarkerFeedback &feedback)
00522 {
00523   static Eigen::Affine3d initial_pose_eigen;
00524 
00525   if (feedback.event_type == feedback.BUTTON_CLICK)
00526   {
00527     //Unselect all but the clicked one. Needs to be in order, first unselect, then select.
00528     QListWidgetItem *item = 0;
00529     for (unsigned int i = 0; i < ui_.goal_poses_list->count(); ++i)
00530     {
00531       item = ui_.goal_poses_list->item(i);
00532       if ( item->text().toStdString() != feedback.marker_name)
00533         JobProcessing::addMainLoopJob(boost::bind(&MainWindow::selectItemJob, this, item, false));
00534     }
00535 
00536     for (unsigned int i = 0; i < ui_.goal_poses_list->count(); ++i)
00537     {
00538       item = ui_.goal_poses_list->item(i);
00539       if (item->text().toStdString() == feedback.marker_name)
00540         JobProcessing::addMainLoopJob(boost::bind(&MainWindow::selectItemJob, this, item, true));
00541     }
00542   }
00543   else if (feedback.event_type == feedback.MOUSE_DOWN)
00544   {
00545     //Store current poses
00546     goals_dragging_initial_pose_.clear();
00547     for (GoalPoseMap::iterator it = goal_poses_.begin(); it != goal_poses_.end(); ++it)
00548     {
00549       if (it->second->isSelected() && it->second->isVisible())
00550       {
00551         Eigen::Affine3d pose(Eigen::Quaterniond(it->second->imarker->getOrientation().w, it->second->imarker->getOrientation().x,
00552                                                 it->second->imarker->getOrientation().y, it->second->imarker->getOrientation().z));
00553         pose(0,3) = it->second->imarker->getPosition().x;
00554         pose(1,3) = it->second->imarker->getPosition().y;
00555         pose(2,3) = it->second->imarker->getPosition().z;
00556         goals_dragging_initial_pose_.insert(std::pair<std::string, Eigen::Affine3d>(it->second->imarker->getName(), pose));
00557 
00558         if (it->second->imarker->getName() == feedback.marker_name)
00559           initial_pose_eigen = pose;
00560       }
00561     }
00562     goal_pose_dragging_=true;
00563   }
00564   else if (feedback.event_type == feedback.POSE_UPDATE && goal_pose_dragging_)
00565   {
00566     //Compute displacement from stored pose, and apply to the rest of selected markers
00567     Eigen::Affine3d current_pose_eigen;
00568     tf::poseMsgToEigen(feedback.pose, current_pose_eigen);
00569 
00570     Eigen::Affine3d current_wrt_initial = initial_pose_eigen.inverse() * current_pose_eigen;
00571 
00572     //Display the pose in the ui
00573     Eigen::Vector3d v = current_pose_eigen.linear().eulerAngles(0,1,2);
00574     setStatusFromBackground(STATUS_INFO, QString().sprintf(
00575         "%.2f %.2f %.2f    %.2f %.2f %.2f",
00576         current_pose_eigen(0,3),
00577         current_pose_eigen(1,3),
00578         current_pose_eigen(2,3),
00579         v(0) * 180.0 / boost::math::constants::pi<double>(),
00580         v(1) * 180.0 / boost::math::constants::pi<double>(),
00581         v(2) * 180.0 / boost::math::constants::pi<double>()));
00582 
00583     //Update the rest of selected markers
00584     for (GoalPoseMap::iterator it = goal_poses_.begin(); it != goal_poses_.end() ; ++it)
00585     {
00586       if (it->second->isVisible() && it->second->imarker->getName() != feedback.marker_name && it->second->isSelected())
00587       {
00588         visualization_msgs::InteractiveMarkerPose impose;
00589 
00590         Eigen::Affine3d newpose = initial_pose_eigen * current_wrt_initial * initial_pose_eigen.inverse() * goals_dragging_initial_pose_[it->second->imarker->getName()];
00591         tf::poseEigenToMsg(newpose, impose.pose);
00592 
00593         it->second->imarker->setPose(Ogre::Vector3(impose.pose.position.x, impose.pose.position.y, impose.pose.position.z),
00594                                       Ogre::Quaternion(impose.pose.orientation.w, impose.pose.orientation.x, impose.pose.orientation.y, impose.pose.orientation.z), "");
00595       }
00596     }
00597   } else if (feedback.event_type == feedback.MOUSE_UP)
00598   {
00599     goal_pose_dragging_ = false;
00600     JobProcessing::addBackgroundJob(boost::bind(&MainWindow::checkIfGoalInCollision, this, feedback.marker_name));
00601   }
00602 }
00603 
00604 void MainWindow::checkGoalsReachable(void)
00605 {
00606   for (GoalPoseMap::iterator it = goal_poses_.begin(); it != goal_poses_.end(); ++it)
00607     JobProcessing::addBackgroundJob(boost::bind(&MainWindow::checkIfGoalReachable, this, it->first, false));
00608 }
00609 
00610 void MainWindow::checkGoalsInCollision(void)
00611 {
00612   for (GoalPoseMap::iterator it = goal_poses_.begin(); it != goal_poses_.end(); ++it)
00613     JobProcessing::addBackgroundJob(boost::bind(&MainWindow::checkIfGoalInCollision, this, it->first));
00614 }
00615 
00616 void MainWindow::checkIfGoalReachable(const std::string &goal_name, bool update_if_reachable)
00617 {
00618   if ( goal_poses_.find(goal_name) == goal_poses_.end())
00619     return;
00620 
00621   if ( ! goal_poses_[goal_name]->isVisible())
00622     return;
00623 
00624   const boost::shared_ptr<rviz::InteractiveMarker> &imarker = goal_poses_[goal_name]->imarker;
00625 
00626   geometry_msgs::Pose current_pose_msg;
00627   current_pose_msg.position.x = imarker->getPosition().x;
00628   current_pose_msg.position.y = imarker->getPosition().y;
00629   current_pose_msg.position.z = imarker->getPosition().z;
00630   current_pose_msg.orientation.x = imarker->getOrientation().x;
00631   current_pose_msg.orientation.y = imarker->getOrientation().y;
00632   current_pose_msg.orientation.z = imarker->getOrientation().z;
00633   current_pose_msg.orientation.w = imarker->getOrientation().w;
00634 
00635   // Call to IK
00636   setStatusFromBackground(STATUS_INFO, "Computing inverse kinematics...");
00637   robot_state::RobotState ks(scene_display_->getPlanningSceneRO()->getCurrentState());
00638   static const int ik_attempts = 5;
00639   static const float ik_timeout = 0.2;
00640   bool feasible = robot_interaction_->updateState(ks,
00641                                                   robot_interaction_->getActiveEndEffectors()[0],
00642                                                   current_pose_msg, ik_attempts, ik_timeout);
00643   if (feasible)
00644   {
00645     setStatusFromBackground(STATUS_INFO, "Updating state...");
00646     if (update_if_reachable)
00647     {
00648       scene_display_->getPlanningSceneRW()->setCurrentState(ks);
00649       scene_display_->queueRenderSceneGeometry();
00650     }
00651 
00652     setStatusFromBackground(STATUS_INFO, "Updating marker...");
00653     //Switch the marker color to reachable
00654     JobProcessing::addMainLoopJob(boost::bind(&MainWindow::updateGoalMarkerStateFromName, this, goal_name,
00655                                                   GripperMarker::REACHABLE));
00656   }
00657   else
00658   {
00659     //Switch the marker color to not-reachable
00660     JobProcessing::addMainLoopJob(boost::bind(&MainWindow::updateGoalMarkerStateFromName, this, goal_name,
00661                                                   GripperMarker::NOT_REACHABLE));
00662   }
00663   setStatusFromBackground(STATUS_INFO, "");
00664 }
00665 
00666 void MainWindow::checkIfGoalInCollision(const std::string & goal_name)
00667 {
00668   if ( goal_poses_.find(goal_name) == goal_poses_.end())
00669     return;
00670 
00671   // Check if the end-effector is in collision at the current pose
00672   if ( ! goal_poses_[goal_name]->isVisible())
00673     return;
00674 
00675   const robot_interaction::RobotInteraction::EndEffector &eef = robot_interaction_->getActiveEndEffectors()[0];
00676 
00677   const boost::shared_ptr<rviz::InteractiveMarker> &im = goal_poses_[goal_name]->imarker;
00678   Eigen::Affine3d marker_pose_eigen;
00679   goal_poses_[goal_name]->getPose(marker_pose_eigen);
00680 
00681   robot_state::RobotState ks(scene_display_->getPlanningSceneRO()->getCurrentState());
00682   ks.updateStateWithLinkAt(eef.parent_link, marker_pose_eigen);
00683   bool in_collision = scene_display_->getPlanningSceneRO()->isStateColliding(ks, eef.eef_group);
00684 
00685   if ( in_collision )
00686   {
00687     JobProcessing::addMainLoopJob(boost::bind(&MainWindow::updateGoalMarkerStateFromName, this, goal_name,
00688                                                   GripperMarker::IN_COLLISION));
00689   }
00690   else
00691   {
00692     JobProcessing::addMainLoopJob(boost::bind(&MainWindow::updateGoalMarkerStateFromName, this, goal_name,
00693                                                       GripperMarker::NOT_TESTED));
00694   }
00695 }
00696 
00697 void MainWindow::switchGoalPoseMarkerSelection(const std::string &marker_name)
00698 {
00699   if (robot_interaction_->getActiveEndEffectors().empty() || ! goal_poses_[marker_name]->isVisible())
00700     return;
00701 
00702   if (goal_poses_[marker_name]->isSelected())
00703   {
00704     //If selected, unselect
00705     goal_poses_[marker_name]->unselect();
00706     setItemSelectionInList(marker_name, false, ui_.goal_poses_list);
00707   }
00708   else
00709   {
00710     //If unselected, select. Only display the gripper mesh for one
00711     if (ui_.goal_poses_list->selectedItems().size() == 1)
00712       goal_poses_[marker_name]->select(true);
00713     else
00714       goal_poses_[marker_name]->select(false);
00715     setItemSelectionInList(marker_name, true, ui_.goal_poses_list);
00716   }
00717 }
00718 
00719 void MainWindow::copySelectedGoalPoses(void)
00720 {
00721   QList<QListWidgetItem *> sel = ui_.goal_poses_list->selectedItems();
00722   if (sel.empty() || robot_interaction_->getActiveEndEffectors().empty())
00723     return;
00724 
00725   std::string scene_name;
00726   {
00727     const planning_scene_monitor::LockedPlanningSceneRO &ps = scene_display_->getPlanningSceneRO();
00728     if (!ps)
00729       return;
00730     else
00731       scene_name = ps->getName();
00732   }
00733 
00734   for (int i = 0 ; i < sel.size() ; ++i)
00735   {
00736     std::string name = sel[i]->text().toStdString();
00737     if (! goal_poses_[name]->isVisible())
00738       continue;
00739 
00740     std::stringstream ss;
00741     ss << scene_name.c_str() << "_pose_" << std::setfill('0') << std::setw(4) << goal_poses_.size();
00742 
00743     Eigen::Affine3d tip_pose = scene_display_->getPlanningSceneRO()->getCurrentState().getLinkState(robot_interaction_->getActiveEndEffectors()[0].parent_link)->getGlobalLinkTransform();
00744     geometry_msgs::Pose marker_pose;
00745     marker_pose.position.x = goal_poses_[name]->imarker->getPosition().x;
00746     marker_pose.position.y = goal_poses_[name]->imarker->getPosition().y;
00747     marker_pose.position.z = goal_poses_[name]->imarker->getPosition().z;
00748     marker_pose.orientation.x = goal_poses_[name]->imarker->getOrientation().x;
00749     marker_pose.orientation.y = goal_poses_[name]->imarker->getOrientation().y;
00750     marker_pose.orientation.z = goal_poses_[name]->imarker->getOrientation().z;
00751     marker_pose.orientation.w = goal_poses_[name]->imarker->getOrientation().w;
00752 
00753     static const float marker_scale = 0.15;
00754     GripperMarkerPtr goal_pose(new GripperMarker(scene_display_->getPlanningSceneRO()->getCurrentState(), scene_display_->getSceneNode(), visualization_manager_, ss.str(), scene_display_->getRobotModel()->getModelFrame(),
00755                             robot_interaction_->getActiveEndEffectors()[0], marker_pose, marker_scale, GripperMarker::NOT_TESTED, true));
00756 
00757     goal_poses_.insert(GoalPosePair(ss.str(), goal_pose));
00758 
00759     // Connect signals
00760     goal_pose->connect(this, SLOT( goalPoseFeedback(visualization_msgs::InteractiveMarkerFeedback &)));
00761 
00762     //Unselect the marker source of the copy
00763     switchGoalPoseMarkerSelection(name);
00764   }
00765 
00766   JobProcessing::addMainLoopJob(boost::bind(&MainWindow::populateGoalPosesList, this));
00767 }
00768 
00769 void MainWindow::saveStartStateButtonClicked(void)
00770 {
00771   bool ok = false;
00772 
00773   std::stringstream ss;
00774   ss << scene_display_->getRobotModel()->getName().c_str() << "_state_" << std::setfill('0') << std::setw(4) << start_states_.size();
00775 
00776   QString text = QInputDialog::getText(this, tr("Choose a name"),
00777                                        tr("Start state name:"), QLineEdit::Normal,
00778                                        QString(ss.str().c_str()), &ok);
00779 
00780   std::string name;
00781   if (ok)
00782   {
00783     if (!text.isEmpty())
00784     {
00785       name = text.toStdString();
00786       if (start_states_.find(name) != start_states_.end())
00787         QMessageBox::warning(this, "Name already exists", QString("The name '").append(name.c_str()).
00788                              append("' already exists. Not creating state."));
00789       else
00790       {
00791         //Store the current start state
00792         moveit_msgs::RobotState msg;
00793         robot_state::robotStateToRobotStateMsg(scene_display_->getPlanningSceneRO()->getCurrentState(), msg);
00794         start_states_.insert(StartStatePair(name, StartStatePtr(new StartState(msg))));
00795 
00796         //Save to the database if connected
00797         if (robot_state_storage_)
00798         {
00799           try
00800           {
00801               robot_state_storage_->addRobotState(msg, name);
00802           }
00803           catch (std::runtime_error &ex)
00804           {
00805             ROS_ERROR("Cannot save robot state on the database: %s", ex.what());
00806           }
00807         }
00808       }
00809     }
00810     else
00811       QMessageBox::warning(this, "Start state not saved", "Cannot use an empty name for a new start state.");
00812   }
00813   populateStartStatesList();
00814 }
00815 
00816 void MainWindow::removeSelectedStatesButtonClicked(void)
00817 {
00818   QList<QListWidgetItem*> found_items = ui_.start_states_list->selectedItems();
00819   for (unsigned int i = 0; i < found_items.size(); i++)
00820   {
00821     start_states_.erase(found_items[i]->text().toStdString());
00822   }
00823   populateStartStatesList();
00824 }
00825 
00826 void MainWindow::removeAllStatesButtonClicked(void)
00827 {
00828   start_states_.clear();
00829   populateStartStatesList();
00830 }
00831 
00832 void MainWindow::populateStartStatesList(void)
00833 {
00834   ui_.start_states_list->clear();
00835   for (StartStateMap::iterator it = start_states_.begin(); it != start_states_.end(); ++it)
00836   {
00837     QListWidgetItem *item = new QListWidgetItem(QString(it->first.c_str()));
00838     ui_.start_states_list->addItem(item);
00839     if (it->second->selected)
00840     {
00841       //If selected, highlight in the list
00842       item->setSelected(true);
00843     }
00844   }
00845 }
00846 
00847 void MainWindow::startStateItemDoubleClicked(QListWidgetItem * item)
00848 {
00849   scene_display_->getPlanningSceneRW()->setCurrentState(start_states_[item->text().toStdString()]->state_msg);
00850   scene_display_->queueRenderSceneGeometry();
00851 }
00852 
00853 void MainWindow::runBenchmark(void)
00854 {
00855   QDialog *dialog = new QDialog(0,0);
00856 
00857   run_benchmark_ui_.setupUi(dialog);
00858   run_benchmark_ui_.benchmark_select_folder_button->setIcon(QIcon::fromTheme("document-open", QApplication::style()->standardIcon(QStyle::SP_DirOpenIcon)));
00859   connect( run_benchmark_ui_.benchmark_button_box, SIGNAL( accepted( ) ), this, SLOT( runBenchmarkOKButtonClicked( ) ));
00860   connect( run_benchmark_ui_.benchmark_select_folder_button, SIGNAL( clicked( ) ), this, SLOT( benchmarkFolderButtonClicked( ) ));
00861 
00862   for (StartStateMap::iterator it = start_states_.begin(); it != start_states_.end(); ++it)
00863   {
00864     run_benchmark_ui_.benchmark_start_state_combo->addItem(it->first.c_str());
00865   }
00866 
00867   dialog->show();
00868 }
00869 
00870 void MainWindow::benchmarkFolderButtonClicked(void)
00871 {
00872   QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
00873                                                   "/home", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
00874   run_benchmark_ui_.benchmark_output_folder_text->setText(dir);
00875 }
00876 
00877 void MainWindow::runBenchmarkOKButtonClicked(void)
00878 {
00879   if (run_benchmark_ui_.benchmark_output_folder_text->text().isEmpty())
00880   {
00881     QMessageBox::warning(this, "Missing data", "Must specify an output folder");
00882     return;
00883   }
00884 
00885   QString outfilename =  run_benchmark_ui_.benchmark_output_folder_text->text().append("/config.cfg");
00886   std::ofstream outfile(outfilename.toUtf8());
00887   if (outfile)
00888   {
00889     outfile << "[scene]" << std::endl;
00890     outfile << "group=" << ui_.planning_group_combo->currentText().toStdString() << std::endl;
00891     outfile << "default_constrained_link=" << robot_interaction_->getActiveEndEffectors()[0].parent_link << std::endl;
00892     outfile << "planning_frame=" << scene_display_->getPlanningSceneMonitor()->getRobotModel()->getModelFrame() << std::endl;
00893     outfile << "name=" << scene_display_->getPlanningSceneRO()->getName() << std::endl;
00894 
00895     //TODO: Let the user select the timeout, runs, and start regex
00896     outfile << "timeout=1" << std::endl;
00897     outfile << "runs=1" << std::endl;
00898     outfile << "output=" << scene_display_->getPlanningSceneMonitor()->getRobotModel()->getName() << "_" <<
00899                         scene_display_->getPlanningSceneRO()->getName() << "_" <<
00900                         ros::Time::now() << std::endl;
00901     outfile << "start=" << run_benchmark_ui_.benchmark_output_folder_text->text().toStdString() << std::endl;
00902     outfile << "query=" << std::endl;
00903     outfile << "goal=" << ui_.load_poses_filter_text->text().toStdString() << std::endl;
00904     outfile << "goal_offset_roll=" << ui_.goal_offset_roll->value() << std::endl;
00905     outfile << "goal_offset_pitch=" << ui_.goal_offset_pitch->value() << std::endl;
00906     outfile << "goal_offset_yaw=" << ui_.goal_offset_yaw->value() << std::endl << std::endl;
00907 
00908     //TODO: Let the user select the planners
00909     outfile << "[plugin]" << std::endl;
00910     outfile << "name=ompl_interface_ros/OMPLPlanner" << std::endl;
00911     outfile << "planners=RRTConnectkConfigDefault" << std::endl;
00912 
00913     outfile.close();
00914   }
00915   else
00916   {
00917     QMessageBox::warning(this, "Error", QString("Cannot open file ").append(outfilename).append(" for writing"));
00918   }
00919 }
00920 
00921 void MainWindow::loadBenchmarkResults(void)
00922 {
00923   //Select a log file of the set.
00924   QString path = QFileDialog::getOpenFileName(this, tr("Select a log file in the set"), tr(""), tr("Log files (*.log)"));
00925   if (!path.isEmpty())
00926   {
00927     JobProcessing::addBackgroundJob(boost::bind(&MainWindow::computeLoadBenchmarkResults, this, path.toStdString()));
00928   }
00929 }
00930 
00931 void MainWindow::computeLoadBenchmarkResults(const std::string &file)
00932 {
00933   std::string logid, basename, logid_text;
00934   try
00935   {
00936     logid = file.substr( file.find_last_of(".", file.length()-5) + 1, file.find_last_of(".") - file.find_last_of(".", file.length()-5) - 1 );
00937     basename = file.substr(0, file.find_last_of(".", file.length()-5));
00938     logid_text = logid.substr(logid.find_first_of("_"), logid.length() - logid.find_first_of("_"));
00939   }
00940   catch (...)
00941   {
00942     ROS_ERROR("Invalid benchmark log file. Cannot load results.");
00943     return;
00944   }
00945 
00946   int count = 1;
00947   bool more_files = true;
00948   //Parse all the log files in the set
00949   while (more_files)
00950   {
00951     std::ifstream ifile;
00952     std::stringstream file_to_load;
00953     file_to_load << basename << "." << count << logid_text << ".log";
00954 
00955     ifile.open(file_to_load.str().c_str());
00956     if (ifile.good())
00957     {
00958       //Parse results
00959       char text_line[512];
00960       static std::streamsize text_line_length = 512;
00961       bool valid_file = false;
00962       ifile.getline(text_line, text_line_length);
00963 
00964       //Check if the file is valid. The first line must contain "Experiment <scene>". There must be a "total_time" line, followed by the results
00965       if (ifile.good() && strstr(text_line, "Experiment") != NULL && strlen(text_line) > 11
00966           && strncmp(&text_line[11], scene_display_->getPlanningSceneRO()->getName().c_str(), scene_display_->getPlanningSceneRO()->getName().length()) == 0)
00967       {
00968         while (ifile.good())
00969         {
00970           if ( strstr(text_line, "total_time REAL") != NULL)
00971           {
00972             valid_file = true;
00973             break;
00974           }
00975 
00976           ifile.getline(text_line, text_line_length);
00977         }
00978       }
00979       else
00980       {
00981         ROS_ERROR("Not a valid log file, or a different planning scene loaded");
00982       }
00983 
00984       if (valid_file)
00985       {
00986         if (basename.find(".trajectory") != std::string::npos)
00987         {
00988           //Trajectory results may contain multiple goals
00989           int nwaypoint = 0;
00990           while (ifile.getline(text_line, text_line_length) && ifile.good() && strlen(text_line) > 6)
00991           {
00992             //This should be a reachability results line composed of reachable, collision-free and time fields (bool; bool; float)
00993             try
00994             {
00995               bool reachable = boost::lexical_cast<bool>(text_line[0]);
00996               bool collision_free = boost::lexical_cast<bool>(text_line[3]);
00997 
00998 
00999               //Update colors accordingly
01000               if (count <= trajectories_.size())
01001               {
01002                 std::string trajectory_name = ui_.trajectory_list->item(count-1)->text().toStdString();
01003                 if ( nwaypoint < trajectories_[trajectory_name]->waypoint_markers.size() )
01004                 {
01005                   if (reachable)
01006                   {
01007                     if (collision_free)
01008                     {
01009                       //Reachable and collision-free
01010                       JobProcessing::addMainLoopJob(boost::bind(&MainWindow::updateMarkerState, this, trajectories_[trajectory_name]->waypoint_markers[nwaypoint],
01011                                                                 GripperMarker::REACHABLE));
01012                     }
01013                     else
01014                     {
01015                       //Reachable, but in collision
01016                       JobProcessing::addMainLoopJob(boost::bind(&MainWindow::updateMarkerState, this, trajectories_[trajectory_name]->waypoint_markers[nwaypoint],
01017                                                                 GripperMarker::IN_COLLISION));
01018                     }
01019                   }
01020                   else
01021                   {
01022                     //Not reachable
01023                     JobProcessing::addMainLoopJob(boost::bind(&MainWindow::updateMarkerState, this, trajectories_[trajectory_name]->waypoint_markers[nwaypoint],
01024                                                               GripperMarker::NOT_REACHABLE));
01025                   }
01026                 }
01027               }
01028               nwaypoint++;
01029             }
01030             catch (...)
01031             {
01032               ROS_ERROR("Error parsing the log file");
01033             }
01034           }
01035         }
01036         else
01037         {
01038           ifile.getline(text_line, text_line_length);
01039           if (ifile.good() && strlen(text_line) > 6)
01040           {
01041             //This should be the reachability results line composed of reachable, collision-free and time fields (bool; bool; float)
01042             try
01043             {
01044               bool reachable = boost::lexical_cast<bool>(text_line[0]);
01045               bool collision_free = boost::lexical_cast<bool>(text_line[3]);
01046 
01047               //Update colors accordingly
01048               if (count <= goal_poses_.size())
01049               {
01050                 std::string goal_name = ui_.goal_poses_list->item(count-1)->text().toStdString();
01051                 if (reachable)
01052                 {
01053                   if (collision_free)
01054                   {
01055                     //Reachable and collision-free
01056                     JobProcessing::addMainLoopJob(boost::bind(&MainWindow::updateGoalMarkerStateFromName, this, goal_name,
01057                                                               GripperMarker::REACHABLE));
01058                   }
01059                   else
01060                   {
01061                     //Reachable, but in collision
01062                     JobProcessing::addMainLoopJob(boost::bind(&MainWindow::updateGoalMarkerStateFromName, this, goal_name,
01063                                                               GripperMarker::IN_COLLISION));
01064                   }
01065                 }
01066                 else
01067                 {
01068                   //Not reachable
01069                   JobProcessing::addMainLoopJob(boost::bind(&MainWindow::updateGoalMarkerStateFromName, this, goal_name,
01070                                                             GripperMarker::NOT_REACHABLE));
01071                 }
01072               }
01073             }
01074             catch (...)
01075             {
01076               ROS_ERROR("Error parsing the log file");
01077             }
01078           }
01079         }
01080       }
01081       else
01082       {
01083         ROS_ERROR("Invalid benchmark log file. Cannot load results.");
01084       }
01085 
01086       ifile.close();
01087     }
01088     else
01089     {
01090       more_files = false;
01091     }
01092     count++;
01093   }
01094 }
01095 
01096 void MainWindow::updateGoalMarkerStateFromName(const std::string &name, const GripperMarker::GripperMarkerState &state)
01097 {
01098   if ( goal_poses_.find(name) != goal_poses_.end())
01099     goal_poses_[name]->setState(state);
01100 }
01101 
01102 void MainWindow::updateMarkerState(GripperMarkerPtr marker, const GripperMarker::GripperMarkerState &state)
01103 {
01104   marker->setState(state);
01105 }
01106 
01107 void MainWindow::goalOffsetChanged()
01108 {
01109   goal_offset_.setIdentity();
01110   goal_offset_ = Eigen::AngleAxisd(ui_.goal_offset_roll->value() * boost::math::constants::pi<double>() / 180.0, Eigen::Vector3d::UnitX()) *
01111       Eigen::AngleAxisd(ui_.goal_offset_pitch->value() * boost::math::constants::pi<double>() / 180.0, Eigen::Vector3d::UnitY()) *
01112       Eigen::AngleAxisd(ui_.goal_offset_yaw->value() * boost::math::constants::pi<double>() / 180.0, Eigen::Vector3d::UnitZ());
01113 
01114   if ( ! robot_interaction_->getActiveEndEffectors().empty() )
01115   {
01116     Eigen::Affine3d current_pose;
01117     for (GoalPoseMap::iterator it = goal_poses_.begin(); it != goal_poses_.end(); ++it)
01118     {
01119       current_pose = goals_initial_pose_[it->first] * goal_offset_;
01120       it->second->setPose(current_pose);
01121     }
01122   }
01123 }
01124 
01125 } //namespace


benchmarks_gui
Author(s): Mario Prats
autogenerated on Mon Oct 6 2014 02:34:25