Compare commits

...

17 Commits

Author SHA1 Message Date
William Woodall
36ca813813 another wip
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-02-04 14:28:03 -08:00
William Woodall
8f0705b08b style
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-02-04 10:21:37 -08:00
William Woodall
2e32bc300c provide implementation for templated declare_parameter method
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-02-01 11:14:31 -08:00
William Woodall
7e5e75a748 replace create_parameter with declare_parameter
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-02-01 09:18:49 -08:00
William Woodall
c94a1bf286 more fixup after rebase
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-02-01 09:16:57 -08:00
William Woodall
17b3b971b3 fixup after rebase
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-02-01 09:16:54 -08:00
William Woodall
c026f89650 match type of enum in C++ to type used in message definition
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-01-29 11:05:30 -08:00
William Woodall
1eecfe73cc avoid const pass by value
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-01-29 11:05:30 -08:00
William Woodall
2e20bcb844 enable get<Parameter> and get<ParameterValue> on Parameter class
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-01-29 11:05:30 -08:00
William Woodall
be9f07a414 add method to access ParameterValue within a Parameter
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-01-29 11:05:30 -08:00
William Woodall
3f344549b9 rename ParameterInfo_t to ParameterInfo and just use struct, no typedef
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-01-29 11:05:30 -08:00
William Woodall
58fd8edee1 use override rather than virtual in places
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-01-29 11:05:30 -08:00
William Woodall
b0ac4453a3 doc fixup
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-01-29 11:04:57 -08:00
Shane Loretz
3845f03d8a Only get parameter if it is set 2019-01-29 11:04:57 -08:00
Shane Loretz
6651abcbe9 test undeclared params 2019-01-29 11:04:57 -08:00
Shane Loretz
e86de20b7a style 2019-01-29 11:04:57 -08:00
Shane Loretz
bf03325e0a in progress broken test_time_source 2019-01-29 11:04:53 -08:00
15 changed files with 671 additions and 81 deletions

View File

@@ -95,6 +95,7 @@ public:
* pipeline to pass messages between nodes in the same process using shared memory.
* \param[in] start_parameter_services True to setup ROS interfaces for accessing parameters
* in the node.
* \param[in] allow_undeclared_parameters True to allow any parameter name to be set on the node.
*/
RCLCPP_PUBLIC
Node(
@@ -105,7 +106,8 @@ public:
const std::vector<Parameter> & initial_parameters,
bool use_global_arguments = true,
bool use_intra_process_comms = false,
bool start_parameter_services = true);
bool start_parameter_services = true,
bool allow_undeclared_parameters = false);
RCLCPP_PUBLIC
virtual ~Node();
@@ -265,6 +267,36 @@ public:
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
/// Declare and initialize a parameter.
/**
* This method is used to declare that a parameter exists on this node.
* If a run-time user has provided an an initial value then it will be set in this method,
* otherwise the default_value will be set.
* \param[in] name the name of the parameter
* \param[in] default_value An initial value to be used if a run-time user did not override it.
* \param[in] read_only if True then this parameter may not be changed after initialization.
* \throws std::runtime_error if parameter has already been declared.
* \throws std::runtime_error if a parameter name is invalid.
* \throws rclcpp::exceptions::InvalidParameterValueException if initial value fails to be set.
*/
RCLCPP_PUBLIC
void
declare_parameter(
const std::string & name,
const rclcpp::ParameterValue & default_value = rclcpp::ParameterValue(),
bool read_only = false);
/// Declare and initialize a parameter with a type.
/**
* See the non-templated declare_parameter() on this class for details.
*/
template<typename ParameterT>
void
declare_parameter(
const std::string & name,
const ParameterT & default_value,
bool read_only = false);
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(const std::vector<rclcpp::Parameter> & parameters);

View File

