6. Reading Data (Input)
6.1. Getting the input
To read/take samples, first get a reference to the Input()
:
input = connector.getInput('MySubscriber::MySquareReader')
Connector.getInput()
returns an Input()
object. This example,
obtains the Input defined by the data_reader
named MySquareReader within
the <subscriber>
named MySubscriber:
<subscriber name="MySubscriber">
<data_reader name="MySquareReader" topic_ref="Square" />
</subscriber>
This <subscriber>
is defined inside the <domain_participant>
selected to
create this connector
(see Creating a new Connector).
6.2. Reading or taking the data
Call Input.take()
to access and remove the samples:
input.take()
or Input.read()
to access the samples but leave them available for
a future read()
or take()
:
input.read()
The method Input.wait()
can be used to identify when there is new data
available on a specific Input()
. It returns a Promise
that will be
resolved when new data is available, or rejected if the supplied timeout
expires.
You can wait for the Promise
using await
in an async function:
await input.wait()
input.take()
Or using the then
method:
input.wait()
.then(() => {
input.take()
})
The method Connector.wait()
has the same behavior as Input.wait()
,
but the returned promise will be resolved when data is available on any of the
Input()
objects within the Connector()
:
await connector.wait()
You can also install a listener in a Connector()
. Connector()
inherits from
EventEmitter.
If a listener for the on_data_available
event is attached to a
Connector()
, this event will be emitted whenever new data is available
on any Input defined within the :class:`Connector()
.
// Store all the inputs in an array
const inputs = [
connector.getInput('MySubscriber::MySquareReader'),
connector.getInput('MySubscriber::MyCircleReader'),
connector.getInput('MySubscriber::MyTriangleReader')
]
// Install the listener
connector.on('on_data_available', () => {
// One or more inputs have data
inputs.forEach(input => {
input.take()
for (const sample of input.samples.validDataIter) {
// Access the data
}
}
})
For more information on how to use the event-based notification, refer to the documentation of the events module.
The web_socket example shows how to use this event.
Warning
There are additional threading concerns to take into account when using the
on_data_available
event. Refer to
Additional considerations when using event-based functionality
for more information.
Note
When using the event-based methods to be notified of available data, errors
are propagated using the error
event.
See Error Handling for more information.
6.3. Accessing the data samples
After calling Input.read()
or Input.take()
,
Input.samples
contains the data samples:
for (const sample of input.samples) {
if (sample.validData) {
console.log(JSON.stringify(sample.getJson()))
}
}
SampleIterator.getJson()
retrieves all the fields of a sample.
If you don’t need to access the meta-data (see Accessing sample meta-data),
the simplest way to access the data is to use Samples.validDataIter
to skip samples with invalid data:
for (const sample of input.samples.validDataIter) {
// It is not necessary to check the sample.validData field
console.log(JSON.stringify(sample.getJson()))
}
It is also possible to access an individual sample:
// Obtain the first sample in the Input's queue
const theSample = input.samples.get(0)
if (theSample.validData) {
console.log(JSON.stringify(theSample.getJson()))
}
Both of the iterables shown above also provide iterator implementations,
allowing them to be incremented outside of a for
loop:
const iterator = input.samples.validDataIter.iterator()
let sample = iterator.next()
// sample.value contains contains the current sample and sample.done is a
// boolean value which will become true when we have iterated over all of
// the available samples
console.log(JSON.stringify(sample.value.getJson()))
Warning
All the methods described in this section return generators. Calling read/take again invalidates all generators currently in use.
Samples.getJson()
can receive a fieldName
to only return the fields
of a complex member. In addition to getJson
, you can get the values of
specific primitive fields using SampleIterator.getNumber()
,
SampleIterator.getBoolean()
and SampleIterator.getString()
,
for example:
for (const sample of input.samples.validDataIter) {
const x = sample.getNumber('x')
const y = sample.getNumber('y')
const size = sample.getNumber('shapesize')
const color = sample.getString('color')
}
See more information and examples in Accessing the data.
6.4. Accessing sample meta-data
Every sample contains an associated SampleInfo with meta-information about the sample:
for (const sample of input.samples) {
const sourceTimestamp = sample.info.get('source_timestamp')
}
See SampleIterator.info
for the list of available meta-data fields.
Connext DDS can produce samples with invalid data, which contain meta-data only. For more information about this, see Valid Data Flag in the RTI Connext DDS Core Libraries User’s Manual. These samples indicate a change in the instance state. Samples with invalid data still provide the following information:
The
SampleInfo()
When an instance is disposed (
sample.info.get('instance_state')
is'NOT_ALIVE_DISPOSED'
), the sample data contains the value of the key that has been disposed. You can access the key fields only. See Accessing key values of disposed samples.
6.5. Matching with a publication
The method Input.waitForPublications()
can be used to detect when a
compatible DDS publication is matched or unmatched. It returns a promise that
resolves to the change in the number of matched publications since the last
time it was called:
// From within an async function. Otherwise, use the .then() syntax
let changeInMatches = await input.waitForPublications()
For example, if 1 new compatible publication is discovered within the specified
timeout
, the promise will resolve to 1; if a previously matching
publication no longer matches, it resolves to -1.
You can obtain information about the existing matched publications through the
Input.matchedPublications
property:
input.matchedPublications.forEach((match) => {
pubName = match.name
}
6.6. Class reference: Input, Samples, SampleIterator
6.6.1. Input class
- class Input(connector, name)
Allows reading data for a DDS Topic.
This class is used to subscribe to a specific DDS Topic.
To get an Input object, use
Connector.getInput()
.- Attributes:
connector (
Connector()
) - The Connector creates this Input.name (string) - The name of the Input (the name used in
Connector.getInput()
).native (pointer) - A native handle that allows accessing additional Connext DDS APIs in C.
matchedPublications (JSON) - A JSON object containing information about all the publications currently matched with this Input.
- Input.matchedPublications
type: JSON
Returns information about matched publications.
This property returns a JSON array, with each element of the array containing information about a matched publication.
Currently the only information contained in this JSON object is the publication name of the matched publication. If the matched publication doesn’t have a name, the name for that specific publication will be null.
Note that
Connector()
Outputs are automatically assigned a name from thedata_writer name
element in the XML configuration.
- Input.samples
type: Samples
Allows iterating over the samples returned by this input.
This container provides iterators to access the data samples retrieved by the most-recent call to
Input.take()
andInput.read()
.
- Input.read()
Accesses the samples received by this Input.
This operation performs the same operation as
Input.take()
but the samples remain accessible (in the internal queue) after the operation has been called.
- Input.take()
Accesses the samples receieved by this Input.
After calling this method, the samples are accessible using
Input.samples()
.
- Input.wait(timeout)
Wait for this Input to receive data.
Note
This operation is asynchronous.
- Arguments:
timeout (number) – The maximum time to wait, in milliseconds. By default, infinite.
- Throws:
TimeoutError –
TimeoutError()
will be thrown if the timeout expires before data is received.- Returns:
Promise – A
Promise
which will be resolved once data is available, or rejected if the timeout expires.
- Input.waitForPublications(timeout)
Waits for this Input to match or unmatch a compatible DDS Subscription.
Note
This operation is asynchronous.
This method waits for the specified timeout (or if no timeout is specified, it waits forever), for a match (or unmatch) to occur.
- Arguments:
timeout (number) – The maximum time to wait, in milliseconds. By default, infinite.
- Throws:
TimeoutError –
TimeoutError()
will be thrown if the timeout expires before any publications are matched.- Returns:
Promise – Promise object resolving with the change in the current number of matched outputs. If this is a positive number, the input has matched with new publishers. If it is negative, the input has unmatched from an output. It is possible for multiple matches and/or unmatches to be returned (e.g., 0 could be returned, indicating that the input matched the same number of outputs as it unmatched).
6.6.2. Samples class
- class Samples(input)
Provides access to the data samples read by an
Input()
.This class provides access to data samples read by an
Input()
(using either theInput.read()
orInput.take()
methods).This class implements a
[Symbol.iterator]()
method, making it an iterable. This allows it to be used infor... of
loops, to iterate through available samples:for (const sample of input.samples) { console.log(JSON.stringify(sample.getJson())) }
The method
Samples.get()
returns aSampleIterator()
which can also be used to access available samples:const sample = input.samples.get(0) console.log(JSON.stringify(sample.getJson()))
The samples returned by these methods may only contain meta-data (see
SampleIterator.info
). TheSamples.validDataIter
iterable only iterates over samples that contain valid data (aValidSampleIterator()
).Samples()
andValidSampleIterator()
both also provide generators to the samples, allowing applications to define their own iterables (seeSamples.iterator()
andValidSampleIterator.iterator()
).Samples
is the type of the propertyInput.samples()
.For more information and examples, see Accessing the data samples.
- Attributes:
length (number) - The number of samples available since the last time
Input.read()
orInput.take()
was called.validDataIter (
ValidSampleIterator()
) - The class used to iterate through the available samples that have valid data.
- Samples.length
The number of samples available.
- Samples.validDataIter
Returns an iterator to the data samples that contain valid data.
The iterator provides access to all the data samples retrieved by the most recent call to
Input.read()
orInput.take()
, and skips samples with invalid data (meta-data only).By using this iterator, it is not necessary to check if each sample contains valid data.
- Samples.get(index)
Returns an iterator to the data samples, starting at the index specified.
The iterator provides access to all the data samples retrieved by the most recent call to
Input.read()
orInput.take()
.This iterator may return samples with invalid data (samples that only contain meta-data). Use
Samples.validDataIter
to avoid having to checkSampleIterator.validData
.- Arguments:
index (number) – The index of the sample from which the iteration should begin. By default, the iterator begins with the first sample.
- Returns:
SampleIterator()
- An iterator to the samples (which implements both iterable and iterator logic).
- Samples.getBoolean(index, fieldName)
Obtains the value of a boolean field within this sample.
See Accessing the data samples.
- Arguments:
index (number) – The index of the sample.
fieldName (string) – The name of the field.
- Returns:
number – The obtained value.
- Samples.getJson(index, memberName)
Gets a JSON object with the values of all the fields of this sample.
- Arguments:
index (number) – The index of the sample.
memberName (string) – The name of the complex member. The type of the member with name memberName must be an array, sequence, struct, value or union.
- Returns:
JSON – The obtained JSON object. See Accessing the data samples.
- Samples.getNative(index)
Obtains a native handle to the sample, which can be used to access additional Connext DDS APIs in C.
- Arguments:
index (number) – The index of the sample for which to obtain the native pointer.
- Returns:
pointer – A native pointer to the sample.
- Samples.getNumber(index, fieldName)
Obtains the value of a numeric field within this sample.
See Accessing the data samples.
- Arguments:
index (number) – The index of the sample.
fieldName (string) – The name of the field.
- Returns:
number – The obtained value.
- Samples.getString(index, fieldName)
Obtains the value of a string field within this sample.
See Accessing the data samples.
- Arguments:
index (number) – The index of the sample.
fieldName (string) – The name of the field.
- Returns:
string – The obtained value.
- Samples.getValue(fieldName)
Gets the value of a field within this sample.
See Accessing the data samples.
This API can be used to obtain strings, numbers, booleans and the JSON representation of complex members.
- Arguments:
fieldName (string) – The name of the field.
- Returns:
number|string|boolean|JSON – The value of the field.
- Samples.iterator()
The iterator generator (used by the iterable).
This method returns a generator, which must be incremented manually by the application (using the iterator.next() method).
Once incremented, the data can be accessed via the
.value
attribute. Once no more samples are available, the.done
attribute will be true.Using this method, it is possible to create your own iterable:
const iterator = input.samples.iterator() const singleSample = iterator.next().value
6.6.3. SampleIterator class
- class SampleIterator(input, index)
Iterates and provides access to a data sample.
A SampleIterator provides access to the data receieved by an
Input()
.The
Input.samples
attribute implements aSampleIterator()
, meaning it can be iterated over. An individual sample can be accessed usingInput.samples.get()
.This class provides both an iterator and iterable, and is used internally by the
Samples()
class. The following options to iterate over the samples exist:// option 1 - The iterable can be used in for...of loops for (const sample of input.samples) // option 2 - Returns an individual sample at the given index const individualSample = input.samples.get(0) // option 3 - Returns a generator which must be incremented by the application const iterator = input.samples.iterator()
- Arguments:
validData (boolean) – Whether or not the current sample contains valid data.
infos (SampleInfo) – The meta-data associated with the current sample.
native (pointer) – A native handle that allows accessing additional Connext DDS APIs in C.
- SampleIterator.info
Provides access to this sample’s meta-data.
The
info
property expects one of theSampleInfo()
field names:const value = sample.info.get('field')
The supported field names are:
'source_timestamp'
returns an integer representing nanoseconds'reception_timestamp'
returns an integer representing nanoseconds'sample_identity'
or'identity'
returns a JSON object (seeOutput.write()
)'related_sample_identity'
returns a JSON object (seeOutput.write()
)'valid_data'
returns a boolean (equivalent toSampleIterator.validData
)'view_state'
, returns a string (either “NEW” or “NOT_NEW”)'instance_state'
, returns a string (one of “ALIVE”, “NOT_ALIVE_DISPOSED” or “NOT_ALIVE_NO_WRITERS”)'sample_state'
, returns a string (either “READ” or “NOT_READ”)
These fields are documented in The SampleInfo Structure section in the RTI Connext DDS Core Libraries User’s Manual.
See
SampleInfo()
.
- SampleIterator.validData
type: boolean
Whether or not this sample contains valid data.
If
false
, the methods to obtain values of the samples (e.g.,SampleIterator.getNumber()
,SampleIterator.getBoolean()
,SampleIterator.getJson()
,SampleIterator.getString()
) should not be called. To avoid this restraint, use aValidSampleIterator()
.
- SampleIterator.get(fieldName)
Gets the value of a field within this sample.
This API can be used to obtain strings, numbers, booleans and the JSON representation of complex members.
- Arguments:
fieldName (string) – The name of the field.
- Returns:
number|string|boolean|JSON – The value of the field.
- SampleIterator.getBoolean(fieldName)
Gets the value of a boolean field in this sample.
- Arguments:
fieldName (string) – The name of the field.
- Returns:
boolean – The boolean value of the field.
- SampleIterator.getJson(memberName)
Returns a JSON object with the values of all the fields of this sample.
See Accessing the data samples.
- Arguments:
memberName (string) – The name of the complex member or field to obtain.
- Returns:
JSON – The obtained JSON object.
- SampleIterator.getNumber(fieldName)
Gets the value of a numeric field in this sample.
Note
This operation should not be used with values with an aboslute value larger than Number.MAX_SAFE_INTEGER. See Accessing 64-bit integers for more information.
- Arguments:
fieldName (string) – The name of the field.
- Returns:
number – The numeric value of the field.
- SampleIterator.getString(fieldName)
Gets the value of a string field in this sample.
- Arguments:
fieldName (string) – The name of the field.
- Returns:
string – The string value of the field.
6.6.4. ValidSampleIterator class
- class ValidSampleIterator()
Iterates and provides access to data samples with valid data.
This iterator provides the same methods as
SampleIterator()
. It can be obtained usingInput.samples.validDataIter
.- ValidSampleIterator.iterator()
The iterator generator (used by the iterable).
Using this method, it is possible to create your own iterable:
const iterator = input.samples.validDataIter.iterator() const singleSample = iterator.next().value
6.6.5. SampleInfo class
- class SampleInfo(input, index)
The type returned by the property
SampleIterator.info()
.This class provides a way to access the SampleInfo of a received data sample.
- SampleInfo.get(fieldName)
Type-independent function to obtain any value from the SampleInfo structure.
The supported fieldNames are:
'source_timestamp'
returns an integer representing nanoseconds'reception_timestamp'
returns an integer representing nanoseconds'sample_identity'
or'identity'
returns a JSON object (seeOutput.write()
)'related_sample_identity'
returns a JSON object (seeOutput.write()
)'valid_data'
returns a boolean (equivalent toSampleIterator.validData
)
These fields are documented in The SampleInfo Structure section in the RTI Connext DDS Core Libraries User’s Manual.
- Arguments:
fieldName (string) – The name of the
SampleInfo
field to obtain
- Returns:
The obtained value from the
SampleInfo
structure
Examples:
const source_timestamp = input.samples.get(0).info.get('source_timestamp')