Data Modeling#

Introduction#

What you’ll learn

In this module, you’ll get an introduction to data modeling for data-centric publish-subscribe.

You will do the following:

  1. Define types to model state, events, and periodic information

  2. Generate an example application for these types

  3. Define the quality of service (QoS) for each data flow

You will use System Designer, a graphical tool to define data types and configuration. For reference, snippets in some text formats (IDL and XML) are also provided.

In the Publish-Subscribe module, you created one data type and simple publisher/subscriber applications to demonstrate the publish-subscribe model. In this module, you’ll use System Designer, instead of IDL, to create multiple data types. (System Designer can do much more than this, which you’ll learn in the Application Design module.)

How to complete this module#

To complete this module, you’ll need the following:

1. Define the data types#

You’ll model a simple system to track vehicles (position and route) and monitor some metrics, such as fuel level. To start, you’ll define the following types:

  • VehicleTransit to identify the vehicle and track it.

  • VehicleMetrics to monitor periodic updates of the vehicle metrics.

Each vehicle will be a unique object in the system. You’ll use the Vehicle Identification Number (VIN) as a key to identify each vehicle.

Start System Designer from RTI Launcher.

Create a new project by clicking Create New.... Call the project VehicleModeling.

When the project opens, click the hamburger icon to open the View menu, then select the Types view. Right-click Types to open the context menu.

From the context menu, add a constant and a type alias (typedef). Configure each item as pictured below.

VehicleModeling.idl#
const uint8 VIN_LENGTH = 17;
typedef string<VIN_LENGTH> VIN;
VehicleModeling.xml#
<const name="VIN_LENGTH" type="uint8" value="17"/>
<typedef name="VIN" type="string" stringMaxLength="VIN_LENGTH"/>

So far, you’ve defined:

  • The constant VIN_LENGTH, with a value of 17.

  • The type alias VIN, a string with a maximum length of VIN_LENGTH characters.

Next, you will define a VehicleTransit type to model the state of a vehicle. The publisher applications can publish updates when the state changes, and the subscriber applications can react to these events.

Define two structs as pictured below. The first is a simple struct to represent a coordinate; the second is the main struct to represent the state of a vehicle.

These structs are currently empty—you need to add fields to them. Right-click each struct and select Add Member. Define the structures as follows:

VehicleModeling.idl#
@nested @final struct Coord {  // WSG84 coordinate used by GPS
    double lat;  // +north, -south
    double lon;  // +west , -east
};

const string VehicleTransitTopic = "VehicleTransit";
@appendable struct VehicleTransit { // State, Event
    @key VIN vehicle_vin;
    Coord current_position;
    @optional sequence<Coord> current_route; // 'no route' == standby
};
VehicleModeling.xml#
<struct name="Coord" extensibility="final" nested="true">
    <member name="lat" type="float64"/>
    <member name="lon" type="float64"/>
</struct>
<const name="VehicleTransitTopic" type="string" value="&quot;VehicleTransit&quot;"/>
<struct name="VehicleTransit" extensibility="appendable">
    <member name="vehicle_vin" type="nonBasic" nonBasicTypeName="VIN" key="true"/>
    <member name="current_position" type="nonBasic" nonBasicTypeName="Coord"/>
    <member name="current_route" type="nonBasic" nonBasicTypeName="Coord" sequenceMaxLength="-1" optional="true"/>
</struct>

VehicleTransit is a struct type with three fields:

  • vehicle_vin: identifies an instance of the type, and is therefore a @key field.

  • current_position: vehicle’s current position, represented by a Coord type.

  • current_route: sequence (a collection) of coordinates. It is @optional, indicating that each update may or may not include a route.

You’ve also specified an extensibility for each type:

  • Coord is a @final type, meaning it cannot be extended.

  • VehicleTransit is an @appendable type (this is the default), which allows applications to add new fields in the future without breaking compatibility.

Type extensibility allows applications to communicate using the same Topic with different, compatible types.

