mrs_lib
Various reusable classes, functions and utilities for use in MRS projects
dynamic_reconfigure_mgr.h
Go to the documentation of this file.
1 // clang: MatousFormat
6 #ifndef DYNAMIC_RECONFIGURE_MGR_H
7 #define DYNAMIC_RECONFIGURE_MGR_H
8 
9 #include <ros/ros.h>
10 #include <dynamic_reconfigure/server.h>
11 #include <string>
12 #include <map>
13 #include <unordered_set>
14 #include <mutex>
15 #include <iostream>
16 #include <boost/any.hpp>
17 #include <Eigen/Dense>
18 #include <mrs_lib/param_loader.h>
19 
20 
21 namespace mrs_lib
22 {
23 
25 // This class handles dynamic reconfiguration of parameters using dynamic_reconfigure server.
26 // Initialize this manager simply by instantiating an object of this templated class
27 // with the template parameter corresponding to the type of your config message, e.g. as
28 // DynamicReconfigureMgr<MyConfig> drmgr;
29 // This will automatically initialize the dynamic_reconfigure server and a callback method
30 // to asynchronously update the values in the config.
31 // Optionally, you can specify the ros NodeHandle to initialize the dynamic_reconfigure server
32 // and a flag 'print_values' to indicate whether to print new received values (only changed ones,
33 // default is true).
34 // The latest configuration is available through the public member 'config'. This should be
35 // changed externally with care since any change risks being overwritten in the next call to
36 // the 'dynamic_reconfigure_callback' method.
37 // Note that in case of a multithreaded ROS node, external mutexes _might_ be necessary
38 // to make access to the 'config' member thread-safe.
39 template <typename ConfigType>
41 {
42  private:
43  using callback_t = typename dynamic_reconfigure::Server<ConfigType>::CallbackType;
44 public:
45  // this variable holds the latest received configuration
46  ConfigType config;
47  // initialize some stuff in the constructor
48  DynamicReconfigureMgr(const ros::NodeHandle& nh = ros::NodeHandle("~"), bool print_values = true, std::string node_name = std::string(), const callback_t& user_callback = {})
49  : m_not_initialized(true),
50  m_loaded_invalid_default(false),
51  m_print_values(print_values),
52  m_node_name(node_name),
53  m_server(m_server_mtx, nh),
54  m_usr_cbf(user_callback),
55  m_pl(nh, print_values, node_name)
56  {
57  // initialize the dynamic reconfigure callback
58  m_server.setCallback(boost::bind(&DynamicReconfigureMgr<ConfigType>::dynamic_reconfigure_callback, this, _1, _2));
59  };
60 
61  /* Constructor overloads //{ */
62  // Convenience constructor to enable writing DynamicReconfigureMgr<MyConfig> drmgr(nh, node_name)
63  DynamicReconfigureMgr(const ros::NodeHandle& nh, std::string node_name) : DynamicReconfigureMgr(nh, true, node_name){};
64  // Convenience constructor to enable writing DynamicReconfigureMgr<MyConfig> drmgr(nh, "node_name")
65  DynamicReconfigureMgr(const ros::NodeHandle& nh, const char* node_name) : DynamicReconfigureMgr(nh, std::string(node_name)){};
66  // Convenience constructor to enable writing DynamicReconfigureMgr<MyConfig> drmgr(node_name)
67  DynamicReconfigureMgr(std::string node_name) : DynamicReconfigureMgr(ros::NodeHandle("~"), node_name){};
68  //}
69 
70  // pushes this config to the server
71  void update_config(const ConfigType& cfg)
72  {
73  m_server.updateConfig(cfg);
74  }
75 
76  // pushes the current config to the server
77  void update_config()
78  {
79  m_server.updateConfig(config);
80  }
81 
82  void publish_descriptions()
83  {
84  ConfigType dflt;
85  m_server.getConfigDefault(dflt);
86  m_server.setConfigDefault(dflt);
87  }
88 
89  bool loaded_successfully()
90  {
91  return !m_not_initialized && !m_loaded_invalid_default && m_pl.loadedSuccessfully();
92  }
93 
94 private:
95  bool m_not_initialized, m_loaded_invalid_default, m_print_values;
96  std::string m_node_name;
97  // dynamic_reconfigure server variables
98  boost::recursive_mutex m_server_mtx;
99  typename dynamic_reconfigure::Server<ConfigType> m_server;
100  callback_t m_usr_cbf;
101 
102  ParamLoader m_pl;
103  std::unordered_set<std::string> m_to_init;
104 
105  // the callback itself
106  void dynamic_reconfigure_callback(ConfigType& new_config, uint32_t level)
107  {
108  if (m_print_values)
109  {
110  if (m_node_name.empty())
111  ROS_INFO("Dynamic reconfigure request received");
112  else
113  ROS_INFO("[%s]: Dynamic reconfigure request received", m_node_name.c_str());
114  }
115 
116  if (m_not_initialized)
117  {
118  load_defaults(new_config);
119  update_config(new_config);
120  }
121  if (m_print_values)
122  {
123  print_changed_params(new_config);
124  }
125  m_not_initialized = false;
126  config = new_config;
127  if (m_usr_cbf)
128  m_usr_cbf(new_config, level);
129  }
130 
131  template <typename T>
132  void load_param(const std::string& name, typename ConfigType::AbstractParamDescriptionConstPtr& descr, ConfigType& config)
133  {
134  using param_descr_t = typename ConfigType::template ParamDescription<T>;
135  boost::shared_ptr<const param_descr_t> cast_descr = boost::dynamic_pointer_cast<const param_descr_t>(descr);
136  m_pl.loadParam(name, config.*(cast_descr->field));
137  }
138 
139  void load_defaults(ConfigType& new_config)
140  {
141  // Note that this part of the API is still unstable and may change! It was tested with ROS Kinetic and Melodic.
142  std::vector<typename ConfigType::AbstractParamDescriptionConstPtr> descrs = new_config.__getParamDescriptions__();
143  for (auto& descr : descrs)
144  {
145  std::string name = descr->name;
146  size_t pos = name.find("__");
147  while (pos != name.npos)
148  {
149  name.replace(pos, 2, "/");
150  pos = name.find("__");
151  }
152 
153  if (descr->type == "bool")
154  load_param<bool>(name, descr, new_config);
155  else if (descr->type == "int")
156  load_param<int>(name, descr, new_config);
157  else if (descr->type == "double")
158  load_param<double>(name, descr, new_config);
159  else if (descr->type == "str")
160  load_param<std::string>(name, descr, new_config);
161  else
162  {
163  ROS_ERROR("[%s]: Unknown parameter type: '%s'", m_node_name.c_str(), descr->type.c_str());
164  m_loaded_invalid_default = true;
165  }
166  }
167  }
168 
169  // method for printing names and values of new received parameters (prints only the changed ones) //{
170  void print_changed_params(const ConfigType& new_config)
171  {
172  // Note that this part of the API is still unstable and may change! It was tested with ROS Kinetic and Melodic.
173  std::vector<typename ConfigType::AbstractParamDescriptionConstPtr> descrs = new_config.__getParamDescriptions__();
174  for (auto& descr : descrs)
175  {
176  boost::any val, old_val;
177  descr->getValue(new_config, val);
178  descr->getValue(config, old_val);
179  std::string name = descr->name;
180  const size_t pos = name.find("__");
181  if (pos != name.npos)
182  {
183  if (m_not_initialized)
184  {
185  continue;
186  } else
187  {
188  name.replace(pos, 2, "/");
189  }
190  }
191 
192  // try to guess the correct type of the parameter (these should be the only ones supported)
193  int* intval;
194  double* doubleval;
195  bool* boolval;
196  std::string* stringval;
197 
198  if (try_cast(val, intval))
199  {
200  if (m_not_initialized || !try_compare(old_val, intval))
201  print_value(name, *intval);
202  } else if (try_cast(val, doubleval))
203  {
204  if (m_not_initialized || !try_compare(old_val, doubleval))
205  print_value(name, *doubleval);
206  } else if (try_cast(val, boolval))
207  {
208  if (m_not_initialized || !try_compare(old_val, boolval))
209  print_value(name, *boolval);
210  } else if (try_cast(val, stringval))
211  {
212  if (m_not_initialized || !try_compare(old_val, stringval))
213  print_value(name, *stringval);
214  } else
215  {
216  print_value(name, std::string("unknown dynamic reconfigure type"));
217  }
218  }
219  }
220  //}
221 
222  // helper method for parameter printing
223  template <typename T>
224  inline void print_value(const std::string& name, const T& val)
225  {
226  if (m_node_name.empty())
227  std::cout << "\t" << name << ":\t" << val << std::endl;
228  else
229  ROS_INFO_STREAM("[" << m_node_name << "]: parameter '" << name << "':\t" << val);
230  }
231  // helper methods for automatic parameter value parsing
232  template <typename T>
233  inline bool try_cast(boost::any& val, T*& out)
234  {
235  return (out = boost::any_cast<T>(&val));
236  }
237  template <typename T>
238  inline bool try_compare(boost::any& val, T*& to_what)
239  {
240  T* tmp;
241  if ((tmp = boost::any_cast<T>(&val)))
242  {
243  /* std::cout << std::endl << *tmp << " vs " << *to_what << std::endl; */
244  return *tmp == *to_what;
245  } else
246  { // the value should not change during runtime - this should never happen (but its better to be safe than sorry)
247  if (m_node_name.empty())
248  ROS_WARN("DynamicReconfigure value type has changed - this should not happen!");
249  else
250  ROS_WARN_STREAM("[" << m_node_name << "]: DynamicReconfigure value type has changed - this should not happen!");
251  return false;
252  }
253  }
254 };
255 //}
256 
257 }
258 
259 #endif // DYNAMIC_RECONFIGURE_MGR_H
mrs_lib::ParamLoader::loadedSuccessfully
bool loadedSuccessfully()
Indicates whether all compulsory parameters were successfully loaded.
Definition: param_loader.h:599
mrs_lib
All mrs_lib functions, classes, variables and definitions are contained in this namespace.
Definition: attitude_converter.h:29
param_loader.h
Defines ParamLoader - a convenience class for loading static ROS parameters.
mrs_lib::ParamLoader::loadParam
bool loadParam(const std::string &name, T &out_value, const T &default_value)
Loads a parameter from the rosparam server with a default value.
Definition: param_loader.h:639
mrs_lib::DynamicReconfigureMgr
Definition: dynamic_reconfigure_mgr.h:40
mrs_lib::ParamLoader
Convenience class for loading parameters from rosparam server.
Definition: param_loader.h:43