Use inheritance for my first program (cpp)

Who is the intruder ?

Setting up the program

Welcome to this first Ontologenius tutorial. In this tutorial, we will take over the Ontologenius API using an OntologyManipulator. This is the most important thing you need to know to use Ontologenius in a simple way. We will also see how to launch Ontologenius by configuring it according to our needs.

To illustrate the different objectives of this tutorial, we will develop a mini-games that will consist of finding the intruder among a list of words. However, finding only the intruder is a much too simple problem. That's why we will also try to find the reason why one word is the intruder among the others.

In this part, we will code the bases of our program from which we will be able to write the more algorithmic part. Before that, I invite you to create a new package that we will call "ontologenius_tutorial" and which will contain only one source file that we will name "intruder.cpp". To be sure that your new package will be well configured to use Ontologenius, we advise you to follow this guide. We will just rename our executable "intruder" because it is much more logical than "my_exe".

add_executable(intruder src/intruder.cpp)
target_link_libraries(intruder ${ontologenius_LIBRARIES})

Now we are ready! Let's open our main file and start coding:

#include <iostream>
#include <vector>
#include <string>

#include "ros/ros.h"
#include "std_msgs/String.h"
#include "ontologenius/OntologyManipulator.h"

onto::OntologyManipulator* onto_;
std::vector<std::string> names_;
bool start_;

void say(const std::string& text)
{
std::cout << "[SAY]" << text << std::endl;
}

std::string summarize(std::vector<std::string> words)
{
std::string to_say = "on ";
for(size_t i = 0; i < words.size(); i++)
to_say += words[i] + " ";
return to_say;
}

void wordCallback(const std_msgs::String& msg)
{
if(msg.data == "start")
start_ = true;
else if(std::find(names_.begin(), names_.end(), msg.data) == names_.end())
names_.push_back(msg.data);
}

Take a break here, we have already written a lot. We will go over all this in detail to better understand what we have done.

First of all, we have all the inclusions we need, like iostream to display messages on the screen, then vectors and strings to get vectors and strings ...

#include "ros/ros.h"

ros/ros.h is a convenience include that includes all the headers necessary to use the most common public pieces of the ROS system.

#include "std_msgs/String.h"

This includes the ROS String message, which resides in the std_msgs ROS package. We use it to create a topic on which we will send the words from which our program will have to find the intruder.

#include "ontologenius/OntologyManipulator.h"

This includes what we call an ontology manipulator. In fact, this class is just an object to access all API ROS abstraction classes so that you can query and manage ontologenius. You can find the documentation for this object on the left panel.

onto::OntologyManipulator* onto_;

As explained above, the ontology manipulator contains several objects to abstract ROS services. The set of services work in a connected mode which allows to greatly save in execution time but which makes the program less robust. For the lack of robustness, each ontologenius client takes care of everything for you by implementing recovery mechanisms. As for the connected mode, this means that the connection is established at the creation of the object and is broken only at its destruction. So we understand that it is better to avoid creating several ontology manipulators but it is better to create a single instance for our entire program. This pointer is used to collect the address of this single instance to make it available to different functions. However, we do not directly create a global instance of a manipulator because it needs a pointer to a NodeHandle of ROS.

std::vector<std::string> names_;

This vector will allow us to store the different words on which we will perform our search for the intruder.

bool start_;

This is our synchronization variable. This variable will be false as long as we continue to collect words and we will put it in the true state as long as we wish to start the search for the intruder.

void say(const std::string& text)

Here, the say function is actually just an on-screen display, but you can customize it to use a TTS for example.

std::string summarize(std::vector<std::string> words)

Here, the summarize function concatenates several words in a single string. We created this function just to make our main function more readable.

void wordCallback(const std_msgs::String& msg)

Here we have the callback function of our input topic. This topic thus receives strings which will be the words from which our program will have to find the intruder. Inside the function, we can see that we use the word "start" as a synchronization word that will launch our reasoning. The different words of "start" are then stored in the vector names_ pending processing. To add security, line 30 verifies that the word is not already present in the names_ vector to avoid duplicates.



We have everything we need to start writing the main function to retrieve the list of words to work on and start the reasoning although we do not have it yet.

int main(int argc, char** argv)
{
ros::init(argc, argv, "intruder");

onto::OntologyManipulator onto;
onto_ = &onto;

onto.close();

ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("intruder/input", 10, wordCallback);
ros::Rate r(100);

while(ros::ok())
{
start_ = false;

say("Let's play!");

while((start_ == false) && ros::ok())
{
ros::spinOnce();
r.sleep();
}

say(summarize(names_));

/*
We will find the intruder here
*/

names_.clear();
}

return 0;
}

Now, let's break the code down.

ros::init(argc, argv, "intruder");

Initialize ROS.

onto::OntologyManipulator onto;
onto_ = &onto;

We finally come back to using ontologenius! We create here our first manipulator ontology, what a great day ... This manipulator is unique to fully enjoy the benefits of connected services.

We do not forget to initialize the pointer on the manipulator by providing the address.

onto.close();

The close function link all the concepts loaded from files and the Internet. Before closing an ontology, exploration requests are not allowed.

onto.actions.fadd("path/to/may/file.owl");
// OR
onto.actions.add("http://path/to/my/file.owl");
onto.close();

For the moment, we are going to consider that the ontological file we are going to work on is provided in the ontologenius launcher. We will configure it in the next part of the tutorial. It is also possible to load files directly from the program (as above) but in doing so it makes our code less reusable.

ros::Subscriber sub = n.subscribe("intruder/input", 10, wordCallback);

We initialise our subscriber to the topic "intruder/input". We also link this topic to our callback function.

while(ros::ok())

Just our almost infinite loop. Escaping with a Ctrl + C.

start_ = false;

say("Let's play!");

We initialize the synchronization variable and inform the user that the program is ready to listen to the words.

while((start_ == false) && ros::ok())

ros::spinOnce() is there to allow us to receive messages and r.sleep() avoids unnecessary use of the processor.

We only wait for the synchronisation...

say(summarize(names_));

We recall the words on which we will make the reasoning. It is just as a result of this that we will implement our reasoning.

names_.clear();

Our loop is over, we can erase the words we used and start over!

Now, you just have to compile that before going on to the following:

catkin_make

Do not forget that this command must be done in your workspace