diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b8a62e2608..e76f772414b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -571,8 +571,8 @@ option(INSTALL_EXAMPLES "Install example" OFF) if(INSTALL_EXAMPLES) # Install examples install(DIRECTORY ${PROJECT_SOURCE_DIR}/examples/cpp - DESTINATION examples/ - COMPONENT examples + DESTINATION ${DATA_INSTALL_DIR}/fastdds/examples/ + COMPONENT ${DATA_INSTALL_DIR}/fastdds/examples PATTERN "examples/CMakeLists.txt" EXCLUDE ) endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d63b96f986c..d41f21bdcab 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,3 +15,4 @@ set(fastdds_FOUND TRUE) add_subdirectory(cpp/dds) add_subdirectory(cpp/rtps) +add_subdirectory(cpp/hello_world) diff --git a/examples/cpp/dds/CMakeLists.txt b/examples/cpp/dds/CMakeLists.txt index 0ade7a00048..ac08a223219 100644 --- a/examples/cpp/dds/CMakeLists.txt +++ b/examples/cpp/dds/CMakeLists.txt @@ -24,7 +24,6 @@ add_subdirectory(DiscoveryServerExample) add_subdirectory(DynamicHelloWorldExample) add_subdirectory(Filtering) add_subdirectory(FlowControlExample) -add_subdirectory(HelloWorldExample) add_subdirectory(HelloWorldExampleDataSharing) add_subdirectory(HelloWorldExampleSharedMem) add_subdirectory(HelloWorldExampleTCP) diff --git a/examples/cpp/dds/HelloWorldExample/CMakeLists.txt b/examples/cpp/dds/HelloWorldExample/CMakeLists.txt deleted file mode 100644 index 87fbf8dc2b4..00000000000 --- a/examples/cpp/dds/HelloWorldExample/CMakeLists.txt +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2019 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(DDSHelloWorldExample 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 HelloWorld example...") -file(GLOB DDS_HELLOWORLD_EXAMPLE_SOURCES_CXX "*.cxx") -file(GLOB DDS_HELLOWORLD_EXAMPLE_SOURCES_CPP "*.cpp") - -add_executable(DDSHelloWorldExample ${DDS_HELLOWORLD_EXAMPLE_SOURCES_CXX} ${DDS_HELLOWORLD_EXAMPLE_SOURCES_CPP}) -target_compile_definitions(DDSHelloWorldExample PRIVATE - $<$>,$>:__DEBUG> - $<$:__INTERNALDEBUG> # Internal debug activated. -) -target_link_libraries(DDSHelloWorldExample fastdds fastcdr fastdds::optionparser) -install(TARGETS DDSHelloWorldExample - RUNTIME DESTINATION examples/cpp/dds/HelloWorldExample/${BIN_INSTALL_DIR}) diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorldPublisher.cpp b/examples/cpp/dds/HelloWorldExample/HelloWorldPublisher.cpp deleted file mode 100644 index 1a2bdfce472..00000000000 --- a/examples/cpp/dds/HelloWorldExample/HelloWorldPublisher.cpp +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2016 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 HelloWorldPublisher.cpp - * - */ - -#include "HelloWorldPublisher.h" - -#include - -#include -#include -#include -#include -#include - -using namespace eprosima::fastdds::dds; - -HelloWorldPublisher::HelloWorldPublisher() - : participant_(nullptr) - , publisher_(nullptr) - , topic_(nullptr) - , writer_(nullptr) - , type_(new HelloWorldPubSubType()) -{ -} - -bool HelloWorldPublisher::init( - bool use_env) -{ - hello_.index(0); - hello_.message("HelloWorld"); - DomainParticipantQos pqos = PARTICIPANT_QOS_DEFAULT; - pqos.name("Participant_pub"); - auto factory = DomainParticipantFactory::get_instance(); - - if (use_env) - { - factory->load_profiles(); - factory->get_default_participant_qos(pqos); - } - - participant_ = factory->create_participant(0, pqos); - - if (participant_ == nullptr) - { - return false; - } - - //REGISTER THE TYPE - type_.register_type(participant_); - - //CREATE THE PUBLISHER - PublisherQos pubqos = PUBLISHER_QOS_DEFAULT; - - if (use_env) - { - participant_->get_default_publisher_qos(pubqos); - } - - publisher_ = participant_->create_publisher( - pubqos, - nullptr); - - if (publisher_ == nullptr) - { - return false; - } - - //CREATE THE TOPIC - TopicQos tqos = TOPIC_QOS_DEFAULT; - - if (use_env) - { - participant_->get_default_topic_qos(tqos); - } - - topic_ = participant_->create_topic( - "HelloWorldTopic", - "HelloWorld", - tqos); - - if (topic_ == nullptr) - { - return false; - } - - // CREATE THE WRITER - DataWriterQos wqos = DATAWRITER_QOS_DEFAULT; - - if (use_env) - { - publisher_->get_default_datawriter_qos(wqos); - } - - writer_ = publisher_->create_datawriter( - topic_, - wqos, - &listener_); - - if (writer_ == nullptr) - { - return false; - } - - return true; -} - -HelloWorldPublisher::~HelloWorldPublisher() -{ - if (writer_ != nullptr) - { - publisher_->delete_datawriter(writer_); - } - if (publisher_ != nullptr) - { - participant_->delete_publisher(publisher_); - } - if (topic_ != nullptr) - { - participant_->delete_topic(topic_); - } - DomainParticipantFactory::get_instance()->delete_participant(participant_); -} - -void HelloWorldPublisher::PubListener::on_publication_matched( - eprosima::fastdds::dds::DataWriter*, - const eprosima::fastdds::dds::PublicationMatchedStatus& info) -{ - if (info.current_count_change == 1) - { - matched_ = info.total_count; - firstConnected_ = true; - std::cout << "Publisher matched." << std::endl; - } - else if (info.current_count_change == -1) - { - matched_ = info.total_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 HelloWorldPublisher::runThread( - uint32_t samples, - uint32_t sleep) -{ - if (samples == 0) - { - while (!stop_) - { - if (publish(false)) - { - std::cout << "Message: " << hello_.message() << " with index: " << hello_.index() - << " SENT" << std::endl; - } - std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); - } - } - else - { - for (uint32_t i = 0; i < samples; ++i) - { - if (!publish()) - { - --i; - } - else - { - std::cout << "Message: " << hello_.message() << " with index: " << hello_.index() - << " SENT" << std::endl; - } - std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); - } - } -} - -void HelloWorldPublisher::run( - uint32_t samples, - uint32_t sleep) -{ - stop_ = false; - std::thread thread(&HelloWorldPublisher::runThread, this, samples, sleep); - if (samples == 0) - { - std::cout << "Publisher running. Please press enter to stop the Publisher at any time." << std::endl; - std::cin.ignore(); - stop_ = true; - } - else - { - std::cout << "Publisher running " << samples << " samples." << std::endl; - } - thread.join(); -} - -bool HelloWorldPublisher::publish( - bool waitForListener) -{ - if (listener_.firstConnected_ || !waitForListener || listener_.matched_ > 0) - { - hello_.index(hello_.index() + 1); - writer_->write(&hello_); - return true; - } - return false; -} diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorldPublisher.h b/examples/cpp/dds/HelloWorldExample/HelloWorldPublisher.h deleted file mode 100644 index b764dfc67f6..00000000000 --- a/examples/cpp/dds/HelloWorldExample/HelloWorldPublisher.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2019 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 HelloWorldPublisher.h - * - */ - -#ifndef HELLOWORLDPUBLISHER_H_ -#define HELLOWORLDPUBLISHER_H_ - -#include "HelloWorldPubSubTypes.h" - -#include -#include -#include - -class HelloWorldPublisher -{ -public: - - HelloWorldPublisher(); - - virtual ~HelloWorldPublisher(); - - //!Initialize - bool init( - bool use_env); - - //!Publish a sample - bool publish( - bool waitForListener = true); - - //!Run for number samples - void run( - uint32_t number, - uint32_t sleep); - -private: - - HelloWorld hello_; - - eprosima::fastdds::dds::DomainParticipant* participant_; - - eprosima::fastdds::dds::Publisher* publisher_; - - eprosima::fastdds::dds::Topic* topic_; - - eprosima::fastdds::dds::DataWriter* writer_; - - bool stop_; - - class PubListener : public eprosima::fastdds::dds::DataWriterListener - { - public: - - PubListener() - : matched_(0) - , firstConnected_(false) - { - } - - ~PubListener() override - { - } - - void on_publication_matched( - eprosima::fastdds::dds::DataWriter* writer, - const eprosima::fastdds::dds::PublicationMatchedStatus& info) override; - - int matched_; - - bool firstConnected_; - } - listener_; - - void runThread( - uint32_t number, - uint32_t sleep); - - eprosima::fastdds::dds::TypeSupport type_; -}; - - - -#endif /* HELLOWORLDPUBLISHER_H_ */ diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorldSubscriber.cpp b/examples/cpp/dds/HelloWorldExample/HelloWorldSubscriber.cpp deleted file mode 100644 index 680194c85b8..00000000000 --- a/examples/cpp/dds/HelloWorldExample/HelloWorldSubscriber.cpp +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2016 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 HelloWorldSubscriber.cpp - * - */ - -#include "HelloWorldSubscriber.h" - -#include -#include - -#include -#include -#include -#include -#include - -using namespace eprosima::fastdds::dds; - -HelloWorldSubscriber::HelloWorldSubscriber() - : participant_(nullptr) - , subscriber_(nullptr) - , topic_(nullptr) - , reader_(nullptr) - , type_(new HelloWorldPubSubType()) -{ -} - -bool HelloWorldSubscriber::init( - bool use_env) -{ - DomainParticipantQos pqos = PARTICIPANT_QOS_DEFAULT; - pqos.name("Participant_sub"); - auto factory = DomainParticipantFactory::get_instance(); - - if (use_env) - { - factory->load_profiles(); - factory->get_default_participant_qos(pqos); - } - - participant_ = factory->create_participant(0, pqos); - - if (participant_ == nullptr) - { - return false; - } - - //REGISTER THE TYPE - type_.register_type(participant_); - - //CREATE THE SUBSCRIBER - SubscriberQos sqos = SUBSCRIBER_QOS_DEFAULT; - - if (use_env) - { - participant_->get_default_subscriber_qos(sqos); - } - - subscriber_ = participant_->create_subscriber(sqos, nullptr); - - if (subscriber_ == nullptr) - { - return false; - } - - //CREATE THE TOPIC - TopicQos tqos = TOPIC_QOS_DEFAULT; - - if (use_env) - { - participant_->get_default_topic_qos(tqos); - } - - topic_ = participant_->create_topic( - "HelloWorldTopic", - "HelloWorld", - tqos); - - if (topic_ == nullptr) - { - return false; - } - - // CREATE THE READER - DataReaderQos rqos = DATAREADER_QOS_DEFAULT; - rqos.reliability().kind = RELIABLE_RELIABILITY_QOS; - - if (use_env) - { - subscriber_->get_default_datareader_qos(rqos); - } - - reader_ = subscriber_->create_datareader(topic_, rqos, &listener_); - - if (reader_ == nullptr) - { - return false; - } - - return true; -} - -HelloWorldSubscriber::~HelloWorldSubscriber() -{ - if (reader_ != nullptr) - { - subscriber_->delete_datareader(reader_); - } - if (topic_ != nullptr) - { - participant_->delete_topic(topic_); - } - if (subscriber_ != nullptr) - { - participant_->delete_subscriber(subscriber_); - } - DomainParticipantFactory::get_instance()->delete_participant(participant_); -} - -void HelloWorldSubscriber::SubListener::on_subscription_matched( - DataReader*, - const SubscriptionMatchedStatus& info) -{ - if (info.current_count_change == 1) - { - matched_ = info.total_count; - std::cout << "Subscriber matched." << std::endl; - } - else if (info.current_count_change == -1) - { - matched_ = info.total_count; - 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 HelloWorldSubscriber::SubListener::on_data_available( - DataReader* reader) -{ - SampleInfo info; - if (reader->take_next_sample(&hello_, &info) == RETCODE_OK) - { - if (info.instance_state == ALIVE_INSTANCE_STATE) - { - samples_++; - // Print your structure data here. - std::cout << "Message " << hello_.message() << " " << hello_.index() << " RECEIVED" << std::endl; - } - } -} - -void HelloWorldSubscriber::run() -{ - std::cout << "Subscriber running. Please press enter to stop the Subscriber" << std::endl; - std::cin.ignore(); -} - -void HelloWorldSubscriber::run( - uint32_t number) -{ - std::cout << "Subscriber running until " << number << "samples have been received" << std::endl; - while (number > listener_.samples_) - { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } -} diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorldSubscriber.h b/examples/cpp/dds/HelloWorldExample/HelloWorldSubscriber.h deleted file mode 100644 index 227198cf191..00000000000 --- a/examples/cpp/dds/HelloWorldExample/HelloWorldSubscriber.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019 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 HelloWorldSubscriber.h - * - */ - -#ifndef HELLOWORLDSUBSCRIBER_H_ -#define HELLOWORLDSUBSCRIBER_H_ - -#include -#include -#include -#include - -#include "HelloWorldPubSubTypes.h" - -class HelloWorldSubscriber -{ -public: - - HelloWorldSubscriber(); - - virtual ~HelloWorldSubscriber(); - - //!Initialize the subscriber - bool init( - bool use_env); - - //!RUN the subscriber - void run(); - - //!Run the subscriber until number samples have been received. - void run( - uint32_t number); - -private: - - eprosima::fastdds::dds::DomainParticipant* participant_; - - eprosima::fastdds::dds::Subscriber* subscriber_; - - eprosima::fastdds::dds::Topic* topic_; - - eprosima::fastdds::dds::DataReader* reader_; - - eprosima::fastdds::dds::TypeSupport type_; - - class SubListener : public eprosima::fastdds::dds::DataReaderListener - { - public: - - SubListener() - : matched_(0) - , samples_(0) - { - } - - ~SubListener() override - { - } - - void on_data_available( - eprosima::fastdds::dds::DataReader* reader) override; - - void on_subscription_matched( - eprosima::fastdds::dds::DataReader* reader, - const eprosima::fastdds::dds::SubscriptionMatchedStatus& info) override; - - HelloWorld hello_; - - int matched_; - - uint32_t samples_; - } - listener_; -}; - -#endif /* HELLOWORLDSUBSCRIBER_H_ */ diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorld_main.cpp b/examples/cpp/dds/HelloWorldExample/HelloWorld_main.cpp deleted file mode 100644 index 7a6fb595f09..00000000000 --- a/examples/cpp/dds/HelloWorldExample/HelloWorld_main.cpp +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2019 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 HelloWorld_main.cpp - * - */ - -#include -#include - -#include -#include -#include - -#include "HelloWorldPublisher.h" -#include "HelloWorldSubscriber.h" - -using eprosima::fastdds::dds::Log; - -namespace option = eprosima::option; - -struct Arg : public option::Arg -{ - static void print_error( - const char* msg1, - const option::Option& opt, - const char* msg2) - { - fprintf(stderr, "%s", msg1); - fwrite(opt.name, opt.namelen, 1, stderr); - fprintf(stderr, "%s", msg2); - } - - static option::ArgStatus Unknown( - const option::Option& option, - bool msg) - { - if (msg) - { - print_error("Unknown option '", option, "'\n"); - } - return option::ARG_ILLEGAL; - } - - static option::ArgStatus Required( - const option::Option& option, - bool msg) - { - if (option.arg != 0 && option.arg[0] != 0) - { - return option::ARG_OK; - } - - if (msg) - { - print_error("Option '", option, "' requires an argument\n"); - } - return option::ARG_ILLEGAL; - } - - static option::ArgStatus Numeric( - const option::Option& option, - bool msg) - { - char* endptr = 0; - if ( option.arg != nullptr ) - { - strtol(option.arg, &endptr, 10); - if (endptr != option.arg && *endptr == 0) - { - return option::ARG_OK; - } - } - - if (msg) - { - print_error("Option '", option, "' requires a numeric argument\n"); - } - return option::ARG_ILLEGAL; - } - - template::max()> - static option::ArgStatus NumericRange( - const option::Option& option, - bool msg) - { - static_assert(min <= max, "NumericRange: invalid range provided."); - - char* endptr = 0; - if ( option.arg != nullptr ) - { - long value = strtol(option.arg, &endptr, 10); - if ( endptr != option.arg && *endptr == 0 && - value >= min && value <= max) - { - return option::ARG_OK; - } - } - - if (msg) - { - std::ostringstream os; - os << "' requires a numeric argument in range [" - << min << ", " << max << "]" << std::endl; - print_error("Option '", option, os.str().c_str()); - } - - return option::ARG_ILLEGAL; - } - - static option::ArgStatus String( - const option::Option& option, - bool msg) - { - if (option.arg != 0) - { - return option::ARG_OK; - } - if (msg) - { - print_error("Option '", option, "' requires an argument\n"); - } - return option::ARG_ILLEGAL; - } - -}; - -enum optionIndex -{ - UNKNOWN_OPT, - HELP, - SAMPLES, - INTERVAL, - ENVIRONMENT -}; - -const option::Descriptor usage[] = { - { UNKNOWN_OPT, 0, "", "", Arg::None, - "Usage: HelloWorldExample \n\nGeneral options:" }, - { HELP, 0, "h", "help", Arg::None, " -h \t--help \tProduce help message." }, - { UNKNOWN_OPT, 0, "", "", Arg::None, "\nPublisher options:"}, - { SAMPLES, 0, "s", "samples", Arg::NumericRange<>, - " -s , \t--samples= \tNumber of samples (0, default, infinite)." }, - { INTERVAL, 0, "i", "interval", Arg::NumericRange<>, - " -i , \t--interval= \tTime between samples in milliseconds (Default: 100)." }, - { ENVIRONMENT, 0, "e", "env", Arg::None, " -e \t--env \tLoad QoS from environment." }, - { 0, 0, 0, 0, 0, 0 } -}; - -int main( - int argc, - char** argv) -{ - int columns; - -#if defined(_WIN32) - char* buf = nullptr; - size_t sz = 0; - if (_dupenv_s(&buf, &sz, "COLUMNS") == 0 && buf != nullptr) - { - columns = strtol(buf, nullptr, 10); - free(buf); - } - else - { - columns = 80; - } -#else - columns = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 80; -#endif // if defined(_WIN32) - - std::cout << "Starting " << std::endl; - - int type = 1; - uint32_t count = 10; - uint32_t sleep = 100; - bool use_environment_qos = false; - - argc -= (argc > 0); - argv += (argc > 0); // skip program name argv[0] if present - option::Stats stats(true, usage, argc, argv); - std::vector options(stats.options_max); - std::vector buffer(stats.buffer_max); - option::Parser parse(true, usage, argc, argv, &options[0], &buffer[0]); - - try - { - if (parse.error()) - { - throw 1; - } - - if (options[HELP] || options[UNKNOWN_OPT]) - { - throw 1; - } - - // For backward compatibility count and sleep may be given positionally - if (parse.nonOptionsCount() > 3 || parse.nonOptionsCount() == 0) - { - throw 2; - } - - // Decide between publisher or subscriber - const char* type_name = parse.nonOption(0); - - // make sure is the first option. - // type_name and buffer[0].name reference the original command line char array - // type_name must precede any other arguments in the array. - // Note buffer[0].arg may be null for non-valued options and is not reliable for - // testing purposes. - if (parse.optionsCount() && type_name >= buffer[0].name) - { - throw 2; - } - - if (strcmp(type_name, "publisher") == 0) - { - type = 1; - } - else if (strcmp(type_name, "subscriber") == 0) - { - type = 2; - } - else - { - throw 2; - } - } - catch (int error) - { - if ( error == 2 ) - { - std::cerr << "ERROR: first argument must be followed by - or -- options" - << std::endl; - } - option::printUsage(fwrite, stdout, usage, columns); - return error; - } - - // Decide between the old and new syntax - if (parse.nonOptionsCount() > 1) - { - // old syntax, only affects publishers - // old and new syntax cannot be mixed - if (type != 1 || parse.optionsCount() > 0) - { - option::printUsage(fwrite, stdout, usage, columns); - return 1; - } - - count = atoi(parse.nonOption(1)); - - if (parse.nonOptionsCount() == 3) - { - sleep = atoi(parse.nonOption(2)); - } - } - else - { - // new syntax - option::Option* opt = options[SAMPLES]; - if (opt) - { - count = strtol(opt->arg, nullptr, 10); - } - - opt = options[INTERVAL]; - if (opt) - { - sleep = strtol(opt->arg, nullptr, 10); - } - - opt = options[ENVIRONMENT]; - if (opt) - { - use_environment_qos = true; - } - } - - switch (type) - { - case 1: - { - HelloWorldPublisher mypub; - if (mypub.init(use_environment_qos)) - { - mypub.run(count, sleep); - } - break; - } - case 2: - { - HelloWorldSubscriber mysub; - if (mysub.init(use_environment_qos)) - { - mysub.run(); - } - break; - } - } - Log::Reset(); - return 0; -} diff --git a/examples/cpp/dds/HelloWorldExample/README.txt b/examples/cpp/dds/HelloWorldExample/README.txt deleted file mode 100644 index 6e02448ed83..00000000000 --- a/examples/cpp/dds/HelloWorldExample/README.txt +++ /dev/null @@ -1,36 +0,0 @@ -To launch this test open two different consoles: - -In the first one launch: ./DDSHelloWorldExample publisher (or DDSHelloWorldExample.exe publisher on windows). -In the second one: ./DDSHelloWorldExample subscriber (or DDSHelloWorldExample.exe subscriber on windows). - -In order to use xml profiles (--env or shorcut -e cli flags): - - reference the xml profiles file setting the environment variable FASTDDS_DEFAULT_PROFILES_FILE to its path. - - name it DEFAULT_FASTDDS_PROFILES.xml and make sure the file is besides the DDSHelloWorldExample binary. -The profile loaded will be the mark as default one with the corresponding attribute. For example: - - - - - - Profiles example name - - - - - - BEST_EFFORT - - - - - - - KEEP_LAST - 5 - - - - - -will create a participant called "Example dummy name" and modify the endpoint set up. -Note the "profile_name" attribute is mandatory. diff --git a/examples/cpp/hello_world/Application.cpp b/examples/cpp/hello_world/Application.cpp new file mode 100644 index 00000000000..fb42f74aafc --- /dev/null +++ b/examples/cpp/hello_world/Application.cpp @@ -0,0 +1,66 @@ +// 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 "ListenerSubscriberApp.hpp" +#include "PublisherApp.hpp" +#include "WaitsetSubscriberApp.hpp" + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace hello_world { + +//! Factory method to create a publisher or subscriber +std::shared_ptr Application::make_app( + const CLIParser::hello_world_config& config, + const std::string& topic_name) +{ + std::shared_ptr entity; + switch (config.entity) + { + case CLIParser::EntityKind::PUBLISHER: + entity = std::make_shared(config.pub_config, topic_name); + break; + case CLIParser::EntityKind::SUBSCRIBER: + if (config.sub_config.use_waitset) + { + entity = std::make_shared(config.sub_config, topic_name); + } + else + { + entity = std::make_shared(config.sub_config, topic_name); + } + break; + case CLIParser::EntityKind::UNDEFINED: + default: + throw std::runtime_error("Entity initialization failed"); + break; + } + return entity; +} + +} // namespace hello_world +} // namespace examples +} // namespace fastdds +} // namespace eprosima diff --git a/examples/cpp/hello_world/Application.hpp b/examples/cpp/hello_world/Application.hpp new file mode 100644 index 00000000000..9d1f01b214b --- /dev/null +++ b/examples/cpp/hello_world/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_HELLO_WORLD_APPLICATION_HPP_ +#define _FASTDDS_HELLO_WORLD_APPLICATION_HPP_ + +#include + +#include "CLIParser.hpp" + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace hello_world { + +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::hello_world_config& config, + const std::string& topic_name); +}; + +} // namespace hello_world +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif /* _FASTDDS_HELLO_WORLD_APPLICATION_HPP_ */ diff --git a/examples/cpp/hello_world/CLIParser.hpp b/examples/cpp/hello_world/CLIParser.hpp new file mode 100644 index 00000000000..8aebfacde8d --- /dev/null +++ b/examples/cpp/hello_world/CLIParser.hpp @@ -0,0 +1,261 @@ +// 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_HELLO_WORLD_CLI_PARSER_HPP_ +#define _FASTDDS_HELLO_WORLD_CLI_PARSER_HPP_ + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace hello_world { + +using dds::Log; + +class CLIParser +{ +public: + + CLIParser() = delete; + + //! Entity kind enumeration + enum class EntityKind : uint8_t + { + PUBLISHER, + SUBSCRIBER, + UNDEFINED + }; + + //! Publisher configuration structure (shared for both publisher and subscriber applications) + struct publisher_config + { + uint16_t samples = 0; + }; + + //! Subscriber application configuration structure + struct subscriber_config : public publisher_config + { + bool use_waitset = false; + }; + + //! Configuration structure for the application + struct hello_world_config + { + CLIParser::EntityKind entity = CLIParser::EntityKind::UNDEFINED; + publisher_config pub_config; + subscriber_config sub_config; + }; + + /** + * @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: hello_world [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::cout << "Subscriber options:" << std::endl; + std::cout << " -w, --waitset Use waitset & read condition" << 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 hello_world_config parse_cli_options( + int argc, + char* argv[]) + { + hello_world_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::PUBLISHER) + { + config.pub_config.samples = static_cast(input); + } + else if (config.entity == CLIParser::EntityKind::SUBSCRIBER) + { + config.sub_config.samples = static_cast(input); + } + else + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "entity not specified for --sample argument"); + print_help(EXIT_FAILURE); + } + } + } + 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 if (arg == "-w" || arg == "--waitset") + { + if (config.entity == CLIParser::EntityKind::SUBSCRIBER) + { + config.sub_config.use_waitset = true; + } + else + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "waitset can only be used with the subscriber entity"); + 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 hello_world +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_HELLO_WORLD_CLI_PARSER_HPP_ diff --git a/examples/cpp/hello_world/CMakeLists.txt b/examples/cpp/hello_world/CMakeLists.txt new file mode 100644 index 00000000000..573fd36c4f4 --- /dev/null +++ b/examples/cpp/hello_world/CMakeLists.txt @@ -0,0 +1,64 @@ +# 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_hello_world_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 hello world example...") +file(GLOB HELLO_WORLD_SOURCES_CXX "*.cxx") +file(GLOB HELLO_WORLD_SOURCES_CPP "*.cpp") + +add_executable(hello_world ${HELLO_WORLD_SOURCES_CXX} ${HELLO_WORLD_SOURCES_CPP}) +target_compile_definitions(hello_world PRIVATE + $<$>,$>:__DEBUG> + $<$:__INTERNALDEBUG> # Internal debug activated. + $<$:SHM_TRANSPORT_BUILTIN> # Enable SHM as built-in transport +) +target_link_libraries(hello_world fastdds fastcdr) +install(TARGETS hello_world + RUNTIME DESTINATION ${DATA_INSTALL_DIR}/fastdds/examples/cpp/hello_world/${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/hello_world/${BIN_INSTALL_DIR}) +endforeach() diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorld.hpp b/examples/cpp/hello_world/HelloWorld.hpp similarity index 100% rename from examples/cpp/dds/HelloWorldExample/HelloWorld.hpp rename to examples/cpp/hello_world/HelloWorld.hpp diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorld.idl b/examples/cpp/hello_world/HelloWorld.idl similarity index 69% rename from examples/cpp/dds/HelloWorldExample/HelloWorld.idl rename to examples/cpp/hello_world/HelloWorld.idl index 0fd2c355aee..192f8f9d487 100644 --- a/examples/cpp/dds/HelloWorldExample/HelloWorld.idl +++ b/examples/cpp/hello_world/HelloWorld.idl @@ -1,3 +1,4 @@ +@extensibility(APPENDABLE) struct HelloWorld { unsigned long index; diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorldCdrAux.hpp b/examples/cpp/hello_world/HelloWorldCdrAux.hpp similarity index 100% rename from examples/cpp/dds/HelloWorldExample/HelloWorldCdrAux.hpp rename to examples/cpp/hello_world/HelloWorldCdrAux.hpp diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorldCdrAux.ipp b/examples/cpp/hello_world/HelloWorldCdrAux.ipp similarity index 100% rename from examples/cpp/dds/HelloWorldExample/HelloWorldCdrAux.ipp rename to examples/cpp/hello_world/HelloWorldCdrAux.ipp diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorldPubSubTypes.cxx b/examples/cpp/hello_world/HelloWorldPubSubTypes.cxx similarity index 100% rename from examples/cpp/dds/HelloWorldExample/HelloWorldPubSubTypes.cxx rename to examples/cpp/hello_world/HelloWorldPubSubTypes.cxx diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorldPubSubTypes.h b/examples/cpp/hello_world/HelloWorldPubSubTypes.h similarity index 100% rename from examples/cpp/dds/HelloWorldExample/HelloWorldPubSubTypes.h rename to examples/cpp/hello_world/HelloWorldPubSubTypes.h diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorldTypeObjectSupport.cxx b/examples/cpp/hello_world/HelloWorldTypeObjectSupport.cxx similarity index 98% rename from examples/cpp/dds/HelloWorldExample/HelloWorldTypeObjectSupport.cxx rename to examples/cpp/hello_world/HelloWorldTypeObjectSupport.cxx index fe783982b0f..fedf539c3fd 100644 --- a/examples/cpp/dds/HelloWorldExample/HelloWorldTypeObjectSupport.cxx +++ b/examples/cpp/hello_world/HelloWorldTypeObjectSupport.cxx @@ -54,13 +54,20 @@ void register_HelloWorld_type_identifier( TypeIdentifier& type_id) { { - StructTypeFlag struct_flags_HelloWorld = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::NOT_APPLIED, + StructTypeFlag struct_flags_HelloWorld = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, false, false); ReturnCode_t return_code_HelloWorld; TypeIdentifierPair type_ids_HelloWorld; QualifiedTypeName type_name_HelloWorld = "HelloWorld"; eprosima::fastcdr::optional type_ann_builtin_HelloWorld; eprosima::fastcdr::optional ann_custom_HelloWorld; + AppliedAnnotationSeq tmp_ann_custom_HelloWorld; + eprosima::fastcdr::optional verbatim_HelloWorld; + if (!tmp_ann_custom_HelloWorld.empty()) + { + ann_custom_HelloWorld = tmp_ann_custom_HelloWorld; + } + CompleteTypeDetail detail_HelloWorld = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_HelloWorld, ann_custom_HelloWorld, type_name_HelloWorld.to_string()); CompleteStructHeader header_HelloWorld; header_HelloWorld = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_HelloWorld); diff --git a/examples/cpp/dds/HelloWorldExample/HelloWorldTypeObjectSupport.hpp b/examples/cpp/hello_world/HelloWorldTypeObjectSupport.hpp similarity index 100% rename from examples/cpp/dds/HelloWorldExample/HelloWorldTypeObjectSupport.hpp rename to examples/cpp/hello_world/HelloWorldTypeObjectSupport.hpp diff --git a/examples/cpp/hello_world/ListenerSubscriberApp.cpp b/examples/cpp/hello_world/ListenerSubscriberApp.cpp new file mode 100644 index 00000000000..4e5491ea0d5 --- /dev/null +++ b/examples/cpp/hello_world/ListenerSubscriberApp.cpp @@ -0,0 +1,169 @@ +// 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 ListenerSubscriber.cpp + * + */ + +#include "ListenerSubscriberApp.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "CLIParser.hpp" +#include "HelloWorldPubSubTypes.h" +#include "Application.hpp" + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace hello_world { + +ListenerSubscriberApp::ListenerSubscriberApp( + const CLIParser::subscriber_config& config, + const std::string& topic_name) + : participant_(nullptr) + , subscriber_(nullptr) + , topic_(nullptr) + , reader_(nullptr) + , type_(new HelloWorldPubSubType()) + , samples_(config.samples) + , received_samples_(0) + , stop_(false) +{ + // 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 + type_.register_type(participant_); + + // Create the subscriber + SubscriberQos sub_qos = SUBSCRIBER_QOS_DEFAULT; + participant_->get_default_subscriber_qos(sub_qos); + subscriber_ = participant_->create_subscriber(sub_qos, nullptr, StatusMask::none()); + if (subscriber_ == nullptr) + { + 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, type_.get_type_name(), topic_qos); + if (topic_ == nullptr) + { + 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, this, StatusMask::all()); + if (reader_ == nullptr) + { + throw std::runtime_error("DataReader initialization failed"); + } +} + +ListenerSubscriberApp::~ListenerSubscriberApp() +{ + if (nullptr != participant_) + { + // Delete DDS entities contained within the DomainParticipant + participant_->delete_contained_entities(); + + // Delete DomainParticipant + DomainParticipantFactory::get_instance()->delete_participant(participant_); + } +} + +void ListenerSubscriberApp::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 ListenerSubscriberApp::on_data_available( + DataReader* reader) +{ + SampleInfo info; + while ((!is_stopped()) && (RETCODE_OK == reader->take_next_sample(&hello_, &info))) + { + if ((info.instance_state == ALIVE_INSTANCE_STATE) && info.valid_data) + { + received_samples_++; + // Print Hello world message data + std::cout << "Message: '" << hello_.message() << "' with index: '" << hello_.index() + << "' RECEIVED" << std::endl; + if (samples_ > 0 && (received_samples_ >= samples_)) + { + stop(); + } + } + } +} + +void ListenerSubscriberApp::run() +{ + std::unique_lock lck(terminate_cv_mtx_); + terminate_cv_.wait(lck, [&] + { + return is_stopped(); + }); +} + +bool ListenerSubscriberApp::is_stopped() +{ + return stop_.load(); +} + +void ListenerSubscriberApp::stop() +{ + stop_.store(true); + terminate_cv_.notify_all(); +} + +} // namespace hello_world +} // namespace examples +} // namespace fastdds +} // namespace eprosima diff --git a/examples/cpp/hello_world/ListenerSubscriberApp.hpp b/examples/cpp/hello_world/ListenerSubscriberApp.hpp new file mode 100644 index 00000000000..cd22791dec6 --- /dev/null +++ b/examples/cpp/hello_world/ListenerSubscriberApp.hpp @@ -0,0 +1,98 @@ +// 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 ListenerSubscriberApp.hpp + * + */ + +#ifndef _FASTDDS_HELLO_WORLD_LISTENER_SUBSCRIBER_APP_HPP_ +#define _FASTDDS_HELLO_WORLD_LISTENER_SUBSCRIBER_APP_HPP_ + +#include + +#include +#include +#include + +#include "CLIParser.hpp" +#include "HelloWorldPubSubTypes.h" +#include "Application.hpp" + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace hello_world { + +class ListenerSubscriberApp : public Application, public DataReaderListener +{ +public: + + ListenerSubscriberApp( + const CLIParser::subscriber_config& config, + const std::string& topic_name); + + ~ListenerSubscriberApp(); + + //! Subscription callback + void on_data_available( + DataReader* reader) override; + + //! Subscriber matched method + void on_subscription_matched( + DataReader* reader, + const SubscriptionMatchedStatus& info) override; + + //! Run subscriber + void run() override; + + //! Trigger the end of execution + void stop() override; + +private: + + //! Return the current state of execution + bool is_stopped(); + + HelloWorld hello_; + + DomainParticipant* participant_; + + Subscriber* subscriber_; + + Topic* topic_; + + DataReader* reader_; + + TypeSupport type_; + + uint16_t samples_; + + uint16_t received_samples_; + + std::atomic stop_; + + mutable std::mutex terminate_cv_mtx_; + + std::condition_variable terminate_cv_; +}; + +} // namespace hello_world +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif /* _FASTDDS_HELLO_WORLD_LISTENER_SUBSCRIBER_APP_HPP_ */ diff --git a/examples/cpp/hello_world/PublisherApp.cpp b/examples/cpp/hello_world/PublisherApp.cpp new file mode 100644 index 00000000000..b2d804bbcf5 --- /dev/null +++ b/examples/cpp/hello_world/PublisherApp.cpp @@ -0,0 +1,178 @@ +// 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 + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace hello_world { + +PublisherApp::PublisherApp( + const CLIParser::publisher_config& config, + const std::string& topic_name) + : participant_(nullptr) + , publisher_(nullptr) + , topic_(nullptr) + , writer_(nullptr) + , type_(new HelloWorldPubSubType()) + , matched_(0) + , samples_(config.samples) + , stop_(false) +{ + // Set up the data type with initial values + hello_.index(0); + hello_.message("Hello 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 + type_.register_type(participant_); + + // Create the publisher + PublisherQos pub_qos = PUBLISHER_QOS_DEFAULT; + participant_->get_default_publisher_qos(pub_qos); + publisher_ = participant_->create_publisher(pub_qos, nullptr, StatusMask::none()); + 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 + participant_->delete_contained_entities(); + + // Delete DomainParticipant + DomainParticipantFactory::get_instance()->delete_participant(participant_); + } +} + +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() +{ + while (!is_stopped() && ((samples_ == 0) || (hello_.index() < samples_))) + { + if (publish()) + { + std::cout << "Message: '" << hello_.message() << "' with index: '" << hello_.index() + << "' SENT" << 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()) + { + hello_.index(hello_.index() + 1); + ret = writer_->write(&hello_); + } + return ret; +} + +bool PublisherApp::is_stopped() +{ + return stop_.load(); +} + +void PublisherApp::stop() +{ + stop_.store(true); + cv_.notify_one(); +} + +} // namespace hello_world +} // namespace examples +} // namespace fastdds +} // namespace eprosima diff --git a/examples/cpp/hello_world/PublisherApp.hpp b/examples/cpp/hello_world/PublisherApp.hpp new file mode 100644 index 00000000000..0b9c6b7f874 --- /dev/null +++ b/examples/cpp/hello_world/PublisherApp.hpp @@ -0,0 +1,99 @@ +// 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_HELLO_WORLD_PUBLISHER_APP_HPP_ +#define _FASTDDS_HELLO_WORLD_PUBLISHER_APP_HPP_ + +#include + +#include +#include +#include + +#include "Application.hpp" +#include "CLIParser.hpp" +#include "HelloWorldPubSubTypes.h" + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace hello_world { + +class PublisherApp : public Application, public DataWriterListener +{ +public: + + PublisherApp( + const CLIParser::publisher_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(); + + HelloWorld hello_; + + DomainParticipant* participant_; + + Publisher* publisher_; + + Topic* topic_; + + DataWriter* writer_; + + TypeSupport type_; + + 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 hello_world +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif /* _FASTDDS_HELLO_WORLD_PUBLISHER_APP_HPP_ */ diff --git a/examples/cpp/hello_world/README.md b/examples/cpp/hello_world/README.md new file mode 100644 index 00000000000..02e90eccacd --- /dev/null +++ b/examples/cpp/hello_world/README.md @@ -0,0 +1,151 @@ +# Hello world example + +The *eProsima Fast DDS hello world* example is a simple application intended to demonstrate a basic DDS deployment. + +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 *hello world* example describes the simplest deployment of a Fast DDS publisher and subscriber. + +* [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. + +This particular example includes two different subscription paradigms; i.e. listening callbacks and wait-sets: + +* The listening callback mechanism consists on declaring a listener class and attaching it to the data reader. + When the data reader is triggered by an event, it runs the listener's method associated to that event, as a callback. + For simplicity, in this example, the subscriber class inherits from the listener class, overriding the corresponding callback. + +* The wait-set is a mechanism where a dedicated thread waits until a status condition occurs. + In that moment, that status condition triggering event would be evaluated to determine witch actions should be taken against it. + +For this example, both listening callback and wait-set implementation would run similar code and generate equivalent output for both triggering events: subscription matching and new data available. + +## 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$ ./hello_world publisher + Publisher running. Please press Ctrl+C to stop the Publisher at any time. + ``` + +* Windows + + ```powershell + example_path> hello_world.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$ ./hello_world subscriber + Subscriber running. Please press Ctrl+C to stop the Subscriber at any time. + ``` + +* Windows + + ```powershell + example_path> hello_world.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. Please press Ctrl+C to stop the Publisher at any time. +Publisher matched. +Message: 'Hello world' with index: '1' SENT +Message: 'Hello world' with index: '2' SENT +Message: 'Hello world' with index: '3' SENT +... +``` + +### Hello world subscriber + +```shell +Subscriber running. Please press Ctrl+C to stop the Subscriber at any time. +Subscriber matched. +Message: 'Hello world' with index: '1' RECEIVED +Message: 'Hello world' with index: '2' RECEIVED +Message: 'Hello world' with index: '3' RECEIVED +... +``` + +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: 'Hello world' with index: '8' SENT +Message: 'Hello world' with index: '9' SENT +Message: 'Hello world' with index: '10' SENT +Message: 'Hello world' with index: '11' SENT +Publisher unmatched. +``` + +## Wait-set subscriber + +As described in the *[Description of the example](#description-of-the-example)* section, the *hello world* example has two listening implementations. Launching the subscriber example with the flag ``-w`` or ``--waitset`` will use the wait-set approach instead of the listening callback. + +* Ubuntu ( / MacOS ) + + ```shell + user@machine:example_path$ ./hello_world subscriber --waitset + ``` + +* Windows + + ```powershell + example_path> hello_world.exe subscriber --waitset + ``` + +The expected output is exactly the same as the described in the *[previous](#expected-output)* section. + +## 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=hello_world_profile.xml + ``` + +* Windows + + ```powershell + example_path> set FASTDDS_DEFAULT_PROFILES_FILE=hello_world_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/hello_world/WaitsetSubscriberApp.cpp b/examples/cpp/hello_world/WaitsetSubscriberApp.cpp new file mode 100644 index 00000000000..ff98788651f --- /dev/null +++ b/examples/cpp/hello_world/WaitsetSubscriberApp.cpp @@ -0,0 +1,188 @@ +// 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 WaitsetSubscriberApp.cpp + * + */ + +#include "WaitsetSubscriberApp.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "HelloWorldPubSubTypes.h" +#include "CLIParser.hpp" +#include "Application.hpp" + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace hello_world { + +WaitsetSubscriberApp::WaitsetSubscriberApp( + const CLIParser::subscriber_config& config, + const std::string& topic_name) + : participant_(nullptr) + , subscriber_(nullptr) + , topic_(nullptr) + , reader_(nullptr) + , type_(new HelloWorldPubSubType()) + , samples_(config.samples) + , received_samples_(0) + , stop_(false) +{ + // 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 + type_.register_type(participant_); + + // Create the subscriber + SubscriberQos sub_qos = SUBSCRIBER_QOS_DEFAULT; + participant_->get_default_subscriber_qos(sub_qos); + subscriber_ = participant_->create_subscriber(sub_qos, nullptr, StatusMask::none()); + if (subscriber_ == nullptr) + { + 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, type_.get_type_name(), topic_qos); + if (topic_ == nullptr) + { + 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, nullptr, StatusMask::all()); + if (reader_ == nullptr) + { + throw std::runtime_error("DataReader initialization failed"); + } + + // Prepare a wait-set + wait_set_.attach_condition(reader_->get_statuscondition()); + wait_set_.attach_condition(terminate_condition_); +} + +WaitsetSubscriberApp::~WaitsetSubscriberApp() +{ + if (nullptr != participant_) + { + // Delete DDS entities contained within the DomainParticipant + participant_->delete_contained_entities(); + + // Delete DomainParticipant + DomainParticipantFactory::get_instance()->delete_participant(participant_); + } +} + +void WaitsetSubscriberApp::run() +{ + while (!is_stopped()) + { + ConditionSeq triggered_conditions; + ReturnCode_t ret_code = wait_set_.wait(triggered_conditions, eprosima::fastrtps::c_TimeInfinite); + if (RETCODE_OK != ret_code) + { + EPROSIMA_LOG_ERROR(SUBSCRIBER_WAITSET, "Error waiting for conditions"); + continue; + } + for (Condition* cond : triggered_conditions) + { + StatusCondition* status_cond = dynamic_cast(cond); + if (nullptr != status_cond) + { + Entity* entity = status_cond->get_entity(); + StatusMask changed_statuses = entity->get_status_changes(); + if (changed_statuses.is_active(StatusMask::subscription_matched())) + { + SubscriptionMatchedStatus status_; + reader_->get_subscription_matched_status(status_); + if (status_.current_count_change == 1) + { + std::cout << "Waitset Subscriber matched." << std::endl; + } + else if (status_.current_count_change == -1) + { + std::cout << "Waitset Subscriber unmatched." << std::endl; + } + else + { + std::cout << status_.current_count_change << + " is not a valid value for SubscriptionMatchedStatus current count change" << + std::endl; + } + } + if (changed_statuses.is_active(StatusMask::data_available())) + { + SampleInfo info; + while ((!is_stopped()) && + (RETCODE_OK == reader_->take_next_sample(&hello_, &info))) + { + if ((info.instance_state == ALIVE_INSTANCE_STATE) && info.valid_data) + { + received_samples_++; + // Print Hello world message data + std::cout << "Message: '" << hello_.message() << "' with index: '" + << hello_.index() << "' RECEIVED" << std::endl; + if (samples_ > 0 && (received_samples_ >= samples_)) + { + stop(); + } + } + } + } + } + } + } +} + +bool WaitsetSubscriberApp::is_stopped() +{ + return stop_.load(); +} + +void WaitsetSubscriberApp::stop() +{ + stop_.store(true); + terminate_condition_.set_trigger_value(true); +} + +} // namespace hello_world +} // namespace examples +} // namespace fastdds +} // namespace eprosima diff --git a/examples/cpp/hello_world/WaitsetSubscriberApp.hpp b/examples/cpp/hello_world/WaitsetSubscriberApp.hpp new file mode 100644 index 00000000000..cb27cc5c016 --- /dev/null +++ b/examples/cpp/hello_world/WaitsetSubscriberApp.hpp @@ -0,0 +1,91 @@ +// 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 WaitsetSubscriberApp.hpp + * + */ + +#ifndef _FASTDDS_HELLO_WORLD_WAITSET_SUBSCRIBER_APP_HPP_ +#define _FASTDDS_HELLO_WORLD_WAITSET_SUBSCRIBER_APP_HPP_ + +#include +#include +#include +#include +#include +#include +#include + +#include "CLIParser.hpp" +#include "HelloWorldPubSubTypes.h" +#include "Application.hpp" + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace hello_world { + +class WaitsetSubscriberApp : public Application +{ +public: + + WaitsetSubscriberApp( + const CLIParser::subscriber_config& config, + const std::string& topic_name); + + ~WaitsetSubscriberApp(); + + //! Run subscriber + void run() override; + + //! Trigger the end of execution + void stop() override; + +private: + + //! Return the current state of execution + bool is_stopped(); + + HelloWorld hello_; + + DomainParticipant* participant_; + + Subscriber* subscriber_; + + Topic* topic_; + + DataReader* reader_; + + TypeSupport type_; + + WaitSet wait_set_; + + uint16_t samples_; + + uint16_t received_samples_; + + std::atomic stop_; + + GuardCondition terminate_condition_; +}; + +} // namespace hello_world +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif /* _FASTDDS_HELLO_WORLD_WAITSET_SUBSCRIBER_APP_HPP_ */ diff --git a/examples/cpp/hello_world/hello_world_profile.xml b/examples/cpp/hello_world/hello_world_profile.xml new file mode 100644 index 00000000000..1cc694fd28b --- /dev/null +++ b/examples/cpp/hello_world/hello_world_profile.xml @@ -0,0 +1,52 @@ + + + + 0 + + hello_world_participant + + + + + + TRANSIENT_LOCAL + + + RELIABLE + + + + + KEEP_LAST + 100 + + + 100 + 1 + 100 + + + + + + + + TRANSIENT_LOCAL + + + RELIABLE + + + + + KEEP_LAST + 100 + + + 100 + 1 + 100 + + + + diff --git a/examples/cpp/hello_world/main.cpp b/examples/cpp/hello_world/main.cpp new file mode 100644 index 00000000000..edfb6ff468e --- /dev/null +++ b/examples/cpp/hello_world/main.cpp @@ -0,0 +1,108 @@ +// 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::hello_world; + +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::hello_world_config config = CLIParser::parse_cli_options(argc, argv); + uint16_t samples = 0; + switch (config.entity) + { + case CLIParser::EntityKind::PUBLISHER: + samples = config.pub_config.samples; + break; + case CLIParser::EntityKind::SUBSCRIBER: + samples = config.sub_config.samples; + break; + default: + break; + } + + 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 (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 " << 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/test/examples/CMakeLists.txt b/test/examples/CMakeLists.txt index 752b05ef2e9..5a3bb0bc5aa 100644 --- a/test/examples/CMakeLists.txt +++ b/test/examples/CMakeLists.txt @@ -25,6 +25,8 @@ endif() set(FILE_EXTENSION "") set(DOCKER_IMAGE_NAME "") set(SHELL_EXECUTABLE "") +set(TINYXML2_LIB_DIR_COMPOSE_VOLUME "") +set(TINYXML2_LIB_DIR_COMPOSE_LD_LIBRARY_PATH "") # Linux configurations if(UNIX AND NOT(APPLE) AND NOT(QNXNTO) AND NOT(ANDROID)) @@ -56,6 +58,13 @@ else() message(FATAL_ERROR "Unsupported platform") endif() +# Configure TinyXML2 library path if installed in user library path +if(NOT (TINYXML2_FROM_SOURCE OR TINYXML2_FROM_THIRDPARTY)) + get_filename_component(TINYXML2_LIB_DIR ${TINYXML2_LIBRARY} DIRECTORY) + set(TINYXML2_LIB_DIR_COMPOSE_VOLUME "- ${TINYXML2_LIB_DIR}:${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DIR}/fastdds:ro") + set(TINYXML2_LIB_DIR_COMPOSE_LD_LIBRARY_PATH ":${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DIR}/fastdds") +endif() + # Find all docker compose yml files for testing file(GLOB files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.yml) diff --git a/test/examples/basic_configuration.compose.yml b/test/examples/basic_configuration.compose.yml index b6ca5c1d4c8..a07ff0196ef 100644 --- a/test/examples/basic_configuration.compose.yml +++ b/test/examples/basic_configuration.compose.yml @@ -7,9 +7,10 @@ services: 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@ + 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/dds/BasicConfigurationExample command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/BasicConfigurationExample@FILE_EXTENSION@ publisher --wait 2 --samples 10 --interval 10 --reliable --transient & $${EXAMPLE_DIR}/BasicConfigurationExample@FILE_EXTENSION@ subscriber --samples 10 --reliable --transient" @@ -18,8 +19,9 @@ services: 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@ + 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/dds/BasicConfigurationExample@FILE_EXTENSION@ command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/BasicConfigurationExample@FILE_EXTENSION@ subscriber --samples 10 --reliable --transient" diff --git a/test/examples/hello_world.compose.yml b/test/examples/hello_world.compose.yml new file mode 100644 index 00000000000..8d03da8134a --- /dev/null +++ b/test/examples/hello_world.compose.yml @@ -0,0 +1,31 @@ +# FASTDDS_TODO_BEFORE(3, 0, "This compose file should be used for the future configuration example"); +version: "3" + +services: + waitset-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 --waitset --samples 10" + + 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/hello_world + 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 10 & $${EXAMPLE_DIR}/hello_world@FILE_EXTENSION@ publisher --samples 10" + depends_on: + - waitset-subscriber diff --git a/test/examples/test_examples.py b/test/examples/test_examples.py index d7d500f88ef..6ed4ebbfaca 100644 --- a/test/examples/test_examples.py +++ b/test/examples/test_examples.py @@ -1,7 +1,6 @@ """.""" import subprocess - def test_basic_configuration(): """.""" ret = False @@ -25,9 +24,50 @@ def test_basic_configuration(): received += 1 continue - if sent * 2 == received: + if sent != 0 and received != 0 and sent * 2 == received: + ret = True + else: + print('ERROR: sent: ' + str(sent) + ', but received: ' + str(received) + + ' (expected: ' + str(sent * 2) + ')') + raise subprocess.CalledProcessError(1, '') + + except subprocess.CalledProcessError: + for l in out: + print(l) + except subprocess.TimeoutExpired: + print('TIMEOUT') + print(out) + + assert(ret) + +def test_hello_world(): + """.""" + ret = False + out = '' + try: + out = subprocess.check_output( + '@DOCKER_EXECUTABLE@ compose -f hello_world.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: + sent += 1 + continue + + if 'RECEIVED' in line: + received += 1 + continue + + if sent != 0 and received != 0 and sent * 2 == received: ret = True else: + print('ERROR: sent: ' + str(sent) + ', but received: ' + str(received) + + ' (expected: ' + str(sent * 2) + ')') raise subprocess.CalledProcessError(1, '') except subprocess.CalledProcessError: @@ -35,5 +75,6 @@ def test_basic_configuration(): print(l) except subprocess.TimeoutExpired: print('TIMEOUT') + print(out) assert(ret) diff --git a/versions.md b/versions.md index dd41d9af90c..4b8d004e0b1 100644 --- a/versions.md +++ b/versions.md @@ -30,6 +30,8 @@ Forthcoming * Refactor Dynamic Language Binding API according to OMG XTypes v1.3 specification. * Refactor ReturnCode complying with OMG DDS specification. * Calling `DataReader::return_loan` returns `ReturnCode_t::RETCODE_OK` both for empty sequences and for sequences that were not loaned. +* Refactor examples: + * Hello world example with wait-sets and environment XML profiles. Version 2.14.0 --------------