This is an old revision of the document!


Tutorial for the C++ Beliefstate Client Library

What it can do

The Beliefstate Client library allows easy, lightweight access to the Beliefstate infracstructure for semantic event logging. Its main tasks consist of opening a new semantic context in which actions take place, and ending that context again, annotating it with success or failure. Such contexts can be nested at will.

Besides recording such events from a live running system (such as a robotic agent performing tasks in the real world, or a simulated machine working out given tasks), the recorded events can be exported as well-structured OWL files, allowing symbolic reasoning about the available information.

What is needed

In order to equip a program with logging functionality, it needs to be linked against the client library. The library is currently only available in C++, but the functionality is available to all programming languages that have access to the ROS messaging (and service) bus.

To get the necessary libraries, you must check out the following repositories into your catkin ROS workspace:

wstool set designator_integration --git https://github.com/code-iai/designator_integration
wstool set iai_common_msgs --git https://github.com/code-iai/iai_common_msgs
wstool set beliefstate_client --git https://github.com/fairlight1337/beliefstate_client

After doing a wstool update to make sure all library checkouts are up to date, make sure that everything compiles just fine:

catkin_make --pkg beliefstate_client

If no problems come up, you are set to connect your own applications to the logging infrastructure.

Using the client library

To clarify how to implement a logging infrastructure interface into your own ROS application, a basic ROS node template will be used. We first create this node's package by running:

catkin_create_pkg logging_node_client roscpp beliefstate_client

The package's node will be linked against the ROS C++ framework (roscpp) and the Beliefstate Client library (beliefstate_client).

This package's main code consists of a simple skeleton .cpp file including:

#include <cstdlib>
#include <ros/ros.h>
#include <beliefstate_client/beliefstate_client.h>
 
int main(int argc, char** argv) {
    ros::init(argc, argv, "logging_client");
 
    // Begin main code here.
    // ...
    // Main code ends here.
 
    return EXIT_SUCCESS;
}

We now add an instance of the Beliefstate Client class that takes advantage of the already present ROS connection:

    // Begin main code here.
 
    BeliefstateClient* bscl = new BeliefstateClient("logging_client");
 
    // Begin actual logging here.
    // ...
    // Actual logging ends here.
 
    delete bscl;
 
    // Main code ends here.

To log semantic events, we create some sample nested contexts:

    // Begin actual logging here.
 
    // Open a top-level context (level 0)
    int nContextID_1 = bscl->startContext("top-level-context");
 
    // Open a sub-context (level 1)
    int nContextID_2 = bscl->startContext("sub-context-1");
    // End the sub-context from level 1
    bscl->endContext(nContextID_2);
 
    // Open another sub-context (level 1)
    nContextID_2 = bscl->startContext("another-sub-context");
 
    // Open yet another sub-context (level 2)
    int nContextID_3 = bscl->startContext("a-sub-sub-context");
    // End the sub-context from level 2
    bscl->endContext(nContextID_3);
 
    // End the sub-context from level 1
    bscl->endContext(nContextID_2);
 
    // End the top-level context
    bscl->endContext(nContextID_1);
 
    // Actual logging ends here.

The logging result of this program is a nested tree with two branches from the top-level node, one with depth 1, and one with depth 2.

To export the logged tree now, we add another call from inside the library:

    // ...
 
    // Export files
    bscl->exportFiles("logged_data");    
 
    // Actual logging ends here.

The Beliefstate system (if properly configured with exporter plugins) now exports a .owl-file, including well-structured OWL code describing the tree semantics, and a .dot-file, in graphviz format for PDF generation.

Non-Default Contexts

To override default-parameters, the function calls startContext and endContext allow optional parameters for changing

  • The start and end timestamps for the context timepoints
  • The result success flag for ending contexts

The signature of the startContext call looks like this:

int startContext(context-name, optional start-timestamp)

So the time point noted in the resulting log-tree can be annotated with a custom timepoint (for non-realtime logging uses). If this parameter is omitted, the current Unix time on the system running the Beliefstate logging system is used.

The same accounts for the endContext call:

int endContext(context-name, optional success-flag, optional end-timestamp)

The success flag is by default true. For the end timestamp, the same rules apply as for the start timestamp.

Changing the OWL Class and Individual Name in the Resulting OWL File

To change the “rdf:type” value and the related node individual name into a class that fits your purpose more, you can use the extended signature of startContext:

int startContext(context-name, optional class-namespace, optional class-name, optional start-timestamp)

So in order to produce OWL output like this:

<owl:namedIndividual rdf:about="&log;CustomClass_RhBAibh4">
    <rdf:type rdf:resource="&knowrob;CustomClass"/>
    <knowrob:taskContext rdf:datatype="&xsd;string">Task-Ctx-1</knowrob:taskContext>
    <knowrob:startTime rdf:resource="&log;timepoint_0"/>
    <knowrob:endTime rdf:resource="&log;timepoint_10"/>
</owl:namedIndividual>

In you C++-program, you would just call:

int nCtx = bscl->startContext("Task-Ctx-1", "&knowrob;", "CustomClass", 0);
bscl->endContext(nCtx, 10)

Of course, for real-time purposes (or if you don't care about the timestamps), you can leave the numerical last parameters of each call out.

Issueing Discrete Events

When your application requires the issuance of discrete, momentarily events you can use the convenience function discreteEvent:

int discreteEvent(event-name, optional class-namespace, optional class-name, optional success-flag, optional timestamp)

This implicitly starts a context with the given information, and directly closes it again. If you decide to specify a timestamp, the start and end time of the event will be the same as this value. Other than that, this call produces the same output as manually starting and ending a context.