ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
0

How to access the response of async request from a client?

asked 2022-06-22 04:22:21 -0500

YashShah gravatar image

I am calling service from service in ROS2. As I have the spin function already being executed in the outer service, I can't use rclcpp::spin_until_future_complete for calling the inner service. To find a hack I stumbled upon the function of async send request where I can pass a callback that will get executed once the response as shown below.

// We give the async_send_request() method a callback that will get executed once the response // is received. // This way we can return immediately from this method and allow other work to be done by the // executor in spin while waiting for the response. using ServiceResponseFuture = rclcpp::Client<example_interfaces::srv::addtwoints>::SharedFuture;

auto response_received_callback = [this](ServiceResponseFuture future) {
    auto result = future.get();
    RCLCPP_INFO(this->get_logger(), "Result of add_two_ints: %" PRId64, result->sum)
    rclcpp::shutdown();
  };

auto future_result = client_->async_send_request(request, response_received_callback);

My question is that if I want to access the result out of the scope for further process. How could I do that?

edit retag flag offensive close merge delete

1 Answer

Sort by » oldest newest most voted
0

answered 2022-06-23 07:17:57 -0500

Sam_Prt gravatar image

updated 2023-05-12 09:02:48 -0500

Hello,

The way you do this is by declaring a shared pointer to the service response outside of the scope, and saving the service response to that variable. Here is a example using the service from your question :

class MyNode : public rclcpp::Node {
public:
    MyNode() : rclcpp::Node("my_node") {
        this->service_client_ = this->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");

        this->service_client_->wait_for_service();
        auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
        request->a = 1;
        request->b = 1;
        const auto future = this->service_client_->async_send_request(request,
                                                                      std::bind(&MyNode::service_response_callback,
                                                                                this, std::placeholders::_1));
        rclcpp::spin_until_future_complete(this->get_node_base_interface(), future);  // Wait for service response
    }

    /// Service response callback
    void service_response_callback(rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture future) {
        this->service_response_ = future.get();  // Save service response to the pointer declared outside the scope for further process
        std::cout << this->service_response_->sum << std::endl;
    }

private:
    rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr service_client_;

    example_interfaces::srv::AddTwoInts::Response::SharedPtr service_response_;  // <-- Declare the pointer to the response like this

};

I hope this helps !

edit flag offensive delete link more

Comments

Sorry for not responding earlier. After not finding the solution, I stopped scratching my head around it and shifted to other things. For ease, I am still turning into this solution. I have tried the following things. I am still getting the error

example_interfaces::srv::addtwoints::Response::SharedPtr result;
auto response_received_callback =[this](ServiceResponseFuture future) 
{
      result = future.get();
}
auto future_result = client_->async_send_request(request, response_received_callback);

Error-:

In lambda function:

 error: ‘result’ is not captured
   36 |       result = future.get();

Am I doing something wrong?

YashShah gravatar image YashShah  ( 2022-07-11 03:39:36 -0500 )edit

I have tested with this lambda function and the service response is saved in the result member:

[this] (rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture future) -> void {
                                                          this->result = future.get();}

Alternatively, you can create a method :

void service_response_callback(rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture future) { this->result = future.get();
}

and then call the service with :

client_->async_send_request(request, std::bind(&NodeName::service_response_callback, this, std::placeholders::_1));
Sam_Prt gravatar image Sam_Prt  ( 2022-07-11 05:21:36 -0500 )edit

Thanks for the solution. It works. I still would like to know one thing. This is a callback function that we have defined. The code goes ahead and doesn't wait for the service to respond. If I would like to wait until the service responds before moving forward in the code, what will be the efficient way to do it?

YashShah gravatar image YashShah  ( 2022-07-11 07:58:15 -0500 )edit

You can wait for the service response with rclcpp::spin_until_future_complete. I will edit my answer to include everything I have said and with a working example.

Sam_Prt gravatar image Sam_Prt  ( 2022-07-12 09:15:19 -0500 )edit

Thank you so much for posting the answer. Actually, I cannot implement this because I am calling the service inside a callback function. So I cannot use spin two times(one is the primary spin and the other spin_until_future). I am doing something similar to this

YashShah gravatar image YashShah  ( 2022-07-12 10:52:09 -0500 )edit

Question Tools

2 followers

Stats

Asked: 2022-06-22 04:22:21 -0500

Seen: 1,085 times

Last updated: May 12 '23