In this laboratory lesson, we will develop a simple Python service server that will provide the functionality
of calculating the sum of two integers.
Then we will also develop a client that will use this service within another Ros two node.
This will help us to understand how services work from a practical perspective and how to use the command
line interface to interact with them.
If you are only interested in the C plus plus development, then you can directly move to the next lesson
where we will implement a service server that offers the same functionalities of summing two numbers
but in C plus plus.
Let's start by opening Visual Studio code and by creating a new Python script within the Arduino Pi
example package and within the Arduino Pi Examples app folder.
And let's call this one simple service server dot pi.
As usual, let's begin by importing the Pi library, which allows us to use all the tools of the two
in the Python script.
So let's import rcl pi and let's also import.
So from RCL pi and from the node module, let's import the node class.
And now we can use the node class to create a new class.
So to create a new node, let's call this one simple service server that inherits from the node class
here.
Let's start by defining the constructor of the class.
So let's define the init function that is a member of the Simple Service server class.
And then we also need to call the constructor of the base class.
So basically with the super function, let's call the init.
Init function.
So the constructor of the base class of the node class.
And let's provide a name to the node.
So for example, let's call it a simple service, a server.
Next, let's create a new variable so a new member of the class.
And let's call this one service.
Just as we used the Create publisher function to create a publisher object and the create subscription
function to create a subscriber object.
Similarly, to create a service server, we use the create service function that we inherited from the
node class.
This function requires the message interface to be provided as input for the communication with the
service server.
And so the interface that specifies how the request and response messages are done for interacting with
the service server.
To do this, we need to create a new message interface for the communication with this new service server.
As a convention and as a general rule in Ros2, it is advisable to place all the new message types so
all the user defined message types in a different package from the ones in which we have developed the
robots functionalities.
So let's open our new terminal and here let's go to the workspace and to the source folder.
And here let's create a new package.
So Ros, to package create, let's use as build type, amend, see make, and let's call this new package
that will just contain messages.
So Ros to interfaces, Arduino bot messages.
And so let's press enter.
Now let's go back to the workspace and let's build it so that the new package is recognized.
Now back in Visual Studio code, we can define an interface for the communication with the service server
within the newly created Arduino bot messages for convention.
The messages that define the interface for the communication with the service are placed within the
serve folder.
So let's create this folder that is called serve.
And within this one, let's create a new file that is called Add to insert Serve.
In this file, we need to define the structure of the request message.
That is the one that has to be sent to the server.
So the request message and this request message will consists of the two integers for which we want
the server to calculate the sum.
So these are two integers that we call A and B, And then we also need to define the structure of the
response message.
That is the one that is returned by the server when it has completed its calculation.
So we can use three dashes to separate the response message from the request one and the server will
simply return an integer that will contain the sum of the two integers that it received as input.
So let's call this one sum.
With this, we have completed the definition of the ad joints interface and now we can proceed to declare
its existence to the compiler so that it can correctly build and compile this message so that other
Ros two nodes and packages can use it.
So let's open the file CMake list.
And here let's add the dependency from the library standard messages and also let's add another find
package instruction and here to compile and generate all the necessary file for using the new message,
let's declare the usage of the Ros Idle.
Default.
Generators library.
And then let's also add here before the build testing a new Ros.
Idle generate interfaces instruction.
And so here we say that we are generating an interface for the package Arduino bot messages.
So let's use the variable project name and then let's declare the name of the service that we are using.
So among the double quotes, let's declare that we have a new service within the service folder and
this called Add to dot Serve.
Now we are only missing to declare the dependencies of the Arduino bot messages package also in the
package dot XML file.
So here, let's add a dependency from the standard messages library.
Then let's also add another build dependency.
So here a build dependency from the Ros IDL default
generators.
Also declare another execution dependency.
So execution dependency from the Ros IDL default runtime.
And then for the generation of the service interface messages, we also need the tag member of group.
And here we need to insert the ros IDL interface packages.
So let's save this file.
And now we can proceed to build our workspace so that the new message interface with the service.
So the add to interface is recognized and we can start using it within the simple service server.
So let's open a new terminal.
Let's go into the workspace and let's build it.
This has compiled the workspace and so it has generated also the interface with the service.
And now we can go back to the simple service server here and still within the Create service function,
we need to insert the interface so the message type that the service needs to use.
So let's import it.
So from the Arduino board messages from the services, let's import the add to insert interface.
And then now let's use this interface within the create service function.
So here to declare that the type of the service object that needs to be initialized should be the request
and response message interface as defined in the Add joins interface.
Still in the create service function, we also need to assign a name to the service, the name by which
the service will be recognized in the Ros environment.
For example, let's call it add two ins.
Then we also need to provide a callback function, namely a function that will be executed by the service
server whenever a new message request is received.
So whenever a client requests the server to calculate the sum of two integers and let's call this function
service callback that still we are going to define as a member of the simple service server class.
To conclude the constructor of this class, let's also print a message in the terminal.
So a log message using the get logger function.
And then let's print an informative message that says service at the joints is ready.
And now still the constructor is done, but still inside the simple service server we are missing to
declare the service callback function.
So let's declare this function.
And let's declare it as a member of the simple service server.
And as this is a callback function, it receives as input the message interface that is used for the
communication and the data exchange with the server.
Therefore, it receives the request and the response message for the communication with the client.
So the request that was the one received from the client and the response, that is the one that we
are going to return to provide to the client.
First in this function, let's log an informative message in the terminal.
So still with the get logger function, let's print an informative message that says new message received.
And then let's print the two numbers, the two integers that we received in the request message.
And so the variable A and then the variable B, So let's print both of them.
And these are contained in the request message.
So in the request message, let's access to the variable A and to the variable B.
Now actually we can perform the logic of this service, which in this case is just to calculate the
sum of the two integers that are received in the request message.
And so let's calculate the sum between the variable A and the variable B, and let's store their value.
So the value of the sum in the response message.
So in the sum variable, so in the variable that we called sum in the response message at the end of
this calculation, let's also print another informative message in the terminal.
And this message says returning some.
And then the value of the sum that is basically the one that we stored in the response message.
Finally, to conclude this function, let's also return the response variable.
So let's return the response message to the client that requested the execution of the service.
This covers the definition of all the components of the simple service server.
And now we can move on to define the main function, which is the one that is automatically executed
when the node starts.
So here, let's execute the main function.
And now let's define.
The main function.
And let's start by initializing Ros with the function init.
Then let's create a new instance of the simple service server class.
And then let's keep this node up and running.
So with the rcl py, let's use the spin function to keep the simple service server up and running.
And finally, if we terminate the execution of the node with control Z, let's destroy the node.
So simple service server.
Destroy the node.
And also, in this case, let's shut down Ros.
So RCMP.
Shut down.
Here is our pie.
Perfect.
With this, the simple Service survey is completed, and now we can proceed to install it by modifying
the file Setup.py, where we have already installed the simple publisher, subscriber and parameter
so we can copy this line and we can paste it below and the new node.
So the new executable will be the simple service server.
And also it is in the simple service server script.
As a final step since we used the Arduino bot Messages library.
So since we used this library within the Arduino Pi example package, we need also to declare the usage
of this library.
And so we need to do this in the package dot XML.
We need to add an execution dependency.
So execution dependency from the Arduino bot messages.
And now we can proceed to run the node.
So let's open a new terminal and let's go to the workspace.
And here let's first build our workspace so that the new node is installed.
And now we can open a new window of the terminal, and here we can actually source the workspace.
So if I set up Bash and we can run our service server node.
So let's make this bigger with the command Ros to run.
And from the Arduino Pi examples, let's start the simple service server node.
So let's press enter.
And as we do so, we can confirm that actually the node and so the server is running from the output
of the terminal.
So we can see that the service chewins is ready.
We can also double check this if we open a new terminal and we use the command Ros two service list,
we can see that actually the service Atkins is there, which is the name that we assigned to our script.
Furthermore, if also in this terminal, we source the workspace.
So let's source the workspace.
We can also get additional information about this service.
So about the communication.
So the interface that it is using with the command Ros2 service type followed by the name of the service
that is the Add Chewins.
So let's press enter.
And as we can see here, it tells us that it is using the interface the that is declared in the Arduino
board messages.
Now still from the terminal.
We can also send our request to this service server by sending our request message.
And we do this with the command rescue service call, followed by the name of the service that still
is the Adkins, followed by the type of the request message.
And so this is the one that belongs to the Adkins interface.
So if we press tab twice, we can see that it is automatically detected.
So let's insert a message of this type and then if we press tab twice.
So let's insert the double quotes and let's insert the letter A and then let's press tab.
We can see that it also enters an empty prototype of the request message.
So the autocompletion of Ross in this is very useful.
For example, let's ask the server to calculate the sum between seven.
And the number five.
So let's press enter.
And as we can see, as soon as we do that, the server tells us that it received a new request and also
it tells us that it is returning there.
So 12 and also in the terminal in which we called.
So we asked for the execution of this server.
We see that we have the sum that was returned by the server.