$search
00001 /* 00002 * Copyright (c) 2011, 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 00030 #include <boost/make_shared.hpp> 00031 00032 #include <QMenu> 00033 00034 #include <OGRE/OgreSceneNode.h> 00035 #include <OGRE/OgreSceneManager.h> 00036 #include <OGRE/OgreMaterialManager.h> 00037 #include <OGRE/OgreResourceGroupManager.h> 00038 #include <OGRE/OgreSubEntity.h> 00039 #include <OGRE/OgreMath.h> 00040 00041 #include <ros/ros.h> 00042 #include <interactive_markers/tools.h> 00043 00044 #include "rviz/frame_manager.h" 00045 #include "rviz/visualization_manager.h" 00046 #include "rviz/selection/selection_manager.h" 00047 #include "rviz/frame_manager.h" 00048 #include "rviz/default_plugin/interactive_marker_display.h" 00049 #include "rviz/render_panel.h" 00050 00051 #include "interactive_markers/integer_action.h" 00052 #include "interactive_marker.h" 00053 00054 namespace rviz 00055 { 00056 00057 InteractiveMarker::InteractiveMarker( InteractiveMarkerDisplay *owner, VisualizationManager *vis_manager, std::string topic_ns, std::string client_id ) : 00058 owner_(owner) 00059 , vis_manager_(vis_manager) 00060 , pose_changed_(false) 00061 , time_since_last_feedback_(0) 00062 , dragging_(false) 00063 , pose_update_requested_(false) 00064 , heart_beat_t_(0) 00065 , topic_ns_(topic_ns) 00066 , client_id_(client_id) 00067 { 00068 ros::NodeHandle nh; 00069 std::string feedback_topic = topic_ns+"/feedback"; 00070 feedback_pub_ = nh.advertise<visualization_msgs::InteractiveMarkerFeedback>( feedback_topic, 100, false ); 00071 00072 reference_node_ = vis_manager->getSceneManager()->getRootSceneNode()->createChildSceneNode(); 00073 00074 axes_node_ = reference_node_->createChildSceneNode(); 00075 axes_ = new ogre_tools::Axes( vis_manager->getSceneManager(), axes_node_, 1, 0.05 ); 00076 } 00077 00078 InteractiveMarker::~InteractiveMarker() 00079 { 00080 delete axes_; 00081 vis_manager_->getSceneManager()->destroySceneNode( axes_node_ ); 00082 vis_manager_->getSceneManager()->destroySceneNode( reference_node_ ); 00083 } 00084 00085 void InteractiveMarker::reset() 00086 { 00087 boost::recursive_mutex::scoped_lock lock(mutex_); 00088 controls_.clear(); 00089 menu_entries_.clear(); 00090 } 00091 00092 void InteractiveMarker::processMessage( visualization_msgs::InteractiveMarkerPoseConstPtr message ) 00093 { 00094 boost::recursive_mutex::scoped_lock lock(mutex_); 00095 Ogre::Vector3 position( message->pose.position.x, message->pose.position.y, message->pose.position.z ); 00096 Ogre::Quaternion orientation( message->pose.orientation.w, message->pose.orientation.x, 00097 message->pose.orientation.y, message->pose.orientation.z ); 00098 00099 if ( orientation.w == 0 && orientation.x == 0 && orientation.y == 0 && orientation.z == 0 ) 00100 { 00101 orientation.w = 1; 00102 } 00103 00104 reference_time_ = message->header.stamp; 00105 reference_frame_ = message->header.frame_id; 00106 frame_locked_ = (message->header.stamp == ros::Time(0)); 00107 00108 requestPoseUpdate( position, orientation ); 00109 vis_manager_->queueRender(); 00110 } 00111 00112 bool InteractiveMarker::processMessage( visualization_msgs::InteractiveMarkerConstPtr message ) 00113 { 00114 boost::recursive_mutex::scoped_lock lock(mutex_); 00115 reset(); 00116 00117 visualization_msgs::InteractiveMarker auto_message = *message; 00118 interactive_markers::autoComplete( auto_message ); 00119 00120 // copy values 00121 00122 name_ = auto_message.name; 00123 description_ = auto_message.description; 00124 00125 if ( auto_message.controls.size() == 0 ) 00126 { 00127 owner_->setStatus( status_levels::Ok, name_, "Marker empty."); 00128 return false; 00129 } 00130 00131 scale_ = auto_message.scale; 00132 00133 reference_frame_ = auto_message.header.frame_id; 00134 reference_time_ = auto_message.header.stamp; 00135 frame_locked_ = (auto_message.header.stamp == ros::Time(0)); 00136 00137 position_ = Ogre::Vector3( 00138 auto_message.pose.position.x, 00139 auto_message.pose.position.y, 00140 auto_message.pose.position.z ); 00141 00142 orientation_ = Ogre::Quaternion( 00143 auto_message.pose.orientation.w, 00144 auto_message.pose.orientation.x, 00145 auto_message.pose.orientation.y, 00146 auto_message.pose.orientation.z ); 00147 00148 pose_changed_ =false; 00149 time_since_last_feedback_ = 0; 00150 00151 // setup axes 00152 axes_->setPosition(position_); 00153 axes_->setOrientation(orientation_); 00154 axes_->set( scale_, scale_*0.05 ); 00155 00156 updateReferencePose(); 00157 00158 for ( unsigned i=0; i<auto_message.controls.size(); i++ ) 00159 { 00160 controls_.push_back( boost::make_shared<InteractiveMarkerControl>( 00161 vis_manager_, auto_message.controls[i], reference_node_, this ) ); 00162 } 00163 00164 description_control_ = boost::make_shared<InteractiveMarkerControl>( 00165 vis_manager_, interactive_markers::makeTitle( auto_message ), reference_node_, this ); 00166 controls_.push_back( description_control_ ); 00167 00168 00169 //create menu 00170 if ( message->menu_entries.size() > 0 ) 00171 { 00172 menu_.reset( new QMenu() ); 00173 menu_entries_.clear(); 00174 top_level_menu_ids_.clear(); 00175 00176 // Put all menu entries into the menu_entries_ map and create the 00177 // tree of menu entry ids. 00178 for ( unsigned m=0; m < message->menu_entries.size(); m++ ) 00179 { 00180 const visualization_msgs::MenuEntry& entry = message->menu_entries[ m ]; 00181 MenuNode node; 00182 node.entry = entry; 00183 menu_entries_[ entry.id ] = node; 00184 if( entry.parent_id == 0 ) 00185 { 00186 top_level_menu_ids_.push_back( entry.id ); 00187 } 00188 else 00189 { 00190 // Find the parent node and add this entry to the parent's list of children. 00191 std::map< uint32_t, MenuNode >::iterator parent_it = menu_entries_.find( entry.parent_id ); 00192 if( parent_it == menu_entries_.end() ) { 00193 ROS_ERROR("interactive marker menu entry %u found before its parent id %u. Ignoring.", entry.id, entry.parent_id); 00194 } 00195 else 00196 { 00197 (*parent_it).second.child_ids.push_back( entry.id ); 00198 } 00199 } 00200 } 00201 00202 populateMenu( menu_.get(), top_level_menu_ids_ ); 00203 } 00204 00205 owner_->setStatus( status_levels::Ok, name_, "OK"); 00206 return true; 00207 } 00208 00209 // Recursively append menu and submenu entries to menu, based on a 00210 // vector of menu entry id numbers describing the menu entries at the 00211 // current level. 00212 void InteractiveMarker::populateMenu( QMenu* menu, std::vector<uint32_t>& ids ) 00213 { 00214 for( size_t id_index = 0; id_index < ids.size(); id_index++ ) 00215 { 00216 uint32_t id = ids[ id_index ]; 00217 std::map< uint32_t, MenuNode >::iterator node_it = menu_entries_.find( id ); 00218 ROS_ASSERT_MSG(node_it != menu_entries_.end(), "interactive marker menu entry %u not found during populateMenu().", id); 00219 MenuNode node = (*node_it).second; 00220 00221 if ( node.child_ids.empty() ) 00222 { 00223 IntegerAction* action = new IntegerAction( makeMenuString( node.entry.title ), 00224 menu, 00225 (int) node.entry.id ); 00226 connect( action, SIGNAL( triggered( int )), this, SLOT( handleMenuSelect( int ))); 00227 menu->addAction( action ); 00228 } 00229 else 00230 { 00231 // make sub-menu 00232 QMenu* sub_menu = menu->addMenu( makeMenuString( node.entry.title )); 00233 populateMenu( sub_menu, node.child_ids ); 00234 } 00235 } 00236 } 00237 00238 QString InteractiveMarker::makeMenuString( const std::string &entry ) 00239 { 00240 QString menu_entry; 00241 if ( entry.find( "[x]" ) == 0 ) 00242 { 00243 menu_entry = QChar( 0x2611 ) + QString::fromStdString( entry.substr( 3 ) ); 00244 } 00245 else if ( entry.find( "[ ]" ) == 0 ) 00246 { 00247 menu_entry = QChar( 0x2610 ) + QString::fromStdString( entry.substr( 3 ) ); 00248 } 00249 else 00250 { 00251 menu_entry = QChar( 0x3000 ) + QString::fromStdString( entry ); 00252 } 00253 return menu_entry; 00254 } 00255 00256 void InteractiveMarker::updateReferencePose() 00257 { 00258 boost::recursive_mutex::scoped_lock lock(mutex_); 00259 Ogre::Vector3 reference_position; 00260 Ogre::Quaternion reference_orientation; 00261 00262 // if we're frame-locked, we need to find out what the most recent transformation time 00263 // actually is so we send back correct feedback 00264 if ( frame_locked_ ) 00265 { 00266 std::string fixed_frame = FrameManager::instance()->getFixedFrame(); 00267 if ( reference_frame_ == fixed_frame ) 00268 { 00269 // if the two frames are identical, we don't need to do anything. 00270 reference_time_ = ros::Time::now(); 00271 } 00272 else 00273 { 00274 std::string error; 00275 int retval = FrameManager::instance()->getTFClient()->getLatestCommonTime( 00276 reference_frame_, fixed_frame, reference_time_, &error ); 00277 if ( retval != tf::NO_ERROR ) 00278 { 00279 std::ostringstream s; 00280 s <<"Error getting time of latest transform between " << reference_frame_ 00281 << " and " << fixed_frame << ": " << error << " (error code: " << retval << ")"; 00282 owner_->setStatus( status_levels::Error, name_, s.str() ); 00283 reference_node_->setVisible( false ); 00284 return; 00285 } 00286 } 00287 } 00288 00289 if (!FrameManager::instance()->getTransform( reference_frame_, reference_time_, 00290 reference_position, reference_orientation )) 00291 { 00292 std::string error; 00293 FrameManager::instance()->transformHasProblems(reference_frame_, reference_time_, error); 00294 owner_->setStatus( status_levels::Error, name_, error); 00295 reference_node_->setVisible( false ); 00296 return; 00297 } 00298 00299 reference_node_->setPosition( reference_position ); 00300 reference_node_->setOrientation( reference_orientation ); 00301 reference_node_->setVisible( true, false ); 00302 00303 vis_manager_->queueRender(); 00304 } 00305 00306 void InteractiveMarker::update(float wall_dt) 00307 { 00308 boost::recursive_mutex::scoped_lock lock(mutex_); 00309 time_since_last_feedback_ += wall_dt; 00310 if ( frame_locked_ ) 00311 { 00312 updateReferencePose(); 00313 } 00314 00315 std::list<InteractiveMarkerControlPtr>::iterator it; 00316 for ( it = controls_.begin(); it != controls_.end(); it++ ) 00317 { 00318 (*it)->update(); 00319 } 00320 00321 if ( dragging_ ) 00322 { 00323 if ( pose_changed_ ) 00324 { 00325 publishPose(); 00326 } 00327 else if ( time_since_last_feedback_ > 0.25 ) 00328 { 00329 //send keep-alive so we don't use control over the marker 00330 visualization_msgs::InteractiveMarkerFeedback feedback; 00331 feedback.event_type = visualization_msgs::InteractiveMarkerFeedback::KEEP_ALIVE; 00332 publishFeedback( feedback ); 00333 } 00334 } 00335 } 00336 00337 void InteractiveMarker::publishPose() 00338 { 00339 boost::recursive_mutex::scoped_lock lock(mutex_); 00340 visualization_msgs::InteractiveMarkerFeedback feedback; 00341 feedback.event_type = visualization_msgs::InteractiveMarkerFeedback::POSE_UPDATE; 00342 feedback.control_name = last_control_name_; 00343 publishFeedback( feedback ); 00344 pose_changed_ = false; 00345 } 00346 00347 void InteractiveMarker::requestPoseUpdate( Ogre::Vector3 position, Ogre::Quaternion orientation ) 00348 { 00349 boost::recursive_mutex::scoped_lock lock(mutex_); 00350 if ( dragging_ ) 00351 { 00352 pose_update_requested_ = true; 00353 requested_position_ = position; 00354 requested_orientation_ = orientation; 00355 } 00356 else 00357 { 00358 updateReferencePose(); 00359 setPose( position, orientation, "" ); 00360 } 00361 } 00362 00363 void InteractiveMarker::setPose( Ogre::Vector3 position, Ogre::Quaternion orientation, const std::string &control_name ) 00364 { 00365 boost::recursive_mutex::scoped_lock lock(mutex_); 00366 position_ = position; 00367 orientation_ = orientation; 00368 pose_changed_ = true; 00369 last_control_name_ = control_name; 00370 00371 axes_->setPosition(position_); 00372 axes_->setOrientation(orientation_); 00373 00374 std::list<InteractiveMarkerControlPtr>::iterator it; 00375 for ( it = controls_.begin(); it != controls_.end(); it++ ) 00376 { 00377 (*it)->interactiveMarkerPoseChanged( position_, orientation_ ); 00378 } 00379 } 00380 00381 void InteractiveMarker::setShowDescription( bool show ) 00382 { 00383 boost::recursive_mutex::scoped_lock lock(mutex_); 00384 if ( description_control_.get() ) 00385 { 00386 description_control_->setVisible( show ); 00387 } 00388 } 00389 00390 void InteractiveMarker::setShowAxes( bool show ) 00391 { 00392 boost::recursive_mutex::scoped_lock lock(mutex_); 00393 axes_node_->setVisible( show ); 00394 } 00395 00396 void InteractiveMarker::translate( Ogre::Vector3 delta_position, const std::string &control_name ) 00397 { 00398 boost::recursive_mutex::scoped_lock lock(mutex_); 00399 setPose( position_+delta_position, orientation_, control_name ); 00400 } 00401 00402 void InteractiveMarker::rotate( Ogre::Quaternion delta_orientation, const std::string &control_name ) 00403 { 00404 boost::recursive_mutex::scoped_lock lock(mutex_); 00405 setPose( position_, delta_orientation * orientation_, control_name ); 00406 } 00407 00408 void InteractiveMarker::startDragging() 00409 { 00410 boost::recursive_mutex::scoped_lock lock(mutex_); 00411 dragging_ = true; 00412 pose_changed_ = false; 00413 } 00414 00415 void InteractiveMarker::stopDragging() 00416 { 00417 boost::recursive_mutex::scoped_lock lock(mutex_); 00418 if ( pose_changed_ ) 00419 { 00420 publishPose(); 00421 } 00422 dragging_ = false; 00423 if ( pose_update_requested_ ) 00424 { 00425 updateReferencePose(); 00426 setPose( requested_position_, requested_orientation_, "" ); 00427 pose_update_requested_ = false; 00428 } 00429 } 00430 00431 bool InteractiveMarker::handleMouseEvent(ViewportMouseEvent& event, const std::string &control_name) 00432 { 00433 boost::recursive_mutex::scoped_lock lock(mutex_); 00434 00435 if( event.acting_button == Qt::LeftButton ) 00436 { 00437 Ogre::Vector3 point_rel_world; 00438 bool got_3D_point = 00439 vis_manager_->getSelectionManager()->get3DPoint( event.viewport, event.x, event.y, point_rel_world ); 00440 00441 visualization_msgs::InteractiveMarkerFeedback feedback; 00442 feedback.event_type = (event.type == QEvent::MouseButtonPress ? 00443 (uint8_t)visualization_msgs::InteractiveMarkerFeedback::MOUSE_DOWN : 00444 (uint8_t)visualization_msgs::InteractiveMarkerFeedback::MOUSE_UP); 00445 00446 feedback.control_name = control_name; 00447 feedback.marker_name = name_; 00448 publishFeedback( feedback, got_3D_point, point_rel_world ); 00449 } 00450 00451 if( menu_.get() ) 00452 { 00453 // Event.right() will be false during a right-button-up event. We 00454 // want to swallow (with the "return true") all other 00455 // right-button-related mouse events. 00456 if( event.right() ) 00457 { 00458 return true; 00459 } 00460 if( event.rightUp() ) 00461 { 00462 // Save the 3D mouse point to send with the menu feedback, if any. 00463 got_3d_point_for_menu_ = 00464 vis_manager_->getSelectionManager()->get3DPoint( event.viewport, event.x, event.y, three_d_point_for_menu_ ); 00465 00466 event.panel->showContextMenu( menu_ ); 00467 00468 last_control_name_ = control_name; 00469 return true; 00470 } 00471 } 00472 00473 return false; 00474 } 00475 00476 00477 void InteractiveMarker::handleMenuSelect( int menu_item_id ) 00478 { 00479 boost::recursive_mutex::scoped_lock lock(mutex_); 00480 00481 std::map< uint32_t, MenuNode >::iterator it = menu_entries_.find( menu_item_id ); 00482 00483 if ( it != menu_entries_.end() ) 00484 { 00485 visualization_msgs::MenuEntry& entry = it->second.entry; 00486 00487 std::string command = entry.command; 00488 uint8_t command_type = entry.command_type; 00489 00490 if ( command_type == visualization_msgs::MenuEntry::FEEDBACK ) 00491 { 00492 visualization_msgs::InteractiveMarkerFeedback feedback; 00493 feedback.event_type = visualization_msgs::InteractiveMarkerFeedback::MENU_SELECT; 00494 feedback.menu_entry_id = entry.id; 00495 feedback.control_name = last_control_name_; 00496 publishFeedback( feedback, got_3d_point_for_menu_, three_d_point_for_menu_ ); 00497 } 00498 else if ( command_type == visualization_msgs::MenuEntry::ROSRUN ) 00499 { 00500 std::string sys_cmd = "rosrun " + command; 00501 ROS_INFO_STREAM( "Running system command: " << sys_cmd ); 00502 sys_thread_ = boost::shared_ptr<boost::thread>( new boost::thread( boost::bind( &system, sys_cmd.c_str() ) ) ); 00503 //system( sys_cmd.c_str() ); 00504 } 00505 else if ( command_type == visualization_msgs::MenuEntry::ROSLAUNCH ) 00506 { 00507 std::string sys_cmd = "roslaunch " + command; 00508 ROS_INFO_STREAM( "Running system command: " << sys_cmd ); 00509 sys_thread_ = boost::shared_ptr<boost::thread>( new boost::thread( boost::bind( &system, sys_cmd.c_str() ) ) ); 00510 //system( sys_cmd.c_str() ); 00511 } 00512 } 00513 } 00514 00515 00516 void InteractiveMarker::publishFeedback(visualization_msgs::InteractiveMarkerFeedback &feedback, 00517 bool mouse_point_valid, 00518 const Ogre::Vector3& mouse_point_rel_world ) 00519 { 00520 boost::recursive_mutex::scoped_lock lock(mutex_); 00521 00522 feedback.client_id = client_id_; 00523 feedback.marker_name = name_; 00524 00525 if ( frame_locked_ ) 00526 { 00527 feedback.header.frame_id = reference_frame_; 00528 feedback.header.stamp = reference_time_; 00529 feedback.pose.position.x = position_.x; 00530 feedback.pose.position.y = position_.y; 00531 feedback.pose.position.z = position_.z; 00532 feedback.pose.orientation.x = orientation_.x; 00533 feedback.pose.orientation.y = orientation_.y; 00534 feedback.pose.orientation.z = orientation_.z; 00535 feedback.pose.orientation.w = orientation_.w; 00536 00537 feedback.mouse_point_valid = mouse_point_valid; 00538 if( mouse_point_valid ) 00539 { 00540 Ogre::Vector3 mouse_rel_reference = reference_node_->convertWorldToLocalPosition( mouse_point_rel_world ); 00541 feedback.mouse_point.x = mouse_rel_reference.x; 00542 feedback.mouse_point.y = mouse_rel_reference.y; 00543 feedback.mouse_point.z = mouse_rel_reference.z; 00544 } 00545 } 00546 else 00547 { 00548 feedback.header.frame_id = vis_manager_->getFixedFrame(); 00549 feedback.header.stamp = ros::Time::now(); 00550 00551 Ogre::Vector3 world_position = reference_node_->convertLocalToWorldPosition( position_ ); 00552 Ogre::Quaternion world_orientation = reference_node_->convertLocalToWorldOrientation( orientation_ ); 00553 00554 feedback.pose.position.x = world_position.x; 00555 feedback.pose.position.y = world_position.y; 00556 feedback.pose.position.z = world_position.z; 00557 feedback.pose.orientation.x = world_orientation.x; 00558 feedback.pose.orientation.y = world_orientation.y; 00559 feedback.pose.orientation.z = world_orientation.z; 00560 feedback.pose.orientation.w = world_orientation.w; 00561 00562 feedback.mouse_point_valid = mouse_point_valid; 00563 feedback.mouse_point.x = mouse_point_rel_world.x; 00564 feedback.mouse_point.y = mouse_point_rel_world.y; 00565 feedback.mouse_point.z = mouse_point_rel_world.z; 00566 } 00567 00568 feedback_pub_.publish( feedback ); 00569 00570 time_since_last_feedback_ = 0; 00571 } 00572 00573 } // end namespace rviz