Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
doc:logging:tutorials-cpp [2014/09/05 06:46] – [Tutorial for the C++ Beliefstate Client Library] winklerdoc:logging:tutorials-cpp [2014/09/19 15:19] (current) – [Non-Default Contexts] winkler
Line 2: Line 2:
  
 Please keep in mind that you need the Beliefstate system running in order to let your program interact with it. Please keep in mind that you need the Beliefstate system running in order to let your program interact with it.
 +
 +
 ==== What it can do ==== ==== What it can do ====
  
Line 34: Line 36:
   wstool set beliefstate_client --git https://github.com/fairlight1337/beliefstate_client   wstool set beliefstate_client --git https://github.com/fairlight1337/beliefstate_client
  
-After doing a wstool update to make sure all library checkouts are up+After doing a ''wstool update'' to make sure all library checkouts are up
 to date, make sure that everything compiles just fine: to date, make sure that everything compiles just fine:
  
Line 52: Line 54:
  
 The package's node will be linked against the ROS C++ framework The package's node will be linked against the ROS C++ framework
-(roscpp) and the Beliefstate Client library (beliefstate_client).+(''roscpp'') and the Beliefstate Client library (''beliefstate_client'').
  
-This package's main code consists of a simple skeleton .cpp file+This package's main code consists of a simple skeleton ''.cpp'' file
 including: including:
  
Line 60: Line 62:
 #include <cstdlib> #include <cstdlib>
 #include <ros/ros.h> #include <ros/ros.h>
