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

Is it possible to call a ros service from another service's callback function in the same node?

asked 2012-10-25 04:21:35 -0500

toniOliver gravatar image

updated 2012-10-25 04:22:35 -0500

I have a node advertising several services (A, B, C), and I want the service callback function for service A, to call services B and C. Is this allowed?

From my first tests it seems that this blocks something in the underlying service mechanism. Maybe the different services in a node are served sequentially using a single thread?

I know doing this may sound strange, and not the right way to do it, but this node is a quite complex etherCAT realtime node, and the C++ object that serves A doesn't have (easily) access to the objects that serve B and C.

edit retag flag offensive close merge delete

2 Answers

Sort by ยป oldest newest most voted
1

answered 2012-10-25 04:35:34 -0500

Lorenz gravatar image

You might need more than one spin threads. When you call spin, roscpp starts a loop that serves service and topic requests, i.e. it calls the callback. When calling a service inside a service, you might need a second spin loop. Have a look at roscpp's AsyncSpinner and this page.

edit flag offensive delete link more

Comments

Thanks for pointing me to the right information. It looks like AsyncSpinner is what I'd need. Although I will try to do it by taking the second service call out of the callback function, as the code I'm working in is a plugin and the ros::spin() declaration is not in my package (not my code).

toniOliver gravatar image toniOliver  ( 2012-10-26 00:37:21 -0500 )edit
2

answered 2020-05-18 05:54:08 -0500

Andreas Z gravatar image

The problem is, that you can't use the spin_node_until_future_complete() command inside the outer callback. The solution is to use an MultiThreadedExecutor and different callback groups. Then you can call the inner service by giving it a callback function (instead of waiting for the return code with spin_node_until_future_complete).

If you want to wait for the result from the inner service call before the outer service callback finishes you need to block the thread with a while(return_from_inner_service == false) loop. This is important if your outer callback is from a service, because then you want to receive the inner service result and use it to compute the result of the outer service. But this is bad design, because you block the spin() call from the outer service. Luckily we can still handle other callbacks with the MultiThreadedExecutor as long as we assign them to a different callback group. If the outer callback is from a topic you don't want to block the callback, just handle the inner service call inside the assigned callback function. It will execute asynchronous.

Here is some code example in C++ (assuming all necessary headers included and ROS services created)

Create the MultiThreadedExecuter and an instance of the Node in main:

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv);
    rclcpp::executors::MultiThreadedExecutor executor;
    auto node = std::make_shared<Communication>();
    executor.add_node(node);
    executor.spin();
    rclcpp::shutdown();
    return 0;
}

Create seperate Callback group and assign it to the inner service client (inside the Node/class constructor):

callback_group_input_ = this->create_callback_group(rclcpp::callback_group::CallbackGroupType::MutuallyExclusive);
get_input_client_ = this->create_client<petra_core::srv::GetInput>("GetInput", rmw_qos_profile_services_default, callback_group_input_);

Inside the outer callback from a topic or service, send the inner_request and define a callback for it:

int choice = -1;
    auto inner_client_callback = [&,this](rclcpp::Client<petra_core::srv::GetInput>::SharedFuture inner_future)
        { 
            auto result = inner_future.get();
            choice = stoi(result->input);
            RCLCPP_INFO(this->get_logger(), "[inner service] callback executed");
        };
    auto inner_future_result = get_input_client_->async_send_request(inner_request, inner_client_callback);

If it is necessary to wait inside the outer callback for the result block the thread:

// Wait for the result.
    while (choice < 0 && rclcpp::ok())
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }

I hope it helps you, or whoever has the same problem. I was stuck with this for weeks...

edit flag offensive delete link more

Comments

+1 for the effort of posting this, but please note that your answer is ROS 2 specific, while the OP was/is using ROS 1.

That makes your answer essentially irrelevant here in this case.

This is also a Q&A from 2012.

gvdhoorn gravatar image gvdhoorn  ( 2020-05-18 07:03:45 -0500 )edit

Question Tools

2 followers

Stats

Asked: 2012-10-25 04:21:35 -0500

Seen: 3,613 times

Last updated: May 18 '20