The @nested annotation is optional and indicates that the Coord type is not intended to be used as a Topic by itself, only as part of another type.

Finally, to provide periodic information about our vehicle (fuel levels, in this example) define a VehicleMetrics type:

As before with VehicleTransit, define the following items:

VehicleModeling.idl#
typedef @range(min=0.0, max=100.0) double Percentage;

const string VehicleMetricsTopic = "VehicleMetrics";
@appendable struct VehicleMetrics { // Periodic
    @key VIN vehicle_vin;
    Percentage fuel_level;
};
VehicleModeling.xml#
<typedef name="Percentage" type="float64" min="0.0" max="100.0"/>
<const name="VehicleMetricsTopic" type="string" value="&quot;VehicleMetrics&quot;"/>
<struct name="VehicleMetrics" extensibility="appendable">
    <member name="vehicle_vin" type="nonBasic" nonBasicTypeName="VIN" key="true"/>
    <member name="fuel_level" type="nonBasic" nonBasicTypeName="Percentage"/>
</struct>

VehicleMetrics identifies instances using the same key field (vehicle_vin) as used for VehicleTransit. This means that the two different Topics may refer to the same vehicle.

In both cases, you’ve defined constants with the Topic names, which can be used by the applications to create the Topics.

In summary, the full data types you’ve defined are as follows:

These type definitions are saved in VehicleModeling.xml.

VehicleModeling.idl#
const uint8 VIN_LENGTH = 17;
typedef string<VIN_LENGTH> VIN;

@nested @final struct Coord {
    double lat;
    double lon;
};

const string VehicleTransitTopic = "VehicleTransit";
@appendable struct VehicleTransit {
    @key VIN vehicle_vin;
    Coord current_position;
    @optional sequence<Coord> current_route;
};

typedef @range(min=0.0, max=100.0) double Percentage;

const string VehicleMetricsTopic = "VehicleMetrics";
@appendable struct VehicleMetrics {
    @key VIN vehicle_vin;
    Percentage fuel_level;
};
VehicleModeling.xml#
<?xml version="1.0" encoding="UTF-8"?>
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://community.rti.com/schema/current/rti_dds_profiles.xsd">
    <types>
    <const name="VIN_LENGTH" type="uint8" value="17"/>
    <typedef name="VIN" type="string" stringMaxLength="VIN_LENGTH"/>
    <struct name="Coord" extensibility="final" nested="true">
        <member name="lat" type="float64"/>
        <member name="lon" type="float64"/>
    </struct>
    <const name="VehicleTransitTopic" type="string" value="&quot;VehicleTransit&quot;"/>
    <struct name="VehicleTransit" extensibility="appendable">
        <member name="vehicle_vin" type="nonBasic" nonBasicTypeName="VIN" key="true"/>
        <member name="current_position" type="nonBasic" nonBasicTypeName="Coord"/>
        <member name="current_route" type="nonBasic" nonBasicTypeName="Coord" sequenceMaxLength="-1" optional="true"/>
    </struct>
    <typedef name="Percentage" type="float64" min="0.0" max="100.0"/>
    <const name="VehicleMetricsTopic" type="string" value="&quot;VehicleMetrics&quot;"/>
    <struct name="VehicleMetrics" extensibility="appendable">
        <member name="vehicle_vin" type="nonBasic" nonBasicTypeName="VIN" key="true"/>
        <member name="fuel_level" type="nonBasic" nonBasicTypeName="Percentage"/>
    </struct>
    </types>
</dds>

2. Generate example applications#

Now that you’ve defined types, use Code Generator (rtiddsgen) to generate simple example applications. The generated application allows you to validate your types and get started writing application code to publish and subscribe to Topics defined by these types.

Run the following command to generate the Python example:

$ rtiddsgen -language python -example universal VehicleModeling.xml

-example will generate three different things:

  • A set of files for your data types.

  • An example publisher application, subscriber application, and QoS configuration for these.

  • A set of files to build and run these example applications.

To generate the build files for the C++ example, specify a target platform. For example:

  • Linux: x64Linux4gcc7.3.0, armv8Linux4gcc7.3.0

  • Windows: x64Win64VS2017

  • macOS: x64Darwin20clang12.0, arm64Darwin20clang12.0

$ rtiddsgen -language c++11 -example x64Linux4gcc7.3.0 VehicleModeling.xml

-example will generate three different things:

  • A set of files for your data types.

  • An example publisher application, subscriber application, and QoS configuration for these.

  • A set of files to build and run these example applications.

Once the code is generated, build the example applications generated by rtiddsgen for your platform.

Build the applications using the makefile_VehicleModeling_<platform> file. For example:

$ make -f makefile_VehicleModeling_x64Linux4gcc7.3.0

Open VehicleModeling-<platform>.sln in Visual Studio and compile the project.

Build the applications using the makefile_VehicleModeling_<platform> file. For example:

$ make -f makefile_VehicleModeling_arm64Darwin20clang12.0

To generate the build files for the C# example, specify a target platform. For example, net8 or net6.

$ rtiddsgen -language 'C#' -example net8 VehicleModeling.xml

-example will generate three different things:

  • A set of files for your data types.

  • An example publisher application, subscriber application, and QoS configuration for these.

  • A set of files to build and run these example applications.

Once the code is generated, build the example applications generated by rtiddsgen using dotnet.

$ dotnet build

Error NU1301

Unless you have installed the Connext .NET Support package, the NuGet.Config file generated by rtiddsgen will point to a directory in the Connext installation that may not exist.

If you encounter an error like NU1301 when building the application, delete the file NuGet.Config file so that .NET gets the Connext NuGet package from nuget.org.

To generate the build files for the Java example, specify a target platform. For example:

  • Linux: x64Linux4gcc7.3.0, armv8Linux4gcc7.3.0

  • Windows: x64Win64VS2017

  • macOS: x64Darwin20clang12.0, arm64Darwin20clang12.0

$ rtiddsgen -language Java -example x64Linux4gcc7.3.0 VehicleModeling.xml

-example will generate three different things:

  • A set of files for your data types.

  • An example publisher application, subscriber application, and QoS configuration for these.

  • A set of files to build and run these example applications.

Once the code is generated, build the example applications generated by rtiddsgen using Ant.

$ ant compile

To generate the build files for the C example, specify a target platform. For example:

  • Linux: x64Linux4gcc7.3.0, armv8Linux4gcc7.3.0

  • Windows: x64Win64VS2017

  • macOS: x64Darwin20clang12.0, arm64Darwin20clang12.0

$ rtiddsgen -language c -example x64Linux4gcc7.3.0 VehicleModeling.xml

-example will generate three different things:

  • A set of files for your data types.

  • An example publisher application, subscriber application, and QoS configuration for these.

  • A set of files to build and run these example applications.

Once the code is generated, build the example applications generated by rtiddsgen for your platform.

Build the applications using the makefile_VehicleModeling_<platform> file. For example:

$ make -f makefile_VehicleModeling_x64Linux4gcc7.3.0

Open VehicleModeling-<platform>.sln in Visual Studio and compile the project.

Build the applications using the makefile_VehicleModeling_<platform> file. For example:

$ make -f makefile_VehicleModeling_arm64Darwin20clang12.0

The generated example consists of two simple applications using the last type you defined (VehicleMetrics):

  • A publisher that sends data for one of the types.

  • A subscriber that prints the received data in the console.

Run the applications:

First, run the publisher:

$ python VehicleModeling_program.py --pub

You should see the following output:

Running VehicleMetricsPublisher on domain 0
Writing VehicleMetrics, count 0
Writing VehicleMetrics, count 1
Writing VehicleMetrics, count 2

While the publisher is running, run the subscriber in another terminal:

$ python VehicleModeling_program.py --sub

You should see the data being received:

