This is now step number four of this final project.
And what do we have so far?
Well, we have a turtle spawner node that will spawn turtles on the turtle SIM with random coordinates.
And for now we spawn one new turtle every two seconds.
Then we have a turtle controller node that will take the first turtle.
So that's called turtle one.
And it's going to make this turtle move to a target position.
And what's the target position?
Well, we are actually publishing a list of turtles that's going from the turtle spawner to the turtle
controller.
And we go to the first turtle.
Now what's next to do is that first, when the master turtle reaches one turtle.
So one target turtle, we should remove that turtle.
So we should kill that turtle and remove it from the screen.
And second, we need to give a new target to the master turtle.
So it's going to go to the next turtle, etc., etc..
Okay.
Let's.
So I have the turtle sim node running here.
I have the turtle controller here.
Well, we just need the turtle sim here and let's just do a Ros2 service list.
So you see, we have lots of services, but we have the kill service.
That's the one that we want to use.
Ros2 service type with kill.
Okay.
So you see it's total SIM service kill.
So now I can do well actually.
Ros2 interface show total sim save kill to see what's inside.
And you see, it's pretty simple.
We just give a name and we don't receive any response.
Let's call it Ros to run.
Actually, no Ros to service.
Call With kill and then tethered SIM service kill.
I will provide the request, which is name.
And while we know this first turtle here.
So that's the turtle one here.
That's the master turtle.
And this first turtle that was spawned is named turtle two.
So if I run this, you see the turtle disappears.
And we want to do that in the code.
All right.
So we will need to call this service from the code.
And where do we call it.
Well you could think we could call it directly from the turtle controller because we know we have reached
the goal.
We know what the position of the target turtle, we could remove it from here.
But as we keep the list of alive turtles in the turtle spawner, it would be best actually to do everything
that's related to what?
Spawning and removing turtles from that node so that we can keep the list accurate.
And then we just need to publish the new list, which is going to contain just the turtles that are
actually spawned and not removed.
And so what I'm going to do here, I'm going to create a service between the turtle controller and the
turtle spawner.
Here we're going to have a service server that's going to be called Catch Turtle.
And whenever the turtle controller has reached you see target reached.
We are going to use a service client to call the catch turtle service.
And here when we receive a request then we can call the kill service of the turtle SIM node.
All right.
So that's what we want to do.
And for that I'm going to create a new interface.
I'm going to go to.
So let's stop everything and let's go to my robot interfaces SRV.
Because we're going to create an interface for a service and then touch I'm going to name it Catch turtle
Dot SRV.
So you could use an existing interface if you want.
Here, I name it Catch Total and I'm going to design it specifically for this application that we want
to do.
Okay.
Let's go to the code.
In my robot interfaces in SV we have catch total.
And the only thing we really need is the name.
So I'm going to put string name.
Okay.
So the total controller is going to send the name of the turtle to the turtle spawner.
Then we have three dashes.
And do we need a response.
Well not really I'm still going to put a bool success okay.
Just in case we can return true or false.
So I save this.
Make sure you have the three dashes.
That's very important.
And then in List.txt I am just going to add my new service SV catch turtle.sv I save all of that and
then let's build the interface.
So Ros2 workspace click on build packages.
Select with my robot interfaces.
All right.
And let's just verify.
So let's source setup bash.
And let's verify with Ros2 interface show my robot interfaces CV.
And then let's use the Autocompletion.
Do we find this.
So yes we have catch total.
Great.
And now well once again I'm going to actually stop everything and also close VSCode and start everything
again.
And let's go to the workspace and open VSCode.
So now we know VS code has been sourced with all the new interfaces we have built.
All right.
So I don't need that anymore.
And as we create here a service, usually the best thing is to start from the server side.
So let's import the interface from my robot interfaces dot save this time import catch turtle I'm going
to create a service here.
Self dot catch turtle service is equal to self dot.
Create service with the catch turtle type.
The name is going to be.
Well let's name it simply Catch turtle.
You see once again we use a verb catch.
And then I need a callback.
So let's add a callback here.
The order it doesn't matter.
Callback.
Catch.
Turtle.
With self request is going to be catch turtle dot request and then response is going to be catch turtle
dot response.
What I can do is put my callback here self dot callback catch turtle and this on a new line.
So it's a bit better.
All right we have the service.
We have the callback.
What do we do in the callback.
Well we are going to call the kill service from turtle SIM.
So I'm going to put a comment here for now call kill service.
Then we will need to return a response.
So response dot success.
I'm not going to do anything here really I'm going to just put true because we're going to call the
service.
But you could decide to wait until you get a response from the service.
But anyway, you don't get any, uh, thing in the response of the kill service.
So this boolean flag is not really useful.
Okay.
We are just using it because I created it.
So let's just put true and return a response.
All right.
So that's for the catch total.
Now here we need to call the kill service.
So the kill service if I start total SIM again rose to run total sim total sim node okay.
Let's just do a rose to service list.
Rose to service type with kill and a rose to interface show with that one.
So we have all the information we need okay.
The name the type and what's inside the interface.
So let's import that interface.
I can put it here for example from turtle sim dot csv import kill.
Just make sure I have everything in the package dot XML.
And actually I forgot to do that before.
You see we are using my robot interfaces.
So I'm going to add a tag.
Here depend my robot interfaces.
Okay, so we have our CLP turtle SIM geometry messages and my robot interfaces.
As you can see, the code was still running fine okay.
This is mostly when you're going to do core code build in case this package is not built before you
try to build the turtle sim catch them all package, then you're going to get an error.
Okay, so that's why you add a depend tag here.
For example for Python code.
It's just to make sure that when you do call on build, all the packages here are found first.
Great.
So let's save that.
And now where to call the kill service.
That's going to be very similar to that.
Call spawn service.
We're going to do the same structure and the same structure here basically.
So let's add a function here.
Def call kill service self.
We're going to put the turtle name and that's it.
So actually if I call the service I need a service client I'm going to create a service client here.
For example just after that one self dot kill client is equal to self create client with kill and name
is slash kill.
You can get that from the terminal.
So in my method here I will do while not self dot kill client wait for service.
Okay, I'm going quite fast here because we've done that already several times.
And uh, let's just take that log.
Wait for.
Kill service.
Even though we should never have this.
If we start turtle sim first, you know.
But just in case, it's a bit delayed so we can create the request, which is going to be a kill dot
request.
And in the request, we can do request.
Dot name is equal to turtle name.
That's the only thing we need to provide as you see here just the name.
And then we can call the service.
So I'm going to get future self dot kill client dot call async with the request.
So it's always the same thing.
And then let's add a future add done callback.
I'm also going to use partial because I'm going to give the request to the callback because we need
the name of the turtle.
Okay.
So before I write this let's write the callback.
So callback call kill service will receive a future.
And the request which is a kill dot request.
Okay let's finish that.
So partial self dot callback Kill.
So callback call.
Kill service and then request is equal to request.
So the request parameter here is equal to the one we've created here.
Okay.
So nothing really new.
It's always the same thing you see for this function.
And then what do we do here.
Well we don't really need to get the response because there is nothing and there is nothing in the response.
So you could get the response object, but then you will not do anything with it.
So that's it.
When we get to this callback, basically we have the information that the service was executed.
Okay.
So we know the server has finished and is getting back to the client.
So basically we know that the turtle was killed.
Okay.
If we provided a name that was valid, we know the turtle was killed.
If the turtle is killed what do we want to do?
We want to remove it from the list of alive turtles here.
Okay, so when we spawn, we add a new one.
But when we remove a turtle, we also remove it from that list.
And what information we have here we have the information of the name.
So we have the turtle name.
So basically we can go through the turtle list and find turtles which correspond to that name and just
remove it.
I'm going to do four and I'm going to use I turtle in enumerate self alive turtles okay.
So I use this because I will need the index.
All right.
So with this we get an element turtle and we get the index for each element in that list I can do if
turtle dot name, which is each turtle we're going to go through is equal to request Dot name.
So that's the turtle name we have provided here to the call kill service.
And actually here to make it a bit more readable, instead of just passing the request, I could just
pass.
Turtle name and say that's the turtle name is equal to the well, the turtle name we get here.
Okay.
Just pass the same parameter name.
We get it here, we pass it here, we receive it here.
And I can say.
Turtle name.
All right.
So if the turtle we go through each turtle in the array.
If one of the turtle.
Name corresponds to the turtle name we have sent to the kill service, then we know it's that turtle.
And we can do, for example del to delete self dot live turtle with the index I okay, that's why I.
Used the I here.
And then what.
I'm just going to add a break Because we just need to do this once.
Once we find the turtle, we don't need to continue this loop.
All right.
And there's one more thing I'm going to do is I'm going to publish.
So just before we break and we exit this callback, I'm going to do self dot publish a live turtles.
Why is that.
Because well now we have updated the list of turtles.
So the array is not the same as it was just before the service.
So I'm going to publish the new list and so that the turtle controller is going to receive the new list.
All right.
So by doing this you see in the turtle controller in the callback live turtles, whenever I receive
the turtle list I just go to the first turtle.
So if I remove one turtle and I publish the list again, then we're going to get a different list and
we can get to another turtle.
Okay.
So by just doing this, it's going to take care of giving the turtle controller an updated list every
time with the turtles to catch.
So you see, we only publish when we spawn a new one and when we delete a new one.
And let's just finish the code here.
We need to actually call the kill service here.
So self dot call kill service with the turtle name, which is going to be from the request dot name
okay.
From the catch turtle interface okay.
So catch turtle we have a name.
We basically forward this name to the kill service.
All right.
So what we did here very basically we created a catch turtle service that will basically forward to
the kill service.
So that's going to be a service client.
We call the service.
And when we get a response from the server we know the turtle has been removed.
And we find the turtle from the list.
We update the list and we publish a new list.
And now we need to do the other side, which is the turtle controller, because who is going to call
this catch turtle?
Okay.
Nobody is calling that for now.
So we're going to create a service client in the turtle controller.
And I'm going to create first here a method.
Def call catch.
Turtle service will just need to provide a turtle name.
And I'm going to create a service client.
So I'm going to create a service client here for example right here self dot catch.
Turtle client is equal to self dot create client.
I need the interface which is from my robot interfaces dot srv import catch turtle.
All right I provide the interface here.
Catch turtle and Servicename is is here.
Catch turtle and make sure I use the same.
Okay, now I can use this client.
And actually I'm just going to take that code for example that one.
I'm going to take all of that actually and put it there to show you that it's basically the same code.
Just make sure to have the indentation.
So it's not the kid client, it's catch turtle client okay.
So why not, uh, client wait for service.
We want waiting for catch turtle service.
Then we create a request which is of type catch turtle dot request the request dot name.
So it's also the same thing as for the kill here.
It's also a name.
Here we have a name attribute.
A name field corresponds to the total name we do self dot catch total client call async.
With the request we add a done callback with partial.
So I'm going to add from func tools import partial.
And it's going to be the actually callback I'm going to call it catch total.
And I'm also going to pass the total name.
All right.
So you see the code is basically the same.
We wait for the service.
We create a request.
We send the request.
We add a callback.
And what do we do in this callback.
Well there's actually not much to do because the only thing we receive is well, a boolean.
So success.
You could just check maybe if Future.
So we actually have the response which is a catch.
Total response is equal to future dot result.
You could say if not response dot success and then self dot get logger error.
Uh, for example turtle could not be removed turtle plus turtle name and then could not be removed.
Okay.
So because you know that there is a success flag inside the response, you could handle that and just
print the log in case something bad happens.
We already know from the server side that, well, it's not going to happen.
We just return true every time.
But as the client, you don't necessarily know that.
So you might just handle the error in case.
But basically for this service client you could also have just done that okay.
And just call the service and not really wait for any reply.
But I'm going to do things completely here.
And the only thing left to do is to call that function here.
And when do we want to call that.
Well when we have reached the target, you see we already have the code structure here.
So after we reach the target I will do self dot call catch turtle service.
And I need to provide the turtle name.
What is the turtle name.
Well we are actually chasing the turtle to catch so self dot turtle to catch dot name as simple as that.
And then I'm going to do something else.
I'm going to do self dot turtle to catch is equal to none.
So I put back it to none because we don't need to send any more comments.
Okay, we have reached the target turtle, so I just put it back to none.
Which means that the next time we go to the control loop, you see, we're not going to do anything.
We're just going to return.
And then when we get a new array, we just get the first turtle that becomes the new turtle to catch.
And we can continue to chase that turtle until we reach it.
We call this service.
It's going to kill the turtle on the turtle spawner.
Send back the new array, and we can go back to this subscriber callback and chase a new turtle, etc.
etc. as long as you have turtles on the screen, it's going to continue to chase turtles.
All right, so the loop is kind of finished.
Now we can test the code.
So I am going to well I have the turtle sim node here.
I'm just going to put that on the side.
Keep the total SIM node here.
And then I can start here across to run total sim.
Catch them all with controller.
So it should not print any error.
It's working.
The control loop is working but we don't have any turtle to chase, so nothing is happening.
And then I'm going to start the spawner.
So Ros to run turtle sim catch them all with the spawner and let's see what's going to happen.
So we wait for one turtle to spawn.
And you see, as soon as one turtle has spawned, we chase the turtle.
Then the turtle disappears and then we chase another one.
So you see, it is currently working.