$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 "interactive_marker_client.h" 00031 00032 namespace rviz 00033 { 00034 00035 InteractiveMarkerClient::PublisherContext::PublisherContext() 00036 { 00037 update_seen = false; 00038 init_seen = false; 00039 last_update_seq_num = 0; 00040 last_init_seq_num = 0; 00041 update_time_ok = true; 00042 initialized = false; 00043 last_update_time = ros::Time::now(); 00044 } 00045 00046 // Update the value of initialized based on previous sequence 00047 // numbers and the number from this new update. 00048 bool InteractiveMarkerClient::PublisherContext::checkInitWith(const visualization_msgs::InteractiveMarkerUpdate::ConstPtr& update) 00049 { 00050 if( (init_seen && 00051 update->type == visualization_msgs::InteractiveMarkerUpdate::UPDATE && 00052 last_init_seq_num + 1 >= update->seq_num) 00053 || 00054 (init_seen && 00055 update->type == visualization_msgs::InteractiveMarkerUpdate::KEEP_ALIVE && 00056 last_init_seq_num >= update->seq_num) ) 00057 { 00058 initialized = true; 00059 } 00060 return initialized; 00061 } 00062 00063 // Update the value of initialized based on previous sequence 00064 // numbers and the number from this init message. 00065 bool InteractiveMarkerClient::PublisherContext::checkInitWith(const visualization_msgs::InteractiveMarkerInit::ConstPtr& init) 00066 { 00067 M_InteractiveMarkerUpdate::const_iterator q_it_same_seq = update_queue.find( init->seq_num ); 00068 M_InteractiveMarkerUpdate::const_iterator q_it_seq_plus_one = update_queue.find( init->seq_num + 1 ); 00069 00070 if( (update_seen && 00071 init->seq_num + 1 >= last_update_seq_num + 1) || 00072 (q_it_seq_plus_one != update_queue.end() && q_it_seq_plus_one->second->type == visualization_msgs::InteractiveMarkerUpdate::UPDATE) || 00073 (q_it_same_seq != update_queue.end() && q_it_same_seq->second->type == visualization_msgs::InteractiveMarkerUpdate::KEEP_ALIVE) ) 00074 { 00075 initialized = true; 00076 } 00077 return initialized; 00078 } 00079 00080 void InteractiveMarkerClient::PublisherContext::enqueueUpdate(const visualization_msgs::InteractiveMarkerUpdate::ConstPtr& update) 00081 { 00082 if( update->type == visualization_msgs::InteractiveMarkerUpdate::UPDATE ) 00083 { 00084 update_queue[ update->seq_num ] = update; 00085 } 00086 else // else this must be a KEEP_ALIVE type. 00087 { 00088 // Keep-alive messages get queued only if there is not an update 00089 // queued at that sequence number already. 00090 if( update_queue.find( update->seq_num ) == update_queue.end() ) 00091 { 00092 update_queue[ update->seq_num ] = update; 00093 } 00094 } 00095 update_seen = true; 00096 last_update_seq_num = update->seq_num; 00097 } 00098 00099 00100 InteractiveMarkerClient::InteractiveMarkerClient( InteractiveMarkerReceiver* receiver ) 00101 : cleared_( true ) 00102 , subscribed_to_init_( false ) 00103 { 00104 receiver_ = receiver; 00105 } 00106 00107 // ROS callback notifying us of an init message (full update with all data) 00108 void InteractiveMarkerClient::processMarkerInit(const visualization_msgs::InteractiveMarkerInit::ConstPtr& marker_init) 00109 { 00110 ROS_DEBUG("InteractiveMarkerClient: %s INIT %lu", 00111 marker_init->server_id.c_str(), 00112 marker_init->seq_num); 00113 00114 // get caller ID of the sending entity 00115 if ( marker_init->server_id.empty() ) 00116 { 00117 receiver_->setStatusError( "Topic", "server_id is empty!"); 00118 } 00119 00120 M_PublisherContext::iterator context_it = publisher_contexts_.find(marker_init->server_id); 00121 00122 // if this is the first message from that server, create new context 00123 if ( context_it == publisher_contexts_.end() ) 00124 { 00125 PublisherContextPtr pc(new PublisherContext()); 00126 context_it = publisher_contexts_.insert( std::make_pair(marker_init->server_id,pc) ).first; 00127 } 00128 00129 PublisherContextPtr context = context_it->second; 00130 00131 // If this context is already initialized, we can safely ignore this 00132 // init message, because the updates will give us any changes 00133 // present here. 00134 if( context->initialized ) 00135 { 00136 return; 00137 } 00138 00139 if( context->checkInitWith( marker_init )) 00140 { 00141 receiver_->processMarkerChanges( &marker_init->markers ); 00142 cleared_ = false; 00143 00144 context->last_init_seq_num = marker_init->seq_num; 00145 context->init_seen = true; 00146 context->last_update_time = ros::Time::now(); 00147 00148 // This update completed initialization for this context. 00149 receiver_->setStatusOk( context_it->first, "Initialization complete."); 00150 00151 // The next update message we want is the one just after this 00152 // init message. Now that this context is initialized, init 00153 // messages will be ignored and updates will get checked against 00154 // last_update_seq_num. 00155 context->last_update_seq_num = context->last_init_seq_num; 00156 00157 // See if we are completely done initializing and can unsubscribe from init. 00158 maybeUnsubscribeFromInit(); 00159 00160 playbackUpdateQueue( context ); 00161 } 00162 else if( context->update_queue.empty() ) 00163 { 00164 // If we've already seen an init for this publisher but we are not 00165 // initialized, we must erase all markers and start again to avoid 00166 // processing two init messages without clearMarkers() in between. 00167 // 00168 // TODO: If we had the ability to clear the markers from a single 00169 // server at a time, we could just clear the one server's worth 00170 // here and not have to re-init all the servers. 00171 if( context->init_seen ) 00172 { 00173 reinit(); 00174 } 00175 00176 receiver_->processMarkerChanges( &marker_init->markers ); 00177 cleared_ = false; 00178 00179 context->last_init_seq_num = marker_init->seq_num; 00180 context->init_seen = true; 00181 context->last_update_time = ros::Time::now(); 00182 } 00183 // else we must have some queued updates but they don't match up with this init, so do nothing. 00184 } 00185 00186 // play back the relevant updates from the queue, if any, and clear the queue. 00187 void InteractiveMarkerClient::playbackUpdateQueue( PublisherContextPtr& context ) 00188 { 00189 uint64_t next_seq_needed = context->last_init_seq_num + 1; 00190 00191 M_InteractiveMarkerUpdate::iterator update_it = context->update_queue.begin(); 00192 for( ; update_it != context->update_queue.end(); update_it++ ) 00193 { 00194 visualization_msgs::InteractiveMarkerUpdate::ConstPtr update = update_it->second; 00195 00196 if( update->seq_num == next_seq_needed ) 00197 { 00198 applyUpdate( update, context ); 00199 next_seq_needed++; 00200 } 00201 else if( update->seq_num < next_seq_needed ) 00202 { 00203 ROS_DEBUG("Ignoring unneeded queued update number %lu, looking for %lu.", update->seq_num, next_seq_needed); 00204 } 00205 else 00206 { 00207 ROS_ERROR("Found queued update number %lu, missed %lu.", update->seq_num, next_seq_needed); 00208 } 00209 } 00210 context->update_queue.clear(); 00211 } 00212 00213 void InteractiveMarkerClient::processMarkerUpdate(const visualization_msgs::InteractiveMarkerUpdate::ConstPtr& marker_update) 00214 { 00215 // ROS_DEBUG("InteractiveMarkerClient: %s %s %lu", 00216 // marker_update->server_id.c_str(), 00217 // (marker_update->type == visualization_msgs::InteractiveMarkerUpdate::UPDATE ? "UPDATE" : "KEEP_ALIVE"), 00218 // marker_update->seq_num); 00219 00220 // get caller ID of the sending entity 00221 if ( marker_update->server_id.empty() ) 00222 { 00223 receiver_->setStatusError( "Topic", "server_id is empty!"); 00224 } 00225 00226 M_PublisherContext::iterator context_it = publisher_contexts_.find(marker_update->server_id); 00227 00228 // If we haven't seen this publisher before, we need to reset the 00229 // display and listen to the init topic, plus of course add this 00230 // publisher to our list. 00231 if ( context_it == publisher_contexts_.end() ) 00232 { 00233 PublisherContextPtr pc(new PublisherContext()); 00234 // Remember this update message, because we may be able to use it. 00235 pc->enqueueUpdate(marker_update); 00236 00237 context_it = publisher_contexts_.insert( std::make_pair(marker_update->server_id,pc) ).first; 00238 00239 reinit(); 00240 00241 // We have enqueued the update message, and we can't do anything 00242 // else until we get an Init message from this publisher, so return. 00243 return; 00244 } 00245 00246 PublisherContextPtr context = context_it->second; 00247 00248 if ( !context->initialized ) 00249 { 00250 if( context->checkInitWith( marker_update )) 00251 { 00252 // This update completed initialization for this context. 00253 receiver_->setStatusOk( context_it->first, "Initialization complete."); 00254 00255 // The next update message we want is the one just after the 00256 // init message. Now that this context is initialized, init 00257 // messages will be ignored and updates will get checked against 00258 // last_update_seq_num. 00259 context->last_update_seq_num = context->last_init_seq_num; 00260 00261 // See if we are completely done initializing and can unsubscribe from init. 00262 maybeUnsubscribeFromInit(); 00263 } 00264 else 00265 { 00266 // We have received an update before the actual init message, so 00267 // just queue it up and return. 00268 receiver_->setStatusWarn( marker_update->server_id, "Received update or keep-alive without previous INIT message. It might be lost."); 00269 context->enqueueUpdate(marker_update); 00270 return; 00271 } 00272 } 00273 00274 // From here down, we know this context is initialized and we are 00275 // just doing a normal update or keepalive. 00276 applyUpdate( marker_update, context ); 00277 } 00278 00279 void InteractiveMarkerClient::clear() 00280 { 00281 publisher_contexts_.clear(); 00282 reinit(); 00283 } 00284 00285 void InteractiveMarkerClient::unsubscribedFromInit() 00286 { 00287 subscribed_to_init_ = false; 00288 } 00289 00290 void InteractiveMarkerClient::reinit() 00291 { 00292 if( !cleared_ ) 00293 { 00294 receiver_->clearMarkers(); 00295 cleared_ = true; 00296 } 00297 if( !subscribed_to_init_ ) 00298 { 00299 subscribed_to_init_ = receiver_->subscribeToInit(); 00300 } 00301 00302 // Set all contexts to uninitialized since we are throwing away all the marker data. 00303 M_PublisherContext::iterator context_it; 00304 for( context_it = publisher_contexts_.begin(); context_it != publisher_contexts_.end(); context_it++ ) 00305 { 00306 context_it->second->initialized = false; 00307 } 00308 } 00309 00310 // If all publisher contexts are initialized, unsubscribe from the "init" topic. 00311 void InteractiveMarkerClient::maybeUnsubscribeFromInit() { 00312 M_PublisherContext::iterator context_it; 00313 for( context_it = publisher_contexts_.begin(); context_it != publisher_contexts_.end(); context_it++ ) 00314 { 00315 if( !context_it->second->initialized ) 00316 { 00317 return; 00318 } 00319 } 00320 receiver_->unsubscribeFromInit(); 00321 subscribed_to_init_ = false; 00322 } 00323 00324 void InteractiveMarkerClient::applyUpdate( const visualization_msgs::InteractiveMarkerUpdate::ConstPtr& marker_update, 00325 PublisherContextPtr& context ) 00326 { 00327 uint64_t expected_seq_num = 0; 00328 00329 switch ( marker_update->type ) 00330 { 00331 case visualization_msgs::InteractiveMarkerUpdate::UPDATE: 00332 expected_seq_num = context->last_update_seq_num + 1; 00333 break; 00334 00335 case visualization_msgs::InteractiveMarkerUpdate::KEEP_ALIVE: 00336 expected_seq_num = context->last_update_seq_num; 00337 break; 00338 } 00339 00340 if ( marker_update->seq_num != expected_seq_num ) 00341 { 00342 if( marker_update->seq_num < expected_seq_num ) 00343 { 00344 ROS_INFO("Received sequence number %lu, less than expected sequence number %lu. Ignoring.", 00345 marker_update->seq_num, expected_seq_num); 00346 return; 00347 } 00348 00349 // we've lost some updates 00350 std::ostringstream s; 00351 s << "Detected lost update or server restart. Resetting. Reason: Received wrong sequence number (expected: " << 00352 expected_seq_num << ", received: " << marker_update->seq_num << ")"; 00353 receiver_->setStatusError( marker_update->server_id, s.str()); 00354 reinit(); 00355 return; 00356 } 00357 00358 context->last_update_seq_num = marker_update->seq_num; 00359 context->update_seen = true; 00360 context->last_update_time = ros::Time::now(); 00361 00362 if( marker_update->type == visualization_msgs::InteractiveMarkerUpdate::UPDATE ) 00363 { 00364 receiver_->processMarkerChanges( &marker_update->markers, &marker_update->poses, &marker_update->erases ); 00365 cleared_ = false; 00366 } 00367 } 00368 00369 bool InteractiveMarkerClient::isPublisherListEmpty() 00370 { 00371 return publisher_contexts_.empty(); 00372 } 00373 00374 void InteractiveMarkerClient::flagLateConnections() 00375 { 00376 M_PublisherContext::iterator it; 00377 for ( it = publisher_contexts_.begin(); it != publisher_contexts_.end(); it++ ) 00378 { 00379 PublisherContextPtr& context = it->second; 00380 double time_since_last_update = (ros::Time::now() - context->last_update_time).toSec(); 00381 if ( time_since_last_update > 1.0 ) 00382 { 00383 std::stringstream s; 00384 s << "No update received for " << (int)time_since_last_update << " seconds. Connection might be lost."; 00385 receiver_->setStatusWarn( it->first, s.str() ); 00386 context->update_time_ok = false; 00387 } 00388 if ( !context->update_time_ok && time_since_last_update <= 1.0 ) 00389 { 00390 receiver_->setStatusOk( it->first, "OK" ); 00391 } 00392 } 00393 } 00394 00395 } // namespace rviz