WEBVTT

00:00.110 --> 00:01.640
This is the step number three.

00:01.670 --> 00:03.740
And it's going to be the last step for this activity.

00:03.770 --> 00:07.610
Number four in this step we are going to create a new node for the battery.

00:07.640 --> 00:12.020
We're going to kind of simulate the behavior of the battery to say that it's full or empty.

00:12.020 --> 00:14.450
And when it's full or empty we are going to change.

00:14.960 --> 00:18.650
So we are going to set an LED with the set led service.

00:18.650 --> 00:24.110
And that's why I've created first the LED panel node, so that we have the service that is already working

00:24.110 --> 00:24.920
and tested.

00:24.950 --> 00:29.030
So now when we create a client we know the service is already working.

00:29.060 --> 00:29.300
Okay.

00:29.330 --> 00:32.240
So it's going to be easier for us to test the client.

00:32.270 --> 00:32.690
Great.

00:32.690 --> 00:36.530
So let's go again in our Python package.

00:37.220 --> 00:47.420
So my pi pkg and let's create here another file that's going to be battery dot pi.

00:47.450 --> 00:53.900
Let's also make it executable with plus x.

00:54.080 --> 00:54.980
Okay.

00:55.490 --> 01:00.180
And let's edit it in the code.

01:00.690 --> 01:07.020
So let's get the templates here in battery dot pi.

01:07.680 --> 01:09.750
So that's going to be the battery node.

01:14.280 --> 01:15.120
Okay.

01:15.210 --> 01:17.010
Battery here.

01:18.210 --> 01:19.710
Let's keep things simple.

01:22.500 --> 01:25.560
And I'm going to do this step actually in two steps.

01:25.560 --> 01:28.230
First we are going to kind of simulate.

01:28.260 --> 01:34.230
We are going to fake the mechanism of the battery to say when it's full or when it's empty.

01:34.230 --> 01:38.730
And when that is working we are going to call the service.

01:38.760 --> 01:39.870
So let's get started.

01:39.870 --> 01:42.570
And how can we monitor the battery state?

01:42.600 --> 01:49.230
How can we say that the battery state is empty after four seconds and then full again after six seconds

01:49.290 --> 01:50.370
repeatedly?

01:50.400 --> 01:55.830
Well, first what I'm going to do is I'm going to create a timer and say that we check the battery state

01:55.860 --> 01:57.330
ten times per second.

01:57.360 --> 02:04.160
So usually if you had a real battery, you would probably create something like a timer and then check

02:04.160 --> 02:06.890
the battery state every X amount of time.

02:06.890 --> 02:08.330
Maybe you don't need that much.

02:08.330 --> 02:11.390
Maybe every one minute, every 10s.

02:11.420 --> 02:14.000
Well, here we're going to do it ten times per second.

02:14.000 --> 02:20.360
So let's create a timer first here self dot.

02:20.390 --> 02:21.890
Let's call it battery.

02:22.400 --> 02:31.580
Timer is equal to self create create timer.

02:31.580 --> 02:33.440
And I set 0.1.

02:33.440 --> 02:35.990
So ten times per second self.

02:35.990 --> 02:44.570
And let's create a function here check battery state.

02:47.330 --> 02:47.810
Okay.

02:47.840 --> 02:50.600
And let's provide the callback here.

02:51.290 --> 02:52.820
Check battery state.

02:53.330 --> 02:53.690
All right.

02:53.690 --> 03:00.720
So here you would in a real application directly check the battery state and from that you can know

03:00.720 --> 03:03.930
if maybe the battery is full or empty and call the service.

03:03.930 --> 03:05.970
But now we don't have a real battery.

03:05.970 --> 03:08.370
So I'm going to fake the behavior.

03:08.370 --> 03:14.400
And like I gave you in the activity, we're going to say that let's say the battery is full and then

03:14.400 --> 03:16.410
after four seconds is going to be empty.

03:16.440 --> 03:18.510
And then six seconds later it's full again.

03:18.510 --> 03:20.430
So we have a ten second cycle.

03:20.430 --> 03:28.470
And how can I check this duration of four seconds and six seconds from the callback here so I could

03:28.470 --> 03:29.220
save the time?

03:29.220 --> 03:32.310
For example, I could save the time when the last time.

