Code Generator: Experiment with New Features

codegen

In previous posts we explained how RTI’s new code generator saves you time with faster code generation. It’s now the default code generator in RTI Connext DDS 5.2.0, and it includes a number of other new features we think you will like.

Did you know that our new code generator is much better at detecting syntax errors?

For instance, the code generator will tell you if you forgot to define a type, showing better error messages. And this is just one example.

Do you want to generate only the type files and your project files without overwriting your publisher/subscriber files?

Try the autoGenFiles option.

Do you want even more control over which files you generate?

The new create and update <typefiles | examplefiles | makefiles> options are your solution.

Are you compiling dynamically?

Generate your project files using the shareLib option. It will link your application with RTI’s core dynamic libraries.

Do you want to share private IDLs without showing their actual content?

Try the new obfuscate option.

Are you using unbounded sequences?

Try the unboundedSupport option. Read this blog post to learn more about this new feature.

It’s all easier than ever with the Apache Velocity (VLT) templates. You can check the predefined set of variables available in RTI_rtiddsgen_template_variables.xlsx, located in the Code Generator documentation folder.

As a simple example, imagine you want to generate a new method that prints whether each member of a type is an array or a sequence. It could also print the corresponding dimensions or size. For example, consider this type:

module Mymodule{ 
    struct MyStruct{ 
       long longMember; 
       long arrayMember [2][100];
       sequence sequenceMember;
       sequence arrayOfSequenceMember[28];
    };
};

Our desired print function would be something like this:

void MyModule_MyStruct_specialPrint(){ 
    printf(" longMember \n");
    printf(" arrayMember is an array [2, 100] \n "); 
    printf(" sequenceMember is a sequence <2> \n"); 
    printf(" arrayOfSequenceMember is an array [28] is a sequence <5> "); 
}

Implementing this is straightforward. You would just need to create a few macros in the code generator templates to generate the above code. We start with the main method, which would be something like this:

void ${node.nativeFQName}_specialPrint(){

#specialPrint ($node)

}

This method calls to specialPrint macro. That macro iterates within the struct members and print whether they are an array or a sequence

#macro (specialPrint $node)

#*--*##foreach($member in $node.memberFieldMapList)

print("$member.name #isAnArray($member) #isASeq($member) \n");

#*--*##end

We just need to define  two auxiliary macros to check each case.

#macro (isAnArray $member)

#if($member.dimensionList) is an array $member.dimensionList #end

#end

#macro (isASeq $member)

#if($member.seqSize) is a sequence &lt;$member.seqSize&gt; #end

#end

If you need more information about supported and new features available in Code Generator, check out the Getting Started Guide or the online documentation.

Unbounded Support For Sequences and Strings 4

When we first designed Connext DDS, deterministic memory allocation was on the top of our priority list. At that time most of our customers used small data types such as sensor and track data. We decided to go with an approach in which we pre-allocated the samples in the DataWriter and DataReader queues to their maximum size. For example:

struct Position {
    double latitude;
    double longitude;
}

struct VehicleTrack {
    string<64> vehicleID; //@key
    Position position;
}

In the previous example, a sample of type VehicleTrack was pre-allocated to its maximum size. Even if vehicleID did not have a length of 64 bytes, Connext DDS pre-allocated a string of 64 bytes to store the sample value.

As our customer base increased, the set of use cases expanded, and with that came the need to be more flexible in our memory allocation policies. For example, customers may use Connext DDS to publish and subscribe video and audio. Typically these data types are characterized for having unbounded members. For example:

struct VideoFrame {
    boolean keyFrame;
    /* The customer does not necessarily know the maximum 
       size of the data sequence */
    sequence<octet> data;
};

Pre-allocating memory for samples like VideoFrame above may become prohibitive and really expensive as the customer will be forced to use an artificial and arbitrarily large bound for the variable size sequence. For example:

struct VideoFrame {
    boolean keyFrame;
    /* Alternatively the customer can use the code generator 
       command-line option -sequenceSize to set an implicit 
       limit for the unbounded sequence */
    sequence<octet,1024000> data; 
};

