4.4. Receiving Data¶
This section discusses how to create, configure, and use Subscribers and DataReaders to receive data. It describes how these objects interact, as well as the types of operations that are available for them.
The goal of this section is to help you become familiar with the Entities you need for receiving data. For up-to-date details such as formal parameters and return codes on any mentioned operations, please see the C API Reference and C++ API Reference documentation.
4.4.1. Preview: Steps to Receiving Data¶
There are three ways to receive data:
- Your application can explicitly check for new data by calling a DataReader’s read() or take() operation. This method is also known as polling for data.
- Your application can be notified asynchronously whenever new DDS data samples arrive—this is done with a Listener on either the Subscriber or the DataReader. RTI Connext DDS Micro will invoke the Listener’s callback routine when there is new data. Within the callback routine, user code can access the data by calling read() or take() on the DataReader. This method is the way for your application to receive data with the least amount of latency.
- Your application can wait for new data by using Conditions and a WaitSet, then calling wait(). Connext DDS Micro will block your application’s thread until the criteria (such as the arrival of DDS samples, or a specific status) set in the Condition becomes true. Then your application resumes and can access the data with read() or take().
The DataReader’s read() operation gives your application a copy of the data and leaves the data in the DataReader’s receive queue. The DataReader’s take() operation removes data from the receive queue before giving it to your application.
To prepare to receive data, create and configure the required Entities:
- Create a DomainParticipant.
- Register user data types with the DomainParticipant. For example, the ‘FooDataType’.
- Use the DomainParticipant to create a Topic with the registered data type.
- Use the DomainParticipant to create a Subscriber.
- Use the Subscriber or DomainParticipant to create a DataReader for the Topic.
- Use a type-safe method to cast the generic DataReader created by the Subscriber to a type-specific DataReader. For example, ‘FooDataReader’.
Then use one of the following mechanisms to receive data.
- To receive DDS data samples by polling for new data:
- Using a FooDataReader, use the read() or take() operations to access the DDS data samples that have been received and stored for the DataReader. These operations can be invoked at any time, even if the receive queue is empty.
- To receive DDS data samples asynchronously:
- Install a Listener on the DataReader or Subscriber that will be called back by an internal Connext DDS Micro thread when new DDS data samples arrive for the DataReader.
- Create a DDSDataReaderListener for the FooDataReader or a DDSSubscriberListener for Subscriber. In C++ you must derive your own Listener class from those base classes. In C, you must create the individual functions and store them in a structure.
If you created a DDSDataReaderListener with the on_data_available() callback enabled: on_data_available() will be called when new data arrives for that DataReader.
If you created a DDSSubscriberListener with the on_data_on_readers() callback enabled: on_data_on_readers() will be called when data arrives for any DataReader created by the Subscriber.
Install the Listener on either the FooDataReader or Subscriber.
For the DataReader, the Listener should be installed to handle changes in the DATA_AVAILABLE status.
For the Subscriber, the Listener should be installed to handle changes in the DATA_ON_READERS status.
Only 1 Listener will be called back when new data arrives for a DataReader.
Connext DDS Micro will call the Subscriber’s Listener if it is installed. Otherwise, the DataReader’s Listener is called if it is installed. That is, the on_data_on_readers() operation takes precedence over the on_data_available() operation.
If neither Listeners are installed or neither Listeners are enabled to handle their respective statuses, then Connext DDS Micro will not call any user functions when new data arrives for the DataReader.
In the on_data_available() method of the DDSDataReaderListener, invoke read() or take() on the FooDataReader to access the data.
If the on_data_on_readers() method of the DDSSubscriberListener is called, the code can invoke read() or take() directly on the Subscriber’s DataReaders that have received new data. Alternatively, the code can invoke the Subscriber’s notify_datareaders() operation. This will in turn call the on_data_available() methods of the DataReaderListeners (if installed and enabled) for each of the DataReaders that have received new DDS data samples.
To wait (block) until DDS data samples arrive:
- Use the DataReader to create a StatusCondition that describes the DDS samples for which you want to wait. For example, you can specify that you want to wait for never-before-seen DDS samples from DataReaders that are still considered to be ‘alive.’
- Create a WaitSet.
- Attach the StatusCondition to the WaitSet.
- Call the WaitSet’s wait() operation, specifying how long you are willing to wait for the desired DDS samples. When wait() returns, it will indicate that it timed out, or that the attached Condition become true (and therefore the desired DDS samples are available).
- Using a FooDataReader, use the read() or take() operations to access the DDS data samples that have been received and stored for the DataReader.
An application that intends to subscribe to information needs the following Entities: DomainParticipant, Topic, Subscriber, and DataReader. All Entities have a corresponding specialized Listener and a set of QosPolicies. The Listener is how RTI Connext DDS Micro notifies your application of status changes relevant to the Entity. The QosPolicies allow your application to configure the behavior and resources of the Entity.
- The DomainParticipant defines the DDS domain on which the information will be available.
- The Topic defines the name of the data to be subscribed, as well as the type (format) of the data itself.
- The DataReader is the Entity used by the application to subscribe to updated values of the data. The DataReader is bound at creation time to a Topic, thus specifying the named and typed data stream to which it is subscribed. The application uses the DataWriter’s read() or take() operation to access DDS data samples received for the Topic.
- The Subscriber manages the activities of several DataReader entities. The application receives data using a DataReader that belongs to a Subscriber. However, the Subscriber will determine when the data received from applications is actually available for access through the DataReader. Depending on the settings of various QosPolicies of the Subscriber and DataReader, data may be buffered until DDS data samples for associated DataReaders are also received. By default, the data is available to the application as soon as it is received.
For more information on creating and deleting Subscribers, as well as setting QosPolicies, see the Subscribers section in the RTI Connext DDS Core Libraries User’s Manual (available here if you have Internet access).
To create a DataReader, you need a DomainParticipant, a Topic, and a Subscriber. You need at least one DataReader for each Topic whose DDS data samples you want to receive.
4.4.4. Using DataReaders to Access Data (Read & Take)¶
For user applications to access the data received for a DataReader, they must use the type-specific derived class or set of functions in the C API Reference. Thus for a user data type ‘Foo’, you must use methods of the FooDataReader class. The type-specific class or functions are automatically generated if you use RTI Code Generator.