Custom Server/Client Example

This page contains examples on how to create custom ROS server/client classes using the parent the ROSServiceServer and ROSServiceClient classes described previously. In general, a server/client model is preferred over a publisher/subscriber one when a task requires execution at certain times specified by the user.

In this example, we’ll create a custom ROS service server and client classes. The client will request the age of a person.

Defining Name Message Class

In the root folder of your package, create a new directory called srv and a file that will contain the definition of the Age service class.

$ mkdir srv
$ touch Age.srv

Next we’ll define the service message. Open the Age.srv file and write the following

string name
---
int8 age

This is our simple service class we’ll be publishing. The --- separates the request made by the client (top) and the response given by the server (bottom). As with the publisher/subscriber example, name and age are attributes of this service class.

ROSServiceServer

First we’ll make the server. We’ll do this in a new file within the package ‘src’ folder called age_server.py.

from Age.srv import AgeResponse

class AgeServer(ROSServiceServer):

    def __init__(self, _service_name, _service_type):
        # only need to redefine __init__ if extending functionality

        super(AgeServer, self).__init__(_service_name, _service_type)
        self.ages = {} # dictionary
        rospy.loginfo("Created a new Age service server")

    def add_person(person, age):
        self.ages[person] = age

    def callback(self, request):
        rospy.loginfo("Received request to get age of %s", request.name)
        try:
            age = self.ages[request.name]
            return AgeResponse(age)
        except KeyError as e:
            rospy.logerr(e)
            return AgeResponse(-1)

ROSServiceClient

Next we’ll create the client, also in the ‘src’ folder. We’ll call the file age_client.py.

class AgeClient(ROSServiceClient):

    def __init__(self, _service_name, _service_type):
        # only need to redefine __init__ if extending functionality

        super(AgeClient, self).__init__(_service_name, _service_type)
        self.name_list = []
        rospy.loginfo("Created a new Age service client")

    def make_request(self, request):
        server_response = self._call_service(request) # ROSServiceClient method to handle service call
        if server_response is None:
            rospy.loginfo("%s server response was none", self.service_name)
            return None

        elif server_response == -1:
            rospy.loginfo("That person does not exist in the server records")
            return server_response

        else:
            rospy.loginfo("%s is %s years old", request, str(server_response.age))
            return server_response.age

Using the custom classes

To use these, classes we will create two ROS nodes, one for the server and one for the client.

First the server

#!/usr/bin/env python
import rospy
from my_package.srv import Age
from age_server import AgeServer

rospy.init_node("age_server")
server = AgeServer('age_service_server', Age)
server.add_person("Jono Falco", 52)
rospy.spin()

Next the client node.

#!/usr/bin/env python
import rospy
from my_package.srv import Age, AgeResponse
from age_client import AgeClient

rospy.init_node("age_client")
client = AgeClient('age_service_server', Age) # has to have the same service_name as server
age = client.make_request("Jono Falco")
if age != -1 and age is not None:
    rospy.loginfo("Jono Falco is %s years old", str(age))

Now execute both, first the server and then the client.

Attention

Make sure that both files are executable. You can make a file executable with the following linux command chmod u+x <file_name>.

$ rosrun my_package server_node.py

and in a new terminal window

$ rosrun my_package client_node.py

You should see the message Jono Falco is 52 years old.