Command line static remapping (#217)

Command line static remapping (ROS1 compatible syntax + nodename prefix)
This commit is contained in:
Shane Loretz 2018-03-16 09:12:34 -07:00 committed by GitHub
parent 7008a7d6e7
commit 3628967496
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 2550 additions and 91 deletions

View file

@ -27,6 +27,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
endif() endif()
set(${PROJECT_NAME}_sources set(${PROJECT_NAME}_sources
src/rcl/arguments.c
src/rcl/client.c src/rcl/client.c
src/rcl/common.c src/rcl/common.c
src/rcl/expand_topic_name.c src/rcl/expand_topic_name.c
@ -35,6 +36,7 @@ set(${PROJECT_NAME}_sources
src/rcl/node.c src/rcl/node.c
src/rcl/publisher.c src/rcl/publisher.c
src/rcl/rcl.c src/rcl/rcl.c
src/rcl/remap.c
src/rcl/rmw_implementation_identifier_check.c src/rcl/rmw_implementation_identifier_check.c
src/rcl/service.c src/rcl/service.c
src/rcl/subscription.c src/rcl/subscription.c

187
rcl/include/rcl/arguments.h Normal file
View file

@ -0,0 +1,187 @@
// 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 RCL__ARGUMENTS_H_
#define RCL__ARGUMENTS_H_
#include "rcl/allocator.h"
#include "rcl/macros.h"
#include "rcl/types.h"
#include "rcl/visibility_control.h"
#if __cplusplus
extern "C"
{
#endif
struct rcl_arguments_impl_t;
/// Hold output of parsing command line arguments.
typedef struct rcl_arguments_t
{
/// Private implementation pointer.
struct rcl_arguments_impl_t * impl;
} rcl_arguments_t;
/// Return a rcl_node_t struct with members initialized to `NULL`.
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_arguments_t
rcl_get_zero_initialized_arguments(void);
/// Parse command line arguments into a structure usable by code.
/**
* If an argument does not appear to be a valid ROS argument then it is skipped and parsing
* continues with the next argument in `argv`.
* \sa rcl_arguments_get_count_unparsed()
* \sa rcl_arguments_get_unparsed()
*
* Successfully parsed remap rules are stored in the order they were given in `argv`.
* If given arguments `{"__ns:=/foo", "__ns:=/bar"}` then the namespace used by nodes in this
* process will be `/foo` and not `/bar`.
* \sa rcl_remap_topic_name()
* \sa rcl_remap_service_name()
* \sa rcl_remap_node_name()
* \sa rcl_remap_node_namespace()
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | Yes
* Thread-Safe | Yes
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] argc The number of arguments in argv.
* \param[in] argv Whe values of the arguments.
* \param[in] allocator A valid allocator.
* \param[out] args_output A structure that will contain the result of parsing.
* \return `RCL_RET_OK` if the arguments were parsed successfully, or
* \return `RCL_RET_INVALID_ARGUMENT` if any function arguments are invalid, or
* \return `RCL_RET_BAD_ALLOC` if allocating memory failed, or
* \return `RCL_RET_ERROR` if an unspecified error occurs.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
rcl_parse_arguments(
int argc,
const char * const argv[],
rcl_allocator_t allocator,
rcl_arguments_t * args_output);
/// Return the number of arguments that were not successfully parsed.
/**
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | Yes
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] args An arguments structure that has been parsed.
* \return number of unparsed arguments, or
* \return -1 if args is `NULL` or zero initialized.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
int
rcl_arguments_get_count_unparsed(
rcl_arguments_t * args);
/// Return a list of indexes that weren't successfully parsed.
/**
* Some arguments may not have been successfully parsed, or were not intended as ROS arguments.
* This function populates an array of indexes to these arguments in the original argv array.
* Since the first argument is always assumed to be a process name, the list will always contain
* the index 0.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | Yes
* Thread-Safe | Yes
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] args An arguments structure that has been parsed.
* \param[in] allocator A valid allocator.
* \param[out] output_unparsed_indices An allocated array of indices into the original argv array.
* This array must be deallocated by the caller using the given allocator.
* If there are no unparsed args then the output will be set to NULL.
* \return `RCL_RET_OK` if everything goes correctly, or
* \return `RCL_RET_INVALID_ARGUMENT` if any function arguments are invalid, or
* \return `RCL_RET_BAD_ALLOC` if allocating memory failed, or
* \return `RCL_RET_ERROR` if an unspecified error occurs.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
rcl_arguments_get_unparsed(
rcl_arguments_t * args,
rcl_allocator_t allocator,
int ** output_unparsed_indices);
/// Reclaim resources held inside rcl_arguments_t structure.
/**
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | Yes
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] args The structure to be deallocated.
* \param[in] allocator A valid allocator.
* \return `RCL_RET_OK` if the memory was successfully freed, or
* \return `RCL_RET_INVALID_ARGUMENT` if any function arguments are invalid, or
* \return `RCL_RET_ERROR` if an unspecified error occurs.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
rcl_arguments_fini(
rcl_arguments_t * args);
/// Get a global instance of command line arguments.
/**
* \sa rcl_init(int, char **, rcl_allocator_t)
* \sa rcl_shutdown()
* This returns parsed command line arguments that were passed to `rcl_init()`.
* The value returned by this function is undefined before `rcl_init()` is called and after
* `rcl_shutdown()` is called.
* The return value must not be finalized.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | Yes
* Uses Atomics | No
* Lock-Free | Yes
*
* \return a global instance of parsed command line arguments.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_arguments_t *
rcl_get_global_arguments();
#if __cplusplus
}
#endif
#endif // RCL__ARGUMENTS_H_

View file

@ -23,6 +23,7 @@ extern "C"
#include <stdint.h> #include <stdint.h>
#include "rcl/allocator.h" #include "rcl/allocator.h"
#include "rcl/arguments.h"
#include "rcl/macros.h" #include "rcl/macros.h"
#include "rcl/types.h" #include "rcl/types.h"
#include "rcl/visibility_control.h" #include "rcl/visibility_control.h"
@ -65,6 +66,12 @@ typedef struct rcl_node_options_t
/// Custom allocator used for internal allocations. /// Custom allocator used for internal allocations.
rcl_allocator_t allocator; rcl_allocator_t allocator;
/// If false then only use arguments in this struct, otherwise use global arguments also.
bool use_global_arguments;
/// Command line arguments that apply only to this node.
rcl_arguments_t arguments;
} rcl_node_options_t; } rcl_node_options_t;
/// Return a rcl_node_t struct with members initialized to `NULL`. /// Return a rcl_node_t struct with members initialized to `NULL`.

239
rcl/include/rcl/remap.h Normal file
View file

@ -0,0 +1,239 @@
// 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 RCL__REMAP_H_
#define RCL__REMAP_H_
#include "rcl/allocator.h"
#include "rcl/arguments.h"
#include "rcl/macros.h"
#include "rcl/types.h"
#include "rcl/visibility_control.h"
#if __cplusplus
extern "C"
{
#endif
// TODO(sloretz) add documentation about rostopic:// when it is supported
/// Remap a topic name based on given rules.
/**
* The supplied topic name must have already been expanded to a fully qualified name.
* \sa rcl_expand_topic_name()
*
* If `local_arguments` is not NULL and not zero intialized then its remap rules are checked first.
* If no rules matched and `global_arguments` is not NULL and not zero intitialized then its rules
* are checked next.
* If both `local_arguments` and global_arguments are NULL or zero intialized then the function will
* return RCL_RET_INVALID_ARGUMENT.
*
* `global_arguments` is usually the arguments passed to `rcl_init()`.
* \sa rcl_init()
* \sa rcl_get_global_arguments()
*
* Remap rules are checked in the order they were given.
* For rules passed to `rcl_init` this usually is the order they were passed on the command line.
* \sa rcl_parse_arguments()
*
* Only the first remap rule that matches is used to remap a name.
* For example, if the command line arguments are `foo:=bar bar:=baz` the topic `foo` is remapped to
* `bar` and not `baz`.
*
* `node_name` and `node_namespace` are used to expand the match and replacement into fully
* qualified names.
* Given node_name `trudy`, namespace `/ns`, and rule `foo:=~/bar` the names in the rule are
* expanded to `/ns/foo:=/ns/trudy/bar`.
* The rule will only apply if the given topic name is `/ns/foo`.
*
* `node_name` is also used to match against node specific rules.
* Given rules `alice:foo:=bar foo:=baz`, node name `alice`, and topic `foo` the remapped topic
* name will be `bar`.
* If given the node name `bob` and topic `foo` the remaped topic name would be `baz` instead.
* Note that processing always stops at the first matching rule even if there is a more specific one
* later on.
* Given `foo:=bar alice:foo:=baz` and topic name `foo` the remapped topic name will always be
* `bar` regardless of the node name given.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | Yes
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] local_arguments Command line arguments to be used before global arguments, or
* if NULL or zero-initialized then only global arguments are used.
* \param[in] global_arguments Command line arguments to use if no local rules matched, or
* `NULL` or zero-initialized to ignore global arguments.
* \param[in] topic_name A fully qualified and expanded topic name to be remapped.
* \param[in] node_name The name of the node to which name belongs.
* \param[in] node_namespace The namespace of a node to which name belongs.
* \param[in] allocator A valid allocator to use.
* \param[out] output_name Either an allocated string with the remapped name, or
* `NULL` if no remap rules matched the name.
* \return `RCL_RET_OK` if the topic name was remapped or no rules matched, or
* \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or
* \return `RCL_RET_BAD_ALLOC` if allocating memory failed, or
* \return `RCL_RET_TOPIC_NAME_INVALID` if the given topic name is invalid, or
* \return `RCL_RET_ERROR` if an unspecified error occurs.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
rcl_remap_topic_name(
const rcl_arguments_t * local_arguments,
const rcl_arguments_t * global_arguments,
const char * topic_name,
const char * node_name,
const char * node_namespace,
rcl_allocator_t allocator,
char ** output_name);
// TODO(sloretz) add documentation about rosservice:// when it is supported
/// Remap a service name based on given rules.
/**
* The supplied service name must have already been expanded to a fully qualified name.
*
* The behavior of this function is identical to rcl_expand_topic_name() except that it applies
* to service names instead of topic names.
* \sa rcl_expand_topic_name()
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | Yes
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] local_arguments Command line arguments to be used before global arguments, or
* if NULL or zero-initialized then only global arguments are used.
* \param[in] global_arguments Command line arguments to use if no local rules matched, or
* `NULL` or zero-initialized to ignore global arguments.
* \param[in] service_name A fully qualified and expanded service name to be remapped.
* \param[in] node_name The name of the node to which name belongs.
* \param[in] node_namespace The namespace of a node to which name belongs.
* \param[in] allocator A valid allocator to use.
* \param[out] output_name Either an allocated string with the remapped name, or
* `NULL` if no remap rules matched the name.
* \return `RCL_RET_OK` if the name was remapped or no rules matched, or
* \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or
* \return `RCL_RET_BAD_ALLOC` if allocating memory failed, or
* \return `RCL_RET_SERVICE_NAME_INVALID` if the given name is invalid, or
* \return `RCL_RET_ERROR` if an unspecified error occurs.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
rcl_remap_service_name(
const rcl_arguments_t * local_arguments,
const rcl_arguments_t * global_arguments,
const char * serivice_name,
const char * node_name,
const char * node_namespace,
rcl_allocator_t allocator,
char ** output_name);
/// Remap a node name based on given rules.
/**
* This function returns the node name that a node with the given name would be remapped to.
* When a node's name is remapped it changes its logger name and the output of expanding relative
* topic and service names.
*
* When composing nodes make sure that the final node names used are unique per process.
* There is not currently a way to independently remap the names of two nodes that were created
* with the same node name and are manually composed into one process.
*
* The behavior of `local_arguments`, `global_arguments`, `node_name`, the order remap rules are
* applied, and node specific rules is identical to rcl_remap_topic_name().
* \sa rcl_remap_topic_name()
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | Yes
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] local_arguments Arguments to be used before global arguments.
* \param[in] global_arguments Command line arguments to use if no local rules matched, or
* `NULL` or zero-initialized to ignore global arguments.
* \param[in] node_name The current name of the node.
* \param[in] allocator A valid allocator to use.
* \param[out] output_name Either an allocated string with the remapped name, or
* `NULL` if no remap rules matched the name.
* \return `RCL_RET_OK` If the name was remapped or no rules matched, or
* \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or
* \return `RCL_RET_BAD_ALLOC` if allocating memory failed, or
* \return `RCL_RET_NODE_INVALID_NAME` if the name is invalid, or
* \return `RCL_RET_ERROR` if an unspecified error occurs.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
rcl_remap_node_name(
const rcl_arguments_t * local_arguments,
const rcl_arguments_t * global_arguments,
const char * node_name,
rcl_allocator_t allocator,
char ** output_name);
/// Remap a namespace based on given rules.
/**
* This function returns the namespace that a node with the given name would be remapped to.
* When a node's namespace is remapped it changes its logger name and the output of expanding
* relative topic and service names.
*
* The behavior of `local_arguments`, `global_arguments`, `node_name`, the order remap rules are
* applied, and node specific rules is identical to rcl_remap_topic_name().
* \sa rcl_remap_topic_name()
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | Yes
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] local_arguments Arguments to be used before global arguments.
* \param[in] global_arguments Command line arguments to use if no local rules matched, or
* `NULL` or zero-initialized to ignore global arguments.
* \param[in] node_name The name of the node whose namespace is being remapped.
* \param[in] allocator A valid allocator to be used.
* \param[out] output_namespace Either an allocated string with the remapped namespace, or
* `NULL` if no remap rules matched the name.
* \return `RCL_RET_OK` if the node name was remapped or no rules matched, or
* \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or
* \return `RCL_RET_BAD_ALLOC` if allocating memory failed, or
* \return `RCL_RET_NODE_INVALID_NAMESPACE` if the remapped namespace is invalid, or
* \return `RCL_RET_ERROR` if an unspecified error occurs.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
rcl_remap_node_namespace(
const rcl_arguments_t * local_arguments,
const rcl_arguments_t * global_arguments,
const char * node_name,
rcl_allocator_t allocator,
char ** output_namespace);
#if __cplusplus
}
#endif
#endif // RCL__REMAP_H_

View file

@ -87,4 +87,8 @@ typedef rmw_ret_t rcl_ret_t;
/// Given rcl_wait_set_t is full return code. /// Given rcl_wait_set_t is full return code.
#define RCL_RET_WAIT_SET_FULL 902 #define RCL_RET_WAIT_SET_FULL 902
// rcl argument parsing specific ret codes in 1XXX
/// Argument is not a valid remap rule
#define RCL_RET_INVALID_REMAP_RULE 1001
#endif // RCL__TYPES_H_ #endif // RCL__TYPES_H_

403
rcl/src/rcl/arguments.c Normal file
View file

@ -0,0 +1,403 @@
// 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.
#include "rcl/arguments.h"
#include <string.h>
#include "./arguments_impl.h"
#include "./remap_impl.h"
#include "rcl/error_handling.h"
#include "rcl/validate_topic_name.h"
#include "rcutils/allocator.h"
#include "rcutils/logging_macros.h"
#include "rcutils/strdup.h"
#include "rmw/validate_namespace.h"
#include "rmw/validate_node_name.h"
#if __cplusplus
extern "C"
{
#endif
// Instance of global arguments.
static rcl_arguments_t __rcl_global_arguments;
/// Parse an argument that may or may not be a remap rule.
/// \param[in] arg the argument to parse
/// \param[in] allocator an allocator to use
/// \param[in,out] output_rule input a zero intialized rule, output a fully initialized one
/// \return RCL_RET_OK if a valid rule was parsed, or
/// \return RCL_RET_INVALID_REMAP_RULE if the argument is not a valid rule, or
/// \return RCL_RET_BAD_ALLOC if an allocation failed, or
/// \return RLC_RET_ERROR if an unspecified error occurred.
/// \internal
RCL_LOCAL
rcl_ret_t
_rcl_parse_remap_rule(
const char * arg,
rcl_allocator_t allocator,
rcl_remap_t * output_rule)
{
size_t len_node_name = 0;
size_t len_match = 0;
size_t len_replacement = 0;
const char * separator = NULL;
const char * colon = NULL;
const char * match_begin = arg;
const char * replacement_begin = NULL;
// A valid rule has two parts separated by :=
separator = strstr(arg, ":=");
if (NULL == separator) {
RCL_SET_ERROR_MSG("missing :=", allocator);
return RCL_RET_INVALID_REMAP_RULE;
}
replacement_begin = separator + 2;
// must have characters on both sides of the separator
len_match = separator - arg;
len_replacement = strlen(replacement_begin);
if (0 == len_match) {
RCL_SET_ERROR_MSG("match is zero length", allocator);
return RCL_RET_INVALID_REMAP_RULE;
} else if (0 == len_replacement) {
RCL_SET_ERROR_MSG("replacement has zero length", allocator);
return RCL_RET_INVALID_REMAP_RULE;
}
colon = strchr(arg, ':');
if (NULL != colon) {
if (colon < separator) {
// If there is a : on the match side then there is a node-name prefix
match_begin = colon + 1;
len_node_name = colon - arg;
len_match = separator - match_begin;
// node name must have at least one character
if (len_node_name <= 0) {
RCL_SET_ERROR_MSG("node name previx has zero length", allocator);
return RCL_RET_INVALID_REMAP_RULE;
}
} else if (colon > separator) {
// If the colon is on the replacement side then this couldn't be a valid rule
RCL_SET_ERROR_MSG("replacement side cannot contain a :", allocator);
return RCL_RET_INVALID_REMAP_RULE;
}
}
// Maybe match length changed because there was a node name prefix
if (0 == len_match) {
RCL_SET_ERROR_MSG("match is zero length", allocator);
return RCL_RET_INVALID_REMAP_RULE;
}
// Make sure node name contains only valid characters
if (len_node_name) {
int validation_result;
size_t invalid_index;
if (
RMW_RET_OK != rmw_validate_node_name_with_size(arg, len_node_name, &validation_result,
&invalid_index))
{
RCL_SET_ERROR_MSG("failed to run check on node name", allocator);
return RCL_RET_ERROR;
}
if (RMW_NODE_NAME_VALID != validation_result) {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
allocator,
"node name prefix invalid: %s", rmw_node_name_validation_result_string(validation_result));
return RCL_RET_INVALID_REMAP_RULE;
}
}
// Figure out what type of rule this is, default is to apply to topic and service names
rcl_remap_type_t type = RCL_TOPIC_REMAP | RCL_SERVICE_REMAP;
if (0 == strncmp("__ns", match_begin, len_match)) {
type = RCL_NAMESPACE_REMAP;
} else if (0 == strncmp("__node", match_begin, len_match)) {
type = RCL_NODENAME_REMAP;
}
if (type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
// Replacement must be a valid topic name
int validation_result;
size_t invalid_index;
rcl_ret_t ret = rcl_validate_topic_name(replacement_begin, &validation_result, &invalid_index);
if (ret != RCL_RET_OK) {
return RCL_RET_ERROR;
} else if (validation_result != RCL_TOPIC_NAME_VALID) {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
allocator,
"replacement is invalid: %s", rcl_topic_name_validation_result_string(validation_result));
return RCL_RET_INVALID_REMAP_RULE;
}
// Match must be a valid topic name
ret = rcl_validate_topic_name_with_size(
match_begin, len_match, &validation_result, &invalid_index);
if (ret != RCL_RET_OK) {
return RCL_RET_ERROR;
} else if (validation_result != RCL_TOPIC_NAME_VALID) {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
allocator,
"match is invalid: %s", rcl_topic_name_validation_result_string(validation_result));
return RCL_RET_INVALID_REMAP_RULE;
}
} else if (RCL_NAMESPACE_REMAP == type) {
int validation_result;
size_t invalid_idx;
if (RMW_RET_OK != rmw_validate_namespace(replacement_begin, &validation_result, &invalid_idx)) {
RCL_SET_ERROR_MSG("failed to run check on namespace", allocator);
return RCL_RET_ERROR;
}
if (RMW_NAMESPACE_VALID != validation_result) {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
allocator,
"namespace is invalid: %s", rmw_namespace_validation_result_string(validation_result));
return RCL_RET_INVALID_REMAP_RULE;
}
} else if (RCL_NODENAME_REMAP == type) {
int validation_result;
size_t invalid_idx;
if (RMW_RET_OK != rmw_validate_node_name(replacement_begin, &validation_result, &invalid_idx)) {
RCL_SET_ERROR_MSG("failed to run check on node name", allocator);
return RCL_RET_ERROR;
}
if (RMW_NODE_NAME_VALID != validation_result) {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
allocator,
"node name is invalid: %s", rmw_node_name_validation_result_string(validation_result));
return RCL_RET_INVALID_REMAP_RULE;
}
}
// Rule is valid, construct a structure for it
output_rule->allocator = allocator;
output_rule->type = type;
if (len_node_name > 0) {
output_rule->node_name = rcutils_strndup(arg, len_node_name, allocator);
if (NULL == output_rule->node_name) {
goto cleanup_rule;
}
}
if (type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
output_rule->match = rcutils_strndup(match_begin, len_match, allocator);
if (NULL == output_rule->match) {
goto cleanup_rule;
}
}
output_rule->replacement = rcutils_strndup(replacement_begin, len_replacement, allocator);
if (NULL == output_rule->replacement) {
goto cleanup_rule;
}
return RCL_RET_OK;
cleanup_rule:
if (RCL_RET_OK != rcl_remap_fini(output_rule)) {
RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME, "Failed to fini remap rule after error occurred");
}
return RCL_RET_BAD_ALLOC;
}
rcl_ret_t
rcl_parse_arguments(
int argc,
const char * const argv[],
rcl_allocator_t allocator,
rcl_arguments_t * args_output)
{
RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
if (argc < 0) {
return RCL_RET_INVALID_ARGUMENT;
} else if (argc > 0) {
RCL_CHECK_ARGUMENT_FOR_NULL(argv, RCL_RET_INVALID_ARGUMENT, allocator);
}
RCL_CHECK_ARGUMENT_FOR_NULL(args_output, RCL_RET_INVALID_ARGUMENT, allocator);
rcl_ret_t ret;
rcl_ret_t fail_ret;
args_output->impl = allocator.allocate(sizeof(rcl_arguments_impl_t), allocator.state);
if (NULL == args_output->impl) {
return RCL_RET_BAD_ALLOC;
}
rcl_arguments_impl_t * args_impl = args_output->impl;
args_impl->num_remap_rules = 0;
args_impl->remap_rules = NULL;
args_impl->unparsed_args = NULL;
args_impl->num_unparsed_args = 0;
args_impl->allocator = allocator;
if (argc == 0) {
// there are no arguments to parse
return RCL_RET_OK;
}
// over-allocate arrays to match the number of arguments
args_impl->remap_rules = allocator.allocate(sizeof(rcl_remap_t) * argc, allocator.state);
if (NULL == args_impl->remap_rules) {
ret = RCL_RET_BAD_ALLOC;
goto fail;
}
args_impl->unparsed_args = allocator.allocate(sizeof(int) * argc, allocator.state);
if (NULL == args_impl->unparsed_args) {
ret = RCL_RET_BAD_ALLOC;
goto fail;
}
// Attempt to parse arguments as remap rules
for (int i = 0; i < argc; ++i) {
rcl_remap_t * rule = &(args_impl->remap_rules[args_impl->num_remap_rules]);
*rule = rcl_remap_get_zero_initialized();
if (RCL_RET_OK == _rcl_parse_remap_rule(argv[i], allocator, rule)) {
++(args_impl->num_remap_rules);
} else {
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "arg %d error '%s'", i, rcl_get_error_string());
rcl_reset_error();
args_impl->unparsed_args[args_impl->num_unparsed_args] = i;
++(args_impl->num_unparsed_args);
}
}
// Shrink remap_rules array to match number of successfully parsed rules
if (args_impl->num_remap_rules > 0) {
args_impl->remap_rules = rcutils_reallocf(
args_impl->remap_rules, sizeof(rcl_remap_t) * args_impl->num_remap_rules, &allocator);
if (NULL == args_impl->remap_rules) {
ret = RCL_RET_BAD_ALLOC;
goto fail;
}
} else {
// No remap rules
allocator.deallocate(args_impl->remap_rules, allocator.state);
args_impl->remap_rules = NULL;
}
// Shrink unparsed_args
if (0 == args_impl->num_unparsed_args) {
// No unparsed args
allocator.deallocate(args_impl->unparsed_args, allocator.state);
args_impl->unparsed_args = NULL;
} else if (args_impl->num_unparsed_args < argc) {
args_impl->unparsed_args = rcutils_reallocf(
args_impl->unparsed_args, sizeof(int) * args_impl->num_unparsed_args, &allocator);
if (NULL == args_impl->unparsed_args) {
ret = RCL_RET_BAD_ALLOC;
goto fail;
}
}
return RCL_RET_OK;
fail:
fail_ret = ret;
if (NULL != args_impl) {
ret = rcl_arguments_fini(args_output);
if (RCL_RET_OK != ret) {
RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME, "Failed to fini arguments after earlier failure");
}
}
return fail_ret;
}
int
rcl_arguments_get_count_unparsed(
rcl_arguments_t * args)
{
if (NULL == args || NULL == args->impl) {
return -1;
}
return args->impl->num_unparsed_args;
}
rcl_ret_t
rcl_arguments_get_unparsed(
rcl_arguments_t * args,
rcl_allocator_t allocator,
int ** output_unparsed_indices)
{
RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(args, RCL_RET_INVALID_ARGUMENT, allocator);
RCL_CHECK_ARGUMENT_FOR_NULL(args->impl, RCL_RET_INVALID_ARGUMENT, allocator);
RCL_CHECK_ARGUMENT_FOR_NULL(output_unparsed_indices, RCL_RET_INVALID_ARGUMENT, allocator);
*output_unparsed_indices = NULL;
if (args->impl->num_unparsed_args) {
*output_unparsed_indices = allocator.allocate(
sizeof(int) * args->impl->num_unparsed_args, allocator.state);
if (NULL == *output_unparsed_indices) {
return RCL_RET_BAD_ALLOC;
}
for (int i = 0; i < args->impl->num_unparsed_args; ++i) {
(*output_unparsed_indices)[i] = args->impl->unparsed_args[i];
}
}
return RCL_RET_OK;
}
rcl_arguments_t
rcl_get_zero_initialized_arguments(void)
{
static rcl_arguments_t default_arguments = {
.impl = NULL
};
return default_arguments;
}
rcl_ret_t
rcl_arguments_fini(
rcl_arguments_t * args)
{
rcl_allocator_t alloc = rcl_get_default_allocator();
RCL_CHECK_ARGUMENT_FOR_NULL(args, RCL_RET_INVALID_ARGUMENT, alloc);
if (args->impl) {
rcl_ret_t ret = RCL_RET_OK;
alloc = args->impl->allocator;
if (args->impl->remap_rules) {
for (int i = 0; i < args->impl->num_remap_rules; ++i) {
rcl_ret_t remap_ret = rcl_remap_fini(&(args->impl->remap_rules[i]));
if (remap_ret != RCL_RET_OK) {
ret = remap_ret;
RCUTILS_LOG_ERROR_NAMED(
ROS_PACKAGE_NAME,
"Failed to finalize remap rule while finalizing arguments. Continuing...");
}
}
args->impl->allocator.deallocate(args->impl->remap_rules, args->impl->allocator.state);
args->impl->remap_rules = NULL;
args->impl->num_remap_rules = 0;
}
args->impl->allocator.deallocate(args->impl->unparsed_args, args->impl->allocator.state);
args->impl->num_unparsed_args = 0;
args->impl->unparsed_args = NULL;
args->impl->allocator.deallocate(args->impl, args->impl->allocator.state);
args->impl = NULL;
return ret;
}
RCL_SET_ERROR_MSG("rcl_arguments_t finalized twice", alloc);
return RCL_RET_ERROR;
}
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_arguments_t *
rcl_get_global_arguments()
{
return &__rcl_global_arguments;
}
#if __cplusplus
}
#endif

View file

@ -0,0 +1,47 @@
// 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 RCL__ARGUMENTS_IMPL_H_
#define RCL__ARGUMENTS_IMPL_H_
#include "rcl/arguments.h"
#include "./remap_impl.h"
#if __cplusplus
extern "C"
{
#endif
/// \internal
typedef struct rcl_arguments_impl_t
{
/// Array of indices that were not valid ROS arguments.
int * unparsed_args;
/// Length of unparsed_args.
int num_unparsed_args;
/// Array of rules for name remapping.
rcl_remap_t * remap_rules;
/// Length of remap_rules.
int num_remap_rules;
/// Allocator used to allocate objects in this struct
rcl_allocator_t allocator;
} rcl_arguments_impl_t;
#if __cplusplus
}
#endif
#endif // RCL__ARGUMENTS_IMPL_H_

View file

@ -24,6 +24,7 @@ extern "C"
#include "rcl/error_handling.h" #include "rcl/error_handling.h"
#include "rcl/expand_topic_name.h" #include "rcl/expand_topic_name.h"
#include "rcl/remap.h"
#include "rcutils/logging_macros.h" #include "rcutils/logging_macros.h"
#include "rmw/error_handling.h" #include "rmw/error_handling.h"
#include "rmw/rmw.h" #include "rmw/rmw.h"
@ -100,6 +101,7 @@ rcl_client_init(
return RCL_RET_ERROR; return RCL_RET_ERROR;
} }
char * expanded_service_name = NULL; char * expanded_service_name = NULL;
char * remapped_service_name = NULL;
ret = rcl_expand_topic_name( ret = rcl_expand_topic_name(
service_name, service_name,
rcl_node_get_name(node), rcl_node_get_name(node),
@ -114,40 +116,60 @@ rcl_client_init(
return RCL_RET_ERROR; return RCL_RET_ERROR;
} }
if (ret != RCL_RET_OK) { if (ret != RCL_RET_OK) {
if (ret == RCL_RET_BAD_ALLOC) { if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
return ret; ret = RCL_RET_SERVICE_NAME_INVALID;
} else if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
return RCL_RET_SERVICE_NAME_INVALID;
} else { } else {
return RCL_RET_ERROR; ret = RCL_RET_ERROR;
} }
goto cleanup;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Expanded service name '%s'", expanded_service_name) RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Expanded service name '%s'", expanded_service_name)
const rcl_node_options_t * node_options = rcl_node_get_options(node);
if (NULL == node_options) {
ret = RCL_RET_ERROR;
goto cleanup;
}
rcl_arguments_t * global_args = NULL;
if (node_options->use_global_arguments) {
global_args = rcl_get_global_arguments();
}
ret = rcl_remap_service_name(
&(node_options->arguments), global_args, expanded_service_name,
rcl_node_get_name(node), rcl_node_get_namespace(node), *allocator, &remapped_service_name);
if (RCL_RET_OK != ret) {
goto fail;
} else if (NULL == remapped_service_name) {
remapped_service_name = expanded_service_name;
expanded_service_name = NULL;
}
// Validate the expanded service name. // Validate the expanded service name.
int validation_result; int validation_result;
rmw_ret_t rmw_ret = rmw_validate_full_topic_name(expanded_service_name, &validation_result, NULL); rmw_ret_t rmw_ret = rmw_validate_full_topic_name(remapped_service_name, &validation_result, NULL);
if (rmw_ret != RMW_RET_OK) { if (rmw_ret != RMW_RET_OK) {
RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator); RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator);
return RCL_RET_ERROR; ret = RCL_RET_ERROR;
goto cleanup;
} }
if (validation_result != RMW_TOPIC_VALID) { if (validation_result != RMW_TOPIC_VALID) {
RCL_SET_ERROR_MSG(rmw_full_topic_name_validation_result_string(validation_result), *allocator) RCL_SET_ERROR_MSG(rmw_full_topic_name_validation_result_string(validation_result), *allocator)
return RCL_RET_SERVICE_NAME_INVALID; ret = RCL_RET_SERVICE_NAME_INVALID;
goto cleanup;
} }
// Allocate space for the implementation struct. // Allocate space for the implementation struct.
client->impl = (rcl_client_impl_t *)allocator->allocate( client->impl = (rcl_client_impl_t *)allocator->allocate(
sizeof(rcl_client_impl_t), allocator->state); sizeof(rcl_client_impl_t), allocator->state);
RCL_CHECK_FOR_NULL_WITH_MSG( RCL_CHECK_FOR_NULL_WITH_MSG(
client->impl, "allocating memory failed", return RCL_RET_BAD_ALLOC, *allocator); client->impl, "allocating memory failed", ret = RCL_RET_BAD_ALLOC; goto cleanup, *allocator);
// Fill out implementation struct. // Fill out implementation struct.
// rmw handle (create rmw client) // rmw handle (create rmw client)
// TODO(wjwwood): pass along the allocator to rmw when it supports it // TODO(wjwwood): pass along the allocator to rmw when it supports it
client->impl->rmw_handle = rmw_create_client( client->impl->rmw_handle = rmw_create_client(
rcl_node_get_rmw_handle(node), rcl_node_get_rmw_handle(node),
type_support, type_support,
expanded_service_name, remapped_service_name,
&options->qos); &options->qos);
allocator->deallocate(expanded_service_name, allocator->state);
if (!client->impl->rmw_handle) { if (!client->impl->rmw_handle) {
RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator); RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator);
goto fail; goto fail;
@ -155,12 +177,22 @@ rcl_client_init(
// options // options
client->impl->options = *options; client->impl->options = *options;
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Client initialized") RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Client initialized")
return RCL_RET_OK; ret = RCL_RET_OK;
goto cleanup;
fail: fail:
if (client->impl) { if (client->impl) {
allocator->deallocate(client->impl, allocator->state); allocator->deallocate(client->impl, allocator->state);
} }
return fail_ret; ret = fail_ret;
// Fall through to cleanup
cleanup:
if (NULL != expanded_service_name) {
allocator->deallocate(expanded_service_name, allocator->state);
}
if (NULL != remapped_service_name) {
allocator->deallocate(remapped_service_name, allocator->state);
}
return ret;
} }
rcl_ret_t rcl_ret_t

View file

@ -24,8 +24,10 @@ extern "C"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "rcl/arguments.h"
#include "rcl/error_handling.h" #include "rcl/error_handling.h"
#include "rcl/rcl.h" #include "rcl/rcl.h"
#include "rcl/remap.h"
#include "rcutils/filesystem.h" #include "rcutils/filesystem.h"
#include "rcutils/find.h" #include "rcutils/find.h"
#include "rcutils/format_string.h" #include "rcutils/format_string.h"
@ -142,6 +144,7 @@ rcl_node_init(
rcl_guard_condition_get_default_options(); rcl_guard_condition_get_default_options();
rcl_ret_t ret; rcl_ret_t ret;
rcl_ret_t fail_ret = RCL_RET_ERROR; rcl_ret_t fail_ret = RCL_RET_ERROR;
char * remapped_node_name = NULL;
// Check options and allocator first, so allocator can be used for errors. // Check options and allocator first, so allocator can be used for errors.
RCL_CHECK_ARGUMENT_FOR_NULL(options, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator()); RCL_CHECK_ARGUMENT_FOR_NULL(options, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator());
@ -191,7 +194,7 @@ rcl_node_init(
// length + 2, because new leading / and terminating \0 // length + 2, because new leading / and terminating \0
char * temp = (char *)allocator->allocate(namespace_length + 2, allocator->state); char * temp = (char *)allocator->allocate(namespace_length + 2, allocator->state);
RCL_CHECK_FOR_NULL_WITH_MSG( RCL_CHECK_FOR_NULL_WITH_MSG(
temp, "allocating memory failed", return RCL_RET_BAD_ALLOC, *allocator); temp, "allocating memory failed", ret = RCL_RET_BAD_ALLOC; goto cleanup, *allocator);
temp[0] = '/'; temp[0] = '/';
memcpy(temp + 1, namespace_, strlen(namespace_) + 1); memcpy(temp + 1, namespace_, strlen(namespace_) + 1);
local_namespace_ = temp; local_namespace_ = temp;
@ -202,34 +205,54 @@ rcl_node_init(
ret = rmw_validate_namespace(local_namespace_, &validation_result, NULL); ret = rmw_validate_namespace(local_namespace_, &validation_result, NULL);
if (ret != RMW_RET_OK) { if (ret != RMW_RET_OK) {
RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator); RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator);
if (should_free_local_namespace_) { goto cleanup;
allocator->deallocate((char *)local_namespace_, allocator->state);
}
return ret;
} }
if (validation_result != RMW_NAMESPACE_VALID) { if (validation_result != RMW_NAMESPACE_VALID) {
const char * msg = rmw_namespace_validation_result_string(validation_result); const char * msg = rmw_namespace_validation_result_string(validation_result);
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING((*allocator), "%s, result: %d", msg, validation_result); RCL_SET_ERROR_MSG_WITH_FORMAT_STRING((*allocator), "%s, result: %d", msg, validation_result);
if (should_free_local_namespace_) { ret = RCL_RET_NODE_INVALID_NAMESPACE;
allocator->deallocate((char *)local_namespace_, allocator->state); goto cleanup;
}
return RCL_RET_NODE_INVALID_NAMESPACE;
} }
// Allocate space for the implementation struct. // Allocate space for the implementation struct.
node->impl = (rcl_node_impl_t *)allocator->allocate(sizeof(rcl_node_impl_t), allocator->state); node->impl = (rcl_node_impl_t *)allocator->allocate(sizeof(rcl_node_impl_t), allocator->state);
if (!node->impl && should_free_local_namespace_) {
allocator->deallocate((char *)local_namespace_, allocator->state);
}
RCL_CHECK_FOR_NULL_WITH_MSG( RCL_CHECK_FOR_NULL_WITH_MSG(
node->impl, "allocating memory failed", return RCL_RET_BAD_ALLOC, *allocator); node->impl, "allocating memory failed", ret = RCL_RET_BAD_ALLOC; goto cleanup, *allocator);
node->impl->rmw_node_handle = NULL; node->impl->rmw_node_handle = NULL;
node->impl->graph_guard_condition = NULL; node->impl->graph_guard_condition = NULL;
node->impl->logger_name = NULL;
// Initialize node impl. // Initialize node impl.
// node options (assume it is trivially copyable) // node options (assume it is trivially copyable)
node->impl->options = *options; node->impl->options = *options;
// Remap the node name and namespace if remap rules are given
rcl_arguments_t * global_args = NULL;
if (node->impl->options.use_global_arguments) {
global_args = rcl_get_global_arguments();
}
ret = rcl_remap_node_name(
&(node->impl->options.arguments), global_args, name, *allocator,
&remapped_node_name);
if (RCL_RET_OK != ret) {
goto fail;
} else if (NULL != remapped_node_name) {
name = remapped_node_name;
}
char * remapped_namespace = NULL;
ret = rcl_remap_node_namespace(
&(node->impl->options.arguments), global_args, local_namespace_,
*allocator, &remapped_namespace);
if (RCL_RET_OK != ret) {
goto fail;
} else if (NULL != remapped_namespace) {
if (should_free_local_namespace_) {
allocator->deallocate((char *)local_namespace_, allocator->state);
}
should_free_local_namespace_ = true;
local_namespace_ = remapped_namespace;
}
// node logger name // node logger name
node->impl->logger_name = rcl_create_node_logger_name(name, local_namespace_, allocator); node->impl->logger_name = rcl_create_node_logger_name(name, local_namespace_, allocator);
RCL_CHECK_FOR_NULL_WITH_MSG( RCL_CHECK_FOR_NULL_WITH_MSG(
@ -264,10 +287,8 @@ rcl_node_init(
RCL_SET_ERROR_MSG( RCL_SET_ERROR_MSG(
"Environment variable " RCUTILS_STRINGIFY(ROS_SECURITY_ENABLE_VAR_NAME) "Environment variable " RCUTILS_STRINGIFY(ROS_SECURITY_ENABLE_VAR_NAME)
" could not be read", rcl_get_default_allocator()); " could not be read", rcl_get_default_allocator());
if (should_free_local_namespace_) { ret = RCL_RET_ERROR;
allocator->deallocate((char *)local_namespace_, allocator->state); goto fail;
}
return RCL_RET_ERROR;
} }
bool use_security = (0 == strcmp(ros_security_enable, "true")); bool use_security = (0 == strcmp(ros_security_enable, "true"));
@ -278,10 +299,8 @@ rcl_node_init(
RCL_SET_ERROR_MSG( RCL_SET_ERROR_MSG(
"Environment variable " RCUTILS_STRINGIFY(ROS_SECURITY_STRATEGY_VAR_NAME) "Environment variable " RCUTILS_STRINGIFY(ROS_SECURITY_STRATEGY_VAR_NAME)
" could not be read", rcl_get_default_allocator()); " could not be read", rcl_get_default_allocator());
if (should_free_local_namespace_) { ret = RCL_RET_ERROR;
allocator->deallocate((char *)local_namespace_, allocator->state); goto fail;
}
return RCL_RET_ERROR;
} }
rmw_node_security_options_t node_security_options = rmw_node_security_options_t node_security_options =
@ -302,10 +321,8 @@ rcl_node_init(
"SECURITY ERROR: unable to find a folder matching the node name in the " "SECURITY ERROR: unable to find a folder matching the node name in the "
RCUTILS_STRINGIFY(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME) RCUTILS_STRINGIFY(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME)
" directory while the requested security strategy requires it", *allocator); " directory while the requested security strategy requires it", *allocator);
if (should_free_local_namespace_) { ret = RCL_RET_ERROR;
allocator->deallocate((char *)local_namespace_, allocator->state); goto cleanup;
}
return RCL_RET_ERROR;
} }
} }
} }
@ -314,11 +331,6 @@ rcl_node_init(
RCL_CHECK_FOR_NULL_WITH_MSG( RCL_CHECK_FOR_NULL_WITH_MSG(
node->impl->rmw_node_handle, rmw_get_error_string_safe(), goto fail, *allocator); node->impl->rmw_node_handle, rmw_get_error_string_safe(), goto fail, *allocator);
// free local_namespace_ if necessary
if (should_free_local_namespace_) {
allocator->deallocate((char *)local_namespace_, allocator->state);
should_free_local_namespace_ = false;
}
// instance id // instance id
node->impl->rcl_instance_id = rcl_get_instance_id(); node->impl->rcl_instance_id = rcl_get_instance_id();
// graph guard condition // graph guard condition
@ -345,7 +357,8 @@ rcl_node_init(
goto fail; goto fail;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Node initialized") RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Node initialized")
return RCL_RET_OK; ret = RCL_RET_OK;
goto cleanup;
fail: fail:
if (node->impl) { if (node->impl) {
if (node->impl->logger_name) { if (node->impl->logger_name) {
@ -374,11 +387,17 @@ fail:
} }
*node = rcl_get_zero_initialized_node(); *node = rcl_get_zero_initialized_node();
ret = fail_ret;
// fall through from fail -> cleanup
cleanup:
if (should_free_local_namespace_) { if (should_free_local_namespace_) {
allocator->deallocate((char *)local_namespace_, allocator->state); allocator->deallocate((char *)local_namespace_, allocator->state);
local_namespace_ = NULL; local_namespace_ = NULL;
} }
return fail_ret; if (NULL != remapped_node_name) {
allocator->deallocate(remapped_node_name, allocator->state);
}
return ret;
} }
rcl_ret_t rcl_ret_t
@ -435,9 +454,11 @@ rcl_node_get_default_options()
// !!! MAKE SURE THAT CHANGES TO THESE DEFAULTS ARE REFLECTED IN THE HEADER DOC STRING // !!! MAKE SURE THAT CHANGES TO THESE DEFAULTS ARE REFLECTED IN THE HEADER DOC STRING
static rcl_node_options_t default_options = { static rcl_node_options_t default_options = {
.domain_id = RCL_NODE_OPTIONS_DEFAULT_DOMAIN_ID, .domain_id = RCL_NODE_OPTIONS_DEFAULT_DOMAIN_ID,
.use_global_arguments = true,
}; };
// Must set the allocator after because it is not a compile time constant. // Must set the allocator after because it is not a compile time constant.
default_options.allocator = rcl_get_default_allocator(); default_options.allocator = rcl_get_default_allocator();
default_options.arguments = rcl_get_zero_initialized_arguments();
return default_options; return default_options;
} }

View file

@ -25,6 +25,7 @@ extern "C"
#include "rcl/allocator.h" #include "rcl/allocator.h"
#include "rcl/error_handling.h" #include "rcl/error_handling.h"
#include "rcl/expand_topic_name.h" #include "rcl/expand_topic_name.h"
#include "rcl/remap.h"
#include "rcutils/logging_macros.h" #include "rcutils/logging_macros.h"
#include "rmw/error_handling.h" #include "rmw/error_handling.h"
#include "rmw/rmw.h" #include "rmw/rmw.h"
@ -99,6 +100,7 @@ rcl_publisher_init(
return RCL_RET_ERROR; return RCL_RET_ERROR;
} }
char * expanded_topic_name = NULL; char * expanded_topic_name = NULL;
char * remapped_topic_name = NULL;
ret = rcl_expand_topic_name( ret = rcl_expand_topic_name(
topic_name, topic_name,
rcl_node_get_name(node), rcl_node_get_name(node),
@ -108,56 +110,85 @@ rcl_publisher_init(
&expanded_topic_name); &expanded_topic_name);
rcutils_ret = rcutils_string_map_fini(&substitutions_map); rcutils_ret = rcutils_string_map_fini(&substitutions_map);
if (rcutils_ret != RCUTILS_RET_OK) { if (rcutils_ret != RCUTILS_RET_OK) {
RCL_SET_ERROR_MSG(rcutils_get_error_string_safe(), *allocator) RCL_SET_ERROR_MSG(rcutils_get_error_string_safe(), *allocator);
allocator->deallocate(expanded_topic_name, allocator->state); ret = RCL_RET_ERROR;
return RCL_RET_ERROR; goto cleanup;
} }
if (ret != RCL_RET_OK) { if (ret != RCL_RET_OK) {
if (ret == RCL_RET_BAD_ALLOC) { if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
return ret; ret = RCL_RET_TOPIC_NAME_INVALID;
} else if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
return RCL_RET_TOPIC_NAME_INVALID;
} else { } else {
return RCL_RET_ERROR; ret = RCL_RET_ERROR;
} }
goto cleanup;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Expanded topic name '%s'", expanded_topic_name) RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Expanded topic name '%s'", expanded_topic_name)
const rcl_node_options_t * node_options = rcl_node_get_options(node);
if (NULL == node_options) {
ret = RCL_RET_ERROR;
goto cleanup;
}
rcl_arguments_t * global_args = NULL;
if (node_options->use_global_arguments) {
global_args = rcl_get_global_arguments();
}
ret = rcl_remap_topic_name(
&(node_options->arguments), global_args, expanded_topic_name,
rcl_node_get_name(node), rcl_node_get_namespace(node), *allocator, &remapped_topic_name);
if (RCL_RET_OK != ret) {
goto fail;
} else if (NULL == remapped_topic_name) {
remapped_topic_name = expanded_topic_name;
expanded_topic_name = NULL;
}
// Validate the expanded topic name. // Validate the expanded topic name.
int validation_result; int validation_result;
rmw_ret_t rmw_ret = rmw_validate_full_topic_name(expanded_topic_name, &validation_result, NULL); rmw_ret_t rmw_ret = rmw_validate_full_topic_name(remapped_topic_name, &validation_result, NULL);
if (rmw_ret != RMW_RET_OK) { if (rmw_ret != RMW_RET_OK) {
RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator); RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator);
return RCL_RET_ERROR; ret = RCL_RET_ERROR;
goto cleanup;
} }
if (validation_result != RMW_TOPIC_VALID) { if (validation_result != RMW_TOPIC_VALID) {
RCL_SET_ERROR_MSG(rmw_full_topic_name_validation_result_string(validation_result), *allocator) RCL_SET_ERROR_MSG(rmw_full_topic_name_validation_result_string(validation_result), *allocator)
return RCL_RET_TOPIC_NAME_INVALID; ret = RCL_RET_TOPIC_NAME_INVALID;
goto cleanup;
} }
// Allocate space for the implementation struct. // Allocate space for the implementation struct.
publisher->impl = (rcl_publisher_impl_t *)allocator->allocate( publisher->impl = (rcl_publisher_impl_t *)allocator->allocate(
sizeof(rcl_publisher_impl_t), allocator->state); sizeof(rcl_publisher_impl_t), allocator->state);
RCL_CHECK_FOR_NULL_WITH_MSG( RCL_CHECK_FOR_NULL_WITH_MSG(
publisher->impl, "allocating memory failed", return RCL_RET_BAD_ALLOC, *allocator); publisher->impl, "allocating memory failed", ret = RCL_RET_BAD_ALLOC; goto cleanup, *allocator);
// Fill out implementation struct. // Fill out implementation struct.
// rmw handle (create rmw publisher) // rmw handle (create rmw publisher)
// TODO(wjwwood): pass along the allocator to rmw when it supports it // TODO(wjwwood): pass along the allocator to rmw when it supports it
publisher->impl->rmw_handle = rmw_create_publisher( publisher->impl->rmw_handle = rmw_create_publisher(
rcl_node_get_rmw_handle(node), rcl_node_get_rmw_handle(node),
type_support, type_support,
expanded_topic_name, remapped_topic_name,
&(options->qos)); &(options->qos));
allocator->deallocate(expanded_topic_name, allocator->state);
RCL_CHECK_FOR_NULL_WITH_MSG(publisher->impl->rmw_handle, RCL_CHECK_FOR_NULL_WITH_MSG(publisher->impl->rmw_handle,
rmw_get_error_string_safe(), goto fail, *allocator); rmw_get_error_string_safe(), goto fail, *allocator);
// options // options
publisher->impl->options = *options; publisher->impl->options = *options;
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Publisher initialized") RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Publisher initialized");
return RCL_RET_OK; goto cleanup;
fail: fail:
if (publisher->impl) { if (publisher->impl) {
allocator->deallocate(publisher->impl, allocator->state); allocator->deallocate(publisher->impl, allocator->state);
} }
return fail_ret; ret = fail_ret;
// Fall through to cleanup
cleanup:
if (NULL != expanded_topic_name) {
allocator->deallocate(expanded_topic_name, allocator->state);
}
if (NULL != remapped_topic_name) {
allocator->deallocate(remapped_topic_name, allocator->state);
}
return ret;
} }
rcl_ret_t rcl_ret_t

View file

@ -21,7 +21,9 @@ extern "C"
#include <string.h> #include <string.h>
#include "./arguments_impl.h"
#include "./stdatomic_helper.h" #include "./stdatomic_helper.h"
#include "rcl/arguments.h"
#include "rcl/error_handling.h" #include "rcl/error_handling.h"
#include "rcutils/logging_macros.h" #include "rcutils/logging_macros.h"
#include "rmw/error_handling.h" #include "rmw/error_handling.h"
@ -49,6 +51,11 @@ __clean_up_init()
} }
__rcl_argc = 0; __rcl_argc = 0;
__rcl_argv = NULL; __rcl_argv = NULL;
// This is the only place where it is OK to finalize the global arguments.
rcl_arguments_t * global_args = rcl_get_global_arguments();
if (NULL != global_args->impl && RCL_RET_OK != rcl_arguments_fini(global_args)) {
rcl_reset_error();
}
rcl_atomic_store(&__rcl_instance_id, 0); rcl_atomic_store(&__rcl_instance_id, 0);
rcl_atomic_store(&__rcl_is_initialized, false); rcl_atomic_store(&__rcl_is_initialized, false);
} }
@ -68,6 +75,11 @@ rcl_init(int argc, char ** argv, rcl_allocator_t allocator)
RCL_SET_ERROR_MSG("rcl_init called while already initialized", allocator); RCL_SET_ERROR_MSG("rcl_init called while already initialized", allocator);
return RCL_RET_ALREADY_INIT; return RCL_RET_ALREADY_INIT;
} }
// Zero initialize global arguments before any chance of calling __clean_up_init()
rcl_arguments_t * global_args = rcl_get_global_arguments();
*global_args = rcl_get_zero_initialized_arguments();
// There is a race condition between the time __rcl_is_initialized is set true, // There is a race condition between the time __rcl_is_initialized is set true,
// and when the allocator is set, in which rcl_shutdown() could get rcl_ok() as // and when the allocator is set, in which rcl_shutdown() could get rcl_ok() as
// true and try to use the allocator, but it isn't set yet... // true and try to use the allocator, but it isn't set yet...
@ -101,6 +113,10 @@ rcl_init(int argc, char ** argv, rcl_allocator_t allocator)
} }
memcpy(__rcl_argv[i], argv[i], strlen(argv[i])); memcpy(__rcl_argv[i], argv[i], strlen(argv[i]));
} }
if (RCL_RET_OK != rcl_parse_arguments(argc, (const char **)argv, allocator, global_args)) {
RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME, "Failed to parse global arguments");
goto fail;
}
rcl_atomic_store(&__rcl_instance_id, ++__rcl_next_unique_id); rcl_atomic_store(&__rcl_instance_id, ++__rcl_next_unique_id);
if (rcl_atomic_load_uint64_t(&__rcl_instance_id) == 0) { if (rcl_atomic_load_uint64_t(&__rcl_instance_id) == 0) {
// Roll over occurred. // Roll over occurred.

279
rcl/src/rcl/remap.c Normal file
View file

@ -0,0 +1,279 @@
// 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.
#include "rcl/remap.h"
#include "./arguments_impl.h"
#include "./remap_impl.h"
#include "rcl/error_handling.h"
#include "rcl/expand_topic_name.h"
#include "rcutils/allocator.h"
#include "rcutils/strdup.h"
#include "rcutils/types/string_map.h"
#if __cplusplus
extern "C"
{
#endif
rcl_remap_t
rcl_remap_get_zero_initialized()
{
rcl_remap_t rule;
rule.type = RCL_UNKNOWN_REMAP;
rule.node_name = NULL;
rule.match = NULL;
rule.replacement = NULL;
rule.allocator = rcutils_get_zero_initialized_allocator();
return rule;
}
rcl_ret_t
rcl_remap_fini(
rcl_remap_t * rule)
{
if (NULL != rule->node_name) {
rule->allocator.deallocate(rule->node_name, rule->allocator.state);
rule->node_name = NULL;
}
if (NULL != rule->match) {
rule->allocator.deallocate(rule->match, rule->allocator.state);
rule->match = NULL;
}
if (NULL != rule->replacement) {
rule->allocator.deallocate(rule->replacement, rule->allocator.state);
rule->replacement = NULL;
}
rule->allocator = rcutils_get_zero_initialized_allocator();
return RCL_RET_OK;
}
/// Get the first matching rule in a chain.
/// \return RCL_RET_OK if no errors occurred while searching for a rule
RCL_LOCAL
rcl_ret_t
_rcl_remap_first_match(
rcl_remap_t * remap_rules,
int num_rules,
rcl_remap_type_t type_bitmask,
const char * name,
const char * node_name,
const char * node_namespace,
const rcutils_string_map_t * substitutions,
rcutils_allocator_t allocator,
rcl_remap_t ** output_rule)
{
*output_rule = NULL;
for (int i = 0; i < num_rules; ++i) {
rcl_remap_t * rule = &(remap_rules[i]);
if (!(rule->type & type_bitmask)) {
// Not the type of remap rule we're looking fore
continue;
}
if (rule->node_name != NULL && 0 != strcmp(rule->node_name, node_name)) {
// Rule has a node name prefix and the supplied node name didn't match
continue;
}
bool matched = false;
if (rule->type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
// topic and service rules need the match side to be expanded to a FQN
char * expanded_match = NULL;
rcl_ret_t ret = rcl_expand_topic_name(
rule->match, node_name, node_namespace, substitutions, allocator, &expanded_match);
if (RCL_RET_OK != ret) {
rcl_reset_error();
if (
RCL_RET_NODE_INVALID_NAMESPACE == ret ||
RCL_RET_NODE_INVALID_NAME == ret ||
RCL_RET_BAD_ALLOC == ret)
{
// these are probably going to happen again. Stop processing rules
return ret;
}
continue;
}
matched = (0 == strcmp(expanded_match, name));
allocator.deallocate(expanded_match, allocator.state);
} else {
// nodename and namespace replacement apply if the type and node name prefix checks passed
matched = true;
}
if (matched) {
*output_rule = rule;
break;
}
}
return RCL_RET_OK;
}
/// Remap from one name to another using rules matching a given type bitmask.
RCL_LOCAL
rcl_ret_t
_rcl_remap_name(
const rcl_arguments_t * local_arguments,
const rcl_arguments_t * global_arguments,
rcl_remap_type_t type_bitmask,
const char * name,
const char * node_name,
const char * node_namespace,
const rcutils_string_map_t * substitutions,
rcl_allocator_t allocator,
char ** output_name)
{
RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT, allocator);
RCL_CHECK_ARGUMENT_FOR_NULL(output_name, RCL_RET_INVALID_ARGUMENT, allocator);
if (NULL != local_arguments && NULL == local_arguments->impl) {
local_arguments = NULL;
}
if (NULL != global_arguments && NULL == global_arguments->impl) {
global_arguments = NULL;
}
if (NULL == local_arguments && NULL == global_arguments) {
RCL_SET_ERROR_MSG("local_arguments invalid and not using global arguments", allocator);
return RCL_RET_INVALID_ARGUMENT;
}
*output_name = NULL;
rcl_remap_t * rule = NULL;
// Look at local rules first
if (NULL != local_arguments) {
rcl_ret_t ret = _rcl_remap_first_match(
local_arguments->impl->remap_rules, local_arguments->impl->num_remap_rules, type_bitmask,
name, node_name, node_namespace, substitutions, allocator, &rule);
if (ret != RCL_RET_OK) {
return ret;
}
}
// Check global rules if no local rule matched
if (NULL == rule && NULL != global_arguments) {
rcl_ret_t ret = _rcl_remap_first_match(
global_arguments->impl->remap_rules, global_arguments->impl->num_remap_rules, type_bitmask,
name, node_name, node_namespace, substitutions, allocator, &rule);
if (ret != RCL_RET_OK) {
return ret;
}
}
// Do the remapping
if (NULL != rule) {
if (rule->type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
// topic and service rules need the replacement to be expanded to a FQN
rcl_ret_t ret = rcl_expand_topic_name(
rule->replacement, node_name, node_namespace, substitutions, allocator, output_name);
if (RCL_RET_OK != ret) {
return ret;
}
} else {
// nodename and namespace rules don't need replacment expanded
*output_name = rcutils_strdup(rule->replacement, allocator);
}
if (NULL == *output_name) {
RCL_SET_ERROR_MSG("Failed to set output", allocator);
return RCL_RET_ERROR;
}
}
return RCL_RET_OK;
}
rcl_ret_t
rcl_remap_topic_name(
const rcl_arguments_t * local_arguments,
const rcl_arguments_t * global_arguments,
const char * topic_name,
const char * node_name,
const char * node_namespace,
rcl_allocator_t allocator,
char ** output_name)
{
RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT, allocator);
rcutils_string_map_t substitutions = rcutils_get_zero_initialized_string_map();
rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions, 0, allocator);
rcl_ret_t ret = RCL_RET_ERROR;
if (RCUTILS_RET_OK == rcutils_ret) {
ret = rcl_get_default_topic_name_substitutions(&substitutions);
if (RCL_RET_OK == ret) {
ret = _rcl_remap_name(
local_arguments, global_arguments, RCL_TOPIC_REMAP, topic_name, node_name,
node_namespace, &substitutions, allocator, output_name);
}
}
if (RCUTILS_RET_OK != rcutils_string_map_fini(&substitutions)) {
return RCL_RET_ERROR;
}
return ret;
}
rcl_ret_t
rcl_remap_service_name(
const rcl_arguments_t * local_arguments,
const rcl_arguments_t * global_arguments,
const char * service_name,
const char * node_name,
const char * node_namespace,
rcl_allocator_t allocator,
char ** output_name)
{
RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT, allocator);
rcutils_string_map_t substitutions = rcutils_get_zero_initialized_string_map();
rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions, 0, allocator);
rcl_ret_t ret = RCL_RET_ERROR;
if (rcutils_ret == RCUTILS_RET_OK) {
ret = rcl_get_default_topic_name_substitutions(&substitutions);
if (ret == RCL_RET_OK) {
ret = _rcl_remap_name(
local_arguments, global_arguments, RCL_SERVICE_REMAP, service_name, node_name,
node_namespace, &substitutions, allocator, output_name);
}
}
if (RCUTILS_RET_OK != rcutils_string_map_fini(&substitutions)) {
return RCL_RET_ERROR;
}
return ret;
}
rcl_ret_t
rcl_remap_node_name(
const rcl_arguments_t * local_arguments,
const rcl_arguments_t * global_arguments,
const char * node_name,
rcl_allocator_t allocator,
char ** output_name)
{
RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
return _rcl_remap_name(
local_arguments, global_arguments, RCL_NODENAME_REMAP, NULL, node_name, NULL, NULL,
allocator, output_name);
}
rcl_ret_t
rcl_remap_node_namespace(
const rcl_arguments_t * local_arguments,
const rcl_arguments_t * global_arguments,
const char * node_name,
rcl_allocator_t allocator,
char ** output_namespace)
{
RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
return _rcl_remap_name(
local_arguments, global_arguments, RCL_NAMESPACE_REMAP, NULL, node_name, NULL, NULL,
allocator, output_namespace);
}
#if __cplusplus
}
#endif

80
rcl/src/rcl/remap_impl.h Normal file
View file

@ -0,0 +1,80 @@
// 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 RCL__REMAP_IMPL_H_
#define RCL__REMAP_IMPL_H_
#include "rcl/types.h"
#if __cplusplus
extern "C"
{
#endif
/// Enum doubles as a bitmask for rule sthat apply to both topics and services.
typedef enum rcl_remap_type_t
{
RCL_UNKNOWN_REMAP = 0,
RCL_TOPIC_REMAP = 1u << 0,
RCL_SERVICE_REMAP = 1u << 1,
RCL_NODENAME_REMAP = 1u << 2,
RCL_NAMESPACE_REMAP = 1u << 3
} rcl_remap_type_t;
typedef struct rcl_remap_t
{
/// Bitmask indicating what type of rule this is.
rcl_remap_type_t type;
/// A node name that this rule is limited to, or NULL if it applies to any node.
char * node_name;
/// Match portion of a rule, or NULL if node name or namespace replacement.
char * match;
/// Replacement portion of a rule.
char * replacement;
/// Allocator used to allocate objects in this struct
rcl_allocator_t allocator;
} rcl_remap_t;
/// Get an rcl_remap_t structure initialized with NULL.
rcl_remap_t
rcl_remap_get_zero_initialized();
/// Reclaim resources used in an rcl_remap_t structure.
/**
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | Yes
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] rule A rule to deallocate back to a zero initialized state.
* \return `RCL_RET_OK` if the structure was free'd, or
* \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or
* \return `RCL_RET_BAD_ALLOC` if allocating memory failed, or
* \return `RCL_RET_NODE_INVALID_NAME` if the name is invalid, or
* \return `RCL_RET_ERROR` if an unspecified error occurs.
*/
RCL_WARN_UNUSED
rcl_ret_t
rcl_remap_fini(
rcl_remap_t * rule);
#if __cplusplus
}
#endif
#endif // RCL__REMAP_IMPL_H_

View file

@ -24,6 +24,7 @@ extern "C"
#include "rcl/error_handling.h" #include "rcl/error_handling.h"
#include "rcl/expand_topic_name.h" #include "rcl/expand_topic_name.h"
#include "rcl/remap.h"
#include "rcutils/logging_macros.h" #include "rcutils/logging_macros.h"
#include "rmw/error_handling.h" #include "rmw/error_handling.h"
#include "rmw/rmw.h" #include "rmw/rmw.h"
@ -97,6 +98,7 @@ rcl_service_init(
return RCL_RET_ERROR; return RCL_RET_ERROR;
} }
char * expanded_service_name = NULL; char * expanded_service_name = NULL;
char * remapped_service_name = NULL;
ret = rcl_expand_topic_name( ret = rcl_expand_topic_name(
service_name, service_name,
rcl_node_get_name(node), rcl_node_get_name(node),
@ -107,35 +109,57 @@ rcl_service_init(
rcutils_ret = rcutils_string_map_fini(&substitutions_map); rcutils_ret = rcutils_string_map_fini(&substitutions_map);
if (rcutils_ret != RCUTILS_RET_OK) { if (rcutils_ret != RCUTILS_RET_OK) {
RCL_SET_ERROR_MSG(rcutils_get_error_string_safe(), *allocator) RCL_SET_ERROR_MSG(rcutils_get_error_string_safe(), *allocator)
allocator->deallocate(expanded_service_name, allocator->state); ret = RCL_RET_ERROR;
goto cleanup;
return RCL_RET_ERROR; return RCL_RET_ERROR;
} }
if (ret != RCL_RET_OK) { if (ret != RCL_RET_OK) {
if (ret == RCL_RET_BAD_ALLOC) { if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
return ret; ret = RCL_RET_SERVICE_NAME_INVALID;
} else if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
return RCL_RET_SERVICE_NAME_INVALID;
} else { } else {
return RCL_RET_ERROR; ret = RCL_RET_ERROR;
} }
goto cleanup;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Expanded service name '%s'", expanded_service_name) RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Expanded service name '%s'", expanded_service_name)
const rcl_node_options_t * node_options = rcl_node_get_options(node);
if (NULL == node_options) {
ret = RCL_RET_ERROR;
goto cleanup;
}
rcl_arguments_t * global_args = NULL;
if (node_options->use_global_arguments) {
global_args = rcl_get_global_arguments();
}
ret = rcl_remap_service_name(
&(node_options->arguments), global_args, expanded_service_name,
rcl_node_get_name(node), rcl_node_get_namespace(node), *allocator, &remapped_service_name);
if (RCL_RET_OK != ret) {
goto fail;
} else if (NULL == remapped_service_name) {
remapped_service_name = expanded_service_name;
expanded_service_name = NULL;
}
// Validate the expanded service name. // Validate the expanded service name.
int validation_result; int validation_result;
rmw_ret_t rmw_ret = rmw_validate_full_topic_name(expanded_service_name, &validation_result, NULL); rmw_ret_t rmw_ret = rmw_validate_full_topic_name(remapped_service_name, &validation_result, NULL);
if (rmw_ret != RMW_RET_OK) { if (rmw_ret != RMW_RET_OK) {
RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator); RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator);
return RCL_RET_ERROR; ret = RCL_RET_ERROR;
goto cleanup;
} }
if (validation_result != RMW_TOPIC_VALID) { if (validation_result != RMW_TOPIC_VALID) {
RCL_SET_ERROR_MSG(rmw_full_topic_name_validation_result_string(validation_result), *allocator) RCL_SET_ERROR_MSG(rmw_full_topic_name_validation_result_string(validation_result), *allocator)
return RCL_RET_SERVICE_NAME_INVALID; ret = RCL_RET_SERVICE_NAME_INVALID;
goto cleanup;
} }
// Allocate space for the implementation struct. // Allocate space for the implementation struct.
service->impl = (rcl_service_impl_t *)allocator->allocate( service->impl = (rcl_service_impl_t *)allocator->allocate(
sizeof(rcl_service_impl_t), allocator->state); sizeof(rcl_service_impl_t), allocator->state);
RCL_CHECK_FOR_NULL_WITH_MSG( RCL_CHECK_FOR_NULL_WITH_MSG(
service->impl, "allocating memory failed", return RCL_RET_BAD_ALLOC, *allocator); service->impl, "allocating memory failed", ret = RCL_RET_BAD_ALLOC; goto cleanup, *allocator);
if (RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL == options->qos.durability) { if (RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL == options->qos.durability) {
RCUTILS_LOG_WARN_NAMED( RCUTILS_LOG_WARN_NAMED(
@ -149,9 +173,8 @@ rcl_service_init(
service->impl->rmw_handle = rmw_create_service( service->impl->rmw_handle = rmw_create_service(
rcl_node_get_rmw_handle(node), rcl_node_get_rmw_handle(node),
type_support, type_support,
expanded_service_name, remapped_service_name,
&options->qos); &options->qos);
allocator->deallocate(expanded_service_name, allocator->state);
if (!service->impl->rmw_handle) { if (!service->impl->rmw_handle) {
RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator); RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator);
goto fail; goto fail;
@ -159,12 +182,22 @@ rcl_service_init(
// options // options
service->impl->options = *options; service->impl->options = *options;
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Service initialized") RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Service initialized")
return RCL_RET_OK; ret = RCL_RET_OK;
goto cleanup;
fail: fail:
if (service->impl) { if (service->impl) {
allocator->deallocate(service->impl, allocator->state); allocator->deallocate(service->impl, allocator->state);
} }
return fail_ret; ret = fail_ret;
// Fall through to clean up
cleanup:
if (NULL != expanded_service_name) {
allocator->deallocate(expanded_service_name, allocator->state);
}
if (NULL != remapped_service_name) {
allocator->deallocate(remapped_service_name, allocator->state);
}
return ret;
} }
rcl_ret_t rcl_ret_t

View file

@ -23,6 +23,7 @@ extern "C"
#include "rcl/error_handling.h" #include "rcl/error_handling.h"
#include "rcl/expand_topic_name.h" #include "rcl/expand_topic_name.h"
#include "rcl/remap.h"
#include "rcutils/logging_macros.h" #include "rcutils/logging_macros.h"
#include "rmw/error_handling.h" #include "rmw/error_handling.h"
#include "rmw/rmw.h" #include "rmw/rmw.h"
@ -96,6 +97,7 @@ rcl_subscription_init(
return RCL_RET_ERROR; return RCL_RET_ERROR;
} }
char * expanded_topic_name = NULL; char * expanded_topic_name = NULL;
char * remapped_topic_name = NULL;
ret = rcl_expand_topic_name( ret = rcl_expand_topic_name(
topic_name, topic_name,
rcl_node_get_name(node), rcl_node_get_name(node),
@ -106,45 +108,66 @@ rcl_subscription_init(
rcutils_ret = rcutils_string_map_fini(&substitutions_map); rcutils_ret = rcutils_string_map_fini(&substitutions_map);
if (rcutils_ret != RCUTILS_RET_OK) { if (rcutils_ret != RCUTILS_RET_OK) {
RCL_SET_ERROR_MSG(rcutils_get_error_string_safe(), *allocator) RCL_SET_ERROR_MSG(rcutils_get_error_string_safe(), *allocator)
allocator->deallocate(expanded_topic_name, allocator->state); ret = RCL_RET_ERROR;
return RCL_RET_ERROR; goto cleanup;
} }
if (ret != RCL_RET_OK) { if (ret != RCL_RET_OK) {
if (ret == RCL_RET_BAD_ALLOC) { if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
return ret; ret = RCL_RET_TOPIC_NAME_INVALID;
} else if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
return RCL_RET_TOPIC_NAME_INVALID;
} else { } else {
return RCL_RET_ERROR; ret = RCL_RET_ERROR;
} }
goto cleanup;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Expanded topic name '%s'", expanded_topic_name) RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Expanded topic name '%s'", expanded_topic_name)
const rcl_node_options_t * node_options = rcl_node_get_options(node);
if (NULL == node_options) {
ret = RCL_RET_ERROR;
goto cleanup;
}
rcl_arguments_t * global_args = NULL;
if (node_options->use_global_arguments) {
global_args = rcl_get_global_arguments();
}
ret = rcl_remap_topic_name(
&(node_options->arguments), global_args, expanded_topic_name,
rcl_node_get_name(node), rcl_node_get_namespace(node), *allocator, &remapped_topic_name);
if (RCL_RET_OK != ret) {
goto fail;
} else if (NULL == remapped_topic_name) {
remapped_topic_name = expanded_topic_name;
expanded_topic_name = NULL;
}
// Validate the expanded topic name. // Validate the expanded topic name.
int validation_result; int validation_result;
rmw_ret_t rmw_ret = rmw_validate_full_topic_name(expanded_topic_name, &validation_result, NULL); rmw_ret_t rmw_ret = rmw_validate_full_topic_name(remapped_topic_name, &validation_result, NULL);
if (rmw_ret != RMW_RET_OK) { if (rmw_ret != RMW_RET_OK) {
RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator); RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator);
return RCL_RET_ERROR; ret = RCL_RET_ERROR;
goto cleanup;
} }
if (validation_result != RMW_TOPIC_VALID) { if (validation_result != RMW_TOPIC_VALID) {
RCL_SET_ERROR_MSG(rmw_full_topic_name_validation_result_string(validation_result), *allocator) RCL_SET_ERROR_MSG(rmw_full_topic_name_validation_result_string(validation_result), *allocator)
return RCL_RET_TOPIC_NAME_INVALID; ret = RCL_RET_TOPIC_NAME_INVALID;
goto cleanup;
} }
// Allocate memory for the implementation struct. // Allocate memory for the implementation struct.
subscription->impl = (rcl_subscription_impl_t *)allocator->allocate( subscription->impl = (rcl_subscription_impl_t *)allocator->allocate(
sizeof(rcl_subscription_impl_t), allocator->state); sizeof(rcl_subscription_impl_t), allocator->state);
RCL_CHECK_FOR_NULL_WITH_MSG( RCL_CHECK_FOR_NULL_WITH_MSG(
subscription->impl, "allocating memory failed", return RCL_RET_BAD_ALLOC, *allocator); subscription->impl, "allocating memory failed", ret = RCL_RET_BAD_ALLOC; goto cleanup,
*allocator);
// Fill out the implemenation struct. // Fill out the implemenation struct.
// rmw_handle // rmw_handle
// TODO(wjwwood): pass allocator once supported in rmw api. // TODO(wjwwood): pass allocator once supported in rmw api.
subscription->impl->rmw_handle = rmw_create_subscription( subscription->impl->rmw_handle = rmw_create_subscription(
rcl_node_get_rmw_handle(node), rcl_node_get_rmw_handle(node),
type_support, type_support,
expanded_topic_name, remapped_topic_name,
&(options->qos), &(options->qos),
options->ignore_local_publications); options->ignore_local_publications);
allocator->deallocate(expanded_topic_name, allocator->state);
if (!subscription->impl->rmw_handle) { if (!subscription->impl->rmw_handle) {
RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator); RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), *allocator);
goto fail; goto fail;
@ -152,12 +175,22 @@ rcl_subscription_init(
// options // options
subscription->impl->options = *options; subscription->impl->options = *options;
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Subscription initialized") RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Subscription initialized")
return RCL_RET_OK; ret = RCL_RET_OK;
goto cleanup;
fail: fail:
if (subscription->impl) { if (subscription->impl) {
allocator->deallocate(subscription->impl, allocator->state); allocator->deallocate(subscription->impl, allocator->state);
} }
return fail_ret; ret = fail_ret;
// Fall through to cleanup
cleanup:
if (NULL != expanded_topic_name) {
allocator->deallocate(expanded_topic_name, allocator->state);
}
if (NULL != remapped_topic_name) {
allocator->deallocate(remapped_topic_name, allocator->state);
}
return ret;
} }
rcl_ret_t rcl_ret_t

View file

@ -100,6 +100,29 @@ function(test_target_function)
AMENT_DEPENDENCIES ${rmw_implementation} AMENT_DEPENDENCIES ${rmw_implementation}
) )
rcl_add_custom_gtest(test_arguments${target_suffix}
SRCS rcl/test_arguments.cpp
ENV ${extra_test_env}
APPEND_LIBRARY_DIRS ${extra_lib_dirs}
LIBRARIES ${PROJECT_NAME} ${extra_test_libraries}
)
rcl_add_custom_gtest(test_remap${target_suffix}
SRCS rcl/test_remap.cpp
ENV ${extra_test_env}
APPEND_LIBRARY_DIRS ${extra_lib_dirs}
LIBRARIES ${PROJECT_NAME} ${extra_test_libraries}
)
rcl_add_custom_gtest(test_remap_integration${target_suffix}
SRCS rcl/test_remap_integration.cpp
TIMEOUT 200
ENV ${extra_test_env}
APPEND_LIBRARY_DIRS ${extra_lib_dirs}
LIBRARIES ${PROJECT_NAME} ${extra_test_libraries}
AMENT_DEPENDENCIES "std_msgs" "example_interfaces"
)
rcl_add_custom_gtest(test_guard_condition${target_suffix} rcl_add_custom_gtest(test_guard_condition${target_suffix}
SRCS rcl/test_guard_condition.cpp SRCS rcl/test_guard_condition.cpp
ENV ${extra_test_env} ENV ${extra_test_env}

View file

@ -0,0 +1,75 @@
// 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 RCL__ARG_MACROS_HPP_
#define RCL__ARG_MACROS_HPP_
#include "rcl/error_handling.h"
#include "rcl/rcl.h"
#include "rcutils/strdup.h"
#include "../scope_exit.hpp"
/// Helper to get around non-const args passed to rcl_init().
char **
copy_args(int argc, const char ** args)
{
rcl_allocator_t allocator = rcl_get_default_allocator();
char ** copy = static_cast<char **>(allocator.allocate(sizeof(char *) * argc, allocator.state));
for (int i = 0; i < argc; ++i) {
copy[i] = rcutils_strdup(args[i], allocator);
}
return copy;
}
/// Destroy args allocated by copy_args.
void
destroy_args(int argc, char ** args)
{
rcl_allocator_t allocator = rcl_get_default_allocator();
for (int i = 0; i < argc; ++i) {
allocator.deallocate(args[i], allocator.state);
}
allocator.deallocate(args, allocator.state);
}
#define SCOPE_GLOBAL_ARGS(argc, argv, ...) \
{ \
const char * const_argv[] = {__VA_ARGS__}; \
argc = (sizeof(const_argv) / sizeof(const char *)); \
argv = copy_args(argc, const_argv); \
rcl_ret_t ret = rcl_init(argc, argv, rcl_get_default_allocator()); \
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe(); \
} \
auto __scope_global_args_exit = make_scope_exit( \
[argc, argv] { \
destroy_args(argc, argv); \
rcl_ret_t ret = rcl_shutdown(); \
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe(); \
})
#define SCOPE_ARGS(local_arguments, ...) \
{ \
const char * local_argv[] = {__VA_ARGS__}; \
unsigned int local_argc = (sizeof(local_argv) / sizeof(const char *)); \
rcl_ret_t ret = rcl_parse_arguments( \
local_argc, local_argv, rcl_get_default_allocator(), &local_arguments); \
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe(); \
} \
auto __scope_ ## local_arguments ## _exit = make_scope_exit( \
[&local_arguments] { \
ASSERT_EQ(RCL_RET_OK, rcl_arguments_fini(&local_arguments)); \
})
#endif // RCL__ARG_MACROS_HPP_

View file

@ -0,0 +1,191 @@
// 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.
#include <gtest/gtest.h>
#include <sstream>
#include "rcl/rcl.h"
#include "rcl/arguments.h"
#include "rcl/error_handling.h"
#ifdef RMW_IMPLEMENTATION
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
#else
# define CLASSNAME(NAME, SUFFIX) NAME
#endif
class CLASSNAME (TestArgumentsFixture, RMW_IMPLEMENTATION) : public ::testing::Test
{
public:
void SetUp()
{
}
void TearDown()
{
}
};
#define EXPECT_UNPARSED(parsed_args, ...) \
do { \
int expect_unparsed[] = {__VA_ARGS__}; \
int expect_num_unparsed = sizeof(expect_unparsed) / sizeof(int); \
rcl_allocator_t alloc = rcl_get_default_allocator(); \
int actual_num_unparsed = rcl_arguments_get_count_unparsed(&parsed_args); \
int * actual_unparsed = NULL; \
if (actual_num_unparsed > 0) { \
rcl_ret_t ret = rcl_arguments_get_unparsed(&parsed_args, alloc, &actual_unparsed); \
ASSERT_EQ(RCL_RET_OK, ret); \
} \
std::stringstream expected; \
expected << "["; \
for (int e = 0; e < expect_num_unparsed; ++e) { \
expected << expect_unparsed[e] << ", "; \
} \
expected << "]"; \
std::stringstream actual; \
actual << "["; \
for (int a = 0; a < actual_num_unparsed; ++a) { \
actual << actual_unparsed[a] << ", "; \
} \
actual << "]"; \
if (NULL != actual_unparsed) { \
alloc.deallocate(actual_unparsed, alloc.state); \
} \
EXPECT_STREQ(expected.str().c_str(), actual.str().c_str()); \
} while (0)
bool
is_valid_arg(const char * arg)
{
const char * argv[] = {arg};
rcl_arguments_t parsed_args;
rcl_ret_t ret = rcl_parse_arguments(1, argv, rcl_get_default_allocator(), &parsed_args);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
bool is_valid = 0 == rcl_arguments_get_count_unparsed(&parsed_args);
EXPECT_EQ(RCL_RET_OK, rcl_arguments_fini(&parsed_args));
return is_valid;
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), check_valid_vs_invalid_args) {
EXPECT_TRUE(is_valid_arg("__node:=node_name"));
EXPECT_TRUE(is_valid_arg("old_name:__node:=node_name"));
EXPECT_TRUE(is_valid_arg("old_name:__node:=nodename123"));
EXPECT_TRUE(is_valid_arg("__node:=nodename123"));
EXPECT_TRUE(is_valid_arg("__ns:=/foo/bar"));
EXPECT_TRUE(is_valid_arg("__ns:=/"));
EXPECT_TRUE(is_valid_arg("nodename:__ns:=/foobar"));
EXPECT_TRUE(is_valid_arg("foo:=bar"));
EXPECT_TRUE(is_valid_arg("~/foo:=~/bar"));
EXPECT_TRUE(is_valid_arg("/foo/bar:=bar"));
EXPECT_TRUE(is_valid_arg("foo:=/bar"));
EXPECT_TRUE(is_valid_arg("/foo123:=/bar123"));
EXPECT_TRUE(is_valid_arg("node:/foo123:=/bar123"));
EXPECT_FALSE(is_valid_arg(":="));
EXPECT_FALSE(is_valid_arg("foo:="));
EXPECT_FALSE(is_valid_arg(":=bar"));
EXPECT_FALSE(is_valid_arg("__ns:="));
EXPECT_FALSE(is_valid_arg("__node:="));
EXPECT_FALSE(is_valid_arg("__node:=/foo/bar"));
EXPECT_FALSE(is_valid_arg("__ns:=foo"));
EXPECT_FALSE(is_valid_arg(":__node:=nodename"));
EXPECT_FALSE(is_valid_arg("~:__node:=nodename"));
EXPECT_FALSE(is_valid_arg("}foo:=/bar"));
EXPECT_FALSE(is_valid_arg("f oo:=/bar"));
EXPECT_FALSE(is_valid_arg("foo:=/b ar"));
EXPECT_FALSE(is_valid_arg("f{oo:=/bar"));
EXPECT_FALSE(is_valid_arg("foo:=/b}ar"));
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_no_args) {
rcl_arguments_t parsed_args;
rcl_ret_t ret = rcl_parse_arguments(0, NULL, rcl_get_default_allocator(), &parsed_args);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_EQ(0, rcl_arguments_get_count_unparsed(&parsed_args));
EXPECT_EQ(RCL_RET_OK, rcl_arguments_fini(&parsed_args));
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_null_args) {
int argc = 1;
rcl_arguments_t parsed_args;
rcl_ret_t ret = rcl_parse_arguments(argc, NULL, rcl_get_default_allocator(), &parsed_args);
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
rcl_reset_error();
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_null_args_output) {
const char * argv[] = {"process_name"};
int argc = sizeof(argv) / sizeof(const char *);
rcl_ret_t ret = rcl_parse_arguments(argc, argv, rcl_get_default_allocator(), NULL);
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
rcl_reset_error();
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_one_remap) {
const char * argv[] = {"process_name", "/foo/bar:=/fiz/buz"};
int argc = sizeof(argv) / sizeof(const char *);
rcl_arguments_t parsed_args;
rcl_ret_t ret;
ret = rcl_parse_arguments(argc, argv, rcl_get_default_allocator(), &parsed_args);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_UNPARSED(parsed_args, 0);
EXPECT_EQ(RCL_RET_OK, rcl_arguments_fini(&parsed_args));
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_mix_valid_invalid_rules) {
const char * argv[] = {"process_name", "/foo/bar:=", "bar:=/fiz/buz", "}bar:=fiz"};
int argc = sizeof(argv) / sizeof(const char *);
rcl_arguments_t parsed_args;
rcl_ret_t ret;
ret = rcl_parse_arguments(argc, argv, rcl_get_default_allocator(), &parsed_args);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_UNPARSED(parsed_args, 0, 1, 3);
EXPECT_EQ(RCL_RET_OK, rcl_arguments_fini(&parsed_args));
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_two_namespace) {
const char * argv[] = {"process_name", "__ns:=/foo/bar", "__ns:=/fiz/buz"};
int argc = sizeof(argv) / sizeof(const char *);
rcl_arguments_t parsed_args;
rcl_ret_t ret;
ret = rcl_parse_arguments(argc, argv, rcl_get_default_allocator(), &parsed_args);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_UNPARSED(parsed_args, 0);
EXPECT_EQ(RCL_RET_OK, rcl_arguments_fini(&parsed_args));
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_fini_null) {
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, rcl_arguments_fini(NULL));
rcl_reset_error();
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_fini_impl_null) {
rcl_arguments_t parsed_args;
parsed_args.impl = NULL;
EXPECT_EQ(RCL_RET_ERROR, rcl_arguments_fini(&parsed_args));
rcl_reset_error();
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_fini_twice) {
const char * argv[] = {"process_name"};
int argc = sizeof(argv) / sizeof(const char *);
rcl_arguments_t parsed_args;
ASSERT_EQ(RCL_RET_OK, rcl_parse_arguments(argc, argv, rcl_get_default_allocator(), &parsed_args));
EXPECT_EQ(RCL_RET_OK, rcl_arguments_fini(&parsed_args));
EXPECT_EQ(RCL_RET_ERROR, rcl_arguments_fini(&parsed_args));
rcl_reset_error();
}

