Skip to content

Commit aecafdf

Browse files
authored
Add convenient node method to get a final topic/service name (#835)
Signed-off-by: Ivan Santiago Paunovic <[email protected]>
1 parent bf461df commit aecafdf

File tree

12 files changed

+374
-354
lines changed

12 files changed

+374
-354
lines changed

rcl/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ set(${PROJECT_NAME}_sources
5353
src/rcl/node_options.c
5454
src/rcl/publisher.c
5555
src/rcl/remap.c
56+
src/rcl/node_resolve_name.c
5657
src/rcl/rmw_implementation_identifier_check.c
5758
src/rcl/security.c
5859
src/rcl/service.c

rcl/include/rcl/expand_topic_name.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ extern "C"
2929
/// Expand a given topic name into a fully-qualified topic name.
3030
/**
3131
* The input_topic_name, node_name, and node_namespace arguments must all be
32-
* vaid, null terminated c strings.
32+
* valid, null terminated c strings.
3333
* The output_topic_name will not be assigned a value in the event of an error.
3434
*
3535
* The output_topic_name will be null terminated.

rcl/include/rcl/node.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,47 @@ RCL_WARN_UNUSED
490490
const char *
491491
rcl_node_get_logger_name(const rcl_node_t * node);
492492

493+
/// Expand a given name into a fully-qualified topic name and apply remapping rules.
494+
/**
495+
* <hr>
496+
* Attribute | Adherence
497+
* ------------------ | -------------
498+
* Allocates Memory | Yes
499+
* Thread-Safe | No
500+
* Uses Atomics | No
501+
* Lock-Free | Yes
502+
*
503+
* \param[in] node Node object. Its name, namespace, local/global command line arguments are used.
504+
* \param[in] input_name Topic name to be expanded and remapped.
505+
* \param[in] allocator The allocator to be used when creating the output topic.
506+
* \param[in] is_service For services use `true`, for topics use `false`.
507+
* \param[in] only_expand When `true`, remapping rules are ignored.
508+
* \param[out] output_name Output char * pointer.
509+
* \return `RCL_RET_OK` if the topic name was expanded successfully, or
510+
* \return `RCL_RET_INVALID_ARGUMENT` if any of input_name, node_name, node_namespace
511+
* or output_name are NULL, or
512+
* \return `RCL_RET_INVALID_ARGUMENT` if both local_args and global_args are NULL, or
513+
* \return `RCL_RET_BAD_ALLOC` if allocating memory failed, or
514+
* \return `RCL_RET_TOPIC_NAME_INVALID` if the given topic name is invalid
515+
* (see \ref rcl_validate_topic_name()), or
516+
* \return `RCL_RET_NODE_INVALID_NAME` if the given node name is invalid
517+
* (see \ref rmw_validate_node_name()), or
518+
* \return `RCL_RET_NODE_INVALID_NAMESPACE` if the given node namespace is invalid
519+
* (see \ref rmw_validate_namespace()), or
520+
* \return `RCL_RET_UNKNOWN_SUBSTITUTION` for unknown substitutions in name, or
521+
* \return `RCL_RET_ERROR` if an unspecified error occurs.
522+
*/
523+
RCL_PUBLIC
524+
RCL_WARN_UNUSED
525+
rcl_ret_t
526+
rcl_node_resolve_name(
527+
const rcl_node_t * node,
528+
const char * input_name,
529+
rcl_allocator_t allocator,
530+
bool is_service,
531+
bool only_expand,
532+
char ** output_name);
533+
493534
#ifdef __cplusplus
494535
}
495536
#endif

rcl/src/rcl/client.c

Lines changed: 12 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,12 @@ extern "C"
2323
#include <string.h>
2424

2525
#include "rcl/error_handling.h"
26-
#include "rcl/expand_topic_name.h"
27-
#include "rcl/remap.h"
26+
#include "rcl/node.h"
2827
#include "rcutils/logging_macros.h"
2928
#include "rcutils/macros.h"
3029
#include "rcutils/stdatomic_helper.h"
3130
#include "rmw/error_handling.h"
3231
#include "rmw/rmw.h"
33-
#include "rmw/validate_full_topic_name.h"
3432
#include "tracetools/tracetools.h"
3533

3634
#include "./common.h"
@@ -75,89 +73,27 @@ rcl_client_init(
7573
RCL_SET_ERROR_MSG("client already initialized, or memory was unintialized");
7674
return RCL_RET_ALREADY_INIT;
7775
}
76+
7877
// Expand the given service name.
79-
rcutils_allocator_t rcutils_allocator = *allocator; // implicit conversion to rcutils version
80-
rcutils_string_map_t substitutions_map = rcutils_get_zero_initialized_string_map();
81-
rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions_map, 0, rcutils_allocator);
82-
if (rcutils_ret != RCUTILS_RET_OK) {
83-
RCL_SET_ERROR_MSG(rcutils_get_error_string().str);
84-
if (rcutils_ret == RCUTILS_RET_BAD_ALLOC) {
85-
return RCL_RET_BAD_ALLOC;
86-
}
87-
return RCL_RET_ERROR;
88-
}
89-
rcl_ret_t ret = rcl_get_default_topic_name_substitutions(&substitutions_map);
90-
if (ret != RCL_RET_OK) {
91-
rcutils_ret = rcutils_string_map_fini(&substitutions_map);
92-
if (rcutils_ret != RCUTILS_RET_OK) {
93-
RCUTILS_LOG_ERROR_NAMED(
94-
ROS_PACKAGE_NAME,
95-
"failed to fini string_map (%d) during error handling: %s\n",
96-
rcutils_ret,
97-
rcutils_get_error_string().str);
98-
}
99-
if (ret == RCL_RET_BAD_ALLOC) {
100-
return ret;
101-
}
102-
return RCL_RET_ERROR;
103-
}
104-
char * expanded_service_name = NULL;
10578
char * remapped_service_name = NULL;
106-
ret = rcl_expand_topic_name(
79+
rcl_ret_t ret = rcl_node_resolve_name(
80+
node,
10781
service_name,
108-
rcl_node_get_name(node),
109-
rcl_node_get_namespace(node),
110-
&substitutions_map,
11182
*allocator,
112-
&expanded_service_name);
113-
rcutils_ret = rcutils_string_map_fini(&substitutions_map);
114-
if (rcutils_ret != RCUTILS_RET_OK) {
115-
RCL_SET_ERROR_MSG(rcutils_get_error_string().str);
116-
allocator->deallocate(expanded_service_name, allocator->state);
117-
return RCL_RET_ERROR;
118-
}
83+
true,
84+
false,
85+
&remapped_service_name);
11986
if (ret != RCL_RET_OK) {
120-
if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
87+
if (ret == RCL_RET_SERVICE_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
12188
ret = RCL_RET_SERVICE_NAME_INVALID;
122-
} else {
89+
} else if (RCL_RET_BAD_ALLOC != ret) {
12390
ret = RCL_RET_ERROR;
12491
}
12592
goto cleanup;
12693
}
127-
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Expanded service name '%s'", expanded_service_name);
128-
129-
const rcl_node_options_t * node_options = rcl_node_get_options(node);
130-
if (NULL == node_options) {
131-
ret = RCL_RET_ERROR;
132-
goto cleanup;
133-
}
134-
rcl_arguments_t * global_args = NULL;
135-
if (node_options->use_global_arguments) {
136-
global_args = &(node->context->global_arguments);
137-
}
138-
ret = rcl_remap_service_name(
139-
&(node_options->arguments), global_args, expanded_service_name,
140-
rcl_node_get_name(node), rcl_node_get_namespace(node), *allocator, &remapped_service_name);
141-
if (RCL_RET_OK != ret) {
142-
goto fail;
143-
} else if (NULL == remapped_service_name) {
144-
remapped_service_name = expanded_service_name;
145-
expanded_service_name = NULL;
146-
}
94+
RCUTILS_LOG_DEBUG_NAMED(
95+
ROS_PACKAGE_NAME, "Expanded and remapped service name '%s'", remapped_service_name);
14796