In Connext DDS 5.2.0, we have introduced support for unbounded sequences and strings.

To support unbounded sequences and strings, the Code Generator has a new command-line option: -unboundedSupport. This new option may only be used when generating code for .NET, C, and C++ (that is, the -language option must be specified for C++/CLI, C#, C, or C++).

With this option, Connext DDS will not pre-allocate the unbounded members to their maximum size. For unbounded members, the generated code will de-serialize the samples by dynamically allocating and de-allocating memory to accommodate the actual size of the unbounded member. Unbounded sequences and strings are also supported with DynamicData and Built-in Types and they integrate with the following tools and services:

  • Routing Service
  • Queuing Service
  • Recording Service on serialized and automatic mode (records in serialized format)
  • Replay Service when recorded in serialized mode
  • Spy
  • Admin Console
  • Toolkit for LabVIEW

The integration with Persistence Service, Database Integration Service and Spreadsheet Add-in for Microsoft Excel is not available at this point.

For additional information, check Chapter 3 Data Types and DDS Data Samples and Chapter 20 DDS Sample-Data and Instance-Data Memory Management in the RTI Connext DDS Core Libraries User’s Manual as well as the User’s Manuals for services and tools.

Visualize your data! Reply

Ok, I have to admit right from the start that I’m very excited about this feature. I’ve wanted a high-performance, platform-independent visualization for DDS data for more than a decade. When I was an RTI customer (we started with the 3.x version), I built a small UI to display data. It used generated code and was quite basic but still useful. Just the other day I heard from a person working on that project that they still use it! I can’t wait to show them what we now ship with Admin Console!

What can you do with it?

First things first, subscribe to your Topic of interest.

image00

If you don’t have a data type, no problem. You can specify it through one (or more) XML files. Use rtiddsgen to generate them from your IDL or XML type file with the -convertToXml option. You can also specify more advanced settings including overriding QoS, setting a content filter, or picking from which DataWriters to receive data.

image03

Here you can set the DataWriter filter.

image02

Once you’ve picked your QoS and subscribed to the Topic, you’ll see the instances start filling in from the DataWriter(s). Instances? If you’re not familiar with this DDS concept, an instance is a unique piece of data within a Topic. In the Triangle topic (below), there are 3 instances, “ORANGE,” “MAGENTA,” AND “CYAN.” Each instance is shown in its own row. This display (which we call the Topic Data Tab) chooses a few fields to show by default but you can customize this to show the fields that you prefer.

image06

But, I want to look at all of the fields. Of course, who wouldn’t?! To see all of the data, select an instance (a row in the Topic Data Tab) and then look at the Sample Inspector. This view shows all of the fields in a tree view. You can view all nested structures and you can see all of the metadata (SampleInfo) as well!

image07

That’s very helpful but I want to see more than just the current value. There are two more views and they can show historical data. Let’s talk first about the Sample Log. This view shows each sample in its own row (regardless of which instance it is). This view stores sample data (10,000 samples by default) so you can look back at captured data (perhaps after using the pause feature). And this view works with the Sample Inspector so when you select a sample (row), the Sample Inspector displays all of the data fields. And, you can display multiple Topics in this view too!
image05

I mentioned that there were two views which show historical data, the second one is the Time Chart. The Time Chart plots numerical field values as a function of time. You can use this view to compare data fields (either from different instances in the same Topic or instances in any number of other Topics). Or you can use it to look for trends in your data. The Time Chart also stores displayed data (1,000,000 values by default for each trace). You can put the Time Chart in historical mode to view this archived data.
image04

This is also cool but how can I save my data to disk? Each of the views contains an export button image01. This button will export the data contained in the view into a comma-separated value (CSV) file (Topic Data Tab, Sample Log, & Time Chart) or a text file (Sample Inspector). You can also export the chart as an image from the Time Chart (using the button that looks like a camera).

What about performance? Data Visualization exhibits very good performance characteristics. But I’ll cover that in detail in a future blog post.

Is that all? There are many things that I haven’t covered here. If you want to see more right away, you can check out Data Visualization => Getting Started section of the Admin Console Help.