@@ -210,6 +210,16 @@ Node::register_param_change_callback(CallbackT && callback)
this->node_parameters_->register_param_change_callback(std::forward<CallbackT>(callback));
}
template<typename ParameterT>
void
Node::declare_parameter(
const std::string & name,
const ParameterT & default_value,
bool read_only)
{
this->declare_parameter(name, rclcpp::ParameterValue(default_value), read_only);
}
template<typename ParameterT>
void
Node::set_parameter_if_not_set(

View File

@@ -0,0 +1,162 @@
// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__NODE_INTERFACES__GET_NODE_PARAMETERS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__GET_NODE_PARAMETERS_INTERFACE_HPP_
#include <memory>
#include <utility>
#include <type_traits>
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
/// This header provides the get_node_parameters_interface() template function.
/**
* This function is useful for getting the NodeParametersInterface pointer from
* various kinds of Node-like classes.
*
* It's able to get the NodeParametersInterface pointer so long as the class
* has a method called ``get_node_parameters_interface()`` which returns
* either a pointer (const or not) to a NodeParametersInterface or a
* std::shared_ptr to a NodeParametersInterface.
*/
namespace rclcpp
{
namespace node_interfaces
{
namespace detail
{
// This is a meta-programming checker for if a given Node-like object has a
// getter called get_node_parameters_interface() which returns various types,
// e.g. const pointer or a shared pointer.
template<typename NodeType, typename ReturnType>
struct has_get_node_parameters_interface
{
private:
template<typename T>
static constexpr
auto
check(T *)->typename std::is_same<
decltype(std::declval<T>().get_node_parameters_interface()),
ReturnType
>::type;
template<typename>
static constexpr
std::false_type
check(...);
public:
using type = decltype(check<NodeType>(nullptr));
static constexpr bool value = type::value;
};
// If NodeType is a pointer to NodeParametersInterface already (just normal function overload).
inline
const rclcpp::node_interfaces::NodeParametersInterface *
get_node_parameters_interface_from_pointer(
const rclcpp::node_interfaces::NodeParametersInterface * pointer)
{
return pointer;
}
// If NodeType has a method called get_node_parameters_interface() which returns a shared pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_parameters_interface<
typename std::remove_pointer<NodeType>::type,
std::shared_ptr<rclcpp::node_interfaces::NodeParametersInterface>
>::value, int>::type = 0
>
const rclcpp::node_interfaces::NodeParametersInterface *
get_node_parameters_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_parameters_interface().get();
}
// If NodeType has a method called get_node_parameters_interface() which returns a pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_parameters_interface<
typename std::remove_pointer<NodeType>::type,
rclcpp::node_interfaces::NodeParametersInterface *
>::value, int>::type = 0
>
const rclcpp::node_interfaces::NodeParametersInterface *
get_node_parameters_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_parameters_interface();
}
// If NodeType has a method called get_node_parameters_interface() which returns a const pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_parameters_interface<
typename std::remove_pointer<NodeType>::type,
const rclcpp::node_interfaces::NodeParametersInterface *
>::value, int>::type = 0
>
const rclcpp::node_interfaces::NodeParametersInterface *
get_node_parameters_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_parameters_interface();
}
// Forward shared_ptr's to const node pointer signatures.
template<
typename NodeType,
typename std::enable_if<std::is_same<
NodeType,
typename std::shared_ptr<typename std::remove_pointer<NodeType>::type::element_type> *
>::value, int>::type = 0
>
const rclcpp::node_interfaces::NodeParametersInterface *
get_node_parameters_interface_from_pointer(NodeType node_shared_pointer)
{
return get_node_parameters_interface_from_pointer(node_shared_pointer->get());
}
} // namespace detail
/// Get the NodeParametersInterface as a const pointer from a pointer to a "Node like" object.
template<
typename NodeType,
typename std::enable_if<std::is_pointer<NodeType>::value, int>::type = 0
>
const rclcpp::node_interfaces::NodeParametersInterface *
get_node_parameters_interface(NodeType && node_pointer)
{
// Forward pointers to detail implmentation directly.
return detail::get_node_parameters_interface_from_pointer(std::forward<NodeType>(node_pointer));
}
/// Get the NodeParametersInterface as a const pointer from a "Node like" object.
template<
typename NodeType,
typename std::enable_if<!std::is_pointer<NodeType>::value, int>::type = 0
>
const rclcpp::node_interfaces::NodeParametersInterface *
get_node_parameters_interface(NodeType && node_reference)
{
// Forward const-references to detail implmentation as a pointer.
return detail::get_node_parameters_interface_from_pointer(&node_reference);
}
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__GET_NODE_PARAMETERS_INTERFACE_HPP_

View File

@@ -61,6 +61,7 @@ private:
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
rclcpp::Clock::SharedPtr ros_clock_;

View File

