add graph functions to support wait_for_service (#57)
* add graph functions to support wait_for_service * is_ready -> is_available * add function to create rcl_guard_condition from an existing rmw_guard_condition * create graph guard condition in node automatically * tests for graph API * style fixup * remove debug stuff from test_graph * only enable test_graph for OpenSplice * fixup * add rcl_node_is_valid function * add tests for new graph API functions * fixup * reorder the destruction of node vs waitset this also addressed the segfault issues, but the proper fix was committed to rmw_opensplice_cpp while this ordering is not strictly necessary it is slightly more correct * disable missing-field-intializers warning it's too strict and will be relaxed in newer gcc's * use to_string since type changes with OS * use more specific gtest assert to avoid warning * added a secondary check in the test_graph.test_rcl_service_server_is_available test * uncrustify * doc fixup * condition pragma on not _WIN32
This commit is contained in:
parent
2811eb1312
commit
818b15463b
11 changed files with 1088 additions and 27 deletions
|
@ -20,6 +20,7 @@ set(${PROJECT_NAME}_sources
|
||||||
src/rcl/allocator.c
|
src/rcl/allocator.c
|
||||||
src/rcl/client.c
|
src/rcl/client.c
|
||||||
src/rcl/common.c
|
src/rcl/common.c
|
||||||
|
src/rcl/graph.c
|
||||||
src/rcl/guard_condition.c
|
src/rcl/guard_condition.c
|
||||||
src/rcl/node.c
|
src/rcl/node.c
|
||||||
src/rcl/publisher.c
|
src/rcl/publisher.c
|
||||||
|
|
188
rcl/include/rcl/graph.h
Normal file
188
rcl/include/rcl/graph.h
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
// Copyright 2016 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__GRAPH_H_
|
||||||
|
#define RCL__GRAPH_H_
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <rmw/rmw.h>
|
||||||
|
#include <rmw/types.h>
|
||||||
|
|
||||||
|
#include "rosidl_generator_c/service_type_support.h"
|
||||||
|
|
||||||
|
#include "rcl/macros.h"
|
||||||
|
#include "rcl/client.h"
|
||||||
|
#include "rcl/node.h"
|
||||||
|
#include "rcl/visibility_control.h"
|
||||||
|
|
||||||
|
typedef rmw_topic_names_and_types_t rcl_topic_names_and_types_t;
|
||||||
|
|
||||||
|
/// Return a list of topic names and their types.
|
||||||
|
/* This function returns a list of topic names in the ROS graph and their types.
|
||||||
|
*
|
||||||
|
* The node parameter must not be NULL, and must point to a valid node.
|
||||||
|
*
|
||||||
|
* The topic_names_and_types parameter must not be NULL, and must point to an
|
||||||
|
* already allocated rcl_topic_names_and_types_t struct.
|
||||||
|
* The topic_names_and_types is the output for this function, and contains
|
||||||
|
* allocated memory.
|
||||||
|
* Therefore, it should be passed to rcl_destroy_topic_names_and_types() when
|
||||||
|
* it is no longer needed.
|
||||||
|
* Failing to do so will result in leaked memory.
|
||||||
|
*
|
||||||
|
* This function does manipulate heap memory.
|
||||||
|
* This function is not thread-safe.
|
||||||
|
* This function is lock-free.
|
||||||
|
*
|
||||||
|
* \param[in] node the handle to the node being used to query the ROS graph
|
||||||
|
* \param[out] topic_names_and_types list of topic names and their types
|
||||||
|
* \return RCL_RET_OK if the query was successful, or
|
||||||
|
* RCL_RET_NODE_INVALID if the node is invalid, or
|
||||||
|
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||||
|
* RCL_RET_ERROR if an unspecified error occurs.
|
||||||
|
*/
|
||||||
|
RCL_PUBLIC
|
||||||
|
RCL_WARN_UNUSED
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_get_topic_names_and_types(
|
||||||
|
const rcl_node_t * node,
|
||||||
|
rcl_topic_names_and_types_t * topic_names_and_types);
|
||||||
|
|
||||||
|
/// Destroy a struct which was previously given to rcl_get_topic_names_and_types.
|
||||||
|
/* The topic_names_and_types parameter must not be NULL, and must point to an
|
||||||
|
* already allocated rcl_topic_names_and_types_t struct that was previously
|
||||||
|
* passed to a successful rcl_get_topic_names_and_types() call.
|
||||||
|
*
|
||||||
|
* This function does manipulate heap memory.
|
||||||
|
* This function is not thread-safe.
|
||||||
|
* This function is lock-free.
|
||||||
|
*
|
||||||
|
* \param[inout] topic_names_and_types struct to be destroyed
|
||||||
|
* \return RCL_RET_OK if successful, or
|
||||||
|
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||||
|
* RCL_RET_ERROR if an unspecified error occurs.
|
||||||
|
*/
|
||||||
|
RCL_PUBLIC
|
||||||
|
RCL_WARN_UNUSED
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_destroy_topic_names_and_types(
|
||||||
|
rcl_topic_names_and_types_t * topic_names_and_types);
|
||||||
|
|
||||||
|
/// Return the number of publishers on a given topic.
|
||||||
|
/* This function returns the number of publishers on a given topic.
|
||||||
|
*
|
||||||
|
* The node parameter must not be NULL, and must point to a valid node.
|
||||||
|
*
|
||||||
|
* The topic_name parameter must not be NULL, and must not be an empty string.
|
||||||
|
* It should also follow the topic name rules.
|
||||||
|
* \TODO(wjwwood): link to the topic name rules.
|
||||||
|
*
|
||||||
|
* The count parameter must not be NULL, and must point to a valid bool.
|
||||||
|
* The count parameter is the output for this function and will be set.
|
||||||
|
*
|
||||||
|
* This function may manipulate heap memory.
|
||||||
|
* This function is not thread-safe.
|
||||||
|
* This function is lock-free.
|
||||||
|
*
|
||||||
|
* \param[in] node the handle to the node being used to query the ROS graph
|
||||||
|
* \param[in] topic_name the name of the topic in question
|
||||||
|
* \param[out] count number of publishers on the given topic
|
||||||
|
* \return RCL_RET_OK if the query was successful, or
|
||||||
|
* RCL_RET_NODE_INVALID if the node is invalid, or
|
||||||
|
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||||
|
* RCL_RET_ERROR if an unspecified error occurs.
|
||||||
|
*/
|
||||||
|
RCL_PUBLIC
|
||||||
|
RCL_WARN_UNUSED
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_count_publishers(
|
||||||
|
const rcl_node_t * node,
|
||||||
|
const char * topic_name,
|
||||||
|
size_t * count);
|
||||||
|
|
||||||
|
/// Return the number of subscriptions on a given topic.
|
||||||
|
/* This function returns the number of subscriptions on a given topic.
|
||||||
|
*
|
||||||
|
* The node parameter must not be NULL, and must point to a valid node.
|
||||||
|
*
|
||||||
|
* The topic_name parameter must not be NULL, and must not be an empty string.
|
||||||
|
* It should also follow the topic name rules.
|
||||||
|
* \TODO(wjwwood): link to the topic name rules.
|
||||||
|
*
|
||||||
|
* The count parameter must not be NULL, and must point to a valid bool.
|
||||||
|
* The count parameter is the output for this function and will be set.
|
||||||
|
*
|
||||||
|
* This function may manipulate heap memory.
|
||||||
|
* This function is not thread-safe.
|
||||||
|
* This function is lock-free.
|
||||||
|
*
|
||||||
|
* \param[in] node the handle to the node being used to query the ROS graph
|
||||||
|
* \param[in] topic_name the name of the topic in question
|
||||||
|
* \param[out] count number of subscriptions on the given topic
|
||||||
|
* \return RCL_RET_OK if the query was successful, or
|
||||||
|
* RCL_RET_NODE_INVALID if the node is invalid, or
|
||||||
|
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||||
|
* RCL_RET_ERROR if an unspecified error occurs.
|
||||||
|
*/
|
||||||
|
RCL_PUBLIC
|
||||||
|
RCL_WARN_UNUSED
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_count_subscribers(
|
||||||
|
const rcl_node_t * node,
|
||||||
|
const char * topic_name,
|
||||||
|
size_t * count);
|
||||||
|
|
||||||
|
/// Check if a service server is available for the given service client.
|
||||||
|
/* This function will return true for is_available if there is a service server
|
||||||
|
* available for the given client.
|
||||||
|
*
|
||||||
|
* The node parameter must not be NULL, and must point to a valid node.
|
||||||
|
*
|
||||||
|
* The client parameter must not be NULL, and must point to a valid client.
|
||||||
|
*
|
||||||
|
* The given client and node must match, i.e. the client must have been created
|
||||||
|
* using the given node.
|
||||||
|
*
|
||||||
|
* The is_available parameter must not be NULL, and must point a bool variable.
|
||||||
|
* The result of the check will be stored in the is_available parameter.
|
||||||
|
*
|
||||||
|
* This function does manipulate heap memory.
|
||||||
|
* This function is not thread-safe.
|
||||||
|
* This function is lock-free.
|
||||||
|
*
|
||||||
|
* \param[in] node the handle to the node being used to query the ROS graph
|
||||||
|
* \param[in] client the handle to the service client being queried
|
||||||
|
* \param[out] is_available set to true if there is a service server available, else false
|
||||||
|
* \return RCL_RET_OK if the check was made successfully (regardless of the service readiness), or
|
||||||
|
* RCL_RET_NODE_INVALID if the node is invalid, or
|
||||||
|
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||||
|
* RCL_RET_ERROR if an unspecified error occurs.
|
||||||
|
*/
|
||||||
|
RCL_PUBLIC
|
||||||
|
RCL_WARN_UNUSED
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_service_server_is_available(
|
||||||
|
const rcl_node_t * node,
|
||||||
|
const rcl_client_t * client,
|
||||||
|
bool * is_available);
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // RCL__GRAPH_H_
|
|
@ -67,7 +67,6 @@ rcl_get_zero_initialized_guard_condition(void);
|
||||||
* This function is not thread-safe.
|
* This function is not thread-safe.
|
||||||
* This function is lock-free.
|
* This function is lock-free.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* \param[inout] guard_condition preallocated guard_condition structure
|
* \param[inout] guard_condition preallocated guard_condition structure
|
||||||
* \param[in] options the guard_condition's options
|
* \param[in] options the guard_condition's options
|
||||||
* \return RCL_RET_OK if guard_condition was initialized successfully, or
|
* \return RCL_RET_OK if guard_condition was initialized successfully, or
|
||||||
|
@ -83,6 +82,34 @@ rcl_guard_condition_init(
|
||||||
rcl_guard_condition_t * guard_condition,
|
rcl_guard_condition_t * guard_condition,
|
||||||
const rcl_guard_condition_options_t options);
|
const rcl_guard_condition_options_t options);
|
||||||
|
|
||||||
|
/// Same as rcl_guard_condition_init(), but reusing an existing rmw handle.
|
||||||
|
/* In addition to the documentation for rcl_guard_condition_init(), the
|
||||||
|
* rmw_guard_condition parameter must not be null and must point to a valid
|
||||||
|
* rmw guard condition.
|
||||||
|
*
|
||||||
|
* Also the life time of the rcl guard condition is tied to the life time of
|
||||||
|
* the rmw guard condition.
|
||||||
|
* So if the rmw guard condition is destroyed before the rcl guard condition,
|
||||||
|
* the rcl guard condition becomes invalid.
|
||||||
|
*
|
||||||
|
* Similarly if the resulting rcl guard condition is fini'ed before the rmw
|
||||||
|
* guard condition, then the rmw guard condition is no longer valid.
|
||||||
|
*
|
||||||
|
* \param[inout] guard_condition preallocated guard_condition structure
|
||||||
|
* \param[in] rmw_guard_condition existing rmw guard condition to reuse
|
||||||
|
* \param[in] options the guard_condition's options
|
||||||
|
* \return RCL_RET_OK if guard_condition was initialized successfully, or
|
||||||
|
* RCL_RET_ALREADY_INIT if the guard condition is already initialized, or
|
||||||
|
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||||
|
* RCL_RET_BAD_ALLOC if allocating memory failed, or
|
||||||
|
* RCL_RET_ERROR if an unspecified error occurs.
|
||||||
|
*/
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_guard_condition_init_from_rmw(
|
||||||
|
rcl_guard_condition_t * guard_condition,
|
||||||
|
const rmw_guard_condition_t * rmw_guard_condition,
|
||||||
|
const rcl_guard_condition_options_t options);
|
||||||
|
|
||||||
/// Finalize a rcl_guard_condition_t.
|
/// Finalize a rcl_guard_condition_t.
|
||||||
/* After calling, calls to rcl_trigger_guard_condition() will fail when using
|
/* After calling, calls to rcl_trigger_guard_condition() will fail when using
|
||||||
* this guard condition.
|
* this guard condition.
|
||||||
|
|
|
@ -29,6 +29,7 @@ extern "C"
|
||||||
|
|
||||||
#define RCL_NODE_OPTIONS_DEFAULT_DOMAIN_ID SIZE_MAX
|
#define RCL_NODE_OPTIONS_DEFAULT_DOMAIN_ID SIZE_MAX
|
||||||
|
|
||||||
|
struct rcl_guard_condition_t;
|
||||||
struct rcl_node_impl_t;
|
struct rcl_node_impl_t;
|
||||||
|
|
||||||
/// Handle for a ROS node.
|
/// Handle for a ROS node.
|
||||||
|
@ -142,6 +143,37 @@ RCL_PUBLIC
|
||||||
rcl_node_options_t
|
rcl_node_options_t
|
||||||
rcl_node_get_default_options(void);
|
rcl_node_get_default_options(void);
|
||||||
|
|
||||||
|
/// Return true if the node is valid, else false.
|
||||||
|
/* Also return false if the node pointer is nullptr.
|
||||||
|
*
|
||||||
|
* A node is invalid if:
|
||||||
|
* - the implementation is null (rcl_node_init not called or failed)
|
||||||
|
* - rcl_shutdown has been called since the node has been initialized
|
||||||
|
* - the node has been finalized with rcl_node_fini
|
||||||
|
*
|
||||||
|
* There is a possible validity race condition.
|
||||||
|
* Consider:
|
||||||
|
*
|
||||||
|
* assert(rcl_node_is_valid(node)); <-- thread 1
|
||||||
|
* rcl_shutdown(); <-- thread 2
|
||||||
|
* // use node as if valid <-- thread 1
|
||||||
|
*
|
||||||
|
* In the third line the node is now invalid, even though on the previous line
|
||||||
|
* of thread 1 it was checked to be valid.
|
||||||
|
* This is why this function is considered not thread-safe.
|
||||||
|
*
|
||||||
|
* This function does not manipulate heap memory.
|
||||||
|
* This function is not thread-safe.
|
||||||
|
* This function is lock-free so long as the C11's stdatomic.h function
|
||||||
|
* atomic_is_lock_free() returns true for atomic_uint_least64_t.
|
||||||
|
*
|
||||||
|
* \param[in] node handle to the node to validated
|
||||||
|
* \return true if the node is valid, otherwise false.
|
||||||
|
*/
|
||||||
|
RCL_PUBLIC
|
||||||
|
bool
|
||||||
|
rcl_node_is_valid(const rcl_node_t * node);
|
||||||
|
|
||||||
/// Get the name of the node.
|
/// Get the name of the node.
|
||||||
/* This function returns the node's internal name string.
|
/* This function returns the node's internal name string.
|
||||||
* This function can fail, and therefore return NULL, if:
|
* This function can fail, and therefore return NULL, if:
|
||||||
|
@ -263,6 +295,33 @@ RCL_WARN_UNUSED
|
||||||
uint64_t
|
uint64_t
|
||||||
rcl_node_get_rcl_instance_id(const rcl_node_t * node);
|
rcl_node_get_rcl_instance_id(const rcl_node_t * node);
|
||||||
|
|
||||||
|
/// Return a guard condition which is triggered when the ROS graph changes.
|
||||||
|
/* The handle returned is a pointer to an internally held rcl guard condition.
|
||||||
|
* This function can fail, and therefore return NULL, if:
|
||||||
|
* - node is NULL
|
||||||
|
* - node is invalid
|
||||||
|
*
|
||||||
|
* The returned handle is made invalid if the node is finialized or if
|
||||||
|
* rcl_shutdown() is called.
|
||||||
|
*
|
||||||
|
* The guard condition will be triggered anytime a change to the ROS graph occurs.
|
||||||
|
* A ROS graph change includes things like (but not limited to) a new publisher
|
||||||
|
* advertises, a new subscription is created, a new service becomes available,
|
||||||
|
* a subscription is canceled, etc.
|
||||||
|
*
|
||||||
|
* This function does not manipulate heap memory.
|
||||||
|
* This function is thread-safe.
|
||||||
|
* This function is lock-free.
|
||||||
|
*
|
||||||
|
* \param[in] node pointer to the rcl node
|
||||||
|
* \return rcl guard condition handle if successful, otherwise NULL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
RCL_PUBLIC
|
||||||
|
RCL_WARN_UNUSED
|
||||||
|
const struct rcl_guard_condition_t *
|
||||||
|
rcl_node_get_graph_guard_condition(const rcl_node_t * node);
|
||||||
|
|
||||||
#if __cplusplus
|
#if __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
99
rcl/src/rcl/graph.c
Normal file
99
rcl/src/rcl/graph.c
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "rcl/graph.h"
|
||||||
|
|
||||||
|
#include "./common.h"
|
||||||
|
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_get_topic_names_and_types(
|
||||||
|
const rcl_node_t * node,
|
||||||
|
rcl_topic_names_and_types_t * topic_names_and_types)
|
||||||
|
{
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
if (!rcl_node_is_valid(node)) {
|
||||||
|
return RCL_RET_NODE_INVALID;
|
||||||
|
}
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(topic_names_and_types, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
return rmw_get_topic_names_and_types(
|
||||||
|
rcl_node_get_rmw_handle(node),
|
||||||
|
topic_names_and_types
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_destroy_topic_names_and_types(
|
||||||
|
rcl_topic_names_and_types_t * topic_names_and_types)
|
||||||
|
{
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(topic_names_and_types, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
return rmw_destroy_topic_names_and_types(topic_names_and_types);
|
||||||
|
}
|
||||||
|
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_count_publishers(
|
||||||
|
const rcl_node_t * node,
|
||||||
|
const char * topic_name,
|
||||||
|
size_t * count)
|
||||||
|
{
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
if (!rcl_node_is_valid(node)) {
|
||||||
|
return RCL_RET_NODE_INVALID;
|
||||||
|
}
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
return rmw_count_publishers(rcl_node_get_rmw_handle(node), topic_name, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_count_subscribers(
|
||||||
|
const rcl_node_t * node,
|
||||||
|
const char * topic_name,
|
||||||
|
size_t * count)
|
||||||
|
{
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
if (!rcl_node_is_valid(node)) {
|
||||||
|
return RCL_RET_NODE_INVALID;
|
||||||
|
}
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
return rmw_count_subscribers(rcl_node_get_rmw_handle(node), topic_name, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_service_server_is_available(
|
||||||
|
const rcl_node_t * node,
|
||||||
|
const rcl_client_t * client,
|
||||||
|
bool * is_available)
|
||||||
|
{
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
if (!rcl_node_is_valid(node)) {
|
||||||
|
return RCL_RET_NODE_INVALID;
|
||||||
|
}
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(is_available, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
return rmw_service_server_is_available(
|
||||||
|
rcl_node_get_rmw_handle(node),
|
||||||
|
rcl_client_get_rmw_handle(client),
|
||||||
|
is_available
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -37,10 +37,13 @@ rcl_get_zero_initialized_guard_condition()
|
||||||
}
|
}
|
||||||
|
|
||||||
rcl_ret_t
|
rcl_ret_t
|
||||||
rcl_guard_condition_init(
|
__rcl_guard_condition_init_from_rmw_impl(
|
||||||
rcl_guard_condition_t * guard_condition,
|
rcl_guard_condition_t * guard_condition,
|
||||||
|
const rmw_guard_condition_t * rmw_guard_condition,
|
||||||
const rcl_guard_condition_options_t options)
|
const rcl_guard_condition_options_t options)
|
||||||
{
|
{
|
||||||
|
// This function will create an rmw_guard_condition if the parameter is null.
|
||||||
|
|
||||||
// Perform argument validation.
|
// Perform argument validation.
|
||||||
RCL_CHECK_ARGUMENT_FOR_NULL(guard_condition, RCL_RET_INVALID_ARGUMENT);
|
RCL_CHECK_ARGUMENT_FOR_NULL(guard_condition, RCL_RET_INVALID_ARGUMENT);
|
||||||
const rcl_allocator_t * allocator = &options.allocator;
|
const rcl_allocator_t * allocator = &options.allocator;
|
||||||
|
@ -61,18 +64,42 @@ rcl_guard_condition_init(
|
||||||
return RCL_RET_BAD_ALLOC;
|
return RCL_RET_BAD_ALLOC;
|
||||||
}
|
}
|
||||||
// Create the rmw guard condition.
|
// Create the rmw guard condition.
|
||||||
guard_condition->impl->rmw_handle = rmw_create_guard_condition();
|
if (rmw_guard_condition) {
|
||||||
if (!guard_condition->impl->rmw_handle) {
|
// If given, just assign (cast away const).
|
||||||
// Deallocate impl and exit.
|
guard_condition->impl->rmw_handle = (rmw_guard_condition_t *)rmw_guard_condition;
|
||||||
allocator->deallocate(guard_condition->impl, allocator->state);
|
} else {
|
||||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
// Otherwise create one.
|
||||||
return RCL_RET_ERROR;
|
guard_condition->impl->rmw_handle = rmw_create_guard_condition();
|
||||||
|
if (!guard_condition->impl->rmw_handle) {
|
||||||
|
// Deallocate impl and exit.
|
||||||
|
allocator->deallocate(guard_condition->impl, allocator->state);
|
||||||
|
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||||
|
return RCL_RET_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Copy options into impl.
|
// Copy options into impl.
|
||||||
guard_condition->impl->options = options;
|
guard_condition->impl->options = options;
|
||||||
return RCL_RET_OK;
|
return RCL_RET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_guard_condition_init(
|
||||||
|
rcl_guard_condition_t * guard_condition,
|
||||||
|
const rcl_guard_condition_options_t options)
|
||||||
|
{
|
||||||
|
// NULL indicates "create a new rmw guard condition".
|
||||||
|
return __rcl_guard_condition_init_from_rmw_impl(guard_condition, NULL, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
rcl_ret_t
|
||||||
|
rcl_guard_condition_init_from_rmw(
|
||||||
|
rcl_guard_condition_t * guard_condition,
|
||||||
|
const rmw_guard_condition_t * rmw_guard_condition,
|
||||||
|
const rcl_guard_condition_options_t options)
|
||||||
|
{
|
||||||
|
return __rcl_guard_condition_init_from_rmw_impl(guard_condition, rmw_guard_condition, options);
|
||||||
|
}
|
||||||
|
|
||||||
rcl_ret_t
|
rcl_ret_t
|
||||||
rcl_guard_condition_fini(rcl_guard_condition_t * guard_condition)
|
rcl_guard_condition_fini(rcl_guard_condition_t * guard_condition)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,6 +20,7 @@ extern "C"
|
||||||
#include "rcl/node.h"
|
#include "rcl/node.h"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -31,9 +32,10 @@ extern "C"
|
||||||
typedef struct rcl_node_impl_t
|
typedef struct rcl_node_impl_t
|
||||||
{
|
{
|
||||||
rcl_node_options_t options;
|
rcl_node_options_t options;
|
||||||
|
size_t actual_domain_id;
|
||||||
rmw_node_t * rmw_node_handle;
|
rmw_node_t * rmw_node_handle;
|
||||||
uint64_t rcl_instance_id;
|
uint64_t rcl_instance_id;
|
||||||
size_t actual_domain_id;
|
rcl_guard_condition_t * graph_guard_condition;
|
||||||
} rcl_node_impl_t;
|
} rcl_node_impl_t;
|
||||||
|
|
||||||
rcl_node_t
|
rcl_node_t
|
||||||
|
@ -48,6 +50,9 @@ rcl_node_init(rcl_node_t * node, const char * name, const rcl_node_options_t * o
|
||||||
{
|
{
|
||||||
size_t domain_id = 0;
|
size_t domain_id = 0;
|
||||||
const char * ros_domain_id;
|
const char * ros_domain_id;
|
||||||
|
const rmw_guard_condition_t * rmw_graph_guard_condition = NULL;
|
||||||
|
rcl_guard_condition_options_t graph_guard_condition_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;
|
||||||
RCL_CHECK_ARGUMENT_FOR_NULL(name, RCL_RET_INVALID_ARGUMENT);
|
RCL_CHECK_ARGUMENT_FOR_NULL(name, RCL_RET_INVALID_ARGUMENT);
|
||||||
|
@ -70,6 +75,8 @@ rcl_node_init(rcl_node_t * node, const char * name, const rcl_node_options_t * o
|
||||||
// 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);
|
||||||
RCL_CHECK_FOR_NULL_WITH_MSG(node->impl, "allocating memory failed", return RCL_RET_BAD_ALLOC);
|
RCL_CHECK_FOR_NULL_WITH_MSG(node->impl, "allocating memory failed", return RCL_RET_BAD_ALLOC);
|
||||||
|
node->impl->rmw_node_handle = NULL;
|
||||||
|
node->impl->graph_guard_condition = NULL;
|
||||||
// Initialize node impl.
|
// Initialize node impl.
|
||||||
// node name
|
// node name
|
||||||
size_t name_len = strlen(name);
|
size_t name_len = strlen(name);
|
||||||
|
@ -97,14 +104,56 @@ rcl_node_init(rcl_node_t * node, const char * name, const rcl_node_options_t * o
|
||||||
} else {
|
} else {
|
||||||
domain_id = node->impl->options.domain_id;
|
domain_id = node->impl->options.domain_id;
|
||||||
}
|
}
|
||||||
|
// actual domain id
|
||||||
node->impl->actual_domain_id = domain_id;
|
node->impl->actual_domain_id = domain_id;
|
||||||
node->impl->rmw_node_handle = rmw_create_node(name, domain_id);
|
node->impl->rmw_node_handle = rmw_create_node(name, domain_id);
|
||||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||||
node->impl->rmw_node_handle, rmw_get_error_string_safe(), goto fail);
|
node->impl->rmw_node_handle, rmw_get_error_string_safe(), goto fail);
|
||||||
|
// instance id
|
||||||
node->impl->rcl_instance_id = rcl_get_instance_id();
|
node->impl->rcl_instance_id = rcl_get_instance_id();
|
||||||
|
// graph guard condition
|
||||||
|
rmw_graph_guard_condition = rmw_node_get_graph_guard_condition(node->impl->rmw_node_handle);
|
||||||
|
if (!rmw_graph_guard_condition) {
|
||||||
|
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
node->impl->graph_guard_condition = (rcl_guard_condition_t *)allocator->allocate(
|
||||||
|
sizeof(rcl_guard_condition_t), allocator->state);
|
||||||
|
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||||
|
node->impl->graph_guard_condition,
|
||||||
|
"allocating memory failed",
|
||||||
|
goto fail
|
||||||
|
);
|
||||||
|
*node->impl->graph_guard_condition = rcl_get_zero_initialized_guard_condition();
|
||||||
|
graph_guard_condition_options.allocator = *allocator;
|
||||||
|
ret = rcl_guard_condition_init_from_rmw(
|
||||||
|
node->impl->graph_guard_condition,
|
||||||
|
rmw_graph_guard_condition,
|
||||||
|
graph_guard_condition_options);
|
||||||
|
if (ret != RCL_RET_OK) {
|
||||||
|
// error message already set
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
return RCL_RET_OK;
|
return RCL_RET_OK;
|
||||||
fail:
|
fail:
|
||||||
if (node->impl) {
|
if (node->impl) {
|
||||||
|
if (node->impl->rmw_node_handle) {
|
||||||
|
ret = rmw_destroy_node(node->impl->rmw_node_handle);
|
||||||
|
if (ret != RMW_RET_OK) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"failed to fini rmw node in error recovery: %s\n", rmw_get_error_string_safe()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node->impl->graph_guard_condition) {
|
||||||
|
ret = rcl_guard_condition_fini(node->impl->graph_guard_condition);
|
||||||
|
if (ret != RCL_RET_OK) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"failed to fini guard condition in error recovery: %s\n", rcl_get_error_string_safe()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
allocator->deallocate(node->impl->graph_guard_condition, allocator->state);
|
||||||
|
}
|
||||||
allocator->deallocate(node->impl, allocator->state);
|
allocator->deallocate(node->impl, allocator->state);
|
||||||
}
|
}
|
||||||
*node = rcl_get_zero_initialized_node();
|
*node = rcl_get_zero_initialized_node();
|
||||||
|
@ -133,6 +182,18 @@ rcl_node_fini(rcl_node_t * node)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
rcl_node_is_valid(const rcl_node_t * node)
|
||||||
|
{
|
||||||
|
RCL_CHECK_ARGUMENT_FOR_NULL(node, false);
|
||||||
|
RCL_CHECK_FOR_NULL_WITH_MSG(node->impl, "rcl node implementation is invalid", return false);
|
||||||
|
if (node->impl->rcl_instance_id != rcl_get_instance_id()) {
|
||||||
|
RCL_SET_ERROR_MSG("rcl node is invalid, rcl instance id does not match");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
rcl_node_options_t
|
rcl_node_options_t
|
||||||
rcl_node_get_default_options()
|
rcl_node_get_default_options()
|
||||||
{
|
{
|
||||||
|
@ -147,10 +208,7 @@ rcl_node_get_default_options()
|
||||||
const char *
|
const char *
|
||||||
rcl_node_get_name(const rcl_node_t * node)
|
rcl_node_get_name(const rcl_node_t * node)
|
||||||
{
|
{
|
||||||
RCL_CHECK_ARGUMENT_FOR_NULL(node, NULL);
|
if (!rcl_node_is_valid(node)) {
|
||||||
RCL_CHECK_FOR_NULL_WITH_MSG(node->impl, "node implementation is invalid", return NULL);
|
|
||||||
if (node->impl->rcl_instance_id != rcl_get_instance_id()) {
|
|
||||||
RCL_SET_ERROR_MSG("rcl node is invalid, rcl instance id does not match");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return node->impl->rmw_node_handle->name;
|
return node->impl->rmw_node_handle->name;
|
||||||
|
@ -159,10 +217,7 @@ rcl_node_get_name(const rcl_node_t * node)
|
||||||
const rcl_node_options_t *
|
const rcl_node_options_t *
|
||||||
rcl_node_get_options(const rcl_node_t * node)
|
rcl_node_get_options(const rcl_node_t * node)
|
||||||
{
|
{
|
||||||
RCL_CHECK_ARGUMENT_FOR_NULL(node, NULL);
|
if (!rcl_node_is_valid(node)) {
|
||||||
RCL_CHECK_FOR_NULL_WITH_MSG(node->impl, "node implementation is invalid", return NULL);
|
|
||||||
if (node->impl->rcl_instance_id != rcl_get_instance_id()) {
|
|
||||||
RCL_SET_ERROR_MSG("rcl node is invalid, rcl instance id does not match");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return &node->impl->options;
|
return &node->impl->options;
|
||||||
|
@ -173,10 +228,7 @@ rcl_node_get_domain_id(const rcl_node_t * node, size_t * domain_id)
|
||||||
{
|
{
|
||||||
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
|
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
|
||||||
RCL_CHECK_ARGUMENT_FOR_NULL(domain_id, RCL_RET_INVALID_ARGUMENT);
|
RCL_CHECK_ARGUMENT_FOR_NULL(domain_id, RCL_RET_INVALID_ARGUMENT);
|
||||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
if (!rcl_node_is_valid(node)) {
|
||||||
node->impl, "node implementation is invalid", return RCL_RET_NODE_INVALID);
|
|
||||||
if (node->impl->rcl_instance_id != rcl_get_instance_id()) {
|
|
||||||
RCL_SET_ERROR_MSG("rcl node is invalid, rcl instance id does not match");
|
|
||||||
return RCL_RET_NODE_INVALID;
|
return RCL_RET_NODE_INVALID;
|
||||||
}
|
}
|
||||||
*domain_id = node->impl->actual_domain_id;
|
*domain_id = node->impl->actual_domain_id;
|
||||||
|
@ -186,10 +238,7 @@ rcl_node_get_domain_id(const rcl_node_t * node, size_t * domain_id)
|
||||||
rmw_node_t *
|
rmw_node_t *
|
||||||
rcl_node_get_rmw_handle(const rcl_node_t * node)
|
rcl_node_get_rmw_handle(const rcl_node_t * node)
|
||||||
{
|
{
|
||||||
RCL_CHECK_ARGUMENT_FOR_NULL(node, NULL);
|
if (!rcl_node_is_valid(node)) {
|
||||||
RCL_CHECK_FOR_NULL_WITH_MSG(node->impl, "node implementation is invalid", return NULL);
|
|
||||||
if (node->impl->rcl_instance_id != rcl_get_instance_id()) {
|
|
||||||
RCL_SET_ERROR_MSG("rcl node is invalid, rcl instance id does not match");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return node->impl->rmw_node_handle;
|
return node->impl->rmw_node_handle;
|
||||||
|
@ -203,6 +252,15 @@ rcl_node_get_rcl_instance_id(const rcl_node_t * node)
|
||||||
return node->impl->rcl_instance_id;
|
return node->impl->rcl_instance_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const struct rcl_guard_condition_t *
|
||||||
|
rcl_node_get_graph_guard_condition(const rcl_node_t * node)
|
||||||
|
{
|
||||||
|
if (!rcl_node_is_valid(node)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return node->impl->graph_guard_condition;
|
||||||
|
}
|
||||||
|
|
||||||
#if __cplusplus
|
#if __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -325,7 +325,7 @@ rcl_wait_set_get_allocator(const rcl_wait_set_t * wait_set, rcl_allocator_t * al
|
||||||
wait_set->size_of_ ## Type ## s = 0; \
|
wait_set->size_of_ ## Type ## s = 0; \
|
||||||
RCL_SET_ERROR_MSG("allocating memory failed"); \
|
RCL_SET_ERROR_MSG("allocating memory failed"); \
|
||||||
return RCL_RET_BAD_ALLOC; \
|
return RCL_RET_BAD_ALLOC; \
|
||||||
} \
|
}
|
||||||
|
|
||||||
/* Implementation-specific notes:
|
/* Implementation-specific notes:
|
||||||
*
|
*
|
||||||
|
|
|
@ -67,6 +67,19 @@ function(test_target_function)
|
||||||
AMENT_DEPENDENCIES ${rmw_implementation}
|
AMENT_DEPENDENCIES ${rmw_implementation}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# TODO(wjwwood): remove this when the graph API works properly for more than OpenSplice.
|
||||||
|
if("${rmw_implementation} " STREQUAL "rmw_opensplice_cpp ")
|
||||||
|
rcl_add_custom_gtest(test_graph${target_suffix}
|
||||||
|
SRCS rcl/test_graph.cpp
|
||||||
|
ENV ${extra_test_env}
|
||||||
|
APPEND_LIBRARY_DIRS ${extra_lib_dirs}
|
||||||
|
LIBRARIES ${PROJECT_NAME}${target_suffix} ${extra_test_libraries}
|
||||||
|
AMENT_DEPENDENCIES ${rmw_implementation} "example_interfaces" "std_msgs"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "Skipping test_graph${target_suffix} test.")
|
||||||
|
endif()
|
||||||
|
|
||||||
rcl_add_custom_gtest(test_rcl${target_suffix}
|
rcl_add_custom_gtest(test_rcl${target_suffix}
|
||||||
SRCS rcl/test_rcl.cpp
|
SRCS rcl/test_rcl.cpp
|
||||||
ENV ${extra_test_env}
|
ENV ${extra_test_env}
|
||||||
|
@ -142,7 +155,6 @@ function(test_target_function)
|
||||||
ENV ${extra_test_env}
|
ENV ${extra_test_env}
|
||||||
APPEND_LIBRARY_DIRS ${extra_lib_dirs}
|
APPEND_LIBRARY_DIRS ${extra_lib_dirs}
|
||||||
TIMEOUT 15
|
TIMEOUT 15
|
||||||
#WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
|
||||||
)
|
)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
550
rcl/test/rcl/test_graph.cpp
Normal file
550
rcl/test/rcl/test_graph.cpp
Normal file
|
@ -0,0 +1,550 @@
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
// Disable -Wmissing-field-initializers, as it is overly strict and will be
|
||||||
|
// relaxed in future versions of GCC (it is not a warning for clang).
|
||||||
|
// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36750#c13
|
||||||
|
#ifndef _WIN32
|
||||||
|
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <future>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "rcl/rcl.h"
|
||||||
|
#include "rcl/graph.h"
|
||||||
|
|
||||||
|
#include "std_msgs/msg/string.h"
|
||||||
|
#include "example_interfaces/srv/add_two_ints.h"
|
||||||
|
|
||||||
|
#include "../memory_tools/memory_tools.hpp"
|
||||||
|
#include "../scope_exit.hpp"
|
||||||
|
#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 (TestGraphFixture, RMW_IMPLEMENTATION) : public ::testing::Test
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
rcl_node_t * old_node_ptr;
|
||||||
|
rcl_node_t * node_ptr;
|
||||||
|
rcl_wait_set_t * wait_set_ptr;
|
||||||
|
void SetUp()
|
||||||
|
{
|
||||||
|
stop_memory_checking();
|
||||||
|
rcl_ret_t ret;
|
||||||
|
ret = rcl_init(0, nullptr, rcl_get_default_allocator());
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
this->old_node_ptr = new rcl_node_t;
|
||||||
|
*this->old_node_ptr = rcl_get_zero_initialized_node();
|
||||||
|
const char * old_name = "old_node_name";
|
||||||
|
rcl_node_options_t node_options = rcl_node_get_default_options();
|
||||||
|
ret = rcl_node_init(this->old_node_ptr, old_name, &node_options);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
ret = rcl_shutdown(); // after this, the old_node_ptr should be invalid
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
|
||||||
|
ret = rcl_init(0, nullptr, rcl_get_default_allocator());
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
this->node_ptr = new rcl_node_t;
|
||||||
|
*this->node_ptr = rcl_get_zero_initialized_node();
|
||||||
|
const char * name = "node_name";
|
||||||
|
ret = rcl_node_init(this->node_ptr, name, &node_options);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
|
||||||
|
this->wait_set_ptr = new rcl_wait_set_t;
|
||||||
|
*this->wait_set_ptr = rcl_get_zero_initialized_wait_set();
|
||||||
|
ret = rcl_wait_set_init(this->wait_set_ptr, 0, 1, 0, 0, 0, rcl_get_default_allocator());
|
||||||
|
|
||||||
|
set_on_unexpected_malloc_callback([]() {ASSERT_FALSE(true) << "UNEXPECTED MALLOC";});
|
||||||
|
set_on_unexpected_realloc_callback([]() {ASSERT_FALSE(true) << "UNEXPECTED REALLOC";});
|
||||||
|
set_on_unexpected_free_callback([]() {ASSERT_FALSE(true) << "UNEXPECTED FREE";});
|
||||||
|
start_memory_checking();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown()
|
||||||
|
{
|
||||||
|
assert_no_malloc_end();
|
||||||
|
assert_no_realloc_end();
|
||||||
|
assert_no_free_end();
|
||||||
|
stop_memory_checking();
|
||||||
|
set_on_unexpected_malloc_callback(nullptr);
|
||||||
|
set_on_unexpected_realloc_callback(nullptr);
|
||||||
|
set_on_unexpected_free_callback(nullptr);
|
||||||
|
rcl_ret_t ret;
|
||||||
|
ret = rcl_node_fini(this->old_node_ptr);
|
||||||
|
delete this->old_node_ptr;
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
|
||||||
|
ret = rcl_wait_set_fini(this->wait_set_ptr);
|
||||||
|
delete this->wait_set_ptr;
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
|
||||||
|
ret = rcl_node_fini(this->node_ptr);
|
||||||
|
delete this->node_ptr;
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
|
||||||
|
ret = rcl_shutdown();
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Test the rcl_get_topic_names_and_types and rcl_destroy_topic_names_and_types functions.
|
||||||
|
*
|
||||||
|
* This does not test content of the rcl_topic_names_and_types_t structure.
|
||||||
|
*/
|
||||||
|
TEST_F(
|
||||||
|
CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
|
||||||
|
test_rcl_get_and_destroy_topic_names_and_types
|
||||||
|
) {
|
||||||
|
stop_memory_checking();
|
||||||
|
rcl_ret_t ret;
|
||||||
|
rcl_topic_names_and_types_t tnat {};
|
||||||
|
rcl_node_t zero_node = rcl_get_zero_initialized_node();
|
||||||
|
// invalid node
|
||||||
|
ret = rcl_get_topic_names_and_types(nullptr, &tnat);
|
||||||
|
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
ret = rcl_get_topic_names_and_types(&zero_node, &tnat);
|
||||||
|
EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
ret = rcl_get_topic_names_and_types(this->old_node_ptr, &tnat);
|
||||||
|
EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// invalid topic_names_and_types
|
||||||
|
ret = rcl_get_topic_names_and_types(this->node_ptr, nullptr);
|
||||||
|
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// invalid argument to rcl_destroy_topic_names_and_types
|
||||||
|
ret = rcl_destroy_topic_names_and_types(nullptr);
|
||||||
|
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// valid calls
|
||||||
|
ret = rcl_get_topic_names_and_types(this->node_ptr, &tnat);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
ret = rcl_destroy_topic_names_and_types(&tnat);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test the rcl_count_publishers function.
|
||||||
|
*
|
||||||
|
* This does not test content the response.
|
||||||
|
*/
|
||||||
|
TEST_F(
|
||||||
|
CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
|
||||||
|
test_rcl_count_publishers
|
||||||
|
) {
|
||||||
|
stop_memory_checking();
|
||||||
|
rcl_ret_t ret;
|
||||||
|
rcl_node_t zero_node = rcl_get_zero_initialized_node();
|
||||||
|
const char * topic_name = "topic_test_rcl_count_publishers";
|
||||||
|
size_t count;
|
||||||
|
// invalid node
|
||||||
|
ret = rcl_count_publishers(nullptr, topic_name, &count);
|
||||||
|
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
ret = rcl_count_publishers(&zero_node, topic_name, &count);
|
||||||
|
EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
ret = rcl_count_publishers(this->old_node_ptr, topic_name, &count);
|
||||||
|
EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// invalid topic name
|
||||||
|
ret = rcl_count_publishers(this->node_ptr, nullptr, &count);
|
||||||
|
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// TODO(wjwwood): test valid strings with invalid topic names in them
|
||||||
|
// invalid count
|
||||||
|
ret = rcl_count_publishers(this->node_ptr, topic_name, nullptr);
|
||||||
|
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// valid call
|
||||||
|
ret = rcl_count_publishers(this->node_ptr, topic_name, &count);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test the rcl_count_subscribers function.
|
||||||
|
*
|
||||||
|
* This does not test content the response.
|
||||||
|
*/
|
||||||
|
TEST_F(
|
||||||
|
CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
|
||||||
|
test_rcl_count_subscribers
|
||||||
|
) {
|
||||||
|
stop_memory_checking();
|
||||||
|
rcl_ret_t ret;
|
||||||
|
rcl_node_t zero_node = rcl_get_zero_initialized_node();
|
||||||
|
const char * topic_name = "topic_test_rcl_count_subscribers";
|
||||||
|
size_t count;
|
||||||
|
// invalid node
|
||||||
|
ret = rcl_count_subscribers(nullptr, topic_name, &count);
|
||||||
|
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
ret = rcl_count_subscribers(&zero_node, topic_name, &count);
|
||||||
|
EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
ret = rcl_count_subscribers(this->old_node_ptr, topic_name, &count);
|
||||||
|
EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// invalid topic name
|
||||||
|
ret = rcl_count_subscribers(this->node_ptr, nullptr, &count);
|
||||||
|
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// TODO(wjwwood): test valid strings with invalid topic names in them
|
||||||
|
// invalid count
|
||||||
|
ret = rcl_count_subscribers(this->node_ptr, topic_name, nullptr);
|
||||||
|
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// valid call
|
||||||
|
ret = rcl_count_subscribers(this->node_ptr, topic_name, &count);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
check_graph_state(
|
||||||
|
const rcl_node_t * node_ptr,
|
||||||
|
rcl_wait_set_t * wait_set_ptr,
|
||||||
|
const rcl_guard_condition_t * graph_guard_condition,
|
||||||
|
std::string & topic_name,
|
||||||
|
size_t expected_publisher_count,
|
||||||
|
size_t expected_subscriber_count,
|
||||||
|
bool expected_in_tnat,
|
||||||
|
size_t number_of_tries)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"Expecting %zu publishers, %zu subscribers, and that the topic is%s in the graph.\n",
|
||||||
|
expected_publisher_count,
|
||||||
|
expected_subscriber_count,
|
||||||
|
expected_in_tnat ? "" : " not"
|
||||||
|
);
|
||||||
|
size_t publisher_count = 0;
|
||||||
|
size_t subscriber_count = 0;
|
||||||
|
bool is_in_tnat = false;
|
||||||
|
rcl_topic_names_and_types_t tnat {};
|
||||||
|
rcl_ret_t ret;
|
||||||
|
for (size_t i = 0; i < number_of_tries; ++i) {
|
||||||
|
ret = rcl_count_publishers(node_ptr, topic_name.c_str(), &publisher_count);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
|
||||||
|
ret = rcl_count_subscribers(node_ptr, topic_name.c_str(), &subscriber_count);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
|
||||||
|
ret = rcl_get_topic_names_and_types(node_ptr, &tnat);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
is_in_tnat = false;
|
||||||
|
for (size_t i = 0; RCL_RET_OK == ret && i < tnat.topic_count; ++i) {
|
||||||
|
if (topic_name == std::string(tnat.topic_names[i])) {
|
||||||
|
ASSERT_FALSE(is_in_tnat) << "duplicates in the tnat"; // Found it more than once!
|
||||||
|
is_in_tnat = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (RCL_RET_OK == ret) {
|
||||||
|
ret = rcl_destroy_topic_names_and_types(&tnat);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(
|
||||||
|
" Try %zu: %zu publishers, %zu subscribers, and that the topic is%s in the graph.\n",
|
||||||
|
i + 1,
|
||||||
|
publisher_count,
|
||||||
|
subscriber_count,
|
||||||
|
is_in_tnat ? "" : " not"
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
expected_publisher_count == publisher_count &&
|
||||||
|
expected_subscriber_count == subscriber_count &&
|
||||||
|
expected_in_tnat == is_in_tnat)
|
||||||
|
{
|
||||||
|
printf(" state correct!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Wait for graph change before trying again.
|
||||||
|
if ((i + 1) == number_of_tries) {
|
||||||
|
// Don't wait for the graph to change on the last loop because we won't check again.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ret = rcl_wait_set_clear_guard_conditions(wait_set_ptr);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
ret = rcl_wait_set_add_guard_condition(wait_set_ptr, graph_guard_condition);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
std::chrono::nanoseconds time_to_sleep = std::chrono::milliseconds(200);
|
||||||
|
printf(
|
||||||
|
" state wrong, waiting up to '%s' nanoseconds for graph changes... ",
|
||||||
|
std::to_string(time_to_sleep.count()).c_str());
|
||||||
|
ret = rcl_wait(wait_set_ptr, time_to_sleep.count());
|
||||||
|
if (ret == RCL_RET_TIMEOUT) {
|
||||||
|
printf("timeout\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
printf("change occurred\n");
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
}
|
||||||
|
EXPECT_EQ(expected_publisher_count, publisher_count);
|
||||||
|
EXPECT_EQ(expected_subscriber_count, subscriber_count);
|
||||||
|
if (expected_in_tnat) {
|
||||||
|
EXPECT_TRUE(is_in_tnat);
|
||||||
|
} else {
|
||||||
|
EXPECT_FALSE(is_in_tnat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test graph queries with a hand crafted graph.
|
||||||
|
*/
|
||||||
|
TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), test_graph_query_functions) {
|
||||||
|
stop_memory_checking();
|
||||||
|
std::string topic_name("test_graph_query_functions__");
|
||||||
|
std::chrono::nanoseconds now = std::chrono::system_clock::now().time_since_epoch();
|
||||||
|
topic_name += std::to_string(now.count());
|
||||||
|
printf("Using topic name: %s\n", topic_name.c_str());
|
||||||
|
rcl_ret_t ret;
|
||||||
|
const rcl_guard_condition_t * graph_guard_condition =
|
||||||
|
rcl_node_get_graph_guard_condition(this->node_ptr);
|
||||||
|
// First assert the "topic_name" is not in use.
|
||||||
|
check_graph_state(
|
||||||
|
this->node_ptr,
|
||||||
|
this->wait_set_ptr,
|
||||||
|
graph_guard_condition,
|
||||||
|
topic_name,
|
||||||
|
0, // expected publishers on topic
|
||||||
|
0, // expected subscribers on topic
|
||||||
|
false, // topic expected in graph
|
||||||
|
9); // number of retries
|
||||||
|
// Now create a publisher on "topic_name" and check that it is seen.
|
||||||
|
rcl_publisher_t pub = rcl_get_zero_initialized_publisher();
|
||||||
|
rcl_publisher_options_t pub_ops = rcl_publisher_get_default_options();
|
||||||
|
auto ts = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, String);
|
||||||
|
ret = rcl_publisher_init(&pub, this->node_ptr, ts, topic_name.c_str(), &pub_ops);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// Check the graph.
|
||||||
|
check_graph_state(
|
||||||
|
this->node_ptr,
|
||||||
|
this->wait_set_ptr,
|
||||||
|
graph_guard_condition,
|
||||||
|
topic_name,
|
||||||
|
1, // expected publishers on topic
|
||||||
|
0, // expected subscribers on topic
|
||||||
|
true, // topic expected in graph
|
||||||
|
9); // number of retries
|
||||||
|
// Now create a subscriber.
|
||||||
|
rcl_subscription_t sub = rcl_get_zero_initialized_subscription();
|
||||||
|
rcl_subscription_options_t sub_ops = rcl_subscription_get_default_options();
|
||||||
|
ret = rcl_subscription_init(&sub, this->node_ptr, ts, topic_name.c_str(), &sub_ops);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// Check the graph again.
|
||||||
|
check_graph_state(
|
||||||
|
this->node_ptr,
|
||||||
|
this->wait_set_ptr,
|
||||||
|
graph_guard_condition,
|
||||||
|
topic_name,
|
||||||
|
1, // expected publishers on topic
|
||||||
|
1, // expected subscribers on topic
|
||||||
|
true, // topic expected in graph
|
||||||
|
9); // number of retries
|
||||||
|
// Destroy the publisher.
|
||||||
|
ret = rcl_publisher_fini(&pub, this->node_ptr);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// Check the graph again.
|
||||||
|
check_graph_state(
|
||||||
|
this->node_ptr,
|
||||||
|
this->wait_set_ptr,
|
||||||
|
graph_guard_condition,
|
||||||
|
topic_name,
|
||||||
|
0, // expected publishers on topic
|
||||||
|
1, // expected subscribers on topic
|
||||||
|
true, // topic expected in graph
|
||||||
|
9); // number of retries
|
||||||
|
// Destroy the subscriber.
|
||||||
|
ret = rcl_subscription_fini(&sub, this->node_ptr);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
rcl_reset_error();
|
||||||
|
// Check the graph again.
|
||||||
|
check_graph_state(
|
||||||
|
this->node_ptr,
|
||||||
|
this->wait_set_ptr,
|
||||||
|
graph_guard_condition,
|
||||||
|
topic_name,
|
||||||
|
0, // expected publishers on topic
|
||||||
|
0, // expected subscribers on topic
|
||||||
|
false, // topic expected in graph
|
||||||
|
9); // number of retries
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test the graph guard condition notices topic changes.
|
||||||
|
*
|
||||||
|
* Note: this test could be impacted by other communications on the same ROS Domain.
|
||||||
|
*/
|
||||||
|
TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), test_graph_guard_condition_topics) {
|
||||||
|
stop_memory_checking();
|
||||||
|
rcl_ret_t ret;
|
||||||
|
// Create a thread to sleep for a time, then create a publisher, sleep more, then a subscriber,
|
||||||
|
// sleep more, destroy the subscriber, sleep more, and then destroy the publisher.
|
||||||
|
std::promise<bool> topic_changes_promise;
|
||||||
|
std::thread topic_thread([this, &topic_changes_promise]() {
|
||||||
|
// sleep
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
// create the publisher
|
||||||
|
rcl_publisher_t pub = rcl_get_zero_initialized_publisher();
|
||||||
|
rcl_publisher_options_t pub_ops = rcl_publisher_get_default_options();
|
||||||
|
rcl_ret_t ret = rcl_publisher_init(
|
||||||
|
&pub, this->node_ptr, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, String),
|
||||||
|
"chatter_test_graph_guard_condition_topics", &pub_ops);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
// sleep
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
// create the subscription
|
||||||
|
rcl_subscription_t sub = rcl_get_zero_initialized_subscription();
|
||||||
|
rcl_subscription_options_t sub_ops = rcl_subscription_get_default_options();
|
||||||
|
ret = rcl_subscription_init(
|
||||||
|
&sub, this->node_ptr, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, String),
|
||||||
|
"chatter_test_graph_guard_condition_topics", &sub_ops);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
// sleep
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
// destroy the subscription
|
||||||
|
ret = rcl_subscription_fini(&sub, this->node_ptr);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
// sleep
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
// destroy the publication
|
||||||
|
ret = rcl_publisher_fini(&pub, this->node_ptr);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
// notify that the thread is done
|
||||||
|
topic_changes_promise.set_value(true);
|
||||||
|
});
|
||||||
|
// Wait for the graph state to change, expecting it to do so at least 4 times,
|
||||||
|
// once for each change in the topics thread.
|
||||||
|
const rcl_guard_condition_t * graph_guard_condition =
|
||||||
|
rcl_node_get_graph_guard_condition(this->node_ptr);
|
||||||
|
ASSERT_NE(nullptr, graph_guard_condition) << rcl_get_error_string_safe();
|
||||||
|
std::shared_future<bool> future = topic_changes_promise.get_future();
|
||||||
|
size_t graph_changes_count = 0;
|
||||||
|
// while the topic thread is not done, wait and count the graph changes
|
||||||
|
while (future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
|
||||||
|
ret = rcl_wait_set_clear_guard_conditions(this->wait_set_ptr);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
ret = rcl_wait_set_add_guard_condition(this->wait_set_ptr, graph_guard_condition);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
std::chrono::nanoseconds time_to_sleep = std::chrono::milliseconds(200);
|
||||||
|
printf(
|
||||||
|
"waiting up to '%s' nanoseconds for graph changes\n",
|
||||||
|
std::to_string(time_to_sleep.count()).c_str());
|
||||||
|
ret = rcl_wait(this->wait_set_ptr, time_to_sleep.count());
|
||||||
|
if (ret == RCL_RET_TIMEOUT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
graph_changes_count++;
|
||||||
|
}
|
||||||
|
topic_thread.join();
|
||||||
|
// expect at least 4 changes
|
||||||
|
ASSERT_GE(graph_changes_count, 4ul);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test the rcl_service_server_is_available function.
|
||||||
|
*/
|
||||||
|
TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), test_rcl_service_server_is_available) {
|
||||||
|
stop_memory_checking();
|
||||||
|
rcl_ret_t ret;
|
||||||
|
// First create a client which will be used to call the function.
|
||||||
|
rcl_client_t client = rcl_get_zero_initialized_client();
|
||||||
|
auto ts = ROSIDL_GET_SRV_TYPE_SUPPORT(example_interfaces, AddTwoInts);
|
||||||
|
const char * service_name = "service_test_rcl_service_server_is_available";
|
||||||
|
rcl_client_options_t client_options = rcl_client_get_default_options();
|
||||||
|
ret = rcl_client_init(&client, this->node_ptr, ts, service_name, &client_options);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
auto client_exit = make_scope_exit([&client, this]() {
|
||||||
|
stop_memory_checking();
|
||||||
|
rcl_ret_t ret = rcl_client_fini(&client, this->node_ptr);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
});
|
||||||
|
// Check, knowing there is no service server (created by us at least).
|
||||||
|
bool is_available;
|
||||||
|
ret = rcl_service_server_is_available(this->node_ptr, &client, &is_available);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
ASSERT_FALSE(is_available);
|
||||||
|
// Setup function to wait for service state to change using graph guard condition.
|
||||||
|
const rcl_guard_condition_t * graph_guard_condition =
|
||||||
|
rcl_node_get_graph_guard_condition(this->node_ptr);
|
||||||
|
ASSERT_NE(nullptr, graph_guard_condition) << rcl_get_error_string_safe();
|
||||||
|
auto wait_for_service_state_to_change = [this, &graph_guard_condition, &client](
|
||||||
|
bool expected_state,
|
||||||
|
bool & is_available)
|
||||||
|
{
|
||||||
|
is_available = false;
|
||||||
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
auto end = start + std::chrono::seconds(10);
|
||||||
|
while (std::chrono::steady_clock::now() < end) {
|
||||||
|
// We wait multiple times in case other graph changes are occurring simultaneously.
|
||||||
|
std::chrono::nanoseconds time_left = end - start;
|
||||||
|
std::chrono::nanoseconds time_to_sleep = time_left;
|
||||||
|
std::chrono::seconds min_sleep(1);
|
||||||
|
if (time_to_sleep > min_sleep) {
|
||||||
|
time_to_sleep = min_sleep;
|
||||||
|
}
|
||||||
|
rcl_ret_t ret = rcl_wait_set_clear_guard_conditions(this->wait_set_ptr);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
ret = rcl_wait_set_add_guard_condition(this->wait_set_ptr, graph_guard_condition);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
printf(
|
||||||
|
"waiting up to '%s' nanoseconds for graph changes\n",
|
||||||
|
std::to_string(time_to_sleep.count()).c_str());
|
||||||
|
ret = rcl_wait(this->wait_set_ptr, time_to_sleep.count());
|
||||||
|
if (ret == RCL_RET_TIMEOUT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
ret = rcl_service_server_is_available(this->node_ptr, &client, &is_available);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
if (is_available == expected_state) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
{
|
||||||
|
// Create the service server.
|
||||||
|
rcl_service_t service = rcl_get_zero_initialized_service();
|
||||||
|
rcl_service_options_t service_options = rcl_service_get_default_options();
|
||||||
|
ret = rcl_service_init(&service, this->node_ptr, ts, service_name, &service_options);
|
||||||
|
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
auto service_exit = make_scope_exit([&service, this]() {
|
||||||
|
stop_memory_checking();
|
||||||
|
rcl_ret_t ret = rcl_service_fini(&service, this->node_ptr);
|
||||||
|
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||||
|
});
|
||||||
|
// Wait for and then assert that it is available.
|
||||||
|
wait_for_service_state_to_change(true, is_available);
|
||||||
|
ASSERT_TRUE(is_available);
|
||||||
|
}
|
||||||
|
// Assert the state goes back to "not available" after the service is removed.
|
||||||
|
wait_for_service_state_to_change(false, is_available);
|
||||||
|
ASSERT_FALSE(is_available);
|
||||||
|
}
|
|
@ -112,6 +112,20 @@ TEST_F(CLASSNAME(TestNodeFixture, RMW_IMPLEMENTATION), test_rcl_node_accessors)
|
||||||
rcl_ret_t ret = rcl_node_fini(&node);
|
rcl_ret_t ret = rcl_node_fini(&node);
|
||||||
EXPECT_EQ(RCL_RET_OK, ret);
|
EXPECT_EQ(RCL_RET_OK, ret);
|
||||||
});
|
});
|
||||||
|
// Test rcl_node_is_valid().
|
||||||
|
bool is_valid;
|
||||||
|
is_valid = rcl_node_is_valid(nullptr);
|
||||||
|
EXPECT_FALSE(is_valid);
|
||||||
|
rcl_reset_error();
|
||||||
|
is_valid = rcl_node_is_valid(&zero_node);
|
||||||
|
EXPECT_FALSE(is_valid);
|
||||||
|
rcl_reset_error();
|
||||||
|
is_valid = rcl_node_is_valid(&invalid_node);
|
||||||
|
EXPECT_FALSE(is_valid);
|
||||||
|
rcl_reset_error();
|
||||||
|
is_valid = rcl_node_is_valid(&node);
|
||||||
|
EXPECT_TRUE(is_valid);
|
||||||
|
rcl_reset_error();
|
||||||
// Test rcl_node_get_name().
|
// Test rcl_node_get_name().
|
||||||
const char * actual_node_name;
|
const char * actual_node_name;
|
||||||
actual_node_name = rcl_node_get_name(nullptr);
|
actual_node_name = rcl_node_get_name(nullptr);
|
||||||
|
@ -229,6 +243,32 @@ TEST_F(CLASSNAME(TestNodeFixture, RMW_IMPLEMENTATION), test_rcl_node_accessors)
|
||||||
assert_no_free_end();
|
assert_no_free_end();
|
||||||
stop_memory_checking();
|
stop_memory_checking();
|
||||||
EXPECT_NE(0u, instance_id);
|
EXPECT_NE(0u, instance_id);
|
||||||
|
// Test rcl_node_get_graph_guard_condition
|
||||||
|
std::string rmw_id(rmw_get_implementation_identifier());
|
||||||
|
if (rmw_id.find("opensplice") != std::string::npos) {
|
||||||
|
// Only test with opensplice for now, as connext and fastrtps are not working.
|
||||||
|
// TODO(wjwwood): remove this check when more middlewares implement this
|
||||||
|
const rcl_guard_condition_t * graph_guard_condition = nullptr;
|
||||||
|
graph_guard_condition = rcl_node_get_graph_guard_condition(nullptr);
|
||||||
|
EXPECT_EQ(nullptr, graph_guard_condition);
|
||||||
|
rcl_reset_error();
|
||||||
|
graph_guard_condition = rcl_node_get_graph_guard_condition(&zero_node);
|
||||||
|
EXPECT_EQ(nullptr, graph_guard_condition);
|
||||||
|
rcl_reset_error();
|
||||||
|
graph_guard_condition = rcl_node_get_graph_guard_condition(&invalid_node);
|
||||||
|
EXPECT_EQ(nullptr, graph_guard_condition);
|
||||||
|
rcl_reset_error();
|
||||||
|
start_memory_checking();
|
||||||
|
assert_no_malloc_begin();
|
||||||
|
assert_no_realloc_begin();
|
||||||
|
assert_no_free_begin();
|
||||||
|
graph_guard_condition = rcl_node_get_graph_guard_condition(&node);
|
||||||
|
assert_no_malloc_end();
|
||||||
|
assert_no_realloc_end();
|
||||||
|
assert_no_free_end();
|
||||||
|
stop_memory_checking();
|
||||||
|
EXPECT_NE(nullptr, graph_guard_condition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tests the node life cycle, including rcl_node_init() and rcl_node_fini().
|
/* Tests the node life cycle, including rcl_node_init() and rcl_node_fini().
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue