Skip to content

Commit

Permalink
Merge pull request ros2#30 from ros2/c_services
Browse files Browse the repository at this point in the history
C clients/services
  • Loading branch information
jacquelinekay committed Mar 24, 2016
2 parents 1932e05 + 534a49c commit 494cfc5
Show file tree
Hide file tree
Showing 17 changed files with 1,760 additions and 23 deletions.
2 changes: 2 additions & 0 deletions rcl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
301 changes: 301 additions & 0 deletions rcl/include/rcl/client.h
Original file line number Diff line number Diff line change
@@ -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_
2 changes: 1 addition & 1 deletion rcl/include/rcl/guard_condition.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading

0 comments on commit 494cfc5

Please sign in to comment.