@@ -40,6 +40,19 @@ namespace rclcpp
namespace node_interfaces
{
// Internal struct for holding useful info about parameters
struct ParameterInfo
{
/// True if a user called declare_parameter()
bool is_declared = false;
/// Current value of the parameter.
rclcpp::ParameterValue value;
/// A description of the parameter
rcl_interfaces::msg::ParameterDescriptor descriptor;
};
/// Implementation of the NodeParameters part of the Node API.
class NodeParameters : public NodeParametersInterface
{
@@ -54,67 +67,65 @@ public:
const node_interfaces::NodeClockInterface::SharedPtr node_clock,
const std::vector<Parameter> & initial_parameters,
bool use_intra_process,
bool start_parameter_services);
bool start_parameter_services,
bool allow_undeclared_parameters);
RCLCPP_PUBLIC
virtual
~NodeParameters();
RCLCPP_PUBLIC
virtual
void
declare_parameter(
const std::string & name,
const rclcpp::ParameterValue & default_value,
bool read_only) override;
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(
const std::vector<rclcpp::Parameter> & parameters);
const std::vector<rclcpp::Parameter> & parameters) override;
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(
const std::vector<rclcpp::Parameter> & parameters);
const std::vector<rclcpp::Parameter> & parameters) override;
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::Parameter>
get_parameters(const std::vector<std::string> & names) const;
get_parameters(const std::vector<std::string> & names) const override;
RCLCPP_PUBLIC
virtual
rclcpp::Parameter
get_parameter(const std::string & name) const;
get_parameter(const std::string & name) const override;
RCLCPP_PUBLIC
virtual
bool
get_parameter(
const std::string & name,
rclcpp::Parameter & parameter) const;
rclcpp::Parameter & parameter) const override;
RCLCPP_PUBLIC
virtual
bool
get_parameters_by_prefix(
const std::string & prefix,
std::map<std::string, rclcpp::Parameter> & parameters) const;
std::map<std::string, rclcpp::Parameter> & parameters) const override;
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const;
describe_parameters(const std::vector<std::string> & names) const override;
RCLCPP_PUBLIC
virtual
std::vector<uint8_t>
get_parameter_types(const std::vector<std::string> & names) const;
get_parameter_types(const std::vector<std::string> & names) const override;
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const;
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const override;
RCLCPP_PUBLIC
virtual
void
register_param_change_callback(ParametersCallbackFunction callback);
register_param_change_callback(ParametersCallbackFunction callback) override;
private:
RCLCPP_DISABLE_COPY(NodeParameters)
@@ -123,7 +134,11 @@ private:
ParametersCallbackFunction parameters_callback_ = nullptr;
std::map<std::string, rclcpp::Parameter> parameters_;
std::map<std::string, ParameterInfo> parameters_;
std::map<std::string, rclcpp::ParameterValue> initial_parameter_values_;
bool allow_undeclared_ = false;
Publisher<rcl_interfaces::msg::ParameterEvent>::SharedPtr events_publisher_;

View File

@@ -38,6 +38,25 @@ class NodeParametersInterface
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeParametersInterface)
/// Declare and initialize a parameter.
/* This method is used to declare that a parameter exists on this node.
* If a run-time user has provided an an initial value then it will be set in this method,
* otherwise the default_value will be set.
* \param[in] name the name of the parameter
* \param[in] default_value An initial value to be used if a run-time user did not override it.
* \param[in] read_only if True then this parameter may not be changed after initialization.
* \throws std::runtime_error if parameter has already been declared.
* \throws std::runtime_error if a parameter name is invalid.
* \throws rclcpp::exceptions::InvalidParameterValueException if initial value fails to be set.
*/
RCLCPP_PUBLIC
virtual
void
declare_parameter(
const std::string & name,
const rclcpp::ParameterValue & default_value = rclcpp::ParameterValue(),
bool read_only = false) = 0;
RCLCPP_PUBLIC
virtual
~NodeParametersInterface() = default;

View File