Running VehicleMetricsSubscriber on domain 0
Hello World subscriber sleeping for 1 seconds...
Received: VehicleMetrics(vehicle_vin='', fuel_level=0.0)
Hello World subscriber sleeping for 1 seconds...
Hello World subscriber sleeping for 1 seconds...
Received: VehicleMetrics(vehicle_vin='', fuel_level=0.0)
Hello World subscriber sleeping for 1 seconds...
Hello World subscriber sleeping for 1 seconds...

Update the publisher to write different samples by modifying the code next to the comment # Modify the data to be written here.

First, run the publisher:

$ objs/x64Linux4gcc7.3.0/VehicleModeling_publisher

You should see the following output:

Writing ::VehicleMetrics, count 0
Writing ::VehicleMetrics, count 1
Writing ::VehicleMetrics, count 2

While the publisher is running, run the subscriber in another terminal:

$ objs/x64Linux4gcc7.3.0/VehicleModeling_subscriber

You should see the data being received:

::VehicleMetrics subscriber sleeping up to 1 sec...
[vehicle_vin: , fuel_level: 0]
::VehicleMetrics subscriber sleeping up to 1 sec...
[vehicle_vin: , fuel_level: 0]
::VehicleMetrics subscriber sleeping up to 1 sec...
[vehicle_vin: , fuel_level: 0]
::VehicleMetrics subscriber sleeping up to 1 sec...

Update the publisher to write different samples by modifying the code next to the comment // Modify the data to be written here.

First, run the publisher:

$ dotnet run --pub

You should see the following output:

Running VehicleMetricsPublisher on domain 0
Writing VehicleMetrics, count 0
Writing VehicleMetrics, count 1
Writing VehicleMetrics, count 2

While the publisher is running, run the subscriber in another terminal:

$ dotnet run --sub

You should see the data being received:

Running VehicleMetricsSubscriber on domain 0
VehicleMetrics subscriber sleeping for 4 sec...
vehicle_vin: ""
fuel_level: 0

VehicleMetrics subscriber sleeping for 4 sec...
vehicle_vin: ""
fuel_level: 0

VehicleMetrics subscriber sleeping for 4 sec...
vehicle_vin: ""
fuel_level: 0

Update the publisher to write different samples by modifying the code next to the comment // Modify the data to be written here.

First, run the publisher:

$ ant VehicleMetricsPublisher

You should see the following output:

VehicleMetricsPublisher:
    [java] Writing VehicleMetrics, count 0
    [java] Writing VehicleMetrics, count 1
    [java] Writing VehicleMetrics, count 2
    [java] Writing VehicleMetrics, count 3

While the publisher is running, run the subscriber in another terminal:

$ ant VehicleMetricsSubscriber

You should see the data being received:

VehicleMetricsSubscriber:
    [java] Received:
    [java]     vehicle_vin:
    [java]     fuel_level: 0.0
    [java]
    [java] No data after 1 seconds.
    [java] Received:
    [java]     vehicle_vin:
    [java]     fuel_level: 0.0
    [java]
    [java] Received:
    [java]     vehicle_vin:
    [java]     fuel_level: 0.0
    [java]
    [java] No data after 1 seconds.
    [java] Received:
    [java]     vehicle_vin:
    [java]     fuel_level: 0.0
    [java]

Update the publisher to write different samples by modifying the code next to the comment // Modify the data to be written here.

First, run the publisher:

$ objs/x64Linux4gcc7.3.0/VehicleModeling_publisher

You should see the following output:

Writing VehicleMetrics, count 0
Writing VehicleMetrics, count 1
Writing VehicleMetrics, count 2

While the publisher is running, run the subscriber in another terminal:

$ objs/x64Linux4gcc7.3.0/VehicleModeling_subscriber

You should see the data being received:

VehicleMetrics subscriber sleeping for 4 sec...
Received data

    vehicle_vin: ""
    fuel_level: 0
VehicleMetrics subscriber sleeping for 4 sec...
Received data

    vehicle_vin: ""
    fuel_level: 0
