Integrating RTI Connext with ENSCO IData
Learn how RTI Connext can be used in conjunction with ENSCO IData.
Introduction
RTI Connext, based on the Data Distribution Service (DDS™) standard, is a peer-to-peer connectivity framework that fits the requirements of high-performance distributed simulation environments. With Connext, you can fine-tune data streams for your network and trade off latency plus throughput — all through XML configuration — to allow you to get the necessary performance and scalability without changing your source code. In this example, we show how Connext can feed real-time data into an ENSCO IData® project for real-time cockpit display.
The following sections show coding examples of step-by-step instructions on how RTI Connext Professional can be used in conjunction with ENSCO IData 4.0. The example builds on a simple IData workspace with an indicator for roll and pitch.
This example is based on Windows but can be modified for other platforms. To follow along with the example, you need RTI Connext, ENSCO IData and Visual Studio installed.
ENSCO IData 4.0 now supports 64-bit Windows applications, so this example uses the x64Win64VS2017 target libraries for RTI Connext. If you are using an older version of IData, you need to use the 32-bit i86Win32VS2017 libraries instead.
-
Download RTI Connext
If you do not already have RTI Connext installed, download a free trial of Connext. Your download will include the libraries that are required to run the example plus the software tools you can use to visualize and debug your distributed system.
-
Request IData Evaluation License
If you do not already have IData installed, you can request a download and a 30 day evaluation license by sending an email to IData@ENSCO.com. The evaluation license includes access to the IData and IDataMap Integrated Development Environment (IDE).
-
ENSCO IData and RTI Connext Example Instructions
The example is based on a simple pitch/roll indicator IData workspace which you can get from ENSCO.
Here are the steps to integrate Connext with IData:
- Create the data definition to which the IData workspace will subscribe to
- Add the required libraries and dependencies to the Make file
- Add Connext support to the C file
-
Creating the Data Model
First, let’s define the data type that the IData application will subscribe to. This example is pretty simple, and uses the ADI Template from the IData Template library. There are only two variables to control for the ADI: Pitch and Roll. Both variables are of type float.
Since this is a simple data type, we can directly configure it in IDL. For more complex applications, RTI System Designer could be used, or you can use types from an existing data model in which the application will be integrated.
For this example, we create a file called Orientation_data.idl with the following definition:
module Connext {
struct Orientation
{
float Pitch_deg;
float Roll_deg;
};
};Once we have the file created, we can generate the required Connext support files as follows:
rtiddsgen -language C -ppDisable Orientation_Data.idl
This is automatically done for you by the makefile in this example.
Since IData uses the C programming language, the generated support files need to be for C. For this example, we used the Connext x64Win64VS2017 target libraries.If you have the Visual Studio compiler in your path, you don’t need the –ppDisable option. However, since the IDL file does not have any pre-processor definitions, running the pre-processor is not needed. If you have a more complex data definition, you want to make sure that a pre-processor is in the path, and then remove the –ppDisable option.
-
Adding Connext to the Build Environment
The IData workspace contains a makefile. In the example IDataRTI.mak, the following needs to be added to the makefile:
- Additional object files (the generated Connext support files)
- The required Connext libraries and dependent Windows libraries.
- The Connext directories (included with your license)
- The Connext library path where the Connext libraries are installed
The generated files need to be added to the OBJECTFILES list. For this example, the files Orientation_Data.obj, Orientation_DataPlugin.obj, and Orientation_DataSupport.obj need to be added.
In addition to the Connext libraries, you need to add some Windows libraries to the LIBRARYFILES list. Those libraries are: nddscored.lib, nddscd.lib, netapi32.lib, advapi32.lib , user32.lib, and WS2_32.lib.
$(NDDSHOME)/include and $(NDDSHOME)/include/ndds also need to be added to the INCLUDEPATHS list.
Finally, the location of the Connext libraries need to be added to the library path (LIBPATHS = /LIBPATH:$(NDDSHOME)\lib\x64Win64VS2017\)
The resulting make file. Additions for RTI Connext are in bold:
# ****************************************************************************
# TARGET SETTINGS
# These settings specify the makefile filename, the target .DLL filename,
# the source modules to compile and the extra libraries to include in the
# link. This section should be changed as necessary.
# ****************************************************************************
MAKEFILE = IDataRTI.mak
TARGET = IDataRTI.dll
# $begin IDATA_TEMPLATE_OBJFILES_BLOCK
OBJECTFILES = \
IDataRTI.obj \
gen/Orientation_Data.obj \
gen/Orientation_DataPlugin.obj \
gen/Orientation_DataSupport.obj
# $end IDATA_TEMPLATE_OBJFILES_BLOCK
# $begin IDATA_TEMPLATE_LIBFILES_BLOCK
LIBRARYFILES = \
nddscored.lib \
nddscd.lib \
netapi32.lib \
advapi32.lib \
user32.lib \
WS2_32.lib \
# $end IDATA_TEMPLATE_LIBFILES_BLOCK
# $begin IDATA_TEMPLATE_INCLUDEPATH_BLOCK
INCLUDEPATHS = \
-I$(IDATA_SDK) \
-I./gen \
-I$(NDDSHOME)/include \
-I$(NDDSHOME)/include/ndds
# $end IDATA_TEMPLATE_INCLUDEPATH_BLOCK
# $begin IDATA_TEMPLATE_LIBPATH_BLOCK
LIBPATHS = /LIBPATH:$(NDDSHOME)\lib\x64Win64VS2017\
# $end IDATA_TEMPLATE_LIBPATH_BLOCK
# ****************************************************************************
# COMPILER/LINKER SETTINGS
# Release mode is the default; to build debug, use this command:
# nmake -f filename.mak cfg=debug
# ****************************************************************************
!IF "$(CFG)" == "debug" || "$(CFG)" == "DEBUG"
!MESSAGE ************************
!MESSAGE Building debug version
!MESSAGE ************************
COMPILERFLAGS = -DIDATAOS_WIN -DPLUGIN_EXPORTS /ZI /Od /MDd
LINKERFLAGS = /DLL /DEBUG /PDB:"IDataRTI.pdb" /pdbtype:sept /MACHINE:X64
!ELSE
!MESSAGE ************************
!MESSAGE Building release version
!MESSAGE ************************
COMPILERFLAGS = -DIDATAOS_WIN -DPLUGIN_EXPORTS /Ox /MD
LINKERFLAGS = /DLL /MACHINE:X64
!ENDIF
-
Adding Connext Support to the Main C File
To start, make sure that the variables to update the display are available in the C file. Open the IData Workspace IDataRTI.IDW_XML. This will load the IData Workspace containing all the IData files needed:
- IDataRTI.IDWL_XML IData Workspace
- IDataRTI.IDL_XML IData Layout File
- IDataRTI.mak Makefile
- IDataRTI.c C File
IDataRTI.COL_XML IData Color Table
Double click the IDataRTI.IDL_XML on the left that contains the UI definition in the IData Modeler and select “Edit variable list”, which in the EDIT | Edit Variable List menu.
Select the variables you want to control through Connext. In this example, the variables are ROLL and PITCH. Click on Export and select the C file to into which you will export the data definition.
This will add the definitions to the IDataRTI.c file as shown below.
Now that we have the variables needed to update available, we can add the Connext code.
There are two function in the C file which are of interest. GetDataPointers (IData initialization function) is where the Connext entities are being created, and DriveData is periodically called. This is where samples will be received and processed to update the displayed information.
The following needs to be added to the C file:
- Additional include files
- Global variables. The reader as it is initialized in the GetDataPointers function but will be used in DriveData to take samples
- Create the Connext instances in GetDataPointers. The steps are:
- Create domain participant
- Create Subscriber
- Register the type
- Create the topic
- Create the reader and cast it to a type specific reader
- Add processing the samples to DriveData
For the example, the following header files need to be included in IDataRTI.c. Code added for RTI Connext is in bold:
#include "IDataOS.h"
#include "IData.h"
/* RTI Connext Includes */
#include "ndds/ndds_c.h"
#include "Orientation_Data.h"
#include "Orientation_DataSupport.h"Define the data reader as a global variable:
/**********************************************************************************/
/* $begin IDATA_TEMPLATE_INCLUDE_BLOCK */
/* $end IDATA_TEMPLATE_INCLUDE_BLOCK */
/**********************************************************************************/
/* DDS Reader */
static Connext_OrientationDataReader *orientation_reader = NULL;Create the Connext entities:
PLUGIN_API void GetDataPointers( void* pInstrument )
{
lastEvent = -1;
MyInstrument = pInstrument;
/* DDS variables */
DDS_DomainParticipant *participant = NULL;
DDS_Subscriber *subscriber = NULL;
DDS_Topic *topic = NULL;
DDS_DataReader *reader = NULL;
DDS_ReturnCode_t retcode;
const char *type_name = NULL;
int domainId = 0; /* Default domain ID */
/* Create Domain Participant with default configuration */
participant = DDS_DomainParticipantFactory_create_participant(
DDS_TheParticipantFactory, domainId, &DDS_PARTICIPANT_QOS_DEFAULT,
NULL /* no listener */, DDS_STATUS_MASK_NONE);
if (participant == NULL)
{
return;
}
/* Create Subscriber with default configuration */
subscriber = DDS_DomainParticipant_create_subscriber(
participant, &DDS_SUBSCRIBER_QOS_DEFAULT, NULL /* no listener */,
DDS_STATUS_MASK_NONE);
if (subscriber == NULL)
{
return;
}
/* Register the type before creating the topic */
type_name = Connext_OrientationTypeSupport_get_type_name();
retcode = Connext_OrientationTypeSupport_register_type(participant, type_name);
if (retcode != DDS_RETCODE_OK)
{
return;
}
/* Create the Topic. The topic name in this example is Orientation */
topic = DDS_DomainParticipant_create_topic(
participant, "Orientation",
type_name, &DDS_TOPIC_QOS_DEFAULT, NULL /* listener */,
DDS_STATUS_MASK_NONE);
if (topic == NULL)
{
return;
}
/* Create reader with default configuration */
reader = DDS_Subscriber_create_datareader(
subscriber, DDS_Topic_as_topicdescription(topic),
&DDS_DATAREADER_QOS_DEFAULT, NULL, DDS_STATUS_MASK_NONE);
if (reader == NULL)
{
return;
}
/* typecast the reader to a typed reader for orientation data */
orientation_reader = Connext_OrientationDataReader_narrow(reader);
if (orientation_reader == NULL)
{
return;
}
/**********************************************************************************/
/* $begin IDATA_TEMPLATE_INITIALIZATION_BLOCK */
pPITCH = ( float* )IDR_CreateDataPointer( pInstrument, "PITCH", IDATA_TYPE_FLOAT, 1, 0 );
pROLL = ( float* )IDR_CreateDataPointer( pInstrument, "ROLL", IDATA_TYPE_FLOAT, 1, 0 );
/* $end IDATA_TEMPLATE_INITIALIZATION_BLOCK */
/**********************************************************************************/
}DriveData contains the receiving and processing of the data. DriveData is periodically called, and each time we check if there is data to process. In this example we use the take call, which will “take” all samples from the DDS receive queue and process them. We don’t need historical data in this example.
PLUGIN_API void DriveData( void* pInstrument )
{
/* Local variables */
int eventCount;
int i;
/* Connect variables */
struct Connext_OrientationSeq data_seq = DDS_SEQUENCE_INITIALIZER;
struct DDS_SampleInfoSeq info_seq = DDS_SEQUENCE_INITIALIZER;
DDS_ReturnCode_t retcode;
struct Connext_Orientation* data = NULL;
/* Get the event list and process it */
eventCount = IDT_GetEvents( events, &lastEvent );
for ( i = 0; i < eventCount; i++ )
{
/*********************************************************************************/
/* $begin IDATA_TEMPLATE_EVENT_BLOCK */
/* $end IDATA_TEMPLATE_EVENT_BLOCK */
/*********************************************************************************/
}
/* Take all available samples */
retcode = Connext_OrientationDataReader_take(
orientation_reader,
&data_seq, &info_seq, DDS_LENGTH_UNLIMITED,
DDS_ANY_SAMPLE_STATE, DDS_ANY_VIEW_STATE, DDS_ANY_INSTANCE_STATE);
/* If there is no data available we can ignore it. */
if (retcode == DDS_RETCODE_NO_DATA) {
//ignore
} else if (retcode != DDS_RETCODE_OK) {
// error.
}
/* Process each received sample. Normally we should have one sample at
* the most as the display update rate is most likely faster than the
* rate samples are sent
*/
for (i = 0; i < Connext_OrientationSeq_get_length(&data_seq); ++i) {
/* Make sure we have valid data */
if (DDS_SampleInfoSeq_get_reference(&info_seq, i)->valid_data) {
/* get a pointer to the data and set the internal pitch and roll variable */
data = Connext_OrientationSeq_get_reference(&data_seq, i);
*pPITCH = data->Pitch_deg;
*pROLL = data->Roll_deg;
}
}
/**********************************************************************************/
/* $begin IDATA_TEMPLATE_UPDATE_BLOCK */
/* $end IDATA_TEMPLATE_UPDATE_BLOCK */
/*********************************************************************************/
}
-
Testing the Connectivity
The RTI Connext code generator rtiddsgen has an option to generate sample code (-example). You can use this to generate a sample publisher and subscriber. The sample publisher can be used to feed data into the IData project. To do this, make sure the topic name is in the sample subscriber. The example uses the topic name “Orientation”; however, the generated example code sets the topic name to “Example Connext_Orientation”. Look for the call to DDS_DomainParticipant_create_topic and change it to:
topic = DDS_DomainParticipant_create_topic(
participant, "Orientation",
type_name, &DDS_TOPIC_QOS_DEFAULT, NULL /* listener */,
DDS_STATUS_MASK_NONE);Look for the comment “Modify the data to be written here” in the sample publisher, and add code to modify the values. Here is a simple example for setting the values:
/* Modify the data to be written here */
instance->Pitch_deg = (float) ((count % 200)) / 10;
instance->Roll_deg = (float) (count % 200) / 10;This will set pitch and roll to a value between 0 and 20 degrees in .1 degrees increments every time a sample is sent.
-
Build and Start the IData Example
You will see the display come up. Next, start the Orientation_Data_publisher.exe and it will display that it publishes data. Each time new data is published, you will see the pitch and roll indicator being updated.
You will see the display come up. Next, start the Orientation_Data_publisher.exe and it will display that it publishes data.And each time new data is published, you will see the pitch and roll indicator being updated.
You can use the RTI Admin Console to further explore the communication between the publisher application and the IData application as well as subscribe and visualize the data.
-
A Note About QoS
The above example uses the default Quality of Service (QoS) settings of Connext. If you would like to change some of the behavior such as reliable or best effort communication or historical data (durability), you can create a USER_QOS_PROFILE.xml and put it in the workspace or working directory from where you start the application. You can use the RTI System Designer to create the configuration file.
-
Next Steps
Now that you have completed the first project integrating ENSCO IData and RTI Connext, go ahead and start your own project. If you have any question or comments, please contact us at:
ENSCO: IData@ENSCO.com
RTI: info@rti.com
The complete code for this example can be found here in GitHub. If you are using an older version of ENSCO IData that only supports 32-bit, the release/IData3.x branch contains an older version of this example.
Visit the RTI FACE webpage to learn more about using RTI Connext in the FACE computing environment
Visit ENSCO’s IData web page to learn more about using IData in the FACE computing environment.