diff --git a/app/mon/mon_cli/CMakeLists.txt b/app/mon/mon_cli/CMakeLists.txt index 3b99c47dae..1b11199c36 100644 --- a/app/mon/mon_cli/CMakeLists.txt +++ b/app/mon/mon_cli/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -45,7 +45,7 @@ target_link_libraries(${PROJECT_NAME} tclap::tclap eCAL::core_protobuf eCAL::core_pb) -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) ecal_install_app(${PROJECT_NAME}) diff --git a/app/mon/mon_cli/src/ecal_mon_cli.cpp b/app/mon/mon_cli/src/ecal_mon_cli.cpp index afa6727f04..39925f7f45 100644 --- a/app/mon/mon_cli/src/ecal_mon_cli.cpp +++ b/app/mon/mon_cli/src/ecal_mon_cli.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -363,7 +363,7 @@ void ProcProto(const std::string& topic_name, int msg_count) // create dynamic subscribers for receiving and decoding messages and assign callback eCAL::protobuf::CDynamicSubscriber sub(topic_name); std::atomic cnt(msg_count); - auto msg_cb = [&cnt](const google::protobuf::Message& msg_) { if (cnt != 0) { std::cout << msg_.DebugString() << std::endl; if (cnt > 0) cnt--; } }; + auto msg_cb = [&cnt](const std::shared_ptr& msg_) { if (cnt != 0) { std::cout << msg_->DebugString() << std::endl; if (cnt > 0) cnt--; } }; sub.AddReceiveCallback(std::bind(msg_cb, std::placeholders::_2)); // enter main loop diff --git a/app/mon/mon_plugins/capnproto_reflection/CMakeLists.txt b/app/mon/mon_plugins/capnproto_reflection/CMakeLists.txt index f948a3ac5b..0bea6fa90e 100644 --- a/app/mon/mon_plugins/capnproto_reflection/CMakeLists.txt +++ b/app/mon/mon_plugins/capnproto_reflection/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -67,6 +67,8 @@ target_link_libraries (${PROJECT_NAME} eCAL::mon_plugin_lib MonitorTreeView ) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) + target_link_options(${PROJECT_NAME} PRIVATE $<$:/ignore:4099>) target_include_directories(${PROJECT_NAME} PRIVATE src) diff --git a/app/mon/mon_plugins/protobuf_reflection/CMakeLists.txt b/app/mon/mon_plugins/protobuf_reflection/CMakeLists.txt index e76cb873e5..4848e5275d 100644 --- a/app/mon/mon_plugins/protobuf_reflection/CMakeLists.txt +++ b/app/mon/mon_plugins/protobuf_reflection/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -69,7 +69,7 @@ target_link_libraries (${PROJECT_NAME} MonitorTreeView eCAL::mon_plugin_lib ) -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) if(MSVC) set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "/wd4127 /wd4714") diff --git a/app/mon/mon_plugins/protobuf_reflection/src/plugin_widget.cpp b/app/mon/mon_plugins/protobuf_reflection/src/plugin_widget.cpp index d7a2201c36..d0051340e7 100644 --- a/app/mon/mon_plugins/protobuf_reflection/src/plugin_widget.cpp +++ b/app/mon/mon_plugins/protobuf_reflection/src/plugin_widget.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -110,7 +110,7 @@ PluginWidget::~PluginWidget() { std::lock_guard lock(proto_message_mutex_); - delete last_proto_message_; + last_proto_message_.reset(); } } @@ -157,19 +157,15 @@ void PluginWidget::updatePublishTimeLabel() //////////////////////////////////////////////////////////////////////////////// // eCAL Callback -void PluginWidget::onProtoMessageCallback(const google::protobuf::Message& message, long long send_time_usecs) +void PluginWidget::onProtoMessageCallback(const std::shared_ptr& message, long long send_time_usecs) { { // Lock the mutex std::lock_guard lock(proto_message_mutex_); - // Delete the old message - delete last_proto_message_; - // Create a copy of the new message as member variable. We cannot use a reference here, as this may cause a deadlock with the GUI thread - last_proto_message_ = message.New(); - last_proto_message_->CopyFrom(message); + last_proto_message_ = message; last_message_publish_timestamp_ = eCAL::Time::ecal_clock::time_point(std::chrono::duration_cast(std::chrono::microseconds(send_time_usecs))); diff --git a/app/mon/mon_plugins/protobuf_reflection/src/plugin_widget.h b/app/mon/mon_plugins/protobuf_reflection/src/plugin_widget.h index 992c148e14..2ebbff3ed9 100644 --- a/app/mon/mon_plugins/protobuf_reflection/src/plugin_widget.h +++ b/app/mon/mon_plugins/protobuf_reflection/src/plugin_widget.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,11 +68,11 @@ private slots: eCAL::protobuf::CProtoDecoder protobuf_decoder; std::shared_ptr protobuf_tree_builder; - std::mutex proto_message_mutex_; - google::protobuf::Message* last_proto_message_; - eCAL::Time::ecal_clock::time_point last_message_publish_timestamp_; - QString last_error_string_; - bool last_message_was_error_; + std::mutex proto_message_mutex_; + std::shared_ptr last_proto_message_; + eCAL::Time::ecal_clock::time_point last_message_publish_timestamp_; + QString last_error_string_; + bool last_message_was_error_; bool currently_showing_error_item_; int error_counter_; @@ -83,7 +83,7 @@ private slots: bool new_msg_available_; int received_message_counter_; - void onProtoMessageCallback(const google::protobuf::Message& message, long long send_time_usecs); + void onProtoMessageCallback(const std::shared_ptr& message, long long send_time_usecs); void onProtoErrorCallback(const std::string& error); void updatePublishTimeLabel(); diff --git a/app/mon/mon_plugins/signals_plotting/CMakeLists.txt b/app/mon/mon_plugins/signals_plotting/CMakeLists.txt index e668f0bcef..961dfedabe 100644 --- a/app/mon/mon_plugins/signals_plotting/CMakeLists.txt +++ b/app/mon/mon_plugins/signals_plotting/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -84,7 +84,7 @@ target_link_libraries (${PROJECT_NAME} CustomQt ) -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) if(MSVC) set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "/wd4127 /wd4714" ) diff --git a/app/mon/mon_plugins/signals_plotting/src/plugin_widget.cpp b/app/mon/mon_plugins/signals_plotting/src/plugin_widget.cpp index 80f8347b47..2b6ddd7159 100644 --- a/app/mon/mon_plugins/signals_plotting/src/plugin_widget.cpp +++ b/app/mon/mon_plugins/signals_plotting/src/plugin_widget.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -132,7 +132,7 @@ PluginWidget::~PluginWidget() { std::lock_guard lock(proto_message_mutex_); - delete last_proto_message_; + last_proto_message_.reset(); } } @@ -256,19 +256,15 @@ void PluginWidget::setVisibleSplitterHandle(bool state) //////////////////////////////////////////////////////////////////////////////// // eCAL Callback -void PluginWidget::onProtoMessageCallback(const google::protobuf::Message& message, long long send_time_usecs) +void PluginWidget::onProtoMessageCallback(const std::shared_ptr& message, long long send_time_usecs) { { // Lock the mutex std::lock_guard lock(proto_message_mutex_); - // Delete the old message - delete last_proto_message_; - // Create a copy of the new message as member variable. We cannot use a reference here, as this may cause a deadlock with the GUI thread - last_proto_message_ = message.New(); - last_proto_message_->CopyFrom(message); + last_proto_message_ = message; last_message_publish_timestamp_ = eCAL::Time::ecal_clock::time_point(std::chrono::duration_cast(std::chrono::microseconds(send_time_usecs))); diff --git a/app/mon/mon_plugins/signals_plotting/src/plugin_widget.h b/app/mon/mon_plugins/signals_plotting/src/plugin_widget.h index e10787c4ee..27ce7c290a 100644 --- a/app/mon/mon_plugins/signals_plotting/src/plugin_widget.h +++ b/app/mon/mon_plugins/signals_plotting/src/plugin_widget.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -102,9 +102,9 @@ private slots: eCAL::protobuf::CProtoDecoder protobuf_decoder; std::shared_ptr protobuf_tree_builder; - std::mutex proto_message_mutex_; - google::protobuf::Message* last_proto_message_; - eCAL::Time::ecal_clock::time_point last_message_publish_timestamp_; + std::mutex proto_message_mutex_; + std::shared_ptr last_proto_message_; + eCAL::Time::ecal_clock::time_point last_message_publish_timestamp_; QString last_error_string_; bool last_message_was_error_; int error_counter_; @@ -119,7 +119,7 @@ private slots: QString key_to_close_; - void onProtoMessageCallback(const google::protobuf::Message& message, long long send_time_usecs); + void onProtoMessageCallback(const std::shared_ptr& message, long long send_time_usecs); void onProtoErrorCallback(const std::string& error); void updatePublishTimeLabel(); bool find_items(QAbstractTreeItem* tree_item); diff --git a/app/mon/mon_tui/src/tui/view/message_visualization/proto_tree.hpp b/app/mon/mon_tui/src/tui/view/message_visualization/proto_tree.hpp index 9864bafff7..7d64516496 100644 --- a/app/mon/mon_tui/src/tui/view/message_visualization/proto_tree.hpp +++ b/app/mon/mon_tui/src/tui/view/message_visualization/proto_tree.hpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -261,7 +261,7 @@ class TreeMessageVisitor : public MessageVisitor } -void PopulateProtoTree(ftxui::TreeNode &root, google::protobuf::Message *message, const std::shared_ptr style) +void PopulateProtoTree(ftxui::TreeNode &root, const std::shared_ptr& message, const std::shared_ptr style) { auto tree_builder = std::make_shared(root, style); if(message) @@ -272,7 +272,7 @@ void PopulateProtoTree(ftxui::TreeNode &root, google::protobuf::Message *message } } -ftxui::TreeNodePtr ProtoTree(google::protobuf::Message *message, const std::shared_ptr style) +ftxui::TreeNodePtr ProtoTree(const std::shared_ptr& message, const std::shared_ptr style) { using namespace ftxui; auto root = std::make_shared(); diff --git a/app/mon/mon_tui/src/tui/viewmodel/message_visualization/proto.hpp b/app/mon/mon_tui/src/tui/viewmodel/message_visualization/proto.hpp index 1a170baf1d..220236ae3d 100644 --- a/app/mon/mon_tui/src/tui/viewmodel/message_visualization/proto.hpp +++ b/app/mon/mon_tui/src/tui/viewmodel/message_visualization/proto.hpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -35,15 +36,13 @@ class ProtoMessageVisualizationViewModel : public MessageVisualizationViewModel eCAL::protobuf::CDynamicSubscriber subscriber; mutable std::mutex message_mtx; - google::protobuf::Message *latest_message = nullptr; + std::shared_ptr latest_message = nullptr; - void OnMessage(const google::protobuf::Message& message, long long send_time_usecs) + void OnMessage(const std::shared_ptr& message, long long send_time_usecs) { { std::lock_guard lock(message_mtx); - delete latest_message; - latest_message = message.New(); - latest_message->CopyFrom(message); + latest_message = message; message_timestamp = send_time_usecs; } @@ -56,7 +55,7 @@ class ProtoMessageVisualizationViewModel : public MessageVisualizationViewModel //NOTE: Use with caution! struct ProtectedMessage { - google::protobuf::Message *message; + std::shared_ptr message; std::unique_lock lock; }; diff --git a/ecal/core/CMakeLists.txt b/ecal/core/CMakeLists.txt index d802dfb75d..9cd185d78c 100644 --- a/ecal/core/CMakeLists.txt +++ b/ecal/core/CMakeLists.txt @@ -199,6 +199,7 @@ endif() if(ECAL_CORE_PUBLISHER) set(ecal_pub_src src/pubsub/ecal_publisher.cpp + src/pubsub/ecal_publisher_config.cpp src/pubsub/ecal_pubgate.cpp src/pubsub/ecal_pubgate.h ) @@ -226,8 +227,8 @@ if(ECAL_CORE_PUBLISHER) ) if(ECAL_CORE_TRANSPORT_UDP) list(APPEND ecal_writer_src - src/readwrite/udp/ecal_writer_udp_mc.cpp - src/readwrite/udp/ecal_writer_udp_mc.h + src/readwrite/udp/ecal_writer_udp.cpp + src/readwrite/udp/ecal_writer_udp.h ) endif() if(ECAL_CORE_TRANSPORT_TCP) @@ -253,8 +254,8 @@ if(ECAL_CORE_SUBSCRIBER) ) if(ECAL_CORE_TRANSPORT_UDP) list(APPEND ecal_reader_src - src/readwrite/udp/ecal_reader_udp_mc.cpp - src/readwrite/udp/ecal_reader_udp_mc.h + src/readwrite/udp/ecal_reader_udp.cpp + src/readwrite/udp/ecal_reader_udp.h ) endif() if(ECAL_CORE_TRANSPORT_TCP) @@ -466,6 +467,7 @@ set(ecal_header_cmn include/ecal/ecal_process.h include/ecal/ecal_process_severity.h include/ecal/ecal_publisher.h + include/ecal/ecal_publisher_config.h include/ecal/ecal_server.h include/ecal/ecal_service_info.h include/ecal/ecal_subscriber.h diff --git a/ecal/core/include/ecal/cimpl/ecal_core_cimpl.h b/ecal/core/include/ecal/cimpl/ecal_core_cimpl.h index 6be50fb54b..109a1e449a 100644 --- a/ecal/core/include/ecal/cimpl/ecal_core_cimpl.h +++ b/ecal/core/include/ecal/cimpl/ecal_core_cimpl.h @@ -83,11 +83,9 @@ extern "C" /** * @brief Finalize eCAL API. * - * @param components_ Defines which component to initialize (not yet supported). - * * @return Zero if succeeded, 1 if already finalized, -1 if failed. **/ - ECALC_API int eCAL_Finalize(unsigned int components_); + ECALC_API int eCAL_Finalize(); /** * @brief Check eCAL initialize state. diff --git a/ecal/core/include/ecal/cimpl/ecal_publisher_cimpl.h b/ecal/core/include/ecal/cimpl/ecal_publisher_cimpl.h index 09f31a172b..754af8dd45 100644 --- a/ecal/core/include/ecal/cimpl/ecal_publisher_cimpl.h +++ b/ecal/core/include/ecal/cimpl/ecal_publisher_cimpl.h @@ -89,26 +89,6 @@ extern "C" **/ ECALC_API int eCAL_Pub_ClearAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_); - /** - * @brief Share topic type. - * - * @param handle_ Publisher handle. - * @param state_ Set type share mode (none zero == share type). - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_ShareType(ECAL_HANDLE handle_, int state_); - - /** - * @brief Share topic description. - * - * @param handle_ Publisher handle. - * @param state_ Set description share mode (none zero == share description). - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_ShareDescription(ECAL_HANDLE handle_, int state_); - /** * @brief Set the specific topic id. * diff --git a/ecal/core/include/ecal/ecal_client.h b/ecal/core/include/ecal/ecal_client.h index 53b3014e64..585c89353a 100644 --- a/ecal/core/include/ecal/ecal_client.h +++ b/ecal/core/include/ecal/ecal_client.h @@ -199,6 +199,6 @@ namespace eCAL protected: std::shared_ptr m_service_client_impl; - bool m_created; + bool m_created; }; } diff --git a/ecal/core/include/ecal/ecal_core.h b/ecal/core/include/ecal/ecal_core.h index 4bf03cc2b2..735c60a009 100644 --- a/ecal/core/include/ecal/ecal_core.h +++ b/ecal/core/include/ecal/ecal_core.h @@ -97,11 +97,9 @@ namespace eCAL /** * @brief Finalize eCAL API. * - * @param components_ Defines which component to finalize (not yet supported). - * * @return Zero if succeeded, 1 if already finalized, -1 if failed. **/ - ECAL_API int Finalize(unsigned int components_ = Init::Default); + ECAL_API int Finalize(); /** * @brief Check eCAL initialize state. diff --git a/ecal/core/include/ecal/ecal_publisher.h b/ecal/core/include/ecal/ecal_publisher.h index 3711f80ced..80c651434f 100644 --- a/ecal/core/include/ecal/ecal_publisher.h +++ b/ecal/core/include/ecal/ecal_publisher.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -69,7 +70,6 @@ namespace eCAL class CPublisher { public: - ECAL_API static constexpr long long DEFAULT_TIME_ARGUMENT = -1; /*!< Use DEFAULT_TIME_ARGUMENT in the `Send()` function to let eCAL determine the send timestamp */ /** @@ -80,17 +80,19 @@ namespace eCAL /** * @brief Constructor. * - * @param topic_name_ Unique topic name. - * @param topic_info_ Topic information (encoding, type, descriptor) + * @param topic_name_ Unique topic name. + * @param data_type_info_ Topic data type information (encoding, type, descriptor). + * @param config_ Optional configuration parameters. **/ - ECAL_API CPublisher(const std::string& topic_name_, const SDataTypeInformation& topic_info_); + ECAL_API CPublisher(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = {}); /** * @brief Constructor. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. **/ - ECAL_API explicit CPublisher(const std::string& topic_name_); + ECAL_API explicit CPublisher(const std::string& topic_name_, const Publisher::Configuration& config_ = {}); /** * @brief Destructor. @@ -120,12 +122,13 @@ namespace eCAL /** * @brief Creates this object. * - * @param topic_name_ Unique topic name. - * @param topic_info_ Topic information (encoding, type, descriptor) + * @param topic_name_ Unique topic name. + * @param data_type_info_ Topic data type information (encoding, type, descriptor). + * @param config_ Optional configuration parameters. * * @return True if it succeeds, false if it fails. **/ - ECAL_API bool Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_); + ECAL_API bool Create(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = {}); /** * @brief Creates this object. @@ -146,11 +149,11 @@ namespace eCAL /** * @brief Setup topic information. * - * @param topic_info_ Topic information attributes. + * @param data_type_info_ Topic data type information attributes. * * @return True if it succeeds, false if it fails. **/ - ECAL_API bool SetDataTypeInformation(const SDataTypeInformation& topic_info_); + ECAL_API bool SetDataTypeInformation(const SDataTypeInformation& data_type_info_); /** * @brief Sets publisher attribute. @@ -172,24 +175,6 @@ namespace eCAL **/ ECAL_API bool ClearAttribute(const std::string& attr_name_); - /** - * @brief Share topic type. - * - * @param state_ Set type share mode (true == share type). - * - * @return True if it succeeds, false if it fails. - **/ - ECAL_API bool ShareType(bool state_ = true); - - /** - * @brief Share topic description. - * - * @param state_ Set description share mode (true == share description). - * - * @return True if it succeeds, false if it fails. - **/ - ECAL_API bool ShareDescription(bool state_ = true); - /** * @brief Set the specific topic id. * @@ -295,9 +280,8 @@ namespace eCAL protected: // class members - std::shared_ptr m_datawriter; - long long m_id; - bool m_created; - bool m_initialized; + std::shared_ptr m_datawriter; + long long m_id; + bool m_created; }; } diff --git a/ecal/core/include/ecal/ecal_publisher_config.h b/ecal/core/include/ecal/ecal_publisher_config.h new file mode 100644 index 0000000000..2e1cb099a7 --- /dev/null +++ b/ecal/core/include/ecal/ecal_publisher_config.h @@ -0,0 +1,143 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @file ecal_publisher_config.h + * @brief eCAL publisher configuration + * + * This publisher configuration struct can be used to define the behavior of an eCAL publisher. Additional information on + * selected configuration parameters: + * + * -------------------------------------------------------------------------------------------------------------- + * Zero copy shared memory transport mode (SHM::Configuration::zero_copy_mode) + * -------------------------------------------------------------------------------------------------------------- + * + * By default, the built-in shared memory layer is configured to make two memory copies + * one on the publisher and one on the subscriber side. + * + * The intention of this implementation is to free the file as fast as possible after writing and reading + * its content to allow other processes to access the content with minimal latency. The publisher and subscribers + * are fully decoupled and can access their internal memory copy independently. + * + * If the zero copy mode is switched on no memory will be copied at all using the low level binary publish / subscribe API. + * On publisher side the memory copy is exectuted into the opened memory file. On the subscriber side the user message + * callback is called right after opening the memory file. A direct pointer to the memory payload is forwarded + * and can be processed with no latency. The memory file will be closed after the user callback function + * returned. + * + * The advantage of this configuration is a much higher performance for large payloads (> 1024 kB). + * The disadvantage of this configuration is that in the time when the callback is executed the memory file + * is blocked for other subscribers and for writing publishers too. Maybe this can be eliminated + * by a better memory file read/write access implementation (lock free read) in future releases. + * + * Today, for specific scenarios (1:1 pub/sub connections with large payloads for example) this feature + * can increase the performance remarkable. But please keep in mind to return from the message callback function + * as fast as possible to not delay subsequent read/write access operations. + * + * By using the eCAL::CPayloadWriter API a full zero copy implementation is possible by providing separate methods + * for the initialization and the modification of the memory file content (see CPayloadWriter documentation). + * + * + * -------------------------------------------------------------------------------------------------------------- + * Subscriber receive acknowledgement with timeout (SHM::Configuration::acknowledge_timeout_ms) + * -------------------------------------------------------------------------------------------------------------- + * + * Most applications perform very well with the default behavior. If subscribers are too slow + * to process incoming messages then the overall software architecture needs to be checked, software components + * need to be optimized or parallelized. + * + * There may still be cases where it could make sense to synchronize the transfer of the payload from a publisher + * to a subscriber by using an additional handshake event. This event is signaled by a subscriber back to the + * sending publisher to confirm the complete payload transmission and the processed subscriber callback. + * + * The publisher will wait up to the specified timeout for the acknowledge signals of all connected subscribers + * before sending new content. Finally that means the publishers CPublisher::Send API function call is now blocked + * and will not return until all subscriber have read and processed their content or the timeout has been reached. + * + * + * -------------------------------------------------------------------------------------------------------------- + * Number of handled memory files (SHM::Configuration::memfile_buffer_count) + * -------------------------------------------------------------------------------------------------------------- + * + * By default, each publisher creates one memory file to distribute its payload to the subscribers. Since eCAL does not + * currently support a rw lock synchronisation mechanism for interprocess communication, reading subscribers are blocking + * the memory file and thus are preventing a publisher from writing the next payload into the file. + * + * This blocking behavior can be mitigated by using multiple memory files per publisher/subscriber connection. These memory + * files are then written in a kind of ring buffer. + * + * The disadvantage of this setting (memfile_buffer_count > 1) is the higher consumption of resources (memory files, events..) + * +**/ + +#pragma once + +#include + +#include + +namespace eCAL +{ + namespace Publisher + { + namespace SHM + { + struct ECAL_API Configuration + { + bool enable = false; //!< enable layer + bool zero_copy_mode = false; //!< enable zero copy shared memory transport mode + int acknowledge_timeout_ms = 0; /*!< force connected subscribers to send acknowledge event after processing the message + the publisher send call is blocked on this event with this timeout (0 == no handshake) */ + size_t memfile_min_size_bytes = 4096; //!< default memory file size for new publisher + size_t memfile_reserve_percent = 50; //!< dynamic file size reserve before recreating memory file if topic size changes + size_t memfile_buffer_count = 1; //!< maximum number of used buffers (needs to be greater than 1, default = 1) + }; + } + + namespace UDP + { + struct ECAL_API Configuration + { + bool enable = false; //!< enable layer + bool loopback = false; //!< enable to receive udp messages on the same local machine + int sndbuf_size_bytes = (5*1024*1024); //!< udp send buffer size in bytes (default 5MB) + }; + } + + namespace TCP + { + struct ECAL_API Configuration + { + bool enable = false; //!< enable layer + }; + } + + struct ECAL_API Configuration + { + Configuration(); + + SHM::Configuration shm; + UDP::Configuration udp; + TCP::Configuration tcp; + + bool share_topic_type = true; //!< share topic type via registration + bool share_topic_description = true; //!< share topic description via registration + }; + } +} diff --git a/ecal/core/include/ecal/ecal_subscriber.h b/ecal/core/include/ecal/ecal_subscriber.h index b3d8f32a6e..17332d1f62 100644 --- a/ecal/core/include/ecal/ecal_subscriber.h +++ b/ecal/core/include/ecal/ecal_subscriber.h @@ -269,8 +269,7 @@ namespace eCAL protected: // class members - std::shared_ptr m_datareader; - bool m_created; - bool m_initialized; + std::shared_ptr m_datareader; + bool m_created; }; } diff --git a/ecal/core/include/ecal/ecal_tlayer.h b/ecal/core/include/ecal/ecal_tlayer.h index 0fba041a9f..3542c5a181 100644 --- a/ecal/core/include/ecal/ecal_tlayer.h +++ b/ecal/core/include/ecal/ecal_tlayer.h @@ -53,6 +53,7 @@ namespace eCAL smode_auto }; + // TODO: Do weed need this ? /** * @brief eCAL transport layer state struct. **/ diff --git a/ecal/core/include/ecal/ecal_types.h b/ecal/core/include/ecal/ecal_types.h index 14187eaf6f..5e32297c9e 100644 --- a/ecal/core/include/ecal/ecal_types.h +++ b/ecal/core/include/ecal/ecal_types.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ #pragma once #include +#include namespace eCAL { @@ -47,6 +48,11 @@ namespace eCAL { return !(*this == other); } + + bool operator<(const SDataTypeInformation& rhs) const + { + return std::tie(name, encoding, descriptor) < std::tie(rhs.name, rhs.encoding, rhs.descriptor); + } //!< @endcond }; @@ -69,6 +75,11 @@ namespace eCAL { return !(*this == other); } + + bool operator<(const SServiceMethodInformation& rhs) const + { + return std::tie(request_type, response_type) < std::tie(rhs.request_type, rhs.response_type); + } //!< @endcond }; } diff --git a/ecal/core/include/ecal/msg/capnproto/dynamic.h b/ecal/core/include/ecal/msg/capnproto/dynamic.h index c856afda15..1061768c1c 100644 --- a/ecal/core/include/ecal/msg/capnproto/dynamic.h +++ b/ecal/core/include/ecal/msg/capnproto/dynamic.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,177 +32,67 @@ #pragma warning(pop) #endif /*_MSC_VER*/ -#include -#include +#include #include namespace eCAL { - namespace capnproto + namespace internal { - class CDynamicSubscriber + class CapnprotoDynamicDeserializer { public: - /** - * @brief Constructor. - **/ - CDynamicSubscriber() - : subscriber() - , builder() - , initialized(false) - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CDynamicSubscriber(const std::string& topic_name_) - : subscriber(topic_name_, GetDataTypeInformation()) - , builder() - , initialized(false) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CDynamicSubscriber(const CDynamicSubscriber&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CDynamicSubscriber& operator=(const CDynamicSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CDynamicSubscriber(CDynamicSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CDynamicSubscriber& operator=(CDynamicSubscriber&&) = default; - - /** - * @brief eCAL protobuf message receive callback function - * - * @param topic_name_ Topic name of the data source (publisher). - * @param msg_ Protobuf message content. - * @param time_ Message time stamp. - **/ - typedef std::function CapnpDynamicMsgCallbackT; - - /** - * @brief Add callback function for incoming receives. - * - * @param callback_ The callback function to add. - * - * @return True if succeeded, false if not. - **/ - bool AddReceiveCallback(CapnpDynamicMsgCallbackT callback_) - { - msg_callback = callback_; - return subscriber.AddReceiveCallback(std::bind(&CDynamicSubscriber::OnReceive, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - } - - bool RemReceiveCallback() + // This function is NOT threadsafe!!! + // what about the lifetime of the objects? + // It's totally unclear to me :/ + capnp::DynamicStruct::Reader Deserialize(const void* buffer_, size_t size_, const SDataTypeInformation& datatype_info_) { - auto ret = subscriber.RemReceiveCallback(); - msg_callback = nullptr; - return ret; - } + try + { + // Put the pointer into a capnp::MallocMessageBuilder, it holds the memory to later access the object via a capnp::Dynami + kj::ArrayPtr words = kj::arrayPtr(reinterpret_cast(buffer_), size_ / sizeof(capnp::word)); + kj::ArrayPtr rest = initMessageBuilderFromFlatArrayCopy(words, m_msg_builder); - void OnReceive(const char* topic_name_, const capnp::MallocMessageBuilder& msg_, long long time_, long long clock_, long long id_) - { - if (!initialized) + capnp::Schema schema = GetSchema(datatype_info_); + capnp::DynamicStruct::Builder root_builder = m_msg_builder.getRoot(schema.asStruct()); + return root_builder.asReader(); + } + catch (...) { - SDataTypeInformation topic_info_; - eCAL::Util::GetTopicDataTypeInformation(topic_name_, topic_info_); - std::string topic_desc = topic_info_.descriptor; - if (!topic_desc.empty()) - { - // We initialize the builder from the string - schema = eCAL::capnproto::SchemaFromDescriptor(topic_desc, loader); - initialized = true; - } - else - { - return; - } + throw new DynamicReflectionException("Error deserializing Capnproto data."); } - - auto root = const_cast(msg_).getRoot(schema.asStruct()); - msg_callback(topic_name_, root.asReader(), time_, clock_, id_); } - /** - * @brief get a Pointer to a temporary message that can be passed to receive - **/ - typename capnp::DynamicStruct::Reader getReader() - { - return root_builder.asReader(); - } - - /** - * @brief Manually receive the next sample - **/ - bool Receive(long long* time_ = nullptr, int rcv_timeout_ = 0) - { - return subscriber.Receive(builder, time_, rcv_timeout_); - } - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(subscriber.Create(topic_name_, GetDataTypeInformation())); - } - - /** - * @brief Get type name of the capnp message. - * - * @return Type name. - **/ - ECAL_DEPRECATE_SINCE_5_13("Please use the method SDataTypeInformation GetDataTypeInformation() instead. You can extract the typename from the STopicInformation variable. This function will be removed in eCAL6.") - std::string GetTypeName() const - { - return (""); - } private: - /** - * @brief Get topic information of the message. - * - * @return Topic information. - **/ - SDataTypeInformation GetDataTypeInformation() const + capnp::Schema GetSchema(const SDataTypeInformation& datatype_info_) { - SDataTypeInformation topic_info; - // this is dynamic information. what should we return now? - return topic_info; + auto schema = m_schema_map.find(datatype_info_); + if (schema != m_schema_map.end()) + { + m_schema_map[datatype_info_] = ::eCAL::capnproto::SchemaFromDescriptor(datatype_info_.descriptor, m_loader); + } + return m_schema_map[datatype_info_]; } - - CBuilderSubscriber subscriber; - capnp::MallocMessageBuilder builder; - capnp::DynamicStruct::Builder root_builder; - CapnpDynamicMsgCallbackT msg_callback; - - capnp::schema::Node::Reader reader; - capnp::SchemaLoader loader; - capnp::Schema schema; - - bool initialized; - + capnp::MallocMessageBuilder m_msg_builder; + std::map m_schema_map; + capnp::SchemaLoader m_loader; }; + } + namespace capnproto + { + /** + * @brief eCAL capnp subscriber class. + * + * Subscriber template class for capnp messages. For details see documentation of CSubscriber class. + * + **/ + using CDynamicSubscriber = CDynamicMessageSubscriber; + + /** @example addressbook_rec.cpp + * This is an example how to use eCAL::capnproto::CSubscriber to receive capnp data with eCAL. To receive the data, see @ref addressbook_rec.cpp . + */ } - -} \ No newline at end of file +} diff --git a/ecal/core/include/ecal/msg/capnproto/publisher.h b/ecal/core/include/ecal/msg/capnproto/publisher.h index 0809c3bc24..bf6b3d6ef1 100644 --- a/ecal/core/include/ecal/msg/capnproto/publisher.h +++ b/ecal/core/include/ecal/msg/capnproto/publisher.h @@ -99,9 +99,10 @@ namespace eCAL * @brief Constructor. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. **/ - CPublisher(const std::string& topic_name_) - : eCAL::CPublisher(topic_name_, GetDataTypeInformation()) + CPublisher(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) + : eCAL::CPublisher(topic_name_, GetDataTypeInformation(), config_) , builder(std::make_unique()) , root_builder(builder->initRoot()) { @@ -145,12 +146,13 @@ namespace eCAL * @brief Creates this object. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. * * @return True if it succeeds, false if it fails. **/ - bool Create(const std::string& topic_name_) + bool Create(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) { - return(eCAL::CPublisher::Create(topic_name_, GetDataTypeInformation())); + return(eCAL::CPublisher::Create(topic_name_, GetDataTypeInformation(), config_)); } typename message_type::Builder GetBuilder() @@ -172,11 +174,11 @@ namespace eCAL **/ SDataTypeInformation GetDataTypeInformation() const { - SDataTypeInformation topic_info; - topic_info.encoding = eCAL::capnproto::EncodingAsString(); - topic_info.name = eCAL::capnproto::TypeAsString(); - topic_info.descriptor = eCAL::capnproto::SchemaAsString(); - return topic_info; + SDataTypeInformation data_type_info; + data_type_info.encoding = eCAL::capnproto::EncodingAsString(); + data_type_info.name = eCAL::capnproto::TypeAsString(); + data_type_info.descriptor = eCAL::capnproto::SchemaAsString(); + return data_type_info; } std::unique_ptr builder; diff --git a/ecal/core/include/ecal/msg/capnproto/subscriber.h b/ecal/core/include/ecal/msg/capnproto/subscriber.h index 02cb9edac5..3574773c88 100644 --- a/ecal/core/include/ecal/msg/capnproto/subscriber.h +++ b/ecal/core/include/ecal/msg/capnproto/subscriber.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,249 +37,51 @@ #pragma warning(pop) #endif /*_MSC_VER*/ - namespace eCAL { - namespace capnproto + namespace internal { - /** - * @brief eCAL capnp subscriber class. - * - * Subscriber template class for capnp messages. For details see documentation of CSubscriber class. - * - **/ - class CBuilderSubscriber : public CMsgSubscriber + template + class CapnprotoDeserializer { public: - - /** - * @brief Constructor. - **/ - CBuilderSubscriber() : CMsgSubscriber() + SDataTypeInformation GetDataTypeInformation() { + SDataTypeInformation topic_info; + topic_info.encoding = eCAL::capnproto::EncodingAsString(); + topic_info.name = eCAL::capnproto::TypeAsString(); + topic_info.descriptor = eCAL::capnproto::SchemaAsString(); + return topic_info; } - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CBuilderSubscriber(const std::string& topic_name_, const SDataTypeInformation& topic_info_) : CMsgSubscriber(topic_name_, topic_info_) - { - } - - /** - * @brief Destructor - **/ - ~CBuilderSubscriber() override - { - this->Destroy(); - } - - /** - * @brief Copy Constructor is not available. - **/ - CBuilderSubscriber(const CBuilderSubscriber&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CBuilderSubscriber& operator=(const CBuilderSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CBuilderSubscriber(CBuilderSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CBuilderSubscriber& operator=(CBuilderSubscriber&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_) - { - return(CMsgSubscriber::Create(topic_name_, topic_info_)); - } + // This function is NOT threadsafe!!! + bool Deserialize(typename T::Reader& msg_, const void* buffer_, size_t size_) + { + kj::ArrayPtr words = kj::arrayPtr(reinterpret_cast(buffer_), size_ / sizeof(capnp::word)); + kj::ArrayPtr rest = initMessageBuilderFromFlatArrayCopy(words, m_msg_builder); + typename T::Builder root_builder = typename T::Builder(m_msg_builder.getRoot()); + msg_ = root_builder.asReader(); - /** - * @brief Deserialize the message object from a message buffer. - * - * @param [out] msg_ The message object. - * @param buffer_ Source buffer. - * @param size_ Source buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Deserialize(capnp::MallocMessageBuilder& msg_, const void* buffer_, size_t size_) const - { - kj::ArrayPtr words = kj::arrayPtr(reinterpret_cast(buffer_), size_ / sizeof(capnp::word)); - kj::ArrayPtr rest = initMessageBuilderFromFlatArrayCopy(words, msg_); return(rest.size() == 0); } + + private: + capnp::MallocMessageBuilder m_msg_builder; }; - /** @example addressbook_rec.cpp - * This is an example how to use eCAL::CCapnpSubscriber to receive capnp data with eCAL. To send the data, see @ref addressbook_snd.cpp . - */ + } + namespace capnproto + { /** - * @brief eCAL capnp subscriber class. - * - * Subscriber template class for capnp messages. For details see documentation of CSubscriber class. - * + * @brief eCAL capnp subscriber class. + * + * Subscriber template class for capnp messages. For details see documentation of CSubscriber class. + * **/ - template - class CSubscriber - { - public: - /** - * @brief Constructor. - **/ - CSubscriber() - : subscriber() - , builder() - , root_builder(builder.getRoot()) - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CSubscriber(const std::string& topic_name_) - : subscriber(topic_name_, GetDataTypeInformation()) - , builder() - , root_builder(builder.getRoot()) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber(const CSubscriber&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber& operator=(const CSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CSubscriber(CSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CSubscriber& operator=(CSubscriber&&) = default; - - /** - * @brief eCAL protobuf message receive callback function - * - * @param topic_name_ Topic name of the data source (publisher). - * @param msg_ Protobuf message content. - * @param time_ Message time stamp. - **/ - typedef std::function MsgCallbackT; - - /** - * @brief Add callback function for incoming receives. - * - * @param callback_ The callback function to add. - * - * @return True if succeeded, false if not. - **/ - bool AddReceiveCallback(MsgCallbackT callback_) - { - msg_callback = callback_; - return subscriber.AddReceiveCallback(std::bind(&CSubscriber::OnReceive, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - } - - bool RemReceiveCallback() - { - auto ret{subscriber.RemReceiveCallback()}; - msg_callback = nullptr; - return ret; - } - - void OnReceive(const char* topic_name_, const capnp::MallocMessageBuilder& msg_, long long time_, long long clock_, long long id_) - { - auto root = const_cast(msg_).getRoot(); - msg_callback(topic_name_, root, time_, clock_, id_); - } - - /** - * @brief get a Pointer to a temporary message that can be passed to receive - **/ - typename message_type::Reader getReader() - { - return root_builder.asReader(); - } - - /** - * @brief Manually receive the next sample - **/ - bool Receive(long long* time_ = nullptr, int rcv_timeout_ = 0) - { - bool success = subscriber.Receive(builder, time_, rcv_timeout_); - // Update the Reader - root_builder = typename message_type::Builder(builder.getRoot()); - return success; - } - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(subscriber.Create(topic_name_, GetDataTypeInformation())); - } - - /** - * @brief Get type name of the capnp message. - * - * @return Type name. - **/ - ECAL_DEPRECATE_SINCE_5_13("Please use SDataTypeInformation GetDataTypeInformation() instead. This function will be removed in eCAL6.") - std::string GetTypeName() const - { - return eCAL::capnproto::TypeAsString(); - } + template + using CSubscriber = CMessageSubscriber>; - private: - /** - * @brief Get topic information of the message. - * - * @return Topic information. - **/ - SDataTypeInformation GetDataTypeInformation() const - { - SDataTypeInformation topic_info; - topic_info.encoding = eCAL::capnproto::EncodingAsString(); - topic_info.name = eCAL::capnproto::TypeAsString(); - topic_info.descriptor = eCAL::capnproto::SchemaAsString(); - return topic_info; - } - - CBuilderSubscriber subscriber; - capnp::MallocMessageBuilder builder; - typename message_type::Builder root_builder; - MsgCallbackT msg_callback; - - - }; /** @example addressbook_rec.cpp * This is an example how to use eCAL::capnproto::CSubscriber to receive capnp data with eCAL. To receive the data, see @ref addressbook_rec.cpp . */ diff --git a/ecal/core/include/ecal/msg/dynamic.h b/ecal/core/include/ecal/msg/dynamic.h index d84721b325..78603f594f 100644 --- a/ecal/core/include/ecal/msg/dynamic.h +++ b/ecal/core/include/ecal/msg/dynamic.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,12 +24,16 @@ #pragma once -#include #include +#include +#include +#include + +#include +#include namespace eCAL { - /* @cond */ class DynamicReflectionException : public std::exception { @@ -40,7 +44,7 @@ namespace eCAL std::string message_; }; /* @endcond */ - + /* @cond */ inline bool StrEmptyOrNull(const std::string& str) { @@ -61,4 +65,263 @@ namespace eCAL } } /* @endcond */ + + + template + class CDynamicMessageSubscriber final : public CSubscriber + { + public: + /** + * @brief Constructor. + **/ + CDynamicMessageSubscriber() : CSubscriber() + { + } + + /** + * @brief Constructor. + * + * @param topic_name_ Unique topic name. + **/ + CDynamicMessageSubscriber(const std::string& topic_name_) : CSubscriber() + , m_topic_name(topic_name_) + , m_deserializer() + { + CSubscriber::Create(topic_name_); + } + + ~CDynamicMessageSubscriber() noexcept + { + Destroy(); + }; + + /** + * @brief Copy Constructor is not available. + **/ + CDynamicMessageSubscriber(const CDynamicMessageSubscriber&) = delete; + + /** + * @brief Copy Constructor is not available. + **/ + CDynamicMessageSubscriber& operator=(const CDynamicMessageSubscriber&) = delete; + + /** + * @brief Move Constructor + **/ + CDynamicMessageSubscriber(CDynamicMessageSubscriber&& rhs) + : CSubscriber(std::move(rhs)) + , m_topic_name(std::move(rhs.m_topic_name)) + , m_cb_callback(std::move(rhs.m_cb_callback)) + , m_deserializer(std::move(rhs.m_deserializer)) + { + bool has_callback = (m_cb_callback != nullptr); + + if (has_callback) + { + // the callback bound to the CSubscriber belongs to rhs, bind to this callback instead + CSubscriber::RemReceiveCallback(); + auto callback = std::bind(&CDynamicMessageSubscriber::ReceiveCallback, this, std::placeholders::_1, std::placeholders::_2); + CSubscriber::AddReceiveCallback(callback); + } + } + + /** + * @brief Move assignment not available + **/ + CDynamicMessageSubscriber& operator=(CDynamicMessageSubscriber&& rhs) = delete; + + /** + * @brief Destroys this object. + * + * @return true if it succeeds, false if it fails. + **/ + bool Destroy() + { + RemReceiveCallback(); + return(CSubscriber::Destroy()); + } + + /** + * @brief Receive deserialized message. + * + * @param [out] time_ Optional receive time stamp. + * @param rcv_timeout_ Receive timeout in ms. + * + * @return std::optional which holds the value if a value could be received, and std::nullopt if it couldn't. + **/ + // Do we want to call error callbacks on receive? Probably not! std::expected wouuld be a good thing to return the reason why things went wrong. + std::optional Receive(long long* time_ = nullptr, int rcv_timeout_ = 0) + { + std::string rec_buf; + bool success = CSubscriber::ReceiveBuffer(rec_buf, time_, rcv_timeout_); + if (!success) + { + return std::nullopt; + } + // In the future, I would like to get m_datatype_info from the ReceiveBuffer function! + + PopulateDatatypeInfo(); + // We can't possibly receive anything if we don't have datatype info available + if (!m_datatype_info_received) + { + return std::nullopt; + } + + try + { + return(m_deserializer.Deserialize(rec_buf.c_str(), rec_buf.size(), m_datatype_info_received.value())); + } + catch (const DynamicReflectionException& /*e*/) + { + return std::nullopt; + } + } + + /** + * @brief eCAL message receive callback function + * + * @param topic_name_ Topic name of the data source (publisher). + * @param msg_ Message content. + * @param time_ Message time stamp. + * @param clock_ Message writer clock. + * @param id_ Message id. + **/ + using MsgReceiveCallbackT = std::function; + + /** + * @brief Add receive callback for incoming messages. + * + * @param callback_ The callback function. + * + * @return True if it succeeds, false if it fails. + **/ + bool AddReceiveCallback(MsgReceiveCallbackT callback_) + { + RemReceiveCallback(); + + { + std::lock_guard callback_lock(m_cb_callback_mutex); + m_cb_callback = callback_; + } + auto callback = std::bind(&CDynamicMessageSubscriber::ReceiveCallback, this, std::placeholders::_1, std::placeholders::_2); + return(CSubscriber::AddReceiveCallback(callback)); + } + + /** + * @brief Remove receive callback for incoming messages. + * + * @return True if it succeeds, false if it fails. + **/ + bool RemReceiveCallback() + { + bool ret = CSubscriber::RemReceiveCallback(); + + std::lock_guard callback_lock(m_cb_callback_mutex); + if (m_cb_callback == nullptr) return(false); + m_cb_callback = nullptr; + return(ret); + } + + /** + * @brief Callback function in case an error occurs. + * + * @param error The error message string. + **/ + using ErrorCallbackT = std::function; + + /** + * @brief Add callback function in case an error occurs. + * + * @param callback_ The callback function to add. + * + * @return True if succeeded, false if not. + **/ + bool AddErrorCallback(ErrorCallbackT callback_) + { + std::lock_guard callback_lock(m_error_callback_mutex); + m_error_callback = callback_; + + return true; + } + + /** + * @brief Remove callback function in case an error occurs. + * + * @return True if succeeded, false if not. + **/ + bool RemErrorCallback() + { + std::lock_guard callback_lock(m_error_callback_mutex); + m_error_callback = nullptr; + + return true; + } + + private: + void ReceiveCallback(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_) + { + MsgReceiveCallbackT fn_callback = nullptr; + { + std::lock_guard callback_lock(m_cb_callback_mutex); + fn_callback = m_cb_callback; + } + + if (fn_callback == nullptr) return; + + PopulateDatatypeInfo(); + + if (!m_datatype_info_received) + { + CallErrorCallback("Dynamic Deserialization: No Prototype available."); + return; + } + + try + { + // In the future, I would like to get m_datatype_info from the ReceiveBuffer function! + auto msg = m_deserializer.Deserialize(data_->buf, data_->size, m_datatype_info_received.value()); + fn_callback(topic_name_, msg, data_->time, data_->clock, data_->id); + } + catch (const DynamicReflectionException& e) + { + CallErrorCallback(std::string("Dynamic Deserialization: Error deserializing data: ") + e.what() ); + } + } + + void PopulateDatatypeInfo() + { + if (!m_datatype_info_received) + { + SDataTypeInformation datatype_info_received; + auto received_info = eCAL::Util::GetTopicDataTypeInformation(m_topic_name, datatype_info_received); + // empty datatype informations are not valid to do reflection on! + if (received_info && datatype_info_received != SDataTypeInformation{}) + { + m_datatype_info_received = datatype_info_received; + } + } + } + + void CallErrorCallback(const std::string& message) + { + ErrorCallbackT error_callback = nullptr; + { + std::lock_guard callback_lock(m_error_callback_mutex); + error_callback = m_error_callback; + } + if (error_callback) + { + error_callback(message); + } + } + + std::string m_topic_name; + std::mutex m_cb_callback_mutex; + MsgReceiveCallbackT m_cb_callback; + std::mutex m_error_callback_mutex; + ErrorCallbackT m_error_callback; + DynamicDeserializer m_deserializer; + std::optional m_datatype_info_received = std::nullopt; + }; + } diff --git a/ecal/core/include/ecal/msg/flatbuffers/publisher.h b/ecal/core/include/ecal/msg/flatbuffers/publisher.h index 51479fe71b..6e9e836715 100644 --- a/ecal/core/include/ecal/msg/flatbuffers/publisher.h +++ b/ecal/core/include/ecal/msg/flatbuffers/publisher.h @@ -51,8 +51,9 @@ namespace eCAL * @brief Constructor. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. **/ - CPublisher(const std::string& topic_name_) : CMsgPublisher(topic_name_, GetDataTypeInformation()) + CPublisher(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) : CMsgPublisher(topic_name_, GetDataTypeInformation(), config_) { } @@ -80,12 +81,13 @@ namespace eCAL * @brief Creates this object. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. * * @return True if it succeeds, false if it fails. **/ - bool Create(const std::string& topic_name_) + bool Create(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) { - return(CMsgPublisher::Create(topic_name_, GetDataTypeInformation())); + return(CMsgPublisher::Create(topic_name_, GetDataTypeInformation(), config_)); } private: @@ -96,10 +98,10 @@ namespace eCAL **/ SDataTypeInformation GetDataTypeInformation() const override { - SDataTypeInformation topic_info; - topic_info.encoding = "flatb"; + SDataTypeInformation data_type_info; + data_type_info.encoding = "flatb"; // empty type, empty descriptor - return topic_info; + return data_type_info; } /** diff --git a/ecal/core/include/ecal/msg/flatbuffers/subscriber.h b/ecal/core/include/ecal/msg/flatbuffers/subscriber.h index 0b274b2f39..714a8c8075 100644 --- a/ecal/core/include/ecal/msg/flatbuffers/subscriber.h +++ b/ecal/core/include/ecal/msg/flatbuffers/subscriber.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,81 +28,13 @@ namespace eCAL { - namespace flatbuffers + namespace internal { - /** - * @brief eCAL google::flatbuffers subscriber class. - * - * Subscriber template class for google::flatbuffers messages. For details see documentation of CSubscriber class. - * - **/ template - class CSubscriber : public CMsgSubscriber + class FlatbuffersDeserializer { public: - /** - * @brief Constructor. - **/ - CSubscriber() : CMsgSubscriber() - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CSubscriber(const std::string& topic_name_) : CMsgSubscriber(topic_name_, CSubscriber::GetDataTypeInformation()) - { - } - - /** - * @brief Destructor - **/ - ~CSubscriber() override - { - this->Destroy(); - } - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber(const CSubscriber&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber& operator=(const CSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CSubscriber(CSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CSubscriber& operator=(CSubscriber&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(CMsgSubscriber::Create(topic_name_, GetDataTypeInformation())); - } - - private: - /** - * @brief Get topic information of the flatbuffers message. - * - * @return Topic information. - **/ - SDataTypeInformation GetDataTypeInformation() const override + static SDataTypeInformation GetDataTypeInformation() { SDataTypeInformation topic_info; topic_info.encoding = "flatb"; @@ -110,23 +42,28 @@ namespace eCAL return topic_info; } - /** - * @brief Deserialize the message object from a message buffer. - * - * @param [out] msg_ The message object. - * @param buffer_ Source buffer. - * @param size_ Source buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Deserialize(T& msg_, const void* buffer_, size_t size_) const + static bool Deserialize(T& msg_, const void* buffer_, size_t size_) { msg_.PushFlatBuffer(static_cast(buffer_), static_cast(size_)); return(true); } }; + } + + namespace flatbuffers + { + + /** + * @brief eCAL google::flatbuffers subscriber class. + * + * Subscriber template class for google::flatbuffers messages. For details see documentation of CSubscriber class. + * + **/ + template + using CSubscriber = CMessageSubscriber>; + /** @example monster_rec.cpp * This is an example how to use eCAL::CSubscriber to receive goggle::flatbuffers data with eCAL. To send the data, see @ref monster_snd.cpp . */ } -} +} \ No newline at end of file diff --git a/ecal/core/include/ecal/msg/messagepack/publisher.h b/ecal/core/include/ecal/msg/messagepack/publisher.h index 8886b95244..70a7298a2f 100644 --- a/ecal/core/include/ecal/msg/messagepack/publisher.h +++ b/ecal/core/include/ecal/msg/messagepack/publisher.h @@ -54,8 +54,9 @@ namespace eCAL * @brief Constructor. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. **/ - CPublisher(const std::string& topic_name_) : CMsgPublisher(topic_name_, GetDataTypeInformation()) + CPublisher(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) : CMsgPublisher(topic_name_, GetDataTypeInformation(), config_) { } @@ -83,12 +84,13 @@ namespace eCAL * @brief Creates this object. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. * * @return True if it succeeds, false if it fails. **/ - bool Create(const std::string& topic_name_) + bool Create(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) { - return(CMsgPublisher::Create(topic_name_, GetDataTypeInformation())); + return(CMsgPublisher::Create(topic_name_, GetDataTypeInformation(), config_)); } private: @@ -99,10 +101,10 @@ namespace eCAL **/ SDataTypeInformation GetDataTypeInformation() const override { - SDataTypeInformation topic_info; - topic_info.encoding = "mpack"; + SDataTypeInformation data_type_info; + data_type_info.encoding = "mpack"; // empty descriptor, empty descriptor - return topic_info; + return data_type_info; } /** diff --git a/ecal/core/include/ecal/msg/messagepack/subscriber.h b/ecal/core/include/ecal/msg/messagepack/subscriber.h index ece4e0d4ef..dc72be7ab5 100644 --- a/ecal/core/include/ecal/msg/messagepack/subscriber.h +++ b/ecal/core/include/ecal/msg/messagepack/subscriber.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,81 +31,13 @@ namespace eCAL { - namespace messagepack + namespace internal { - /** - * @brief eCAL msgpack subscriber class. - * - * Subscriber template class for msgpack messages. For details see documentation of CSubscriber class. - * - **/ template - class CSubscriber : public CMsgSubscriber + class MessagePackDeserializer { public: - /** - * @brief Constructor. - **/ - CSubscriber() : CMsgSubscriber() - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CSubscriber(const std::string& topic_name_) : CMsgSubscriber(topic_name_, GetDataTypeInformation()) - { - } - - /** - * @brief Destructor - **/ - ~CSubscriber() override - { - this->Destroy(); - } - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber(const CSubscriber&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber& operator=(const CSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CSubscriber(CSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CSubscriber& operator=(CSubscriber&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(CMsgSubscriber::Create(topic_name_, GetDataTypeInformation())); - } - - private: - /** - * @brief Get topic information of the message. - * - * @return Topic information. - **/ - SDataTypeInformation GetDataTypeInformation() const override + static SDataTypeInformation GetDataTypeInformation() { SDataTypeInformation topic_info; topic_info.encoding = "mpack"; @@ -113,16 +45,7 @@ namespace eCAL return topic_info; } - /** - * @brief Deserialize the message object from a message buffer. - * - * @param [out] msg_ The message object. - * @param buffer_ Source buffer. - * @param size_ Source buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Deserialize(T& msg_, const void* buffer_, size_t size_) const + static bool Deserialize(T& msg_, const void* buffer_, size_t size_, const SDataTypeInformation& /*datatype_info_*/) { msgpack::unpacked ubuffer; msgpack::unpack(ubuffer, static_cast(buffer_), size_); @@ -131,8 +54,23 @@ namespace eCAL return(true); } }; + } + + namespace messagepack + { + + /** + * @brief eCAL msgpack subscriber class. + * + * Subscriber template class for msgpack messages. For details see documentation of CSubscriber class. + * + **/ + template + using CSubscriber = CMessageSubscriber>; + /** @example address_rec.cpp * This is an example how to use eCAL::CSubscriber to receive msgpack data with eCAL. To send the data, see @ref address_snd.cpp . */ } } + diff --git a/ecal/core/include/ecal/msg/protobuf/client.h b/ecal/core/include/ecal/msg/protobuf/client.h index 66c4c7db0f..b98606e33c 100644 --- a/ecal/core/include/ecal/msg/protobuf/client.h +++ b/ecal/core/include/ecal/msg/protobuf/client.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,11 +26,8 @@ #include #include -#include #include - - // protobuf includes #ifdef _MSC_VER #pragma warning(push, 0) // disable proto warnings diff --git a/ecal/core/include/ecal/msg/protobuf/dynamic_publisher.h b/ecal/core/include/ecal/msg/protobuf/dynamic_publisher.h index 686ac1ddd5..1a7ca4f2a1 100644 --- a/ecal/core/include/ecal/msg/protobuf/dynamic_publisher.h +++ b/ecal/core/include/ecal/msg/protobuf/dynamic_publisher.h @@ -60,9 +60,10 @@ namespace eCAL * * @param topic_name_ Unique topic name. * @param msg_ Protobuf message object. + * @param config_ Optional configuration parameters. **/ - CDynamicPublisher(const std::string& topic_name_, const std::shared_ptr& msg_) - : CMsgPublisher(topic_name_, GetTopicInformationFromMessage(msg_.get())) + CDynamicPublisher(const std::string& topic_name_, const std::shared_ptr& msg_, const eCAL::Publisher::Configuration& config_ = {}) + : CMsgPublisher(topic_name_, GetTopicInformationFromMessage(msg_.get()), config_) , m_msg{ msg_ } {} /** @@ -140,11 +141,11 @@ namespace eCAL assert(msg_ptr_); if(msg_ptr_ == nullptr) return SDataTypeInformation(); - SDataTypeInformation topic_info; - topic_info.encoding = "proto"; - topic_info.name = msg_ptr_->GetTypeName(); - topic_info.descriptor = GetDescriptorFromMessage(msg_ptr_); - return topic_info; + SDataTypeInformation data_type_info; + data_type_info.encoding = "proto"; + data_type_info.name = msg_ptr_->GetTypeName(); + data_type_info.descriptor = GetDescriptorFromMessage(msg_ptr_); + return data_type_info; } static std::string GetDescriptorFromMessage(const google::protobuf::Message* msg_ptr_) diff --git a/ecal/core/include/ecal/msg/protobuf/dynamic_subscriber.h b/ecal/core/include/ecal/msg/protobuf/dynamic_subscriber.h index 032536e89a..b95d766bb6 100644 --- a/ecal/core/include/ecal/msg/protobuf/dynamic_subscriber.h +++ b/ecal/core/include/ecal/msg/protobuf/dynamic_subscriber.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,306 +46,88 @@ namespace eCAL { - namespace protobuf - { - /** - * @brief eCAL dynamic protobuf subscriber. - **/ - class CDynamicSubscriber + namespace internal { - public: - /** - * @brief Constructor. - **/ - CDynamicSubscriber(); - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CDynamicSubscriber(const std::string& topic_name_); - - /** - * @brief Destructor. - **/ - virtual ~CDynamicSubscriber(); - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return true if it succeeds, false if it fails. - **/ - void Create(const std::string& topic_name_); - - /** - * @brief Destroys this object. - * - * @return true if it succeeds, false if it fails. - **/ - void Destroy(); - - /** - * @brief Query if this object is created. - * - * @return true if created, false if not. - **/ - bool IsCreated() { return(created); } - - /** - * @brief eCAL protobuf message receive callback function - * - * @param topic_name_ Topic name of the data source (publisher). - * @param msg_ Protobuf message content. - * @param time_ Message time stamp. - **/ - typedef std::function ProtoMsgCallbackT; - - /** - * @brief Add callback function for incoming receives. - * - * @param callback_ The callback function to add. - * - * @return True if succeeded, false if not. - **/ - bool AddReceiveCallback(ProtoMsgCallbackT callback_); - - /** - * @brief get a Pointer to a temporary message that can be passed to receive - * - **/ - google::protobuf::Message* getMessagePointer(); - - /** - * @brief Manually receive the next sample - **/ - bool Receive(google::protobuf::Message& msg_, long long* time_ = nullptr, int rcv_timeout_ = 0); - - /** - * @brief Remove callback function for incoming receives. - * - * @return True if succeeded, false if not. - **/ - bool RemReceiveCallback(); - - /** - * @brief Callback function in case an error occurs. - * - * @param error The error message string. - **/ - typedef std::function ProtoErrorCallbackT; - - /** - * @brief Add callback function in case an error occurs. - * - * @param callback_ The callback function to add. - * - * @return True if succeeded, false if not. - **/ - bool AddErrorCallback(ProtoErrorCallbackT callback_); - - /** - * @brief Remove callback function in case an error occurs. - * - * @return True if succeeded, false if not. - **/ - bool RemErrorCallback(); - - protected: - void OnReceive(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_); - - std::shared_ptr CreateMessagePointer(const std::string& topic_name_); - - bool created; - std::string topic_name; - std::unique_ptr msg_decoder; - std::shared_ptr msg_ptr; - eCAL::CSubscriber msg_sub; - ProtoMsgCallbackT msg_callback; - ProtoErrorCallbackT err_callback; - - private: - // this object must not be copied. - CDynamicSubscriber(const CDynamicSubscriber&); - CDynamicSubscriber& operator=(const CDynamicSubscriber&); - }; - /** @example proto_dyn.cpp - * This is an example how to use CDynamicSubscriber to receive dynamic google::protobuf data with eCAL. - **/ - - inline CDynamicSubscriber::CDynamicSubscriber() : - created(false), - msg_decoder(nullptr) - { - } - - inline CDynamicSubscriber::CDynamicSubscriber(const std::string& topic_name_) : - created(false), - msg_decoder(nullptr) - { - Create(topic_name_); - } - - inline CDynamicSubscriber::~CDynamicSubscriber() - { - Destroy(); - } - - inline void CDynamicSubscriber::Create(const std::string& topic_name_) - { - if (created) return; - - // save the topic name (required for receive polling) - topic_name = topic_name_; - - // create message decoder - msg_decoder = std::make_unique(); - - // create subscriber - msg_sub.Create(topic_name_); - - created = true; - } - - inline void CDynamicSubscriber::Destroy() - { - if (!created) return; - - // destroy subscriber - msg_sub.Destroy(); - - // delete message pointer - msg_ptr = nullptr; - - // delete message decoder - msg_decoder.reset(); - - created = false; - } - - inline google::protobuf::Message* CDynamicSubscriber::getMessagePointer() - { - try + class ProtobufDynamicDeserializer { - // Create Message Pointer for our topic name. - if (msg_ptr == nullptr) + public: + std::shared_ptr Deserialize(const void* buffer_, size_t size_, const SDataTypeInformation& datatype_info_) { - msg_ptr = CreateMessagePointer(topic_name); - } - } - catch (DynamicReflectionException& /*e*/) - { - return nullptr; - } - return msg_ptr.get(); - } - - inline bool CDynamicSubscriber::Receive(google::protobuf::Message& msg_, long long* time_, int rcv_timeout_) - { - std::string rec_buf; - bool success = msg_sub.ReceiveBuffer(rec_buf, time_, rcv_timeout_); - if (!success) return(false); - - // Parse current message content - auto parsed = msg_.ParseFromString(rec_buf); - return parsed; - } - - inline bool CDynamicSubscriber::AddReceiveCallback(ProtoMsgCallbackT callback_) - { - msg_callback = callback_; - msg_sub.AddReceiveCallback(std::bind(&CDynamicSubscriber::OnReceive, this, std::placeholders::_1, std::placeholders::_2)); - return(true); - } - - inline bool CDynamicSubscriber::RemReceiveCallback() - { - msg_sub.RemReceiveCallback(); - msg_callback = nullptr; - return(true); - } + auto message_prototype = GetMessagePointer(datatype_info_); + // for some reason cannot use std::make_shared, however should be ok in this context. + std::shared_ptr message_with_content(message_prototype->New()); + message_with_content->CopyFrom(*message_prototype); - inline bool CDynamicSubscriber::AddErrorCallback(ProtoErrorCallbackT callback_) - { - err_callback = callback_; - return(true); - } - - inline bool CDynamicSubscriber::RemErrorCallback() - { - err_callback = nullptr; - return(true); - } + try + { + message_with_content->ParseFromArray(buffer_, (int)size_); + return message_with_content; + } + catch (...) + { + throw new DynamicReflectionException("Error deserializing Protobuf data."); + } + } - inline void CDynamicSubscriber::OnReceive(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_) - { - try - { - // Create Message Pointer for our topic name. - if (msg_ptr == nullptr) + private: + std::shared_ptr GetMessagePointer(const SDataTypeInformation& datatype_info_) { - msg_ptr = CreateMessagePointer(topic_name_); + auto schema = m_message_map.find(datatype_info_); + if (schema == m_message_map.end()) + { + m_message_map[datatype_info_] = CreateMessagePointer(datatype_info_); + } + return m_message_map[datatype_info_]; } - if (msg_callback && msg_ptr) + std::shared_ptr CreateMessagePointer(const SDataTypeInformation& topic_info_) { - // Parse current message content - auto parsed = msg_ptr->ParseFromArray(data_->buf, data_->size); - if (parsed) + // get topic type + std::string topic_type{ topic_info_.name }; + topic_type = topic_type.substr(topic_type.find_last_of('.') + 1, topic_type.size()); + if (StrEmptyOrNull(topic_type)) { - msg_callback(topic_name_, *msg_ptr, data_->time); + throw DynamicReflectionException("ProtobufDynamicDeserializer: Could not get type"); } - else + + std::string topic_desc = topic_info_.descriptor; + if (StrEmptyOrNull(topic_desc)) { - throw DynamicReflectionException("CDynamicSubscriber: DataContent could not be parsed"); + throw DynamicReflectionException("ProtobufDynamicDeserializer: Could not get description for type" + std::string(topic_type)); } + + google::protobuf::FileDescriptorSet proto_desc; + proto_desc.ParseFromString(topic_desc); + std::string error_s; + std::shared_ptr proto_msg_ptr(m_dynamic_decoder.GetProtoMessageFromDescriptorSet(proto_desc, topic_type, error_s)); + if (proto_msg_ptr == nullptr) + { + std::stringstream s; + s << "ProtobufDynamicDeserializer: Message of type " + std::string(topic_type) << " could not be decoded" << std::endl; + s << error_s; + throw DynamicReflectionException(s.str()); + } + + return proto_msg_ptr; } - } - catch (DynamicReflectionException& e) - { - if (err_callback) - { - err_callback(e.what()); - } - } + + eCAL::protobuf::CProtoDynDecoder m_dynamic_decoder; + std::map> m_message_map; + }; } - /** - * Might throw DynamicReflectionException! - **/ - inline std::shared_ptr CDynamicSubscriber::CreateMessagePointer(const std::string& topic_name_) + namespace protobuf { - // get topic type - SDataTypeInformation topic_info; - eCAL::Util::GetTopicDataTypeInformation(topic_name, topic_info); - std::string topic_type{ topic_info.name }; - topic_type = topic_type.substr(topic_type.find_last_of('.') + 1, topic_type.size()); - if (StrEmptyOrNull(topic_type)) - { - throw DynamicReflectionException("CDynamicSubscriber: Could not get type for topic " + std::string(topic_name_)); - } - - std::string topic_desc = topic_info.descriptor; - if (StrEmptyOrNull(topic_desc)) - { - throw DynamicReflectionException("CDynamicSubscriber: Could not get description for topic " + std::string(topic_name_)); - } - - google::protobuf::FileDescriptorSet proto_desc; - proto_desc.ParseFromString(topic_desc); - std::string error_s; - std::shared_ptr proto_msg_ptr(msg_decoder->GetProtoMessageFromDescriptorSet(proto_desc, topic_type, error_s)); - if (proto_msg_ptr == nullptr) - { - std::stringstream s; - s << "CDynamicSubscriber: Message of type " + std::string(topic_name_) << " could not be decoded" << std::endl; - s << error_s; - throw DynamicReflectionException(s.str()); - } + /** + * @brief eCAL protobuf dynamic subscriber class. + * + * Dynamic subscriber class for protobuf messages. For details see documentation of CDynamicMessageSubscriber class. + * + **/ + using CDynamicSubscriber = CDynamicMessageSubscriber, internal::ProtobufDynamicDeserializer>; - return proto_msg_ptr; + /** @example proto_dyn_rec.cpp + * This is an example how to use eCAL::protobuf::CDynamicSubscriber to receive dynamic protobuf data with eCAL. To receive the data, see @ref proto_dyn_rec.cpp . + */ } - } } diff --git a/ecal/core/include/ecal/msg/protobuf/publisher.h b/ecal/core/include/ecal/msg/protobuf/publisher.h index 8db60be9a3..e583292877 100644 --- a/ecal/core/include/ecal/msg/protobuf/publisher.h +++ b/ecal/core/include/ecal/msg/protobuf/publisher.h @@ -100,13 +100,14 @@ namespace eCAL * @brief Constructor. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. **/ // call the function via its class because it's a virtual function that is called in constructor/destructor,- // where the vtable is not created yet, or it's destructed. // Probably we can handle the Message publishers differently. One message publisher class and then one class for payloads and getting type // descriptor information. - explicit CPublisher(const std::string& topic_name_) : eCAL::CPublisher(topic_name_, CPublisher::GetDataTypeInformation()) + explicit CPublisher(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) : eCAL::CPublisher(topic_name_, CPublisher::GetDataTypeInformation(), config_) { } @@ -139,12 +140,13 @@ namespace eCAL * @brief Creates this object. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. * * @return True if it succeeds, false if it fails. **/ - bool Create(const std::string& topic_name_) + bool Create(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) { - return(eCAL::CPublisher::Create(topic_name_, GetDataTypeInformation())); + return(eCAL::CPublisher::Create(topic_name_, GetDataTypeInformation(), config_)); } /** @@ -170,12 +172,12 @@ namespace eCAL **/ struct SDataTypeInformation GetDataTypeInformation() const { - struct SDataTypeInformation topic_info; + struct SDataTypeInformation data_type_info; static T msg{}; - topic_info.encoding = "proto"; - topic_info.name = msg.GetTypeName(); - topic_info.descriptor = protobuf::GetProtoMessageDescription(msg); - return topic_info; + data_type_info.encoding = "proto"; + data_type_info.name = msg.GetTypeName(); + data_type_info.descriptor = protobuf::GetProtoMessageDescription(msg); + return data_type_info; } }; diff --git a/ecal/core/include/ecal/msg/protobuf/server.h b/ecal/core/include/ecal/msg/protobuf/server.h index cb0eb87a73..241bbe992c 100644 --- a/ecal/core/include/ecal/msg/protobuf/server.h +++ b/ecal/core/include/ecal/msg/protobuf/server.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,6 @@ #pragma once #include -#include #include #include diff --git a/ecal/core/include/ecal/msg/protobuf/subscriber.h b/ecal/core/include/ecal/msg/protobuf/subscriber.h index b176805a47..cc8acd8c3c 100644 --- a/ecal/core/include/ecal/msg/protobuf/subscriber.h +++ b/ecal/core/include/ecal/msg/protobuf/subscriber.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,104 +44,23 @@ namespace eCAL { - namespace protobuf + namespace internal { - /** - * @brief eCAL google::protobuf subscriber class. - * - * Subscriber template class for google::protobuf messages. For details see documentation of CSubscriber class. - * - **/ template - class CSubscriber : public CMsgSubscriber + class ProtobufDeserializer { public: - /** - * @brief Constructor. - **/ - CSubscriber() : CMsgSubscriber() - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - - // call the function via its class because it's a virtual function that is called in constructor/destructor,- - // where the vtable is not created yet, or it's destructed. - explicit CSubscriber(const std::string& topic_name_) : CMsgSubscriber(topic_name_, CSubscriber::GetDataTypeInformation()) - { - } - - /** - * @brief Destructor - **/ - ~CSubscriber() override - { - this->Destroy(); - } - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber(const CSubscriber&) = delete; - - /** - * @brief Copy Assignment is not available. - **/ - CSubscriber& operator=(const CSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CSubscriber(CSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CSubscriber& operator=(CSubscriber&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(CMsgSubscriber::Create(topic_name_, GetDataTypeInformation())); - } - - private: - /** - * @brief Get topic information of the protobuf message. - * - * @return Topic information. - **/ - SDataTypeInformation GetDataTypeInformation() const override + static SDataTypeInformation GetDataTypeInformation() { SDataTypeInformation topic_info; static T msg{}; - topic_info.encoding = "proto"; - topic_info.name = msg.GetTypeName(); + topic_info.encoding = "proto"; + topic_info.name = msg.GetTypeName(); topic_info.descriptor = protobuf::GetProtoMessageDescription(msg); return topic_info; } - - /** - * @brief Deserialize the message object from a message buffer. - * - * @param [out] msg_ The message object. - * @param buffer_ Source buffer. - * @param size_ Source buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Deserialize(T& msg_, const void* buffer_, size_t size_) const override + static bool Deserialize(T& msg_, const void* buffer_, size_t size_) { // we try to parse the message from the received buffer if (msg_.ParseFromArray(buffer_, static_cast(size_))) @@ -150,10 +69,24 @@ namespace eCAL } return(false); } - }; - /** @example person_rec.cpp - * This is an example how to use eCAL::CSubscriber to receive google::protobuf data with eCAL. To send the data, see @ref person_snd.cpp . + } + + namespace protobuf + { + + /** + * @brief eCAL google::protobuf subscriber class. + * + * Subscriber template class for google::protobuf messages. For details see documentation of CSubscriber class. + * **/ + template + using CSubscriber = CMessageSubscriber>; + + /** @example person_rec.cpp + * This is an example how to use eCAL::CSubscriber to receive google::protobuf data with eCAL. To send the data, see @ref person_snd.cpp . + */ } } + diff --git a/ecal/core/include/ecal/msg/publisher.h b/ecal/core/include/ecal/msg/publisher.h index 91988c93c9..5f3b0625f2 100644 --- a/ecal/core/include/ecal/msg/publisher.h +++ b/ecal/core/include/ecal/msg/publisher.h @@ -59,10 +59,11 @@ namespace eCAL * @brief Constructor, that automatically intializes the Publisher. * This should be the preferred constructor. * - * @param topic_name_ Unique topic name. - * @param topic_info_ Struct that contains information of the datatype (name, encoding, description) of the topic. + * @param topic_name_ Unique topic name. + * @param data_type_info_ Topic data type information (encoding, type, descriptor). + * @param config_ Optional configuration parameters. **/ - CMsgPublisher(const std::string& topic_name_, const struct SDataTypeInformation& topic_info_) : CPublisher(topic_name_, topic_info_) + CMsgPublisher(const std::string& topic_name_, const struct SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = {}) : CPublisher(topic_name_, data_type_info_, config_) { } @@ -71,8 +72,9 @@ namespace eCAL * If no datatype information about the topic is available, this constructor can be used. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. **/ - explicit CMsgPublisher(const std::string& topic_name_) : CMsgPublisher(topic_name_, GetDataTypeInformation()) + explicit CMsgPublisher(const std::string& topic_name_, const Publisher::Configuration& config_ = {}) : CMsgPublisher(topic_name_, GetDataTypeInformation(), config_) { } @@ -101,14 +103,15 @@ namespace eCAL /** * @brief Creates this object. * - * @param topic_name_ Unique topic name. - * @param topic_info_ Associated datatype description. + * @param topic_name_ Unique topic name. + * @param data_type_info_ Topic data type information (encoding, type, descriptor). + * @param config_ Optional configuration parameters. * * @return True if it succeeds, false if it fails. **/ - bool Create(const std::string& topic_name_, const struct SDataTypeInformation& topic_info_) + bool Create(const std::string& topic_name_, const struct SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = {}) { - return(CPublisher::Create(topic_name_, topic_info_)); + return(CPublisher::Create(topic_name_, data_type_info_, config_)); } /** diff --git a/ecal/core/include/ecal/msg/string/publisher.h b/ecal/core/include/ecal/msg/string/publisher.h index 8555c49cfe..f1d45d58af 100644 --- a/ecal/core/include/ecal/msg/string/publisher.h +++ b/ecal/core/include/ecal/msg/string/publisher.h @@ -60,7 +60,7 @@ namespace eCAL // call the function via its class because it's a virtual function that is called in constructor/destructor,- // where the vtable is not created yet, or it's destructed. - explicit CPublisher(const std::string& topic_name_) : CMsgPublisher(topic_name_, GetDataTypeInformation()) + explicit CPublisher(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) : CMsgPublisher(topic_name_, GetDataTypeInformation(), config_) { } @@ -88,12 +88,13 @@ namespace eCAL * @brief Creates this object. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. * * @return True if it succeeds, false if it fails. **/ - bool Create(const std::string& topic_name_) + bool Create(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) { - return(CMsgPublisher::Create(topic_name_, GetDataTypeInformation())); + return(CMsgPublisher::Create(topic_name_, GetDataTypeInformation(), config_)); } private: @@ -104,11 +105,11 @@ namespace eCAL **/ SDataTypeInformation GetDataTypeInformation() const override { - SDataTypeInformation topic_info; - topic_info.encoding = "base"; - topic_info.name = "std::string"; + SDataTypeInformation data_type_info; + data_type_info.encoding = "base"; + data_type_info.name = "std::string"; // empty descriptor - return topic_info; + return data_type_info; } /** @@ -116,7 +117,7 @@ namespace eCAL * * @param msg_ The message object. * - * @return String site. + * @return String size. **/ size_t GetSize(const T& msg_) const override { @@ -143,7 +144,7 @@ namespace eCAL } }; /** @example minimal_snd.cpp - * This is an example how to use eCAL::CPublisher to send a std::string with eCAL. To receive the strings, see @ref minimal_rec.cpp . + * This is an example how to use eCAL::string::CPublisher to send a std::string with eCAL. To receive the strings, see @ref minimal_rec.cpp . **/ } } diff --git a/ecal/core/include/ecal/msg/string/subscriber.h b/ecal/core/include/ecal/msg/string/subscriber.h index 949acf37b0..0747cf977a 100644 --- a/ecal/core/include/ecal/msg/string/subscriber.h +++ b/ecal/core/include/ecal/msg/string/subscriber.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,84 +32,13 @@ namespace eCAL { - namespace string + namespace internal { - /** - * @brief eCAL subscriber class for standard strings. - * - * Subscriber class for STL standard strings. For details see documentation of CSubscriber class. - * - **/ template - class CSubscriber : public CMsgSubscriber + class StringDeserializer { public: - /** - * @brief Constructor. - **/ - CSubscriber() : CMsgSubscriber() - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - - // call the function via its class because it's a virtual function that is called in constructor/destructor,- - // where the vtable is not created yet, or it's destructed. - explicit CSubscriber(const std::string& topic_name_) : CMsgSubscriber(topic_name_, CSubscriber::GetDataTypeInformation()) - { - } - - /** - * @brief Destructor - **/ - ~CSubscriber() override - { - this->Destroy(); - } - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber(const CSubscriber&) = delete; - - /** - * @brief Copy Assignment is not available. - **/ - CSubscriber& operator=(const CSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CSubscriber(CSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CSubscriber& operator=(CSubscriber&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(CMsgSubscriber::Create(topic_name_, GetDataTypeInformation())); - } - - private: - /** - * @brief Get topic information of the protobuf message. - * - * @return Topic information. ("base", "std::string", "") - **/ - SDataTypeInformation GetDataTypeInformation() const override + static SDataTypeInformation GetDataTypeInformation() { SDataTypeInformation topic_info; topic_info.encoding = "base"; @@ -118,24 +47,28 @@ namespace eCAL return topic_info; } - /** - * @brief Copy the string object from char buffer. - * - * @param [out] msg_ The message object. - * @param buffer_ Target buffer. - * @param size_ Target buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Deserialize(T& msg_, const void* buffer_, size_t size_) const override + static bool Deserialize(T& msg_, const void* buffer_, size_t size_) { msg_ = std::string(static_cast(buffer_), size_); return true; } - }; - /** @example minimal_rec.cpp - * This is an example how to use eCAL::CSubscriber to receive a std::string with eCAL. To send the strings, see @ref minimal_snd.cpp . + } + + namespace string + { + + /** + * @brief eCAL subscriber class for standard strings. + * + * Subscriber class for STL standard strings. For details see documentation of CSubscriber class. + * **/ + template + using CSubscriber = CMessageSubscriber>; + + /** @example minimal_rec.cpp + * This is an example how to use eCAL::CSubscriber to receive a std::string with eCAL. To send the strings, see @ref minimal_snd.cpp . + */ } } diff --git a/ecal/core/include/ecal/msg/subscriber.h b/ecal/core/include/ecal/msg/subscriber.h index 8322bdf87c..b59d926daa 100644 --- a/ecal/core/include/ecal/msg/subscriber.h +++ b/ecal/core/include/ecal/msg/subscriber.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ namespace eCAL * **/ template - class CMsgSubscriber : public CSubscriber + class [[deprecated("Please use CMessageSubscriber instead")]] CMsgSubscriber : public CSubscriber { public: /** @@ -229,4 +229,196 @@ namespace eCAL std::mutex m_cb_callback_mutex; MsgReceiveCallbackT m_cb_callback; }; + + /** + * @brief eCAL abstract message subscriber class. + * + * Abstract subscriber template class for messages. + * This class has two template arguments, the actual type and a deserializer class. + * The deserializer class is responsible for providing datatype information, and providing a method to convert from `void*` to `T`. + * This allows to specify classes with common deserializers, e.g. like a ProtobufMessageSubscriber, StringMessageSubscriber etc. + * + **/ + template + class CMessageSubscriber final : public CSubscriber + { + public: + /** + * @brief Constructor. + **/ + CMessageSubscriber() : CSubscriber() + { + } + + /** + * @brief Constructor. + * + * @param topic_name_ Unique topic name. + **/ + CMessageSubscriber(const std::string& topic_name_) : CSubscriber() + , m_deserializer() + { + SDataTypeInformation topic_info = m_deserializer.GetDataTypeInformation(); + CSubscriber::Create(topic_name_, topic_info); + } + + ~CMessageSubscriber() noexcept + { + Destroy(); + }; + + /** + * @brief Copy Constructor is not available. + **/ + CMessageSubscriber(const CMessageSubscriber&) = delete; + + /** + * @brief Copy Constructor is not available. + **/ + CMessageSubscriber& operator=(const CMessageSubscriber&) = delete; + + /** + * @brief Move Constructor + **/ + CMessageSubscriber(CMessageSubscriber&& rhs) + : CSubscriber(std::move(rhs)) + , m_cb_callback(std::move(rhs.m_cb_callback)) + , m_deserializer(std::move(rhs.m_deserializer)) + { + bool has_callback = (m_cb_callback != nullptr); + + if (has_callback) + { + // the callback bound to the CSubscriber belongs to rhs, bind to this callback instead + CSubscriber::RemReceiveCallback(); + auto callback = std::bind(&CMessageSubscriber::ReceiveCallback, this, std::placeholders::_1, std::placeholders::_2); + CSubscriber::AddReceiveCallback(callback); + } + } + + /** + * @brief Move assignment + **/ + CMessageSubscriber& operator=(CMessageSubscriber&& rhs) + { + Destroy(); + + CSubscriber::operator=(std::move(rhs)); + + m_cb_callback = std::move(rhs.m_cb_callback); + m_deserializer = std::move(rhs.m_deserializer); + + bool has_callback(m_cb_callback != nullptr); + + if (has_callback) + { + // the callback bound to the CSubscriber belongs to rhs, bind to this callback instead; + CSubscriber::RemReceiveCallback(); + auto callback = std::bind(&CMessageSubscriber::ReceiveCallback, this, std::placeholders::_1, std::placeholders::_2); + CSubscriber::AddReceiveCallback(callback); + } + + return *this; + } + + /** + * @brief Destroys this object. + * + * @return true if it succeeds, false if it fails. + **/ + bool Destroy() + { + RemReceiveCallback(); + return(CSubscriber::Destroy()); + } + + /** + * @brief Receive deserialized message. + * + * @param [out] msg_ The message object. + * @param [out] time_ Optional receive time stamp. + * @param rcv_timeout_ Receive timeout in ms. + * + * @return True if a message could received, false otherwise. + **/ + bool Receive(T& msg_, long long* time_ = nullptr, int rcv_timeout_ = 0) + { + std::string rec_buf; + bool success = CSubscriber::ReceiveBuffer(rec_buf, time_, rcv_timeout_); + if (!success) return(false); + // In the future, I would like to get m_datatype_info from the ReceiveBuffer fuunction! + return(m_deserializer.Deserialize(msg_, rec_buf.c_str(), rec_buf.size())); + } + + /** + * @brief eCAL message receive callback function + * + * @param topic_name_ Topic name of the data source (publisher). + * @param msg_ Message content. + * @param time_ Message time stamp. + * @param clock_ Message writer clock. + * @param id_ Message id. + **/ + typedef std::function MsgReceiveCallbackT; + + /** + * @brief Add receive callback for incoming messages. + * + * @param callback_ The callback function. + * + * @return True if it succeeds, false if it fails. + **/ + bool AddReceiveCallback(MsgReceiveCallbackT callback_) + { + RemReceiveCallback(); + + { + std::lock_guard callback_lock(m_cb_callback_mutex); + m_cb_callback = callback_; + } + auto callback = std::bind(&CMessageSubscriber::ReceiveCallback, this, std::placeholders::_1, std::placeholders::_2); + return(CSubscriber::AddReceiveCallback(callback)); + } + + /** + * @brief Remove receive callback for incoming messages. + * + * @return True if it succeeds, false if it fails. + **/ + bool RemReceiveCallback() + { + bool ret = CSubscriber::RemReceiveCallback(); + + std::lock_guard callback_lock(m_cb_callback_mutex); + if (m_cb_callback == nullptr) return(false); + m_cb_callback = nullptr; + return(ret); + } + + private: + void ReceiveCallback(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_) + { + MsgReceiveCallbackT fn_callback = nullptr; + { + std::lock_guard callback_lock(m_cb_callback_mutex); + fn_callback = m_cb_callback; + } + + if (fn_callback == nullptr) return; + + T msg; + // In the future, I would like to get m_datatype_info from the ReceiveBuffer function! + if (m_deserializer.Deserialize(msg, data_->buf, data_->size)) + { + (fn_callback)(topic_name_, msg, data_->time, data_->clock, data_->id); + } + } + + std::mutex m_cb_callback_mutex; + MsgReceiveCallbackT m_cb_callback; + Deserializer m_deserializer; + }; + + + } diff --git a/ecal/core/src/cimpl/ecal_core_cimpl.cpp b/ecal/core/src/cimpl/ecal_core_cimpl.cpp index 2b6b51434d..ead763f96d 100644 --- a/ecal/core/src/cimpl/ecal_core_cimpl.cpp +++ b/ecal/core/src/cimpl/ecal_core_cimpl.cpp @@ -56,9 +56,9 @@ extern "C" return(eCAL::SetUnitName(unit_name_)); } - ECALC_API int eCAL_Finalize(unsigned int components_) + ECALC_API int eCAL_Finalize() { - return(eCAL::Finalize(components_)); + return(eCAL::Finalize()); } ECALC_API int eCAL_IsInitialized(unsigned int component_) diff --git a/ecal/core/src/cimpl/ecal_publisher_cimpl.cpp b/ecal/core/src/cimpl/ecal_publisher_cimpl.cpp index 3d1acdfc2f..cec91d2776 100644 --- a/ecal/core/src/cimpl/ecal_publisher_cimpl.cpp +++ b/ecal/core/src/cimpl/ecal_publisher_cimpl.cpp @@ -89,22 +89,6 @@ extern "C" return(0); } - ECALC_API int eCAL_Pub_ShareType(ECAL_HANDLE handle_, int state_) - { - if (handle_ == nullptr) return(0); - auto* pub = static_cast(handle_); - pub->ShareType(state_ != 0); - return(1); - } - - ECALC_API int eCAL_Pub_ShareDescription(ECAL_HANDLE handle_, int state_) - { - if (handle_ == nullptr) return(0); - auto* pub = static_cast(handle_); - pub->ShareDescription(state_ != 0); - return(1); - } - ECALC_API int eCAL_Pub_SetID(ECAL_HANDLE handle_, long long id_) { if (handle_ == nullptr) return(0); diff --git a/ecal/core/src/ecal.cpp b/ecal/core/src/ecal.cpp index 365d29ee2e..4da3d25a1f 100644 --- a/ecal/core/src/ecal.cpp +++ b/ecal/core/src/ecal.cpp @@ -191,16 +191,14 @@ namespace eCAL /** * @brief Finalize eCAL API. * - * @param components_ Defines which component to finalize. - * * @return Zero if succeeded, 1 if already finalized, -1 if failed. **/ - int Finalize(unsigned int components_) + int Finalize() { if (g_globals_ctx == nullptr) return 1; g_globals_ctx_ref_cnt--; if (g_globals_ctx_ref_cnt > 0) return 0; - int const ret = g_globals()->Finalize(components_); + int const ret = g_globals()->Finalize(); delete g_globals_ctx; g_globals_ctx = nullptr; return(ret); diff --git a/ecal/core/src/ecal_globals.cpp b/ecal/core/src/ecal_globals.cpp index 5f404bbb8a..41db952be2 100644 --- a/ecal/core/src/ecal_globals.cpp +++ b/ecal/core/src/ecal_globals.cpp @@ -42,7 +42,7 @@ namespace eCAL CGlobals::~CGlobals() { - Finalize(Init::All); + Finalize(); } void CGlobals::SetEcalConfig(Config::eCALConfig& ecal_config_) @@ -220,14 +220,14 @@ namespace eCAL if (memfile_pool_instance) memfile_pool_instance->Create(); #endif #if ECAL_CORE_SUBSCRIBER - if (subgate_instance && ((components_ & Init::Subscriber) != 0u)) subgate_instance->Create(); + if (subgate_instance && ((components_ & Init::Subscriber) != 0u)) subgate_instance->Start(); #endif #if ECAL_CORE_PUBLISHER if (pubgate_instance && ((components_ & Init::Publisher) != 0u)) pubgate_instance->Create(); #endif #if ECAL_CORE_SERVICE - if (servicegate_instance && ((components_ & Init::Service) != 0u)) servicegate_instance->Create(); - if (clientgate_instance && ((components_ & Init::Service) != 0u)) clientgate_instance->Create(); + if (servicegate_instance && ((components_ & Init::Service) != 0u)) servicegate_instance->Start(); + if (clientgate_instance && ((components_ & Init::Service) != 0u)) clientgate_instance->Start(); #endif #if ECAL_CORE_TIMEPLUGIN if (timegate_instance && ((components_ & Init::TimeSync) != 0u)) timegate_instance->Create(CTimeGate::eTimeSyncMode::realtime); @@ -280,7 +280,7 @@ namespace eCAL } } - int CGlobals::Finalize(unsigned int /*components_*/) + int CGlobals::Finalize() { if (!initialized) return(1); @@ -298,14 +298,14 @@ namespace eCAL // raw pointers to the gate's functions, so we must make sure that everything // has been executed, before we delete the gates. eCAL::service::ServiceManager::instance()->stop(); - if (clientgate_instance) clientgate_instance->Destroy(); - if (servicegate_instance) servicegate_instance->Destroy(); + if (clientgate_instance) clientgate_instance->Stop(); + if (servicegate_instance) servicegate_instance->Stop(); #endif #if ECAL_CORE_PUBLISHER if (pubgate_instance) pubgate_instance->Destroy(); #endif #if ECAL_CORE_SUBSCRIBER - if (subgate_instance) subgate_instance->Destroy(); + if (subgate_instance) subgate_instance->Stop(); #endif if (descgate_instance) { diff --git a/ecal/core/src/ecal_globals.h b/ecal/core/src/ecal_globals.h index 2078bde8c7..48bca65cc5 100644 --- a/ecal/core/src/ecal_globals.h +++ b/ecal/core/src/ecal_globals.h @@ -69,7 +69,7 @@ namespace eCAL unsigned int GetComponents() const { return(components); }; - int Finalize(unsigned int components_); + int Finalize(); const std::unique_ptr& config() { return config_instance; }; const std::unique_ptr& log() { return log_instance; }; diff --git a/ecal/core/src/ecal_util.cpp b/ecal/core/src/ecal_util.cpp index 3bb2047e2d..4df41fb12a 100644 --- a/ecal/core/src/ecal_util.cpp +++ b/ecal/core/src/ecal_util.cpp @@ -17,6 +17,7 @@ * ========================= eCAL LICENSE ================================= */ +#include "ecal_def.h" #include "ecal_globals.h" #include "ecal_event.h" #include "registration/ecal_registration_receiver.h" diff --git a/ecal/core/src/monitoring/ecal_monitoring_impl.cpp b/ecal/core/src/monitoring/ecal_monitoring_impl.cpp index 928c4e28fd..73fac8cc29 100644 --- a/ecal/core/src/monitoring/ecal_monitoring_impl.cpp +++ b/ecal/core/src/monitoring/ecal_monitoring_impl.cpp @@ -202,14 +202,14 @@ namespace eCAL const int process_id = sample_topic.pid; const std::string& topic_name = sample_topic.tname; const int32_t topic_size = sample_topic.tsize; - bool topic_tlayer_ecal_udp_mc(false); + bool topic_tlayer_ecal_udp(false); bool topic_tlayer_ecal_shm(false); bool topic_tlayer_ecal_tcp(false); for (const auto& layer : sample_topic.tlayer) { - topic_tlayer_ecal_udp_mc |= (layer.type == tl_ecal_udp_mc) && layer.confirmed; - topic_tlayer_ecal_shm |= (layer.type == tl_ecal_shm) && layer.confirmed; - topic_tlayer_ecal_tcp |= (layer.type == tl_ecal_tcp) && layer.confirmed; + topic_tlayer_ecal_udp |= (layer.type == tl_ecal_udp) && layer.confirmed; + topic_tlayer_ecal_shm |= (layer.type == tl_ecal_shm) && layer.confirmed; + topic_tlayer_ecal_tcp |= (layer.type == tl_ecal_tcp) && layer.confirmed; } const int32_t connections_loc = sample_topic.connections_loc; const int32_t connections_ext = sample_topic.connections_ext; @@ -306,7 +306,7 @@ namespace eCAL { eCAL::Monitoring::TLayer tlayer; tlayer.type = eCAL::Monitoring::tl_ecal_udp_mc; - tlayer.confirmed = topic_tlayer_ecal_udp_mc; + tlayer.confirmed = topic_tlayer_ecal_udp; TopicInfo.tlayer.push_back(tlayer); } // tlayer shm diff --git a/ecal/core/src/pubsub/ecal_pubgate.cpp b/ecal/core/src/pubsub/ecal_pubgate.cpp index e285cef2c6..7a075f7c85 100644 --- a/ecal/core/src/pubsub/ecal_pubgate.cpp +++ b/ecal/core/src/pubsub/ecal_pubgate.cpp @@ -58,12 +58,13 @@ namespace eCAL { if(!m_created) return; - // destroy all remaining publisher + // stop & destroy all remaining publisher const std::unique_lock lock(m_topic_name_datawriter_sync); - for (auto iter = m_topic_name_datawriter_map.begin(); iter != m_topic_name_datawriter_map.end(); ++iter) + for (const auto& datawriter : m_topic_name_datawriter_map) { - iter->second->Destroy(); + datawriter.second->Stop(); } + m_topic_name_datawriter_map.clear(); m_created = false; } @@ -109,69 +110,43 @@ namespace eCAL return(ret_state); } - void CPubGate::ApplyLocSubRegistration(const Registration::Sample& ecal_sample_) + void CPubGate::ApplySubRegistration(const Registration::Sample& ecal_sample_) { if(!m_created) return; const auto& ecal_topic = ecal_sample_.topic; const std::string& topic_name = ecal_topic.tname; - CDataWriter::SLocalSubscriptionInfo subscription_info; + // check topic name + if (topic_name.empty()) return; + + CDataWriter::SSubscriptionInfo subscription_info; + subscription_info.host_name = ecal_topic.hname; subscription_info.topic_id = ecal_topic.tid; - subscription_info.process_id = std::to_string(ecal_topic.pid); + subscription_info.process_id = ecal_topic.pid; const SDataTypeInformation topic_information = ecal_topic.tdatatype; - std::string reader_par; -#if 0 - for (const auto& layer : ecal_sample.tlayer()) - { - // layer parameter as protobuf message - // this parameter is not used at all currently - // for local subscriber registrations - reader_par = layer.par_layer().SerializeAsString(); - } -#endif - - // register local subscriber - const std::shared_lock lock(m_topic_name_datawriter_sync); - auto res = m_topic_name_datawriter_map.equal_range(topic_name); - for(TopicNameDataWriterMapT::const_iterator iter = res.first; iter != res.second; ++iter) - { - iter->second->ApplyLocSubscription(subscription_info, topic_information, reader_par); - } - } - - void CPubGate::ApplyLocSubUnregistration(const Registration::Sample& ecal_sample_) - { - if (!m_created) return; - - const auto& ecal_sample = ecal_sample_.topic; - const std::string& topic_name = ecal_sample.tname; - CDataWriter::SLocalSubscriptionInfo subscription_info; - subscription_info.topic_id = ecal_sample.tid; - subscription_info.process_id = std::to_string(ecal_sample.pid); - - // unregister local subscriber - const std::shared_lock lock(m_topic_name_datawriter_sync); - auto res = m_topic_name_datawriter_map.equal_range(topic_name); - for (TopicNameDataWriterMapT::const_iterator iter = res.first; iter != res.second; ++iter) + CDataWriter::SLayerStates layer_states; + for (const auto& layer : ecal_topic.tlayer) { - iter->second->RemoveLocSubscription(subscription_info); + if (layer.confirmed) + { + switch (layer.type) + { + case TLayer::tlayer_udp_mc: + layer_states.udp = true; + break; + case TLayer::tlayer_shm: + layer_states.shm = true; + break; + case TLayer::tlayer_tcp: + layer_states.tcp = true; + break; + default: + break; + } + } } - } - - void CPubGate::ApplyExtSubRegistration(const Registration::Sample& ecal_sample_) - { - if(!m_created) return; - - const auto& ecal_topic = ecal_sample_.topic; - const std::string& topic_name = ecal_topic.tname; - - CDataWriter::SExternalSubscriptionInfo subscription_info; - subscription_info.host_name = ecal_topic.hname; - subscription_info.topic_id = ecal_topic.tid; - subscription_info.process_id = std::to_string(ecal_topic.pid); - const SDataTypeInformation topic_information = ecal_topic.tdatatype; std::string reader_par; #if 0 @@ -179,37 +154,41 @@ namespace eCAL { // layer parameter as protobuf message // this parameter is not used at all currently - // for external subscriber registrations + // for subscriber registrations reader_par = layer.par_layer().SerializeAsString(); } #endif - // register external subscriber + // register subscriber const std::shared_lock lock(m_topic_name_datawriter_sync); auto res = m_topic_name_datawriter_map.equal_range(topic_name); for(TopicNameDataWriterMapT::const_iterator iter = res.first; iter != res.second; ++iter) { - iter->second->ApplyExtSubscription(subscription_info, topic_information, reader_par); + iter->second->ApplySubscription(subscription_info, topic_information, layer_states, reader_par); } } - void CPubGate::ApplyExtSubUnregistration(const Registration::Sample& ecal_sample_) + void CPubGate::ApplySubUnregistration(const Registration::Sample& ecal_sample_) { if (!m_created) return; - const auto& ecal_sample = ecal_sample_.topic; - const std::string& topic_name = ecal_sample.tname; - CDataWriter::SExternalSubscriptionInfo subscription_info; - subscription_info.host_name = ecal_sample.hname; - subscription_info.topic_id = ecal_sample.tid; - subscription_info.process_id = std::to_string(ecal_sample.pid); + const auto& ecal_topic = ecal_sample_.topic; + const std::string& topic_name = ecal_topic.tname; + + // check topic name + if (topic_name.empty()) return; + + CDataWriter::SSubscriptionInfo subscription_info; + subscription_info.host_name = ecal_topic.hname; + subscription_info.topic_id = ecal_topic.tid; + subscription_info.process_id = ecal_topic.pid; - // unregister external subscriber + // unregister subscriber const std::shared_lock lock(m_topic_name_datawriter_sync); auto res = m_topic_name_datawriter_map.equal_range(topic_name); for (TopicNameDataWriterMapT::const_iterator iter = res.first; iter != res.second; ++iter) { - iter->second->RemoveExtSubscription(subscription_info); + iter->second->RemoveSubscription(subscription_info); } } diff --git a/ecal/core/src/pubsub/ecal_pubgate.h b/ecal/core/src/pubsub/ecal_pubgate.h index ba7ec50948..770c6c84e2 100644 --- a/ecal/core/src/pubsub/ecal_pubgate.h +++ b/ecal/core/src/pubsub/ecal_pubgate.h @@ -53,11 +53,8 @@ namespace eCAL bool Register(const std::string& topic_name_, const std::shared_ptr& datawriter_); bool Unregister(const std::string& topic_name_, const std::shared_ptr& datawriter_); - void ApplyLocSubRegistration(const Registration::Sample& ecal_sample_); - void ApplyLocSubUnregistration(const Registration::Sample& ecal_sample_); - - void ApplyExtSubRegistration(const Registration::Sample& ecal_sample_); - void ApplyExtSubUnregistration(const Registration::Sample& ecal_sample_); + void ApplySubRegistration(const Registration::Sample& ecal_sample_); + void ApplySubUnregistration(const Registration::Sample& ecal_sample_); void RefreshRegistrations(); diff --git a/ecal/core/src/pubsub/ecal_publisher.cpp b/ecal/core/src/pubsub/ecal_publisher.cpp index 9a917e8983..3369d43bba 100644 --- a/ecal/core/src/pubsub/ecal_publisher.cpp +++ b/ecal/core/src/pubsub/ecal_publisher.cpp @@ -38,19 +38,18 @@ namespace eCAL CPublisher::CPublisher() : m_datawriter(nullptr), m_id(0), - m_created(false), - m_initialized(false) + m_created(false) { } - CPublisher::CPublisher(const std::string& topic_name_, const SDataTypeInformation& data_type_info_) + CPublisher::CPublisher(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_) : CPublisher() { - CPublisher::Create(topic_name_, data_type_info_); + CPublisher::Create(topic_name_, data_type_info_, config_); } - CPublisher::CPublisher(const std::string& topic_name_) - : CPublisher(topic_name_, SDataTypeInformation{}) + CPublisher::CPublisher(const std::string& topic_name_, const Publisher::Configuration& config_) + : CPublisher(topic_name_, SDataTypeInformation{}, config_) {} CPublisher::~CPublisher() @@ -64,11 +63,9 @@ namespace eCAL CPublisher::CPublisher(CPublisher&& rhs) noexcept : m_datawriter(std::move(rhs.m_datawriter)), m_id(rhs.m_id), - m_created(rhs.m_created), - m_initialized(rhs.m_initialized) + m_created(rhs.m_created) { - rhs.m_created = false; - rhs.m_initialized = false; + rhs.m_created = false; } /** @@ -79,50 +76,28 @@ namespace eCAL // Call destroy, to clean up the current state, then afterwards move all elements Destroy(); - m_datawriter = std::move(rhs.m_datawriter); - m_id = rhs.m_id; - m_created = rhs.m_created; - m_initialized = rhs.m_initialized; + m_datawriter = std::move(rhs.m_datawriter); + m_id = rhs.m_id; + m_created = rhs.m_created; - rhs.m_created = false; - rhs.m_initialized = false; + rhs.m_created = false; return *this; } - bool CPublisher::Create(const std::string& topic_name_, const SDataTypeInformation& data_type_info_) + bool CPublisher::Create(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_) { - if (m_created) return(false); - if (topic_name_.empty()) return(false); - if (g_globals() == nullptr) return(false); - - // initialize globals - if (g_globals()->IsInitialized(Init::Publisher) == 0) - { - g_globals()->Initialize(Init::Publisher); - m_initialized = true; - } - - // create data writer - m_datawriter = std::make_shared(); - if (!m_datawriter->Create(topic_name_, data_type_info_)) - { -#ifndef NDEBUG - // log it - if (g_log() != nullptr) g_log()->Log(log_level_debug1, topic_name_ + "::CPublisher::Create - FAILED"); -#endif - return(false); - } -#ifndef NDEBUG - // log it - if (g_log() != nullptr) g_log()->Log(log_level_debug1, topic_name_ + "::CPublisher::Create - SUCCESS"); -#endif - // register publisher gateway (for publisher memory file and event name) + if (m_created) return(false); + if (topic_name_.empty()) return(false); + + // create datawriter + m_datawriter = std::make_shared(topic_name_, data_type_info_, config_); + + // register datawriter g_pubgate()->Register(topic_name_, m_datawriter); // we made it :-) m_created = true; - return(m_created); } @@ -133,33 +108,22 @@ namespace eCAL bool CPublisher::Destroy() { - if(!m_created) return(false); - if(g_globals() == nullptr) return(false); - - // destroy data writer - m_datawriter->Destroy(); + if(!m_created) return(false); - // unregister data writer + // unregister datawriter if(g_pubgate() != nullptr) g_pubgate()->Unregister(m_datawriter->GetTopicName(), m_datawriter); #ifndef NDEBUG // log it if (g_log() != nullptr) g_log()->Log(log_level_debug1, std::string(m_datawriter->GetTopicName() + "::CPublisher::Destroy")); #endif - // free datawriter + // stop & destroy datawriter + m_datawriter->Stop(); m_datawriter.reset(); // we made it :-) m_created = false; - // if we initialize the globals then we finalize - // here to decrease reference counter - if (m_initialized) - { - g_globals()->Finalize(Init::Publisher); - m_initialized = false; - } - return(true); } @@ -181,20 +145,6 @@ namespace eCAL return m_datawriter->ClearAttribute(attr_name_); } - bool CPublisher::ShareType(bool state_ /*= true*/) - { - if (m_datawriter == nullptr) return false; - m_datawriter->ShareType(state_); - return true; - } - - bool CPublisher::ShareDescription(bool state_ /*= true*/) - { - if (m_datawriter == nullptr) return false; - m_datawriter->ShareDescription(state_); - return true; - } - bool CPublisher::SetID(long long id_) { m_id = id_; @@ -289,4 +239,4 @@ namespace eCAL return(out.str()); } -} +} \ No newline at end of file diff --git a/ecal/core/src/pubsub/ecal_publisher_config.cpp b/ecal/core/src/pubsub/ecal_publisher_config.cpp new file mode 100644 index 0000000000..949980e690 --- /dev/null +++ b/ecal/core/src/pubsub/ecal_publisher_config.cpp @@ -0,0 +1,53 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCAL publisher configuration +**/ + +#include +#include + +namespace eCAL +{ + namespace Publisher + { + Configuration::Configuration() : + share_topic_type(eCAL::Config::IsTopicTypeSharingEnabled()), + share_topic_description(eCAL::Config::IsTopicDescriptionSharingEnabled()) + { + // shm config + shm.enable = eCAL::Config::GetPublisherShmMode() != TLayer::eSendMode::smode_off; + shm.zero_copy_mode = eCAL::Config::IsMemfileZerocopyEnabled(); + shm.acknowledge_timeout_ms = eCAL::Config::GetMemfileAckTimeoutMs(); + + shm.memfile_min_size_bytes = eCAL::Config::GetMemfileMinsizeBytes(); + shm.memfile_reserve_percent = eCAL::Config::GetMemfileOverprovisioningPercentage(); + shm.memfile_buffer_count = eCAL::Config::GetMemfileBufferCount(); + + // udp config + udp.enable = eCAL::Config::GetPublisherUdpMulticastMode() != TLayer::eSendMode::smode_off; + udp.loopback = false; // TODO: make this configurable + udp.sndbuf_size_bytes = eCAL::Config::GetUdpMulticastSndBufSizeBytes(); + + // tcp config + tcp.enable = eCAL::Config::GetPublisherTcpMode() != TLayer::eSendMode::smode_off; + } + } +} diff --git a/ecal/core/src/pubsub/ecal_subgate.cpp b/ecal/core/src/pubsub/ecal_subgate.cpp index 5c13504954..44261b5e63 100644 --- a/ecal/core/src/pubsub/ecal_subgate.cpp +++ b/ecal/core/src/pubsub/ecal_subgate.cpp @@ -46,10 +46,10 @@ namespace eCAL CSubGate::~CSubGate() { - Destroy(); + Stop(); } - void CSubGate::Create() + void CSubGate::Start() { if(m_created) return; @@ -59,16 +59,17 @@ namespace eCAL m_created = true; } - void CSubGate::Destroy() + void CSubGate::Stop() { if(!m_created) return; - // destroy all remaining subscriber + // stop & destroy all remaining subscriber const std::unique_lock lock(m_topic_name_datareader_sync); - for (auto iter = m_topic_name_datareader_map.begin(); iter != m_topic_name_datareader_map.end(); ++iter) + for (const auto& datareader : m_topic_name_datareader_map) { - iter->second->Destroy(); + datareader.second->Stop(); } + m_topic_name_datareader_map.clear(); m_created = false; } @@ -212,66 +213,45 @@ namespace eCAL return (applied_size > 0); } - void CSubGate::ApplyLocPubRegistration(const Registration::Sample& ecal_sample_) + void CSubGate::ApplyPubRegistration(const Registration::Sample& ecal_sample_) { if(!m_created) return; - // check topic name const auto& ecal_topic = ecal_sample_.topic; const std::string& topic_name = ecal_topic.tname; - if (topic_name.empty()) return; - // get topic id - const std::string& topic_id = ecal_topic.tid; + // check topic name + if (topic_name.empty()) return; - // get process id - const std::string process_id = std::to_string(ecal_sample_.topic.pid); + CDataReader::SPublicationInfo publication_info; + publication_info.host_name = ecal_topic.hname; + publication_info.topic_id = ecal_topic.tid; + publication_info.process_id = ecal_topic.pid; + const SDataTypeInformation topic_information = ecal_topic.tdatatype; - // handle local publisher connection - const std::shared_lock lock(m_topic_name_datareader_sync); - auto res = m_topic_name_datareader_map.equal_range(topic_name); - for (auto iter = res.first; iter != res.second; ++iter) + CDataReader::SLayerStates layer_states; + for (const auto& layer : ecal_topic.tlayer) { - // apply layer specific parameter - for (const auto& tlayer : ecal_sample_.topic.tlayer) + if (layer.confirmed) { - iter->second->ApplyLocLayerParameter(process_id, topic_id, tlayer.type, tlayer.par_layer); + switch (layer.type) + { + case TLayer::tlayer_udp_mc: + layer_states.udp = true; + break; + case TLayer::tlayer_shm: + layer_states.shm = true; + break; + case TLayer::tlayer_tcp: + layer_states.tcp = true; + break; + default: + break; + } } - // inform for local publisher connection - iter->second->ApplyLocPublication(process_id, topic_id, ecal_topic.tdatatype); } - } - void CSubGate::ApplyLocPubUnregistration(const Registration::Sample& ecal_sample_) - { - if (!m_created) return; - - // check topic name - const auto& ecal_topic = ecal_sample_.topic; - const std::string& topic_name = ecal_topic.tname; - const std::string& topic_id = ecal_topic.tid; - const std::string process_id = std::to_string(ecal_sample_.topic.pid); - - // unregister local publisher - const std::shared_lock lock(m_topic_name_datareader_sync); - auto res = m_topic_name_datareader_map.equal_range(topic_name); - for (auto iter = res.first; iter != res.second; ++iter) - { - iter->second->RemoveLocPublication(process_id, topic_id); - } - } - - void CSubGate::ApplyExtPubRegistration(const Registration::Sample& ecal_sample_) - { - if(!m_created) return; - - const auto& ecal_topic = ecal_sample_.topic; - const std::string& host_name = ecal_topic.hname; - const std::string& topic_name = ecal_topic.tname; - const std::string& topic_id = ecal_topic.tid; - const std::string process_id = std::to_string(ecal_topic.pid); - - // handle external publisher connection + // register publisher const std::shared_lock lock(m_topic_name_datareader_sync); auto res = m_topic_name_datareader_map.equal_range(topic_name); for (auto iter = res.first; iter != res.second; ++iter) @@ -279,30 +259,34 @@ namespace eCAL // apply layer specific parameter for (const auto& tlayer : ecal_sample_.topic.tlayer) { - iter->second->ApplyExtLayerParameter(host_name, tlayer.type, tlayer.par_layer); + iter->second->ApplyLayerParameter(publication_info, tlayer.type, tlayer.par_layer); } - - // inform for external publisher connection - iter->second->ApplyExtPublication(host_name, process_id, topic_id, ecal_topic.tdatatype); + // inform for publisher connection + iter->second->ApplyPublication(publication_info, topic_information, layer_states); } } - void CSubGate::ApplyExtPubUnregistration(const Registration::Sample& ecal_sample_) + void CSubGate::ApplyPubUnregistration(const Registration::Sample& ecal_sample_) { if (!m_created) return; - const auto& ecal_sample = ecal_sample_.topic; - const std::string& host_name = ecal_sample.hname; - const std::string& topic_name = ecal_sample.tname; - const std::string& topic_id = ecal_sample.tid; - const std::string process_id = std::to_string(ecal_sample.pid); + const auto& ecal_topic = ecal_sample_.topic; + const std::string& topic_name = ecal_topic.tname; + + // check topic name + if (topic_name.empty()) return; + + CDataReader::SPublicationInfo publication_info; + publication_info.host_name = ecal_topic.hname; + publication_info.topic_id = ecal_topic.tid; + publication_info.process_id = ecal_topic.pid; - // unregister local subscriber + // unregister publisher const std::shared_lock lock(m_topic_name_datareader_sync); auto res = m_topic_name_datareader_map.equal_range(topic_name); for (auto iter = res.first; iter != res.second; ++iter) { - iter->second->RemoveExtPublication(host_name, process_id, topic_id); + iter->second->RemovePublication(publication_info); } } diff --git a/ecal/core/src/pubsub/ecal_subgate.h b/ecal/core/src/pubsub/ecal_subgate.h index 21442f2d26..1e78578a71 100644 --- a/ecal/core/src/pubsub/ecal_subgate.h +++ b/ecal/core/src/pubsub/ecal_subgate.h @@ -40,8 +40,8 @@ namespace eCAL CSubGate(); ~CSubGate(); - void Create(); - void Destroy(); + void Start(); + void Stop(); bool Register(const std::string& topic_name_, const std::shared_ptr& datareader_); bool Unregister(const std::string& topic_name_, const std::shared_ptr& datareader_); @@ -51,11 +51,8 @@ namespace eCAL bool ApplySample(const char* serialized_sample_data_, size_t serialized_sample_size_, eTLayerType layer_); bool ApplySample(const std::string& topic_name_, const std::string& topic_id_, const char* buf_, size_t len_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_); - void ApplyLocPubRegistration(const Registration::Sample& ecal_sample_); - void ApplyLocPubUnregistration(const Registration::Sample& ecal_sample_); - - void ApplyExtPubRegistration(const Registration::Sample& ecal_sample_); - void ApplyExtPubUnregistration(const Registration::Sample& ecal_sample_); + void ApplyPubRegistration(const Registration::Sample& ecal_sample_); + void ApplyPubUnregistration(const Registration::Sample& ecal_sample_); void RefreshRegistrations(); diff --git a/ecal/core/src/pubsub/ecal_subscriber.cpp b/ecal/core/src/pubsub/ecal_subscriber.cpp index 898af14e3b..d1338001fa 100644 --- a/ecal/core/src/pubsub/ecal_subscriber.cpp +++ b/ecal/core/src/pubsub/ecal_subscriber.cpp @@ -36,8 +36,7 @@ namespace eCAL { CSubscriber::CSubscriber() : m_datareader(nullptr), - m_created(false), - m_initialized(false) + m_created(false) { } @@ -58,11 +57,9 @@ namespace eCAL CSubscriber::CSubscriber(CSubscriber&& rhs) noexcept : m_datareader(std::move(rhs.m_datareader)), - m_created(rhs.m_created), - m_initialized(rhs.m_initialized) + m_created(rhs.m_created) { - rhs.m_created = false; - rhs.m_initialized = false; + rhs.m_created = false; } CSubscriber& CSubscriber::operator=(CSubscriber&& rhs) noexcept @@ -70,12 +67,10 @@ namespace eCAL // Call destroy, to clean up the current state, then afterwards move all elements Destroy(); - m_datareader = std::move(rhs.m_datareader); - m_created = rhs.m_created; - m_initialized = rhs.m_initialized; + m_datareader = std::move(rhs.m_datareader); + m_created = rhs.m_created; - rhs.m_created = false; - rhs.m_initialized = false; + rhs.m_created = false; return *this; } @@ -87,73 +82,42 @@ namespace eCAL bool CSubscriber::Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_) { - if (m_created) return(false); - if (g_globals() == nullptr) return(false); - if (topic_name_.empty()) return(false); - - // initialize globals - if (g_globals()->IsInitialized(Init::Subscriber) == 0) - { - g_globals()->Initialize(Init::Subscriber); - m_initialized = true; - } - - // create data reader - m_datareader = std::make_shared(); - // create it - if (!m_datareader->Create(topic_name_, topic_info_)) - { -#ifndef NDEBUG - // log it - if (g_log() != nullptr) g_log()->Log(log_level_debug1, std::string(topic_name_ + "::CSubscriber::Create - FAILED")); -#endif - return(false); - } -#ifndef NDEBUG - // log it - if (g_log() != nullptr) g_log()->Log(log_level_debug1, std::string(topic_name_ + "::CSubscriber::Create - SUCCESS")); -#endif - // register to subscriber gateway for publisher memory file receive thread + if (m_created) return(false); + if (topic_name_.empty()) return(false); + + // create datareader + m_datareader = std::make_shared(topic_name_, topic_info_); + + // register datareader g_subgate()->Register(topic_name_, m_datareader); // we made it :-) m_created = true; - return(m_created); } bool CSubscriber::Destroy() { - if(!m_created) return(false); - if(g_globals() == nullptr) return(false); + if(!m_created) return(false); // remove receive callback RemReceiveCallback(); - // first unregister data reader + // unregister datareader if(g_subgate() != nullptr) g_subgate()->Unregister(m_datareader->GetTopicName(), m_datareader); + #ifndef NDEBUG // log it if (g_log() != nullptr) g_log()->Log(log_level_debug1, std::string(m_datareader->GetTopicName() + "::CSubscriber::Destroy")); #endif - // destroy local data reader - m_datareader->Destroy(); - - // free datareader + // stop & destroy datareader + m_datareader->Stop(); m_datareader.reset(); - + // we made it :-) m_created = false; - // if we initialize the globals then we finalize - // here to decrease reference counter - if (m_initialized) - { - g_globals()->Finalize(Init::Subscriber); - m_initialized = false; - } - return(true); } diff --git a/ecal/core/src/readwrite/ecal_reader.cpp b/ecal/core/src/readwrite/ecal_reader.cpp index 71ad234d0e..9fedd736f9 100644 --- a/ecal/core/src/readwrite/ecal_reader.cpp +++ b/ecal/core/src/readwrite/ecal_reader.cpp @@ -37,7 +37,7 @@ #include "ecal_reader_layer.h" #if ECAL_CORE_TRANSPORT_UDP -#include "udp/ecal_reader_udp_mc.h" +#include "udp/ecal_reader_udp.h" #endif #if ECAL_CORE_TRANSPORT_SHM @@ -59,50 +59,27 @@ namespace eCAL //////////////////////////////////////// // CDataReader //////////////////////////////////////// - CDataReader::CDataReader() : + CDataReader::CDataReader(const std::string& topic_name_, const SDataTypeInformation& topic_info_) : m_host_name(Process::GetHostName()), m_host_group_name(Process::GetHostGroupName()), m_pid(Process::GetProcessID()), m_pname(Process::GetProcessName()), + m_topic_name(topic_name_), + m_topic_info(topic_info_), m_topic_size(0), m_connected(false), - m_read_buf_received(false), - m_read_time(0), m_receive_time(0), m_clock(0), m_frequency_calculator(3.0f), - m_message_drops(0), - m_loc_published(false), - m_ext_published(false), - m_use_ttype(true), - m_use_tdesc(true), - m_use_udp_mc_confirmed(false), - m_use_shm_confirmed(false), - m_use_tcp_confirmed(false), + m_share_ttype(Config::IsTopicTypeSharingEnabled()), + m_share_tdesc(Config::IsTopicDescriptionSharingEnabled()), m_created(false) { - } - - CDataReader::~CDataReader() - { - Destroy(); - } - - bool CDataReader::Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_) - { - if(m_created) return(false); - - // set defaults - m_topic_name = topic_name_; - m_topic_id.clear(); - m_topic_info = topic_info_; - m_clock = 0; - m_message_drops = 0; - m_created = false; #ifndef NDEBUG // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataReader::Create"); + Logging::Log(log_level_debug1, m_topic_name + "::CDataReader::Constructor"); #endif + // build topic id std::stringstream counter; counter << std::chrono::steady_clock::now().time_since_epoch().count(); @@ -110,14 +87,7 @@ namespace eCAL // set registration expiration const std::chrono::milliseconds registration_timeout(Config::GetRegistrationTimeoutMs()); - m_loc_pub_map.set_expiration(registration_timeout); - m_ext_pub_map.set_expiration(registration_timeout); - - // allow to share topic type - m_use_ttype = Config::IsTopicTypeSharingEnabled(); - - // allow to share topic description - m_use_tdesc = Config::IsTopicDescriptionSharingEnabled(); + m_pub_map.set_expiration(registration_timeout); // start transport layers SubscribeToLayers(); @@ -127,17 +97,24 @@ namespace eCAL // register Register(false); - - return(true); } - bool CDataReader::Destroy() + CDataReader::~CDataReader() { - if (!m_created) return(false); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug1, m_topic_name + "::CDataReader::Destructor"); +#endif + Stop(); + } + + bool CDataReader::Stop() + { + if (!m_created) return false; #ifndef NDEBUG // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataReader::Destroy"); + Logging::Log(log_level_debug1, m_topic_name + "::CDataReader::Stop"); #endif // stop transport layers @@ -145,13 +122,13 @@ namespace eCAL // reset receive callback { - const std::lock_guard lock(m_receive_callback_sync); + const std::lock_guard lock(m_receive_callback_mtx); m_receive_callback = nullptr; } // reset event callback map { - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); m_event_callback_map.clear(); } @@ -161,17 +138,9 @@ namespace eCAL // and unregister Unregister(); - // reset defaults - m_clock = 0; - m_message_drops = 0; - - m_use_udp_mc_confirmed = false; - m_use_shm_confirmed = false; - m_use_tcp_confirmed = false; - - return(true); + return true; } - + void CDataReader::InitializeLayers() { // initialize udp multicast layer @@ -246,12 +215,12 @@ namespace eCAL // topic_information { auto& ecal_reg_sample_tdatatype = ecal_reg_sample_topic.tdatatype; - if (m_use_ttype) + if (m_share_ttype) { ecal_reg_sample_tdatatype.encoding = m_topic_info.encoding; ecal_reg_sample_tdatatype.name = m_topic_info.name; } - if (m_use_tdesc) + if (m_share_tdesc) { ecal_reg_sample_tdatatype.descriptor = m_topic_info.descriptor; } @@ -263,9 +232,9 @@ namespace eCAL // udp multicast layer { Registration::TLayer udp_tlayer; - udp_tlayer.type = tl_ecal_udp_mc; + udp_tlayer.type = tl_ecal_udp; udp_tlayer.version = 1; - udp_tlayer.confirmed = m_use_udp_mc_confirmed; + udp_tlayer.confirmed = m_confirmed_layers.udp; ecal_reg_sample_topic.tlayer.push_back(udp_tlayer); } #endif @@ -276,7 +245,7 @@ namespace eCAL Registration::TLayer shm_tlayer; shm_tlayer.type = tl_ecal_shm; shm_tlayer.version = 1; - shm_tlayer.confirmed = m_use_shm_confirmed; + shm_tlayer.confirmed = m_confirmed_layers.shm; ecal_reg_sample_topic.tlayer.push_back(shm_tlayer); } #endif @@ -287,7 +256,7 @@ namespace eCAL Registration::TLayer tcp_tlayer; tcp_tlayer.type = tl_ecal_tcp; tcp_tlayer.version = 1; - tcp_tlayer.confirmed = m_use_tcp_confirmed; + tcp_tlayer.confirmed = m_confirmed_layers.tcp; ecal_reg_sample_topic.tlayer.push_back(tcp_tlayer); } #endif @@ -381,7 +350,7 @@ namespace eCAL { if (!m_created) return(false); - std::unique_lock read_buffer_lock(m_read_buf_mutex); + std::unique_lock read_buffer_lock(m_read_buf_mtx); // No need to wait (for whatever time) if something has been received if (!m_read_buf_received) @@ -421,13 +390,13 @@ namespace eCAL size_t CDataReader::AddSample(const std::string& tid_, const char* payload_, size_t size_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_) { // ensure thread safety - const std::lock_guard lock(m_receive_callback_sync); + const std::lock_guard lock(m_receive_callback_mtx); if (!m_created) return(0); // store receive layer - m_use_udp_mc_confirmed |= layer_ == tl_ecal_udp_mc; - m_use_shm_confirmed |= layer_ == tl_ecal_shm; - m_use_tcp_confirmed |= layer_ == tl_ecal_tcp; + m_confirmed_layers.udp |= layer_ == tl_ecal_udp; + m_confirmed_layers.shm |= layer_ == tl_ecal_shm; + m_confirmed_layers.tcp |= layer_ == tl_ecal_tcp; // number of hash values to track for duplicates constexpr int hash_queue_size(64); @@ -477,7 +446,7 @@ namespace eCAL // Update frequency calculation { const auto receive_time = std::chrono::steady_clock::now(); - const std::lock_guard freq_lock(m_frequency_calculator_mutex); + const std::lock_guard freq_lock(m_frequency_calculator_mtx); m_frequency_calculator.addTick(receive_time); } @@ -514,7 +483,7 @@ namespace eCAL if(!processed) { // push sample into read buffer - const std::lock_guard read_buffer_lock(m_read_buf_mutex); + const std::lock_guard read_buffer_lock(m_read_buf_mtx); m_read_buf.clear(); m_read_buf.assign(payload_, payload_ + size_); m_read_time = time_; @@ -537,7 +506,7 @@ namespace eCAL // store receive callback { - const std::lock_guard lock(m_receive_callback_sync); + const std::lock_guard lock(m_receive_callback_mtx); #ifndef NDEBUG // log it Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::AddReceiveCallback"); @@ -554,7 +523,7 @@ namespace eCAL // reset receive callback { - const std::lock_guard lock(m_receive_callback_sync); + const std::lock_guard lock(m_receive_callback_mtx); #ifndef NDEBUG // log it Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::RemReceiveCallback"); @@ -575,7 +544,7 @@ namespace eCAL // log it Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::AddEventCallback"); #endif - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); m_event_callback_map[type_] = std::move(callback_); } @@ -592,7 +561,7 @@ namespace eCAL // log it Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::RemEventCallback"); #endif - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); m_event_callback_map[type_] = nullptr; } @@ -604,71 +573,33 @@ namespace eCAL m_id_set = id_set_; } - void CDataReader::ApplyLocPublication(const std::string& process_id_, const std::string& tid_, const SDataTypeInformation& tinfo_) + void CDataReader::ApplyPublication(const SPublicationInfo& publication_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& layer_states_) { - Connect(tid_, tinfo_); + Connect(publication_info_.topic_id, data_type_info_); - // add key to local publisher map - const std::string topic_key = process_id_ + tid_; + // add key to publisher map { - const std::lock_guard lock(m_pub_map_sync); - m_loc_pub_map[topic_key] = true; - } - - m_loc_published = true; - } - - void CDataReader::RemoveLocPublication(const std::string& process_id_, const std::string& tid_) - { - // remove key from local publisher map - const std::string topic_key = process_id_ + tid_; - { - const std::lock_guard lock(m_pub_map_sync); - m_loc_pub_map.erase(topic_key); + const std::lock_guard lock(m_pub_map_mtx); + m_pub_map[publication_info_] = std::make_tuple(data_type_info_, layer_states_); } } - void CDataReader::ApplyExtPublication(const std::string& host_name_, const std::string& process_id_, const std::string& tid_, const SDataTypeInformation& tinfo_) + void CDataReader::RemovePublication(const SPublicationInfo& publication_info_) { - Connect(tid_, tinfo_); - - // add key to external publisher map - const std::string topic_key = host_name_ + process_id_ + tid_; + // remove key from publisher map { - const std::lock_guard lock(m_pub_map_sync); - m_ext_pub_map[topic_key] = true; + const std::lock_guard lock(m_pub_map_mtx); + m_pub_map.erase(publication_info_); } - - m_ext_published = true; } - void CDataReader::RemoveExtPublication(const std::string& host_name_, const std::string& process_id_, const std::string& tid_) + void CDataReader::ApplyLayerParameter(const SPublicationInfo& publication_info_, eTLayerType type_, const Registration::ConnectionPar& parameter_) { - // remove key from external publisher map - const std::string topic_key = host_name_ + process_id_ + tid_; - { - const std::lock_guard lock(m_pub_map_sync); - m_ext_pub_map.erase(topic_key); - } - } - - void CDataReader::ApplyLocLayerParameter(const std::string& process_id_, const std::string& topic_id_, eTLayerType type_, const Registration::ConnectionPar& parameter_) - { - // process only for shm and tcp layer - switch (type_) - { - case tl_ecal_shm: - case tl_ecal_tcp: - break; - default: - return; - } - SReaderLayerPar par; - par.host_name = m_host_name; - par.process_id = process_id_; + par.host_name = publication_info_.host_name; + par.process_id = publication_info_.process_id; par.topic_name = m_topic_name; - par.topic_id = topic_id_; + par.topic_id = publication_info_.topic_id; par.parameter = parameter_; switch (type_) @@ -688,33 +619,6 @@ namespace eCAL } } - void CDataReader::ApplyExtLayerParameter(const std::string& host_name_, eTLayerType type_, const Registration::ConnectionPar& parameter_) - { - // process only for tcp layer - switch (type_) - { - case tl_ecal_tcp: -#if ECAL_CORE_TRANSPORT_TCP - { - SReaderLayerPar par; - par.host_name = host_name_; - par.topic_name = m_topic_name; - par.topic_id = m_topic_id; - par.parameter = parameter_; - - // process only for tcp layer - CTCPReaderLayer::Get()->SetConnectionParameter(par); - } -#else - (void)host_name_; - (void)parameter_; -#endif - break; - default: - return; - } - } - void CDataReader::Connect(const std::string& tid_, const SDataTypeInformation& tinfo_) { SSubEventCallbackData data; @@ -727,7 +631,7 @@ namespace eCAL // fire sub_event_connected { - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); auto iter = m_event_callback_map.find(sub_event_connected); if (iter != m_event_callback_map.end() && iter->second) { @@ -741,7 +645,7 @@ namespace eCAL // fire sub_event_update_connection { - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); auto iter = m_event_callback_map.find(sub_event_update_connection); if (iter != m_event_callback_map.end() && iter->second) { @@ -761,7 +665,7 @@ namespace eCAL // fire sub_event_disconnected { - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); auto iter = m_event_callback_map.find(sub_event_disconnected); if (iter != m_event_callback_map.end() && iter->second) { @@ -831,7 +735,7 @@ namespace eCAL #endif // we fire the message drop event { - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); auto citer = m_event_callback_map.find(sub_event_dropped); if (citer != m_event_callback_map.end() && citer->second) { @@ -897,7 +801,7 @@ namespace eCAL int32_t CDataReader::GetFrequency() { const auto frequency_time = std::chrono::steady_clock::now(); - const std::lock_guard lock(m_frequency_calculator_mutex); + const std::lock_guard lock(m_frequency_calculator_mtx); return static_cast(m_frequency_calculator.getFrequency(frequency_time) * 1000); } @@ -912,19 +816,14 @@ namespace eCAL Register(false); // check connection timeouts - const std::shared_ptr> loc_timeouts = std::make_shared>(); { - const std::lock_guard lock(m_pub_map_sync); - m_loc_pub_map.remove_deprecated(loc_timeouts.get()); - m_ext_pub_map.remove_deprecated(); - - m_loc_published = !m_loc_pub_map.empty(); - m_ext_published = !m_ext_pub_map.empty(); - } + const std::lock_guard lock(m_pub_map_mtx); + m_pub_map.remove_deprecated(); - if (!m_loc_published && !m_ext_published) - { - Disconnect(); + if (m_pub_map.empty()) + { + Disconnect(); + } } } diff --git a/ecal/core/src/readwrite/ecal_reader.h b/ecal/core/src/readwrite/ecal_reader.h index 2aa3d10d08..5d48a3c1f0 100644 --- a/ecal/core/src/readwrite/ecal_reader.h +++ b/ecal/core/src/readwrite/ecal_reader.h @@ -23,10 +23,6 @@ #pragma once -#include -#include -#include -#include #include #include #include @@ -34,31 +30,52 @@ #include "serialization/ecal_serialize_sample_payload.h" #include "serialization/ecal_serialize_sample_registration.h" #include "util/ecal_expmap.h" +#include "util/frequency_calculator.h" +#include +#include #include +#include +#include #include #include -#include #include -#include - #include +#include +#include #include -#include - namespace eCAL { class CDataReader { public: - CDataReader(); + struct SLayerStates + { + bool udp = false; + bool shm = false; + bool tcp = false; + }; + + struct SPublicationInfo + { + std::string host_name; + int32_t process_id = 0; + std::string topic_id; + + friend bool operator<(const SPublicationInfo& l, const SPublicationInfo& r) + { + return std::tie(l.host_name, l.process_id, l.topic_id) + < std::tie(r.host_name, r.process_id, r.topic_id); + } + }; + + CDataReader(const std::string& topic_name_, const SDataTypeInformation& topic_info_); ~CDataReader(); - static void InitializeLayers(); + bool Stop(); - bool Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_); - bool Destroy(); + static void InitializeLayers(); bool Receive(std::string& buf_, long long* time_ = nullptr, int rcv_timeout_ms_ = 0); @@ -73,27 +90,29 @@ namespace eCAL void SetID(const std::set& id_set_); - void ApplyLocPublication(const std::string& process_id_, const std::string& tid_, const SDataTypeInformation& tinfo_); - void RemoveLocPublication(const std::string& process_id_, const std::string& tid_); + void ApplyPublication(const SPublicationInfo& publication_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& layer_states_); + void RemovePublication(const SPublicationInfo& publication_info_); - void ApplyExtPublication(const std::string& host_name_, const std::string& process_id_, const std::string& tid_, const SDataTypeInformation& tinfo_); - void RemoveExtPublication(const std::string& host_name_, const std::string& process_id_, const std::string& tid_); - - void ApplyLocLayerParameter(const std::string& process_id_, const std::string& topic_id_, eTLayerType type_, const Registration::ConnectionPar& parameter_); - void ApplyExtLayerParameter(const std::string& host_name_, eTLayerType type_, const Registration::ConnectionPar& parameter_); + void ApplyLayerParameter(const SPublicationInfo& publication_info_, eTLayerType type_, const Registration::ConnectionPar& parameter_); std::string Dump(const std::string& indent_ = ""); bool IsCreated() const { return(m_created); } + bool IsPublished() const + { + std::lock_guard const lock(m_pub_map_mtx); + return(!m_pub_map.empty()); + } + size_t GetPublisherCount() const { - const std::lock_guard lock(m_pub_map_sync); - return(m_loc_pub_map.size() + m_ext_pub_map.size()); + const std::lock_guard lock(m_pub_map_mtx); + return(m_pub_map.size()); } - std::string GetTopicName() const { return(m_topic_name); } - std::string GetTopicID() const { return(m_topic_id); } + std::string GetTopicName() const { return(m_topic_name); } + std::string GetTopicID() const { return(m_topic_id); } SDataTypeInformation GetDataTypeInformation() const { return(m_topic_info); } void RefreshRegistration(); @@ -115,7 +134,7 @@ namespace eCAL std::string m_host_name; std::string m_host_group_name; - int m_pid; + int m_pid = 0; std::string m_pname; std::string m_topic_name; std::string m_topic_id; @@ -124,48 +143,41 @@ namespace eCAL std::atomic m_topic_size; std::atomic m_connected; - using ConnectedMapT = Util::CExpMap; - mutable std::mutex m_pub_map_sync; - ConnectedMapT m_loc_pub_map; - ConnectedMapT m_ext_pub_map; + using PublicationMapT = Util::CExpMap>; + mutable std::mutex m_pub_map_mtx; + PublicationMapT m_pub_map; - mutable std::mutex m_read_buf_mutex; + mutable std::mutex m_read_buf_mtx; std::condition_variable m_read_buf_cv; - bool m_read_buf_received; + bool m_read_buf_received = false; std::string m_read_buf; - long long m_read_time; + long long m_read_time = 0; - std::mutex m_receive_callback_sync; + std::mutex m_receive_callback_mtx; ReceiveCallbackT m_receive_callback; std::atomic m_receive_time; std::deque m_sample_hash_queue; using EventCallbackMapT = std::map; - std::mutex m_event_callback_map_sync; + std::mutex m_event_callback_map_mtx; EventCallbackMapT m_event_callback_map; std::atomic m_clock; - std::mutex m_frequency_calculator_mutex; + std::mutex m_frequency_calculator_mtx; ResettableFrequencyCalculator m_frequency_calculator; std::set m_id_set; using WriterCounterMapT = std::unordered_map; WriterCounterMapT m_writer_counter_map; - long long m_message_drops; - - std::atomic m_loc_published; - std::atomic m_ext_published; - - bool m_use_ttype; - bool m_use_tdesc; + long long m_message_drops = 0; - bool m_use_udp_mc_confirmed; - bool m_use_shm_confirmed; - bool m_use_tcp_confirmed; + bool m_share_ttype = false; + bool m_share_tdesc = false; + SLayerStates m_confirmed_layers; std::atomic m_created; }; } diff --git a/ecal/core/src/readwrite/ecal_reader_layer.h b/ecal/core/src/readwrite/ecal_reader_layer.h index 978b75f031..276a45a663 100644 --- a/ecal/core/src/readwrite/ecal_reader_layer.h +++ b/ecal/core/src/readwrite/ecal_reader_layer.h @@ -35,7 +35,7 @@ namespace eCAL struct SReaderLayerPar { std::string host_name; - std::string process_id; + int32_t process_id = 0; std::string topic_name; std::string topic_id; Registration::ConnectionPar parameter; diff --git a/ecal/core/src/readwrite/ecal_writer.cpp b/ecal/core/src/readwrite/ecal_writer.cpp index 6b77f7aa23..3b1a1603a2 100644 --- a/ecal/core/src/readwrite/ecal_writer.cpp +++ b/ecal/core/src/readwrite/ecal_writer.cpp @@ -29,7 +29,6 @@ #include #include -#include "ecal_def.h" #include "config/ecal_config_reader_hlp.h" #if ECAL_CORE_REGISTRATION @@ -68,60 +67,22 @@ namespace std namespace eCAL { - CDataWriter::CDataWriter() : + CDataWriter::CDataWriter(const std::string& topic_name_, const SDataTypeInformation& topic_info_, const Publisher::Configuration& config_) : m_host_name(Process::GetHostName()), m_host_group_name(Process::GetHostGroupName()), m_pid(Process::GetProcessID()), m_pname(Process::GetProcessName()), - m_topic_size(0), - m_buffering_shm(PUB_MEMFILE_BUF_COUNT), - m_zero_copy(PUB_MEMFILE_ZERO_COPY), - m_acknowledge_timeout_ms(PUB_MEMFILE_ACK_TO), + m_topic_name(topic_name_), + m_topic_info(topic_info_), + m_config(config_), m_connected(false), - m_id(0), - m_clock(0), m_frequency_calculator(3.0f), - m_loc_subscribed(false), - m_ext_subscribed(false), - m_use_ttype(true), - m_use_tdesc(true), - m_share_ttype(-1), - m_share_tdesc(-1), m_created(false) { - // initialize layer modes with configuration settings -#if ECAL_CORE_TRANSPORT_UDP - m_writer.udp_mode.requested = Config::GetPublisherUdpMulticastMode(); -#endif -#if ECAL_CORE_TRANSPORT_SHM - m_writer.shm_mode.requested = Config::GetPublisherShmMode(); -#endif -#if ECAL_CORE_TRANSPORT_TCP - m_writer.tcp_mode.requested = Config::GetPublisherTcpMode(); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Constructor"); #endif - } - - CDataWriter::~CDataWriter() - { - Destroy(); - } - - bool CDataWriter::Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_) - { - if (m_created) return(false); - - // set defaults - m_topic_name = topic_name_; - m_topic_id.clear(); - m_topic_info = topic_info_; - m_id = 0; - m_clock = 0; - m_buffering_shm = Config::GetMemfileBufferCount(); - m_zero_copy = Config::IsMemfileZerocopyEnabled(); - m_acknowledge_timeout_ms = Config::GetMemfileAckTimeoutMs(); - m_connected = false; - m_ext_subscribed = false; - m_created = false; // build topic id std::stringstream counter; @@ -130,14 +91,7 @@ namespace eCAL // set registration expiration const std::chrono::milliseconds registration_timeout(Config::GetRegistrationTimeoutMs()); - m_loc_sub_map.set_expiration(registration_timeout); - m_ext_sub_map.set_expiration(registration_timeout); - - // allow to share topic type - m_use_ttype = Config::IsTopicTypeSharingEnabled(); - - // allow to share topic description - m_use_tdesc = Config::IsTopicDescriptionSharingEnabled(); + m_sub_map.set_expiration(registration_timeout); // mark as created m_created = true; @@ -145,68 +99,40 @@ namespace eCAL // register Register(false); - // create udp multicast layer - SetUseUdpMC(m_writer.udp_mode.requested); - - // create shm layer - SetUseShm(m_writer.shm_mode.requested); - - // create tcp layer - SetUseTcp(m_writer.tcp_mode.requested); + // start udp, shm, tcp layer + StartTransportLayer(); + } + CDataWriter::~CDataWriter() + { #ifndef NDEBUG // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Created"); + Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Destructor"); #endif - // adapt number of used memory file - ShmSetBufferCount(m_buffering_shm); - - return(true); + Stop(); } - bool CDataWriter::Destroy() + bool CDataWriter::Stop() { - if (!m_created) return(false); - + if (!m_created) return false; #ifndef NDEBUG // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Destroy"); -#endif - - // destroy udp multicast writer -#if ECAL_CORE_TRANSPORT_UDP - m_writer.udp.reset(); -#endif - - // destroy memory file writer -#if ECAL_CORE_TRANSPORT_SHM - m_writer.shm.reset(); -#endif - - // destroy tcp writer -#if ECAL_CORE_TRANSPORT_TCP - m_writer.tcp.reset(); + Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Stop"); #endif - // reset defaults - m_id = 0; - m_clock = 0; - m_buffering_shm = Config::GetMemfileBufferCount(); - m_zero_copy = Config::IsMemfileZerocopyEnabled(); - m_acknowledge_timeout_ms = Config::GetMemfileAckTimeoutMs(); - m_connected = false; + // stop udp, shm, tcp layer + StopTransportLayer(); - // reset subscriber maps + // clear subscriber maps { - const std::lock_guard lock(m_sub_map_sync); - m_loc_sub_map.clear(); - m_ext_sub_map.clear(); + const std::lock_guard lock(m_sub_map_mtx); + m_sub_map.clear(); } - // reset event callback map + // clear event callback map { - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); m_event_callback_map.clear(); } @@ -216,7 +142,7 @@ namespace eCAL // and unregister Unregister(); - return(true); + return true; } bool CDataWriter::SetDataTypeInformation(const SDataTypeInformation& topic_info_) @@ -271,103 +197,6 @@ namespace eCAL return(true); } - void CDataWriter::ShareType(bool state_) - { - if (state_) - { - m_share_ttype = 1; - } - else - { - m_share_ttype = 0; - } - } - - void CDataWriter::ShareDescription(bool state_) - { - if (state_) - { - m_share_tdesc = 1; - } - else - { - m_share_tdesc = 0; - } - } - - bool CDataWriter::SetLayerMode(TLayer::eTransportLayer layer_, TLayer::eSendMode mode_) - { - switch (layer_) - { - case TLayer::tlayer_udp_mc: - SetUseUdpMC(mode_); - break; - case TLayer::tlayer_shm: - SetUseShm(mode_); - break; - case TLayer::tlayer_tcp: - SetUseTcp(mode_); - break; - case TLayer::tlayer_all: - SetUseUdpMC(mode_); - SetUseShm(mode_); - SetUseTcp(mode_); - break; - default: - break; - } - return true; - } - - bool CDataWriter::ShmSetBufferCount(size_t buffering_) - { -#if ECAL_CORE_TRANSPORT_SHM - if (!m_writer.shm) return false; - - if (buffering_ < 1) - { - Logging::Log(log_level_error, m_topic_name + "::CDataWriter::ShmSetBufferCount minimal number of memory files is 1 !"); - return false; - } - m_buffering_shm = static_cast(buffering_); - - // adapt number of used memory files - if (m_created) - { - m_writer.shm->SetBufferCount(buffering_); - } - - return true; -#else - return false; -#endif - } - - bool CDataWriter::ShmEnableZeroCopy(bool state_) - { -#if ECAL_CORE_TRANSPORT_SHM - m_zero_copy = state_; - return true; -#else - return false; -#endif - } - - bool CDataWriter::ShmSetAcknowledgeTimeout(long long acknowledge_timeout_ms_) - { -#if ECAL_CORE_TRANSPORT_SHM - m_acknowledge_timeout_ms = acknowledge_timeout_ms_; - return true; -#else - return false; -#endif - } - - long long CDataWriter::ShmGetAcknowledgeTimeout() const - { - return m_acknowledge_timeout_ms; - } - bool CDataWriter::AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_) { if (!m_created) return(false); @@ -378,7 +207,7 @@ namespace eCAL // log it Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::AddEventCallback"); #endif - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); m_event_callback_map[type_] = std::move(callback_); } @@ -395,7 +224,7 @@ namespace eCAL // log it Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::RemEventCallback"); #endif - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); m_event_callback_map[type_] = nullptr; } @@ -407,26 +236,26 @@ namespace eCAL { // we should think about if we would like to potentially use the `time_` variable to tick with (but we would need the same base for checking incoming samples then.... const auto send_time = std::chrono::steady_clock::now(); - const std::lock_guard lock(m_frequency_calculator_mutex); + const std::lock_guard lock(m_frequency_calculator_mtx); m_frequency_calculator.addTick(send_time); } - // check writer modes - if (!CheckWriterModes()) - { - // incompatible writer configurations - return 0; - } - // get payload buffer size (one time, to avoid multiple computations) const size_t payload_buf_size(payload_.GetSize()); - // can we do a zero copy write ? - const bool allow_zero_copy = - m_zero_copy // zero copy mode activated by user - && m_writer.shm_mode.activated // shm layer active - && !m_writer.udp_mode.activated - && !m_writer.tcp_mode.activated; + // are we allowed to perform zero copy writing? + bool allow_zero_copy(false); +#if ECAL_CORE_TRANSPORT_SHM + allow_zero_copy = m_config.shm.zero_copy_mode; // zero copy mode activated by user +#endif +#if ECAL_CORE_TRANSPORT_UDP + // udp is active -> no zero copy + allow_zero_copy &= !m_writer_udp; +#endif +#if ECAL_CORE_TRANSPORT_TCP + // tcp is active -> no zero copy + allow_zero_copy &= !m_writer_tcp; +#endif // create a payload copy for all layer if (!allow_zero_copy) @@ -445,7 +274,7 @@ namespace eCAL // SHM //////////////////////////////////////////////////////////////////////////// #if ECAL_CORE_TRANSPORT_SHM - if (m_writer.shm && m_writer.shm_mode.activated) + if (m_writer_shm) { #ifndef NDEBUG // log it @@ -462,12 +291,11 @@ namespace eCAL wattr.clock = m_clock; wattr.hash = snd_hash; wattr.time = time_; - wattr.buffering = m_buffering_shm; - wattr.zero_copy = m_zero_copy; - wattr.acknowledge_timeout_ms = m_acknowledge_timeout_ms; + wattr.zero_copy = m_config.shm.zero_copy_mode; + wattr.acknowledge_timeout_ms = m_config.shm.acknowledge_timeout_ms; // prepare send - if (m_writer.shm->PrepareWrite(wattr)) + if (m_writer_shm->PrepareWrite(wattr)) { // register new to update listening subscribers and rematch Register(true); @@ -478,7 +306,7 @@ namespace eCAL if (allow_zero_copy) { // write to shm layer (write content into the opened memory file without additional copy) - shm_sent = m_writer.shm->Write(payload_, wattr); + shm_sent = m_writer_shm->Write(payload_, wattr); } // multiple layer are active -> we make a copy and use that one else @@ -486,10 +314,10 @@ namespace eCAL // wrap the buffer into a payload object CBufferPayloadWriter payload_buf(m_payload_buffer.data(), m_payload_buffer.size()); // write to shm layer (write content into the opened memory file without additional copy) - shm_sent = m_writer.shm->Write(payload_buf, wattr); + shm_sent = m_writer_shm->Write(payload_buf, wattr); } - m_writer.shm_mode.confirmed = true; + m_confirmed_layers.shm = true; } written |= shm_sent; @@ -511,24 +339,16 @@ namespace eCAL // UDP (MC) //////////////////////////////////////////////////////////////////////////// #if ECAL_CORE_TRANSPORT_UDP - if (m_writer.udp && m_writer.udp_mode.activated) + if (m_writer_udp) { #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::UDP_MC"); + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::udp"); #endif // send it - bool udp_mc_sent(false); + bool udp_sent(false); { -#if ECAL_CORE_TRANSPORT_SHM - // if shared memory layer for local communication is switched off - // we activate udp message loopback to communicate with local processes too - const bool loopback = m_writer.shm_mode.requested == TLayer::smode_off; -#else - const bool loopback = true; -#endif - // fill writer data struct SWriterAttr wattr; wattr.len = payload_buf_size; @@ -536,10 +356,10 @@ namespace eCAL wattr.clock = m_clock; wattr.hash = snd_hash; wattr.time = time_; - wattr.loopback = loopback; + wattr.loopback = m_config.udp.loopback; // prepare send - if (m_writer.udp->PrepareWrite(wattr)) + if (m_writer_udp->PrepareWrite(wattr)) { // register new to update listening subscribers and rematch Register(true); @@ -547,20 +367,20 @@ namespace eCAL } // write to udp multicast layer - udp_mc_sent = m_writer.udp->Write(m_payload_buffer.data(), wattr); - m_writer.udp_mode.confirmed = true; + udp_sent = m_writer_udp->Write(m_payload_buffer.data(), wattr); + m_confirmed_layers.udp = true; } - written |= udp_mc_sent; + written |= udp_sent; #ifndef NDEBUG // log it - if (udp_mc_sent) + if (udp_sent) { - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::UDP_MC - SUCCESS"); + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::udp - SUCCESS"); } else { - Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send::UDP_MC - FAILED"); + Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send::udp - FAILED"); } #endif } @@ -570,7 +390,7 @@ namespace eCAL // TCP //////////////////////////////////////////////////////////////////////////// #if ECAL_CORE_TRANSPORT_TCP - if (m_writer.tcp && m_writer.tcp_mode.activated) + if (m_writer_tcp) { #ifndef NDEBUG // log it @@ -589,8 +409,8 @@ namespace eCAL wattr.time = time_; // write to tcp layer - tcp_sent = m_writer.tcp->Write(m_payload_buffer.data(), wattr); - m_writer.tcp_mode.confirmed = true; + tcp_sent = m_writer_tcp->Write(m_payload_buffer.data(), wattr); + m_confirmed_layers.tcp = true; } written |= tcp_sent; @@ -613,106 +433,55 @@ namespace eCAL else return 0; } - void CDataWriter::ApplyLocSubscription(const SLocalSubscriptionInfo& local_info_, const SDataTypeInformation& tinfo_, const std::string& reader_par_) + void CDataWriter::ApplySubscription(const SSubscriptionInfo& subscription_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& layer_states_, const std::string& reader_par_) { - Connect(local_info_.topic_id, tinfo_); + Connect(subscription_info_.topic_id, data_type_info_); - // add key to local subscriber map + // add key to subscriber map { - const std::lock_guard lock(m_sub_map_sync); - m_loc_sub_map[local_info_] = true; + const std::lock_guard lock(m_sub_map_mtx); + m_sub_map[subscription_info_] = std::make_tuple(data_type_info_, layer_states_); } - m_loc_subscribed = true; - - // add a new local subscription + // add a new subscription #if ECAL_CORE_TRANSPORT_UDP - if (m_writer.udp) m_writer.udp->AddLocConnection(local_info_.process_id, local_info_.topic_id, reader_par_); + if (m_writer_udp) m_writer_udp->ApplySubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id, reader_par_); #endif #if ECAL_CORE_TRANSPORT_SHM - if (m_writer.shm) m_writer.shm->AddLocConnection(local_info_.process_id, local_info_.topic_id, reader_par_); + if (m_writer_shm) m_writer_shm->ApplySubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id, reader_par_); #endif #if ECAL_CORE_TRANSPORT_TCP - if (m_writer.tcp) m_writer.tcp->AddLocConnection(local_info_.process_id, local_info_.topic_id, reader_par_); + if (m_writer_tcp) m_writer_tcp->ApplySubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id, reader_par_); #endif #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::ApplyLocSubscription"); + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::ApplySubscription"); #endif } - void CDataWriter::RemoveLocSubscription(const SLocalSubscriptionInfo& local_info_) + void CDataWriter::RemoveSubscription(const SSubscriptionInfo& subscription_info_) { - // remove key from local subscriber map + // remove key from subscriber map { - const std::lock_guard lock(m_sub_map_sync); - m_loc_sub_map.erase(local_info_); + const std::lock_guard lock(m_sub_map_mtx); + m_sub_map.erase(subscription_info_); } - // remove a local subscription -#if ECAL_CORE_TRANSPORT_UDP - if (m_writer.udp) m_writer.udp->RemLocConnection(local_info_.process_id, local_info_.topic_id); -#endif -#if ECAL_CORE_TRANSPORT_SHM - if (m_writer.shm) m_writer.shm->RemLocConnection(local_info_.process_id, local_info_.topic_id); -#endif -#if ECAL_CORE_TRANSPORT_TCP - if (m_writer.tcp) m_writer.tcp->RemLocConnection(local_info_.process_id, local_info_.topic_id); -#endif - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::RemoveLocSubscription"); -#endif - } - - void CDataWriter::ApplyExtSubscription(const SExternalSubscriptionInfo& external_info_, const SDataTypeInformation& tinfo_, const std::string& reader_par_) - { - Connect(external_info_.topic_id, tinfo_); - - // add key to external subscriber map - { - const std::lock_guard lock(m_sub_map_sync); - m_ext_sub_map[external_info_] = true; - } - - m_ext_subscribed = true; - - // add a new external subscription + // remove subscription #if ECAL_CORE_TRANSPORT_UDP - if (m_writer.udp) m_writer.udp->AddExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id, reader_par_); + if (m_writer_udp) m_writer_udp->RemoveSubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id); #endif #if ECAL_CORE_TRANSPORT_SHM - if (m_writer.shm) m_writer.shm->AddExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id, reader_par_); + if (m_writer_shm) m_writer_shm->RemoveSubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id); #endif #if ECAL_CORE_TRANSPORT_TCP - if (m_writer.tcp) m_writer.tcp->AddExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id, reader_par_); + if (m_writer_tcp) m_writer_tcp->RemoveSubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id); #endif #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::ApplyExtSubscription"); -#endif - } - - void CDataWriter::RemoveExtSubscription(const SExternalSubscriptionInfo& external_info_) - { - // remove key from external subscriber map - { - const std::lock_guard lock(m_sub_map_sync); - m_ext_sub_map.erase(external_info_); - } - - // remove external subscription -#if ECAL_CORE_TRANSPORT_UDP - if (m_writer.udp) m_writer.udp->RemExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id); -#endif -#if ECAL_CORE_TRANSPORT_SHM - if (m_writer.shm) m_writer.shm->RemExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id); -#endif -#if ECAL_CORE_TRANSPORT_TCP - if (m_writer.tcp) m_writer.tcp->RemExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id); + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::RemoveSubscription"); #endif } @@ -725,17 +494,13 @@ namespace eCAL // check connection timeouts { - const std::lock_guard lock(m_sub_map_sync); - m_loc_sub_map.remove_deprecated(); - m_ext_sub_map.remove_deprecated(); + const std::lock_guard lock(m_sub_map_mtx); + m_sub_map.remove_deprecated(); - m_loc_subscribed = !m_loc_sub_map.empty(); - m_ext_subscribed = !m_ext_sub_map.empty(); - } - - if (!m_loc_subscribed && !m_ext_subscribed) - { - Disconnect(); + if (m_sub_map.empty()) + { + Disconnect(); + } } } @@ -764,8 +529,6 @@ namespace eCAL out << indent_ << "m_clock: " << m_clock << '\n'; out << indent_ << "frequency [mHz]: " << GetFrequency() << '\n'; out << indent_ << "m_created: " << m_created << '\n'; - out << indent_ << "m_loc_subscribed: " << m_loc_subscribed << '\n'; - out << indent_ << "m_ext_subscribed: " << m_ext_subscribed << '\n'; out << std::endl; return(out.str()); @@ -777,19 +540,6 @@ namespace eCAL if (!m_created) return(false); if (m_topic_name.empty()) return(false); - //@Rex: why is the logic different in CDataReader??? - // check share modes - bool share_ttype(m_use_ttype && (g_pubgate() != nullptr) && g_pubgate()->TypeShared()); - if (m_share_ttype != -1) - { - share_ttype = m_share_ttype == 1; - } - bool share_tdesc(m_use_tdesc && (g_pubgate() != nullptr) && g_pubgate()->DescriptionShared()); - if (m_share_tdesc != -1) - { - share_tdesc = m_share_tdesc == 1; - } - // create command parameter Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_reg_publisher; @@ -802,12 +552,12 @@ namespace eCAL // topic_information { auto& ecal_reg_sample_tdatatype = ecal_reg_sample_topic.tdatatype; - if (share_ttype) + if (m_config.share_topic_type) { ecal_reg_sample_tdatatype.encoding = m_topic_info.encoding; ecal_reg_sample_tdatatype.name = m_topic_info.name; } - if (share_tdesc) + if (m_config.share_topic_description) { ecal_reg_sample_tdatatype.descriptor = m_topic_info.descriptor; } @@ -817,39 +567,39 @@ namespace eCAL #if ECAL_CORE_TRANSPORT_UDP // udp multicast layer - if (m_writer.udp) + if (m_writer_udp) { eCAL::Registration::TLayer udp_tlayer; - udp_tlayer.type = tl_ecal_udp_mc; + udp_tlayer.type = tl_ecal_udp; udp_tlayer.version = 1; - udp_tlayer.confirmed = m_writer.udp_mode.confirmed; - udp_tlayer.par_layer.layer_par_udpmc = m_writer.udp->GetConnectionParameter().layer_par_udpmc; + udp_tlayer.confirmed = m_confirmed_layers.udp; + udp_tlayer.par_layer.layer_par_udpmc = m_writer_udp->GetConnectionParameter().layer_par_udpmc; ecal_reg_sample_topic.tlayer.push_back(udp_tlayer); } #endif #if ECAL_CORE_TRANSPORT_SHM // shm layer - if (m_writer.shm) + if (m_writer_shm) { eCAL::Registration::TLayer shm_tlayer; shm_tlayer.type = tl_ecal_shm; shm_tlayer.version = 1; - shm_tlayer.confirmed = m_writer.shm_mode.confirmed; - shm_tlayer.par_layer.layer_par_shm = m_writer.shm->GetConnectionParameter().layer_par_shm; + shm_tlayer.confirmed = m_confirmed_layers.shm; + shm_tlayer.par_layer.layer_par_shm = m_writer_shm->GetConnectionParameter().layer_par_shm; ecal_reg_sample_topic.tlayer.push_back(shm_tlayer); } #endif #if ECAL_CORE_TRANSPORT_TCP // tcp layer - if (m_writer.tcp) + if (m_writer_tcp) { eCAL::Registration::TLayer tcp_tlayer; tcp_tlayer.type = tl_ecal_tcp; tcp_tlayer.version = 1; - tcp_tlayer.confirmed = m_writer.tcp_mode.confirmed; - tcp_tlayer.par_layer.layer_par_tcp = m_writer.tcp->GetConnectionParameter().layer_par_tcp; + tcp_tlayer.confirmed = m_confirmed_layers.tcp; + tcp_tlayer.par_layer.layer_par_tcp = m_writer_tcp->GetConnectionParameter().layer_par_tcp; ecal_reg_sample_topic.tlayer.push_back(tcp_tlayer); } #endif @@ -864,9 +614,15 @@ namespace eCAL size_t loc_connections(0); size_t ext_connections(0); { - const std::lock_guard lock(m_sub_map_sync); - loc_connections = m_loc_sub_map.size(); - ext_connections = m_ext_sub_map.size(); + const std::lock_guard lock(m_sub_map_mtx); + for (const auto& sub : m_sub_map) + { + if (sub.first.host_name == m_host_name) + { + loc_connections++; + } + } + ext_connections = m_sub_map.size() - loc_connections; } ecal_reg_sample_topic.connections_loc = static_cast(loc_connections); ecal_reg_sample_topic.connections_ext = static_cast(ext_connections); @@ -925,7 +681,7 @@ namespace eCAL // fire pub_event_connected { - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); auto iter = m_event_callback_map.find(pub_event_connected); if (iter != m_event_callback_map.end() && iter->second) { @@ -939,7 +695,7 @@ namespace eCAL // fire pub_event_update_connection { - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); auto iter = m_event_callback_map.find(pub_event_update_connection); if (iter != m_event_callback_map.end() && iter->second) { @@ -959,7 +715,7 @@ namespace eCAL // fire pub_event_disconnected { - const std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_mtx); auto iter = m_event_callback_map.find(pub_event_disconnected); if (iter != m_event_callback_map.end() && iter->second) { @@ -973,186 +729,89 @@ namespace eCAL } } - void CDataWriter::SetUseUdpMC(TLayer::eSendMode mode_) + void CDataWriter::StartTransportLayer() { #if ECAL_CORE_TRANSPORT_UDP - m_writer.udp_mode.requested = mode_; - if (!m_created) return; - - // log send mode - LogSendMode(mode_, m_topic_name + "::CDataWriter::Create::UDP_MC_SENDMODE::"); - - switch (mode_) + if (m_config.udp.enable) { - case TLayer::eSendMode::smode_auto: - case TLayer::eSendMode::smode_on: - m_writer.udp = std::make_unique(m_host_name, m_topic_name, m_topic_id); -#ifndef NDEBUG - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Create::UDP_MC_WRITER"); -#endif - break; - case TLayer::eSendMode::smode_none: - case TLayer::eSendMode::smode_off: - m_writer.udp.reset(); - break; + ActivateUdpLayer(); } -#endif // ECAL_CORE_TRANSPORT_UDP - } - - void CDataWriter::SetUseShm(TLayer::eSendMode mode_) - { +#endif #if ECAL_CORE_TRANSPORT_SHM - m_writer.shm_mode.requested = mode_; - if (!m_created) return; - - // log send mode - LogSendMode(mode_, m_topic_name + "::CDataWriter::Create::SHM_SENDMODE::"); - - switch (mode_) + if (m_config.shm.enable) { - case TLayer::eSendMode::smode_auto: - case TLayer::eSendMode::smode_on: - m_writer.shm = std::make_unique(m_host_name, m_topic_name, m_topic_id); -#ifndef NDEBUG - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Create::SHM_WRITER"); -#endif - break; - case TLayer::eSendMode::smode_none: - case TLayer::eSendMode::smode_off: - m_writer.shm.reset(); - break; + ActivateShmLayer(); } -#endif // ECAL_CORE_TRANSPORT_SHM - } - - void CDataWriter::SetUseTcp(TLayer::eSendMode mode_) - { +#endif #if ECAL_CORE_TRANSPORT_TCP - m_writer.tcp_mode.requested = mode_; - if (!m_created) return; - - // log send mode - LogSendMode(mode_, m_topic_name + "::CDataWriter::Create::TCP_SENDMODE::"); - - switch (mode_) + if (m_config.tcp.enable) { - case TLayer::eSendMode::smode_auto: - case TLayer::eSendMode::smode_on: - m_writer.tcp = std::make_unique(m_host_name, m_topic_name, m_topic_id); -#ifndef NDEBUG - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Create::TCP_WRITER - SUCCESS"); -#endif - break; - case TLayer::eSendMode::smode_none: - case TLayer::eSendMode::smode_off: - m_writer.tcp.reset(); - break; + ActivateTcpLayer(); } -#else // ECAL_CORE_TRANSPORT_TCP - (void)mode_; -#endif // ECAL_CORE_TRANSPORT_TCP +#endif } - bool CDataWriter::CheckWriterModes() + void CDataWriter::StopTransportLayer() { - // if nothing is activated, we use defaults shm = auto, udp = auto - if ((m_writer.udp_mode.requested == TLayer::smode_off) - && (m_writer.shm_mode.requested == TLayer::smode_off) - && (m_writer.tcp_mode.requested == TLayer::smode_off) - ) - { + // destroy udp writer #if ECAL_CORE_TRANSPORT_UDP - m_writer.udp_mode.requested = TLayer::smode_auto; + m_writer_udp.reset(); #endif + + // destroy shm writer #if ECAL_CORE_TRANSPORT_SHM - m_writer.shm_mode.requested = TLayer::smode_auto; + m_writer_shm.reset(); #endif - } - // if shm layer is off, we need a local transport layer switch to on - // prio 1: udp = on - // prio 2: tcp = on - if ( (m_writer.shm_mode.requested == TLayer::smode_off) - && (m_writer.shm_mode.requested != TLayer::smode_on) - && (m_writer.tcp_mode.requested != TLayer::smode_on) - ) - { - bool new_local_layer(false); -#if ECAL_CORE_TRANSPORT_UDP - if (m_writer.udp_mode.requested != TLayer::smode_on) - { - m_writer.udp_mode.requested = TLayer::smode_on; - new_local_layer = true; - } -#else - #if ECAL_CORE_TRANSPORT_TCP - if (m_writer.tcp_mode.requested != TLayer::smode_on) - { - m_writer.tcp_mode.requested = TLayer::smode_on; - new_local_layer = true; - } -#endif + // destroy tcp writer +#if ECAL_CORE_TRANSPORT_TCP + m_writer_tcp.reset(); #endif - if (new_local_layer) - { - if (m_writer.udp_mode.requested == TLayer::smode_on) - { - Logging::Log(log_level_warning, m_topic_name + "::CDataWriter: Switched to udp for local communication."); - SetUseUdpMC(TLayer::smode_on); - } - if (m_writer.tcp_mode.requested == TLayer::smode_on) - { - Logging::Log(log_level_warning, m_topic_name + "::CDataWriter: Switched to tcp for local communication."); - SetUseTcp(TLayer::smode_on); - } - } - } - - if ( (m_writer.tcp_mode.requested == TLayer::smode_auto) - && (m_writer.udp_mode.requested == TLayer::smode_auto) - ) - { - Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send: TCP layer and UDP layer are both set to auto mode - Publication failed !"); - return false; - } + } + void CDataWriter::ActivateUdpLayer() + { #if ECAL_CORE_TRANSPORT_UDP - //////////////////////////////////////////////////////////////////////////// - // UDP (MC) - //////////////////////////////////////////////////////////////////////////// - if (((m_writer.udp_mode.requested == TLayer::smode_auto) && m_ext_subscribed) - || (m_writer.udp_mode.requested == TLayer::smode_on) - ) - { - m_writer.udp_mode.activated = true; - } + // log state + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateUdpLayer::ACTIVATED"); + + // create writer + m_writer_udp = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.udp); + +#ifndef NDEBUG + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateUdpLayer::WRITER_CREATED"); #endif +#endif // ECAL_CORE_TRANSPORT_UDP + } + void CDataWriter::ActivateShmLayer() + { #if ECAL_CORE_TRANSPORT_SHM - //////////////////////////////////////////////////////////////////////////// - // SHM - //////////////////////////////////////////////////////////////////////////// - if (((m_writer.shm_mode.requested == TLayer::smode_auto) && m_loc_subscribed) - || (m_writer.shm_mode.requested == TLayer::smode_on) - ) - { - m_writer.shm_mode.activated = true; - } + // log state + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateShmLayer::ACTIVATED"); + + // create writer + m_writer_shm = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.shm); + +#ifndef NDEBUG + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateShmLayer::WRITER_CREATED"); #endif +#endif // ECAL_CORE_TRANSPORT_SHM + } + void CDataWriter::ActivateTcpLayer() + { #if ECAL_CORE_TRANSPORT_TCP - //////////////////////////////////////////////////////////////////////////// - // TCP - //////////////////////////////////////////////////////////////////////////// - if (((m_writer.tcp_mode.requested == TLayer::smode_auto) && m_ext_subscribed) - || (m_writer.tcp_mode.requested == TLayer::smode_on) - ) - { - m_writer.tcp_mode.activated = true; - } -#endif + // log state + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateTcpLayer::ACTIVATED"); - return true; + // create writer + m_writer_tcp = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.tcp); + +#ifndef NDEBUG + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateTcpLayer::WRITER_CREATED"); +#endif +#endif // ECAL_CORE_TRANSPORT_TCP } size_t CDataWriter::PrepareWrite(long long id_, size_t len_) @@ -1176,10 +835,10 @@ namespace eCAL bool CDataWriter::IsInternalSubscribedOnly() { - const std::string process_id = Process::GetProcessIDAsString(); + const int32_t process_id = static_cast(Process::GetProcessID()); bool is_internal_only(true); - const std::lock_guard lock(m_sub_map_sync); - for (auto sub : m_loc_sub_map) + const std::lock_guard lock(m_sub_map_mtx); + for (auto sub : m_sub_map) { if (sub.first.process_id != process_id) { @@ -1190,33 +849,10 @@ namespace eCAL return is_internal_only; } - void CDataWriter::LogSendMode(TLayer::eSendMode smode_, const std::string& base_msg_) - { -#ifndef NDEBUG - switch (smode_) - { - case TLayer::eSendMode::smode_none: - Logging::Log(log_level_debug4, base_msg_ + "NONE"); - break; - case TLayer::eSendMode::smode_auto: - Logging::Log(log_level_debug4, base_msg_ + "AUTO"); - break; - case TLayer::eSendMode::smode_on: - Logging::Log(log_level_debug4, base_msg_ + "ON"); - break; - case TLayer::eSendMode::smode_off: - Logging::Log(log_level_debug4, base_msg_ + "OFF"); - break; - } -#else - (void)smode_; - (void)base_msg_; -#endif - } int32_t CDataWriter::GetFrequency() { const auto frequency_time = std::chrono::steady_clock::now(); - const std::lock_guard lock(m_frequency_calculator_mutex); + const std::lock_guard lock(m_frequency_calculator_mtx); return static_cast(m_frequency_calculator.getFrequency(frequency_time) * 1000); } } diff --git a/ecal/core/src/readwrite/ecal_writer.h b/ecal/core/src/readwrite/ecal_writer.h index f3f87b4789..019c06fd26 100644 --- a/ecal/core/src/readwrite/ecal_writer.h +++ b/ecal/core/src/readwrite/ecal_writer.h @@ -23,21 +23,16 @@ #pragma once -#include -#include -#include #include #include -#include +#include #include -#include -#include "ecal_def.h" #include "util/ecal_expmap.h" #include #if ECAL_CORE_TRANSPORT_UDP -#include "udp/ecal_writer_udp_mc.h" +#include "udp/ecal_writer_udp.h" #endif #if ECAL_CORE_TRANSPORT_SHM @@ -48,11 +43,14 @@ #include "tcp/ecal_writer_tcp.h" #endif +#include +#include +#include #include #include #include -#include #include +#include #include namespace eCAL @@ -60,63 +58,43 @@ namespace eCAL class CDataWriter { public: - struct SExternalSubscriptionInfo + struct SLayerStates { - std::string host_name; - std::string process_id; - std::string topic_id; - - friend bool operator<(const SExternalSubscriptionInfo& l, const SExternalSubscriptionInfo& r) - { - return std::tie(l.host_name, l.process_id, l.topic_id) - < std::tie(r.host_name, r.process_id, r.topic_id); - } + bool udp = false; + bool shm = false; + bool tcp = false; }; - struct SLocalSubscriptionInfo + struct SSubscriptionInfo { - std::string process_id; - std::string topic_id; + std::string host_name; + int32_t process_id = 0; + std::string topic_id; - friend bool operator<(const SLocalSubscriptionInfo& l, const SLocalSubscriptionInfo& r) + friend bool operator<(const SSubscriptionInfo& l, const SSubscriptionInfo& r) { - return std::tie(l.process_id, l.topic_id) - < std::tie(r.process_id, r.topic_id); + return std::tie(l.host_name, l.process_id, l.topic_id) + < std::tie(r.host_name, r.process_id, r.topic_id); } }; - CDataWriter(); + CDataWriter(const std::string& topic_name_, const SDataTypeInformation& topic_info_, const Publisher::Configuration& config_ = {}); ~CDataWriter(); - bool Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_); - bool Destroy(); + bool Stop(); bool SetDataTypeInformation(const SDataTypeInformation& topic_info_); bool SetAttribute(const std::string& attr_name_, const std::string& attr_value_); bool ClearAttribute(const std::string& attr_name_); - void ShareType(bool state_); - void ShareDescription(bool state_); - - bool SetLayerMode(TLayer::eTransportLayer layer_, TLayer::eSendMode mode_); - - bool ShmSetBufferCount(size_t buffering_); - bool ShmEnableZeroCopy(bool state_); - - bool ShmSetAcknowledgeTimeout(long long acknowledge_timeout_ms_); - long long ShmGetAcknowledgeTimeout() const; - bool AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_); bool RemEventCallback(eCAL_Publisher_Event type_); size_t Write(CPayloadWriter& payload_, long long time_, long long id_); - void ApplyLocSubscription(const SLocalSubscriptionInfo& local_info_, const SDataTypeInformation& tinfo_, const std::string& reader_par_); - void RemoveLocSubscription(const SLocalSubscriptionInfo& local_info_); - - void ApplyExtSubscription(const SExternalSubscriptionInfo& external_info_, const SDataTypeInformation& tinfo_, const std::string& reader_par_); - void RemoveExtSubscription(const SExternalSubscriptionInfo& external_info_); + void ApplySubscription(const SSubscriptionInfo& subscription_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& layer_states_, const std::string& reader_par_); + void RemoveSubscription(const SSubscriptionInfo& subscription_info_); void RefreshRegistration(); void RefreshSendCounter(); @@ -124,12 +102,17 @@ namespace eCAL std::string Dump(const std::string& indent_ = ""); bool IsCreated() const { return(m_created); } - bool IsSubscribed() const { return(m_loc_subscribed || m_ext_subscribed); } - bool IsExtSubscribed() const { return(m_ext_subscribed); } + + bool IsSubscribed() const + { + std::lock_guard const lock(m_sub_map_mtx); + return(!m_sub_map.empty()); + } + size_t GetSubscriberCount() const { - std::lock_guard const lock(m_sub_map_sync); - return(m_loc_sub_map.size() + m_ext_sub_map.size()); + std::lock_guard const lock(m_sub_map_mtx); + return(m_sub_map.size()); } const std::string& GetTopicName() const { return(m_topic_name); } @@ -142,14 +125,15 @@ namespace eCAL void Connect(const std::string& tid_, const SDataTypeInformation& tinfo_); void Disconnect(); - void SetUseUdpMC(TLayer::eSendMode mode_); - void SetUseShm(TLayer::eSendMode mode_); - void SetUseTcp(TLayer::eSendMode mode_); + void StartTransportLayer(); + void StopTransportLayer(); + + void ActivateUdpLayer(); + void ActivateShmLayer(); + void ActivateTcpLayer(); - bool CheckWriterModes(); size_t PrepareWrite(long long id_, size_t len_); bool IsInternalSubscribedOnly(); - void LogSendMode(TLayer::eSendMode smode_, const std::string& base_msg_); int32_t GetFrequency(); @@ -161,64 +145,38 @@ namespace eCAL std::string m_topic_id; SDataTypeInformation m_topic_info; std::map m_attr; - size_t m_topic_size; - - size_t m_buffering_shm; - bool m_zero_copy; - long long m_acknowledge_timeout_ms; + size_t m_topic_size = 0; + Publisher::Configuration m_config; std::vector m_payload_buffer; std::atomic m_connected; - using LocalConnectedMapT = Util::CExpMap; - using ExternalConnectedMapT = Util::CExpMap; - mutable std::mutex m_sub_map_sync; - LocalConnectedMapT m_loc_sub_map; - ExternalConnectedMapT m_ext_sub_map; + using SSubscriptionMapT = Util::CExpMap>; + mutable std::mutex m_sub_map_mtx; + SSubscriptionMapT m_sub_map; using EventCallbackMapT = std::map; - std::mutex m_event_callback_map_sync; + std::mutex m_event_callback_map_mtx; EventCallbackMapT m_event_callback_map; - long long m_id; - long long m_clock; + long long m_id = 0; + long long m_clock = 0; - std::mutex m_frequency_calculator_mutex; + std::mutex m_frequency_calculator_mtx; ResettableFrequencyCalculator m_frequency_calculator; - std::atomic m_loc_subscribed; - std::atomic m_ext_subscribed; - - struct SWriter - { - struct SWriterMode - { - TLayer::eSendMode requested = TLayer::smode_off; - bool activated = false; - bool confirmed = false; - }; - - SWriterMode udp_mode; - SWriterMode tcp_mode; - SWriterMode shm_mode; - #if ECAL_CORE_TRANSPORT_UDP - std::unique_ptr udp; + std::unique_ptr m_writer_udp; #endif #if ECAL_CORE_TRANSPORT_SHM - std::unique_ptr shm; + std::unique_ptr m_writer_shm; #endif #if ECAL_CORE_TRANSPORT_TCP - std::unique_ptr tcp; + std::unique_ptr m_writer_tcp; #endif - }; - SWriter m_writer; - bool m_use_ttype; - bool m_use_tdesc; - int m_share_ttype; - int m_share_tdesc; + SLayerStates m_confirmed_layers; std::atomic m_created; }; } diff --git a/ecal/core/src/readwrite/ecal_writer_base.h b/ecal/core/src/readwrite/ecal_writer_base.h index dfb8c00ceb..48ef9cdec0 100644 --- a/ecal/core/src/readwrite/ecal_writer_base.h +++ b/ecal/core/src/readwrite/ecal_writer_base.h @@ -42,11 +42,8 @@ namespace eCAL virtual SWriterInfo GetInfo() = 0; - virtual void AddLocConnection(const std::string& /*process_id_*/, const std::string& /*topic_id_*/, const std::string& /*conn_par_*/) {}; - virtual void RemLocConnection(const std::string& /*process_id_*/, const std::string& /*topic_id_*/) {}; - - virtual void AddExtConnection(const std::string& /*host_name_*/, const std::string& /*process_id_*/, const std::string& /*topic_id_*/, const std::string& /*conn_par_*/) {}; - virtual void RemExtConnection(const std::string& /*host_name_*/, const std::string& /*process_id_*/, const std::string& /*topic_id_*/) {}; + virtual void ApplySubscription(const std::string& /*host_name_*/, const int32_t /*process_id_*/, const std::string& /*topic_id_*/, const std::string& /*conn_par_*/) {}; + virtual void RemoveSubscription(const std::string& /*host_name_*/, const int32_t /*process_id_*/, const std::string& /*topic_id_*/) {}; virtual Registration::ConnectionPar GetConnectionParameter() { return {}; }; diff --git a/ecal/core/src/readwrite/ecal_writer_data.h b/ecal/core/src/readwrite/ecal_writer_data.h index cae573f5ad..347c9de329 100644 --- a/ecal/core/src/readwrite/ecal_writer_data.h +++ b/ecal/core/src/readwrite/ecal_writer_data.h @@ -34,7 +34,6 @@ namespace eCAL long long clock = 0; size_t hash = 0; long long time = 0; - size_t buffering = 1; bool loopback = false; bool zero_copy = false; long long acknowledge_timeout_ms = 0; diff --git a/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp b/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp index ca8484d647..43d1b56fe7 100644 --- a/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp +++ b/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp @@ -22,7 +22,6 @@ **/ #include -#include #include #include @@ -33,18 +32,14 @@ namespace eCAL { const std::string CDataWriterSHM::m_memfile_base_name = "ecal_"; - CDataWriterSHM::CDataWriterSHM(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_) + CDataWriterSHM::CDataWriterSHM(const std::string& host_name_, const std::string& topic_name_, const std::string& /*topic_id_*/, const Publisher::SHM::Configuration& shm_config_) : + m_config(shm_config_) { + m_host_name = host_name_; m_topic_name = topic_name_; - // set attributes - m_memory_file_attr.min_size = Config::GetMemfileMinsizeBytes(); - m_memory_file_attr.reserve = Config::GetMemfileOverprovisioningPercentage(); - m_memory_file_attr.timeout_open_ms = PUB_MEMFILE_OPEN_TO; - m_memory_file_attr.timeout_ack_ms = Config::GetMemfileAckTimeoutMs(); - // initialize memory file buffer - SetBufferCount(m_buffer_count /*= 1*/); + SetBufferCount(m_config.memfile_buffer_count); } SWriterInfo CDataWriterSHM::GetInfo() @@ -61,49 +56,6 @@ namespace eCAL return info_; } - - bool CDataWriterSHM::SetBufferCount(size_t buffer_count_) - { - // no need to adapt anything - if (m_memory_file_vec.size() == buffer_count_) return true; - - // buffer count zero not allowed - if (buffer_count_ < 1) - { - Logging::Log(log_level_error, m_topic_name + "::CDataWriterSHM::SetBufferCount minimal number of memory files is 1 !"); - return false; - } - - // retrieve the memory file size of existing files - size_t memory_file_size(0); - if (!m_memory_file_vec.empty()) - { - memory_file_size = m_memory_file_vec[0]->GetSize(); - } - else - { - memory_file_size = m_memory_file_attr.min_size; - } - - // create memory file vector - m_memory_file_vec.clear(); - while (m_memory_file_vec.size() < buffer_count_) - { - auto sync_memfile = std::make_shared(m_memfile_base_name, memory_file_size, m_memory_file_attr); - if (sync_memfile->IsCreated()) - { - m_memory_file_vec.push_back(sync_memfile); - } - else - { - m_memory_file_vec.clear(); - Logging::Log(log_level_error, "CDataWriterSHM::SetBufferCount - FAILED"); - return false; - } - } - - return true; - } bool CDataWriterSHM::PrepareWrite(const SWriterAttr& attr_) { @@ -111,16 +63,6 @@ namespace eCAL // connection parameters needed bool ret_state(false); - // adapt number of used memory files if needed - if (attr_.buffering != m_buffer_count) - { - SetBufferCount(attr_.buffering); - - // store new buffer count and flag change - m_buffer_count = attr_.buffering; - ret_state |= true; - } - // adapt write index if needed m_write_idx %= m_memory_file_vec.size(); @@ -143,13 +85,16 @@ namespace eCAL return sent; } - void CDataWriterSHM::AddLocConnection(const std::string& process_id_, const std::string& /*topic_id_*/, const std::string& /*conn_par_*/) + void CDataWriterSHM::ApplySubscription(const std::string& host_name_, const int32_t process_id_, const std::string& /*topic_id_*/, const std::string& /*conn_par_*/) { + // we accept local connections only + if (host_name_ != m_host_name) return; + for (auto& memory_file : m_memory_file_vec) { - memory_file->Connect(process_id_); + memory_file->Connect(std::to_string(process_id_)); #ifndef NDEBUG - Logging::Log(log_level_debug1, std::string("CDataWriterSHM::AddLocConnection - Memory FileName: ") + memory_file->GetName() + " to ProcessId " + process_id_); + Logging::Log(log_level_debug1, std::string("CDataWriterSHM::ApplySubscription - Memory FileName: ") + memory_file->GetName() + " to ProcessId " + std::to_string(process_id_)); #endif } } @@ -163,4 +108,54 @@ namespace eCAL } return connection_par; } + + bool CDataWriterSHM::SetBufferCount(size_t buffer_count_) + { + // no need to adapt anything + if (m_memory_file_vec.size() == buffer_count_) return true; + + // buffer count zero not allowed + if (buffer_count_ < 1) + { + Logging::Log(log_level_error, m_topic_name + "::CDataWriterSHM::SetBufferCount minimal number of memory files is 1 !"); + return false; + } + + // prepare memfile attributes + SSyncMemoryFileAttr memory_file_attr = {}; + memory_file_attr.min_size = m_config.memfile_min_size_bytes; + memory_file_attr.reserve = m_config.memfile_reserve_percent; + memory_file_attr.timeout_open_ms = PUB_MEMFILE_OPEN_TO; + memory_file_attr.timeout_ack_ms = m_config.acknowledge_timeout_ms; + + // retrieve the memory file size of existing files + size_t memory_file_size(0); + if (!m_memory_file_vec.empty()) + { + memory_file_size = m_memory_file_vec[0]->GetSize(); + } + else + { + memory_file_size = memory_file_attr.min_size; + } + + // create memory file vector + m_memory_file_vec.clear(); + while (m_memory_file_vec.size() < buffer_count_) + { + auto sync_memfile = std::make_shared(m_memfile_base_name, memory_file_size, memory_file_attr); + if (sync_memfile->IsCreated()) + { + m_memory_file_vec.push_back(sync_memfile); + } + else + { + m_memory_file_vec.clear(); + Logging::Log(log_level_error, "CDataWriterSHM::SetBufferCount - FAILED"); + return false; + } + } + + return true; + } } diff --git a/ecal/core/src/readwrite/shm/ecal_writer_shm.h b/ecal/core/src/readwrite/shm/ecal_writer_shm.h index ddc5f4bc46..01517db79a 100644 --- a/ecal/core/src/readwrite/shm/ecal_writer_shm.h +++ b/ecal/core/src/readwrite/shm/ecal_writer_shm.h @@ -23,6 +23,8 @@ #pragma once +#include + #include "readwrite/ecal_writer_base.h" #include "io/shm/ecal_memfile_sync.h" @@ -36,24 +38,24 @@ namespace eCAL class CDataWriterSHM : public CDataWriterBase { public: - CDataWriterSHM(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_); + CDataWriterSHM(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::SHM::Configuration& shm_config_); SWriterInfo GetInfo() override; - bool SetBufferCount(size_t buffer_count_); - bool PrepareWrite(const SWriterAttr& attr_) override; bool Write(CPayloadWriter& payload_, const SWriterAttr& attr_) override; - void AddLocConnection(const std::string& process_id_, const std::string& topic_id_, const std::string& conn_par_) override; + void ApplySubscription(const std::string& host_name_, int32_t process_id_, const std::string& topic_id_, const std::string& conn_par_) override; Registration::ConnectionPar GetConnectionParameter() override; - protected: - size_t m_write_idx = 0; - size_t m_buffer_count = 1; - SSyncMemoryFileAttr m_memory_file_attr = {}; + protected: + bool SetBufferCount(size_t buffer_count_); + + Publisher::SHM::Configuration m_config; + + size_t m_write_idx = 0; std::vector> m_memory_file_vec; static const std::string m_memfile_base_name; }; diff --git a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.cpp b/ecal/core/src/readwrite/tcp/ecal_writer_tcp.cpp index 85bb6234ab..6334a40008 100644 --- a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.cpp +++ b/ecal/core/src/readwrite/tcp/ecal_writer_tcp.cpp @@ -38,7 +38,8 @@ namespace eCAL std::mutex CDataWriterTCP::g_tcp_writer_executor_mtx; std::shared_ptr CDataWriterTCP::g_tcp_writer_executor; - CDataWriterTCP::CDataWriterTCP(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_) + CDataWriterTCP::CDataWriterTCP(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::TCP::Configuration& tcp_config_) : + m_config(tcp_config_) { { const std::lock_guard lock(g_tcp_writer_executor_mtx); diff --git a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h b/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h index bd2cc4d66e..6258ae280c 100644 --- a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h +++ b/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h @@ -23,6 +23,8 @@ #pragma once +#include + #include "readwrite/ecal_writer_base.h" #include @@ -38,7 +40,7 @@ namespace eCAL class CDataWriterTCP : public CDataWriterBase { public: - CDataWriterTCP(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_); + CDataWriterTCP(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::TCP::Configuration& tcp_config_); SWriterInfo GetInfo() override; @@ -47,6 +49,8 @@ namespace eCAL Registration::ConnectionPar GetConnectionParameter() override; private: + Publisher::TCP::Configuration m_config; + std::vector m_header_buffer; static std::mutex g_tcp_writer_executor_mtx; diff --git a/ecal/core/src/readwrite/udp/ecal_reader_udp_mc.cpp b/ecal/core/src/readwrite/udp/ecal_reader_udp.cpp similarity index 98% rename from ecal/core/src/readwrite/udp/ecal_reader_udp_mc.cpp rename to ecal/core/src/readwrite/udp/ecal_reader_udp.cpp index d3a7ad9f1d..1bdcdf443e 100644 --- a/ecal/core/src/readwrite/udp/ecal_reader_udp_mc.cpp +++ b/ecal/core/src/readwrite/udp/ecal_reader_udp.cpp @@ -21,7 +21,7 @@ * @brief udp multicast reader and layer **/ -#include "ecal_reader_udp_mc.h" +#include "ecal_reader_udp.h" #include "ecal_global_accessors.h" #include "pubsub/ecal_subgate.h" @@ -111,6 +111,6 @@ namespace eCAL bool CUDPReaderLayer::ApplySample(const char* serialized_sample_data_, size_t serialized_sample_size_) { if (g_subgate() == nullptr) return false; - return g_subgate()->ApplySample(serialized_sample_data_, serialized_sample_size_, tl_ecal_udp_mc); + return g_subgate()->ApplySample(serialized_sample_data_, serialized_sample_size_, tl_ecal_udp); } } diff --git a/ecal/core/src/readwrite/udp/ecal_reader_udp_mc.h b/ecal/core/src/readwrite/udp/ecal_reader_udp.h similarity index 100% rename from ecal/core/src/readwrite/udp/ecal_reader_udp_mc.h rename to ecal/core/src/readwrite/udp/ecal_reader_udp.h diff --git a/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.cpp b/ecal/core/src/readwrite/udp/ecal_writer_udp.cpp similarity index 95% rename from ecal/core/src/readwrite/udp/ecal_writer_udp_mc.cpp rename to ecal/core/src/readwrite/udp/ecal_writer_udp.cpp index 60055517d3..8466a38f5d 100644 --- a/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.cpp +++ b/ecal/core/src/readwrite/udp/ecal_writer_udp.cpp @@ -21,10 +21,9 @@ * @brief udp data writer **/ -#include #include -#include "ecal_writer_udp_mc.h" +#include "ecal_writer_udp.h" #include "io/udp/ecal_udp_configurations.h" #include "serialization/ecal_serialize_sample_payload.h" @@ -32,7 +31,8 @@ namespace eCAL { - CDataWriterUdpMC::CDataWriterUdpMC(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_) + CDataWriterUdpMC::CDataWriterUdpMC(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::UDP::Configuration& udp_config_) : + m_config(udp_config_) { m_host_name = host_name_; m_topic_name = topic_name_; @@ -44,7 +44,7 @@ namespace eCAL attr.port = UDP::GetPayloadPort(); attr.ttl = UDP::GetMulticastTtl(); attr.broadcast = UDP::IsBroadcast(); - attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); + attr.sndbuf = m_config.sndbuf_size_bytes; // create udp/sample sender with activated loop-back attr.loopback = true; diff --git a/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.h b/ecal/core/src/readwrite/udp/ecal_writer_udp.h similarity index 88% rename from ecal/core/src/readwrite/udp/ecal_writer_udp_mc.h rename to ecal/core/src/readwrite/udp/ecal_writer_udp.h index fc7d820ae2..a9b2d4eabc 100644 --- a/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.h +++ b/ecal/core/src/readwrite/udp/ecal_writer_udp.h @@ -23,6 +23,8 @@ #pragma once +#include + #include "io/udp/ecal_udp_sample_sender.h" #include "readwrite/ecal_writer_base.h" @@ -35,13 +37,15 @@ namespace eCAL class CDataWriterUdpMC : public CDataWriterBase { public: - CDataWriterUdpMC(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_); + CDataWriterUdpMC(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::UDP::Configuration& udp_config_); SWriterInfo GetInfo() override; bool Write(const void* buf_, const SWriterAttr& attr_) override; protected: + Publisher::UDP::Configuration m_config; + std::vector m_sample_buffer; std::shared_ptr m_sample_sender_loopback; std::shared_ptr m_sample_sender_no_loopback; diff --git a/ecal/core/src/registration/ecal_registration_receiver.cpp b/ecal/core/src/registration/ecal_registration_receiver.cpp index b0e05d5ceb..002bd0fed1 100644 --- a/ecal/core/src/registration/ecal_registration_receiver.cpp +++ b/ecal/core/src/registration/ecal_registration_receiver.cpp @@ -47,7 +47,7 @@ namespace eCAL std::atomic CRegistrationReceiver::m_created; CRegistrationReceiver::CRegistrationReceiver() : - m_network(NET_ENABLED), + m_network(Config::IsNetworkEnabled()), m_loopback(false), m_callback_pub(nullptr), m_callback_sub(nullptr), @@ -69,9 +69,6 @@ namespace eCAL { if(m_created) return; - // network mode - m_network = Config::IsNetworkEnabled(); - // receive registration from shared memory and or udp m_use_registration_udp = !Config::Experimental::IsNetworkMonitoringDisabled(); m_use_registration_shm = Config::Experimental::IsShmMonitoringEnabled(); @@ -272,25 +269,24 @@ namespace eCAL void CRegistrationReceiver::ApplySubscriberRegistration(const Registration::Sample& sample_) { #if ECAL_CORE_PUBLISHER + if (g_pubgate() == nullptr) return; + // process registrations from same host group if (IsHostGroupMember(sample_)) { // do not register local entities, only if loop back flag is set true if (m_loopback || (sample_.topic.pid != Process::GetProcessID())) { - if (g_pubgate() != nullptr) + switch (sample_.cmd_type) { - switch (sample_.cmd_type) - { - case bct_reg_subscriber: - g_pubgate()->ApplyLocSubRegistration(sample_); - break; - case bct_unreg_subscriber: - g_pubgate()->ApplyLocSubUnregistration(sample_); - break; - default: - break; - } + case bct_reg_subscriber: + g_pubgate()->ApplySubRegistration(sample_); + break; + case bct_unreg_subscriber: + g_pubgate()->ApplySubUnregistration(sample_); + break; + default: + break; } } } @@ -299,19 +295,16 @@ namespace eCAL { if (m_network) { - if (g_pubgate() != nullptr) + switch (sample_.cmd_type) { - switch (sample_.cmd_type) - { - case bct_reg_subscriber: - g_pubgate()->ApplyExtSubRegistration(sample_); - break; - case bct_unreg_subscriber: - g_pubgate()->ApplyExtSubUnregistration(sample_); - break; - default: - break; - } + case bct_reg_subscriber: + g_pubgate()->ApplySubRegistration(sample_); + break; + case bct_unreg_subscriber: + g_pubgate()->ApplySubUnregistration(sample_); + break; + default: + break; } } } @@ -321,25 +314,24 @@ namespace eCAL void CRegistrationReceiver::ApplyPublisherRegistration(const Registration::Sample& sample_) { #if ECAL_CORE_SUBSCRIBER + if (g_subgate() == nullptr) return; + // process registrations from same host group if (IsHostGroupMember(sample_)) { // do not register local entities, only if loop back flag is set true if (m_loopback || (sample_.topic.pid != Process::GetProcessID())) { - if (g_subgate() != nullptr) + switch (sample_.cmd_type) { - switch (sample_.cmd_type) - { - case bct_reg_publisher: - g_subgate()->ApplyLocPubRegistration(sample_); - break; - case bct_unreg_publisher: - g_subgate()->ApplyLocPubUnregistration(sample_); - break; - default: - break; - } + case bct_reg_publisher: + g_subgate()->ApplyPubRegistration(sample_); + break; + case bct_unreg_publisher: + g_subgate()->ApplyPubUnregistration(sample_); + break; + default: + break; } } } @@ -348,19 +340,16 @@ namespace eCAL { if (m_network) { - if (g_subgate() != nullptr) + switch (sample_.cmd_type) { - switch (sample_.cmd_type) - { - case bct_reg_publisher: - g_subgate()->ApplyExtPubRegistration(sample_); - break; - case bct_unreg_publisher: - g_subgate()->ApplyExtPubUnregistration(sample_); - break; - default: - break; - } + case bct_reg_publisher: + g_subgate()->ApplyPubRegistration(sample_); + break; + case bct_unreg_publisher: + g_subgate()->ApplyPubUnregistration(sample_); + break; + default: + break; } } } diff --git a/ecal/core/src/registration/ecal_registration_receiver.h b/ecal/core/src/registration/ecal_registration_receiver.h index 1d64032da3..15a4945d20 100644 --- a/ecal/core/src/registration/ecal_registration_receiver.h +++ b/ecal/core/src/registration/ecal_registration_receiver.h @@ -30,8 +30,6 @@ #include #include -#include "ecal_def.h" - #include "io/udp/ecal_udp_sample_receiver.h" #include "serialization/ecal_struct_sample_registration.h" diff --git a/ecal/core/src/serialization/ecal_struct_sample_common.h b/ecal/core/src/serialization/ecal_struct_sample_common.h index a30247b6cc..3524f24d7e 100644 --- a/ecal/core/src/serialization/ecal_struct_sample_common.h +++ b/ecal/core/src/serialization/ecal_struct_sample_common.h @@ -44,10 +44,10 @@ namespace eCAL enum eTLayerType { - tl_none = 0, - tl_ecal_udp_mc = 1, - tl_ecal_shm = 4, - tl_ecal_tcp = 5, - tl_all = 255, + tl_none = 0, + tl_ecal_udp = 1, + tl_ecal_shm = 4, + tl_ecal_tcp = 5, + tl_all = 255, }; } diff --git a/ecal/core/src/service/ecal_clientgate.cpp b/ecal/core/src/service/ecal_clientgate.cpp index 9bdc535404..f3e49e33d3 100644 --- a/ecal/core/src/service/ecal_clientgate.cpp +++ b/ecal/core/src/service/ecal_clientgate.cpp @@ -40,16 +40,16 @@ namespace eCAL CClientGate::~CClientGate() { - Destroy(); + Stop(); } - void CClientGate::Create() + void CClientGate::Start() { if (m_created) return; m_created = true; } - void CClientGate::Destroy() + void CClientGate::Stop() { if (!m_created) return; @@ -57,7 +57,7 @@ namespace eCAL const std::shared_lock lock(m_client_set_sync); for (const auto& client : m_client_set) { - client->Destroy(); + client->Stop(); } m_created = false; diff --git a/ecal/core/src/service/ecal_clientgate.h b/ecal/core/src/service/ecal_clientgate.h index e48675190c..d3a539d9cb 100644 --- a/ecal/core/src/service/ecal_clientgate.h +++ b/ecal/core/src/service/ecal_clientgate.h @@ -45,8 +45,8 @@ namespace eCAL CClientGate(); ~CClientGate(); - void Create(); - void Destroy(); + void Start(); + void Stop(); bool Register (CServiceClientImpl* client_); bool Unregister(CServiceClientImpl* client_); diff --git a/ecal/core/src/service/ecal_service_client.cpp b/ecal/core/src/service/ecal_service_client.cpp index 6d8993011a..aaab7df3f9 100644 --- a/ecal/core/src/service/ecal_service_client.cpp +++ b/ecal/core/src/service/ecal_service_client.cpp @@ -96,13 +96,15 @@ namespace eCAL { if (m_created) return(false); + // create client m_service_client_impl = CServiceClientImpl::CreateInstance(service_name_, method_information_map_); - // register this client + // register client if (g_clientgate() != nullptr) g_clientgate()->Register(m_service_client_impl.get()); + // we made it :-) m_created = true; - return(true); + return(m_created); } /** @@ -115,11 +117,12 @@ namespace eCAL if(!m_created) return(false); m_created = false; - // unregister this client + // unregister client if (g_clientgate() != nullptr) g_clientgate()->Unregister(m_service_client_impl.get()); - m_service_client_impl->Destroy(); - m_service_client_impl = nullptr; + // stop & destroy client + m_service_client_impl->Stop(); + m_service_client_impl.reset(); return(true); } diff --git a/ecal/core/src/service/ecal_service_client_impl.cpp b/ecal/core/src/service/ecal_service_client_impl.cpp index 3a3bd20e10..90e2c37579 100644 --- a/ecal/core/src/service/ecal_service_client_impl.cpp +++ b/ecal/core/src/service/ecal_service_client_impl.cpp @@ -48,7 +48,7 @@ namespace eCAL std::shared_ptr CServiceClientImpl::CreateInstance(const std::string& service_name_, const ServiceMethodInformationMapT& method_information_map_) { auto instance = std::shared_ptr(new CServiceClientImpl()); - instance->Create(service_name_, method_information_map_); + instance->Start(service_name_, method_information_map_); return instance; } @@ -63,16 +63,16 @@ namespace eCAL CServiceClientImpl::~CServiceClientImpl() { - Destroy(); + Stop(); } - bool CServiceClientImpl::Create(const std::string& service_name_, const ServiceMethodInformationMapT& methods_information_map_) + bool CServiceClientImpl::Start(const std::string& service_name_, const ServiceMethodInformationMapT& method_information_map_) { if (m_created) return(false); // set service name and methods - m_service_name = service_name_; - m_method_information_map = methods_information_map_; + m_service_name = service_name_; + m_method_information_map = method_information_map_; // initialize call count map for (const auto& method_information_pair : m_method_information_map) @@ -94,7 +94,7 @@ namespace eCAL return(true); } - bool CServiceClientImpl::Destroy() + bool CServiceClientImpl::Stop() { if (!m_created) return(false); diff --git a/ecal/core/src/service/ecal_service_client_impl.h b/ecal/core/src/service/ecal_service_client_impl.h index 91f75219d5..c1368c10f6 100644 --- a/ecal/core/src/service/ecal_service_client_impl.h +++ b/ecal/core/src/service/ecal_service_client_impl.h @@ -49,9 +49,8 @@ namespace eCAL public: ~CServiceClientImpl(); - bool Create(const std::string& service_name_, const ServiceMethodInformationMapT& method_information_map_); - - bool Destroy(); + bool Start(const std::string& service_name_, const ServiceMethodInformationMapT& method_information_map_); + bool Stop(); bool SetHostName(const std::string& host_name_); diff --git a/ecal/core/src/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp index 1257c1088c..0545deee11 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -74,13 +74,15 @@ namespace eCAL { if(m_created) return(false); + // create service m_service_server_impl = CServiceServerImpl::CreateInstance(service_name_); - // register this service + // register service if (g_servicegate() != nullptr) g_servicegate()->Register(m_service_server_impl.get()); + // we made it :-) m_created = true; - return(true); + return(m_created); } /** @@ -93,11 +95,12 @@ namespace eCAL if(!m_created) return(false); m_created = false; - // unregister this service + // unregister service if (g_servicegate() != nullptr) g_servicegate()->Unregister(m_service_server_impl.get()); - m_service_server_impl->Destroy(); - m_service_server_impl = nullptr; + // stop & destroy service + m_service_server_impl->Stop(); + m_service_server_impl.reset(); return(true); } diff --git a/ecal/core/src/service/ecal_service_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index bb61c979a3..bc35617942 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -46,7 +46,7 @@ namespace eCAL std::shared_ptr CServiceServerImpl::CreateInstance(const std::string& service_name_) { auto instance = std::shared_ptr (new CServiceServerImpl()); - instance->Create(service_name_); + instance->Start(service_name_); return instance; } @@ -57,10 +57,10 @@ namespace eCAL CServiceServerImpl::~CServiceServerImpl() { - Destroy(); + Stop(); } - bool CServiceServerImpl::Create(const std::string& service_name_) + bool CServiceServerImpl::Start(const std::string& service_name_) { if (m_created) return(false); @@ -134,7 +134,7 @@ namespace eCAL return(true); } - bool CServiceServerImpl::Destroy() + bool CServiceServerImpl::Stop() { if (!m_created) return(false); diff --git a/ecal/core/src/service/ecal_service_server_impl.h b/ecal/core/src/service/ecal_service_server_impl.h index 6aee86de4f..fec56d9071 100644 --- a/ecal/core/src/service/ecal_service_server_impl.h +++ b/ecal/core/src/service/ecal_service_server_impl.h @@ -60,9 +60,8 @@ namespace eCAL ~CServiceServerImpl(); - bool Create(const std::string& service_name_); - - bool Destroy(); + bool Start(const std::string& service_name_); + bool Stop(); bool AddDescription(const std::string& method_, const SDataTypeInformation& request_type_information_, const SDataTypeInformation& response_type_information_); diff --git a/ecal/core/src/service/ecal_service_singleton_manager.cpp b/ecal/core/src/service/ecal_service_singleton_manager.cpp index 7ad89086d9..fcaf6a99f5 100644 --- a/ecal/core/src/service/ecal_service_singleton_manager.cpp +++ b/ecal/core/src/service/ecal_service_singleton_manager.cpp @@ -43,7 +43,7 @@ namespace eCAL eCAL::Logging::Log(eCAL_Logging_eLogLevel::log_level_debug1, "[" + node_name + "] " + message); break; case LogLevel::Info: - eCAL::Logging::Log(eCAL_Logging_eLogLevel::log_level_info, "[" + node_name + "] " + message); + eCAL::Logging::Log(eCAL_Logging_eLogLevel::log_level_debug1, "[" + node_name + "] " + message); break; case LogLevel::Warning: eCAL::Logging::Log(eCAL_Logging_eLogLevel::log_level_warning, "[" + node_name + "] " + message); diff --git a/ecal/core/src/service/ecal_servicegate.cpp b/ecal/core/src/service/ecal_servicegate.cpp index 76d0aa7ad4..fe587ea929 100644 --- a/ecal/core/src/service/ecal_servicegate.cpp +++ b/ecal/core/src/service/ecal_servicegate.cpp @@ -37,16 +37,16 @@ namespace eCAL CServiceGate::~CServiceGate() { - Destroy(); + Stop(); } - void CServiceGate::Create() + void CServiceGate::Start() { if(m_created) return; m_created = true; } - void CServiceGate::Destroy() + void CServiceGate::Stop() { if(!m_created) return; @@ -54,7 +54,7 @@ namespace eCAL const std::shared_lock lock(m_service_set_sync); for (const auto& service : m_service_set) { - service->Destroy(); + service->Stop(); } m_created = false; diff --git a/ecal/core/src/service/ecal_servicegate.h b/ecal/core/src/service/ecal_servicegate.h index 488746122a..da3047f6a1 100644 --- a/ecal/core/src/service/ecal_servicegate.h +++ b/ecal/core/src/service/ecal_servicegate.h @@ -39,8 +39,8 @@ namespace eCAL CServiceGate(); ~CServiceGate(); - void Create(); - void Destroy(); + void Start(); + void Stop(); bool Register (CServiceServerImpl* service_); bool Unregister(CServiceServerImpl* service_); diff --git a/ecal/samples/CMakeLists.txt b/ecal/samples/CMakeLists.txt index bb51ec3aa6..ccb0f0a67c 100644 --- a/ecal/samples/CMakeLists.txt +++ b/ecal/samples/CMakeLists.txt @@ -103,6 +103,12 @@ if(ECAL_CORE_PUBLISHER) if(ECAL_CORE_HAS_PROTOBUF) add_subdirectory(cpp/pubsub/protobuf/person_events_snd) add_subdirectory(cpp/pubsub/protobuf/person_snd) + if(ECAL_CORE_TRANSPORT_UDP) + add_subdirectory(cpp/pubsub/protobuf/person_snd_udp) + endif() + if(ECAL_CORE_TRANSPORT_TCP) + add_subdirectory(cpp/pubsub/protobuf/person_snd_tcp) + endif() endif() endif() diff --git a/ecal/samples/cpp/benchmarks/datarate_snd/src/datarate_snd.cpp b/ecal/samples/cpp/benchmarks/datarate_snd/src/datarate_snd.cpp index 4f2c224da4..6349ee7a6f 100644 --- a/ecal/samples/cpp/benchmarks/datarate_snd/src/datarate_snd.cpp +++ b/ecal/samples/cpp/benchmarks/datarate_snd/src/datarate_snd.cpp @@ -64,8 +64,17 @@ int main(int argc, char **argv) // initialize eCAL API eCAL::Initialize(argc, argv, "datarate_snd"); + // create publisher config + eCAL::Publisher::Configuration pub_config; + // set zero copy + pub_config.shm.zero_copy_mode = zero_copy; + // set buffering + pub_config.shm.memfile_buffer_count = buffer_count; + // set handshake acknowledgement timeout [ms] + pub_config.shm.acknowledge_timeout_ms = acknowledge_time; + // new publisher - eCAL::CPublisher pub(topic_name); + eCAL::CPublisher pub(topic_name, pub_config); // default send string size *= 1024 * 1024; @@ -76,15 +85,6 @@ int main(int argc, char **argv) } send_s.resize(size); - // set zero copy - //pub.ShmEnableZeroCopy(zero_copy); // TODO: NEW PARAMETER API - - // set buffering - //pub.ShmSetBufferCount(buffer_count); // TODO: NEW PARAMETER API - - // set handshake acknowledgement timeout [ms] - //pub.ShmSetAcknowledgeTimeout(acknowledge_time); // TODO: NEW PARAMETER API - // send updates while(eCAL::Ok()) { diff --git a/ecal/samples/cpp/benchmarks/latency_snd/src/latency_snd.cpp b/ecal/samples/cpp/benchmarks/latency_snd/src/latency_snd.cpp index 16860f4f09..bddb0e29ff 100644 --- a/ecal/samples/cpp/benchmarks/latency_snd/src/latency_snd.cpp +++ b/ecal/samples/cpp/benchmarks/latency_snd/src/latency_snd.cpp @@ -49,14 +49,17 @@ void do_run(const int runs, int snd_size /*kB*/, int mem_buffer, bool zero_copy) // initialize eCAL API eCAL::Initialize(0, nullptr, "latency_snd"); - // create publisher and subscriber - eCAL::CPublisher pub("ping"); - + // create publisher config + eCAL::Publisher::Configuration pub_config; // set number of publisher memory buffers - //pub.ShmSetBufferCount(mem_buffer); // TODO: NEW PARAMETER API - + pub_config.shm.memfile_buffer_count = mem_buffer; // enable zero copy mode - //pub.ShmEnableZeroCopy(zero_copy); // TODO: NEW PARAMETER API + pub_config.shm.zero_copy_mode = zero_copy; + // set acknowledgement timeout to 100ms + pub_config.shm.acknowledge_timeout_ms = 100; + + // create publisher + eCAL::CPublisher pub("ping", pub_config); // prepare send buffer CBinaryPayload payload(snd_size * 1024); @@ -70,9 +73,7 @@ void do_run(const int runs, int snd_size /*kB*/, int mem_buffer, bool zero_copy) { // get microseconds auto snd_time = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - // send message (with receive timeout 100 ms) - //pub.Send(payload, snd_time, 100 /*ms*/); - pub.Send(payload, snd_time); // TODO: NEW PARAMETER API + pub.Send(payload, snd_time); } // log test diff --git a/ecal/samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp b/ecal/samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp index 67fe065cbc..0e99485386 100644 --- a/ecal/samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp +++ b/ecal/samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp @@ -45,22 +45,25 @@ int main(int argc, char **argv) // create payload CBinaryPayload binary_payload(payload_size); - // create publisher - eCAL::CPublisher pub("Performance"); - - // enable zero copy mode - //std::cout << "Zero copy mode: " << zero_copy << std::endl; - //pub.ShmEnableZeroCopy(zero_copy); // TODO: NEW PARAMETER API + // create publisher config + eCAL::Publisher::Configuration pub_config; - // set write buffer count - //std::cout << "Number of write buffers: " << buffer_count << std::endl; - //pub.ShmSetBufferCount(buffer_count); // TODO: NEW PARAMETER API + // enable zero copy mode + std::cout << "Zero copy mode: " << zero_copy << std::endl; + pub_config.shm.zero_copy_mode = zero_copy; + // set write buffer count + std::cout << "Number of write buffers: " << buffer_count << std::endl; + pub_config.shm.memfile_buffer_count = buffer_count; + // enable handshake mode - //std::cout << "Acknowledge timeout: " << acknowledge_timeout_ms << " ms" << std::endl; - //pub.ShmSetAcknowledgeTimeout(acknowledge_timeout_ms); // TODO: NEW PARAMETER API - //std::cout << std::endl; + std::cout << "Acknowledge timeout: " << acknowledge_timeout_ms << " ms" << std::endl; + pub_config.shm.acknowledge_timeout_ms = acknowledge_timeout_ms; + std::cout << std::endl; + // create publisher + eCAL::CPublisher pub("Performance", pub_config); + // counter long long msgs (0); long long bytes(0); diff --git a/ecal/samples/cpp/benchmarks/pubsub_throughput/src/pubsub_throughput.cpp b/ecal/samples/cpp/benchmarks/pubsub_throughput/src/pubsub_throughput.cpp index 0a42e4a219..f306a9f819 100644 --- a/ecal/samples/cpp/benchmarks/pubsub_throughput/src/pubsub_throughput.cpp +++ b/ecal/samples/cpp/benchmarks/pubsub_throughput/src/pubsub_throughput.cpp @@ -28,19 +28,39 @@ const auto g_snd_size (8* 1024 * 1024); const auto g_snd_loops(1000); -void throughput_test(int snd_size, int snd_loops, eCAL::TLayer::eTransportLayer /*layer*/, bool /*zero_copy*/) +void throughput_test(int snd_size, int snd_loops, eCAL::TLayer::eTransportLayer layer, bool zero_copy) { // create payload CBinaryPayload payload(snd_size); - // create publisher - eCAL::CPublisher pub("throughput"); + // create publisher config + eCAL::Publisher::Configuration pub_config; + // set transport layer - //pub.SetLayerMode(eCAL::TLayer::tlayer_all, eCAL::TLayer::smode_off); // TODO: NEW PARAMETER API - //DO: NEW PARAMETER API - // set attributes - //pub.ShmEnableZeroCopy(zero_copy); // TODO: NEW PARAMETER API - //pub.ShmSetAcknowledgeTimeout(100); // TODO: NEW PARAMETER API + pub_config.shm.enable = false; + pub_config.udp.enable = false; + pub_config.tcp.enable = false; + switch (layer) + { + case eCAL::TLayer::tlayer_shm: + pub_config.shm.enable = true; + break; + case eCAL::TLayer::tlayer_udp_mc: + pub_config.udp.enable = true; + break; + case eCAL::TLayer::tlayer_tcp: + pub_config.tcp.enable = true; + break; + } + + // enable zero copy mode + pub_config.shm.zero_copy_mode = zero_copy; + + // enable handshake mode + pub_config.shm.acknowledge_timeout_ms = 100; + + // create publisher + eCAL::CPublisher pub("throughput", pub_config); // create subscriber eCAL::CSubscriber sub("throughput"); diff --git a/ecal/samples/cpp/pubsub/binary/binary_zero_copy_snd/src/binary_zero_copy_snd.cpp b/ecal/samples/cpp/pubsub/binary/binary_zero_copy_snd/src/binary_zero_copy_snd.cpp index 05ef1cc6a4..4524357bc1 100644 --- a/ecal/samples/cpp/pubsub/binary/binary_zero_copy_snd/src/binary_zero_copy_snd.cpp +++ b/ecal/samples/cpp/pubsub/binary/binary_zero_copy_snd/src/binary_zero_copy_snd.cpp @@ -123,11 +123,14 @@ int main(int argc, char** argv) // initialize eCAL API eCAL::Initialize(argc, argv, nodeName); - // create the publisher - eCAL::CPublisher pub(topicName, { "custom", structTypeName, "" }); + // create publisher config + eCAL::Publisher::Configuration pub_config; // turn zero copy mode on - //pub.ShmEnableZeroCopy(true); // TODO: NEW PARAMETER API + pub_config.shm.zero_copy_mode = true; + + // create the publisher + eCAL::CPublisher pub(topicName, { "custom", structTypeName, "" }, pub_config); // create the simple struct payload CStructPayload struct_payload; diff --git a/ecal/samples/cpp/pubsub/protobuf/person_rec/CMakeLists.txt b/ecal/samples/cpp/pubsub/protobuf/person_rec/CMakeLists.txt index 8925dd042b..303df38810 100644 --- a/ecal/samples/cpp/pubsub/protobuf/person_rec/CMakeLists.txt +++ b/ecal/samples/cpp/pubsub/protobuf/person_rec/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ target_link_libraries(${PROJECT_NAME} eCAL::core_protobuf ) -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) ecal_install_sample(${PROJECT_NAME}) diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/CMakeLists.txt b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/CMakeLists.txt new file mode 100644 index 0000000000..131fa14ab9 --- /dev/null +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/CMakeLists.txt @@ -0,0 +1,50 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2019 Continental Corporation +# +# 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. +# +# ========================= eCAL LICENSE ================================= + +cmake_minimum_required(VERSION 3.10) + +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) + +project(person_snd_tcp) + +find_package(eCAL REQUIRED) +find_package(Protobuf REQUIRED) + +set(person_snd_tcp_src + src/person_snd_tcp.cpp +) + +set(person_snd_tcp_proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto +) + +ecal_add_sample(${PROJECT_NAME} ${person_snd_tcp_src}) +PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_snd_tcp_proto}) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + eCAL::core_protobuf +) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +ecal_install_sample(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/protobuf) diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/person_snd_tcp.cpp b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/person_snd_tcp.cpp new file mode 100644 index 0000000000..69432a494b --- /dev/null +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/person_snd_tcp.cpp @@ -0,0 +1,81 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include +#include + +#include + +#include "person.pb.h" + +int main(int argc, char **argv) +{ + // initialize eCAL API + eCAL::Initialize(argc, argv, "person publisher tcp"); + + // set process state + eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); + + // create a publisher config + eCAL::Publisher::Configuration pub_config; + + // switch shm and udp layer off, tcp layer on + pub_config.shm.enable = false; + pub_config.udp.enable = false; + pub_config.tcp.enable = true; + + // create a publisher (topic name "person") + eCAL::protobuf::CPublisher pub("person", pub_config); + + // generate a class instance of Person + pb::People::Person person; + + // enter main loop + auto cnt(0); + while (eCAL::Ok()) + { + // set person object content + person.set_id(++cnt); + person.set_name("Max"); + person.set_stype(pb::People::Person_SType_MALE); + person.set_email("max@mail.net"); + person.mutable_dog()->set_name("Brandy"); + person.mutable_house()->set_rooms(4); + + // send the person object + pub.Send(person); + + // print content + std::cout << "person id : " << person.id() << std::endl; + std::cout << "person name : " << person.name() << std::endl; + std::cout << "person stype : " << person.stype() << std::endl; + std::cout << "person email : " << person.email() << std::endl; + std::cout << "dog.name : " << person.dog().name() << std::endl; + std::cout << "house.rooms : " << person.house().rooms() << std::endl; + std::cout << std::endl; + + // sleep 500 ms + eCAL::Process::SleepMS(500); + } + + // finalize eCAL API + eCAL::Finalize(); + + return(0); +} diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/protobuf/animal.proto b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/protobuf/animal.proto new file mode 100644 index 0000000000..625a78db82 --- /dev/null +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/protobuf/animal.proto @@ -0,0 +1,28 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +syntax = "proto3"; + +package pb.Animal; + +message Dog +{ + string name = 1; + string colour = 2; +} diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/protobuf/house.proto b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/protobuf/house.proto new file mode 100644 index 0000000000..fbe8cce4c7 --- /dev/null +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/protobuf/house.proto @@ -0,0 +1,27 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +syntax = "proto3"; + +package pb.Environment; + +message House +{ + int32 rooms = 1; +} diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/protobuf/person.proto b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/protobuf/person.proto new file mode 100644 index 0000000000..4200a433f0 --- /dev/null +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/protobuf/person.proto @@ -0,0 +1,42 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +syntax = "proto3"; + +import "animal.proto"; +import "house.proto"; + +package pb.People; + +message Person +{ + enum SType + { + MALE = 0; + FEMALE = 1; + } + + int32 id = 1; + string name = 2; + SType stype = 3; + string email = 4; + + Animal.Dog dog = 5; + Environment.House house = 6; +} diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/CMakeLists.txt b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/CMakeLists.txt new file mode 100644 index 0000000000..05e183baca --- /dev/null +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/CMakeLists.txt @@ -0,0 +1,50 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2019 Continental Corporation +# +# 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. +# +# ========================= eCAL LICENSE ================================= + +cmake_minimum_required(VERSION 3.10) + +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) + +project(person_snd_udp) + +find_package(eCAL REQUIRED) +find_package(Protobuf REQUIRED) + +set(person_snd_udp_src + src/person_snd_udp.cpp +) + +set(person_snd_udp_proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto +) + +ecal_add_sample(${PROJECT_NAME} ${person_snd_udp_src}) +PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_snd_udp_proto}) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + eCAL::core_protobuf +) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +ecal_install_sample(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/protobuf) diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/person_snd_udp.cpp b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/person_snd_udp.cpp new file mode 100644 index 0000000000..655909392f --- /dev/null +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/person_snd_udp.cpp @@ -0,0 +1,81 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include +#include + +#include + +#include "person.pb.h" + +int main(int argc, char **argv) +{ + // initialize eCAL API + eCAL::Initialize(argc, argv, "person publisher multicast"); + + // set process state + eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); + + // create a publisher config + eCAL::Publisher::Configuration pub_config; + + // switch shm and tcp layer off, udp layer on + pub_config.shm.enable = false; + pub_config.udp.enable = true; + pub_config.tcp.enable = false; + + // create a publisher (topic name "person") + eCAL::protobuf::CPublisher pub("person", pub_config); + + // generate a class instance of Person + pb::People::Person person; + + // enter main loop + auto cnt(0); + while (eCAL::Ok()) + { + // set person object content + person.set_id(++cnt); + person.set_name("Max"); + person.set_stype(pb::People::Person_SType_MALE); + person.set_email("max@mail.net"); + person.mutable_dog()->set_name("Brandy"); + person.mutable_house()->set_rooms(4); + + // send the person object + pub.Send(person); + + // print content + std::cout << "person id : " << person.id() << std::endl; + std::cout << "person name : " << person.name() << std::endl; + std::cout << "person stype : " << person.stype() << std::endl; + std::cout << "person email : " << person.email() << std::endl; + std::cout << "dog.name : " << person.dog().name() << std::endl; + std::cout << "house.rooms : " << person.house().rooms() << std::endl; + std::cout << std::endl; + + // sleep 500 ms + eCAL::Process::SleepMS(500); + } + + // finalize eCAL API + eCAL::Finalize(); + + return(0); +} diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/protobuf/animal.proto b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/protobuf/animal.proto new file mode 100644 index 0000000000..625a78db82 --- /dev/null +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/protobuf/animal.proto @@ -0,0 +1,28 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +syntax = "proto3"; + +package pb.Animal; + +message Dog +{ + string name = 1; + string colour = 2; +} diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/protobuf/house.proto b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/protobuf/house.proto new file mode 100644 index 0000000000..fbe8cce4c7 --- /dev/null +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/protobuf/house.proto @@ -0,0 +1,27 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +syntax = "proto3"; + +package pb.Environment; + +message House +{ + int32 rooms = 1; +} diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/protobuf/person.proto b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/protobuf/person.proto new file mode 100644 index 0000000000..4200a433f0 --- /dev/null +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/protobuf/person.proto @@ -0,0 +1,42 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +syntax = "proto3"; + +import "animal.proto"; +import "house.proto"; + +package pb.People; + +message Person +{ + enum SType + { + MALE = 0; + FEMALE = 1; + } + + int32 id = 1; + string name = 2; + SType stype = 3; + string email = 4; + + Animal.Dog dog = 5; + Environment.House house = 6; +} diff --git a/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/CMakeLists.txt b/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/CMakeLists.txt index 0bff96455d..2e62b42879 100644 --- a/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/CMakeLists.txt +++ b/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ target_link_libraries(${PROJECT_NAME} eCAL::core_protobuf ) -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) ecal_install_sample(${PROJECT_NAME}) diff --git a/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/src/proto_dyn_rec.cpp b/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/src/proto_dyn_rec.cpp index f597681421..8863f64922 100644 --- a/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/src/proto_dyn_rec.cpp +++ b/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/src/proto_dyn_rec.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -272,9 +272,9 @@ void ProcProtoMsg(const google::protobuf::Message& msg_, const std::string& pref } } -void ProtoMsgCallback(const char* topic_name_, const google::protobuf::Message& msg_) +void ProtoMsgCallback(const char* topic_name_, const std::shared_ptr& msg_) { - ProcProtoMsg(msg_, topic_name_); + ProcProtoMsg(*msg_, topic_name_); std::cout << std::endl; } diff --git a/ecal/tests/c/core_test/src/core_test.cpp b/ecal/tests/c/core_test/src/core_test.cpp index 2eff646da6..5f04527770 100644 --- a/ecal/tests/c/core_test/src/core_test.cpp +++ b/ecal/tests/c/core_test/src/core_test.cpp @@ -58,19 +58,19 @@ TEST(core_c_core, InitializeFinalize) EXPECT_EQ(1, eCAL_Initialize(0, nullptr, "initialize_test", 0)); // finalize eCAL API we expect return value 0 even it will not be really finalized because it's 2 times initialzed and 1 time finalized - EXPECT_EQ(0, eCAL_Finalize(0)); + EXPECT_EQ(0, eCAL_Finalize()); // Is eCAL API initialized ? yes it' still initialized EXPECT_EQ(1, eCAL_IsInitialized(0)); // finalize eCAL API we expect return value 0 because now it will be finalized - EXPECT_EQ(0, eCAL_Finalize(0)); + EXPECT_EQ(0, eCAL_Finalize()); // Is eCAL API initialized ? no EXPECT_EQ(0, eCAL_IsInitialized(0)); // finalize eCAL API we expect return value 1 because it was finalized before - EXPECT_EQ(1, eCAL_Finalize(0)); + EXPECT_EQ(1, eCAL_Finalize()); } TEST(core_c_core, MultipleInitializeFinalize) @@ -82,7 +82,7 @@ TEST(core_c_core, MultipleInitializeFinalize) EXPECT_EQ(0, eCAL_Initialize(0, nullptr, "multiple_initialize_finalize_test", 0)); // finalize eCAL API - EXPECT_EQ(0, eCAL_Finalize(0)); + EXPECT_EQ(0, eCAL_Finalize()); } } @@ -148,7 +148,7 @@ TEST(core_c_core, SetGetUnitName) EXPECT_STREQ("unit name", unit_name); // finalize eCAL API we expect return value 0 because it will be finalized - EXPECT_EQ(0, eCAL_Finalize(0)); + EXPECT_EQ(0, eCAL_Finalize()); } TEST(core_c_core, eCAL_Ok) @@ -163,7 +163,7 @@ TEST(core_c_core, eCAL_Ok) EXPECT_EQ(1, eCAL_Ok()); // finalize eCAL API we expect return value 0 because it will be finalized - EXPECT_EQ(0, eCAL_Finalize(0)); + EXPECT_EQ(0, eCAL_Finalize()); // check finalized eCAL, should not be okay EXPECT_EQ(0, eCAL_Ok()); diff --git a/ecal/tests/cpp/pubsub_proto_test/CMakeLists.txt b/ecal/tests/cpp/pubsub_proto_test/CMakeLists.txt index cc0c9af257..d012019194 100644 --- a/ecal/tests/cpp/pubsub_proto_test/CMakeLists.txt +++ b/ecal/tests/cpp/pubsub_proto_test/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ target_link_libraries(${PROJECT_NAME} ) -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) ecal_install_gtest(${PROJECT_NAME}) diff --git a/ecal/tests/cpp/pubsub_proto_test/src/proto_dyn_subscriber_test.cpp b/ecal/tests/cpp/pubsub_proto_test/src/proto_dyn_subscriber_test.cpp index 3f17615b36..e44071b29f 100644 --- a/ecal/tests/cpp/pubsub_proto_test/src/proto_dyn_subscriber_test.cpp +++ b/ecal/tests/cpp/pubsub_proto_test/src/proto_dyn_subscriber_test.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,7 +58,7 @@ class ProtoDynSubscriberTest : public ::testing::Test { pub.Send(p); } - void OnPerson(const char*, const google::protobuf::Message&, long long) + void OnPerson(const char* /*topic_name*/, const std::shared_ptr& /*message*/, long long /*time*/) { received_callbacks++; } @@ -122,12 +122,9 @@ TEST_F(core_cpp_pubsub_proto_dyn, ProtoDynSubscriberTest_SendReceive) SendPerson(person_pub); std::this_thread::sleep_for(std::chrono::milliseconds(100)); - google::protobuf::Message* message = person_dyn_rec.getMessagePointer(); - ASSERT_NE(message, nullptr) << "pointer returned by dynamic subscriber may not be null"; - - bool received = person_dyn_rec.Receive(*message, nullptr, 500); + auto received_message = person_dyn_rec.Receive(nullptr, 500); // assert that the OnPerson callback has been called once. - ASSERT_TRUE(received) << "we should have received data that was sent"; - auto id = extract_id(*message); - ASSERT_EQ(id, 1); + ASSERT_TRUE(received_message.has_value()) << "we should have received data that was sent"; + auto id = extract_id(*received_message.value()); + ASSERT_EQ(id, 1) << "Extracted ID needs to be 1"; } diff --git a/ecal/tests/cpp/pubsub_test/CMakeLists.txt b/ecal/tests/cpp/pubsub_test/CMakeLists.txt index 5a9e74effd..02862a6b94 100644 --- a/ecal/tests/cpp/pubsub_test/CMakeLists.txt +++ b/ecal/tests/cpp/pubsub_test/CMakeLists.txt @@ -21,11 +21,25 @@ project(test_pubsub) find_package(Threads REQUIRED) find_package(GTest REQUIRED) +if(ECAL_CORE_TRANSPORT_SHM) + set(pubsub_test_src_shm + src/pubsub_acknowledge.cpp + src/pubsub_multibuffer.cpp + src/pubsub_test_shm.cpp + ) +endif() + +if(ECAL_CORE_TRANSPORT_UDP) + set(pubsub_test_src_udp + src/pubsub_test_udp.cpp + ) +endif() + set(pubsub_test_src - src/pubsub_acknowledge.cpp - src/pubsub_multibuffer.cpp src/pubsub_receive_test.cpp src/pubsub_test.cpp + ${pubsub_test_src_shm} + ${pubsub_test_src_udp} ) ecal_add_gtest(${PROJECT_NAME} ${pubsub_test_src}) diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_acknowledge.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_acknowledge.cpp index fe94f64196..903854a18c 100644 --- a/ecal/tests/cpp/pubsub_test/src/pubsub_acknowledge.cpp +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_acknowledge.cpp @@ -17,8 +17,12 @@ * ========================= eCAL LICENSE ================================= */ +#include #include +#include + #include + #include namespace @@ -40,9 +44,8 @@ namespace } } -#if 0 // reactivate this if ShmSetAcknowledgeTimeout API is implemented again // This test asserts that a timeouted acknowledge does not break subsequent calls -TEST(PubSub, TimeoutAcknowledgment) +TEST(core_cpp_pubsub, TimeoutAcknowledgment) { // initialize eCAL API EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "TimeoutAcknowledgment", eCAL::Init::All)); @@ -50,8 +53,12 @@ TEST(PubSub, TimeoutAcknowledgment) // enable loop back communication in the same thread eCAL::Util::EnableLoopback(true); - eCAL::string::CPublisher pub("topic"); - //pub.ShmSetAcknowledgeTimeout(500); // TODO: NEW PARAMETER API + // create publisher config + eCAL::Publisher::Configuration pub_config; + pub_config.shm.acknowledge_timeout_ms = 500; + + // create publisher + eCAL::string::CPublisher pub("topic", pub_config); auto sub1 = std::make_shared< eCAL::string::CSubscriber>("topic"); auto sleeper_variable_time = [](const char* /*topic_name_*/, const std::string& msg_, long long /*time_*/, long long /*clock_*/, long long /*id_*/) { @@ -73,7 +80,7 @@ TEST(PubSub, TimeoutAcknowledgment) EXPECT_TRUE(send); } , std::chrono::milliseconds(99) - , std::chrono::milliseconds(120) + , std::chrono::milliseconds(140) ); } @@ -95,7 +102,7 @@ TEST(PubSub, TimeoutAcknowledgment) EXPECT_TRUE(send); } , std::chrono::milliseconds(0) - , std::chrono::milliseconds(120) + , std::chrono::milliseconds(140) ); std::this_thread::sleep_until(now + std::chrono::milliseconds(200)); } @@ -104,4 +111,3 @@ TEST(PubSub, TimeoutAcknowledgment) // without destroying any pub / sub EXPECT_EQ(0, eCAL::Finalize()); } -#endif diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_multibuffer.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_multibuffer.cpp index f140303054..31acdb610d 100644 --- a/ecal/tests/cpp/pubsub_test/src/pubsub_multibuffer.cpp +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_multibuffer.cpp @@ -26,9 +26,11 @@ #include -#define CMN_REGISTRATION_REFRESH 1000 -#define DATA_FLOW_TIME 50 -#define PAYLOAD_SIZE 1024 +enum { + CMN_REGISTRATION_REFRESH_MS = 1000, + DATA_FLOW_TIME_MS = 50, + PAYLOAD_SIZE_BYTE = 1024 +}; // a binary payload object for testing // full (WriteFull) and partial (WriteModified) writing @@ -65,204 +67,172 @@ class CBinaryPayload : public eCAL::CPayloadWriter int clock = 0; }; -#if 0 // reactivate this if SetLayerMode, ShmEnableZeroCopy, ShmSetBufferCount API is implemented again -TEST(PubSub, MultibufferPubSub) -{ +std::vector multibuffer_pub_sub_test(int buffer_count, bool zero_copy, int publications, int bytes_to_read) +{ // create payload - CBinaryPayload binary_payload(PAYLOAD_SIZE); - - // initialize eCAL API - eCAL::Initialize(0, nullptr, "pubsub_test"); - - // publish / subscribe match in the same process - eCAL::Util::EnableLoopback(true); + CBinaryPayload binary_payload(PAYLOAD_SIZE_BYTE); // create subscriber for topic "A" eCAL::CSubscriber sub("A"); + // create publisher config + eCAL::Publisher::Configuration pub_config; + // set transport layer + pub_config.shm.enable = true; + pub_config.udp.enable = false; + pub_config.tcp.enable = false; + // set zero copy mode + pub_config.shm.zero_copy_mode = zero_copy; + // set number of memory buffer + pub_config.shm.memfile_buffer_count = buffer_count; + // create publisher for topic "A" - eCAL::CPublisher pub("A"); - //pub.SetLayerMode(eCAL::TLayer::tlayer_all, eCAL::TLayer::smode_off); // TODO: NEW PARAMETER API - //pub.SetLayerMode(eCAL::TLayer::tlayer_shm, eCAL::TLayer::smode_on); // TODO: NEW PARAMETER API + eCAL::CPublisher pub("A", pub_config); std::atomic received_count{ 0 }; std::atomic received_bytes{ 0 }; std::vector received_content; // add callback - auto lambda = [&received_count, &received_bytes, &received_content](const char* /*topic_name_*/, const eCAL::SReceiveCallbackData* data_) { + auto lambda = [&](const char* /*topic_name_*/, const eCAL::SReceiveCallbackData* data_) { received_bytes += data_->size; ++received_count; - for (auto i = 0; i < 10; ++i) + for (auto i = 0; i < bytes_to_read; ++i) { const char rec_char(static_cast(data_->buf)[i]); received_content.push_back(rec_char); std::cout << std::setw(2) << std::setfill('0') << static_cast(rec_char) << " "; } std::cout << std::endl; - }; + }; EXPECT_EQ(true, sub.AddReceiveCallback(lambda)); - const int iterations(11); - int rec_char_sum(0); - - ////////////////////////////////////////////////////////// - // one buffer, no zero copy / PARTIAL WRITING DISABLED - ////////////////////////////////////////////////////////// - // expected output: - // 42 42 42 42 42 42 42 42 42 42 < full initial write - // 42 42 42 42 42 42 42 42 42 42 < full write - // 42 42 42 42 42 42 42 42 42 42 < .. - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - - std::cout << std::endl << "Buffer = 1, Zero Copy Off -> partial writing disabled" << std::endl; - binary_payload.ResetClock(); - received_content.clear(); - //pub.ShmSetBufferCount(1); // TODO: NEW PARAMETER API - //pub.ShmEnableZeroCopy(false); // TODO: NEW PARAMETER API - // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); - // run 11 iterations (11 full writes) - for (int i = 0; i < iterations; ++i) + // run publications + for (int i = 0; i < publications; ++i) { - EXPECT_EQ(PAYLOAD_SIZE, pub.Send(binary_payload)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); + EXPECT_EQ(PAYLOAD_SIZE_BYTE, pub.Send(binary_payload)); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); } - std::cout << std::endl; - // check receive content - // one buffer, no zero copy - // we expect 11 full writes == 11 * 10 * 42 == 4620 - rec_char_sum = std::accumulate(received_content.begin(), received_content.end(), 0); - EXPECT_EQ(11 * 10 * 42, rec_char_sum); + return received_content; +} - ////////////////////////////////////////////////////////// - // two buffer, no zero copy / PARTIAL WRITING DISABLED - ////////////////////////////////////////////////////////// - // expected output: - // 42 42 42 42 42 42 42 42 42 42 < full initial write - // 42 42 42 42 42 42 42 42 42 42 < full write - // 42 42 42 42 42 42 42 42 42 42 < .. - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 +TEST(core_cpp_pubsub, MultibufferPubSub) +{ + // initialize eCAL API + eCAL::Initialize(0, nullptr, "pubsub_test"); - std::cout << std::endl << "Buffer = 2, Zero Copy Off -> partial writing disabled" << std::endl; - binary_payload.ResetClock(); - received_content.clear(); - //pub.ShmSetBufferCount(2); // TODO: NEW PARAMETER API - //pub.ShmEnableZeroCopy(false); // TODO: NEW PARAMETER API + // publish / subscribe match in the same process + eCAL::Util::EnableLoopback(true); - // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + // number of iterations + const int publications(11); + const int bytes_to_read(10); - // run 11 iterations (11 full writes) - for (int i = 0; i < iterations; ++i) { - EXPECT_EQ(PAYLOAD_SIZE, pub.Send(binary_payload)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); + std::cout << std::endl << "Buffer = 1, Zero Copy Off -> partial writing disabled" << std::endl; + ////////////////////////////////////////////////////////// + // one buffer, no zero copy / PARTIAL WRITING DISABLED + ////////////////////////////////////////////////////////// + // expected output: + // 42 42 42 42 42 42 42 42 42 42 < full initial write + // 42 42 42 42 42 42 42 42 42 42 < full write + // 42 42 42 42 42 42 42 42 42 42 < .. + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + auto received_content = multibuffer_pub_sub_test(1, false, publications, bytes_to_read); + // check receive content + // one buffer, no zero copy + // we expect 11 full writes == 11 * 10 * 42 == 4620 + auto rec_char_sum = std::accumulate(received_content.begin(), received_content.end(), 0); + EXPECT_EQ(publications * bytes_to_read * 42, rec_char_sum); } - std::cout << std::endl; - - // check receive content - // two buffer, no zero copy - // we expect 11 full writes == 11 * 10 * 42 == 4620 - rec_char_sum = std::accumulate(received_content.begin(), received_content.end(), 0); - EXPECT_EQ(11 * 10 * 42, rec_char_sum); - - ////////////////////////////////////////////////////////// - // two buffer, zero copy on / PARTIAL WRITING DISABLED - ////////////////////////////////////////////////////////// - // expected output: - // 42 42 42 42 42 42 42 42 42 42 < full initial write - // 42 42 42 42 42 42 42 42 42 42 < full write - // 42 42 42 42 42 42 42 42 42 42 < .. - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - // 42 42 42 42 42 42 42 42 42 42 - - std::cout << std::endl << "Buffer = 2, Zero Copy On -> partial writing disabled" << std::endl; - binary_payload.ResetClock(); - received_content.clear(); - //pub.ShmSetBufferCount(2); // TODO: NEW PARAMETER API - //pub.ShmEnableZeroCopy(true); // TODO: NEW PARAMETER API - - // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); - // run 11 iterations (11 full writes) - for (int i = 0; i < iterations; ++i) { - EXPECT_EQ(PAYLOAD_SIZE, pub.Send(binary_payload)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); + std::cout << std::endl << "Buffer = 2, Zero Copy Off -> partial writing disabled" << std::endl; + ////////////////////////////////////////////////////////// + // two buffer, no zero copy / PARTIAL WRITING DISABLED + ////////////////////////////////////////////////////////// + // expected output: + // 42 42 42 42 42 42 42 42 42 42 < full initial write + // 42 42 42 42 42 42 42 42 42 42 < full write + // 42 42 42 42 42 42 42 42 42 42 < .. + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + auto received_content = multibuffer_pub_sub_test(2, false, publications, bytes_to_read); + // check receive content + // two buffer, no zero copy + // we expect 11 full writes == 11 * 10 * 42 == 4620 + auto rec_char_sum = std::accumulate(received_content.begin(), received_content.end(), 0); + EXPECT_EQ(publications * bytes_to_read * 42, rec_char_sum); } - std::cout << std::endl; - - // check receive content - // two buffer, zero copy on - // we expect 11 full writes == 11 * 10 * 42 == 4620 - rec_char_sum = std::accumulate(received_content.begin(), received_content.end(), 0); - EXPECT_EQ(11 * 10 * 42, rec_char_sum); - - ////////////////////////////////////////////////////////// - // one buffer, zero copy on / PARTIAL WRITING ENABLED - ////////////////////////////////////////////////////////// - // expected output: - // 42 42 42 42 42 42 42 42 42 42 < full initial write - // 00 42 42 42 42 42 42 42 42 42 < 1. partial write - // 00 01 42 42 42 42 42 42 42 42 < 2. partial write - // 00 01 02 42 42 42 42 42 42 42 < .. - // 00 01 02 03 42 42 42 42 42 42 - // 00 01 02 03 04 42 42 42 42 42 - // 00 01 02 03 04 05 42 42 42 42 - // 00 01 02 03 04 05 06 42 42 42 - // 00 01 02 03 04 05 06 07 42 42 - // 00 01 02 03 04 05 06 07 08 42 - // 00 01 02 03 04 05 06 07 08 09 - - std::cout << std::endl << "Buffer = 1, Zero Copy On -> partial writing enabled" << std::endl; - binary_payload.ResetClock(); - received_content.clear(); - //pub.ShmSetBufferCount(1); // TODO: NEW PARAMETER API - //pub.ShmEnableZeroCopy(true); // TODO: NEW PARAMETER API - // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + { + std::cout << std::endl << "Buffer = 2, Zero Copy On -> partial writing disabled" << std::endl; + ////////////////////////////////////////////////////////// + // two buffer, zero copy on / PARTIAL WRITING DISABLED + ////////////////////////////////////////////////////////// + // expected output: + // 42 42 42 42 42 42 42 42 42 42 < full initial write + // 42 42 42 42 42 42 42 42 42 42 < full write + // 42 42 42 42 42 42 42 42 42 42 < .. + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + // 42 42 42 42 42 42 42 42 42 42 + auto received_content = multibuffer_pub_sub_test(2, true, publications, bytes_to_read); + // check receive content + // two buffer, zero copy on + // we expect 11 full writes == 11 * 10 * 42 == 4620 + auto rec_char_sum = std::accumulate(received_content.begin(), received_content.end(), 0); + EXPECT_EQ(11 * 10 * 42, rec_char_sum); + } - // run 11 iterations (1 full write, 10 updates) - for (int i = 0; i < iterations; ++i) { - EXPECT_EQ(PAYLOAD_SIZE, pub.Send(binary_payload)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); + std::cout << std::endl << "Buffer = 1, Zero Copy On -> partial writing enabled" << std::endl; + ////////////////////////////////////////////////////////// + // one buffer, zero copy on / PARTIAL WRITING ENABLED + ////////////////////////////////////////////////////////// + // expected output: + // 42 42 42 42 42 42 42 42 42 42 < full initial write + // 00 42 42 42 42 42 42 42 42 42 < 1. partial write + // 00 01 42 42 42 42 42 42 42 42 < 2. partial write + // 00 01 02 42 42 42 42 42 42 42 < .. + // 00 01 02 03 42 42 42 42 42 42 + // 00 01 02 03 04 42 42 42 42 42 + // 00 01 02 03 04 05 42 42 42 42 + // 00 01 02 03 04 05 06 42 42 42 + // 00 01 02 03 04 05 06 07 42 42 + // 00 01 02 03 04 05 06 07 08 42 + // 00 01 02 03 04 05 06 07 08 09 + auto received_content = multibuffer_pub_sub_test(1, true, publications, bytes_to_read); + // check receive content + // one buffer, zero copy on + // we expect 1 full write + 10 updates == 2475 + auto rec_char_sum = std::accumulate(received_content.begin(), received_content.end(), 0); + EXPECT_EQ(2475, rec_char_sum); } std::cout << std::endl; - // check receive content - // one buffer, zero copy on - // we expect 1 full write + 10 updates == 2475 - rec_char_sum = std::accumulate(received_content.begin(), received_content.end(), 0); - EXPECT_EQ(2475, rec_char_sum); - // finalize eCAL API eCAL::Finalize(); } -#endif diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_receive_test.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_receive_test.cpp index cc6bc53170..4e18f043e6 100644 --- a/ecal/tests/cpp/pubsub_test/src/pubsub_receive_test.cpp +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_receive_test.cpp @@ -28,7 +28,9 @@ #include -#define CMN_REGISTRATION_REFRESH 1000 +enum { + CMN_REGISTRATION_REFRESH_MS = 1000 +}; using namespace std::chrono_literals; @@ -76,7 +78,7 @@ TEST(core_cpp_pubsub, TimingSubscriberReceive) eCAL::string::CSubscriber sub("CLOCK"); // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // Send nothing and make sure the functions return as specified std::string received; @@ -173,7 +175,7 @@ TEST(core_cpp_pubsub, SporadicEmptyReceives) eCAL::string::CSubscriber sub("CLOCK"); // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // start publishing thread std::atomic pub_stop(false); diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_test.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_test.cpp index 36ddf4476b..39dba78be1 100644 --- a/ecal/tests/cpp/pubsub_test/src/pubsub_test.cpp +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_test.cpp @@ -26,28 +26,33 @@ #include -#define CMN_REGISTRATION_REFRESH 1000 -#define DATA_FLOW_TIME 50 -#define PAYLOAD_SIZE 1024 - -// subscriber callback function -std::atomic g_callback_received_bytes; -std::atomic g_callback_received_count; -void OnReceive(const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackData* data_) -{ - g_callback_received_bytes += data_->size; - g_callback_received_count++; -} +enum { + CMN_REGISTRATION_REFRESH_MS = 1000, + DATA_FLOW_TIME_MS = 50, + PAYLOAD_SIZE_BYTE = 1024 +}; -static std::string CreatePayLoad(size_t payload_size_) +namespace { - std::string s = "Hello World "; - while(s.size() < payload_size_) + // subscriber callback function + std::atomic g_callback_received_bytes; + std::atomic g_callback_received_count; + void OnReceive(const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackData* data_) + { + g_callback_received_bytes += data_->size; + g_callback_received_count++; + } + + std::string CreatePayLoad(size_t payload_size_) { - s += s; + std::string s = "Hello World "; + while(s.size() < payload_size_) + { + s += s; + } + s.resize(payload_size_); + return(s); } - s.resize(payload_size_); - return(s); } TEST(core_cpp_pubsub, LeakedPubSub) @@ -66,7 +71,7 @@ TEST(core_cpp_pubsub, LeakedPubSub) eCAL::CPublisher pub("foo"); // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // start publishing thread std::atomic pub_stop(false); @@ -198,7 +203,7 @@ TEST(core_cpp_pubsub, CreateDestroy) TEST(core_cpp_pubsub, SimpleMessage1) { // default send / receive strings - std::string send_s = CreatePayLoad(PAYLOAD_SIZE); + const std::string send_s = CreatePayLoad(PAYLOAD_SIZE_BYTE); std::string recv_s; // initialize eCAL API @@ -214,20 +219,20 @@ TEST(core_cpp_pubsub, SimpleMessage1) eCAL::CSubscriber sub("foo"); // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // send content EXPECT_EQ(send_s.size(), pub.Send(send_s)); - // receive content with DATA_FLOW_TIME ms timeout + // receive content with DATA_FLOW_TIME_MS timeout recv_s.clear(); - EXPECT_EQ(true, sub.ReceiveBuffer(recv_s, nullptr, DATA_FLOW_TIME)); + EXPECT_EQ(true, sub.ReceiveBuffer(recv_s, nullptr, DATA_FLOW_TIME_MS)); EXPECT_EQ(send_s.size(), recv_s.size()); - // receive content with DATA_FLOW_TIME ms timeout + // receive content with DATA_FLOW_TIME_MS timeout // should return because no new publishing recv_s.clear(); - EXPECT_EQ(false, sub.ReceiveBuffer(recv_s, nullptr, DATA_FLOW_TIME)); + EXPECT_EQ(false, sub.ReceiveBuffer(recv_s, nullptr, DATA_FLOW_TIME_MS)); EXPECT_EQ(0, recv_s.size()); // destroy publisher @@ -243,7 +248,7 @@ TEST(core_cpp_pubsub, SimpleMessage1) TEST(core_cpp_pubsub, SimpleMessage2) { // default send / receive strings - std::string send_s = CreatePayLoad(PAYLOAD_SIZE); + const std::string send_s = CreatePayLoad(PAYLOAD_SIZE_BYTE); std::string recv_s; // initialize eCAL API @@ -259,14 +264,14 @@ TEST(core_cpp_pubsub, SimpleMessage2) eCAL::CPublisher pub("foo"); // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // send content EXPECT_EQ(send_s.size(), pub.Send(send_s)); - // receive content with DATA_FLOW_TIME ms timeout + // receive content with DATA_FLOW_TIME_MS timeout recv_s.clear(); - EXPECT_EQ(true, sub.ReceiveBuffer(recv_s, nullptr, DATA_FLOW_TIME)); + EXPECT_EQ(true, sub.ReceiveBuffer(recv_s, nullptr, DATA_FLOW_TIME_MS)); EXPECT_EQ(send_s.size(), recv_s.size()); // destroy publisher @@ -282,7 +287,7 @@ TEST(core_cpp_pubsub, SimpleMessage2) TEST(core_cpp_pubsub, SimpleMessageCB) { // default send string - std::string send_s = CreatePayLoad(PAYLOAD_SIZE); + const std::string send_s = CreatePayLoad(PAYLOAD_SIZE_BYTE); // initialize eCAL API eCAL::Initialize(0, nullptr, "pubsub_test"); @@ -300,14 +305,14 @@ TEST(core_cpp_pubsub, SimpleMessageCB) EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // send content g_callback_received_bytes = 0; EXPECT_EQ(send_s.size(), pub.Send(send_s)); // let the data flow - eCAL::Process::SleepMS(DATA_FLOW_TIME); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); // check callback receive EXPECT_EQ(send_s.size(), g_callback_received_bytes); @@ -320,7 +325,7 @@ TEST(core_cpp_pubsub, SimpleMessageCB) EXPECT_EQ(send_s.size(), pub.Send(send_s)); // let the data flow - eCAL::Process::SleepMS(DATA_FLOW_TIME); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); // check callback receive EXPECT_EQ(0, g_callback_received_bytes); @@ -333,7 +338,7 @@ TEST(core_cpp_pubsub, SimpleMessageCB) EXPECT_EQ(send_s.size(), pub.Send(send_s)); // let the data flow - eCAL::Process::SleepMS(DATA_FLOW_TIME); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); // check callback receive EXPECT_EQ(send_s.size(), g_callback_received_bytes); @@ -346,7 +351,7 @@ TEST(core_cpp_pubsub, SimpleMessageCB) EXPECT_EQ(send_s.size(), pub.Send(send_s)); // let the data flow - eCAL::Process::SleepMS(DATA_FLOW_TIME); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); // check callback receive EXPECT_EQ(0, g_callback_received_bytes); @@ -361,7 +366,7 @@ TEST(core_cpp_pubsub, SimpleMessageCB) TEST(core_cpp_pubsub, DynamicSizeCB) { // default send string - std::string send_s = CreatePayLoad(PAYLOAD_SIZE); + std::string send_s = CreatePayLoad(PAYLOAD_SIZE_BYTE); // initialize eCAL API eCAL::Initialize(0, nullptr, "pubsub_test"); @@ -379,27 +384,27 @@ TEST(core_cpp_pubsub, DynamicSizeCB) EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // send content g_callback_received_bytes = 0; EXPECT_EQ(send_s.size(), pub.Send(send_s)); // let the data flow - eCAL::Process::SleepMS(DATA_FLOW_TIME); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); // check callback receive EXPECT_EQ(send_s.size(), g_callback_received_bytes); // increase payload size - send_s = CreatePayLoad(PAYLOAD_SIZE*10); + send_s = CreatePayLoad(PAYLOAD_SIZE_BYTE*10); // send content g_callback_received_bytes = 0; EXPECT_EQ(send_s.size(), pub.Send(send_s)); // let the data flow - eCAL::Process::SleepMS(DATA_FLOW_TIME); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); // check callback receive EXPECT_EQ(send_s.size(), g_callback_received_bytes); @@ -417,7 +422,7 @@ TEST(core_cpp_pubsub, DynamicSizeCB) TEST(core_cpp_pubsub, DynamicCreate) { // default send string - std::string send_s = CreatePayLoad(PAYLOAD_SIZE); + const std::string send_s = CreatePayLoad(PAYLOAD_SIZE_BYTE); // initialize eCAL API eCAL::Initialize(0, nullptr, "pubsub_test"); @@ -437,14 +442,14 @@ TEST(core_cpp_pubsub, DynamicCreate) EXPECT_EQ(true, sub->AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // send content g_callback_received_bytes = 0; EXPECT_EQ(send_s.size(), pub->Send(send_s)); // let the data flow - eCAL::Process::SleepMS(DATA_FLOW_TIME); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); // check callback receive EXPECT_EQ(send_s.size(), g_callback_received_bytes); @@ -460,14 +465,14 @@ TEST(core_cpp_pubsub, DynamicCreate) EXPECT_EQ(true, sub->AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // send content g_callback_received_bytes = 0; EXPECT_EQ(send_s.size(), pub->Send(send_s)); // let the data flow - eCAL::Process::SleepMS(DATA_FLOW_TIME); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); // check callback receive EXPECT_EQ(send_s.size(), g_callback_received_bytes); @@ -482,14 +487,14 @@ TEST(core_cpp_pubsub, DynamicCreate) EXPECT_EQ(true, sub->AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // send content g_callback_received_bytes = 0; EXPECT_EQ(send_s.size(), pub->Send(send_s)); // let the data flow - eCAL::Process::SleepMS(DATA_FLOW_TIME); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); // check callback receive EXPECT_EQ(send_s.size(), g_callback_received_bytes); @@ -506,101 +511,6 @@ TEST(core_cpp_pubsub, DynamicCreate) eCAL::Finalize(); } -TEST(core_cpp_pubsub, ZeroPayloadMessageUDP) -{ - // default send string - std::string send_s; - - // initialize eCAL API - eCAL::Initialize(0, nullptr, "pubsub_test"); - - // publish / subscribe match in the same process - eCAL::Util::EnableLoopback(true); - - // create subscriber for topic "foo" - eCAL::CSubscriber sub("foo"); - - // create publisher for topic "foo" - eCAL::CPublisher pub("foo"); - - // add callback - EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); - - // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); - - g_callback_received_bytes = 0; - g_callback_received_count = 0; - - EXPECT_EQ(send_s.size(), pub.Send(send_s)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); - - EXPECT_EQ(send_s.size(), pub.Send(nullptr, 0)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); - - // check callback receive - EXPECT_EQ(send_s.size(), g_callback_received_bytes); - EXPECT_EQ(2, g_callback_received_count); - - // destroy subscriber - sub.Destroy(); - - // destroy publisher - pub.Destroy(); - - // finalize eCAL API - eCAL::Finalize(); -} - -TEST(core_cpp_pubsub, MultipleSendsUDP) -{ - // default send string - std::vector send_vector{ "this", "is", "a", "", "testtest" }; - std::string last_received_msg; - long long last_received_timestamp; - - // initialize eCAL API - eCAL::Initialize(0, nullptr, "pubsub_test"); - - // publish / subscribe match in the same process - eCAL::Util::EnableLoopback(true); - - // create subscriber for topic "foo" - eCAL::string::CSubscriber sub("foo"); - - // create publisher for topic "foo" - eCAL::string::CPublisher pub("foo"); - - // add callback - auto save_data = [&last_received_msg, &last_received_timestamp](const char* /*topic_name_*/, const std::string& msg_, long long time_, long long /*clock_*/, long long /*id_*/) - { - last_received_msg = msg_; - last_received_timestamp = time_; - }; - EXPECT_TRUE(sub.AddReceiveCallback(save_data)); - - // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); - long long timestamp = 1; - for (const auto& elem : send_vector) - { - pub.Send(elem, timestamp); - eCAL::Process::SleepMS(DATA_FLOW_TIME); - EXPECT_EQ(last_received_msg, elem); - EXPECT_EQ(last_received_timestamp, timestamp); - ++timestamp; - } - - // destroy subscriber - sub.Destroy(); - - // destroy publisher - pub.Destroy(); - - // finalize eCAL API - eCAL::Finalize(); -} - TEST(core_cpp_pubsub, DestroyInCallback) { /* Test setup : diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_test_shm.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_test_shm.cpp new file mode 100644 index 0000000000..fe40695b37 --- /dev/null +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_test_shm.cpp @@ -0,0 +1,153 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include +#include +#include + +#include +#include + +#include + +enum { + CMN_REGISTRATION_REFRESH_MS = 1000, + DATA_FLOW_TIME_MS = 50, +}; + +namespace +{ + // subscriber callback function + std::atomic g_callback_received_bytes; + std::atomic g_callback_received_count; + void OnReceive(const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackData* data_) + { + g_callback_received_bytes += data_->size; + g_callback_received_count++; + } +} + +TEST(core_cpp_pubsub, ZeroPayloadMessageSHM) +{ + // default send string + const std::string send_s; + + // initialize eCAL API + eCAL::Initialize(0, nullptr, "pubsub_test"); + + // publish / subscribe match in the same process + eCAL::Util::EnableLoopback(true); + + // create subscriber for topic "A" + eCAL::CSubscriber sub("A"); + + // create publisher config + eCAL::Publisher::Configuration pub_config; + // set transport layer + pub_config.shm.enable = true; + pub_config.udp.enable = false; + pub_config.tcp.enable = false; + + // create publisher for topic "A" + eCAL::CPublisher pub("A", pub_config); + + // add callback + EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); + + // let's match them + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + + g_callback_received_bytes = 0; + g_callback_received_count = 0; + + EXPECT_EQ(send_s.size(), pub.Send(send_s)); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); + + EXPECT_EQ(send_s.size(), pub.Send(nullptr, 0)); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); + + // check callback receive + EXPECT_EQ(send_s.size(), g_callback_received_bytes); + EXPECT_EQ(2, g_callback_received_count); + + // destroy subscriber + sub.Destroy(); + + // destroy publisher + pub.Destroy(); + + // finalize eCAL API + eCAL::Finalize(); +} + +TEST(core_cpp_pubsub, MultipleSendsSHM) +{ + // default send string + const std::vector send_vector{ "this", "is", "a", "", "testtest" }; + std::string last_received_msg; + long long last_received_timestamp(0); + + // initialize eCAL API + eCAL::Initialize(0, nullptr, "pubsub_test"); + + // publish / subscribe match in the same process + eCAL::Util::EnableLoopback(true); + + // create subscriber for topic "A" + eCAL::string::CSubscriber sub("A"); + + // create publisher config + eCAL::Publisher::Configuration pub_config; + // set transport layer + pub_config.shm.enable = true; + pub_config.udp.enable = false; + pub_config.tcp.enable = false; + + // create publisher for topic "A" + eCAL::string::CPublisher pub("A", pub_config); + + // add callback + auto save_data = [&last_received_msg, &last_received_timestamp](const char* /*topic_name_*/, const std::string& msg_, long long time_, long long /*clock_*/, long long /*id_*/) + { + last_received_msg = msg_; + last_received_timestamp = time_; + }; + EXPECT_TRUE(sub.AddReceiveCallback(save_data)); + + // let's match them + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + long long timestamp = 1; + for (const auto& elem : send_vector) + { + pub.Send(elem, timestamp); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); + EXPECT_EQ(last_received_msg, elem); + EXPECT_EQ(last_received_timestamp, timestamp); + ++timestamp; + } + + // destroy subscriber + sub.Destroy(); + + // destroy publisher + pub.Destroy(); + + // finalize eCAL API + eCAL::Finalize(); +} diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_test_udp.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_test_udp.cpp new file mode 100644 index 0000000000..0776132e37 --- /dev/null +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_test_udp.cpp @@ -0,0 +1,153 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * 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. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include +#include +#include + +#include +#include + +#include + +enum { + CMN_REGISTRATION_REFRESH_MS = 1000, + DATA_FLOW_TIME_MS = 50, +}; + +namespace +{ + // subscriber callback function + std::atomic g_callback_received_bytes; + std::atomic g_callback_received_count; + void OnReceive(const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackData* data_) + { + g_callback_received_bytes += data_->size; + g_callback_received_count++; + } +} + +TEST(core_cpp_pubsub, ZeroPayloadMessageUDP) +{ + // default send string + const std::string send_s; + + // initialize eCAL API + eCAL::Initialize(0, nullptr, "pubsub_test"); + + // publish / subscribe match in the same process + eCAL::Util::EnableLoopback(true); + + // create subscriber for topic "A" + eCAL::CSubscriber sub("A"); + + // create publisher config + eCAL::Publisher::Configuration pub_config; + // set transport layer + pub_config.shm.enable = false; + pub_config.udp.enable = true; + pub_config.tcp.enable = false; + + // create publisher for topic "A" + eCAL::CPublisher pub("A", pub_config); + + // add callback + EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); + + // let's match them + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + + g_callback_received_bytes = 0; + g_callback_received_count = 0; + + EXPECT_EQ(send_s.size(), pub.Send(send_s)); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); + + EXPECT_EQ(send_s.size(), pub.Send(nullptr, 0)); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); + + // check callback receive + EXPECT_EQ(send_s.size(), g_callback_received_bytes); + EXPECT_EQ(2, g_callback_received_count); + + // destroy subscriber + sub.Destroy(); + + // destroy publisher + pub.Destroy(); + + // finalize eCAL API + eCAL::Finalize(); +} + +TEST(core_cpp_pubsub, MultipleSendsUDP) +{ + // default send string + const std::vector send_vector{ "this", "is", "a", "", "testtest" }; + std::string last_received_msg; + long long last_received_timestamp(0); + + // initialize eCAL API + eCAL::Initialize(0, nullptr, "pubsub_test"); + + // publish / subscribe match in the same process + eCAL::Util::EnableLoopback(true); + + // create subscriber for topic "A" + eCAL::string::CSubscriber sub("A"); + + // create publisher config + eCAL::Publisher::Configuration pub_config; + // set transport layer + pub_config.shm.enable = false; + pub_config.udp.enable = true; + pub_config.tcp.enable = false; + + // create publisher for topic "A" + eCAL::string::CPublisher pub("A", pub_config); + + // add callback + auto save_data = [&last_received_msg, &last_received_timestamp](const char* /*topic_name_*/, const std::string& msg_, long long time_, long long /*clock_*/, long long /*id_*/) + { + last_received_msg = msg_; + last_received_timestamp = time_; + }; + EXPECT_TRUE(sub.AddReceiveCallback(save_data)); + + // let's match them + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + long long timestamp = 1; + for (const auto& elem : send_vector) + { + pub.Send(elem, timestamp); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); + EXPECT_EQ(last_received_msg, elem); + EXPECT_EQ(last_received_timestamp, timestamp); + ++timestamp; + } + + // destroy subscriber + sub.Destroy(); + + // destroy publisher + pub.Destroy(); + + // finalize eCAL API + eCAL::Finalize(); +} diff --git a/samples/cpp/pubsub/capnp/addressbook_rec/CMakeLists.txt b/samples/cpp/pubsub/capnp/addressbook_rec/CMakeLists.txt index 28cf9e8dfd..a40923fdee 100644 --- a/samples/cpp/pubsub/capnp/addressbook_rec/CMakeLists.txt +++ b/samples/cpp/pubsub/capnp/addressbook_rec/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -36,6 +36,8 @@ target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/s target_link_libraries(${PROJECT_NAME} PRIVATE CapnProto::capnp eCAL::core) target_link_options(${PROJECT_NAME} PRIVATE $<$:/ignore:4099>) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) + ecal_install_sample(${PROJECT_NAME}) set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/capnproto) diff --git a/samples/cpp/pubsub/capnp/addressbook_rec/src/addressbook_rec.cpp b/samples/cpp/pubsub/capnp/addressbook_rec/src/addressbook_rec.cpp index 9d7f43fe91..d35e448099 100644 --- a/samples/cpp/pubsub/capnp/addressbook_rec/src/addressbook_rec.cpp +++ b/samples/cpp/pubsub/capnp/addressbook_rec/src/addressbook_rec.cpp @@ -88,9 +88,9 @@ int main(int argc, char **argv) while (eCAL::Ok()) { // receive content - if (sub.Receive(nullptr, 0)) + AddressBook::Reader reader; + if (sub.Receive(reader)) { - AddressBook::Reader reader{ sub.getReader() }; printAddressBook(reader); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); diff --git a/samples/cpp/pubsub/capnp/addressbook_rec_cb/CMakeLists.txt b/samples/cpp/pubsub/capnp/addressbook_rec_cb/CMakeLists.txt index 7719484ef9..002123ae36 100644 --- a/samples/cpp/pubsub/capnp/addressbook_rec_cb/CMakeLists.txt +++ b/samples/cpp/pubsub/capnp/addressbook_rec_cb/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -36,6 +36,8 @@ target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/s target_link_libraries(${PROJECT_NAME} PRIVATE CapnProto::capnp eCAL::core) target_link_options(${PROJECT_NAME} PRIVATE $<$:/ignore:4099>) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) + ecal_install_sample(${PROJECT_NAME}) set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/capnproto) \ No newline at end of file diff --git a/samples/cpp/pubsub/capnp/addressbook_rec_dynamic/CMakeLists.txt b/samples/cpp/pubsub/capnp/addressbook_rec_dynamic/CMakeLists.txt index cdb1817eeb..1992303cc8 100644 --- a/samples/cpp/pubsub/capnp/addressbook_rec_dynamic/CMakeLists.txt +++ b/samples/cpp/pubsub/capnp/addressbook_rec_dynamic/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,6 +32,8 @@ target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/c target_link_libraries(${PROJECT_NAME} PRIVATE CapnProto::capnp eCAL::core) target_link_options(${PROJECT_NAME} PRIVATE $<$:/ignore:4099>) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) + ecal_install_sample(${PROJECT_NAME}) set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/capnproto) diff --git a/samples/cpp/pubsub/capnp/addressbook_rec_dynamic/src/addressbook_rec_dynamic.cpp b/samples/cpp/pubsub/capnp/addressbook_rec_dynamic/src/addressbook_rec_dynamic.cpp index c25c8f3d38..d5562fd031 100644 --- a/samples/cpp/pubsub/capnp/addressbook_rec_dynamic/src/addressbook_rec_dynamic.cpp +++ b/samples/cpp/pubsub/capnp/addressbook_rec_dynamic/src/addressbook_rec_dynamic.cpp @@ -105,7 +105,7 @@ void dynamicPrintValue(const capnp::DynamicValue::Reader& value) } } -int main(int argc, char **argv) +int main(int argc, char** argv) { // initialize eCAL API eCAL::Initialize(argc, argv, "addressbook subscriber"); @@ -115,8 +115,10 @@ int main(int argc, char **argv) // create a subscriber (topic name "addressbook") eCAL::capnproto::CDynamicSubscriber sub("addressbook"); - auto callback = std::bind(dynamicPrintValue, std::placeholders::_2); - sub.AddReceiveCallback(callback); + auto lambda = [](const char* /*topic_name_*/, const capnp::DynamicValue::Reader& msg_, long long /*time_*/, long long /*clock_*/, long long /*id_*/) -> void { + dynamicPrintValue(msg_); + }; + sub.AddReceiveCallback(lambda); // enter main loop while (eCAL::Ok()) diff --git a/samples/cpp/pubsub/capnp/addressbook_snd/CMakeLists.txt b/samples/cpp/pubsub/capnp/addressbook_snd/CMakeLists.txt index 89a7571036..f49bbdc611 100644 --- a/samples/cpp/pubsub/capnp/addressbook_snd/CMakeLists.txt +++ b/samples/cpp/pubsub/capnp/addressbook_snd/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -36,6 +36,8 @@ target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/s target_link_libraries(${PROJECT_NAME} PRIVATE CapnProto::capnp eCAL::core) target_link_options(${PROJECT_NAME} PRIVATE $<$:/ignore:4099>) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) + ecal_install_sample(${PROJECT_NAME}) set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/capnproto)