-#include <beliefstate_client/beliefstate_client.h>+#include <beliefstate_client/BeliefstateClient.h> 
 +#include <beliefstate_client/Context.h> 
 + 
 +using namespace beliefstate_client; 
  
 int main(int argc, char** argv) { int main(int argc, char** argv) {
Line 96: Line 102:
          
     // Open a top-level context (level 0)     // Open a top-level context (level 0)
-    int nContextID_1 bscl->startContext("top-level-context");+    Context* ctxMain new Context(bscl, "MainTimeline");
          
     // Open a sub-context (level 1)     // Open a sub-context (level 1)
-    int nContextID_2 bscl->startContext("sub-context-1"); +    Context* ctxCupOnTable ctxMain->startContext("ObjectsInContact"); 
-    // End the sub-context from level 1 +
-    bscl->endContext(nContextID_2); +
-    +
     // Open another sub-context (level 1)     // Open another sub-context (level 1)
-    nContextID_2 bscl->startContext("another-sub-context"); +    Context* ctxCupInRoom ctxMain->startContext("ObjectsInRoom"); 
-     + 
-    // Open yet another sub-context (level 2) +    // Close the first sub-context 
-    int nContextID_3 = bscl->startContext("a-sub-sub-context"); +    ctxCupOnTable->end();
-    // End the sub-context from level 2 +
-    bscl->endContext(nContextID_3);+
          
-    // End the sub-context from level 1 +    // Close the second sub-context 
-    bscl->endContext(nContextID_2);+    ctxCupInRoom->end();
          
     // End the top-level context     // End the top-level context
-    bscl->endContext(nContextID_1);+    ctxMain->end();
          
     // Actual logging ends here.     // Actual logging ends here.
Line 121: Line 123:
  
 The logging result of this program is a nested tree with two branches 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.+from the top-level node. The logged circumstances here could be that a cup that formerly sat on a table in a room was first removed from the table (ending the ''ObjectsInContact'' context) and then transporting it out of the room (ending the ''ObjectsInRoom'' context). 
 + 
 +Contexts can be hierarchically nestedor can be concatenated concurrently. A good practice is to always have one main context from which all other contexts derive. Don't create multiple contexts using ''new Context(bscl...);'', as it will definitely mess up your results. Always use ''ctxMain->startContext(...);'' to create subsequent events and contexts.
  
 To export the logged tree now, we add another call from inside the To export the logged tree now, we add another call from inside the
Line 136: Line 140:
  
 The Beliefstate system (if properly configured with exporter plugins) The Beliefstate system (if properly configured with exporter plugins)
-now exports a .owl-file, including well-structured OWL code describing +now exports a ''.owl''-file, including well-structured OWL data describing 
-the tree semantics, and a .dot-file, in graphviz format for PDF+the tree semantics, and a ''.dot''-file, in graphviz format for PDF
 generation. generation.
  
 +A resulting PDF could then look like this: 
 +{{ :doc:logging:concurrentlogging.png?800 |}}
 ==== Non-Default Contexts ==== ==== Non-Default Contexts ====
  
-To override default-parameters, the function calls startContext and endContext allow optional parameters for changing+To override default-parameters, the function calls ''startContext'' and ''end'' allow optional parameters for changing
  
-  * The start and end timestamps for the context timepoints +  * the start and end timestamps for the context timepoints, and 
-  * The result success flag for ending contexts+  * the result success flag for ending contexts
  
-The signature of the startContext call looks like this:+The signature of the ''startContext'' call looks like this:
  
 <code pseudo> <code pseudo>
-int startContext(context-name, optional start-timestamp)+Context* Context::startContext(context-name, optional start-timestamp)
 </code> </code>
 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. 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:+The same accounts for the ''end'' call:
 <code pseudo> <code pseudo>
-int endContext(context-name, optional success-flag, optional end-timestamp)+void Context::end(optional success-flag, optional end-timestamp)
 </code> </code>
-The success flag is by default true. For the end timestamp, the same rules apply as for the start timestamp.+The success flag is by default set to ''true''. For the ''endTime'' timestamp, the same rules apply as for the ''startTime'' timestamp.
  
  
 === Changing the OWL Class and Individual Name in the Resulting OWL File === === Changing the OWL Class and Individual Name in the Resulting OWL File ===
  
-To change the "rdf:typevalue and the related node individual name into a class that fits your purpose more, you can use the extended signature of startContext:+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:
  
 <code pseudo> <code pseudo>
-int startContext(context-name, optional class-namespace, optional class-name, optional start-timestamp)+Context* Context::startContext(context-name, optional class-namespace, optional class-name, optional start-timestamp)
 </code> </code>
  
Line 182: Line 187:
 In you C++-program, you would just call: In you C++-program, you would just call:
 <code cpp> <code cpp>
-int nCtx bscl->startContext("Task-Ctx-1", "&knowrob;", "CustomClass", 0); +Context* ctxContext ctxMain->startContext("Task-Ctx-1", "&knowrob;", "CustomClass", 0); 
-bscl->endContext(nCtx, 10)+ctxContext->end(true, 10)
 </code> </code>
  
-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.+Of course, for real-time purposes (or if you don't care about the timestamps), you can leave out the numerical last parameters of each call.
  
  
 === Issueing Discrete Events === === Issueing Discrete Events ===
  
-When your application requires the issuance of discrete, momentarily events you can use the convenience function discreteEvent:+When your application requires the issuance of discrete, momentarily events you can use the convenience function ''discreteEvent'':
 <code pseudo> <code pseudo>
-int discreteEvent(event-name, optional class-namespace, optional class-name, optional success-flag, optional timestamp)+void Context::discreteEvent(event-name, optional class-namespace, optional class-name, optional success-flag, optional timestamp)
 </code> </code>
  
 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. 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.
 +
 +
 +=== Annotating Custom Parameters ===
 +
 +To add custom information to individual task contexts, parameters can be manually annotated to each context. Let's assume, the information
 +<code>
 +X = 5
 +Room = Kitchen
 +</code>
 +should be added to the current context. To achieve this, in your program, call these lines while you are in the appropriate context:
 +<code cpp>
 +ctxContext->annotateParameter("X", 5);
 +ctxContext->annotateParameter("Room", "Kitchen");
 +</code>
 +The resulting ''.owl''-file will now include information about the manually annotated parameters:
 +<code xml>
 +<owl:namedIndividual rdf:about="&log;SomeClass_1zpsieIi">
 +    <rdf:type rdf:resource="&knowrob;SomeClass"/>
 +    <knowrob:taskContext rdf:datatype="&xsd;string">some-task-context</knowrob:taskContext>
 +    <knowrob:taskSuccess rdf:datatype="&xsd;boolean">true</knowrob:taskSuccess>
 +    <knowrob:startTime rdf:resource="&log;timepoint_5120"/>
 +    <knowrob:endTime rdf:resource="&log;timepoint_5200"/>
 +    <knowrob:X>5</knowrob:X>
 +    <knowrob:annotatedParameterType rdf:datatype="&xsd;string">X</knowrob:annotatedParameterType>
 +    <knowrob:parameterAnnotation rdf:resource="&log;object_4RfZlRiev7LPJW"/>
 +    <knowrob:Room>Kitchen</knowrob:Room>
 +    <knowrob:annotatedParameterType rdf:datatype="&xsd;string">Room</knowrob:annotatedParameterType>
 +    <knowrob:parameterAnnotation rdf:resource="&log;object_4RfZlRiev7LPJW"/>
 +</owl:namedIndividual>
 +</code>
 +The lines
 +<code xml>
 +<knowrob:X>5</knowrob:X>
 +<knowrob:Room>Kitchen</knowrob:Room>
 +</code>
 +now explicitly denote the given parameter values, while
 +<code xml>
 +<knowrob:annotatedParameterType rdf:datatype="&xsd;string">X</knowrob:annotatedParameterType>
 +<knowrob:annotatedParameterType rdf:datatype="&xsd;string">Room</knowrob:annotatedParameterType>
 +</code>
 +Describe the parameter names that were manually annotated. So looking for all properties of type ''knowrob:annotatedParameterType'' gives a list of all (for this task) relevant parameter annotations. Finally,
 +<code xml>
 +<knowrob:parameterAnnotation rdf:resource="&log;object_4RfZlRiev7LPJW"/>
 +</code>
 +is a reference to the designator published via the ROS topic ''/logged_designators'' in case this mechanism is used.
 +
 +
 +=== Adding Object References ===
 +In some situations, it is useful to manually annotate objects that are part of the current scene, and maybe play a significant role for the current task. Object references to the currently active task can be annotated in a convenient way, producing output like this:
 +<code xml>
 +<owl:namedIndividual rdf:about="&log;ObjectsInContact_unmkCtDP">
 +    <rdf:type rdf:resource="&sim;ObjectsInContact"/>
 +    <knowrob:taskContext rdf:datatype="&xsd;string">ObjectsInContact</knowrob:taskContext>
 +    <knowrob:taskSuccess rdf:datatype="&xsd;boolean">true</knowrob:taskSuccess>
 +    <knowrob:startTime rdf:resource="&log;timepoint_1410254697"/>
 +    <knowrob:endTime rdf:resource="&log;timepoint_1410254697"/>
 +    <knowrob:objectInContact rdf:resource="&sim;Cup_object_s8hJIQ3L"/>
 +    <knowrob:objectInContact rdf:resource="&sim;Table_object_jfFU038A"/>
 +</owl:namedIndividual>
 +</code>
 +This is achieved by using the following, simple code:
 +<code cpp>
 +Context* ctxInCtct = ctxMain->startContext("ObjectsInContact", "&sim;", "ObjectsInContact");
 +
 +Object* objCup = new Object("&sim;", "Cup");
 +ctxInCtct->addObject(objCup, "knowrob:objectInContact");
 +
 +Object* objTable = new Object("&sim;", "Table");
 +ctxInCtct->addObject(objTable, "knowrob:objectInContact");
 +
 +delete objCup;
 +delete objTable;
 +
 +ctxInCtct->end();
 +</code>
 +Additionally, two object individuals will be created, reflecting the object's respective type:
 +<code xml>
 +<owl:namedIndividual rdf:about="&sim;Cup_object_s8hJIQ3L">
 +    <knowrob:designator rdf:resource="&log;object_syiJzh35HBm4Tf"/>
 +    <rdf:type rdf:resource="&sim;Cup"/>
 +</owl:namedIndividual>
 +
 +<owl:namedIndividual rdf:about="&sim;Table_object_jfFU038A">
 +    <knowrob:designator rdf:resource="&log;object_6ti909YZVsLifo"/>
 +    <rdf:type rdf:resource="&sim;Table"/>
 +</owl:namedIndividual>
 +</code>
 +The ''Object'' class offers two constructor parameters:
 +<code pseudo>
 +new Object(string object-class-namespace, string object-class)
 +</code>
 +If they are left out, the object will be of type ''&knowrob;HumanScaleObject''. When adding the object to the current context, the call
 +<code pseudo>
 +void Context::addObject(object, optional string property)
 +</code>
 +produces the actual addition of the object reference to the current context (and creation of the object individual). The optional ''property'' parameter specifies the property tag in the OWL context individual. If left out, it defaults to ''knowrob:objectActedOn''. You have to specify it along its namespace.
 +
 +
 +=== Registering custom OWL namespaces ===
 +
 +When using custom namespaces in the entity class definitions, these namespaces need to be registered properly in the OWL file when exporting it. In order to do that, add this line to your program before exporting your logged data:
 +<code cpp>
 +bscl->registerOWLNamespace("sim", "http://some-namespace.org/#");
 +</code>
 +with both parameters representing your use-case, of course.
 +
 +Custom namespaces can therefore be registered by calling
 +''void BeliefstateClient::registerOWLNamespace(std::string strShortcut, std::string strIRI);''
 +where the shortcut is the short version of the namespace, i.e. ''sim'', and the IRI is the actual URL you want to have associated.
 +==== Sample Program ====
 +
 +A complete sample program depicting the ''beliefstate_client'' usage can be found [[doc:logging:tutorials-cpp:sample-program|here]].