VehicleMetrics subscriber sleeping for 4 sec...
Received data

    vehicle_vin: ""
    fuel_level: 0
VehicleMetrics subscriber sleeping for 4 sec...

Update the publisher to write different samples by modifying the code next to the comment /* Modify the data to be written here */.

The applications use a generic QoS profile defined in the USER_QOS_PROFILES.xml file. In the next section, you’ll define the appropriate QoS for each Topic’s use case to complete your data model.

Troubleshooting

In case of errors building or running your application, make sure you have set up your environment and license file.

For instructions, go to Get Started, select your platform and installation method, then find the section Run a Hello World.

3. Define the quality of service for the data flows#

Different Topics may have different QoS requirements. For example, you may want to ensure that you reliably receive “Event” or “State” Topic samples, or that “Periodic” updates are produced at a regular interval.

For the VehicleMetrics data flow, allow for some data to be lost but also ensure that data is published at a steady rate. To do this, use the Reliability and Deadline QoS policies.

In System Designer, select QoS in the View menu.

Using a similar workflow as you did adding new Types, create a new QoS Library called VehicleModeling_Library. Then create a QoS Profile called VehicleMetrics_Profile.

In the VehicileMetrics_Profile add two new QoS for the VehicleMetrics data flow, one DataWriter QoS and one DataReader QoS.

VehicleModeling.xml#
<?xml version="1.0" encoding="UTF-8"?>
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://community.rti.com/schema/current/rti_dds_profiles.xsd">
    <qos_library name="VehicleModeling_Library">
        <qos_profile name="VehicleMetrics_Profile">
            <datareader_qos base_name="BuiltinQosLib::Generic.BestEffort">
                <deadline>
                    <period>
                        <sec>15</sec>
                        <nanosec>0</nanosec>
                    </period>
                </deadline>
            </datareader_qos>
            <datawriter_qos base_name="BuiltinQosLib::Generic.BestEffort">
                <deadline>
                    <period>
                        <sec>10</sec>
                        <nanosec>0</nanosec>
                    </period>
                </deadline>
            </datawriter_qos>
        </qos_profile>
    </qos_library>
</dds>

With this QoS configuration:

  • Communication will be best effort, meaning that the publisher won’t attempt to resend a data sample if the subscriber misses it.

  • Publishers will be expected to produce data every ten seconds, while subscribers allow for 15 seconds between each data reception. A status will be triggered if either deadline is missed, allowing your applications to act accordingly.

For the VehicleTransit data flow, ensure that no data is lost even though subscribers may only be interested in the last available state. To do this, use the Reliability, Durability, and History QoS policies.

In the same VehicleModeling_Library QoS Library, add a new VehicleTransit_Profile QoS Profile.

Add two new DataWriter and DataReader QoS configurations:

VehicleModeling.xml#
<?xml version="1.0" encoding="UTF-8"?>
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://community.rti.com/schema/current/rti_dds_profiles.xsd">
    <qos_library name="VehicleModeling_Library">
        <qos_profile name="VehicleTransit_Profile">
            <datareader_qos base_name="BuiltinQosLib::Generic.KeepLastReliable">
                <durability>
                    <kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
                </durability>
            </datareader_qos>
            <datawriter_qos base_name="BuiltinQosLib::Generic.StrictReliable">
                <durability>
                    <kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
                </durability>
            </datawriter_qos>
        </qos_profile>
    </qos_library>
</dds>

With this QoS configuration:

  • Communication will be reliable, meaning that the publisher will expect an acknowlegment from the subscriber for each data sample. If a sample is acknowledged as lost, the publisher will resend it.

  • Publishers will keep as many data samples as needed in memory to ensure reliable delivery with active subscribers. Additionally, publishers will deliver the latest update to any new subscriber.

After defining the QoS profiles, update the publisher and subscriber applications to load the VehicleMetrics_Profile profile.

Update VehicleModeling_publisher.py to replace the creation of the DataWriter with the following code:

