RTI Connext DDS Micro  Version 2.4.6
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
Getting Started

Install RTI Connext DDS Micro

RTI Connext DDS Micro is provided in separate host and target packages (.zip or .tar.gz).

The host bundle contains this product documentation, the rtiddsgen code generation tool, and example source code.

Each target bundle contains libraries of RTI Connext DDS Micro for a specific platform.

Download the host bundle and target bundle(s), and follow the directions below to install them.

Installing on a Windows System

The distribution is packaged in one or more .zip files. Unzip them into a directory of your choice.

Installing on a UNIX-Based System

The distribution is packaged in one or more files. Unpack them as described below. You do not need to be logged in as root during installation.

  • Make sure you have GNU's version of unzip and tar (which handle long file names). On Linux systems, this is the default tar executable.
  • Create a directory for RTI Connext DDS Micro.
  • Move the downloaded file(s) into your newly created directory.
  • Extract the distribution from the uncompressed files. For example:
    • unzip RTI_Connext_Micro-Host-2.4.4.zip
    • tar zxvf RTI_Connext_Micro_Target-2.4.4-i86Linux2.6gcc4.4.5.tar.gz
    • Assuming your directory is /home/user/, you will end up with /home/user/ rti_connext_micro.2.4.4.

Unpacking Buildable Source

For the optional buildable source bundle, unpack it in the same directory
(from the example above, /home/user).

Environment

The RTIMEHOME environment variable must be set to the installation directory path for RTI Connext DDS Micro.


Define a Data Type

To distribute data using RTI Connext DDS Micro, you must first define a data type, and then run the rtiddsgen utility to generate the type-specific support code that RTI Connext DDS Micro needs and calls to publish and subscribe that data type.

RTI Connext DDS Micro accepts types definitions in Interface Definition Language (IDL) format.

       struct HelloWorld {
           string<128> msg;
       };

As an example, the HelloWorld examples provided with RTI Connext DDS Micro use this simple type defined in HelloWorld.idl. It contains a string "msg" with max length of 128 chars.


Generate Type Support Code with rtiddsgen

You will provide your IDL as an input to rtiddsgen. rtiddsgen supports code generation for the following standard types:

  • octet, char, wchar
  • short, unsigned short
  • long, unsigned long
  • long long, unsigned long long float
  • double, long double
  • boolean
  • string
  • struct
  • array
  • enum
  • wstring
  • sequence
  • union
  • typedef
  • value type

The script to run rtiddsgen is in <your_top_level_dir>/rti_connext_micro.2.4.4/rtiddsgen/scripts.

Calling the script to generate support code for HelloWorld.idl:

    rtiddsgen -micro -language C -replace HelloWorld.idl

Run "rtiddsgen -help" to see all available options. For the options used here:

  • The "-micro" option is necessary to generate support code specific to RTI Connext DDS Micro, as rtiddsgen is also capable of generating support code for RTI Connext DDS, and the generated code of the two are different.
  • The "-language" option specifies the language of the generated code. RTI Connext DDS Micro supports C and C++ (with "-language C++").
  • The "-replace" option specifies that the new generated code will replace, or overwrite, any existing files with the same name.

The generated code output consists of these files for HelloWorld.idl:

  • HelloWorld.h and HelloWorld.c. Operations to manage a sample of the type, and a DDS sequence of the type.
  • HelloWorldPlugin.h and HelloWorldPlugin.c. Implements the type-plugin interface defined by RTI Connext DDS Micro. Includes operations to serialize and deserialize a sample of the type and its DDS instance keys.
  • HelloWorldSupport.h and HelloWorldSupport.c. Support operations to generate a type-specific DataWriter and DataReader, and to register the type with a DDS DomainParticipant.

rtiddsgen does not generate publisher or subscriber code for RTI Connext DDS Micro. This is different than for RTI Connext DDS, where rtiddsgen will generate HelloWorld_publisher.c and HelloWorld_subscriber.c.


Create an Application

The rest of this guide will walk you through the steps of creating an application and will provide example code snippets. It assumes that you have defined your types and have used rtiddsgen to generate their support code.

Registry Configuration

The DomainParticipantFactory, in addition to its standard role of creating and deleting DomainParticipants, contains the RT Registry that a new application register with some necessary components.

