diff --git a/fastddsspy_tool/test/CMakeLists.txt b/fastddsspy_tool/test/CMakeLists.txt index 7122e5ff..9bed2926 100644 --- a/fastddsspy_tool/test/CMakeLists.txt +++ b/fastddsspy_tool/test/CMakeLists.txt @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Add subdirectory with tests +add_subdirectory(integration) diff --git a/fastddsspy_tool/test/integration/CMakeLists.txt b/fastddsspy_tool/test/integration/CMakeLists.txt new file mode 100644 index 00000000..cb87eb16 --- /dev/null +++ b/fastddsspy_tool/test/integration/CMakeLists.txt @@ -0,0 +1,110 @@ +# Copyright 2023 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. + +########################################################################### +# Create tests # +########################################################################### + +find_package(PythonInterp 3 REQUIRED) + +# Name of files to test +set(TEST_LIST + trivial + publisher_participant +) + +# windows auxiliary script to fork test execution +set(PWS_LAUNCHER + ${CMAKE_CURRENT_SOURCE_DIR}/launcher.ps1 +) + +# Calculate environment +set(TEST_ENVIRONMENT + "PATH=$ENV{PATH};$") + +foreach(PROJECT_DEPENDENCY ${MODULE_DEPENDENCIES}) + + message(STATUS "Finding target for libraries for ${PROJECT_DEPENDENCY}") + + if(TARGET ${PROJECT_DEPENDENCY} OR TARGET eprosima::${PROJECT_DEPENDENCY} OR TARGET ${PROJECT_DEPENDENCY}::${PROJECT_DEPENDENCY}) + set(TEST_ENVIRONMENT + "${TEST_ENVIRONMENT};$") + + elseif(EXISTS ${PROJECT_DEPENDENCY}_LIBRARY) + get_filename_component( + ${PROJECT_DEPENDENCY}_LIBRARY_DIR ${${PROJECT_DEPENDENCY}_LIBRARY} DIRECTORY) + set(TEST_ENVIRONMENT + "${TEST_ENVIRONMENT};${${PROJECT_DEPENDENCY}_LIBRARY_DIR}") + unset(${PROJECT_DEPENDENCY}_LIBRARY_DIR) + + else() + + message(STATUS "${PROJECT_DEPENDENCY} could not be added to TEST_ENVIRONMENT") + + endif() + +endforeach() + +if(WIN32) + + if(TARGET tinyxml2 OR TARGET tinyxml2::tinyxml2) + set(TEST_ENVIRONMENT + "${TEST_ENVIRONMENT};$") + elseif(EXISTS TINYXML2_LIBRARY) + get_filename_component( + TINYXML2_LIBRARY_DIR ${TINYXML2_LIBRARY} DIRECTORY) + set(TEST_ENVIRONMENT + "${TEST_ENVIRONMENT};${TINYXML2_LIBRARY_DIR}") + unset(TINYXML2_LIBRARY_DIR) + endif() + + if(TARGET yamlcpp OR TARGET yamlcpp::yamlcpp) + set(TEST_ENVIRONMENT + "${TEST_ENVIRONMENT};$") + elseif(EXISTS YAMLCPP_LIBRARY) + get_filename_component( + YAMLCPP_LIBRARY_DIR ${YAMLCPP_LIBRARY} DIRECTORY) + set(TEST_ENVIRONMENT + "${TEST_ENVIRONMENT};${YAMLCPP_LIBRARY_DIR}") + unset(YAMLCPP_LIBRARY_DIR) + endif() + + string(REPLACE ";" "\\;" TEST_ENVIRONMENT "${TEST_ENVIRONMENT}") + +endif(WIN32) + +# populate the tests +foreach(TEST IN LISTS TEST_LIST) + + set(TEST_NAME "tool.application.fastddsspy.${TEST}") + + add_test( + NAME ${TEST_NAME} + COMMAND ${PYTHON_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests.py + "--exe" $ + ) + + # Set test properties + set_tests_properties( + ${TEST_NAME} + PROPERTIES + ENVIRONMENT "${TEST_ENVIRONMENT}" + ) +endforeach() + + +unset(TEST_ENVIRONMENT) + +add_subdirectory(dds/AdvancedConfigurationExample) # Source directory diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationPublisher.cpp b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationPublisher.cpp new file mode 100644 index 00000000..66022add --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationPublisher.cpp @@ -0,0 +1,365 @@ +// Copyright 2022 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 AdvancedConfigurationPublisher.cpp + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AdvancedConfigurationPublisher.h" + +using namespace eprosima::fastdds::dds; +using namespace eprosima::fastdds::rtps; + +std::atomic HelloWorldPublisher::stop_(false); +std::mutex HelloWorldPublisher::PubListener::wait_matched_cv_mtx_; +std::condition_variable HelloWorldPublisher::PubListener::wait_matched_cv_; + +HelloWorldPublisher::HelloWorldPublisher() + : participant_(nullptr) + , publisher_(nullptr) + , topic_(nullptr) + , type_(new HelloWorldPubSubType()) +{ +} + +bool HelloWorldPublisher::is_stopped() +{ + return stop_; +} + +void HelloWorldPublisher::stop() +{ + stop_ = true; + PubListener::awake(); +} + +bool HelloWorldPublisher::init( + const std::string& topic_name, + uint32_t domain, + uint32_t num_wait_matched, + bool async, + TransportType transport, + bool reliable, + bool transient, + int hops, + const std::string& partitions, + bool use_ownership, + unsigned int ownership_strength /* = 0 */) +{ + hello_.index(0); + memcpy(hello_.message().data(), "HelloWorld ", strlen("HelloWorld") + 1); + + DomainParticipantQos pqos; + pqos.name("Participant_pub"); + listener_.set_num_wait_matched(num_wait_matched); + + // TRANSPORT CONFIG + // If it is set, not use default and set the transport + if (transport != DEFAULT || hops > 0 ) + { + pqos.transport().use_builtin_transports = false; + + switch ( transport ) + { + case SHM: + { + auto shm_transport = std::make_shared(); + pqos.transport().user_transports.push_back(shm_transport); + } + break; + case UDPv4: + { + auto udp_transport = std::make_shared(); + pqos.transport().user_transports.push_back(udp_transport); + } + break; + case UDPv6: + { + auto udp_transport = std::make_shared(); + pqos.transport().user_transports.push_back(udp_transport); + } + break; + case DEFAULT: + default: + { + // mimick default transport selection + auto udp_transport = std::make_shared(); + pqos.transport().user_transports.push_back(udp_transport); +#ifdef SHM_TRANSPORT_BUILTIN + auto shm_transport = std::make_shared(); + pqos.transport().user_transports.push_back(shm_transport); +#endif // SHM_TRANSPORT_BUILTIN + } + } + + if ( hops > 0 ) + { + for (auto& transportDescriptor : pqos.transport().user_transports) + { + SocketTransportDescriptor* pT = dynamic_cast(transportDescriptor.get()); + if (pT) + { + pT->TTL = (uint8_t)std::min(hops, 255); + } + } + } + } + + // CREATE THE PARTICIPANT + participant_ = DomainParticipantFactory::get_instance()->create_participant(domain, pqos); + + if (participant_ == nullptr) + { + return false; + } + + // REGISTER THE TYPE + type_.register_type(participant_); + + // CREATE THE PUBLISHER + PublisherQos pbqos; + + if (!partitions.empty()) + { + // Divide in partitions by ; + std::stringstream spartitions(partitions); + std::string partition_cut; + while (std::getline(spartitions, partition_cut, ';')) + { + pbqos.partition().push_back(partition_cut.c_str()); + } + } + + // Create publisher + publisher_ = participant_->create_publisher(pbqos, nullptr); + + if (publisher_ == nullptr) + { + return false; + } + + for(unsigned int i = 0; i < NUM_TOPICS; i++) + { + // CREATE THE TOPIC + topic_ = participant_->create_topic(topic_name+std::to_string(i), "HelloWorld", TOPIC_QOS_DEFAULT); + + if (topic_ == nullptr) + { + return false; + } + + // CREATE THE WRITER + DataWriterQos wqos = DATAWRITER_QOS_DEFAULT; + + // Data sharing set in endpoint. If it is not default, set it to off + if (transport != DEFAULT) + { + wqos.data_sharing().off(); + } + else + { + wqos.data_sharing().automatic(); // default + } + + if (async) + { + wqos.publish_mode().kind = ASYNCHRONOUS_PUBLISH_MODE; + } + else + { + wqos.publish_mode().kind = SYNCHRONOUS_PUBLISH_MODE; // default + } + + if (reliable) + { + wqos.reliability().kind = RELIABLE_RELIABILITY_QOS; + wqos.history().kind = KEEP_ALL_HISTORY_QOS; + } + else + { + wqos.reliability().kind = BEST_EFFORT_RELIABILITY_QOS; // default in this example (although default value for + // writters' qos actually is RELIABLE) + } + + if (transient) + { + wqos.durability().kind = TRANSIENT_LOCAL_DURABILITY_QOS; + wqos.history().kind = KEEP_ALL_HISTORY_QOS; // store previously sent samples so they can be resent to newly + // matched DataReaders + } + else + { + wqos.durability().kind = VOLATILE_DURABILITY_QOS; // default in this example (although default value for + // writters' qos actually is TRANSIENT_LOCAL) + } + + // Set ownership + if (use_ownership) + { + wqos.ownership().kind = OwnershipQosPolicyKind::EXCLUSIVE_OWNERSHIP_QOS; + wqos.ownership_strength().value = ownership_strength; + } + + writers_[i] = publisher_->create_datawriter(topic_, wqos, &listener_); + + if (writers_[i] == nullptr) + { + return false; + } + + std::cout << "Publisher Participant created with DataWriter Guid [ " << writers_[i]->guid() << " ]." << std::endl; + } + return true; +} + +HelloWorldPublisher::~HelloWorldPublisher() +{ + if (participant_ != nullptr) + { + if (publisher_ != nullptr) + { + for(unsigned int i = 0; i < NUM_TOPICS; i++) + { + if (writers_[i] != nullptr) + { + publisher_->delete_datawriter(writers_[i]); + } + } + 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.current_count; + std::cout << "Publisher matched [ " << iHandle2GUID(info.last_subscription_handle) << " ]." << std::endl; + if (enough_matched()) + { + awake(); + } + } + else if (info.current_count_change == -1) + { + matched_ = info.current_count; + std::cout << "Publisher unmatched [ " << iHandle2GUID(info.last_subscription_handle) << " ]." << std::endl; + } + else + { + std::cout << info.current_count_change + << " is not a valid value for PublicationMatchedStatus current count change" << std::endl; + } +} + +void HelloWorldPublisher::PubListener::set_num_wait_matched( + uint32_t num_wait_matched) +{ + num_wait_matched_ = num_wait_matched; +} + +bool HelloWorldPublisher::PubListener::enough_matched() +{ + return matched_ >= num_wait_matched_; +} + +void HelloWorldPublisher::PubListener::wait() +{ + std::unique_lock lck(wait_matched_cv_mtx_); + wait_matched_cv_.wait(lck, [this] + { + return enough_matched() || is_stopped(); + }); +} + +void HelloWorldPublisher::PubListener::awake() +{ + wait_matched_cv_.notify_all(); +} + +void HelloWorldPublisher::runThread( + uint32_t samples, + uint32_t sleep) +{ + while (!is_stopped() && (samples == 0 || hello_.index() < samples)) + { + if (listener_.enough_matched()) + { + publish(); + std::cout << "Message: " << hello_.message().data() << " with index: " << hello_.index() + << " SENT" << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); + } + else + { + listener_.wait(); + } + } +} + +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 CTRL+C to stop the Publisher at any time." << std::endl; + } + else + { + std::cout << "Publisher running " << samples << + " samples. Please press CTRL+C to stop the Publisher at any time." << std::endl; + } + signal(SIGINT, [](int signum) + { + std::cout << "SIGINT received, stopping Publisher execution." << std::endl; + static_cast(signum); HelloWorldPublisher::stop(); + }); + thread.join(); +} + +void HelloWorldPublisher::publish() +{ + hello_.index(hello_.index() + 1); + for(unsigned int i = 0; i < NUM_TOPICS; i++) + { + writers_[i]->write(&hello_); + } +} diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationPublisher.h b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationPublisher.h new file mode 100644 index 00000000..6c47d468 --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationPublisher.h @@ -0,0 +1,151 @@ +// Copyright 2022 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 AdvancedConfigurationPublisher.h + * + */ + +#ifndef _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_ADVANCEDCONFIGURATIONPUBLISHER_H_ +#define _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_ADVANCEDCONFIGURATIONPUBLISHER_H_ + +#include +#include +#include + +#include +#include +#include + +#include "HelloWorldPubSubTypes.h" +#include "types.hpp" + +/** + * Class used to group into a single working unit a Publisher with a DataWriter, its listener, and a TypeSupport member + * corresponding to the HelloWorld datatype + */ +class HelloWorldPublisher +{ +public: + + HelloWorldPublisher(); + + virtual ~HelloWorldPublisher(); + + //! Initialize the publisher + bool init( + const std::string& topic_name, + uint32_t domain, + uint32_t num_wait_matched, + bool async, + TransportType transport, + bool reliable, + bool transient, + int hops, + const std::string& partitions, + bool use_ownership, + unsigned int ownership_strength); + + //! Publish a sample + void publish(); + + //! Run for number samples, publish every sleep seconds + void run( + uint32_t number, + uint32_t sleep); + + //! Return the current state of execution + static bool is_stopped(); + + //! Trigger the end of execution + static void stop(); + +private: + + HelloWorld hello_; + + eprosima::fastdds::dds::DomainParticipant* participant_; + + eprosima::fastdds::dds::Publisher* publisher_; + + eprosima::fastdds::dds::Topic* topic_; + + // std::array writers_; + std::vector writers_ = std::vector(NUM_TOPICS, nullptr); + + eprosima::fastdds::dds::TypeSupport type_; + + /** + * Class handling discovery events and dataflow + */ + class PubListener : public eprosima::fastdds::dds::DataWriterListener + { + public: + + PubListener() + : matched_(0) + , num_wait_matched_(0) + { + } + + ~PubListener() override + { + } + + //! Callback executed when a DataReader is matched or unmatched + void on_publication_matched( + eprosima::fastdds::dds::DataWriter* writer, + const eprosima::fastdds::dds::PublicationMatchedStatus& info) override; + + //! Set the number of matched DataReaders required for publishing + void set_num_wait_matched( + uint32_t num_wait_matched); + + //! Return true if there are at least num_wait_matched_ matched DataReaders + bool enough_matched(); + + //! Block the thread until enough DataReaders are matched + void wait(); + + //! Unblock the thread so publication of samples begins/resumes + static void awake(); + + private: + + //! Number of DataReaders matched to the associated DataWriter + std::atomic matched_; + + //! Number of matched DataReaders required for publishing + uint32_t num_wait_matched_; + + //! Protects wait_matched condition variable + static std::mutex wait_matched_cv_mtx_; + + //! Waits until enough DataReaders are matched + static std::condition_variable wait_matched_cv_; + } + listener_; + + //! Run thread for number samples, publish every sleep seconds + void runThread( + uint32_t number, + uint32_t sleep); + + //! Member used for control flow purposes + static std::atomic stop_; +}; + + + +#endif /* _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_ADVANCEDCONFIGURATIONPUBLISHER_H_ */ diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationSubscriber.cpp b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationSubscriber.cpp new file mode 100644 index 00000000..10440bdf --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationSubscriber.cpp @@ -0,0 +1,320 @@ +// Copyright 2022 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 AdvancedConfigurationSubscriber.cpp + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AdvancedConfigurationSubscriber.h" + +using namespace eprosima::fastdds::dds; +using namespace eprosima::fastdds::rtps; + +std::atomic HelloWorldSubscriber::stop_(false); +std::mutex HelloWorldSubscriber::terminate_cv_mtx_; +std::condition_variable HelloWorldSubscriber::terminate_cv_; + +HelloWorldSubscriber::HelloWorldSubscriber() + : participant_(nullptr) + , subscriber_(nullptr) + , topic_(nullptr) + , type_(new HelloWorldPubSubType()) +{ +} + +bool HelloWorldSubscriber::is_stopped() +{ + return stop_; +} + +void HelloWorldSubscriber::stop() +{ + stop_ = true; + terminate_cv_.notify_all(); +} + +bool HelloWorldSubscriber::init( + const std::string& topic_name, + uint32_t max_messages, + uint32_t domain, + TransportType transport, + bool reliable, + bool transient, + int hops, + const std::string& partitions, + bool use_ownership) +{ + DomainParticipantQos pqos; + pqos.name("Participant_sub"); + + // TRANSPORT CONFIG + // If it is set, not use default and set the transport + if (transport != DEFAULT || hops > 0 ) + { + pqos.transport().use_builtin_transports = false; + + switch ( transport ) + { + case SHM: + { + auto shm_transport = std::make_shared(); + pqos.transport().user_transports.push_back(shm_transport); + } + break; + case UDPv4: + { + auto udp_transport = std::make_shared(); + pqos.transport().user_transports.push_back(udp_transport); + } + break; + case UDPv6: + { + auto udp_transport = std::make_shared(); + pqos.transport().user_transports.push_back(udp_transport); + } + break; + case DEFAULT: + default: + { + // mimick default transport selection + auto udp_transport = std::make_shared(); + pqos.transport().user_transports.push_back(udp_transport); +#ifdef SHM_TRANSPORT_BUILTIN + auto shm_transport = std::make_shared(); + pqos.transport().user_transports.push_back(shm_transport); +#endif // SHM_TRANSPORT_BUILTIN + } + } + + if ( hops > 0 ) + { + for (auto& transportDescriptor : pqos.transport().user_transports) + { + SocketTransportDescriptor* pT = dynamic_cast(transportDescriptor.get()); + if (pT) + { + pT->TTL = (uint8_t)std::min(hops, 255); + } + } + } + } + + // CREATE THE PARTICIPANT + participant_ = DomainParticipantFactory::get_instance()->create_participant(domain, pqos); + + if (participant_ == nullptr) + { + return false; + } + + // REGISTER THE TYPE + type_.register_type(participant_); + + // CREATE THE SUBSCRIBER + SubscriberQos sqos; + + if (!partitions.empty()) + { + // Divide in partitions by ; + std::stringstream spartitions(partitions); + std::string partition_cut; + while (std::getline(spartitions, partition_cut, ';')) + { + sqos.partition().push_back(partition_cut.c_str()); + } + } + + subscriber_ = participant_->create_subscriber(sqos, nullptr); + + if (subscriber_ == nullptr) + { + return false; + } + + for(unsigned int i = 0; i < NUM_TOPICS; i++) + { + // CREATE THE TOPIC + topic_ = participant_->create_topic( + topic_name+std::to_string(i), + "HelloWorld", + TOPIC_QOS_DEFAULT); + + if (topic_ == nullptr) + { + return false; + } + + // CREATE THE READER + if (max_messages > 0) + { + listener_.set_max_messages(max_messages); + } + DataReaderQos rqos = DATAREADER_QOS_DEFAULT; + + // Data sharing set in endpoint. If it is not default, set it to off + if (transport != DEFAULT) + { + rqos.data_sharing().off(); + } + else + { + rqos.data_sharing().automatic(); // default + } + + if (reliable) + { + rqos.reliability().kind = RELIABLE_RELIABILITY_QOS; + rqos.history().kind = KEEP_ALL_HISTORY_QOS; + } + else + { + rqos.reliability().kind = BEST_EFFORT_RELIABILITY_QOS; // default + } + + if (transient) + { + rqos.durability().kind = TRANSIENT_LOCAL_DURABILITY_QOS; + } + else + { + rqos.durability().kind = VOLATILE_DURABILITY_QOS; // default + } + + // Set ownership + if (use_ownership) + { + rqos.ownership().kind = OwnershipQosPolicyKind::EXCLUSIVE_OWNERSHIP_QOS; + } + + readers_[i] = subscriber_->create_datareader(topic_, rqos, &listener_); + + if (readers_[i] == nullptr) + { + return false; + } + + std::cout << "Subscriber Participant created with DataReader Guid [ " << readers_[i]->guid() << " ]." << std::endl; + } + + + return true; +} + +HelloWorldSubscriber::~HelloWorldSubscriber() +{ + if (participant_ != nullptr) + { + if (topic_ != nullptr) + { + participant_->delete_topic(topic_); + } + if (subscriber_ != nullptr) + { + for(unsigned int i = 0; i < NUM_TOPICS; i++) + { + if (readers_[i] != nullptr) + { + subscriber_->delete_datareader(readers_[i]); + } + } + participant_->delete_subscriber(subscriber_); + } + DomainParticipantFactory::get_instance()->delete_participant(participant_); + } +} + +void HelloWorldSubscriber::SubListener::set_max_messages( + uint32_t max_messages) +{ + max_messages_ = max_messages; +} + +void HelloWorldSubscriber::SubListener::on_subscription_matched( + DataReader*, + const SubscriptionMatchedStatus& info) +{ + if (info.current_count_change == 1) + { + matched_ = info.current_count; + std::cout << "Subscriber matched [ " << iHandle2GUID(info.last_publication_handle) << " ]." << std::endl; + } + else if (info.current_count_change == -1) + { + matched_ = info.current_count; + std::cout << "Subscriber unmatched [ " << iHandle2GUID(info.last_publication_handle) << " ]." << 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; + while ((reader->take_next_sample(&hello_, &info) == ReturnCode_t::RETCODE_OK) && !is_stopped()) + { + if (info.instance_state == ALIVE_INSTANCE_STATE) + { + samples_++; + // Print your structure data here. + std::cout << "Message " << hello_.message().data() << " " << hello_.index() << " RECEIVED" << std::endl; + if (max_messages_ > 0 && (samples_ >= max_messages_)) + { + stop(); + } + } + } +} + +void HelloWorldSubscriber::run( + uint32_t samples) +{ + stop_ = false; + if (samples > 0) + { + std::cout << "Subscriber running until " << samples << + " samples have been received. Please press CTRL+C to stop the Subscriber at any time." << std::endl; + } + else + { + std::cout << "Subscriber running. Please press CTRL+C to stop the Subscriber." << std::endl; + } + signal(SIGINT, [](int signum) + { + std::cout << "SIGINT received, stopping Subscriber execution." << std::endl; + static_cast(signum); HelloWorldSubscriber::stop(); + }); + std::unique_lock lck(terminate_cv_mtx_); + terminate_cv_.wait(lck, [] + { + return is_stopped(); + }); +} diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationSubscriber.h b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationSubscriber.h new file mode 100644 index 00000000..f6de427d --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationSubscriber.h @@ -0,0 +1,137 @@ +// Copyright 2022 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 AdvancedConfigurationSubscriber.h + * + */ + +#ifndef _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_ADVANCEDCONFIGURATIONSUBSCRIBER_H_ +#define _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_ADVANCEDCONFIGURATIONSUBSCRIBER_H_ + +#include +#include +#include +#include + +#include +#include +#include + +#include "HelloWorldPubSubTypes.h" +#include "types.hpp" + +/** + * Class used to group into a single working unit a Subscriber with a DataReader, its listener, and a TypeSupport member + * corresponding to the HelloWorld datatype + */ +class HelloWorldSubscriber +{ +public: + + HelloWorldSubscriber(); + + virtual ~HelloWorldSubscriber(); + + //! Initialize the subscriber + bool init( + const std::string& topic_name, + uint32_t max_messages, + uint32_t domain, + TransportType transport, + bool reliable, + bool transient, + int hops, + const std::string& partitions, + bool use_ownership); + + //! RUN the subscriber until number samples are received + void run( + uint32_t number); + + //! Return the current state of execution + static bool is_stopped(); + + //! Trigger the end of execution + static void stop(); + +private: + + eprosima::fastdds::dds::DomainParticipant* participant_; + + eprosima::fastdds::dds::Subscriber* subscriber_; + + eprosima::fastdds::dds::Topic* topic_; + + std::vector readers_ = std::vector(NUM_TOPICS, nullptr); + + eprosima::fastdds::dds::TypeSupport type_; + + /** + * Class handling discovery and dataflow events + */ + class SubListener : public eprosima::fastdds::dds::DataReaderListener + { + public: + + SubListener() + : matched_(0) + , samples_(0) + , max_messages_(0) + { + } + + ~SubListener() override + { + } + + //! Set the maximum number of messages to receive before exiting + void set_max_messages( + uint32_t max_messages); + + //! Callback executed when a new sample is received + void on_data_available( + eprosima::fastdds::dds::DataReader* reader) override; + + //! Callback executed when a DataWriter is matched or unmatched + void on_subscription_matched( + eprosima::fastdds::dds::DataReader* reader, + const eprosima::fastdds::dds::SubscriptionMatchedStatus& info) override; + + private: + + HelloWorld hello_; + + //! Number of DataWriters matched to the associated DataReader + int matched_; + + //! Number of samples received + uint32_t samples_; + + //! Number of messages to be received before triggering termination of execution + uint32_t max_messages_; + } + listener_; + + //! Member used for control flow purposes + static std::atomic stop_; + + //! Protects terminate condition variable + static std::mutex terminate_cv_mtx_; + + //! Waits during execution until SIGINT or max_messages_ samples are received + static std::condition_variable terminate_cv_; +}; + +#endif /* _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_ADVANCEDCONFIGURATIONSUBSCRIBER_H_ */ diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfiguration_main.cpp b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfiguration_main.cpp new file mode 100644 index 00000000..ce6e7b43 --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfiguration_main.cpp @@ -0,0 +1,267 @@ +// Copyright 2022 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 AdvancedConfiguration_main.cpp + * + */ + +#include + +#include "arg_configuration.h" +#include "AdvancedConfigurationPublisher.h" +#include "AdvancedConfigurationSubscriber.h" +#include "types.hpp" + +enum EntityType +{ + PUBLISHER, + SUBSCRIBER +}; + +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) + + EntityType type = PUBLISHER; + std::string topic_name = "HelloWorldTopic"; + int count = 0; + long sleep = 100; + int num_wait_matched = 0; + int domain = 0; + bool async = false; + TransportType transport = DEFAULT; + int hops = -1; + bool reliable = false; + bool transient = false; // transient local + std::string partitions = ""; + bool use_ownership = false; + unsigned int ownership_strength = 0; + // + 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]); + + if (parse.error()) + { + option::printUsage(fwrite, stdout, usage, columns); + return 1; + } + + if (options[optionIndex::HELP]) + { + option::printUsage(fwrite, stdout, usage, columns); + return 0; + } + + // Decide between publisher or subscriber + try + { + if (parse.nonOptionsCount() != 1) + { + throw 1; + } + + const char* type_name = parse.nonOption(0); + + // make sure is the first option + if (parse.optionsCount() && type_name >= buffer[0].name) + { + throw 1; + } + + if (strcmp(type_name, "publisher") == 0) + { + type = PUBLISHER; + } + else if (strcmp(type_name, "subscriber") == 0) + { + type = SUBSCRIBER; + } + else + { + throw 1; + } + } + catch (int error) + { + std::cerr << "ERROR: first argument must be followed by - or -- options" << std::endl; + option::printUsage(fwrite, stdout, usage, columns); + return error; + } + + for (int i = 0; i < parse.optionsCount(); ++i) + { + option::Option& opt = buffer[i]; + switch (opt.index()) + { + case optionIndex::HELP: + // not possible, because handled further above and exits the program + break; + + case optionIndex::TOPIC: + topic_name = std::string(opt.arg); + break; + + case optionIndex::DOMAIN_ID: + domain = strtol(opt.arg, nullptr, 10); + break; + + case optionIndex::SAMPLES: + count = strtol(opt.arg, nullptr, 10); + break; + + case optionIndex::INTERVAL: + if (type == PUBLISHER) + { + sleep = strtol(opt.arg, nullptr, 10); + } + else + { + print_warning("publisher", opt.name); + } + break; + + case optionIndex::WAIT: + if (type == PUBLISHER) + { + num_wait_matched = strtol(opt.arg, nullptr, 10); + } + else + { + print_warning("publisher", opt.name); + } + break; + + case optionIndex::ASYNC: + if (type == PUBLISHER) + { + async = true; + } + else + { + print_warning("publisher", opt.name); + } + break; + + case optionIndex::TRANSPORT: + if (strcmp(opt.arg, "shm") == 0) + { + transport = SHM; + } + else if (strcmp(opt.arg, "udp") == 0 || (strcmp(opt.arg, "udpv4") == 0)) + { + transport = UDPv4; + } + else if (strcmp(opt.arg, "udpv6") == 0) + { + transport = UDPv6; + } + break; + + case optionIndex::RELIABLE: + reliable = true; + break; + + case optionIndex::TRANSIENT_LOCAL: + transient = true; + break; + + case optionIndex::TTL: + hops = strtol(opt.arg, nullptr, 10); + break; + + case optionIndex::PARTITIONS: + partitions = std::string(opt.arg); + break; + + case optionIndex::OWNERSHIP: + use_ownership = true; + break; + + case optionIndex::OWNERSHIP_STRENGTH: + if (type == PUBLISHER) + { + use_ownership = true; + ownership_strength = strtol(opt.arg, nullptr, 10); + } + else + { + print_warning("publisher", opt.name); + } + break; + + case optionIndex::UNKNOWN_OPT: + std::cerr << "ERROR: " << opt.name << " is not a valid argument." << std::endl; + option::printUsage(fwrite, stdout, usage, columns); + return 1; + break; + } + } + if (transient && !reliable) + { + std::cerr << "WARNING: --transient will take no effect since not reliable." << std::endl; + } + + if (transport == SHM && hops > 0 ) + { + std::cerr << "WARNING: --ttl will take no effect since not using UDP transport." << std::endl; + } + + switch (type) + { + case PUBLISHER: + { + HelloWorldPublisher mypub; + if (mypub.init(topic_name, static_cast(domain), static_cast(num_wait_matched), async, + transport, reliable, transient, hops, partitions, use_ownership, ownership_strength)) + { + mypub.run(static_cast(count), static_cast(sleep)); + } + break; + } + case SUBSCRIBER: + { + HelloWorldSubscriber mysub; + if (mysub.init(topic_name, static_cast(count), static_cast(domain), transport, + reliable, transient, hops, partitions, use_ownership)) + { + mysub.run(static_cast(count)); + } + break; + } + } + return 0; +} diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/CMakeLists.txt b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/CMakeLists.txt new file mode 100644 index 00000000..59b34836 --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/CMakeLists.txt @@ -0,0 +1,56 @@ +# Copyright 2023 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.16.3) + +project(AdvancedConfigurationExample) + +# Find requirements +if(NOT fastcdr_FOUND) + find_package(fastcdr REQUIRED) +endif() + +if(NOT fastrtps_FOUND) + find_package(fastrtps REQUIRED) +endif() + +#Check C++11 +include(CheckCXXCompilerFlag) +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + check_cxx_compiler_flag(-std=c++11 SUPPORTS_CXX11) + 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 AdvancedConfiguration example...") +file(GLOB ADVANCED_CONFIG_EXAMPLE_SOURCES_CXX "*.cxx") +file(GLOB ADVANCED_CONFIG_EXAMPLE_SOURCES_CPP "*.cpp") + +set( + EXAMPLE_SOURCES + ${ADVANCED_CONFIG_EXAMPLE_SOURCES_CXX} + ${ADVANCED_CONFIG_EXAMPLE_SOURCES_CPP} + ) + +add_executable(${PROJECT_NAME} ${EXAMPLE_SOURCES}) +target_compile_definitions(${PROJECT_NAME} PRIVATE + $<$>,$>:__DEBUG> + $<$:__INTERNALDEBUG> # Internal debug activated. +) + +target_link_libraries(${PROJECT_NAME} fastrtps fastcdr fastdds::optionparser) +# install(TARGETS ${PROJECT_NAME} +# RUNTIME DESTINATION examples/cpp/dds/${PROJECT_NAME}/${BIN_INSTALL_DIR}) diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorld.cxx b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorld.cxx new file mode 100644 index 00000000..775eb649 --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorld.cxx @@ -0,0 +1,239 @@ +// 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 HelloWorld.cpp + * This source file contains the definition of the described types in the IDL file. + * + * This file was generated by the tool gen. + */ + +#ifdef _WIN32 +// Remove linker warning LNK4221 on Visual Studio +namespace { +char dummy; +} // namespace +#endif // _WIN32 + +#include "HelloWorld.h" +#include + +#include +using namespace eprosima::fastcdr::exception; + +#include + +HelloWorld::HelloWorld() +{ + // m_index com.eprosima.idl.parser.typecode.PrimitiveTypeCode@2b552920 + m_index = 0; + // m_message com.eprosima.idl.parser.typecode.ArrayTypeCode@1f36e637 + memset(&m_message, 0, (20) * 1); + +} + +HelloWorld::~HelloWorld() +{ + + +} + +HelloWorld::HelloWorld( + const HelloWorld& x) +{ + m_index = x.m_index; + m_message = x.m_message; +} + +HelloWorld::HelloWorld( + HelloWorld&& x) noexcept +{ + m_index = x.m_index; + m_message = std::move(x.m_message); +} + +HelloWorld& HelloWorld::operator =( + const HelloWorld& x) +{ + + m_index = x.m_index; + m_message = x.m_message; + + return *this; +} + +HelloWorld& HelloWorld::operator =( + HelloWorld&& x) noexcept +{ + + m_index = x.m_index; + m_message = std::move(x.m_message); + + return *this; +} + +bool HelloWorld::operator ==( + const HelloWorld& x) const +{ + + return (m_index == x.m_index && m_message == x.m_message); +} + +bool HelloWorld::operator !=( + const HelloWorld& x) const +{ + return !(*this == x); +} + +size_t HelloWorld::getMaxCdrSerializedSize( + size_t current_alignment) +{ + size_t initial_alignment = current_alignment; + + + current_alignment += 4 + eprosima::fastcdr::Cdr::alignment(current_alignment, 4); + + + current_alignment += ((20) * 1) + eprosima::fastcdr::Cdr::alignment(current_alignment, 1); + + + + return current_alignment - initial_alignment; +} + +size_t HelloWorld::getCdrSerializedSize( + const HelloWorld& data, + size_t current_alignment) +{ + (void)data; + size_t initial_alignment = current_alignment; + + + current_alignment += 4 + eprosima::fastcdr::Cdr::alignment(current_alignment, 4); + + + current_alignment += ((20) * 1) + eprosima::fastcdr::Cdr::alignment(current_alignment, 1); + + + return current_alignment - initial_alignment; +} + +void HelloWorld::serialize( + eprosima::fastcdr::Cdr& scdr) const +{ + + scdr << m_index; + scdr << m_message; + + +} + +void HelloWorld::deserialize( + eprosima::fastcdr::Cdr& dcdr) +{ + + dcdr >> m_index; + dcdr >> m_message; + +} + +/*! + * @brief This function sets a value in member index + * @param _index New value for member index + */ +void HelloWorld::index( + uint32_t _index) +{ + m_index = _index; +} + +/*! + * @brief This function returns the value of member index + * @return Value of member index + */ +uint32_t HelloWorld::index() const +{ + return m_index; +} + +/*! + * @brief This function returns a reference to member index + * @return Reference to member index + */ +uint32_t& HelloWorld::index() +{ + return m_index; +} + +/*! + * @brief This function copies the value in member message + * @param _message New value to be copied in member message + */ +void HelloWorld::message( + const std::array& _message) +{ + m_message = _message; +} + +/*! + * @brief This function moves the value in member message + * @param _message New value to be moved in member message + */ +void HelloWorld::message( + std::array&& _message) +{ + m_message = std::move(_message); +} + +/*! + * @brief This function returns a constant reference to member message + * @return Constant reference to member message + */ +const std::array& HelloWorld::message() const +{ + return m_message; +} + +/*! + * @brief This function returns a reference to member message + * @return Reference to member message + */ +std::array& HelloWorld::message() +{ + return m_message; +} + +size_t HelloWorld::getKeyMaxCdrSerializedSize( + size_t current_alignment) +{ + size_t current_align = current_alignment; + + + + + + return current_align; +} + +bool HelloWorld::isKeyDefined() +{ + return false; +} + +void HelloWorld::serializeKey( + eprosima::fastcdr::Cdr& scdr) const +{ + (void) scdr; + +} diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorld.h b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorld.h new file mode 100644 index 00000000..d714752b --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorld.h @@ -0,0 +1,234 @@ +// 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 HelloWorld.h + * This header file contains the declaration of the described types in the IDL file. + * + * This file was generated by the tool gen. + */ + +#ifndef _FAST_DDS_GENERATED_HELLOWORLD_H_ +#define _FAST_DDS_GENERATED_HELLOWORLD_H_ + + +#include + +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#define eProsima_user_DllExport __declspec( dllexport ) +#else +#define eProsima_user_DllExport +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define eProsima_user_DllExport +#endif // _WIN32 + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#if defined(HelloWorld_SOURCE) +#define HelloWorld_DllAPI __declspec( dllexport ) +#else +#define HelloWorld_DllAPI __declspec( dllimport ) +#endif // HelloWorld_SOURCE +#else +#define HelloWorld_DllAPI +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define HelloWorld_DllAPI +#endif // _WIN32 + +namespace eprosima { +namespace fastcdr { +class Cdr; +} // namespace fastcdr +} // namespace eprosima + + +/*! + * @brief This class represents the structure HelloWorld defined by the user in the IDL file. + * @ingroup HELLOWORLD + */ +class HelloWorld +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport HelloWorld(); + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~HelloWorld(); + + /*! + * @brief Copy constructor. + * @param x Reference to the object HelloWorld that will be copied. + */ + eProsima_user_DllExport HelloWorld( + const HelloWorld& x); + + /*! + * @brief Move constructor. + * @param x Reference to the object HelloWorld that will be copied. + */ + eProsima_user_DllExport HelloWorld( + HelloWorld&& x) noexcept; + + /*! + * @brief Copy assignment. + * @param x Reference to the object HelloWorld that will be copied. + */ + eProsima_user_DllExport HelloWorld& operator =( + const HelloWorld& x); + + /*! + * @brief Move assignment. + * @param x Reference to the object HelloWorld that will be copied. + */ + eProsima_user_DllExport HelloWorld& operator =( + HelloWorld&& x) noexcept; + + /*! + * @brief Comparison operator. + * @param x HelloWorld object to compare. + */ + eProsima_user_DllExport bool operator ==( + const HelloWorld& x) const; + + /*! + * @brief Comparison operator. + * @param x HelloWorld object to compare. + */ + eProsima_user_DllExport bool operator !=( + const HelloWorld& x) const; + + /*! + * @brief This function sets a value in member index + * @param _index New value for member index + */ + eProsima_user_DllExport void index( + uint32_t _index); + + /*! + * @brief This function returns the value of member index + * @return Value of member index + */ + eProsima_user_DllExport uint32_t index() const; + + /*! + * @brief This function returns a reference to member index + * @return Reference to member index + */ + eProsima_user_DllExport uint32_t& index(); + + /*! + * @brief This function copies the value in member message + * @param _message New value to be copied in member message + */ + eProsima_user_DllExport void message( + const std::array& _message); + + /*! + * @brief This function moves the value in member message + * @param _message New value to be moved in member message + */ + eProsima_user_DllExport void message( + std::array&& _message); + + /*! + * @brief This function returns a constant reference to member message + * @return Constant reference to member message + */ + eProsima_user_DllExport const std::array& message() const; + + /*! + * @brief This function returns a reference to member message + * @return Reference to member message + */ + eProsima_user_DllExport std::array& message(); + + /*! + * @brief This function returns the maximum serialized size of an object + * depending on the buffer alignment. + * @param current_alignment Buffer alignment. + * @return Maximum serialized size. + */ + eProsima_user_DllExport static size_t getMaxCdrSerializedSize( + size_t current_alignment = 0); + + /*! + * @brief This function returns the serialized size of a data depending on the buffer alignment. + * @param data Data which is calculated its serialized size. + * @param current_alignment Buffer alignment. + * @return Serialized size. + */ + eProsima_user_DllExport static size_t getCdrSerializedSize( + const HelloWorld& data, + size_t current_alignment = 0); + + + /*! + * @brief This function serializes an object using CDR serialization. + * @param cdr CDR serialization object. + */ + eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& cdr) const; + + /*! + * @brief This function deserializes an object using CDR serialization. + * @param cdr CDR serialization object. + */ + eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr); + + + + /*! + * @brief This function returns the maximum serialized size of the Key of an object + * depending on the buffer alignment. + * @param current_alignment Buffer alignment. + * @return Maximum serialized size. + */ + eProsima_user_DllExport static size_t getKeyMaxCdrSerializedSize( + size_t current_alignment = 0); + + /*! + * @brief This function tells you if the Key has been defined for this type + */ + eProsima_user_DllExport static bool isKeyDefined(); + + /*! + * @brief This function serializes the key members of an object using CDR serialization. + * @param cdr CDR serialization object. + */ + eProsima_user_DllExport void serializeKey( + eprosima::fastcdr::Cdr& cdr) const; + +private: + + uint32_t m_index; + std::array m_message; +}; + +#endif // _FAST_DDS_GENERATED_HELLOWORLD_H_ \ No newline at end of file diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorld.idl b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorld.idl new file mode 100644 index 00000000..9750fbe1 --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorld.idl @@ -0,0 +1,5 @@ +struct HelloWorld +{ + unsigned long index; + char message[20]; +}; diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorldPubSubTypes.cxx b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorldPubSubTypes.cxx new file mode 100644 index 00000000..0741f6fd --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorldPubSubTypes.cxx @@ -0,0 +1,170 @@ +// 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 HelloWorldPubSubTypes.cpp + * This header file contains the implementation of the serialization functions. + * + * This file was generated by the tool fastcdrgen. + */ + + +#include +#include + +#include "HelloWorldPubSubTypes.h" + +using SerializedPayload_t = eprosima::fastrtps::rtps::SerializedPayload_t; +using InstanceHandle_t = eprosima::fastrtps::rtps::InstanceHandle_t; + +HelloWorldPubSubType::HelloWorldPubSubType() +{ + setName("HelloWorld"); + auto type_size = HelloWorld::getMaxCdrSerializedSize(); + type_size += eprosima::fastcdr::Cdr::alignment(type_size, 4); /* possible submessage alignment */ + m_typeSize = static_cast(type_size) + 4; /*encapsulation*/ + m_isGetKeyDefined = HelloWorld::isKeyDefined(); + size_t keyLength = HelloWorld::getKeyMaxCdrSerializedSize() > 16 ? + HelloWorld::getKeyMaxCdrSerializedSize() : 16; + m_keyBuffer = reinterpret_cast(malloc(keyLength)); + memset(m_keyBuffer, 0, keyLength); +} + +HelloWorldPubSubType::~HelloWorldPubSubType() +{ + if (m_keyBuffer != nullptr) + { + free(m_keyBuffer); + } +} + +bool HelloWorldPubSubType::serialize( + void* data, + SerializedPayload_t* payload) +{ + HelloWorld* p_type = static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload->data), payload->max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, eprosima::fastcdr::Cdr::DDS_CDR); + payload->encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + // Serialize encapsulation + ser.serialize_encapsulation(); + + try + { + // Serialize the object. + p_type->serialize(ser); + } + catch (eprosima::fastcdr::exception::NotEnoughMemoryException& /*exception*/) + { + return false; + } + + // Get the serialized length + payload->length = static_cast(ser.getSerializedDataLength()); + return true; +} + +bool HelloWorldPubSubType::deserialize( + SerializedPayload_t* payload, + void* data) +{ + try + { + //Convert DATA to pointer of your type + HelloWorld* p_type = static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload->data), payload->length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, eprosima::fastcdr::Cdr::DDS_CDR); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload->encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + p_type->deserialize(deser); + } + catch (eprosima::fastcdr::exception::NotEnoughMemoryException& /*exception*/) + { + return false; + } + + return true; +} + +std::function HelloWorldPubSubType::getSerializedSizeProvider( + void* data) +{ + return [data]() -> uint32_t + { + return static_cast(type::getCdrSerializedSize(*static_cast(data))) + + 4u /*encapsulation*/; + }; +} + +void* HelloWorldPubSubType::createData() +{ + return reinterpret_cast(new HelloWorld()); +} + +void HelloWorldPubSubType::deleteData( + void* data) +{ + delete(reinterpret_cast(data)); +} + +bool HelloWorldPubSubType::getKey( + void* data, + InstanceHandle_t* handle, + bool force_md5) +{ + if (!m_isGetKeyDefined) + { + return false; + } + + HelloWorld* p_type = static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(m_keyBuffer), + HelloWorld::getKeyMaxCdrSerializedSize()); + + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::BIG_ENDIANNESS); + p_type->serializeKey(ser); + if (force_md5 || HelloWorld::getKeyMaxCdrSerializedSize() > 16) + { + m_md5.init(); + m_md5.update(m_keyBuffer, static_cast(ser.getSerializedDataLength())); + m_md5.finalize(); + for (uint8_t i = 0; i < 16; ++i) + { + handle->value[i] = m_md5.digest[i]; + } + } + else + { + for (uint8_t i = 0; i < 16; ++i) + { + handle->value[i] = m_keyBuffer[i]; + } + } + return true; +} + diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorldPubSubTypes.h b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorldPubSubTypes.h new file mode 100644 index 00000000..af8725f1 --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/HelloWorldPubSubTypes.h @@ -0,0 +1,101 @@ +// 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 HelloWorldPubSubTypes.h + * This header file contains the declaration of the serialization functions. + * + * This file was generated by the tool fastcdrgen. + */ + + +#ifndef _FAST_DDS_GENERATED_HELLOWORLD_PUBSUBTYPES_H_ +#define _FAST_DDS_GENERATED_HELLOWORLD_PUBSUBTYPES_H_ + +#include +#include + +#include "HelloWorld.h" + +#if !defined(GEN_API_VER) || (GEN_API_VER != 1) +#error \ + Generated HelloWorld is not compatible with current installed Fast DDS. Please, regenerate it with fastddsgen. +#endif // GEN_API_VER + +/*! + * @brief This class represents the TopicDataType of the type HelloWorld defined by the user in the IDL file. + * @ingroup HELLOWORLD + */ +class HelloWorldPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef HelloWorld type; + + eProsima_user_DllExport HelloWorldPubSubType(); + + eProsima_user_DllExport virtual ~HelloWorldPubSubType() override; + + eProsima_user_DllExport virtual bool serialize( + void* data, + eprosima::fastrtps::rtps::SerializedPayload_t* payload) override; + + eProsima_user_DllExport virtual bool deserialize( + eprosima::fastrtps::rtps::SerializedPayload_t* payload, + void* data) override; + + eProsima_user_DllExport virtual std::function getSerializedSizeProvider( + void* data) override; + + eProsima_user_DllExport virtual bool getKey( + void* data, + eprosima::fastrtps::rtps::InstanceHandle_t* ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport virtual void* createData() override; + + eProsima_user_DllExport virtual void deleteData( + void* data) override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return true; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + eProsima_user_DllExport inline bool is_plain() const override + { + return true; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + new (memory) HelloWorld(); + return true; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + + MD5 m_md5; + unsigned char* m_keyBuffer; +}; + +#endif // _FAST_DDS_GENERATED_HELLOWORLD_PUBSUBTYPES_H_ \ No newline at end of file diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/README.md b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/README.md new file mode 100644 index 00000000..4d3d6137 --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/README.md @@ -0,0 +1,85 @@ +# Advanced Configuration Example + +This example extends the configuration options of a trivial HelloWorld by letting the user specify properties of +entities such as durability, reliability or specify the transport protocol to be used, among other possibilities. This +could be useful, for example, to quickly test whether two endpoints are compatible and hence would match. + +## Execution instructions + +To launch this test open two different consoles: + +In the first one launch: ./AdvancedConfigurationExample publisher (or AdvancedConfigurationExample.exe publisher on windows). +In the second one: ./AdvancedConfigurationExample subscriber (or AdvancedConfigurationExample.exe subscriber on windows). + +## Arguments + +First argument is `publisher` or `subscriber` and then the rest of arguments are read unordered + +```sh +Usage: AdvancedConfigurationExample + +General options: + -h --help + Produce help message. + +Publisher options: + -t --topic= + Topic name (Default: HelloWorldTopic). + -d --domain= + DDS domain ID (Default: 0). + -w --wait= + Number of matched subscribers required to publish (Default: + 0 => does not wait). + -s --samples= + Number of samples to send (Default: 0 => infinite samples). + -i --interval= + Time between samples in milliseconds (Default: 100). + -a --async + Asynchronous publish mode (synchronous by default). + --transport= + Use only shared-memory, UDPv4, or UDPv6 transport.If not + set, use Fast DDS default transports (depending on the + scenario it will use the most efficient one: data-sharing + delivery mechanism > shared-memory > UDP). + -o --ownership + Use Topic with EXCLUSIVE_OWNERSHIP (SHARED_OWNERSHIP by + default). + --strength= + Set this Publisher strength. Set Topic with + EXCLUSIVE_OWNERSHIP. Default: 0 + +Subscriber options: + -t --topic= + Topic name (Default: HelloWorldTopic). + -d --domain= + DDS domain ID (Default: 0). + -s --samples= + Number of samples to wait for (Default: 0 => infinite + samples). + --transport= + Use only shared-memory, UDPv4, or UDPv6 transport.If not + set, use Fast DDS default transports (depending on the + scenario it will use the most efficient one: data-sharing + delivery mechanism > shared-memory > UDP). + -o --ownership + Use Topic with EXCLUSIVE_OWNERSHIP (SHARED_OWNERSHIP by + default). + +QoS options: + -r --reliable + Set reliability to reliable (best-effort by default). + --transient + Set durability to transient local (volatile by default, + ineffective when not reliable). + -p --partitions= + Partitions to match separated by ';'. Single or double + quotes required with multiple partitions. With empty string + ('') no partitions used. (Default: ''). + +Discovery options: + --ttl + Set multicast discovery Time To Live on IPv4 or Hop Limit + for IPv6. If not set, uses Fast-DDS default (1 hop). + Increase it to avoid discovery issues on scenarios with + several routers. Maximum: 255. +``` diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/arg_configuration.h b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/arg_configuration.h new file mode 100644 index 00000000..c53a98f7 --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/arg_configuration.h @@ -0,0 +1,249 @@ +// Copyright 2022 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 arg_configuration.h + * + */ + +#ifndef _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_ARG_CONFIGURATION_H_ +#define _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_ARG_CONFIGURATION_H_ + +#include +#include +#include +#include + +#include + +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 a string argument\n"); + } + return option::ARG_ILLEGAL; + } + + static option::ArgStatus Transport( + const option::Option& option, + bool msg) + { + if (option.arg != 0) + { + std::string transport = std::string(option.arg); + if (transport != "shm" && transport != "udp" && transport != "udpv4" && transport != "udpv6") + { + if (msg) + { + print_error("Option '", option, "' only accepts values\n"); + } + return option::ARG_ILLEGAL; + } + return option::ARG_OK; + } + if (msg) + { + print_error("Option '", option, "' requires a string argument\n"); + } + return option::ARG_ILLEGAL; + } + +}; + +enum optionIndex +{ + UNKNOWN_OPT, + HELP, + TOPIC, + WAIT, + SAMPLES, + INTERVAL, + ASYNC, + DOMAIN_ID, + TRANSPORT, + RELIABLE, + TRANSIENT_LOCAL, + TTL, + PARTITIONS, + OWNERSHIP_STRENGTH, + OWNERSHIP +}; + +const option::Descriptor usage[] = { + { UNKNOWN_OPT, 0, "", "", Arg::None, + "Usage: AdvancedConfigurationExample \n\nGeneral options:" }, + { HELP, 0, "h", "help", Arg::None, " -h \t--help \tProduce help message." }, + + { UNKNOWN_OPT, 0, "", "", Arg::None, "\nPublisher options:"}, + { TOPIC, 0, "t", "topic", Arg::String, + " -t \t--topic= \tTopic name (Default: HelloWorldTopic)." }, + { DOMAIN_ID, 0, "d", "domain", Arg::NumericRange<0, 230>, + " -d \t--domain= \tDDS domain ID (Default: 0)." }, + { WAIT, 0, "w", "wait", Arg::NumericRange<>, + " -w \t--wait= \tNumber of matched subscribers required to publish" + " (Default: 0 => does not wait)." }, + { SAMPLES, 0, "s", "samples", Arg::NumericRange<>, + " -s \t--samples= \tNumber of samples to send (Default: 0 => infinite samples)." }, + { INTERVAL, 0, "i", "interval", Arg::NumericRange<>, + " -i \t--interval= \tTime between samples in milliseconds (Default: 100)." }, + { ASYNC, 0, "a", "async", Arg::None, + " -a \t--async \tAsynchronous publish mode (synchronous by default)." }, + { TRANSPORT, 0, "", "transport", Arg::Transport, + " \t--transport= \tUse only shared-memory, UDPv4, or UDPv6 transport." + "If not set, use Fast DDS default transports (depending on the scenario it will use the most efficient one:" + " data-sharing delivery mechanism > shared-memory > UDP)." }, + { OWNERSHIP, 0, "o", "ownership", Arg::None, + " -o \t--ownership \tUse Topic with EXCLUSIVE_OWNERSHIP (SHARED_OWNERSHIP by default)."}, + { OWNERSHIP_STRENGTH, 0, "", "strength", Arg::NumericRange<>, + " \t--strength= \tSet this Publisher strength. Set Topic with EXCLUSIVE_OWNERSHIP. Default: 0"}, + + { UNKNOWN_OPT, 0, "", "", Arg::None, "\nSubscriber options:"}, + { TOPIC, 0, "t", "topic", Arg::String, + " -t \t--topic= \tTopic name (Default: HelloWorldTopic)." }, + { DOMAIN_ID, 0, "d", "domain", Arg::NumericRange<0, 230>, + " -d \t--domain= \tDDS domain ID (Default: 0)." }, + { SAMPLES, 0, "s", "samples", Arg::NumericRange<>, + " -s \t--samples= \tNumber of samples to wait for (Default: 0 => infinite samples)." }, + { TRANSPORT, 0, "", "transport", Arg::Transport, + " \t--transport= \tUse only shared-memory, UDPv4, or UDPv6 transport." + "If not set, use Fast DDS default transports (depending on the scenario it will use the most efficient one:" + " data-sharing delivery mechanism > shared-memory > UDP)." }, + { OWNERSHIP, 0, "o", "ownership", Arg::None, + " -o \t--ownership \tUse Topic with EXCLUSIVE_OWNERSHIP (SHARED_OWNERSHIP by default)."}, + + { UNKNOWN_OPT, 0, "", "", Arg::None, "\nQoS options:"}, + { RELIABLE, 0, "r", "reliable", Arg::None, + " -r \t--reliable \tSet reliability to reliable (best-effort by default)." }, + { TRANSIENT_LOCAL, 0, "", "transient", Arg::None, + " \t--transient \tSet durability to transient local (volatile by default, ineffective when not reliable)." }, + { PARTITIONS, 0, "p", "partitions", Arg::String, + " -p \t--partitions= \tPartitions to match separated by ';'." + " Single or double quotes required with multiple partitions." + " With empty string ('') no partitions used. (Default: '')." }, + + { UNKNOWN_OPT, 0, "", "", Arg::None, "\nDiscovery options:"}, + { TTL, 0, "", "ttl", Arg::NumericRange<1, 255>, + "\t--ttl \tSet multicast discovery Time To Live on IPv4 or Hop Limit for IPv6." + " If not set, uses Fast-DDS default (1 hop). Increase it to avoid discovery issues" + " on scenarios with several routers. Maximum: 255."}, + + { 0, 0, 0, 0, 0, 0 } +}; + +void print_warning( + std::string type, + const char* opt) +{ + std::cerr << "WARNING: " << opt << " is a " << type << " option, ignoring argument." << std::endl; +} + +#endif /* _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_ARG_CONFIGURATION_H_ */ diff --git a/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/types.hpp b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/types.hpp new file mode 100644 index 00000000..317ea399 --- /dev/null +++ b/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/types.hpp @@ -0,0 +1,33 @@ +// Copyright 2022 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 AdvancedConfigurationPublisher.h + * + */ + +#ifndef _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_TYPES_HPP_ +#define _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_TYPES_HPP_ + +enum TransportType +{ + DEFAULT, + SHM, + UDPv4, + UDPv6, +}; + +const unsigned int NUM_TOPICS = 10; + +#endif /* _EPROSIMA_FASTDDS_EXAMPLES_CPP_DDS_ADVANCEDCONFIGURATIONEXAMPLE_TYPES_HPP_ */ diff --git a/fastddsspy_tool/test/integration/launcher.ps1 b/fastddsspy_tool/test/integration/launcher.ps1 new file mode 100644 index 00000000..f1b6bfbd --- /dev/null +++ b/fastddsspy_tool/test/integration/launcher.ps1 @@ -0,0 +1,34 @@ +Param( + [Parameter(Position=0, Mandatory=$true)] + [ValidateScript({Test-Path $_ -PathType Leaf -IsValid })] + [String] + # python3 binary + $python_path, + + [Parameter(Position=1, Mandatory=$true)] + [ValidateScript({Test-Path $_ -PathType Leaf -IsValid })] + [String] + # python script that keeps the testing + $test_script, + + [Parameter(Position=2, Mandatory=$true)] + [ValidateScript({Test-Path $_ -PathType Leaf -IsValid })] + [String] + # fastddsspy creation binary full qualified path + $tool_path +) + +$test = Start-Process -Passthru -Wait ` + -FilePath $python_path ` + -ArgumentList ( + $test_script, + "--exe", $tool_path, + "--debug", + "--signal", "sigint") ` + -WindowStyle Hidden + +if( $test.ExitCode -ne 0 ) +{ + $error_message = "Test: $test_name failed with exit code $($test.ExitCode)." + throw $error_message +} diff --git a/fastddsspy_tool/test/integration/publisher_participant.py b/fastddsspy_tool/test/integration/publisher_participant.py new file mode 100644 index 00000000..7f2c1c1b --- /dev/null +++ b/fastddsspy_tool/test/integration/publisher_participant.py @@ -0,0 +1,207 @@ +# Copyright 2023 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. +""" +Tests for the fastddsspy executable. +Contains a package of system test for fastddsspy tool +Usage: test.py -e +Arguments: + Fast DDS Spy binary path : -e | --exe binary_path + Run test in Debug mode : -d | --debug + Samples to receive : -s | --samples +""" + +import argparse +import logging +import os +import subprocess +import sys +import time +import re +import unittest +import yaml + +DESCRIPTION = """Script to validate talkers output""" +USAGE = ('python3 publisher_participant.py -e ' + ' [-d]') + +# Sleep time to let process init and finish +SLEEP_TIME = 1 + +def executable_permission_value(): + """Return executable permissions value depending on the OS.""" + if os.name == 'nt': + return os.X_OK # windows + else: + return os.EX_OK + + +def file_exist_and_have_permissions(file_path): + """Check if a file exists and have executable permissions.""" + if os.access(file_path, executable_permission_value()): + return file_path + else: + return None + +def parse_options(): + """ + Parse arguments. + :return: The arguments parsed. + """ + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + add_help=True, + description=(DESCRIPTION), + usage=(USAGE) + ) + required_args = parser.add_argument_group('required arguments') + required_args.add_argument( + '-e', + '--exe', + type=file_exist_and_have_permissions, + required=True, + help='Path to discovery-server executable.' + ) + parser.add_argument( + '-d', + '--debug', + action='store_true', + help='Print test debugging info.' + ) + return parser.parse_args() + + +def start_spy(fastddsspy, entities = None): + if not entities is None: + command = [fastddsspy, entities] + else: + command = [fastddsspy] + logger.info('Executing command: ' + str(command)) + + proc = subprocess.run(command, capture_output=True, text=True) + + # sleep to let the server run + time.sleep(SLEEP_TIME) + + # Check whether the process has terminated already + # Typically, an exit status of 0 indicates that it ran successfully. + if proc.returncode: + logger.debug('-----------------------------------------------------') + logger.error('Command ' + str(command) + ' failed.') + logger.debug('-----------------------------------------------------') + return 1 + return proc + + +def start_publisher(args): + """ + Build the command to execute the publisher. + + :param args: Arguments parsed + :return: Command to execute the publisher + """ + exec_publisher = args.exe.replace( + "/fastddsspy_tool/fastddsspy", + "/fastddsspy_tool/test/integration/dds/AdvancedConfigurationExample/AdvancedConfigurationExample") + + command = [ + exec_publisher, + 'publisher'] + + proc = subprocess.Popen(command, + stdout=subprocess.PIPE, + universal_newlines=True) + + return proc + + +def stop_publisher(proc): + + # Check whether SIGINT was able to terminate the process + if proc.poll() is None: + # SIGINT couldn't terminate the process. Kill it and exit with code 2 + proc.kill() + output, err = proc.communicate() + logger.debug('-----------------------------------------------------') + logger.debug('Internal Fast DDS Spy output:') + logger.debug('Stdout: \n' + str(output)) + logger.debug('Stderr: \n' + str(err)) + logger.error('Signal could not kill process') + logger.debug('-----------------------------------------------------') + return 1 + + output, err = proc.communicate() + logger.debug('-----------------------------------------------------') + logger.info( + 'Command finished correctly') + logger.debug('Command output:') + logger.debug('Stdout: \n' + str(output)) + logger.debug('Stderr: \n' + str(err)) + logger.debug('-----------------------------------------------------') + + return 0 + + +def check_information(expected_info, result): + """Validate any data as correct.""" + name = "Participant_pub" + guid = "01.0f.af.e6.4f.f7.7c.9b.00.00.00.00|0.0.1.c1" + if not name.__eq__("Participant_pub"): + print('s1 and s3 are not equal') + sys.exit(1) + +if __name__ == '__main__': + + args = parse_options() + + # Create a custom logger + logger = logging.getLogger('SYS_TEST') + # Create handlers + l_handler = logging.StreamHandler() + # Create formatters and add it to handlers + l_format = '[%(asctime)s][%(name)s][%(levelname)s] %(message)s' + l_format = logging.Formatter(l_format) + l_handler.setFormatter(l_format) + # Add handlers to the logger + logger.addHandler(l_handler) + # Set log level + if args.debug: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + + if args.exe is None: + logger.error( + 'Executable binary file does not exist or has no ' + 'executable permissions.') + sys.exit(1) + + publisher = start_publisher(args) + + spy = start_spy(args.exe, "participants") + + print(spy.stdout) + # result = yaml.safe_load(spy.stdout) + # result = yaml.dump(spy.stdout) + result = yaml.load(spy.stdout) + + print("testingggg") + name = result['name'] + print(name) + # assert result['name'] == 'John' + + stop_publisher(publisher) + + check_information("hello", spy.stdout) + + sys.exit(0) # Signal to kill subprocess diff --git a/fastddsspy_tool/test/integration/trivial.py b/fastddsspy_tool/test/integration/trivial.py new file mode 100644 index 00000000..25e6ee7b --- /dev/null +++ b/fastddsspy_tool/test/integration/trivial.py @@ -0,0 +1,130 @@ +# Copyright 2023 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. +""" +Tests for the fastddsspy executable. +Contains a package of system test for fastddsspy tool +Usage: test.py -e +Arguments: + Fast DDS Spy binary path : -e | --exe binary_path + Run test in Debug mode : -d | --debug +""" + +import argparse +import logging +import os +import subprocess +import sys +import time + +DESCRIPTION = """Script to execute Fast DDS Spy executable test""" +USAGE = ('python3 tests.py -e ' + ' [-d]') + +# Sleep time to let process init and finish +SLEEP_TIME = 1 + + +def executable_permission_value(): + """Return executable permissions value depending on the OS.""" + if os.name == 'nt': + return os.X_OK # windows + else: + return os.EX_OK + + +def file_exist_and_have_permissions(file_path): + """Check if a file exists and have executable permissions.""" + if os.access(file_path, executable_permission_value()): + return file_path + else: + return None + + +def parse_options(): + """ + Parse arguments. + :return: The arguments parsed. + """ + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + add_help=True, + description=(DESCRIPTION), + usage=(USAGE) + ) + required_args = parser.add_argument_group('required arguments') + required_args.add_argument( + '-e', + '--exe', + type=file_exist_and_have_permissions, + required=True, + help='Path to discovery-server executable.' + ) + parser.add_argument( + '-d', + '--debug', + action='store_true', + help='Print test debugging info.' + ) + return parser.parse_args() + + +def test_spy_closure(fastddsspy): + + command = [fastddsspy, 'exit'] + logger.info('Executing command: ' + str(command)) + + proc = subprocess.run(command, capture_output=True, text=True) + + # sleep to let the server run + time.sleep(SLEEP_TIME) + + # Check whether the process has terminated already + # Typically, an exit status of 0 indicates that it ran successfully. + if proc.returncode: + logger.debug('-----------------------------------------------------') + logger.error('Command ' + str(command) + ' failed.') + logger.debug('-----------------------------------------------------') + return 1 + return 0 + + +if __name__ == '__main__': + + args = parse_options() + + # Create a custom logger + logger = logging.getLogger('SYS_TEST') + # Create handlers + l_handler = logging.StreamHandler() + # Create formatters and add it to handlers + l_format = '[%(asctime)s][%(name)s][%(levelname)s] %(message)s' + l_format = logging.Formatter(l_format) + l_handler.setFormatter(l_format) + # Add handlers to the logger + logger.addHandler(l_handler) + # Set log level + if args.debug: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + + if args.exe is None: + logger.error( + 'Executable binary file does not exist or has no ' + 'executable permissions.') + sys.exit(1) + + sys.exit( + test_spy_closure( + args.exe)) # Path to executable diff --git a/fastddsspy_tool/test/integration/validation.py b/fastddsspy_tool/test/integration/validation.py new file mode 100644 index 00000000..c4764d93 --- /dev/null +++ b/fastddsspy_tool/test/integration/validation.py @@ -0,0 +1,197 @@ +# Copyright 2022 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 signal +import subprocess +import time +from enum import Enum +from typing import List + +import log + +import utils + + +class ReturnCode(Enum): + """Enumeration for return codes of this script.""" + + SUCCESS = 0 + TIMEOUT = 1 + HARD_TIMEOUT = 2 + NOT_VALID_MESSAGES = 3 + COMMAND_FAIL = 4 + STDERR_OUTPUT = 5 + + +def run_command( + command: 'list[str]', + timeout: float, + delay: float = 0, + timeout_as_error: bool = True): + """ + Run command with timeout. + + :param command: Command to run in list format + :param timeout: Timeout for the process + :return: + - ret_code - The process exit code + - stdout - Output of the process + - stderr - Error output of the process + """ + ret_code = ReturnCode.SUCCESS + + # Delay + utils.delay(delay) + + log.logger.debug(f'Running command: {command}') + + proc = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + + try: + proc.wait(timeout=timeout) + + except subprocess.TimeoutExpired: + if timeout_as_error: + log.logger.error( + 'Timeout expired. ' + 'Killing process before receiving all samples...') + proc.send_signal(signal.SIGINT) + ret_code = ReturnCode.TIMEOUT + + else: + proc.send_signal(signal.SIGINT) + + else: + if not timeout_as_error: + log.logger.error(f'Command finished before expected.') + ret_code = ReturnCode.COMMAND_FAIL + + # Wait a minimum elapsed time to the signal to be received + time.sleep(0.2) + + stdout, stderr = proc.communicate() + + # Check whether SIGINT was able to terminate the process + if proc.poll() is None: + # SIGINT couldn't terminate the process + log.logger.error( + 'SIGINT could not kill process. ' + 'Killing process hardly...') + proc.kill() + ret_code = ReturnCode.HARD_TIMEOUT + + if not stdout: + stdout = '' + if not stderr: + stderr = '' + + return (ret_code, stdout, stderr) + + +def run_and_validate( + command: List[str], + timeout: int, + parse_output_function, + validate_output_function, + delay: float = 0, + timeout_as_error: bool = True): + """ + Run the subscriber and validate its output. + + :param command: Command to run in list format + :param timeout: Timeout for the process + :param parse_output_function: Function to parse the output of the process + :param validate_output_function: Function to validate the output of process + :param timeout_as_error: Whether the timeout reach should be taken as error + + :return: exit code + """ + ret_code, stdout, stderr = run_command( + command=command, + timeout=timeout, + delay=delay, + timeout_as_error=timeout_as_error) + + if ret_code != ReturnCode.SUCCESS: + log.logger.error( + f'Executable exited with ' + f'return code {ret_code}' + f'\n stdout output: \n{stdout}' + f'\n stderr output: \n{stderr}') + + return ret_code + + else: + + log.logger.debug( + f'Executable execution output:' + f'\n stdout output: \n{stdout}') + + if stderr != '': + log.logger.warning( + f'Executable execution output in stderr:' + f'\n{stderr}') + + stdout_parsed, stderr_parsed = parse_output_function(stdout, stderr) + + validate_ret_code = validate_output_function( + stdout_parsed, + stderr_parsed) + + return validate_ret_code + + +""" +AUXILIARY GENERIC FUNCTIONS +""" + + +def find_duplicates(data): + """ + Find duplicates in a list os strings. + + :param data: List of strings + :return: List of tuples with the index of the duplicated strings + """ + duplicates = [] + lines_seen = {} + + for idx, line in enumerate(data): + if line not in lines_seen: + lines_seen[line] = idx + else: + duplicates.append((lines_seen[line], idx)) + + if duplicates: + log.logger.info('Found duplicated messages') + else: + log.logger.debug('None duplicated messages found') + + return duplicates + + +def validate_default(stdout_parsed, stderr_parsed) -> ReturnCode: + """Validate any data as correct.""" + if stderr_parsed != '' and stderr_parsed != []: + return ReturnCode.STDERR_OUTPUT + else: + return ReturnCode.SUCCESS + + +def parse_default(stdout, stderr) -> ReturnCode: + """Return stdout and stderr as they are.""" + return (stdout, stderr)