robot_activity.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  *
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2018, University of Luxembourg
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 University of Luxembourg 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: Maciej Zurad
00036  *********************************************************************/
00037 #include <robot_activity/robot_activity.h>
00038 
00039 #include <string>
00040 #include <vector>
00041 
00042 #define PRINT_FUNC_CALL(state) \
00043   ROS_DEBUG_STREAM(#state << "() method called")
00044 
00045 namespace robot_activity
00046 {
00047 
00048 RobotActivity::RobotActivity(int argc, char* argv[],
00049                            const std::string& name_space,
00050                            const std::string& name)
00051   : node_namespace_(name_space),
00052     node_name_(name),
00053     state_request_callback_queue_()
00054 {
00055   if (ros::isInitialized())
00056   {
00057     node_name_ = ros::this_node::getName();
00058     return;
00059   }
00060 
00061   if (node_name_.empty())
00062   {
00063     ros::init(argc, argv, "robot_activity", ros::init_options::AnonymousName);
00064     node_name_ = ros::this_node::getName();
00065   }
00066   else
00067     ros::init(argc, argv, name);
00068 }
00069 
00070 RobotActivity::~RobotActivity()
00071 {
00072   ROS_DEBUG_STREAM("RobotActivity dtor [" << getNamespace() << "]");
00073 }
00074 
00075 RobotActivity& RobotActivity::init(bool autostart)
00076 {
00077   node_handle_ = ros::NodeHandlePtr(new ros::NodeHandle(node_namespace_));
00078   node_handle_private_ = ros::NodeHandlePtr(new ros::NodeHandle("~" + node_namespace_));
00079 
00080   ros::param::param<bool>("~wait_for_supervisor", wait_for_supervisor_, true);
00081   ROS_INFO_STREAM("wait_for_supervisor = "
00082                   << std::boolalpha << wait_for_supervisor_);
00083 
00084   process_state_pub_ = node_handle_private_->advertise<robot_activity_msgs::State>("/heartbeat", 0, true);
00085   process_error_pub_ = node_handle_private_->advertise<robot_activity_msgs::Error>("/error", 0, true);
00086 
00087   if (wait_for_supervisor_)
00088   {
00089     ros::Rate poll_rate(100);
00090     while (process_state_pub_.getNumSubscribers() == 0 && ros::ok())
00091       poll_rate.sleep();
00092   }
00093 
00094   notifyState();
00095 
00096   ros::param::param<bool>("~autostart_after_reconfigure", autostart_after_reconfigure_, false);
00097   ROS_INFO_STREAM("autostart_after_reconfigure = "
00098                   << std::boolalpha << autostart_after_reconfigure_);
00099 
00100   terminate_server_ = registerStateChangeRequest("robot_activity/terminate", {State::TERMINATED}); // NOLINT
00101   reconfigure_server_ = registerStateChangeRequest("robot_activity/reconfigure",
00102     {
00103       State::UNCONFIGURED,
00104       autostart_after_reconfigure_ ? State::RUNNING : State::STOPPED
00105     }); // NOLINT
00106 
00107   restart_server_ = registerStateChangeRequest("robot_activity/restart", {State::STOPPED, State::RUNNING}); // NOLINT
00108   start_server_   = registerStateChangeRequest("robot_activity/start", {State::RUNNING}); // NOLINT
00109   stop_server_    = registerStateChangeRequest("robot_activity/stop",  {State::STOPPED}); // NOLINT
00110   pause_server_   = registerStateChangeRequest("robot_activity/pause", {State::PAUSED}); // NOLINT
00111 
00112   float heartbeat_rate;
00113   ros::param::param<float>("~heartbeat_rate", heartbeat_rate, 1.0f);
00114   ROS_INFO("heartbeat_rate = %.3f [Hz]", heartbeat_rate);
00115 
00116   IsolatedAsyncTimer::LambdaCallback heartbeat_callback = [this]()
00117   {
00118     notifyState();
00119   };
00120   heartbeat_timer_ = std::make_shared<IsolatedAsyncTimer>(
00121                        *node_handle_private_,
00122                        heartbeat_callback,
00123                        heartbeat_rate,
00124                        false);
00125 
00126   ros::param::param<bool>("~autostart", autostart_, false);
00127 
00128   state_request_spinner_ = std::make_shared<ros::AsyncSpinner>(1, &state_request_callback_queue_);
00129   state_request_spinner_->start();
00130 
00131   autostart_ = autostart_ || autostart;
00132   ROS_INFO_STREAM("autostart = " << std::boolalpha << autostart_);
00133 
00134   if (autostart_)
00135     transitionToState(State::RUNNING);
00136   else
00137     transitionToState(State::STOPPED);
00138 
00139   return *this;
00140 }
00141 
00142 void RobotActivity::run(uint8_t threads)
00143 {
00144   runAsync(threads);
00145   ros::waitForShutdown();
00146 }
00147 
00148 void RobotActivity::runAsync(uint8_t threads)
00149 {
00150   global_callback_queue_spinner_ = std::make_shared<ros::AsyncSpinner>(threads);
00151   global_callback_queue_spinner_->start();
00152 }
00153 
00154 State RobotActivity::getState()
00155 {
00156   return current_state_;
00157 }
00158 
00159 void RobotActivity::notifyError(uint8_t error_type,
00160                                const std::string& function,
00161                                const std::string& description)
00162 {
00163   ROS_DEBUG_STREAM("Publishing error msg with code: "
00164                    << error_type << " function: " << function
00165                    << " description: " << description);
00166   robot_activity_msgs::Error error_msg;
00167   error_msg.header.stamp = ros::Time::now();
00168   error_msg.node_name = getNamespace();
00169   error_msg.error_type = error_type;
00170   error_msg.function = function;
00171   error_msg.description = description;
00172   process_error_pub_.publish(error_msg);
00173 }
00174 
00175 std::shared_ptr<IsolatedAsyncTimer> RobotActivity::registerIsolatedTimer(
00176   const IsolatedAsyncTimer::LambdaCallback& callback,
00177   const float& frequency,
00178   bool stoppable,
00179   bool autostart,
00180   bool oneshot)
00181 {
00182   auto isolated_async_timer = std::make_shared<IsolatedAsyncTimer>(
00183     *node_handle_private_,
00184     callback,
00185     frequency,
00186     stoppable,
00187     autostart,
00188     oneshot);
00189   process_timers_.push_back(isolated_async_timer);
00190   return isolated_async_timer;
00191 }
00192 
00193 std::string RobotActivity::getNamespace() const
00194 {
00195   if (node_handle_private_)
00196     return node_handle_private_->getNamespace();
00197   else
00198     return std::string();
00199 }
00200 
00201 
00202 bool RobotActivity::create()
00203 {
00204   PRINT_FUNC_CALL("create");
00205   onCreate();
00206   return true;
00207 }
00208 
00209 bool RobotActivity::terminate()
00210 {
00211   PRINT_FUNC_CALL("terminate");
00212   onTerminate();
00213   return true;
00214   // ros::Rate(2).sleep();
00215   // ros::shutdown();
00216 }
00217 
00218 bool RobotActivity::configure()
00219 {
00220   PRINT_FUNC_CALL("configure");
00221   return onConfigure();
00222 }
00223 
00224 bool RobotActivity::unconfigure()
00225 {
00226   PRINT_FUNC_CALL("unconfigure");
00227   return onUnconfigure();
00228 }
00229 
00230 bool RobotActivity::start()
00231 {
00232   PRINT_FUNC_CALL("start");
00233   for (const auto & timer : process_timers_)
00234   {
00235     ROS_DEBUG("Starting timer");
00236     timer->start();
00237   }
00238   return onStart();
00239 }
00240 
00241 bool RobotActivity::stop()
00242 {
00243   PRINT_FUNC_CALL("stop");
00244   for (const auto & timer : process_timers_)
00245   {
00246     ROS_DEBUG("Stopping timer");
00247     timer->stop();
00248   }
00249   return onStop();
00250 }
00251 
00252 bool RobotActivity::resume()
00253 {
00254   PRINT_FUNC_CALL("resume");
00255   for (const auto & timer : process_timers_)
00256   {
00257     ROS_DEBUG("Resuming timer");
00258     timer->resume();
00259   }
00260   return onResume();
00261 }
00262 
00263 bool RobotActivity::pause()
00264 {
00265   PRINT_FUNC_CALL("pause");
00266   for (const auto & timer : process_timers_)
00267   {
00268     ROS_DEBUG("Pausing timer");
00269     timer->pause();
00270   }
00271   return onPause();
00272 }
00273 
00274 ros::ServiceServer RobotActivity::registerStateChangeRequest(
00275   const std::string& service_name,
00276   const std::vector<State>& states)
00277 {
00278   ROS_DEBUG_STREAM(
00279     "Registering state transition request for state " << service_name);
00280   using std_srvs::Empty;
00281   EmptyServiceCallback callback = [ = ](Empty::Request & req, Empty::Response & res)
00282   {
00283     bool success = true;
00284     for (const auto & s : states)
00285     {
00286       success = success && transitionToState(s);
00287     }
00288     return success;
00289   };
00290 
00291   auto options = ros::AdvertiseServiceOptions::create<Empty>(
00292                    service_name,
00293                    callback,
00294                    ros::VoidConstPtr(),
00295                    &state_request_callback_queue_);
00296 
00297   return node_handle_private_->advertiseService(options);
00298 }
00299 
00300 void RobotActivity::notifyState() const
00301 {
00302   ROS_DEBUG("Heartbeat sent!");
00303   robot_activity_msgs::State state_msg;
00304   state_msg.header.stamp = ros::Time::now();
00305   state_msg.node_name = getNamespace();
00306   state_msg.state = static_cast<uint8_t>(current_state_);
00307   process_state_pub_.publish(state_msg);
00308 }
00309 
00310 bool RobotActivity::transitionToState(const State& goal_state)
00311 {
00312   const State& starting_state = current_state_;
00313   if (starting_state == goal_state)
00314   {
00315     ROS_WARN_STREAM("Node is already at state " << goal_state);
00316     return false;
00317   }
00318 
00319   while (current_state_ != goal_state)
00320   {
00321     auto from_state = static_cast<uint8_t>(current_state_);
00322     auto to_state = static_cast<uint8_t>(goal_state);
00323     State next_state = STATE_TRANSITIONS_PATHS[from_state][to_state];
00324     if (next_state == State::INVALID)
00325     {
00326       ROS_WARN_STREAM("There is no transition path from [" << starting_state
00327                       << "] to [" << goal_state << "]");
00328       return false;
00329     }
00330     bool transition_result = changeState(next_state);
00331     if (!transition_result)
00332     {
00333       ROS_WARN_STREAM("Transition from [" << starting_state
00334                       << "] to [" << goal_state << "] has failed during ["
00335                       << current_state_ << "]");
00336       return false;
00337     }
00338   }
00339   return true;
00340 }
00341 
00342 bool RobotActivity::changeState(const State& new_state)
00343 {
00344   uint8_t from_state = static_cast<uint8_t>(current_state_);
00345   uint8_t to_state = static_cast<uint8_t>(new_state);
00346   MemberLambdaCallback callback = STATE_TRANSITIONS[from_state][to_state];
00347   if (callback == nullptr)
00348   {
00349     ROS_FATAL_STREAM_ONCE(
00350       "Tried changing state from [" << current_state_
00351       << "] to [" << new_state << "]. Transition does NOT exist!");
00352     return false;
00353   }
00354   bool transition_result = boost::bind(callback, this)();
00355   if (transition_result)
00356   {
00357     ROS_DEBUG_STREAM(
00358       "Chaning state from [" << current_state_ << "] to ["
00359       << new_state << "] succeeded!");
00360     current_state_ = new_state;
00361   }
00362   else
00363   {
00364     ROS_ERROR_STREAM(
00365       "Changing state from [" << current_state_ << "] to ["
00366       << new_state << "] failed!");
00367   }
00368   notifyState();
00369   return transition_result;
00370 }
00371 
00372 std::ostream& operator<<(std::ostream& os, State state)
00373 {
00374   switch (state)
00375   {
00376   case State::INVALID      :
00377     os << "INVALID";
00378     break;
00379   case State::LAUNCHING    :
00380     os << "LAUNCHING";
00381     break;
00382   case State::UNCONFIGURED :
00383     os << "UNCONFIGURED";
00384     break;
00385   case State::STOPPED      :
00386     os << "STOPPED";
00387     break;
00388   case State::PAUSED       :
00389     os << "PAUSED";
00390     break;
00391   case State::RUNNING      :
00392     os << "RUNNING";
00393     break;
00394   case State::TERMINATED   :
00395     os << "TERMINATED";
00396     break;
00397   default                  :
00398     os.setstate(std::ios_base::failbit);
00399   }
00400   return os;
00401 }
00402 
00403 const RobotActivity::StateTransitions RobotActivity::STATE_TRANSITIONS =
00404 {
00405   /* Valid transitions for State::INVALID */
00406   {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
00407   /* Valid transitions for  State::LAUNCHING */
00408   {nullptr, nullptr, &RobotActivity::create, nullptr, nullptr, nullptr, nullptr},
00409   /* Valid transitions for State::UNCONFIGURED */
00410   {nullptr, nullptr, nullptr, &RobotActivity::configure, nullptr, nullptr, &RobotActivity::terminate},
00411   /* Valid transitions for State::STOPPED */
00412   {nullptr, nullptr, &RobotActivity::unconfigure, nullptr, &RobotActivity::start, nullptr, nullptr},
00413   /* Valid transitions for State::PAUSED */
00414   {nullptr, nullptr, nullptr, &RobotActivity::stop, nullptr, &RobotActivity::resume, nullptr},
00415   /* Valid transitions for State::RUNNING */
00416   {nullptr, nullptr, nullptr, nullptr, &RobotActivity::pause, nullptr, nullptr},
00417   /* Valid transitions for State::TERMINATED */
00418   {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}
00419 };
00420 
00421 
00422 const RobotActivity::StateTransitionPaths RobotActivity::STATE_TRANSITIONS_PATHS =
00423 {
00424   {
00425     /* State::INVALID to other states */
00426     State::INVALID, State::INVALID, State::INVALID,
00427     State::INVALID, State::INVALID, State::INVALID,
00428     State::INVALID
00429   }, // NOLINT
00430   {
00431     /* State::LAUNCHING to other states */
00432     State::INVALID, State::LAUNCHING, State::UNCONFIGURED,
00433     State::UNCONFIGURED, State::UNCONFIGURED, State::UNCONFIGURED,
00434     State::UNCONFIGURED
00435   }, // NOLINT
00436   {
00437     /* State::UNCONFIGURED to other states */
00438     State::INVALID, State::INVALID, State::UNCONFIGURED,
00439     State::STOPPED, State::STOPPED, State::STOPPED,
00440     State::TERMINATED
00441   }, // NOLINT
00442   {
00443     /* State::STOPPED to other states */
00444     State::INVALID, State::INVALID, State::UNCONFIGURED,
00445     State::STOPPED, State::PAUSED, State::PAUSED,
00446     State::UNCONFIGURED
00447   }, // NOLINT
00448   {
00449     /* State::PAUSED to other states */
00450     State::INVALID, State::INVALID, State::STOPPED,
00451     State::STOPPED, State::PAUSED, State::RUNNING,
00452     State::STOPPED
00453   }, // NOLINT
00454   {
00455     /* State::RUNNING to other states */
00456     State::INVALID, State::INVALID, State::PAUSED,
00457     State::PAUSED, State::PAUSED, State::PAUSED,
00458     State::PAUSED
00459   }, // NOLINT
00460   {
00461     /* State::TERMINATED to other states */
00462     State::INVALID, State::INVALID, State::INVALID,
00463     State::INVALID, State::INVALID, State::INVALID,
00464     State::INVALID
00465   }
00466 };
00467 
00468 }  // namespace robot_activity


robot_activity
Author(s): Maciej ZURAD
autogenerated on Thu Jun 6 2019 18:10:04