HelloWorld source code

Subscriber

The Subscriber.c file mainly contains the statements to wait for a HelloWorld message and reads it when it receives it.

Important

The Cyclone DDS read semantics keep the data sample in the data reader cache. To prevent resource exhaustion, It is important to use take, where appropriate.

The subscriber application implements the steps defined in HelloWorld keys steps.

The following is a copy of the subscriber.c file that is available from the GitHub cyclonedds/examples/helloworld/ repository.

 1#include "dds/dds.h"
 2#include "HelloWorldData.h"
 3#include <stdio.h>
 4#include <string.h>
 5#include <stdlib.h>
 6
 7/* An array of one message (aka sample in dds terms) will be used. */
 8#define MAX_SAMPLES 1
 9
10int main (int argc, char ** argv)
11{
12  dds_entity_t participant;
13  dds_entity_t topic;
14  dds_entity_t reader;
15  HelloWorldData_Msg *msg;
16  void *samples[MAX_SAMPLES];
17  dds_sample_info_t infos[MAX_SAMPLES];
18  dds_return_t rc;
19  dds_qos_t *qos;
20  (void)argc;
21  (void)argv;
22
23  /* Create a Participant. */
24  participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
25  if (participant < 0)
26    DDS_FATAL("dds_create_participant: %s\n", dds_strretcode(-participant));
27
28  /* Create a Topic. */
29  topic = dds_create_topic (
30    participant, &HelloWorldData_Msg_desc, "HelloWorldData_Msg", NULL, NULL);
31  if (topic < 0)
32    DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
33
34  /* Create a reliable Reader. */
35  qos = dds_create_qos ();
36  dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_SECS (10));
37  reader = dds_create_reader (participant, topic, qos, NULL);
38  if (reader < 0)
39    DDS_FATAL("dds_create_reader: %s\n", dds_strretcode(-reader));
40  dds_delete_qos(qos);
41
42  printf ("\n=== [Subscriber] Waiting for a sample ...\n");
43  fflush (stdout);
44
45  /* Initialize sample buffer, by pointing the void pointer within
46   * the buffer array to a valid sample memory location. */
47  samples[0] = HelloWorldData_Msg__alloc ();
48
49  /* Poll until data has been read. */
50  while (true)
51  {
52    /* Do the actual read.
53     * The return value contains the number of read samples. */
54    rc = dds_read (reader, samples, infos, MAX_SAMPLES, MAX_SAMPLES);
55    if (rc < 0)
56      DDS_FATAL("dds_read: %s\n", dds_strretcode(-rc));
57
58    /* Check if we read some data and it is valid. */
59    if ((rc > 0) && (infos[0].valid_data))
60    {
61      /* Print Message. */
62      msg = (HelloWorldData_Msg*) samples[0];
63      printf ("=== [Subscriber] Received : ");
64      printf ("Message (%"PRId32", %s)\n", msg->userID, msg->message);
65      fflush (stdout);
66      break;
67    }
68    else
69    {
70      /* Polling sleep. */
71      dds_sleepfor (DDS_MSECS (20));
72    }
73  }
74
75  /* Free the data location. */
76  HelloWorldData_Msg_free (samples[0], DDS_FREE_ALL);
77
78  /* Deleting the participant will delete all its children recursively as well. */
79  rc = dds_delete (participant);
80  if (rc != DDS_RETCODE_OK)
81    DDS_FATAL("dds_delete: %s\n", dds_strretcode(-rc));
82
83  return EXIT_SUCCESS;
84}

