Add services and clients
This commit is contained in:
parent
1932e052ec
commit
534a49c480
17 changed files with 1760 additions and 23 deletions
|
@ -18,11 +18,13 @@ endif()
|
|||
|
||||
set(${PROJECT_NAME}_sources
|
||||
src/rcl/allocator.c
|
||||
src/rcl/client.c
|
||||
src/rcl/common.c
|
||||
src/rcl/guard_condition.c
|
||||
src/rcl/node.c
|
||||
src/rcl/publisher.c
|
||||
src/rcl/rcl.c
|
||||
src/rcl/service.c
|
||||
src/rcl/subscription.c
|
||||
src/rcl/wait.c
|
||||
src/rcl/time.c
|
||||
|
|
301
rcl/include/rcl/client.h
Normal file
301
rcl/include/rcl/client.h
Normal file
|
@ -0,0 +1,301 @@
|
|||
// 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__CLIENT_H_
|
||||
#define RCL__CLIENT_H_
|
||||
|
||||
#if __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "rosidl_generator_c/service_type_support.h"
|
||||
|
||||
#include "rcl/macros.h"
|
||||
#include "rcl/node.h"
|
||||
#include "rcl/visibility_control.h"
|
||||
|
||||
/// Internal rcl client implementation struct.
|
||||
struct rcl_client_impl_t;
|
||||
|
||||
/// Handle for a rcl client.
|
||||
typedef struct rcl_client_t
|
||||
{
|
||||
struct rcl_client_impl_t * impl;
|
||||
} rcl_client_t;
|
||||
|
||||
/// Options available for a rcl client.
|
||||
typedef struct rcl_client_options_t
|
||||
{
|
||||
/// Middleware quality of service settings for the client.
|
||||
rmw_qos_profile_t qos;
|
||||
/// Custom allocator for the client, used for incidental allocations.
|
||||
/* For default behavior (malloc/free), use: rcl_get_default_allocator() */
|
||||
rcl_allocator_t allocator;
|
||||
} rcl_client_options_t;
|
||||
|
||||
/// Return a rcl_client_t struct with members set to NULL.
|
||||
/* Should be called to get a null rcl_client_t before passing to
|
||||
* rcl_initalize_client().
|
||||
* It's also possible to use calloc() instead of this if the rcl_client is
|
||||
* being allocated on the heap.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_client_t
|
||||
rcl_get_zero_initialized_client(void);
|
||||
|
||||
|
||||
/// Initialize a rcl client.
|
||||
/* After calling this function on a rcl_client_t, it can be used to send requests of the given
|
||||
* type by calling rcl_send_request().
|
||||
* If the request is received by a (possibly remote) service and if the service sends a response,
|
||||
* the client can access the response through rcl_take_response once the response is available to
|
||||
* the client.
|
||||
*
|
||||
* The given rcl_node_t must be valid and the resulting rcl_client_t is only
|
||||
* valid as long as the given rcl_node_t remains valid.
|
||||
*
|
||||
* The rosidl_service_type_support_t is obtained on a per .srv type basis.
|
||||
* When the user defines a ROS service, code is generated which provides the
|
||||
* required rosidl_service_type_support_t object.
|
||||
* This object can be obtained using a language appropriate mechanism.
|
||||
* \TODO(wjwwood) probably should talk about this once and link to it instead
|
||||
* For C this macro can be used (using example_interfaces/AddTwoInts as an example):
|
||||
*
|
||||
* #include <rosidl_generator_c/service_type_support.h>
|
||||
* #include <example_interfaces/srv/add_two_ints.h>
|
||||
* rosidl_service_type_support_t * ts =
|
||||
* ROSIDL_GET_SERVICE_TYPE_SUPPORT(example_interfaces, AddTwoInts);
|
||||
*
|
||||
* For C++ a template function is used:
|
||||
*
|
||||
* #include <rosidl_generator_cpp/service_type_support.hpp>
|
||||
* #include <example_interfaces/srv/add_two_ints.h>
|
||||
* rosidl_service_type_support_t * ts = rosidl_generator_cpp::get_service_type_support_handle<
|
||||
* example_interfaces::srv::AddTwoInts>();
|
||||
*
|
||||
* The rosidl_service_type_support_t object contains service type specific
|
||||
* information used to send or take requests and responses.
|
||||
*
|
||||
* \TODO(wjwwood) update this once we've come up with an official scheme.
|
||||
* The service name must be a non-empty string which follows the topic/service naming
|
||||
* format.
|
||||
*
|
||||
* The options struct allows the user to set the quality of service settings as
|
||||
* well as a custom allocator which is used when initializing/finalizing the
|
||||
* client to allocate space for incidentals, e.g. the service name string.
|
||||
*
|
||||
* Expected usage (for C services):
|
||||
*
|
||||
* #include <rcl/rcl.h>
|
||||
* #include <rosidl_generator_c/service_type_support.h>
|
||||
* #include <example_interfaces/srv/add_two_ints.h>
|
||||
*
|
||||
* rcl_node_t node = rcl_get_zero_initialized_node();
|
||||
* rcl_node_options_t node_ops = rcl_node_get_default_options();
|
||||
* rcl_ret_t ret = rcl_node_init(&node, "node_name", &node_ops);
|
||||
* // ... error handling
|
||||
* rosidl_service_type_support_t * ts = ROSIDL_GET_SERVICE_TYPE_SUPPORT(
|
||||
* example_interfaces, AddTwoInts);
|
||||
* rcl_client_t client = rcl_get_zero_initialized_client();
|
||||
* rcl_client_options_t client_ops = rcl_client_get_default_options();
|
||||
* ret = rcl_client_init(&client, &node, ts, "add_two_ints", &client_ops);
|
||||
* // ... error handling, and on shutdown do finalization:
|
||||
* ret = rcl_client_fini(&client, &node);
|
||||
* // ... error handling for rcl_client_fini()
|
||||
* ret = rcl_node_fini(&node);
|
||||
* // ... error handling for rcl_node_fini()
|
||||
*
|
||||
* This function is not thread-safe.
|
||||
*
|
||||
* \param[inout] client preallocated client structure
|
||||
* \param[in] node valid rcl node handle
|
||||
* \param[in] type_support type support object for the service's type
|
||||
* \param[in] service_name the name of the service to request
|
||||
* \param[in] options client options, including quality of service settings
|
||||
* \return RCL_RET_OK if the client was initialized successfully, or
|
||||
* RCL_RET_NODE_INVALID if the node is invalid, or
|
||||
* RCL_RET_ALREADY_INIT if the client is already initialized, or
|
||||
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||
* RCL_RET_BAD_ALLOC if allocating memory fails, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_client_init(
|
||||
rcl_client_t * client,
|
||||
const rcl_node_t * node,
|
||||
const rosidl_service_type_support_t * type_support,
|
||||
const char * service_name,
|
||||
const rcl_client_options_t * options);
|
||||
|
||||
/// Finalize a rcl_client_t.
|
||||
/*
|
||||
* After calling, calls to rcl_send_request and rcl_take_response will fail when using this client.
|
||||
* However, the given node handle is still valid.
|
||||
*
|
||||
* This function is not thread-safe.
|
||||
*
|
||||
* \param[inout] client handle to the client to be finalized
|
||||
* \param[in] node handle to the node used to create the client
|
||||
* \return RCL_RET_OK if client was finalized successfully, 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_client_fini(rcl_client_t * client, rcl_node_t * node);
|
||||
|
||||
/// Return the default client options in a rcl_client_options_t.
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_client_options_t
|
||||
rcl_client_get_default_options(void);
|
||||
|
||||
/// Send a ROS request using a client.
|
||||
/* It is the job of the caller to ensure that the type of the ros_request
|
||||
* parameter and the type associate with the client (via the type support)
|
||||
* match.
|
||||
* Passing a different type to send_request produces undefined behavior and cannot
|
||||
* be checked by this function and therefore no deliberate error will occur.
|
||||
*
|
||||
* send_request is an non-blocking call.
|
||||
*
|
||||
* The ROS request message given by the ros_request void pointer is always owned by the
|
||||
* calling code, but should remain constant during send_request.
|
||||
*
|
||||
* This function is thread safe so long as access to both the client and the
|
||||
* ros_request is synchronized.
|
||||
* That means that calling rcl_send_request from multiple threads is allowed, but
|
||||
* calling rcl_send_request at the same time as non-thread safe client functions
|
||||
* is not, e.g. calling rcl_send_request and rcl_client_fini concurrently
|
||||
* is not allowed.
|
||||
* Before calling rcl_send_request the message can change and after calling
|
||||
* rcl_send_request the message can change, but it cannot be changed during the
|
||||
* send_request call.
|
||||
* The same ros_request, however, can be passed to multiple calls of
|
||||
* rcl_send_request simultaneously, even if the clients differ.
|
||||
* The ros_request is unmodified by rcl_send_request.
|
||||
*
|
||||
* \param[in] client handle to the client which will make the response
|
||||
* \param[in] ros_request type-erased pointer to the ROS request message
|
||||
* \param[out] sequence_number the sequence number
|
||||
* \return RCL_RET_OK if the request was sent successfully, or
|
||||
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||
* RCL_RET_CLIENT_INVALID if the client is invalid, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_send_request(const rcl_client_t * client, const void * ros_request, int64_t * sequence_number);
|
||||
|
||||
|
||||
// Take a ROS response using a client
|
||||
/* It is the job of the caller to ensure that the type of the ros_response
|
||||
* parameter and the type associate with the client (via the type support)
|
||||
* match.
|
||||
* Passing a different type to take_response produces undefined behavior and cannot
|
||||
* be checked by this function and therefore no deliberate error will occur.
|
||||
* The request_header is an rmw struct for meta-information about the request sent
|
||||
* (e.g. the sequence number).
|
||||
* ros_response should point to an already allocated ROS response message struct of the
|
||||
* correct type, into which the response from the service will be copied.
|
||||
*
|
||||
* \param[in] client handle to the client which will take the response
|
||||
* \param[inout] request_header pointer to the request header
|
||||
* \param[inout] ros_response type-erased pointer to the ROS response message
|
||||
* \return RCL_RET_OK if the response was taken successfully, or
|
||||
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||
* RCL_RET_CLIENT_INVALID if the client is invalid, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_take_response(
|
||||
const rcl_client_t * client,
|
||||
rmw_request_id_t * request_header,
|
||||
void * ros_response);
|
||||
|
||||
/// Get the name of the service that this client will request a response from.
|
||||
/* This function returns the client's internal service name string.
|
||||
* This function can fail, and therefore return NULL, if the:
|
||||
* - client is NULL
|
||||
* - client is invalid (never called init, called fini, or invalid node)
|
||||
*
|
||||
* The returned string is only valid as long as the rcl_client_t is valid.
|
||||
* The value of the string may change if the service name changes, and therefore
|
||||
* copying the string is recommended if this is a concern.
|
||||
*
|
||||
* This function is not thread-safe, and copying the result is not thread-safe.
|
||||
*
|
||||
* \param[in] client pointer to the client
|
||||
* \return name string if successful, otherwise NULL
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
const char *
|
||||
rcl_client_get_service_name(const rcl_client_t * client);
|
||||
|
||||
/// Return the rcl client options.
|
||||
/* This function returns the client's internal options struct.
|
||||
* This function can fail, and therefore return NULL, if the:
|
||||
* - client is NULL
|
||||
* - client is invalid (never called init, called fini, or invalid node)
|
||||
*
|
||||
* The returned struct is only valid as long as the rcl_client_t is valid.
|
||||
* The values in the struct may change if the options of the client change,
|
||||
* and therefore copying the struct is recommended if this is a concern.
|
||||
*
|
||||
* This function is not thread-safe, and copying the result is not thread-safe.
|
||||
*
|
||||
* \param[in] client pointer to the client
|
||||
* \return options struct if successful, otherwise NULL
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
const rcl_client_options_t *
|
||||
rcl_client_get_options(const rcl_client_t * client);
|
||||
|
||||
/// Return the rmw client handle.
|
||||
/* The handle returned is a pointer to the internally held rmw handle.
|
||||
* This function can fail, and therefore return NULL, if the:
|
||||
* - client is NULL
|
||||
* - client is invalid (never called init, called fini, or invalid node)
|
||||
*
|
||||
* The returned handle is made invalid if the client is finalized or if
|
||||
* rcl_shutdown() is called.
|
||||
* The returned handle is not guaranteed to be valid for the life time of the
|
||||
* client as it may be finalized and recreated itself.
|
||||
* Therefore it is recommended to get the handle from the client using
|
||||
* this function each time it is needed and avoid use of the handle
|
||||
* concurrently with functions that might change it.
|
||||
*
|
||||
* \param[in] client pointer to the rcl client
|
||||
* \return rmw client handle if successful, otherwise NULL
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rmw_client_t *
|
||||
rcl_client_get_rmw_handle(const rcl_client_t * client);
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RCL__CLIENT_H_
|
|
@ -68,7 +68,7 @@ rcl_get_zero_initialized_guard_condition(void);
|
|||
* ret = rcl_guard_condition_fini(&guard_condition, &node);
|
||||
* // ... error handling for rcl_guard_condition_fini()
|
||||
* ret = rcl_node_fini(&node);
|
||||
* // ... error handling for rcl_deinitialize_node()
|
||||
* // ... error handling for rcl_node_fini()
|
||||
*
|
||||
* This function does allocate heap memory.
|
||||
* This function is not thread-safe.
|
||||
|
|
317
rcl/include/rcl/service.h
Normal file
317
rcl/include/rcl/service.h
Normal file
|
@ -0,0 +1,317 @@
|
|||
// 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__SERVICE_H_
|
||||
#define RCL__SERVICE_H_
|
||||
|
||||
#if __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "rosidl_generator_c/service_type_support.h"
|
||||
|
||||
#include "rcl/macros.h"
|
||||
#include "rcl/node.h"
|
||||
#include "rcl/visibility_control.h"
|
||||
|
||||
/// Internal rcl implementation struct.
|
||||
struct rcl_service_impl_t;
|
||||
|
||||
/// Handle for a rcl service.
|
||||
typedef struct rcl_service_t
|
||||
{
|
||||
struct rcl_service_impl_t * impl;
|
||||
} rcl_service_t;
|
||||
|
||||
/// Options available for a rcl service.
|
||||
typedef struct rcl_service_options_t
|
||||
{
|
||||
/// Middleware quality of service settings for the service.
|
||||
rmw_qos_profile_t qos;
|
||||
/// Custom allocator for the service, used for incidental allocations.
|
||||
/* For default behavior (malloc/free), see: rcl_get_default_allocator() */
|
||||
rcl_allocator_t allocator;
|
||||
} rcl_service_options_t;
|
||||
|
||||
/// Return a rcl_service_t struct with members set to NULL.
|
||||
/* Should be called to get a null rcl_service_t before passing to
|
||||
* rcl_initalize_service().
|
||||
* It's also possible to use calloc() instead of this if the rcl_service_t
|
||||
* is being allocated on the heap.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_service_t
|
||||
rcl_get_zero_initialized_service(void);
|
||||
|
||||
/// Initialize a rcl service.
|
||||
/* After calling this function on a rcl_service_t, it can be used to take
|
||||
* requests of the given type to the given topic using rcl_take_request().
|
||||
* It can also send a response to a request using rcl_send_response().
|
||||
*
|
||||
* The given rcl_node_t must be valid and the resulting rcl_service_t is
|
||||
* only valid as long as the given rcl_node_t remains valid.
|
||||
*
|
||||
* The rosidl_service_type_support_t is obtained on a per .srv type basis.
|
||||
* When the user defines a ROS service, code is generated which provides the
|
||||
* required rosidl_service_type_support_t object.
|
||||
* This object can be obtained using a language appropriate mechanism.
|
||||
* \TODO(wjwwood) probably should talk about this once and link to it instead
|
||||
* \TODO(jacquelinekay) reworded this for services with substitutions, should it refer to messages?
|
||||
* For C this macro can be used (using example_interfaces/AddTwoInts as an example):
|
||||
*
|
||||
* #include <rosidl_generator_c/service_type_support.h>
|
||||
* #include <example_interfaces/srv/add_two_ints.h>
|
||||
* rosidl_service_type_support_t * ts =
|
||||
* ROSIDL_GET_SERVICE_TYPE_SUPPORT(example_interfaces, AddTwoInts);
|
||||
*
|
||||
* For C++ a template function is used:
|
||||
*
|
||||
* #include <rosidl_generator_cpp/service_type_support.hpp>
|
||||
* #include <example_interfaces/srv/add_two_ints.h>
|
||||
* rosidl_service_type_support_t * ts = rosidl_generator_cpp::get_service_type_support_handle<
|
||||
* example_interfaces::srv::AddTwoInts>();
|
||||
*
|
||||
* The rosidl_service_type_support_t object contains service type specific
|
||||
* information used to send or take requests and responses.
|
||||
*
|
||||
* \TODO(wjwwood) update this once we've come up with an official scheme.
|
||||
* The service name must be a non-empty string which follows the service/topic naming
|
||||
* format.
|
||||
*
|
||||
* The options struct allows the user to set the quality of service settings as
|
||||
* well as a custom allocator which is used when initializing/finalizing the
|
||||
* client to allocate space for incidentals, e.g. the service name string.
|
||||
*
|
||||
* Expected usage (for C services):
|
||||
*
|
||||
* #include <rcl/rcl.h>
|
||||
* #include <rosidl_generator_c/service_type_support.h>
|
||||
* #include <example_interfaces/srv/add_two_ints.h>
|
||||
*
|
||||
* rcl_node_t node = rcl_get_zero_initialized_node();
|
||||
* rcl_node_options_t node_ops = rcl_node_get_default_options();
|
||||
* rcl_ret_t ret = rcl_node_init(&node, "node_name", &node_ops);
|
||||
* // ... error handling
|
||||
* rosidl_service_type_support_t * ts = ROSIDL_GET_SERVICE_TYPE_SUPPORT(
|
||||
* example_interfaces, AddTwoInts);
|
||||
* rcl_service_t service = rcl_get_zero_initialized_service();
|
||||
* rcl_service_options_t service_ops = rcl_service_get_default_options();
|
||||
* ret = rcl_service_init(&service, &node, ts, "add_two_ints", &service_ops);
|
||||
* // ... error handling, and on shutdown do finalization:
|
||||
* ret = rcl_service_fini(&service, &node);
|
||||
* // ... error handling for rcl_service_fini()
|
||||
* ret = rcl_node_fini(&node);
|
||||
* // ... error handling for rcl_node_fini()
|
||||
*
|
||||
* \param[out] service preallocated service structure
|
||||
* \param[in] node valid rcl node handle
|
||||
* \param[in] type_support type support object for the service's type
|
||||
* \param[in] service_name the name of the service
|
||||
* \param[in] options service options, including quality of service settings
|
||||
* \return RCL_RET_OK if service was initialized successfully, or
|
||||
* RCL_RET_INVALID_ARGUMENT if any arugments are invalid, or
|
||||
* RCL_RET_BAD_ALLOC if allocating memory failed, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_service_init(
|
||||
rcl_service_t * service,
|
||||
const rcl_node_t * node,
|
||||
const rosidl_service_type_support_t * type_support,
|
||||
const char * service_name,
|
||||
const rcl_service_options_t * options);
|
||||
|
||||
/// Finalize a rcl_service_t.
|
||||
/* After calling, the node will no longer listen for requests for this service.
|
||||
* (assuming this is the only service of this type in this node).
|
||||
*
|
||||
* After calling, calls to rcl_wait, rcl_take_request, and rcl_send_response will fail when using
|
||||
* this service.
|
||||
* Additionally rcl_wait will be interrupted if currently blocking.
|
||||
* However, the given node handle is still valid.
|
||||
*
|
||||
* This function is not thread-safe.
|
||||
*
|
||||
* \param[inout] service handle to the service to be deinitialized
|
||||
* \param[in] node handle to the node used to create the service
|
||||
* \return RCL_RET_OK if service was deinitialized successfully, or
|
||||
* RCL_RET_INVALID_ARGUMENT if any arugments are invalid, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_service_fini(rcl_service_t * service, rcl_node_t * node);
|
||||
|
||||
/// Return the default service options in a rcl_service_options_t.
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_service_options_t
|
||||
rcl_service_get_default_options(void);
|
||||
|
||||
/// Take a pending ROS request using a rcl service.
|
||||
/* It is the job of the caller to ensure that the type of the ros_request
|
||||
* argument and the type associate with the service, via the type
|
||||
* support, match.
|
||||
* Passing a different type to rcl_take produces undefined behavior and cannot
|
||||
* be checked by this function and therefore no deliberate error will occur.
|
||||
*
|
||||
* TODO(jacquelinekay) blocking of take?
|
||||
* TODO(jacquelinekay) pre-, during-, and post-conditions for message ownership?
|
||||
* TODO(jacquelinekay) is rcl_take_request thread-safe?
|
||||
* TODO(jacquelinekay) Should there be an rcl_request_id_t?
|
||||
*
|
||||
* The ros_request pointer should point to an already allocated ROS request message
|
||||
* struct of the correct type, into which the taken ROS request will be copied
|
||||
* if one is available.
|
||||
* If taken is false after calling, then the ROS request will be unmodified.
|
||||
*
|
||||
* If allocation is required when taking the request, e.g. if space needs to
|
||||
* be allocated for a dynamically sized array in the target message, then the
|
||||
* allocator given in the service options is used.
|
||||
*
|
||||
* request_header is a pointer to pre-allocated a rmw struct containing
|
||||
* meta-information about the request (e.g. the sequence number).
|
||||
*
|
||||
* \param[in] service the handle to the service from which to take
|
||||
* \param[inout] request_header ptr to the struct holding metadata about the request ID
|
||||
* \param[inout] ros_request type-erased ptr to an allocated ROS request message
|
||||
* \return RCL_RET_OK if the request was taken, or
|
||||
* RCL_RET_INVALID_ARGUMENT if any arugments are invalid, or
|
||||
* RCL_RET_SERVICE_INVALID if the service is invalid, or
|
||||
* RCL_RET_BAD_ALLOC if allocating memory failed, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_take_request(
|
||||
const rcl_service_t * service,
|
||||
rmw_request_id_t * request_header,
|
||||
void * ros_request);
|
||||
|
||||
/// Send a ROS response to a client using a service.
|
||||
/* It is the job of the caller to ensure that the type of the ros_response
|
||||
* parameter and the type associate with the service (via the type support)
|
||||
* match.
|
||||
* Passing a different type to send_response produces undefined behavior and cannot
|
||||
* be checked by this function and therefore no deliberate error will occur.
|
||||
*
|
||||
* send_response is an non-blocking call.
|
||||
*
|
||||
* The ROS response message given by the ros_response void pointer is always owned by the
|
||||
* calling code, but should remain constant during send_response.
|
||||
*
|
||||
e This function is thread safe so long as access to both the service and the
|
||||
* ros_response is synchronized.
|
||||
* That means that calling rcl_send_response from multiple threads is allowed, but
|
||||
* calling rcl_send_response at the same time as non-thread safe service functions
|
||||
* is not, e.g. calling rcl_send_response and rcl_service_fini concurrently
|
||||
* is not allowed.
|
||||
* Before calling rcl_send_response the message can change and after calling
|
||||
* rcl_send_response the message can change, but it cannot be changed during the
|
||||
* send_response call.
|
||||
* The same ros_response, however, can be passed to multiple calls of
|
||||
* rcl_send_response simultaneously, even if the services differ.
|
||||
* The ros_response is unmodified by rcl_send_response.
|
||||
*
|
||||
* \param[in] service handle to the service which will make the response
|
||||
* \param[inout] response_header ptr to the struct holding metadata about the request ID
|
||||
* \param[in] ros_response type-erased pointer to the ROS response message
|
||||
* \return RCL_RET_OK if the response was sent successfully, or
|
||||
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||
* RCL_RET_CLIENT_INVALID if the service is invalid, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_send_response(
|
||||
const rcl_service_t * service,
|
||||
rmw_request_id_t * response_header,
|
||||
void * ros_response);
|
||||
|
||||
/// Get the topic name for the service.
|
||||
/* This function returns the service's internal topic name string.
|
||||
* This function can fail, and therefore return NULL, if the:
|
||||
* - service is NULL
|
||||
* - service is invalid (never called init, called fini, or invalid)
|
||||
*
|
||||
* The returned string is only valid as long as the service is valid.
|
||||
* The value of the string may change if the topic name changes, and therefore
|
||||
* copying the string is recommended if this is a concern.
|
||||
*
|
||||
* This function is not thread-safe, and copying the result is not thread-safe.
|
||||
*
|
||||
* \param[in] service the pointer to the service
|
||||
* \return name string if successful, otherwise NULL
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
const char *
|
||||
rcl_service_get_service_name(const rcl_service_t * service);
|
||||
|
||||
/// Return the rcl service options.
|
||||
/* This function returns the service's internal options struct.
|
||||
* This function can fail, and therefore return NULL, if the:
|
||||
* - service is NULL
|
||||
* - service is invalid (never called init, called fini, or invalid)
|
||||
*
|
||||
* The returned struct is only valid as long as the service is valid.
|
||||
* The values in the struct may change if the service's options change,
|
||||
* and therefore copying the struct is recommended if this is a concern.
|
||||
*
|
||||
* This function is not thread-safe, and copying the result is not thread-safe.
|
||||
*
|
||||
* \param[in] service pointer to the service
|
||||
* \return options struct if successful, otherwise NULL
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
const rcl_service_options_t *
|
||||
rcl_service_get_options(const rcl_service_t * service);
|
||||
|
||||
/// Return the rmw service handle.
|
||||
/* The handle returned is a pointer to the internally held rmw handle.
|
||||
* This function can fail, and therefore return NULL, if the:
|
||||
* - service is NULL
|
||||
* - service is invalid (never called init, called fini, or invalid)
|
||||
*
|
||||
* The returned handle is made invalid if the service is finalized or if
|
||||
* rcl_shutdown() is called.
|
||||
* The returned handle is not guaranteed to be valid for the life time of the
|
||||
* service as it may be finalized and recreated itself.
|
||||
* Therefore it is recommended to get the handle from the service using
|
||||
* this function each time it is needed and avoid use of the handle
|
||||
* concurrently with functions that might change it.
|
||||
*
|
||||
* This function is not thread-safe.
|
||||
*
|
||||
* \param[in] service pointer to the rcl service
|
||||
* \return rmw service handle if successful, otherwise NULL
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rmw_service_t *
|
||||
rcl_service_get_rmw_handle(const rcl_service_t * service);
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RCL__SERVICE_H_
|
|
@ -114,7 +114,7 @@ rcl_get_zero_initialized_subscription(void);
|
|||
* ret = rcl_subscription_fini(&subscription, &node);
|
||||
* // ... error handling for rcl_subscription_fini()
|
||||
* ret = rcl_node_fini(&node);
|
||||
* // ... error handling for rcl_deinitialize_node()
|
||||
* // ... error handling for rcl_node_fini()
|
||||
*
|
||||
* This function is not thread-safe.
|
||||
*
|
||||
|
@ -138,7 +138,7 @@ rcl_subscription_init(
|
|||
const char * topic_name,
|
||||
const rcl_subscription_options_t * options);
|
||||
|
||||
/// Deinitialize a rcl_subscription_t.
|
||||
/// Finalize a rcl_subscription_t.
|
||||
/* After calling, the node will no longer be subscribed on this topic
|
||||
* (assuming this is the only subscription on this topic in this node).
|
||||
*
|
||||
|
@ -206,6 +206,8 @@ rcl_subscription_get_default_options(void);
|
|||
* RCL_RET_INVALID_ARGUMENT if any arugments are invalid, or
|
||||
* RCL_RET_SUBSCRIPTION_INVALID if the subscription is invalid, or
|
||||
* RCL_RET_BAD_ALLOC if allocating memory failed, or
|
||||
* RCL_RET_SUBSCRIPTION_TAKE_FAILED if take failed but no error
|
||||
* occurred in the middleware, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
|
@ -214,7 +216,6 @@ rcl_ret_t
|
|||
rcl_take(
|
||||
const rcl_subscription_t * subscription,
|
||||
void * ros_message,
|
||||
bool * taken,
|
||||
rmw_message_info_t * message_info);
|
||||
|
||||
/// Get the topic name for the subscription.
|
||||
|
|
|
@ -32,8 +32,13 @@ typedef rmw_ret_t rcl_ret_t;
|
|||
#define RCL_RET_PUBLISHER_INVALID 300
|
||||
// rcl subscription specific ret codes in 4XX
|
||||
#define RCL_RET_SUBSCRIPTION_INVALID 400
|
||||
#define RCL_RET_SUBSCRIPTION_TAKE_FAILED 501
|
||||
// rcl service client specific ret codes in 5XX
|
||||
#define RCL_RET_CLIENT_INVALID 500
|
||||
#define RCL_RET_CLIENT_TAKE_FAILED 501
|
||||
// rcl service server specific ret codes in 6XX
|
||||
#define RCL_RET_SERVICE_INVALID 600
|
||||
#define RCL_RET_SERVICE_TAKE_FAILED 601
|
||||
// rcl guard condition specific ret codes in 7XX
|
||||
// rcl timer specific ret codes in 8XX
|
||||
#define RCL_RET_TIMER_INVALID 800
|
||||
|
|
|
@ -23,8 +23,10 @@ extern "C"
|
|||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "rcl/client.h"
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/macros.h"
|
||||
#include "rcl/service.h"
|
||||
#include "rcl/subscription.h"
|
||||
#include "rcl/timer.h"
|
||||
#include "rcl/types.h"
|
||||
|
@ -44,6 +46,12 @@ typedef struct rcl_wait_set_t
|
|||
/// Storage for timer pointers.
|
||||
const rcl_timer_t ** timers;
|
||||
size_t size_of_timers;
|
||||
/// Storage for client pointers.
|
||||
const rcl_client_t ** clients;
|
||||
size_t size_of_clients;
|
||||
/// Storage for service pointers.
|
||||
const rcl_service_t ** services;
|
||||
size_t size_of_services;
|
||||
/// Implementation specific storage.
|
||||
struct rcl_wait_set_impl_t * impl;
|
||||
} rcl_wait_set_t;
|
||||
|
@ -89,6 +97,8 @@ rcl_get_zero_initialized_wait_set(void);
|
|||
* \param[in] number_of_subscriptions non-zero size of the subscriptions set
|
||||
* \param[in] number_of_guard_conditions non-zero size of the guard conditions set
|
||||
* \param[in] number_of_timers non-zero size of the timers set
|
||||
* \param[in] number_of_clients non-zero size of the clients set
|
||||
* \param[in] number_of_services non-zero size of the services set
|
||||
* \param[in] allocator the allocator to use when allocating space in the sets
|
||||
* \return RCL_RET_OK if the wait set is initialized successfully, or
|
||||
* RCL_RET_ALREADY_INIT if the wait set is not zero initialized, or
|
||||
|
@ -104,6 +114,8 @@ rcl_wait_set_init(
|
|||
size_t number_of_subscriptions,
|
||||
size_t number_of_guard_conditions,
|
||||
size_t number_of_timers,
|
||||
size_t number_of_clients,
|
||||
size_t number_of_services,
|
||||
rcl_allocator_t allocator);
|
||||
|
||||
/// Finalize a rcl wait set.
|
||||
|
@ -278,6 +290,150 @@ RCL_WARN_UNUSED
|
|||
rcl_ret_t
|
||||
rcl_wait_set_resize_timers(rcl_wait_set_t * wait_set, size_t size);
|
||||
|
||||
/// Store a pointer to the given client in the next empty spot in the set.
|
||||
/* This function does not guarantee that the client is not already in the
|
||||
* wait set.
|
||||
*
|
||||
* This function is not thread-safe.
|
||||
* This function is lock-free.
|
||||
*
|
||||
* \param[inout] wait_set struct in which the client is to be stored
|
||||
* \param[in] client the client to be added to the wait set
|
||||
* \return RCL_RET_OK if added successfully, or
|
||||
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||
* RCL_RET_WAIT_SET_INVALID if the wait set is zero initialized, or
|
||||
* RCL_RET_WAIT_SET_FULL if the client set is full, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_wait_set_add_client(
|
||||
rcl_wait_set_t * wait_set,
|
||||
const rcl_client_t * client);
|
||||
|
||||
/// Remove (sets to NULL) the clients in the wait set.
|
||||
/* This function should be used after passing using rcl_wait, but before
|
||||
* adding new clients to the set.
|
||||
*
|
||||
* Calling this on an uninitialized (zero initialized) wait set will fail.
|
||||
*
|
||||
* This function is not thread-safe.
|
||||
* This function is lock-free.
|
||||
*
|
||||
* \param[inout] wait_set struct to have its clients cleared
|
||||
* \return RCL_RET_OK if cleared successfully, or
|
||||
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||
* RCL_RET_WAIT_SET_INVALID if the wait set is zero initialized, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_wait_set_clear_clients(rcl_wait_set_t * wait_set);
|
||||
|
||||
/// Reallocate space for the clients in the wait set.
|
||||
/* This function will deallocate and reallocate the memory for the
|
||||
* clients set.
|
||||
*
|
||||
* A size of 0 will just deallocate the memory and assign NULL to the array.
|
||||
*
|
||||
* Allocation and deallocation is done with the allocator given during the
|
||||
* wait set's initialization.
|
||||
*
|
||||
* After calling this function all values in the set will be set to NULL,
|
||||
* effectively the same as calling rcl_wait_set_clear_clients().
|
||||
*
|
||||
* If the requested size matches the current size, no allocation will be done.
|
||||
*
|
||||
* This can be called on an uninitialized (zero initialized) wait set.
|
||||
*
|
||||
* This function is not thread-safe.
|
||||
*
|
||||
* \param[inout] wait_set struct to have its clients cleared
|
||||
* \param[in] size a size for the new set
|
||||
* \return RCL_RET_OK if resized successfully, 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_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_wait_set_resize_clients(rcl_wait_set_t * wait_set, size_t size);
|
||||
|
||||
/// Store a pointer to the given service in the next empty spot in the set.
|
||||
/* This function does not guarantee that the service is not already in the
|
||||
* wait set.
|
||||
*
|
||||
* This function is not thread-safe.
|
||||
* This function is lock-free.
|
||||
*
|
||||
* \param[inout] wait_set struct in which the service is to be stored
|
||||
* \param[in] service the service to be added to the wait set
|
||||
* \return RCL_RET_OK if added successfully, or
|
||||
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||
* RCL_RET_WAIT_SET_INVALID if the wait set is zero initialized, or
|
||||
* RCL_RET_WAIT_SET_FULL if the service set is full, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_wait_set_add_service(
|
||||
rcl_wait_set_t * wait_set,
|
||||
const rcl_service_t * service);
|
||||
|
||||
/// Remove (sets to NULL) the services in the wait set.
|
||||
/* This function should be used after passing using rcl_wait, but before
|
||||
* adding new services to the set.
|
||||
*
|
||||
* Calling this on an uninitialized (zero initialized) wait set will fail.
|
||||
*
|
||||
* This function is not thread-safe.
|
||||
* This function is lock-free.
|
||||
*
|
||||
* \param[inout] wait_set struct to have its services cleared
|
||||
* \return RCL_RET_OK if cleared successfully, or
|
||||
* RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or
|
||||
* RCL_RET_WAIT_SET_INVALID if the wait set is zero initialized, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_wait_set_clear_services(rcl_wait_set_t * wait_set);
|
||||
|
||||
/// Reallocate space for the services in the wait set.
|
||||
/* This function will deallocate and reallocate the memory for the
|
||||
* services set.
|
||||
*
|
||||
* A size of 0 will just deallocate the memory and assign NULL to the array.
|
||||
*
|
||||
* Allocation and deallocation is done with the allocator given during the
|
||||
* wait set's initialization.
|
||||
*
|
||||
* After calling this function all values in the set will be set to NULL,
|
||||
* effectively the same as calling rcl_wait_set_clear_services().
|
||||
*
|
||||
* If the requested size matches the current size, no allocation will be done.
|
||||
*
|
||||
* This can be called on an uninitialized (zero initialized) wait set.
|
||||
*
|
||||
* This function is not thread-safe.
|
||||
*
|
||||
* \param[inout] wait_set struct to have its services cleared
|
||||
* \param[in] size a size for the new set
|
||||
* \return RCL_RET_OK if resized successfully, 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_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_wait_set_resize_services(rcl_wait_set_t * wait_set, size_t size);
|
||||
|
||||
/// Block until the wait set is ready or until the timeout has been exceeded.
|
||||
/* This function will collect the items in the rcl_wait_set_t and pass them
|
||||
* to the underlying rmw_wait function.
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
<test_depend>rmw</test_depend>
|
||||
<test_depend>example_interfaces</test_depend>
|
||||
<test_depend>std_msgs</test_depend>
|
||||
|
||||
<export>
|
||||
|
|
208
rcl/src/rcl/client.c
Normal file
208
rcl/src/rcl/client.c
Normal file
|
@ -0,0 +1,208 @@
|
|||
// 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/client.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
#include "./common.h"
|
||||
#include "./stdatomic_helper.h"
|
||||
|
||||
typedef struct rcl_client_impl_t
|
||||
{
|
||||
rcl_client_options_t options;
|
||||
rmw_client_t * rmw_handle;
|
||||
atomic_int_least64_t sequence_number;
|
||||
} rcl_client_impl_t;
|
||||
|
||||
rcl_client_t
|
||||
rcl_get_zero_initialized_client()
|
||||
{
|
||||
static rcl_client_t null_client = {0};
|
||||
return null_client;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_client_init(
|
||||
rcl_client_t * client,
|
||||
const rcl_node_t * node,
|
||||
const rosidl_service_type_support_t * type_support,
|
||||
const char * service_name,
|
||||
const rcl_client_options_t * options)
|
||||
{
|
||||
rcl_ret_t fail_ret = RCL_RET_ERROR;
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
|
||||
if (!node->impl) {
|
||||
RCL_SET_ERROR_MSG("invalid node");
|
||||
return RCL_RET_NODE_INVALID;
|
||||
}
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(type_support, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(options, RCL_RET_INVALID_ARGUMENT);
|
||||
if (client->impl) {
|
||||
RCL_SET_ERROR_MSG("client already initialized, or memory was unintialized");
|
||||
return RCL_RET_ALREADY_INIT;
|
||||
}
|
||||
const rcl_allocator_t * allocator = &options->allocator;
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
allocator->allocate, "allocate not set", return RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
allocator->deallocate, "deallocate not set", return RCL_RET_INVALID_ARGUMENT);
|
||||
// Allocate space for the implementation struct.
|
||||
client->impl = (rcl_client_impl_t *)allocator->allocate(
|
||||
sizeof(rcl_client_impl_t), allocator->state);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
client->impl, "allocating memory failed", return RCL_RET_BAD_ALLOC);
|
||||
// Fill out implementation struct.
|
||||
// rmw handle (create rmw client)
|
||||
// TODO(wjwwood): pass along the allocator to rmw when it supports it
|
||||
client->impl->rmw_handle = rmw_create_client(
|
||||
rcl_node_get_rmw_handle(node),
|
||||
type_support,
|
||||
service_name,
|
||||
&rmw_qos_profile_default);
|
||||
if (!client->impl->rmw_handle) {
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
goto fail;
|
||||
}
|
||||
// options
|
||||
client->impl->options = *options;
|
||||
return RCL_RET_OK;
|
||||
fail:
|
||||
if (client->impl) {
|
||||
allocator->deallocate(client->impl, allocator->state);
|
||||
}
|
||||
return fail_ret;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_client_fini(rcl_client_t * client, rcl_node_t * node)
|
||||
{
|
||||
(void)node;
|
||||
rcl_ret_t result = RCL_RET_OK;
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
|
||||
if (client->impl) {
|
||||
rmw_ret_t ret =
|
||||
rmw_destroy_client(client->impl->rmw_handle);
|
||||
if (ret != RMW_RET_OK) {
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
result = RCL_RET_ERROR;
|
||||
}
|
||||
rcl_allocator_t allocator = client->impl->options.allocator;
|
||||
allocator.deallocate(client->impl, allocator.state);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
rcl_client_options_t
|
||||
rcl_client_get_default_options()
|
||||
{
|
||||
static rcl_client_options_t default_options;
|
||||
// Must set the allocator and qos after because they are not a compile time constant.
|
||||
default_options.qos = rmw_qos_profile_default;
|
||||
default_options.allocator = rcl_get_default_allocator();
|
||||
return default_options;
|
||||
}
|
||||
|
||||
const char *
|
||||
rcl_client_get_service_name(const rcl_client_t * client)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(client, NULL);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
client->impl, "client is invalid", return NULL);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
client->impl->rmw_handle, "client is invalid", return NULL);
|
||||
return client->impl->rmw_handle->service_name;
|
||||
}
|
||||
|
||||
const rcl_client_options_t *
|
||||
rcl_client_get_options(const rcl_client_t * client)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(client, NULL);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
client->impl, "client is invalid", return NULL);
|
||||
return &client->impl->options;
|
||||
}
|
||||
|
||||
rmw_client_t *
|
||||
rcl_client_get_rmw_handle(const rcl_client_t * client)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(client, NULL);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
client->impl, "client is invalid", return NULL);
|
||||
return client->impl->rmw_handle;
|
||||
}
|
||||
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_send_request(const rcl_client_t * client, const void * ros_request, int64_t * sequence_number)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(ros_request, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(sequence_number, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
client->impl, "client is invalid", return RCL_RET_INVALID_ARGUMENT);
|
||||
*sequence_number = rcl_atomic_load_int64_t(&client->impl->sequence_number);
|
||||
if (rmw_send_request(
|
||||
client->impl->rmw_handle, ros_request, sequence_number) != RMW_RET_OK)
|
||||
{
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
rcl_atomic_exchange_int64_t(&client->impl->sequence_number, *sequence_number);
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_take_response(
|
||||
const rcl_client_t * client,
|
||||
rmw_request_id_t * request_header,
|
||||
void * ros_response)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
client->impl, "client is invalid", return RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(request_header, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(ros_response, RCL_RET_INVALID_ARGUMENT);
|
||||
|
||||
bool taken = false;
|
||||
if (rmw_take_response(
|
||||
client->impl->rmw_handle, request_header, ros_response, &taken) != RMW_RET_OK)
|
||||
{
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
if (!taken) {
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
return RCL_RET_CLIENT_TAKE_FAILED;
|
||||
}
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
202
rcl/src/rcl/service.c
Normal file
202
rcl/src/rcl/service.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
// 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/service.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "./common.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
typedef struct rcl_service_impl_t
|
||||
{
|
||||
rcl_service_options_t options;
|
||||
rmw_service_t * rmw_handle;
|
||||
} rcl_service_impl_t;
|
||||
|
||||
rcl_service_t
|
||||
rcl_get_zero_initialized_service()
|
||||
{
|
||||
static rcl_service_t null_service = {0};
|
||||
return null_service;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_service_init(
|
||||
rcl_service_t * service,
|
||||
const rcl_node_t * node,
|
||||
const rosidl_service_type_support_t * type_support,
|
||||
const char * service_name,
|
||||
const rcl_service_options_t * options)
|
||||
{
|
||||
rcl_ret_t fail_ret = RCL_RET_ERROR;
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(service, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
|
||||
if (!node->impl) {
|
||||
RCL_SET_ERROR_MSG("invalid node");
|
||||
return RCL_RET_NODE_INVALID;
|
||||
}
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(type_support, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(options, RCL_RET_INVALID_ARGUMENT);
|
||||
if (service->impl) {
|
||||
RCL_SET_ERROR_MSG("service already initialized, or memory was unintialized");
|
||||
return RCL_RET_ALREADY_INIT;
|
||||
}
|
||||
const rcl_allocator_t * allocator = &options->allocator;
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
allocator->allocate, "allocate not set", return RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
allocator->deallocate, "deallocate not set", return RCL_RET_INVALID_ARGUMENT);
|
||||
// Allocate space for the implementation struct.
|
||||
service->impl = (rcl_service_impl_t *)allocator->allocate(
|
||||
sizeof(rcl_service_impl_t), allocator->state);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
service->impl, "allocating memory failed", return RCL_RET_BAD_ALLOC);
|
||||
// Fill out implementation struct.
|
||||
// rmw handle (create rmw service)
|
||||
// TODO(wjwwood): pass along the allocator to rmw when it supports it
|
||||
service->impl->rmw_handle = rmw_create_service(
|
||||
rcl_node_get_rmw_handle(node),
|
||||
type_support,
|
||||
service_name,
|
||||
&rmw_qos_profile_default);
|
||||
if (!service->impl->rmw_handle) {
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
goto fail;
|
||||
}
|
||||
// options
|
||||
service->impl->options = *options;
|
||||
return RCL_RET_OK;
|
||||
fail:
|
||||
if (service->impl) {
|
||||
allocator->deallocate(service->impl, allocator->state);
|
||||
}
|
||||
return fail_ret;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_service_fini(rcl_service_t * service, rcl_node_t * node)
|
||||
{
|
||||
rcl_ret_t result = RCL_RET_OK;
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(service, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
|
||||
if (service->impl) {
|
||||
rmw_ret_t ret =
|
||||
rmw_destroy_service(service->impl->rmw_handle);
|
||||
if (ret != RMW_RET_OK) {
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
result = RCL_RET_ERROR;
|
||||
}
|
||||
rcl_allocator_t allocator = service->impl->options.allocator;
|
||||
allocator.deallocate(service->impl, allocator.state);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
rcl_service_options_t
|
||||
rcl_service_get_default_options()
|
||||
{
|
||||
static rcl_service_options_t default_options;
|
||||
// Must set the allocator and qos after because they are not a compile time constant.
|
||||
default_options.qos = rmw_qos_profile_default;
|
||||
default_options.allocator = rcl_get_default_allocator();
|
||||
return default_options;
|
||||
}
|
||||
|
||||
const char *
|
||||
rcl_service_get_service_name(const rcl_service_t * service)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(service, NULL);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
service->impl, "service is invalid", return NULL);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
service->impl->rmw_handle, "service is invalid", return NULL);
|
||||
return service->impl->rmw_handle->service_name;
|
||||
}
|
||||
|
||||
const rcl_service_options_t *
|
||||
rcl_service_get_options(const rcl_service_t * service)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(service, NULL);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
service->impl, "service is invalid", return NULL);
|
||||
return &service->impl->options;
|
||||
}
|
||||
|
||||
rmw_service_t *
|
||||
rcl_service_get_rmw_handle(const rcl_service_t * service)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(service, NULL);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
service->impl, "service is invalid", return NULL);
|
||||
return service->impl->rmw_handle;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_take_request(
|
||||
const rcl_service_t * service,
|
||||
rmw_request_id_t * request_header,
|
||||
void * ros_request)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(service, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
service->impl, "service is invalid", return RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(request_header, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(ros_request, RCL_RET_INVALID_ARGUMENT);
|
||||
|
||||
bool taken = false;
|
||||
if (rmw_take_request(
|
||||
service->impl->rmw_handle, request_header, ros_request, &taken) != RMW_RET_OK)
|
||||
{
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
if (!taken) {
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
return RCL_RET_CLIENT_TAKE_FAILED;
|
||||
}
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_send_response(
|
||||
const rcl_service_t * service,
|
||||
rmw_request_id_t * request_header,
|
||||
void * ros_response)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(service, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
service->impl, "service is invalid", return RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(request_header, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(ros_response, RCL_RET_INVALID_ARGUMENT);
|
||||
|
||||
if (rmw_send_response(
|
||||
service->impl->rmw_handle, request_header, ros_response) != RMW_RET_OK)
|
||||
{
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -61,6 +61,14 @@ rcl_atomic_load_bool(atomic_bool * a_bool)
|
|||
return result;
|
||||
}
|
||||
|
||||
static inline int64_t
|
||||
rcl_atomic_load_int64_t(atomic_int_least64_t * a_int64_t)
|
||||
{
|
||||
int64_t result = 0;
|
||||
rcl_atomic_load(a_int64_t, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
rcl_atomic_load_uint64_t(atomic_uint_least64_t * a_uint64_t)
|
||||
{
|
||||
|
@ -94,6 +102,14 @@ rcl_atomic_exchange_bool(atomic_bool * a_bool, bool desired)
|
|||
return result;
|
||||
}
|
||||
|
||||
static inline int64_t
|
||||
rcl_atomic_exchange_int64_t(atomic_int_least64_t * a_int64_t, int64_t desired)
|
||||
{
|
||||
int64_t result;
|
||||
rcl_atomic_exchange(a_int64_t, result, desired);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
rcl_atomic_exchange_uint64_t(atomic_uint_least64_t * a_uint64_t, uint64_t desired)
|
||||
{
|
||||
|
|
|
@ -121,12 +121,10 @@ rcl_ret_t
|
|||
rcl_take(
|
||||
const rcl_subscription_t * subscription,
|
||||
void * ros_message,
|
||||
bool * taken,
|
||||
rmw_message_info_t * message_info)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(subscription, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(ros_message, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(taken, RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
subscription->impl, "subscription is invalid", return RCL_RET_SUBSCRIPTION_INVALID);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
|
@ -136,12 +134,17 @@ rcl_take(
|
|||
rmw_message_info_t dummy_message_info;
|
||||
rmw_message_info_t * message_info_local = message_info ? message_info : &dummy_message_info;
|
||||
// Call rmw_take_with_info.
|
||||
bool taken = false;
|
||||
rmw_ret_t ret =
|
||||
rmw_take_with_info(subscription->impl->rmw_handle, ros_message, taken, message_info_local);
|
||||
rmw_take_with_info(subscription->impl->rmw_handle, ros_message, &taken, message_info_local);
|
||||
if (ret != RMW_RET_OK) {
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
if (!taken) {
|
||||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
|
||||
return RCL_RET_SUBSCRIPTION_TAKE_FAILED;
|
||||
}
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,10 @@ typedef struct rcl_wait_set_impl_t
|
|||
rmw_subscriptions_t rmw_subscriptions;
|
||||
size_t guard_condition_index;
|
||||
rmw_guard_conditions_t rmw_guard_conditions;
|
||||
size_t client_index;
|
||||
rmw_clients_t rmw_clients;
|
||||
size_t service_index;
|
||||
rmw_services_t rmw_services;
|
||||
rmw_waitset_t * rmw_waitset;
|
||||
size_t timer_index;
|
||||
rcl_allocator_t allocator;
|
||||
|
@ -48,6 +52,10 @@ rcl_get_zero_initialized_wait_set()
|
|||
.size_of_subscriptions = 0,
|
||||
.guard_conditions = NULL,
|
||||
.size_of_guard_conditions = 0,
|
||||
.clients = NULL,
|
||||
.size_of_clients = 0,
|
||||
.services = NULL,
|
||||
.size_of_services = 0,
|
||||
.timers = NULL,
|
||||
.size_of_timers = 0,
|
||||
.impl = NULL,
|
||||
|
@ -79,6 +87,16 @@ __wait_set_clean_up(rcl_wait_set_t * wait_set, rcl_allocator_t allocator)
|
|||
(void)ret; // NO LINT
|
||||
assert(ret == RCL_RET_OK); // Defensive, shouldn't fail with size 0.
|
||||
}
|
||||
if (wait_set->clients) {
|
||||
rcl_ret_t ret = rcl_wait_set_resize_clients(wait_set, 0);
|
||||
(void)ret; // NO LINT
|
||||
assert(ret == RCL_RET_OK); // Defensive, shouldn't fail with size 0.
|
||||
}
|
||||
if (wait_set->services) {
|
||||
rcl_ret_t ret = rcl_wait_set_resize_services(wait_set, 0);
|
||||
(void)ret; // NO LINT
|
||||
assert(ret == RCL_RET_OK); // Defensive, shouldn't fail with size 0.
|
||||
}
|
||||
if (wait_set->impl) {
|
||||
allocator.deallocate(wait_set->impl, allocator.state);
|
||||
wait_set->impl = NULL;
|
||||
|
@ -91,6 +109,8 @@ rcl_wait_set_init(
|
|||
size_t number_of_subscriptions,
|
||||
size_t number_of_guard_conditions,
|
||||
size_t number_of_timers,
|
||||
size_t number_of_clients,
|
||||
size_t number_of_services,
|
||||
rcl_allocator_t allocator)
|
||||
{
|
||||
rcl_ret_t fail_ret = RCL_RET_ERROR;
|
||||
|
@ -115,11 +135,18 @@ rcl_wait_set_init(
|
|||
wait_set->impl->rmw_subscriptions.subscriber_count = 0;
|
||||
wait_set->impl->rmw_guard_conditions.guard_conditions = NULL;
|
||||
wait_set->impl->rmw_guard_conditions.guard_condition_count = 0;
|
||||
wait_set->impl->rmw_clients.clients = NULL;
|
||||
wait_set->impl->rmw_clients.client_count = 0;
|
||||
wait_set->impl->rmw_services.services = NULL;
|
||||
wait_set->impl->rmw_services.service_count = 0;
|
||||
|
||||
static rmw_guard_conditions_t fixed_guard_conditions;
|
||||
fixed_guard_conditions.guard_conditions = NULL;
|
||||
fixed_guard_conditions.guard_condition_count = 0;
|
||||
wait_set->impl->rmw_waitset = rmw_create_waitset(
|
||||
&fixed_guard_conditions, 2 * number_of_subscriptions + number_of_guard_conditions);
|
||||
&fixed_guard_conditions,
|
||||
2 * number_of_subscriptions + number_of_guard_conditions + number_of_clients +
|
||||
number_of_services);
|
||||
if (!wait_set->impl->rmw_waitset) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -156,6 +183,26 @@ rcl_wait_set_init(
|
|||
fail_ret = ret;
|
||||
goto fail;
|
||||
}
|
||||
// Initialize client space.
|
||||
ret = rcl_wait_set_resize_clients(wait_set, number_of_clients);
|
||||
if (ret != RCL_RET_OK) {
|
||||
fail_ret = ret;
|
||||
goto fail;
|
||||
}
|
||||
if ((ret = rcl_wait_set_clear_clients(wait_set)) != RCL_RET_OK) {
|
||||
fail_ret = ret;
|
||||
goto fail;
|
||||
}
|
||||
// Initialize service space.
|
||||
ret = rcl_wait_set_resize_services(wait_set, number_of_services);
|
||||
if (ret != RCL_RET_OK) {
|
||||
fail_ret = ret;
|
||||
goto fail;
|
||||
}
|
||||
if ((ret = rcl_wait_set_clear_services(wait_set)) != RCL_RET_OK) {
|
||||
fail_ret = ret;
|
||||
goto fail;
|
||||
}
|
||||
return RCL_RET_OK;
|
||||
fail:
|
||||
if (__wait_set_is_valid(wait_set)) {
|
||||
|
@ -236,7 +283,7 @@ rcl_wait_set_get_allocator(const rcl_wait_set_t * wait_set, rcl_allocator_t * al
|
|||
memset( \
|
||||
wait_set->impl->RMWStorage, \
|
||||
0, \
|
||||
sizeof(rmw_subscription_t *) * wait_set->impl->RMWCount);
|
||||
sizeof(rmw_ ## Type ## _t *) * wait_set->impl->RMWCount);
|
||||
|
||||
#define SET_RESIZE(Type, ExtraDealloc, ExtraRealloc) \
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(wait_set, RCL_RET_INVALID_ARGUMENT); \
|
||||
|
@ -373,6 +420,70 @@ rcl_wait_set_resize_timers(rcl_wait_set_t * wait_set, size_t size)
|
|||
SET_RESIZE(timer,;,;) // NOLINT
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_wait_set_add_client(
|
||||
rcl_wait_set_t * wait_set,
|
||||
const rcl_client_t * client)
|
||||
{
|
||||
SET_ADD(client)
|
||||
SET_ADD_RMW(client, rmw_clients.clients)
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_wait_set_clear_clients(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
SET_CLEAR(client)
|
||||
SET_CLEAR_RMW(
|
||||
clients,
|
||||
rmw_clients.clients,
|
||||
rmw_clients.client_count)
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_wait_set_resize_clients(rcl_wait_set_t * wait_set, size_t size)
|
||||
{
|
||||
SET_RESIZE(client,
|
||||
SET_RESIZE_RMW_DEALLOC(
|
||||
rmw_clients.clients, rmw_clients.client_count),
|
||||
SET_RESIZE_RMW_REALLOC(
|
||||
client, rmw_clients.clients, rmw_clients.client_count)
|
||||
)
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_wait_set_add_service(
|
||||
rcl_wait_set_t * wait_set,
|
||||
const rcl_service_t * service)
|
||||
{
|
||||
SET_ADD(service)
|
||||
SET_ADD_RMW(service, rmw_services.services)
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_wait_set_clear_services(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
SET_CLEAR(service)
|
||||
SET_CLEAR_RMW(
|
||||
services,
|
||||
rmw_services.services,
|
||||
rmw_services.service_count)
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_wait_set_resize_services(rcl_wait_set_t * wait_set, size_t size)
|
||||
{
|
||||
SET_RESIZE(service,
|
||||
SET_RESIZE_RMW_DEALLOC(
|
||||
rmw_services.services, rmw_services.service_count),
|
||||
SET_RESIZE_RMW_REALLOC(
|
||||
service, rmw_services.services, rmw_services.service_count)
|
||||
)
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_wait(rcl_wait_set_t * wait_set, int64_t timeout)
|
||||
{
|
||||
|
@ -382,13 +493,12 @@ rcl_wait(rcl_wait_set_t * wait_set, int64_t timeout)
|
|||
RCL_SET_ERROR_MSG("wait set is invalid");
|
||||
return RCL_RET_WAIT_SET_INVALID;
|
||||
}
|
||||
if (wait_set->size_of_subscriptions == 0 && wait_set->size_of_guard_conditions == 0) {
|
||||
if (wait_set->size_of_subscriptions == 0 && wait_set->size_of_guard_conditions == 0 &&
|
||||
wait_set->size_of_clients == 0 && wait_set->size_of_services == 0)
|
||||
{
|
||||
RCL_SET_ERROR_MSG("wait set is empty");
|
||||
return RCL_RET_WAIT_SET_EMPTY;
|
||||
}
|
||||
// Create dummy sets for currently unsupported wait-ables.
|
||||
static rmw_services_t dummy_services = {0, NULL};
|
||||
static rmw_clients_t dummy_clients = {0, NULL};
|
||||
// Calculate the timeout argument.
|
||||
rmw_time_t * timeout_argument;
|
||||
rmw_time_t temporary_timeout_storage;
|
||||
|
@ -432,8 +542,8 @@ rcl_wait(rcl_wait_set_t * wait_set, int64_t timeout)
|
|||
rmw_ret_t ret = rmw_wait(
|
||||
&wait_set->impl->rmw_subscriptions,
|
||||
&wait_set->impl->rmw_guard_conditions,
|
||||
&dummy_services,
|
||||
&dummy_clients,
|
||||
&wait_set->impl->rmw_services,
|
||||
&wait_set->impl->rmw_clients,
|
||||
wait_set->impl->rmw_waitset,
|
||||
timeout_argument);
|
||||
// Check for timeout.
|
||||
|
@ -446,6 +556,10 @@ rcl_wait(rcl_wait_set_t * wait_set, int64_t timeout)
|
|||
assert(rcl_ret == RCL_RET_OK); // Defensive, shouldn't fail with valid wait_set.
|
||||
rcl_ret = rcl_wait_set_clear_guard_conditions(wait_set);
|
||||
assert(rcl_ret == RCL_RET_OK); // Defensive, shouldn't fail with valid wait_set.
|
||||
rcl_ret = rcl_wait_set_clear_services(wait_set);
|
||||
assert(rcl_ret == RCL_RET_OK); // Defensive, shouldn't fail with valid wait_set.
|
||||
rcl_ret = rcl_wait_set_clear_clients(wait_set);
|
||||
assert(rcl_ret == RCL_RET_OK); // Defensive, shouldn't fail with valid wait_set.
|
||||
return RCL_RET_TIMEOUT;
|
||||
}
|
||||
// Check for error.
|
||||
|
@ -479,6 +593,20 @@ rcl_wait(rcl_wait_set_t * wait_set, int64_t timeout)
|
|||
wait_set->guard_conditions[i] = NULL;
|
||||
}
|
||||
}
|
||||
// Set corresponding rcl client handles NULL.
|
||||
for (i = 0; i < wait_set->size_of_clients; ++i) {
|
||||
assert(i < wait_set->impl->rmw_clients.client_count); // Defensive.
|
||||
if (!wait_set->impl->rmw_clients.clients[i]) {
|
||||
wait_set->clients[i] = NULL;
|
||||
}
|
||||
}
|
||||
// Set corresponding rcl service handles NULL.
|
||||
for (i = 0; i < wait_set->size_of_services; ++i) {
|
||||
assert(i < wait_set->impl->rmw_services.service_count); // Defensive.
|
||||
if (!wait_set->impl->rmw_services.services[i]) {
|
||||
wait_set->services[i] = NULL;
|
||||
}
|
||||
}
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
find_package(ament_cmake_gtest REQUIRED)
|
||||
find_package(example_interfaces REQUIRED)
|
||||
find_package(std_msgs REQUIRED)
|
||||
|
||||
include(rcl_add_custom_gtest.cmake)
|
||||
|
@ -33,6 +34,14 @@ function(test_target_function)
|
|||
AMENT_DEPENDENCIES ${rmw_implementation}
|
||||
)
|
||||
|
||||
rcl_add_custom_gtest(test_client${target_suffix}
|
||||
SRCS rcl/test_client.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"
|
||||
)
|
||||
|
||||
rcl_add_custom_gtest(test_time${target_suffix}
|
||||
SRCS rcl/test_time.cpp
|
||||
ENV ${extra_test_env}
|
||||
|
@ -76,6 +85,14 @@ function(test_target_function)
|
|||
AMENT_DEPENDENCIES ${rmw_implementation} "std_msgs"
|
||||
)
|
||||
|
||||
rcl_add_custom_gtest(test_service${target_suffix}
|
||||
SRCS rcl/test_service.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"
|
||||
)
|
||||
|
||||
rcl_add_custom_gtest(test_subscription${target_suffix}
|
||||
SRCS rcl/test_subscription.cpp
|
||||
ENV ${extra_test_env}
|
||||
|
|
188
rcl/test/rcl/test_client.cpp
Normal file
188
rcl/test/rcl/test_client.cpp
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.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "rcl/client.h"
|
||||
|
||||
#include "rcl/rcl.h"
|
||||
|
||||
#include "example_interfaces/srv/add_two_ints.h"
|
||||
#include "rosidl_generator_c/string_functions.h"
|
||||
|
||||
#include "../memory_tools/memory_tools.hpp"
|
||||
#include "../scope_exit.hpp"
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
class TestClientFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
rcl_node_t * node_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->node_ptr = new rcl_node_t;
|
||||
*this->node_ptr = rcl_get_zero_initialized_node();
|
||||
const char * name = "node_name";
|
||||
rcl_node_options_t node_options = rcl_node_get_default_options();
|
||||
ret = rcl_node_init(this->node_ptr, name, &node_options);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
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 = 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();
|
||||
}
|
||||
};
|
||||
|
||||
/* Basic nominal test of a client.
|
||||
*/
|
||||
TEST_F(TestClientFixture, test_client_nominal) {
|
||||
stop_memory_checking();
|
||||
rcl_ret_t ret;
|
||||
rcl_client_t client = rcl_get_zero_initialized_client();
|
||||
|
||||
// Initialize the client.
|
||||
const char * topic_name = "add_two_ints";
|
||||
rcl_client_options_t client_options = rcl_client_get_default_options();
|
||||
|
||||
const rosidl_service_type_support_t * ts = ROSIDL_GET_TYPE_SUPPORT(
|
||||
example_interfaces, srv, AddTwoInts);
|
||||
ret = rcl_client_init(&client, this->node_ptr, ts, topic_name, &client_options);
|
||||
|
||||
// Check the return code of initialization and that the service name matches what's expected
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_EQ(strcmp(rcl_client_get_service_name(&client), topic_name), 0);
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
// Initialize the client request.
|
||||
example_interfaces__srv__AddTwoInts_Request req;
|
||||
example_interfaces__srv__AddTwoInts_Request__init(&req);
|
||||
req.a = 1;
|
||||
req.b = 2;
|
||||
|
||||
// Check that there were no errors while sending the request.
|
||||
int64_t sequence_number = 0;
|
||||
ret = rcl_send_request(&client, &req, &sequence_number);
|
||||
EXPECT_EQ(sequence_number, 1);
|
||||
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
}
|
||||
|
||||
|
||||
/* Testing the client init and fini functions.
|
||||
*/
|
||||
TEST_F(TestClientFixture, test_client_init_fini) {
|
||||
stop_memory_checking();
|
||||
rcl_ret_t ret;
|
||||
// Setup valid inputs.
|
||||
rcl_client_t client;
|
||||
|
||||
const rosidl_service_type_support_t * ts = ROSIDL_GET_TYPE_SUPPORT(
|
||||
example_interfaces, srv, AddTwoInts);
|
||||
const char * topic_name = "chatter";
|
||||
rcl_client_options_t default_client_options = rcl_client_get_default_options();
|
||||
|
||||
// Try passing null for client in init.
|
||||
ret = rcl_client_init(nullptr, this->node_ptr, ts, topic_name, &default_client_options);
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
|
||||
// Try passing null for a node pointer in init.
|
||||
client = rcl_get_zero_initialized_client();
|
||||
ret = rcl_client_init(&client, nullptr, ts, topic_name, &default_client_options);
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
|
||||
// Try passing an invalid (uninitialized) node in init.
|
||||
client = rcl_get_zero_initialized_client();
|
||||
rcl_node_t invalid_node = rcl_get_zero_initialized_node();
|
||||
ret = rcl_client_init(&client, &invalid_node, ts, topic_name, &default_client_options);
|
||||
EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
|
||||
// Try passing null for the type support in init.
|
||||
client = rcl_get_zero_initialized_client();
|
||||
ret = rcl_client_init(
|
||||
&client, this->node_ptr, nullptr, topic_name, &default_client_options);
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
|
||||
// Try passing null for the topic name in init.
|
||||
client = rcl_get_zero_initialized_client();
|
||||
ret = rcl_client_init(&client, this->node_ptr, ts, nullptr, &default_client_options);
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
|
||||
// Try passing null for the options in init.
|
||||
client = rcl_get_zero_initialized_client();
|
||||
ret = rcl_client_init(&client, this->node_ptr, ts, topic_name, nullptr);
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
|
||||
// Try passing options with an invalid allocate in allocator with init.
|
||||
client = rcl_get_zero_initialized_client();
|
||||
rcl_client_options_t client_options_with_invalid_allocator;
|
||||
client_options_with_invalid_allocator = rcl_client_get_default_options();
|
||||
client_options_with_invalid_allocator.allocator.allocate = nullptr;
|
||||
ret = rcl_client_init(
|
||||
&client, this->node_ptr, ts, topic_name, &client_options_with_invalid_allocator);
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
|
||||
// Try passing options with an invalid deallocate in allocator with init.
|
||||
client = rcl_get_zero_initialized_client();
|
||||
client_options_with_invalid_allocator = rcl_client_get_default_options();
|
||||
client_options_with_invalid_allocator.allocator.deallocate = nullptr;
|
||||
ret = rcl_client_init(
|
||||
&client, this->node_ptr, ts, topic_name, &client_options_with_invalid_allocator);
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
|
||||
// An allocator with an invalid realloc will probably work (so we will not test it).
|
||||
|
||||
// Try passing options with a failing allocator with init.
|
||||
client = rcl_get_zero_initialized_client();
|
||||
rcl_client_options_t client_options_with_failing_allocator;
|
||||
client_options_with_failing_allocator = rcl_client_get_default_options();
|
||||
client_options_with_failing_allocator.allocator.allocate = failing_malloc;
|
||||
client_options_with_failing_allocator.allocator.deallocate = failing_free;
|
||||
client_options_with_failing_allocator.allocator.reallocate = failing_realloc;
|
||||
ret = rcl_client_init(
|
||||
&client, this->node_ptr, ts, topic_name, &client_options_with_failing_allocator);
|
||||
EXPECT_EQ(RCL_RET_BAD_ALLOC, ret) << rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
}
|
196
rcl/test/rcl/test_service.cpp
Normal file
196
rcl/test/rcl/test_service.cpp
Normal file
|
@ -0,0 +1,196 @@
|
|||
// Copyright 2015 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 <chrono>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "rcl/service.h"
|
||||
|
||||
#include "rcl/rcl.h"
|
||||
|
||||
#include "example_interfaces/srv/add_two_ints.h"
|
||||
|
||||
#include "../memory_tools/memory_tools.hpp"
|
||||
#include "../scope_exit.hpp"
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
class TestServiceFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
rcl_node_t * node_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->node_ptr = new rcl_node_t;
|
||||
*this->node_ptr = rcl_get_zero_initialized_node();
|
||||
const char * name = "node_name";
|
||||
rcl_node_options_t node_options = rcl_node_get_default_options();
|
||||
ret = rcl_node_init(this->node_ptr, name, &node_options);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
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 = 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();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
wait_for_service_to_be_ready(
|
||||
rcl_service_t * service,
|
||||
size_t max_tries,
|
||||
int64_t period_ms,
|
||||
bool & success)
|
||||
{
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_ret_t ret = rcl_wait_set_init(&wait_set, 0, 0, 0, 0, 1, rcl_get_default_allocator());
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
auto wait_set_exit = make_scope_exit([&wait_set]() {
|
||||
stop_memory_checking();
|
||||
rcl_ret_t ret = rcl_wait_set_fini(&wait_set);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
});
|
||||
size_t iteration = 0;
|
||||
do {
|
||||
++iteration;
|
||||
ret = rcl_wait_set_clear_services(&wait_set);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
ret = rcl_wait_set_add_service(&wait_set, service);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
ret = rcl_wait(&wait_set, RCL_MS_TO_NS(period_ms));
|
||||
if (ret == RCL_RET_TIMEOUT) {
|
||||
continue;
|
||||
}
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
for (size_t i = 0; i < wait_set.size_of_services; ++i) {
|
||||
if (wait_set.services[i] && wait_set.services[i] == service) {
|
||||
success = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} while (iteration < max_tries);
|
||||
success = false;
|
||||
}
|
||||
|
||||
/* Basic nominal test of a service.
|
||||
*/
|
||||
TEST_F(TestServiceFixture, test_service_nominal) {
|
||||
stop_memory_checking();
|
||||
rcl_ret_t ret;
|
||||
const rosidl_service_type_support_t * ts = ROSIDL_GET_TYPE_SUPPORT(
|
||||
example_interfaces, srv, AddTwoInts);
|
||||
const char * topic = "add_two_ints";
|
||||
|
||||
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, topic, &service_options);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
|
||||
// Check that the service name matches what we assigned.
|
||||
EXPECT_EQ(strcmp(rcl_service_get_service_name(&service), topic), 0);
|
||||
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();
|
||||
});
|
||||
|
||||
rcl_client_t client = rcl_get_zero_initialized_client();
|
||||
rcl_client_options_t client_options = rcl_client_get_default_options();
|
||||
ret = rcl_client_init(&client, this->node_ptr, ts, topic, &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();
|
||||
});
|
||||
|
||||
|
||||
// TODO(wjwwood): add logic to wait for the connection to be established
|
||||
// use count_services busy wait mechanism
|
||||
// until then we will sleep for a short period of time
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
|
||||
// Initialize a request.
|
||||
example_interfaces__srv__AddTwoInts_Request client_request;
|
||||
example_interfaces__srv__AddTwoInts_Request__init(&client_request);
|
||||
client_request.a = 1;
|
||||
client_request.b = 2;
|
||||
int64_t sequence_number;
|
||||
ret = rcl_send_request(&client, &client_request, &sequence_number);
|
||||
EXPECT_EQ(sequence_number, 1);
|
||||
example_interfaces__srv__AddTwoInts_Request__fini(&client_request);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
|
||||
bool success;
|
||||
wait_for_service_to_be_ready(&service, 10, 100, success);
|
||||
ASSERT_TRUE(success);
|
||||
|
||||
// This scope simulates the service responding in a different context so that we can
|
||||
// test take_request/send_response in a single-threaded, deterministic execution.
|
||||
{
|
||||
// Initialize a response.
|
||||
example_interfaces__srv__AddTwoInts_Response service_response;
|
||||
example_interfaces__srv__AddTwoInts_Response__init(&service_response);
|
||||
auto msg_exit = make_scope_exit([&service_response]() {
|
||||
stop_memory_checking();
|
||||
example_interfaces__srv__AddTwoInts_Response__fini(&service_response);
|
||||
});
|
||||
|
||||
// Initialize a separate instance of the request and take the pending request.
|
||||
example_interfaces__srv__AddTwoInts_Request service_request;
|
||||
example_interfaces__srv__AddTwoInts_Request__init(&service_request);
|
||||
rmw_request_id_t header;
|
||||
ret = rcl_take_request(&service, &header, &service_request);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
|
||||
EXPECT_EQ(1, service_request.a);
|
||||
EXPECT_EQ(2, service_request.b);
|
||||
// Simulate a response callback by summing the request and send the response..
|
||||
service_response.sum = service_request.a + service_request.b;
|
||||
ret = rcl_send_response(&service, &header, &service_response);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
}
|
||||
wait_for_service_to_be_ready(&service, 10, 100, success);
|
||||
|
||||
// Initialize the response owned by the client and take the response.
|
||||
example_interfaces__srv__AddTwoInts_Response client_response;
|
||||
example_interfaces__srv__AddTwoInts_Response__init(&client_response);
|
||||
|
||||
rmw_request_id_t header;
|
||||
ret = rcl_take_response(&client, &header, &client_response);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_EQ(client_response.sum, 3);
|
||||
EXPECT_EQ(header.sequence_number, 1);
|
||||
}
|
|
@ -83,7 +83,7 @@ wait_for_subscription_to_be_ready(
|
|||
bool & success)
|
||||
{
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_ret_t ret = rcl_wait_set_init(&wait_set, 1, 0, 0, rcl_get_default_allocator());
|
||||
rcl_ret_t ret = rcl_wait_set_init(&wait_set, 1, 0, 0, 0, 0, rcl_get_default_allocator());
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
auto wait_set_exit = make_scope_exit([&wait_set]() {
|
||||
stop_memory_checking();
|
||||
|
@ -170,10 +170,8 @@ TEST_F(CLASSNAME(TestSubscriptionFixture, RMW_IMPLEMENTATION), test_subscription
|
|||
stop_memory_checking();
|
||||
std_msgs__msg__Int64__fini(&msg);
|
||||
});
|
||||
bool taken = false;
|
||||
ret = rcl_take(&subscription, &msg, &taken, nullptr);
|
||||
ret = rcl_take(&subscription, &msg, nullptr);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
ASSERT_TRUE(taken) << "failed to take a message, even though the subscription was ready";
|
||||
ASSERT_EQ(42, msg.data);
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +183,7 @@ TEST_F(CLASSNAME(TestSubscriptionFixture, RMW_IMPLEMENTATION), test_subscription
|
|||
rcl_ret_t ret;
|
||||
rcl_publisher_t publisher = rcl_get_zero_initialized_publisher();
|
||||
const rosidl_message_type_support_t * ts = ROSIDL_GET_TYPE_SUPPORT(std_msgs, msg, String);
|
||||
const char * topic = "chatter";
|
||||
const char * topic = "rcl_test_subscription_chatter";
|
||||
rcl_publisher_options_t publisher_options = rcl_publisher_get_default_options();
|
||||
ret = rcl_publisher_init(&publisher, this->node_ptr, ts, topic, &publisher_options);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
|
@ -228,10 +226,8 @@ TEST_F(CLASSNAME(TestSubscriptionFixture, RMW_IMPLEMENTATION), test_subscription
|
|||
stop_memory_checking();
|
||||
std_msgs__msg__String__fini(&msg);
|
||||
});
|
||||
bool taken = false;
|
||||
ret = rcl_take(&subscription, &msg, &taken, nullptr);
|
||||
ret = rcl_take(&subscription, &msg, nullptr);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
ASSERT_TRUE(taken) << "failed to take a message, even though the subscription was ready";
|
||||
ASSERT_EQ(std::string(test_string), std::string(msg.data.data, msg.data.size));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue