Accessing DDS Data Samples with Read or Take

To access the DDS data samples that Connext DDS has received for a DataReader, you must invoke the read() or take() methods. These methods return a list (sequence) of DDS data samples and additional information about the DDS samples in a corresponding list (sequence) of SampleInfo structures. The contents of SampleInfo are described in The SampleInfo Structure.

Calling read(), take(), or one of their variations resets the DATA_AVAILABLE status.

The way Connext DDS builds the collection of DDS samples depends on QoS policies set on the DataReader and Subscriber, the source_timestamp of the DDS samples, and the sample_states, view_states, and instance_states parameters passed to the read/take operation.

In read() and take(), you may enter parameters so that Connext DDS selectively returns DDS data samples currently stored in the DataReader’s receive queue. You may want Connext DDS to return all of the data in a single list or only a subset of the available DDS samples as configured using the sample_states, view_states, and instance_states masks. The SampleInfo Structure describes how these masks are used to determine which DDS data samples should be returned.

Read vs. Take

The difference between read() and take() is how Connext DDS treats the data that is returned. With take(), Connext DDS will remove the data from the DataReader’s receive queue. The data returned by Connext DDS is no longer stored by Connext DDS. With read(), Connext DDS will continue to store the data in the DataReader’s receive queue. The same data may be read again until it is taken in subsequent take() calls. Note that the data stored in the DataReader’s receive queue may be overwritten, even if it has not been read, depending on the setting of the HISTORY QosPolicy.

The read() and take() operations are non-blocking calls, so that they may return no data (DDS_RETCODE_NO_DATA) if the receive queue is empty or has no data that matches the criteria specified by the StateMasks.

The read_w_condition() and take_w_condition() operations take a ReadCondition as a parameter instead of DDS sample, view or instance states. The only DDS samples returned will be those for which the ReadCondition is TRUE. These operations, in conjunction with ReadConditions and a WaitSet, allow you to perform ‘waiting reads.’ For more information, see ReadConditions and QueryConditions.

As you will see, read and take have the same parameters:

DDS_ReturnCode_t read( FooSeq &received_data_seq,
			DDS_SampleInfoSeq &info_seq,
			DDS_Long max_samples,
			DDS_SampleStateMask sample_states,
			DDS_ViewStateMask view_states,
			DDS_InstanceStateMask instance_states);
 
DDS_ReturnCode_t take(	FooSeq &received_data_seq,
			DDS_SampleInfoSeq &info_seq,
			DDS_Long max_samples,
			DDS_SampleStateMask sample_states,
			DDS_ViewStateMask view_states,
			DDS_InstanceStateMask instance_states);

Note: These operations may loan internal Connext DDS memory, which must be returned with return_loan(). See Loaning and Returning Data and SampleInfo Sequences.

Both operations return an ordered collection of DDS data samples (in the received_data_seq parameter) and information about each DDS sample (in the info_seq parameter). Exactly how they are ordered depends on the setting of the PRESENTATION QosPolicy and the DESTINATION_ORDER QosPolicy. For more details please see the API Reference HTML documentation for read() and take().

In read() and take(), you can use the sample_states, view_states, and instance_states parameters to specify properties that are used to select the actual DDS samples that are returned by those methods. With different combinations of these three parameters, you can direct Connext DDS to return all DDS data samples, DDS data samples that you have not accessed before, the DDS data samples of instances that you have not seen before, DDS data samples of instances that have been disposed, etc. The possible values for the different states are described both in the API Reference HTML documentation and in The SampleInfo Structure.

Read and Take Operations lists the variations of the read() and take() operations.

Read and Take Operations

Read Operations

Take Operations

Modern C++1For the Modern C++, only the read() operation is shown; the take() variant is parallel.

Description

Reference

read

take

reader.read()

or

reader.select()

.state(...)

.read()

Reads/takes a collection of DDS data samples from the DataReader.

Can be used for both keyed and non-keyed data types.

Accessing DDS Data Samples with Read or Take

read_instance

take_instance

reader.select()

.instance(...)

.read()

Identical to read() and take(), but all returned DDS samples belong to a single instance, which you specify as a parameter.

Can only be used with keyed data types.

read_instance and take_instance

read_instance_
w_condition

take_instance_
w_condition

reader.select()

.instance()

.condition(...)

.read()

Identical to read_instance() and take_instance(), but all returned DDS samples belong to the single specified instance and satisfy the specified ReadCondition.

read_instance_w_condition and take_instance_w_condition

read_next_instance

take_next_instance

reader.select().next_instance(...).read()

Similar to read_instance() and take_instance(), but the actual instance is not directly specified as a parameter. Instead, the DDS samples will all belong to instance ordered after the instance that is specified by the previous_handle parameter.

read_next_instance and take_next_instance

read_next_instance_
w_condition

take_next_instance_
w_condition

reader.select()

.next_instance(...)

.condition(...)

.read()

Accesses a collection of DDS data samples of the next instance that match a specific set of ReadConditions, from the DataReader.

