5. Node.js API for Vortex DDS¶
The Node.js DCPS API provides users with Node.js classes to model DDS communication using JavaScript and pure DDS applications.
The Node.js DCPS API consists of one module.
vortexdds
This section provides an overview of the main DDS concepts and Node.js API examples for these DDS concepts.
Note
The Node.js DCPS API documentation can be found in the following directory:
$OSPL_HOME/docs/nodejs/html
5.1. API Usage Patterns¶
The typical usage pattern for the Node.js DCPS API for Vortex DDS is the following:
Model your DDS topics using IDL and generate Node.js topic classes from IDL.
AND/OR generate Node.js topic classes for topics that already exist in the DDS system.
Start writing your Node.js program using the Node.js API for Vortex DDS.
The core classes are Participant
, Topic
, Reader
and Writer
.
Publisher
and Subscriber
classes can be used to adjust the Quality of Service (QoS) defaults.
For details on setting QoS values with the API, see Quality of Service (QoS).
The following list shows the sequence in which you would use the Vortex classes:
Create a
Participant
instance.Create one or more
Topic
using the Participant instance.If you require publisher or subscriber level non-default QoS settings, create
Publisher
and/orSubscriber
using the Participant instance. (The most common reason for changing publisher/subscriber QoS is to define non-default partitions.)Create
Reader
and/orWriter
classes using theTopic
instances that you created.If you required data filtering, create a
QueryCondition
using the Reader instance.Create the core of program, writing and/or reading data and processing it.
5.1.1. Asynchronous Aspects of the API¶
Some DDS operations can take a long time. In order to not block the execution of other asynchronous JavaScript operation, the Vortex DDS API for NodeJS uses asynchronous operations the return standard JavaScript Promise objects.
In addition, at creation, some entities accept a ‘listener’ object, which defines one or more ‘callback’ methods, which are called asynchronously by the NodeJS engine when the appropriate event occurs. Entities supporting listeners are: Topic, Reader and Writer. See their factory methods for documentation on these listeners.
5.1.2. Releasing DDS resources¶
Many DDS objects have associated with them resources obtained from the DDS system. The NodeJS engine does not reclaim these resources, even if it garbage collects an object the represents such a resource.
In order to avoid leaking of DDS resources, you should take care to call the appropriate delete() method when you are finished with a DDS object. Once the delete() method is called on an object, it is no longer usable.
Many DDS objects are organized into a hierarchy. DDS objects created by factory methods on another DDS object are implicitly ‘owned’ by that ‘parent’ object. If a parent object is deleted, then all directly and indirectly owned objects are also deleted. The only DDS types that are not ‘owned’ by other objects are:
Participant, which sit at the top of the ownership hierarchy
Waitset instances.
GuardCondition instances.
QoSProvider instances.
To completely clean-up DDS resources, at a minimum, your program must explicitly delete all instances of the above types. Instances of other DDS objects may be explicitly deleted by your program once you no longer require them. Explicitly deleted such objects will reduce the footprint of your DDS application.
5.1.3. Exceptions¶
Most APIs will throw exceptions, rather than return ‘error codes’.
The most common exception thrown is DDSError, which represents an error within the DDS system. Frequently, additional information is written to the file dds-error.log. All methods that throw DDSError have this fact explicitly documented.
Most methods also type-check their arguments. If errors are found in these arguments, then a standard JavaScript TypeError is typically throw. The documentation does not explicitly document when TypeError is thrown.
5.1.4. Key API References¶
To import data types defined in an Vortex DDS compliant IDL file, see importIDL().
To connect to a DDS domain, create a Participant.
To register a DDS topic, use Participant.createTopic().
To create a DDS data reader, use Participant.createReader() or Subscriber.createReader().
To create a DDS data writer, use Participant.createWriter() or Subscriber.createWriter().
Import a externally defined quality-of-service (QoS) ‘profiles’, see QoSProvider.
To programmatically create or examine quality-of-service (QoS) policies on an entity, see QoS and Entity.qos.
To create a DDS ‘waitset’ that enables you to wait (asynchronously) for specific conditions, see Waitset.
5.2. Participant¶
The Node.js Participant
class represents a DDS domain participant entity.
In DDS - “A domain participant represents the local membership of the application in a domain. A domain is a distributed concept that links all the applications able to communicate with each other. It represents a communication plane: only the publishers and subscribers attached to the same domain may interact.”
A DDS domain participant, represents a connection by your program to a DDS Domain.
Typically, your application will create only one participant. All other DDS entities are created through a participant or one of its child entities via factory methods such as Participant.createPublisher()
.
Note
An explicit delete()
is required for the Participant
to release DDS resources. All entities owned directly or indirectly by the Participant
will be released on delete()
.
Example: Create new participant
const dds = require('vortexdds');
// create domain participant
const participant = new dds.Participant();
Example: Create new participant on the default domain with a QoS profile
const dds = require('vortexdds');
const path = require('path');
const QOS_XML_PATH = __dirname + path.sep + 'DDS_Get_Set_QoS.xml';
const QOS_PROFILE = 'DDS GetSetQosProfile';
const DOMAIN_ID = dds.DDS_DOMAIN_DEFAULT;
//...
async function setup(){
let participant = null;
let qp = null;
try {
// create a qos provider using qos xml file
qp = new dds.QoSProvider(QOS_XML_PATH, QOS_PROFILE);
// get participant qos from qos provider and create a participant
const pqos = qp.getParticipantQos();
participant = new dds.Participant(DOMAIN_ID, pqos);
//...
} finally {
console.log('=== Cleanup resources');
if (qp !== null){
qp.delete();
}
if (participant !== null){
participant.delete().catch((error) => {
console.log('Error cleaning up resources: '
+ error.message);
});
}
}
}
5.3. Topic¶
The Node.js Topic
class represents a DDS topic type.
A Topic
represents a globally named data type, along with quality-of-service policies. The Topic
is available to all participants in a DDS domain, and must be registered with the same data type definition and QoS policies.
You must create a Topic
before you can create Reader
or Writer
instances.
Class instances are created by the Participant.createTopic()
function.
createTopic(
topicName,
typeSupport,
qos = null,
listener = null
)
In order to create a Topic
, a TypeSupport
instance must be created from an IDL file.
Step 1 - Generate TypeSupport objects from IDL file
The function
importIDL(idlPath)
is provided to generate aTypeSupport
instance for every topic defined in an IDL file. This function returns a promise, therefore should be called from an async function.Step 2 - Create Topic instance using TypeSupport
The
createTopic
method in theParticipant
class can then be used to create a topic instance.
Example: Create a topic
const dds = require('vortexdds');
const path = require('path');
//...
async function publishData(){
console.log('=== HelloWorldPublisher start');
let participant = null;
try {
participant = new dds.Participant();
const topicName = 'HelloWorldData_Msg';
const idlName = 'HelloWorldData.idl';
const idlPath = __dirname + path.sep + idlName;
const typeSupports = await dds.importIDL(idlPath);
const typeSupport = typeSupports.get('HelloWorldData::Msg');
const tqos = dds.QoS.topicDefault();
tqos.durability = {kind: dds.DurabilityKind.Transient};
tqos.reliability = {kind: dds.ReliabilityKind.Reliable};
const topic = participant.createTopic(
topicName,
typeSupport,
tqos
);
//...
} finally {
console.log('=== Cleanup resources');
if (participant !== null){
participant.delete().catch((error) => {
console.log('Error cleaning up resources: '
+ error.message);
});
}
}
};
5.4. Publisher¶
The Node.js Publisher
class represents a DDS publisher entity.
A Publisher
typically owns one or more Writer instances created via the Publisher.createWriter()
method.
Class instances are created by the Participant.createPublisher()
method.
Use of the Publisher
class is optional. You typically create a Publisher
because you require quality-of-service parameters not available on the ‘default publisher’. Frequently, you wall want to specify a QoS.partition policy so that non-default partitions are used by the contained Writer instances.
Note
An explicit delete()
is not required for the Publisher
. When delete()
is called on the owning Participant
, the Publisher delete()
is triggered.
Example: Create a Publisher
//...
const pub = participant.createPublisher();
Create a publisher with a participant and QoS profile. Consider the code snippet below taken from example: GetSetQoSExample/GetSetQoSExample.js file.
//...
// get publisher qos from previously created qos provider: qp
const pubqos = qp.getPublisherQos();
// create a publisher
const publisher = participant.createPublisher(pubqos);
5.5. Writer¶
The Node.js Writer
class represents a DDS data writer entity.
A Writer
may be owned by either a Participant
or Publisher
. Using a Publisher
to create a Writer
allows the writer to benefit from quality-of-service policies assigned the the Publisher
, in particular the QoS.partition policy.
Class instances are created by the Participant.createWriter()
or Publisher.createWriter()
methods.
Note
An explicit delete()
is not required for the Writer
. When delete()
is called on the owning Participant
, the Writer delete()
is triggered.
Example: Create a Writer and write sample
const dds = require('vortexdds');
//...
async function writeData(publisher, topic){
const wqos = dds.QoS.writerDefault();
wqos.durability = {kind: dds.DurabilityKind.Transient};
wqos.reliability = {kind: dds.ReliabilityKind.Reliable};
const writer = publisher.createWriter(topic, wqos);
// send one message
const msg = {userID: 1, message: 'Hello World'};
await writer.writeReliable(msg);
//writer will be deleted on participant delete
}
5.6. Subscriber¶
The Node.js Subscriber
class represents a DDS subscriber entity.
A Subscriber
typically owns one or more Reader
instances created via the Subscriber.createReader()
method.
Class instances are created by the Participant.createSubscriber()
method.
You typically create a Subscriber
because you require quality-of-service parameters not available on the ‘default subscriber’. Frequently, you will want to specify a QoS.partition policy so that non-default partitions are used by the contained Reader
instances.
Note
An explicit delete()
is not required for the Subscriber
. When delete()
is called on the owning Participant
, the Subscriber delete()
is triggered.
Example: Create a Subscriber
//...
//create Subscriber
const sub = participant.createSubscriber();
Create a subscriber with participant and QoS profile.
//...
// get subscriber qos from previously created qos provider: qp
const subqos = qp.getSubscriberQos();
// create a subscriber
const subscriber = participant.createSubscriber(subqos);
5.7. Reader¶
The Node.js Reader
class represents a DDS data reader entity.
A Reader
may be owned by either a Participant
or Subscriber
. Using a Subscriber
to create a Reader
allows the reader to benefit from quality-of-service policies assigned the the Subscriber
, in particular the QoS.partition policy.
Class instances are created by the Participant.createReader()
or Subscriber.createReader()
methods.
Note
An explicit delete()
is not required for the Reader
. When delete()
is called on the owning Participant
, the Reader delete()
is triggered.
Example: Create a reader and take data*
const dds = require('vortexdds');
//...
function readData(subscriber, topic) {
const rqos = dds.QoS.readerDefault();
rqos.durability = {kind: dds.DurabilityKind.Transient};
rqos.reliability = {kind: dds.ReliabilityKind.Reliable};
const reader = subscriber.createReader(topic, rqos);
let takeArray = reader.take(10);
//...
}
5.7.1. QueryCondition¶
QueryCondition
class represents a condition on Reader
input based on samples, instances and view state AND on values found in the actual samples.
A query condition is attached to the Reader
entity and created using the Reader.createQueryCondition()
function.
Note
An explicit delete()
is not required for the QueryCondition
. When delete()
is called on the owning Reader
, the QueryCondition delete()
is triggered.
Example: Read using a QueryCondition
const dds = require('vortexdds');
//...
async function readBlueCircleWriteRedSquare(
circleReader,
squareWriter
) {
let queryCond = null;
let queryWaitset = null;
try {
// set up a waitset on our circle reader for the query condition
// (shape read = blue circle)
const mask = dds.StateMask.sample.not_read;
const sqlExpression = 'color=%0';
const params = ['BLUE'];
queryCond = circleReader.createQueryCondition(mask, sqlExpression, params);
queryWaitset = new dds.Waitset(queryCond);
for (let i = 0; i < 100; i++) {
await queryWaitset.wait(10000000000);
let sampleArray = circleReader.takeCond(1, queryCond);
if (sampleArray.length > 0 && sampleArray[0].info.valid_data) {
let sample = sampleArray[0].sample;
console.log(
util.format(
'%s %s of size %d at (%d,%d)',
sample.color,
'Circle',
sample.shapesize,
sample.x,
sample.y
)
);
squareWriter.write({
color: 'RED',
x: sample.x,
y: sample.y,
shapesize: 45,
});
}
}
} finally {
if (queryWaitset !== null){
queryWaitset.delete();
}
}
5.7.2. ReadCondition¶
ReadCondition
represents a condition on Reader
input based on samples, instances and view state.
ReadCondition
is used by the Reader to wait for the availablity of data based on a condition. A read condition is attached to the Reader
entity and created using the Reader.createReadCondition()
function.
Note
An explicit delete()
is not required for the ReadCondition
. When delete()
is called on the owning Reader
, the ReadCondition delete()
is triggered.
Example: Read data with read condition
const dds = require('vortexdds');
//...
function readCondition(reader) {
const maxSamples = 100;
const cond = reader.createReadCondition(dds.StateMask.sample.not_read);
const readArray = reader.readCond(maxSamples, cond);
//...
}
5.8. WaitSet¶
WaitSet
class represents a collection of Conditions upon which you can wait.
A WaitSet object allows an application to wait until one or more of the attached Condition objects evaluates to true or until the timeout expires.
Note
An explicit delete()
is required for the WaitSet
to release DDS resources.
Example Create a WaitSet with a ReadCondition
const dds = require('vortexdds');
//...
async function waitExample(reader) {
let newDataCondition = null;
let newDataWaitset = null;
try {
// create waitset for new data
newDataCondition = reader.createReadCondition(
dds.StateMask.sample.not_read
);
newDataWaitset = new dds.Waitset(newDataCondition);
await newDataWaitset.wait();
//...
} finally {
if (newDataWaitset !== null){
newDataWaitset.delete();
}
}
}
5.9. StatusCondition¶
StatusCondition
class represents a condition on an Entities ‘communication statuses’.
It is created using the createStatusCondition()
function on a dds entity instance.
Note
An explicit delete()
is not required for the StatusCondition
. When delete()
is called on the owning Entity
, the StatusCondition delete()
is triggered.
Example: Create a StatusCondition
const dds = require('vortexdds');
//...
const condition = squareWriter.createStatusCondition(
dds.StatusMask.publication_matched
);
//...
5.10. GuardCondition¶
GuardCondition
class is a user-triggerable condition for interrupting Waitsets
.
A GuardCondition
, when attached to a Waitset
, enables you to interrupt an asynchronous Waitset.wait()
operation by calling the GuardCondition.trigger()
method.
Note
An explicit delete()
is required for the GuardCondition
to release DDS resources.
Example
Create a guard condition and attach it to a waitset.
const dds = require('vortexdds');
const ws = new dds.Waitset();
const guard = new dds.GuardCondition();
ws.attach(guard);
// ws.attach(other conditions);
ws.wait()
.then(triggeredConds => {
for(const cond of triggeredConds) {
if(cond === guard) {
// guard was triggered
}
}
})
.catch(err => {
// wait set error, including timeout
});
// sometime later, cause the wait set to trigger.
guard.trigger();