In this laboratory lesson, we will create a service client in Ros2 using Python that requires the usage
of the functionalities offered by the service server that we developed in the previous laboratory lessons.
If you are only interested in the cplusplus development, then you can skip directly to the next lesson
in which we are going to develop the same service client in Cplusplus.
In practice, we are going to create a python node that use the service for the sum of two integers,
which we called Adjoints.
And so based on what we have learned in the theoretical lesson, the client will send a request to the
service according to the format defined by the service.
So following its interface and it will be notified with a response message when the service has finished
its execution.
So let's start by creating a new script within our Arduino Pi examples.
So within the Arduino Pi example sub folder and let's call this one simple service client dot pi.
As always, let's start by importing the Pi library and also from the Pi library, RCL pi library,
and from the node modules.
Let's import the node class which we can use to define a new ros2 node.
And so let's call this one simple service client that inherits from the node class.
As always, let's start by defining the constructor of this class so the init function in it as a member,
so self as a member of the simple Service class.
And here now, let's start also by calling the constructor of the base class.
So let's call the init function of the node class which takes as input the name that we want to provide
to the node.
For example, let's call this one simple service.
Client.
We make the constructor of this class.
So this variable here, this function here to receive two numbers in input.
So two variables that we call A and B, and these are the two numbers that we want to sum.
So these are the two numbers that we want to send in the request message to the server, and we want
this service to add together.
So now in order to send a request to our service server, let's create a new object.
So let's create a new variable and call this one client.
And as you can certainly imagine, just like we used the create service function from the Node class
to initialize a service server here, we can use the create client function to create the service client
similarly to the create service function.
Also, the create client function requires as input the message interface type that is to be used in
order to contact with the service server.
So within this script let's import from the Arduino both messages and from the services that we have
defined.
Let's import the add to its interface and let's use this interface to contact the action server.
Also, this function requires another parameter as input that is the name of the service, namely the
name by which the server is recognized in Ros2 and we named it Add two ints when we initialized the
service server in the previous lesson.
Now that we have our client before actually using it to send the request to the server, let's first
verify that the server is running and so is available to receive new requests.
So we can use the function on the client object.
We can use the function, wait for service, and also we can specify a certain amount of time that we
want to wait for the service.
So let's set a timeout of 1.0 second.
So let's wait until this call is not correctly executed.
So let's wait until the wait for service is not executed correctly.
And so every time, if, for example, the service is not ready yet, in one second, we will wait again.
So we will wait again infinitely until the server is not ready.
And every time.
So every time that we are waiting and the server is not ready, let's print an informative message.
So with the get logger function and the informative message says service not available, waiting more.
Okay.
And now once we are out of this function, it means that actually the client was able to find a service
with this name and it is using this interface.
And so now we can actually create the request message and send it to the service server.
So let's create a new variable and let's call this one request.
And this is from the interface Add joins.
This is of type request.
And here we can assign the values to the request message.
So to the request variable, let's assign a value to the variable A That is the number A that we received
as input of the constructor and let's do the same.
So the request B takes as input.
The B variable that was received as input by the constructor.
Now we can send these requests or this message to the service by using the function.
So on the client, let's use the function call async and we need to pass to this function the request
message that we just created.
So the variable that we called request, the output of this function is a future variable.
So let's store this one within the variable future, a new variable that we are defining now.
And basically this function immediately returns a result that we are storing within the future function
and that we can use in the future to add a callback function that can be executed as soon as the server
finishes the calculation and returns a response message.
So let's use this future variable.
And let's add a callback function.
So add done callback.
And let's execute the function response callback.
So basically here we are saying that after calling the service server, we have a future variable.
And here let's add the done callback function that we called the response callback that will be executed
once we get the response from the server.
So now let's define this function still within the simple service client class.
So let's define this one as a member of this class, which takes as input the future variable that we
are following.
So the future variable that returns from the call async.
And now we can use these variables.
So the future variable to access to the result of the service.
So let's print an informative message into the terminal.
And this message says service response, followed by the value that the service returned that we can
take from the future.
Then let's take the result.
And from the result, let's take the sum variable.
With this, the simple service client.
So this class is completed.
And now what is missing is just to define the main function.
So.
Here.
Let's execute the main function and let's also define it.
And in the main function.
Let's start by initializing Ros two so the ros two node with rcl pi dot init.
And then let's create a new instance of the simple service client class.
So let's call this one simple service client as an instance of the simple service client.
And however, since we are automatically executing the constructor of the Simple Service class, when
we instantiate the new object, we also need to pass the two integers so the two numbers that will be
contained in the request message.
So the one that will be here in the request message the value A and B, So to the constructor here,
we cannot pass an empty constructor, but we have to pass two numbers.
We can take these two numbers from the parameters of the script that we are creating, specifically
from the parameters of the main function.
And to do so we need to import the system library of Python.
So let's import the system library from Python.
And then after the init.
So here let's check if the script.
So the node, the simple service client node has been started with the correct number of arguments.
So let's check that the length of the system argument.
Is different than three.
So we expect the script to be started with three parameters because the main function always receives
an argument as input, so that one is by default and we want to be passed two more arguments.
So apart from the automatic argument that is always passed, we need this script to be started with
two additional arguments, which are the two integers that we want to add together and if these two
are not provided.
So for example, if we are starting this script with a wrong number of parameters, let's print a message
in the console that says wrong number of arguments.
And so we complain about this one and we specify that the usage is simple service client.
So this script has to be started as simple service client followed by a and B, so followed by two numbers.
That will be the two numbers that will be added together.
And in this case, so if the user who started this note has not provided the correct number of parameters,
let's just return minus one.
So let's return an error.
Alternatively.
So out of this, if if the script has been started correctly.
So with the correct number of arguments, we can pass the first and the second of these argument as
input to the constructor.
So here we can pass the first one.
So system argument in the first position and the system argument in the second position as input to
the service client.
So this will be the two numbers that then we will pass to the service server.
As always, let's keep this node up and running with the RCL pi dot spin function.
So let's keep the simple service client node up and running.
And then if we stop the execution with Control-c, let's destroy the node.
So simple service client.
Destroy the node and then also shut down Ros.
So rcl dot shut down.
With this, the simple service client is complete and now we can proceed to install it alongside all
the other nodes, all the other executable that we have already created for the Arduino Pi examples.
So in the setup.py, let's copy this line that we used for installing the simple service server and
let's just change the name of the executable into simple service client.
And the name of the script also is the simple service client from which still we want the main function
to be executed.
For this node.
We don't need to modify the package dot XML since we don't have introduced any new dependency so we
can directly proceed to run this node and see how it works.
So let's open a new terminal and let's go to the workspace and here we can build it.
Now the build is successful so we can split the terminal and here we can source the file setup dot bash
and finally we can run our client node.
So first let's start the server.
So with Ros to run and from the Arduino board by examples, let's start the simple service server node.
So let's press enter and this will start the simple service server.
And now we can contact this server so we can send a request to this server in a new terminal.
Let's again source the workspace and let's run the simple service client with Ros to run and from the
Arduino bot by examples.
Let's start the simple service client node.
If you remember, we need to provide two additional arguments to this script, which are the two numbers
that we want to send.
For example, let's add together five entry.
So let's press enter here.
We can see that there is an error since we need to cast.
In the simple service.
So here, by default, the arguments that we pass to the main are treated as string.
So we need first to cast them to integers.
And now we can build again the workspace.
And we can execute again our script.
And now we can see that correctly.
The node.
So the client is correctly calling.
So it's sending a request to this service.
And this service is receiving the request and is returning a response that contains the sum.
If now, for example, we restart this same node so the simple service client with a wrong number of
parameters.
Let's say that we don't pass any number.
Now we can see that of course the node is stopped so the node fails as we are not passing the A and
B, So the two numbers that we need to add together.
We can also try to stop the service server and to start this time only the service client.
So let's start the correct version of it.
So let's start the service client passing the numbers five and three.
So let's say that we want to add together five and three.
So let's press enter.
And as you can see, the service client is not doing anything.
So it's just printing the message service not available, waiting more.
So basically because we stopped the service server and as soon as we restart the server, actually we
can see that it received the request and also returns the response.