WEBVTT

00:00.450 --> 00:08.070
In this laboratory lesson, we will create a service client in Ros2 using Python that requires the usage

00:08.070 --> 00:14.250
of the functionalities offered by the service server that we developed in the previous laboratory lessons.

00:14.790 --> 00:20.430
If you are only interested in the cplusplus development, then you can skip directly to the next lesson

00:20.430 --> 00:24.660
in which we are going to develop the same service client in Cplusplus.

00:25.310 --> 00:31.070
In practice, we are going to create a python node that use the service for the sum of two integers,

00:31.070 --> 00:33.350
which we called Adjoints.

00:33.350 --> 00:38.750
And so based on what we have learned in the theoretical lesson, the client will send a request to the

00:38.750 --> 00:42.350
service according to the format defined by the service.

00:42.350 --> 00:49.310
So following its interface and it will be notified with a response message when the service has finished

00:49.310 --> 00:50.720
its execution.

00:50.900 --> 00:56.570
So let's start by creating a new script within our Arduino Pi examples.

00:56.570 --> 01:08.420
So within the Arduino Pi example sub folder and let's call this one simple service client dot pi.

01:08.870 --> 01:21.050
As always, let's start by importing the Pi library and also from the Pi library, RCL pi library,

01:21.050 --> 01:22.730
and from the node modules.

01:23.090 --> 01:28.490
Let's import the node class which we can use to define a new ros2 node.

01:29.010 --> 01:38.460
And so let's call this one simple service client that inherits from the node class.

01:38.730 --> 01:49.470
As always, let's start by defining the constructor of this class so the init function in it as a member,

01:49.740 --> 01:53.790
so self as a member of the simple Service class.

01:54.030 --> 01:59.400
And here now, let's start also by calling the constructor of the base class.

02:00.030 --> 02:07.560
So let's call the init function of the node class which takes as input the name that we want to provide

02:07.560 --> 02:08.310
to the node.

02:08.340 --> 02:13.770
For example, let's call this one simple service.

02:15.530 --> 02:16.220
Client.

02:16.490 --> 02:19.010
We make the constructor of this class.

02:19.010 --> 02:24.380
So this variable here, this function here to receive two numbers in input.

02:24.380 --> 02:30.950
So two variables that we call A and B, and these are the two numbers that we want to sum.

02:30.950 --> 02:35.960
So these are the two numbers that we want to send in the request message to the server, and we want

02:35.960 --> 02:38.090
this service to add together.

02:38.420 --> 02:44.150
So now in order to send a request to our service server, let's create a new object.

02:44.150 --> 02:48.830
So let's create a new variable and call this one client.

02:48.830 --> 02:55.880
And as you can certainly imagine, just like we used the create service function from the Node class

02:55.880 --> 03:03.980
to initialize a service server here, we can use the create client function to create the service client

03:04.280 --> 03:06.800
similarly to the create service function.

03:06.800 --> 03:14.000
Also, the create client function requires as input the message interface type that is to be used in

03:14.000 --> 03:16.650
order to contact with the service server.

03:16.740 --> 03:24.540
So within this script let's import from the Arduino both messages and from the services that we have

03:24.540 --> 03:25.320
defined.

03:25.830 --> 03:35.310
Let's import the add to its interface and let's use this interface to contact the action server.

03:35.790 --> 03:42.420
Also, this function requires another parameter as input that is the name of the service, namely the

03:42.420 --> 03:52.530
name by which the server is recognized in Ros2 and we named it Add two ints when we initialized the

03:52.530 --> 03:54.720
service server in the previous lesson.

03:55.570 --> 04:01.960
Now that we have our client before actually using it to send the request to the server, let's first

04:01.960 --> 04:06.610
verify that the server is running and so is available to receive new requests.

04:06.910 --> 04:12.460
So we can use the function on the client object.

04:12.490 --> 04:19.120
We can use the function, wait for service, and also we can specify a certain amount of time that we

