cmd_vel_mux_nodelet.cpp
Go to the documentation of this file.
1 
9 /*****************************************************************************
10  ** Includes
11  *****************************************************************************/
12 
13 #include <fstream>
14 #include <std_msgs/String.h>
16 
19 
20 /*****************************************************************************
21 ** Namespaces
22 *****************************************************************************/
23 
24 namespace yocs_cmd_vel_mux {
25 
26 /*****************************************************************************
27  ** Implementation
28  *****************************************************************************/
29 
30 void CmdVelMuxNodelet::cmdVelCallback(const geometry_msgs::Twist::ConstPtr& msg, unsigned int idx)
31 {
32  // Reset general timer
35 
36  // Reset timer for this source
37  cmd_vel_subs[idx]->timer.stop();
38  cmd_vel_subs[idx]->timer.start();
39 
40  cmd_vel_subs[idx]->active = true; // obviously his source is sending commands, so active
41 
42  // Give permit to publish to this source if it's the only active or is
43  // already allowed or has higher priority that the currently allowed
44  if ((cmd_vel_subs.allowed == VACANT) ||
45  (cmd_vel_subs.allowed == idx) ||
46  (cmd_vel_subs[idx]->priority > cmd_vel_subs[cmd_vel_subs.allowed]->priority))
47  {
48  if (cmd_vel_subs.allowed != idx)
49  {
50  cmd_vel_subs.allowed = idx;
51 
52  // Notify the world that a new cmd_vel source took the control
53  std_msgs::StringPtr acv_msg(new std_msgs::String);
54  acv_msg->data = cmd_vel_subs[idx]->name;
55  active_subscriber.publish(acv_msg);
56  }
57 
59  }
60 }
61 
62 void CmdVelMuxNodelet::timerCallback(const ros::TimerEvent& event, unsigned int idx)
63 {
64  if (cmd_vel_subs.allowed == idx || (idx == GLOBAL_TIMER && cmd_vel_subs.allowed != VACANT))
65  {
66  if (idx == GLOBAL_TIMER)
67  {
68  // No cmd_vel messages timeout happened for ANYONE, so last active source got stuck without further
69  // messages; not a big problem, just dislodge it; but possibly reflect a problem in the controller
70  NODELET_WARN("CmdVelMux : No cmd_vel messages from ANY input received in the last %fs", common_timer_period);
71  NODELET_WARN("CmdVelMux : %s dislodged due to general timeout",
72  cmd_vel_subs[cmd_vel_subs.allowed]->name.c_str());
73  }
74 
75  // No cmd_vel messages timeout happened to currently active source, so...
77 
78  // ...notify the world that nobody is publishing on cmd_vel; its vacant
79  std_msgs::StringPtr acv_msg(new std_msgs::String);
80  acv_msg->data = "idle";
81  active_subscriber.publish(acv_msg);
82  }
83 
84  if (idx != GLOBAL_TIMER)
85  cmd_vel_subs[idx]->active = false;
86 }
87 
89 {
91 
92  /*********************
93  ** Dynamic Reconfigure
94  **********************/
96  dynamic_reconfigure_server = new dynamic_reconfigure::Server<yocs_cmd_vel_mux::reloadConfig>(nh);
98 
99  active_subscriber = nh.advertise <std_msgs::String> ("active", 1, true); // latched topic
100 
101  // Notify the world that by now nobody is publishing on cmd_vel yet
102  std_msgs::StringPtr active_msg(new std_msgs::String);
103  active_msg->data = "idle";
104  active_subscriber.publish(active_msg);
105 
106  // could use a call to reloadConfiguration here, but it seems to automatically call it once with defaults anyway.
107  NODELET_DEBUG("CmdVelMux : successfully initialized");
108 }
109 
110 void CmdVelMuxNodelet::reloadConfiguration(yocs_cmd_vel_mux::reloadConfig &config, uint32_t unused_level)
111 {
112  ros::NodeHandle &pnh = this->getPrivateNodeHandle();
113 
114  std::unique_ptr<std::istream> is;
115 
116  // Configuration can come directly as a yaml-formatted string or as a file path,
117  // but not both, so we give priority to the first option
118  if (config.yaml_cfg_data.size() > 0)
119  {
120  is.reset(new std::istringstream(config.yaml_cfg_data));
121  }
122  else
123  {
124  std::string yaml_cfg_file;
125  if (config.yaml_cfg_file == "")
126  {
127  // typically fired on startup, so look for a parameter to set a default
128  pnh.getParam("yaml_cfg_file", yaml_cfg_file);
129  }
130  else
131  {
132  yaml_cfg_file = config.yaml_cfg_file;
133  }
134 
135  is.reset(new std::ifstream(yaml_cfg_file.c_str(), std::ifstream::in));
136  if (is->good() == false)
137  {
138  NODELET_ERROR_STREAM("CmdVelMux : configuration file not found [" << yaml_cfg_file << "]");
139  return;
140  }
141  }
142 
143  /*********************
144  ** Yaml File Parsing
145  **********************/
146 
147  // probably need to bring the try catches back here
148  YAML::Node doc;
149 #ifdef HAVE_NEW_YAMLCPP
150  doc = YAML::Load(*is);
151 #else
152  YAML::Parser parser(*is);
153  parser.GetNextDocument(doc);
154 #endif
155 
156  /*********************
157  ** Output Publisher
158  **********************/
159  std::string output_name("output");
160 #ifdef HAVE_NEW_YAMLCPP
161  if (doc["publisher"])
162  {
163  doc["publisher"] >> output_name;
164  }
165 #else
166  const YAML::Node *node = doc.FindValue("publisher");
167  if (node != NULL)
168  {
169  *node >> output_name;
170  }
171 #endif
172 
173  if (output_topic_name != output_name)
174  {
175  output_topic_name = output_name;
176  output_topic_pub = pnh.advertise<geometry_msgs::Twist>(output_topic_name, 10);
177  NODELET_DEBUG_STREAM("CmdVelMux : subscribe to output topic '" << output_name << "'");
178  }
179  else
180  {
181  NODELET_DEBUG_STREAM("CmdVelMux : no need to re-subscribe to output topic '" << output_name << "'");
182  }
183 
184  /*********************
185  ** Input Subscribers
186  **********************/
187  try
188  {
189  cmd_vel_subs.configure(doc["subscribers"]);
190  }
191  catch (EmptyCfgException& e)
192  {
193  NODELET_WARN_STREAM("CmdVelMux : yaml configured zero subscribers, check yaml content");
194  }
195  catch (YamlException& e)
196  {
197  NODELET_ERROR_STREAM("CmdVelMux : yaml parsing problem [" << std::string(e.what()) << "]");
198  }
199 
200  // (Re)create subscribers whose topic is invalid: new ones and those with changed names
201  double longest_timeout = 0.0;
202  for (unsigned int i = 0; i < cmd_vel_subs.size(); i++)
203  {
204  if (!cmd_vel_subs[i]->subs)
205  {
206  cmd_vel_subs[i]->subs =
207  pnh.subscribe<geometry_msgs::Twist>(cmd_vel_subs[i]->topic, 10, CmdVelFunctor(i, this));
208  NODELET_DEBUG("CmdVelMux : subscribed to '%s' on topic '%s'. pr: %d, to: %.2f",
209  cmd_vel_subs[i]->name.c_str(), cmd_vel_subs[i]->topic.c_str(),
210  cmd_vel_subs[i]->priority, cmd_vel_subs[i]->timeout);
211  }
212  else
213  {
214  NODELET_DEBUG_STREAM("CmdVelMux : no need to re-subscribe to input topic '" << cmd_vel_subs[i]->topic << "'");
215  }
216 
217  if (!cmd_vel_subs[i]->timer)
218  {
219  // Create (stopped by now) a one-shot timer for every subscriber, if it doesn't exist yet
220  cmd_vel_subs[i]->timer =
221  pnh.createTimer(ros::Duration(cmd_vel_subs[i]->timeout), TimerFunctor(i, this), true, false);
222  }
223 
224  if (cmd_vel_subs[i]->timeout > longest_timeout)
225  longest_timeout = cmd_vel_subs[i]->timeout;
226  }
227 
228  if (!common_timer)
229  {
230  // Create another timer for cmd_vel messages from any source, so we can
231  // dislodge last active source if it gets stuck without further messages
232  common_timer_period = longest_timeout * 2.0;
233  common_timer =
235  }
236  else if (longest_timeout != (common_timer_period / 2.0))
237  {
238  // Longest timeout changed; just update existing timer period
239  common_timer_period = longest_timeout * 2.0;
241  }
242 
243  NODELET_INFO_STREAM("CmdVelMux : (re)configured");
244 }
245 
246 } // namespace yocs_cmd_vel_mux
247 
#define NODELET_INFO_STREAM(...)
dynamic_reconfigure::Server< yocs_cmd_vel_mux::reloadConfig >::CallbackType dynamic_reconfigure_cb
dynamic_reconfigure::Server< yocs_cmd_vel_mux::reloadConfig > * dynamic_reconfigure_server
void configure(const YAML::Node &node)
Configures the subscribers from a yaml file.
#define NODELET_WARN(...)
void publish(const boost::shared_ptr< M > &message) const
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
Structure for the yocs_cmd_vel_mux.
void stop()
void setPeriod(const Duration &period, bool reset=true)
void start()
void cmdVelCallback(const geometry_msgs::Twist::ConstPtr &msg, unsigned int idx)
#define NODELET_WARN_STREAM(...)
ros::NodeHandle & getPrivateNodeHandle() const
#define NODELET_ERROR_STREAM(...)
std::vector< std::shared_ptr< CmdVelSubs > >::size_type size()
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
#define NODELET_DEBUG_STREAM(...)
PLUGINLIB_EXPORT_CLASS(yocs_cmd_vel_mux::CmdVelMuxNodelet, nodelet::Nodelet)
bool getParam(const std::string &key, std::string &s) const
void reloadConfiguration(yocs_cmd_vel_mux::reloadConfig &config, uint32_t unused_level)
static const unsigned int GLOBAL_TIMER
#define NODELET_DEBUG(...)
void timerCallback(const ros::TimerEvent &event, unsigned int idx)


yocs_cmd_vel_mux
Author(s): Jorge Santos Simon
autogenerated on Mon Jun 10 2019 15:53:29