diff --git a/rcl/include/rcl/client.h b/rcl/include/rcl/client.h index 71d5a4294..f7995fabc 100644 --- a/rcl/include/rcl/client.h +++ b/rcl/include/rcl/client.h @@ -29,6 +29,7 @@ extern "C" #include "rcl/macros.h" #include "rcl/node.h" #include "rcl/publisher.h" +#include "rcl/service_introspection.h" #include "rcl/time.h" #include "rcl/visibility_control.h" @@ -49,15 +50,9 @@ typedef struct rcl_client_options_s { /// Middleware quality of service settings for the client. rmw_qos_profile_t qos; - /// Publisher options for the service event publisher - rcl_publisher_options_t event_publisher_options; /// Custom allocator for the client, used for incidental allocations. /** For default behavior (malloc/free), use: rcl_get_default_allocator() */ rcl_allocator_t allocator; - /// Enable/Disable service introspection features - bool enable_service_introspection; - /// The clock to use for service introspection message timestampes - rcl_clock_t * clock; } rcl_client_options_t; /// Return a rcl_client_t struct with members set to `NULL`. @@ -205,10 +200,7 @@ rcl_client_fini(rcl_client_t * client, rcl_node_t * node); * The defaults are: * * - qos = rmw_qos_profile_services_default - * - event_publisher_options = rcl_publisher_get_default_options() * - allocator = rcl_get_default_allocator() - * - enable_service_introspection = False - * - clock = NULL */ RCL_PUBLIC RCL_WARN_UNUSED @@ -510,6 +502,10 @@ rcl_client_set_on_new_response_callback( /// Configures service introspection features for the client. /** * Enables or disables service introspection features for this client. + * If the introspection state is RCL_SERVICE_INTROSPECTION_OFF, then introspection will + * be disabled. If the state is RCL_SERVICE_INTROSPECTION_METADATA, the client metadata + * will be published. If the state is RCL_SERVICE_INTROSPECTION_CONTENTS, then the client + * metadata and service request and response contents will be published. * *
* Attribute | Adherence @@ -520,47 +516,29 @@ rcl_client_set_on_new_response_callback( * Lock-Free | Maybe [1] * [1] rmw implementation defined * - * \param[in] client The client on which to configure service introspection - * \param[in] node The node for which the service event publisher is to be associated to - * \param[in] enable Whether to enable or disable service introspection for the client. - * \return `RCL_RET_ERROR` if the event publisher is invalid, or - * \return `RCL_RET_NODE_INVALID` if the given node is invalid, or - * \return `RCL_RET_INVALID_ARGUMENT` if the client or node structure is invalid, - * \return `RCL_RET_BAD_ALLOC` if a memory allocation failed, or - * \return `RCL_RET_OK` if the call was successful + * \param[in] client client on which to configure service introspection + * \param[in] node valid rcl_node_t to use to create the introspection publisher + * \param[in] clock valid rcl_clock_t to use to generate the introspection timestamps + * \param[in] type_support type support library associated with this client + * \param[in] publisher_options options to use when creating the introspection publisher + * \param[in] introspection_state rcl_service_introspection_state_t describing whether + * introspection should be OFF, METADATA, or CONTENTS + * \return #RCL_RET_OK if the call was successful, or + * \return #RCL_RET_ERROR if the event publisher is invalid, or + * \return #RCL_RET_NODE_INVALID if the given node is invalid, or + * \return #RCL_RET_INVALID_ARGUMENT if the client or node structure is invalid, + * \return #RCL_RET_BAD_ALLOC if a memory allocation failed */ RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t -rcl_service_introspection_configure_client_service_events( +rcl_client_configure_service_introspection( rcl_client_t * client, rcl_node_t * node, - bool enable); - -/// Configures whether service introspection messages contain only metadata or content. -/** - * Enables or disables whether service introspection messages contain just the metadata - * about the transaction, or also contains the content. - * - *
- * Attribute | Adherence - * ------------------ | ------------- - * Allocates Memory | No - * Thread-Safe | No - * Uses Atomics | No - * Lock-Free | Yes - * - * \param[in] client The client on which to configure content - * \param[in] enable Whether to enable or disable service introspection content - * \return `RCL_RET_INVALID_ARGUMENT` if the client structure is invalid, - * \return `RCL_RET_OK` if the call was successful - */ -RCL_PUBLIC -RCL_WARN_UNUSED -rcl_ret_t -rcl_service_introspection_configure_client_service_event_message_payload( - rcl_client_t * client, - bool enable); + rcl_clock_t * clock, + const rosidl_service_type_support_t * type_support, + const rcl_publisher_options_t publisher_options, + rcl_service_introspection_state_t introspection_state); #ifdef __cplusplus } diff --git a/rcl/include/rcl/node_options.h b/rcl/include/rcl/node_options.h index de2915f96..551d79437 100644 --- a/rcl/include/rcl/node_options.h +++ b/rcl/include/rcl/node_options.h @@ -54,9 +54,6 @@ typedef struct rcl_node_options_s /// Middleware quality of service settings for /rosout. rmw_qos_profile_t rosout_qos; - - /// Flag to enable introspection features for services related to this node - bool enable_service_introspection; } rcl_node_options_t; /// Return the default node options in a rcl_node_options_t. diff --git a/rcl/include/rcl/service.h b/rcl/include/rcl/service.h index 877da6ff3..dec7dc913 100644 --- a/rcl/include/rcl/service.h +++ b/rcl/include/rcl/service.h @@ -29,6 +29,7 @@ extern "C" #include "rcl/macros.h" #include "rcl/node.h" #include "rcl/publisher.h" +#include "rcl/service_introspection.h" #include "rcl/time.h" #include "rcl/visibility_control.h" @@ -49,15 +50,9 @@ typedef struct rcl_service_options_s { /// Middleware quality of service settings for the service. rmw_qos_profile_t qos; - /// Publisher options for the service event publisher - rcl_publisher_options_t event_publisher_options; /// Custom allocator for the service, used for incidental allocations. /** For default behavior (malloc/free), see: rcl_get_default_allocator() */ rcl_allocator_t allocator; - /// Enable/Disable service introspection features - bool enable_service_introspection; - /// The clock to use for service introspection message timestamps - rcl_clock_t * clock; } rcl_service_options_t; /// Return a rcl_service_t struct with members set to `NULL`. @@ -208,10 +203,7 @@ rcl_service_fini(rcl_service_t * service, rcl_node_t * node); * The defaults are: * * - qos = rmw_qos_profile_services_default - * - event_publisher_options = rcl_publisher_get_default_options() * - allocator = rcl_get_default_allocator() - * - enable_service_introspection = False - * - clock = NULL */ RCL_PUBLIC RCL_WARN_UNUSED @@ -538,9 +530,13 @@ rcl_service_set_on_new_request_callback( rcl_event_callback_t callback, const void * user_data); -/// Configure service introspection features for the service +/// Configure service introspection features for the service. /** * Enables or disables service introspection features for this service. + * If the introspection state is RCL_SERVICE_INTROSPECTION_OFF, then introspection will + * be disabled. If the state is RCL_SERVICE_INTROSPECTION_METADATA, the client metadata + * will be published. If the state is RCL_SERVICE_INTROSPECTION_CONTENTS, then the client + * metadata and service request and response contents will be published. * *
* Attribute | Adherence @@ -551,44 +547,29 @@ rcl_service_set_on_new_request_callback( * Lock-Free | Maybe [1] * [1] rmw implementation defined * - * \param[in] server The server on which to enable service introspection - * \param[in] node The node for which the service event publisher is to be associated to - * \param[in] enable Whether to enable or disable service introspection - * \return `RCL_RET_ERROR` if the event publisher is invalid, or - * \return `RCL_RET_NODE_INVALID` if the given node is invalid, or - * \return `RCL_RET_INVALID_ARGUMENT` if the client or node structure is invalid, - * \return `RCL_RET_BAD_ALLOC` if a memory allocation failed, or - * \return `RCL_RET_OK` if the call was successful + * \param[in] service service on which to configure service introspection + * \param[in] node valid rcl_node_t to use to create the introspection publisher + * \param[in] clock valid rcl_clock_t to use to generate the introspection timestamps + * \param[in] type_support type support library associated with this service + * \param[in] publisher_options options to use when creating the introspection publisher + * \param[in] introspection_state rcl_service_introspection_state_t describing whether + * introspection should be OFF, METADATA, or CONTENTS + * \return #RCL_RET_OK if the call was successful, or + * \return #RCL_RET_ERROR if the event publisher is invalid, or + * \return #RCL_RET_NODE_INVALID if the given node is invalid, or + * \return #RCL_RET_INVALID_ARGUMENT if the client or node structure is invalid, + * \return #RCL_RET_BAD_ALLOC if a memory allocation failed */ RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t -rcl_service_introspection_configure_server_service_events( +rcl_service_configure_service_introspection( rcl_service_t * service, rcl_node_t * node, - bool enable); - -/// Configure if the payload (server response) is included in service event messages. -/** - *
- * Attribute | Adherence - * ------------------ | ------------- - * Allocates Memory | No - * Thread-Safe | No - * Uses Atomics | No - * Lock-Free | Yes - * - * \param[in] server The server on which to enable/disable payload for service event messages. - * \param[in] enable Whether to enable or disable including the payload in the event message. - * \return `RCL_RET_INVALID_ARGUMENT` if the service structure is invalid, - * \return `RCL_RET_OK` if the call was successful - */ -RCL_PUBLIC -RCL_WARN_UNUSED -rcl_ret_t -rcl_service_introspection_configure_server_service_event_message_payload( - rcl_service_t * service, - bool enable); + rcl_clock_t * clock, + const rosidl_service_type_support_t * type_support, + const rcl_publisher_options_t publisher_options, + rcl_service_introspection_state_t introspection_state); #ifdef __cplusplus } diff --git a/rcl/include/rcl/service_introspection.h b/rcl/include/rcl/service_introspection.h index 884f37ea4..1055d6b9d 100644 --- a/rcl/include/rcl/service_introspection.h +++ b/rcl/include/rcl/service_introspection.h @@ -17,4 +17,15 @@ #define RCL_SERVICE_INTROSPECTION_TOPIC_POSTFIX "/_service_event" +/// The introspection state for a client or service. +typedef enum rcl_service_introspection_state_e +{ + /// Introspection disabled + RCL_SERVICE_INTROSPECTION_OFF, + /// Introspect metadata only + RCL_SERVICE_INTROSPECTION_METADATA, + /// Introspection metadata and contents + RCL_SERVICE_INTROSPECTION_CONTENTS, +} rcl_service_introspection_state_t; + #endif // RCL__SERVICE_INTROSPECTION_H_ diff --git a/rcl/src/rcl/client.c b/rcl/src/rcl/client.c index 6b36c73c6..63d905855 100644 --- a/rcl/src/rcl/client.c +++ b/rcl/src/rcl/client.c @@ -25,6 +25,7 @@ extern "C" #include "rcl/error_handling.h" #include "rcl/node.h" #include "rcl/publisher.h" +#include "rcl/time.h" #include "rcutils/logging_macros.h" #include "rcutils/macros.h" #include "rcutils/stdatomic_helper.h" @@ -33,10 +34,22 @@ extern "C" #include "service_msgs/msg/service_event_info.h" #include "tracetools/tracetools.h" +#include "rosidl_runtime_c/service_type_support_struct.h" + #include "./common.h" -#include "./client_impl.h" #include "./service_event_publisher.h" +struct rcl_client_impl_s +{ + rcl_client_options_t options; + rmw_qos_profile_t actual_request_publisher_qos; + rmw_qos_profile_t actual_response_subscription_qos; + rmw_client_t * rmw_handle; + atomic_int_least64_t sequence_number; + rcl_service_event_publisher_t * service_event_publisher; + char * remapped_service_name; +}; + rcl_client_t rcl_get_zero_initialized_client() { @@ -51,7 +64,11 @@ unconfigure_service_introspection( struct rcl_client_impl_s * client_impl, rcl_allocator_t * allocator) { - if (!client_impl->service_event_publisher) { + if (client_impl == NULL) { + return RCL_RET_ERROR; + } + + if (client_impl->service_event_publisher == NULL) { return RCL_RET_OK; } @@ -63,44 +80,6 @@ unconfigure_service_introspection( return ret; } -static -rcl_ret_t -configure_service_introspection( - const rcl_node_t * node, - struct rcl_client_impl_s * client_impl, - rcl_allocator_t * allocator, - const rcl_client_options_t * options, - const rosidl_service_type_support_t * type_support, - const char * remapped_service_name) -{ - if (!rcl_node_get_options(node)->enable_service_introspection) { - return RCL_RET_OK; - } - - client_impl->service_event_publisher = allocator->zero_allocate( - 1, sizeof(rcl_service_event_publisher_t), allocator->state); - RCL_CHECK_FOR_NULL_WITH_MSG( - client_impl->service_event_publisher, "allocating memory failed", return RCL_RET_BAD_ALLOC;); - - rcl_service_event_publisher_options_t service_event_options = - rcl_service_event_publisher_get_default_options(); - service_event_options.publisher_options = options->event_publisher_options; - service_event_options.clock = options->clock; - - *client_impl->service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); - rcl_ret_t ret = rcl_service_event_publisher_init( - client_impl->service_event_publisher, node, &service_event_options, - remapped_service_name, type_support); - if (RCL_RET_OK != ret) { - RCL_SET_ERROR_MSG(rcl_get_error_string().str); - allocator->deallocate(client_impl->service_event_publisher, allocator->state); - client_impl->service_event_publisher = NULL; - return ret; - } - - return RCL_RET_OK; -} - rcl_ret_t rcl_client_init( rcl_client_t * client, @@ -109,8 +88,6 @@ rcl_client_init( const char * service_name, const rcl_client_options_t * options) { - rcl_ret_t fail_ret = RCL_RET_ERROR; - // check the options and allocator first, so the allocator can be passed to errors RCL_CHECK_ARGUMENT_FOR_NULL(options, RCL_RET_INVALID_ARGUMENT); rcl_allocator_t * allocator = (rcl_allocator_t *)&options->allocator; @@ -128,50 +105,45 @@ rcl_client_init( return RCL_RET_ALREADY_INIT; } + // Allocate space for the implementation struct. + client->impl = (rcl_client_impl_t *)allocator->zero_allocate( + 1, sizeof(rcl_client_impl_t), allocator->state); + RCL_CHECK_FOR_NULL_WITH_MSG( + client->impl, "allocating memory failed", + return RCL_RET_BAD_ALLOC;); + // Expand the given service name. - char * remapped_service_name = NULL; rcl_ret_t ret = rcl_node_resolve_name( node, service_name, *allocator, true, false, - &remapped_service_name); + &client->impl->remapped_service_name); if (ret != RCL_RET_OK) { if (ret == RCL_RET_SERVICE_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) { ret = RCL_RET_SERVICE_NAME_INVALID; } else if (RCL_RET_BAD_ALLOC != ret) { ret = RCL_RET_ERROR; } - return ret; + goto free_client_impl; } RCUTILS_LOG_DEBUG_NAMED( - ROS_PACKAGE_NAME, "Expanded and remapped service name '%s'", remapped_service_name); + ROS_PACKAGE_NAME, "Expanded and remapped service name '%s'", + client->impl->remapped_service_name); - // Allocate space for the implementation struct. - client->impl = (rcl_client_impl_t *)allocator->zero_allocate( - 1, sizeof(rcl_client_impl_t), allocator->state); - RCL_CHECK_FOR_NULL_WITH_MSG( - client->impl, "allocating memory failed", - ret = RCL_RET_BAD_ALLOC; goto free_remapped_service_name); // 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, - remapped_service_name, + client->impl->remapped_service_name, &options->qos); if (!client->impl->rmw_handle) { RCL_SET_ERROR_MSG(rmw_get_error_string().str); ret = RCL_RET_ERROR; - goto free_client_impl; - } - - ret = configure_service_introspection( - node, client->impl, allocator, options, type_support, remapped_service_name); - if (RCL_RET_OK != ret) { - goto destroy_client; + goto free_remapped_service_name; } // get actual qos, and store it @@ -181,7 +153,7 @@ rcl_client_init( if (RMW_RET_OK != rmw_ret) { RCL_SET_ERROR_MSG(rmw_get_error_string().str); ret = rcl_convert_rmw_ret_to_rcl_ret(rmw_ret); - goto unconfigure_introspection; + goto destroy_client; } rmw_ret = rmw_client_response_subscription_get_actual_qos( @@ -190,7 +162,7 @@ rcl_client_init( if (RMW_RET_OK != rmw_ret) { RCL_SET_ERROR_MSG(rmw_get_error_string().str); ret = rcl_convert_rmw_ret_to_rcl_ret(rmw_ret); - goto unconfigure_introspection; + goto destroy_client; } // ROS specific namespacing conventions avoidance @@ -204,37 +176,30 @@ rcl_client_init( client->impl->options = *options; atomic_init(&client->impl->sequence_number, 0); RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Client initialized"); - ret = RCL_RET_OK; TRACEPOINT( rcl_client_init, (const void *)client, (const void *)node, (const void *)client->impl->rmw_handle, - remapped_service_name); - - goto free_remapped_service_name; + client->impl->remapped_service_name); -unconfigure_introspection: - // TODO(clalancette): I don't love casting away the const from node here, - // but the cleanup path goes deep and I didn't want to change 6 or so - // different signatures. - fail_ret = unconfigure_service_introspection((rcl_node_t *)node, client->impl, allocator); - if (RCL_RET_OK != fail_ret) { - // TODO(clalancette): print the error message here - } + return RCL_RET_OK; destroy_client: rmw_ret = rmw_destroy_client(rcl_node_get_rmw_handle(node), client->impl->rmw_handle); if (RMW_RET_OK != rmw_ret) { - // TODO(clalancette): print the error message here + RCUTILS_SAFE_FWRITE_TO_STDERR(rmw_get_error_string().str); + RCUTILS_SAFE_FWRITE_TO_STDERR("\n"); } +free_remapped_service_name: + allocator->deallocate(client->impl->remapped_service_name, allocator->state); + client->impl->remapped_service_name = NULL; + free_client_impl: allocator->deallocate(client->impl, allocator->state); client->impl = NULL; -free_remapped_service_name: - allocator->deallocate(remapped_service_name, allocator->state); return ret; } @@ -271,6 +236,9 @@ rcl_client_fini(rcl_client_t * client, rcl_node_t * node) result = RCL_RET_ERROR; } + allocator.deallocate(client->impl->remapped_service_name, allocator.state); + client->impl->remapped_service_name = NULL; + allocator.deallocate(client->impl, allocator.state); client->impl = NULL; } @@ -285,10 +253,7 @@ 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_services_default; - default_options.event_publisher_options = rcl_publisher_get_default_options(); default_options.allocator = rcl_get_default_allocator(); - default_options.enable_service_introspection = false; - default_options.clock = NULL; return default_options; } @@ -339,7 +304,7 @@ rcl_send_request(const rcl_client_t * client, const void * ros_request, int64_t } rcutils_atomic_exchange_int64_t(&client->impl->sequence_number, *sequence_number); - if (rcl_client_get_options(client)->enable_service_introspection) { + if (client->impl->service_event_publisher != NULL) { rmw_gid_t gid; rmw_ret_t rmw_ret = rmw_get_gid_for_client(client->impl->rmw_handle, &gid); if (rmw_ret != RMW_RET_OK) { @@ -389,7 +354,7 @@ rcl_take_response_with_info( return RCL_RET_CLIENT_TAKE_FAILED; } - if (rcl_client_get_options(client)->enable_service_introspection) { + if (client->impl->service_event_publisher != NULL) { rmw_gid_t gid; rmw_ret_t rmw_ret = rmw_get_gid_for_client(client->impl->rmw_handle, &gid); if (rmw_ret != RMW_RET_OK) { @@ -470,32 +435,48 @@ rcl_client_set_on_new_response_callback( } rcl_ret_t -rcl_service_introspection_configure_client_service_events( +rcl_client_configure_service_introspection( rcl_client_t * client, rcl_node_t * node, - bool enable) + rcl_clock_t * clock, + const rosidl_service_type_support_t * type_support, + const rcl_publisher_options_t publisher_options, + rcl_service_introspection_state_t introspection_state) { - RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT); + if (!rcl_client_is_valid(client)) { + return RCL_RET_CLIENT_INVALID; // error already set + } RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT); - RCL_CHECK_ARGUMENT_FOR_NULL(client->impl, RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_ARGUMENT_FOR_NULL(clock, RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_ARGUMENT_FOR_NULL(type_support, RCL_RET_INVALID_ARGUMENT); - if (enable) { - return rcl_service_introspection_enable(client->impl->service_event_publisher, node); + rcl_allocator_t allocator = client->impl->options.allocator; + + if (introspection_state == RCL_SERVICE_INTROSPECTION_OFF) { + return unconfigure_service_introspection(node, client->impl, &allocator); } - return rcl_service_introspection_disable(client->impl->service_event_publisher, node); -} -rcl_ret_t -rcl_service_introspection_configure_client_service_event_message_payload( - rcl_client_t * client, - bool enable) -{ - RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT); - RCL_CHECK_ARGUMENT_FOR_NULL(client->impl, RCL_RET_INVALID_ARGUMENT); + if (client->impl->service_event_publisher == NULL) { + // We haven't been introspecting, so we need to allocate the service event publisher - client->impl->service_event_publisher->impl->options._content_enabled = enable; + client->impl->service_event_publisher = allocator.allocate( + sizeof(rcl_service_event_publisher_t), allocator.state); + RCL_CHECK_FOR_NULL_WITH_MSG( + client->impl->service_event_publisher, "allocating memory failed", return RCL_RET_BAD_ALLOC;); - return RCL_RET_OK; + *client->impl->service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); + rcl_ret_t ret = rcl_service_event_publisher_init( + client->impl->service_event_publisher, node, clock, publisher_options, + client->impl->remapped_service_name, type_support); + if (RCL_RET_OK != ret) { + allocator.deallocate(client->impl->service_event_publisher, allocator.state); + client->impl->service_event_publisher = NULL; + return ret; + } + } + + return rcl_service_event_publisher_change_state( + client->impl->service_event_publisher, introspection_state); } #ifdef __cplusplus diff --git a/rcl/src/rcl/client_impl.h b/rcl/src/rcl/client_impl.h deleted file mode 100644 index cf8963c99..000000000 --- a/rcl/src/rcl/client_impl.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2022 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_IMPL_H_ -#define RCL__CLIENT_IMPL_H_ - -#include "rcl/client.h" -#include "rcutils/stdatomic_helper.h" -#include "rmw/types.h" - -#include "./service_event_publisher.h" - -struct rcl_client_impl_s -{ - rcl_client_options_t options; - rmw_qos_profile_t actual_request_publisher_qos; - rmw_qos_profile_t actual_response_subscription_qos; - rmw_client_t * rmw_handle; - atomic_int_least64_t sequence_number; - rcl_service_event_publisher_t * service_event_publisher; -}; - -#endif // RCL__CLIENT_IMPL_H_ diff --git a/rcl/src/rcl/node_options.c b/rcl/src/rcl/node_options.c index f3c295983..34408f18b 100644 --- a/rcl/src/rcl/node_options.c +++ b/rcl/src/rcl/node_options.c @@ -36,7 +36,6 @@ rcl_node_get_default_options() .arguments = rcl_get_zero_initialized_arguments(), .enable_rosout = true, .rosout_qos = rcl_qos_profile_rosout_default, - .enable_service_introspection = false, }; return default_options; } @@ -62,7 +61,6 @@ rcl_node_options_copy( options_out->use_global_arguments = options->use_global_arguments; options_out->enable_rosout = options->enable_rosout; options_out->rosout_qos = options->rosout_qos; - options_out->enable_service_introspection = options->enable_service_introspection; if (NULL != options->arguments.impl) { return rcl_arguments_copy(&(options->arguments), &(options_out->arguments)); } diff --git a/rcl/src/rcl/service.c b/rcl/src/rcl/service.c index 756ee0336..78eb70d0f 100644 --- a/rcl/src/rcl/service.c +++ b/rcl/src/rcl/service.c @@ -17,11 +17,12 @@ extern "C" { #endif +#include "rcl/service.h" + #include #include #include "rcl/error_handling.h" -#include "rcl/graph.h" #include "rcl/node.h" #include "rcl/publisher.h" #include "rcl/time.h" @@ -34,12 +35,19 @@ extern "C" #include "tracetools/tracetools.h" #include "rosidl_runtime_c/service_type_support_struct.h" -#include "rosidl_runtime_c/message_type_support_struct.h" #include "./common.h" #include "./service_event_publisher.h" -#include "./service_impl.h" +struct rcl_service_impl_s +{ + rcl_service_options_t options; + rmw_qos_profile_t actual_request_subscription_qos; + rmw_qos_profile_t actual_response_publisher_qos; + rmw_service_t * rmw_handle; + rcl_service_event_publisher_t * service_event_publisher; + char * remapped_service_name; +}; rcl_service_t rcl_get_zero_initialized_service() @@ -55,7 +63,11 @@ unconfigure_service_introspection( struct rcl_service_impl_s * service_impl, rcl_allocator_t * allocator) { - if (!service_impl->service_event_publisher) { + if (service_impl == NULL) { + return RCL_RET_ERROR; + } + + if (service_impl->service_event_publisher == NULL) { return RCL_RET_OK; } @@ -67,44 +79,6 @@ unconfigure_service_introspection( return ret; } -static -rcl_ret_t -configure_service_introspection( - const rcl_node_t * node, - struct rcl_service_impl_s * service_impl, - rcl_allocator_t * allocator, - const rcl_service_options_t * options, - const rosidl_service_type_support_t * type_support, - const char * remapped_service_name) -{ - if (!rcl_node_get_options(node)->enable_service_introspection) { - return RCL_RET_OK; - } - - service_impl->service_event_publisher = allocator->zero_allocate( - 1, sizeof(rcl_service_event_publisher_t), allocator->state); - RCL_CHECK_FOR_NULL_WITH_MSG( - service_impl->service_event_publisher, "allocating memory failed", return RCL_RET_BAD_ALLOC;); - - rcl_service_event_publisher_options_t service_event_options = - rcl_service_event_publisher_get_default_options(); - service_event_options.publisher_options = options->event_publisher_options; - service_event_options.clock = options->clock; - - *service_impl->service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); - rcl_ret_t ret = rcl_service_event_publisher_init( - service_impl->service_event_publisher, node, &service_event_options, - remapped_service_name, type_support); - if (RCL_RET_OK != ret) { - RCL_SET_ERROR_MSG(rcl_get_error_string().str); - allocator->deallocate(service_impl->service_event_publisher, allocator->state); - service_impl->service_event_publisher = NULL; - return ret; - } - - return RCL_RET_OK; -} - rcl_ret_t rcl_service_init( rcl_service_t * service, @@ -120,8 +94,6 @@ rcl_service_init( RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR); RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_SERVICE_NAME_INVALID); - rcl_ret_t fail_ret = RCL_RET_ERROR; - // Check options and allocator first, so the allocator can be used in errors. RCL_CHECK_ARGUMENT_FOR_NULL(options, RCL_RET_INVALID_ARGUMENT); rcl_allocator_t * allocator = (rcl_allocator_t *)&options->allocator; @@ -140,32 +112,32 @@ rcl_service_init( return RCL_RET_ALREADY_INIT; } + // Allocate space for the implementation struct. + service->impl = (rcl_service_impl_t *)allocator->zero_allocate( + 1, sizeof(rcl_service_impl_t), allocator->state); + RCL_CHECK_FOR_NULL_WITH_MSG( + service->impl, "allocating memory failed", + return RCL_RET_BAD_ALLOC;); + // Expand and remap the given service name. - char * remapped_service_name = NULL; rcl_ret_t ret = rcl_node_resolve_name( node, service_name, *allocator, true, false, - &remapped_service_name); + &service->impl->remapped_service_name); if (ret != RCL_RET_OK) { if (ret == RCL_RET_SERVICE_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) { ret = RCL_RET_SERVICE_NAME_INVALID; } else if (ret != RCL_RET_BAD_ALLOC) { ret = RCL_RET_ERROR; } - return ret; + goto free_service_impl; } RCUTILS_LOG_DEBUG_NAMED( - ROS_PACKAGE_NAME, "Expanded and remapped service name '%s'", remapped_service_name); - - // Allocate space for the implementation struct. - service->impl = (rcl_service_impl_t *)allocator->zero_allocate( - 1, sizeof(rcl_service_impl_t), allocator->state); - RCL_CHECK_FOR_NULL_WITH_MSG( - service->impl, "allocating memory failed", - ret = RCL_RET_BAD_ALLOC; goto free_remapped_service_name); + ROS_PACKAGE_NAME, "Expanded and remapped service name '%s'", + service->impl->remapped_service_name); if (RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL == options->qos.durability) { RCUTILS_LOG_WARN_NAMED( @@ -179,18 +151,12 @@ rcl_service_init( service->impl->rmw_handle = rmw_create_service( rcl_node_get_rmw_handle(node), type_support, - remapped_service_name, + service->impl->remapped_service_name, &options->qos); if (!service->impl->rmw_handle) { RCL_SET_ERROR_MSG(rmw_get_error_string().str); ret = RCL_RET_ERROR; - goto free_service_impl; - } - - ret = configure_service_introspection( - node, service->impl, allocator, options, type_support, remapped_service_name); - if (RCL_RET_OK != ret) { - goto destroy_service; + goto free_remapped_service_name; } // get actual qos, and store it @@ -200,7 +166,7 @@ rcl_service_init( if (RMW_RET_OK != rmw_ret) { RCL_SET_ERROR_MSG(rmw_get_error_string().str); ret = rcl_convert_rmw_ret_to_rcl_ret(rmw_ret); - goto unconfigure_introspection; + goto destroy_service; } rmw_ret = rmw_service_response_publisher_get_actual_qos( @@ -209,7 +175,7 @@ rcl_service_init( if (RMW_RET_OK != rmw_ret) { RCL_SET_ERROR_MSG(rmw_get_error_string().str); ret = rcl_convert_rmw_ret_to_rcl_ret(rmw_ret); - goto unconfigure_introspection; + goto destroy_service; } // ROS specific namespacing conventions is not retrieved by get_actual_qos @@ -221,37 +187,30 @@ rcl_service_init( // options service->impl->options = *options; RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Service initialized"); - ret = RCL_RET_OK; TRACEPOINT( rcl_service_init, (const void *)service, (const void *)node, (const void *)service->impl->rmw_handle, - remapped_service_name); - - goto free_remapped_service_name; + service->impl->remapped_service_name); -unconfigure_introspection: - // TODO(clalancette): I don't love casting away the const from node here, - // but the cleanup path goes deep and I didn't want to change 6 or so - // different signatures. - fail_ret = unconfigure_service_introspection((rcl_node_t *)node, service->impl, allocator); - if (RCL_RET_OK != fail_ret) { - // TODO(clalancette): print the error message here - } + return RCL_RET_OK; destroy_service: rmw_ret = rmw_destroy_service(rcl_node_get_rmw_handle(node), service->impl->rmw_handle); if (RMW_RET_OK != rmw_ret) { - // TODO(clalancette): print the error message here + RCUTILS_SAFE_FWRITE_TO_STDERR(rmw_get_error_string().str); + RCUTILS_SAFE_FWRITE_TO_STDERR("\n"); } +free_remapped_service_name: + allocator->deallocate(service->impl->remapped_service_name, allocator->state); + service->impl->remapped_service_name = NULL; + free_service_impl: allocator->deallocate(service->impl, allocator->state); service->impl = NULL; -free_remapped_service_name: - allocator->deallocate(remapped_service_name, allocator->state); return ret; } @@ -289,6 +248,9 @@ rcl_service_fini(rcl_service_t * service, rcl_node_t * node) result = RCL_RET_ERROR; } + allocator.deallocate(service->impl->remapped_service_name, allocator.state); + service->impl->remapped_service_name = NULL; + allocator.deallocate(service->impl, allocator.state); service->impl = NULL; } @@ -303,10 +265,7 @@ 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_services_default; - default_options.event_publisher_options = rcl_publisher_get_default_options(); default_options.allocator = rcl_get_default_allocator(); - default_options.enable_service_introspection = false; - default_options.clock = NULL; return default_options; } @@ -371,7 +330,7 @@ rcl_take_request_with_info( if (!taken) { return RCL_RET_SERVICE_TAKE_FAILED; } - if (rcl_service_get_options(service)->enable_service_introspection) { + if (service->impl->service_event_publisher != NULL) { rcl_ret_t rclret = rcl_send_service_event_message( service->impl->service_event_publisher, service_msgs__msg__ServiceEventInfo__REQUEST_RECEIVED, @@ -422,7 +381,7 @@ rcl_send_response( } // publish out the introspected content - if (rcl_service_get_options(service)->enable_service_introspection) { + if (service->impl->service_event_publisher != NULL) { rcl_ret_t ret = rcl_send_service_event_message( service->impl->service_event_publisher, service_msgs__msg__ServiceEventInfo__RESPONSE_SENT, @@ -484,32 +443,49 @@ rcl_service_set_on_new_request_callback( } rcl_ret_t -rcl_service_introspection_configure_server_service_events( +rcl_service_configure_service_introspection( rcl_service_t * service, rcl_node_t * node, - bool enable) + rcl_clock_t * clock, + const rosidl_service_type_support_t * type_support, + const rcl_publisher_options_t publisher_options, + rcl_service_introspection_state_t introspection_state) { - RCL_CHECK_ARGUMENT_FOR_NULL(service, RCL_RET_INVALID_ARGUMENT); + if (!rcl_service_is_valid(service)) { + return RCL_RET_SERVICE_INVALID; // error already set + } RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT); - RCL_CHECK_ARGUMENT_FOR_NULL(service->impl, RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_ARGUMENT_FOR_NULL(clock, RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_ARGUMENT_FOR_NULL(type_support, RCL_RET_INVALID_ARGUMENT); - if (enable) { - return rcl_service_introspection_enable(service->impl->service_event_publisher, node); + rcl_allocator_t allocator = service->impl->options.allocator; + + if (introspection_state == RCL_SERVICE_INTROSPECTION_OFF) { + return unconfigure_service_introspection(node, service->impl, &allocator); } - return rcl_service_introspection_disable(service->impl->service_event_publisher, node); -} -rcl_ret_t -rcl_service_introspection_configure_server_service_event_message_payload( - rcl_service_t * service, - bool enable) -{ - RCL_CHECK_ARGUMENT_FOR_NULL(service, RCL_RET_INVALID_ARGUMENT); - RCL_CHECK_ARGUMENT_FOR_NULL(service->impl, RCL_RET_INVALID_ARGUMENT); + if (service->impl->service_event_publisher == NULL) { + // We haven't been introspecting, so we need to allocate the service event publisher - service->impl->service_event_publisher->impl->options._content_enabled = enable; + service->impl->service_event_publisher = allocator.allocate( + sizeof(rcl_service_event_publisher_t), allocator.state); + RCL_CHECK_FOR_NULL_WITH_MSG( + service->impl->service_event_publisher, "allocating memory failed", + return RCL_RET_BAD_ALLOC;); - return RCL_RET_OK; + *service->impl->service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); + rcl_ret_t ret = rcl_service_event_publisher_init( + service->impl->service_event_publisher, node, clock, publisher_options, + service->impl->remapped_service_name, type_support); + if (RCL_RET_OK != ret) { + allocator.deallocate(service->impl->service_event_publisher, allocator.state); + service->impl->service_event_publisher = NULL; + return ret; + } + } + + return rcl_service_event_publisher_change_state( + service->impl->service_event_publisher, introspection_state); } #ifdef __cplusplus diff --git a/rcl/src/rcl/service_event_publisher.c b/rcl/src/rcl/service_event_publisher.c index d7cebf038..8b3e8c16c 100644 --- a/rcl/src/rcl/service_event_publisher.c +++ b/rcl/src/rcl/service_event_publisher.c @@ -16,24 +16,17 @@ #include -#include "rcl/service_introspection.h" - -#include "./client_impl.h" -#include "./service_impl.h" - #include "rcl/allocator.h" #include "rcl/macros.h" #include "rcl/error_handling.h" #include "rcl/publisher.h" #include "rcl/node.h" +#include "rcl/service_introspection.h" #include "rcl/time.h" #include "rcl/types.h" #include "rcutils/logging_macros.h" #include "rcutils/macros.h" -#include "rcutils/shared_library.h" #include "rmw/error_handling.h" -#include "rosidl_runtime_c/primitives_sequence_functions.h" -#include "rosidl_runtime_c/string_functions.h" #include "service_msgs/msg/service_event_info.h" rcl_service_event_publisher_t rcl_get_zero_initialized_service_event_publisher() @@ -42,40 +35,55 @@ rcl_service_event_publisher_t rcl_get_zero_initialized_service_event_publisher() return zero_service_event_publisher; } -rcl_service_event_publisher_options_t -rcl_service_event_publisher_get_default_options() -{ - static rcl_service_event_publisher_options_t default_options; - // Must set the options after because they are not a compile time constant. - default_options._enabled = true; - default_options._content_enabled = true; - default_options.publisher_options = rcl_publisher_get_default_options(); - default_options.clock = NULL; - return default_options; -} - bool rcl_service_event_publisher_is_valid(const rcl_service_event_publisher_t * service_event_publisher) { RCL_CHECK_FOR_NULL_WITH_MSG( service_event_publisher, "service_event_publisher is invalid", return false); RCL_CHECK_FOR_NULL_WITH_MSG( - service_event_publisher->impl, "service_event_publisher's implementation is invalid", - return false); - RCL_CHECK_FOR_NULL_WITH_MSG( - service_event_publisher->impl->service_type_support, + service_event_publisher->service_type_support, "service_event_publisher's service type support is invalid", return false); - if (!rcl_clock_valid(service_event_publisher->impl->options.clock)) { + if (!rcl_clock_valid(service_event_publisher->clock)) { RCL_SET_ERROR_MSG("service_event_publisher's clock is invalid"); return false; } return true; } +static rcl_ret_t introspection_create_publisher( + rcl_service_event_publisher_t * service_event_publisher, + const rcl_node_t * node) +{ + rcl_allocator_t allocator = service_event_publisher->publisher_options.allocator; + RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_ERROR); + + service_event_publisher->publisher = allocator.allocate( + sizeof(rcl_publisher_t), allocator.state); + RCL_CHECK_FOR_NULL_WITH_MSG( + service_event_publisher->publisher, + "allocate service_event_publisher failed in enable", return RCL_RET_BAD_ALLOC); + *service_event_publisher->publisher = rcl_get_zero_initialized_publisher(); + rcl_ret_t ret = rcl_publisher_init( + service_event_publisher->publisher, node, + service_event_publisher->service_type_support->event_typesupport, + service_event_publisher->service_event_topic_name, + &service_event_publisher->publisher_options); + if (RCL_RET_OK != ret) { + allocator.deallocate(service_event_publisher->publisher, allocator.state); + service_event_publisher->publisher = NULL; + rcutils_reset_error(); + RCL_SET_ERROR_MSG(rcl_get_error_string().str); + return ret; + } + + return RCL_RET_OK; +} + rcl_ret_t rcl_service_event_publisher_init( rcl_service_event_publisher_t * service_event_publisher, const rcl_node_t * node, - const rcl_service_event_publisher_options_t * options, + rcl_clock_t * clock, + const rcl_publisher_options_t publisher_options, const char * service_name, const rosidl_service_type_support_t * service_type_support) { @@ -89,28 +97,22 @@ rcl_ret_t rcl_service_event_publisher_init( RCL_CHECK_ARGUMENT_FOR_NULL(service_event_publisher, RCL_RET_INVALID_ARGUMENT); RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT); - RCL_CHECK_ARGUMENT_FOR_NULL(options, RCL_RET_INVALID_ARGUMENT); RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT); RCL_CHECK_ARGUMENT_FOR_NULL(service_type_support, RCL_RET_INVALID_ARGUMENT); RCL_CHECK_ALLOCATOR_WITH_MSG( - &options->publisher_options.allocator, + &publisher_options.allocator, "allocator is invalid", return RCL_RET_ERROR); - rcl_allocator_t allocator = options->publisher_options.allocator; - rcl_ret_t ret = RCL_RET_OK; + rcl_allocator_t allocator = publisher_options.allocator; - if (service_event_publisher->impl) { - rcutils_reset_error(); - RCL_SET_ERROR_MSG("service event publisher already initialized, or memory was unintialized"); - return RCL_RET_ALREADY_INIT; - } + rcl_ret_t ret = RCL_RET_OK; if (!rcl_node_is_valid(node)) { return RCL_RET_NODE_INVALID; } - if (!rcl_clock_valid(options->clock)) { + if (!rcl_clock_valid(clock)) { rcutils_reset_error(); RCL_SET_ERROR_MSG("clock is invalid"); return RCL_RET_ERROR; @@ -118,46 +120,37 @@ rcl_ret_t rcl_service_event_publisher_init( RCUTILS_LOG_DEBUG_NAMED( ROS_PACKAGE_NAME, "Initializing service introspection for service name '%s'", service_name); - service_event_publisher->impl = (rcl_service_event_publisher_impl_t *) allocator.allocate( - sizeof(rcl_service_event_publisher_impl_t), allocator.state); - RCL_CHECK_FOR_NULL_WITH_MSG( - service_event_publisher->impl, "allocating memory for rcl_service_event_publisher failed", - return RCL_RET_BAD_ALLOC;); // Typesupports have static lifetimes - service_event_publisher->impl->service_type_support = service_type_support; - service_event_publisher->impl->options = *options; + service_event_publisher->service_type_support = service_type_support; + service_event_publisher->clock = clock; + service_event_publisher->publisher_options = publisher_options; size_t topic_length = strlen(service_name) + strlen(RCL_SERVICE_INTROSPECTION_TOPIC_POSTFIX) + 1; - service_event_publisher->impl->service_event_topic_name = (char *) allocator.allocate( + service_event_publisher->service_event_topic_name = (char *) allocator.allocate( topic_length, allocator.state); RCL_CHECK_FOR_NULL_WITH_MSG( - service_event_publisher->impl->service_event_topic_name, + service_event_publisher->service_event_topic_name, "allocating memory for service introspection topic name failed", - ret = RCL_RET_BAD_ALLOC; goto free_impl;); + return RCL_RET_BAD_ALLOC;); snprintf( - service_event_publisher->impl->service_event_topic_name, + service_event_publisher->service_event_topic_name, topic_length, "%s%s", service_name, RCL_SERVICE_INTROSPECTION_TOPIC_POSTFIX); - service_event_publisher->impl->options._enabled = false; - service_event_publisher->impl->publisher = NULL; - ret = rcl_service_introspection_enable(service_event_publisher, node); + ret = introspection_create_publisher(service_event_publisher, node); if (ret != RCL_RET_OK) { goto free_topic_name; } RCUTILS_LOG_DEBUG_NAMED( ROS_PACKAGE_NAME, "Service introspection for service '%s' initialized", service_name); + return RCL_RET_OK; free_topic_name: - allocator.deallocate(service_event_publisher->impl->service_event_topic_name, allocator.state); - -free_impl: - allocator.deallocate(service_event_publisher->impl, allocator.state); - service_event_publisher->impl = NULL; + allocator.deallocate(service_event_publisher->service_event_topic_name, allocator.state); return ret; } @@ -173,21 +166,28 @@ rcl_ret_t rcl_service_event_publisher_fini( RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR); RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC); - // Skip checking service_event_publisher and node as they will - // be checked by rcl_service_introspection_disable - rcl_ret_t ret = rcl_service_introspection_disable(service_event_publisher, node); - if (RCL_RET_OK != ret && RCL_RET_ALREADY_SHUTDOWN != ret) { - RCL_SET_ERROR_MSG(rcl_get_error_string().str); - rcutils_reset_error(); - return ret; + if (!rcl_service_event_publisher_is_valid(service_event_publisher)) { + return RCL_RET_ERROR; } - rcl_allocator_t allocator = service_event_publisher->impl->options.publisher_options.allocator; - allocator.deallocate(service_event_publisher->impl->service_event_topic_name, allocator.state); - service_event_publisher->impl->service_event_topic_name = NULL; + if (!rcl_node_is_valid(node)) { + return RCL_RET_NODE_INVALID; + } - allocator.deallocate(service_event_publisher->impl, allocator.state); - service_event_publisher->impl = NULL; + rcl_allocator_t allocator = service_event_publisher->publisher_options.allocator; + RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_ERROR); + + if (service_event_publisher->publisher) { + rcl_ret_t ret = rcl_publisher_fini(service_event_publisher->publisher, node); + allocator.deallocate(service_event_publisher->publisher, allocator.state); + service_event_publisher->publisher = NULL; + if (RCL_RET_OK != ret) { + return ret; + } + } + + allocator.deallocate(service_event_publisher->service_event_topic_name, allocator.state); + service_event_publisher->service_event_topic_name = NULL; return RCL_RET_OK; } @@ -211,22 +211,21 @@ rcl_ret_t rcl_send_service_event_message( return RCL_RET_ERROR; } - // early exit if service introspection disabled during runtime - if (!service_event_publisher->impl->options._enabled) { - return RCL_RET_OK; + if (service_event_publisher->introspection_state == RCL_SERVICE_INTROSPECTION_OFF) { + return RCL_RET_ERROR; } - rcl_allocator_t allocator = service_event_publisher->impl->options.publisher_options.allocator; + rcl_allocator_t allocator = service_event_publisher->publisher_options.allocator; RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT); - if (!rcl_publisher_is_valid(service_event_publisher->impl->publisher)) { + if (!rcl_publisher_is_valid(service_event_publisher->publisher)) { return RCL_RET_PUBLISHER_INVALID; } rcl_ret_t ret; rcl_time_point_value_t now; - ret = rcl_clock_get_now(service_event_publisher->impl->options.clock, &now); + ret = rcl_clock_get_now(service_event_publisher->clock, &now); if (RMW_RET_OK != ret) { rcutils_reset_error(); RCL_SET_ERROR_MSG(rmw_get_error_string().str); @@ -243,20 +242,20 @@ rcl_ret_t rcl_send_service_event_message( memcpy(info.client_gid, guid, 16); void * service_introspection_message; - if (!service_event_publisher->impl->options._content_enabled) { + if (service_event_publisher->introspection_state == RCL_SERVICE_INTROSPECTION_METADATA) { ros_response_request = NULL; } switch (event_type) { case service_msgs__msg__ServiceEventInfo__REQUEST_RECEIVED: case service_msgs__msg__ServiceEventInfo__REQUEST_SENT: service_introspection_message = - service_event_publisher->impl->service_type_support->event_message_create_handle_function( + service_event_publisher->service_type_support->event_message_create_handle_function( &info, &allocator, ros_response_request, NULL); break; case service_msgs__msg__ServiceEventInfo__RESPONSE_RECEIVED: case service_msgs__msg__ServiceEventInfo__RESPONSE_SENT: service_introspection_message = - service_event_publisher->impl->service_type_support->event_message_create_handle_function( + service_event_publisher->service_type_support->event_message_create_handle_function( &info, &allocator, NULL, ros_response_request); break; default: @@ -268,10 +267,9 @@ rcl_ret_t rcl_send_service_event_message( service_introspection_message, "service_introspection_message is NULL", return RCL_RET_ERROR); // and publish it out! - // TODO(ihasdapie): Publisher context can become invalidated on shutdown - ret = rcl_publish(service_event_publisher->impl->publisher, service_introspection_message, NULL); + ret = rcl_publish(service_event_publisher->publisher, service_introspection_message, NULL); // clean up before error checking - service_event_publisher->impl->service_type_support->event_message_destroy_handle_function( + service_event_publisher->service_type_support->event_message_destroy_handle_function( service_introspection_message, &allocator); if (RCL_RET_OK != ret) { rcutils_reset_error(); @@ -281,93 +279,16 @@ rcl_ret_t rcl_send_service_event_message( return ret; } -rcl_ret_t rcl_service_introspection_enable( +rcl_ret_t +rcl_service_event_publisher_change_state( rcl_service_event_publisher_t * service_event_publisher, - const rcl_node_t * node) + rcl_service_introspection_state_t introspection_state) { - RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT); - RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR); - RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC); - RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID); - RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_TOPIC_NAME_INVALID); - - if (!rcl_service_event_publisher_is_valid(service_event_publisher)) { - return RCL_RET_ERROR; - } - - if (!rcl_node_is_valid(node)) { - return RCL_RET_NODE_INVALID; - } - // need to check if node_opt is disabled - - // Early exit if already enabled - if (service_event_publisher->impl->options._enabled) { - return RCL_RET_OK; - } - - rcl_allocator_t allocator = service_event_publisher->impl->options.publisher_options.allocator; - RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_ERROR); - - service_event_publisher->impl->publisher = allocator.allocate( - sizeof(rcl_publisher_t), allocator.state); - RCL_CHECK_FOR_NULL_WITH_MSG( - service_event_publisher->impl->publisher, - "allocate service_event_publisher failed in enable", return RCL_RET_BAD_ALLOC); - *service_event_publisher->impl->publisher = rcl_get_zero_initialized_publisher(); - rcl_ret_t ret = rcl_publisher_init( - service_event_publisher->impl->publisher, node, - service_event_publisher->impl->service_type_support->event_typesupport, - service_event_publisher->impl->service_event_topic_name, - &service_event_publisher->impl->options.publisher_options); - if (RCL_RET_OK != ret) { - allocator.deallocate(service_event_publisher->impl->publisher, allocator.state); - service_event_publisher->impl->publisher = NULL; - rcutils_reset_error(); - RCL_SET_ERROR_MSG(rcl_get_error_string().str); - return ret; - } - - service_event_publisher->impl->options._enabled = true; - return RCL_RET_OK; -} - -rcl_ret_t rcl_service_introspection_disable( - rcl_service_event_publisher_t * service_event_publisher, - rcl_node_t * node) -{ - RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_PUBLISHER_INVALID); - RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID); - RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT); - RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR); - RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ALREADY_SHUTDOWN); - if (!rcl_service_event_publisher_is_valid(service_event_publisher)) { return RCL_RET_ERROR; } - if (!rcl_node_is_valid(node)) { - return RCL_RET_NODE_INVALID; - } - - // Early exit if already disabled - if (!service_event_publisher->impl->options._enabled) { - return RCL_RET_OK; - } - - rcl_allocator_t allocator = service_event_publisher->impl->options.publisher_options.allocator; - RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_ERROR); - - if (service_event_publisher->impl->publisher) { - rcl_ret_t ret = rcl_publisher_fini(service_event_publisher->impl->publisher, node); - allocator.deallocate(service_event_publisher->impl->publisher, allocator.state); - service_event_publisher->impl->publisher = NULL; - if (RCL_RET_OK != ret) { - rcutils_reset_error(); - RCL_SET_ERROR_MSG(rmw_get_error_string().str); - return ret; - } - } + service_event_publisher->introspection_state = introspection_state; - service_event_publisher->impl->options._enabled = false; return RCL_RET_OK; } diff --git a/rcl/src/rcl/service_event_publisher.h b/rcl/src/rcl/service_event_publisher.h index d546b8caa..05f9af481 100644 --- a/rcl/src/rcl/service_event_publisher.h +++ b/rcl/src/rcl/service_event_publisher.h @@ -23,63 +23,109 @@ extern "C" #include "rcl/macros.h" #include "rcl/node.h" #include "rcl/publisher.h" +#include "rcl/service_introspection.h" #include "rcl/time.h" #include "rcl/types.h" #include "rcl/visibility_control.h" #include "rosidl_runtime_c/service_type_support_struct.h" - -typedef struct rcl_service_event_publisher_options_s -{ - // Enable/disable service introspection during runtime - bool _enabled; - // Enable/disable including request/response payload in service event message during runtime - bool _content_enabled; - /// Handle to clock for timestamping service events - rcl_clock_t * clock; - /// publisher options for service event publisher - rcl_publisher_options_t publisher_options; -} rcl_service_event_publisher_options_t; - -typedef struct rcl_service_event_publisher_impl_s +typedef struct rcl_service_event_publisher_s { /// Handle to publisher for publishing service events rcl_publisher_t * publisher; /// Name of service introspection topic: / char * service_event_topic_name; - /// rcl_service_event_publisher options - rcl_service_event_publisher_options_t options; + /// Current state of introspection; off, metadata, or contents + rcl_service_introspection_state_t introspection_state; + /// Handle to clock for timestamping service events + rcl_clock_t * clock; + /// Publisher options for service event publisher + rcl_publisher_options_t publisher_options; /// Handle to service typesupport const rosidl_service_type_support_t * service_type_support; -} rcl_service_event_publisher_impl_t; - -typedef struct rcl_service_event_publisher_s -{ - /// Pointer to implementation struct - rcl_service_event_publisher_impl_t * impl; } rcl_service_event_publisher_t; -RCL_PUBLIC -RCL_WARN_UNUSED -rcl_service_event_publisher_options_t -rcl_service_event_publisher_get_default_options(); - +/// Return a rcl_service_event_publisher_t struct with members set to `NULL`. +/** + * Should be called to get a null rcl_service_event_publisher_t before passing to + * rcl_service_event_publisher_init(). + */ RCL_PUBLIC RCL_WARN_UNUSED rcl_service_event_publisher_t rcl_get_zero_initialized_service_event_publisher(); +/// Initialize a service event publisher. +/** + * After calling this function on a rcl_service_event_publisher_t, it can be used to + * send service introspection messages by calling rcl_send_service_event_message(). + * + * The given rcl_node_t must be valid and the resulting rcl_service_event_publisher_t is + * only valid as long as the given rcl_node_t remains valid. + * + * Similarly, the given rcl_clock_t must be valid and the resulting rcl_service_event_publisher_t + * is only valid as long as the given rcl_clock_t remains valid. + * + * The passed in service_name should be the fully-qualified, remapped service name. + * The service event publisher will add a custom suffix as the topic name. + * + * 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. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | Maybe [1] + * Lock-Free | Maybe [1] + * [1] rmw implementation defined + * + * \param[inout] service_event_publisher preallocated rcl_service_event_publisher_t + * \param[in] node valid rcl_node_t to use to create the introspection publisher + * \param[in] clock valid rcl_clock_t to use to generate the introspection timestamps + * \param[in] publisher_options options to use when creating the introspection publisher + * \param[in] service_name fully-qualified and remapped service name + * \param[in] service_type_support type support library associated with this service + * \return #RCL_RET_OK if the call was successful + * \return #RCL_RET_INVALID_ARGUMENT if the event publisher, client, or node is invalid, + * \return #RCL_RET_NODE_INVALID if the given node is invalid, or + * \return #RCL_RET_BAD_ALLOC if a memory allocation failed, or + */ RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_service_event_publisher_init( rcl_service_event_publisher_t * service_event_publisher, const rcl_node_t * node, - const rcl_service_event_publisher_options_t * options, + rcl_clock_t * clock, + const rcl_publisher_options_t publisher_options, const char * service_name, const rosidl_service_type_support_t * service_type_support); +/// Finalize a rcl_service_event_publisher_t. +/** + * After calling this function, calls to any of the other functions here + * (except for rcl_service_event_publisher_init()) will fail. + * However, the given node handle is still valid. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[inout] service_event_publisher handle to the event publisher to be finalized + * \param[in] node a valid (not finalized) handle to the node used to create the client + * \return #RCL_RET_OK if client was finalized successfully, or + * \return #RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or + * \return #RCL_RET_NODE_INVALID if the node is invalid, or + * \return #RCL_RET_ERROR if an unspecified error occurs. + */ RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t @@ -87,10 +133,58 @@ rcl_service_event_publisher_fini( rcl_service_event_publisher_t * service_event_publisher, rcl_node_t * node); +/// Check that the service event publisher is valid. +/** + * The bool returned is `false` if the service event publisher is invalid. + * The bool returned is `true` otherwise. + * In the case where `false` is returned, an error message is set. + * This function cannot fail. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | No + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] service_event_publisher pointer to the service event publisher + * \return `true` if `service_event_publisher` is valid, otherwise `false` + */ RCL_PUBLIC bool rcl_service_event_publisher_is_valid(const rcl_service_event_publisher_t * service_event_publisher); +/// Send a service event message. +/** + * It is the job of the caller to ensure that the type of the `ros_request` + * parameter and the type associated with the event publisher (via the type support) + * match. + * Passing a different type to publish produces undefined behavior and cannot + * be checked by this function and therefore no deliberate error will occur. + * + * rcl_send_service_event_message() is a potentially blocking call. + * + * The ROS request message given by the `ros_response_request` void pointer is always + * owned by the calling code, but should remain constant during rcl_send_service_event_message(). + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | No + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] service_event_publisher pointer to the service event publisher + * \param[in] event_type introspection event type from service_msgs::msg::ServiceEventInfo + * \param[in] ros_response_request type-erased pointer to the ROS response request + * \param[in] sequence_number sequence number of the event + * \param[in] guid GUID associated with this event + * \return #RCL_RET_OK if the event was published successfully, or + * \return #RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or + * \return #RCL_RET_ERROR if an unspecified error occurs. + */ RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t @@ -101,27 +195,18 @@ rcl_send_service_event_message( int64_t sequence_number, const uint8_t guid[16]); -/* Enables service introspection by reconstructing the introspection clock and publisher - * - * Does nothing and returns RCL_RET_OK if already enabled +/// Change the operating state of this service event publisher. +/** + * \param[in] service_event_publisher pointer to the service event publisher + * \param[in] introspection_state new introspection state + * \return #RCL_RET_OK if the event was published successfully, or + * \return #RCL_RET_ERROR if an unspecified error occurs. */ RCL_PUBLIC -RCL_WARN_UNUSED rcl_ret_t -rcl_service_introspection_enable( +rcl_service_event_publisher_change_state( rcl_service_event_publisher_t * service_event_publisher, - const rcl_node_t * node); - -/* Disables service introspection by fini-ing and freeing the introspection clock and publisher - * - * Does nothing and returns RCL_RET_OK if already disabled - */ -RCL_PUBLIC -RCL_WARN_UNUSED -rcl_ret_t -rcl_service_introspection_disable( - rcl_service_event_publisher_t * service_event_publisher, - rcl_node_t * node); + rcl_service_introspection_state_t introspection_state); #ifdef __cplusplus } diff --git a/rcl/src/rcl/service_impl.h b/rcl/src/rcl/service_impl.h deleted file mode 100644 index 8f0e31171..000000000 --- a/rcl/src/rcl/service_impl.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2022 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_IMPL_H_ -#define RCL__SERVICE_IMPL_H_ - - -#include "rcl/service.h" -#include "./service_event_publisher.h" - -struct rcl_service_impl_s -{ - rcl_service_options_t options; - rmw_qos_profile_t actual_request_subscription_qos; - rmw_qos_profile_t actual_response_publisher_qos; - rmw_service_t * rmw_handle; - rcl_service_event_publisher_t * service_event_publisher; -}; - -#endif // RCL__SERVICE_IMPL_H_ diff --git a/rcl/test/rcl/test_service_event_publisher.cpp b/rcl/test/rcl/test_service_event_publisher.cpp index da93a1d92..bab3e0c03 100644 --- a/rcl/test/rcl/test_service_event_publisher.cpp +++ b/rcl/test/rcl/test_service_event_publisher.cpp @@ -53,6 +53,7 @@ class CLASSNAME (TestServiceEventPublisherFixture, RMW_IMPLEMENTATION) : public rcl_ret_t ret; const rosidl_service_type_support_t * srv_ts = ROSIDL_GET_SRV_TYPE_SUPPORT(test_msgs, srv, BasicTypes); + void SetUp() override { rcl_ret_t ret; @@ -74,7 +75,6 @@ class CLASSNAME (TestServiceEventPublisherFixture, RMW_IMPLEMENTATION) : public *this->node_ptr = rcl_get_zero_initialized_node(); const char * name = "test_service_event_publisher_node"; rcl_node_options_t node_options = rcl_node_get_default_options(); - node_options.enable_service_introspection = true; ret = rcl_node_init(this->node_ptr, name, "", this->context_ptr, &node_options); ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; } @@ -100,18 +100,19 @@ TEST_F( { rcl_service_event_publisher_t service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); - rcl_service_event_publisher_options_t service_event_publisher_options = - rcl_service_event_publisher_get_default_options(); rcl_clock_t clock; - service_event_publisher_options.clock = &clock; ret = rcl_clock_init(RCL_STEADY_TIME, &clock, &allocator); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ret = rcl_service_event_publisher_init( - &service_event_publisher, this->node_ptr, &service_event_publisher_options, + &service_event_publisher, this->node_ptr, &clock, rcl_publisher_get_default_options(), "test_service_event_publisher", srv_ts); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + + ret = rcl_service_event_publisher_change_state( + &service_event_publisher, RCL_SERVICE_INTROSPECTION_METADATA); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; test_msgs__srv__BasicTypes_Request client_request; test_msgs__srv__BasicTypes_Request__init(&client_request); @@ -125,7 +126,6 @@ TEST_F( ret = rcl_send_service_event_message( &service_event_publisher, service_msgs__msg__ServiceEventInfo__REQUEST_SENT, &client_request, sequence_number, guid); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ret = rcl_service_event_publisher_fini(&service_event_publisher, this->node_ptr); @@ -138,55 +138,46 @@ TEST_F( { rcl_service_event_publisher_t service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); - rcl_service_event_publisher_options_t service_event_publisher_options = - rcl_service_event_publisher_get_default_options(); rcl_clock_t clock; ret = rcl_service_event_publisher_init( - &service_event_publisher, nullptr, &service_event_publisher_options, + &service_event_publisher, nullptr, &clock, rcl_publisher_get_default_options(), "test_service_event_publisher", srv_ts); EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcutils_reset_error(); ret = rcl_service_event_publisher_init( - &service_event_publisher, this->node_ptr, &service_event_publisher_options, - "test_service_event_publisher", srv_ts); - EXPECT_EQ(RCL_RET_ERROR, ret) << rcl_get_error_string().str; - - ret = rcl_service_event_publisher_init( - &service_event_publisher, node_ptr, &service_event_publisher_options, + &service_event_publisher, this->node_ptr, nullptr, rcl_publisher_get_default_options(), "test_service_event_publisher", srv_ts); EXPECT_EQ(RCL_RET_ERROR, ret) << rcl_get_error_string().str; + rcutils_reset_error(); ret = rcl_clock_init(RCL_STEADY_TIME, &clock, &allocator); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - service_event_publisher_options.clock = &clock; - ret = rcl_service_event_publisher_init( - &service_event_publisher, node_ptr, &service_event_publisher_options, - "test_service_event_publisher", srv_ts); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ret = rcl_service_event_publisher_init( - &service_event_publisher, node_ptr, &service_event_publisher_options, + &service_event_publisher, node_ptr, &clock, rcl_publisher_get_default_options(), "test_service_event_publisher", srv_ts); - EXPECT_EQ(RCL_RET_ALREADY_INIT, ret) << rcl_get_error_string().str; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ret = rcl_service_event_publisher_fini(&service_event_publisher, nullptr); EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string().str; + rcutils_reset_error(); ret = rcl_service_event_publisher_fini(&service_event_publisher, node_ptr); EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); ret = rcl_service_event_publisher_init( - &service_event_publisher, node_ptr, &service_event_publisher_options, + &service_event_publisher, node_ptr, &clock, rcl_publisher_get_default_options(), "123test_service_event_publisher<>h", srv_ts); EXPECT_EQ(RCL_RET_TOPIC_NAME_INVALID, ret) << rcl_get_error_string().str; + rcutils_reset_error(); service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); ret = rcl_service_event_publisher_init( - &service_event_publisher, node_ptr, &service_event_publisher_options, + &service_event_publisher, node_ptr, &clock, rcl_publisher_get_default_options(), "test_service_event_publisher", srv_ts); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ret = rcl_service_event_publisher_fini(&service_event_publisher, node_ptr); EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; @@ -196,9 +187,10 @@ TEST_F( auto mock = mocking_utils::patch_to_fail( "lib:rcl", rcl_publisher_init, "patch rcl_publisher_init to fail", RCL_RET_ERROR); ret = rcl_service_event_publisher_init( - &service_event_publisher, node_ptr, &service_event_publisher_options, + &service_event_publisher, node_ptr, &clock, rcl_publisher_get_default_options(), "test_service_event_publisher", srv_ts); EXPECT_EQ(RCL_RET_ERROR, ret) << rcl_get_error_string().str; + rcutils_reset_error(); } /* Test sending service introspection message via service_event_publisher.h @@ -214,21 +206,24 @@ TEST_F( rcl_service_event_publisher_t service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); - rcl_service_event_publisher_options_t service_event_publisher_options = - rcl_service_event_publisher_get_default_options(); rcl_clock_t clock; ret = rcl_clock_init(RCL_STEADY_TIME, &clock, &allocator); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - service_event_publisher_options.clock = &clock; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ret = rcl_service_event_publisher_init( - &service_event_publisher, node_ptr, &service_event_publisher_options, topic.c_str(), srv_ts); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + &service_event_publisher, node_ptr, &clock, rcl_publisher_get_default_options(), + topic.c_str(), srv_ts); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( { ret = rcl_service_event_publisher_fini(&service_event_publisher, node_ptr); - ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; }); + ret = rcl_service_event_publisher_change_state( + &service_event_publisher, RCL_SERVICE_INTROSPECTION_CONTENTS); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + rcl_subscription_t subscription = rcl_get_zero_initialized_subscription(); ret = rcl_subscription_init( &subscription, node_ptr, srv_ts->event_typesupport, service_event_topic.c_str(), &sub_opts); @@ -247,29 +242,26 @@ TEST_F( test_req.uint32_value = 123; OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({test_msgs__srv__BasicTypes_Request__fini(&test_req);}); - { - ret = rcl_send_service_event_message( - &service_event_publisher, service_msgs__msg__ServiceEventInfo__REQUEST_RECEIVED, &test_req, 1, - guid); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - } + ret = rcl_send_service_event_message( + &service_event_publisher, service_msgs__msg__ServiceEventInfo__REQUEST_RECEIVED, &test_req, 1, + guid); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ASSERT_TRUE(wait_for_subscription_to_be_ready(&subscription, context_ptr, 10, 100)); - { - rmw_message_info_t message_info = rmw_get_zero_initialized_message_info(); - test_msgs__srv__BasicTypes_Event event_msg; - test_msgs__srv__BasicTypes_Event__init(&event_msg); - OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({test_msgs__srv__BasicTypes_Event__fini(&event_msg);}); - ret = rcl_take(&subscription, &event_msg, &message_info, nullptr); - ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - ASSERT_EQ(1, event_msg.info.sequence_number); - ASSERT_EQ(0, memcmp(guid, event_msg.info.client_gid, sizeof(guid))); - ASSERT_EQ(0U, event_msg.response.size); - ASSERT_EQ(1U, event_msg.request.size); - ASSERT_EQ(test_req.bool_value, event_msg.request.data[0].bool_value); - ASSERT_EQ(test_req.uint16_value, event_msg.request.data[0].uint16_value); - ASSERT_EQ(test_req.uint32_value, event_msg.request.data[0].uint32_value); - } + + rmw_message_info_t message_info = rmw_get_zero_initialized_message_info(); + test_msgs__srv__BasicTypes_Event event_msg; + test_msgs__srv__BasicTypes_Event__init(&event_msg); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({test_msgs__srv__BasicTypes_Event__fini(&event_msg);}); + ret = rcl_take(&subscription, &event_msg, &message_info, nullptr); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(1, event_msg.info.sequence_number); + ASSERT_EQ(0, memcmp(guid, event_msg.info.client_gid, sizeof(guid))); + ASSERT_EQ(0U, event_msg.response.size); + ASSERT_EQ(1U, event_msg.request.size); + ASSERT_EQ(test_req.bool_value, event_msg.request.data[0].bool_value); + ASSERT_EQ(test_req.uint16_value, event_msg.request.data[0].uint16_value); + ASSERT_EQ(test_req.uint32_value, event_msg.request.data[0].uint32_value); } TEST_F( @@ -278,21 +270,23 @@ TEST_F( { rcl_service_event_publisher_t service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); - rcl_service_event_publisher_options_t service_event_publisher_options = - rcl_service_event_publisher_get_default_options(); uint8_t guid[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; char topic[] = "test_service_event_publisher"; rcl_clock_t clock; ret = rcl_clock_init(RCL_STEADY_TIME, &clock, &allocator); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - service_event_publisher_options.clock = &clock; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ret = rcl_service_event_publisher_init( - &service_event_publisher, node_ptr, &service_event_publisher_options, topic, srv_ts); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + &service_event_publisher, node_ptr, &clock, rcl_publisher_get_default_options(), topic, srv_ts); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + + ret = rcl_service_event_publisher_change_state( + &service_event_publisher, RCL_SERVICE_INTROSPECTION_METADATA); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ret = rcl_send_service_event_message(nullptr, 0, nullptr, 0, nullptr); EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcutils_reset_error(); test_msgs__srv__BasicTypes_Request test_req; test_msgs__srv__BasicTypes_Request__init(&test_req); @@ -305,14 +299,16 @@ TEST_F( &service_event_publisher, service_msgs__msg__ServiceEventInfo__REQUEST_SENT, &test_req, 0, nullptr); EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcutils_reset_error(); ret = rcl_send_service_event_message( &service_event_publisher, service_msgs__msg__ServiceEventInfo__RESPONSE_RECEIVED, &test_req, 0, guid); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ret = rcl_send_service_event_message(&service_event_publisher, 5, &test_req, 0, guid); EXPECT_EQ(RCL_RET_ERROR, ret) << rcl_get_error_string().str; + rcutils_reset_error(); ret = rcl_service_event_publisher_fini(&service_event_publisher, node_ptr); EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; @@ -324,20 +320,17 @@ TEST_F( { rcl_service_event_publisher_t service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); - rcl_service_event_publisher_options_t service_event_publisher_options = - rcl_service_event_publisher_get_default_options(); rcl_clock_t clock; ret = rcl_clock_init(RCL_STEADY_TIME, &clock, &allocator); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - service_event_publisher_options.clock = &clock; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ret = rcl_service_event_publisher_init( - &service_event_publisher, node_ptr, &service_event_publisher_options, + &service_event_publisher, node_ptr, &clock, rcl_publisher_get_default_options(), "test_service_event_publisher", srv_ts); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; EXPECT_TRUE(rcl_service_event_publisher_is_valid(&service_event_publisher)); - rcl_publisher_fini(service_event_publisher.impl->publisher, node_ptr); + rcl_publisher_fini(service_event_publisher.publisher, node_ptr); EXPECT_TRUE(rcl_service_event_publisher_is_valid(&service_event_publisher)); ret = rcl_service_event_publisher_fini(&service_event_publisher, node_ptr); @@ -345,17 +338,18 @@ TEST_F( service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); ret = rcl_service_event_publisher_init( - &service_event_publisher, node_ptr, &service_event_publisher_options, + &service_event_publisher, node_ptr, &clock, rcl_publisher_get_default_options(), "test_service_event_publisher", srv_ts); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - service_event_publisher.impl->options.clock = nullptr; + service_event_publisher.clock = nullptr; EXPECT_FALSE(rcl_service_event_publisher_is_valid(&service_event_publisher)); ret = rcl_service_event_publisher_fini(&service_event_publisher, node_ptr); EXPECT_EQ(RCL_RET_ERROR, ret) << rcl_get_error_string().str; + rcutils_reset_error(); - service_event_publisher.impl->options.clock = &clock; + service_event_publisher.clock = &clock; ret = rcl_service_event_publisher_fini(&service_event_publisher, node_ptr); EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; } @@ -366,31 +360,32 @@ TEST_F( { rcl_service_event_publisher_t service_event_publisher = rcl_get_zero_initialized_service_event_publisher(); - rcl_service_event_publisher_options_t service_event_publisher_options = - rcl_service_event_publisher_get_default_options(); rcl_clock_t clock; ret = rcl_clock_init(RCL_STEADY_TIME, &clock, &allocator); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - service_event_publisher_options.clock = &clock; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ret = rcl_service_event_publisher_init( - &service_event_publisher, node_ptr, &service_event_publisher_options, + &service_event_publisher, node_ptr, &clock, rcl_publisher_get_default_options(), "test_service_event_publisher", srv_ts); - EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - // ok to enable twice, starts enabled + // ok to enable twice EXPECT_EQ( - RCL_RET_OK, rcl_service_introspection_enable(&service_event_publisher, node_ptr)); - - EXPECT_EQ(RCL_RET_OK, rcl_service_introspection_disable(&service_event_publisher, node_ptr)); - + RCL_RET_OK, + rcl_service_event_publisher_change_state( + &service_event_publisher, RCL_SERVICE_INTROSPECTION_METADATA)); EXPECT_EQ( RCL_RET_OK, - rcl_service_introspection_disable(&service_event_publisher, node_ptr)); - - EXPECT_EQ(RCL_RET_OK, rcl_service_introspection_enable(&service_event_publisher, node_ptr)); + rcl_service_event_publisher_change_state( + &service_event_publisher, RCL_SERVICE_INTROSPECTION_METADATA)); EXPECT_EQ( - RCL_RET_OK, rcl_service_introspection_enable(&service_event_publisher, node_ptr)); + RCL_RET_OK, + rcl_service_event_publisher_change_state( + &service_event_publisher, RCL_SERVICE_INTROSPECTION_OFF)); + EXPECT_EQ( + RCL_RET_OK, + rcl_service_event_publisher_change_state( + &service_event_publisher, RCL_SERVICE_INTROSPECTION_OFF)); ret = rcl_service_event_publisher_fini(&service_event_publisher, node_ptr); EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; @@ -400,13 +395,6 @@ class CLASSNAME (TestServiceEventPublisherWithServicesAndClientsFixture, RMW_IMP : public ::testing::Test { public: - rcl_context_t * context_ptr; - rcl_node_t * node_ptr; - rcl_service_t service; - rcl_client_t client; - rcl_clock_t clock; - rcl_subscription_t subscription; - rosidl_service_type_support_t * srv_ts; void SetUp() { rcl_allocator_t allocator = rcl_get_default_allocator(); @@ -428,10 +416,9 @@ class CLASSNAME (TestServiceEventPublisherWithServicesAndClientsFixture, RMW_IMP *this->node_ptr = rcl_get_zero_initialized_node(); const char * name = "test_service_node"; rcl_node_options_t node_options = rcl_node_get_default_options(); - node_options.enable_service_introspection = true; ret = rcl_node_init(this->node_ptr, name, "", this->context_ptr, &node_options); ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - const rosidl_service_type_support_t * srv_ts = + srv_ts = ROSIDL_GET_SRV_TYPE_SUPPORT(test_msgs, srv, BasicTypes); // rcl_clock_t clock; @@ -443,18 +430,24 @@ class CLASSNAME (TestServiceEventPublisherWithServicesAndClientsFixture, RMW_IMP service = rcl_get_zero_initialized_service(); rcl_service_options_t service_options = rcl_service_get_default_options(); - service_options.enable_service_introspection = true; - service_options.clock = &clock; ret = rcl_service_init(&service, this->node_ptr, srv_ts, srv_name.c_str(), &service_options); ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ret = rcl_service_configure_service_introspection( + &service, this->node_ptr, &clock, srv_ts, rcl_publisher_get_default_options(), + RCL_SERVICE_INTROSPECTION_CONTENTS); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + client = rcl_get_zero_initialized_client(); rcl_client_options_t client_options = rcl_client_get_default_options(); - client_options.enable_service_introspection = true; - client_options.clock = &clock; ret = rcl_client_init(&client, this->node_ptr, srv_ts, srv_name.c_str(), &client_options); ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ret = rcl_client_configure_service_introspection( + &client, this->node_ptr, &clock, srv_ts, rcl_publisher_get_default_options(), + RCL_SERVICE_INTROSPECTION_CONTENTS); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + subscription = rcl_get_zero_initialized_subscription(); rcl_subscription_options_t subscription_options = rcl_subscription_get_default_options(); ret = rcl_subscription_init( @@ -478,6 +471,15 @@ class CLASSNAME (TestServiceEventPublisherWithServicesAndClientsFixture, RMW_IMP delete this->context_ptr; EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; } + +protected: + rcl_context_t * context_ptr; + rcl_node_t * node_ptr; + rcl_service_t service; + rcl_client_t client; + rcl_clock_t clock; + rcl_subscription_t subscription; + const rosidl_service_type_support_t * srv_ts; }; /* Whole test of service event publisher with service, client, and subscription @@ -507,49 +509,44 @@ TEST_F( ASSERT_TRUE(wait_for_service_to_be_ready(&service, context_ptr, 10, 100)); - { // expect a REQUEST_SENT event - ASSERT_TRUE(wait_for_subscription_to_be_ready(&subscription, context_ptr, 10, 100)); - ret = rcl_take(&subscription, &event_msg, &message_info, nullptr); - ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - ASSERT_EQ(service_msgs__msg__ServiceEventInfo__REQUEST_SENT, event_msg.info.event_type); - } + // expect a REQUEST_SENT event + ASSERT_TRUE(wait_for_subscription_to_be_ready(&subscription, context_ptr, 10, 100)); + ret = rcl_take(&subscription, &event_msg, &message_info, nullptr); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(service_msgs__msg__ServiceEventInfo__REQUEST_SENT, event_msg.info.event_type); - { - test_msgs__srv__BasicTypes_Response service_response; - memset(&service_response, 0, sizeof(test_msgs__srv__BasicTypes_Response)); - test_msgs__srv__BasicTypes_Response__init(&service_response); - OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( - {test_msgs__srv__BasicTypes_Response__fini(&service_response);}); + test_msgs__srv__BasicTypes_Response service_response; + memset(&service_response, 0, sizeof(test_msgs__srv__BasicTypes_Response)); + test_msgs__srv__BasicTypes_Response__init(&service_response); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + {test_msgs__srv__BasicTypes_Response__fini(&service_response);}); - test_msgs__srv__BasicTypes_Request service_request; - memset(&service_request, 0, sizeof(test_msgs__srv__BasicTypes_Request)); - test_msgs__srv__BasicTypes_Request__init(&service_request); + test_msgs__srv__BasicTypes_Request service_request; + memset(&service_request, 0, sizeof(test_msgs__srv__BasicTypes_Request)); + test_msgs__srv__BasicTypes_Request__init(&service_request); - rmw_service_info_t header; - ret = rcl_take_request( - &service, &(header.request_id), &service_request); - ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - ASSERT_EQ(2U, service_request.uint32_value); + rmw_service_info_t header; + ret = rcl_take_request( + &service, &(header.request_id), &service_request); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(2U, service_request.uint32_value); - { // expect a REQUEST_RECEIVED event - ret = rcl_take(&subscription, &event_msg, &message_info, nullptr); - ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - ASSERT_EQ(service_msgs__msg__ServiceEventInfo__REQUEST_RECEIVED, event_msg.info.event_type); - } + // expect a REQUEST_RECEIVED event + ret = rcl_take(&subscription, &event_msg, &message_info, nullptr); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(service_msgs__msg__ServiceEventInfo__REQUEST_RECEIVED, event_msg.info.event_type); - service_response.uint32_value = 2; - service_response.uint8_value = 3; - ret = rcl_send_response(&service, &header.request_id, &service_response); - ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + service_response.uint32_value = 2; + service_response.uint8_value = 3; + ret = rcl_send_response(&service, &header.request_id, &service_response); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - { // expect a RESPONSE_SEND event - OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({test_msgs__srv__BasicTypes_Event__fini(&event_msg);}); - ret = rcl_take(&subscription, &event_msg, &message_info, nullptr); - ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - ASSERT_EQ(service_msgs__msg__ServiceEventInfo__RESPONSE_SENT, event_msg.info.event_type); - } - test_msgs__srv__BasicTypes_Request__fini(&service_request); - } + // expect a RESPONSE_SEND event + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({test_msgs__srv__BasicTypes_Event__fini(&event_msg);}); + ret = rcl_take(&subscription, &event_msg, &message_info, nullptr); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(service_msgs__msg__ServiceEventInfo__RESPONSE_SENT, event_msg.info.event_type); + test_msgs__srv__BasicTypes_Request__fini(&service_request); ASSERT_TRUE( wait_for_client_to_be_ready(&client, context_ptr, 10, 100)); @@ -558,17 +555,16 @@ TEST_F( memset(&client_response, 0, sizeof(test_msgs__srv__BasicTypes_Response)); test_msgs__srv__BasicTypes_Response__init(&client_response); - rmw_service_info_t header; ret = rcl_take_response(&client, &(header.request_id), &client_response); ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; test_msgs__srv__BasicTypes_Response__fini(&client_response); - { // expect a RESPONSE_RECEIVED event - ret = rcl_take(&subscription, &event_msg, &message_info, nullptr); - ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; - ASSERT_EQ(service_msgs__msg__ServiceEventInfo__RESPONSE_RECEIVED, event_msg.info.event_type); - ASSERT_EQ(2U, event_msg.response.data[0].uint32_value); - } + // expect a RESPONSE_RECEIVED event + ret = rcl_take(&subscription, &event_msg, &message_info, nullptr); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + ASSERT_EQ(service_msgs__msg__ServiceEventInfo__RESPONSE_RECEIVED, event_msg.info.event_type); + ASSERT_EQ(1U, event_msg.response.size); + ASSERT_EQ(2U, event_msg.response.data[0].uint32_value); test_msgs__srv__BasicTypes_Event__fini(&event_msg); } @@ -584,7 +580,9 @@ TEST_F( test_msgs__srv__BasicTypes_Event event_msg; test_msgs__srv__BasicTypes_Event__init(&event_msg); - ret = rcl_service_introspection_configure_server_service_events(&service, node_ptr, false); + ret = rcl_service_configure_service_introspection( + &service, node_ptr, &clock, srv_ts, rcl_publisher_get_default_options(), + RCL_SERVICE_INTROSPECTION_OFF); ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; ASSERT_TRUE(wait_for_server_to_be_available(this->node_ptr, &client, 10, 1000));