Introduction to OpenDDS
Note:
This article has not been kept up to date for the most part. The
OpenDDS Developer's Guide
is kept up to date. Also there is a complete version of the example from this
article that has been kept up to date in the source code at
examples/DCPS/IntroductionToOpenDDS
.
Introduction
Distributed real-time applications are sometimes more data-centric than service-centric, meaning that the primary objective of the participants in the distributed system is the dissemination of application data rather than access to shared services. The set of suppliers and/or consumers of application data may not be known at design time, and may change through the execution lifetime of the application. Typically, the data-centric paradigm is most efficiently realized by a publish/subscribe communication model rather than a request/response model.
The OMG Data Distribution Service (DDS) for Real-Time Systems addresses the performance requirements and hard real-time requirements of distributed data-centric applications. DDS increases the range of publish/subscribe options available to developers of distributed real-time systems. For convenience, the DDS interfaces are defined using OMG Interface Definition Language (IDL). However, most details are left to the implementation, most significantly how data transfer occurs between the publishers and subscribers. A DDS implementer decides on the underlying communication mechanism that moves data from a publisher to a subscriber -- via TCP, UDP, UDP multicast, shared memory, etc. An implementation of the DDS specification is not required to use CORBA or the IIOP protocol to transfer data from a publisher to a subscriber.
OpenDDS is an open source, C++ implementation of the OMG Data Distribution
Service specification. OpenDDS includes a file-based configuration mechanism. Through a
configuration file, an OpenDDS user may configure a publisher's or
subscriber's transport(s), debugging output, memory allocation, the
location of the DCPSInfoRepo
broker process, and many other settings.
The complete set of configuration settings is described in the Configuration Chapter of the OpenDDS Developer's Guide.
In this article, we cover the following topics:
The OpenDDS Implementation of OMG DDS
OpenDDS leverages a pluggable transport architecture, enabling data delivery through transport and marshaling implementations of the application developer's choosing. Conceptually, the architecture borrows from TAO's Pluggable Protocols Framework. OpenDDS currently supports TCP and UDP point-to-point transports as well as unreliable and reliable multicast, and uses a high-performance marshaling implementation.
This pluggable transport architecture permits a DDS user to optimize a DDS installation based on the desired transport and the homogeneous or heterogeneous nature of the application's deployment. These choices can be made without affecting the application code itself.
Marshaling code is generated by a specialized OpenDDS IDL compiler. A
single, separate DCPS Information Repository
(DCPSInfoRepo
) process
acts as a central clearinghouse, associating publishers and subscribers.
Under the covers, OpenDDS uses CORBA to communicate with the
DCPSInfoRepo
process to associate publishers and subscribers.
Data transfer between publishers and subscribers is direct between the publishing and subscribing processes. OpenDDS
creates its own threads for the RB and for the non-CORBA I/O that
takes place when sending or receiving DDS data.
DDS Architecture
The OMG Data Distribution Service specification separates DDS into two separate architectural layers. The lower layer is the Data-Centric Publish and Subscribe (DCPS) layer, containing type-safe interfaces to a publish/subscribe communication mechanism. The upper layer is the Data Local Reconstruction Layer (DLRL), which enables an application developer to construct a local object model on top of the DCPS layer, shielding the application from DCPS knowledge. Each layer has its own set of concepts and usage patterns, and thus the concepts and terminology of the two layers can be discussed separately.
Data-Centric Publish and Subscribe - DCPS
The DCPS layer is responsible for efficiently disseminating data from publishers to interested subscribers. It is implemented using the concepts of publisher and data writer on the sending side and subscriber and data reader on the receiving side. The DCPS layer consists of one or more data domains, each of which contains a set of participants (publishers and subscribers) that communicate via DDS. Each entity (i.e. publisher or subscriber) belongs to a domain. Each process has one domain participant for each data domain of which it is a member.
Within any data domain, data is identified by a topic, which is a type-specific domain segment that allows publishers and subscribers to refer to data unambiguously. Within a domain, a topic associates a unique topic name, data type, and a set of Quality of Service (QoS) policies with the data itself. Each topic is associated with only one data type, although many different topics can publish the same data type. Behavior of publishers is determined by the QoS policies associated with the publisher, data writer, and topic elements for a particular data source. Likewise, behavior of subscribers is determined by the QoS policies associated with the subscriber, data reader, and topic elements for a particular data sink.
For more information on DCPS terminology, please see the OpenDDS Developer's Guide.
The DDS specification defines a number of Quality of Service (QoS) policies that applications use to specify their reliability, resource usage, fault tolerance, and other requirements to the service. Participants specify the behavior that they require from the service; the service decides how to achieve these behaviors. These policies may be applied to the various DCPS entities (Topic, Data Writer, Data Reader, Publisher, Subscriber, and Domain Participant) although not all policies are valid for all types of entities.
Subscribers and publishers collaborate to specify QoS policies through an offer-request paradigm. A publisher offers a set of QoS policies to all subscribers; a subscriber requests the set of QoS policies that it requires. The DDS implementation then attempts to match the requested policies with the offered policies. If the policies are consistent, then the publication and the subscription are matched.
OpenDDS supports the full set of DCPS Quality-of-Service (QoS) Policies, including:
QoS Policy | Description |
Liveliness |
Controls liveliness checks to make sure expected entities in the system are still alive |
Reliability |
Determines whether the service is allowed to drop samples |
History |
Controls what happens to an instance whose value changes before it is communicated to all Subscribers |
Resource Limits |
Controls resources that the
service can use to meet other QoS requirements |
Data-Local Reconstruction Layer - DLRL
The Data-Local Reconstruction Layer (DLRL) is an object-oriented layer on top of DCPS. A DLRL object is a native-language (i.e. C++) object with one or more shared attributes. Each DLRL class is mapped to one or more DCPS topics; each shared attribute value is mapped to a field in a topic's data type, and its value is distributed across the application via DCPS. A DLRL participant communicates data to the rest of the application by modifying a DLRL object, resulting in the the publication of a data sample on the associated topic. A DLRL shared attribute may be a simple value or structure, a reference to another DLRL object, or a collection (list, map) of those. DLRL supports complex object graphs and complex relationships between DLRL objects.
The developer is responsible for deciding how DCPS entities are mapped to DLRL objects. The model is specified in OMG Interface Definition Language (IDL) using IDL valuetypes. The mapping is conceptually similar to an object-relational database mapping, which maps an object model onto relational database tables. We think of each DCPS topic as analogous to a relational database table, and each sample as a row in that table. The DDS specification has a default mapping from DCPS to DLRL. Or, a developer can choose to specify his own custom mapping via an XML mapping file.
OpenDDS does not currently implement the DLRL.
OpenDDS Stock Quoter Example
Our example illustrates publication of and subscription to data samples though the DDS DCPS layer. The example contains two DCPS topics, both related to the stock market.
A stock quote publisher publishes stock quote samples to interested subscribers; each quote contains the ticker symbol of a security, its value, and a time stamp. Quotes are published periodically throughout the trading day, as buy and sell transactions affect the underlying value of the security. In addition, a stock exchange event publisher publishes important events relating to a stock exchange, namely when the exchange opens, closes, and when trading is suspended or resumed.
Our subscriber subscribes to both stock quotes and stock exchange events. The subscriber prints the ticker symbol and value of each quote it sees. When the subscriber receives an event indicating that the stock exchange has closed for the day, it gracefully shuts down. Thus, the receipt of a "closed" stock exchange event is the subscriber's signal to stop expecting stock quote samples.
We will demonstrate how to use the same publisher and subscriber code to communicate via the TCP and UDP transports. The transport configuration is isolated in a set of configuration files, allowing us to switch transports without making any code changes.
IDL Types
First, we define our published DDS data types in IDL:
We publish two data types: a Quote type for each
stock quote, and an ExchangeEvent type to indicate when
the stock exchange is opened, closed, and when trading is suspended or
resumed. The @topic
annotation marks a type for use with DDS. The
@key
annotation defined for each type is a unique identifier for each
instance of the data type. Our Quote type's key is the ticker
symbol of the stock.
Throughout the day, we would expect to publish many values, or samples,
for each ticker symbol. The set of published samples for each ticker
symbol belongs to the same instance. In our example, we'll publish two
ticker symbols, and thus two instances: SPY (S&P Depository
Receipts, i.e. the S&P 500) and MDY (S&P Midcap Depository
Receipts, i.e. the S&P Midcap 400).
Next, we compile the IDL with OpenDDS's opendds_idl
compiler to generate
type support code. The type support code consists of generated DCPS
data writer and data reader C++ classes and additional IDL code. DDS
uses type-safe interfaces for publication and subscription. Type-safe
interfaces have several advantages: first, programming errors are more
likely to be caught at compile time; second, generated marshaling code
can be made very efficient when the marshaled data type is known at
compile time; and third, we can avoid the use of inefficient types such as
the CORBA any
in data transfer.
The command to generate type support code for the Stock Quoter's IDL types is as follows:
$DDS_ROOT/bin/opendds_idl StockQuoter.idl
This command generates the following files:
StockQuoterTypeSupport.idl StockQuoterTypeSupportImpl.h StockQuoterTypeSupportImpl.cpp
However, we don't need to run the opendds_idl
compiler manually. Later,
we'll use a Make Project Creator (MPC) project to automate the build
steps for us.
Next, we use TAO's IDL compiler to compile all three IDL files -- the
StockQuoter.idl
file we wrote manually, plus the type support file generated
by opendds_idl
.
tao_idl -I$DDS_ROOT -I$TAO_ROOT/orbsvcs StockQuoter.idl tao_idl -I$DDS_ROOT -I$TAO_ROOT/orbsvcs StockQuoterTypeSupport.idl
Publisher
Next, we write a publisher to publish stock quotes and stock exchange
events via DDS. First, we include the two type support header files
generated by the opendds_idl
compiler.
We also include DCPS publisher, service participant, and QoS header files.
The following constants are used for our domain, type names and topic names. Each type is published on a separate topic. The subscriber must use the same values for its domain, type names and topic names.
When a stock exchange event - i.e. opened, closed, suspended, or resumed -- is published, we also publish the name of the stock exchange to which the event applies.
This is a simple helper method to get the current date and time.
The remainder of the publisher's source code file contains its main()
. We enter the publisher's main()
First, we create a domain participant. A DDS publisher may publish
on many independent domains, but our example only publishes on one
domain.
We use the TheDomainParticipantFactoryWithArgs
macro to
pass command-line arguments into DCPS and get the singleton domain
participant factory. We create one domain participant, for the "Quote"
domain, using the default Quality-of-Service policies for a domain
participant. The value of QUOTER_DOMAIN_ID
passed into the factory must be identical in the publisher and the subscriber.
Then, we create a publisher through the domain participant with default Quality-of-Service values. We can attach a PublisherListener
that is called by DCPS when certain publication-related events happen.
However, we don't care about those events, so we attach a nil listener.
There are three steps involved in publishing through DCPS. First, we register each type for the published data samples. Our example publishes samples of two IDL types, Quote and ExchangeEvent. Second, we create one or more topics upon which we publish. Each topic can only be bound to one type; thus we create a topic for each of our two types. Third, we create a data writer for each topic, and publish samples through the data writer.
We first register the IDL Quote type with the domain participant, passing an instance of the generated QuoteTypeSupportImpl
class for the Quote type. The name that we use for the Quote type, which is stored in the constant value QUOTER_QUOTE_TYPE
,
must match the name used on the subscriber. When we create a topic, we
specify this type name, enabling DCPS to later create the appropriate
type of data writer for the topic.
We then register the IDL ExchangeEvent type with the domain participant in the same way, using the generated ExchangeEventTypeSupportImpl
class. Our DCPS domain participant is able to publish on topics for Quote or ExchangeEvent types.
We create a topic for our Quote samples, indicating the topic name and the registered name of the Quote data type and using the default Quality-of-Service settings. Again, the Quote topic and type names must match on the publisher and the subscriber.
Similarly, we create a topic for our ExchangeEvent samples, indicating the topic name and the registered name of the ExchangeEvent type, and using the default Quality-of-Service settings. Again, the stock exchange event topic and type names must match on the publisher and on the subscriber.
We create two data writers, one for each topic. We pass in the topic created above; the topic knows its type. Each data writer is associated to exactly one publisher and publishes on one topic. Later, our publisher publishes on each topic by writing data samples to each data writer. The following code creates a data writer for the "Stock Quotes" topic.
We then create a data writer for the "Stock Exchange Event" topic. Again, we pass in the topic created above, and the topic knows its type.
We may choose to register each data instance. Registering each data instance will slightly improve latency while writing samples of that instance.
A publisher may publish many data samples on each data instance. A
data instance is identified by a unique key. For the Quote type, we
identified ticker
as the key field in its IDL type definition. Each Quote data sample
with the same key value is considered part of the same data instance.
In other words, each Quote sample published on the ticker symbol "SPY"
is part of the same instance.
We have two Quote instances, for tickers symbols "SPY" (S&P Depository Receipts, i.e. the S&P 500) and "MDY" (S&P Midcap Depository Receipts, i.e. the S&P Midcap 400), and one ExchangeEvent instance, for the "Test Stock Exchange". We register each instance with the appropriate data writer.
Finally, we publish. First, we publish a TRADING_OPENED
event on the "Stock Exchange Event" topic.
Then, we publish several stock quote data samples for the "SPY" and "MDY" instances on the "Stock Quote" topic. We simply loop, increasing the quoted value for each ticker symbol a bit each time to simulate active trading on a really good day.
Last, we publish a TRADING_CLOSED
event on the "Stock Exchange Event" topic to indicate that the stock exchange is closed for the day.
Finally, we clean up after ourselves before exiting.
This completes the C++ code for the publisher.
Subscriber
Our subscriber subscribes to the stock quotes and stock exchange
events, receiving data samples from the publisher. We use the
publisher's TRADING_CLOSED
event to indicate that trading has finished for the day, triggering the subscriber's graceful shutdown.
Much of the code in the subscriber is similar to that in the publisher. We get a domain participant, register types, etc. in the same way we did in the publisher. The main difference is that the subscriber is passive, waiting to receive samples, while the publisher is active. The subscriber uses listener objects to receive samples from the publisher.
First, we include the two type support header files generated by the dcps_ts.pl
script. We also included these files in the publisher. However, we also
include two listener header files, one for each published type. A
listener is called by DDS when a data sample is published on the
associated topic.
We also include DCPS subscriber, service participant, and QoS header files.
The following constants are used for our domain, type names and topic names. These must match the domain, type names, and topic names used by the publisher.
The remainder of the subscriber's source code file contains its main()
. We enter the subscriber's main()
.
We create a domain participant, just as we did in the publisher. The specified domain matches the publisher's domain.
Then, we create a subscriber through the domain participant with default Quality-of-Service policy values. We can attach a SubscriberListener
that is called by DCPS when certain subscription-related events happen.
However, we don't care about those events, so we attach a nil. This is
almost the same as what we did in the publisher when we called create_publisher
.
As in the publisher, we must register the IDL Quote and ExchangeEvent types with the domain participant to subscribe to topics on those types.
As in the publisher, we create a topic for our stock quotes, indicating the topic name and the registered name of the Quote type and using the default Quality-of-Service settings. Again, the stock quote topic name must match on the publisher and the subscriber.
Similarly, we create a topic for our ExchangeEvent samples, indicating the topic name and the registered name of the ExchangeEvent type, and using the default Quality-of-Service settings. Again, the stock exchange event topic name must match on the publisher and the subscriber.
On the publisher, we created two data writers, one for each topic. On the subscriber, we'll create two data readers, one for each topic. Each data reader has exactly one subscriber and subscribes to one topic. We also attach a listener to each data reader to receive notification of published data samples. This is where the publisher and subscriber code diverges.
The following code creates a listener for the "Stock Quotes" topic.
The listener is a local CORBA object, implementing the DDS::DataReaderListener
IDL interface. We use OpenDDS's convenient servant_to_reference
function template to obtain a reference of the interface type.
We create a second listener for the "Stock Exchange Event" topic.
Finally, we create a data reader for each of the two topics. First, we create a data reader for the "Stock Quotes" topic, attaching the relevant listener we created above.
Then, we create a data reader for the "Stock Exchange Events" topic, attaching the other listener we created above.
OpenDDS spawns it own threads to handle incoming events from the
publisher. Thus, there is no event loop code in the subscriber.
However, we must be sure not to allow the main thread to exit before
we're ready to shut down the entire subscriber process. So, we loop,
processing stock quotes and stock exchange events until the TRADING_CLOSED
event is received on the "Stock Exchange Events" topic. Essentially, we
want to receive published data samples until the stock exchange tells
us that it has closed. The sleep
call causes us to check this once per second to avoid consuming too much of the CPU.
When we have received the TRADING_CLOSED
event, we gracefully exit the loop.
Finally, we clean up after ourselves before exiting.
Subscriber's "Stock Quote" and "Stock Exchange Event" Listeners
The "Stock Quote" data reader and the "Stock Exchange Event" data reader each has a listener attached. These listeners are called by the DDS framework each time a data sample is received from a publisher. We have decided that each of the two data readers shall have its own listener, although we could use a single listener for both data readers if we code the listener to handle both data types.
Each listener implements the DDS::DataReaderListener
IDL interface. We used both a QuoteDataReaderListenerImpl
and an ExchangeEventDataReaderListenerImpl
in the subscriber code above, but we have not yet defined those classes. We will do that now.
First, we write a listener for the Quote type's data reader. This listener class implements the DDS::DataReaderListener
IDL interface, overriding seven pure virtual methods from the
interface. It is a CORBA local object implementation
of an IDL interface, inheriting from the generated class for
the IDL interface.
The listener class must override all seven methods, including
methods for which the listener's implementation is empty. However, for
simplicity, we'll show only the on_data_available
method, which is called when a new Quote data sample is available. The
other six methods have empty implementations. We'll also use a default
constructor and destructor.
We first narrow the value of the data reader parameter to the appropriate type for a Quote sample.
Then, we take the next Quote sample from the data reader. Note the type safety of the QuoteDataReader interface.
Once we have received the Quote sample, we simply print out its contents.
The Quote sample's memory is cleaned up by the stack when it goes out of scope.
We have not shown implementations of the other methods in the DDS::DataReaderListener
interface, but we must override them as well, even if their implementations are empty.
Next, we write a listener for the ExchangeEvent type's data reader. The basic structure is the same as that of the QuoteDataReaderListenerImpl
.
We add the is_exchange_closed_received
method to the data reader so the subscriber's main program can find out when the TRADING_CLOSED
stock exchange event has been received. This method checks a boolean
value under the protection of a mutex lock. The boolean value is set by
the listener's on_data_available
method when a TRADING_CLOSED
stock exchange event is received.
DDS calls on_data_available
on the listener for each received ExchangeEvent sample.
As in the QuoteDataReaderListenerImpl
, we first narrow the value of the data reader parameter to the appropriate type, which in this case is an ExchangeEventDataReader
.
Then, we take the next ExchangeEvent sample from the data reader. Note the type safety.
Once we have received the ExchangeEvent sample, we simply print out its contents.
When we receive a TRADING_CLOSED
event, we set a flag indicating that the stock exchange has been closed for the day.
The ExchangeEvent sample is cleaned up by the stack when it goes out of scope.
We have added two private class attributes to keep track of the TRADING_CLOSED
event and protect that value with a lock.
This completes the C++ code for the subscriber.
Building the Publisher and Subscriber
We use MPC, the Make Project Creator, to generate build files for the publisher and subscriber. MPC provides a simple syntax and is capable of generating build files for GNU Make, Visual C++, and many other build systems. For more information on MPC, please see OCI's MPC page at http://www.objectcomputing.com/products/mpc.
We create two files to build our Stock Quoter, a workspace file and a
project file. Our workspace file simply tells MPC where to find the MPC
dcps
and dcpsexe
base project files that we'll use later.
Next, we create an MPC file containing three projects - a Common
project containing IDL and TypeSupport files, a Publisher, an a
Subscriber. Each of the three projects inherits from either the dcps
or dcpsexe
base project, which are located in $DDS_ROOT
. First, we create a library called StockQuoterCommon to hold the code generated by the TAO IDL and opendds_idl
compilers.
A dcps
project has a new section, TypeSupport_Files
. This section executes the opendds_idl
script to generate TypeSupport files from our DDS data types. Here, we
indicate the IDL file that contains our DDS data types., and also
indicate the TypeSupport files generated from it.
Our IDL_Files
section contains our original IDL files plus the TypeSupport IDL files generated by the previous section.
The Header_Files
and Source_Files
sections contain the opendds_idl
-generated
TypeSupport implementation files. MPC automatically adds the generated
IDL stubs and skeletons, so we don't need to add those manually.
Our publisher uses the StockQuoterCommon library from above, and adds a publisher.cpp
source file containing the publisher's main()
.
Our subscriber also uses the StockQuoterCommon library, adds a subscriber.cpp
source file containing the subscriber's main()
, and its two listeners.
We use this MPC file to generate build files for our build system. For example, to generate GNU Makefiles, we execute
$ACE_ROOT/bin/mwc.pl -type gnuace StockQuoter.mwc
To generate Visual C++ 7.1 solution files, we execute
perl %ACE_ROOT%/bin/mwc.pl -type vc71 StockQuoter.mwc
We then build the project.
Configuring the Stock Quoter
OpenDDS includes a file-based configuration mechanism. With
it, an OpenDDS user may configure a publisher's or subscriber's
transport, the location of the DCPSInfoRepo
process, and many other settings.
The syntax of the configuration file is similar to the syntax of a Windows INI
file. It contains several sections, which in turn contain property-like entries. The basic syntax is as follows:
The complete set of configuration settings is described in the Configuration Chapter of the OpenDDS Developer's Guide.
Our TCP-based example uses one configuration file, dds_tcp_conf.ini
, for both the publisher and the subscriber:
Please note that there are three sections, [common]
, [config/config1]
, and [transport/tcp1]
. The [common]
section contains configuration values that apply to the entire process;
in this configuration file, we specify a debug level, an object
reference for the DCPSInfoRepo
process, and a global transport
configuration. Here, our DCPSInfoRepo
process is listening on the loopback (127.0.0.1) interface, which means
we have configured it to only be available to DDS processes running on
the same host. To make it available across the network, use an IP
address or a network hostname instead of localhost
.
We have specified config1
as our global transport configuration, meaning
it that the transport configuration with that name is used by all readers and
writers in our process that do not explicitly specify another transport configuration.
The [config/config1]
section defines a transport configuration
with the name config1
. The transports
option specifies
tcp1
as the only transport instance included in this configuration.
The [transport/tcp1]
section defines a transport instance named
tcp1
and specifies its transport type as tcp
.
This section can also be used to configure the transport with a number of
configuration options as described in the OpenDDS documentation.
Running the Stock Quoter over a TCP Transport
To run the example, we must start a DCPSInfoRepo
process, and start at least one publisher and one subscriber.
To start the DCPSInfoRepo
, we use the following command line:
$DDS_ROOT/bin/DCPSInfoRepo -ORBListenEndpoints iiop://localhost:12345
Our DCPSInfoRepo
process listens on port 12345. That port matches the port that we specified in the DCPSInfoRepo
object reference in the transport configuration file above. This DCPSInfoRepo
process is listening on the loopback (127.0.0.1) interface, which means
we have configured it to only be available to DDS processes running on
the same host. Again, to make it available across the network, use an
IP address or a network hostname instead of localhost
.
We run two subscribers and one publisher:
subscriber -DCPSConfigFile dds_tcp_conf.ini subscriber -DCPSConfigFile dds_tcp_conf.ini publisher -DCPSConfigFile dds_tcp_conf.ini
We use the -DCPSConfigFile
command-line argument to
indicate the name of the configuration file we created above. Note that
each subscriber and publisher uses the same transport configuration
file.
The above command lines are used to run the DCPSInfoRepo, publisher and
subscriber with built-in-topic on which is the default case. We can
also run these processes with the built-in-topic off. The -NOBITS
is used by DCPSInfoRepo to turn off built-in-topic and the "-DCPSBit 0"
is used by other DDS applications. The command lines are as follows:
$DDS_ROOT/bin/DCPSInfoRepo -NOBITS -ORBListenEndpoints iiop://localhost:12345 subscriber -DCPSBit 0 -DCPSConfigFile dds_tcp_conf.ini subscriber -DCPSBit 0 -DCPSConfigFile dds_tcp_conf.ini publisher -DCPSBit 0 -DCPSConfigFile dds_tcp_conf.ini
The publisher publishes 20 stock quotes for each the SPY and MDY ticker symbols, and each subscriber receives them. When the publisher is finished, it publishes a "TRADING_CLOSED" message, which causes the subscribers to exit.
Running the Stock Quoter over a UDP Transport
We can use the same code base to run the example over a UDP transport by simply running with a configuration file that defines a global transport configuration that specifies a UDP transport instance.
Here is the dds_udp_conf.ini
file:
We then start the DCPSInfoRepo
process as before:
$DDS_ROOT/bin/DCPSInfoRepo -ORBListenEndpoints iiop://localhost:12345
We start the two subscribers and the publisher, using our new transport configuration files:
subscriber -DCPSConfigFile dds_udp_conf.ini subscriber -DCPSConfigFile dds_udp_conf.ini publisher -DCPSConfigFile dds_udp_conf.ini
We can also run each process with the built-in-topic off. The command-lines are as follows:
$DDS_ROOT/bin/DCPSInfoRepo -NOBITS -ORBListenEndpoints iiop://localhost:12345 subscriber -DCPSBit 0 -DCPSConfigFile dds_udp_conf.ini subscriber -DCPSBit 0 -DCPSConfigFile dds_udp_conf.ini publisher -DCPSBit 0 -DCPSConfigFile dds_udp_conf.ini
As before, the publisher publishes 20 stock quotes for each the SPY and MDY ticker symbols, and each subscriber receives them. When the publisher is finished, it again publishes a "TRADING_CLOSED" message, which causes the subscribers to exit. The only difference is that we have substituted a UDP transport for a TCP transport; the change in transport required no code changes.
Summary
The OMG Data Distribution Service (DDS) for Real-Time Systems is a specification for a high-performance, type-safe, publish-and-subscribe communication middleware. DDS addresses data-centric applications, i.e. those for which dissemination of application data is a significant requirement.
OpenDDS is an open source implementation of the OMG Data Distribution Service specification, providing users with an efficient publish-and-subscribe framework with the advantages of the open-source software development model.
OpenDDS includes a
file-based configuration mechanism. Through a configuration file, an OpenDDS user may configure a publisher's or subscriber's transport(s),
debugging output, memory allocation, the location of the DCPSInfoRepo
broker process, and many other settings. We have shown in our example
that an OpenDDS application's underlying transport can be swapped out
without making any code changes.
References
- Sample Code is located in the OpenDDS source code distribution at examples/DCPS/IntroductionToOpenDDS
- OMG Data Distribution Service (DDS) for Real-Time Systems (https://www.omg.org/spec/DDS/)
- OMG DDS Portal (https://www.omgwiki.org/dds)
- OpenDDS Home Page (http://www.opendds.org)
- TAO Developer's Guide Home Page (https://theaceorb.com/purchase.html
- OpenDDS Developer's Guide (https://opendds.readthedocs.io/en/latest-release/)
- MPC (https://github.com/objectcomputing/MPC)