read_next_instance_w_condition and take_next_instance_w_condition

read_next_sample

take_next_sample

reader.select()

.state(DataState::not_read())

Provides a convenient way to access the next DDS DDS sample in the receive queue that has not been accessed before.

read_next_sample and take_next_sample

read_w_condition

take_w_condition

reader.select()

.condition(...)

Accesses a collection of DDS data samples from the DataReader that match specific ReadCondition criteria.

read_w_condition and take_w_condition

General Patterns for Accessing Data

Once the DDS data samples are available to the data readers, the DDS samples can be read or taken by the application. The basic rule is that the application may do this in any order it wishes. This approach is very flexible and allows the application ultimate control.

To access data coherently, or in order, the PRESENTATION QosPolicy must be set properly.

read_next_sample and take_next_sample

The read_next_sample() or take_next_sample() operation is used to retrieve the next DDS sample that hasn’t already been accessed. It is a simple way to 'read' DDS samples and frees your application from managing sequences and specifying DDS sample, instance or view states. It behaves the same as calling read() or take() with max_samples = 1, sample_states = NOT_READ, view_states = ANY_VIEW_STATE, and instance_states = ANY_INSTANCE_STATE.

DDS_ReturnCode_t read_next_sample(
	Foo & received_data, DDS_SampleInfo & sample_info);
DDS_ReturnCode_t take_next_sample(
	Foo & received_data, DDS_SampleInfo & sample_info);

It copies the next, not-previously-accessed data value from the DataReader. It also copies the DDS sample’s corresponding DDS_SampleInfo structure.

If there is no unread data in the DataReader, the operation will return DDS_RETCODE_NO_DATA and nothing is copied.

Since this operation copies both the DDS data sample and the SampleInfo into user-provided storage, it does not allocate nor loan memory. You do not have to call return_loan() after this operation.

Note: If the received_data parameter references a structure that contains a sequence and that sequence has not been initialized, the operation will return DDS_RETCODE_ERROR.

read_instance and take_instance

The read_instance() and take_instance() operations are identical to read() and take(), but they are used to access DDS samples for just a specific instance (key value). The parameters are the same, except you must also supply an instance handle. These functions can only be used when the DataReader is tied to a keyed type, see DDS Samples, Instances, and Keys for more about keyed data types.

These operations may return BAD_PARAMETER if the instance handle does not correspond to an existing data-object known to the DataReader.

The handle to a particular data instance could have been cached from a previous read() operation (value taken from the SampleInfo struct) or created by using the DataReader’s lookup_instance() operation.

DDS_ReturnCode_t read_instance(
	FooSeq &received_data,
	DDS_SampleInfoSeq &info_seq,
	DDS_Long max_samples, 
	const DDS_InstanceHandle_t &a_handle,
	DDS_SampleStateMask sample_states,
	DDS_ViewStateMask view_states,
	DDS_InstanceStateMask instance_states); 

Note: This operation may loan internal Connext DDS memory, which must be returned with return_loan(). See Loaning and Returning Data and SampleInfo Sequences.

read_next_instance and take_next_instance

The read_next_instance() and take_next_instance() operations are similar to read_instance() and take_instance() in that they return DDS samples for a specific data instance (key value). The difference is that instead of passing the handle of the data instance for which you want DDS data samples, instead you pass the handle to a ‘previous’ instance. The returned DDS samples will all belong to the 'next' instance, where the ordering of instances is explained below.

DDS_ReturnCode_t read_next_instance(
	FooSeq &received_data, 
	DDS_Long max_samples,
	const DDS_InstanceHandle_t &previous_handle
	DDS_SampleStateMask sample_states,
	DDS_ViewStateMask view_states,
	DDS_InstanceStateMask instance_states) 

Connext DDS orders all instances relative to each other.2The ordering of the instances is specific to each implementation of the DDS standard; to maximize the portability of your code, do not assume any particular order. In the case of Connext DDS (and likely other DDS implementations as well), the order is not likely to be meaningful to you as a developer; it is simply important that some ordering exists. This ordering depends on the value of the key as defined for the data type associated with the Topic. For the purposes of this discussion, it is 'as if' each instance handle is represented by a unique integer and thus different instance handles can be ordered by their value.

This operation will return values for the next instance handle that has DDS data samples stored in the receive queue (that meet the criteria specified by the StateMasks). The next instance handle will be ordered after the previous_handle that is passed in as a parameter.

The special value DDS_HANDLE_NIL can be passed in as the previous_handle. Doing so, you will receive values for the “smallest” instance handle that has DDS data samples stored in the receive queue that you have not yet accessed.

You can call the read_next_instance() operation with a previous_handle that does not correspond to an instance currently managed by the DataReader. For example, you could use this approach to iterate though all the instances, take all the DDS samples with a NOT_ALIVE_NO_WRITERS instance_state, return the loans (at which point the instance information may be removed, and thus the handle becomes invalid), and then try to read the next instance.

