From 495ecfe93c35b5b71ca2b12c2f59df3712326a3f Mon Sep 17 00:00:00 2001 From: eduponz Date: Thu, 6 Jun 2024 08:02:19 +0200 Subject: [PATCH 1/6] Refs #21099: X-Types application Signed-off-by: eduponz --- examples/CMakeLists.txt | 1 + examples/cpp/xtypes/Application.cpp | 62 +++++ examples/cpp/xtypes/Application.hpp | 56 +++++ examples/cpp/xtypes/CLIParser.hpp | 227 +++++++++++++++++ examples/cpp/xtypes/CMakeLists.txt | 63 +++++ examples/cpp/xtypes/PublisherApp.cpp | 318 ++++++++++++++++++++++++ examples/cpp/xtypes/PublisherApp.hpp | 117 +++++++++ examples/cpp/xtypes/README.md | 144 +++++++++++ examples/cpp/xtypes/SubscriberApp.cpp | 326 +++++++++++++++++++++++++ examples/cpp/xtypes/SubscriberApp.hpp | 115 +++++++++ examples/cpp/xtypes/main.cpp | 96 ++++++++ examples/cpp/xtypes/xtypes_profile.xml | 52 ++++ 12 files changed, 1577 insertions(+) create mode 100644 examples/cpp/xtypes/Application.cpp create mode 100644 examples/cpp/xtypes/Application.hpp create mode 100644 examples/cpp/xtypes/CLIParser.hpp create mode 100644 examples/cpp/xtypes/CMakeLists.txt create mode 100644 examples/cpp/xtypes/PublisherApp.cpp create mode 100644 examples/cpp/xtypes/PublisherApp.hpp create mode 100644 examples/cpp/xtypes/README.md create mode 100644 examples/cpp/xtypes/SubscriberApp.cpp create mode 100644 examples/cpp/xtypes/SubscriberApp.hpp create mode 100644 examples/cpp/xtypes/main.cpp create mode 100644 examples/cpp/xtypes/xtypes_profile.xml diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 84ef0d3d6dc..d6490502433 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -18,3 +18,4 @@ add_subdirectory(cpp/custom_payload_pool) add_subdirectory(cpp/dds) add_subdirectory(cpp/hello_world) add_subdirectory(cpp/rtps) +add_subdirectory(cpp/xtypes) diff --git a/examples/cpp/xtypes/Application.cpp b/examples/cpp/xtypes/Application.cpp new file mode 100644 index 00000000000..60889bb8ad7 --- /dev/null +++ b/examples/cpp/xtypes/Application.cpp @@ -0,0 +1,62 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file Application.cpp + * + */ + +#include "Application.hpp" + +#include "CLIParser.hpp" +#include "SubscriberApp.hpp" +#include "PublisherApp.hpp" + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace xtypes { + +//! Factory method to create a publisher or subscriber +std::shared_ptr Application::make_app( + const CLIParser::config& config, + const std::string& topic_name) +{ + std::shared_ptr entity; + switch (config.entity) + { + case CLIParser::EntityKind::PUBLISHER: + { + entity = std::make_shared(config, topic_name); + break; + } + case CLIParser::EntityKind::SUBSCRIBER: + { + entity = std::make_shared(config, topic_name); + break; + } + case CLIParser::EntityKind::UNDEFINED: + default: + throw std::runtime_error("Entity initialization failed"); + break; + } + return entity; +} + +} // namespace xtypes +} // namespace examples +} // namespace fastdds +} // namespace eprosima diff --git a/examples/cpp/xtypes/Application.hpp b/examples/cpp/xtypes/Application.hpp new file mode 100644 index 00000000000..94ad75ac43c --- /dev/null +++ b/examples/cpp/xtypes/Application.hpp @@ -0,0 +1,56 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file Application.hpp + * + */ + +#ifndef _FASTDDS_EXAMPLES_XTYPES_APPLICATION_HPP_ +#define _FASTDDS_EXAMPLES_XTYPES_APPLICATION_HPP_ + +#include + +#include "CLIParser.hpp" + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace xtypes { + +class Application +{ +public: + + //! Virtual destructor + virtual ~Application() = default; + + //! Run application + virtual void run() = 0; + + //! Trigger the end of execution + virtual void stop() = 0; + + //! Factory method to create applications based on configuration + static std::shared_ptr make_app( + const CLIParser::config& config, + const std::string& topic_name); +}; + +} // namespace xtypes +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif /* _FASTDDS_EXAMPLES_XTYPES_APPLICATION_HPP_ */ diff --git a/examples/cpp/xtypes/CLIParser.hpp b/examples/cpp/xtypes/CLIParser.hpp new file mode 100644 index 00000000000..0425640a0e1 --- /dev/null +++ b/examples/cpp/xtypes/CLIParser.hpp @@ -0,0 +1,227 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include + +#ifndef _FASTDDS_EXAMPLES_XTYPES_CLI_PARSER_HPP_ +#define _FASTDDS_EXAMPLES_XTYPES_CLI_PARSER_HPP_ + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace xtypes { + +// using dds::Log; + +class CLIParser +{ +public: + + CLIParser() = delete; + + //! Entity kind enumeration + enum class EntityKind : uint8_t + { + PUBLISHER, + SUBSCRIBER, + UNDEFINED + }; + + //! Configuration structure for the application + struct config + { + CLIParser::EntityKind entity = CLIParser::EntityKind::UNDEFINED; + uint16_t samples = 0; + }; + + /** + * @brief Print usage help message and exit with the given return code + * + * @param return_code return code to exit with + * + * @warning This method finishes the execution of the program with the input return code + */ + static void print_help( + uint8_t return_code) + { + std::cout << "Usage: xtypes [options]" << std::endl; + std::cout << "" << std::endl; + std::cout << "Entities:" << std::endl; + std::cout << " publisher Run a publisher entity" << std::endl; + std::cout << " subscriber Run a subscriber entity" << std::endl; + std::cout << "" << std::endl; + std::cout << "Common options:" << std::endl; + std::cout << " -h, --help Print this help message" << std::endl; + std::cout << " -s , --samples Number of samples to send or receive" << std::endl; + std::cout << " [0 <= <= 65535]" << std::endl; + std::cout << " (Default: 0 [unlimited])" << std::endl; + std::exit(return_code); + } + + /** + * @brief Parse the command line options and return the configuration_config object + * + * @param argc number of arguments + * @param argv array of arguments + * @return configuration_config object with the parsed options + * + * @warning This method finishes the execution of the program if the input arguments are invalid + */ + static config parse_cli_options( + int argc, + char* argv[]) + { + config config; + + if (argc < 2) + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "missing entity argument"); + print_help(EXIT_FAILURE); + } + + std::string first_argument = argv[1]; + + if (first_argument == "publisher" ) + { + config.entity = CLIParser::EntityKind::PUBLISHER; + } + else if (first_argument == "subscriber") + { + config.entity = CLIParser::EntityKind::SUBSCRIBER; + } + else if (first_argument == "-h" || first_argument == "--help") + { + print_help(EXIT_SUCCESS); + } + else + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "parsing entity argument " + first_argument); + print_help(EXIT_FAILURE); + } + + for (int i = 2; i < argc; ++i) + { + std::string arg = argv[i]; + if (arg == "-h" || arg == "--help") + { + print_help(EXIT_SUCCESS); + } + else if (arg == "-s" || arg == "--samples") + { + if (i + 1 < argc) + { + try + { + int input = std::stoi(argv[++i]); + if (input < std::numeric_limits::min() || + input > std::numeric_limits::max()) + { + throw std::out_of_range("sample argument out of range"); + } + else + { + if (config.entity == CLIParser::EntityKind::UNDEFINED) + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "entity not specified for --sample argument"); + print_help(EXIT_FAILURE); + } + config.samples = static_cast(input); + } + } + catch (const std::invalid_argument& e) + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "invalid sample argument for " + arg + ": " + e.what()); + print_help(EXIT_FAILURE); + } + catch (const std::out_of_range& e) + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "sample argument out of range for " + arg + ": " + e.what()); + print_help(EXIT_FAILURE); + } + } + else + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "missing argument for " + arg); + print_help(EXIT_FAILURE); + } + } + else + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "unknown option " + arg); + print_help(EXIT_FAILURE); + } + } + + return config; + } + + /** + * @brief Parse the signal number into the signal name + * + * @param signum signal number + * @return std::string signal name + */ + static std::string parse_signal( + const int& signum) + { + switch (signum) + { + case SIGINT: + return "SIGINT"; + case SIGTERM: + return "SIGTERM"; +#ifndef _WIN32 + case SIGQUIT: + return "SIGQUIT"; + case SIGHUP: + return "SIGHUP"; +#endif // _WIN32 + default: + return "UNKNOWN SIGNAL"; + } + } + + /** + * @brief Parse the entity kind into std::string + * + * @param entity entity kind + * @return std::string entity kind + */ + static std::string parse_entity_kind( + const EntityKind& entity) + { + switch (entity) + { + case EntityKind::PUBLISHER: + return "Publisher"; + case EntityKind::SUBSCRIBER: + return "Subscriber"; + case EntityKind::UNDEFINED: + default: + return "Undefined entity"; + } + } + +}; + +} // namespace xtypes +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_EXAMPLES_XTYPES_CLI_PARSER_HPP_ diff --git a/examples/cpp/xtypes/CMakeLists.txt b/examples/cpp/xtypes/CMakeLists.txt new file mode 100644 index 00000000000..3f1088a1697 --- /dev/null +++ b/examples/cpp/xtypes/CMakeLists.txt @@ -0,0 +1,63 @@ +# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.20) + +project(fastdds_xtypes_example VERSION 1 LANGUAGES CXX) + +# Find requirements +if(NOT fastcdr_FOUND) + find_package(fastcdr 2 REQUIRED) +endif() + +if(NOT fastdds_FOUND) + find_package(fastdds 3 REQUIRED) +endif() + +#Check C++11 +include(CheckCXXCompilerFlag) +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + check_cxx_compiler_flag(-std=c++11 SUPPORTS_CXX11) + if(NOT SUPPORTS_CXX11) + message(FATAL_ERROR "Compiler doesn't support C++11") + endif() +endif() + +message(STATUS "Configuring ${PROJECT_NAME}...") +file(GLOB EXAMPLE_SOURCES "*.cpp") + +add_executable(xtypes ${EXAMPLE_SOURCES}) +target_compile_definitions(xtypes PRIVATE + $<$>,$>:__DEBUG> + $<$:__INTERNALDEBUG> # Internal debug activated. + $<$:SHM_TRANSPORT_BUILTIN> # Enable SHM as built-in transport +) +target_link_libraries(xtypes fastdds fastcdr) +install(TARGETS xtypes + RUNTIME DESTINATION ${DATA_INSTALL_DIR}/fastdds/examples/cpp/xtypes/${BIN_INSTALL_DIR}) + +# Copy the XML files over to the build directory +file(GLOB_RECURSE XML_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.xml) +# for each xml file detected +foreach(XML_FILE_COMPLETE_PATH ${XML_FILES}) + # obtain the file name + get_filename_component(XML_FILE ${XML_FILE_COMPLETE_PATH} NAME_WE) + # copy the file from src to build folders + configure_file( + ${XML_FILE_COMPLETE_PATH} # from full src path + ${CMAKE_CURRENT_BINARY_DIR}/${XML_FILE}.xml # to relative build path + COPYONLY) + install(FILES ${XML_FILE_COMPLETE_PATH} + DESTINATION ${DATA_INSTALL_DIR}/fastdds/examples/cpp/xtypes/${BIN_INSTALL_DIR}) +endforeach() diff --git a/examples/cpp/xtypes/PublisherApp.cpp b/examples/cpp/xtypes/PublisherApp.cpp new file mode 100644 index 00000000000..0cc866694ca --- /dev/null +++ b/examples/cpp/xtypes/PublisherApp.cpp @@ -0,0 +1,318 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file PublisherApp.cpp + * + */ + +#include "PublisherApp.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace xtypes { + +PublisherApp::PublisherApp( + const CLIParser::config& config, + const std::string& topic_name) + : participant_(nullptr) + , publisher_(nullptr) + , topic_(nullptr) + , writer_(nullptr) + , matched_(0) + , samples_(config.samples) + , stop_(false) +{ + // Create the type + DynamicType::_ref_type dynamic_type = create_type(); + + if (!dynamic_type) + { + throw std::runtime_error("Error creating dynamic type"); + } + + // Set up the data type with initial values + hello_ = DynamicDataFactory::get_instance()->create_data(dynamic_type); + + if (!hello_) + { + throw std::runtime_error("Error creating dynamic data"); + } + + hello_->set_uint32_value(hello_->get_member_id_by_name("index"), 0); + hello_->set_string_value(hello_->get_member_id_by_name("message"), "Hello xtypes world"); + + // Create the participant + auto factory = DomainParticipantFactory::get_instance(); + participant_ = factory->create_participant_with_default_profile(nullptr, StatusMask::none()); + + if (participant_ == nullptr) + { + throw std::runtime_error("Participant initialization failed"); + } + + // Register the type + TypeSupport type(new DynamicPubSubType(dynamic_type)); + + if (RETCODE_OK != type.register_type(participant_)) + { + throw std::runtime_error("Type registration failed"); + } + + // Create the publisher + PublisherQos pub_qos = PUBLISHER_QOS_DEFAULT; + participant_->get_default_publisher_qos(pub_qos); + publisher_ = participant_->create_publisher(pub_qos); + + if (publisher_ == nullptr) + { + throw std::runtime_error("Publisher initialization failed"); + } + + // Create the topic + TopicQos topic_qos = TOPIC_QOS_DEFAULT; + participant_->get_default_topic_qos(topic_qos); + topic_ = participant_->create_topic(topic_name, type.get_type_name(), topic_qos); + + if (topic_ == nullptr) + { + throw std::runtime_error("Topic initialization failed"); + } + + // Create the data writer + DataWriterQos writer_qos = DATAWRITER_QOS_DEFAULT; + publisher_->get_default_datawriter_qos(writer_qos); + writer_ = publisher_->create_datawriter(topic_, writer_qos, this, StatusMask::all()); + + if (writer_ == nullptr) + { + throw std::runtime_error("DataWriter initialization failed"); + } +} + +PublisherApp::~PublisherApp() +{ + if (nullptr != participant_) + { + // Delete DDS entities contained within the DomainParticipant + if (RETCODE_OK != participant_->delete_contained_entities()) + { + // C++11 dtors default to noexcept + std::cerr << "Error deleting the contained entities." << std::endl; + } + + // Delete DomainParticipant + if (RETCODE_OK != DomainParticipantFactory::get_instance()->delete_participant(participant_)) + { + // C++11 dtors default to noexcept + std::cerr << "Error deleting the participant." << std::endl; + } + } +} + +void PublisherApp::on_publication_matched( + DataWriter* /*writer*/, + const PublicationMatchedStatus& info) +{ + if (info.current_count_change == 1) + { + matched_ = static_cast(info.current_count); + std::cout << "Publisher matched." << std::endl; + cv_.notify_one(); + } + else if (info.current_count_change == -1) + { + matched_ = static_cast(info.current_count); + std::cout << "Publisher unmatched." << std::endl; + } + else + { + std::cout << info.current_count_change + << " is not a valid value for PublicationMatchedStatus current count change" << std::endl; + } +} + +void PublisherApp::run() +{ + uint32_t index = get_uint32_value(hello_, "index"); + std::string message = get_string_value(hello_, "message"); + + while (!is_stopped() && ((samples_ == 0) || (index < samples_))) + { + if (publish()) + { + // Retrieve the new index and message + index = get_uint32_value(hello_, "index"); + message = get_string_value(hello_, "message"); + + std::cout << "Message sent:" << std::endl; + std::cout << " - index: " << index << std::endl; + std::cout << " - message: '" << message << "'" << std::endl; + } + else if (!is_stopped()) + { + index = get_uint32_value(hello_, "index"); + std::cout << "Error sending message with index" << index << std::endl; + } + + // Wait for period or stop event + std::unique_lock period_lock(mutex_); + cv_.wait_for(period_lock, std::chrono::milliseconds(period_ms_), [&]() + { + return is_stopped(); + }); + } +} + +bool PublisherApp::publish() +{ + bool ret = false; + // Wait for the data endpoints discovery + std::unique_lock matched_lock(mutex_); + cv_.wait(matched_lock, [&]() + { + // at least one has been discovered + return ((matched_ > 0) || is_stopped()); + }); + + if (!is_stopped()) + { + // Increase the index by 1 + uint32_t index = get_uint32_value(hello_, "index"); + set_uint32_value(hello_, "index", index); + + // Publish the sample + ret = writer_->write(&hello_); + } + return ret; +} + +bool PublisherApp::is_stopped() +{ + return stop_.load(); +} + +void PublisherApp::stop() +{ + stop_.store(true); + cv_.notify_one(); +} + +DynamicType::_ref_type PublisherApp::create_type() +{ + // Define a struct type with various primitive members + TypeDescriptor::_ref_type type_descriptor {traits::make_shared()}; + type_descriptor->kind(TK_STRUCTURE); + type_descriptor->name("HelloWorld"); + DynamicTypeBuilder::_ref_type struct_builder {DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)}; + + if (!struct_builder) + { + throw std::runtime_error("Error creating type builder"); + } + + // Add index member + MemberDescriptor::_ref_type index_member_descriptor {traits::make_shared()}; + index_member_descriptor->name("index"); + index_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_UINT32)); + + if (RETCODE_OK != struct_builder->add_member(index_member_descriptor)) + { + throw std::runtime_error("Error adding index member"); + } + + // Add message member + MemberDescriptor::_ref_type message_member_descriptor {traits::make_shared()}; + message_member_descriptor->name("message"); + message_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->create_string_type(static_cast( + LENGTH_UNLIMITED))->build()); + + if (!message_member_descriptor) + { + throw std::runtime_error("Error creating string type"); + } + + if (RETCODE_OK != struct_builder->add_member(message_member_descriptor)) + { + throw std::runtime_error("Error adding message member"); + } + + // Build the type + return struct_builder->build(); +} + +uint32_t PublisherApp::get_uint32_value( + const DynamicData::_ref_type data, + const std::string& member_name) +{ + uint32_t ui32 {0}; + + if (RETCODE_OK != data->get_uint32_value(ui32, data->get_member_id_by_name(member_name))) + { + auto error_msg = "Error getting " + member_name + " value"; + throw std::runtime_error(error_msg); + } + + return ui32; +} + +void PublisherApp::set_uint32_value( + DynamicData::_ref_type data, + const std::string& member_name, + const uint32_t value) +{ + if (RETCODE_OK != data->set_uint32_value(data->get_member_id_by_name(member_name), value + 1)) + { + auto error_msg = "Error setting " + member_name + " value"; + throw std::runtime_error(error_msg); + } +} + +std::string PublisherApp::get_string_value( + const DynamicData::_ref_type data, + const std::string& member_name) +{ + std::string str; + + if (RETCODE_OK != data->get_string_value(str, data->get_member_id_by_name(member_name))) + { + auto error_msg = "Error getting " + member_name + " value"; + throw std::runtime_error(error_msg); + } + + return str; +} + +} // namespace xtypes +} // namespace examples +} // namespace fastdds +} // namespace eprosima diff --git a/examples/cpp/xtypes/PublisherApp.hpp b/examples/cpp/xtypes/PublisherApp.hpp new file mode 100644 index 00000000000..01d5485f707 --- /dev/null +++ b/examples/cpp/xtypes/PublisherApp.hpp @@ -0,0 +1,117 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file PublisherApp.hpp + * + */ + +#ifndef _FASTDDS_EXAMPLES_XTYPES_PUBLISHER_APP_HPP_ +#define _FASTDDS_EXAMPLES_XTYPES_PUBLISHER_APP_HPP_ + +#include + +#include +#include +#include +#include +#include + +#include "Application.hpp" +#include "CLIParser.hpp" + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace xtypes { + +class PublisherApp : public Application, public DataWriterListener +{ +public: + + PublisherApp( + const CLIParser::config& config, + const std::string& topic_name); + + ~PublisherApp(); + + //! Publisher matched method + void on_publication_matched( + DataWriter* writer, + const PublicationMatchedStatus& info) override; + + //! Run publisher + void run() override; + + //! Stop publisher + void stop() override; + +private: + + //! Return the current state of execution + bool is_stopped(); + + //! Publish a sample + bool publish(); + + //! Create the dynamic type used by the PublisherApp + static DynamicType::_ref_type create_type(); + + //! Auxilary function to get a uint32_t value from a DynamicData object + static uint32_t get_uint32_value( + const DynamicData::_ref_type data, + const std::string& member_name); + + //! Auxilary function to set a uint32_t value in a DynamicData object + static void set_uint32_value( + DynamicData::_ref_type data, + const std::string& member_name, + const uint32_t value); + + //! Auxilary function to get a string value from a DynamicData object + static std::string get_string_value( + const DynamicData::_ref_type data, + const std::string& member_name); + + DynamicData::_ref_type hello_; + + DomainParticipant* participant_; + + Publisher* publisher_; + + Topic* topic_; + + DataWriter* writer_; + + int16_t matched_; + + uint16_t samples_; + + std::mutex mutex_; + + std::condition_variable cv_; + + std::atomic stop_; + + const uint32_t period_ms_ = 100; // in ms +}; + +} // namespace xtypes +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif /* _FASTDDS_EXAMPLES_XTYPES_PUBLISHER_APP_HPP_ */ diff --git a/examples/cpp/xtypes/README.md b/examples/cpp/xtypes/README.md new file mode 100644 index 00000000000..abb9e062146 --- /dev/null +++ b/examples/cpp/xtypes/README.md @@ -0,0 +1,144 @@ +# X-Types example + +The *eProsima Fast DDS xtypes* example is an application intended to demonstrate the use of both the Dynamic Types binding and the remote type discovery features. + +This example is part of the suite of examples designed by eProsima that aims to illustrate the features and possible configurations of DDS deployments through *eProsima Fast DDS*. + +In this case, the *xtypes* example describes a simple deployment of a Fast DDS publisher and subscriber, withe the particularity of it leveraging X-Types. + +* [Description of the example](#description-of-the-example) +* [Run the example](#run-the-example) +* [Wait-set subscriber](#wait-set-subscriber) +* [XML profile playground](#xml-profile-playground) + +## Description of the example + +Each example application (publisher and subscriber) creates different nested DDS entities: domain participant, publisher, and data writer; and domain participant, subscriber, and data reader, respectively. +In both cases, the three DDS entities (domain participant, publisher/subscriber and data writer/data reader) load their default configuration from the environment. +If the environment does not specify the expected configuration, they take the default configuration per entity. +For further information regarding the configuration environment, please refer to the *[XML profile playground](#xml-profile-playground)* section. + +The particularity of this example resides in the use of X-Types, which allows the definition of types at runtime. +In this case: + +1. The publisher application defines a type at runtime using the Fast DDS Dynamic Types API. +2. The subscriber application discovers the type defined by the publisher and uses it to create a data reader, introspect the type, and print the received data. + +It is important to note that this example is fully type compatible with the [Hello world](../hello_world/README.md) example, meaning that the publisher and subscriber applications can be run interchangeably with the *hello world* example applications. + +## Run the example + +To launch this example, two different terminals are required. +One of them will run the publisher example application, and the other will run the subscriber application. + +### Hello world publisher + +* Ubuntu ( / MacOS ) + + ```shell + user@machine:example_path$ ./xtypes publisher + Publisher running. Please press Ctrl+C to stop the Publisher at any time. + ``` + +* Windows + + ```powershell + example_path> xtypes.exe publisher + Publisher running. Please press Ctrl+C to stop the Publisher at any time. + ``` + +### Hello world subscriber + +* Ubuntu ( / MacOS ) + + ```shell + user@machine:example_path$ ./xtypes subscriber + Subscriber running. Please press Ctrl+C to stop the Subscriber at any time. + ``` + +* Windows + + ```powershell + example_path> xtypes.exe subscriber + Subscriber running. Please press Ctrl+C to stop the Subscriber at any time. + ``` + +All the example available flags can be queried running the executable with the ``-h`` or ``--help`` flag. + +### Expected output + +Regardless of which application is run first, since the publisher will not start sending data until a subscriber is discovered, the expected output both for publishers and subscribers is a first displayed message acknowledging the match, followed by the amount of samples sent or received until Ctrl+C is pressed. + +### Hello world publisher + +```shell +Publisher running for 10 samples. Please press Ctrl+C to stop the Publisher at any time. +Publisher matched. +Message sent: + - index: 1 + - message: 'Hello xtypes world' +Message sent: + - index: 2 + - message: 'Hello xtypes world' +Message sent: + - index: 3 + - message: 'Hello xtypes world' +... +``` + +### Hello world subscriber + +```shell +Subscriber running. Please press Ctrl+C to stop the Subscriber at any time. +Subscriber matched. +Message received: + - index: 1 + - message: 'Hello xtypes world' +Message received: + - index: 2 + - message: 'Hello xtypes world' +Message received: + - index: 3 + - message: 'Hello xtypes world' +... +``` + +When Ctrl+C is pressed to stop one of the applications, the other one will show the unmatched status, displaying an informative message, and it will stop sending / receiving messages. +The following is a possible output of the publisher application when stopping the subscriber app. + +```shell +... +Message sent: + - index: 5 + - message: 'Hello xtypes world' +Message sent: + - index: 6 + - message: 'Hello xtypes world' +Publisher unmatched. +``` + +## XML profile playground + +The *eProsima Fast DDS* entities can be configured through an XML profile from the environment. +This is accomplished by setting the environment variable ``FASTDDS_DEFAULT_PROFILES_FILE`` to path to the XML profiles file: + +* Ubuntu ( / MacOS ) + + ```shell + user@machine:example_path$ export FASTDDS_DEFAULT_PROFILES_FILE=xtypes_profile.xml + ``` + +* Windows + + ```powershell + example_path> set FASTDDS_DEFAULT_PROFILES_FILE=xtypes_profile.xml + ``` + +The example provides with an XML profiles files with certain QoS: + +- Reliable reliability: avoid sample loss. +- Transient local durability: enable late-join subscriber applications to receive previous samples. +- Keep-last history with high depth: ensure certain amount of previous samples for late-joiners. + +Applying different configurations to the entities will change to a greater or lesser extent how the application behaves in relation to sample management. +Even when these settings affect the behavior of the sample management, the applications' output will be the similar. diff --git a/examples/cpp/xtypes/SubscriberApp.cpp b/examples/cpp/xtypes/SubscriberApp.cpp new file mode 100644 index 00000000000..46335e5c449 --- /dev/null +++ b/examples/cpp/xtypes/SubscriberApp.cpp @@ -0,0 +1,326 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file SubscriberApp.cpp + * + */ + +#include "SubscriberApp.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CLIParser.hpp" +#include "Application.hpp" + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace xtypes { + +SubscriberApp::SubscriberApp( + const CLIParser::config& config, + const std::string& topic_name) + : participant_(nullptr) + , subscriber_(nullptr) + , topic_name_(topic_name) + , topic_(nullptr) + , reader_(nullptr) + , samples_(config.samples) + , received_samples_(0) + , stop_(false) +{ + // Create the participant + auto factory = DomainParticipantFactory::get_instance(); + + StatusMask participant_mask = StatusMask::data_available(); + participant_mask << eprosima::fastdds::dds::StatusMask::subscription_matched(); + + participant_ = factory->create_participant_with_default_profile(this, participant_mask); + + if (participant_ == nullptr) + { + throw std::runtime_error("Participant initialization failed"); + } +} + +SubscriberApp::~SubscriberApp() +{ + if (nullptr != participant_) + { + // Delete DDS entities contained within the DomainParticipant + if (RETCODE_OK != participant_->delete_contained_entities()) + { + // C++11 dtors default to noexcept + std::cerr << "Error deleting contained entities" << std::endl; + } + + // Delete DomainParticipant + if (RETCODE_OK != DomainParticipantFactory::get_instance()->delete_participant(participant_)) + { + // C++11 dtors default to noexcept + std::cerr << "Error deleting participant" << std::endl; + } + } +} + +void SubscriberApp::on_subscription_matched( + DataReader* /*reader*/, + const SubscriptionMatchedStatus& info) +{ + if (info.current_count_change == 1) + { + std::cout << "Subscriber matched." << std::endl; + } + else if (info.current_count_change == -1) + { + std::cout << "Subscriber unmatched." << std::endl; + } + else + { + std::cout << info.current_count_change + << " is not a valid value for SubscriptionMatchedStatus current count change" << std::endl; + } +} + +void SubscriberApp::on_data_available( + DataReader* reader) +{ + SampleInfo info; + while (RETCODE_OK == reader->take_next_sample(&hello_, &info)) + { + if (ALIVE_INSTANCE_STATE == info.instance_state && info.valid_data && + TK_STRUCTURE == remote_type_object_.complete()._d()) + { + // Increase received samples + received_samples_++; + + std::cout << "Message received:" << std::endl; + + // Get all the members by name + DynamicTypeMembersByName members; + + if (RETCODE_OK != hello_->type()->get_all_members_by_name(members)) + { + throw std::runtime_error("Error getting members by name"); + } + + // Print all the members + for (auto elem : members) + { + std::cout << " - " << elem.first << ": "; + + MemberDescriptor::_ref_type member_descriptor = {traits::make_shared()}; + + if (RETCODE_OK != elem.second->get_descriptor(member_descriptor)) + { + throw std::runtime_error("Error getting member descriptor for member"); + } + + switch (member_descriptor->type()->get_kind()) + { + case TK_UINT32: + { + uint32_t uint_data {0}; + + if (RETCODE_OK != hello_->get_uint32_value(uint_data, elem.second->get_id())) + { + throw std::runtime_error("Error getting uint32 value"); + } + + std::cout << uint_data << std::endl; + break; + } + case TK_STRING8: + { + std::string str_data {0}; + + if (RETCODE_OK != hello_->get_string_value(str_data, elem.second->get_id())) + { + throw std::runtime_error("Error getting string value"); + } + + std::cout << "'" << str_data << "'" << std::endl; + break; + } + default: + { + std::cout << "Unhandled type " << member_descriptor->type()->get_kind() << std::endl; + break; + } + } + } + } + + // Stop if the sample limit has been reached + if (samples_ > 0 && (received_samples_ >= samples_)) + { + stop(); + } + } +} + +void SubscriberApp::on_data_writer_discovery( + DomainParticipant* /*participant*/, + eprosima::fastrtps::rtps::WriterDiscoveryInfo&& info, + bool& should_be_ignored) +{ + // We don't want to ignore the writer + should_be_ignored = false; + + // Check if the discovered topic is the one we are interested in + if (topic_name_ == info.info.topicName().to_string()) + { + // Get remote type information and use it to retrieve the type object + auto type_info = info.info.type_information().type_information; + auto type_id = type_info.complete().typeid_with_size().type_id(); + + if (RETCODE_OK != DomainParticipantFactory::get_instance()->type_object_registry().get_type_object( + type_id, + remote_type_object_)) + { + std::cout << "Cannot get discovered type from registry:" << std::endl; + std::cout << " - Topic name: " << info.info.topicName() << std::endl; + std::cout << " - Type name: " << info.info.typeName() << std::endl; + } + + // Notify run thread that type has been discovered + type_discovered_.store(true); + terminate_cv_.notify_one(); + } +} + +void SubscriberApp::run() +{ + // Wait for type discovery + { + std::unique_lock lck(terminate_cv_mtx_); + terminate_cv_.wait(lck, [&] + { + return is_stopped() || type_discovered_.load(); + }); + } + + // Create entities unless we need to exit + if (type_discovered_) + { + initialize_entities(); + } + + // Wait for shutdown command + { + std::unique_lock lck(terminate_cv_mtx_); + terminate_cv_.wait(lck, [&] + { + return is_stopped(); + }); + } +} + +bool SubscriberApp::is_stopped() +{ + return stop_.load(); +} + +void SubscriberApp::stop() +{ + stop_.store(true); + terminate_cv_.notify_all(); +} + +void SubscriberApp::initialize_entities() +{ + // Register remotely discovered type + DynamicTypeBuilder::_ref_type type_builder = DynamicTypeBuilderFactory::get_instance()->create_type_w_type_object( + remote_type_object_); + + if (!type_builder) + { + throw std::runtime_error("Error creating type builder"); + } + + // Build the type + remote_type_ = type_builder->build(); + + if (!remote_type_) + { + throw std::runtime_error("Error building type"); + } + + TypeSupport dyn_type_support(new DynamicPubSubType(remote_type_)); + + if (RETCODE_OK != dyn_type_support.register_type(participant_)) + { + throw std::runtime_error("Error registering type"); + } + + // Initialize the DynamicData + hello_ = DynamicDataFactory::get_instance()->create_data(remote_type_); + + if (!hello_) + { + throw std::runtime_error("Error creating DynamicData"); + } + + // Create the subscriber + SubscriberQos sub_qos = SUBSCRIBER_QOS_DEFAULT; + participant_->get_default_subscriber_qos(sub_qos); + subscriber_ = participant_->create_subscriber(sub_qos); + + if (nullptr == subscriber_) + { + throw std::runtime_error("Subscriber initialization failed"); + } + + // Create the topic + TopicQos topic_qos = TOPIC_QOS_DEFAULT; + participant_->get_default_topic_qos(topic_qos); + topic_ = participant_->create_topic(topic_name_, dyn_type_support.get_type_name(), topic_qos); + + if (nullptr == topic_) + { + throw std::runtime_error("Topic initialization failed"); + } + + // Create the reader + DataReaderQos reader_qos = DATAREADER_QOS_DEFAULT; + subscriber_->get_default_datareader_qos(reader_qos); + reader_ = subscriber_->create_datareader(topic_, reader_qos); + + if (nullptr == reader_) + { + throw std::runtime_error("DataReader initialization failed"); + } +} + +} // namespace xtypes +} // namespace examples +} // namespace fastdds +} // namespace eprosima diff --git a/examples/cpp/xtypes/SubscriberApp.hpp b/examples/cpp/xtypes/SubscriberApp.hpp new file mode 100644 index 00000000000..951b94694ea --- /dev/null +++ b/examples/cpp/xtypes/SubscriberApp.hpp @@ -0,0 +1,115 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file SubscriberApp.hpp + * + */ + +#ifndef _FASTDDS_EXAMPLES_XTYPES_SUBSCRIBER_APP_HPP_ +#define _FASTDDS_EXAMPLES_XTYPES_SUBSCRIBER_APP_HPP_ + +#include + +#include +#include +#include +#include +#include +#include + +#include "CLIParser.hpp" +#include "Application.hpp" + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace xtypes { + +class SubscriberApp : public Application, public DomainParticipantListener +{ +public: + + SubscriberApp( + const CLIParser::config& config, + const std::string& topic_name); + + ~SubscriberApp(); + + //! Subscription callback + void on_data_available( + DataReader* reader) override; + + //! Subscriber matched method + void on_subscription_matched( + DataReader* reader, + const SubscriptionMatchedStatus& info) override; + + void on_data_writer_discovery( + DomainParticipant* participant, + eprosima::fastrtps::rtps::WriterDiscoveryInfo&& info, + bool& should_be_ignored) override; + + //! Run subscriber + void run() override; + + //! Trigger the end of execution + void stop() override; + +private: + + //! Return the current state of execution + bool is_stopped(); + + //! Function to initialize the entities once the type is discovered + void initialize_entities(); + + DynamicData::_ref_type hello_; + + ::xtypes::TypeObject remote_type_object_; + + DynamicType::_ref_type remote_type_; + + DomainParticipant* participant_; + + Subscriber* subscriber_; + + const std::string topic_name_; + + Topic* topic_; + + DataReader* reader_; + + uint16_t samples_; + + uint16_t received_samples_; + + std::atomic type_discovered_; + + std::atomic stop_; + + mutable std::mutex terminate_cv_mtx_; + + std::condition_variable terminate_cv_; + +}; + +} // namespace xtypes +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif /* _FASTDDS_EXAMPLES_XTYPES_SUBSCRIBER_APP_HPP_ */ diff --git a/examples/cpp/xtypes/main.cpp b/examples/cpp/xtypes/main.cpp new file mode 100644 index 00000000000..764bea3db43 --- /dev/null +++ b/examples/cpp/xtypes/main.cpp @@ -0,0 +1,96 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file main.cpp + * + */ + +#include +#include +#include +#include + +#include + +#include "Application.hpp" +#include "CLIParser.hpp" + +using eprosima::fastdds::dds::Log; + +using namespace eprosima::fastdds::examples::xtypes; + +std::function stop_app_handler; +void signal_handler( + int signum) +{ + stop_app_handler(signum); +} + +int main( + int argc, + char** argv) +{ + auto ret = EXIT_SUCCESS; + const std::string topic_name = "hello_world_topic"; + CLIParser::config config = CLIParser::parse_cli_options(argc, argv); + + std::string app_name = CLIParser::parse_entity_kind(config.entity); + std::shared_ptr app; + + try + { + app = Application::make_app(config, topic_name); + } + catch (const std::runtime_error& e) + { + EPROSIMA_LOG_ERROR(app_name, e.what()); + ret = EXIT_FAILURE; + } + + if (EXIT_FAILURE != ret) + { + std::thread thread(&Application::run, app); + + if (config.samples == 0) + { + std::cout << app_name << " running. Please press Ctrl+C to stop the " + << app_name << " at any time." << std::endl; + } + else + { + std::cout << app_name << " running for " << config.samples << " samples. Please press Ctrl+C to stop the " + << app_name << " at any time." << std::endl; + } + + stop_app_handler = [&](int signum) + { + std::cout << "\n" << CLIParser::parse_signal(signum) << " received, stopping " << app_name + << " execution." << std::endl; + app->stop(); + }; + + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + #ifndef _WIN32 + signal(SIGQUIT, signal_handler); + signal(SIGHUP, signal_handler); + #endif // _WIN32 + + thread.join(); + } + + Log::Reset(); + return ret; +} diff --git a/examples/cpp/xtypes/xtypes_profile.xml b/examples/cpp/xtypes/xtypes_profile.xml new file mode 100644 index 00000000000..62ae0364a04 --- /dev/null +++ b/examples/cpp/xtypes/xtypes_profile.xml @@ -0,0 +1,52 @@ + + + + 0 + + xtypes_participant + + + + + + TRANSIENT_LOCAL + + + RELIABLE + + + + + KEEP_LAST + 100 + + + 100 + 1 + 100 + + + + + + + + TRANSIENT_LOCAL + + + RELIABLE + + + + + KEEP_LAST + 100 + + + 100 + 1 + 100 + + + + From 4dba0fd94e7f1069931716300f06461f42ac6653 Mon Sep 17 00:00:00 2001 From: eduponz Date: Mon, 10 Jun 2024 14:43:54 +0200 Subject: [PATCH 2/6] Refs #21099: Add test Signed-off-by: eduponz --- test/examples/test_xtypes.py | 41 ++++++++++++++++++++++ test/examples/xtypes.compose.yml | 60 ++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 test/examples/test_xtypes.py create mode 100644 test/examples/xtypes.compose.yml diff --git a/test/examples/test_xtypes.py b/test/examples/test_xtypes.py new file mode 100644 index 00000000000..8bf82e8e0a1 --- /dev/null +++ b/test/examples/test_xtypes.py @@ -0,0 +1,41 @@ +import subprocess +import pytest + +def test_xtypes(): + """.""" + ret = False + out = '' + try: + out = subprocess.check_output( + '@DOCKER_EXECUTABLE@ compose -f xtypes.compose.yml up', + stderr=subprocess.STDOUT, + shell=True, + timeout=30 + ).decode().split('\n') + + sent = 0 + received = 0 + for line in out: + if 'sent' in line or 'SENT' in line: + sent += 1 + continue + + if 'received' in line or 'RECEIVED' in line: + received += 1 + continue + + if sent != 0 and received != 0 and sent * 3 == received: + ret = True + else: + print('ERROR: sent: ' + str(sent) + ', but received: ' + str(received) + + ' (expected: ' + str(sent * 3) + ')') + raise subprocess.CalledProcessError(1, '') + + except subprocess.CalledProcessError: + for l in out: + print(l) + except subprocess.TimeoutExpired: + print('TIMEOUT') + print(out) + + assert(ret) diff --git a/test/examples/xtypes.compose.yml b/test/examples/xtypes.compose.yml new file mode 100644 index 00000000000..146d5e2b0f2 --- /dev/null +++ b/test/examples/xtypes.compose.yml @@ -0,0 +1,60 @@ +version: "3" + +services: + xtypes-subscriber: + image: @DOCKER_IMAGE_NAME@ + volumes: + - @PROJECT_BINARY_DIR@:@PROJECT_BINARY_DIR@ + - @fastcdr_LIB_DIR@:@fastcdr_LIB_DIR@ + @TINYXML2_LIB_DIR_COMPOSE_VOLUME@ + environment: + # TODO(eduponz): LD_LIBRARY_PATH is not the correct variable for Windows + LD_LIBRARY_PATH: @PROJECT_BINARY_DIR@/src/cpp:@fastcdr_LIB_DIR@@TINYXML2_LIB_DIR_COMPOSE_LD_LIBRARY_PATH@ + EXAMPLE_DIR: @PROJECT_BINARY_DIR@/examples/cpp/xtypes@FILE_EXTENSION@ + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/xtypes/xtypes_profile.xml + command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/xtypes@FILE_EXTENSION@ subscriber --samples 20" + + xtypes-subscriber-publisher: + image: @DOCKER_IMAGE_NAME@ + volumes: + - @PROJECT_BINARY_DIR@:@PROJECT_BINARY_DIR@ + - @fastcdr_LIB_DIR@:@fastcdr_LIB_DIR@ + @TINYXML2_LIB_DIR_COMPOSE_VOLUME@ + environment: + # TODO(eduponz): LD_LIBRARY_PATH is not the correct variable for Windows + LD_LIBRARY_PATH: @PROJECT_BINARY_DIR@/src/cpp:@fastcdr_LIB_DIR@@TINYXML2_LIB_DIR_COMPOSE_LD_LIBRARY_PATH@ + EXAMPLE_DIR: @PROJECT_BINARY_DIR@/examples/cpp/xtypes + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/xtypes/xtypes_profile.xml + command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/xtypes@FILE_EXTENSION@ subscriber --samples 20 & $${EXAMPLE_DIR}/xtypes@FILE_EXTENSION@ publisher --samples 10" + depends_on: + - xtypes-subscriber + - helloworld-subscriber + + helloworld-subscriber: + image: @DOCKER_IMAGE_NAME@ + volumes: + - @PROJECT_BINARY_DIR@:@PROJECT_BINARY_DIR@ + - @fastcdr_LIB_DIR@:@fastcdr_LIB_DIR@ + @TINYXML2_LIB_DIR_COMPOSE_VOLUME@ + environment: + # TODO(eduponz): LD_LIBRARY_PATH is not the correct variable for Windows + LD_LIBRARY_PATH: @PROJECT_BINARY_DIR@/src/cpp:@fastcdr_LIB_DIR@@TINYXML2_LIB_DIR_COMPOSE_LD_LIBRARY_PATH@ + EXAMPLE_DIR: @PROJECT_BINARY_DIR@/examples/cpp/hello_world@FILE_EXTENSION@ + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/hello_world/hello_world_profile.xml + command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/hello_world@FILE_EXTENSION@ subscriber --samples 20" + + helloworld-publisher: + image: @DOCKER_IMAGE_NAME@ + volumes: + - @PROJECT_BINARY_DIR@:@PROJECT_BINARY_DIR@ + - @fastcdr_LIB_DIR@:@fastcdr_LIB_DIR@ + @TINYXML2_LIB_DIR_COMPOSE_VOLUME@ + environment: + # TODO(eduponz): LD_LIBRARY_PATH is not the correct variable for Windows + LD_LIBRARY_PATH: @PROJECT_BINARY_DIR@/src/cpp:@fastcdr_LIB_DIR@@TINYXML2_LIB_DIR_COMPOSE_LD_LIBRARY_PATH@ + EXAMPLE_DIR: @PROJECT_BINARY_DIR@/examples/cpp/hello_world@FILE_EXTENSION@ + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/hello_world/hello_world_profile.xml + command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/hello_world@FILE_EXTENSION@ publisher --samples 10" + depends_on: + - xtypes-subscriber + - helloworld-subscriber From a498759ca359487e13800c250c5be13e4732d4ef Mon Sep 17 00:00:00 2001 From: eduponz Date: Mon, 10 Jun 2024 14:52:19 +0200 Subject: [PATCH 3/6] Refs #21099: Remove old TODO Signed-off-by: eduponz --- test/examples/configuration.compose.yml | 1 - test/examples/hello_world.compose.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/test/examples/configuration.compose.yml b/test/examples/configuration.compose.yml index ed20fcdd719..aab1d88e7c6 100644 --- a/test/examples/configuration.compose.yml +++ b/test/examples/configuration.compose.yml @@ -1,4 +1,3 @@ -# FASTDDS_TODO_BEFORE(3, 0, "This compose file should be used for the future configuration example"); version: "3" services: diff --git a/test/examples/hello_world.compose.yml b/test/examples/hello_world.compose.yml index 8d03da8134a..ecb2f82bfd5 100644 --- a/test/examples/hello_world.compose.yml +++ b/test/examples/hello_world.compose.yml @@ -1,4 +1,3 @@ -# FASTDDS_TODO_BEFORE(3, 0, "This compose file should be used for the future configuration example"); version: "3" services: From e4c09d46ab4f6c6620e17bbf59075bd815c63c3c Mon Sep 17 00:00:00 2001 From: eduponz Date: Mon, 10 Jun 2024 14:59:00 +0200 Subject: [PATCH 4/6] Refs #21099: Add missing licenses Signed-off-by: eduponz --- test/examples/configuration.compose.yml | 13 +++++++++++++ test/examples/hello_world.compose.yml | 13 +++++++++++++ test/examples/test_configuration.py | 14 ++++++++++++++ test/examples/test_hello_world.py | 14 ++++++++++++++ test/examples/test_xtypes.py | 15 ++++++++++++++- test/examples/xtypes.compose.yml | 13 +++++++++++++ 6 files changed, 81 insertions(+), 1 deletion(-) diff --git a/test/examples/configuration.compose.yml b/test/examples/configuration.compose.yml index aab1d88e7c6..f35dd6c3c00 100644 --- a/test/examples/configuration.compose.yml +++ b/test/examples/configuration.compose.yml @@ -1,3 +1,16 @@ +# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. version: "3" services: diff --git a/test/examples/hello_world.compose.yml b/test/examples/hello_world.compose.yml index ecb2f82bfd5..affe3533b8a 100644 --- a/test/examples/hello_world.compose.yml +++ b/test/examples/hello_world.compose.yml @@ -1,3 +1,16 @@ +# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. version: "3" services: diff --git a/test/examples/test_configuration.py b/test/examples/test_configuration.py index 4dc5b185998..74abb86aa65 100644 --- a/test/examples/test_configuration.py +++ b/test/examples/test_configuration.py @@ -1,3 +1,17 @@ +# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import subprocess import pytest diff --git a/test/examples/test_hello_world.py b/test/examples/test_hello_world.py index ddc4b290437..73fbbaa9568 100644 --- a/test/examples/test_hello_world.py +++ b/test/examples/test_hello_world.py @@ -1,3 +1,17 @@ +# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import subprocess def test_hello_world(): diff --git a/test/examples/test_xtypes.py b/test/examples/test_xtypes.py index 8bf82e8e0a1..9c59355ac0e 100644 --- a/test/examples/test_xtypes.py +++ b/test/examples/test_xtypes.py @@ -1,5 +1,18 @@ +# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import subprocess -import pytest def test_xtypes(): """.""" diff --git a/test/examples/xtypes.compose.yml b/test/examples/xtypes.compose.yml index 146d5e2b0f2..d321cf98c2f 100644 --- a/test/examples/xtypes.compose.yml +++ b/test/examples/xtypes.compose.yml @@ -1,3 +1,16 @@ +# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. version: "3" services: From 5a7aec185bf513b38efecc211b24e9a7f8e48acc Mon Sep 17 00:00:00 2001 From: eduponz Date: Mon, 10 Jun 2024 15:12:55 +0200 Subject: [PATCH 5/6] Refs #21099: Add example to versions Signed-off-by: eduponz --- versions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/versions.md b/versions.md index 974d0d18d68..e38b5bb94a4 100644 --- a/versions.md +++ b/versions.md @@ -42,6 +42,7 @@ Forthcoming * Hello world example with wait-sets and environment XML profiles. * Configuration example that condenses multiple QoS examples. Multiple configurations allowed through argument parsing. * Custom payload pool example that uses a user-defined payload pool instead of the default + * X-Types example with dynamic type discovery and Hello world example compatibility. * Removed `TypeConsistencyQos` from DataReader, and included `TypeConsistencyEnforcementQosPolicy` and `DataRepresentationQosPolicy` * Added new `flow_controller_descriptor_list` XML configuration, remove `ThroughtputController`. * Migrate `#define`s within `BuiltinEndpoints.hpp` to namespaced `constexpr` variables. From f37a3275197a7fdbd211d5c032846278b3deb989 Mon Sep 17 00:00:00 2001 From: eduponz Date: Fri, 14 Jun 2024 11:10:27 +0200 Subject: [PATCH 6/6] Refs #21099: Apply Mario's suggestions Signed-off-by: eduponz --- examples/cpp/xtypes/CLIParser.hpp | 4 +--- examples/cpp/xtypes/main.cpp | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/cpp/xtypes/CLIParser.hpp b/examples/cpp/xtypes/CLIParser.hpp index 0425640a0e1..feb8019c3e8 100644 --- a/examples/cpp/xtypes/CLIParser.hpp +++ b/examples/cpp/xtypes/CLIParser.hpp @@ -26,8 +26,6 @@ namespace fastdds { namespace examples { namespace xtypes { -// using dds::Log; - class CLIParser { public: @@ -59,7 +57,7 @@ class CLIParser static void print_help( uint8_t return_code) { - std::cout << "Usage: xtypes [options]" << std::endl; + std::cout << "Usage: xtypes [options]" << std::endl; std::cout << "" << std::endl; std::cout << "Entities:" << std::endl; std::cout << " publisher Run a publisher entity" << std::endl; diff --git a/examples/cpp/xtypes/main.cpp b/examples/cpp/xtypes/main.cpp index 764bea3db43..214e01d55cd 100644 --- a/examples/cpp/xtypes/main.cpp +++ b/examples/cpp/xtypes/main.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include