Fully Define Your DDS Types; Do Not Rely on Opaque Bytes or Other Custom Encapsulations

Mapping all of your data model to opaque bytes or strings (even XML strings) is a bad practice for several reasons, some obvious and some subtle:

If you use opaque bytes or strings, you must write, test, and maintain more code – including error-prone byte swapping across CPUs of different endianness.  This forces you to maintain a document that explains your marshalling design. Moreover, the code to marshal and unmarshal must be maintained or recreated by each team and in each programming language used. These steps increase cost and the likelihood of error.

Perhaps less obvious is that with opaque types, you lose opportunities to offload work from your application to RTI Connext DDS.  For example, the middleware will not be able to perform content filtering for you. This means that your application must do any filtering required, which typically involves discarding data on the receiver side. When RTI Connext DDS is aware of your data type and filtering requirements, it can filter on the DataWriter. This avoids sending the data to DataReaders that are not interested, saving both CPU and network bandwidth.

In addition, by defining your data type within the middleware, you can represent multiple real-world objects – called instances – within the same Topic by using key fields.  This facilitates greater system scale, and gives additional benefits described in this best practice.

Many RTI tools and services make use of your data type for tasks such as routing and mediation, integration with relational databases, visualization, and integration with Microsoft Excel. These features are made much more powerful through content awareness; using opaque types throws this away.

The concrete steps you must take to define your data are: 

  1. Describe the structure of your data types in IDL, XML, or XSD.
  2. Use the RTI code generator on your file.  

The RTI code generator (rtiddsgen) generates source code in C, C++, Java, C++/CLI, C#, or Ada to define your data types, and to marshal/unmarshal your types into and out of network messages.  

Here is an example type defined equivalently in IDL, XML, and XSD:

 IDL:

struct TruckPosition
{
    string VIN; //@key
    double latitude;
    double longitude;
    long speed;
    long direction;
};

 

XML:

<?xml version="1.0" encoding="UTF-8"?>
<types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xsi:noNamespaceSchemaLocation="[Connext DDS installation]/ndds.5.0.0/resource/rtiddsgen/schema/rti_dds_topic_types.xsd">
  <struct name="TruckPosition">
    <member name="VIN" type="string" key="true"/>
    <member name="latitude" type="double"/>
    <member name="longitude" type="double"/>
    <member name="speed" type="long"/>
    <member name="direction" type="long"/>
  </struct>
</types>
 
 
 XSD:
 <?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:dds="http://www.omg.org/dds" 
 xmlns:tns="http://www.omg.org/IDL-Mapped/" targetNamespace="http://www.omg.org/IDL-Mapped/">
  <xsd:import namespace="http://www.omg.org/dds" schemaLocation="rti_dds_topic_types_common.xsd"/>
  <xsd:complexType name="TruckPosition">
      <xsd:sequence>
          <xsd:element name="VIN" minOccurs="1" maxOccurs="1" type="xsd:string"/>
              <!-- @key true --> 
          <xsd:element name="latitude" minOccurs="1" maxOccurs="1" type="xsd:double"/>
          <xsd:element name="longitude" minOccurs="1" maxOccurs="1" type="xsd:double"/>
          <xsd:element name="speed" minOccurs="1" maxOccurs="1" type="xsd:int"/>
          <xsd:element name="direction" minOccurs="1" maxOccurs="1" type="xsd:int"/>
      </xsd:sequence>
  </xsd:complexType>
  <!-- @struct true -->
</xsd:schema>