ROS Services are defined by srv files, which contains a request message and a response message. These are identical to the messages used with ROS Topics.
roscpp converts these srv files into C++ source code and creates three classes that you need to be familiar with: services defitions, request messages and response messages.

Using srv

Before creating a service server and service clients, the srv file is required. srv files are saved in the folder named srv in the same diretory as the folder named msg.
We can create a messages package with msg and srv folders in it.

1
2
3
4
>> cd work_space/src
>> catkin_create_pkg tutorial_msgs
>> cd tutorial_msgs
>> mkdir msg srv

Then we should define our own srv file in the srv folder. The srv file need types of request messages and response messages respectively, using “—” to divide them.

1
2
3
4
5
6
7
8
>> cd srv
>> vim AddTwoInts.srv

# Add the following contents in AddTwoInts.srv
int64 a
int64 b
---
int64 sum

There’s one mroe step, though. We need to make sure that the srv file is turned into source code for C++, Python, and other languages. Open package.xml and uncomment two lines as follow:

1
2
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

As before, note that at build time, we need “message_generation”, while at runtime, we only need “message_runtime”. Besides, we also need to add message_generation dependency at CMakeLists.txt.
(Despite its name, message_generation works for both msg and srv) Adding your srv file in add_services_files is also required.

1
2
3
4
5
6
7
8
9
10
11
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)

add_services_files (
FILES
AddTwoInts.srv
)

Similar to msg files, we need to add generate_messages in the CMakeLists.txt:

1
2
3
4
generate_messages(
DEPENDENCYIES
std_msgs
)

After finish the above steps, now we can make a new srv by compiling the package:

1
2
>> cd work_space
>> catkin_make install

Writing a Simple Service and Client (C++)

Please make sure that you have followed the directions in the previous tutorial for creating the AddTwoInts.srv needed in the communication.

Writing a Service Node

Here we’ll create the service(“add_two_ints_server”) node which will receive two ints and return the sum. Create a new package and create the src/add_two_ints_server.cpp file in the folder,
and then paste the following inside it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "ros/ros.h" 
// tutorial_msgs is the package which the srv file locates in
#include "tutorial_msgs/AddTwoInts.h"

bool add(tutorial_msgs::AddTwoInts::Request &req,
tutorial_msgs::AddTwoInts::Response &res)
{
res.sum = req.a + req.b;
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true;
}

int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_server");
ros::NodeHandle n;

ros::ServiceServer service = n.advertiseService("add_two_ints", add);
ROS_INFO("Ready to add two ints.");
ros::spin();

return 0;
}

The key is that we have to provdie a callback function with the operation needed inside it. In the routines above, add is the callback function and res.sum = req.a + req.b is the key operation.

Writing a Client Node

Create the src/add_two_ints_client.cpp file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "ros/ros.h"
#include "tutorial_msgs/AddTwoInts.h"
#incldue <cstdlib>

int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_client");
if (argc != 3)
{
ROS_INFO("usage: add_two_inits_client X Y");
return 1;
}

ros::NodeHandle n;
ros::ServiceClient client = n.serviceClient<tutorial_msgs::AddTwoInts>("add_two_ints");

tutorial_msgs::AddTwoInts 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 add_two_ints");
return 1;
}

return 0;
}

Codes of client are simple. The key is that we create a ros::ServiceClient object for to call the service later on. The key is that we instantiate an autogenerated service class,
and assign values into its request member. A service class contains two members, request and response. It also contains two definitions, Request and Reponse.

client.call(srv) actually calls teh service. If the service call succeeded, call() will return true and the value in srv.response will be valid. otherwise,
call() will return false and the value in srv.response will be invalid if the call did not succeed.

Building nodes

Finally, edit the CMakeLists.txt in the package to compile and link codes. Add the following within CMakeLists.txt:

1
2
3
4
5
6
7
add_executable(add_two_ints_server src/add_two_ints_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server tutorial_msgs_gencpp)

add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client tutorial_msgs_gencpp)

Now run catkin_make

1
2
>> cd work_space
>> catkin_make

Appendix

Sometimes error occurs when we compile multiple packages because interdependence. To solve the problem, editing package.xml to clear the dependency of each package is a good habit.
Besides, the command of compiling a single package is also useful here.

1
>> catkin_make -DCATKIN_WHITELIST_PACKAGES="${PACAKGE_NAME}"

References