148-
// Validate the expanded service name.
149-
int validation_result;
150-
rmw_ret_t rmw_ret = rmw_validate_full_topic_name(remapped_service_name, &validation_result, NULL);
151-
if (rmw_ret != RMW_RET_OK) {
152-
RCL_SET_ERROR_MSG(rmw_get_error_string().str);
153-
ret = RCL_RET_ERROR;
154-
goto cleanup;
155-
}
156-
if (validation_result != RMW_TOPIC_VALID) {
157-
RCL_SET_ERROR_MSG(rmw_full_topic_name_validation_result_string(validation_result));
158-
ret = RCL_RET_SERVICE_NAME_INVALID;
159-
goto cleanup;
160-
}
16197
// Allocate space for the implementation struct.
16298
client->impl = (rcl_client_impl_t *)allocator->allocate(
16399
sizeof(rcl_client_impl_t), allocator->state);
@@ -195,12 +131,7 @@ rcl_client_init(
195131
ret = fail_ret;
196132
// Fall through to cleanup
197133
cleanup:
198-
if (NULL != expanded_service_name) {
199-
allocator->deallocate(expanded_service_name, allocator->state);
200-
}
201-
if (NULL != remapped_service_name) {
202-
allocator->deallocate(remapped_service_name, allocator->state);
203-
}
134+
allocator->deallocate(remapped_service_name, allocator->state);
204135
return ret;
205136
}
206137

