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:
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 APIsThe
HelloWorldData.h
is specific to the data type defined in the IDL
1 #include "ddsc/dds.h" 2 #include "HelloWorldData.h"
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;
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.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);
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));
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
.)Allocate memory for one
HelloWorldData_Msg
:47samples[0] = HelloWorldData_Msg__alloc ();
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 parameterMAX_SAMPLE
. If data has arrived in the reader’s cache, that number will exceed 0.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 isFalse
:59if ((ret > 0) && (info[0].valid_data))
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;
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);
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));
The subscriber.cpp
file mainly contains the statements to wait for a HelloWorld
message and reads it when it receives it.
Note
The Cyclone DDS read
semantics keep the data sample in the data
reader cache.
The subscriber application implements the steps defined in HelloWorld keys steps.
The following is a copy of the subscriber.cpp file that is available from the GitHub cyclonedds-cxx/examples/helloworld/ repository.
1/*
2 * Copyright(c) 2006 to 2021 ZettaScale Technology and others
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
7 * v. 1.0 which is available at
8 * http://www.eclipse.org/org/documents/edl-v10.php.
9 *
10 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11 */
12#include <cstdlib>
13#include <iostream>
14#include <chrono>
15#include <thread>
16
17/* Include the C++ DDS API. */
18#include "dds/dds.hpp"
19
20/* Include data type and specific traits to be used with the C++ DDS API. */
21#include "HelloWorldData.hpp"
22
23using namespace org::eclipse::cyclonedds;
24
25int main() {
26 try {
27 std::cout << "=== [Subscriber] Create reader." << std::endl;
28
29 /* First, a domain participant is needed.
30 * Create one on the default domain. */
31 dds::domain::DomainParticipant participant(domain::default_id());
32
33 /* To subscribe to something, a topic is needed. */
34 dds::topic::Topic<HelloWorldData::Msg> topic(participant, "HelloWorldData_Msg");
35
36 /* A reader also needs a subscriber. */
37 dds::sub::Subscriber subscriber(participant);
38
39 /* Now, the reader can be created to subscribe to a HelloWorld message. */
40 dds::sub::DataReader<HelloWorldData::Msg> reader(subscriber, topic);
41
42 /* Poll until a message has been read.
43 * It isn't really recommended to do this kind wait in a polling loop.
44 * It's done here just to illustrate the easiest way to get data.
45 * Please take a look at Listeners and WaitSets for much better
46 * solutions, albeit somewhat more elaborate ones. */
47 std::cout << "=== [Subscriber] Wait for message." << std::endl;
48 bool poll = true;
49 while (poll) {
50 /* For this example, the reader will return a set of messages (aka
51 * Samples). There are other ways of getting samples from reader.
52 * See the various read() and take() functions that are present. */
53 dds::sub::LoanedSamples<HelloWorldData::Msg> samples;
54
55 /* Try taking samples from the reader. */
56 samples = reader.take();
57
58 /* Are samples read? */
59 if (samples.length() > 0) {
60 /* Use an iterator to run over the set of samples. */
61 dds::sub::LoanedSamples<HelloWorldData::Msg>::const_iterator sample_iter;
62 for (sample_iter = samples.begin();
63 sample_iter < samples.end();
64 ++sample_iter) {
65 /* Get the message and sample information. */
66 const HelloWorldData::Msg& msg = sample_iter->data();
67 const dds::sub::SampleInfo& info = sample_iter->info();
68
69 /* Sometimes a sample is read, only to indicate a data
70 * state change (which can be found in the info). If
71 * that's the case, only the key value of the sample
72 * is set. The other data parts are not.
73 * Check if this sample has valid data. */
74 if (info.valid()) {
75 std::cout << "=== [Subscriber] Message received:" << std::endl;
76 std::cout << " userID : " << msg.userID() << std::endl;
77 std::cout << " Message : \"" << msg.message() << "\"" << std::endl;
78
79 /* Only 1 message is expected in this example. */
80 poll = false;
81 }
82 }
83 } else {
84 std::this_thread::sleep_for(std::chrono::milliseconds(20));
85 }
86 }
87 } catch (const dds::core::Exception& e) {
88 std::cerr << "=== [Subscriber] DDS exception: " << e.what() << std::endl;
89 return EXIT_FAILURE;
90 } catch (const std::exception& e) {
91 std::cerr << "=== [Subscriber] C++ exception: " << e.what() << std::endl;
92 return EXIT_FAILURE;
93 }
94
95 std::cout << "=== [Subscriber] Done." << std::endl;
96
97 return EXIT_SUCCESS;
98}
To create a subscriber:
To recieve data using the DDS ISOCPP API and the
HelloWorldData_Msg
type, include the appropriate header files:The
dds.hpp
file give access to the DDS APIs,The
HelloWorldData.hpp
is specific to the data type defined in the IDL.
18#include "dds/dds.hpp"
21#include "HelloWorldData.hpp"
At least four DDS entities are needed to build a minimalistic application:
Domain participant
Topic
Subscriber
Reader
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_URI
). For further information, refer to the Configuration guide and the Configuration File Reference.31dds::domain::DomainParticipant participant(domain::default_id());
Create the topic with a given name (
ddsC++_helloworld_example
) and the predefined data type (HelloWorldData::Msg
). 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.34dds::topic::Topic<HelloWorldData::Msg> topic(participant, "HelloWorldData_Msg");
Create the subscriber:
37dds::sub::Subscriber subscriber(participant);
Create a data reader and attach to it:
40dds::sub::DataReader<HelloWorldData::Msg> reader(subscriber, topic);
The Cyclone DDS C++ API simplifies and extends how data can be read or taken. To handle the data some,
LoanedSamples
is declared and created which loan samples from the Service pool. Return of the loan is implicit and managed by scoping:53dds::sub::LoanedSamples<HelloWorldData::Msg> samples;
61dds::sub::LoanedSamples<HelloWorldData::Msg>::const_iterator sample_iter;
The
read()/take()
operation can return more than one data sample (where several publishing applications are started simultaneously to write different message instances), an an iterator is used:66 const HelloWorldData::Msg& msg = sample_iter->data(); 67 const dds::sub::SampleInfo& info = sample_iter->info();
In Cyclone DDS, data and metadata are propagated together. The samples are a set of data samples (that is, user-defined data) and metadata describing the sample state and validity, etc ,,, (
info
). To get the data and metadata from each sample, use iterators:87} catch (const dds::core::Exception& e) { 88 std::cerr << "=== [Subscriber] DDS exception: " << e.what() << std::endl; 89 return EXIT_FAILURE; 90} catch (const std::exception& e) { 91 std::cerr << "=== [Subscriber] C++ exception: " << e.what() << std::endl; 92 return EXIT_FAILURE; 93}
To locate issues precisely when they occur, it is good practice to surround every key verb of the DDS APIs with
try/catch
block. For example, the following shows how one block is used to facilitate the programming model of the applications and improve source code readability:26try {
87} catch (const dds::core::Exception& e) {
To retrieve data in your application code from the data reader’s cache, you can either:
Use a pre-allocated buffer to store the data. Create an array/vector-like like container.
Loan it from the middleware. These buffers are actually ‘owned’ by the middleware, precisely by the
DataReader
. The Cyclone DDS C++ API implicitly allows you to return the loans through scoping.
In the example, use the loan samples mode (
LoanedSamples
).Samples
are an unbounded sequence of samples; the sequence length depends on the amount of data available in the data reader’s cache:53dds::sub::LoanedSamples<HelloWorldData::Msg> samples;
Attempt to read data by going into a polling loop that regularly scrutinizes and examines the arrival of a message. Samples are removed from the reader’s cache when taken with the
take()
:56samples = reader.take();
If you choose to read the samples with
read()
, data remains in the data reader cache. A length() of samples greater than zero indicates that the data reader cache was not empty:59if (samples.length() > 0)
As sequences are NOT pre-allocated by the user, buffers are ‘loaned’ by the
DataReader
:61dds::sub::LoanedSamples<HelloWorldData::Msg>::const_iterator sample_iter; 62for (sample_iter = samples.begin(); 63 sample_iter < samples.end(); 64 ++sample_iter)
For each sample, cast and extract its user-defined data (
Msg
) and metadate (info
):66const HelloWorldData::Msg& msg = sample_iter->data(); 67const dds::sub::SampleInfo& info = sample_iter->info();
The SampleInfo (
info
) shows whether the data we are taking is Valid or Invalid. Valid data means that it contains the payload provided by the publishing application. Invalid data means that we are reading the DDS state of the data Instance. The state of a data instance can beDISPOSED
by the writer, or it isNOT_ALIVE
anymore, which could happen when the publisher application terminates while the subscriber is still active. In this case, the sample is not considered valid, and its sampleinfo.valid()
field is False.74if (info.valid())
Display the sample containing valid data:
75std::cout << "=== [Subscriber] Message received:" << std::endl; 76std::cout << " userID : " << msg.userID() << std::endl; 77std::cout << " Message : \"" << msg.message() << "\"" << std::endl;
As we are using the Poll data reading mode, we repeat the above steps every 20 milliseconds.
83else { 84 std::this_thread::sleep_for(std::chrono::milliseconds(20)); 85}
This example uses the polling mode to read or take data. Cyclone DDS offers waitSet and Listener mechanism to notify the application that data is available in their cache, which avoids polling the cache at a regular intervals. The discretion of these mechanisms is beyond the scope of this document.
All the entities that are created under the participant, such as the data reader, subscriber, and topic are automatically deleted by middleware through the scoping mechanism.
Note
To modify the data reader default reliability Qos to reliable:
60/* With a normal configuration (see dds::pub::qos::DataWriterQos
dds::sub::qos::DataReaderQos drqos = topic.qos() << dds::core::policy::Reliability::Reliable();
dds::sub::DataReader<HelloWorldData::Msg> dr(subscriber, topic, drqos);
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:
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"
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;
Create a Participant.
18participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
Create a Topic.
23topic = dds_create_topic (participant, &HelloWorldData_Msg_desc, "HelloWorldData_Msg", NULL, NULL);
Create a Writer.
28writer = dds_create_writer (participant, topic, NULL, NULL);
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);
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.
To write the data instance, create and initialize the data:
12HelloWorldData_Msg msg;
51msg.userID = 1; 52msg.message = "Hello World";
Send the data instance of the keyed message:
58rc = dds_write (writer, &msg);
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.
The Publisher.cpp
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 publisher and DataWriter
. 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.cpp file that is available from the GitHub cyclonedds-cxx/examples/helloworld/ repository.
1/*
2 * Copyright(c) 2006 to 2020 ZettaScale Technology and others
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
7 * v. 1.0 which is available at
8 * http://www.eclipse.org/org/documents/edl-v10.php.
9 *
10 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11 */
12#include <cstdlib>
13#include <iostream>
14#include <chrono>
15#include <thread>
16
17/* Include the C++ DDS API. */
18#include "dds/dds.hpp"
19
20/* Include data type and specific traits to be used with the C++ DDS API. */
21#include "HelloWorldData.hpp"
22
23using namespace org::eclipse::cyclonedds;
24
25int main() {
26 try {
27 std::cout << "=== [Publisher] Create writer." << std::endl;
28
29 /* First, a domain participant is needed.
30 * Create one on the default domain. */
31 dds::domain::DomainParticipant participant(domain::default_id());
32
33 /* To publish something, a topic is needed. */
34 dds::topic::Topic<HelloWorldData::Msg> topic(participant, "HelloWorldData_Msg");
35
36 /* A writer also needs a publisher. */
37 dds::pub::Publisher publisher(participant);
38
39 /* Now, the writer can be created to publish a HelloWorld message. */
40 dds::pub::DataWriter<HelloWorldData::Msg> writer(publisher, topic);
41
42 /* For this example, we'd like to have a subscriber to actually read
43 * our message. This is not always necessary. Also, the way it is
44 * done here is just to illustrate the easiest way to do so. It isn't
45 * really recommended to do a wait in a polling loop, however.
46 * Please take a look at Listeners and WaitSets for much better
47 * solutions, albeit somewhat more elaborate ones. */
48 std::cout << "=== [Publisher] Waiting for subscriber." << std::endl;
49 while (writer.publication_matched_status().current_count() == 0) {
50 std::this_thread::sleep_for(std::chrono::milliseconds(20));
51 }
52
53 /* Create a message to write. */
54 HelloWorldData::Msg msg(1, "Hello World");
55
56 /* Write the message. */
57 std::cout << "=== [Publisher] Write sample." << std::endl;
58 writer.write(msg);
59
60 /* With a normal configuration (see dds::pub::qos::DataWriterQos
61 * for various different writer configurations), deleting a writer will
62 * dispose all its related message.
63 * Wait for the subscriber to have stopped to be sure it received the
64 * message. Again, not normally necessary and not recommended to do
65 * this in a polling loop. */
66 std::cout << "=== [Publisher] Waiting for sample to be accepted." << std::endl;
67 while (writer.publication_matched_status().current_count() > 0) {
68 std::this_thread::sleep_for(std::chrono::milliseconds(50));
69 }
70 }
71 catch (const dds::core::Exception& e) {
72 std::cerr << "=== [Publisher] Exception: " << e.what() << std::endl;
73 return EXIT_FAILURE;
74 }
75
76 std::cout << "=== [Publisher] Done." << std::endl;
77
78 return EXIT_SUCCESS;
79}
To create a publisher:
Send data using the ISOCPP DDS API and the
HelloWorldData_Msg
type, include the appropriate header files:18#include "dds/dds.hpp"
21#include "HelloWorldData.hpp"
An exception handling mechanism
try/catch
block is used.26try {
Create a writer. You must have a participant, a topic, and a publisher (must have the same topic name as specified in
subscriber.cpp
):31dds::domain::DomainParticipant participant(domain::default_id());
34dds::topic::Topic<HelloWorldData::Msg> topic(participant, "HelloWorldData_Msg");
37dds::pub::Publisher publisher(participant);
Create the writer for a specific topic
“ddsC++_helloworld_example”
in the default DDS domain.40dds::pub::DataWriter<HelloWorldData::Msg> writer(publisher, topic);
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
writer.publication_matched_status()
:40dds::pub::DataWriter<HelloWorldData::Msg> writer(publisher, topic);
At regular intervals, the status change and a matching publication is received. In between, the writing thread sleeps for for 20 milliseconds:
49while (writer.publication_matched_status().current_count() == 0) { 50 std::this_thread::sleep_for(std::chrono::milliseconds(20)); 51}
After this loop, a matching reader has been discovered.
To write the data instance, create and initialize the data:
54HelloWorldData::Msg msg(1, "Hello World");
Send the data instance of the keyed message.
58writer.write(msg);
After writing the data to the writer, the DDS C++ Hello World example checks if a matching subscriber(s) is still available. If matching subscribers exist, the example waits for 50ms and starts publishing the data again. If no matching subscriber is found, then the publisher program is ended:
78return EXIT_SUCCESS;
Through scoping, all the entities such as topic, writer, etc. are deleted automatically.
Note
To modify the DataWriter
Default Reliability Qos to Reliable:
60/* With a normal configuration (see dds::pub::qos::DataWriterQos
dds::pub::qos::DataReaderQos dwqos = topic.qos() << dds::core::policy::Reliability::Reliable();
dds::sub::DataWriter<HelloWorldData::Msg> dr(publisher, topic, dwqos);