Extending the Real-Time Toolkit
<!--Couldn't selectively extract content, Imported Full Body :(--> <div class="article" lang="en" xml:lang="en"><div class="titlepage"><div><div></div><div><div class="authorgroup"><div class="author"><h3 class="author"><span class="firstname">Peter</span> <span class="surname">Soetens</span></h3><div class="affiliation"><span class="orgname">FMTC/K.U.Leuven<br></br></span><div class="address"><p><span class="country">Belgium</span></p></div></div></div></div></div><div><p class="copyright">Copyright © 2006 Peter Soetens, FMTC</p></div><div><div class="legalnotice"><a id="id2505325"></a><p> Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation, with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of this license can be found at <a href="http://www.fsf.org/copyleft/fdl.html" target="_top">http://www.fsf.org/copyleft/fdl.html</a>. </p></div></div><div><div class="revhistory"><table border="1" width="100%" summary="Revision history"><tr><th align="left" valign="top" colspan="3"><b>Revision History</b></th></tr><tr><td align="left">Revision 1.0.1</td><td align="left">24 Nov 2006</td><td align="left">ps</td></tr><tr><td align="left" colspan="3">Separated from the Developer's Manual.</td></tr></table></div></div><div><div class="abstract"><p class="title"><b>Abstract</b></p><p> This document is an introduction to making user defined types (classes) visible within <span class="acronym">Orocos</span>. You need to read this document when you want to see the value of an object you defined yourself, for example in the TaskBrowser component or in an Orocos script. Other uses are reading and writing objects to and from XML and generally, anything a built-in Orocos type can do, so can yours. </p></div></div></div><hr></hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id2482806">1. The Orocos Type System : Toolkits</a></span></dt><dd><dl><dt><span class="section"><a href="#id2482820">1.1. The Real-Time Toolkit</a></span></dt><dt><span class="section"><a href="#id2482877">1.2. Telling Orocos about your data</a></span></dt><dt><span class="section"><a href="#id2481988">1.3. Building your own Toolkit</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="id2482806"></a>1. The Orocos Type System : Toolkits</h2></div></div></div><p> Most applications define their own classes or structs to exchange data between objects. It is possible to tell Orocos about these user defined types such that they can be displayed, stored to XML, used in the scripting engine or even transfered over a network connection. </p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2482820"></a>1.1. The Real-Time Toolkit</h3></div></div></div><p> Orocos uses the 'Toolkit' principle to make it aware of user types. Orocos' Real-Time Toolkit already provides support for the C++ types <code xmlns:str="http://xsltsl.org/string" class="classname">int</code>,<code xmlns:str="http://xsltsl.org/string" class="classname">unsigned int</code>,<code xmlns:str="http://xsltsl.org/string" class="classname">double</code>, <code xmlns:str="http://xsltsl.org/string" class="classname">char</code>,<code xmlns:str="http://xsltsl.org/string" class="classname">bool</code>,<code xmlns:str="http://xsltsl.org/string" class="classname">float</code>, <code xmlns:str="http://xsltsl.org/string" class="classname">vector<double></code> and <code xmlns:str="http://xsltsl.org/string" class="classname">string</code>. </p><p> A toolkit can be imported into the application by writing: </p><pre class="programlisting"> #include <rtt/RealTimeToolkit.hpp> // ... RTT::Toolkit::Import( RTT::RealTimeToolkit ); </pre><p> This is however done automatically, unless you disabled that option in the configuration system. After this line is executed, Orocos is able to display, transfer over a network or recognise these types in scripts. </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2482877"></a>1.2. Telling Orocos about your data</h3></div></div></div><p> Say that you have an application which transfers data in a struct <code xmlns:str="http://xsltsl.org/string" class="classname">ControlData</code> : </p><pre class="programlisting"> struct ControlData { double x, y, z; int sample_nbr; }</pre><p> How can you tell Orocos how to handle this type ? A helper class is provided which you can extend to fit your needs, <a xmlns="http://www.w3.org/TR/xhtml1/transitional" href="../api/html/classRTT_1_1TemplateTypeInfo.html">TemplateTypeInfo</a>. </p><pre class="programlisting"> #include <rtt/TemplateTypeInfo.hpp> // ... struct ControlDataTypeInfo : public RTT::TemplateTypeInfo<ControlData> { ControlDataTypeInfo() : RTT::TemplateTypeInfo<ControlData>("ControlData") {} };
// Tell Orocos the name and type of this struct:
RTT::TypeInfoRepository::Instance()->addType( new ControlDataTypeInfo() );
</pre><p>
From now on, Orocos knows the 'ControlData' type name and allows
you to create a scripting variable of that type. It does however
not know yet how to display it or write it to an XML file.
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="Note" src="../../../../../stable/documentation/rtt/v1.0.x/doc-xml/images/icons/note.png"></img></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>
The type is now usable as a 'var' in a script, however,
in order to initialise a variable, you need to add a constructor as
well. See <a href="#overloading-constructors" title="1.3.2. Loading Constructors">Section 1.3.2, “Loading Constructors”</a>.
</p></td></tr></table></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h4 class="title"><a id="id2483044"></a>1.2.1. Displaying</h4></div></div></div><p>
In order to tell Orocos how to display your type, you
may overload the <code class="function">TemplateTypeInfo::write</code> fuction
or define <code class="function">operator<<()</code> for your type:
</p><pre class="programlisting"> #include <rtt/TemplateTypeInfo.hpp>
#include <ostream>
std::ostream& operator<<(std::ostream& os, const ControlData& cd) {
return os << '('<< cd.x << cd.y << cd.z << '): ' << cd.sample_nbr;
}
// ...
// 'true' argument: <span class="emphasis"><em> it has operator<<</em></span>
struct ControlDataTypeInfo
: public RTT::TemplateTypeInfo<ControlData,<span class="emphasis"><em>true</em></span>>
{
ControlDataTypeInfo()
: RTT::TemplateTypeInfo<ControlData,<span class="emphasis"><em>true</em></span>>("ControlData")
{}
};
// Tell Orocos the name and type of this struct RTT::TypeInfoRepository::Instance()->addType( new ControlDataTypeInfo() ); </pre><p> If you use the above line of code to add the type, Orocos will be able to display it as well, for example in the TaskBrowser or in the ReportingComponent. </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h4 class="title"><a id="id2483102"></a>1.2.2. Writing to XML</h4></div></div></div><p> In order to inform Orocos of the structure of your data type, it must be given a 'decompose' function: Of which primitive types does the struct consists ? Representing structured data is what Orocos <a xmlns="http://www.w3.org/TR/xhtml1/transitional" href="../api/html/classRTT_1_1Property.html">Property</a> objects do. Here is how to tell Orocos how the "ControlData" is structured: </p><pre class="programlisting"> // ... struct ControlDataTypeInfo : public TemplateTypeInfo<ControlData,true> { // ... other functions omitted
virtual bool decomposeTypeImpl(const ControlData& in, PropertyBag& targetbag ) const {
targetbag.setType("ControlData");
targetbag.add( new Property<double>("X", "X value of my Data", in.x ) );
targetbag.add( new Property<double>("Y", "Y value of my Data", in.y ) );
targetbag.add( new Property<double>("Z", "Z value of my Data", in.z ) );
targetbag.add( new Property<int>("Sample", "The sample number of the Data", in.sample_nbr ) );
return true;
}
}</pre><p>
That was easy ! For each member of your struct, add a
<a xmlns="http://www.w3.org/TR/xhtml1/transitional" href="../api/html/classRTT_1_1Property.html">Property</a> of the correct
type to the targetbag and you're done !
<code class="function">setType()</code> can be used lateron to
determine the version or type of your XML representation. Next,
if Orocos tries to write an XML file with ControlData in it,
it will look like:
</p><pre class="programlisting">
<struct name="MyData" type="ControlData">
<simple name="X" type="double">
<description>X value of my Data</description>
<value>0.12</value>
</simple>
<simple name="Y" type="double">
<description>Y value of my Data</description>
<value>1.23</value>
</simple>
<simple name="Z" type="double">
<description>Z value of my Data</description>
<value>3.21</value>
</simple>
<simple name="Sample" type="short">
<description>The sample number of the Data</description>
<value>3123</value>
</simple>
</struct>
</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h4 class="title"><a id="id2483170"></a>1.2.3. Reading from XML</h4></div></div></div><p>
Orocos does not know yet how to convert an XML format back
to the ControlData object. This operation is called 'composition'
and is fairly simple as well:
Here is how to tell Orocos how the "ControlData" is read:
</p><pre class="programlisting"> // ...
struct ControlDataTypeInfo
: public TemplateTypeInfo<ControlData,true>
{
// ... other functions omitted
virtual bool composeTypeImpl(const PropertyBag& bag, ControlData& out ) const
{
if ( bag.getType() == std::string("ControlData") ) // check the type
{
Property<double>* x = targetbag.getProperty<double>("X");
Property<double>* y = targetbag.getProperty<double>("Y");
Property<double>* z = targetbag.getProperty<double>("Z");
Property<int>* t = targetbag.getProperty<int>("Sample");
if ( !x || !y || !z || !t ) return false;
out.x = x->get();
out.y = y->get();
out.z = z->get();
out.sample_nbr = t->get();
return true;
}
return false; // unknown type !
}
}</pre><p>
First the properties are located in the bag, it should look just
like we stored them. If not, return false, otherwise, read the
values and store them in the out variable.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h4 class="title"><a id="id2483233"></a>1.2.4. Network transfer (CORBA)</h4></div></div></div><p>
In order to transfer your data between components over a
network, Orocos requires that you provide the conversion
from your type to a CORBA::Any type and back, quite similar
to the 'composition' and 'decomposition' of your data.
Look at the <a xmlns="http://www.w3.org/TR/xhtml1/transitional" href="../api/html/classRTT_1_1TemplateTypeInfo.html">TemplateTypeInfo</a>
interface for the functions you need to implement.
</p><p>
The first step is describing your struct in IDL and generate
the 'client' headers with 'Any' support. Next you
create such a struct, fill it with your data type's data
and next 'stream' it to an Any. The other way around is
required as well.
</p><p>
In addition, you will need the CORBA support of Orocos
enabled in your build configuration.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h4 class="title"><a id="id2481974"></a>1.2.5. Advanced types</h4></div></div></div><p>
In order to add more complex types, take a look at the
code of the RealTimeToolkit and the KDL Toolkit Plugin of Orocos.
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2481988"></a>1.3. Building your own Toolkit</h3></div></div></div><p>
The number of types may grow in your application to such
a number or diversity that it may be convenient to
build your own toolkit and import them when appropriate.
Non-Orocos libraries benefit from this system as well because
they can introduce their data types into Orocos.
</p><p>
Each toolkit must inherit from the
<a xmlns="http://www.w3.org/TR/xhtml1/transitional" href="../api/html/classRTT_1_1ToolkitPlugin.html">ToolkitPlugin</a> class and implement
four functions: <code class="function">loadTypes()</code>,
<code class="function">loadConstructors</code>,
<code class="function">loadOperators()</code> and
<code class="function">getName()</code>.
</p><p>
The name of a toolkit must be unique. Each toolkit will be loaded
no more than once. The loadTypes function contains all
'TemplateTypeInfo' constructs to tell Orocos about the types
of your toolkit. The loadOperators function contains all
operations that can be performed on your data such as addition ('+'),
indexing ('i'), comparison ('==') etc. Finally, type constructors
are added in the loadConstructors function. They allow a newly
created script variable to be initialised with a (set of) values.
</p><p>
Mimick the code of the
<a xmlns="http://www.w3.org/TR/xhtml1/transitional" href="../api/html/classRTT_1_1RealTimeToolkitPlugin.html">RealTimeToolkitPlugin</a> and
<a xmlns="http://www.w3.org/TR/xhtml1/transitional" href="../../../kdl/v0.2.x/api/html/classKDL_1_1KDLToolkitPlugin.html">KDLToolkitPlugin</a> to build
your own.
</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h4 class="title"><a id="overloading-operators"></a>1.3.1. Loading Operators</h4></div></div></div><p>
Operator are stored in the class <a xmlns="http://www.w3.org/TR/xhtml1/transitional" href="../api/html/classRTT_1_1OperatorRepository.html">OperatorRepository</a>
in <code class="filename">Operators.hpp</code>. The list of
supported operators is set by the toolkit and added to the OperatorRepository
It looks something like this:
</p><pre class="programlisting">
bool loadOperators() {
OperatorRepository::shared_ptr or = OperatorRepository::Instance(); // boolean stuff: or->add( newUnaryOperator( "!", std::logical_not<bool>() ) ); or->add( newBinaryOperator( "&&", std::logical_and<bool>() ) ); or->add( newBinaryOperator( "||", std::logical_or<bool>() ) ); or->add( newBinaryOperator( "==", std::equal_to<bool>() ) ); or->add( newBinaryOperator( "!=", std::not_equal_to<bool>() ) ); return true;
} </pre><p>
Adding your own should not be terribly hard. The hardest
part is that as the second argument to newUnaryOperator,
newBinaryOperator or newTernaryOperator, you need to specify
a STL Adaptable Functor, and even though the STL provides
many predefined one's, it does not provide all possible
combinations, and you might end up having to write your
own. The STL does not at all provide any "ternary
operators", so if you need one of those, you'll definitely
have to write it yourself.
</p><p>
Note that this section is only about adding overloads for
existing operators, if you want to add new operators to
the scripting engine, the parsers need to be extended as well.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h4 class="title"><a id="overloading-constructors"></a>1.3.2. Loading Constructors</h4></div></div></div><p>
Constructors can only be added <span class="emphasis"><em>after</em></span> a
type has been loaded using <code class="function">addType</code>.
Say that the ControlData struct has a constructor:
</p><pre class="programlisting"> struct ControlData {
ControlData(double a, double b, double c)
: x(a), y(b), z(c), sample_nbr(0)
{}
double x, y, z;
int sample_nbr;
}</pre><p>
This constructor is not automatically known to the type system.
You need to write a constructor function and add that to the
type info:
</p><pre class="programlisting">
ControlData createCD(double a, double b, double c) {
return ControlData(a,b,c);
}
// Tell Orocos a constructor is available:
// Attention: "ControlData" must have been added before with 'addType' !
RTT::TypeInfoRepository::Instance()->type("ControlData")->addConstructor( &createCD );
</pre><p>
From now on, one can write in a script:
</p><pre class="programlisting"> var ControlData cd = ControlData(3.4, 5.0, 1.7);</pre><p>
Multiple constructors can be added for the same type. The first
one that matches with the given arguments is then taken.
</p></div></div></div></div>
