oro_action_server_imp.h
Go to the documentation of this file.
00001 /*********************************************************************
00002 *
00003 * Software License Agreement (BSD License)
00004 *
00005 *  Copyright (c) 2008, Willow Garage, Inc.
00006 *  All rights reserved.
00007 *
00008 *  Redistribution and use in source and binary forms, with or without
00009 *  modification, are permitted provided that the following conditions
00010 *  are met:
00011 *
00012 *   * Redistributions of source code must retain the above copyright
00013 *     notice, this list of conditions and the following disclaimer.
00014 *   * Redistributions in binary form must reproduce the above
00015 *     copyright notice, this list of conditions and the following
00016 *     disclaimer in the documentation and/or other materials provided
00017 *     with the distribution.
00018 *   * Neither the name of Willow Garage, Inc. nor the names of its
00019 *     contributors may be used to endorse or promote products derived
00020 *     from this software without specific prior written permission.
00021 *
00022 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 *  POSSIBILITY OF SUCH DAMAGE.
00034 *
00035 * Author: Eitan Marder-Eppstein
00036 *********************************************************************/
00037 #ifndef ACTIONLIB_ACTION_SERVER_IMP_H_
00038 #define ACTIONLIB_ACTION_SERVER_IMP_H_
00039 namespace actionlib {
00040   template <class ActionSpec>
00041   ActionServer<ActionSpec>::ActionServer(RTT::TaskContext* n, std::string name,
00042       bool auto_start)
00043     : node_(n), goal_sub_("goal"), cancel_sub_("cancel"), status_pub_("status"), result_pub_("result"), feedback_pub_("feedback"), goal_callback_(boost::function<void (GoalHandle)>()),
00044       cancel_callback_(boost::function<void (GoalHandle)>()), started_(auto_start), guard_(new DestructionGuard){
00045     //if we're to autostart... then we'll initialize things
00046     if(started_){ 
00047       initialize();
00048       publishStatus();
00049     }
00050   }
00051 
00052   template <class ActionSpec>
00053   ActionServer<ActionSpec>::ActionServer(RTT::TaskContext* n, std::string name,
00054       boost::function<void (GoalHandle)> goal_cb,
00055       boost::function<void (GoalHandle)> cancel_cb,
00056       bool auto_start)
00057     : node_(n), goal_sub_("goal"), cancel_sub_("cancel"), status_pub_("status"), result_pub_("result"), feedback_pub_("feedback"), goal_callback_(goal_cb), cancel_callback_(cancel_cb), started_(auto_start), guard_(new DestructionGuard) {
00058     //if we're to autostart... then we'll initialize things
00059     if(started_){ 
00060       initialize();
00061       publishStatus();
00062     }
00063   }
00064 
00065   template <class ActionSpec>
00066   ActionServer<ActionSpec>::~ActionServer(){
00067     //block until we can safely destruct
00068     guard_->destruct();
00069   }
00070 
00071   template <class ActionSpec>
00072   void ActionServer<ActionSpec>::initialize(){
00073    // result_pub_ = node_.advertise<ActionResult>("result", 50);
00074   //  feedback_pub_ = node_.advertise<ActionFeedback>("feedback", 50);
00075    // status_pub_ = node_.advertise<actionlib_msgs::GoalStatusArray>("status", 50);
00076         
00077         node_->ports()->addPort(result_pub_);
00078         node_->ports()->addPort(feedback_pub_);
00079         node_->ports()->addPort(status_pub_);
00080 
00081    // goal_sub_ = node_.subscribe<ActionGoal>("goal", 50,
00082    //     boost::bind(&ActionServer::goalCallback, this, _1));
00083 
00084    // cancel_sub_ = node_.subscribe<actionlib_msgs::GoalID>("cancel", 50,
00085     //    boost::bind(&ActionServer::cancelCallback, this, _1));
00086 
00087         node_->ports()->addEventPort(goal_sub_, boost::bind(&ActionServer::goalPortCB, this));
00088         node_->ports()->addEventPort(cancel_sub_, boost::bind(&ActionServer::cancelPortCB, this));
00089 
00090     //read the frequency with which to publish status from the parameter server
00091 //    double status_frequency, status_list_timeout;
00092 //    node_.param("status_frequency", status_frequency, 5.0);
00093 //    node_.param("status_list_timeout", status_list_timeout, 5.0);
00094 
00095     status_list_timeout_ = ros::Duration(5.0);
00096 
00097 //    if(status_frequency > 0){
00098 //      status_timer_ = node_.createTimer(ros::Duration(1.0 / status_frequency),
00099  //         boost::bind(&ActionServer::publishStatus, this, _1));
00100 //    }
00101   }
00102 
00103   template <class ActionSpec>
00104   void ActionServer<ActionSpec>::registerGoalCallback(boost::function<void (GoalHandle)> cb){
00105     goal_callback_ = cb;
00106   }
00107 
00108   template <class ActionSpec>
00109   void ActionServer<ActionSpec>::registerCancelCallback(boost::function<void (GoalHandle)> cb){
00110     cancel_callback_ = cb;
00111   }
00112 
00113   template <class ActionSpec>
00114   void ActionServer<ActionSpec>::publishResult(const actionlib_msgs::GoalStatus& status, const Result& result){
00115     boost::recursive_mutex::scoped_lock lock(lock_);
00116     //we'll create a shared_ptr to pass to ROS to limit copying
00117     ActionResult ar;
00118     ar.header.stamp = ros::Time::now();
00119     ar.status = status;
00120     ar.result = result;
00121  //   ROS_DEBUG("Publishing result for goal with id: %s and stamp: %.2f", status.goal_id.id.c_str(), status.goal_id.stamp.toSec());
00122     RTT::Logger::log(RTT::Logger::Debug) << "Publishing result for goal with id: " << status.goal_id.id << " and stamp: " << status.goal_id.stamp.toSec() << RTT::endlog();
00123     result_pub_.write(ar);
00124   }
00125 
00126   template <class ActionSpec>
00127   void ActionServer<ActionSpec>::publishFeedback(const actionlib_msgs::GoalStatus& status, const Feedback& feedback){
00128     boost::recursive_mutex::scoped_lock lock(lock_);
00129     //we'll create a shared_ptr to pass to ROS to limit copying
00130     ActionFeedback af;
00131     af.header.stamp = ros::Time::now();
00132     af.status = status;
00133     af.feedback = feedback;
00134   //  ROS_DEBUG("Publishing feedback for goal with id: %s and stamp: %.2f", status.goal_id.id.c_str(), status.goal_id.stamp.toSec());
00135     RTT::Logger::log(RTT::Logger::Debug) << "Publishing feedback for goal with id: %s and stamp: " << status.goal_id.id << " and stamp: " << status.goal_id.stamp.toSec() << RTT::endlog();
00136     feedback_pub_.write(af);
00137   }
00138 
00139   template <class ActionSpec>
00140   void ActionServer<ActionSpec>::cancelCallback(const boost::shared_ptr<const actionlib_msgs::GoalID>& goal_id){
00141     boost::recursive_mutex::scoped_lock lock(lock_);
00142 
00143     //if we're not started... then we're not actually going to do anything
00144     if(!started_)
00145       return;
00146 
00147     //we need to handle a cancel for the user
00148  //   ROS_DEBUG("The action server has received a new cancel request");
00149     RTT::Logger::log(RTT::Logger::Debug) << "The action server has received a new cancel request" << RTT::endlog();
00150     bool goal_id_found = false;
00151     for(typename std::list<StatusTracker<ActionSpec> >::iterator it = status_list_.begin(); it != status_list_.end(); ++it){
00152       //check if the goal id is zero or if it is equal to the goal id of
00153       //the iterator or if the time of the iterator warrants a cancel
00154       if(
00155           (goal_id->id == "" && goal_id->stamp == ros::Time()) //id and stamp 0 --> cancel everything
00156           || goal_id->id == (*it).status_.goal_id.id //ids match... cancel that goal
00157           || (goal_id->stamp != ros::Time() && (*it).status_.goal_id.stamp <= goal_id->stamp) //stamp != 0 --> cancel everything before stamp
00158         ){
00159         //we need to check if we need to store this cancel request for later
00160         if(goal_id->id == (*it).status_.goal_id.id)
00161           goal_id_found = true;
00162 
00163         //attempt to get the handle_tracker for the list item if it exists
00164         boost::shared_ptr<void> handle_tracker = (*it).handle_tracker_.lock();
00165 
00166         if((*it).handle_tracker_.expired()){
00167           //if the handle tracker is expired, then we need to create a new one
00168           HandleTrackerDeleter<ActionSpec> d(this, it, guard_);
00169           handle_tracker = boost::shared_ptr<void>((void *)NULL, d);
00170           (*it).handle_tracker_ = handle_tracker;
00171 
00172           //we also need to reset the time that the status is supposed to be removed from the list
00173           (*it).handle_destruction_time_ = ros::Time();
00174         }
00175 
00176         //set the status of the goal to PREEMPTING or RECALLING as approriate
00177         //and check if the request should be passed on to the user
00178         GoalHandle gh(it, this, handle_tracker, guard_);
00179         if(gh.setCancelRequested()){
00180           //make sure that we're unlocked before we call the users callbstack
00181           lock_.unlock();
00182 
00183           //call the user's cancel callback on the relevant goal
00184           cancel_callback_(gh);
00185 
00186           //lock for further modification of the status list
00187           lock_.lock();
00188         }
00189       }
00190     }
00191 
00192     //if the requested goal_id was not found, and it is non-zero, then we need to store the cancel request
00193     if(goal_id->id != "" && !goal_id_found){
00194       typename std::list<StatusTracker<ActionSpec> >::iterator it = status_list_.insert(status_list_.end(),
00195           StatusTracker<ActionSpec> (*goal_id, actionlib_msgs::GoalStatus::RECALLING));
00196       //start the timer for how long the status will live in the list without a goal handle to it
00197       (*it).handle_destruction_time_ = ros::Time::now();
00198     }
00199 
00200     //make sure to set last_cancel_ based on the stamp associated with this cancel request
00201     if(goal_id->stamp > last_cancel_)
00202       last_cancel_ = goal_id->stamp;
00203   }
00204 
00205   template <class ActionSpec>
00206   void ActionServer<ActionSpec>::goalCallback(const boost::shared_ptr<const ActionGoal>& goal){
00207     boost::recursive_mutex::scoped_lock lock(lock_);
00208 
00209     //if we're not started... then we're not actually going to do anything
00210     if(!started_)
00211       return;
00212 
00213   //  ROS_DEBUG("The action server has received a new goal request");
00214     RTT::Logger::log(RTT::Logger::Debug) << "The action server has received a new goal request" << RTT::endlog();
00215     //we need to check if this goal already lives in the status list
00216     for(typename std::list<StatusTracker<ActionSpec> >::iterator it = status_list_.begin(); it != status_list_.end(); ++it){
00217       if(goal->goal_id.id == (*it).status_.goal_id.id){
00218 
00219         //if this is a request for a goal that has no active handles left,
00220         //we'll bump how long it stays in the list
00221         if((*it).handle_tracker_.expired()){
00222           (*it).handle_destruction_time_ = ros::Time::now();
00223         }
00224 
00225         //make sure not to call any user callbacks or add duplicate status onto the list
00226         return;
00227       }
00228     }
00229 
00230     //if the goal is not in our list, we need to create a StatusTracker associated with this goal and push it on
00231     typename std::list<StatusTracker<ActionSpec> >::iterator it = status_list_.insert(status_list_.end(), StatusTracker<ActionSpec> (goal));
00232 
00233     //we need to create a handle tracker for the incoming goal and update the StatusTracker
00234     HandleTrackerDeleter<ActionSpec> d(this, it, guard_);
00235     boost::shared_ptr<void> handle_tracker((void *)NULL, d);
00236     (*it).handle_tracker_ = handle_tracker;
00237 
00238     //check if this goal has already been canceled based on its timestamp
00239     if(goal->goal_id.stamp != ros::Time() && goal->goal_id.stamp <= last_cancel_){
00240       //if it has... just create a GoalHandle for it and setCanceled
00241       GoalHandle gh(it, this, handle_tracker, guard_);
00242       gh.setCanceled(Result(), "This goal handle was canceled by the action server because its timestamp is before the timestamp of the last cancel request");
00243     }
00244     else{
00245       GoalHandle gh = GoalHandle(it, this, handle_tracker, guard_);
00246 
00247       //make sure that we unlock before calling the users callback
00248       lock_.unlock();
00249 
00250       //now, we need to create a goal handle and call the user's callback
00251       goal_callback_(gh);
00252 
00253       lock_.lock();
00254     }
00255   }
00256 
00257   template <class ActionSpec>
00258   void ActionServer<ActionSpec>::start(){
00259     initialize();
00260     started_ = true;
00261     publishStatus();
00262   }
00263 
00264 template <class ActionSpec>
00265   void ActionServer<ActionSpec>::spinOnce()
00266 {
00267     
00268   if(started_)
00269         {
00270         /*      ActionGoal ag;
00271                 if(goal_sub_.read(ag) == RTT::NewData)
00272                 {
00273                         goalCallback(boost::shared_ptr<const ActionGoal>(new ActionGoal(ag)));
00274                 }
00275 
00276                 actionlib_msgs::GoalID id;
00277                 if(cancel_sub_.read(id) == RTT::NewData)
00278                 {
00279                         cancelCallback(boost::shared_ptr<const actionlib_msgs::GoalID>(new actionlib_msgs::GoalID(id)));
00280                 }*/
00281                 {                   
00282                 boost::recursive_mutex::scoped_lock lock(lock_);
00283                 publishStatus();
00284                 }
00285   }
00286 }
00287 
00288 template <class ActionSpec>
00289  void ActionServer<ActionSpec>::goalPortCB()
00290 {
00291   ActionGoal ag;
00292   if(goal_sub_.read(ag) == RTT::NewData)
00293   {
00294     goalCallback(boost::shared_ptr<const ActionGoal>(new ActionGoal(ag)));
00295   }
00296 }
00297 
00298 template <class ActionSpec>
00299   void ActionServer<ActionSpec>::cancelPortCB()
00300 {
00301   actionlib_msgs::GoalID id;
00302   if(cancel_sub_.read(id) == RTT::NewData)
00303   {
00304     cancelCallback(boost::shared_ptr<const actionlib_msgs::GoalID>(new actionlib_msgs::GoalID(id)));
00305   }
00306 }
00307 
00308 /*
00309   template <class ActionSpec>
00310   void ActionServer<ActionSpec>::publishStatus(const ros::TimerEvent& e){
00311     boost::recursive_mutex::scoped_lock lock(lock_);
00312     //we won't publish status unless we've been started
00313     if(!started_)
00314       return;
00315 
00316     publishStatus();
00317   }
00318 */
00319   template <class ActionSpec>
00320   void ActionServer<ActionSpec>::publishStatus(){
00321     boost::recursive_mutex::scoped_lock lock(lock_);
00322     //build a status array
00323     actionlib_msgs::GoalStatusArray status_array;
00324 
00325     status_array.header.stamp = ros::Time::now();
00326 
00327     status_array.status_list.resize(status_list_.size());
00328 
00329     unsigned int i = 0;
00330     for(typename std::list<StatusTracker<ActionSpec> >::iterator it = status_list_.begin(); it != status_list_.end();){
00331       status_array.status_list[i] = (*it).status_;
00332 
00333       //check if the item is due for deletion from the status list
00334       if((*it).handle_destruction_time_ != ros::Time()
00335           && (*it).handle_destruction_time_ + status_list_timeout_ < ros::Time::now()){
00336         it = status_list_.erase(it);
00337         RTT::Logger::log(RTT::Logger::Debug) << "status_list_timeout" << RTT::endlog();
00338       }
00339       else
00340         ++it;
00341 
00342       ++i;
00343     }
00344 
00345     status_pub_.write(status_array);
00346   }
00347 };
00348 #endif


oro_action_server
Author(s): Konrad Banachowicz
autogenerated on Wed Jan 8 2014 11:54:25