04:19.120 --> 04:21.030
want to wait for the service.

04:21.040 --> 04:25.190
So let's set a timeout of 1.0 second.

04:25.210 --> 04:32.260
So let's wait until this call is not correctly executed.

04:32.260 --> 04:37.510
So let's wait until the wait for service is not executed correctly.

04:37.510 --> 04:44.110
And so every time, if, for example, the service is not ready yet, in one second, we will wait again.

04:44.110 --> 04:48.070
So we will wait again infinitely until the server is not ready.

04:48.070 --> 04:49.240
And every time.

04:49.240 --> 04:54.510
So every time that we are waiting and the server is not ready, let's print an informative message.

04:54.520 --> 05:07.940
So with the get logger function and the informative message says service not available, waiting more.

05:09.830 --> 05:10.310
Okay.

05:10.790 --> 05:18.830
And now once we are out of this function, it means that actually the client was able to find a service

05:18.830 --> 05:21.980
with this name and it is using this interface.

05:21.980 --> 05:28.070
And so now we can actually create the request message and send it to the service server.

05:28.190 --> 05:33.740
So let's create a new variable and let's call this one request.

05:33.920 --> 05:37.520
And this is from the interface Add joins.

05:37.730 --> 05:41.210
This is of type request.

05:41.840 --> 05:46.130
And here we can assign the values to the request message.

05:46.250 --> 05:54.980
So to the request variable, let's assign a value to the variable A That is the number A that we received

05:54.980 --> 05:58.100
as input of the constructor and let's do the same.

05:58.730 --> 06:02.930
So the request B takes as input.

06:02.960 --> 06:07.010
The B variable that was received as input by the constructor.

06:07.840 --> 06:14.040
Now we can send these requests or this message to the service by using the function.

06:14.050 --> 06:22.300
So on the client, let's use the function call async and we need to pass to this function the request

06:22.300 --> 06:24.010
message that we just created.

06:24.010 --> 06:31.510
So the variable that we called request, the output of this function is a future variable.

06:31.660 --> 06:39.670
So let's store this one within the variable future, a new variable that we are defining now.

06:40.060 --> 06:47.320
And basically this function immediately returns a result that we are storing within the future function

06:47.320 --> 06:54.430
and that we can use in the future to add a callback function that can be executed as soon as the server

06:54.430 --> 06:58.120
finishes the calculation and returns a response message.

06:58.330 --> 07:00.730
So let's use this future variable.

07:03.010 --> 07:04.870
And let's add a callback function.

07:04.870 --> 07:07.720
So add done callback.

07:07.960 --> 07:13.200
And let's execute the function response callback.

07:13.210 --> 07:19.870
So basically here we are saying that after calling the service server, we have a future variable.

07:19.870 --> 07:25.840
And here let's add the done callback function that we called the response callback that will be executed

07:25.840 --> 07:28.550
once we get the response from the server.

07:28.570 --> 07:35.340
So now let's define this function still within the simple service client class.

07:35.350 --> 07:44.200
So let's define this one as a member of this class, which takes as input the future variable that we

07:44.200 --> 07:44.980
are following.

07:44.980 --> 07:49.000
So the future variable that returns from the call async.

07:49.650 --> 07:51.270
And now we can use these variables.

07:51.270 --> 07:54.980
So the future variable to access to the result of the service.

07:54.990 --> 07:58.710
So let's print an informative message into the terminal.

08:00.240 --> 08:09.300
And this message says service response, followed by the value that the service returned that we can

08:09.300 --> 08:12.090
take from the future.

08:12.480 --> 08:16.200
Then let's take the result.

08:16.380 --> 08:20.010
And from the result, let's take the sum variable.

08:20.920 --> 08:23.230
With this, the simple service client.

08:23.230 --> 08:25.180
So this class is completed.

08:25.180 --> 08:28.900
And now what is missing is just to define the main function.

