diff --git a/examples/connext_dds/remote_procedure_call/py/Inventory.py b/examples/connext_dds/remote_procedure_call/py/Inventory.py
index e2f9e9675..cec44ceb2 100644
--- a/examples/connext_dds/remote_procedure_call/py/Inventory.py
+++ b/examples/connext_dds/remote_procedure_call/py/Inventory.py
@@ -9,6 +9,7 @@
# damages arising out of the use or inability to use the software.
#
+from abc import ABC
from dataclasses import field
from typing import Sequence
import rti.idl as idl
@@ -32,7 +33,7 @@ class UnknownItemError(Exception):
@rpc.service
-class InventoryService:
+class InventoryService(ABC):
@rpc.operation
def get_inventory(self) -> InventoryContents:
"""Get the current inventory inventory"""
diff --git a/tutorials/application_design/VehicleModeling.xml b/tutorials/application_design/VehicleModeling.xml
index 27e87be1b..0e99496c4 100644
--- a/tutorials/application_design/VehicleModeling.xml
+++ b/tutorials/application_design/VehicleModeling.xml
@@ -1,76 +1,65 @@
-
-
+
-
-
-
+
+
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
+
- 10
+ 15
0
-
-
+
+
- 15
+ 10
0
-
+
-
+
TRANSIENT_LOCAL_DURABILITY_QOS
-
-
+
+
TRANSIENT_LOCAL_DURABILITY_QOS
-
+
+
+
@@ -80,24 +69,26 @@
-
+
+
-
-
-
-
+
+
+
+
-
-
+
+
-
+
+
-
+
diff --git a/tutorials/application_design/c++11/CMakeLists.txt b/tutorials/application_design/c++11/CMakeLists.txt
new file mode 100644
index 000000000..9c80da8f0
--- /dev/null
+++ b/tutorials/application_design/c++11/CMakeLists.txt
@@ -0,0 +1,52 @@
+#
+# (c) 2024 Copyright, Real-Time Innovations, Inc. All rights reserved.
+#
+# RTI grants Licensee a license to use, modify, compile, and create derivative
+# works of the Software. Licensee has the right to distribute object form
+# only for use with RTI products. The Software is provided "as is", with no
+# warranty of any type, including any warranty for fitness for any purpose.
+# RTI is under no obligation to maintain or support the Software. RTI shall
+# not be liable for any incidental or consequential damages arising out of the
+# use or inability to use the software.
+#
+
+cmake_minimum_required(VERSION 3.11)
+project(connext-tutorial-application-design)
+list(APPEND CMAKE_MODULE_PATH
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../../resources/cmake/Modules"
+)
+include(ConnextDdsConfigureCmakeUtils)
+connextdds_configure_cmake_utils()
+
+# Include ConnextDdsAddExample.cmake from resources/cmake
+include(ConnextDdsAddExample)
+
+connextdds_call_codegen(
+ TYPE_PATH "../VehicleModeling.xml"
+ LANG "C++11"
+ PREFIX "VehicleModeling"
+)
+
+connextdds_add_application(
+ TARGET VehicleModeling_publisher
+ LANG "C++11"
+ OUTPUT_NAME "publisher"
+ SOURCES
+ $
+ "${CMAKE_CURRENT_SOURCE_DIR}/publisher.cxx"
+ DEPENDENCIES
+ NO_REQUIRE_QOS
+ ${_CONNEXT_DEPENDENCIES}
+)
+
+connextdds_add_application(
+ TARGET VehicleModeling_subscriber
+ LANG "C++11"
+ OUTPUT_NAME "subscriber"
+ SOURCES
+ $
+ "${CMAKE_CURRENT_SOURCE_DIR}/subscriber.cxx"
+ DEPENDENCIES
+ NO_REQUIRE_QOS
+ ${_CONNEXT_DEPENDENCIES}
+)
diff --git a/tutorials/application_design/c++11/README.md b/tutorials/application_design/c++11/README.md
new file mode 100644
index 000000000..65414477c
--- /dev/null
+++ b/tutorials/application_design/c++11/README.md
@@ -0,0 +1,48 @@
+# Tutorial: Application Design
+
+This code is part of the Connext Application Design tutorial.
+
+## Building the Example :wrench:
+
+You can build the example following the instructions in the tutorial, or you can
+build it using CMake as follows.
+
+1. Generate the build files:
+
+```sh
+mkdir build
+cd build
+cmake ..
+```
+
+This command will try to find the location of your Connext installation. If it
+can't find it, specify it with the ``-DCONNEXTDDS_DIR`` option, for example:
+
+```sh
+cmake -DCONNEXTDDS_DIR=/home/rti/rti_connext_dds-x.y.z ..
+```
+
+If you are compiling for windows you may also need to specify the
+[generator](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html),
+and platform.
+
+```sh
+cmake .. -G "Visual Studio 17 2022" -A x64
+```
+
+2. Build the applications:
+
+```sh
+cmake --build .
+```
+
+If you are using a multi-configuration generator, such as Visual Studio
+solutions, you can specify the configuration mode to build as follows:
+
+```sh
+cmake --build . --config Release|Debug
+```
+
+## Running the Example :rocket:
+
+See the tutorial for instructions.
diff --git a/tutorials/application_design/c++11/common.hpp b/tutorials/application_design/c++11/common.hpp
new file mode 100644
index 000000000..e244dc00f
--- /dev/null
+++ b/tutorials/application_design/c++11/common.hpp
@@ -0,0 +1,103 @@
+//
+// (c) 2024 Copyright, Real-Time Innovations, Inc. All rights reserved.
+//
+// RTI grants Licensee a license to use, modify, compile, and create derivative
+// works of the Software. Licensee has the right to distribute object form
+// only for use with RTI products. The Software is provided "as is", with no
+// warranty of any type, including any warranty for fitness for any purpose.
+// RTI is under no obligation to maintain or support the Software. RTI shall
+// not be liable for any incidental or consequential damages arising out of the
+// use or inability to use the software.
+
+#ifndef COMMON_HPP
+#define COMMON_HPP
+
+#include
+#include
+#include
+
+#include "VehicleModeling.hpp"
+
+using CoordSequence = rti::core::bounded_sequence;
+
+namespace { // Coord namespace
+
+std::string to_string(const Coord &coord)
+{
+ std::ostringstream ss;
+ ss << "Coord(lat: " << coord.lat() << ", lon: " << coord.lon() << ")";
+ return ss.str();
+}
+
+} // namespace
+
+namespace rti::core { // bounded_sequence namespace
+
+std::string to_string(const CoordSequence &route)
+{
+ using ::to_string;
+
+ std::ostringstream ss;
+ ss << "Route(";
+ for (auto it = route.begin(); it != route.end();) {
+ ss << to_string(*it);
+ if (++it != route.end()) {
+ ss << ", ";
+ }
+ }
+ ss << ")";
+ return ss.str();
+}
+
+} // namespace rti::core
+
+namespace utils {
+
+namespace details {
+
+static std::random_device rd;
+static std::mt19937 gen { rd() };
+
+}; // namespace details
+
+void set_random_seed(unsigned seed)
+{
+ details::gen.seed(seed);
+}
+
+double random_range(double min, double max)
+{
+ return std::uniform_real_distribution<>(min, max)(details::gen);
+}
+
+double random_stduniform()
+{
+ return random_range(0.0, 1.0);
+}
+
+std::string new_vin()
+{
+ static const std::string choices("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+
+ std::ostringstream ss;
+ for (int i = 0; i < VIN_LENGTH; ++i) {
+ ss << choices[random_range(0, choices.size())];
+ }
+ return ss.str();
+}
+
+
+CoordSequence new_route(int n = 5)
+{
+ CoordSequence route;
+ for (int i = 0; i < n; ++i) {
+ double r1 = random_stduniform() * 100.0;
+ double r2 = random_stduniform() * 100.0;
+ route.push_back(Coord { r1, r2 });
+ }
+ return route;
+}
+
+} // namespace utils
+
+#endif
diff --git a/tutorials/application_design/c++11/publisher.cxx b/tutorials/application_design/c++11/publisher.cxx
new file mode 100644
index 000000000..7555c650e
--- /dev/null
+++ b/tutorials/application_design/c++11/publisher.cxx
@@ -0,0 +1,131 @@
+//
+// (c) 2024 Copyright, Real-Time Innovations, Inc. All rights reserved.
+//
+// RTI grants Licensee a license to use, modify, compile, and create derivative
+// works of the Software. Licensee has the right to distribute object form
+// only for use with RTI products. The Software is provided "as is", with no
+// warranty of any type, including any warranty for fitness for any purpose.
+// RTI is under no obligation to maintain or support the Software. RTI shall
+// not be liable for any incidental or consequential damages arising out of the
+// use or inability to use the software.
+//
+
+#include
+#include
+#include
+
+#include
+#include "VehicleModeling.hpp"
+#include "common.hpp"
+
+class PublisherSimulation {
+public:
+ explicit PublisherSimulation(
+ dds::pub::DataWriter metrics_writer,
+ dds::pub::DataWriter transit_writer)
+ : metrics_writer_(metrics_writer),
+ transit_writer_(transit_writer),
+ vehicle_vin_(utils::new_vin()),
+ vehicle_fuel_(100.0),
+ vehicle_route_(utils::new_route()),
+ vehicle_position_(vehicle_route_[0])
+ {
+ }
+
+ bool has_ended() const
+ {
+ return is_out_of_fuel();
+ }
+
+ void run();
+
+ friend std::string to_string(const PublisherSimulation &sim);
+
+private:
+ dds::pub::DataWriter metrics_writer_;
+ dds::pub::DataWriter transit_writer_;
+
+ std::string vehicle_vin_;
+ double vehicle_fuel_;
+ CoordSequence vehicle_route_;
+ Coord vehicle_position_;
+
+ bool is_out_of_fuel() const
+ {
+ return vehicle_fuel_ <= 0.0;
+ }
+
+ bool is_on_standby() const
+ {
+ return vehicle_route_.empty();
+ }
+};
+
+void PublisherSimulation::run()
+{
+ while (!has_ended()) {
+ metrics_writer_.write(VehicleMetrics { vehicle_vin_, vehicle_fuel_ });
+
+ transit_writer_.write(VehicleTransit { vehicle_vin_,
+ vehicle_position_,
+ vehicle_route_ });
+
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ if (is_on_standby()) {
+ std::cout << "Vehicle '" << vehicle_vin_
+ << "' has reached its destination, now moving to a "
+ "new location..."
+ << std::endl;
+ vehicle_route_ = utils::new_route();
+ vehicle_route_[0] = vehicle_position_;
+ }
+
+ vehicle_fuel_ -= 10 * utils::random_stduniform();
+ vehicle_position_ = vehicle_route_.front();
+ vehicle_route_.erase(vehicle_route_.begin());
+
+ if (is_out_of_fuel()) {
+ vehicle_fuel_ = 0.0;
+ std::cout << "Vehicle '" << vehicle_vin_ << "' ran out of fuel!"
+ << std::endl;
+ }
+ }
+}
+
+std::string to_string(const PublisherSimulation &sim)
+{
+ std::ostringstream ss;
+ ss << "PublisherSimulation(vehicle_vin: " << sim.vehicle_vin_;
+ ss << ", vehicle_fuel: " << sim.vehicle_fuel_;
+ ss << ", vehicle_route: " << to_string(sim.vehicle_route_);
+ ss << ", vehicle_position: " << to_string(sim.vehicle_position_) << ")";
+ return ss.str();
+}
+
+int main(int argc, char **argv)
+{
+ utils::set_random_seed(std::time(nullptr));
+
+ rti::domain::register_type();
+ rti::domain::register_type();
+
+ dds::core::QosProvider qos_provider("../VehicleModeling.xml");
+
+ auto participant = qos_provider.extensions().create_participant_from_config(
+ "ParticipantLibrary::PublisherApp");
+
+ using MetricsWriter = dds::pub::DataWriter;
+ auto metrics_writer = rti::pub::find_datawriter_by_name(
+ participant,
+ "Publisher::MetricsWriter");
+
+ using TransitWriter = dds::pub::DataWriter;
+ auto transit_writer = rti::pub::find_datawriter_by_name(
+ participant,
+ "Publisher::TransitWriter");
+
+ PublisherSimulation simulation(metrics_writer, transit_writer);
+ std::cout << "Running simulation " << to_string(simulation) << std::endl;
+ simulation.run();
+}
diff --git a/tutorials/application_design/c++11/subscriber.cxx b/tutorials/application_design/c++11/subscriber.cxx
new file mode 100644
index 000000000..900202f09
--- /dev/null
+++ b/tutorials/application_design/c++11/subscriber.cxx
@@ -0,0 +1,254 @@
+//
+// (c) 2024 Copyright, Real-Time Innovations, Inc. All rights reserved.
+//
+// RTI grants Licensee a license to use, modify, compile, and create derivative
+// works of the Software. Licensee has the right to distribute object form
+// only for use with RTI products. The Software is provided "as is", with no
+// warranty of any type, including any warranty for fitness for any purpose.
+// RTI is under no obligation to maintain or support the Software. RTI shall
+// not be liable for any incidental or consequential damages arising out of the
+// use or inability to use the software.
+//
+
+#include
+#include
+#include
+#include
+
+#include
+#include "VehicleModeling.hpp"
+#include "common.hpp"
+
+struct DashboardItem {
+ std::string vin;
+ bool is_historical;
+ std::vector fuel_history;
+ int completed_routes;
+ rti::core::optional current_destination;
+ std::vector reached_destinations;
+};
+
+class SubscriberDashboard {
+public:
+ explicit SubscriberDashboard(
+ dds::sub::DataReader metrics_reader,
+ dds::sub::DataReader transit_reader)
+ : metrics_reader_(metrics_reader), transit_reader_(transit_reader)
+ {
+ }
+
+ void run();
+
+ friend std::string to_string(const SubscriberDashboard &dashboard);
+
+private:
+ dds::sub::DataReader metrics_reader_;
+ dds::sub::DataReader transit_reader_;
+ dds::core::cond::WaitSet waitset;
+ std::unordered_map
+ dashboard_data_;
+
+ void display_app();
+ void metrics_app();
+ void transit_app();
+
+ std::vector online_vehicles() const
+ {
+ std::vector online;
+ for (const auto &item : dashboard_data_) {
+ if (!item.second.is_historical) {
+ online.push_back(item.second);
+ }
+ }
+ return online;
+ }
+
+ std::vector offline_vehicles() const
+ {
+ std::vector offline;
+ for (const auto &item : dashboard_data_) {
+ if (item.second.is_historical) {
+ offline.push_back(item.second);
+ }
+ }
+ return offline;
+ }
+};
+
+void SubscriberDashboard::run()
+{
+ std::mutex mutex;
+
+ dds::sub::cond::ReadCondition metrics_condition(
+ metrics_reader_,
+ dds::sub::status::DataState::any(),
+ [this]() { metrics_app(); });
+
+ dds::sub::cond::ReadCondition transit_condition(
+ transit_reader_,
+ dds::sub::status::DataState::any(),
+ [this]() { transit_app(); });
+
+ dds::core::cond::GuardCondition display_condition;
+ display_condition.extensions().handler([this]() { display_app(); });
+
+ std::thread display_thread([&display_condition, &mutex]() {
+ for (;;) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ std::lock_guard lock(mutex);
+ display_condition.trigger_value(true);
+ }
+ });
+
+ waitset.attach_condition(metrics_condition);
+ waitset.attach_condition(transit_condition);
+ waitset.attach_condition(display_condition);
+
+ for (;;) {
+ waitset.dispatch();
+ std::lock_guard lock(mutex);
+ display_condition.trigger_value(false);
+ }
+}
+
+void SubscriberDashboard::display_app()
+{
+ using ::to_string;
+ using std::to_string;
+
+ std::stringstream ss;
+ auto now = std::chrono::system_clock::now();
+ ss << "[[ DASHBOARD: " << now.time_since_epoch().count() << " ]]\n";
+ {
+ auto online = online_vehicles();
+ ss << "Online vehicles: " << online.size() << "\n";
+ for (auto &item : online) {
+ ss << "- Vehicle " << item.vin << ":\n";
+ ss << " Fuel updates: " << item.fuel_history.size() << "\n";
+ ss << " Last known destination: "
+ << (item.current_destination
+ ? to_string(*item.current_destination)
+ : "None")
+ << "\n";
+ ss << " Last known fuel level: "
+ << (item.fuel_history.empty()
+ ? "None"
+ : to_string(item.fuel_history.back()))
+ << "\n";
+ }
+ }
+ {
+ auto offline = offline_vehicles();
+ ss << "Offline vehicles: " << offline.size() << "\n";
+ for (auto &item : offline) {
+ ss << "- Vehicle " << item.vin << ":\n";
+ ss << " Mean fuel consumption: "
+ << std::accumulate(
+ item.fuel_history.begin(),
+ item.fuel_history.end(),
+ 0.0)
+ / item.fuel_history.size()
+ << "\n";
+ ss << " Known reached destinations: "
+ << item.reached_destinations.size() << "\n";
+ for (auto &destination : item.reached_destinations) {
+ ss << " - " << to_string(destination) << "\n";
+ }
+ }
+ }
+
+ std::cout << ss.str() << std::endl;
+}
+
+void SubscriberDashboard::metrics_app()
+{
+ for (const auto &sample : metrics_reader_.take()) {
+ auto it = dashboard_data_.find(sample.info().instance_handle());
+ // If not a tracked vehicle, track it.
+ if (it == dashboard_data_.end()) {
+ if (!sample.info().valid())
+ continue;
+
+ auto new_handle = sample.info().instance_handle();
+ auto new_data = DashboardItem { sample.data().vehicle_vin() };
+ it = dashboard_data_.emplace(new_handle, new_data).first;
+ }
+
+ auto &item = it->second;
+ item.is_historical = sample.info().state().instance_state()
+ != dds::sub::status::InstanceState::alive();
+
+ if (!sample.info().valid() && item.is_historical) {
+ continue;
+ }
+
+ item.fuel_history.push_back(sample.data().fuel_level());
+ }
+}
+
+void SubscriberDashboard::transit_app()
+{
+ for (const auto &sample : transit_reader_.take()) {
+ auto it = dashboard_data_.find(sample.info().instance_handle());
+ // If not a tracked vehicle, track it.
+ if (it == dashboard_data_.end()) {
+ if (!sample.info().valid())
+ continue;
+
+ auto new_handle = sample.info().instance_handle();
+ auto new_data = DashboardItem { sample.data().vehicle_vin() };
+ it = dashboard_data_.emplace(new_handle, new_data).first;
+ }
+
+ auto &item = it->second;
+ item.is_historical = sample.info().state().instance_state()
+ != dds::sub::status::InstanceState::alive();
+
+ if (!sample.info().valid() && item.is_historical) {
+ continue;
+ }
+
+ auto ¤t_route = sample.data().current_route();
+ if (current_route->size() > 0) {
+ item.current_destination = current_route->back();
+ } else {
+ item.reached_destinations.push_back(*item.current_destination);
+ item.current_destination.reset();
+ item.completed_routes++;
+ }
+ }
+}
+
+std::string to_string(const SubscriberDashboard &dashboard)
+{
+ std::ostringstream ss;
+ ss << "Dashboard()";
+ return ss.str();
+}
+
+int main(int argc, char **argv)
+{
+ utils::set_random_seed(std::time(nullptr));
+
+ rti::domain::register_type();
+ rti::domain::register_type();
+
+ dds::core::QosProvider qos_provider("../VehicleModeling.xml");
+
+ auto participant = qos_provider.extensions().create_participant_from_config(
+ "ParticipantLibrary::SubscriberApp");
+
+ using MetricsReader = dds::sub::DataReader;
+ auto metrics_reader = rti::sub::find_datareader_by_name(
+ participant,
+ "Subscriber::MetricsReader");
+
+ using TransitReader = dds::sub::DataReader;
+ auto transit_reader = rti::sub::find_datareader_by_name(
+ participant,
+ "Subscriber::TransitReader");
+
+ SubscriberDashboard dashboard(metrics_reader, transit_reader);
+ std::cout << "Running dashboard " << to_string(dashboard) << std::endl;
+ dashboard.run();
+}
diff --git a/tutorials/application_design/py/README.md b/tutorials/application_design/py/README.md
index 62888abc2..383c00aaa 100644
--- a/tutorials/application_design/py/README.md
+++ b/tutorials/application_design/py/README.md
@@ -1,4 +1,4 @@
-# Tutorial: Data Persistence
+# Tutorial: Application Design
This code is part of the Connext Application Design tutorial and is included
here in full for convenience.
diff --git a/tutorials/data_persistence/c++11/USER_QOS_PROFILES.xml b/tutorials/data_persistence/c++11/USER_QOS_PROFILES.xml
index 3df796002..dbebb31c4 100644
--- a/tutorials/data_persistence/c++11/USER_QOS_PROFILES.xml
+++ b/tutorials/data_persistence/c++11/USER_QOS_PROFILES.xml
@@ -14,9 +14,21 @@
BuiltinQosSnippetLib::QosPolicy.Durability.Persistent
- BuiltinQosSnippetLib::QosPolicy.History.KeepAll
BuiltinQosSnippetLib::QosPolicy.Reliability.Reliable
+
+
+
+ KEEP_LAST_HISTORY_QOS
+ 10
+
+
+
+
+ KEEP_LAST_HISTORY_QOS
+ 10
+
+
diff --git a/tutorials/data_persistence/py/USER_QOS_PROFILES.xml b/tutorials/data_persistence/py/USER_QOS_PROFILES.xml
index 3df796002..dbebb31c4 100644
--- a/tutorials/data_persistence/py/USER_QOS_PROFILES.xml
+++ b/tutorials/data_persistence/py/USER_QOS_PROFILES.xml
@@ -14,9 +14,21 @@
BuiltinQosSnippetLib::QosPolicy.Durability.Persistent
- BuiltinQosSnippetLib::QosPolicy.History.KeepAll
BuiltinQosSnippetLib::QosPolicy.Reliability.Reliable
+
+
+
+ KEEP_LAST_HISTORY_QOS
+ 10
+
+
+
+
+ KEEP_LAST_HISTORY_QOS
+ 10
+
+
diff --git a/tutorials/last_value_cache/c++11/USER_QOS_PROFILES.xml b/tutorials/last_value_cache/c++11/USER_QOS_PROFILES.xml
index 79068323a..9e2e7cde1 100644
--- a/tutorials/last_value_cache/c++11/USER_QOS_PROFILES.xml
+++ b/tutorials/last_value_cache/c++11/USER_QOS_PROFILES.xml
@@ -13,22 +13,26 @@
- BuiltinQosLib::Generic.KeepLastReliable.TransientLocal
+ BuiltinQosSnippetLib::QosPolicy.Durability.TransientLocal
+ BuiltinQosSnippetLib::QosPolicy.Reliability.Reliable
+ BuiltinQosSnippetLib::QosPolicy.History.KeepLast_1
- BuiltinQosLib::Generic.KeepLastReliable.TransientLocal
+ BuiltinQosSnippetLib::QosPolicy.Durability.TransientLocal
+ BuiltinQosSnippetLib::QosPolicy.Reliability.Reliable
-
+
+ KEEP_LAST_HISTORY_QOS
10
+ KEEP_LAST_HISTORY_QOS
10
diff --git a/tutorials/last_value_cache/py/USER_QOS_PROFILES.xml b/tutorials/last_value_cache/py/USER_QOS_PROFILES.xml
index 79068323a..9e2e7cde1 100644
--- a/tutorials/last_value_cache/py/USER_QOS_PROFILES.xml
+++ b/tutorials/last_value_cache/py/USER_QOS_PROFILES.xml
@@ -13,22 +13,26 @@
- BuiltinQosLib::Generic.KeepLastReliable.TransientLocal
+ BuiltinQosSnippetLib::QosPolicy.Durability.TransientLocal
+ BuiltinQosSnippetLib::QosPolicy.Reliability.Reliable
+ BuiltinQosSnippetLib::QosPolicy.History.KeepLast_1
- BuiltinQosLib::Generic.KeepLastReliable.TransientLocal
+ BuiltinQosSnippetLib::QosPolicy.Durability.TransientLocal
+ BuiltinQosSnippetLib::QosPolicy.Reliability.Reliable
-
+
+ KEEP_LAST_HISTORY_QOS
10
+ KEEP_LAST_HISTORY_QOS
10