qos_provider = dds.QosProvider("VehicleModeling.xml")

writer_qos = qos_provider.datawriter_qos_from_profile(
    "VehicleModeling_Library::VehicleMetrics_Profile"
)
writer = dds.DataWriter(participant.implicit_publisher, topic, writer_qos)

Update VehicleModeling_subscriber.py to replace the creation of the DataReader with the following code:

qos_provider = dds.QosProvider("VehicleModeling.xml")

reader_qos = qos_provider.datareader_qos_from_profile(
    "VehicleModeling_Library::VehicleMetrics_Profile"
)
reader = dds.DataReader(participant.implicit_subscriber, topic, reader_qos)

Update VehicleModeling_publisher.cxx to replace the creation of the DataWriter with the following code:

dds::core::QosProvider qos_provider("VehicleModeling.xml");

dds::pub::qos::DataWriterQos writer_qos = qos_provider.datawriter_qos(
        "VehicleModeling_Library::VehicleTransit_Profile");
dds::pub::DataWriter< ::VehicleTransit> writer(
        publisher,
        topic,
        writer_qos);

Similarly, update VehicleModeling_subscriber.cxx to replace the creation of the DataReader with the following code:

dds::core::QosProvider qos_provider("VehicleModeling.xml");

dds::sub::qos::DataReaderQos reader_qos = qos_provider.datareader_qos(
        "VehicleModeling_Library::VehicleTransit_Profile");
dds::sub::DataReader< ::VehicleTransit> reader(
        subscriber,
        topic,
        reader_qos);

Update VehicleMetricsPublisher.cs to replace the creation of the DataWriter with the following code:

QosProvider qosProvider = new QosProvider("VehicleModeling.xml");
DataWriterQos writerQos = qosProvider.GetDataWriterQos("VehicleModeling_Library::VehicleTransit_Profile");
DataWriter<VehicleMetrics> writer = publisher.CreateDataWriter(topic, writerQos);

Update VehicleMetricsSubscriber.cs to replace the creation of the DataReader with the following code:

QosProvider qosProvider = new QosProvider("VehicleModeling.xml");
DataReaderQos readerQos = qosProvider.GetDataReaderQos("VehicleModeling_Library::VehicleTransit_Profile");
// This DataReader reads data on Topic "Example VehicleMetrics".
// DataReader QoS is configured in USER_QOS_PROFILES.xml
DataReader<VehicleMetrics> reader = subscriber.CreateDataReader(topic, readerQos);

Update VehicleMetricsPublisher.java to replace the creation of the DataWriter with the following code:

DomainParticipantFactory factory = DomainParticipantFactory.get_instance();
DomainParticipantFactoryQos factoryQos = new DomainParticipantFactoryQos();
factory.get_qos(factoryQos);
factoryQos.profile.url_profile.add("VehicleModeling.xml");
factory.set_qos(factoryQos);

DataWriterQos writerQos = new DataWriterQos();
factory.get_datawriter_qos_from_profile(
    writerQos,
    "VehicleModeling_Library",
    "VehicleMetrics_Profile");

// This DataWriter writes data on "Example VehicleMetrics" Topic
VehicleMetricsDataWriter writer = (VehicleMetricsDataWriter) Objects.requireNonNull(
    publisher.create_datawriter(
        topic,
        writerQos,
        null, // listener
        StatusKind.STATUS_MASK_NONE));

Update VehicleMetricsSubscriber.java to replace the creation of the DataReader with the following code:

DomainParticipantFactory factory = DomainParticipantFactory.get_instance();
DomainParticipantFactoryQos factoryQos = new DomainParticipantFactoryQos();
factory.get_qos(factoryQos);
factoryQos.profile.url_profile.add("VehicleModeling.xml");
factory.set_qos(factoryQos);

DataReaderQos readerQos = new DataReaderQos();
factory.get_datareader_qos_from_profile(
        readerQos,
        "VehicleModeling_Library",
        "VehicleMetrics_Profile");