rcl/src/rcl/node_resolve_name.c

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// Copyright 2020 Open Source Robotics Foundation, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "rcl/node.h"
16+
17+
#include "rcutils/error_handling.h"
18+
#include "rcutils/logging_macros.h"
19+
#include "rcutils/types/string_map.h"
20+
21+
#include "rmw/error_handling.h"
22+
#include "rmw/validate_full_topic_name.h"
23+
24+
#include "rcl/error_handling.h"
25+
#include "rcl/expand_topic_name.h"
26+
#include "rcl/remap.h"
27+
28+
#include "./remap_impl.h"
29+
30+
static
31+
rcl_ret_t
32+
rcl_resolve_name(
33+
const rcl_arguments_t * local_args,
34+
const rcl_arguments_t * global_args,
35+
const char * input_topic_name,
36+
const char * node_name,
37+
const char * node_namespace,
38+
rcl_allocator_t allocator,
39+
bool is_service,
40+
bool only_expand,
41+
char ** output_topic_name)
42+
{
43+
// the other arguments are checked by rcl_expand_topic_name() and rcl_remap_name()
44+
RCL_CHECK_ARGUMENT_FOR_NULL(output_topic_name, RCL_RET_INVALID_ARGUMENT);
45+
// Create default topic name substitutions map
46+
rcutils_string_map_t substitutions_map = rcutils_get_zero_initialized_string_map();
47+
rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions_map, 0, allocator);
48+
if (rcutils_ret != RCUTILS_RET_OK) {
49+
rcutils_error_string_t error = rcutils_get_error_string();
50+
rcutils_reset_error();
51+
RCL_SET_ERROR_MSG(error.str);
52+
if (RCUTILS_RET_BAD_ALLOC == rcutils_ret) {
53+
return RCL_RET_BAD_ALLOC;
54+
}
55+
return RCL_RET_ERROR;
56+
}
57+
char * expanded_topic_name = NULL;
58+
char * remapped_topic_name = NULL;
59+
rcl_ret_t ret = rcl_get_default_topic_name_substitutions(&substitutions_map);
60+
if (ret != RCL_RET_OK) {
61+
if (RCL_RET_BAD_ALLOC != ret) {
62+
ret = RCL_RET_ERROR;
63+
}
64+
goto cleanup;
65+
}
66+
// expand topic name
67+
ret = rcl_expand_topic_name(
68+
input_topic_name,
69+
node_name,
70+
node_namespace,
71+
&substitutions_map,
72+
allocator,
73+
&expanded_topic_name);
74+
if (RCL_RET_OK != ret) {
75+
goto cleanup;
76+
}
77+
// remap topic name
78+
if (!only_expand) {
79+
ret = rcl_remap_name(
80+
local_args, global_args, is_service ? RCL_SERVICE_REMAP : RCL_TOPIC_REMAP,
81+
expanded_topic_name, node_name, node_namespace, &substitutions_map, allocator,
82+
&remapped_topic_name);
83+
if (RCL_RET_OK != ret) {
84+
goto cleanup;
85+
}
86+
}
87+
if (NULL == remapped_topic_name) {
88+
remapped_topic_name = expanded_topic_name;
89+
expanded_topic_name = NULL;
90+
}
91+
// validate the result
92+
int validation_result;
93+
rmw_ret_t rmw_ret = rmw_validate_full_topic_name(remapped_topic_name, &validation_result, NULL);
94+
if (rmw_ret != RMW_RET_OK) {
95+
const char * error = rmw_get_error_string().str;
96+
rmw_reset_error();
97+
RCL_SET_ERROR_MSG(error);
98+
ret = RCL_RET_ERROR;
99+
goto cleanup;
100+
}
101+
if (validation_result != RMW_TOPIC_VALID) {
102+
RCL_SET_ERROR_MSG(rmw_full_topic_name_validation_result_string(validation_result));
103+
ret = RCL_RET_TOPIC_NAME_INVALID;
104+
goto cleanup;
105+
}
106+
*output_topic_name = remapped_topic_name;
107+
remapped_topic_name = NULL;
108+
109+
cleanup:
110+
rcutils_ret = rcutils_string_map_fini(&substitutions_map);
111+
if (rcutils_ret != RCUTILS_RET_OK) {
112+
rcutils_error_string_t error = rcutils_get_error_string();
113+
rcutils_reset_error();
114+
if (RCL_RET_OK == ret) {
115+
RCL_SET_ERROR_MSG(error.str);
116+
ret = RCL_RET_ERROR;
117+
} else {
118+
RCUTILS_LOG_ERROR_NAMED(
119+
ROS_PACKAGE_NAME,
120+
"failed to fini string_map (%d) during error handling: %s",
121+
rcutils_ret,
122+
error.str);
123+
}
124+
}
125+
allocator.deallocate(expanded_topic_name, allocator.state);
126+
allocator.deallocate(remapped_topic_name, allocator.state);
127+
if (is_service && RCL_RET_TOPIC_NAME_INVALID == ret) {
128+
ret = RCL_RET_SERVICE_NAME_INVALID;
129+
}
130+
return ret;
131+
}
132+
133+
rcl_ret_t
134+
rcl_node_resolve_name(
135+
const rcl_node_t * node,
136+
const char * input_topic_name,
137+
rcl_allocator_t allocator,
138+
bool is_service,
139+
bool only_expand,
140+
char ** output_topic_name)
141+
{
142+
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
143+
const rcl_node_options_t * node_options = rcl_node_get_options(node);
144+
if (NULL == node_options) {
145+
return RCL_RET_ERROR;
146+
}
147+
rcl_arguments_t * global_args = NULL;
148+
if (node_options->use_global_arguments) {
149+
global_args = &(node->context->global_arguments);
150+
}
151+
152+
return rcl_resolve_name(
153+
&(node_options->arguments),
154+
global_args,
155+
input_topic_name,
156+
rcl_node_get_name(node),
157+
rcl_node_get_namespace(node),
158+
allocator,
159+
is_service,
160+
only_expand,
161+
output_topic_name);
162+
}

0 commit comments

Comments
 (0)