467
rcl/test/rcl/test_remap.cpp Normal file
View file

@ -0,0 +1,467 @@
// 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.
#include <gtest/gtest.h>
#include "rcl/rcl.h"
#include "rcl/remap.h"
#include "rcl/error_handling.h"
#include "./arg_macros.hpp"
#ifdef RMW_IMPLEMENTATION
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
#else
# define CLASSNAME(NAME, SUFFIX) NAME
#endif
class CLASSNAME (TestRemapFixture, RMW_IMPLEMENTATION) : public ::testing::Test
{
public:
void SetUp()
{
}
void TearDown()
{
}
};
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), global_namespace_replacement) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name", "__ns:=/foo/bar");
char * output = NULL;
ret = rcl_remap_node_namespace(
NULL, &global_arguments, "NodeName", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/foo/bar", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), nodename_prefix_namespace_remap) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(
global_arguments,
"process_name", "Node1:__ns:=/foo/bar", "Node2:__ns:=/this_one", "Node3:__ns:=/bar/foo");
{
char * output = NULL;
ret = rcl_remap_node_namespace(
NULL, &global_arguments, "Node1", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/foo/bar", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
{
char * output = NULL;
ret = rcl_remap_node_namespace(
NULL, &global_arguments, "Node2", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/this_one", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
{
char * output = NULL;
ret = rcl_remap_node_namespace(
NULL, &global_arguments, "Node3", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/bar/foo", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), no_namespace_replacement) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name");
char * output = NULL;
ret = rcl_remap_node_namespace(
NULL, &global_arguments, "NodeName", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(NULL, output);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), local_namespace_replacement_before_global) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name", "__ns:=/global_args");
rcl_arguments_t local_arguments;
SCOPE_ARGS(local_arguments, "process_name", "__ns:=/local_args");
char * output = NULL;
ret = rcl_remap_node_namespace(
&local_arguments, &global_arguments, "NodeName", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/local_args", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), no_use_global_namespace_replacement) {
rcl_ret_t ret;
rcl_arguments_t local_arguments;
SCOPE_ARGS(local_arguments, "process_name");
char * output = NULL;
ret = rcl_remap_node_namespace(
&local_arguments, NULL, "NodeName", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(NULL, output);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), other_rules_before_namespace_rule) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(
global_arguments, "process_name", "/foobar:=/foo/bar", "__ns:=/namespace", "__node:=new_name");
rcl_allocator_t allocator = rcl_get_default_allocator();
char * output = NULL;
ret = rcl_remap_node_namespace(NULL, &global_arguments, "NodeName", allocator, &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/namespace", output);
allocator.deallocate(output, allocator.state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), global_topic_name_replacement) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name", "/bar/foo:=/foo/bar");
{
char * output = NULL;
ret = rcl_remap_topic_name(
NULL, &global_arguments, "/bar/foo", "NodeName", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
ASSERT_STREQ("/foo/bar", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
{
char * output = NULL;
ret = rcl_remap_topic_name(
NULL, &global_arguments, "/foo/bar", "NodeName", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(NULL, output);
}
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), relative_topic_name_remap) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name", "foo:=bar");
char * output = NULL;
ret = rcl_remap_topic_name(
NULL, &global_arguments, "/ns/foo", "NodeName", "/ns", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
ASSERT_STREQ("/ns/bar", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), nodename_prefix_topic_remap) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(
global_arguments,
"process_name", "Node1:/foo:=/foo/bar", "Node2:/foo:=/this_one", "Node3:/foo:=/bar/foo");
{
char * output = NULL;
ret = rcl_remap_topic_name(
NULL, &global_arguments, "/foo", "Node1", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/foo/bar", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
{
char * output = NULL;
ret = rcl_remap_topic_name(
NULL, &global_arguments, "/foo", "Node2", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/this_one", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
{
char * output = NULL;
ret = rcl_remap_topic_name(
NULL, &global_arguments, "/foo", "Node3", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/bar/foo", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), no_use_global_topic_name_replacement) {
rcl_ret_t ret;
rcl_arguments_t local_arguments;
SCOPE_ARGS(local_arguments, "process_name");
char * output = NULL;
ret = rcl_remap_topic_name(
&local_arguments, NULL, "/bar/foo", "NodeName", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(NULL, output);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), no_topic_name_replacement) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name");
char * output = NULL;
ret = rcl_remap_topic_name(
NULL, &global_arguments, "/bar/foo", "NodeName", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(NULL, output);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), local_topic_replacement_before_global) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name", "/bar/foo:=/foo/global_args");
rcl_arguments_t local_arguments;
SCOPE_ARGS(local_arguments, "process_name", "/bar/foo:=/foo/local_args");
char * output = NULL;
ret = rcl_remap_topic_name(
&local_arguments, &global_arguments, "/bar/foo", "NodeName", "/", rcl_get_default_allocator(),
&output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/foo/local_args", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), other_rules_before_topic_rule) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(
global_arguments, "process_name", "__ns:=/namespace", "__node:=remap_name",
"/foobar:=/foo/bar");
rcl_allocator_t allocator = rcl_get_default_allocator();
char * output = NULL;
ret = rcl_remap_topic_name(
NULL, &global_arguments, "/foobar", "NodeName", "/", allocator, &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/foo/bar", output);
allocator.deallocate(output, allocator.state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), global_service_name_replacement) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name", "/bar/foo:=/foo/bar");
{
char * output = NULL;
ret = rcl_remap_service_name(
NULL, &global_arguments, "/bar/foo", "NodeName", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
ASSERT_STREQ("/foo/bar", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
{
char * output = NULL;
ret = rcl_remap_service_name(
NULL, &global_arguments, "/foobar", "NodeName", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(NULL, output);
}
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), relative_service_name_remap) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name", "foo:=bar");
char * output = NULL;
ret = rcl_remap_service_name(
NULL, &global_arguments, "/ns/foo", "NodeName", "/ns", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
ASSERT_STREQ("/ns/bar", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), nodename_prefix_service_remap) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(
global_arguments,
"process_name", "Node1:/foo:=/foo/bar", "Node2:/foo:=/this_one", "Node3:/foo:=/bar/foo");
{
char * output = NULL;
ret = rcl_remap_service_name(
NULL, &global_arguments, "/foo", "Node1", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/foo/bar", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
{
char * output = NULL;
ret = rcl_remap_service_name(
NULL, &global_arguments, "/foo", "Node2", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/this_one", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
{
char * output = NULL;
ret = rcl_remap_service_name(
NULL, &global_arguments, "/foo", "Node3", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/bar/foo", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), no_use_global_service_name_replacement) {
rcl_ret_t ret;
rcl_arguments_t local_arguments;
SCOPE_ARGS(local_arguments, "process_name");
char * output = NULL;
ret = rcl_remap_service_name(
&local_arguments, NULL, "/bar/foo", "NodeName", "/", rcl_get_default_allocator(),
&output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(NULL, output);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), no_service_name_replacement) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name");
char * output = NULL;
ret = rcl_remap_service_name(
NULL, &global_arguments, "/bar/foo", "NodeName", "/", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(NULL, output);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), local_service_replacement_before_global) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name", "/bar/foo:=/foo/global_args");
rcl_arguments_t local_arguments;
SCOPE_ARGS(local_arguments, "process_name", "/bar/foo:=/foo/local_args");
char * output = NULL;
ret = rcl_remap_service_name(
&local_arguments, &global_arguments, "/bar/foo", "NodeName", "/", rcl_get_default_allocator(),
&output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/foo/local_args", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), other_rules_before_service_rule) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(
global_arguments, "process_name", "__ns:=/namespace", "__node:=remap_name",
"/foobar:=/foo/bar");
rcl_allocator_t allocator = rcl_get_default_allocator();
char * output = NULL;
ret = rcl_remap_service_name(
NULL, &global_arguments, "/foobar", "NodeName", "/", allocator, &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("/foo/bar", output);
allocator.deallocate(output, allocator.state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), global_nodename_replacement) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name", "__node:=globalname");
rcl_allocator_t allocator = rcl_get_default_allocator();
char * output = NULL;
ret = rcl_remap_node_name(NULL, &global_arguments, "NodeName", allocator, &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("globalname", output);
allocator.deallocate(output, allocator.state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), no_nodename_replacement) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name");
char * output = NULL;
ret = rcl_remap_node_name(NULL, &global_arguments, "NodeName", rcl_get_default_allocator(),
&output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(NULL, output);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), local_nodename_replacement_before_global) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name", "__node:=global_name");
rcl_arguments_t local_arguments;
SCOPE_ARGS(local_arguments, "process_name", "__node:=local_name");
char * output = NULL;
ret = rcl_remap_node_name(
&local_arguments, &global_arguments, "NodeName", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("local_name", output);
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), no_use_global_nodename_replacement) {
rcl_ret_t ret;
rcl_arguments_t local_arguments;
SCOPE_ARGS(local_arguments, "process_name");
char * output = NULL;
ret = rcl_remap_node_name(
&local_arguments, NULL, "NodeName", rcl_get_default_allocator(), &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(NULL, output);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), use_first_nodename_rule) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(global_arguments, "process_name", "__node:=firstname", "__node:=secondname");
rcl_allocator_t allocator = rcl_get_default_allocator();
char * output = NULL;
ret = rcl_remap_node_name(NULL, &global_arguments, "NodeName", allocator, &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("firstname", output);
allocator.deallocate(output, allocator.state);
}
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), other_rules_before_nodename_rule) {
rcl_ret_t ret;
rcl_arguments_t global_arguments;
SCOPE_ARGS(
global_arguments, "process_name", "/foobar:=/foo", "__ns:=/namespace", "__node:=remap_name");
rcl_allocator_t allocator = rcl_get_default_allocator();
char * output = NULL;
ret = rcl_remap_node_name(NULL, &global_arguments, "NodeName", allocator, &output);
EXPECT_EQ(RCL_RET_OK, ret);
EXPECT_STREQ("remap_name", output);
allocator.deallocate(output, allocator.state);
}

View file

@ -0,0 +1,289 @@
// 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.
#include <gtest/gtest.h>
#include "example_interfaces/srv/add_two_ints.h"
#include "rcl/rcl.h"
#include "rcl/remap.h"
#include "rcl/error_handling.h"
#include "std_msgs/msg/int64.h"
#include "./arg_macros.hpp"
#ifdef RMW_IMPLEMENTATION
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
#else
# define CLASSNAME(NAME, SUFFIX) NAME
#endif
class CLASSNAME (TestRemapIntegrationFixture, RMW_IMPLEMENTATION) : public ::testing::Test
{
public:
void SetUp()
{
}
void TearDown()
{
}
};
TEST_F(CLASSNAME(TestRemapIntegrationFixture, RMW_IMPLEMENTATION), remap_using_global_rule) {
int argc;
char ** argv;
SCOPE_GLOBAL_ARGS(
argc, argv, "process_name", "__node:=new_name", "__ns:=/new_ns", "/foo/bar:=/bar/foo");
rcl_node_t node = rcl_get_zero_initialized_node();
rcl_node_options_t default_options = rcl_node_get_default_options();
ASSERT_EQ(RCL_RET_OK, rcl_node_init(&node, "original_name", "/original_ns", &default_options));
{ // Node name gets remapped
EXPECT_STREQ("new_name", rcl_node_get_name(&node));
}
{ // Node namespace gets remapped
EXPECT_STREQ("/new_ns", rcl_node_get_namespace(&node));
}
{ // Logger name gets remapped
EXPECT_STREQ("new_ns.new_name", rcl_node_get_logger_name(&node));
}
{ // Publisher topic gets remapped
const rosidl_message_type_support_t * ts = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int64);
rcl_publisher_options_t publisher_options = rcl_publisher_get_default_options();
rcl_publisher_t publisher = rcl_get_zero_initialized_publisher();
rcl_ret_t ret = rcl_publisher_init(&publisher, &node, ts, "/foo/bar", &publisher_options);
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/bar/foo", rcl_publisher_get_topic_name(&publisher));
EXPECT_EQ(RCL_RET_OK, rcl_publisher_fini(&publisher, &node));
}
{ // Subscription topic gets remapped
const rosidl_message_type_support_t * ts = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int64);
rcl_subscription_options_t subscription_options = rcl_subscription_get_default_options();
rcl_subscription_t subscription = rcl_get_zero_initialized_subscription();
rcl_ret_t ret = rcl_subscription_init(
&subscription, &node, ts, "/foo/bar", &subscription_options);
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/bar/foo", rcl_subscription_get_topic_name(&subscription));
EXPECT_EQ(RCL_RET_OK, rcl_subscription_fini(&subscription, &node));
}
{ // Client service name gets remapped
const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT(
example_interfaces, AddTwoInts);
rcl_client_options_t client_options = rcl_client_get_default_options();
rcl_client_t client = rcl_get_zero_initialized_client();
rcl_ret_t ret = rcl_client_init(&client, &node, ts, "/foo/bar", &client_options);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/bar/foo", rcl_client_get_service_name(&client));
EXPECT_EQ(RCL_RET_OK, rcl_client_fini(&client, &node));
}
{ // Server service name gets remapped
const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT(
example_interfaces, AddTwoInts);
rcl_service_options_t service_options = rcl_service_get_default_options();
rcl_service_t service = rcl_get_zero_initialized_service();
rcl_ret_t ret = rcl_service_init(&service, &node, ts, "/foo/bar", &service_options);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/bar/foo", rcl_service_get_service_name(&service));
EXPECT_EQ(RCL_RET_OK, rcl_service_fini(&service, &node));
}
EXPECT_EQ(RCL_RET_OK, rcl_node_fini(&node));
}
TEST_F(CLASSNAME(TestRemapIntegrationFixture, RMW_IMPLEMENTATION), ignore_global_rules) {
int argc;
char ** argv;
SCOPE_GLOBAL_ARGS(
argc, argv, "process_name", "__node:=new_name", "__ns:=/new_ns", "/foo/bar:=/bar/foo");
rcl_arguments_t local_arguments;
SCOPE_ARGS(local_arguments, "local_process_name");
rcl_node_t node = rcl_get_zero_initialized_node();
rcl_node_options_t options = rcl_node_get_default_options();
options.use_global_arguments = false;
options.arguments = local_arguments;
ASSERT_EQ(RCL_RET_OK, rcl_node_init(&node, "original_name", "/original_ns", &options));
{ // Node name does not get remapped
EXPECT_STREQ("original_name", rcl_node_get_name(&node));
}
{ // Node namespace does not get remapped
EXPECT_STREQ("/original_ns", rcl_node_get_namespace(&node));
}
{ // Logger name gets remapped
EXPECT_STREQ("original_ns.original_name", rcl_node_get_logger_name(&node));
}
{ // Publisher topic does not get remapped
const rosidl_message_type_support_t * ts = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int64);
rcl_publisher_options_t publisher_options = rcl_publisher_get_default_options();
rcl_publisher_t publisher = rcl_get_zero_initialized_publisher();
rcl_ret_t ret = rcl_publisher_init(&publisher, &node, ts, "/foo/bar", &publisher_options);
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/foo/bar", rcl_publisher_get_topic_name(&publisher));
EXPECT_EQ(RCL_RET_OK, rcl_publisher_fini(&publisher, &node));
}
{ // Subscription topic does not get remapped
const rosidl_message_type_support_t * ts = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int64);
rcl_subscription_options_t subscription_options = rcl_subscription_get_default_options();
rcl_subscription_t subscription = rcl_get_zero_initialized_subscription();
rcl_ret_t ret = rcl_subscription_init(
&subscription, &node, ts, "/foo/bar", &subscription_options);
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/foo/bar", rcl_subscription_get_topic_name(&subscription));
EXPECT_EQ(RCL_RET_OK, rcl_subscription_fini(&subscription, &node));
}
{ // Client service name does not get remapped
const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT(
example_interfaces, AddTwoInts);
rcl_client_options_t client_options = rcl_client_get_default_options();
rcl_client_t client = rcl_get_zero_initialized_client();
rcl_ret_t ret = rcl_client_init(&client, &node, ts, "/foo/bar", &client_options);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/foo/bar", rcl_client_get_service_name(&client));
EXPECT_EQ(RCL_RET_OK, rcl_client_fini(&client, &node));
}
{ // Server service name does not get remapped
const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT(
example_interfaces, AddTwoInts);
rcl_service_options_t service_options = rcl_service_get_default_options();
rcl_service_t service = rcl_get_zero_initialized_service();
rcl_ret_t ret = rcl_service_init(&service, &node, ts, "/foo/bar", &service_options);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/foo/bar", rcl_service_get_service_name(&service));
EXPECT_EQ(RCL_RET_OK, rcl_service_fini(&service, &node));
}
EXPECT_EQ(RCL_RET_OK, rcl_node_fini(&node));
}
TEST_F(CLASSNAME(TestRemapIntegrationFixture, RMW_IMPLEMENTATION), local_rules_before_global) {
int argc;
char ** argv;
SCOPE_GLOBAL_ARGS(
argc, argv, "process_name", "__node:=global_name", "__ns:=/global_ns", "/foo/bar:=/bar/global");
rcl_arguments_t local_arguments;
SCOPE_ARGS(
local_arguments,
"process_name", "__node:=local_name", "__ns:=/local_ns", "/foo/bar:=/bar/local");
rcl_node_t node = rcl_get_zero_initialized_node();
rcl_node_options_t options = rcl_node_get_default_options();
options.arguments = local_arguments;
ASSERT_EQ(RCL_RET_OK, rcl_node_init(&node, "original_name", "/original_ns", &options));
{ // Node name
EXPECT_STREQ("local_name", rcl_node_get_name(&node));
}
{ // Node namespace
EXPECT_STREQ("/local_ns", rcl_node_get_namespace(&node));
}
{ // Logger name
EXPECT_STREQ("local_ns.local_name", rcl_node_get_logger_name(&node));
}
{ // Publisher topic
const rosidl_message_type_support_t * ts = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int64);
rcl_publisher_options_t publisher_options = rcl_publisher_get_default_options();
rcl_publisher_t publisher = rcl_get_zero_initialized_publisher();
rcl_ret_t ret = rcl_publisher_init(&publisher, &node, ts, "/foo/bar", &publisher_options);
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/bar/local", rcl_publisher_get_topic_name(&publisher));
EXPECT_EQ(RCL_RET_OK, rcl_publisher_fini(&publisher, &node));
}
{ // Subscription topic
const rosidl_message_type_support_t * ts = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int64);
rcl_subscription_options_t subscription_options = rcl_subscription_get_default_options();
rcl_subscription_t subscription = rcl_get_zero_initialized_subscription();
rcl_ret_t ret = rcl_subscription_init(
&subscription, &node, ts, "/foo/bar", &subscription_options);
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/bar/local", rcl_subscription_get_topic_name(&subscription));
EXPECT_EQ(RCL_RET_OK, rcl_subscription_fini(&subscription, &node));
}
{ // Client service name
const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT(
example_interfaces, AddTwoInts);
rcl_client_options_t client_options = rcl_client_get_default_options();
rcl_client_t client = rcl_get_zero_initialized_client();
rcl_ret_t ret = rcl_client_init(&client, &node, ts, "/foo/bar", &client_options);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/bar/local", rcl_client_get_service_name(&client));
EXPECT_EQ(RCL_RET_OK, rcl_client_fini(&client, &node));
}
{ // Server service name
const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT(
example_interfaces, AddTwoInts);
rcl_service_options_t service_options = rcl_service_get_default_options();
rcl_service_t service = rcl_get_zero_initialized_service();
rcl_ret_t ret = rcl_service_init(&service, &node, ts, "/foo/bar", &service_options);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/bar/local", rcl_service_get_service_name(&service));
EXPECT_EQ(RCL_RET_OK, rcl_service_fini(&service, &node));
}
EXPECT_EQ(RCL_RET_OK, rcl_node_fini(&node));
}
TEST_F(CLASSNAME(TestRemapIntegrationFixture, RMW_IMPLEMENTATION), remap_relative_topic) {
int argc;
char ** argv;
SCOPE_GLOBAL_ARGS(argc, argv, "process_name", "/foo/bar:=remap/global");
rcl_node_t node = rcl_get_zero_initialized_node();
rcl_node_options_t default_options = rcl_node_get_default_options();
ASSERT_EQ(RCL_RET_OK, rcl_node_init(&node, "original_name", "/foo", &default_options));
{ // Publisher topic
const rosidl_message_type_support_t * ts = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int64);
rcl_publisher_options_t publisher_options = rcl_publisher_get_default_options();
rcl_publisher_t publisher = rcl_get_zero_initialized_publisher();
rcl_ret_t ret = rcl_publisher_init(&publisher, &node, ts, "bar", &publisher_options);
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/foo/remap/global", rcl_publisher_get_topic_name(&publisher));
EXPECT_EQ(RCL_RET_OK, rcl_publisher_fini(&publisher, &node));
}
{ // Subscription topic
const rosidl_message_type_support_t * ts = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int64);
rcl_subscription_options_t subscription_options = rcl_subscription_get_default_options();
rcl_subscription_t subscription = rcl_get_zero_initialized_subscription();
rcl_ret_t ret = rcl_subscription_init(
&subscription, &node, ts, "bar", &subscription_options);
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/foo/remap/global", rcl_subscription_get_topic_name(&subscription));
EXPECT_EQ(RCL_RET_OK, rcl_subscription_fini(&subscription, &node));
}
{ // Client service name
const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT(
example_interfaces, AddTwoInts);
rcl_client_options_t client_options = rcl_client_get_default_options();
rcl_client_t client = rcl_get_zero_initialized_client();
rcl_ret_t ret = rcl_client_init(&client, &node, ts, "bar", &client_options);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/foo/remap/global", rcl_client_get_service_name(&client));
EXPECT_EQ(RCL_RET_OK, rcl_client_fini(&client, &node));
}
{ // Server service name
const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT(
example_interfaces, AddTwoInts);
rcl_service_options_t service_options = rcl_service_get_default_options();
rcl_service_t service = rcl_get_zero_initialized_service();
rcl_ret_t ret = rcl_service_init(&service, &node, ts, "bar", &service_options);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
EXPECT_STREQ("/foo/remap/global", rcl_service_get_service_name(&service));
EXPECT_EQ(RCL_RET_OK, rcl_service_fini(&service, &node));
}
EXPECT_EQ(RCL_RET_OK, rcl_node_fini(&node));
}