// This DataReader reads data on "Example VehicleMetrics" Topic
reader = (VehicleMetricsDataReader) Objects.requireNonNull(
    subscriber.create_datareader(
        topic,
        readerQos,
        null, // listener
        StatusKind.STATUS_MASK_NONE));

Update VehicleModeling_publisher.c to replace the creation of the DataWriter with the following code (error checking omitted for brevity):

struct DDS_DomainParticipantFactoryQos factory_qos =
        DDS_DomainParticipantFactoryQos_INITIALIZER;
DDS_DomainParticipantFactory_get_qos(
        DDS_TheParticipantFactory,
        &factory_qos);
DDS_Long factory_qos_url_profile_length =
        DDS_StringSeq_get_length(&factory_qos.profile.url_profile);
DDS_StringSeq_ensure_length(
        &factory_qos.profile.url_profile,
        1 + factory_qos_url_profile_length,
        1 + factory_qos_url_profile_length);
*DDS_StringSeq_get_reference(
        &factory_qos.profile.url_profile,
        factory_qos_url_profile_length) = "VehicleModeling.xml";
DDS_DomainParticipantFactory_set_qos(
        DDS_TheParticipantFactory,
        &factory_qos);

struct DDS_DataWriterQos writer_qos = DDS_DataWriterQos_INITIALIZER;
DDS_DomainParticipantFactory_get_datawriter_qos_from_profile(
        DDS_TheParticipantFactory,
        &writer_qos,
        "VehicleModeling_Library",
        "VehicleMetrics_Profile");
writer = DDS_Publisher_create_datawriter(
        publisher,
        topic,
        &writer_qos,
        NULL /* listener */,
        DDS_STATUS_MASK_NONE);

Similarly, update VehicleModeling_subscriber.c to replace the creation of the DataReader with the following code (error checking omitted for brevity):

struct DDS_DomainParticipantFactoryQos factory_qos =
        DDS_DomainParticipantFactoryQos_INITIALIZER;
DDS_DomainParticipantFactory_get_qos(
        DDS_TheParticipantFactory,
        &factory_qos);
DDS_Long factory_qos_url_profile_length =
        DDS_StringSeq_get_length(&factory_qos.profile.url_profile);
DDS_StringSeq_ensure_length(
        &factory_qos.profile.url_profile,
        1 + factory_qos_url_profile_length,
        1 + factory_qos_url_profile_length);
*DDS_StringSeq_get_reference(
        &factory_qos.profile.url_profile,
        factory_qos_url_profile_length) = "VehicleModeling.xml";
DDS_DomainParticipantFactory_set_qos(
        DDS_TheParticipantFactory,
        &factory_qos);

struct DDS_DataReaderQos reader_qos = DDS_DataReaderQos_INITIALIZER;
DDS_DomainParticipantFactory_get_datareader_qos_from_profile(
        DDS_TheParticipantFactory,
        &reader_qos,
        "VehicleModeling_Library",
        "VehicleMetrics_Profile");
reader = DDS_Subscriber_create_datareader(
        subscriber,
        DDS_Topic_as_topicdescription(topic),
        &reader_qos,
        &reader_listener,
        DDS_STATUS_MASK_ALL);

Running your applications as you did in the previous section, you’ll see similar output. However, under the hood, the actual behavior is different and subject to the QoS you’ve defined.

These applications publish and subscribe to the VehicleMetrics Topic. You can create similar applications for the VehicleTransit topic, or you can continue this exercise in the Application Design module, where you’ll use System Designer to define both Topics and their DataReaders and DataWriters, and instantiate these in your application.

Learn More#

You have learned how to define the data model for a simple vehicle tracking system, generate example applications, and define the quality of service for the data flows.

Next Steps

Related modules:

  • Application Design builds on this module, showing how to use System Designer to define Topics, DataReaders, and DataWriters to create a full application.

Full code examples:

Reference documentation:

More resources:

Was this page helpful?

Back to Learn