The architecture of RTI Connext DDS Micro defines a run-time (RT) component interface that provides a generic framework for organizing and extending functionality of an application. An RT component is created and deleted with an RT component factory, and each RT component factory must be registered within an RT registry in order for its components to be usable by an application.

RTI Connext DDS Micro automatically registers components that provide necessary functionality. These include components for DDS Writers and Readers, RTPS protocol, and the UDP transport.

In addition, every DDS application must register three components:

  • Writer History. Queue of written samples of a DataWriter. Must be registered with name "wh".
  • Reader History. Queue of received samples of a DataReader. Must be registered with name "rh".
  • Discovery (DPDE or DPSE). Discovery component. Choose either dynamic (DPDE) or static (DPSE) endpoint discovery.

Example source:

  • Get the RT Registry from the DomainParticipantFactory singleton
RT_Registry_T *registry = NULL;
  • Register the Writer History and Reader History components with the registry
/* Register Writer History */
if (!RT_Registry_register(registry, "wh",
{
/* failure */
}
/* Register Reader History */
if (!RT_Registry_register(registry, "rh",
{
/* failure */
}

Only one discovery component can be registered, either DPDE or DPSE. Each has its own properties that can be configured upon registration.

  • Register DPDE for dynamic participant, dynamic endpoint discovery
struct DPDE_DiscoveryPluginProperty discovery_plugin_properties =
/* Configure properties */
discovery_plugin_properties.participant_liveliness_assert_period.sec = 5;
discovery_plugin_properties.participant_liveliness_assert_period.nanosec = 0;
discovery_plugin_properties.participant_liveliness_lease_duration.sec = 30;
discovery_plugin_properties.participant_liveliness_lease_duration.nanosec = 0;
/* Register DPDE with updated properties */
if (!RT_Registry_register(registry,
"dpde",
&discovery_plugin_properties._parent,
NULL))
{
/* failure */
}
  • Register DPSE for dynamic participant, static endpoint discovery
struct DPSE_DiscoveryPluginProperty discovery_plugin_properties =
/* Configure properties */
discovery_plugin_properties.participant_liveliness_assert_period.sec = 5;
discovery_plugin_properties.participant_liveliness_assert_period.nanosec = 0;
discovery_plugin_properties.participant_liveliness_lease_duration.sec = 30;
discovery_plugin_properties.participant_liveliness_lease_duration.nanosec = 0;
/* Register DPSE with updated properties */
if (!RT_Registry_register(registry,
"dpse",
&discovery_plugin_properties._parent,
NULL))
{
printf("failed to register dpse\n");
goto done;
}

Configuring UDP Transport

You may need to configure the UDP transport component that is pre-registered
by RTI Connext DDS Micro.  To change the properties of the UDP transport, first the UDP
component has be unregistered, then the properties have to be updated, and
finally the component must be re-registered with the updated properties.

Example code:

- Unregister the pre-registered UDP component
/* Unregister the pre-registered UDP component */
if (!RT_Registry_unregister(registry, "_udp", NULL, NULL))
{
/* failure */
}
  • Configure UDP transport properties
    struct UDP_InterfaceFactoryProperty udp_property =
    UDP_INTERFACE_FACTORY_PROPERTY_DEFAULT;
    /* allow_interface: Names of network interfaces allowed to send/receive.
    Allow one loopback (lo) and one NIC (eth0).
    */
    REDA_StringSeq_set_maximum(&udp_property->allow_interface,2);
    REDA_StringSeq_set_length(&udp_property->allow_interface,2);
    *REDA_StringSeq_get_reference(&udp_property->allow_interface,0) = "lo";
    *REDA_StringSeq_get_reference(&udp_property->allow_interface,1) = "eth0";

Create DomainParticipant, Topic, and Type

A DomainParticipantFactory creates DomainParticipants, and a DomainParticipant itself is the factory for creating Publishers, Subscribers, and Topic.

When creating a DomainParticipant, you may need to customize DomainParticipantQos, notably for

  • Resource limits. Default resource limits are set at minimum values.
  • Initial peers.
  • Discovery. The name of the registered discovery component ("dpde" or "dpse") must be assigned to DiscoveryQosPolicy's name.
  • Participant Name. Every DomainParticipant is given the same default name. Must be unique when using DPSE discovery.

Example code:

Register Type

Your data types that have been generated from IDL need to be registered with the DomainParticipants that will be using them. Each registered type must have a unique name, preferably the same as its IDL defined name.

"HelloWorld",
HelloWorldTypePlugin_get());
if (retcode != DDS_RETCODE_OK)
{
/* failure */
}

Create Topic of Registered Type

DDS Topics encapsulate the types being communicated, and you can create Topics for your type once your type is registered.

A topic is given a name at creation (e.g. "Example HelloWorld"). The type associated with the Topic is specified with its registered name.

DDS_Topic *topic = NULL;
"Example HelloWorld",
"HelloWorld",
NULL,
if (topic == NULL)
{
/* failure */
}

DPSE Discovery: Assert Remote Participant

DPSE discovery relies on the application to specify the other, or remote, DomainParticipants that its local DomainParticipants are allowed to discover. Your application must call a DPSE API for each remote participant to discover. The API takes as input the name of the remote participant.

/* Enable discovery of remote participant with name Participant_2 */
retcode = DPSE_RemoteParticipant_assert(participant, "Participant_2");
if (retcode != DDS_RETCODE_OK)
{
/* failure */
}

Create Publisher

A publishing application needs to create a DDS Publisher and then a DataWriter for each Topic it wants to publish.

In RTI Connext DDS Micro, PublisherQos in general contains no policies that need to be customized, while DataWriterQos does contain several customizable policies.

Create DataWriter

DDS_DataWriter *datawriter = NULL;
/* Configure writer Qos */
dw_qos.protocol.rtps_object_id = 100;
dw_qos.history.depth = 1;
/* Set enabled listener callbacks */
dw_listener.on_publication_matched = HelloWorldPublisher_on_publication_matched;
datawriter =
topic,
&dw_qos,
&dw_listener,
if (datawriter == NULL)
{
/* failure */
}

The DataWriterListener has its callbacks selectively enabled by the DDS status mask. In the example, the mask has set the on_publication_matched status, and accordingly the DataWriterListener has its on_publication_matched assigned to a callback function.

void HelloWorldPublisher_on_publication_matched(void *listener_data,
DDS_DataWriter * writer,
const struct DDS_PublicationMatchedStatus *status)
{
/* Print on match/unmatch */
if (status->current_count_change > 0)
{
printf("Matched a subscriber\n");
}
else
{
printf("Unmatched a subscriber\n");
}
}

DPSE Discovery: Assert Remote Subscription

A publishing application using DPSE discovery must specify the other DataReaders that its DataWriters are allowed to discover. Like the API for asserting a remote participant, the DPSE API for asserting a remote subscription must be called for each remote DataReader that a DataWriter may discover.

Whereas asserting a remote participant requires only the remote participant's name, asserting a remote subscription requires more configuration, as all QoS policies of the subscription necessary to determine matching must be known and thus specified.

struct DDS_SubscriptionBuiltinTopicData rem_subscription_data =
DDS_SubscriptionBuiltinTopicData_INITIALIZER;
/* Set Reader's protocol.rtps_object_id */
rem_subscription_data.key.value[DDS_BUILTIN_TOPIC_KEY_OBJECT_ID] = 200;
rem_subscription_data.topic_name = DDS_String_dup("Example HelloWorld");
rem_subscription_data.type_name = DDS_String_dup("HelloWorld");
retcode = DPSE_RemoteSubscription_assert(participant,
"Participant_2",
&rem_subscription_data,
HelloWorld_get_key_kind(HelloWorldTypePlugin_get(),
NULL)));
if (retcode != DDS_RETCODE_OK)
{
/* failure */
}

Writing Samples

Within the generated type support code are declarations of the type-specific DataWriter. For the HelloWorld type, this is the HelloWorldDataWriter.

Writing a HelloWorld sample is done by calling the write API of the HelloWorldDataWriter.

HelloWorldDataWriter *hw_datawriter;
HelloWorld *sample = NULL;
/* Create and set sample */
sample = HelloWorld_create();
if (sample == NULL)
{
/* failure */
}
sprintf(sample->msg, "Hello World!");
/* Write sample */
hw_datawriter = HelloWorldDataWriter_narrow(datawriter);
retcode = HelloWorldDataWriter_write(hw_datawriter, sample, &DDS_HANDLE_NIL);
if (retcode != DDS_RETCODE_OK)
{
/* failure */
}

Create Subscriber

A subscribing application needs to create a DDS Subscriber and then a DataReader for each Topic to which it wants to subscribe.

In RTI Connext DDS Micro, SubscriberQos in general contains no policies that need to be customized, while DataReaderQos does contain several customizable policies.

DDS_Subscriber *subscriber = NULL;
subscriber = DDS_DomainParticipant_create_subscriber(participant,
NULL,
if (subscriber == NULL)
{
/* failure */
}

Create DataReader

DDS_DataReader *datareader = NULL;
/* Configure Reader Qos */
dr_qos.protocol.rtps_object_id = 200;
dr_qos.history.depth = 1;
/* Set listener callbacks */
dr_listener.on_data_available = HelloWorldSubscriber_on_data_available;
dr_listener.on_subscription_matched = HelloWorldSubscriber_on_subscription_matched;
datareader = DDS_Subscriber_create_datareader(subscriber,
&dr_qos,
&dr_listener,
if (datareader == NULL)
{
/* failure */
}

The DataReaderListener has its callbacks selectively enabled by the DDS status mask. In the example, the mask has set the DDS_SUBSCRIPTION_MATCHED_STATUS and DDS_DATA_AVAILABLE_STATUS statuses, and accordingly the DataReaderListener has its on_subscription_matched and on_data_available assigned to callback functions.

void HelloWorldSubscriber_on_subscription_matched(void *listener_data,
DDS_DataReader * reader,
const struct DDS_SubscriptionMatchedStatus *status)
{
if (status->current_count_change > 0)
{
printf("Matched a publisher\n");
}
else
{
printf("Unmatched a publisher\n");
}
}
void HelloWorldSubscriber_on_data_available(void* listener_data,
DDS_DataReader* reader)
{
HelloWorldDataReader *hw_reader = HelloWorldDataReader_narrow(reader);
struct DDS_SampleInfo *sample_info = NULL;
HelloWorld *sample = NULL;
struct DDS_SampleInfoSeq info_seq =
struct HelloWorldSeq sample_seq =
const DDS_Long TAKE_MAX_SAMPLES = 32;
retcode = HelloWorldDataReader_take(hw_reader,
&sample_seq, &info_seq, TAKE_MAX_SAMPLES,
if (retcode != DDS_RETCODE_OK)
{
printf("failed to take data: %d\n", retcode);
goto done;
}
/* Print each valid sample taken */
for (i = 0; i < HelloWorldSeq_get_length(&sample_seq); ++i)
{
sample_info = DDS_SampleInfoSeq_get_reference(&info_seq, i);
if (sample_info->valid_data)
{
sample = HelloWorldSeq_get_reference(&sample_seq, i);
printf("\nSample received\n\tmsg: %s\n", sample->msg);
}
else
{
printf("not valid data\n");
}
}
HelloWorldDataReader_return_loan(hw_reader, &sample_seq, &info_seq);
done:
HelloWorldSeq_finalize(&sample_seq);
DDS_SampleInfoSeq_finalize(&info_seq);
}

DPSE Discovery: Assert Remote Publication

A subscribing application using DPSE discovery must specify the other DataWriters that its DataReaders are allowed to discover. Like the API for asserting a remote participant, the DPSE API for asserting a remote publication must be called for each remote DataWriter that a DataReader may discover.

Like done when asserting a remote publication, asserting a remote subscription requires configuration of all QoS policies of the subscription necessary to determine matching.

Receiving Samples

Accessing received samples can be done in a few ways:

  • Polling. Do read or take within a periodic polling loop.
  • Listener. When a new sample is received, the DataReaderListener's on_data_available is called. Processing is done in the context of the middleware's receive thread. See the above HelloWorldSubscriber_on_data_available callback for example code.
  • Waitset. Create a waitset, attach it to a status condition with the data_available status enabled, and wait for a received sample to trigger the waitset. Processing is done in the context of the user's application thread.
DDS_WaitSet *waitset = NULL;
struct DDS_Duration_t wait_timeout = { 10, 0 }; /* 10 seconds */
DDS_StatusCondition *dr_condition = NULL;
struct DDS_ConditionSeq active_conditions =
if (!DDS_ConditionSeq_initialize(&active_conditions))
{
/* failure */
}
if (!DDS_ConditionSeq_set_maximum(&active_conditions, 1))
{
/* failure */
}
waitset = DDS_WaitSet_new();
if (waitset == NULL )
{
/* failure */
}
if (retcode != DDS_RETCODE_OK)
{
/* failure */
}
retcode = DDS_WaitSet_attach_condition(waitset,
if (retcode != DDS_RETCODE_OK)
{
/* failure */
}
retcode = DDS_WaitSet_wait(waitset, active_conditions, &wait_timeout);
switch (retcode) {
{
/* This WaitSet only has a single condition attached to it
so we can implicitly assume the DataReader's status condition
to be active (with the enabled DATA_AVAILABLE status) upon
successful return of wait().
If more than one conditions were attached to the WaitSet,
the returned sequence must be examined using the
commented out code instead of the following.
*/
HelloWorldSubscriber_take_data(HelloWorldDataReader_narrow(datareader));
/*
DDS_Long active_len = DDS_ConditionSeq_get_length(&active_conditions);
for (i = active_len - 1; i >= 0; --i)
{
DDS_Condition *active_condition =
*DDS_ConditionSeq_get_reference(&active_conditions, i);
if (active_condition ==
DDS_StatusCondition_as_condition(dr_condition))
{
total_samples += HelloWorldSubscriber_take_data(
HelloWorldDataReader_narrow(datareader));
}
else if (active_condition == some_other_condition)
{
do_something_else();
}
}
*/
break;
}
{
printf("WaitSet_wait timed out\n");
break;
}
default:
{
printf("ERROR in WaitSet_wait: retcode=%d\n", retcode);
break;
}
}
struct DDS_PublicationBuiltinTopicData rem_publication_data =
DDS_PublicationBuiltinTopicData_INITIALIZER;
/* Set Writer's protocol.rtps_object_id */
rem_publication_data.key.value[DDS_BUILTIN_TOPIC_KEY_OBJECT_ID] = 100;
rem_publication_data.topic_name = DDS_String_dup("Example HelloWorld");
rem_publication_data.type_name = DDS_String_dup("HelloWorld");
retcode = DPSE_RemotePublication_assert(participant,
"Participant_1",
&rem_publication_data,
HelloWorld_get_key_kind(HelloWorldTypePlugin_get(),
NULL)));
if (retcode != DDS_RETCODE_OK)
{
/* failure */
}

Filtering Samples

In lieu of supporting Content-Filtered Topics, a DataReaderListener in RTI Connext DDS Micro provides callbacks to do application-level filtering per sample.

  • on_before_sample_deserialize. Through this callback, a received sample is presented to the application before it has been deserialized or stored in the DataReader's queue.
  • on_before_sample_commit. Through this callback, a received sample is presented to the application after it has been deserialized but before it has been stored in the DataReader's queue.

You control the callbacks' sample_dropped parameter; upon exiting either callback, the DataReader will drop the sample if sample_dropped is true. Consequently, dropped samples are not stored in the DataReader's queue and are not available to be read or taken.

Neither callback is associated with a DDS Status. Rather, each is enabled when assigned, to a non-NULL callback.

Note, because it is called after the sample has been deserialized, on_before_sample_commit provides an additional sample_info parameter, containing some of the usual sample information that would be available when the sample is read or taken.

The HelloWorld_dpde example's subscriber has this on_before_sample_commit callback:

DDS_Boolean HelloWorldSubscriber_on_before_sample_commit(
void *listener_data,
DDS_DataReader *reader,
const void *const sample,
const struct DDS_SampleInfo *const sample_info,
DDS_Boolean *dropped)
{
HelloWorld *hw_sample = (HelloWorld *)sample;
/* Drop samples with even-numbered count in msg */
HelloWorldSubscriber_filter_sample(hw_sample, dropped);
if (*dropped)
{
printf("\nSample filtered, before commit\n\tDROPPED - msg: %s\n",
hw_sample->msg);
}
}
...
dr_listener.on_before_sample_deserialize =
HelloWorldSubscriber_on_before_sample_commit;

RTI Connext DDS Micro Version 2.4.6 Copyright © Mon Jan 25 2016 Real-Time Innovations, Inc