08:29.500 --> 08:30.160
So.

08:38.190 --> 08:38.610
Here.

08:38.610 --> 08:43.290
Let's execute the main function and let's also define it.

08:45.660 --> 08:46.890
And in the main function.

08:46.890 --> 08:54.990
Let's start by initializing Ros two so the ros two node with rcl pi dot init.

08:55.560 --> 08:59.520
And then let's create a new instance of the simple service client class.

08:59.520 --> 09:08.430
So let's call this one simple service client as an instance of the simple service client.

09:08.670 --> 09:14.700
And however, since we are automatically executing the constructor of the Simple Service class, when

09:14.700 --> 09:21.420
we instantiate the new object, we also need to pass the two integers so the two numbers that will be

09:21.420 --> 09:23.160
contained in the request message.

09:23.160 --> 09:30.660
So the one that will be here in the request message the value A and B, So to the constructor here,

09:30.840 --> 09:35.190
we cannot pass an empty constructor, but we have to pass two numbers.

09:35.550 --> 09:41.760
We can take these two numbers from the parameters of the script that we are creating, specifically

09:41.760 --> 09:44.490
from the parameters of the main function.

09:44.490 --> 09:49.020
And to do so we need to import the system library of Python.

09:49.020 --> 09:53.610
So let's import the system library from Python.

09:53.760 --> 09:55.960
And then after the init.

09:55.960 --> 09:58.570
So here let's check if the script.

09:58.570 --> 10:04.210
So the node, the simple service client node has been started with the correct number of arguments.

10:04.240 --> 10:10.600
So let's check that the length of the system argument.

10:11.320 --> 10:14.320
Is different than three.

10:14.320 --> 10:21.520
So we expect the script to be started with three parameters because the main function always receives

10:21.550 --> 10:27.700
an argument as input, so that one is by default and we want to be passed two more arguments.

10:27.700 --> 10:33.940
So apart from the automatic argument that is always passed, we need this script to be started with

10:33.940 --> 10:40.030
two additional arguments, which are the two integers that we want to add together and if these two

10:40.030 --> 10:40.810
are not provided.

10:40.810 --> 10:47.290
So for example, if we are starting this script with a wrong number of parameters, let's print a message

10:47.290 --> 10:55.480
in the console that says wrong number of arguments.

10:55.480 --> 11:06.070
And so we complain about this one and we specify that the usage is simple service client.

11:06.070 --> 11:13.460
So this script has to be started as simple service client followed by a and B, so followed by two numbers.

11:13.460 --> 11:16.400
That will be the two numbers that will be added together.

11:16.490 --> 11:22.460
And in this case, so if the user who started this note has not provided the correct number of parameters,

11:22.490 --> 11:24.920
let's just return minus one.

11:24.920 --> 11:26.810
So let's return an error.

11:27.500 --> 11:28.370
Alternatively.

11:28.550 --> 11:32.540
So out of this, if if the script has been started correctly.

11:32.540 --> 11:38.330
So with the correct number of arguments, we can pass the first and the second of these argument as

11:38.330 --> 11:40.040
input to the constructor.

11:40.130 --> 11:44.440
So here we can pass the first one.

11:44.450 --> 11:56.510
So system argument in the first position and the system argument in the second position as input to

11:56.510 --> 11:57.290
the service client.

11:57.290 --> 12:02.420
So this will be the two numbers that then we will pass to the service server.

12:02.960 --> 12:10.910
As always, let's keep this node up and running with the RCL pi dot spin function.

12:10.910 --> 12:14.480
So let's keep the simple service client node up and running.

12:14.480 --> 12:18.820
And then if we stop the execution with Control-c, let's destroy the node.

12:18.830 --> 12:20.840
So simple service client.

12:22.370 --> 12:26.440
Destroy the node and then also shut down Ros.

12:26.450 --> 12:30.090
So rcl dot shut down.

