Skip to content

Example explanation (Python)

Carlos Maestre edited this page Jun 5, 2016 · 8 revisions

Example explanation (Python)

Creating and deleting nodes

A simple example has been created to better explain CAFER's functionality using Python. This example shows how to define a simple CAFER node creating and deleting another 2 CAFER nodes.

First, the basic services of CAFER are launched, and then, a CAFER node and its correspodant component are created using the basic_example_launch_set_nodes.cpp file, executed by a launch file basic_example_launch_set_nodes_python.launch. Let's take a look to the launcher code:

<launch>
  <node name="getid" pkg="cafer_core" type="getid" ns="/cafer_core" />

  <arg name="ns" default="basic_example_ns"/>
  <arg name="management_topic" default="/basic_topic_mgmt"/>
  <arg name="frequency" default="10"/>
  <arg name="creator_ns" default="unset"/>
  <arg name="creator_id" default="-1"/>
  <arg name="nb_nodes" default="2"/>
  <arg name="type_value" default="basic_example_launch_set_nodes"/>
  <node name="basic_example_launch_set_nodes" pkg="cafer_core" type="basic_example_launch_set_nodes.py" ns="$(arg ns)" output="screen">
      <param name="frequency" type="double" value="$(arg frequency)" />
      <param name="management_topic" type="string" value="$(arg management_topic)" />
      <param name="creator_ns" type="string" value="$(arg creator_ns)"/>
      <param name="creator_id" type="int" value="$(arg creator_id)"/>
      <param name="created_ns" type="string" value="$(arg ns)"/>
      <param name="nb_nodes" type="int" value="$(arg nb_nodes)"/>
      <param name="type_value" type="string" value="$(arg type_value)"/>
  </node>
</launch>

This launch file defines first creates a CAFER node generating a unique ID each time is called, calling to the getid.cpp file. Then, it defines a set of parameters, and executes the Python file where the functionality of the node is contained. The parameters defined are the namespace to add the node, a management topic to share information between the different nodes of the exercise, the message exchange frecuency, and the type of nodes to be used. These parameters are global for all the nodes of the example, and any of them can get them. It's revelevant to mention the output="screen" option of the node. If this option is provided the node will be able to print in the stardard output (usually a terminal). The launch file is executed as:

roslaunch cafer_core basic_example_launch_set_nodes_python.launch

Now, let's take a look to the functionality of the node basic_example_launch_set_nodes.py. This file has to be set as executable, both adding #!/usr/bin/python at the beginning of the .py file, and providing the right permissions:

chmod +x basic_example_launch_set_nodes.py

First, a dummy client is defined inheritating from the cafer_core::Component class, overriding its virtual methods to be able to access its functionality, but not adding any other functionality:

#!/usr/bin/python

import rospy
from cafer_core.msg import *
from std_msgs.msg import String
import sys
import rospkg

import component

class DummyClient (component.Component) :

	def client_connect_to_ros(self):
		pass

	def client_disconnect_from_ros(self):
		pass

	def update (self):
		pass

	def init (self):
		pass

	def is_initialized (self):
		return True

A main method executes the functionality of the CAFER node, encoded on the function set_nodes_function:

if __name__ == '__main__':
	try:
		set_nodes_function()
	except rospy.ROSInterruptException:
		pass
}

def set_nodes_function():

	''' New node '''
	component.python_init('basic_example_launch_set_nodes')

	name_tmp = rospy.search_param('management_topic')
	param_dict = rospy.get_param(name_tmp)['basic_example_launch_set_nodes']
	management_topic = param_dict['management_topic']
	type_value = param_dict['type_value']
	nb_nodes = param_dict['nb_nodes']
	freq = param_dict['frequency']
	ns = component.python_get_node_name()
	# ns = rospy.get_namespace()
	# print("Launching "+str(nb_nodes)+")  new nodes (name="+rospy.get_name()+")")	
	print("Launching "+str(nb_nodes)+" new nodes (name="+ns+")")

	''' Create the component in charge of calling the launch file for creating the new nodes '''
	cc = DummyClient(management_topic, type_value, int(freq), False)
	cc.wait_for_init()	

	''' Finding the path towards the launch_file to call '''
	rospack = rospkg.RosPack()
	basic_example_new_node_launch = rospack.get_path('cafer_core') + '/launch/basic_example_new_node.launch'

	''' Creation of the new nodes '''
	created_namespaces = []
	for i in range(0,nb_nodes):
		print("Params : " + " " + ns + " " + management_topic + " " + type_value + " " + str(nb_nodes) + " " + str(freq))
		created_namespaces.append(cc.call_launch_file(basic_example_new_node_launch, ns+'/basic_node', management_topic))
		cc.sleep()

	''' Checking that they are up '''
	nb_tries=10
	count = -1
	while nb_tries > 0 :
		count=20
		# while (count>0) and len(cc.python_get_created_nodes_id) != nb_nodes :
		print ('cc.python_get_created_nodes_number() : ', cc.python_get_created_nodes_number())
		while (count>0) and cc.python_get_created_nodes_number() != nb_nodes :		
			cc.spin()
			cc.update()
			cc.sleep()
			count = count - 1

		if count == 0 :
			print("PROBLEM: we haven't received the ack from some nodes. We ask for a new ack.")
			print("============= Ack received from "+cc.get_created_nodes().size()+" components: ")

			cc.python_print_created_nodes_id()

			print("=========== End ack received")
			print("")

			cc.ask_new_ack()
			cc.spin()
			cc.update()
			cc.sleep()

		else :
			break    

		nb_tries = nb_tries -1

	if nb_tries  == 0:
		print("PROBLEM: the nodes haven't been all launched.")
		while not rospy.is_shutdown() and not cc.get_terminate() :
			cc.spin()
			cc.update()
			cc.sleep()
		sys.exit()

	''' At this point at least one node from each call to call_launch_file has answered. 
	As there is only one node in the launch file, it should be fine, but just to be sure 
	(and to show how to check it) we verify that all of those nodes are up. '''
	all_up = cc.python_clients_status("up")

	if not all_up :
		print("PROBLEM: the nodes aren't up.")
		sys.exit()

	''' We kill all created_nodes '''
	cc.kill_created_nodes()

	''' We check that they are down '''
	count = 100*nb_nodes
	all_down = False
	while (count>0) and not all_down :
		all_down = cc.python_clients_status("down")
		cc.spin()
		cc.update()
		cc.sleep()
		count = count -1

	if not all_down :
		print("PROBLEM: the nodes aren't down after a kill.")
		sys.exit()
	else :
		print("GREAT ! All nodes were created and killed as expected (count=" + str(count) + ")")

	''' The roslaunch may need to be stopped with a ctrl-c as the getid node will go on working after this node exits. '''

	print('Done.')

The parameters defined in the launch file are read, and the dummy client is created inheritating all the functionalities of the Component class. The launch file basic_example_new_node.launch is executed twice to run the other 2 CAFER nodes of the example, which keep listening to the management topic for any further action to be done.

Then, this node checks that the 2 nodes have been created using the wrapper function python_get_created_nodes_number. If the dummy client is not aware of the creation of some node a mechanism shows the ID of the nodes the dummy client is aware of, and asks for a new acknowledgment signal from all the created nodes, checking again if all the nodes are up.

If all of them are up, a kill signal is sent to the created nodes using the dummy client's function kill_created_nodes, checking all of them are down.