In this laboratory lesson, we will create a simple ros2 lifecycle node in Python that subscribes to
a topic and displays in the terminal the current state of the life cycle node, and also any message
that is received from the topic only if the lifecycle node is in the active state.
So back to Visual Studio Code.
Since we are creating a simple code that we won't use to add a new functionality to the robot, but
only to provide you with a sample code.
So with a template that you can use as a starting point to create also new life cycle nodes for your
projects.
We are going to create this node inside the Arduino board Pi example package.
And here within the Arduino board Pi example subfolder, let's create a new file and let's call it simple
life lifecycle node.py.
So let's create this file.
And here as always let's start by importing the CLP library.
And then still from this library.
So from the CLP library whether to create a simple node we had to import the node class.
To create a lifecycle node we need to import the node class, but this time from the life cycle module.
So let's import from the life cycle.
So from the RCMP life cycle.
Let's import the node class.
So now this node class that we are importing here is from the life cycle module.
Now whether to create a simple Ros2 node we had to create a class that inherited from the node class
of the CLP library.
To create a life cycle node, we need to do the same.
So we need to create a new class.
Let's call it simple life cycle node and this one inherits from the node class.
So from the life cycle node class that we have imported here from the CLP lifecycle module.
Now let's start by declaring the constructor of this class.
So the init function and within this constructor.
So this one here takes as input a list of arguments starting with the node name.
And also eventually any arguments.
So k w args like this.
And here within the constructor, the only thing that actually we need to do is to initialize the base
class.
So we need to call the constructor of the base class.
So basically let's call the init function of the base class.
And to this function we can pass the same arguments that we have received as input.
So here the node name and the arguments.
So let's provide all of them to the constructor of the simple life cycle node.
At this point we have created and initialized our life cycle node class.
And as we have seen in the theoretical lesson, when the life cycle node is created, it is in the unconfigured
state in which we only have two transitions available that are the configure and the shutdown transition.
So let's define within the simple lifecycle node class.
So here the behavior of these two transitions.
So the behavior of these two functions that respectively bring the state machine so the lifecycle node
to the inactive state or to the finalized state.
So let's start defining the on configure Unconfigure transition.
So let's define the unconfigure.
And this is a member of the simple lifecycle node class here.
And this function takes one input that is the current state of the lifecycle node represented as an
object of the state class that is still declared here in this module here.
So let's import also the state class.
And here the Unconfigure function receives a state as input.
That is an object of type state.
Now the output of this function instead represents whether or not the transition was successful.
And this is represented by another object still from the RCL pi lifecycle module.
And this is an object of type.
transition callback return.
So actually we can use this class here in order to set that the Unconfigured function will eventually
return an object of type transition callback return perfect.
And so now we can move on to define actually this function here.
So to define the Unconfigured function.
Now here within the body of this function, when this transition is activated, we want the lifecycle
node to register a new subscriber to ascertain Ros2 topic.
So let's create a new subscriber.
So a new variable called subscriber that we can initialize with the create subscription function.
So still let's use the create subscription function.
This one here that actually we are still inheriting from this class here.
So from the node class.
And now within the create subscription function we need to indicate the type of the interface.
So the type of the message that we want to receive with this subscriber.
And actually we want to receive messages that are of type string.
So let's import this message.
So from the standard messages and from the messages let's import the string message.
And now we can use it here within the create subscription function.
So this receives a string.
And also here in the create subscription function we need to indicate the name of the topic in which
we want to subscribe.
So where we want to receive the messages and let's call this one for example the chapter topic.
And then we also need to set the name of the callback function that we want to execute whenever we receive
a new message on this topic here.
So for example, let's execute a new function called message callback.
And so this function here we are going to define this function here still within the simple lifecycle
node class.
Also let's set the queue size of ten.
So a standard queue size of ten.
Now once the subscriber is correctly initialized let's also print a message in the terminal.
So with the get logger function and let's print an informative message that says life cycle node.
Unconfigure cold.
So we are just informing the user that the unconfigure function of the life cycle node has been called.
Now here.
So after printing this message, let's simply return a success message.
So let's return an object of type transition callback.
return of type success.
So this one here, and basically this success message indicates that the Unconfigure function was successfully
executed.
And so now the lifecycle node is in the inactive state.
Now let's define also the second transition that is available while the lifecycle node is in the unconfigured
state.
And so this is the shut down transition.
Actually, in order to define this we can copy all the Unconfigured function as basically also the on.
Shutdown function will receive the same input.
So we receive the state of the lifecycle node.
And also we will return an object of type transition callback return.
Now when we execute this transaction, we want to move the state machine of the lifecycle node into
the finalized state, which is actually the state that precedes the destruction of the node.
So before destroying the node, we want to deactivate the subscriber.
So actually let's take the subscriber and let's just use the.
destroy subscription function.
So this is another function that we have inherited from the node class.
And let's simply destroy the subscriber.
So the sub object that we have created so that we have created and initialized previously.
And also let's print an informative message in the terminal to inform the user that the on.
shut down function has been correctly called and executed, and also in this case, let's return a message
of type transition callback return of type success.
So in order to indicate that also this transition here has been correctly executed.
Now once we have configured the lifecycle node with the configure transition it enters the inactive
state.
In this state, there are three transitions available the clean up transition, which brings the state
machine back to the unconfigured state.
Then there is the shut down which we have already defined and which brings the state machine to the
finalized state.
And finally, we have the active state transition, which instead brings the state machine to the active
state.
Now let's start by defining the clean up transition.
So the one that brings the note back to the unconfigured state.
Actually, to define this function, we can simply copy the on shut down function.
And we can paste it here.
Let's call it on clean up.
So also this one receives an object of type state and also will return an object of type transition
callback return.
Now in this function, we can still destroy the subscription, so we can still destroy the subscriber
that we have initialized here in the Unconfigured transition.
And also here let's print a different message in the terminal that says on clean up called.
And also in this case let's return success.
Let's also define the on activate function.
So again we can copy the on clean up function.
We can paste it.
Let's rename it to on activate.
And when this function is executed.
So the activate transition we want to print a message in the terminal that informs that this function
is being called.
So we can remove this line here, and we can simply print the message on activate called.
And also in this function here, in order to simulate the execution of various operations.
For example, when activating a real life cycle node.
Let's pause this thread.
So let's pause this current thread for a few seconds.
So here let's import also another Python library that is the time library.
And here before actually returning the success, let's simply sleep for two seconds.
Let's say perfect also when we call.
So when we execute the on activate function, let's also call the on activate function for the parent.
So for the base class that is the node class.
So let's use the super in order to execute the UN activate function for the parent.
And this one receives as input the same state that we have here.
And actually we can just take this one and we can paste it here.
So actually we can return the result of this operation here.
So the result of the activation of the base class.
Now once in this state.
So once in the active state there are only two transitions available.
The shutdown transition which we have already defined and implemented and which brings the state machine
into the finalized state.
And then we have the on deactivate, which instead brings the life cycle node back to the inactive state.
So let's define this last transition that is the on deactivate.
So actually we can just copy the on activate.
And let's change its name to be on deactivate.
And and inside this function, let's change the message that we are printing.
That is the on the activate cold.
Perfect.
And here actually we don't need the sleep so we can remove it.
And also in this case so also when we deactivate the lifecycle node let's call the on the activate.
So let's deactivate also the base class.
So basically we are deactivating also the base class from which we are inheriting.
With this we have defined the behavior of all the transition of the lifecycle node.
Now the only thing that is left to do is to define the message callback function.
So this last function here that is executed whenever we receive a new message on the chatter topic.
So let's define the message callback.
This is still a member of the simple life cycle node class, and as it is a callback function, it receives
the message that was just received in the topic.
Now, when we receive a new message on the chapter topic, we first want to check whether the current
state of the life cycle node is active or not in order to decide whether to display the message.
So to do so, let's take the state machine so we can access to the state machine that we have inherited
from the node class.
And actually we can access to the current state of the state machine.
And actually in order to access to the current state easily, let's save this one into a new variable
that we are simply calling current state.
And now let's check this current state.
So if the current state and actually the first element is of type active.
So if the state machine is in the active state.
So if the name of the current state is active in this case we just want to print an informative message
in the terminal with the content of the message that we have just received.
So let's get the logger and let's print an informative message that says I erred.
Followed by the actual message.
So this is a string and let's print the content of the message data.
So perfect.
This one is a string message.
And so here we are simply printing the content of the message that we have received otherwise.
So if the current state of the state machine is not active so is any of the other states.
We are just not going to print anything.
So we are just ignoring the message.
Perfect.
So with this we have completed our simple life cycle node class and so we can move on to define the
main function.
So here let's define the main function.
And so we are missing to define the function main like this.
Perfect.
And here within this function, let's start by initializing rcl py with rcl py.
Init.
And then let's create a new object of type single thread executors.
So let's call this one executor.
And from the RCMP executors module let's create a new object of type single thread executor.
And also we can create a new instance of the simple single node that we have just created.
So simple lifecycle node.
And this is an instance of the simple lifecycle node.
And now the constructor.
As you remember the constructor of this class here takes as input the name of the node.
So for example, let's call it simple.
Life cycle node.
Perfect.
And now we simply in order to execute actually this node here we can add it to this executor.
So with executor let's add a node.
And the node that we want to add is the simple life cycle node that we have just created.
Now in order to keep this node up and running.
So in order to start actually the execution of this node, let's try to execute the function executor
spin.
Perfect.
And if there is an exception.
So if for example there is a keyboard interrupt.
So if we stop manually the execution of the node with Ctrl C or for example, if there is an error of
type rcl pi executors external shutdown exception.
So in other cases we want to simply destroy the node.
So let's take the simple lifecycle node and let's destroy it.
So let's use the function destroy node perfect so we can save this file.
And finally to conclude this lesson let's install the new node that we have just created.
So let's go to the file setup.py that is in the Arduino bot Pi examples package here.
Actually we can copy one of the instructions that we've already used, and we can paste it here at the
end of the file.
So the new node that we are creating is called simple lifecycle node.
And the script is in the Arduino Pi examples folder is called simple lifecycle node.
And also from this node we want to execute the main function.
Now let's save also this file.
And to conclude this lesson let's simply apply these changes.
So let's simply make this new node.
So the simple cycle node and executable.
So to do so let's open a new terminal.
Let's go to the workspace and let's build it.