To create a subscriber:

  1. To recieve data using the DDS API and the HelloWorldData_Msg type, include the appropriate header files:

    • The dds.h file to give access to the DDS APIs

    • The HelloWorldData.h is specific to the data type defined in the IDL

    1 #include "ddsc/dds.h"
    2 #include "HelloWorldData.h"
    
  2. At least three DDS entities are needed to build a minimalistic application:

    • Domain participant

    • Topic

    • Reader

    Cyclone DDS implicitly creates a DDS Subscriber. If required, this behavior can be overridden.

    12dds_entity_t participant;
    13dds_entity_t topic;
    14dds_entity_t reader;
    
  3. To handle the data, create and declare some buffers:

    15HelloWorldData_Msg *msg;
    16void *samples[MAX_SAMPLES];
    17dds_sample_info_t info[MAX_SAMPLES];
    

    The read() operation can return more than one data sample (where several publishing applications are started simultaneously to write different message instances), an array of samples is therefore needed.

    In Cyclone DDS, data and metadata are propagated together. To handle the metadata, the dds_sample_info array must be declared.

  4. The DDS participant is always attached to a specific DDS domain. In the HelloWorld example, it is part of the Default\_Domain, which is specified in the XML configuration file. To override the default behavior, create or edit a configuration file (for example, cyclonedds.xml). For further information, refer to the Configuration guide and the Configuration File Reference.

    24participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
    
  5. Create the topic with a given name. Topics with the same data type description and with different names are considered other topics. This means that readers or writers created for a given topic do not interfere with readers or writers created with another topic even if they have the same data type. Topics with the same name but incompatible datatype are considered an error and should be avoided.

    29topic = dds_create_topic (
    30    participant, &HelloWorldData_Msg_desc, "HelloWorldData_Msg", NULL, NULL);
    31if (topic < 0)
    32    DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
    
  6. Create a data reader and attach to it:

    35qos = dds_create_qos ();
    36dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_SECS (10));
    37reader = dds_create_reader (participant, topic, qos, NULL);
    38if (reader < 0)
    39    DDS_FATAL("dds_create_reader: %s\n", dds_strretcode(-reader));
    40dds_delete_qos(qos);
    

    The read operation expects an array of pointers to a valid memory location. This means the samples array needs initialization by pointing the void pointer within the buffer array to a valid sample memory location. In the example, there is an array of one element; (#define MAX_SAMPLES 1.)

  7. Allocate memory for one HelloWorldData_Msg:

    47samples[0] = HelloWorldData_Msg__alloc ();
    
  8. Attempt to read data by going into a polling loop that regularly scrutinizes and examines the arrival of a message:

    54rc = dds_read (reader, samples, infos, MAX_SAMPLES, MAX_SAMPLES);
    55if (rc < 0)
    56    DDS_FATAL("dds_read: %s\n", dds_strretcode(-rc));
    

    The dds_read operation returns the number of samples equal to the parameter MAX_SAMPLE. If data has arrived in the reader’s cache, that number will exceed 0.

  9. The Sample_info (info) structure shows whether the data is either:

    • Valid data means that it contains the payload provided by the publishing application.

    • Invalid data means we are reading the DDS state of the data Instance.

    The state of a data instance can be, DISPOSED by the writer, or it is NOT_ALIVE anymore, which could happen if the publisher application terminates while the subscriber is still active. In this case, the sample is not considered valid, and its sample info[].Valid_data the field is False:

    59if ((ret > 0) && (info[0].valid_data))
    
  10. If data is read, cast the void pointer to the actual message data type and display the contents:

    62msg = (HelloWorldData_Msg*) samples[0];
    63printf ("=== [Subscriber] Received : ");
    64printf ("Message (%d, %s)\n", msg->userID, msg->message);
    65break;
    
  11. When data is received and the polling loop is stopped, release the allocated memory and delete the domain participant:

    76HelloWorldData_Msg_free (samples[0], DDS_FREE_ALL);
    
  12. All the entities that are created under the participant, such as the data reader and topic, are recursively deleted.

    79rc = dds_delete (participant);
    80if (rc != DDS_RETCODE_OK)
    81    DDS_FATAL("dds_delete: %s\n", dds_strretcode(-rc));
    

Publisher

The Publisher.c contains the source that writes a Hello World! Message.

The DDS publisher application code is almost symmetric to the subscriber, except that you must create a data writer instead of a data reader. To ensure data is written only when at least one matching reader is discovered, a synchronization statement is added to the main thread. Synchronizing the main thread until a reader is discovered ensures we can start the publisher or subscriber program in any order.

The following is a copy of the publisher.c file that is available from the GitHub cyclonedds/examples/helloworld/ repository.

 1#include "dds/dds.h"
 2#include "HelloWorldData.h"
 3#include <stdio.h>
 4#include <stdlib.h>
 5
 6int main (int argc, char ** argv)
 7{
 8  dds_entity_t participant;
 9  dds_entity_t topic;
10  dds_entity_t writer;
11  dds_return_t rc;
12  HelloWorldData_Msg msg;
13  uint32_t status = 0;
14  (void)argc;
15  (void)argv;
16
17  /* Create a Participant. */
18  participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
19  if (participant < 0)
20    DDS_FATAL("dds_create_participant: %s\n", dds_strretcode(-participant));
21
22  /* Create a Topic. */
23  topic = dds_create_topic (
24    participant, &HelloWorldData_Msg_desc, "HelloWorldData_Msg", NULL, NULL);
25  if (topic < 0)
26    DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
27
28  /* Create a Writer. */
29  writer = dds_create_writer (participant, topic, NULL, NULL);
30  if (writer < 0)
31    DDS_FATAL("dds_create_writer: %s\n", dds_strretcode(-writer));
32
33  printf("=== [Publisher]  Waiting for a reader to be discovered ...\n");
34  fflush (stdout);
35
36  rc = dds_set_status_mask(writer, DDS_PUBLICATION_MATCHED_STATUS);
37  if (rc != DDS_RETCODE_OK)
38    DDS_FATAL("dds_set_status_mask: %s\n", dds_strretcode(-rc));
39
40  while(!(status & DDS_PUBLICATION_MATCHED_STATUS))
41  {
42    rc = dds_get_status_changes (writer, &status);
43    if (rc != DDS_RETCODE_OK)
44      DDS_FATAL("dds_get_status_changes: %s\n", dds_strretcode(-rc));
45
46    /* Polling sleep. */
47    dds_sleepfor (DDS_MSECS (20));
48  }
49
50  /* Create a message to write. */
51  msg.userID = 1;
52  msg.message = "Hello World";
53
54  printf ("=== [Publisher]  Writing : ");
55  printf ("Message (%"PRId32", %s)\n", msg.userID, msg.message);
56  fflush (stdout);
57
58  rc = dds_write (writer, &msg);
59  if (rc != DDS_RETCODE_OK)
60    DDS_FATAL("dds_write: %s\n", dds_strretcode(-rc));
61
62  /* Deleting the participant will delete all its children recursively as well. */
63  rc = dds_delete (participant);
64  if (rc != DDS_RETCODE_OK)
65    DDS_FATAL("dds_delete: %s\n", dds_strretcode(-rc));
66
67  return EXIT_SUCCESS;
68}

To create a publisher:

  1. Send data using the DDS API and the HelloWorldData_Msg type, include the appropriate header files:

    1#include "ddsc/dds.h"
    2#include "HelloWorldData.h"
    
  2. Create a writer. You must have a participant and a topic (must have the same topic name as specified in subscriber.c):

     8dds_entity_t participant;
     9dds_entity_t topic;
    10dds_entity_t writer;
    
  3. Create a Participant.

    18participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
    
  4. Create a Topic.

    23topic = dds_create_topic (participant, &HelloWorldData_Msg_desc, "HelloWorldData_Msg", NULL, NULL);
    
  5. Create a Writer.

    28writer = dds_create_writer (participant, topic, NULL, NULL);
    
  6. When readers and writers are sharing the same data type and topic name, it connects them without the application’s involvement. To write data only when a DataReader appears, a rendezvous pattern is required. A rendezvous pattern can be implemented by either:

    • Regularly polling the publication matching status (the preferred option in this example).

    • Waiting for the publication/subscription matched events, where the publisher waits and blocks the writing thread until the appropriate publication-matched event is raised.

    The following line of code instructs Cyclone DDS to listen on the DDS_PUBLICATION_MATCHED_STATUS:

    36dds_set_status_mask(writer, DDS_PUBLICATION_MATCHED_STATUS);
    
  7. At regular intervals, the status change and a matching publication is received. In between, the writing thread sleeps for a time period equal DDS\_MSECS (in milliseconds).

    40while(!(status & DDS_PUBLICATION_MATCHED_STATUS))
    41{
    42    rc = dds_get_status_changes (writer, &status);
    43    if (rc != DDS_RETCODE_OK)
    44        DDS_FATAL("dds_get_status_changes: %s\n", dds_strretcode(-rc));
    45
    46    /* Polling sleep. */
    47    dds_sleepfor (DDS_MSECS (20));
    48}
    

    After this loop, a matching reader has been discovered.

  8. To write the data instance, create and initialize the data:

    12HelloWorldData_Msg msg;
    
    51msg.userID = 1;
    52msg.message = "Hello World";
    
  9. Send the data instance of the keyed message:

    58rc = dds_write (writer, &msg);
    
  10. When terminating the program, free the DDS allocated resources by deleting the root entity of all the others (the domain participant):

    63rc = dds_delete (participant);
    

    All the underlying entities, such as topic, writer, and so on, are deleted.