@@ -28,6 +28,25 @@
namespace rclcpp
{
class Parameter;
namespace node_interfaces
{
struct ParameterInfo;
} // namespace node_interfaces
namespace detail
{
// This helper function is required because you cannot do specialization on a
// class method, so instead we specialize this template function and call it
// from the unspecialized, but dependent, class method.
template<typename T>
auto
get_value_helper(const rclcpp::Parameter * parameter);
} // namespace detail
/// Structure to store an arbitrary parameter with templated get/set methods.
class Parameter
{
@@ -44,6 +63,9 @@ public:
{
}
RCLCPP_PUBLIC
explicit Parameter(const rclcpp::node_interfaces::ParameterInfo & parameter_info);
RCLCPP_PUBLIC
ParameterType
get_type() const;
@@ -60,6 +82,11 @@ public:
rcl_interfaces::msg::ParameterValue
get_value_message() const;
/// Get the internal storage for the parameter value.
RCLCPP_PUBLIC
const rclcpp::ParameterValue &
get_parameter_value() const;
/// Get value of parameter using rclcpp::ParameterType as template argument.
template<ParameterType ParamT>
decltype(auto)
@@ -71,10 +98,7 @@ public:
/// Get value of parameter using c++ types as template argument.
template<typename T>
decltype(auto)
get_value() const
{
return value_.get<T>();
}
get_value() const;
RCLCPP_PUBLIC
bool
@@ -142,6 +166,49 @@ RCLCPP_PUBLIC
std::ostream &
operator<<(std::ostream & os, const std::vector<Parameter> & parameters);
namespace detail
{
template<typename T>
auto
get_value_helper(const rclcpp::Parameter * parameter)
{
return parameter->get_parameter_value().get<T>();
}
// Specialization allowing Parameter::get() to return a const ref to the parameter value object.
template<>
inline
auto
get_value_helper<rclcpp::ParameterValue>(const rclcpp::Parameter * parameter)
{
return parameter->get_parameter_value();
}
// Specialization allowing Parameter::get() to return a const ref to the parameter itself.
template<>
inline
auto
get_value_helper<rclcpp::Parameter>(const rclcpp::Parameter * parameter)
{
// Use this labmda to ensure it's a const reference being returned (and not a copy).
auto type_enforcing_lambda =
[&parameter]() -> const rclcpp::Parameter & {
return *parameter;
};
return type_enforcing_lambda();
}
} // namespace detail
template<typename T>
decltype(auto)
Parameter::get_value() const
{
// use the helper to specialize for the ParameterValue and Parameter cases.
return detail::get_value_helper<T>(this);
}
} // namespace rclcpp
namespace std

View File

@@ -28,7 +28,8 @@
namespace rclcpp
{
enum ParameterType
enum ParameterType : uint8_t
{
PARAMETER_NOT_SET = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET,
PARAMETER_BOOL = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
@@ -45,11 +46,11 @@ enum ParameterType
/// Return the name of a parameter type
RCLCPP_PUBLIC
std::string
to_string(const ParameterType type);
to_string(ParameterType type);
RCLCPP_PUBLIC
std::ostream &
operator<<(std::ostream & os, const ParameterType type);
operator<<(std::ostream & os, ParameterType type);
/// Indicate the parameter type does not match the expected type.
class ParameterTypeException : public std::runtime_error

View File

@@ -0,0 +1,115 @@
// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__UNUSED_PARAMETERS_CHECKER_HPP_
#define RCLCPP__UNUSED_PARAMETERS_CHECKER_HPP_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "rclcpp/node_interfaces/get_node_parameters_interface.hpp"
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// Check a Node-like class for unused parameters.
/**
* This class can be used to detect misconfigurations and typos by ensuring all
* initial parameter values that were passed to the Node-like object were used.
* So this class's methods are used after or during Node construction and after
* all parameters have been declared.
*
* This class must not outlive the Node that it's being used with.
*/
class UnusedParametersChecker
{
public:
template<typename NodeType>
explicit UnusedParametersChecker(NodeType && node_like)
: node_parameters_interface_(
rclcpp::node_interfaces::get_node_parameters_interface(std::forward<NodeType>(node_like)))
{}
/// Warn if any initial parameter values have not been used.
/**
* This function will complain with a RCLCPP_WARN if any provided initial
* parameter values have not been used.
*
* \throws std::bad_alloc when trying to create an error message
*/
RCLCPP_PUBLIC
void
check_and_warn() const;
/// Throw an UnusedParameterExecption if any initial parameter values have not been used.
/**
* This function will throw an exception if any provided initial
* parameter values have not been used.
*
* \throws std::bad_alloc when trying to create an error message
* \throws rclcpp::UnusedParametersException when there are unused parameters
*/
RCLCPP_PUBLIC
void
check_and_throw() const;
/// Return the number of unused initial parameter values.
/**
* Similar to get_unused_initial_parameter_values(), but it returns the
* number of unused parameter values rather than a vector of the unused
* parameters (which involves allocating storage for the copies).
* This function is faster and avoids memory allocation while checking for a
* problem, and if one is detected then get_unused_initial_parameter_values()
* may be used to format a useful error message.
*
* \returns the number of unused initial parameter values
*/
RCLCPP_PUBLIC
size_t
count_unused_initial_parameter_values() const;
/// Return the list of unused initial parameter values.
/**
* A common case where this returns a non-empty vector, is when someone makes
* a typo when setting the parameters from outside the node.
* For example, if they use `ip_addr` rather than the expected `ip_address`.
*
* \returns vector of parameters which where passed to the node but where
* not declared before this function was called.
* \throws std::bad_alloc
*/
RCLCPP_PUBLIC
std::vector<rclcpp::Parameter>
get_unused_initial_parameter_values() const;
private:
const rclcpp::node_interfaces::NodeParametersInterface * node_parameters_interface_;
};
/// Thrown when throw_if_unused_initialized_parameter_values() finds unused parameters.
class UnusedParametersException : public std::runtime_error
{
public:
explicit UnusedParametersException(const std::vector<rclcpp::Parameter> & unused_parameters);
};
} // namespace rclcpp
#endif // RCLCPP__UNUSED_PARAMETERS_CHECKER_HPP_

View File

@@ -60,7 +60,8 @@ Node::Node(
const std::vector<rclcpp::Parameter> & initial_parameters,
bool use_global_arguments,
bool use_intra_process_comms,
bool start_parameter_services)
bool start_parameter_services,
bool allow_undeclared_parameters)
: node_base_(new rclcpp::node_interfaces::NodeBase(
node_name, namespace_, context, arguments, use_global_arguments)),
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
@@ -82,7 +83,8 @@ Node::Node(
node_clock_,
initial_parameters,
use_intra_process_comms,
start_parameter_services
start_parameter_services,
allow_undeclared_parameters
)),
node_time_source_(new rclcpp::node_interfaces::NodeTimeSource(
node_base_,
@@ -132,6 +134,15 @@ Node::group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group)
return node_base_->callback_group_in_node(group);
}
void
Node::declare_parameter(
const std::string & name,
const rclcpp::ParameterValue & default_value,
bool read_only)
{
return this->node_parameters_->declare_parameter(name, default_value, read_only);
}
std::vector<rcl_interfaces::msg::SetParametersResult>
Node::set_parameters(
const std::vector<rclcpp::Parameter> & parameters)