03:32.310 --> 03:37.080
So the actual the last time that the battery state was changed, and then I could compare with the current

03:37.110 --> 03:41.730
time and see if we have spent at least 4 seconds or 6 seconds.

03:41.730 --> 03:44.850
So here I could start with creating a variable.

03:44.880 --> 03:48.750
Time now is equal to the current time.

03:48.750 --> 03:50.130
How to get the current time.

03:50.130 --> 03:56.360
There is actually something you can use in Ros two, and I'm going to create a new function for that.

03:56.390 --> 03:59.240
Diff get current time.

03:59.240 --> 04:02.150
You will see why in a second.

04:02.180 --> 04:04.430
I want the time in seconds to make it simple.

04:04.430 --> 04:06.110
You can use the self.

04:06.110 --> 04:18.170
So from the node you have get clock and then dot know and then dot sequence nanoseconds.

04:18.200 --> 04:23.270
This will give you a tuple of seconds and nanoseconds two integers.

04:23.270 --> 04:33.410
So for example seconds nano seconds is equal to self get clock now seconds nanoseconds.

04:33.740 --> 04:34.370
All right.

04:34.400 --> 04:37.430
And well we don't really want a tuple like this.

04:37.460 --> 04:39.920
We want a float number.

04:39.920 --> 04:47.720
So let's just do a return seconds plus nano seconds divided by.

04:47.750 --> 04:49.760
And then we need nine zeros.

04:49.760 --> 04:53.670
So Eight nine.

04:53.790 --> 04:55.830
And let's put dot zero.

04:55.920 --> 04:56.190
Okay.

04:56.220 --> 05:00.540
So make sure you have nine zeros 123456789.

05:00.570 --> 05:00.930
All right.

05:00.960 --> 05:05.010
So now we have the current time with a comma with a decimal point.

05:05.010 --> 05:09.210
And I've created a function so that we don't need to do that in the code.

05:09.210 --> 05:10.890
We just get the current time sequence.

05:10.890 --> 05:12.060
And that's going to be easier.

05:12.090 --> 05:17.880
So self dot get current time sequence.

05:17.910 --> 05:18.390
All right.

05:18.390 --> 05:25.350
So every time we get to this callback timer here which is ten times per second, we get the current

05:25.380 --> 05:25.650
time.

05:25.650 --> 05:28.950
And we're going to check with the last time.

05:28.950 --> 05:30.600
The last time what actually.

05:30.600 --> 05:33.390
Well the last time that the battery state changed.

05:33.390 --> 05:47.580
So I'm going to add here self last time battery state changed for example I can initialize it to the

05:47.580 --> 05:52.700
current time get current time sequence.

05:52.730 --> 05:53.450
Okay.

05:53.540 --> 06:00.170
And so I will do if time now minus the last.

06:00.830 --> 06:07.010
So last time the battery state change is greater than 4 seconds or 6 seconds.

06:07.040 --> 06:07.430
Okay.

06:07.460 --> 06:10.310
And how to know which one we want to check.

06:10.340 --> 06:17.960
Well we could create here I'm just going to create a string self dot battery.

06:18.350 --> 06:21.770
Battery state.

06:22.010 --> 06:25.910
Here I'm just going to say full.

06:26.300 --> 06:26.750
Okay.

06:26.780 --> 06:33.380
You could create a number for example that goes between 0 and 100 something like that.

06:33.410 --> 06:36.230
Well here we don't have a battery.

06:36.230 --> 06:38.390
So I'm just going to make a very simple example.

06:38.390 --> 06:41.930
So it's going to be full or it's going to be empty with a string.

06:41.930 --> 06:50.610
And I can say if the self battery state is full.

06:52.080 --> 07:00.240
Then inside this if I add this other if I check the time now minus the last time the battery state change.

07:00.240 --> 07:03.510
So time minus time gives us a duration.

07:03.660 --> 07:14.130
If this is greater than four seconds, then I can say that the battery state is empty.

07:14.970 --> 07:18.660
And then maybe add a log get logger.

07:18.690 --> 07:22.350
That's going to help us to do debug what's happening in the code.

07:23.250 --> 07:30.090
Let's say battery is empty, um, charging.

07:31.170 --> 07:36.390
And there's one more thing we need to do in this case is that, uh, well, we have changed the battery

07:36.390 --> 07:40.590
state, so we need to update the last time the battery state changed.

07:40.590 --> 07:45.810
So self dot last time battery state changed.

07:45.960 --> 07:48.690
This is going to be time now.

07:48.720 --> 07:53.410
So Now that's the time we have right now when we execute this specific callback.

07:53.440 --> 07:53.950
Okay.

07:53.980 --> 07:58.270
So this structure is quite common in programming and in robotics in general.

07:58.300 --> 07:58.660
Okay.

07:58.690 --> 08:03.250
You have a function that you call quite often here ten times per second.

08:03.250 --> 08:05.470
Or it could be much, much faster.

08:05.470 --> 08:09.580
You save the time of an event that you want to monitor.

08:09.580 --> 08:14.680
And if you want to check a duration, then you get the current time and you just do current time minus

08:14.680 --> 08:15.460
last time.

08:15.460 --> 08:20.530
If this is greater than the duration you want, then you can well, you can do an action, change a

08:20.530 --> 08:21.490
state, whatever.

08:21.490 --> 08:26.140
And you keep basically the last time becomes the current time.

08:26.170 --> 08:26.680
Okay.

08:26.710 --> 08:32.080
And we have done half of the code here because the battery.

08:32.080 --> 08:34.450
So it's full and then it's going to be empty.

08:34.450 --> 08:37.150
But then we also need to check else.

08:37.150 --> 08:46.360
So we could do else if uh self dot battery state is empty.

08:46.390 --> 08:48.690
Actually I'm going to keep it like that.

08:48.720 --> 08:53.550
We could just do an else because we will have only two states, but it's probably more explicit like

08:53.550 --> 08:54.030
this.

08:54.030 --> 08:56.160
So if it's empty, I do.

08:56.190 --> 09:03.660
If time now minus self last time battery state change.

09:03.660 --> 09:15.780
And this time we are going to wait for six seconds, then I can do self battery state is full and let's

09:15.810 --> 09:17.070
add another log.

09:17.670 --> 09:21.150
Get logger info.

09:21.660 --> 09:33.180
Let's say the battery is now full and we also do self dot last time battery state change.

09:33.240 --> 09:39.330
This is time now and that's how we're going to fake the battery state.

09:39.330 --> 09:41.850
So this is just my implementation okay.

09:41.880 --> 09:43.320
This is my solution.

09:43.320 --> 09:46.120
Maybe you have come up with a different solution Okay.

09:46.150 --> 09:52.060
I didn't give you so many information about using those functions here and this mechanism.

09:52.090 --> 09:52.300
Okay.

09:52.330 --> 09:54.310
That was to make you think more.

09:54.340 --> 09:54.550
Okay.

09:54.730 --> 09:56.050
That's a real challenge.

09:56.080 --> 09:59.800
You're not going to have the solution to everything you do when you program with Ros2 and when you do

09:59.830 --> 10:00.370
robotics.

10:00.370 --> 10:03.580
So you might have come up with a complete different solution.

10:03.580 --> 10:05.980
But if it still works the same, it's fine.

10:06.010 --> 10:06.340
Okay.

10:06.370 --> 10:09.460
So we have let's make sure we have the timer.

10:09.460 --> 10:12.160
So that's going to get called ten times per second.

10:12.160 --> 10:14.410
And just a quick recap of what's going to happen.

10:14.410 --> 10:16.000
So the battery state is full.

10:16.000 --> 10:19.240
The last time the state changed is right now.

10:19.240 --> 10:22.420
So we're going to save it in the constructor.

10:22.450 --> 10:24.850
Then we call that and then we check.

10:24.850 --> 10:27.610
So we have if the battery is full that's the case.

10:27.610 --> 10:32.500
We're going to check if the time to the current time minus the last time is greater than four seconds.

10:32.500 --> 10:39.640
So we're not going to go inside this until four seconds have passed since basically the constructor

10:39.640 --> 10:40.690
was executed.

10:40.690 --> 10:42.940
And then we change the battery state.

10:43.030 --> 10:47.100
We print a log and we save the current time as the last time.

10:47.100 --> 10:52.830
So then the next time that we enter this check battery state method, we're going to also get the current

10:52.860 --> 10:55.680
time and the battery state is going to be empty.

10:55.680 --> 10:58.290
So we're going to go inside this Elif.

10:58.290 --> 11:02.730
And then we can only enter this if after six seconds.

11:02.730 --> 11:09.480
And when we enter this if we change the battery state to full, we also save the last time.

11:09.480 --> 11:12.420
So basically the current time becomes the last time.

11:12.420 --> 11:20.370
And then again we will go back to this first if okay etc. etc..

11:20.370 --> 11:21.390
So let's try that.

11:21.390 --> 11:22.530
Let's see if it works.

11:22.530 --> 11:24.930
We have some logs that's going to be very useful.

11:24.960 --> 11:28.020
I'm going to save and let's go to setup.py.

11:28.020 --> 11:30.540
And let's add yet another executable here.

11:30.540 --> 11:39.930
Battery is equal to my Python package dot battery because it's battery.py and the main function.

11:39.960 --> 11:52.300
Okay let's build Built that Qualcomm build packages select let's build a Python package and let's also

11:52.300 --> 11:54.400
add Simulink install.

11:56.320 --> 11:56.830
Great.

11:56.830 --> 11:59.080
And let's run the battery node here.

11:59.080 --> 12:07.330
Source workspace and Ros to run my Pi PG with battery.

12:08.290 --> 12:09.790
And let's see what we have.

12:09.820 --> 12:12.520
I didn't have any log in the constructor.

12:12.550 --> 12:13.750
Maybe I'm going to add one.

12:13.750 --> 12:15.970
And you see battery is empty.

12:16.930 --> 12:18.340
Let's wait a bit more.

12:19.360 --> 12:22.570
Battery is now full and you can see the difference of time.

12:22.570 --> 12:24.970
Here is six seconds.

12:24.970 --> 12:29.890
And now we have four seconds okay so let's wait a bit more okay.

12:29.920 --> 12:33.370
And you see we have two full cycle now.

12:35.710 --> 12:40.060
So when we look at the logs you see we have a cycle here 10s.

12:40.090 --> 12:41.400
And then 10s.

12:41.430 --> 12:46.800
We have six seconds between empty and full and four seconds between full and empty.

12:46.830 --> 12:47.310
All right.

12:47.310 --> 12:53.700
So that was the probably the main challenge of this step is to come up with a solution to fake this

12:53.700 --> 12:54.390
behavior.

12:54.390 --> 12:57.480
And now what's left is quite straightforward.

12:57.480 --> 13:03.720
We need to call the set led service that we have created in the LED panel node.

13:03.720 --> 13:07.230
When the battery is empty, we want to turn on the third LED.

13:07.590 --> 13:10.650
And when the battery is full we want to turn it off.

13:10.650 --> 13:15.660
And so we're going to use the basically the same thing we did for the add to insert client.

13:15.690 --> 13:16.230
Okay.

13:16.260 --> 13:19.620
We're going to create a client inside the constructor.

13:19.620 --> 13:28.800
So let's create a client here I'm first going to add a log actually self dot get logger.

13:29.580 --> 13:37.470
Battery node has been started okay.

13:37.500 --> 13:42.400
And here I'm going to create a client self dot set.

13:42.430 --> 13:50.290
LED client is equal to Self-create client and I need to provide a service type.

13:50.290 --> 14:01.780
So I need to do from my robot interfaces dot srv import set LED okay.

14:01.810 --> 14:04.480
And I'm already using my robot interfaces in this package.

14:04.480 --> 14:08.350
I've already set up the package dot XML with the dependency.

14:08.560 --> 14:12.640
So let's put set LED and the name.

14:12.640 --> 14:18.700
So let's make sure we use the same name here set LED.

14:19.210 --> 14:19.600
All right.

14:19.600 --> 14:21.940
So we have our client here.

14:22.240 --> 14:27.580
Then what we can do is we can create a function to call the service.

14:27.820 --> 14:29.470
So let's do that here.

14:29.470 --> 14:34.720
For example the order doesn't matter call set LED.

14:34.870 --> 14:39.960
And I'm going to put self and then led number state.

14:40.620 --> 14:41.040
All right.

14:41.040 --> 14:42.750
And what do I need to do?

14:42.780 --> 14:47.760
Once again, if I come back to this example, I'm going to first wait for the service.

14:47.850 --> 14:51.330
Then I'm going to create a request and send that request.

14:51.330 --> 14:56.670
So let's do that while not self.

14:57.690 --> 15:02.940
And let's set LED client dot wait for service.

15:03.150 --> 15:15.660
With one second we could say self dot get logger one waiting for set LED service.

15:16.050 --> 15:19.050
And you might want to handle that differently.

15:19.050 --> 15:23.550
Maybe because here it's going to block here in this function.

15:23.550 --> 15:31.170
But because this service is quite important for this node, you could also decide to do the while not

15:31.200 --> 15:37.750
here directly in the constructor for example, to check that the service is up before you finish the

15:37.750 --> 15:39.880
initialization of the node.

15:39.910 --> 15:40.480
Okay.

15:40.510 --> 15:46.840
Or here, instead of doing a while loop, you could just return an error if the service is not found.

15:46.840 --> 15:48.760
So there are different ways of handling that.

15:48.790 --> 15:50.470
It's going to depend on your application.

15:50.470 --> 15:50.980
Here.

15:50.980 --> 15:54.040
We just wait for the service to be up and that's it.

15:54.340 --> 16:03.250
Now we can create our requests which is going to be set LED dot request.

16:03.370 --> 16:11.980
And then we do request dot led number, which is the LED number we got from the parameter here.

16:11.980 --> 16:17.950
And request dot state which is the state.

16:17.950 --> 16:30.220
And then we can do so we're going to get a future out of that self set LED client dot call async with

16:30.220 --> 16:31.810
the request.

16:32.020 --> 16:32.740
All right.

16:32.740 --> 16:35.050
So same thing what we did here.

16:35.050 --> 16:37.760
and we can add a Dawn callback.

16:37.910 --> 16:40.340
So let's create a callback actually.

16:41.180 --> 16:47.570
So def callback set led.

16:47.570 --> 16:50.300
So maybe callback call set led.

16:50.600 --> 16:54.140
That's the callback for this method self.

16:54.140 --> 16:56.210
And we get a future.

16:57.320 --> 16:57.560
Okay.

16:57.590 --> 17:00.200
So here let's do pass here.

17:00.200 --> 17:01.700
So we don't have the error.

17:02.720 --> 17:11.960
And we can do future dot add dawn callback self callback call set LED okay.

17:11.960 --> 17:13.010
Just like we did here.

17:13.010 --> 17:16.550
And note that I didn't provide the request.

17:16.550 --> 17:21.170
You could pass the request as well, or you could pass any other parameter.

17:21.170 --> 17:26.570
In this case you would have to use the partial keyword here with the Functools functionality.

17:26.600 --> 17:28.010
I'm not going to do it here.

17:28.040 --> 17:30.530
It's not necessarily useful for this application.

17:30.530 --> 17:36.790
So I'm just going to provide the callback here with We've had done callback and we just receive a future.

17:36.790 --> 17:44.020
So it's a bit simplified and then we can just do future dot result to get the response.

17:44.320 --> 17:50.590
So response is equal to future dot result.

17:50.590 --> 17:53.260
And let's just print this.

17:53.260 --> 17:56.620
So actually this is going to be a boolean.

17:56.620 --> 18:00.970
We can say if response dot okay.

18:01.000 --> 18:09.520
And here maybe I can also provide the type I can say that's going to be set LED um response.

18:09.850 --> 18:15.580
So then I have the auto completion response dot success.

18:15.580 --> 18:22.720
If success self get logger and then info.

18:23.710 --> 18:39.110
And we can say led turned on for example else self get logger info led not changed.

18:39.140 --> 18:43.550
Okay, that's probably not going to happen because we're going to give something that's valid.

18:43.550 --> 18:49.520
But well you can handle the response here if needed and monitor the different errors.

18:49.550 --> 18:49.850
Great.

18:49.850 --> 18:51.770
So let's do a quick recap here.

18:51.770 --> 18:59.540
Once again we have so we have our client here that we have created with the interface and the name.

18:59.540 --> 19:00.740
Then we have a function.

19:00.740 --> 19:01.880
We wait for the service.

19:01.910 --> 19:06.710
We create a request and we call asynchronously the service.

19:06.740 --> 19:12.110
We register a callback that's going to be called when the server replies to the client.

19:12.110 --> 19:15.680
And then we get the response and we just print a log here.

19:15.710 --> 19:19.070
Now the only thing left is to actually call this method.

19:19.070 --> 19:20.690
And where do we call this method.

19:20.690 --> 19:25.370
Well, after we just change the state where we print a log.

19:25.400 --> 19:29.630
And I'm going to do here self dot call set LED.

19:29.960 --> 19:31.600
So what's the LED number?

19:31.630 --> 19:33.490
I start to count at zero.

19:33.490 --> 19:34.960
So that's zero one and two.

19:34.990 --> 19:35.890
For the third LED.

19:36.100 --> 19:38.770
And the state is going to be one.

19:38.830 --> 19:39.040
Okay.

19:39.040 --> 19:41.860
When the battery is empty I want to notify the user.

19:41.860 --> 19:50.140
And then when the battery is full I'm going to do call set LED with also the same index.

19:50.140 --> 19:53.350
And then it's going to be zero for the state zero.

19:53.380 --> 19:53.950
All right.

19:53.950 --> 19:55.630
And let's save.

19:55.630 --> 19:57.370
And that's going to be it for the code.

19:57.370 --> 19:59.530
So we've already tested the behavior here.

19:59.560 --> 20:04.240
Now let's test if the client call is going to work successfully.

20:04.240 --> 20:06.310
So let's go here.

20:06.310 --> 20:10.090
And well I have run with Simulink install.

20:10.090 --> 20:12.970
So it should work.

20:13.960 --> 20:20.590
I'm just going to well I'm just going to run it here and let's do a source bash SC just in case.

20:21.520 --> 20:31.640
Bash SC I'm going to run here the LED panel node which is needed first because, well, we need the

20:31.640 --> 20:33.230
service for the battery.

20:34.220 --> 20:34.550
Okay.

20:34.580 --> 20:40.610
LED panel and I'm going to run my battery node.

20:41.840 --> 20:47.870
So just run my Pi PG with battery.

20:47.870 --> 20:57.170
And actually before that I'm just going to do a Ros two topic eco led panel state to see what's happening

20:57.290 --> 20:58.460
on those LEDs.

20:58.460 --> 21:00.500
So for now they are powered off.

21:00.530 --> 21:02.900
Okay, let's run the battery.

21:04.070 --> 21:05.750
And you see battery has been started.

21:05.750 --> 21:10.610
We need to wait six seconds and you see battery is empty turned on.

21:10.610 --> 21:13.910
And we see that LED is one still one.

21:13.910 --> 21:17.840
And then LED turned uh, on what?

21:17.870 --> 21:20.060
Actually, I probably need to change the log.

21:20.780 --> 21:22.100
Let's go back here.

21:24.170 --> 21:28.370
Well, you could pass the request and know what you have decided to do.

21:28.940 --> 21:35.090
Let's say led state was changed.

21:37.160 --> 21:38.870
And let's run it again.

21:40.310 --> 21:41.600
So we start again.

21:41.600 --> 21:43.220
But reload has been started.

21:43.250 --> 21:44.630
Let's wait for six seconds.

21:44.660 --> 21:46.550
You see, battery is empty.

21:46.580 --> 21:51.440
Charging LED state was changed and then is no fool.

21:51.440 --> 21:53.000
We go back to zero.

21:53.450 --> 21:54.740
Let's wait another.

21:55.190 --> 22:00.740
You see empty is one and then full.

22:00.770 --> 22:02.390
It goes back to zero.

22:02.390 --> 22:04.250
And again and again and again.

22:04.280 --> 22:08.360
That's going to be infinitely repeating the same cycle.

22:11.390 --> 22:11.810
All right.

22:11.810 --> 22:13.700
And that's the end of this activity.

22:13.700 --> 22:16.250
So you have practice here with everything.

22:16.250 --> 22:17.540
You have created nodes.

22:17.540 --> 22:19.970
You have created topics services.

22:19.970 --> 22:26.480
You have run those communications between two nodes and all of that also using custom interfaces.
