From 0bd9128361c573428a1f5c98c08f6b1edb4479c3 Mon Sep 17 00:00:00 2001 From: Eduardo Ponz Segrelles Date: Tue, 16 Jul 2024 14:16:02 +0200 Subject: [PATCH] Example Refactor: Request-Reply (#5014) * Refs #21188: Update header guards in other examples Signed-off-by: eduponz * Refs #21188: CLIParser and example skeleton Signed-off-by: eduponz * Refs #21188: Add IDL and type support Signed-off-by: eduponz * Refs #21188: Add Server initialization Signed-off-by: eduponz * Refs #21188: Add Server on_subscription_matched Signed-off-by: eduponz * Refs #21188: Add Client initialization Signed-off-by: eduponz * Refs #21188: Add Client on_publication_matched Signed-off-by: eduponz * Refs #21188: Uncrustify Signed-off-by: eduponz * Refs #21188: Code grouping Signed-off-by: eduponz * Refs #21188: Refactor matched status and create a utils header Signed-off-by: eduponz * Refs #21188: Client waits until a server is fully matched Signed-off-by: eduponz * Refs #21188: Server runs until it is stopped Signed-off-by: eduponz * Refs #21188: Client sends requests which are put in a queue Signed-off-by: eduponz * Refs #21188: Rename reply z to result Signed-off-by: eduponz * Refs #21188: Server sends reply and client waits for it Signed-off-by: eduponz * Refs #21188: Move type support to dedicated directory Signed-off-by: eduponz * Refs #21188: Server reply history cleanup when possible Signed-off-by: eduponz * Refs #21188: Client uses CFT to only receive replies to its requests Signed-off-by: eduponz * Refs #21188: Client waits before sending request Signed-off-by: eduponz * Refs #21188: Add logging utils Signed-off-by: eduponz * Refs #21188: Client SIGINT exit before any server has matched Signed-off-by: eduponz * Refs #21188: Add debug logging Signed-off-by: eduponz * Refs #21188: Add request ID to log messages Signed-off-by: eduponz * Refs #21188: Add small TODO for follow-up PR to fulfil Signed-off-by: eduponz * Refs #21188: Add context to log messages Signed-off-by: eduponz * Refs #21188: Add tests Signed-off-by: eduponz * Refs #21188: Add README.md Signed-off-by: eduponz * Refs #21188: Add versions Signed-off-by: eduponz * Refs #21188: Remove old example Signed-off-by: eduponz * Refs #21188: Address minor Eliana's comments Signed-off-by: eduponz * Refs #21188: Remove operation param from CLI. Client always requests + Signed-off-by: eduponz * Refs #21188: Add space between doxygen @param and direction specifier Signed-off-by: eduponz * Refs #21188: Client sends 1 request per operation and waits for all replies Signed-off-by: eduponz * Refs #21188: Fix data race on reader filter removal Signed-off-by: eduponz * Refs #21188: Unmatch participant on participant removed Signed-off-by: eduponz * Refs #21188: Add DomainParticipantFactory::get_participant_extended_qos_from_default_profile Signed-off-by: eduponz * Refs #21188: Server ignores non-client participants Signed-off-by: eduponz * Refs #21188: Client ignores non-server participants Signed-off-by: eduponz * Refs #21188: Improve client log Signed-off-by: eduponz * Refs #21188: Update tests Signed-off-by: eduponz * Refs #21188: Increase timeouts for CI testing Signed-off-by: eduponz * Refs #21188: Adjust for change in write return type Signed-off-by: eduponz * Refs #21188: Fix data race between requests_status_ init and update Signed-off-by: eduponz * Refs #21188: Take timeouts back to 3s Signed-off-by: eduponz * Refs #21188: Remove compose name from clients to cope with different docker compose versions Signed-off-by: eduponz * Refs #21188: Apply Eliana's suggestions Signed-off-by: eduponz --------- Signed-off-by: eduponz --- examples/CMakeLists.txt | 1 + examples/cpp/content_filter/CLIParser.hpp | 6 +- .../content_filter/CustomContentFilter.hpp | 14 + .../CustomContentFilterFactory.hpp | 14 + examples/cpp/dds/CMakeLists.txt | 1 - .../dds/RequestReplyExample/CMakeLists.txt | 71 --- .../dds/RequestReplyExample/Calculator.hpp | 386 ------------ .../dds/RequestReplyExample/Calculator.idl | 19 - .../RequestReplyExample/CalculatorClient.cpp | 267 -------- .../RequestReplyExample/CalculatorServer.cpp | 203 ------- .../CalculatorTypeObjectSupport.cxx | 284 --------- .../cpp/dds/RequestReplyExample/README.md | 10 - examples/cpp/request_reply/Application.cpp | 62 ++ examples/cpp/request_reply/Application.hpp | 57 ++ examples/cpp/request_reply/CLIParser.hpp | 271 +++++++++ examples/cpp/request_reply/CMakeLists.txt | 64 ++ examples/cpp/request_reply/ClientApp.cpp | 551 +++++++++++++++++ examples/cpp/request_reply/ClientApp.hpp | 167 +++++ examples/cpp/request_reply/README.md | 107 ++++ examples/cpp/request_reply/ServerApp.cpp | 568 ++++++++++++++++++ examples/cpp/request_reply/ServerApp.hpp | 164 +++++ examples/cpp/request_reply/app_utils.hpp | 309 ++++++++++ examples/cpp/request_reply/main.cpp | 91 +++ .../request_reply/request_reply_profile.xml | 54 ++ .../cpp/request_reply/types/Calculator.hpp | 482 +++++++++++++++ .../cpp/request_reply/types/Calculator.idl | 24 + .../types}/CalculatorCdrAux.hpp | 13 +- .../types}/CalculatorCdrAux.ipp | 60 +- .../types}/CalculatorPubSubTypes.cxx | 96 +-- .../types}/CalculatorPubSubTypes.hpp | 24 +- .../types/CalculatorTypeObjectSupport.cxx | 407 +++++++++++++ .../types}/CalculatorTypeObjectSupport.hpp | 12 +- .../dds/domain/DomainParticipantFactory.hpp | 9 + .../domain/DomainParticipantFactory.cpp | 9 + src/cpp/fastdds/publisher/DataWriterImpl.cpp | 2 + test/examples/request_reply.compose.yml | 41 ++ .../request_reply_isolated.compose.yml | 105 ++++ test/examples/test_request_reply.py | 88 +++ test/examples/test_request_reply_isolated.py | 100 +++ .../dds/participant/ParticipantTests.cpp | 16 + versions.md | 1 + 41 files changed, 3896 insertions(+), 1334 deletions(-) delete mode 100644 examples/cpp/dds/RequestReplyExample/CMakeLists.txt delete mode 100644 examples/cpp/dds/RequestReplyExample/Calculator.hpp delete mode 100644 examples/cpp/dds/RequestReplyExample/Calculator.idl delete mode 100644 examples/cpp/dds/RequestReplyExample/CalculatorClient.cpp delete mode 100644 examples/cpp/dds/RequestReplyExample/CalculatorServer.cpp delete mode 100644 examples/cpp/dds/RequestReplyExample/CalculatorTypeObjectSupport.cxx delete mode 100644 examples/cpp/dds/RequestReplyExample/README.md create mode 100644 examples/cpp/request_reply/Application.cpp create mode 100644 examples/cpp/request_reply/Application.hpp create mode 100644 examples/cpp/request_reply/CLIParser.hpp create mode 100644 examples/cpp/request_reply/CMakeLists.txt create mode 100644 examples/cpp/request_reply/ClientApp.cpp create mode 100644 examples/cpp/request_reply/ClientApp.hpp create mode 100644 examples/cpp/request_reply/README.md create mode 100644 examples/cpp/request_reply/ServerApp.cpp create mode 100644 examples/cpp/request_reply/ServerApp.hpp create mode 100644 examples/cpp/request_reply/app_utils.hpp create mode 100644 examples/cpp/request_reply/main.cpp create mode 100644 examples/cpp/request_reply/request_reply_profile.xml create mode 100644 examples/cpp/request_reply/types/Calculator.hpp create mode 100644 examples/cpp/request_reply/types/Calculator.idl rename examples/cpp/{dds/RequestReplyExample => request_reply/types}/CalculatorCdrAux.hpp (77%) rename examples/cpp/{dds/RequestReplyExample => request_reply/types}/CalculatorCdrAux.ipp (80%) rename examples/cpp/{dds/RequestReplyExample => request_reply/types}/CalculatorPubSubTypes.cxx (78%) rename examples/cpp/{dds/RequestReplyExample => request_reply/types}/CalculatorPubSubTypes.hpp (89%) create mode 100644 examples/cpp/request_reply/types/CalculatorTypeObjectSupport.cxx rename examples/cpp/{dds/RequestReplyExample => request_reply/types}/CalculatorTypeObjectSupport.hpp (87%) create mode 100644 test/examples/request_reply.compose.yml create mode 100644 test/examples/request_reply_isolated.compose.yml create mode 100644 test/examples/test_request_reply.py create mode 100644 test/examples/test_request_reply_isolated.py diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0e302bf89dd..e90047deb56 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(cpp/dds) add_subdirectory(cpp/delivery_mechanisms) add_subdirectory(cpp/discovery_server) add_subdirectory(cpp/hello_world) +add_subdirectory(cpp/request_reply) add_subdirectory(cpp/rtps) add_subdirectory(cpp/xtypes) diff --git a/examples/cpp/content_filter/CLIParser.hpp b/examples/cpp/content_filter/CLIParser.hpp index 291ce9a2d22..de6f13eea3c 100644 --- a/examples/cpp/content_filter/CLIParser.hpp +++ b/examples/cpp/content_filter/CLIParser.hpp @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifndef FASTDDS_EXAMPLES_CPP_CONTENT_FILTER__CLIPARSER_HPP +#define FASTDDS_EXAMPLES_CPP_CONTENT_FILTER__CLIPARSER_HPP + #include #include #include @@ -19,9 +22,6 @@ #include -#ifndef FASTDDS_EXAMPLES_CPP_CONTENT_FILTER__CLIPARSER_HPP -#define FASTDDS_EXAMPLES_CPP_CONTENT_FILTER__CLIPARSER_HPP - namespace eprosima { namespace fastdds { namespace examples { diff --git a/examples/cpp/content_filter/CustomContentFilter.hpp b/examples/cpp/content_filter/CustomContentFilter.hpp index ae63b9759f3..9d99f9b2f5d 100644 --- a/examples/cpp/content_filter/CustomContentFilter.hpp +++ b/examples/cpp/content_filter/CustomContentFilter.hpp @@ -1,3 +1,17 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef FASTDDS_EXAMPLES_CPP_CONTENT_FILTER__CUSTOMCONTENTFILTER_HPP #define FASTDDS_EXAMPLES_CPP_CONTENT_FILTER__CUSTOMCONTENTFILTER_HPP diff --git a/examples/cpp/content_filter/CustomContentFilterFactory.hpp b/examples/cpp/content_filter/CustomContentFilterFactory.hpp index aed72191c01..8d710e123c9 100644 --- a/examples/cpp/content_filter/CustomContentFilterFactory.hpp +++ b/examples/cpp/content_filter/CustomContentFilterFactory.hpp @@ -1,3 +1,17 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef FASTDDS_EXAMPLES_CPP_CONTENT_FILTER__CUSTOMCONTENTFILTERFACTORY_HPP #define FASTDDS_EXAMPLES_CPP_CONTENT_FILTER__CUSTOMCONTENTFILTERFACTORY_HPP diff --git a/examples/cpp/dds/CMakeLists.txt b/examples/cpp/dds/CMakeLists.txt index 19f83e1edea..99c38526dee 100644 --- a/examples/cpp/dds/CMakeLists.txt +++ b/examples/cpp/dds/CMakeLists.txt @@ -18,7 +18,6 @@ add_subdirectory(HelloWorldExampleDataSharing) add_subdirectory(HelloWorldExampleSharedMem) add_subdirectory(HelloWorldExampleTCP) add_subdirectory(Keys) -add_subdirectory(RequestReplyExample) add_subdirectory(StaticHelloWorldExample) add_subdirectory(WriterLoansExample) add_subdirectory(ZeroCopyExample) diff --git a/examples/cpp/dds/RequestReplyExample/CMakeLists.txt b/examples/cpp/dds/RequestReplyExample/CMakeLists.txt deleted file mode 100644 index a3efdf02707..00000000000 --- a/examples/cpp/dds/RequestReplyExample/CMakeLists.txt +++ /dev/null @@ -1,71 +0,0 @@ -# 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. - -cmake_minimum_required(VERSION 3.20) - -project(RequestReplyExample 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 Request-Reply example...") -set(DDS_REQUEST_REPLY_EXAMPLE_COMMON_SOURCES - CalculatorPubSubTypes.cxx - CalculatorTypeObjectSupport.cxx - ) -set( DDS_REQUEST_REPLY_EXAMPLE_CLIENT_SOURCES - CalculatorClient.cpp - ) -set( DDS_REQUEST_REPLY_EXAMPLE_SERVER_SOURCES - CalculatorServer.cpp - ) - -add_executable(DDSCalculatorClient - ${DDS_REQUEST_REPLY_EXAMPLE_COMMON_SOURCES} - ${DDS_REQUEST_REPLY_EXAMPLE_CLIENT_SOURCES} - ) -target_compile_definitions(DDSCalculatorClient PRIVATE - $<$>,$>:__DEBUG> - $<$:__INTERNALDEBUG> # Internal debug activated. -) -target_link_libraries(DDSCalculatorClient fastdds fastcdr) -install(TARGETS DDSCalculatorClient - RUNTIME DESTINATION examples/cpp/dds/RequestReplyExample/${BIN_INSTALL_DIR}) - -add_executable(DDSCalculatorServer - ${DDS_REQUEST_REPLY_EXAMPLE_COMMON_SOURCES} - ${DDS_REQUEST_REPLY_EXAMPLE_SERVER_SOURCES} - ) -target_compile_definitions(DDSCalculatorServer PRIVATE - $<$>,$>:__DEBUG> - $<$:__INTERNALDEBUG> # Internal debug activated. -) -target_link_libraries(DDSCalculatorServer fastdds fastcdr) -install(TARGETS DDSCalculatorServer - RUNTIME DESTINATION examples/cpp/dds/RequestReplyExample/${BIN_INSTALL_DIR}) diff --git a/examples/cpp/dds/RequestReplyExample/Calculator.hpp b/examples/cpp/dds/RequestReplyExample/Calculator.hpp deleted file mode 100644 index 96e6c550547..00000000000 --- a/examples/cpp/dds/RequestReplyExample/Calculator.hpp +++ /dev/null @@ -1,386 +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 Calculator.hpp - * This header file contains the declaration of the described types in the IDL file. - * - * This file was generated by the tool fastddsgen. - */ - -#ifndef FAST_DDS_GENERATED__CALCULATOR_HPP -#define FAST_DDS_GENERATED__CALCULATOR_HPP - -#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(CALCULATOR_SOURCE) -#define CALCULATOR_DllAPI __declspec( dllexport ) -#else -#define CALCULATOR_DllAPI __declspec( dllimport ) -#endif // CALCULATOR_SOURCE -#else -#define CALCULATOR_DllAPI -#endif // EPROSIMA_USER_DLL_EXPORT -#else -#define CALCULATOR_DllAPI -#endif // _WIN32 - -/*! - * @brief This class represents the enumeration OperationType defined by the user in the IDL file. - * @ingroup Calculator - */ -enum class OperationType : int32_t -{ - ADDITION, - SUBTRACTION, - MULTIPLICATION, - DIVISION -}; -/*! - * @brief This class represents the structure RequestType defined by the user in the IDL file. - * @ingroup Calculator - */ -class RequestType -{ -public: - - /*! - * @brief Default constructor. - */ - eProsima_user_DllExport RequestType() - { - } - - /*! - * @brief Default destructor. - */ - eProsima_user_DllExport ~RequestType() - { - } - - /*! - * @brief Copy constructor. - * @param x Reference to the object RequestType that will be copied. - */ - eProsima_user_DllExport RequestType( - const RequestType& x) - { - m_operation = x.m_operation; - - m_x = x.m_x; - - m_y = x.m_y; - - } - - /*! - * @brief Move constructor. - * @param x Reference to the object RequestType that will be copied. - */ - eProsima_user_DllExport RequestType( - RequestType&& x) noexcept - { - m_operation = x.m_operation; - m_x = x.m_x; - m_y = x.m_y; - } - - /*! - * @brief Copy assignment. - * @param x Reference to the object RequestType that will be copied. - */ - eProsima_user_DllExport RequestType& operator =( - const RequestType& x) - { - - m_operation = x.m_operation; - - m_x = x.m_x; - - m_y = x.m_y; - - return *this; - } - - /*! - * @brief Move assignment. - * @param x Reference to the object RequestType that will be copied. - */ - eProsima_user_DllExport RequestType& operator =( - RequestType&& x) noexcept - { - - m_operation = x.m_operation; - m_x = x.m_x; - m_y = x.m_y; - return *this; - } - - /*! - * @brief Comparison operator. - * @param x RequestType object to compare. - */ - eProsima_user_DllExport bool operator ==( - const RequestType& x) const - { - return (m_operation == x.m_operation && - m_x == x.m_x && - m_y == x.m_y); - } - - /*! - * @brief Comparison operator. - * @param x RequestType object to compare. - */ - eProsima_user_DllExport bool operator !=( - const RequestType& x) const - { - return !(*this == x); - } - - /*! - * @brief This function sets a value in member operation - * @param _operation New value for member operation - */ - eProsima_user_DllExport void operation( - OperationType _operation) - { - m_operation = _operation; - } - - /*! - * @brief This function returns the value of member operation - * @return Value of member operation - */ - eProsima_user_DllExport OperationType operation() const - { - return m_operation; - } - - /*! - * @brief This function returns a reference to member operation - * @return Reference to member operation - */ - eProsima_user_DllExport OperationType& operation() - { - return m_operation; - } - - - /*! - * @brief This function sets a value in member x - * @param _x New value for member x - */ - eProsima_user_DllExport void x( - int32_t _x) - { - m_x = _x; - } - - /*! - * @brief This function returns the value of member x - * @return Value of member x - */ - eProsima_user_DllExport int32_t x() const - { - return m_x; - } - - /*! - * @brief This function returns a reference to member x - * @return Reference to member x - */ - eProsima_user_DllExport int32_t& x() - { - return m_x; - } - - - /*! - * @brief This function sets a value in member y - * @param _y New value for member y - */ - eProsima_user_DllExport void y( - int32_t _y) - { - m_y = _y; - } - - /*! - * @brief This function returns the value of member y - * @return Value of member y - */ - eProsima_user_DllExport int32_t y() const - { - return m_y; - } - - /*! - * @brief This function returns a reference to member y - * @return Reference to member y - */ - eProsima_user_DllExport int32_t& y() - { - return m_y; - } - - - -private: - - OperationType m_operation{OperationType::ADDITION}; - int32_t m_x{0}; - int32_t m_y{0}; - -}; -/*! - * @brief This class represents the structure ReplyType defined by the user in the IDL file. - * @ingroup Calculator - */ -class ReplyType -{ -public: - - /*! - * @brief Default constructor. - */ - eProsima_user_DllExport ReplyType() - { - } - - /*! - * @brief Default destructor. - */ - eProsima_user_DllExport ~ReplyType() - { - } - - /*! - * @brief Copy constructor. - * @param x Reference to the object ReplyType that will be copied. - */ - eProsima_user_DllExport ReplyType( - const ReplyType& x) - { - m_z = x.m_z; - - } - - /*! - * @brief Move constructor. - * @param x Reference to the object ReplyType that will be copied. - */ - eProsima_user_DllExport ReplyType( - ReplyType&& x) noexcept - { - m_z = x.m_z; - } - - /*! - * @brief Copy assignment. - * @param x Reference to the object ReplyType that will be copied. - */ - eProsima_user_DllExport ReplyType& operator =( - const ReplyType& x) - { - - m_z = x.m_z; - - return *this; - } - - /*! - * @brief Move assignment. - * @param x Reference to the object ReplyType that will be copied. - */ - eProsima_user_DllExport ReplyType& operator =( - ReplyType&& x) noexcept - { - - m_z = x.m_z; - return *this; - } - - /*! - * @brief Comparison operator. - * @param x ReplyType object to compare. - */ - eProsima_user_DllExport bool operator ==( - const ReplyType& x) const - { - return (m_z == x.m_z); - } - - /*! - * @brief Comparison operator. - * @param x ReplyType object to compare. - */ - eProsima_user_DllExport bool operator !=( - const ReplyType& x) const - { - return !(*this == x); - } - - /*! - * @brief This function sets a value in member z - * @param _z New value for member z - */ - eProsima_user_DllExport void z( - int64_t _z) - { - m_z = _z; - } - - /*! - * @brief This function returns the value of member z - * @return Value of member z - */ - eProsima_user_DllExport int64_t z() const - { - return m_z; - } - - /*! - * @brief This function returns a reference to member z - * @return Reference to member z - */ - eProsima_user_DllExport int64_t& z() - { - return m_z; - } - - - -private: - - int64_t m_z{0}; - -}; - -#endif // _FAST_DDS_GENERATED_CALCULATOR_HPP_ - - diff --git a/examples/cpp/dds/RequestReplyExample/Calculator.idl b/examples/cpp/dds/RequestReplyExample/Calculator.idl deleted file mode 100644 index a998fb63bbc..00000000000 --- a/examples/cpp/dds/RequestReplyExample/Calculator.idl +++ /dev/null @@ -1,19 +0,0 @@ -enum OperationType -{ - ADDITION, - SUBTRACTION, - MULTIPLICATION, - DIVISION -}; - -struct RequestType -{ - OperationType operation; - long x; - long y; -}; - -struct ReplyType -{ - long long z; -}; diff --git a/examples/cpp/dds/RequestReplyExample/CalculatorClient.cpp b/examples/cpp/dds/RequestReplyExample/CalculatorClient.cpp deleted file mode 100644 index 84d4334d519..00000000000 --- a/examples/cpp/dds/RequestReplyExample/CalculatorClient.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// 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. - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "CalculatorPubSubTypes.hpp" - -class CalculatorClient -{ - class Listener : public eprosima::fastdds::dds::DataReaderListener - { - public: - - void on_data_available( - eprosima::fastdds::dds::DataReader* reader) override - { - ReplyType reply; - eprosima::fastdds::dds::SampleInfo sample_info; - - reader->take_next_sample(&reply, &sample_info); - - if (eprosima::fastdds::dds::InstanceStateKind::ALIVE_INSTANCE_STATE == sample_info.instance_state) - { - if (sample_info.related_sample_identity == write_params.sample_identity()) - { - { - std::unique_lock lock(reception_mutex); - received_reply = true; - z = reply.z(); - } - reception_cv.notify_one(); - } - } - } - - eprosima::fastdds::rtps::WriteParams write_params; - - // Structures for waiting reply - std::mutex reception_mutex; - std::condition_variable reception_cv; - bool received_reply = false; - int64_t z = 0; - - } - listener_; - -public: - - bool init() - { - participant_ = eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->create_participant(0, - eprosima::fastdds::dds::PARTICIPANT_QOS_DEFAULT); - - if (nullptr == participant_) - { - return false; - } - - request_type_ = eprosima::fastdds::dds::TypeSupport(new RequestTypePubSubType()); - reply_type_ = eprosima::fastdds::dds::TypeSupport(new ReplyTypePubSubType()); - - participant_->register_type(request_type_); - participant_->register_type(reply_type_); - - publisher_ = participant_->create_publisher(eprosima::fastdds::dds::PUBLISHER_QOS_DEFAULT); - - if (nullptr == publisher_) - { - return false; - } - - subscriber_ = participant_->create_subscriber(eprosima::fastdds::dds::SUBSCRIBER_QOS_DEFAULT); - - if (nullptr == subscriber_) - { - return false; - } - - request_topic_ = participant_->create_topic("CalculatorRequest", - request_type_.get_type_name(), eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); - - if (nullptr == request_topic_) - { - return false; - } - - reply_topic_ = participant_->create_topic("CalculatorReply", - reply_type_.get_type_name(), eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); - - if (nullptr == reply_topic_) - { - return false; - } - - request_writer_ = publisher_->create_datawriter(request_topic_, eprosima::fastdds::dds::DATAWRITER_QOS_DEFAULT); - - if (nullptr == request_writer_) - { - return false; - } - - eprosima::fastdds::dds::DataReaderQos reader_qos; - reader_qos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; - reader_qos.history().kind = eprosima::fastdds::dds::KEEP_ALL_HISTORY_QOS; - reply_reader_ = subscriber_->create_datareader(reply_topic_, reader_qos, &listener_); - - if (nullptr == reply_reader_) - { - return false; - } - - return true; - } - - void deinit() - { - if (nullptr != participant_) - { - participant_->delete_contained_entities(); - eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->delete_participant(participant_); - } - } - - int64_t request( - OperationType operation, - int32_t x, - int32_t y) - { - int64_t z = 0; - RequestType request; - request.operation(operation); - request.x(x); - request.y(y); - - if (eprosima::fastdds::dds::RETCODE_OK == - request_writer_->write(static_cast(&request), listener_.write_params)) - { - std::unique_lock lock(listener_.reception_mutex); - listener_.reception_cv.wait(lock, [&]() - { - return listener_.received_reply; - }); - z = listener_.z; - } - else - { - std::cerr << "Error writing the request" << std::endl; - } - - return z; - } - -private: - - eprosima::fastdds::dds::DomainParticipant* participant_ = nullptr; - - eprosima::fastdds::dds::Publisher* publisher_ = nullptr; - - eprosima::fastdds::dds::Subscriber* subscriber_ = nullptr; - - eprosima::fastdds::dds::Topic* request_topic_ = nullptr; - - eprosima::fastdds::dds::Topic* reply_topic_ = nullptr; - - eprosima::fastdds::dds::DataWriter* request_writer_ = nullptr; - - eprosima::fastdds::dds::DataReader* reply_reader_ = nullptr; - - eprosima::fastdds::dds::TypeSupport request_type_; - - eprosima::fastdds::dds::TypeSupport reply_type_; -}; - -void print_help() -{ - std::cout << "Usage: CalculatorClient <+|-|x|/> " << std::endl; -} - -int main( - int argc, - char** argv) -{ - argc -= (argc > 0); - argv += (argc > 0); // skip program name argv[0] if present - - if (3 != argc) - { - print_help(); - return -1; - } - - int32_t x; - int32_t y; - OperationType operation; - char* endptr = 0; - - x = strtol(argv[0], &endptr, 10); - if (endptr == argv[0] || *endptr != 0) - { - std::cerr << "Error reading numeric argument x." << std::endl; - print_help(); - return -1; - } - - if (0 == strcmp(argv[1], "+")) - { - operation = OperationType::ADDITION; - } - else if (0 == strcmp(argv[1], "-")) - { - operation = OperationType::SUBTRACTION; - } - else if (0 == strcmp(argv[1], "x")) - { - operation = OperationType::MULTIPLICATION; - } - else if (0 == strcmp(argv[1], "/")) - { - operation = OperationType::DIVISION; - } - else - { - std::cerr << "Error reading operation argument. Valid values: <+|-|x|/>" << std::endl; - print_help(); - return -1; - } - - y = strtol(argv[2], &endptr, 10); - if (endptr == argv[2] || *endptr != 0) - { - std::cerr << "Error reading numeric argument y." << std::endl; - print_help(); - return -1; - } - - CalculatorClient client; - client.init(); - int64_t z = client.request(operation, x, y); - - std::cout << "Result: " << argv[0] << " " << argv[1] << " " << argv[2] << " = " << z << std::endl; - - client.deinit(); - - return 0; -} diff --git a/examples/cpp/dds/RequestReplyExample/CalculatorServer.cpp b/examples/cpp/dds/RequestReplyExample/CalculatorServer.cpp deleted file mode 100644 index a7ee65d9e5e..00000000000 --- a/examples/cpp/dds/RequestReplyExample/CalculatorServer.cpp +++ /dev/null @@ -1,203 +0,0 @@ -// 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. - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "CalculatorPubSubTypes.hpp" - -class CalculatorServer -{ - class Listener : public eprosima::fastdds::dds::DataReaderListener - { - public: - - Listener( - eprosima::fastdds::dds::DataWriter* writer) - : writer_(writer) - { - } - - void on_data_available( - eprosima::fastdds::dds::DataReader* reader) override - { - RequestType request; - eprosima::fastdds::dds::SampleInfo sample_info; - - reader->take_next_sample(&request, &sample_info); - - if (eprosima::fastdds::dds::InstanceStateKind::ALIVE_INSTANCE_STATE == sample_info.instance_state) - { - ReplyType reply; - - if (OperationType::ADDITION == request.operation()) - { - reply.z(request.x() + request.y()); - } - else if (OperationType::SUBTRACTION == request.operation()) - { - reply.z(request.x() - request.y()); - } - else if (OperationType::MULTIPLICATION == request.operation()) - { - reply.z(request.x() * request.y()); - } - else if (OperationType::DIVISION == request.operation()) - { - if (0 != request.y()) - { - reply.z(request.x() / request.y()); - } - } - eprosima::fastdds::rtps::WriteParams write_params; - write_params.related_sample_identity().writer_guid(sample_info.sample_identity.writer_guid()); - write_params.related_sample_identity().sequence_number(sample_info.sample_identity.sequence_number()); - writer_->write(reinterpret_cast(&reply), write_params); - } - } - - private: - - eprosima::fastdds::dds::DataWriter* writer_ = nullptr; - - }; - -public: - - bool init() - { - participant_ = eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->create_participant(0, - eprosima::fastdds::dds::PARTICIPANT_QOS_DEFAULT); - - if (nullptr == participant_) - { - return false; - } - - request_type_ = eprosima::fastdds::dds::TypeSupport(new RequestTypePubSubType()); - reply_type_ = eprosima::fastdds::dds::TypeSupport(new ReplyTypePubSubType()); - - participant_->register_type(request_type_); - participant_->register_type(reply_type_); - - publisher_ = participant_->create_publisher(eprosima::fastdds::dds::PUBLISHER_QOS_DEFAULT); - - if (nullptr == publisher_) - { - return false; - } - - subscriber_ = participant_->create_subscriber(eprosima::fastdds::dds::SUBSCRIBER_QOS_DEFAULT); - - if (nullptr == subscriber_) - { - return false; - } - - request_topic_ = participant_->create_topic("CalculatorRequest", - request_type_.get_type_name(), eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); - - if (nullptr == request_topic_) - { - return false; - } - - reply_topic_ = participant_->create_topic("CalculatorReply", - reply_type_.get_type_name(), eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); - - if (nullptr == reply_topic_) - { - return false; - } - - eprosima::fastdds::dds::DataWriterQos writer_qos; - writer_qos.history().kind = eprosima::fastdds::dds::KEEP_ALL_HISTORY_QOS; - reply_writer_ = publisher_->create_datawriter(reply_topic_, writer_qos); - - if (nullptr == reply_writer_) - { - return false; - } - - listener_ = { reply_writer_}; - - eprosima::fastdds::dds::DataReaderQos reader_qos; - reader_qos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; - reader_qos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; - reader_qos.history().kind = eprosima::fastdds::dds::KEEP_ALL_HISTORY_QOS; - request_reader_ = subscriber_->create_datareader(request_topic_, reader_qos, &listener_); - - if (nullptr == request_reader_) - { - return false; - } - - return true; - } - - void deinit() - { - if (nullptr != participant_) - { - participant_->delete_contained_entities(); - eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->delete_participant(participant_); - } - } - -private: - - eprosima::fastdds::dds::DomainParticipant* participant_ = nullptr; - - eprosima::fastdds::dds::Publisher* publisher_ = nullptr; - - eprosima::fastdds::dds::Subscriber* subscriber_ = nullptr; - - eprosima::fastdds::dds::Topic* request_topic_ = nullptr; - - eprosima::fastdds::dds::Topic* reply_topic_ = nullptr; - - eprosima::fastdds::dds::DataWriter* reply_writer_ = nullptr; - - eprosima::fastdds::dds::DataReader* request_reader_ = nullptr; - - eprosima::fastdds::dds::TypeSupport request_type_; - - eprosima::fastdds::dds::TypeSupport reply_type_; - - Listener listener_ = {nullptr}; -}; - -int main() -{ - CalculatorServer server; - server.init(); - - std::cout << "Press a key + ENTER to close the server" << std::endl; - char c; - std::cin >> c; - - server.deinit(); - - return 0; -} diff --git a/examples/cpp/dds/RequestReplyExample/CalculatorTypeObjectSupport.cxx b/examples/cpp/dds/RequestReplyExample/CalculatorTypeObjectSupport.cxx deleted file mode 100644 index 574938928b7..00000000000 --- a/examples/cpp/dds/RequestReplyExample/CalculatorTypeObjectSupport.cxx +++ /dev/null @@ -1,284 +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 CalculatorTypeObjectSupport.cxx - * Source file containing the implementation to register the TypeObject representation of the described types in the IDL file - * - * This file was generated by the tool fastddsgen. - */ - -#include "CalculatorTypeObjectSupport.hpp" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Calculator.hpp" - - -using namespace eprosima::fastdds::dds::xtypes; - -void register_OperationType_type_identifier( - TypeIdentifierPair& type_ids_OperationType) -{ - ReturnCode_t return_code_OperationType {eprosima::fastdds::dds::RETCODE_OK}; - return_code_OperationType = - eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( - "OperationType", type_ids_OperationType); - if (eprosima::fastdds::dds::RETCODE_OK != return_code_OperationType) - { - EnumTypeFlag enum_flags_OperationType = 0; - BitBound bit_bound_OperationType = 32; - CommonEnumeratedHeader common_OperationType = TypeObjectUtils::build_common_enumerated_header(bit_bound_OperationType); - QualifiedTypeName type_name_OperationType = "OperationType"; - eprosima::fastcdr::optional type_ann_builtin_OperationType; - eprosima::fastcdr::optional ann_custom_OperationType; - CompleteTypeDetail detail_OperationType = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_OperationType, ann_custom_OperationType, type_name_OperationType.to_string()); - CompleteEnumeratedHeader header_OperationType = TypeObjectUtils::build_complete_enumerated_header(common_OperationType, detail_OperationType); - CompleteEnumeratedLiteralSeq literal_seq_OperationType; - { - EnumeratedLiteralFlag flags_ADDITION = TypeObjectUtils::build_enumerated_literal_flag(false); - CommonEnumeratedLiteral common_ADDITION = TypeObjectUtils::build_common_enumerated_literal(0, flags_ADDITION); - eprosima::fastcdr::optional member_ann_builtin_ADDITION; - ann_custom_OperationType.reset(); - MemberName name_ADDITION = "ADDITION"; - CompleteMemberDetail detail_ADDITION = TypeObjectUtils::build_complete_member_detail(name_ADDITION, member_ann_builtin_ADDITION, ann_custom_OperationType); - CompleteEnumeratedLiteral literal_ADDITION = TypeObjectUtils::build_complete_enumerated_literal(common_ADDITION, detail_ADDITION); - TypeObjectUtils::add_complete_enumerated_literal(literal_seq_OperationType, literal_ADDITION); - } - { - EnumeratedLiteralFlag flags_SUBTRACTION = TypeObjectUtils::build_enumerated_literal_flag(false); - CommonEnumeratedLiteral common_SUBTRACTION = TypeObjectUtils::build_common_enumerated_literal(1, flags_SUBTRACTION); - eprosima::fastcdr::optional member_ann_builtin_SUBTRACTION; - ann_custom_OperationType.reset(); - MemberName name_SUBTRACTION = "SUBTRACTION"; - CompleteMemberDetail detail_SUBTRACTION = TypeObjectUtils::build_complete_member_detail(name_SUBTRACTION, member_ann_builtin_SUBTRACTION, ann_custom_OperationType); - CompleteEnumeratedLiteral literal_SUBTRACTION = TypeObjectUtils::build_complete_enumerated_literal(common_SUBTRACTION, detail_SUBTRACTION); - TypeObjectUtils::add_complete_enumerated_literal(literal_seq_OperationType, literal_SUBTRACTION); - } - { - EnumeratedLiteralFlag flags_MULTIPLICATION = TypeObjectUtils::build_enumerated_literal_flag(false); - CommonEnumeratedLiteral common_MULTIPLICATION = TypeObjectUtils::build_common_enumerated_literal(2, flags_MULTIPLICATION); - eprosima::fastcdr::optional member_ann_builtin_MULTIPLICATION; - ann_custom_OperationType.reset(); - MemberName name_MULTIPLICATION = "MULTIPLICATION"; - CompleteMemberDetail detail_MULTIPLICATION = TypeObjectUtils::build_complete_member_detail(name_MULTIPLICATION, member_ann_builtin_MULTIPLICATION, ann_custom_OperationType); - CompleteEnumeratedLiteral literal_MULTIPLICATION = TypeObjectUtils::build_complete_enumerated_literal(common_MULTIPLICATION, detail_MULTIPLICATION); - TypeObjectUtils::add_complete_enumerated_literal(literal_seq_OperationType, literal_MULTIPLICATION); - } - { - EnumeratedLiteralFlag flags_DIVISION = TypeObjectUtils::build_enumerated_literal_flag(false); - CommonEnumeratedLiteral common_DIVISION = TypeObjectUtils::build_common_enumerated_literal(3, flags_DIVISION); - eprosima::fastcdr::optional member_ann_builtin_DIVISION; - ann_custom_OperationType.reset(); - MemberName name_DIVISION = "DIVISION"; - CompleteMemberDetail detail_DIVISION = TypeObjectUtils::build_complete_member_detail(name_DIVISION, member_ann_builtin_DIVISION, ann_custom_OperationType); - CompleteEnumeratedLiteral literal_DIVISION = TypeObjectUtils::build_complete_enumerated_literal(common_DIVISION, detail_DIVISION); - TypeObjectUtils::add_complete_enumerated_literal(literal_seq_OperationType, literal_DIVISION); - } - CompleteEnumeratedType enumerated_type_OperationType = TypeObjectUtils::build_complete_enumerated_type(enum_flags_OperationType, header_OperationType, - literal_seq_OperationType); - if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == - TypeObjectUtils::build_and_register_enumerated_type_object(enumerated_type_OperationType, type_name_OperationType.to_string(), type_ids_OperationType)) - { - EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, - "OperationType already registered in TypeObjectRegistry for a different type."); - } - } -}// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method -void register_RequestType_type_identifier( - TypeIdentifierPair& type_ids_RequestType) -{ - - ReturnCode_t return_code_RequestType {eprosima::fastdds::dds::RETCODE_OK}; - return_code_RequestType = - eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( - "RequestType", type_ids_RequestType); - if (eprosima::fastdds::dds::RETCODE_OK != return_code_RequestType) - { - StructTypeFlag struct_flags_RequestType = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, - false, false); - QualifiedTypeName type_name_RequestType = "RequestType"; - eprosima::fastcdr::optional type_ann_builtin_RequestType; - eprosima::fastcdr::optional ann_custom_RequestType; - CompleteTypeDetail detail_RequestType = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_RequestType, ann_custom_RequestType, type_name_RequestType.to_string()); - CompleteStructHeader header_RequestType; - header_RequestType = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_RequestType); - CompleteStructMemberSeq member_seq_RequestType; - { - TypeIdentifierPair type_ids_operation; - ReturnCode_t return_code_operation {eprosima::fastdds::dds::RETCODE_OK}; - return_code_operation = - eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( - "OperationType", type_ids_operation); - - if (eprosima::fastdds::dds::RETCODE_OK != return_code_operation) - { - ::register_OperationType_type_identifier(type_ids_operation); - } - StructMemberFlag member_flags_operation = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, - false, false, false, false); - MemberId member_id_operation = 0x00000000; - bool common_operation_ec {false}; - CommonStructMember common_operation {TypeObjectUtils::build_common_struct_member(member_id_operation, member_flags_operation, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_operation, common_operation_ec))}; - if (!common_operation_ec) - { - EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure operation member TypeIdentifier inconsistent."); - return; - } - MemberName name_operation = "operation"; - eprosima::fastcdr::optional member_ann_builtin_operation; - ann_custom_RequestType.reset(); - CompleteMemberDetail detail_operation = TypeObjectUtils::build_complete_member_detail(name_operation, member_ann_builtin_operation, ann_custom_RequestType); - CompleteStructMember member_operation = TypeObjectUtils::build_complete_struct_member(common_operation, detail_operation); - TypeObjectUtils::add_complete_struct_member(member_seq_RequestType, member_operation); - } - { - TypeIdentifierPair type_ids_x; - ReturnCode_t return_code_x {eprosima::fastdds::dds::RETCODE_OK}; - return_code_x = - eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( - "_int32_t", type_ids_x); - - if (eprosima::fastdds::dds::RETCODE_OK != return_code_x) - { - EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, - "x Structure member TypeIdentifier unknown to TypeObjectRegistry."); - return; - } - StructMemberFlag member_flags_x = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, - false, false, false, false); - MemberId member_id_x = 0x00000001; - bool common_x_ec {false}; - CommonStructMember common_x {TypeObjectUtils::build_common_struct_member(member_id_x, member_flags_x, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_x, common_x_ec))}; - if (!common_x_ec) - { - EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure x member TypeIdentifier inconsistent."); - return; - } - MemberName name_x = "x"; - eprosima::fastcdr::optional member_ann_builtin_x; - ann_custom_RequestType.reset(); - CompleteMemberDetail detail_x = TypeObjectUtils::build_complete_member_detail(name_x, member_ann_builtin_x, ann_custom_RequestType); - CompleteStructMember member_x = TypeObjectUtils::build_complete_struct_member(common_x, detail_x); - TypeObjectUtils::add_complete_struct_member(member_seq_RequestType, member_x); - } - { - TypeIdentifierPair type_ids_y; - ReturnCode_t return_code_y {eprosima::fastdds::dds::RETCODE_OK}; - return_code_y = - eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( - "_int32_t", type_ids_y); - - if (eprosima::fastdds::dds::RETCODE_OK != return_code_y) - { - EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, - "y Structure member TypeIdentifier unknown to TypeObjectRegistry."); - return; - } - StructMemberFlag member_flags_y = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, - false, false, false, false); - MemberId member_id_y = 0x00000002; - bool common_y_ec {false}; - CommonStructMember common_y {TypeObjectUtils::build_common_struct_member(member_id_y, member_flags_y, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_y, common_y_ec))}; - if (!common_y_ec) - { - EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure y member TypeIdentifier inconsistent."); - return; - } - MemberName name_y = "y"; - eprosima::fastcdr::optional member_ann_builtin_y; - ann_custom_RequestType.reset(); - CompleteMemberDetail detail_y = TypeObjectUtils::build_complete_member_detail(name_y, member_ann_builtin_y, ann_custom_RequestType); - CompleteStructMember member_y = TypeObjectUtils::build_complete_struct_member(common_y, detail_y); - TypeObjectUtils::add_complete_struct_member(member_seq_RequestType, member_y); - } - CompleteStructType struct_type_RequestType = TypeObjectUtils::build_complete_struct_type(struct_flags_RequestType, header_RequestType, member_seq_RequestType); - if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == - TypeObjectUtils::build_and_register_struct_type_object(struct_type_RequestType, type_name_RequestType.to_string(), type_ids_RequestType)) - { - EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, - "RequestType already registered in TypeObjectRegistry for a different type."); - } - } -} -// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method -void register_ReplyType_type_identifier( - TypeIdentifierPair& type_ids_ReplyType) -{ - - ReturnCode_t return_code_ReplyType {eprosima::fastdds::dds::RETCODE_OK}; - return_code_ReplyType = - eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( - "ReplyType", type_ids_ReplyType); - if (eprosima::fastdds::dds::RETCODE_OK != return_code_ReplyType) - { - StructTypeFlag struct_flags_ReplyType = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, - false, false); - QualifiedTypeName type_name_ReplyType = "ReplyType"; - eprosima::fastcdr::optional type_ann_builtin_ReplyType; - eprosima::fastcdr::optional ann_custom_ReplyType; - CompleteTypeDetail detail_ReplyType = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_ReplyType, ann_custom_ReplyType, type_name_ReplyType.to_string()); - CompleteStructHeader header_ReplyType; - header_ReplyType = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_ReplyType); - CompleteStructMemberSeq member_seq_ReplyType; - { - TypeIdentifierPair type_ids_z; - ReturnCode_t return_code_z {eprosima::fastdds::dds::RETCODE_OK}; - return_code_z = - eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( - "_int64_t", type_ids_z); - - if (eprosima::fastdds::dds::RETCODE_OK != return_code_z) - { - EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, - "z Structure member TypeIdentifier unknown to TypeObjectRegistry."); - return; - } - StructMemberFlag member_flags_z = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, - false, false, false, false); - MemberId member_id_z = 0x00000000; - bool common_z_ec {false}; - CommonStructMember common_z {TypeObjectUtils::build_common_struct_member(member_id_z, member_flags_z, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_z, common_z_ec))}; - if (!common_z_ec) - { - EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure z member TypeIdentifier inconsistent."); - return; - } - MemberName name_z = "z"; - eprosima::fastcdr::optional member_ann_builtin_z; - ann_custom_ReplyType.reset(); - CompleteMemberDetail detail_z = TypeObjectUtils::build_complete_member_detail(name_z, member_ann_builtin_z, ann_custom_ReplyType); - CompleteStructMember member_z = TypeObjectUtils::build_complete_struct_member(common_z, detail_z); - TypeObjectUtils::add_complete_struct_member(member_seq_ReplyType, member_z); - } - CompleteStructType struct_type_ReplyType = TypeObjectUtils::build_complete_struct_type(struct_flags_ReplyType, header_ReplyType, member_seq_ReplyType); - if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == - TypeObjectUtils::build_and_register_struct_type_object(struct_type_ReplyType, type_name_ReplyType.to_string(), type_ids_ReplyType)) - { - EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, - "ReplyType already registered in TypeObjectRegistry for a different type."); - } - } -} - diff --git a/examples/cpp/dds/RequestReplyExample/README.md b/examples/cpp/dds/RequestReplyExample/README.md deleted file mode 100644 index 15e1d1152df..00000000000 --- a/examples/cpp/dds/RequestReplyExample/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Request-Reply example - -This example illustrates how to achieve a Client/Server communication between two applications using the *Fast DDS* -request-reply mechanism. -More information in [Fast DDS documentation](https://fast-dds.docs.eprosima.com/en/latest/fastdds/use_cases/request_reply/request_reply.html). - -To launch this example open two different consoles: - -1. In the first one launch: `./DDSCalculatorServer` (or `DDSCalculatorServer.exe` on Windows). -1. In the second one: `./DDSCalculatorClient 3 + 3` (or `DDSCalculatorClient.exe 3 + 3` on windows). diff --git a/examples/cpp/request_reply/Application.cpp b/examples/cpp/request_reply/Application.cpp new file mode 100644 index 00000000000..2001b763096 --- /dev/null +++ b/examples/cpp/request_reply/Application.cpp @@ -0,0 +1,62 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file Application.cpp + * + */ + +#include "Application.hpp" + +#include "CLIParser.hpp" +#include "ServerApp.hpp" +#include "ClientApp.hpp" + +using namespace eprosima::fastdds::dds; + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace request_reply { + +//! Factory method to create a server or client +std::shared_ptr Application::make_app( + const CLIParser::config& config, + const std::string& service_name) +{ + std::shared_ptr entity; + switch (config.entity) + { + case CLIParser::EntityKind::SERVER: + { + entity = std::make_shared(service_name); + break; + } + case CLIParser::EntityKind::CLIENT: + { + entity = std::make_shared(config, service_name); + break; + } + case CLIParser::EntityKind::UNDEFINED: + default: + throw std::runtime_error("Entity initialization failed"); + break; + } + return entity; +} + +} // namespace request_reply +} // namespace examples +} // namespace fastdds +} // namespace eprosima diff --git a/examples/cpp/request_reply/Application.hpp b/examples/cpp/request_reply/Application.hpp new file mode 100644 index 00000000000..41e533717d0 --- /dev/null +++ b/examples/cpp/request_reply/Application.hpp @@ -0,0 +1,57 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file Application.hpp + * + */ + +#ifndef FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__APPLICATION_HPP +#define FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__APPLICATION_HPP + +#include +#include + +#include "CLIParser.hpp" + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace request_reply { + +class Application +{ +public: + + //! Virtual destructor + virtual ~Application() = default; + + //! Run application + virtual void run() = 0; + + //! Trigger the end of execution + virtual void stop() = 0; + + //! Factory method to create applications based on configuration + static std::shared_ptr make_app( + const CLIParser::config& config, + const std::string& service_name); +}; + +} // namespace request_reply +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif /* FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__APPLICATION_HPP */ diff --git a/examples/cpp/request_reply/CLIParser.hpp b/examples/cpp/request_reply/CLIParser.hpp new file mode 100644 index 00000000000..3a4e1940224 --- /dev/null +++ b/examples/cpp/request_reply/CLIParser.hpp @@ -0,0 +1,271 @@ +// 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 +#include +#include +#include +#include + +#include + +#ifndef FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__CLIPARSER_HPP +#define FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__CLIPARSER_HPP + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace request_reply { + +using dds::Log; + +class CLIParser +{ +public: + + CLIParser() = delete; + + //! Entity kind enumeration + enum class EntityKind : std::uint8_t + { + SERVER, + CLIENT, + UNDEFINED + }; + + //! Configuration structure for the application + struct config + { + CLIParser::EntityKind entity = CLIParser::EntityKind::UNDEFINED; + std::int16_t x = 0; + std::int16_t y = 0; + }; + + /** + * @brief Print usage help message and exit with the given return code + * + * @param [in] 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( + const std::uint8_t return_code) + { + std::cout << "Service to perform several operations (addition, " << std::endl; + std::cout << "subtraction, multiplication, and division) on two 16-bit" << std::endl; + std::cout << "integers" << std::endl; + std::cout << "" << std::endl; + std::cout << "Usage: request_reply [options] [arguments]" << std::endl; + std::cout << "" << std::endl; + std::cout << "Example:" << std::endl; + std::cout << " - request_reply server" << std::endl; + std::cout << " - request_reply client 4 5" << std::endl; + std::cout << "" << std::endl; + std::cout << "Entities:" << std::endl; + std::cout << " server Run a server entity" << std::endl; + std::cout << " client Run a client 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 << "" << std::endl; + std::cout << "Client arguments:" << std::endl; + std::cout << " [NUMBER] [NUMBER] The numbers of which the" << std::endl; + std::cout << " operations are performed" << std::endl; + std::exit(return_code); + } + + /** + * @brief Parse the command line options and return the config object + * + * @param [in] argc number of arguments + * @param [in] argv array of arguments + * + * @return config object with the parsed options + * + * @warning This method finishes the execution of the program if the input arguments are invalid + */ + static config parse_cli_options( + const int argc, + const char* const argv[]) + { + config config; + + if (argc < 2) + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "missing entity argument"); + print_help(EXIT_FAILURE); + } + + std::string first_argument = argv[1]; + + if (first_argument == "server" ) + { + config.entity = CLIParser::EntityKind::SERVER; + } + else if (first_argument == "client") + { + config.entity = CLIParser::EntityKind::CLIENT; + } + 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) + { + if (std::string(argv[i]) == "-h" || std::string(argv[i]) == "--help") + { + print_help(EXIT_SUCCESS); + } + } + + if (CLIParser::EntityKind::CLIENT == config.entity) + { + if (argc != 4) + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "Incorrect number of arguments for client entity"); + print_help(EXIT_FAILURE); + } + + consume_client_arguments(argv, config); + } + + return config; + } + + /** + * @brief Parse the signal number into the signal name + * + * @param [in] 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 [in] entity entity kind + * + * @return std::string entity kind + */ + static std::string parse_entity_kind( + const EntityKind& entity) + { + switch (entity) + { + case EntityKind::SERVER: + return "Server"; + case EntityKind::CLIENT: + return "Client"; + case EntityKind::UNDEFINED: + default: + return "Undefined entity"; + } + } + +private: + + /** + * @brief Consume the client arguments and store them in the config object + * + * @pre argc == 4 + * + * @param [in] argv array of arguments + * @param [in,out] config config object to store the arguments + * + * @warning This method finishes the execution of the program if the input arguments are invalid + */ + static void consume_client_arguments( + const char* const argv[], + config& config) + { + config.x = consume_int16_argument(argv[2]); + config.y = consume_int16_argument(argv[3]); + } + + /** + * @brief Consume an int16 argument and return it + * + * @param [in] arg string argument to consume + * + * @return std::int16_t int16 argument + * + * @warning This method finishes the execution of the program if the input arguments are invalid + */ + static std::int16_t consume_int16_argument( + const std::string& arg) + { + std::int16_t value = 0; + + try + { + int input = std::stoi(arg); + + if (input < std::numeric_limits::min() || + input > std::numeric_limits::max()) + { + throw std::out_of_range("int16 argument out of range"); + } + + value = static_cast(input); + } + catch (const std::invalid_argument& e) + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "invalid int16 argument for " + arg + ": " + e.what()); + print_help(EXIT_FAILURE); + } + catch (const std::out_of_range& e) + { + EPROSIMA_LOG_ERROR(CLI_PARSER, "int16 argument out of range for " + arg + ": " + e.what()); + print_help(EXIT_FAILURE); + } + + return value; + } + +}; + +} // namespace request_reply +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif // FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__CLIPARSER_HPP diff --git a/examples/cpp/request_reply/CMakeLists.txt b/examples/cpp/request_reply/CMakeLists.txt new file mode 100644 index 00000000000..868bea9713a --- /dev/null +++ b/examples/cpp/request_reply/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_request_reply_example VERSION 1 LANGUAGES CXX) + +# Find requirements +if(NOT fastcdr_FOUND) + find_package(fastcdr 2 REQUIRED) +endif() + +if(NOT fastdds_FOUND) + find_package(fastdds 3 REQUIRED) +endif() + +#Check C++11 +include(CheckCXXCompilerFlag) +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + check_cxx_compiler_flag(-std=c++11 SUPPORTS_CXX11) + if(NOT SUPPORTS_CXX11) + message(FATAL_ERROR "Compiler doesn't support C++11") + endif() +endif() + +message(STATUS "Configuring ${PROJECT_NAME}...") +file(GLOB_RECURSE REQUEST_REPLY_SOURCES_CXX "*.cxx") +file(GLOB_RECURSE REQUEST_REPLY_SOURCES_CPP "*.cpp") + +add_executable(request_reply ${REQUEST_REPLY_SOURCES_CXX} ${REQUEST_REPLY_SOURCES_CPP}) +target_compile_definitions(request_reply PRIVATE + $<$>,$>:__DEBUG> + $<$:__INTERNALDEBUG> # Internal debug activated. + $<$:SHM_TRANSPORT_BUILTIN> # Enable SHM as built-in transport +) +target_link_libraries(request_reply fastdds fastcdr) +install(TARGETS request_reply + RUNTIME DESTINATION ${DATA_INSTALL_DIR}/fastdds/examples/cpp/request_reply/${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/request_reply/${BIN_INSTALL_DIR}) +endforeach() diff --git a/examples/cpp/request_reply/ClientApp.cpp b/examples/cpp/request_reply/ClientApp.cpp new file mode 100644 index 00000000000..aa17ec00021 --- /dev/null +++ b/examples/cpp/request_reply/ClientApp.cpp @@ -0,0 +1,551 @@ +// 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 ClientApp.cpp + * + */ + +#include "ClientApp.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CLIParser.hpp" +#include "types/Calculator.hpp" +#include "types/CalculatorPubSubTypes.hpp" + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace request_reply { + +using namespace eprosima::fastdds::dds; + +/******* HELPER FUNCTIONS DECLARATIONS *******/ +namespace detail { + +template +Topic* create_topic( + const std::string& topic_name, + DomainParticipant* participant, + TypeSupport& type); + +} // namespace detail + +/******** CLIENTAPP CLASS DEFINITION ********/ +ClientApp::ClientApp( + const CLIParser::config& config, + const std::string& service_name) + : request_input_({config.x, config.y}) + , participant_(nullptr) + , request_type_(nullptr) + , request_topic_(nullptr) + , publisher_(nullptr) + , request_writer_(nullptr) + , reply_type_(nullptr) + , reply_topic_(nullptr) + , reply_cf_topic_(nullptr) + , reply_topic_filter_expression_("") + , subscriber_(nullptr) + , reply_reader_(nullptr) + , stop_(false) +{ + create_participant(); + create_request_entities(service_name); + create_reply_entities(service_name); + + request_reply_info("ClientApp", "Client initialized with ID: " << participant_->guid().guidPrefix); +} + +ClientApp::~ClientApp() +{ + if (nullptr != participant_) + { + // Delete DDS entities contained within the DomainParticipant + participant_->delete_contained_entities(); + + // Delete DomainParticipant + DomainParticipantFactory::get_shared_instance()->delete_participant(participant_); + } + + server_matched_status_.clear(); + reply_topic_filter_parameters_.clear(); +} + +void ClientApp::run() +{ + request_reply_debug("ClientApp", "Waiting for a server to be available"); + { + std::unique_lock lock(mtx_); + cv_.wait(lock, [&]() + { + return server_matched_status_.is_any_server_matched() || is_stopped(); + }); + } + + if (!is_stopped()) + { + request_reply_debug("ClientApp", + "One server is available. Waiting for some time to ensure matching on the server side"); + + // TODO(eduponz): This wait should be conditioned to upcoming fully-matched API on the endpoints + std::unique_lock lock(mtx_); + cv_.wait_for(lock, std::chrono::seconds(1), [&]() + { + return is_stopped(); + }); + } + + if (!is_stopped()) + { + if (!send_requests()) + { + throw std::runtime_error("Failed to send request"); + } + + wait_for_replies(); + } +} + +void ClientApp::stop() +{ + stop_.store(true); + cv_.notify_all(); +} + +void ClientApp::on_participant_discovery( + DomainParticipant* /* participant */, + rtps::ParticipantDiscoveryInfo&& info, + bool& should_be_ignored) +{ + std::lock_guard lock(mtx_); + + should_be_ignored = false; + + rtps::GuidPrefix_t remote_participant_guid_prefix = info.info.m_guid.guidPrefix; + std::string status_str = TypeConverter::to_string(info.status); + + if (info.info.m_userData.data_vec().size() != 1) + { + should_be_ignored = true; + request_reply_debug("ClientApp", "Ignoring participant with invalid user data: " + << remote_participant_guid_prefix); + } + + if (!should_be_ignored) + { + CLIParser::EntityKind entity_kind = static_cast(info.info.m_userData.data_vec()[0]); + if (CLIParser::EntityKind::SERVER != entity_kind) + { + should_be_ignored = true; + request_reply_debug("ClientApp", "Ignoring " << status_str << " " + << CLIParser::parse_entity_kind(entity_kind) + << ": " << remote_participant_guid_prefix); + } + } + + if (!should_be_ignored) + { + std::string server_str = CLIParser::parse_entity_kind(CLIParser::EntityKind::SERVER); + + if (info.status == rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS::DISCOVERED_PARTICIPANT) + { + request_reply_debug("ClientApp", server_str << " " << status_str << ": " << remote_participant_guid_prefix); + } + else if (info.status == rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS::REMOVED_PARTICIPANT || + info.status == rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS::DROPPED_PARTICIPANT) + { + request_reply_debug("ClientApp", server_str << " " << status_str << ": " << remote_participant_guid_prefix); + } + } +} + +void ClientApp::on_publication_matched( + DataWriter* /* writer */, + const PublicationMatchedStatus& info) +{ + std::lock_guard lock(mtx_); + + rtps::GuidPrefix_t server_guid_prefix = rtps::iHandle2GUID(info.last_subscription_handle).guidPrefix; + + if (info.current_count_change == 1) + { + request_reply_debug("ClientApp", "Remote request reader matched."); + + server_matched_status_.match_request_reader(server_guid_prefix, true); + } + else if (info.current_count_change == -1) + { + request_reply_debug("ClientApp", "Remote request reader unmatched."); + server_matched_status_.match_request_reader(server_guid_prefix, false); + } + else + { + request_reply_error("ClientApp", info.current_count_change + << " is not a valid value for SubscriptionMatchedStatus current count change"); + } + cv_.notify_all(); +} + +void ClientApp::on_subscription_matched( + DataReader* /* reader */, + const SubscriptionMatchedStatus& info) +{ + std::lock_guard lock(mtx_); + + rtps::GuidPrefix_t server_guid_prefix = rtps::iHandle2GUID(info.last_publication_handle).guidPrefix; + + if (info.current_count_change == 1) + { + request_reply_debug("ClientApp", "Remote reply writer matched."); + + server_matched_status_.match_reply_writer(server_guid_prefix, true); + } + else if (info.current_count_change == -1) + { + request_reply_debug("ClientApp", "Remote reply writer unmatched."); + server_matched_status_.match_reply_writer(server_guid_prefix, false); + } + else + { + request_reply_error("ClientApp", info.current_count_change + << " is not a valid value for SubscriptionMatchedStatus current count change"); + } + cv_.notify_all(); +} + +void ClientApp::on_data_available( + DataReader* reader) +{ + SampleInfo info; + CalculatorReplyType reply; + + while ((!is_stopped()) && (RETCODE_OK == reader->take_next_sample(&reply, &info))) + { + if ((info.instance_state == ALIVE_INSTANCE_STATE) && info.valid_data) + { + std::lock_guard lock(mtx_); + + rtps::GuidPrefix_t server_guid_prefix = rtps::iHandle2GUID(info.publication_handle).guidPrefix; + + auto request_status = requests_status_.find(info.related_sample_identity); + + if (requests_status_.end() != request_status) + { + if (!request_status->second) + { + request_status->second = true; + request_reply_info("ClientApp", "Reply received from server " + << server_guid_prefix << " to request with ID '" << request_status->first.sequence_number() + << "' with result: '" << reply.result() << "'"); + } + else + { + request_reply_debug("ClientApp", "Duplicate reply received from server " + << server_guid_prefix << " to request with ID '" << request_status->first.sequence_number() + << "' with result: '" << reply.result() << "'"); + continue; + } + } + else + { + request_reply_error("ClientApp", + "Reply received from server " << server_guid_prefix << " with unknown request ID '" + << info.related_sample_identity.sequence_number() << "'"); + continue; + } + + // Check if all responses have been received + if (requests_status_.size() == 4) + { + bool all_responses_received = true; + + for (auto status : requests_status_) + { + all_responses_received &= status.second; + } + + if (all_responses_received) + { + stop(); + break; + } + } + } + } +} + +void ClientApp::create_participant() +{ + // Create the participant + auto factory = DomainParticipantFactory::get_shared_instance(); + + if (nullptr == factory) + { + throw std::runtime_error("Failed to get participant factory instance"); + } + + StatusMask participant_mask = StatusMask::none(); + participant_mask << StatusMask::publication_matched(); + participant_mask << StatusMask::subscription_matched(); + participant_mask << StatusMask::data_available(); + + DomainParticipantExtendedQos participant_qos; + factory->get_participant_extended_qos_from_default_profile(participant_qos); + + participant_qos.user_data().data_vec().push_back(static_cast(CLIParser::EntityKind::CLIENT)); + + participant_ = factory->create_participant(participant_qos.domainId(), participant_qos, this, participant_mask); + + if (nullptr == participant_) + { + throw std::runtime_error("Participant initialization failed"); + } +} + +template<> +Topic* ClientApp::create_topic( + const std::string& topic_name, + TypeSupport& type) +{ + return detail::create_topic(topic_name, participant_, type); +} + +template<> +Topic* ClientApp::create_topic( + const std::string& topic_name, + TypeSupport& type) +{ + return detail::create_topic(topic_name, participant_, type); +} + +void ClientApp::create_request_entities( + const std::string& service_name) +{ + // Create the request topic + request_topic_ = create_topic("rq/" + service_name, request_type_); + + // Create the publisher + PublisherQos pub_qos = PUBLISHER_QOS_DEFAULT; + + if (RETCODE_OK != participant_->get_default_publisher_qos(pub_qos)) + { + throw std::runtime_error("Failed to get default publisher qos"); + } + + publisher_ = participant_->create_publisher(pub_qos, nullptr, StatusMask::none()); + + if (nullptr == publisher_) + { + throw std::runtime_error("Publisher initialization failed"); + } + + // Create the writer + DataWriterQos writer_qos = DATAWRITER_QOS_DEFAULT; + + if (RETCODE_OK != publisher_->get_default_datawriter_qos(writer_qos)) + { + throw std::runtime_error("Failed to get default datawriter qos"); + } + + request_writer_ = publisher_->create_datawriter(request_topic_, writer_qos, nullptr, StatusMask::none()); + + if (nullptr == request_writer_) + { + throw std::runtime_error("Request writer initialization failed"); + } +} + +void ClientApp::create_reply_entities( + const std::string& service_name) +{ + // Create the reply topic + reply_topic_ = create_topic("rr/" + service_name, reply_type_); + + reply_topic_filter_expression_ = "client_id = '" + + TypeConverter::to_string(participant_->guid().guidPrefix) + "'"; + + reply_cf_topic_ = participant_->create_contentfilteredtopic("rr/" + service_name + "_cft", reply_topic_, + reply_topic_filter_expression_, + reply_topic_filter_parameters_); + + if (nullptr == reply_cf_topic_) + { + throw std::runtime_error("Failed to create CFT"); + } + + // Create the subscriber + SubscriberQos sub_qos = SUBSCRIBER_QOS_DEFAULT; + + if (RETCODE_OK != participant_->get_default_subscriber_qos(sub_qos)) + { + throw std::runtime_error("Failed to get default subscriber qos"); + } + + subscriber_ = participant_->create_subscriber(sub_qos, nullptr, StatusMask::none()); + + if (nullptr == subscriber_) + { + throw std::runtime_error("Subscriber initialization failed"); + } + + // Create the reader + DataReaderQos reader_qos = DATAREADER_QOS_DEFAULT; + + if (RETCODE_OK != subscriber_->get_default_datareader_qos(reader_qos)) + { + throw std::runtime_error("Failed to get default datareader qos"); + } + + reply_reader_ = subscriber_->create_datareader(reply_cf_topic_, reader_qos, nullptr, StatusMask::none()); + + if (nullptr == reply_reader_) + { + throw std::runtime_error("Reply reader initialization failed"); + } +} + +bool ClientApp::send_requests() +{ + CalculatorRequestType request; + + request.client_id(TypeConverter::to_string(participant_->guid().guidPrefix)); + request.x(request_input_.first); + request.y(request_input_.second); + + request.operation(CalculatorOperationType::ADDITION); + bool ret = send_request(request); + + if (ret) + { + request.operation(CalculatorOperationType::SUBTRACTION); + ret = send_request(request); + } + + if (ret) + { + request.operation(CalculatorOperationType::MULTIPLICATION); + ret = send_request(request); + } + + if (ret) + { + request.operation(CalculatorOperationType::DIVISION); + ret = send_request(request); + } + + return ret; +} + +bool ClientApp::send_request( + const CalculatorRequestType& request) +{ + // Taking the mutex here to avoid taking a reply on the on_data_available callback + // coming from a very fast server who replied before the request_status_ entry was set + std::lock_guard lock(mtx_); + + rtps::WriteParams wparams; + ReturnCode_t ret = request_writer_->write(&request, wparams); + + requests_status_[wparams.sample_identity()] = false; + + request_reply_info("ClientApp", + "Request sent with ID '" << wparams.sample_identity().sequence_number() << + "': '" << TypeConverter::to_string(request) << "'"); + + return (RETCODE_OK == ret); +} + +bool ClientApp::is_stopped() +{ + return stop_.load(); +} + +void ClientApp::wait_for_replies() +{ + std::unique_lock lock(mtx_); + cv_.wait(lock, [&]() + { + return is_stopped(); + }); +} + +/******* HELPER FUNCTIONS DEFINITIONS *******/ +namespace detail { + +template +Topic* create_topic( + const std::string& topic_name, + DomainParticipant* participant, + TypeSupport& type) +{ + assert(nullptr != participant); + assert(nullptr == type.get()); + + Topic* topic = nullptr; + + // Create the TypeSupport + type.reset(new TypeSupportClass()); + + if (nullptr == type) + { + throw std::runtime_error("Failed to create type"); + } + + // Register the type + if (RETCODE_OK != type.register_type(participant)) + { + throw std::runtime_error("Failed to register type"); + } + + // Create the topic + TopicQos topic_qos = TOPIC_QOS_DEFAULT; + + if (RETCODE_OK != participant->get_default_topic_qos(topic_qos)) + { + throw std::runtime_error("Failed to get default topic qos"); + } + + topic = participant->create_topic(topic_name, type.get_type_name(), topic_qos); + + if (nullptr == topic) + { + throw std::runtime_error("Request topic initialization failed"); + } + + return topic; +} + +} // namespace detail +} // namespace request_reply +} // namespace examples +} // namespace fastdds +} // namespace eprosima diff --git a/examples/cpp/request_reply/ClientApp.hpp b/examples/cpp/request_reply/ClientApp.hpp new file mode 100644 index 00000000000..84624520800 --- /dev/null +++ b/examples/cpp/request_reply/ClientApp.hpp @@ -0,0 +1,167 @@ +// 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 ClientApp.hpp + * + */ + +#ifndef FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__CLIENTAPP_HPP +#define FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__CLIENTAPP_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "app_utils.hpp" +#include "Application.hpp" +#include "CLIParser.hpp" +#include "types/Calculator.hpp" +#include "types/CalculatorPubSubTypes.hpp" + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace request_reply { + +using namespace eprosima::fastdds::dds; + +class ClientApp : public Application, public DomainParticipantListener +{ +public: + + ClientApp( + const CLIParser::config& config, + const std::string& service_name); + + ~ClientApp(); + + //! Run subscriber + void run() override; + + //! Trigger the end of execution + void stop() override; + + //! Participant discovery method + void on_participant_discovery( + DomainParticipant* participant, + rtps::ParticipantDiscoveryInfo&& info, + bool& should_be_ignored) override; + + //! Publication matched method + void on_publication_matched( + DataWriter* writer, + const PublicationMatchedStatus& info) override; + + //! Subscription matched method + void on_subscription_matched( + DataReader* reader, + const SubscriptionMatchedStatus& info) override; + + //! Reply received method + void on_data_available( + DataReader* reader) override; + +private: + + void create_participant(); + + template + Topic* create_topic( + const std::string& topic_name, + TypeSupport& type); + + void create_request_entities( + const std::string& service_name); + + void create_reply_entities( + const std::string& service_name); + + bool send_requests(); + + bool send_request( + const CalculatorRequestType& request); + + bool is_stopped(); + + void wait_for_replies(); + + std::pair request_input_; + + DomainParticipant* participant_; + + TypeSupport request_type_; + + Topic* request_topic_; + + Publisher* publisher_; + + DataWriter* request_writer_; + + TypeSupport reply_type_; + + Topic* reply_topic_; + + ContentFilteredTopic* reply_cf_topic_; + + std::string reply_topic_filter_expression_; + + std::vector reply_topic_filter_parameters_; + + Subscriber* subscriber_; + + DataReader* reply_reader_; + + mutable std::mutex mtx_; + + std::condition_variable cv_; + + RemoteServerMatchedStatus server_matched_status_; + + std::atomic stop_; + + std::map requests_status_; +}; + +template<> +Topic* ClientApp::create_topic( + const std::string& topic_name, + TypeSupport& type); + +template<> +Topic* ClientApp::create_topic( + const std::string& topic_name, + TypeSupport& type); + +} // namespace request_reply +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif /* FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__CLIENTAPP_HPP */ diff --git a/examples/cpp/request_reply/README.md b/examples/cpp/request_reply/README.md new file mode 100644 index 00000000000..6e66dd2aa20 --- /dev/null +++ b/examples/cpp/request_reply/README.md @@ -0,0 +1,107 @@ +# Request-Reply example + +The *eProsima Fast DDS Request-Reply* example shows how to create a service oriented architecture using the *Request-Reply* communication pattern over Fast DDS. + +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*. + +This *request_reply* example shows a simple approach for a multi-server multi-client architecture, where servers are both prepared to reply to several clients, and also to be run alongside other server instances (to provide redundancy). +In this example, the clients take one "calculator" operation as input, and exit as soon as any of the servers has provided a response for their request. + +* [Description of the example](#description-of-the-example) +* [Run the example](#run-the-example) +* [XML profile playground](#xml-profile-playground) + +## Description of the example + +The RPC like communication pattern is implemented using a pair of related topics, one for the request and one for the reply. +The approach creates the following data flow for a request-reply operation: + +```mermaid +sequenceDiagram +participant Client +participant Client Request Writer +participant Client Reply Reader + +participant Server +participant Server Request Reader +participant Server Reply Writer + +Client->>Client: Wait for server availability +Client->>Client Request Writer: request +Client Request Writer->>Server Request Reader: request +Server Request Reader->>Server: calculate response +Server->>Server Reply Writer: response +Server Reply Writer->>Client Reply Reader: response +Client Reply Reader->>Client: response +``` + +## Run the example + +To launch this example, two different terminals are required. +One of them will run the server application, and the other will run the client application. +Mind that it is possible to run multiple server instances and client instances simultaneously. + +### Server + +* Ubuntu ( / MacOS ) + + ```shell + user@machine:example_path$ ./request_reply server + 2024-07-01T16:26:45.314 [INFO] [ServerApp] Server initialized with ID: 01.0f.cc.6d.01.3d.fd.74.00.00.00.00 + 2024-07-01T16:26:45.315 [INFO] [main] Server running. Please press Ctrl+C to stop the Server at any time. + ``` + +* Windows + + ```powershell + example_path> request_reply.exe server + 2024-07-01T16:26:45.314 [INFO] [ServerApp] Server initialized with ID: 01.0f.cc.6d.01.3d.fd.74.00.00.00.00 + 2024-07-01T16:26:45.315 [INFO] [main] Server running. Please press Ctrl+C to stop the Server at any time. + ``` + +### Client + +* Ubuntu ( / MacOS ) + + ```shell + user@machine:example_path$ ./request_reply client 2 x 5 + 2024-07-01T16:08:28.296 [INFO] [ClientApp] Client initialized with ID: 01.0f.cc.6d.21.0f.f0.6b.00.00.00.00 + 2024-07-01T16:08:28.296 [INFO] [main] Client running. Please press Ctrl+C to stop the Client at any time. + 2024-07-01T16:08:29.296 [INFO] [ClientApp] Request sent with ID '1': '2 x 5' + 2024-07-01T16:08:29.297 [INFO] [ClientApp] Reply received from server 01.0f.cc.6d.92.0e.de.f0.00.00.00.00 with result: 10 + ``` + +* Windows + + ```powershell + example_path> request_reply.exe client 2 x 5 + 2024-07-01T16:08:28.296 [INFO] [ClientApp] Client initialized with ID: 01.0f.cc.6d.21.0f.f0.6b.00.00.00.00 + 2024-07-01T16:08:28.296 [INFO] [main] Client running. Please press Ctrl+C to stop the Client at any time. + 2024-07-01T16:08:29.296 [INFO] [ClientApp] Request sent with ID '1': '2 x 5' + 2024-07-01T16:08:29.297 [INFO] [ClientApp] Reply received from server 01.0f.cc.6d.92.0e.de.f0.00.00.00.00 with result: 10 + ``` + +## 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=request_reply_profile.xml + ``` + +* Windows + + ```powershell + example_path> set FASTDDS_DEFAULT_PROFILES_FILE=request_reply_profile.xml + ``` + +The example provides with an XML profiles files with certain QoS to ensure correct reception of the requests and replies. + +- 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. diff --git a/examples/cpp/request_reply/ServerApp.cpp b/examples/cpp/request_reply/ServerApp.cpp new file mode 100644 index 00000000000..b3ee98b9f08 --- /dev/null +++ b/examples/cpp/request_reply/ServerApp.cpp @@ -0,0 +1,568 @@ +// 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 ServerApp.cpp + * + */ + +#include "ServerApp.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CLIParser.hpp" +#include "types/Calculator.hpp" +#include "types/CalculatorPubSubTypes.hpp" + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace request_reply { + +/******* HELPER FUNCTIONS DECLARATIONS *******/ +namespace detail { + +template +Topic* create_topic( + const std::string& topic_name, + DomainParticipant* participant, + TypeSupport& type); + +} // namespace detail + +/******** SERVERAPP CLASS DEFINITION ********/ +ServerApp::ServerApp( + const std::string& service_name) + : participant_(nullptr) + , request_type_(nullptr) + , request_topic_(nullptr) + , subscriber_(nullptr) + , request_reader_(nullptr) + , reply_type_(nullptr) + , reply_topic_(nullptr) + , publisher_(nullptr) + , reply_writer_(nullptr) + , stop_(false) +{ + // Spawn the reply thread + reply_thread_ = std::thread(&ServerApp::reply_routine, this); + + // Create the DDS entities + create_participant(); + create_request_entities(service_name); + create_reply_entities(service_name); + + request_reply_info("ServerApp", "Server initialized with ID: " << participant_->guid().guidPrefix); +} + +ServerApp::~ServerApp() +{ + // Join reply thread + if (reply_thread_.joinable()) + { + reply_thread_.join(); + } + + if (nullptr != participant_) + { + // Delete DDS entities contained within the DomainParticipant + participant_->delete_contained_entities(); + + // Delete DomainParticipant + DomainParticipantFactory::get_instance()->delete_participant(participant_); + } + + // Cleanup the pending requests + while (requests_.size() > 0) + { + requests_.pop(); + } + + client_matched_status_.clear(); +} + +void ServerApp::run() +{ + std::unique_lock lock(mtx_); + cv_.wait(lock, [this]() -> bool + { + return is_stopped(); + }); +} + +void ServerApp::stop() +{ + stop_.store(true); + cv_.notify_all(); +} + +void ServerApp::on_participant_discovery( + DomainParticipant* /* participant */, + rtps::ParticipantDiscoveryInfo&& info, + bool& should_be_ignored) +{ + std::lock_guard lock(mtx_); + + should_be_ignored = false; + + rtps::GuidPrefix_t remote_participant_guid_prefix = info.info.m_guid.guidPrefix; + std::string status_str = TypeConverter::to_string(info.status); + + if (info.info.m_userData.data_vec().size() != 1) + { + should_be_ignored = true; + request_reply_debug("ServerApp", "Ignoring participant with invalid user data: " + << remote_participant_guid_prefix); + } + + if (!should_be_ignored) + { + CLIParser::EntityKind entity_kind = static_cast(info.info.m_userData.data_vec()[0]); + if (CLIParser::EntityKind::CLIENT != entity_kind) + { + should_be_ignored = true; + request_reply_debug("ServerApp", "Ignoring " << status_str << " " + << CLIParser::parse_entity_kind(entity_kind) + << ": " << remote_participant_guid_prefix); + } + } + + if (!should_be_ignored) + { + std::string client_str = CLIParser::parse_entity_kind(CLIParser::EntityKind::CLIENT); + + if (info.status == rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS::DISCOVERED_PARTICIPANT) + { + request_reply_debug("ServerApp", client_str << " " << status_str << ": " << remote_participant_guid_prefix); + } + else if (info.status == rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS::REMOVED_PARTICIPANT || + info.status == rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS::DROPPED_PARTICIPANT) + { + client_matched_status_.match_reply_reader(remote_participant_guid_prefix, false); + client_matched_status_.match_request_writer(remote_participant_guid_prefix, false); + + request_reply_debug("ServerApp", client_str << " " << status_str << ": " << remote_participant_guid_prefix); + } + } +} + +void ServerApp::on_publication_matched( + DataWriter* /* writer */, + const PublicationMatchedStatus& info) +{ + std::lock_guard lock(mtx_); + + rtps::GuidPrefix_t client_guid_prefix = rtps::iHandle2GUID(info.last_subscription_handle).guidPrefix; + + if (info.current_count_change == 1) + { + request_reply_debug("ServerApp", "Remote reply reader matched with client " << client_guid_prefix); + client_matched_status_.match_reply_reader(client_guid_prefix, true); + } + else if (info.current_count_change == -1) + { + request_reply_debug("ServerApp", "Remote reply reader unmatched from client " << client_guid_prefix); + client_matched_status_.match_reply_reader(client_guid_prefix, false); + + // Remove old replies since no one is waiting for them + if (client_matched_status_.no_client_matched()) + { + std::size_t removed; + reply_writer_->clear_history(&removed); + } + } + else + { + request_reply_error("ServerApp", info.current_count_change + << " is not a valid value for PublicationMatchedStatus current count change"); + } +} + +void ServerApp::on_subscription_matched( + DataReader* /* reader */, + const SubscriptionMatchedStatus& info) +{ + std::lock_guard lock(mtx_); + + rtps::GuidPrefix_t client_guid_prefix = rtps::iHandle2GUID(info.last_publication_handle).guidPrefix; + + if (info.current_count_change == 1) + { + request_reply_debug("ServerApp", "Remote request writer matched with client " << client_guid_prefix); + client_matched_status_.match_request_writer(client_guid_prefix, true); + } + else if (info.current_count_change == -1) + { + request_reply_debug("ServerApp", "Remote request writer unmatched from client " << client_guid_prefix); + client_matched_status_.match_request_writer(client_guid_prefix, false); + + // Remove old replies since no one is waiting for them + if (client_matched_status_.no_client_matched()) + { + std::size_t removed; + reply_writer_->clear_history(&removed); + } + } + else + { + request_reply_error("ServerApp", + info.current_count_change << + " is not a valid value for SubscriptionMatchedStatus current count change"); + } +} + +void ServerApp::on_data_available( + DataReader* reader) +{ + SampleInfo info; + auto request = std::make_shared(); + + while ((!is_stopped()) && (RETCODE_OK == reader->take_next_sample(request.get(), &info))) + { + if ((info.instance_state == ALIVE_INSTANCE_STATE) && info.valid_data) + { + rtps::GuidPrefix_t client_guid_prefix = rtps::iHandle2GUID(info.publication_handle).guidPrefix; + rtps::SequenceNumber_t request_id = info.sample_identity.sequence_number(); + + request_reply_info("ServerApp", + "Request with ID '" << request_id << "' received from client " << client_guid_prefix); + + { + // Only lock to push the request into the queue so that the consumer thread gets + // a chance to process it in between subsequent takes + std::lock_guard lock(mtx_); + requests_.push({info, request}); + cv_.notify_all(); + } + } + } +} + +void ServerApp::create_participant() +{ + // Create the participant + auto factory = DomainParticipantFactory::get_instance(); + + if (nullptr == factory) + { + throw std::runtime_error("Failed to get participant factory instance"); + } + + StatusMask participant_mask = StatusMask::none(); + participant_mask << StatusMask::publication_matched(); + participant_mask << StatusMask::subscription_matched(); + participant_mask << StatusMask::data_available(); + + DomainParticipantExtendedQos participant_qos; + factory->get_participant_extended_qos_from_default_profile(participant_qos); + + participant_qos.user_data().data_vec().push_back(static_cast(CLIParser::EntityKind::SERVER)); + + participant_ = factory->create_participant(participant_qos.domainId(), participant_qos, this, participant_mask); + + if (nullptr == participant_) + { + throw std::runtime_error("Participant initialization failed"); + } +} + +template<> +Topic* ServerApp::create_topic( + const std::string& topic_name, + TypeSupport& type) +{ + return detail::create_topic(topic_name, participant_, type); +} + +template<> +Topic* ServerApp::create_topic( + const std::string& topic_name, + TypeSupport& type) +{ + return detail::create_topic(topic_name, participant_, type); +} + +void ServerApp::create_request_entities( + const std::string& service_name) +{ + request_topic_ = create_topic("rq/" + service_name, request_type_); + + // Create the subscriber + SubscriberQos sub_qos = SUBSCRIBER_QOS_DEFAULT; + + if (RETCODE_OK != participant_->get_default_subscriber_qos(sub_qos)) + { + throw std::runtime_error("Failed to get default subscriber qos"); + } + + subscriber_ = participant_->create_subscriber(sub_qos, nullptr, StatusMask::none()); + + if (nullptr == subscriber_) + { + throw std::runtime_error("Subscriber initialization failed"); + } + + // Create the request reader + DataReaderQos reader_qos = DATAREADER_QOS_DEFAULT; + + if (RETCODE_OK != subscriber_->get_default_datareader_qos(reader_qos)) + { + throw std::runtime_error("Failed to get default datareader qos"); + } + + request_reader_ = subscriber_->create_datareader(request_topic_, reader_qos, nullptr, StatusMask::none()); + + if (nullptr == request_reader_) + { + throw std::runtime_error("Request reader initialization failed"); + } +} + +void ServerApp::create_reply_entities( + const std::string& service_name) +{ + reply_topic_ = create_topic("rr/" + service_name, reply_type_); + + // Create the publisher + PublisherQos pub_qos = PUBLISHER_QOS_DEFAULT; + + if (RETCODE_OK != participant_->get_default_publisher_qos(pub_qos)) + { + throw std::runtime_error("Failed to get default publisher qos"); + } + + publisher_ = participant_->create_publisher(pub_qos, nullptr, StatusMask::none()); + + if (nullptr == publisher_) + { + throw std::runtime_error("Publisher initialization failed"); + } + + // Create the reply writer + DataWriterQos writer_qos = DATAWRITER_QOS_DEFAULT; + + if (RETCODE_OK != publisher_->get_default_datawriter_qos(writer_qos)) + { + throw std::runtime_error("Failed to get default datawriter qos"); + } + + reply_writer_ = publisher_->create_datawriter(reply_topic_, writer_qos, nullptr, StatusMask::none()); + + if (nullptr == reply_writer_) + { + throw std::runtime_error("Reply writer initialization failed"); + } +} + +bool ServerApp::is_stopped() +{ + return stop_.load(); +} + +void ServerApp::reply_routine() +{ + while (!is_stopped()) + { + // Wait for a request to arrive + std::unique_lock lock(mtx_); + + cv_.wait(lock, [this]() -> bool + { + return requests_.size() > 0 || is_stopped(); + }); + + if (!is_stopped()) + { + // Process one request per iteration + Request request = requests_.front(); + requests_.pop(); + + rtps::GuidPrefix_t client_guid_prefix = rtps::iHandle2GUID(request.info.publication_handle).guidPrefix; + request_reply_debug("ServerApp", "Processing request from client " << client_guid_prefix); + + // If none of the client's endpoints are matched, ignore the request as the client is gone + if (!client_matched_status_.is_fully_unmatched(client_guid_prefix)) + { + request_reply_info("ServerApp", "Ignoring request from already gone client " << client_guid_prefix); + continue; + } + + // If the request's client is not fully matched, save it for later + if (!client_matched_status_.is_matched(client_guid_prefix)) + { + request_reply_debug("ServerApp", + "Client " << client_guid_prefix << " not fully matched, saving request for later"); + requests_.push(request); + continue; + } + + // Calculate the result + std::int32_t result; + + // If the calculation fails, ignore the request, as the failure cause is a malformed request + if (!calculate(*request.request, result)) + { + request_reply_error("ServerApp", + "Failed to calculate result for request from client " << client_guid_prefix); + continue; + } + + // Prepare the reply + CalculatorReplyType reply; + reply.client_id(request.request->client_id()); + reply.result(result); + + // Prepare the WriteParams to link the reply to the request + rtps::WriteParams write_params; + rtps::SequenceNumber_t request_id = request.info.sample_identity.sequence_number(); + write_params.related_sample_identity().writer_guid(request.info.sample_identity.writer_guid()); + write_params.related_sample_identity().sequence_number(request_id); + + // Send the reply + if (RETCODE_OK != reply_writer_->write(&reply, write_params)) + { + // In case of failure, save the request for a later retry + request_reply_error("ServerApp", + "Failed to send reply to request with ID '" << request_id << "' to client " << + client_guid_prefix); + requests_.push(request); + } + else + { + request_reply_info("ServerApp", + "Reply to request with ID '" << request_id << "' sent to client " << client_guid_prefix); + } + } + } +} + +bool ServerApp::calculate( + const CalculatorRequestType& request, + std::int32_t& result) +{ + bool success = true; + + switch (request.operation()) + { + case CalculatorOperationType::ADDITION: + { + result = request.x() + request.y(); + break; + } + case CalculatorOperationType::SUBTRACTION: + { + result = request.x() - request.y(); + break; + } + case CalculatorOperationType::MULTIPLICATION: + { + result = request.x() * request.y(); + break; + } + case CalculatorOperationType::DIVISION: + { + if (0 == request.y()) + { + request_reply_error("ServerApp", "Division by zero request received"); + success = false; + } + else + { + result = request.x() / request.y(); + } + break; + } + default: + { + request_reply_error("ServerApp", "Unknown operation received"); + success = false; + break; + } + } + + return success; +} + +/******* HELPER FUNCTIONS DEFINITIONS *******/ +namespace detail { + +template +Topic* create_topic( + const std::string& topic_name, + DomainParticipant* participant, + TypeSupport& type) +{ + assert(nullptr != participant); + assert(nullptr == type.get()); + + Topic* topic = nullptr; + + // Create the TypeSupport + type.reset(new TypeSupportClass()); + + if (nullptr == type) + { + throw std::runtime_error("Failed to create type"); + } + + // Register the type + if (RETCODE_OK != type.register_type(participant)) + { + throw std::runtime_error("Failed to register type"); + } + + // Create the topic + TopicQos topic_qos = TOPIC_QOS_DEFAULT; + + if (RETCODE_OK != participant->get_default_topic_qos(topic_qos)) + { + throw std::runtime_error("Failed to get default topic qos"); + } + + topic = participant->create_topic(topic_name, type.get_type_name(), topic_qos); + + if (nullptr == topic) + { + throw std::runtime_error("Request topic initialization failed"); + } + + return topic; +} + +} // namespace detail +} // namespace request_reply +} // namespace examples +} // namespace fastdds +} // namespace eprosima diff --git a/examples/cpp/request_reply/ServerApp.hpp b/examples/cpp/request_reply/ServerApp.hpp new file mode 100644 index 00000000000..a195b8164ff --- /dev/null +++ b/examples/cpp/request_reply/ServerApp.hpp @@ -0,0 +1,164 @@ +// 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 ServerApp.hpp + * + */ + +#ifndef FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__SERVERAPP_HPP +#define FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__SERVERAPP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "app_utils.hpp" +#include "Application.hpp" +#include "types/CalculatorPubSubTypes.hpp" + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace request_reply { + +using namespace eprosima::fastdds::dds; + +class ServerApp : public Application, public DomainParticipantListener +{ +public: + + ServerApp( + const std::string& service_name); + + ~ServerApp(); + + //! Run server + void run() override; + + //! Stop server + void stop() override; + + //! Participant discovery method + void on_participant_discovery( + DomainParticipant* participant, + rtps::ParticipantDiscoveryInfo&& info, + bool& should_be_ignored) override; + + //! Publication matched method + void on_publication_matched( + DataWriter* writer, + const PublicationMatchedStatus& info) override; + + //! Subscription matched method + void on_subscription_matched( + DataReader* reader, + const SubscriptionMatchedStatus& info) override; + + //! Request received method + void on_data_available( + DataReader* reader) override; + +private: + + void create_participant(); + + template + Topic* create_topic( + const std::string& topic_name, + TypeSupport& type); + + void create_request_entities( + const std::string& service_name); + + void create_reply_entities( + const std::string& service_name); + + bool is_stopped(); + + void reply_routine(); + + bool calculate( + const CalculatorRequestType& request, + std::int32_t& result); + + DomainParticipant* participant_; + + TypeSupport request_type_; + + Topic* request_topic_; + + Subscriber* subscriber_; + + DataReader* request_reader_; + + TypeSupport reply_type_; + + Topic* reply_topic_; + + Publisher* publisher_; + + DataWriter* reply_writer_; + + std::mutex mtx_; + + std::condition_variable cv_; + + std::atomic stop_; + + RemoteClientMatchedStatus client_matched_status_; + + struct Request + { + SampleInfo info; + std::shared_ptr request; + }; + + std::queue requests_; + + std::thread reply_thread_; + +}; + +template<> +Topic* ServerApp::create_topic( + const std::string& topic_name, + TypeSupport& type); + +template<> +Topic* ServerApp::create_topic( + const std::string& topic_name, + TypeSupport& type); + +} // namespace request_reply +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif /* FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__SERVERAPP_HPP */ diff --git a/examples/cpp/request_reply/app_utils.hpp b/examples/cpp/request_reply/app_utils.hpp new file mode 100644 index 00000000000..65633406cbb --- /dev/null +++ b/examples/cpp/request_reply/app_utils.hpp @@ -0,0 +1,309 @@ +// 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 app_utils.hpp + * + */ + +#ifndef FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__APP_UTILS_HPP +#define FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__APP_UTILS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "types/Calculator.hpp" + +namespace eprosima { +namespace fastdds { +namespace examples { +namespace request_reply { + +class RemoteServerMatchedStatus +{ +public: + + void match_request_reader( + const rtps::GuidPrefix_t& guid_prefix, + const bool& status) + { + matched_status_[guid_prefix].set(request_reader_position, status); + } + + void match_reply_writer( + const rtps::GuidPrefix_t& guid_prefix, + const bool& status) + { + matched_status_[guid_prefix].set(reply_writer_position, status); + } + + bool is_matched( + const rtps::GuidPrefix_t& guid_prefix) + { + bool is_server_matched = false; + + auto status = matched_status_.find(guid_prefix); + + if (status != matched_status_.end()) + { + is_server_matched = status->second.all(); + } + + return is_server_matched; + } + + bool is_any_server_matched() + { + bool any_server_matched = false; + for (const auto& status : matched_status_) + { + if (status.second.all()) + { + any_server_matched = true; + break; + } + } + return any_server_matched; + } + + void clear() + { + matched_status_.clear(); + } + +private: + + std::map> matched_status_; + + static const size_t request_reader_position = 0; + + static const size_t reply_writer_position = 1; +}; + +class RemoteClientMatchedStatus +{ +public: + + void match_request_writer( + const rtps::GuidPrefix_t& guid_prefix, + const bool& status) + { + matched_status_[guid_prefix].set(request_writer_position, status); + } + + void match_reply_reader( + const rtps::GuidPrefix_t& guid_prefix, + const bool& status) + { + matched_status_[guid_prefix].set(reply_reader_position, status); + } + + bool is_matched( + const rtps::GuidPrefix_t& guid_prefix) + { + bool is_client_matched = false; + + auto status = matched_status_.find(guid_prefix); + + if (status != matched_status_.end()) + { + is_client_matched = status->second.all(); + } + + return is_client_matched; + } + + bool is_fully_unmatched( + const rtps::GuidPrefix_t& guid_prefix) + { + bool is_client_unmatched = false; + + auto status = matched_status_.find(guid_prefix); + + if (status != matched_status_.end()) + { + is_client_unmatched = !status->second.none(); + } + + return is_client_unmatched; + } + + bool no_client_matched() + { + bool no_client_matched = true; + + for (const auto& status : matched_status_) + { + if (status.second.any()) + { + no_client_matched = false; + break; + } + } + + return no_client_matched; + } + + void clear() + { + matched_status_.clear(); + } + +private: + + std::map> matched_status_; + + static const size_t request_writer_position = 0; + + static const size_t reply_reader_position = 1; +}; + +struct TypeConverter +{ + static std::string to_string( + const CalculatorRequestType& request) + { + std::ostringstream request_ss; + request_ss << request.x() << " " << to_string(request.operation()) << " " << request.y(); + return request_ss.str(); + } + + static std::string to_string( + const CalculatorOperationType& operation) + { + std::string operation_str = "Unknown"; + switch (operation) + { + case CalculatorOperationType::ADDITION: + operation_str = "+"; + break; + case CalculatorOperationType::SUBTRACTION: + operation_str = "-"; + break; + case CalculatorOperationType::MULTIPLICATION: + operation_str = "*"; + break; + case CalculatorOperationType::DIVISION: + operation_str = "/"; + break; + default: + break; + } + return operation_str; + } + + static std::string to_string( + const rtps::GuidPrefix_t& guid_prefix) + { + std::ostringstream client_id; + client_id << guid_prefix; + return client_id.str(); + } + + static std::string to_string( + const rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS& info) + { + std::string info_str = "Unknown"; + + switch (info) + { + case rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS::DISCOVERED_PARTICIPANT: + info_str = "discovered"; + break; + case rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS::CHANGED_QOS_PARTICIPANT: + info_str = "changed QoS"; + break; + case rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS::REMOVED_PARTICIPANT: + info_str = "removed"; + break; + case rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS::DROPPED_PARTICIPANT: + info_str = "dropped"; + break; + case rtps::ParticipantDiscoveryInfo::DISCOVERY_STATUS::IGNORED_PARTICIPANT: + info_str = "ignored"; + break; + default: + break; + } + + return info_str; + } + +}; + +struct Timestamp +{ + static std::string now() + { + // Get current time + auto now = std::chrono::system_clock::now(); + auto time_t_now = std::chrono::system_clock::to_time_t(now); + + // Convert to tm struct for local time + std::tm tm_now; +#if defined(_WIN32) || defined(_WIN64) + localtime_s(&tm_now, &time_t_now); +#else + localtime_r(&time_t_now, &tm_now); +#endif // if defined(_WIN32) || defined(_WIN64) + + // Format date and time + std::ostringstream oss; + oss << std::put_time(&tm_now, "%Y-%m-%dT%H:%M:%S"); + + // Add milliseconds + auto duration = now.time_since_epoch(); + auto milliseconds = std::chrono::duration_cast(duration).count() % 1000; + oss << '.' << std::setfill('0') << std::setw(3) << milliseconds; + + return oss.str(); + } + +}; + +#ifndef NDEGUB +#define request_reply_debug(context, message) \ + std::cout << C_B_WHITE << Timestamp::now() << C_B_BLUE << " [DEBUG] " << C_B_WHITE \ + << "[" << context << "] " << C_DEF << message << std::endl +#else +#define request_reply_debug(context, message) +#endif // ifdef NDEGUB + +#define request_reply_info(context, message) \ + std::cout << C_B_WHITE << Timestamp::now() << C_B_GREEN << " [INFO] " << C_B_WHITE \ + << "[" << context << "] " << C_DEF << message << std::endl + +#define request_reply_error(context, message) \ + std::cerr << C_B_WHITE << Timestamp::now() << C_B_RED << " [ERROR] " << C_B_WHITE \ + << "[" << context << "] " << C_DEF << message << std::endl + +} // namespace request_reply +} // namespace examples +} // namespace fastdds +} // namespace eprosima + +#endif // FASTDDS_EXAMPLES_CPP_REQUEST_REPLY__APP_UTILS_HPP diff --git a/examples/cpp/request_reply/main.cpp b/examples/cpp/request_reply/main.cpp new file mode 100644 index 00000000000..f17b7b70fb1 --- /dev/null +++ b/examples/cpp/request_reply/main.cpp @@ -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 main.cpp + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "app_utils.hpp" +#include "Application.hpp" +#include "CLIParser.hpp" + +using eprosima::fastdds::dds::Log; + +using namespace eprosima::fastdds::examples::request_reply; + +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 service_name = "calculator_service"; + CLIParser::config config = CLIParser::parse_cli_options(argc, argv); + + std::string app_name = CLIParser::parse_entity_kind(config.entity); + std::shared_ptr app; + + try + { + app = Application::make_app(config, service_name); + } + catch (const std::runtime_error& e) + { + request_reply_error("main", e.what()); + ret = EXIT_FAILURE; + } + + if (EXIT_FAILURE != ret) + { + std::thread thread(&Application::run, app); + + stop_app_handler = [&](int signum) + { + request_reply_info("main", + CLIParser::parse_signal(signum) << " received, stopping " << app_name << " execution."); + + app->stop(); + }; + + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + #ifndef _WIN32 + signal(SIGQUIT, signal_handler); + signal(SIGHUP, signal_handler); + #endif // _WIN32 + + request_reply_info("main", + app_name << " running. Please press Ctrl+C to stop the " << app_name << " at any time."); + + thread.join(); + } + + return ret; +} diff --git a/examples/cpp/request_reply/request_reply_profile.xml b/examples/cpp/request_reply/request_reply_profile.xml new file mode 100644 index 00000000000..878878b21c3 --- /dev/null +++ b/examples/cpp/request_reply/request_reply_profile.xml @@ -0,0 +1,54 @@ + + + + + 77 + + request_reply_participant + + + + + + TRANSIENT_LOCAL + + + RELIABLE + + + + + KEEP_LAST + 10000 + + + 10000 + 100 + 100 + + + + + + + + TRANSIENT_LOCAL + + + RELIABLE + + + + + KEEP_LAST + 10000 + + + 10000 + 100 + 100 + + + + + diff --git a/examples/cpp/request_reply/types/Calculator.hpp b/examples/cpp/request_reply/types/Calculator.hpp new file mode 100644 index 00000000000..56d1f977813 --- /dev/null +++ b/examples/cpp/request_reply/types/Calculator.hpp @@ -0,0 +1,482 @@ +// 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 Calculator.hpp + * This header file contains the declaration of the described types in the IDL file. + * + * This file was generated by the tool fastddsgen. + */ + +#ifndef FAST_DDS_GENERATED__CALCULATOR_HPP +#define FAST_DDS_GENERATED__CALCULATOR_HPP + +#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(CALCULATOR_SOURCE) +#define CALCULATOR_DllAPI __declspec( dllexport ) +#else +#define CALCULATOR_DllAPI __declspec( dllimport ) +#endif // CALCULATOR_SOURCE +#else +#define CALCULATOR_DllAPI +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define CALCULATOR_DllAPI +#endif // _WIN32 + +/*! + * @brief This class represents the enumeration CalculatorOperationType defined by the user in the IDL file. + * @ingroup Calculator + */ +enum class CalculatorOperationType : int32_t +{ + ADDITION, + SUBTRACTION, + MULTIPLICATION, + DIVISION +}; +/*! + * @brief This class represents the structure CalculatorRequestType defined by the user in the IDL file. + * @ingroup Calculator + */ +class CalculatorRequestType +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport CalculatorRequestType() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~CalculatorRequestType() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object CalculatorRequestType that will be copied. + */ + eProsima_user_DllExport CalculatorRequestType( + const CalculatorRequestType& x) + { + m_client_id = x.m_client_id; + + m_operation = x.m_operation; + + m_x = x.m_x; + + m_y = x.m_y; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object CalculatorRequestType that will be copied. + */ + eProsima_user_DllExport CalculatorRequestType( + CalculatorRequestType&& x) noexcept + { + m_client_id = std::move(x.m_client_id); + m_operation = x.m_operation; + m_x = x.m_x; + m_y = x.m_y; + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object CalculatorRequestType that will be copied. + */ + eProsima_user_DllExport CalculatorRequestType& operator =( + const CalculatorRequestType& x) + { + + m_client_id = x.m_client_id; + + m_operation = x.m_operation; + + m_x = x.m_x; + + m_y = x.m_y; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object CalculatorRequestType that will be copied. + */ + eProsima_user_DllExport CalculatorRequestType& operator =( + CalculatorRequestType&& x) noexcept + { + + m_client_id = std::move(x.m_client_id); + m_operation = x.m_operation; + m_x = x.m_x; + m_y = x.m_y; + return *this; + } + + /*! + * @brief Comparison operator. + * @param x CalculatorRequestType object to compare. + */ + eProsima_user_DllExport bool operator ==( + const CalculatorRequestType& x) const + { + return (m_client_id == x.m_client_id && + m_operation == x.m_operation && + m_x == x.m_x && + m_y == x.m_y); + } + + /*! + * @brief Comparison operator. + * @param x CalculatorRequestType object to compare. + */ + eProsima_user_DllExport bool operator !=( + const CalculatorRequestType& x) const + { + return !(*this == x); + } + + /*! + * @brief This function copies the value in member client_id + * @param _client_id New value to be copied in member client_id + */ + eProsima_user_DllExport void client_id( + const std::string& _client_id) + { + m_client_id = _client_id; + } + + /*! + * @brief This function moves the value in member client_id + * @param _client_id New value to be moved in member client_id + */ + eProsima_user_DllExport void client_id( + std::string&& _client_id) + { + m_client_id = std::move(_client_id); + } + + /*! + * @brief This function returns a constant reference to member client_id + * @return Constant reference to member client_id + */ + eProsima_user_DllExport const std::string& client_id() const + { + return m_client_id; + } + + /*! + * @brief This function returns a reference to member client_id + * @return Reference to member client_id + */ + eProsima_user_DllExport std::string& client_id() + { + return m_client_id; + } + + + /*! + * @brief This function sets a value in member operation + * @param _operation New value for member operation + */ + eProsima_user_DllExport void operation( + CalculatorOperationType _operation) + { + m_operation = _operation; + } + + /*! + * @brief This function returns the value of member operation + * @return Value of member operation + */ + eProsima_user_DllExport CalculatorOperationType operation() const + { + return m_operation; + } + + /*! + * @brief This function returns a reference to member operation + * @return Reference to member operation + */ + eProsima_user_DllExport CalculatorOperationType& operation() + { + return m_operation; + } + + + /*! + * @brief This function sets a value in member x + * @param _x New value for member x + */ + eProsima_user_DllExport void x( + int16_t _x) + { + m_x = _x; + } + + /*! + * @brief This function returns the value of member x + * @return Value of member x + */ + eProsima_user_DllExport int16_t x() const + { + return m_x; + } + + /*! + * @brief This function returns a reference to member x + * @return Reference to member x + */ + eProsima_user_DllExport int16_t& x() + { + return m_x; + } + + + /*! + * @brief This function sets a value in member y + * @param _y New value for member y + */ + eProsima_user_DllExport void y( + int16_t _y) + { + m_y = _y; + } + + /*! + * @brief This function returns the value of member y + * @return Value of member y + */ + eProsima_user_DllExport int16_t y() const + { + return m_y; + } + + /*! + * @brief This function returns a reference to member y + * @return Reference to member y + */ + eProsima_user_DllExport int16_t& y() + { + return m_y; + } + + + +private: + + std::string m_client_id; + CalculatorOperationType m_operation{CalculatorOperationType::ADDITION}; + int16_t m_x{0}; + int16_t m_y{0}; + +}; +/*! + * @brief This class represents the structure CalculatorReplyType defined by the user in the IDL file. + * @ingroup Calculator + */ +class CalculatorReplyType +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport CalculatorReplyType() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~CalculatorReplyType() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object CalculatorReplyType that will be copied. + */ + eProsima_user_DllExport CalculatorReplyType( + const CalculatorReplyType& x) + { + m_client_id = x.m_client_id; + + m_result = x.m_result; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object CalculatorReplyType that will be copied. + */ + eProsima_user_DllExport CalculatorReplyType( + CalculatorReplyType&& x) noexcept + { + m_client_id = std::move(x.m_client_id); + m_result = x.m_result; + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object CalculatorReplyType that will be copied. + */ + eProsima_user_DllExport CalculatorReplyType& operator =( + const CalculatorReplyType& x) + { + + m_client_id = x.m_client_id; + + m_result = x.m_result; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object CalculatorReplyType that will be copied. + */ + eProsima_user_DllExport CalculatorReplyType& operator =( + CalculatorReplyType&& x) noexcept + { + + m_client_id = std::move(x.m_client_id); + m_result = x.m_result; + return *this; + } + + /*! + * @brief Comparison operator. + * @param x CalculatorReplyType object to compare. + */ + eProsima_user_DllExport bool operator ==( + const CalculatorReplyType& x) const + { + return (m_client_id == x.m_client_id && + m_result == x.m_result); + } + + /*! + * @brief Comparison operator. + * @param x CalculatorReplyType object to compare. + */ + eProsima_user_DllExport bool operator !=( + const CalculatorReplyType& x) const + { + return !(*this == x); + } + + /*! + * @brief This function copies the value in member client_id + * @param _client_id New value to be copied in member client_id + */ + eProsima_user_DllExport void client_id( + const std::string& _client_id) + { + m_client_id = _client_id; + } + + /*! + * @brief This function moves the value in member client_id + * @param _client_id New value to be moved in member client_id + */ + eProsima_user_DllExport void client_id( + std::string&& _client_id) + { + m_client_id = std::move(_client_id); + } + + /*! + * @brief This function returns a constant reference to member client_id + * @return Constant reference to member client_id + */ + eProsima_user_DllExport const std::string& client_id() const + { + return m_client_id; + } + + /*! + * @brief This function returns a reference to member client_id + * @return Reference to member client_id + */ + eProsima_user_DllExport std::string& client_id() + { + return m_client_id; + } + + + /*! + * @brief This function sets a value in member result + * @param _result New value for member result + */ + eProsima_user_DllExport void result( + int32_t _result) + { + m_result = _result; + } + + /*! + * @brief This function returns the value of member result + * @return Value of member result + */ + eProsima_user_DllExport int32_t result() const + { + return m_result; + } + + /*! + * @brief This function returns a reference to member result + * @return Reference to member result + */ + eProsima_user_DllExport int32_t& result() + { + return m_result; + } + + + +private: + + std::string m_client_id; + int32_t m_result{0}; + +}; + +#endif // _FAST_DDS_GENERATED_CALCULATOR_HPP_ + + diff --git a/examples/cpp/request_reply/types/Calculator.idl b/examples/cpp/request_reply/types/Calculator.idl new file mode 100644 index 00000000000..b3d5eda203d --- /dev/null +++ b/examples/cpp/request_reply/types/Calculator.idl @@ -0,0 +1,24 @@ +@extensibility(APPENDABLE) +enum CalculatorOperationType +{ + ADDITION, + SUBTRACTION, + MULTIPLICATION, + DIVISION +}; + +@extensibility(APPENDABLE) +struct CalculatorRequestType +{ + @key string client_id; + CalculatorOperationType operation; + short x; + short y; +}; + +@extensibility(APPENDABLE) +struct CalculatorReplyType +{ + @key string client_id; + long result; +}; diff --git a/examples/cpp/dds/RequestReplyExample/CalculatorCdrAux.hpp b/examples/cpp/request_reply/types/CalculatorCdrAux.hpp similarity index 77% rename from examples/cpp/dds/RequestReplyExample/CalculatorCdrAux.hpp rename to examples/cpp/request_reply/types/CalculatorCdrAux.hpp index a9afaef8f47..1fefda14869 100644 --- a/examples/cpp/dds/RequestReplyExample/CalculatorCdrAux.hpp +++ b/examples/cpp/request_reply/types/CalculatorCdrAux.hpp @@ -24,11 +24,12 @@ #include "Calculator.hpp" -constexpr uint32_t ReplyType_max_cdr_typesize {16UL}; -constexpr uint32_t ReplyType_max_key_cdr_typesize {0UL}; +constexpr uint32_t CalculatorRequestType_max_cdr_typesize {272UL}; +constexpr uint32_t CalculatorRequestType_max_key_cdr_typesize {260UL}; + +constexpr uint32_t CalculatorReplyType_max_cdr_typesize {268UL}; +constexpr uint32_t CalculatorReplyType_max_key_cdr_typesize {260UL}; -constexpr uint32_t RequestType_max_cdr_typesize {16UL}; -constexpr uint32_t RequestType_max_key_cdr_typesize {0UL}; namespace eprosima { @@ -39,11 +40,11 @@ class CdrSizeCalculator; eProsima_user_DllExport void serialize_key( eprosima::fastcdr::Cdr& scdr, - const RequestType& data); + const CalculatorRequestType& data); eProsima_user_DllExport void serialize_key( eprosima::fastcdr::Cdr& scdr, - const ReplyType& data); + const CalculatorReplyType& data); } // namespace fastcdr diff --git a/examples/cpp/dds/RequestReplyExample/CalculatorCdrAux.ipp b/examples/cpp/request_reply/types/CalculatorCdrAux.ipp similarity index 80% rename from examples/cpp/dds/RequestReplyExample/CalculatorCdrAux.ipp rename to examples/cpp/request_reply/types/CalculatorCdrAux.ipp index d46633a3c92..7b1ddfc73b9 100644 --- a/examples/cpp/dds/RequestReplyExample/CalculatorCdrAux.ipp +++ b/examples/cpp/request_reply/types/CalculatorCdrAux.ipp @@ -37,7 +37,7 @@ namespace fastcdr { template<> eProsima_user_DllExport size_t calculate_serialized_size( eprosima::fastcdr::CdrSizeCalculator& calculator, - const RequestType& data, + const CalculatorRequestType& data, size_t& current_alignment) { static_cast(data); @@ -51,12 +51,15 @@ eProsima_user_DllExport size_t calculate_serialized_size( calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), - data.operation(), current_alignment); + data.client_id(), current_alignment); calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), - data.x(), current_alignment); + data.operation(), current_alignment); calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(2), + data.x(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(3), data.y(), current_alignment); @@ -68,7 +71,7 @@ eProsima_user_DllExport size_t calculate_serialized_size( template<> eProsima_user_DllExport void serialize( eprosima::fastcdr::Cdr& scdr, - const RequestType& data) + const CalculatorRequestType& data) { eprosima::fastcdr::Cdr::state current_state(scdr); scdr.begin_serialize_type(current_state, @@ -77,9 +80,10 @@ eProsima_user_DllExport void serialize( eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); scdr - << eprosima::fastcdr::MemberId(0) << data.operation() - << eprosima::fastcdr::MemberId(1) << data.x() - << eprosima::fastcdr::MemberId(2) << data.y() + << eprosima::fastcdr::MemberId(0) << data.client_id() + << eprosima::fastcdr::MemberId(1) << data.operation() + << eprosima::fastcdr::MemberId(2) << data.x() + << eprosima::fastcdr::MemberId(3) << data.y() ; scdr.end_serialize_type(current_state); } @@ -87,7 +91,7 @@ eProsima_user_DllExport void serialize( template<> eProsima_user_DllExport void deserialize( eprosima::fastcdr::Cdr& cdr, - RequestType& data) + CalculatorRequestType& data) { cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : @@ -98,14 +102,18 @@ eProsima_user_DllExport void deserialize( switch (mid.id) { case 0: - dcdr >> data.operation(); + dcdr >> data.client_id(); break; case 1: - dcdr >> data.x(); + dcdr >> data.operation(); break; case 2: + dcdr >> data.x(); + break; + + case 3: dcdr >> data.y(); break; @@ -119,17 +127,22 @@ eProsima_user_DllExport void deserialize( void serialize_key( eprosima::fastcdr::Cdr& scdr, - const RequestType& data) + const CalculatorRequestType& data) { static_cast(scdr); static_cast(data); + scdr << data.client_id(); + + + + } template<> eProsima_user_DllExport size_t calculate_serialized_size( eprosima::fastcdr::CdrSizeCalculator& calculator, - const ReplyType& data, + const CalculatorReplyType& data, size_t& current_alignment) { static_cast(data); @@ -143,7 +156,10 @@ eProsima_user_DllExport size_t calculate_serialized_size( calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), - data.z(), current_alignment); + data.client_id(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.result(), current_alignment); calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); @@ -154,7 +170,7 @@ eProsima_user_DllExport size_t calculate_serialized_size( template<> eProsima_user_DllExport void serialize( eprosima::fastcdr::Cdr& scdr, - const ReplyType& data) + const CalculatorReplyType& data) { eprosima::fastcdr::Cdr::state current_state(scdr); scdr.begin_serialize_type(current_state, @@ -163,7 +179,8 @@ eProsima_user_DllExport void serialize( eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); scdr - << eprosima::fastcdr::MemberId(0) << data.z() + << eprosima::fastcdr::MemberId(0) << data.client_id() + << eprosima::fastcdr::MemberId(1) << data.result() ; scdr.end_serialize_type(current_state); } @@ -171,7 +188,7 @@ eProsima_user_DllExport void serialize( template<> eProsima_user_DllExport void deserialize( eprosima::fastcdr::Cdr& cdr, - ReplyType& data) + CalculatorReplyType& data) { cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : @@ -182,7 +199,11 @@ eProsima_user_DllExport void deserialize( switch (mid.id) { case 0: - dcdr >> data.z(); + dcdr >> data.client_id(); + break; + + case 1: + dcdr >> data.result(); break; default: @@ -195,10 +216,13 @@ eProsima_user_DllExport void deserialize( void serialize_key( eprosima::fastcdr::Cdr& scdr, - const ReplyType& data) + const CalculatorReplyType& data) { static_cast(scdr); static_cast(data); + scdr << data.client_id(); + + } diff --git a/examples/cpp/dds/RequestReplyExample/CalculatorPubSubTypes.cxx b/examples/cpp/request_reply/types/CalculatorPubSubTypes.cxx similarity index 78% rename from examples/cpp/dds/RequestReplyExample/CalculatorPubSubTypes.cxx rename to examples/cpp/request_reply/types/CalculatorPubSubTypes.cxx index 754dcb694bf..28b085a472b 100644 --- a/examples/cpp/dds/RequestReplyExample/CalculatorPubSubTypes.cxx +++ b/examples/cpp/request_reply/types/CalculatorPubSubTypes.cxx @@ -31,24 +31,24 @@ using SerializedPayload_t = eprosima::fastdds::rtps::SerializedPayload_t; using InstanceHandle_t = eprosima::fastdds::rtps::InstanceHandle_t; using DataRepresentationId_t = eprosima::fastdds::dds::DataRepresentationId_t; -RequestTypePubSubType::RequestTypePubSubType() +CalculatorRequestTypePubSubType::CalculatorRequestTypePubSubType() { - setName("RequestType"); + setName("CalculatorRequestType"); uint32_t type_size = #if FASTCDR_VERSION_MAJOR == 1 - static_cast(RequestType::getMaxCdrSerializedSize()); + static_cast(CalculatorRequestType::getMaxCdrSerializedSize()); #else - RequestType_max_cdr_typesize; + CalculatorRequestType_max_cdr_typesize; #endif type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ m_typeSize = type_size + 4; /*encapsulation*/ - m_isGetKeyDefined = false; - uint32_t keyLength = RequestType_max_key_cdr_typesize > 16 ? RequestType_max_key_cdr_typesize : 16; + m_isGetKeyDefined = true; + uint32_t keyLength = CalculatorRequestType_max_key_cdr_typesize > 16 ? CalculatorRequestType_max_key_cdr_typesize : 16; m_keyBuffer = reinterpret_cast(malloc(keyLength)); memset(m_keyBuffer, 0, keyLength); } -RequestTypePubSubType::~RequestTypePubSubType() +CalculatorRequestTypePubSubType::~CalculatorRequestTypePubSubType() { if (m_keyBuffer != nullptr) { @@ -56,12 +56,12 @@ RequestTypePubSubType::~RequestTypePubSubType() } } -bool RequestTypePubSubType::serialize( +bool CalculatorRequestTypePubSubType::serialize( const void* const data, SerializedPayload_t* payload, DataRepresentationId_t data_representation) { - const RequestType* p_type = static_cast(data); + const CalculatorRequestType* p_type = static_cast(data); // Object that manages the raw buffer. eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload->data), payload->max_size); @@ -98,14 +98,14 @@ bool RequestTypePubSubType::serialize( return true; } -bool RequestTypePubSubType::deserialize( +bool CalculatorRequestTypePubSubType::deserialize( SerializedPayload_t* payload, void* data) { try { // Convert DATA to pointer of your type - RequestType* p_type = static_cast(data); + CalculatorRequestType* p_type = static_cast(data); // Object that manages the raw buffer. eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload->data), payload->length); @@ -132,7 +132,7 @@ bool RequestTypePubSubType::deserialize( return true; } -std::function RequestTypePubSubType::getSerializedSizeProvider( +std::function CalculatorRequestTypePubSubType::getSerializedSizeProvider( const void* const data, DataRepresentationId_t data_representation) { @@ -140,7 +140,7 @@ std::function RequestTypePubSubType::getSerializedSizeProvider( { #if FASTCDR_VERSION_MAJOR == 1 static_cast(data_representation); - return static_cast(type::getCdrSerializedSize(*static_cast(data))) + + return static_cast(type::getCdrSerializedSize(*static_cast(data))) + 4u /*encapsulation*/; #else try @@ -150,7 +150,7 @@ std::function RequestTypePubSubType::getSerializedSizeProvider( eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); size_t current_alignment {0}; return static_cast(calculator.calculate_serialized_size( - *static_cast(data), current_alignment)) + + *static_cast(data), current_alignment)) + 4u /*encapsulation*/; } catch (eprosima::fastcdr::exception::Exception& /*exception*/) @@ -161,18 +161,18 @@ std::function RequestTypePubSubType::getSerializedSizeProvider( }; } -void* RequestTypePubSubType::createData() +void* CalculatorRequestTypePubSubType::createData() { - return reinterpret_cast(new RequestType()); + return reinterpret_cast(new CalculatorRequestType()); } -void RequestTypePubSubType::deleteData( +void CalculatorRequestTypePubSubType::deleteData( void* data) { - delete(reinterpret_cast(data)); + delete(reinterpret_cast(data)); } -bool RequestTypePubSubType::getKey( +bool CalculatorRequestTypePubSubType::getKey( const void* const data, InstanceHandle_t* handle, bool force_md5) @@ -182,11 +182,11 @@ bool RequestTypePubSubType::getKey( return false; } - const RequestType* p_type = static_cast(data); + const CalculatorRequestType* p_type = static_cast(data); // Object that manages the raw buffer. eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(m_keyBuffer), - RequestType_max_key_cdr_typesize); + CalculatorRequestType_max_key_cdr_typesize); // Object that serializes the data. eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::BIG_ENDIANNESS, eprosima::fastcdr::CdrVersion::XCDRv1); @@ -195,7 +195,7 @@ bool RequestTypePubSubType::getKey( #else eprosima::fastcdr::serialize_key(ser, *p_type); #endif // FASTCDR_VERSION_MAJOR == 1 - if (force_md5 || RequestType_max_key_cdr_typesize > 16) + if (force_md5 || CalculatorRequestType_max_key_cdr_typesize > 16) { m_md5.init(); #if FASTCDR_VERSION_MAJOR == 1 @@ -219,29 +219,29 @@ bool RequestTypePubSubType::getKey( return true; } -void RequestTypePubSubType::register_type_object_representation() +void CalculatorRequestTypePubSubType::register_type_object_representation() { - register_RequestType_type_identifier(type_identifiers_); + register_CalculatorRequestType_type_identifier(type_identifiers_); } -ReplyTypePubSubType::ReplyTypePubSubType() +CalculatorReplyTypePubSubType::CalculatorReplyTypePubSubType() { - setName("ReplyType"); + setName("CalculatorReplyType"); uint32_t type_size = #if FASTCDR_VERSION_MAJOR == 1 - static_cast(ReplyType::getMaxCdrSerializedSize()); + static_cast(CalculatorReplyType::getMaxCdrSerializedSize()); #else - ReplyType_max_cdr_typesize; + CalculatorReplyType_max_cdr_typesize; #endif type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ m_typeSize = type_size + 4; /*encapsulation*/ - m_isGetKeyDefined = false; - uint32_t keyLength = ReplyType_max_key_cdr_typesize > 16 ? ReplyType_max_key_cdr_typesize : 16; + m_isGetKeyDefined = true; + uint32_t keyLength = CalculatorReplyType_max_key_cdr_typesize > 16 ? CalculatorReplyType_max_key_cdr_typesize : 16; m_keyBuffer = reinterpret_cast(malloc(keyLength)); memset(m_keyBuffer, 0, keyLength); } -ReplyTypePubSubType::~ReplyTypePubSubType() +CalculatorReplyTypePubSubType::~CalculatorReplyTypePubSubType() { if (m_keyBuffer != nullptr) { @@ -249,12 +249,12 @@ ReplyTypePubSubType::~ReplyTypePubSubType() } } -bool ReplyTypePubSubType::serialize( +bool CalculatorReplyTypePubSubType::serialize( const void* const data, SerializedPayload_t* payload, DataRepresentationId_t data_representation) { - const ReplyType* p_type = static_cast(data); + const CalculatorReplyType* p_type = static_cast(data); // Object that manages the raw buffer. eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload->data), payload->max_size); @@ -291,14 +291,14 @@ bool ReplyTypePubSubType::serialize( return true; } -bool ReplyTypePubSubType::deserialize( +bool CalculatorReplyTypePubSubType::deserialize( SerializedPayload_t* payload, void* data) { try { // Convert DATA to pointer of your type - ReplyType* p_type = static_cast(data); + CalculatorReplyType* p_type = static_cast(data); // Object that manages the raw buffer. eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload->data), payload->length); @@ -325,7 +325,7 @@ bool ReplyTypePubSubType::deserialize( return true; } -std::function ReplyTypePubSubType::getSerializedSizeProvider( +std::function CalculatorReplyTypePubSubType::getSerializedSizeProvider( const void* const data, DataRepresentationId_t data_representation) { @@ -333,7 +333,7 @@ std::function ReplyTypePubSubType::getSerializedSizeProvider( { #if FASTCDR_VERSION_MAJOR == 1 static_cast(data_representation); - return static_cast(type::getCdrSerializedSize(*static_cast(data))) + + return static_cast(type::getCdrSerializedSize(*static_cast(data))) + 4u /*encapsulation*/; #else try @@ -343,7 +343,7 @@ std::function ReplyTypePubSubType::getSerializedSizeProvider( eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); size_t current_alignment {0}; return static_cast(calculator.calculate_serialized_size( - *static_cast(data), current_alignment)) + + *static_cast(data), current_alignment)) + 4u /*encapsulation*/; } catch (eprosima::fastcdr::exception::Exception& /*exception*/) @@ -354,18 +354,18 @@ std::function ReplyTypePubSubType::getSerializedSizeProvider( }; } -void* ReplyTypePubSubType::createData() +void* CalculatorReplyTypePubSubType::createData() { - return reinterpret_cast(new ReplyType()); + return reinterpret_cast(new CalculatorReplyType()); } -void ReplyTypePubSubType::deleteData( +void CalculatorReplyTypePubSubType::deleteData( void* data) { - delete(reinterpret_cast(data)); + delete(reinterpret_cast(data)); } -bool ReplyTypePubSubType::getKey( +bool CalculatorReplyTypePubSubType::getKey( const void* const data, InstanceHandle_t* handle, bool force_md5) @@ -375,11 +375,11 @@ bool ReplyTypePubSubType::getKey( return false; } - const ReplyType* p_type = static_cast(data); + const CalculatorReplyType* p_type = static_cast(data); // Object that manages the raw buffer. eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(m_keyBuffer), - ReplyType_max_key_cdr_typesize); + CalculatorReplyType_max_key_cdr_typesize); // Object that serializes the data. eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::BIG_ENDIANNESS, eprosima::fastcdr::CdrVersion::XCDRv1); @@ -388,7 +388,7 @@ bool ReplyTypePubSubType::getKey( #else eprosima::fastcdr::serialize_key(ser, *p_type); #endif // FASTCDR_VERSION_MAJOR == 1 - if (force_md5 || ReplyType_max_key_cdr_typesize > 16) + if (force_md5 || CalculatorReplyType_max_key_cdr_typesize > 16) { m_md5.init(); #if FASTCDR_VERSION_MAJOR == 1 @@ -412,9 +412,9 @@ bool ReplyTypePubSubType::getKey( return true; } -void ReplyTypePubSubType::register_type_object_representation() +void CalculatorReplyTypePubSubType::register_type_object_representation() { - register_ReplyType_type_identifier(type_identifiers_); + register_CalculatorReplyType_type_identifier(type_identifiers_); } diff --git a/examples/cpp/dds/RequestReplyExample/CalculatorPubSubTypes.hpp b/examples/cpp/request_reply/types/CalculatorPubSubTypes.hpp similarity index 89% rename from examples/cpp/dds/RequestReplyExample/CalculatorPubSubTypes.hpp rename to examples/cpp/request_reply/types/CalculatorPubSubTypes.hpp index 99aaac7df59..946ca0be8a8 100644 --- a/examples/cpp/dds/RequestReplyExample/CalculatorPubSubTypes.hpp +++ b/examples/cpp/request_reply/types/CalculatorPubSubTypes.hpp @@ -39,18 +39,18 @@ /*! - * @brief This class represents the TopicDataType of the type RequestType defined by the user in the IDL file. + * @brief This class represents the TopicDataType of the type CalculatorRequestType defined by the user in the IDL file. * @ingroup Calculator */ -class RequestTypePubSubType : public eprosima::fastdds::dds::TopicDataType +class CalculatorRequestTypePubSubType : public eprosima::fastdds::dds::TopicDataType { public: - typedef RequestType type; + typedef CalculatorRequestType type; - eProsima_user_DllExport RequestTypePubSubType(); + eProsima_user_DllExport CalculatorRequestTypePubSubType(); - eProsima_user_DllExport ~RequestTypePubSubType() override; + eProsima_user_DllExport ~CalculatorRequestTypePubSubType() override; eProsima_user_DllExport bool serialize( const void* const data, @@ -94,7 +94,7 @@ class RequestTypePubSubType : public eprosima::fastdds::dds::TopicDataType #ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED eProsima_user_DllExport inline bool is_bounded() const override { - return true; + return false; } #endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED @@ -130,18 +130,18 @@ class RequestTypePubSubType : public eprosima::fastdds::dds::TopicDataType }; /*! - * @brief This class represents the TopicDataType of the type ReplyType defined by the user in the IDL file. + * @brief This class represents the TopicDataType of the type CalculatorReplyType defined by the user in the IDL file. * @ingroup Calculator */ -class ReplyTypePubSubType : public eprosima::fastdds::dds::TopicDataType +class CalculatorReplyTypePubSubType : public eprosima::fastdds::dds::TopicDataType { public: - typedef ReplyType type; + typedef CalculatorReplyType type; - eProsima_user_DllExport ReplyTypePubSubType(); + eProsima_user_DllExport CalculatorReplyTypePubSubType(); - eProsima_user_DllExport ~ReplyTypePubSubType() override; + eProsima_user_DllExport ~CalculatorReplyTypePubSubType() override; eProsima_user_DllExport bool serialize( const void* const data, @@ -185,7 +185,7 @@ class ReplyTypePubSubType : public eprosima::fastdds::dds::TopicDataType #ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED eProsima_user_DllExport inline bool is_bounded() const override { - return true; + return false; } #endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED diff --git a/examples/cpp/request_reply/types/CalculatorTypeObjectSupport.cxx b/examples/cpp/request_reply/types/CalculatorTypeObjectSupport.cxx new file mode 100644 index 00000000000..737382899f1 --- /dev/null +++ b/examples/cpp/request_reply/types/CalculatorTypeObjectSupport.cxx @@ -0,0 +1,407 @@ +// 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 CalculatorTypeObjectSupport.cxx + * Source file containing the implementation to register the TypeObject representation of the described types in the IDL file + * + * This file was generated by the tool fastddsgen. + */ + +#include "CalculatorTypeObjectSupport.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Calculator.hpp" + + +using namespace eprosima::fastdds::dds::xtypes; + +void register_CalculatorOperationType_type_identifier( + TypeIdentifierPair& type_ids_CalculatorOperationType) +{ + ReturnCode_t return_code_CalculatorOperationType {eprosima::fastdds::dds::RETCODE_OK}; + return_code_CalculatorOperationType = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "CalculatorOperationType", type_ids_CalculatorOperationType); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_CalculatorOperationType) + { + EnumTypeFlag enum_flags_CalculatorOperationType = 0; + BitBound bit_bound_CalculatorOperationType = 32; + CommonEnumeratedHeader common_CalculatorOperationType = TypeObjectUtils::build_common_enumerated_header(bit_bound_CalculatorOperationType); + QualifiedTypeName type_name_CalculatorOperationType = "CalculatorOperationType"; + eprosima::fastcdr::optional type_ann_builtin_CalculatorOperationType; + eprosima::fastcdr::optional ann_custom_CalculatorOperationType; + AppliedAnnotationSeq tmp_ann_custom_CalculatorOperationType; + eprosima::fastcdr::optional verbatim_CalculatorOperationType; + if (!tmp_ann_custom_CalculatorOperationType.empty()) + { + ann_custom_CalculatorOperationType = tmp_ann_custom_CalculatorOperationType; + } + + CompleteTypeDetail detail_CalculatorOperationType = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_CalculatorOperationType, ann_custom_CalculatorOperationType, type_name_CalculatorOperationType.to_string()); + CompleteEnumeratedHeader header_CalculatorOperationType = TypeObjectUtils::build_complete_enumerated_header(common_CalculatorOperationType, detail_CalculatorOperationType); + CompleteEnumeratedLiteralSeq literal_seq_CalculatorOperationType; + { + EnumeratedLiteralFlag flags_ADDITION = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_ADDITION = TypeObjectUtils::build_common_enumerated_literal(0, flags_ADDITION); + eprosima::fastcdr::optional member_ann_builtin_ADDITION; + ann_custom_CalculatorOperationType.reset(); + MemberName name_ADDITION = "ADDITION"; + CompleteMemberDetail detail_ADDITION = TypeObjectUtils::build_complete_member_detail(name_ADDITION, member_ann_builtin_ADDITION, ann_custom_CalculatorOperationType); + CompleteEnumeratedLiteral literal_ADDITION = TypeObjectUtils::build_complete_enumerated_literal(common_ADDITION, detail_ADDITION); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_CalculatorOperationType, literal_ADDITION); + } + { + EnumeratedLiteralFlag flags_SUBTRACTION = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_SUBTRACTION = TypeObjectUtils::build_common_enumerated_literal(1, flags_SUBTRACTION); + eprosima::fastcdr::optional member_ann_builtin_SUBTRACTION; + ann_custom_CalculatorOperationType.reset(); + MemberName name_SUBTRACTION = "SUBTRACTION"; + CompleteMemberDetail detail_SUBTRACTION = TypeObjectUtils::build_complete_member_detail(name_SUBTRACTION, member_ann_builtin_SUBTRACTION, ann_custom_CalculatorOperationType); + CompleteEnumeratedLiteral literal_SUBTRACTION = TypeObjectUtils::build_complete_enumerated_literal(common_SUBTRACTION, detail_SUBTRACTION); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_CalculatorOperationType, literal_SUBTRACTION); + } + { + EnumeratedLiteralFlag flags_MULTIPLICATION = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_MULTIPLICATION = TypeObjectUtils::build_common_enumerated_literal(2, flags_MULTIPLICATION); + eprosima::fastcdr::optional member_ann_builtin_MULTIPLICATION; + ann_custom_CalculatorOperationType.reset(); + MemberName name_MULTIPLICATION = "MULTIPLICATION"; + CompleteMemberDetail detail_MULTIPLICATION = TypeObjectUtils::build_complete_member_detail(name_MULTIPLICATION, member_ann_builtin_MULTIPLICATION, ann_custom_CalculatorOperationType); + CompleteEnumeratedLiteral literal_MULTIPLICATION = TypeObjectUtils::build_complete_enumerated_literal(common_MULTIPLICATION, detail_MULTIPLICATION); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_CalculatorOperationType, literal_MULTIPLICATION); + } + { + EnumeratedLiteralFlag flags_DIVISION = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_DIVISION = TypeObjectUtils::build_common_enumerated_literal(3, flags_DIVISION); + eprosima::fastcdr::optional member_ann_builtin_DIVISION; + ann_custom_CalculatorOperationType.reset(); + MemberName name_DIVISION = "DIVISION"; + CompleteMemberDetail detail_DIVISION = TypeObjectUtils::build_complete_member_detail(name_DIVISION, member_ann_builtin_DIVISION, ann_custom_CalculatorOperationType); + CompleteEnumeratedLiteral literal_DIVISION = TypeObjectUtils::build_complete_enumerated_literal(common_DIVISION, detail_DIVISION); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_CalculatorOperationType, literal_DIVISION); + } + CompleteEnumeratedType enumerated_type_CalculatorOperationType = TypeObjectUtils::build_complete_enumerated_type(enum_flags_CalculatorOperationType, header_CalculatorOperationType, + literal_seq_CalculatorOperationType); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_enumerated_type_object(enumerated_type_CalculatorOperationType, type_name_CalculatorOperationType.to_string(), type_ids_CalculatorOperationType)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "CalculatorOperationType already registered in TypeObjectRegistry for a different type."); + } + } +}// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_CalculatorRequestType_type_identifier( + TypeIdentifierPair& type_ids_CalculatorRequestType) +{ + + ReturnCode_t return_code_CalculatorRequestType {eprosima::fastdds::dds::RETCODE_OK}; + return_code_CalculatorRequestType = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "CalculatorRequestType", type_ids_CalculatorRequestType); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_CalculatorRequestType) + { + StructTypeFlag struct_flags_CalculatorRequestType = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_CalculatorRequestType = "CalculatorRequestType"; + eprosima::fastcdr::optional type_ann_builtin_CalculatorRequestType; + eprosima::fastcdr::optional ann_custom_CalculatorRequestType; + AppliedAnnotationSeq tmp_ann_custom_CalculatorRequestType; + eprosima::fastcdr::optional verbatim_CalculatorRequestType; + if (!tmp_ann_custom_CalculatorRequestType.empty()) + { + ann_custom_CalculatorRequestType = tmp_ann_custom_CalculatorRequestType; + } + + CompleteTypeDetail detail_CalculatorRequestType = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_CalculatorRequestType, ann_custom_CalculatorRequestType, type_name_CalculatorRequestType.to_string()); + CompleteStructHeader header_CalculatorRequestType; + header_CalculatorRequestType = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_CalculatorRequestType); + CompleteStructMemberSeq member_seq_CalculatorRequestType; + { + TypeIdentifierPair type_ids_client_id; + ReturnCode_t return_code_client_id {eprosima::fastdds::dds::RETCODE_OK}; + return_code_client_id = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_client_id); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_client_id) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_client_id)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_client_id = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, true, false); + MemberId member_id_client_id = 0x00000000; + bool common_client_id_ec {false}; + CommonStructMember common_client_id {TypeObjectUtils::build_common_struct_member(member_id_client_id, member_flags_client_id, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_client_id, common_client_id_ec))}; + if (!common_client_id_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure client_id member TypeIdentifier inconsistent."); + return; + } + MemberName name_client_id = "client_id"; + eprosima::fastcdr::optional member_ann_builtin_client_id; + ann_custom_CalculatorRequestType.reset(); + AppliedAnnotationSeq tmp_ann_custom_client_id; + eprosima::fastcdr::optional unit_client_id; + eprosima::fastcdr::optional min_client_id; + eprosima::fastcdr::optional max_client_id; + eprosima::fastcdr::optional hash_id_client_id; + if (unit_client_id.has_value() || min_client_id.has_value() || max_client_id.has_value() || hash_id_client_id.has_value()) + { + member_ann_builtin_client_id = TypeObjectUtils::build_applied_builtin_member_annotations(unit_client_id, min_client_id, max_client_id, hash_id_client_id); + } + if (!tmp_ann_custom_client_id.empty()) + { + ann_custom_CalculatorRequestType = tmp_ann_custom_client_id; + } + CompleteMemberDetail detail_client_id = TypeObjectUtils::build_complete_member_detail(name_client_id, member_ann_builtin_client_id, ann_custom_CalculatorRequestType); + CompleteStructMember member_client_id = TypeObjectUtils::build_complete_struct_member(common_client_id, detail_client_id); + TypeObjectUtils::add_complete_struct_member(member_seq_CalculatorRequestType, member_client_id); + } + { + TypeIdentifierPair type_ids_operation; + ReturnCode_t return_code_operation {eprosima::fastdds::dds::RETCODE_OK}; + return_code_operation = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "CalculatorOperationType", type_ids_operation); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_operation) + { + ::register_CalculatorOperationType_type_identifier(type_ids_operation); + } + StructMemberFlag member_flags_operation = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_operation = 0x00000001; + bool common_operation_ec {false}; + CommonStructMember common_operation {TypeObjectUtils::build_common_struct_member(member_id_operation, member_flags_operation, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_operation, common_operation_ec))}; + if (!common_operation_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure operation member TypeIdentifier inconsistent."); + return; + } + MemberName name_operation = "operation"; + eprosima::fastcdr::optional member_ann_builtin_operation; + ann_custom_CalculatorRequestType.reset(); + CompleteMemberDetail detail_operation = TypeObjectUtils::build_complete_member_detail(name_operation, member_ann_builtin_operation, ann_custom_CalculatorRequestType); + CompleteStructMember member_operation = TypeObjectUtils::build_complete_struct_member(common_operation, detail_operation); + TypeObjectUtils::add_complete_struct_member(member_seq_CalculatorRequestType, member_operation); + } + { + TypeIdentifierPair type_ids_x; + ReturnCode_t return_code_x {eprosima::fastdds::dds::RETCODE_OK}; + return_code_x = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_int16_t", type_ids_x); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_x) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "x Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_x = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_x = 0x00000002; + bool common_x_ec {false}; + CommonStructMember common_x {TypeObjectUtils::build_common_struct_member(member_id_x, member_flags_x, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_x, common_x_ec))}; + if (!common_x_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure x member TypeIdentifier inconsistent."); + return; + } + MemberName name_x = "x"; + eprosima::fastcdr::optional member_ann_builtin_x; + ann_custom_CalculatorRequestType.reset(); + CompleteMemberDetail detail_x = TypeObjectUtils::build_complete_member_detail(name_x, member_ann_builtin_x, ann_custom_CalculatorRequestType); + CompleteStructMember member_x = TypeObjectUtils::build_complete_struct_member(common_x, detail_x); + TypeObjectUtils::add_complete_struct_member(member_seq_CalculatorRequestType, member_x); + } + { + TypeIdentifierPair type_ids_y; + ReturnCode_t return_code_y {eprosima::fastdds::dds::RETCODE_OK}; + return_code_y = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_int16_t", type_ids_y); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_y) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "y Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_y = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_y = 0x00000003; + bool common_y_ec {false}; + CommonStructMember common_y {TypeObjectUtils::build_common_struct_member(member_id_y, member_flags_y, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_y, common_y_ec))}; + if (!common_y_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure y member TypeIdentifier inconsistent."); + return; + } + MemberName name_y = "y"; + eprosima::fastcdr::optional member_ann_builtin_y; + ann_custom_CalculatorRequestType.reset(); + CompleteMemberDetail detail_y = TypeObjectUtils::build_complete_member_detail(name_y, member_ann_builtin_y, ann_custom_CalculatorRequestType); + CompleteStructMember member_y = TypeObjectUtils::build_complete_struct_member(common_y, detail_y); + TypeObjectUtils::add_complete_struct_member(member_seq_CalculatorRequestType, member_y); + } + CompleteStructType struct_type_CalculatorRequestType = TypeObjectUtils::build_complete_struct_type(struct_flags_CalculatorRequestType, header_CalculatorRequestType, member_seq_CalculatorRequestType); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_CalculatorRequestType, type_name_CalculatorRequestType.to_string(), type_ids_CalculatorRequestType)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "CalculatorRequestType already registered in TypeObjectRegistry for a different type."); + } + } +} +// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_CalculatorReplyType_type_identifier( + TypeIdentifierPair& type_ids_CalculatorReplyType) +{ + + ReturnCode_t return_code_CalculatorReplyType {eprosima::fastdds::dds::RETCODE_OK}; + return_code_CalculatorReplyType = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "CalculatorReplyType", type_ids_CalculatorReplyType); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_CalculatorReplyType) + { + StructTypeFlag struct_flags_CalculatorReplyType = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_CalculatorReplyType = "CalculatorReplyType"; + eprosima::fastcdr::optional type_ann_builtin_CalculatorReplyType; + eprosima::fastcdr::optional ann_custom_CalculatorReplyType; + AppliedAnnotationSeq tmp_ann_custom_CalculatorReplyType; + eprosima::fastcdr::optional verbatim_CalculatorReplyType; + if (!tmp_ann_custom_CalculatorReplyType.empty()) + { + ann_custom_CalculatorReplyType = tmp_ann_custom_CalculatorReplyType; + } + + CompleteTypeDetail detail_CalculatorReplyType = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_CalculatorReplyType, ann_custom_CalculatorReplyType, type_name_CalculatorReplyType.to_string()); + CompleteStructHeader header_CalculatorReplyType; + header_CalculatorReplyType = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_CalculatorReplyType); + CompleteStructMemberSeq member_seq_CalculatorReplyType; + { + TypeIdentifierPair type_ids_client_id; + ReturnCode_t return_code_client_id {eprosima::fastdds::dds::RETCODE_OK}; + return_code_client_id = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_client_id); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_client_id) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_client_id)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_client_id = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, true, false); + MemberId member_id_client_id = 0x00000000; + bool common_client_id_ec {false}; + CommonStructMember common_client_id {TypeObjectUtils::build_common_struct_member(member_id_client_id, member_flags_client_id, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_client_id, common_client_id_ec))}; + if (!common_client_id_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure client_id member TypeIdentifier inconsistent."); + return; + } + MemberName name_client_id = "client_id"; + eprosima::fastcdr::optional member_ann_builtin_client_id; + ann_custom_CalculatorReplyType.reset(); + AppliedAnnotationSeq tmp_ann_custom_client_id; + eprosima::fastcdr::optional unit_client_id; + eprosima::fastcdr::optional min_client_id; + eprosima::fastcdr::optional max_client_id; + eprosima::fastcdr::optional hash_id_client_id; + if (unit_client_id.has_value() || min_client_id.has_value() || max_client_id.has_value() || hash_id_client_id.has_value()) + { + member_ann_builtin_client_id = TypeObjectUtils::build_applied_builtin_member_annotations(unit_client_id, min_client_id, max_client_id, hash_id_client_id); + } + if (!tmp_ann_custom_client_id.empty()) + { + ann_custom_CalculatorReplyType = tmp_ann_custom_client_id; + } + CompleteMemberDetail detail_client_id = TypeObjectUtils::build_complete_member_detail(name_client_id, member_ann_builtin_client_id, ann_custom_CalculatorReplyType); + CompleteStructMember member_client_id = TypeObjectUtils::build_complete_struct_member(common_client_id, detail_client_id); + TypeObjectUtils::add_complete_struct_member(member_seq_CalculatorReplyType, member_client_id); + } + { + TypeIdentifierPair type_ids_result; + ReturnCode_t return_code_result {eprosima::fastdds::dds::RETCODE_OK}; + return_code_result = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_int32_t", type_ids_result); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_result) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "result Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_result = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_result = 0x00000001; + bool common_result_ec {false}; + CommonStructMember common_result {TypeObjectUtils::build_common_struct_member(member_id_result, member_flags_result, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_result, common_result_ec))}; + if (!common_result_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure result member TypeIdentifier inconsistent."); + return; + } + MemberName name_result = "result"; + eprosima::fastcdr::optional member_ann_builtin_result; + ann_custom_CalculatorReplyType.reset(); + CompleteMemberDetail detail_result = TypeObjectUtils::build_complete_member_detail(name_result, member_ann_builtin_result, ann_custom_CalculatorReplyType); + CompleteStructMember member_result = TypeObjectUtils::build_complete_struct_member(common_result, detail_result); + TypeObjectUtils::add_complete_struct_member(member_seq_CalculatorReplyType, member_result); + } + CompleteStructType struct_type_CalculatorReplyType = TypeObjectUtils::build_complete_struct_type(struct_flags_CalculatorReplyType, header_CalculatorReplyType, member_seq_CalculatorReplyType); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_CalculatorReplyType, type_name_CalculatorReplyType.to_string(), type_ids_CalculatorReplyType)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "CalculatorReplyType already registered in TypeObjectRegistry for a different type."); + } + } +} + diff --git a/examples/cpp/dds/RequestReplyExample/CalculatorTypeObjectSupport.hpp b/examples/cpp/request_reply/types/CalculatorTypeObjectSupport.hpp similarity index 87% rename from examples/cpp/dds/RequestReplyExample/CalculatorTypeObjectSupport.hpp rename to examples/cpp/request_reply/types/CalculatorTypeObjectSupport.hpp index d8bc006c6bf..1fb9bccf445 100644 --- a/examples/cpp/dds/RequestReplyExample/CalculatorTypeObjectSupport.hpp +++ b/examples/cpp/request_reply/types/CalculatorTypeObjectSupport.hpp @@ -38,7 +38,7 @@ #ifndef DOXYGEN_SHOULD_SKIP_THIS_PUBLIC /** - * @brief Register OperationType related TypeIdentifier. + * @brief Register CalculatorOperationType related TypeIdentifier. * Fully-descriptive TypeIdentifiers are directly registered. * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is * indirectly registered as well. @@ -47,10 +47,10 @@ * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. * Invalid TypeIdentifier is returned in case of error. */ -eProsima_user_DllExport void register_OperationType_type_identifier( +eProsima_user_DllExport void register_CalculatorOperationType_type_identifier( eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); /** - * @brief Register RequestType related TypeIdentifier. + * @brief Register CalculatorRequestType related TypeIdentifier. * Fully-descriptive TypeIdentifiers are directly registered. * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is * indirectly registered as well. @@ -59,10 +59,10 @@ eProsima_user_DllExport void register_OperationType_type_identifier( * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. * Invalid TypeIdentifier is returned in case of error. */ -eProsima_user_DllExport void register_RequestType_type_identifier( +eProsima_user_DllExport void register_CalculatorRequestType_type_identifier( eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); /** - * @brief Register ReplyType related TypeIdentifier. + * @brief Register CalculatorReplyType related TypeIdentifier. * Fully-descriptive TypeIdentifiers are directly registered. * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is * indirectly registered as well. @@ -71,7 +71,7 @@ eProsima_user_DllExport void register_RequestType_type_identifier( * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. * Invalid TypeIdentifier is returned in case of error. */ -eProsima_user_DllExport void register_ReplyType_type_identifier( +eProsima_user_DllExport void register_CalculatorReplyType_type_identifier( eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); diff --git a/include/fastdds/dds/domain/DomainParticipantFactory.hpp b/include/fastdds/dds/domain/DomainParticipantFactory.hpp index 29bca773014..a492fce1e09 100644 --- a/include/fastdds/dds/domain/DomainParticipantFactory.hpp +++ b/include/fastdds/dds/domain/DomainParticipantFactory.hpp @@ -240,6 +240,15 @@ class DomainParticipantFactory const std::string& profile_name, DomainParticipantExtendedQos& extended_qos) const; + /** + * Fills the DomainParticipantExtendedQos with the values of the default XML profile. + * + * @param extended_qos DomainParticipantExtendedQos object where the domain and qos are returned. + * @return RETCODE_OK + */ + FASTDDS_EXPORTED_API ReturnCode_t get_participant_extended_qos_from_default_profile( + DomainParticipantExtendedQos& extended_qos) const; + /** * Remove a Participant and all associated publishers and subscribers. * diff --git a/src/cpp/fastdds/domain/DomainParticipantFactory.cpp b/src/cpp/fastdds/domain/DomainParticipantFactory.cpp index 4d59024d6a7..eb6f9b65ffe 100644 --- a/src/cpp/fastdds/domain/DomainParticipantFactory.cpp +++ b/src/cpp/fastdds/domain/DomainParticipantFactory.cpp @@ -356,6 +356,15 @@ ReturnCode_t DomainParticipantFactory::get_participant_extended_qos_from_profile return RETCODE_BAD_PARAMETER; } +ReturnCode_t DomainParticipantFactory::get_participant_extended_qos_from_default_profile( + DomainParticipantExtendedQos& extended_qos) const +{ + ParticipantAttributes attr; + XMLProfileManager::getDefaultParticipantAttributes(attr); + utils::set_extended_qos_from_attributes(extended_qos, attr); + return RETCODE_OK; +} + ReturnCode_t DomainParticipantFactory::load_profiles() { // NOTE: This could be done with a bool atomic to avoid taking the mutex in most cases, however the use of diff --git a/src/cpp/fastdds/publisher/DataWriterImpl.cpp b/src/cpp/fastdds/publisher/DataWriterImpl.cpp index 5b28f70402e..2eedc826720 100644 --- a/src/cpp/fastdds/publisher/DataWriterImpl.cpp +++ b/src/cpp/fastdds/publisher/DataWriterImpl.cpp @@ -2225,6 +2225,8 @@ void DataWriterImpl::remove_reader_filter( { if (reader_filters_) { + assert(writer_); + std::lock_guard guard(writer_->getMutex()); reader_filters_->remove_reader(reader_guid); } } diff --git a/test/examples/request_reply.compose.yml b/test/examples/request_reply.compose.yml new file mode 100644 index 00000000000..d5399d18cc8 --- /dev/null +++ b/test/examples/request_reply.compose.yml @@ -0,0 +1,41 @@ +# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +version: "3" + +services: + server-client: + 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/request_reply@FILE_EXTENSION@ + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/request_reply/request_reply_profile.xml + command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/request_reply@FILE_EXTENSION@ client 2 5 & timeout --preserve-status 3 $${EXAMPLE_DIR}/request_reply@FILE_EXTENSION@ server" + + alone-client: + 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/request_reply@FILE_EXTENSION@ + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/request_reply/request_reply_profile.xml + command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/request_reply@FILE_EXTENSION@ client 67 24" diff --git a/test/examples/request_reply_isolated.compose.yml b/test/examples/request_reply_isolated.compose.yml new file mode 100644 index 00000000000..2e35faf7657 --- /dev/null +++ b/test/examples/request_reply_isolated.compose.yml @@ -0,0 +1,105 @@ +# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +version: "3" + +services: + server-1: + 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/request_reply@FILE_EXTENSION@ + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/request_reply/request_reply_profile.xml + command: @SHELL_EXECUTABLE@ -c "timeout --preserve-status 3 $${EXAMPLE_DIR}/request_reply@FILE_EXTENSION@ server" + + server-2: + 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/request_reply@FILE_EXTENSION@ + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/request_reply/request_reply_profile.xml + command: @SHELL_EXECUTABLE@ -c "timeout --preserve-status 3 $${EXAMPLE_DIR}/request_reply@FILE_EXTENSION@ server" + + client-1: + 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/request_reply@FILE_EXTENSION@ + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/request_reply/request_reply_profile.xml + command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/request_reply@FILE_EXTENSION@ client 2 5" + depends_on: + - server-1 + - server-2 + + client-2: + 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/request_reply@FILE_EXTENSION@ + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/request_reply/request_reply_profile.xml + command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/request_reply@FILE_EXTENSION@ client 67 24" + depends_on: + - server-1 + - server-2 + + client-3: + 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/request_reply@FILE_EXTENSION@ + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/request_reply/request_reply_profile.xml + command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/request_reply@FILE_EXTENSION@ client 3 2" + depends_on: + - server-1 + - server-2 + + client-4: + 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/request_reply@FILE_EXTENSION@ + FASTDDS_DEFAULT_PROFILES_FILE: @PROJECT_BINARY_DIR@/examples/cpp/request_reply/request_reply_profile.xml + command: @SHELL_EXECUTABLE@ -c "$${EXAMPLE_DIR}/request_reply@FILE_EXTENSION@ client 20 5" + depends_on: + - server-1 + - server-2 diff --git a/test/examples/test_request_reply.py b/test/examples/test_request_reply.py new file mode 100644 index 00000000000..b0941099093 --- /dev/null +++ b/test/examples/test_request_reply.py @@ -0,0 +1,88 @@ +# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +import subprocess + +def parse_response(client_responses: dict, response: str): + """.""" + # Define the regex pattern with two capturing groups + pattern = r"ID '(\d+)' with result: '(-?\d+)'" + + # Use re.search to find the first match + match = re.search(pattern, response) + + if match: + id_str = match.group(1) # First capturing group + result_int = int(match.group(2)) # Second capturing group, convert to int + + client_responses[id_str] = result_int + + return client_responses + + +def test_request_reply(): + """.""" + expected_responses = { + 'server-client-1': { + '1': 7, + '2': -3, + '3': 10, + '4': 0 + }, + 'alone-client-1': { + '1': 91, + '2': 43, + '3': 1608, + '4': 2 + }, + } + + responses = { + 'server-client-1': {}, + 'alone-client-1': {}, + } + + ret = True + out = '' + + try: + out = subprocess.check_output( + '/usr/bin/docker compose -f request_reply.compose.yml up', + stderr=subprocess.STDOUT, + shell=True, + timeout=30 + ).decode().split('\n') + + for line in out: + for client in expected_responses: + if client in line and 'Reply received from server' in line: + responses[client] = parse_response(responses[client], line) + + for client in responses: + if responses[client] != expected_responses[client]: + ret = False + print(f'ERROR: {client} expected "{expected_responses[client]}" but received "{responses[client]}"') + raise subprocess.CalledProcessError(1, '') + + except subprocess.CalledProcessError: + for l in out: + ret = False + print(l) + except subprocess.TimeoutExpired: + ret = False + print('TIMEOUT') + print(out) + + assert(ret) diff --git a/test/examples/test_request_reply_isolated.py b/test/examples/test_request_reply_isolated.py new file mode 100644 index 00000000000..4a504a2cc00 --- /dev/null +++ b/test/examples/test_request_reply_isolated.py @@ -0,0 +1,100 @@ +# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +import subprocess + +def parse_response(client_responses: dict, response: str): + """.""" + # Define the regex pattern with two capturing groups + pattern = r"ID '(\d+)' with result: '(-?\d+)'" + + # Use re.search to find the first match + match = re.search(pattern, response) + + if match: + id_str = match.group(1) # First capturing group + result_int = int(match.group(2)) # Second capturing group, convert to int + + client_responses[id_str] = result_int + + return client_responses + + +def test_request_reply_isolated(): + """.""" + expected_responses = { + 'client-1-1': { + '1': 7, + '2': -3, + '3': 10, + '4': 0 + }, + 'client-2-1': { + '1': 91, + '2': 43, + '3': 1608, + '4': 2 + }, + 'client-3-1': { + '1': 5, + '2': 1, + '3': 6, + '4': 1 + }, + 'client-4-1': { + '1': 25, + '2': 15, + '3': 100, + '4': 4 + } + } + + responses = { + 'client-1-1': {}, + 'client-2-1': {}, + 'client-3-1': {}, + 'client-4-1': {} + } + + ret = True + out = '' + + try: + out = subprocess.check_output( + '/usr/bin/docker compose -f request_reply_isolated.compose.yml up', + stderr=subprocess.STDOUT, + shell=True, + timeout=30 + ).decode().split('\n') + + for line in out: + for client in expected_responses: + if client in line and 'Reply received from server' in line: + responses[client] = parse_response(responses[client], line) + + for client in responses: + if responses[client] != expected_responses[client]: + ret = False + print(f'ERROR: {client} expected "{expected_responses[client]} but received "{responses[client]}') + raise subprocess.CalledProcessError(1, '') + + except subprocess.CalledProcessError: + for l in out: + print(l) + except subprocess.TimeoutExpired: + print('TIMEOUT') + print(out) + + assert(ret) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index d13768accb7..9aacba03bec 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -648,6 +648,22 @@ TEST(ParticipantTests, CreateDomainParticipantWithExtendedQosFromProfile) ASSERT_TRUE(DomainParticipantFactory::get_instance()->delete_participant(new_participant) == RETCODE_OK); } +/** + * This test checks that get_participant_extended_qos_from_default_profile holds the correct domain ID. + */ +TEST(ParticipantTests, get_participant_extended_qos_from_default_profile) +{ + DomainParticipantFactory::get_instance()->load_XML_profiles_file("test_xml_profile.xml"); + + uint32_t domain_id = 123u; // This is the domain ID set in the default profile above + + DomainParticipantExtendedQos extended_qos; + ASSERT_TRUE(DomainParticipantFactory::get_instance()->get_participant_extended_qos_from_default_profile( + extended_qos) == RETCODE_OK); + + ASSERT_EQ(extended_qos.domainId(), domain_id); +} + TEST(ParticipantTests, CreateDomainParticipantWithDefaultProfile) { uint32_t domain_id = 123u; // This is the domain ID set in the default profile above diff --git a/versions.md b/versions.md index ce47dc7df7c..997cec7f404 100644 --- a/versions.md +++ b/versions.md @@ -55,6 +55,7 @@ Forthcoming * Custom Content filter example * Delivery mechanisms example with SHM, UDP, TCP, data-sharing and intra-process mechanisms. * Discovery server example. + * Request-reply example to showcase RPC paradigms over Fast DDS. * Removed `TypeConsistencyQos` from DataReader, and included `TypeConsistencyEnforcementQosPolicy` and `DataRepresentationQosPolicy` * Added new `flow_controller_descriptor_list` XML configuration, remove `ThroughtputController`. * Migrate `#define`s within `BuiltinEndpoints.hpp` to namespaced `constexpr` variables.