From 973fbf28aed8359bc61473c71a9c1a7ab7f79b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santiago=20Tapia-Fern=C3=A1ndez?= Date: Wed, 13 Nov 2024 12:36:39 +0100 Subject: [PATCH 1/2] add delayed service example in c++ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santiago Tapia-Fernández --- .../services/delayed_service/CMakeLists.txt | 29 ++++++++ rclcpp/services/delayed_service/README.md | 9 +++ rclcpp/services/delayed_service/main.cpp | 67 +++++++++++++++++++ rclcpp/services/delayed_service/package.xml | 30 +++++++++ 4 files changed, 135 insertions(+) create mode 100644 rclcpp/services/delayed_service/CMakeLists.txt create mode 100644 rclcpp/services/delayed_service/README.md create mode 100644 rclcpp/services/delayed_service/main.cpp create mode 100644 rclcpp/services/delayed_service/package.xml diff --git a/rclcpp/services/delayed_service/CMakeLists.txt b/rclcpp/services/delayed_service/CMakeLists.txt new file mode 100644 index 00000000..2c6e906d --- /dev/null +++ b/rclcpp/services/delayed_service/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.5) +project(examples_rclcpp_delayed_service) + +# Default to C++17 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake REQUIRED) +find_package(example_interfaces REQUIRED) +find_package(rclcpp REQUIRED) + +add_executable(service_main main.cpp) +ament_target_dependencies(service_main rclcpp example_interfaces) + +install(TARGETS service_main + DESTINATION lib/${PROJECT_NAME}) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/rclcpp/services/delayed_service/README.md b/rclcpp/services/delayed_service/README.md new file mode 100644 index 00000000..ad00c180 --- /dev/null +++ b/rclcpp/services/delayed_service/README.md @@ -0,0 +1,9 @@ +# Delayed service + +This package contains an example that includes a node that +implements *add_two_ints* service and has a parameter to +hold the response for a given time. + +The goal of this service is helping programmers to understand +how asyncronous services work. + diff --git a/rclcpp/services/delayed_service/main.cpp b/rclcpp/services/delayed_service/main.cpp new file mode 100644 index 00000000..d149d81f --- /dev/null +++ b/rclcpp/services/delayed_service/main.cpp @@ -0,0 +1,67 @@ +// Copyright 2024 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "example_interfaces/srv/add_two_ints.hpp" +#include "rclcpp/rclcpp.hpp" + +class DelayedSumService : public rclcpp::Node +{ +public: + DelayedSumService() + : Node("delayed_service") + { + // Declares a parameter for delaying (default to 2.0 seconds) + this->declare_parameter("response_delay", 2.0); + + service_ = this->create_service( + "add_two_ints", std::bind( + &DelayedSumService::add_two_ints_callback, this, std::placeholders::_1, + std::placeholders::_2)); + + RCLCPP_INFO(this->get_logger(), "DelayedSumService is ready."); + } + +private: + void add_two_ints_callback( + const std::shared_ptr request, + std::shared_ptr response) + { + // Gets parameter value + double delay; + this->get_parameter("response_delay", delay); + + auto result = request->a + request->b; + RCLCPP_INFO_STREAM( + this->get_logger(), + "Request:" << request->a << " + " << request->b << " delayed " << delay << " seconds"); + + // Simulates the delay + std::this_thread::sleep_for(std::chrono::duration(delay)); + + response->sum = result; + RCLCPP_INFO_STREAM(this->get_logger(), "Response: " << result); + } + + rclcpp::Service::SharedPtr service_; +}; + +int main(int argc, char ** argv) +{ + rclcpp::init(argc, argv); + auto node = std::make_shared(); + rclcpp::spin(node); + rclcpp::shutdown(); + return 0; +} diff --git a/rclcpp/services/delayed_service/package.xml b/rclcpp/services/delayed_service/package.xml new file mode 100644 index 00000000..9e0b56c0 --- /dev/null +++ b/rclcpp/services/delayed_service/package.xml @@ -0,0 +1,30 @@ + + + + examples_rclcpp_delayed_service + 0.20.2 + A service server which adds two numbers but imposes a delay + in the response for showing the effect of delayed responses at time scale + that can be observed by the user/programmer. + + Santiago Tapia-Fernández + + Apache License 2.0 + + Santiago Tapia-Fernández + + ament_cmake + + rclcpp + example_interfaces + + rclcpp + example_interfaces + + ament_lint_auto + ament_lint_common + + + ament_cmake + + From ebfa3dc6d96fb72699813a2313c4a5229233eac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santiago=20Tapia-Fern=C3=A1ndez?= Date: Wed, 13 Nov 2024 13:30:17 +0100 Subject: [PATCH 2/2] add another async client example in c++ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santiago Tapia-Fernández --- .../async_recv_cb_client/CMakeLists.txt | 30 +++++++ .../services/async_recv_cb_client/README.md | 53 +++++++++++ rclcpp/services/async_recv_cb_client/main.cpp | 90 +++++++++++++++++++ .../services/async_recv_cb_client/package.xml | 31 +++++++ 4 files changed, 204 insertions(+) create mode 100644 rclcpp/services/async_recv_cb_client/CMakeLists.txt create mode 100644 rclcpp/services/async_recv_cb_client/README.md create mode 100644 rclcpp/services/async_recv_cb_client/main.cpp create mode 100644 rclcpp/services/async_recv_cb_client/package.xml diff --git a/rclcpp/services/async_recv_cb_client/CMakeLists.txt b/rclcpp/services/async_recv_cb_client/CMakeLists.txt new file mode 100644 index 00000000..dc555984 --- /dev/null +++ b/rclcpp/services/async_recv_cb_client/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.5) +project(examples_rclcpp_async_recv_cb_client) + +# Default to C++17 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake REQUIRED) +find_package(example_interfaces REQUIRED) +find_package(rclcpp REQUIRED) +find_package(std_msgs REQUIRED) + +add_executable(client_main main.cpp) +ament_target_dependencies(client_main rclcpp std_msgs example_interfaces) + +install(TARGETS client_main + DESTINATION lib/${PROJECT_NAME}) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/rclcpp/services/async_recv_cb_client/README.md b/rclcpp/services/async_recv_cb_client/README.md new file mode 100644 index 00000000..0ce0846a --- /dev/null +++ b/rclcpp/services/async_recv_cb_client/README.md @@ -0,0 +1,53 @@ +# Async Receive Callback Client + +## Summary + +This package contains an example that includes a node that +is a client to the *add_two_ints* service. It shows how +to receive the response in a callback, thus avoiding any +waiting mechanism that might impact in the spinning of +the node. + +Please, note that the former example in package: +*examples_rclcpp_async_client* has a very specific way for +spinning that are avoid in this example. + +The goal of this package is helping programmers to understand +how asyncronous services work. + +## Usage + +Launch: + +```bash +ros2 run examples_rclcpp_delayed_service service_main +``` + +```bash +ros2 run examples_rclcpp_async_recv_cb_client client_main +``` + +And trigger the call to the service by: + +```bash +ros2 topic pub --once /input_topic std_msgs/msg/Int32 "data: 5" +``` + +Try to issue more than one call by publishing inmediately another +value to /input_topic. + +You might listen to the topic that publishs the result: + +```bash +ros2 topic echo /output_topic +``` + +## TODO + +* Resolve the following questions: + +> 1. What is the callback group for the response callback? +> 2. Is it possible to set another callback group? + +Actually, answer to 1 should be the default node callback group. +Answer to 2 should be at api documentation (or sources?). \ No newline at end of file diff --git a/rclcpp/services/async_recv_cb_client/main.cpp b/rclcpp/services/async_recv_cb_client/main.cpp new file mode 100644 index 00000000..36aaf535 --- /dev/null +++ b/rclcpp/services/async_recv_cb_client/main.cpp @@ -0,0 +1,90 @@ +// Copyright 2024 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +class AsyncReceiveCallbackClient : public rclcpp::Node +{ +public: + AsyncReceiveCallbackClient() + : Node("examples_rclcpp_async_recv_cb_client") + { + // Create AddTwoInts client + client_ = this->create_client("add_two_ints"); + + // Wait until service is avaible + while (!client_->wait_for_service(std::chrono::seconds(1))) { + RCLCPP_ERROR(this->get_logger(), "Service is not available, trying again after 1 second"); + } + + // Create a subcription to an input topic + subscription_ = this->create_subscription( + "input_topic", 10, + std::bind(&AsyncReceiveCallbackClient::topic_callback, this, std::placeholders::_1)); + + // Create a publisher for broadcasting the result + publisher_ = this->create_publisher("output_topic", 10); + + RCLCPP_INFO(this->get_logger(), "DelayedSumClient Initialized."); + } + +private: + void topic_callback(const std::shared_ptr msg) + { + RCLCPP_INFO(this->get_logger(), "Received %d at topic.", msg->data); + if (msg->data >= 0) { + RCLCPP_INFO(this->get_logger(), " Input topic is %d >= 0. Requesting sum...", msg->data); + + // Create request to sum msg->data + 100 + auto request = std::make_shared(); + request->a = msg->data; + request->b = 100; + + // Calls the service and bind the callback to receive response (not blocking!) + auto future_result = client_->async_send_request( + request, + std::bind( + &AsyncReceiveCallbackClient::handle_service_response, this, std::placeholders::_1)); + } else { + RCLCPP_INFO(this->get_logger(), " Input topic is %d < 0. No request is sent", msg->data); + } + } + + // Callback to receive response (call inside the spinning method like any other callback) + void handle_service_response( + rclcpp::Client::SharedFuture future) + { + auto response = future.get(); + RCLCPP_INFO(this->get_logger(), "Response: %ld", response->sum); + + // Publish response at output topic + auto result_msg = std_msgs::msg::Int32(); + result_msg.data = response->sum; + publisher_->publish(result_msg); + } + + rclcpp::Client::SharedPtr client_; + rclcpp::Subscription::SharedPtr subscription_; + rclcpp::Publisher::SharedPtr publisher_; +}; + +int main(int argc, char ** argv) +{ + rclcpp::init(argc, argv); + rclcpp::spin(std::make_shared()); + rclcpp::shutdown(); + return 0; +} diff --git a/rclcpp/services/async_recv_cb_client/package.xml b/rclcpp/services/async_recv_cb_client/package.xml new file mode 100644 index 00000000..f5fac87e --- /dev/null +++ b/rclcpp/services/async_recv_cb_client/package.xml @@ -0,0 +1,31 @@ + + + + examples_rclcpp_async_recv_cb_client + 0.20.2 + Example of an async service client that uses + a callback to receive the response from the server. + + Santiago Tapia-Fernández + + Apache License 2.0 + + Santiago Tapia-Fernández + + ament_cmake + + rclcpp + std_msgs + example_interfaces + + rclcpp + std_msgs + example_interfaces + + ament_lint_auto + ament_lint_common + + + ament_cmake + +