Creating ROS services

In this section, we will to learn how to create a .srv file, which will be used in our nodes. It contains a specification about the type of data to be transmitted. The ROS build system will use this file to create the necessary code to implement the srv in the ROS computational framework or network.

In the previous section, Creating ROS messages, we created two nodes with a custom type message. Now, we will learn how to create services with ROS tools.

First, create an srv folder in the chapter2_tutorials package. Furthermore, create a new chapter2_srv.srv file there and add the following lines:

int32 A  
int32 B  
---  
int32 sum 

Here, A and B are data types for a request from the client and sum is the response data type from the server.

We will have to search for the following lines and uncomment them:

<build_depend>message_generation</build_depend> 
<exec_depend>message_runtime</exec_depend> 

These lines enable the configuration of messages and services in the ROS build system. Also, we will add the message_generation line in CMakeList.txt:

find_package(catkin REQUIRED COMPONENTS 
  roscpp 
  std_msgs 
  message_generation 
) 

Additionally, we will have to find and uncomment the add_service_file lines in CMakeList.txt, along with adding the name of the new message file, as follows:

## Generate services in the 'srv' folder 
add_service_files( 
    FILES 
    chapter2_srv.srv 
) 
## Generate added messages and services with any dependencies listed here 
 generate_messages( 
   DEPENDENCIES 
   std_msgs 
 ) 

Finally, we can build the package using the following command:

$ cd ~/catkin_ws/
$ catkin_make

To check whether everything is OK, we can use the rossrv command:

$ rossrv show chapter2_tutorials/chapter2_srv

As of this moment, we have learned how to create the service data type in ROS. Following, we will look into the creation of a service that will calculate the sum of two numbers. We will create two nodes, a server and a client, in the chapter2_tutorials package, with the following names: example_3a.cpp and example_3b.cpp. Create them in the src folder.

In the first file, example_3a.cpp, we will the have following code (https://github.com/kbipin/Robot-Operating-System-Cookbook/blob/master/chapter2_tutorials/src/example_3a.cpp):

#include "ros/ros.h" 
#include "chapter2_tutorials/chapter2_srv.h" 
 
bool add(chapter2_tutorials::chapter2_srv::Request  &req, 
         chapter2_tutorials::chapter2_srv::Response &res) 
{ 
  res.sum = req.A + req.B; 
  ROS_INFO("Request: A=%d, B=%d", (int)req.A, (int)req.B); 
  ROS_INFO("Response: [%d]", (int)res.sum); 
  return true; 
} 
 
int main(int argc, char **argv) 
{ 
  ros::init(argc, argv, "adder_server"); 
  ros::NodeHandle n; 
 
  ros::ServiceServer service = n.advertiseService("chapter2_tutorials/adder", add); 
  ROS_INFO("adder_server has started"); 
  ros::spin(); 
 
  return 0; 
}

Let's discuss the code. These lines include the necessary headers and the srv file which was created previously:

#include "ros/ros.h" 
#include "chapter2_tutorials/chapter2_srv.h" 

The following function will add two variables and send the result to the client node:

bool add(chapter2_tutorials::chapter2_srv::Request  &req, 
         chapter2_tutorials::chapter2_srv::Response &res) 
{ 
  res.sum = req.A + req.B; 
  ROS_INFO("Request: A=%d, B=%d", (int)req.A, (int)req.B); 
  ROS_INFO("Response: [%d]", (int)res.sum); 
  return true; 
} 

Here, the service is created and advertised over the ROS computational network:

ros::ServiceServer service = n.advertiseService("chapter2_tutorials/adder", add); 

In the second file, example_3b.cpp, we will add this code:

#include "ros/ros.h" 
#include "chapter2_tutorials/chapter2_srv.h" 
#include <cstdlib> 
 
int main(int argc, char **argv) 
{ 
  ros::init(argc, argv, "adder_client"); 
  if (argc != 3) 
  { 
    ROS_INFO("Usage: adder_client A B "); 
    return 1; 
  } 
 
  ros::NodeHandle n; 
  ros::ServiceClient client = n.serviceClient<chapter2_tutorials::chapter2_srv>("chapter2_tutorials/adder"); 
  chapter2_tutorials::chapter2_srv srv; 
  srv.request.A = atoll(argv[1]); 
  srv.request.B = atoll(argv[2]); 
   
  if (client.call(srv)) 
  { 
    ROS_INFO("Sum: %ld", (long int)srv.response.sum); 
  } 
  else 
  { 
    ROS_ERROR("Failed to call service adder_server"); 
    return 1; 
  } 
 
  return 0; 
} 

We will create a client for the service with the name chapter2_tutorials/adder:

ros::ServiceClient client = n.serviceClient<chapter2_tutorials::chapter2_srv>("chapter2_tutorials/adder");

In the following code, we will create an instance of our srv request type and fill all the values to be sent, which has two fields:

chapter2_tutorials::chapter2_srv srv; 
srv.request.A = atoll(argv[1]); 
srv.request.B = atoll(argv[2]); 

In the next line, the service is called and the data is sent. If the call succeeds, call() will return true, otherwise, call() will return false:

if (client.call(srv))

To build the service and client nodes created just now, we will have to add the following lines in CMakeList.txt:

add_executable(example3a src/example_3a.cpp) 
add_executable(example3b src/example_3b.cpp) 
 
add_dependencies(example3a chapter2_tutorials_generate_messages_cpp) 
add_dependencies(example3b chapter2_tutorials_generate_messages_cpp) 
target_link_libraries(example3a ${catkin_LIBRARIES}) 
target_link_libraries(example3b ${catkin_LIBRARIES}) 

The catkin_make tool is used to build the package, which will compile all the nodes:

$ cd ~/catkin_ws
$ catkin_make

To work with these nodes, we will have to execute the following commands in two separate shells:

$ rosrun chapter2_tutorials example3a
$ rosrun chapter2_tutorials example3b 2 3

The output will be as follows:

Server and client