diff --git a/doc/HowToUseYourParameterPtr.md b/doc/HowToUseYourParameterPtr.md new file mode 100644 index 0000000..6a39987 --- /dev/null +++ b/doc/HowToUseYourParameterPtr.md @@ -0,0 +1,117 @@ +# How to Use Your `rosparam_handler::ParameterPtr` +**Description**: This tutorial will familiarize you with how you can use the autogenerated parameter structs in your nodes from a base class smart-pointer. It is thus recommended to read the tutorial [How to use your parameters struct file](HowToUseYourParametersStruct.md) if you haven't yet. +**Tutorial Level**: INTERMEDIATE + +## Generated files +When creating params files as described in [How to write your first .params file](HowToWriteYourFirstParamsFile.md), you will end up with the following two files: +- *'include/rosparam_tutorials/TutorialParameters.h'* +- *'include/rosparam_tutorials/TutorialConfig.h'* + +The 'Parameters.h' file will hold a struct called `Parameters`. +The 'Config.h' file will hold the normal dynamic_reconfigure Config struct. + +For your code it is enough to include the \*Parameters.h file, e.g. + +```cpp +#include "rosparam_tutorials/TutorialParameters.h" +``` + +You can now add an instance of the base parameter pointer to your class: + +```cpp +rosparam_tutorials::ParametersPtr params_ptr_; +``` + +## Initializing the pointer +When initializing your node, the params pointer `params_ptr_` must be instantiated to the appropriate parameter type with a private `NodeHandle`. + +```cpp +MyNodeClass::MyNodeClass() + : params_ptr_{boost::make_shared(ros::NodeHandle("~"))} +{ + ... +} +``` + +In case your node has several classes, each using a different `ParameterPtr` object, the private `NodeHandle` must have a sub-namespace that is unique to your object in order to avoid parameters name collision. + +```cpp +MyNodeClass::MyClass() + : params_ptr_{boost::make_shared(ros::NodeHandle("~/my_class"))} +{ + ... +} + +MyNodeClass::MyOtherClass() + : params_ptr_{boost::make_shared(ros::NodeHandle("~/my_other_class"))} +{ + ... +} +``` + +## Initializing the struct + +The call to `fromParamServer()` is done the very same manner as for a normal parameter object. +It will take care of getting all parameter values from the parameter server, checking their type, and checking that a default value is set, if you haven't provided one on your own. If you have specified a default value, but the parameter is not yet present on the parameter server, it will be set. When min and max values are specified, these bounds will be checked as well. + +```cpp +MyNodeClass::MyNodeClass() + : params_ptr_{boost::make_shared(ros::NodeHandle("~"))} +{ + params_ptr_->fromParamServer(); +} +``` + +If you do not use a class (which you should do though in my opinion), you can create it like so: +```cpp +rosparam_tutorials::ParametersPtr params_ptr{boost::make_shared(ros::NodeHandle("~"))} +params_ptr->fromParamServer(); +``` +Note: If you have set the logger level for your node to debug, you will get information on which values have been retrieved. +Note: If you use nodelets, you have to use the `getPrivateNodeHandle()` function instead. + +## Retrieving the actual parameter + +Since you are using here a `ParametersPtr`, you cannot access directly the parameters held by the structure: +```cpp +params_ptr_->my_int = 42; // Will NOT compile +``` + +To do so, you have to cast the base pointer to its appropriate derived type: +```cpp +boost::shared_ptr params_ptr_cast = rosparam_handler::dynamic_parameters_cast(params_ptr_); +assert(params_ptr_cast != nullptr); + +if (params_ptr_cast != nullptr) +{ + params_ptr_cast->my_int = 42; +} +``` +Both dynamic and static cast helper function are provided: +```cpp +auto my_cast = rosparam_handler::dynamic_parameters_cast(ptr); +auto my_cast = rosparam_handler::static_parameters_cast(ptr); +``` +which simply are aliases to: +```cpp +auto my_cast = boost::dynamic_pointer_cast(ptr); +auto my_cast = boost::static_pointer_cast_cast(ptr); +``` + +## Using dynamic_reconfigure +Your dynamic_reconfigure callback can now look as simple as: +```cpp +void reconfigureRequest(TutorialConfig& config, uint32_t level) { + params_ptr_->fromConfig(config); +} +``` +This will update all values that were specified as configurable. At the same time, it assures that all dynamic_reconfigure parameters live in the same namespace as those on the parameter server to avoid problems with redundant parameters. + +You can find a running version of this example code in the [rosparam_handler_tutorial](https://github.com/cbandera/rosparam_handler_tutorial)-Repository + +## Setting parameters on the server +If you change your parameters at runtime from within the code, you can upload the current state of the parameters with +```cpp +params_ptr_->toParamServer(); +``` +This will set all non-const parameters with their current value on the ros parameter server. diff --git a/doc/HowToUseYourParameterStruct.md b/doc/HowToUseYourParameterStruct.md index 74b223d..acdd88d 100644 --- a/doc/HowToUseYourParameterStruct.md +++ b/doc/HowToUseYourParameterStruct.md @@ -54,6 +54,17 @@ This will update all values that were specified as configurable. At the same tim You can find a running version of this example code in the [rosparam_handler_tutorial](https://github.com/cbandera/rosparam_handler_tutorial)-Repository +## From another parameters structure +You can also simply copy your current parameters: +```cpp +rosparam_tutorials::TutorialParameters params_{ros::NodeHandle("~")} +params_.fromParamServer(); + +rosparam_tutorials::TutorialParameters params_copy_(params_); +// equivalently +params_copy_ = params_; +``` + ## Setting parameters on the server If you change your parameters at runtime from within the code, you can upload the current state of the parameters with ```cpp diff --git a/include/rosparam_handler/ParametersBase.h b/include/rosparam_handler/ParametersBase.h new file mode 100644 index 0000000..b1536c0 --- /dev/null +++ b/include/rosparam_handler/ParametersBase.h @@ -0,0 +1,103 @@ +/** + * \file ParametersBase.h + * + * Created on: Oct 28, 2017 + * + * \authors: Claudio Bandera + * Sascha Wirges + * Niels Ole Salscheider + * Jeremie Deray + */ + +#ifndef _ROSPARAM_HANDLER_PARAMETERS_BASE_H_ +#define _ROSPARAM_HANDLER_PARAMETERS_BASE_H_ + +#include +#include +#include +#include + +namespace rosparam_handler { + +/// \brief ParametersBase An abstract base struct for struct generated by rosparam_handler. +struct ParametersBase { + + ParametersBase(const ros::NodeHandle& private_node_handle) + : globalNamespace{"/"}, + privateNamespace{private_node_handle.getNamespace() + "/"}, + nodeName{rosparam_handler::getNodeName(private_node_handle)} {} + + ParametersBase(const ParametersBase& o) + : globalNamespace(o.globalNamespace) + , privateNamespace(o.privateNamespace) + , nodeName(o.nodeName) {} + + virtual ~ParametersBase() = default; + + ParametersBase& operator=(const ParametersBase& o) + { + globalNamespace = o.globalNamespace; + privateNamespace = o.privateNamespace; + nodeName = o.nodeName; + return *this; + } + + /// \brief Get values from parameter server + /// + /// Will fail if a value can not be found and no default value is given. + inline void fromParamServer(){ + if(!fromParamServerImpl()){ + missingParamsWarning(); + rosparam_handler::exit("RosparamHandler: GetParam could net retrieve parameter."); + } + ROS_DEBUG_STREAM(*this); + } + + /// \brief Set parameters on ROS parameter server. + virtual void toParamServer() = 0; + + /// \brief Update configurable parameters. + /// + /// \param config dynamic reconfigure struct + /// \level ? + template + inline void fromConfig(const T& config, const uint32_t level = 0){ +#ifdef DYNAMIC_RECONFIGURE_FOUND + fromConfigImpl(config, level); +#else + ROS_FATAL_STREAM("dynamic_reconfigure was not found during compilation. So fromConfig() is not available. Please recompile with dynamic_reconfigure."); + rosparam_handler::exit("dynamic_reconfigure was not found during compilation. So fromConfig() is not available. Please recompile with dynamic_reconfigure."); +#endif + } + + /// \brief Stream operator for printing parameter struct + inline friend std::ostream& operator<<(std::ostream& os, const ParametersBase& p) + { + return p.print(os); + } + +protected: + + /// \brief The actual implementation of 'fromParamServer' + /// overriden by the derived class + virtual bool fromParamServerImpl() = 0; + + /// \brief The actual implementation os 'fromConfig' specialized + /// for the 'DerivedConfig' type. + template + void fromConfigImpl(const T& config, const uint32_t level); + + /// \brief A helper function for ostram operator << + virtual std::ostream& print(std::ostream& os) const = 0; + + /// \brief Issue a warning about missing default parameters. + virtual void missingParamsWarning() = 0; + + std::string globalNamespace = {"/"}; + std::string privateNamespace; + std::string nodeName; +}; + +} // namespace rosparam_handler + +#endif /* _ROSPARAM_HANDLER_PARAMETERS_BASE_H_ */ diff --git a/include/rosparam_handler/pointer.hpp b/include/rosparam_handler/pointer.hpp new file mode 100644 index 0000000..8ef79f5 --- /dev/null +++ b/include/rosparam_handler/pointer.hpp @@ -0,0 +1,37 @@ +/** + * \file pointer.h + * + * Created on: Oct 28, 2017 + * + * \authors: Jeremie Deray + */ + +#ifndef _ROSPARAM_HANDLER_POINTER_H_ +#define _ROSPARAM_HANDLER_POINTER_H_ + +#include + +namespace rosparam_handler { + +/// \brief forward declaration +struct ParametersBase; + +/// \brief base pointer declaration +using ParametersPtr = boost::shared_ptr; +using ParametersConstPtr = boost::shared_ptr; + +template +boost::shared_ptr static_parameters_cast(const rosparam_handler::ParametersPtr& ptr) +{ + return boost::static_pointer_cast(ptr); +} + +template +boost::shared_ptr dynamic_parameters_cast(const rosparam_handler::ParametersPtr& ptr) +{ + return boost::dynamic_pointer_cast(ptr); +} + +} /* namespace rosparam_handler */ + +#endif /* _ROSPARAM_HANDLER_POINTER_H_ */ diff --git a/src/rosparam_handler/parameter_generator_catkin.py b/src/rosparam_handler/parameter_generator_catkin.py index 015b112..ce8b078 100644 --- a/src/rosparam_handler/parameter_generator_catkin.py +++ b/src/rosparam_handler/parameter_generator_catkin.py @@ -383,6 +383,8 @@ def _generatehpp(self): template = f.read() param_entries = [] + copy_constructor_entries = [] + assignment_operator_entries = [] string_representation = [] from_server = [] to_server = [] @@ -405,17 +407,21 @@ def _generatehpp(self): # Test for default value if param["default"] is None: + value = "" default = "" non_default_params.append(Template(' << "\t" << $namespace << "$name" << " ($type) ' '\\n"\n').substitute( namespace=namespace, name=name, type=param["type"])) else: if param['is_vector']: - default = ', {}'.format(str(param['type']) + "{" + self._get_cvaluelist(param, "default") + "}") + value = ' = {}'.format("{" + self._get_cvaluelist(param, "default") + "}") + default = ', ' + name elif param['is_map']: - default = ', {}'.format(str(param['type']) + "{" + self._get_cvaluedict(param, "default") + "}") + value = ' = {}'.format("{" + self._get_cvaluedict(param, "default") + "}") + default = ', ' + name else: - default = ', {}'.format(str(param['type']) + "{" + self._get_cvalue(param, "default") + "}") + value = ' = {}'.format(self._get_cvalue(param, "default")) + default = ', ' + name # Test for constant value if param['constant']: @@ -425,8 +431,10 @@ def _generatehpp(self): default=self._get_cvalue(param, "default"))) from_server.append(Template(' rosparam_handler::testConstParam($paramname);').substitute(paramname=full_name)) else: - param_entries.append(Template(' ${type} ${name}; /*!< ${description} */').substitute( - type=param['type'], name=name, description=param['description'])) + param_entries.append(Template(' ${type} ${name}${value}; /*!< ${description} */').substitute( + type=param['type'], name=name, value=value, description=param['description'])) + copy_constructor_entries.append(Template(' , ${name}(o.${name})').substitute(name=name)) + assignment_operator_entries.append(Template(' ${name} = o.${name};').substitute(name=name)) from_server.append(Template(' success &= rosparam_handler::getParam($paramname, $name$default);').substitute( paramname=full_name, name=name, default=default, description=param['description'])) to_server.append( @@ -434,7 +442,7 @@ def _generatehpp(self): # Test for configurable params if param['configurable']: - from_config.append(Template(' $name = config.$name;').substitute(name=name)) + from_config.append(Template(' casted_ref.$name = config.$name;').substitute(name=name)) # Test limits if param['is_vector']: @@ -451,10 +459,12 @@ def _generatehpp(self): paramname=full_name, name=name, max=param['max'], type=ttype)) # Add debug output - string_representation.append(Template(' << "\t" << p.$namespace << "$name:" << p.$name << ' + string_representation.append(Template(' << "\t" << $namespace << "$name:" << $name << ' '"\\n"\n').substitute(namespace=namespace, name=name)) param_entries = "\n".join(param_entries) + copy_constructor_entries = "\n".join(copy_constructor_entries) + assignment_operator_entries = "\n".join(assignment_operator_entries) string_representation = "".join(string_representation) non_default_params = "".join(non_default_params) from_server = "\n".join(from_server) @@ -463,7 +473,10 @@ def _generatehpp(self): test_limits = "\n".join(test_limits) content = Template(template).substitute(pkgname=self.pkgname, ClassName=self.classname, - parameters=param_entries, fromConfig=from_config, + parameters=param_entries, + copyConstructor=copy_constructor_entries, + assignOperator=assignment_operator_entries, + fromConfig=from_config, fromParamServer=from_server, string_representation=string_representation, non_default_params=non_default_params, nodename=self.nodename, diff --git a/templates/Parameters.h.template b/templates/Parameters.h.template index a493f0d..2e5f32d 100644 --- a/templates/Parameters.h.template +++ b/templates/Parameters.h.template @@ -6,82 +6,114 @@ // // ********************************************************/ -#pragma once +#ifndef _${pkgname}_ROSPARAM_HANDLER_${ClassName}Parameters_H_ +#define _${pkgname}_ROSPARAM_HANDLER_${ClassName}Parameters_H_ + +#include -#include -#include -#include #ifdef DYNAMIC_RECONFIGURE_FOUND #include <${pkgname}/${ClassName}Config.h> #else struct ${ClassName}Config{}; #endif - namespace ${pkgname} { /// \brief Parameter struct generated by rosparam_handler -struct ${ClassName}Parameters { +struct ${ClassName}Parameters : public rosparam_handler::ParametersBase { using Config = ${ClassName}Config; + using Ptr = boost::shared_ptr<${ClassName}Parameters>; + using ConstPtr = boost::shared_ptr; - ${ClassName}Parameters(const ros::NodeHandle& private_node_handle) - : globalNamespace{"/"}, - privateNamespace{private_node_handle.getNamespace() + "/"}, - nodeName{rosparam_handler::getNodeName(private_node_handle)} {} - - /// \brief Get values from parameter server - /// - /// Will fail if a value can not be found and no default value is given. - void fromParamServer(){ - bool success = true; -$fromParamServer -$test_limits - if(!success){ - missingParamsWarning(); - rosparam_handler::exit("RosparamHandler: GetParam could net retrieve parameter."); - } - ROS_DEBUG_STREAM(*this); - } + inline ${ClassName}Parameters(const ros::NodeHandle& private_node_handle) : + rosparam_handler::ParametersBase(private_node_handle) {} - /// \brief Set parameters on ROS parameter server. - void toParamServer(){ -$toParamServer - } + inline ${ClassName}Parameters(const ${ClassName}Parameters& o) + : ParametersBase(o) + $copyConstructor - /// \brief Update configurable parameters. - /// - /// \param config dynamic reconfigure struct - /// \level ? - void fromConfig(const Config& config, const uint32_t level = 0){ -#ifdef DYNAMIC_RECONFIGURE_FOUND -$fromConfig -#else - ROS_FATAL_STREAM("dynamic_reconfigure was not found during compilation. So fromConfig() is not available. Please recompile with dynamic_reconfigure."); - rosparam_handler::exit("dynamic_reconfigure was not found during compilation. So fromConfig() is not available. Please recompile with dynamic_reconfigure."); -#endif + { + // } - /// \brief Stream operator for printing parameter struct - friend std::ostream& operator<<(std::ostream& os, const ${ClassName}Parameters& p) + ${ClassName}Parameters& operator=(const ${ClassName}Parameters& o) { - os << "[" << p.nodeName << "]\nNode " << p.nodeName << " has the following parameters:\n" -$string_representation; - return os; + rosparam_handler::ParametersBase::operator=(o); + $assignOperator + return *this; + } + + /// \brief Set parameters on ROS parameter server. + void toParamServer() override { +$toParamServer } + /// \brief the parameters members. + $parameters -private: +protected: + + /// \brief Get values from parameter server + /// + /// Will fail if a value can not be found and no default value is given. + inline bool fromParamServerImpl() override { + + bool success = true; + + $fromParamServer + $test_limits + + return success; + + } + + /// \brief print. A helper function to be called from base stream operator for printing parameter struct + inline std::ostream& print(std::ostream& os) const override + { + os << "[" << nodeName << "]\nNode " << nodeName << " has the following parameters:\n" + $string_representation; + return os; + } + /// \brief Issue a warning about missing default parameters. - void missingParamsWarning(){ + inline void missingParamsWarning() override { ROS_WARN_STREAM("[" << nodeName << "]\nThe following parameters do not have default values and need to be specified:\n" $non_default_params ); } - const std::string globalNamespace; - const std::string privateNamespace; - const std::string nodeName; }; +using ${ClassName}ParametersPtr = ${ClassName}Parameters::Ptr; +using ${ClassName}ParametersConstPtr = ${ClassName}Parameters::ConstPtr; + } // namespace ${pkgname} + +#ifdef DYNAMIC_RECONFIGURE_FOUND +namespace rosparam_handler +{ + + /// \brief Specialize base class fromConfigImpl. + /// to handle ${pkgname}::${ClassName}Config + + template<> + inline void ParametersBase::fromConfigImpl<${pkgname}::${ClassName}Config>( + const ${pkgname}::${ClassName}Config& config, const uint32_t /*level*/ ) + { + try { + ${pkgname}::${ClassName}Parameters& casted_ref = dynamic_cast<${pkgname}::${ClassName}Parameters&>(*this); + + $fromConfig + } + catch (const std::bad_cast& /*e*/) + { + ROS_FATAL("In ParametersBase::fromConfig, bad cast to type ${pkgname}::${ClassName}Parameters !"); + rosparam_handler::exit("In ParametersBase::fromConfig, bad cast to type ${pkgname}::${ClassName}Parameters !"); + } + } + +} +#endif + +#endif /* _${pkgname}_ROSPARAM_HANDLER_${ClassName}Parameters_H_ */ diff --git a/test/cfg/Bar.params b/test/cfg/Bar.params new file mode 100755 index 0000000..e37c2e9 --- /dev/null +++ b/test/cfg/Bar.params @@ -0,0 +1,16 @@ +#!/usr/bin/env python +####### workaround so that the module is found ####### +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__),"../../src")) +###################################################### + +from rosparam_handler.parameter_generator_catkin import * +gen = ParameterGenerator() + +# Parameters with different types +gen.add("str_param", paramtype="std::string", description="A string parameter", default="param b", configurable=True) +gen.add("vector_int_param", paramtype="std::vector", description="A vector of int parameter", default=[1,2,3]) + +#Syntax : Package, Node, Config Name(The final name will be MyDummyConfig) +exit(gen.generate("rosparam_handler", "rosparam_handler_test", "Bar")) diff --git a/test/cfg/Foo.params b/test/cfg/Foo.params new file mode 100755 index 0000000..3f4eace --- /dev/null +++ b/test/cfg/Foo.params @@ -0,0 +1,16 @@ +#!/usr/bin/env python +####### workaround so that the module is found ####### +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__),"../../src")) +###################################################### + +from rosparam_handler.parameter_generator_catkin import * +gen = ParameterGenerator() + +# Parameters with different types +gen.add("int_param", paramtype="int", description="An Integer parameter", default=1, configurable=True) +gen.add("str_param", paramtype="std::string", description="A string parameter", default="param a", configurable=True) + +#Syntax : Package, Node, Config Name(The final name will be MyDummyConfig) +exit(gen.generate("rosparam_handler", "rosparam_handler_test", "Foo")) diff --git a/test/launch/params/test_launch_parameters.yaml b/test/launch/params/test_launch_parameters.yaml index f7fe217..0cd1c1e 100644 --- a/test/launch/params/test_launch_parameters.yaml +++ b/test/launch/params/test_launch_parameters.yaml @@ -19,4 +19,4 @@ vector_double_param_wo_default: [1.1, 1.2, 1.3] vector_bool_param_wo_default: [false, true] vector_string_param_wo_default: ["Hello","World"] map_param_wo_default: {"Hello": "World"} -enum_param_wo_default: 1 \ No newline at end of file +enum_param_wo_default: 1 diff --git a/test/launch/rosparam_handler.test b/test/launch/rosparam_handler.test index cfecce2..c42da59 100644 --- a/test/launch/rosparam_handler.test +++ b/test/launch/rosparam_handler.test @@ -1,6 +1,6 @@ - + diff --git a/test/src/test_baseParams.cpp b/test/src/test_baseParams.cpp new file mode 100644 index 0000000..f87c9d6 --- /dev/null +++ b/test/src/test_baseParams.cpp @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include + +typedef rosparam_handler::FooParameters FooParameters; +typedef rosparam_handler::BarParameters BarParameters; + +typedef rosparam_handler::FooConfig ConfigA; +typedef rosparam_handler::BarConfig ConfigB; + +class TestRosparamHandlerBase : public testing::Test +{ +public: + + TestRosparamHandlerBase() = default; + ~TestRosparamHandlerBase() = default; + + struct DummyBase + { + DummyBase(const std::string& nh_namespace) : + nh_(nh_namespace) { } + + virtual ~DummyBase() = default; + + template + void init() + { + params_ptr_ = boost::make_shared(nh_); + } + + void fromParamServer() + { + params_ptr_->fromParamServer(); + } + + void toParamServer() + { + params_ptr_->toParamServer(); + } + + std::string getNamespace() + { + return nh_.getNamespace(); + } + + template + void configCallback(T& config, uint32_t /*level*/) + { + params_ptr_->fromConfig(config); + } + + rosparam_handler::ParametersPtr params_ptr_; + + ros::NodeHandle nh_ = ros::NodeHandle("~"); + }; + + struct Foo : public DummyBase + { + Foo() : DummyBase("foo"), dr_srv_(nh_) + { + init(); + + auto cb = boost::bind(&DummyBase::configCallback, this, _1, _2); + dr_srv_.setCallback(cb); + } + + dynamic_reconfigure::Server dr_srv_; + }; + + struct Bar : public DummyBase + { + Bar() : DummyBase("bar"), dr_srv_(nh_) + { + init(); + + auto cb = boost::bind(&DummyBase::configCallback, this, _1, _2); + dr_srv_.setCallback(cb); + } + + dynamic_reconfigure::Server dr_srv_; + }; + + Foo foo_; + Bar bar_; +}; + +TEST_F(TestRosparamHandlerBase, Defaults) { + + ASSERT_NO_THROW(foo_.fromParamServer()); + ASSERT_NO_THROW(bar_.fromParamServer()); + + boost::shared_ptr foo_param_ptr; + boost::shared_ptr bar_param_ptr; + + // Test casting + ASSERT_NO_THROW(foo_param_ptr = boost::dynamic_pointer_cast(foo_.params_ptr_)); + ASSERT_NO_THROW(bar_param_ptr = boost::dynamic_pointer_cast(bar_.params_ptr_)); + + ASSERT_NE(nullptr, foo_param_ptr); + ASSERT_NE(nullptr, bar_param_ptr); + + // Test casting with helper function + ASSERT_NO_THROW(foo_param_ptr = rosparam_handler::dynamic_parameters_cast(foo_.params_ptr_)); + ASSERT_NO_THROW(bar_param_ptr = rosparam_handler::dynamic_parameters_cast(bar_.params_ptr_)); + + ASSERT_NE(nullptr, foo_param_ptr); + ASSERT_NE(nullptr, bar_param_ptr); + + ASSERT_EQ(1, foo_param_ptr->int_param); + ASSERT_EQ("param a", foo_param_ptr->str_param); + + ASSERT_EQ("param b", bar_param_ptr->str_param); + ASSERT_EQ(std::vector({1, 2, 3}), bar_param_ptr->vector_int_param); +} + +TEST_F(TestRosparamHandlerBase, DefaultsOnParamServer) { + + ASSERT_NO_THROW(foo_.fromParamServer()); + ASSERT_NO_THROW(bar_.fromParamServer()); + + boost::shared_ptr foo_param_ptr; + boost::shared_ptr bar_param_ptr; + + ASSERT_NO_THROW(foo_param_ptr = rosparam_handler::dynamic_parameters_cast(foo_.params_ptr_)); + ASSERT_NO_THROW(bar_param_ptr = rosparam_handler::dynamic_parameters_cast(bar_.params_ptr_)); + + ASSERT_NE(nullptr, foo_param_ptr); + ASSERT_NE(nullptr, bar_param_ptr); + + ros::NodeHandle nh("~"); + + // values should now be set on param server + { + int value; + ASSERT_TRUE(nh.getParam(foo_.getNamespace() + "/int_param", value)); + ASSERT_EQ(value, foo_param_ptr->int_param); + } + { + std::string value; + ASSERT_TRUE(nh.getParam(foo_.getNamespace() + "/str_param", value)); + ASSERT_EQ(value, foo_param_ptr->str_param); + } + { + std::vector value; + ASSERT_TRUE(nh.getParam(bar_.getNamespace() + "/vector_int_param", value)); + ASSERT_EQ(value, bar_param_ptr->vector_int_param); + } + { + std::string value; + ASSERT_TRUE(nh.getParam(bar_.getNamespace() + "/str_param", value)); + ASSERT_EQ(value, bar_param_ptr->str_param); + } +} + +TEST_F(TestRosparamHandlerBase, SetParamOnServer) { + + ASSERT_NO_THROW(foo_.fromParamServer()); + ASSERT_NO_THROW(bar_.fromParamServer()); + + boost::shared_ptr foo_param_ptr; + boost::shared_ptr bar_param_ptr; + + ASSERT_NO_THROW(foo_param_ptr = rosparam_handler::dynamic_parameters_cast(foo_.params_ptr_)); + ASSERT_NO_THROW(bar_param_ptr = rosparam_handler::dynamic_parameters_cast(bar_.params_ptr_)); + + ASSERT_NE(nullptr, foo_param_ptr); + ASSERT_NE(nullptr, bar_param_ptr); + + foo_param_ptr->int_param = 9; + foo_param_ptr->str_param = "Hello"; + + bar_param_ptr->vector_int_param = {4,2}; + bar_param_ptr->str_param = "World"; + + ASSERT_NO_THROW(foo_.toParamServer()); + ASSERT_NO_THROW(bar_.toParamServer()); + + ros::NodeHandle nh("~"); + + // values should now be set on param server + { + int value; + ASSERT_TRUE(nh.getParam(foo_.getNamespace() + "/int_param", value)); + ASSERT_EQ(value, foo_param_ptr->int_param); + } + { + std::string value; + ASSERT_TRUE(nh.getParam(foo_.getNamespace() + "/str_param", value)); + ASSERT_EQ(value, foo_param_ptr->str_param); + } + { + std::vector value; + ASSERT_TRUE(nh.getParam(bar_.getNamespace() + "/vector_int_param", value)); + ASSERT_EQ(value, bar_param_ptr->vector_int_param); + } + { + std::string value; + ASSERT_TRUE(nh.getParam(bar_.getNamespace() + "/str_param", value)); + ASSERT_EQ(value, bar_param_ptr->str_param); + } +} + +TEST_F(TestRosparamHandlerBase, DynamicReconf) +{ + ASSERT_NO_THROW(foo_.fromParamServer()); + ASSERT_NO_THROW(bar_.fromParamServer()); + + boost::shared_ptr foo_param_ptr; + boost::shared_ptr bar_param_ptr; + + ASSERT_NO_THROW(foo_param_ptr = rosparam_handler::dynamic_parameters_cast(foo_.params_ptr_)); + ASSERT_NO_THROW(bar_param_ptr = rosparam_handler::dynamic_parameters_cast(bar_.params_ptr_)); + + ASSERT_NE(nullptr, foo_param_ptr); + ASSERT_NE(nullptr, bar_param_ptr); + + dynamic_reconfigure::ReconfigureRequest srv_req; + dynamic_reconfigure::ReconfigureResponse srv_resp; + dynamic_reconfigure::Config conf; + + dynamic_reconfigure::IntParameter int_param; + int_param.name = "int_param"; + int_param.value = 0; + + dynamic_reconfigure::StrParameter str_param; + str_param.name = "str_param"; + str_param.value = "dynamic foo string"; + + conf.ints.push_back(int_param); + conf.strs.push_back(str_param); + + srv_req.config = conf; + + ros::service::call(foo_.getNamespace() + "/set_parameters", srv_req, srv_resp); + + conf = dynamic_reconfigure::Config(); + str_param.name = "str_param"; + str_param.value = "dynamic bar string"; + + conf.strs.push_back(str_param); + + srv_req.config = conf; + + ros::service::call(bar_.getNamespace() + "/set_parameters", srv_req, srv_resp); + + ros::Duration(1).sleep(); + + ASSERT_EQ(0, foo_param_ptr->int_param); + ASSERT_EQ("dynamic foo string", foo_param_ptr->str_param); + + ASSERT_EQ("dynamic bar string", bar_param_ptr->str_param); +} diff --git a/test/src/test_copy.cpp b/test/src/test_copy.cpp new file mode 100644 index 0000000..62936a2 --- /dev/null +++ b/test/src/test_copy.cpp @@ -0,0 +1,118 @@ +#include +#include +#include + +typedef rosparam_handler::DefaultsParameters ParamType; + +TEST(RosparamHandler, CopyInit) { + ParamType testParams(ros::NodeHandle("~copy_init")); + ASSERT_NO_THROW(testParams.fromParamServer()); + + ASSERT_EQ(1, testParams.int_param_w_default); + ASSERT_DOUBLE_EQ(1.1, testParams.double_param_w_default); + ASSERT_EQ("Hello World", testParams.str_param_w_default); + ASSERT_TRUE(testParams.bool_param_w_default); + + ASSERT_EQ(std::vector({1, 2, 3}), testParams.vector_int_param_w_default); + ASSERT_EQ(std::vector({1.1, 1.2, 1.3}), testParams.vector_double_param_w_default); + ASSERT_EQ(std::vector({false, true}), testParams.vector_bool_param_w_default); + ASSERT_EQ(std::vector({"Hello", "World"}), testParams.vector_string_param_w_default); + + std::map tmp{{"Hello", "World"}}; + ASSERT_EQ(tmp, testParams.map_param_w_default); + + ASSERT_EQ(1, testParams.enum_param_w_default); + + testParams.int_param_w_default = 2; + testParams.double_param_w_default = 2.2; + testParams.str_param_w_default = "Foo Bar"; + testParams.bool_param_w_default = false; + testParams.vector_int_param_w_default = {4, 5, 6}; + testParams.vector_double_param_w_default = {1.4, 1.5, 1.6}; + testParams.vector_bool_param_w_default = {true, false}; + testParams.vector_string_param_w_default = {"Foo", "Bar"}; + testParams.map_param_w_default = {{"Foo", "Bar"}}; + testParams.enum_param_w_default = 2; + + ParamType testParamsCopy(testParams); + + ASSERT_EQ(2, testParamsCopy.int_param_w_default); + ASSERT_DOUBLE_EQ(2.2, testParamsCopy.double_param_w_default); + ASSERT_EQ("Foo Bar", testParamsCopy.str_param_w_default); + ASSERT_FALSE(testParamsCopy.bool_param_w_default); + + ASSERT_EQ(std::vector({4, 5, 6}), testParamsCopy.vector_int_param_w_default); + ASSERT_EQ(std::vector({1.4, 1.5, 1.6}), testParamsCopy.vector_double_param_w_default); + ASSERT_EQ(std::vector({true, false}), testParamsCopy.vector_bool_param_w_default); + ASSERT_EQ(std::vector({"Foo", "Bar"}), testParamsCopy.vector_string_param_w_default); + + tmp = {{"Foo", "Bar"}}; + ASSERT_EQ(tmp, testParamsCopy.map_param_w_default); + + ASSERT_EQ(2, testParamsCopy.enum_param_w_default); +} + +TEST(RosparamHandler, CopyAssign) { + ParamType testParams(ros::NodeHandle("~copy_assign")); + ASSERT_NO_THROW(testParams.fromParamServer()); + + ASSERT_EQ(1, testParams.int_param_w_default); + ASSERT_DOUBLE_EQ(1.1, testParams.double_param_w_default); + ASSERT_EQ("Hello World", testParams.str_param_w_default); + ASSERT_TRUE(testParams.bool_param_w_default); + + ASSERT_EQ(std::vector({1, 2, 3}), testParams.vector_int_param_w_default); + ASSERT_EQ(std::vector({1.1, 1.2, 1.3}), testParams.vector_double_param_w_default); + ASSERT_EQ(std::vector({false, true}), testParams.vector_bool_param_w_default); + ASSERT_EQ(std::vector({"Hello", "World"}), testParams.vector_string_param_w_default); + + std::map tmp{{"Hello", "World"}}; + ASSERT_EQ(tmp, testParams.map_param_w_default); + + ASSERT_EQ(1, testParams.enum_param_w_default); + + ParamType testParamsCopy(ros::NodeHandle("~")); + ASSERT_NO_THROW(testParamsCopy.fromParamServer()); + + ASSERT_EQ(1, testParamsCopy.int_param_w_default); + ASSERT_DOUBLE_EQ(1.1, testParamsCopy.double_param_w_default); + ASSERT_EQ("Hello World", testParamsCopy.str_param_w_default); + ASSERT_TRUE(testParamsCopy.bool_param_w_default); + + ASSERT_EQ(std::vector({1, 2, 3}), testParamsCopy.vector_int_param_w_default); + ASSERT_EQ(std::vector({1.1, 1.2, 1.3}), testParamsCopy.vector_double_param_w_default); + ASSERT_EQ(std::vector({false, true}), testParamsCopy.vector_bool_param_w_default); + ASSERT_EQ(std::vector({"Hello", "World"}), testParamsCopy.vector_string_param_w_default); + + ASSERT_EQ(tmp, testParamsCopy.map_param_w_default); + + ASSERT_EQ(1, testParamsCopy.enum_param_w_default); + + testParams.int_param_w_default = 2; + testParams.double_param_w_default = 2.2; + testParams.str_param_w_default = "Foo Bar"; + testParams.bool_param_w_default = false; + testParams.vector_int_param_w_default = {4, 5, 6}; + testParams.vector_double_param_w_default = {1.4, 1.5, 1.6}; + testParams.vector_bool_param_w_default = {true, false}; + testParams.vector_string_param_w_default = {"Foo", "Bar"}; + testParams.map_param_w_default = {{"Foo", "Bar"}}; + testParams.enum_param_w_default = 2; + + testParamsCopy = testParams; + + ASSERT_EQ(2, testParamsCopy.int_param_w_default); + ASSERT_DOUBLE_EQ(2.2, testParamsCopy.double_param_w_default); + ASSERT_EQ("Foo Bar", testParamsCopy.str_param_w_default); + ASSERT_FALSE(testParamsCopy.bool_param_w_default); + + ASSERT_EQ(std::vector({4, 5, 6}), testParamsCopy.vector_int_param_w_default); + ASSERT_EQ(std::vector({1.4, 1.5, 1.6}), testParamsCopy.vector_double_param_w_default); + ASSERT_EQ(std::vector({true, false}), testParamsCopy.vector_bool_param_w_default); + ASSERT_EQ(std::vector({"Foo", "Bar"}), testParamsCopy.vector_string_param_w_default); + + tmp = {{"Foo", "Bar"}}; + ASSERT_EQ(tmp, testParamsCopy.map_param_w_default); + + ASSERT_EQ(2, testParamsCopy.enum_param_w_default); +} diff --git a/test/src/test_defaults.cpp b/test/src/test_defaults.cpp index ef3312d..c58e531 100644 --- a/test/src/test_defaults.cpp +++ b/test/src/test_defaults.cpp @@ -6,7 +6,7 @@ typedef rosparam_handler::DefaultsParameters ParamType; typedef rosparam_handler::DefaultsConfig ConfigType; TEST(RosparamHandler, Defaults) { - ParamType testParams(ros::NodeHandle("~")); + ParamType testParams(ros::NodeHandle("~defaults")); ASSERT_NO_THROW(testParams.fromParamServer()); ASSERT_EQ(1, testParams.int_param_w_default); @@ -26,7 +26,7 @@ TEST(RosparamHandler, Defaults) { } TEST(RosparamHandler, DefaultsOnParamServer) { - ros::NodeHandle nh("~"); + ros::NodeHandle nh("~defaults_on_param_server"); ParamType testParams(nh); ASSERT_NO_THROW(testParams.fromParamServer()); @@ -84,7 +84,7 @@ TEST(RosparamHandler, DefaultsOnParamServer) { } TEST(RosparamHandler, SetParamOnServer) { - ros::NodeHandle nh("~"); + ros::NodeHandle nh("~set_param_on_server"); ParamType testParams(nh); ASSERT_NO_THROW(testParams.fromParamServer()); diff --git a/test/src/test_defaultsAtLaunch.cpp b/test/src/test_defaultsAtLaunch.cpp index 4e935f9..d3d4c73 100644 --- a/test/src/test_defaultsAtLaunch.cpp +++ b/test/src/test_defaultsAtLaunch.cpp @@ -6,7 +6,7 @@ typedef rosparam_handler::DefaultsAtLaunchParameters ParamType; typedef rosparam_handler::DefaultsAtLaunchConfig ConfigType; TEST(RosparamHandler, DefaultsAtLaunch) { - ParamType testParams(ros::NodeHandle("~")); + ParamType testParams(ros::NodeHandle("~defaults_at_launch")); ASSERT_NO_THROW(testParams.fromParamServer()); ASSERT_EQ(1, testParams.int_param_wo_default); diff --git a/test/src/test_defaultsMissing.cpp b/test/src/test_defaultsMissing.cpp index 6910285..b33ba92 100644 --- a/test/src/test_defaultsMissing.cpp +++ b/test/src/test_defaultsMissing.cpp @@ -6,6 +6,6 @@ typedef rosparam_handler::DefaultsMissingParameters ParamType; typedef rosparam_handler::DefaultsMissingConfig ConfigType; TEST(RosparamHandler, DefaultsMissing) { - ParamType testParams(ros::NodeHandle("~")); + ParamType testParams(ros::NodeHandle("~default_missing")); ASSERT_THROW(testParams.fromParamServer(), std::runtime_error); } diff --git a/test/src/test_dynamicReconfigure.cpp b/test/src/test_dynamicReconfigure.cpp index eb71c66..bc775c1 100644 --- a/test/src/test_dynamicReconfigure.cpp +++ b/test/src/test_dynamicReconfigure.cpp @@ -12,7 +12,7 @@ class TestDynamicReconfigure : public testing::Test public: TestDynamicReconfigure() - : nh("~") + : nh("~dynamic_reconfigure") , testParams(nh) , drSrv(nh) { @@ -36,12 +36,6 @@ class TestDynamicReconfigure : public testing::Test TEST_F(TestDynamicReconfigure, DynamicReconf) { - // Delete in case they are still on the server. - nh.deleteParam("int_param_w_default"); - nh.deleteParam("double_param_w_default"); - nh.deleteParam("str_param_w_default"); - nh.deleteParam("bool_param_w_default"); - ASSERT_NO_THROW(testParams.fromParamServer()); ASSERT_EQ(1, testParams.int_param_w_default); diff --git a/test/src/test_minMax.cpp b/test/src/test_minMax.cpp index 4d21f96..42102e8 100644 --- a/test/src/test_minMax.cpp +++ b/test/src/test_minMax.cpp @@ -6,7 +6,7 @@ typedef rosparam_handler::MinMaxParameters ParamType; typedef rosparam_handler::MinMaxConfig ConfigType; TEST(RosparamHandler, MinMax) { - ParamType testParams(ros::NodeHandle("~")); + ParamType testParams(ros::NodeHandle("~min_max")); ASSERT_NO_THROW(testParams.fromParamServer()); ASSERT_EQ(2, testParams.int_param_w_minmax); @@ -17,4 +17,4 @@ TEST(RosparamHandler, MinMax) { std::map tmp{{"value1", 0.}, {"value2", 1.2}, {"value3", 2.}}; ASSERT_EQ(tmp, testParams.map_param_w_minmax); -} \ No newline at end of file +}