View File

@@ -39,8 +39,9 @@ NodeParameters::NodeParameters(
const rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
const std::vector<rclcpp::Parameter> & initial_parameters,
bool use_intra_process,
bool start_parameter_services)
: node_clock_(node_clock)
bool start_parameter_services,
bool allow_undeclared_parameters)
: allow_undeclared_(allow_undeclared_parameters), node_clock_(node_clock)
{
using MessageT = rcl_interfaces::msg::ParameterEvent;
using PublisherT = rclcpp::Publisher<MessageT>;
@@ -114,8 +115,6 @@ NodeParameters::NodeParameters(
combined_name_ = node_namespace + '/' + node_name;
}
std::map<std::string, rclcpp::Parameter> parameters;
// TODO(sloretz) use rcl to parse yaml when circular dependency is solved
// See https://github.com/ros2/rcl/issues/252
for (const std::string & yaml_path : yaml_paths) {
@@ -140,25 +139,19 @@ NodeParameters::NodeParameters(
// Combine parameter yaml files, overwriting values in older ones
for (auto & param : iter->second) {
parameters[param.get_name()] = param;
initial_parameter_values_[param.get_name()] =
rclcpp::ParameterValue(param.get_value_message());
}
}
// initial values passed to constructor overwrite yaml file sources
for (auto & param : initial_parameters) {
parameters[param.get_name()] = param;
initial_parameter_values_[param.get_name()] =
rclcpp::ParameterValue(param.get_value_message());
}
std::vector<rclcpp::Parameter> combined_values;
combined_values.reserve(parameters.size());
for (auto & kv : parameters) {
combined_values.emplace_back(kv.second);
}
// TODO(sloretz) store initial values and use them when a parameter is created ros2/rclcpp#475
// Set initial parameter values
if (!combined_values.empty()) {
rcl_interfaces::msg::SetParametersResult result = set_parameters_atomically(combined_values);
if (!initial_parameters.empty() && allow_undeclared_) {
rcl_interfaces::msg::SetParametersResult result = set_parameters_atomically(initial_parameters);
if (!result.successful) {
throw std::runtime_error("Failed to set initial parameters");
}
@@ -168,6 +161,59 @@ NodeParameters::NodeParameters(
NodeParameters::~NodeParameters()
{}
void
NodeParameters::declare_parameter(
const std::string & name,
const rclcpp::ParameterValue & default_value,
bool read_only)
{
std::lock_guard<std::mutex> lock(mutex_);
// TODO(sloretz) parameter name validation
if (name.empty()) {
throw std::runtime_error("parameter name must not be empty");
}
// Error if this parameter has already been declared and is different
auto param_iter = parameters_.find(name);
if (param_iter != parameters_.end() && param_iter->second.is_declared) {
if (
param_iter->second.descriptor.type != default_value.get_type() ||
param_iter->second.descriptor.read_only != read_only)
{
throw std::runtime_error("parameter '" + name + "' exists with conflicting description");
}
}
// Check if run-time user passed an initial value, else use the default.
rclcpp::ParameterValue initial_value = default_value;
auto value_iter = initial_parameter_values_.find(name);
if (value_iter != initial_parameter_values_.end()) {
initial_value = value_iter->second;
}
// Save a description of the parameter
rcl_interfaces::msg::ParameterDescriptor desc;
desc.name = name;
desc.type = initial_value.get_type();
desc.read_only = read_only;
rclcpp::node_interfaces::ParameterInfo pinfo;
pinfo.is_declared = true;
pinfo.value = initial_value;
pinfo.descriptor = desc;
// Add declared parameters to storage, even when they're not set.
parameters_[name] = pinfo;
// If it has an actual value then add it to 'new_parameters' event
if (rclcpp::ParameterType::PARAMETER_NOT_SET != initial_value.get_type()) {
auto parameter_event = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
parameter_event->new_parameters.push_back(
rclcpp::Parameter(name, initial_value).to_parameter_msg());
events_publisher_->publish(parameter_event);
}
}
std::vector<rcl_interfaces::msg::SetParametersResult>
NodeParameters::set_parameters(
const std::vector<rclcpp::Parameter> & parameters)
@@ -185,7 +231,7 @@ NodeParameters::set_parameters_atomically(
const std::vector<rclcpp::Parameter> & parameters)
{
std::lock_guard<std::mutex> lock(mutex_);
std::map<std::string, rclcpp::Parameter> tmp_map;
std::map<std::string, rclcpp::node_interfaces::ParameterInfo> tmp_map;
auto parameter_event = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
parameter_event->node = combined_name_;
@@ -202,33 +248,79 @@ NodeParameters::set_parameters_atomically(
return result;
}
std::vector<std::string> to_delete;
for (auto p : parameters) {
if (p.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
if (parameters_.find(p.get_name()) != parameters_.end()) {
// case: parameter was set before, and input is "NOT_SET"
// therefore we will erase the parameter from parameters_ later
auto param_iter = parameters_.find(p.get_name());
bool exists = parameters_.end() != param_iter;
bool want_to_delete = p.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET;
if (exists) {
if (param_iter->second.descriptor.read_only) {
result.successful = false;
result.reason = "read_only parameter: '" + p.get_name() + "'";
return result;
}
if (want_to_delete) {
parameter_event->deleted_parameters.push_back(p.to_parameter_msg());
if (param_iter->second.is_declared) {
// clear declared parameter value, but don't delete it
rclcpp::node_interfaces::ParameterInfo cleared_param_info = param_iter->second;
cleared_param_info.value = rclcpp::ParameterValue();
tmp_map[p.get_name()] = cleared_param_info;
} else {
// Truly delete an undeclared parameter
to_delete.push_back(p.get_name());
}
} else {
if (param_iter->second.value.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
// case: setting a value on a declared parameter that currently is unset
parameter_event->new_parameters.push_back(p.to_parameter_msg());
} else {
// case: changing a parameter value
parameter_event->changed_parameters.push_back(p.to_parameter_msg());
}
rclcpp::node_interfaces::ParameterInfo changed_param_info = param_iter->second;
// TODO(sloretz) Add accessor for ParameterValue on Parameter class
changed_param_info.value = rclcpp::ParameterValue(p.get_value_message());
tmp_map[p.get_name()] = changed_param_info;
}
} else {
if (parameters_.find(p.get_name()) == parameters_.end()) {
// case: parameter not set before, and input is something other than "NOT_SET"
parameter_event->new_parameters.push_back(p.to_parameter_msg());
// case: parameter does not exist already
if (!allow_undeclared_) {
result.successful = false;
result.reason = "undeclared parameter: '" + p.get_name() + "'";
return result;
} else if (want_to_delete) {
result.successful = false;
result.reason = "deleting parameter: '" + p.get_name() + "' that does not exist";
return result;
} else {
// case: parameter was set before, and input is something other than "NOT_SET"
parameter_event->changed_parameters.push_back(p.to_parameter_msg());
// Create new undeclared parameter
parameter_event->new_parameters.push_back(p.to_parameter_msg());
rcl_interfaces::msg::ParameterDescriptor desc;
desc.name = p.get_name();
desc.type = p.get_type();
desc.read_only = false;
rclcpp::node_interfaces::ParameterInfo new_param_info;
new_param_info.is_declared = false;
// TODO(sloretz) Add accessor for ParameterValue on Parameter class
new_param_info.value = rclcpp::ParameterValue(p.get_value_message());
new_param_info.descriptor = desc;
tmp_map[p.get_name()] = new_param_info;
}
tmp_map[p.get_name()] = p;
}
}
// std::map::insert will not overwrite elements, so we'll keep the new
// ones and add only those that already exist in the Node's internal map
tmp_map.insert(parameters_.begin(), parameters_.end());
// remove explicitly deleted parameters
for (auto p : parameters) {
if (p.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
tmp_map.erase(p.get_name());
}
// remove truly deleted parameters
for (const std::string & param_name : to_delete) {
tmp_map.erase(param_name);
}
std::swap(tmp_map, parameters_);
@@ -246,11 +338,11 @@ NodeParameters::get_parameters(const std::vector<std::string> & names) const
for (auto & name : names) {
if (std::any_of(parameters_.cbegin(), parameters_.cend(),
[&name](const std::pair<std::string, rclcpp::Parameter> & kv) {
[&name](const std::pair<std::string, rclcpp::node_interfaces::ParameterInfo> & kv) {
return name == kv.first;
}))
{
results.push_back(parameters_.at(name));
results.emplace_back(name, parameters_.at(name).value);
}
}
return results;
@@ -275,8 +367,12 @@ NodeParameters::get_parameter(
{
std::lock_guard<std::mutex> lock(mutex_);
if (parameters_.count(name)) {
parameter = parameters_.at(name);
auto param_iter = parameters_.find(name);
if (
parameters_.end() != param_iter &&
param_iter->second.value.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET)
{
parameter = {name, param_iter->second.value};
return true;
} else {
return false;
@@ -296,7 +392,7 @@ NodeParameters::get_parameters_by_prefix(
for (const auto & param : parameters_) {
if (param.first.find(prefix_with_dot) == 0 && param.first.length() > prefix_with_dot.length()) {
// Found one!
parameters[param.first.substr(prefix_with_dot.length())] = param.second;
parameters[param.first.substr(prefix_with_dot.length())] = rclcpp::Parameter(param.second);
ret = true;
}
}
@@ -314,10 +410,7 @@ NodeParameters::describe_parameters(const std::vector<std::string> & names) cons
return name == kv.first;
}))
{
rcl_interfaces::msg::ParameterDescriptor parameter_descriptor;
parameter_descriptor.name = kv.first;
parameter_descriptor.type = kv.second.get_type();
results.push_back(parameter_descriptor);
results.push_back(kv.second.descriptor);
}
}
return results;
@@ -333,7 +426,7 @@ NodeParameters::get_parameter_types(const std::vector<std::string> & names) cons
return name == kv.first;
}))
{
results.push_back(kv.second.get_type());
results.push_back(kv.second.value.get_type());
} else {
results.push_back(rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET);
}

View File

@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/parameter.hpp"
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
#include "rclcpp/parameter.hpp"
#include "rclcpp/node_interfaces/node_parameters.hpp"
#include "rclcpp/utilities.hpp"
using rclcpp::ParameterType;
@@ -33,6 +35,11 @@ Parameter::Parameter(const std::string & name, const rclcpp::ParameterValue & va
{
}
Parameter::Parameter(const rclcpp::node_interfaces::ParameterInfo & parameter_info)
: Parameter(parameter_info.descriptor.name, parameter_info.value)
{
}
ParameterType
Parameter::get_type() const
{
@@ -57,6 +64,12 @@ Parameter::get_value_message() const
return value_.to_value_msg();
}
const rclcpp::ParameterValue &
Parameter::get_parameter_value() const
{
return value_;
}
bool
Parameter::as_bool() const
{

View File

@@ -44,10 +44,11 @@ TEST_F(TestNodeWithInitialValues, no_initial_values) {
auto node = rclcpp::Node::make_shared(
"node_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
auto list_params_result = node->list_parameters({}, 0);
EXPECT_EQ(0u, list_params_result.names.size());
// Has use_sim_time parameter
EXPECT_EQ(1u, list_params_result.names.size());
}
TEST_F(TestNodeWithInitialValues, multiple_initial_values) {
TEST_F(TestNodeWithInitialValues, multiple_undeclared_initial_values) {
auto context = rclcpp::contexts::default_context::get_global_default_context();
const std::vector<std::string> arguments = {};
const std::vector<rclcpp::Parameter> initial_values = {
@@ -60,7 +61,53 @@ TEST_F(TestNodeWithInitialValues, multiple_initial_values) {
auto node = rclcpp::Node::make_shared(
"node_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
auto list_params_result = node->list_parameters({}, 0);
EXPECT_EQ(3u, list_params_result.names.size());
// Has use_sim_time parameter
EXPECT_EQ(1u, list_params_result.names.size());
}
TEST_F(TestNodeWithInitialValues, multiple_declared_initial_values) {
auto context = rclcpp::contexts::default_context::get_global_default_context();
const std::vector<std::string> arguments = {};
const std::vector<rclcpp::Parameter> initial_values = {
rclcpp::Parameter("foo", true),
rclcpp::Parameter("bar", "hello world"),
rclcpp::Parameter("baz", std::vector<double>{3.14, 2.718})
};
const bool use_global_arguments = false;
const bool use_intra_process = false;
auto node = rclcpp::Node::make_shared(
"node_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
node->declare_parameter("foo");
node->declare_parameter("bar");
node->declare_parameter("baz");
auto list_params_result = node->list_parameters({}, 0);
EXPECT_TRUE(node->get_parameter("foo").get_value<bool>());
EXPECT_STREQ("hello world", node->get_parameter("bar").get_value<std::string>().c_str());
std::vector<double> double_array = node->get_parameter("baz").get_value<std::vector<double>>();
ASSERT_EQ(2u, double_array.size());
EXPECT_DOUBLE_EQ(3.14, double_array.at(0));
EXPECT_DOUBLE_EQ(2.718, double_array.at(1));
}
TEST_F(TestNodeWithInitialValues, multiple_undeclared_initial_values_allowed) {
auto context = rclcpp::contexts::default_context::get_global_default_context();
const std::vector<std::string> arguments = {};
const std::vector<rclcpp::Parameter> initial_values = {
rclcpp::Parameter("foo", true),
rclcpp::Parameter("bar", "hello world"),
rclcpp::Parameter("baz", std::vector<double>{3.14, 2.718})
};
const bool use_global_arguments = false;
const bool use_intra_process = false;
const bool start_param_services = true;
const bool allow_undeclared_params = true;
auto node = rclcpp::Node::make_shared(
"node_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process,
start_param_services, allow_undeclared_params);
auto list_params_result = node->list_parameters({}, 0);
EXPECT_TRUE(node->get_parameter("foo").get_value<bool>());
EXPECT_STREQ("hello world", node->get_parameter("bar").get_value<std::string>().c_str());
std::vector<double> double_array = node->get_parameter("baz").get_value<std::vector<double>>();

View File

@@ -97,6 +97,7 @@ public:
* pipeline to pass messages between nodes in the same process using shared memory.
* \param[in] start_parameter_services True to setup ROS interfaces for accessing parameters
* in the node.
* \param[in] allow_undeclared_parameters True to allow any parameter name to be set on the node.
*/
RCLCPP_LIFECYCLE_PUBLIC
LifecycleNode(
@@ -107,7 +108,8 @@ public:
const std::vector<rclcpp::Parameter> & initial_parameters,
bool use_global_arguments = true,
bool use_intra_process_comms = false,
bool start_parameter_services = true);
bool start_parameter_services = true,
bool allow_undeclared_parameters = false);
RCLCPP_LIFECYCLE_PUBLIC
virtual ~LifecycleNode();

View File

@@ -66,7 +66,8 @@ LifecycleNode::LifecycleNode(
const std::vector<rclcpp::Parameter> & initial_parameters,
bool use_global_arguments,
bool use_intra_process_comms,
bool start_parameter_services)
bool start_parameter_services,
bool allow_undeclared_parameters)
: node_base_(new rclcpp::node_interfaces::NodeBase(
node_name, namespace_, context, arguments, use_global_arguments)),
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
@@ -88,7 +89,8 @@ LifecycleNode::LifecycleNode(
node_clock_,
initial_parameters,
use_intra_process_comms,
start_parameter_services
start_parameter_services,
allow_undeclared_parameters
)),
node_waitables_(new rclcpp::node_interfaces::NodeWaitables(node_base_.get())),
use_intra_process_comms_(use_intra_process_comms),