12:31.310 --> 12:38.120
With this, the simple service client is complete and now we can proceed to install it alongside all

12:38.120 --> 12:44.240
the other nodes, all the other executable that we have already created for the Arduino Pi examples.

12:44.240 --> 12:52.460
So in the setup.py, let's copy this line that we used for installing the simple service server and

12:52.460 --> 12:57.530
let's just change the name of the executable into simple service client.

12:57.830 --> 13:04.910
And the name of the script also is the simple service client from which still we want the main function

13:04.910 --> 13:06.050
to be executed.

13:07.030 --> 13:07.780
For this node.

13:07.810 --> 13:14.230
We don't need to modify the package dot XML since we don't have introduced any new dependency so we

13:14.230 --> 13:17.650
can directly proceed to run this node and see how it works.

13:18.100 --> 13:26.140
So let's open a new terminal and let's go to the workspace and here we can build it.

13:28.000 --> 13:36.760
Now the build is successful so we can split the terminal and here we can source the file setup dot bash

13:36.850 --> 13:40.000
and finally we can run our client node.

13:40.890 --> 13:43.320
So first let's start the server.

13:43.320 --> 13:55.020
So with Ros to run and from the Arduino board by examples, let's start the simple service server node.

13:55.260 --> 13:59.730
So let's press enter and this will start the simple service server.

13:59.730 --> 14:06.300
And now we can contact this server so we can send a request to this server in a new terminal.

14:06.300 --> 14:15.810
Let's again source the workspace and let's run the simple service client with Ros to run and from the

14:15.810 --> 14:18.450
Arduino bot by examples.

14:18.690 --> 14:22.680
Let's start the simple service client node.

14:22.800 --> 14:29.610
If you remember, we need to provide two additional arguments to this script, which are the two numbers

14:29.610 --> 14:30.540
that we want to send.

14:30.870 --> 14:34.380
For example, let's add together five entry.

14:34.470 --> 14:37.350
So let's press enter here.

14:37.350 --> 14:40.990
We can see that there is an error since we need to cast.

14:41.410 --> 14:42.910
In the simple service.

14:42.910 --> 14:49.960
So here, by default, the arguments that we pass to the main are treated as string.

14:50.290 --> 14:54.730
So we need first to cast them to integers.

14:55.700 --> 14:58.790
And now we can build again the workspace.

15:00.520 --> 15:03.160
And we can execute again our script.

15:03.400 --> 15:05.590
And now we can see that correctly.

15:05.590 --> 15:06.520
The node.

15:06.520 --> 15:09.190
So the client is correctly calling.

15:09.280 --> 15:11.830
So it's sending a request to this service.

15:11.830 --> 15:18.010
And this service is receiving the request and is returning a response that contains the sum.

15:18.520 --> 15:24.820
If now, for example, we restart this same node so the simple service client with a wrong number of

15:24.820 --> 15:25.660
parameters.

15:25.810 --> 15:28.360
Let's say that we don't pass any number.

15:28.810 --> 15:35.830
Now we can see that of course the node is stopped so the node fails as we are not passing the A and

15:35.830 --> 15:38.440
B, So the two numbers that we need to add together.

15:39.130 --> 15:45.610
We can also try to stop the service server and to start this time only the service client.

15:45.640 --> 15:47.920
So let's start the correct version of it.

15:47.920 --> 15:52.210
So let's start the service client passing the numbers five and three.

15:52.210 --> 15:55.360
So let's say that we want to add together five and three.

15:55.600 --> 15:57.250
So let's press enter.

15:57.340 --> 16:01.600
And as you can see, the service client is not doing anything.

16:01.600 --> 16:05.860
So it's just printing the message service not available, waiting more.

16:05.860 --> 16:12.820
So basically because we stopped the service server and as soon as we restart the server, actually we

16:12.820 --> 16:17.530
can see that it received the request and also returns the response.
