An Introduction to DDS with Examples

The following guide aims to present some of the core functionalities and features of DDS that were used by Team Hurricane when implementing the Matrix Board Communication demonstrator in C. The main goal of this article is to present a naive and simple approach to the complete process of creating a DDS application.

Topics in DDS

Topics in DDS are data objects for which values will be published/received to/from. Each topic consists of the following:

  • The topic type.
  • The topic name.
  • A set of Quality of Service (QoS) properties.

The topic type defines the data type that will be stored in that particular topic, while the topic name is a string that identifies that topic within that particular communication domain. A DDS topic type contains an IDL structure and a key. The QoS policies of the topic control the behavior of the DataReaders and DataWriters of that topic.

Topics are defined using the IDL specification language enabling the topics to retain independence from a certain programming language. Mapping of IDL data types presents the mapping between various IDL data types to data types of various programming languages. The figure below shows how a typical DDS application is built.

title “DDS Compilation Process”
left to right direction

(DDS\nData Types\nDefinitions\n[*.idl])  --> (DDS IDLC-\nCompiler)
(DDS IDLC-\nCompiler) --> (Generated\n.h and .c\nfiles)
(Generated\n.h and .c\nfiles) --> (Topic instantiation\nusing macros from\ngenerated files)

In the case of the Matrix Board Communication the topic type Msg was defined and it includes a structure with two long and one string IDL primitives. The userID defines the identity number of the matrix board writing to that topic while the traffic defines the traffic that was sensed by that matrix board. Finally the message string is simply the name of that particular topic. The following snippets of code show the mapping from the IDL to C language of the topic type Msg defined for the communication of the matrix boards.

struct Msg
{
  long userID;
  long traffic;
  string message;
};
#pragma keylist Msg userID
typedef struct MBCData_Msg
{
  int32_t userID;
  int32_t traffic;
  char * message;
} MBCData_Msg;

Within our application before initializing a topic, we must create a participant on a given domain (in this case the default domain) using:

participant = dds_create_participant(DDS_DOMAIN_DEFAULT, NULL, NULL);

In the context of the matrix board communication, a matrix board can initialize a topic using the following function:

topic = dds_create_topic(participant, &MBCData_Msg_desc, topic_name, NULL, NULL);

In the function above, the &MBCData_Msg_desc refers to the data type that the topic is initialized to, while topic_name is a string containing the name of the topic. Topics with the same data type but different names are considered different topics.

Data Writers in DDS

Data writers are DDS entities which have the ability to write to a given topic.

After initializing a participant on a certain domain as well as a topic, one can create a data writer on that particular topic using:

writer = dds_create_writer(participant, topic, NULL, NULL);

It is possible to add another layer of abstraction which is a publisher. A publisher includes multiple writers and its main purpose is to manage his group of writers.

In the context of the matrix board communication, each matrix board has only one data writer which writes data only to the topic of that particular board.

Writing Data to a Topic

In order to demonstrate how data can be written to a particular topic let us look again at the example of the matrix board communication. First initialize the data that needs to be written:

MBCData_Msg msg;
/* Create a message to write. */
msg.userID = MBC_id;
msg.traffic = traffic_number;
msg.message = topic_name;

And then simply write the data using the writer and initialized data using:

dds_write (writer, &msg);

Also, DDS provides event-based activation. Meaning that we can perform a write operation depending on a particular event, (e.g. a data reader coming online etc.). In the case of the matrix board communication, each board updates its own topic every second (polling based write).

Data Readers in DDS

Data readers are DDS entities which have the ability to read from a given topic.

After initializing a participant on a certain domain as well as a topic, one can create a data reader on that particular topic using:

reader = dds_create_reader(participant, topic, NULL, NULL);

Similarly to publishers, it is possible to add another layer of abstraction which is a subscriber. A subscriber includes multiple readers and its main purpose is to manage his group of readers.

In the context of the matrix board communication, each matrix board has an array of readers starting from its own ID number and up to a certain predefined ID number. It is obvious that it has the respective array of topics since they are required to create each reader. This enables the scanning and detection of two of the closest matrix boards which are “alive”.

Reading Data from a Topic

In order to read data from a topic, one has to perform some additional steps when compared to a write operation. First of all, we must initialize a sample array using the macros created from the compilation of our .idl file (see resulting .c and .h files from Topics in DDS chapter):

samples[0] = MBCData_Msg__alloc ();

Then, for the actual data read we have to options. The first option is to use the following function:

dds_read(reader, samples, infos, MAX_SAMPLES, MAX_SAMPLES);

Using dds_read will return a copy of the read samples, leaving them available in the reader’s history cache so they can be read again later (marking it as “read” along the way). While the second option is to utilize:

dds_take(reader, samples, infos, MAX_SAMPLES, MAX_SAMPLES);

Using dds_take will return the samples but also removes them from the reader’s history cache (but not from any other readers’ history caches). In the case of the matrix board the dds_take function was used since we do not use previous samples for anything and it is more efficient to remove them from our cache. After reading the data one has to also free the data location that was initialized, again using the provided macros. An example from the MBC is the following:

MBCData_Msg_free (samples[0], DDS_FREE_ALL);