Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

We need a node that can subscribe to a topic and call a service request from another node at any given time. I couldn't find any solutions (probably because it is a relatively simple concept)... but I think there should be some documentation for other people stuck with the same problem.

The problem we were having: Most of the service request examples have the service request being called from inside the main function. When we tried to do this, we were only able to request a service once and then the node would be caught in a spin loop. To avoid this error, we decided to move the service request into the subscriber callback function. This way it would be called whenever the subscriber heard a message.

The basic idea: The client is instantiated in the main function, but it needs to be used in the callback function. I made a ros::ServiceClient pointer at a global level (w.r.t. the node) and then gave it the address of the client. In the callback function I dereference the client pointer and use it to request a service.

 void SUBSCRIBE_CALLBACK_FUNCTION (const PACKAGE_NAME::MESSAGE_NAME::ConstPtr& msg);
    ros::ServiceClient *clientPtr; //pointer for a client

    int main (int argc, char **argv)
    {

        ros::init(argc, argv, "MY_NODE");
        ros::NodeHandle n;
        ros::ServiceClient client = n.serviceClient<PACKAGE_NAME::SERVICE_NAME>("SERVICE_NAME"); //create the client

        clientPtr = &client; //give the address of the client to the clientPtr

        ros::Subscriber sub = n.subscribe<PACKAGE_NAME::MESSAGE_NAME>("TOPIC_NAME",1000,SUBSCRIBE_CALLBACK_FUNCTION); //subscribing

        ros::spin();

        return 0;

    }

    void SUBSCRIBE_CALLBACK_FUNCTION (const PACKAGE_NAME::MESSAGE_NAME::ConstPtr& msg)
    {
        PACKAGE_NAME::SERVICE_NAME srv;
        srv.request.someBool= false; //just some variables to give the service some structure.
        srv.request.someFloat = 4;
        srv.request.someFloat = 30;

        ros::ServiceClient client = (ros::ServiceClient)*clientPtr; //dereference the clientPtr

        if(client.call(srv)) //request service from the client
        {
            ROS_INFO("Success = %d", srv.response.success);
        }   
        else
        {
            ROS_ERROR("Failed to call service from motor_control_node");    
        }

    }

Short answer: Yes. A node can be a subscriber and a client at the same time.

We need needed a node that can could subscribe to a topic and call a service request from another node at any given time. I couldn't find any solutions (probably because it is a relatively simple concept)... but I think there should be some documentation for other people stuck with the same problem.

The problem we were having: Most of the service request examples have the service request being called from inside the main function. When we tried to do this, we were only able to request a service once and then the node would be caught in a spin loop. To avoid this error, we decided to move the service request into the subscriber callback function. This way it would be called whenever the subscriber heard a message.

The basic idea: The client is instantiated in the main function, but it needs to be used in the callback function. I made a ros::ServiceClient pointer at a global level (w.r.t. the node) and then gave it the address of the client. In the callback function I dereference the client pointer and use it to request a service.

 void SUBSCRIBE_CALLBACK_FUNCTION (const PACKAGE_NAME::MESSAGE_NAME::ConstPtr& msg);
    ros::ServiceClient *clientPtr; //pointer for a client

    int main (int argc, char **argv)
    {

        ros::init(argc, argv, "MY_NODE");
        ros::NodeHandle n;
        ros::ServiceClient client = n.serviceClient<PACKAGE_NAME::SERVICE_NAME>("SERVICE_NAME"); //create the client

        clientPtr = &client; //give the address of the client to the clientPtr

        ros::Subscriber sub = n.subscribe<PACKAGE_NAME::MESSAGE_NAME>("TOPIC_NAME",1000,SUBSCRIBE_CALLBACK_FUNCTION); //subscribing

        ros::spin();

        return 0;

    }

    void SUBSCRIBE_CALLBACK_FUNCTION (const PACKAGE_NAME::MESSAGE_NAME::ConstPtr& msg)
    {
        PACKAGE_NAME::SERVICE_NAME srv;
        srv.request.someBool= false; //just some variables to give the service some structure.
        srv.request.someFloat = 4;
        srv.request.someFloat = 30;

        ros::ServiceClient client = (ros::ServiceClient)*clientPtr; //dereference the clientPtr

        if(client.call(srv)) //request service from the client
        {
            ROS_INFO("Success = %d", srv.response.success);
        }   
        else
        {
            ROS_ERROR("Failed to call service from motor_control_node");    
        }

    }

Short answer: Yes. A node can be a subscriber and a client at the same time.

We needed a node that could subscribe to a topic and call a service request from another node at any given time. I couldn't find any solutions (probably because it is a relatively simple concept)... but I think there should be some documentation for other people stuck with the same problem.

The problem we were having: Most of the service request examples have the service request being called from inside the main function. When we tried to do this, we were only able to request a service once and then the node would be caught in a spin loop. To avoid this error, we decided to move the service request into the subscriber callback function. This way it would be called whenever the subscriber heard a message.

The basic idea: The client is instantiated in the main function, but it needs to be used in the callback function. I made a ros::ServiceClient pointer at a global level (w.r.t. the node) and then gave it the address of the client. In the callback function I dereference the client pointer and use it to request a service.

 void SUBSCRIBE_CALLBACK_FUNCTION (const PACKAGE_NAME::MESSAGE_NAME::ConstPtr& msg);
    ros::ServiceClient *clientPtr; //pointer for a client

    int main (int argc, char **argv)
    {

        ros::init(argc, argv, "MY_NODE");
        ros::NodeHandle n;
        ros::ServiceClient client = n.serviceClient<PACKAGE_NAME::SERVICE_NAME>("SERVICE_NAME"); //create the client

        clientPtr = &client; //give the address of the client to the clientPtr

        ros::Subscriber sub = n.subscribe<PACKAGE_NAME::MESSAGE_NAME>("TOPIC_NAME",1000,SUBSCRIBE_CALLBACK_FUNCTION); //subscribing

        ros::spin();

        return 0;

    }

    void SUBSCRIBE_CALLBACK_FUNCTION (const PACKAGE_NAME::MESSAGE_NAME::ConstPtr& msg)
    {
        PACKAGE_NAME::SERVICE_NAME srv;
        srv.request.someBool= false; //just some variables to give the service some structure.
        srv.request.someFloat = 4;
        srv.request.someFloat = 30;

        ros::ServiceClient client = (ros::ServiceClient)*clientPtr; //dereference the clientPtr

        if(client.call(srv)) //request service from the client
        {
            ROS_INFO("Success = %d", srv.response.success);
        }   
        else
        {
            ROS_ERROR("Failed to call service from motor_control_node");    
        }

    }