The example below shows how to use take_next_instance() iteratively to process all the data received for an instance, one instance at a time. We always pass in DDS_HANDLE_NIL as the value of previous_handle. Each time through the loop, we will receive DDS samples for a different instance, since the previous time through the loop, all of the DDS samples of the previous instance were returned (and thus accessed).

FooSeq received_data;3In the C API, you must use the FooSeq_initialize() and DDS_SampleInfoSeq_initialize() operations or the
macro DDS_SEQUENCE_INITIALIZER to initialize the FooSeq and DDS_SampleInfoSeq to be empty. For example,
 DDS_SampleInfoSeq infoSeq;
DDS_SampleInfoSeq_initialize(&infoSeq);
or
FooSeq fooSeq = DDS_SEQUENCE_INITIALIZER;
DDS_SampleInfoSeq info_seq;
while (retcode = reader->take_next_instance(received_data, info_seq, 
			DDS_LENGTH_UNLIMITED, DDS_HANDLE_NIL,
			DDS_ANY_SAMPLE_STATE, DDS_ANY_VIEW_STATE, 
			DDS_ANY_INSTANCE_STATE)
			!= DDS_RETCODE_NO_DATA) {
	// the data samples returned in received_data will all
	// be for a single instance
	// process the data 
	// now return the loaned sequences 
	if (reader->return_loan(received_data, info_seq) 
	!= DDS_RETCODE_OK) { 
		// handle error 
	}
} 

Note: This operation may loan internal Connext DDS memory, which must be returned with return_loan(). See Loaning and Returning Data and SampleInfo Sequences.

read_w_condition and take_w_condition

The read_w_condition() and take_w_condition() operations are identical to read() and take(), but instead of passing in the sample_states, view_states, and instance_states mask parameters directly, you pass in a ReadCondition (which specifies these masks).

DDS_ReturnCode_t read_w_condition (
		FooSeq &received_data,
		DDS_SampleInfoSeq &info_seq,
		DDS_Long max_samples,
		DDSReadCondition *condition)

Note: This operation may loan internal Connext DDS memory, which must be returned with return_loan(). See Loaning and Returning Data and SampleInfo Sequences.

read_instance_w_condition and take_instance_w_condition

The read_instance_w_condition() and take_instance_w_condition() operations are similar to read_instance() and take_instance(), respectively, except that the returned DDS samples must also satisfy a specified ReadCondition.

DDS_ReturnCode_t read_instance_w_condition(
		FooSeq & received_data,
		DDS_SampleInfoSeq & info_seq,
		DDS_Long max_samples,
		const DDS_InstanceHandle_t & a_handle,
		DDSReadCondition * condition);

The behavior of read_instance_w_condition() and take_instance_w_condition() follows the same rules as read() and take() regarding pre-conditions and post-conditions for the received_data and sample_info parameters.

These functions can only be used when the DataReader is tied to a keyed type, see DDS Samples, Instances, and Keys for more about keyed data types.

Similar to read(), these operations must be provided on the specialized class that is generated for the particular application data-type that is being accessed.

Note: These operations may loan internal Connext DDS memory, which must be returned with return_loan(). See Loaning and Returning Data and SampleInfo Sequences.

read_next_instance_w_condition and take_next_instance_w_condition

The read_next_instance_w_condition() and take_next_instance_w_condition() operations are identical to read_next_instance() and take_next_instance(), but instead of passing in the sample_states, view_states, and instance_states mask parameters directly, you pass in a ReadCondition (which specifies these masks).

DDS_ReturnCode_t read_next_instance_w_condition (
		FooSeq &received_data,
DDS_SampleInfoSeq &info_seq, DDS_Long max_samples, const DDS_InstanceHandle_t &previous_handle, DDSReadCondition *condition)

Note: This operation may loan internal Connext DDS memory, which must be returned with return_loan(). See Loaning and Returning Data and SampleInfo Sequences.

The select() API (Modern C++)

The Modern C++ API combines all the previous ways to read data into a single operation: reader.select().This call is followed by one or more calls to functions that configure the query and always ends in a call to read() or take(). These are the functions that configure a select():

Function

Description

Default

max_samples()

Specifies the maximum number of samples to read or take in this call

Up to the value specified in max_samples_per_read

instance()

Specifies an instance to read or take

All instances

next_instance()

Indicates that read or take should return samples for the instance that follows the one being passed (Note: both next_instance() and instance() can't be specified at the same time)

All instances

state()

Specifies the sample state, view state and instance state

All samples

content()

Specifies a query on the data values to read

All samples

condition()

Specifies a condition (see read_w_condition()). If condition() is specified state() and content()cannot be specified.

When running a query more than once on the same DataReader, it is more efficient to create a QueryCondition and pass it to condition() rather than using content().

All samples

To read or take using the default options, simply call reader.read() or reader.take() with no arguments.

The following example shows how to call select():

dds::sub::LoanedSamples<Foo> samples =
    reader.select()
        .max_samples(20)
        .state(dds::sub::status::DataState::new_instance())
        .content(dds::sub::Query(reader, "x > 10"))
        .instance(my_instance_handle)
        .take();

© 2018 RTI