diff --git a/CMakeLists.txt b/CMakeLists.txt index d240a156b7..af447a1a42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,6 @@ option(BUILD_ECAL_TESTS "Build the eCAL google tests" option(ECAL_INCLUDE_PY_SAMPLES "Include python language sample projects into CMake" OFF) option(ECAL_INSTALL_SAMPLE_SOURCES "Install the sources of eCAL samples" ON) -option(ECAL_JOIN_MULTICAST_TWICE "Specific Multicast Network Bug Workaround" OFF) option(ECAL_NPCAP_SUPPORT "Enable the eCAL Npcap Receiver (i.e. the Win10 performance fix)" OFF) option(ECAL_USE_CLOCKLOCK_MUTEX "Use native mutex with monotonic clock (requires glibc >= 2.30)" OFF) @@ -467,7 +466,7 @@ if(BUILD_ECAL_TESTS) # ------------------------------------------------------ # test ecal # ------------------------------------------------------ - add_subdirectory(testing/ecal/clientserver_test) #THIS TEST IS NOT ABLE TO RUN ON GH ACTIONS + add_subdirectory(testing/ecal/clientserver_test) add_subdirectory(testing/ecal/core_test) add_subdirectory(testing/ecal/event_test) @@ -568,7 +567,6 @@ message(STATUS "BUILD_CSHARP_BINDING : ${BUILD_CSHARP_ message(STATUS "BUILD_ECAL_TESTS : ${BUILD_ECAL_TESTS}") message(STATUS "ECAL_INCLUDE_PY_SAMPLES : ${ECAL_INCLUDE_PY_SAMPLES}") message(STATUS "ECAL_INSTALL_SAMPLE_SOURCES : ${ECAL_INSTALL_SAMPLE_SOURCES}") -message(STATUS "ECAL_JOIN_MULTICAST_TWICE : ${ECAL_JOIN_MULTICAST_TWICE}") message(STATUS "ECAL_NPCAP_SUPPORT : ${ECAL_NPCAP_SUPPORT}") message(STATUS "ECAL_THIRDPARTY_BUILD_ASIO : ${ECAL_THIRDPARTY_BUILD_ASIO}") message(STATUS "ECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS : ${ECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS}") diff --git a/app/rec/rec_tests/rec_rpc_tests/src/external_ecal_rec.cpp b/app/rec/rec_tests/rec_rpc_tests/src/external_ecal_rec.cpp index c41bd44d39..a10e85d405 100644 --- a/app/rec/rec_tests/rec_rpc_tests/src/external_ecal_rec.cpp +++ b/app/rec/rec_tests/rec_rpc_tests/src/external_ecal_rec.cpp @@ -64,7 +64,17 @@ ExternalEcalRecInstance::ExternalEcalRecInstance(bool gui) return; } - std::this_thread::sleep_for(std::chrono::milliseconds(3000)); + for (int i = 0; i < 20; ++i) + { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + eCAL::pb::rec_server::RecServerConfig config_pb; + auto error = GetConfigViaRpc(config_pb); + if (!error) + { + break; + } + } if (gui) { diff --git a/doc/rst/development/ecal_cmake_options.rst b/doc/rst/development/ecal_cmake_options.rst index f74d84782f..dcaec2bac0 100644 --- a/doc/rst/development/ecal_cmake_options.rst +++ b/doc/rst/development/ecal_cmake_options.rst @@ -35,7 +35,6 @@ CMake option Default Description ``BUILD_ECAL_TESTS`` ``OFF`` Build the eCAL google tests ``ECAL_INCLUDE_PY_SAMPLES`` ``OFF`` Include python language sample projects into CMake ``ECAL_INSTALL_SAMPLE_SOURCES`` ``ON`` Install the sources of eCAL samples -``ECAL_JOIN_MULTICAST_TWICE`` ``OFF`` Specific multicast network bug workaround ``ECAL_NPCAP_SUPPORT`` ``OFF`` Enable the eCAL to use Npcap for udp socket communication (i.e. the Win10 performance fix) ``ECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS`` ``ON`` Build CMakeFunctions with eCAL ``ECAL_THIRDPARTY_BUILD_SPDLOG`` ``ON`` Build Spdlog with eCAL, included as a submodule in the thirdparty folder. You can always use your custom spdlog installation, this is only for convenience. diff --git a/ecal/core/CMakeLists.txt b/ecal/core/CMakeLists.txt index 814d7270b2..9a565a3a40 100644 --- a/ecal/core/CMakeLists.txt +++ b/ecal/core/CMakeLists.txt @@ -27,11 +27,6 @@ if (ECAL_NPCAP_SUPPORT) find_package(udpcap REQUIRED) endif() -if (ECAL_JOIN_MULTICAST_TWICE) - message(STATUS "eCAL ${PROJECT_NAME}: Enabling Specific Multicast Network Bug Workaround") - add_definitions(-DECAL_JOIN_MULTICAST_TWICE) -endif(ECAL_JOIN_MULTICAST_TWICE) - # If we're currently doing a build within a git repository, we will configure the header files. # Else, (e.g. for source packages such as debian source packages) we will use a preconfigured file. # If there is really no information available, it will generate a dummy version file 0.0.0 @@ -137,43 +132,63 @@ endif() ###################################### # io/udp ###################################### +# io/udp/fragmentation +set(ecal_io_udp_fragmentation_src + src/io/udp/fragmentation/msg_type.h + src/io/udp/fragmentation/rcv_fragments.cpp + src/io/udp/fragmentation/rcv_fragments.h + src/io/udp/fragmentation/snd_fragments.cpp + src/io/udp/fragmentation/snd_fragments.h +) + +# io/udp/sendreceive (npcap) if(ECAL_NPCAP_SUPPORT) -set(ecal_io_udp_src_npcap - src/io/udp/udp_receiver_npcap.cpp - src/io/udp/udp_receiver_npcap.h +set(ecal_io_udp_sendreceive_src_npcap + src/io/udp/sendreceive/udp_receiver_npcap.cpp + src/io/udp/sendreceive/udp_receiver_npcap.h ) endif() -set(ecal_io_udp_src - src/io/udp/rcv_sample.cpp - src/io/udp/snd_raw_buffer.cpp - src/io/udp/snd_sample.cpp - src/io/udp/udp_configurations.cpp - src/io/udp/udp_init.cpp - src/io/udp/udp_receiver.cpp - src/io/udp/udp_receiver_asio.cpp - src/io/udp/udp_sender.cpp - src/io/udp/ecal_receiver.h - src/io/udp/msg_type.h - src/io/udp/rcv_sample.h - src/io/udp/snd_raw_buffer.h - src/io/udp/snd_sample.h - src/io/udp/udp_configurations.h - src/io/udp/udp_init.h - src/io/udp/udp_receiver.h - src/io/udp/udp_receiver_base.h - src/io/udp/udp_receiver_asio.h - src/io/udp/udp_sender.h - ${ecal_io_udp_src_npcap} -) - -# io/udp/linux +# io/udp/sendreceive +set(ecal_io_udp_sendreceive_src + src/io/udp/sendreceive/udp_receiver.cpp + src/io/udp/sendreceive/udp_receiver.h + src/io/udp/sendreceive/udp_receiver_asio.cpp + src/io/udp/sendreceive/udp_receiver_asio.h + src/io/udp/sendreceive/udp_sender.cpp + src/io/udp/sendreceive/udp_sender.h + ${ecal_io_udp_sendreceive_src_npcap} +) + +# io/udp/sendreceive/linux if(UNIX) - set(ecal_io_udp_linux_src - src/io/udp/linux/ecal_socket_option_linux.h + set(ecal_io_udp_sendreceive_linux_src + src/io/udp/sendreceive/linux/socket_os.h ) endif() +# io/udp/sendreceive/win32 +if (WIN32) + set(ecal_io_udp_sendreceive_win_src + src/io/udp/sendreceive/win32/socket_os.h +) +endif() + +# io/udp +set(ecal_io_udp_src + src/io/udp/ecal_udp_configurations.cpp + src/io/udp/ecal_udp_configurations.h + src/io/udp/ecal_udp_logging_receiver.cpp + src/io/udp/ecal_udp_logging_receiver.h + src/io/udp/ecal_udp_logging_sender.cpp + src/io/udp/ecal_udp_logging_sender.h + src/io/udp/ecal_udp_sample_receiver.cpp + src/io/udp/ecal_udp_sample_receiver.h + src/io/udp/ecal_udp_sample_sender.cpp + src/io/udp/ecal_udp_sample_sender.h + src/io/udp/ecal_udp_topic2mcast.h +) + ###################################### # logging ###################################### @@ -189,10 +204,8 @@ set(ecal_logging_src set(ecal_monitoring_src src/monitoring/ecal_monitoring_def.cpp src/monitoring/ecal_monitoring_impl.cpp - src/monitoring/ecal_monitoring_threads.cpp src/monitoring/ecal_monitoring_def.h src/monitoring/ecal_monitoring_impl.h - src/monitoring/ecal_monitoring_threads.h ) ###################################### @@ -297,7 +310,6 @@ set(ecal_util_src src/util/convert_utf.cpp src/util/convert_utf.h src/util/ecal_expmap.h - src/util/ecal_thread.cpp src/util/ecal_thread.h src/util/getenvvar.h src/util/sys_usage.cpp @@ -323,13 +335,11 @@ set(ecal_cmn_src src/ecal_global_accessors.h src/ecal_globals.h src/ecal_sample_to_topicinfo.h - src/topic2mcast.h ) if (WIN32) list (APPEND ecal_cmn_src src/ecal_win_main.h - src/ecal_win_socket.h ) endif() @@ -447,9 +457,12 @@ ecal_add_ecal_shared_library(${PROJECT_NAME} ${ecal_io_mtx_win_src} ${ecal_io_shm_src} ${ecal_io_shm_linux_src} - ${ecal_io_shm_win_src} + ${ecal_io_shm_win_src} + ${ecal_io_udp_fragmentation_src} + ${ecal_io_udp_sendreceive_src} ${ecal_io_udp_src} - ${ecal_io_udp_linux_src} + ${ecal_io_udp_sendreceive_linux_src} + ${ecal_io_udp_sendreceive_win_src} ${ecal_logging_src} ${ecal_monitoring_src} ${ecal_pubsub_src} @@ -567,7 +580,11 @@ if(NOT ${CMAKE_VERSION} VERSION_LESS "3.8.0") ${ecal_io_shm_src} ${ecal_io_shm_linux_src} ${ecal_io_shm_win_src} + ${ecal_io_udp_fragmentation_src} + ${ecal_io_udp_sendreceive_src} ${ecal_io_udp_src} + ${ecal_io_udp_sendreceive_linux_src} + ${ecal_io_udp_sendreceive_win_src} ${ecal_logging_src} ${ecal_monitoring_src} ${ecal_pubsub_src} diff --git a/ecal/core/include/ecal/ecal_monitoring.h b/ecal/core/include/ecal/ecal_monitoring.h index 59c3b503d2..ed247a8bb0 100644 --- a/ecal/core/include/ecal/ecal_monitoring.h +++ b/ecal/core/include/ecal/ecal_monitoring.h @@ -107,7 +107,7 @@ namespace eCAL * * @return Zero if succeeded. **/ - ECAL_DEPRECATE_SINCE_5_12("use GetMonitoring and publish yourself") + ECAL_DEPRECATE_SINCE_5_12("Function is no longer implemented. Instead use GetMonitoring") ECAL_API int PubMonitoring(bool state_, std::string name_ = "ecal.monitoring"); /** @@ -118,7 +118,7 @@ namespace eCAL * * @return Zero if succeeded. **/ - ECAL_DEPRECATE_SINCE_5_12("use GetLogging and publish yourself") + ECAL_DEPRECATE_SINCE_5_12("Function is no longer implemented. Instead use GetLogging") ECAL_API int PubLogging(bool state_, std::string name_ = "ecal.logging"); } /** @example monitoring_rec.cpp diff --git a/ecal/core/src/ecal_def.h b/ecal/core/src/ecal_def.h index 11c08c4954..5a07639c4a 100644 --- a/ecal/core/src/ecal_def.h +++ b/ecal/core/src/ecal_def.h @@ -174,14 +174,8 @@ /* delta time to check timeout for data readers in ms */ #define CMN_DATAREADER_TIMEOUT_RESOLUTION_MS 100 -/* cylce time udp registration receive thread in ms */ -#define CMN_REGISTRATION_RECEIVE_THREAD_CYCLE_TIME_MS 1000 - -/* cylce time udp logging receive thread in ms */ -#define CMN_LOGGING_RECEIVE_THREAD_CYCLE_TIME_MS 1000 - -/* cylce time udp paylaod receive thread in ms */ -#define CMN_PAYLOAD_RECEIVE_THREAD_CYCLE_TIME_MS 1000 +/* cylce time udp receive threads in ms */ +#define CMN_UDP_RECEIVE_THREAD_CYCLE_TIME_MS 1000 /**********************************************************************************************/ /* events */ diff --git a/ecal/core/src/ecal_globals.cpp b/ecal/core/src/ecal_globals.cpp index fb12903713..69f8906c8b 100644 --- a/ecal/core/src/ecal_globals.cpp +++ b/ecal/core/src/ecal_globals.cpp @@ -22,7 +22,7 @@ **/ #include "ecal_globals.h" -#include "io/udp/udp_init.h" + #include "config/ecal_config_reader.h" #include @@ -44,9 +44,6 @@ namespace eCAL // will be set if any new module was initialized bool new_initialization(false); - // this is needed here for functions like "GetHostName" on windows - Net::Initialize(); - ///////////////////// // CONFIG ///////////////////// @@ -297,9 +294,6 @@ namespace eCAL log_instance = nullptr; config_instance = nullptr; - // last not least we close all - Net::Finalize(); - initialized = false; return(0); diff --git a/ecal/core/src/ecal_process.cpp b/ecal/core/src/ecal_process.cpp index f631a201c6..47dfc72fe5 100644 --- a/ecal/core/src/ecal_process.cpp +++ b/ecal/core/src/ecal_process.cpp @@ -29,7 +29,7 @@ #include "registration/ecal_registration_receiver.h" #include "ecal_globals.h" #include "ecal_process.h" -#include "io/udp/udp_configurations.h" +#include "io/udp/ecal_udp_configurations.h" #include #include diff --git a/ecal/core/src/io/udp/ecal_receiver.h b/ecal/core/src/io/udp/ecal_receiver.h deleted file mode 100644 index 6d29dee2f8..0000000000 --- a/ecal/core/src/io/udp/ecal_receiver.h +++ /dev/null @@ -1,88 +0,0 @@ -/* ========================= 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 receiver base class -**/ - -#pragma once - -#include -#include "ecal_def.h" - -#ifdef ECAL_OS_WINDOWS -#include "ecal_win_socket.h" -#endif - -#ifdef ECAL_OS_LINUX -#include -#include -#include -#endif - -#include - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // receiver base class - //////////////////////////////////////////////////////// - struct SReceiverAttr - { - std::string address; - int port = 0; - bool broadcast = false; - bool loopback = true; - int rcvbuf = 1024 * 1024; - }; - - class CReceiver - { - public: - enum eSocketType - { - SType_Unknown = 0, - SType_ReceiverUDP, - SType_ReceiverTCP - }; - - public: - CReceiver() : m_skt_type(SType_Unknown) - { - }; - - explicit CReceiver(eSocketType skt_type_) : m_skt_type(skt_type_) - { - }; - - virtual ~CReceiver() - { - }; - - virtual bool Create(const SReceiverAttr& attr_) = 0; - virtual bool Destroy() = 0; - - virtual size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) = 0; - - inline eSocketType GetType() const {return m_skt_type;}; - - protected: - eSocketType m_skt_type; - }; -} diff --git a/ecal/core/src/io/udp/udp_configurations.cpp b/ecal/core/src/io/udp/ecal_udp_configurations.cpp similarity index 82% rename from ecal/core/src/io/udp/udp_configurations.cpp rename to ecal/core/src/io/udp/ecal_udp_configurations.cpp index 54ab932451..47919811fe 100644 --- a/ecal/core/src/io/udp/udp_configurations.cpp +++ b/ecal/core/src/io/udp/ecal_udp_configurations.cpp @@ -17,17 +17,49 @@ * ========================= eCAL LICENSE ================================= */ -#include "io/udp/udp_configurations.h" - -#include +#include "ecal_udp_configurations.h" #include "ecal_def.h" -#include "topic2mcast.h" +#include "ecal_udp_topic2mcast.h" + +#include namespace eCAL { namespace UDP { + /** + * @brief IsBroadcast() retrieves if we communicate via UDP Broadcast or UDP Multicast. + * + * @return True if broadcast mode is active. + */ + bool IsBroadcast() + { + return !Config::IsNetworkEnabled(); + } + + /** + * @brief IsNpcapEnabled() retrieves if we use the npcap UDP receiver (windows only). + * + * @return True if npcap mode is active. + */ + bool IsNpcapEnabled() + { + return Config::IsNpcapEnabled(); + } + + /** + * @brief Linux specific setting to enable joining multicast groups on all network interfacs independent of their link state. + * + * Enabling this makes sure that eCAL processes receive data if they are started before network devices are up and running. + * + * @return True if this setting is active. + */ + bool IsUdpMulticastJoinAllIfEnabled() + { + return Config::IsUdpMulticastJoinAllIfEnabled(); + } + /** * @brief GetLocalBroadcastAddress retrieves the broadcast address within the loopback range. * @@ -126,7 +158,7 @@ namespace eCAL if (local_only) { // if network is disabled, return a TTL of 0 to restrict multicast packets to the local machine - return 0; + return 1; } // if network is enabled, return the configured UDP multicast TTL value diff --git a/ecal/core/src/io/udp/udp_configurations.h b/ecal/core/src/io/udp/ecal_udp_configurations.h similarity index 86% rename from ecal/core/src/io/udp/udp_configurations.h rename to ecal/core/src/io/udp/ecal_udp_configurations.h index 7c0800a7a8..a5f4cbbd91 100644 --- a/ecal/core/src/io/udp/udp_configurations.h +++ b/ecal/core/src/io/udp/ecal_udp_configurations.h @@ -29,6 +29,29 @@ namespace eCAL { namespace UDP { + /** + * @brief IsBroadcast() retrieves if we communicate via UDP Broadcast or UDP Multicast. + * + * @return True if broadcast mode is active. + */ + bool IsBroadcast(); + + /** + * @brief IsNpcapEnabled() retrieves if we use the npcap UDP receiver (windows only). + * + * @return True if npcap mode is active. + */ + bool IsNpcapEnabled(); + + /** + * @brief Linux specific setting to enable joining multicast groups on all network interfacs independent of their link state. + * + * Enabling this makes sure that eCAL processes receive data if they are started before network devices are up and running. + * + * @return True if this setting is active. + */ + bool IsUdpMulticastJoinAllIfEnabled(); + /** * @brief GetRegistrationAddress retrieves the UDP registration address based on network configuration. * diff --git a/ecal/core/src/io/udp/ecal_udp_logging_receiver.cpp b/ecal/core/src/io/udp/ecal_udp_logging_receiver.cpp new file mode 100644 index 0000000000..483a06ea3a --- /dev/null +++ b/ecal/core/src/io/udp/ecal_udp_logging_receiver.cpp @@ -0,0 +1,65 @@ +/* ========================= 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 UDP logging receiver to receive messages of type eCAL::pb::LogMessage +**/ + +#include "ecal_udp_logging_receiver.h" + +#include "ecal_def.h" +#include "io/udp/fragmentation/msg_type.h" + +namespace eCAL +{ + namespace UDP + { + CLoggingReceiver::CLoggingReceiver(const IO::UDP::SReceiverAttr& attr_, LogMessageCallbackT log_message_callback_) : + m_network_mode(!attr_.broadcast), m_log_message_callback(log_message_callback_) + { + // create udp receiver + m_udp_receiver.Create(attr_); + + // allocate receive buffer + m_msg_buffer.resize(MSG_BUFFER_SIZE); + + // start receiver thread + m_udp_receiver_thread = std::make_shared(std::bind(&CLoggingReceiver::ReceiveThread, this)); + m_udp_receiver_thread->start(std::chrono::milliseconds(0)); + } + + CLoggingReceiver::~CLoggingReceiver() + { + m_udp_receiver_thread->stop(); + } + + void CLoggingReceiver::ReceiveThread() + { + // wait for any incoming message + const size_t recv_len = m_udp_receiver.Receive(m_msg_buffer.data(), m_msg_buffer.size(), CMN_UDP_RECEIVE_THREAD_CYCLE_TIME_MS); + if (recv_len > 0) + { + if (m_log_message.ParseFromArray(m_msg_buffer.data(), static_cast(recv_len))) + { + m_log_message_callback(m_log_message); + } + } + } + } +} diff --git a/ecal/core/src/io/udp/ecal_udp_logging_receiver.h b/ecal/core/src/io/udp/ecal_udp_logging_receiver.h new file mode 100644 index 0000000000..3a6714c449 --- /dev/null +++ b/ecal/core/src/io/udp/ecal_udp_logging_receiver.h @@ -0,0 +1,68 @@ +/* ========================= 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 UDP logging receiver to receive messages of type eCAL::pb::LogMessage +**/ + +#pragma once + +#include "io/udp/sendreceive/udp_receiver.h" +#include "util/ecal_thread.h" + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 0) // disable proto warnings +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace eCAL +{ + namespace UDP + { + class CLoggingReceiver + { + public: + using LogMessageCallbackT = std::function; + + CLoggingReceiver(const IO::UDP::SReceiverAttr& attr_, LogMessageCallbackT log_message_callback_); + virtual ~CLoggingReceiver(); + + protected: + void ReceiveThread(); + + bool m_network_mode; + + LogMessageCallbackT m_log_message_callback; + + IO::UDP::CUDPReceiver m_udp_receiver; + std::shared_ptr m_udp_receiver_thread; + + std::vector m_msg_buffer; + eCAL::pb::LogMessage m_log_message; + }; + } +} \ No newline at end of file diff --git a/ecal/core/src/io/udp/ecal_udp_logging_sender.cpp b/ecal/core/src/io/udp/ecal_udp_logging_sender.cpp new file mode 100644 index 0000000000..99e8df7035 --- /dev/null +++ b/ecal/core/src/io/udp/ecal_udp_logging_sender.cpp @@ -0,0 +1,48 @@ +/* ========================= 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 UDP logging sender to send messages of type eCAL::pb::LogMessage +**/ + +#include "ecal_udp_logging_sender.h" + +namespace eCAL +{ + namespace UDP + { + CLoggingSender::CLoggingSender(const IO::UDP::SSenderAttr& attr_) + { + m_udp_sender = std::make_shared(attr_); + } + + size_t CLoggingSender::Send(const eCAL::pb::LogMessage& ecal_log_message_) + { + if (!m_udp_sender) return(0); + + m_logmessage_s = ecal_log_message_.SerializeAsString(); + if (!m_logmessage_s.empty()) + { + return m_udp_sender->Send((void*)m_logmessage_s.data(), m_logmessage_s.size()); + } + + return 0; + } + } +} diff --git a/ecal/core/src/io/udp/ecal_udp_logging_sender.h b/ecal/core/src/io/udp/ecal_udp_logging_sender.h new file mode 100644 index 0000000000..c973ac9f3b --- /dev/null +++ b/ecal/core/src/io/udp/ecal_udp_logging_sender.h @@ -0,0 +1,56 @@ +/* ========================= 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 UDP logging sender to send messages of type eCAL::pb::LogMessage +**/ + +#pragma once + +#include "io/udp/sendreceive/udp_sender.h" + +#ifdef _MSC_VER +#pragma warning(push, 0) // disable proto warnings +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include + +namespace eCAL +{ + namespace UDP + { + class CLoggingSender + { + public: + CLoggingSender(const IO::UDP::SSenderAttr& attr_); + size_t Send(const eCAL::pb::LogMessage& ecal_log_message_); + + private: + IO::UDP::SSenderAttr m_attr; + std::shared_ptr m_udp_sender; + + std::string m_logmessage_s; + }; + } +} diff --git a/ecal/core/src/io/udp/ecal_udp_sample_receiver.cpp b/ecal/core/src/io/udp/ecal_udp_sample_receiver.cpp new file mode 100644 index 0000000000..175da4e058 --- /dev/null +++ b/ecal/core/src/io/udp/ecal_udp_sample_receiver.cpp @@ -0,0 +1,358 @@ +/* ========================= 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 UDP sample receiver to receive messages of type eCAL::pb::Sample +**/ + +#include "ecal_udp_sample_receiver.h" +#include "io/udp/fragmentation/msg_type.h" + +#include + +namespace eCAL +{ + namespace UDP + { + CSampleReceiver::CSampleDefragmentation::CSampleDefragmentation(CSampleReceiver* sample_receiver_) + : m_sample_receiver(sample_receiver_) + { + } + + CSampleReceiver::CSampleDefragmentation::~CSampleDefragmentation() = default; + + int CSampleReceiver::CSampleDefragmentation::OnMessageCompleted(std::vector&& msg_buffer_) + { + if (m_sample_receiver == nullptr) return(0); + + // read sample_name size + const unsigned short sample_name_size = ((unsigned short*)(msg_buffer_.data()))[0]; + // read sample_name + const std::string sample_name(msg_buffer_.data() + sizeof(sample_name_size)); + + if (m_sample_receiver->m_has_sample_callback(sample_name)) + { + // read sample + if (!m_ecal_sample.ParseFromArray(msg_buffer_.data() + sizeof(sample_name_size) + sample_name_size, static_cast(msg_buffer_.size() - (sizeof(sample_name_size) + sample_name_size)))) return(0); +#ifndef NDEBUG + // log it + eCAL::Logging::Log(log_level_debug3, sample_name + "::UDP Sample Completed"); + + // log it + switch (m_ecal_sample.cmd_type()) + { + case eCAL::pb::bct_none: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - NONE"); + break; + case eCAL::pb::bct_set_sample: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - SAMPLE"); + break; + case eCAL::pb::bct_reg_publisher: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PUBLISHER"); + break; + case eCAL::pb::bct_reg_subscriber: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SUBSCRIBER"); + break; + case eCAL::pb::bct_reg_process: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PROCESS"); + break; + case eCAL::pb::bct_reg_service: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SERVER"); + break; + case eCAL::pb::bct_reg_client: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER CLIENT"); + break; + default: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - UNKNOWN"); + break; + } +#endif + // get layer if this is a payload sample + eCAL::pb::eTLayerType layer = eCAL::pb::eTLayerType::tl_none; + if (m_ecal_sample.cmd_type() == eCAL::pb::eCmdType::bct_set_sample) + { + if (m_ecal_sample.topic().tlayer_size() > 0) + { + layer = m_ecal_sample.topic().tlayer(0).type(); + } + } + // apply sample + m_sample_receiver->m_apply_sample_callback(m_ecal_sample, layer); + } + + return(0); + } + + CSampleReceiver::CSampleReceiver(const IO::UDP::SReceiverAttr& attr_, HasSampleCallbackT has_sample_callback_, ApplySampleCallbackT apply_sample_callback_) : + m_has_sample_callback(has_sample_callback_), m_apply_sample_callback(apply_sample_callback_) + { + // create udp receiver + m_udp_receiver.Create(attr_); + + // allocate receive buffer + m_msg_buffer.resize(MSG_BUFFER_SIZE); + + // start receiver thread + m_udp_receiver_thread = std::make_shared(std::bind(&CSampleReceiver::ReceiveThread, this)); + m_udp_receiver_thread->start(std::chrono::milliseconds(0)); + + m_cleanup_start = std::chrono::steady_clock::now(); + } + + CSampleReceiver::~CSampleReceiver() + { + m_udp_receiver_thread->stop(); + } + + bool CSampleReceiver::AddMultiCastGroup(const char* ipaddr_) + { + return m_udp_receiver.AddMultiCastGroup(ipaddr_); + } + + bool CSampleReceiver::RemMultiCastGroup(const char* ipaddr_) + { + return m_udp_receiver.RemMultiCastGroup(ipaddr_); + } + + void CSampleReceiver::ReceiveThread() + { + // wait for any incoming message + const size_t recv_len = m_udp_receiver.Receive(m_msg_buffer.data(), m_msg_buffer.size(), CMN_UDP_RECEIVE_THREAD_CYCLE_TIME_MS); + if (recv_len > 0) + { + Process(m_msg_buffer.data(), recv_len); + } + } + + void CSampleReceiver::Process(const char* sample_buffer_, size_t sample_buffer_len_) + { + // we need at least the header information to start + if (sample_buffer_len_ < sizeof(IO::UDP::SUDPMessageHead)) return; + + // cast buffer to udp message struct + struct IO::UDP::SUDPMessage* ecal_message = (struct IO::UDP::SUDPMessage*)sample_buffer_; + + // check for eCAL 4.x header + if ( + (ecal_message->header.head[0] == 'e') + && (ecal_message->header.head[1] == 'C') + && (ecal_message->header.head[2] == 'A') + && (ecal_message->header.head[3] == 'L') + ) + { + eCAL::Logging::Log(log_level_warning, "Received eCAL 4 traffic"); + return; + } + + // check for valid header + else if ( + (ecal_message->header.head[0] != 'E') + || (ecal_message->header.head[1] != 'C') + || (ecal_message->header.head[2] != 'A') + || (ecal_message->header.head[3] != 'L') + ) + { + eCAL::Logging::Log(log_level_warning, "Received invalid traffic (eCAL Header missing)"); + return; + } + + // check integrity + switch (ecal_message->header.type) + { + case IO::UDP::msg_type_header: + break; + case IO::UDP::msg_type_content: + case IO::UDP::msg_type_header_with_content: + if (sample_buffer_len_ < sizeof(IO::UDP::SUDPMessageHead) + static_cast(ecal_message->header.len)) + return; + break; + default: + return; + } + +#ifndef NDEBUG + // log it + switch (ecal_message->header.type) + { + case IO::UDP::msg_type_header_with_content: + eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - HEADER_WITH_CONTENT"); + break; + case IO::UDP::msg_type_header: + eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - HEADER"); + break; + case IO::UDP::msg_type_content: + eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - CONTENT"); + break; + } +#endif + + switch (ecal_message->header.type) + { + case IO::UDP::msg_type_header_with_content: + { + // read sample_name size + unsigned short sample_name_size = 0; + memcpy(&sample_name_size, ecal_message->payload, 2); + // read sample_name + const std::string sample_name = ecal_message->payload + sizeof(sample_name_size); + + if (m_has_sample_callback(sample_name)) + { + // read sample + if (!m_ecal_sample.ParseFromArray(ecal_message->payload + static_cast(sizeof(sample_name_size) + sample_name_size), static_cast(static_cast(ecal_message->header.len) - (sizeof(sample_name_size) + sample_name_size)))) return; + +#ifndef NDEBUG + // log it + eCAL::Logging::Log(log_level_debug3, sample_name + "::UDP Sample Completed"); + + // log it + switch (m_ecal_sample.cmd_type()) + { + case eCAL::pb::bct_none: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - NONE"); + break; + case eCAL::pb::bct_set_sample: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - SAMPLE"); + break; + case eCAL::pb::bct_reg_publisher: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PUBLISHER"); + break; + case eCAL::pb::bct_reg_subscriber: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SUBSCRIBER"); + break; + case eCAL::pb::bct_reg_process: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PROCESS"); + break; + case eCAL::pb::bct_reg_service: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SERVICE"); + break; + case eCAL::pb::bct_reg_client: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER CLIENT"); + break; + default: + eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - UNKNOWN"); + break; + } +#endif + // get layer if this is a payload sample + eCAL::pb::eTLayerType layer = eCAL::pb::eTLayerType::tl_none; + if (m_ecal_sample.cmd_type() == eCAL::pb::eCmdType::bct_set_sample) + { + if (m_ecal_sample.topic().tlayer_size() > 0) + { + layer = m_ecal_sample.topic().tlayer(0).type(); + } + } + // apply sample + m_apply_sample_callback(m_ecal_sample, layer); + } + } + break; + // if we have a header only package + // we create a receive defragmentation buffer and apply it to the receive defragmentation map + // to process the following data packages + // we do not know the name here unfortunately + // so we have to wait for the first payload package :-( + case IO::UDP::msg_type_header: + { + // create new receive defragmentation buffer + std::shared_ptr receive_defragmentation_buf(nullptr); + receive_defragmentation_buf = std::make_shared(CSampleDefragmentation(this)); + m_defrag_sample_map[ecal_message->header.id] = receive_defragmentation_buf; + // apply message + receive_defragmentation_buf->ApplyMessage(*ecal_message); + } + break; + // if we have a payload package + // we check for an existing receive defragmentation buffer and apply the data to it + case IO::UDP::msg_type_content: + { + // first data package ? + if (ecal_message->header.num == 0) + { + // read sample_name size + unsigned short sample_name_size = 0; + memcpy(&sample_name_size, ecal_message->payload, 2); + // read sample_name + const std::string sample_name = ecal_message->payload + sizeof(sample_name_size); + + // remove the matching defragmentation buffer if we are not interested in this sample + if (!m_has_sample_callback(sample_name)) + { + auto riter = m_defrag_sample_map.find(ecal_message->header.id); + if (riter != m_defrag_sample_map.end()) + { +#ifndef NDEBUG + // log timeouted defragmentation buffers + eCAL::Logging::Log(log_level_debug3, "CUDPSampleReceiver::Receive - DISCARD PACKAGE FOR TOPIC: " + sample_name); +#endif + m_defrag_sample_map.erase(riter); + break; + } + } + } + + // process data package + auto iter = m_defrag_sample_map.find(ecal_message->header.id); + if (iter != m_defrag_sample_map.end()) + { + // apply message + iter->second->ApplyMessage(*ecal_message); + } + } + break; + default: + break; + } + + // cleanup finished or zombie received defragmentation buffers + auto diff_time = std::chrono::steady_clock::now() - m_cleanup_start; + const std::chrono::duration step_time = std::chrono::milliseconds(NET_UDP_RECBUFFER_CLEANUP); + if (diff_time > step_time) + { + m_cleanup_start = std::chrono::steady_clock::now(); + + for (SampleDefragmentationMapT::iterator riter = m_defrag_sample_map.begin(); riter != m_defrag_sample_map.end();) + { + const bool finished = riter->second->HasFinished(); + const bool timeouted = riter->second->HasTimedOut(step_time); + if (finished || timeouted) + { +#ifndef NDEBUG + const int32_t total_len = riter->second->GetMessageTotalLength(); + const int32_t current_len = riter->second->GetMessageCurrentLength(); +#endif + riter = m_defrag_sample_map.erase(riter); +#ifndef NDEBUG + // log timeouted defragmentation buffer + if (timeouted) + { + eCAL::Logging::Log(log_level_debug3, "CUDPSampleReceiver::Receive - TIMEOUT (TotalLength / CurrentLength): " + std::to_string(total_len) + " / " + std::to_string(current_len)); + } +#endif + } + else + { + ++riter; + } + } + } + } + } +} diff --git a/ecal/core/src/io/udp/ecal_udp_sample_receiver.h b/ecal/core/src/io/udp/ecal_udp_sample_receiver.h new file mode 100644 index 0000000000..698b845d4c --- /dev/null +++ b/ecal/core/src/io/udp/ecal_udp_sample_receiver.h @@ -0,0 +1,93 @@ +/* ========================= 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 UDP sample receiver to receive messages of type eCAL::pb::Sample +**/ + +#pragma once + +#include "io/udp/sendreceive/udp_receiver.h" +#include "io/udp/fragmentation/rcv_fragments.h" +#include "util/ecal_thread.h" + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 0) // disable proto warnings +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace eCAL +{ + namespace UDP + { + class CSampleReceiver + { + public: + using HasSampleCallbackT = std::function; + using ApplySampleCallbackT = std::function; + + CSampleReceiver(const IO::UDP::SReceiverAttr& attr_, HasSampleCallbackT has_sample_callback_, ApplySampleCallbackT apply_sample_callback_); + virtual ~CSampleReceiver(); + + bool AddMultiCastGroup(const char* ipaddr_); + bool RemMultiCastGroup(const char* ipaddr_); + + protected: + void ReceiveThread(); + void Process(const char* sample_buffer_, size_t sample_buffer_len_); + + HasSampleCallbackT m_has_sample_callback; + ApplySampleCallbackT m_apply_sample_callback; + + IO::UDP::CUDPReceiver m_udp_receiver; + std::shared_ptr m_udp_receiver_thread; + + std::vector m_msg_buffer; + eCAL::pb::Sample m_ecal_sample; + + std::chrono::steady_clock::time_point m_cleanup_start; + + class CSampleDefragmentation : public IO::UDP::CMsgDefragmentation + { + public: + explicit CSampleDefragmentation(CSampleReceiver* sample_receiver_); + ~CSampleDefragmentation() override; + + int OnMessageCompleted(std::vector&& msg_buffer_) override; + + protected: + CSampleReceiver* m_sample_receiver; + eCAL::pb::Sample m_ecal_sample; + }; + + using SampleDefragmentationMapT = std::unordered_map>; + SampleDefragmentationMapT m_defrag_sample_map; + }; + } +} diff --git a/ecal/core/src/io/udp/ecal_udp_sample_sender.cpp b/ecal/core/src/io/udp/ecal_udp_sample_sender.cpp new file mode 100644 index 0000000000..70c16a5399 --- /dev/null +++ b/ecal/core/src/io/udp/ecal_udp_sample_sender.cpp @@ -0,0 +1,69 @@ +/* ========================= 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 UDP sample sender to send messages of type eCAL::pb::Sample +**/ + +#include "ecal_udp_sample_sender.h" +#include "io/udp/fragmentation/snd_fragments.h" + +#include + +namespace +{ + size_t TransmitToUDP(const void* buf_, const size_t len_, const std::shared_ptr& sample_sender_, const std::string& mcast_address_) + { + return (sample_sender_->Send(buf_, len_, mcast_address_.c_str())); + } +} + +namespace eCAL +{ + namespace UDP + { + CSampleSender::CSampleSender(const IO::UDP::SSenderAttr& attr_) + { + m_udp_sender = std::make_shared(attr_); + } + + size_t CSampleSender::Send(const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, long bandwidth_) + { + if (!m_udp_sender) return(0); + + // return value + size_t sent_sum(0); + + const size_t data_size = IO::UDP::CreateSampleBuffer(sample_name_, ecal_sample_, m_payload); + if (data_size > 0) + { + // and send it + sent_sum = SendFragmentedMessage(m_payload.data(), data_size, bandwidth_, std::bind(TransmitToUDP, std::placeholders::_1, std::placeholders::_2, m_udp_sender, m_attr.address)); + +#ifndef NDEBUG + // log it + eCAL::Logging::Log(log_level_debug4, "UDP Sample Buffer Sent (" + std::to_string(sent_sum) + " Bytes)"); +#endif + } + + // return bytes sent + return(sent_sum); + } + } +} diff --git a/ecal/core/src/io/udp/snd_sample.h b/ecal/core/src/io/udp/ecal_udp_sample_sender.h similarity index 64% rename from ecal/core/src/io/udp/snd_sample.h rename to ecal/core/src/io/udp/ecal_udp_sample_sender.h index 8f2ff2be13..afb4dc5411 100644 --- a/ecal/core/src/io/udp/snd_sample.h +++ b/ecal/core/src/io/udp/ecal_udp_sample_sender.h @@ -18,14 +18,13 @@ */ /** - * @brief Sender thread for ecal samples + * @brief UDP sample sender to send messages of type eCAL::pb::Sample **/ #pragma once -#include +#include "io/udp/sendreceive/udp_sender.h" -#include "udp_sender.h" #ifdef _MSC_VER #pragma warning(push, 0) // disable proto warnings @@ -35,18 +34,24 @@ #pragma warning(pop) #endif +#include +#include + namespace eCAL { - class CSampleSender + namespace UDP { - public: - CSampleSender(const SSenderAttr& attr_); - size_t SendSample(const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, long bandwidth_); - - private: - SSenderAttr m_attr; - - std::shared_ptr m_udp_sender; - std::vector m_payload; - }; + class CSampleSender + { + public: + CSampleSender(const IO::UDP::SSenderAttr& attr_); + size_t Send(const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, long bandwidth_); + + private: + IO::UDP::SSenderAttr m_attr; + std::shared_ptr m_udp_sender; + + std::vector m_payload; + }; + } } diff --git a/ecal/core/src/topic2mcast.h b/ecal/core/src/io/udp/ecal_udp_topic2mcast.h similarity index 99% rename from ecal/core/src/topic2mcast.h rename to ecal/core/src/io/udp/ecal_udp_topic2mcast.h index e8811a10fd..15c3f4ac0e 100644 --- a/ecal/core/src/topic2mcast.h +++ b/ecal/core/src/io/udp/ecal_udp_topic2mcast.h @@ -180,7 +180,5 @@ namespace eCAL return topic2mcast_hash(hash_v, mcast_base_, mcast_mask_); } } - - } } diff --git a/ecal/core/src/io/udp/msg_type.h b/ecal/core/src/io/udp/fragmentation/msg_type.h similarity index 52% rename from ecal/core/src/io/udp/msg_type.h rename to ecal/core/src/io/udp/fragmentation/msg_type.h index 8346bf4c63..689f5d8d40 100644 --- a/ecal/core/src/io/udp/msg_type.h +++ b/ecal/core/src/io/udp/fragmentation/msg_type.h @@ -23,43 +23,45 @@ #pragma once -#include +#include -enum eUDPMessageType +namespace IO { - msg_type_unknown = 0, - msg_type_header = 1, - msg_type_content = 2, - msg_type_header_with_content = 3 -}; - -struct alignas(4) SUDPMessageHead -{ - SUDPMessageHead() + namespace UDP { - head[0] = 'E'; - head[1] = 'C'; - head[2] = 'A'; - head[3] = 'L'; - version = 5; - type = msg_type_unknown; - id = 0; - num = 0; - len = 0; - } + enum eUDPMessageType + { + msg_type_unknown = 0, + msg_type_header = 1, + msg_type_content = 2, + msg_type_header_with_content = 3 + }; + + struct alignas(4) SUDPMessageHead + { + SUDPMessageHead() + { + head[0] = 'E'; + head[1] = 'C'; + head[2] = 'A'; + head[3] = 'L'; + } - char head[4]; //-V112 - int32_t version; - int32_t type; - int32_t id; // unique id for all message parts - int32_t num; // header: number of all parts, data: current number of that part - int32_t len; // header: complete size of message, data: current size of that part -}; + char head[4]{}; //-V112 + int32_t version = 5; + int32_t type = msg_type_unknown; + int32_t id = 0; // unique id for all message parts + int32_t num = 0; // header: number of all parts, data: current number of that part + int32_t len = 0; // header: complete size of message, data: current size of that part + }; #define MSG_BUFFER_SIZE (64*1024 - 20 /* IP header */ - 8 /* UDP header */ - 1 /* don't ask */) #define MSG_PAYLOAD_SIZE (MSG_BUFFER_SIZE-sizeof(struct SUDPMessageHead)) -struct SUDPMessage -{ - struct SUDPMessageHead header; - char payload[MSG_PAYLOAD_SIZE]; -}; + + struct SUDPMessage + { + struct SUDPMessageHead header; + char payload[MSG_PAYLOAD_SIZE]{}; + }; + } +} diff --git a/ecal/core/src/io/udp/fragmentation/rcv_fragments.cpp b/ecal/core/src/io/udp/fragmentation/rcv_fragments.cpp new file mode 100644 index 0000000000..b82efdfb51 --- /dev/null +++ b/ecal/core/src/io/udp/fragmentation/rcv_fragments.cpp @@ -0,0 +1,151 @@ +/* ========================= 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 UDP sample receiver to receive messages of type eCAL::pb::Sample +**/ + + +#include "rcv_fragments.h" +#include "msg_type.h" + +#include + +#include + +namespace IO +{ + namespace UDP + { + CMsgDefragmentation::CMsgDefragmentation() + : m_timeout(0.0) + , m_recv_mode(rcm_waiting) + , m_message_id(0) + , m_message_total_num(0) + , m_message_total_len(0) + , m_message_curr_num(0) + , m_message_curr_len(0) + { + } + + CMsgDefragmentation::~CMsgDefragmentation() = default; + + int CMsgDefragmentation::ApplyMessage(const struct SUDPMessage& ecal_message_) + { + // reset timeout + m_timeout = std::chrono::duration(0.0); + + // process current packet + switch (ecal_message_.header.type) + { + // new message started + case msg_type_header: + OnMessageStart(ecal_message_); + break; + // message data package + case msg_type_content: + if (m_recv_mode == rcm_reading) + { + OnMessageData(ecal_message_); + } + break; + } + + // we have a complete message in the receive buffer + if (m_recv_mode == rcm_completed) + { + // call complete event + OnMessageCompleted(std::move(m_recv_buffer)); + } + + return(0); + } + + int CMsgDefragmentation::OnMessageStart(const struct SUDPMessage& ecal_message_) + { + // store header info + m_message_id = ecal_message_.header.id; + m_message_total_num = ecal_message_.header.num; + m_message_total_len = ecal_message_.header.len; + + // reset current message states + m_message_curr_num = 0; + m_message_curr_len = 0; + + // prepare receive buffer + m_recv_buffer.reserve(static_cast(m_message_total_len)); + + // switch to reading mode + m_recv_mode = rcm_reading; + + return(0); + } + + int CMsgDefragmentation::OnMessageData(const struct SUDPMessage& ecal_message_) + { + // check message id + if (ecal_message_.header.id != m_message_id) + { +#ifndef NDEBUG + // log it + eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET ID " + std::to_string(ecal_message_.header.id)); +#endif + m_recv_mode = rcm_aborted; + return(-1); + } + + // check current packet counter + if (ecal_message_.header.num != m_message_curr_num) + { +#ifndef NDEBUG + // log it + eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET NUMBER " + std::to_string(ecal_message_.header.num) + " / " + std::to_string(m_message_curr_num)); +#endif + m_recv_mode = rcm_aborted; + return(-1); + } + + // check current packet length + if (ecal_message_.header.len <= 0) + { +#ifndef NDEBUG + // log it + eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET LENGTH " + std::to_string(ecal_message_.header.len)); +#endif + m_recv_mode = rcm_aborted; + return(-1); + } + + // copy the message part to the receive message buffer + m_recv_buffer.resize(m_recv_buffer.size() + static_cast(ecal_message_.header.len)); + memcpy(m_recv_buffer.data() + m_recv_buffer.size() - static_cast(ecal_message_.header.len), ecal_message_.payload, static_cast(ecal_message_.header.len)); + + // increase packet counter + m_message_curr_num++; + + // increase current length + m_message_curr_len += ecal_message_.header.len; + + // last message packet ? -> switch to completed mode + if (m_message_curr_num == m_message_total_num) m_recv_mode = rcm_completed; + + return(0); + } + } +} diff --git a/ecal/core/src/io/udp/fragmentation/rcv_fragments.h b/ecal/core/src/io/udp/fragmentation/rcv_fragments.h new file mode 100644 index 0000000000..e83dfdb9df --- /dev/null +++ b/ecal/core/src/io/udp/fragmentation/rcv_fragments.h @@ -0,0 +1,75 @@ +/* ========================= 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 UDP sample receiver to receive messages of type eCAL::pb::Sample +**/ + +#pragma once + +#include "ecal_def.h" + +#include +#include + +namespace IO +{ + namespace UDP + { + class CMsgDefragmentation + { + public: + CMsgDefragmentation(); + virtual ~CMsgDefragmentation(); + + int ApplyMessage(const struct SUDPMessage& ecal_message_); + + bool HasFinished() { return((m_recv_mode == rcm_aborted) || (m_recv_mode == rcm_completed)); }; + bool HasTimedOut(const std::chrono::duration& diff_time_) { m_timeout += diff_time_; return(m_timeout >= std::chrono::milliseconds(NET_UDP_RECBUFFER_TIMEOUT)); }; + + int32_t GetMessageTotalLength() const { return(m_message_total_len); }; + int32_t GetMessageCurrentLength() const { return(m_message_curr_len); }; + + virtual int OnMessageCompleted(std::vector&& msg_buffer_) = 0; + + protected: + int OnMessageStart(const struct SUDPMessage& ecal_message_); + int OnMessageData(const struct SUDPMessage& ecal_message_); + + enum eReceiveMode + { + rcm_waiting = 1, + rcm_reading, + rcm_aborted, + rcm_completed + }; + + std::chrono::duration m_timeout; + std::vector m_recv_buffer; + eReceiveMode m_recv_mode; + + int32_t m_message_id; + int32_t m_message_total_num; + int32_t m_message_total_len; + + int32_t m_message_curr_num; + int32_t m_message_curr_len; + }; + } +} diff --git a/ecal/core/src/io/udp/fragmentation/snd_fragments.cpp b/ecal/core/src/io/udp/fragmentation/snd_fragments.cpp new file mode 100644 index 0000000000..4cfafa26a4 --- /dev/null +++ b/ecal/core/src/io/udp/fragmentation/snd_fragments.cpp @@ -0,0 +1,203 @@ +/* ========================= 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 raw message buffer handling +**/ + +#include "snd_fragments.h" +#include "msg_type.h" + +#include +#include +#include + +namespace +{ + // random number generator + unsigned long xorshf96(unsigned long& x, unsigned long& y, unsigned long& z) // period 2^96-1 + { + unsigned long t(0); + x ^= x << 16; + x ^= x >> 5; + x ^= x << 1; + + t = x; + x = y; + y = z; + z = t ^ x ^ y; + + return z; + } +} + +namespace IO +{ + namespace UDP + { + size_t CreateSampleBuffer(const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, std::vector& payload_) + { + const unsigned short sample_name_size = (unsigned short)sample_name_.size() + 1; +#if GOOGLE_PROTOBUF_VERSION >= 3001000 + const size_t sample_size = ecal_sample_.ByteSizeLong(); +#else + size_t sample_size = ecal_sample_.ByteSize(); +#endif + const size_t data_size = sizeof(sample_name_size) + sample_name_size + sample_size; + + // create payload buffer with reserved space for first message head + payload_.resize(data_size + sizeof(struct SUDPMessageHead)); + char* payload_data = payload_.data() + sizeof(struct SUDPMessageHead); + + // write topic name size + ((unsigned short*)payload_data)[0] = sample_name_size; + // write topic name + memcpy(payload_data + sizeof(sample_name_size), sample_name_.c_str(), sample_name_size); + + // write payload + if (ecal_sample_.SerializeWithCachedSizesToArray((google::protobuf::uint8*)payload_data + sizeof(sample_name_size) + sample_name_size)) + { + return data_size; + } + + return (0); + } + + size_t SendFragmentedMessage(char* buf_, size_t buf_len_, long bandwidth_, const TransmitCallbackT& transmit_cb_) + { + if (buf_ == nullptr) return(0); + + size_t sent(0); + size_t sent_sum(0); + + int32_t total_packet_num = int32_t(buf_len_ / MSG_PAYLOAD_SIZE); + if (buf_len_ % MSG_PAYLOAD_SIZE) total_packet_num++; + + // create message header + struct SUDPMessageHead msg_header; + + switch (total_packet_num) + { + case 1: + { + // create start packet + msg_header.type = msg_type_header_with_content; + msg_header.id = -1; // not needed for combined header / data message + msg_header.num = 1; + msg_header.len = int32_t(buf_len_); + + // copy msg_header in send buffer + memcpy(buf_, &msg_header, sizeof(struct SUDPMessageHead)); + + // send single header + data package + sent = transmit_cb_(buf_, sizeof(struct SUDPMessageHead) + buf_len_); + if (sent == 0) return(sent); + sent_sum += sent; + +#ifndef NDEBUG + // log it + //std::cout << "SendRawBuffer Packet Sent - HEADER_WITH_CONTENT (" + std::to_string(sent) + " Bytes)" << std::endl; +#endif + } + break; + default: + { + // calculate bandwidth timing parameter + long long send_sleep_us(0); + if (bandwidth_ > 0) + { + send_sleep_us = MSG_BUFFER_SIZE; + send_sleep_us *= 1000 * 1000; + send_sleep_us /= bandwidth_; + } + + // create start package + msg_header.type = msg_type_header; + { + // create random number for message id + { + static std::mutex xorshf96_mtx; + const std::lock_guard lock(xorshf96_mtx); + + static unsigned long x = static_cast(std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()).count() + ); + static unsigned long y = 362436069; + static unsigned long z = 521288629; + + msg_header.id = xorshf96(x, y, z); + } + } + msg_header.num = total_packet_num; + msg_header.len = int32_t(buf_len_); + + // send start package + sent = transmit_cb_(&msg_header, sizeof(struct SUDPMessageHead)); + if (sent == 0) return(sent); + sent_sum += sent; + +#ifndef NDEBUG + // log it + //std::cout << "SendRawBuffer Packet Sent - HEADER (" + std::to_string(sent) + " Bytes)" << std::endl; +#endif + + // send data packages + msg_header.type = msg_type_content; + for (int32_t current_packet_num = 0; current_packet_num < total_packet_num; current_packet_num++) + { + // calculate current payload + size_t current_snd_len = buf_len_; + if (current_snd_len > MSG_PAYLOAD_SIZE) current_snd_len = MSG_PAYLOAD_SIZE; + + if (current_snd_len > 0) + { + // reduce total send len + buf_len_ -= current_snd_len; + + // create data packet numbering + msg_header.num = current_packet_num; + msg_header.len = int32_t(current_snd_len); + + // copy msg_header in send buffer + memcpy(buf_ + static_cast(current_packet_num) * MSG_PAYLOAD_SIZE, &msg_header, sizeof(struct SUDPMessageHead)); + + // send data package + sent = transmit_cb_(buf_ + static_cast(current_packet_num) * MSG_PAYLOAD_SIZE, sizeof(struct SUDPMessageHead) + current_snd_len); + if (sent == 0) return(sent); + if (send_sleep_us != 0) + { + auto start = std::chrono::steady_clock::now(); + std::this_thread::sleep_until(start + std::chrono::microseconds(send_sleep_us)); + } + +#ifndef NDEBUG + // log it + //std::cout << "SendRawBuffer Packet Sent - CONTENT (" + std::to_string(sent) + " Bytes)" << std::endl; +#endif + sent_sum += sent; + } + } + } + break; + } + + return(sent_sum); + } + } +} diff --git a/ecal/core/src/io/udp/snd_raw_buffer.h b/ecal/core/src/io/udp/fragmentation/snd_fragments.h similarity index 74% rename from ecal/core/src/io/udp/snd_raw_buffer.h rename to ecal/core/src/io/udp/fragmentation/snd_fragments.h index 4cc864f1ac..3638e7070d 100644 --- a/ecal/core/src/io/udp/snd_raw_buffer.h +++ b/ecal/core/src/io/udp/fragmentation/snd_fragments.h @@ -32,10 +32,13 @@ #pragma warning(pop) #endif -namespace eCAL +namespace IO { - size_t CreateSampleBuffer(const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, std::vector& payload_); + namespace UDP + { + size_t CreateSampleBuffer(const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, std::vector& payload_); - using TransmitCallbackT = std::function; - size_t SendSampleBuffer(char* buf_, size_t buf_len_, long bandwidth_, TransmitCallbackT transmit_cb_); + using TransmitCallbackT = std::function; + size_t SendFragmentedMessage(char* buf_, size_t buf_len_, long bandwidth_, const TransmitCallbackT& transmit_cb_); + } } diff --git a/ecal/core/src/io/udp/linux/ecal_socket_option_linux.h b/ecal/core/src/io/udp/linux/ecal_socket_option_linux.h deleted file mode 100644 index f4cdafd474..0000000000 --- a/ecal/core/src/io/udp/linux/ecal_socket_option_linux.h +++ /dev/null @@ -1,85 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2022 - Teknique Limited - * - * 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 ================================= -*/ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - - -namespace eCAL -{ - inline static std::vector get_interface_index_list() - { - std::vector interface_index_list; - ifaddrs* ifa = nullptr; - ifaddrs* ifap = nullptr; - - // get a list of network interfaces - getifaddrs(&ifap); - - // create a list of network interfaces indexes - for (ifa = ifap; ifa; ifa = ifa->ifa_next) - { - if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_PACKET) - { - int index = if_nametoindex(ifa->ifa_name); - if (index) - { - interface_index_list.push_back(index); - } - } - } - - freeifaddrs(ifap); - - return interface_index_list; - } - - inline static bool set_socket_mcast_group_option(int socket, const char* ipaddr_, int option) - { - // set the multicast socket option on all interfaces - for (int iface : get_interface_index_list()) - { - group_req group_req = {}; - sockaddr_in *group = nullptr; - - memset(&group_req, 0, sizeof(group_req)); - group_req.gr_interface = iface; - group = reinterpret_cast(&group_req.gr_group); - group->sin_family = AF_INET; - group->sin_addr.s_addr = inet_addr(ipaddr_); - group->sin_port = 0; - - int rc = setsockopt(socket, IPPROTO_IP, option, &group_req, sizeof(group_source_req)); - if (rc != 0) - { - std::cerr << "setsockopt failed. Unable to set multicast group option: " << strerror(errno) << std::endl; - return(false); - } - } - - return(true); - } -} diff --git a/ecal/core/src/io/udp/rcv_sample.cpp b/ecal/core/src/io/udp/rcv_sample.cpp deleted file mode 100644 index 6d94094516..0000000000 --- a/ecal/core/src/io/udp/rcv_sample.cpp +++ /dev/null @@ -1,452 +0,0 @@ -/* ========================= 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 Receiver thread for ecal samples -**/ - -#include - -#include - -#include "ecal_def.h" -#include "rcv_sample.h" - - -CReceiveSlot::CReceiveSlot() - : m_timeout(0.0) - , m_recv_mode(rcm_waiting) - , m_message_id(0) - , m_message_total_num(0) - , m_message_total_len(0) - , m_message_curr_num(0) - , m_message_curr_len(0) -{ -} - -CReceiveSlot::~CReceiveSlot() = default; - -int CReceiveSlot::ApplyMessage(const struct SUDPMessage& ecal_message_) -{ - // reset timeout - m_timeout = std::chrono::duration(0.0); - - // process current packet - switch(ecal_message_.header.type) - { - // new message started - case msg_type_header: - OnMessageStart(ecal_message_); - break; - // message data package - case msg_type_content: - if(m_recv_mode == rcm_reading) - { - OnMessageData(ecal_message_); - } - break; - } - - // we have a complete message in the receive buffer - if(m_recv_mode == rcm_completed) - { - // call complete event - OnMessageCompleted(std::move(m_recv_buffer)); - } - - return(0); -} - -int CReceiveSlot::OnMessageStart(const struct SUDPMessage& ecal_message_) -{ - // store header info - m_message_id = ecal_message_.header.id; - m_message_total_num = ecal_message_.header.num; - m_message_total_len = ecal_message_.header.len; - - // reset current message states - m_message_curr_num = 0; - m_message_curr_len = 0; - - // prepare receive buffer - m_recv_buffer.reserve(static_cast(m_message_total_len)); - - // switch to reading mode - m_recv_mode = rcm_reading; - - return(0); -} - -int CReceiveSlot::OnMessageData(const struct SUDPMessage& ecal_message_) -{ - // check message id - if(ecal_message_.header.id != m_message_id) - { -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET ID " + std::to_string(ecal_message_.header.id)); -#endif - m_recv_mode = rcm_aborted; - return(-1); - } - - // check current packet counter - if(ecal_message_.header.num != m_message_curr_num) - { -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET NUMBER " + std::to_string(ecal_message_.header.num) + " / " + std::to_string(m_message_curr_num)); -#endif - m_recv_mode = rcm_aborted; - return(-1); - } - - // check current packet length - if(ecal_message_.header.len <= 0) - { -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET LENGTH " + std::to_string(ecal_message_.header.len)); -#endif - m_recv_mode = rcm_aborted; - return(-1); - } - - // copy the message part to the receive message buffer - m_recv_buffer.resize(m_recv_buffer.size() + static_cast(ecal_message_.header.len)); - memcpy(m_recv_buffer.data() + m_recv_buffer.size() - static_cast(ecal_message_.header.len), ecal_message_.payload, static_cast(ecal_message_.header.len)); - - // increase packet counter - m_message_curr_num++; - - // increase current length - m_message_curr_len += ecal_message_.header.len; - - // last message packet ? -> switch to completed mode - if(m_message_curr_num == m_message_total_num) m_recv_mode = rcm_completed; - - return(0); -} - - -CSampleReceiver::CSampleReceiveSlot::CSampleReceiveSlot(CSampleReceiver* sample_receiver_) : - m_sample_receiver(sample_receiver_) -{ -} - -CSampleReceiver::CSampleReceiveSlot::~CSampleReceiveSlot() = default; - -int CSampleReceiver::CSampleReceiveSlot::OnMessageCompleted(std::vector &&msg_buffer_) -{ - if(m_sample_receiver == nullptr) return(0); - - // read sample_name size - const unsigned short sample_name_size = ((unsigned short*)(msg_buffer_.data()))[0]; - // read sample_name - const std::string sample_name(msg_buffer_.data() + sizeof(sample_name_size)); - - if(m_sample_receiver->HasSample(sample_name)) - { - // read sample - if(!m_ecal_sample.ParseFromArray(msg_buffer_.data() + sizeof(sample_name_size) + sample_name_size, static_cast(msg_buffer_.size() - (sizeof(sample_name_size) + sample_name_size)))) return(0); -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug3, sample_name + "::UDP Sample Completed"); - - // log it - switch(m_ecal_sample.cmd_type()) - { - case eCAL::pb::bct_none: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - NONE"); - break; - case eCAL::pb::bct_set_sample: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - SAMPLE"); - break; - case eCAL::pb::bct_reg_publisher: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PUBLISHER"); - break; - case eCAL::pb::bct_reg_subscriber: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SUBSCRIBER"); - break; - case eCAL::pb::bct_reg_process: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PROCESS"); - break; - case eCAL::pb::bct_reg_service: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SERVER"); - break; - case eCAL::pb::bct_reg_client: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER CLIENT"); - break; - default: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - UNKNOWN"); - break; - } -#endif - // get layer if this is a payload sample - eCAL::pb::eTLayerType layer = eCAL::pb::eTLayerType::tl_none; - if (m_ecal_sample.cmd_type() == eCAL::pb::eCmdType::bct_set_sample) - { - if (m_ecal_sample.topic().tlayer_size() > 0) - { - layer = m_ecal_sample.topic().tlayer(0).type(); - } - } - // apply sample - m_sample_receiver->ApplySample(m_ecal_sample, layer); - } - - return(0); -} - -CSampleReceiver::CSampleReceiver() -{ - m_msg_buffer.resize(MSG_BUFFER_SIZE); - m_cleanup_start = std::chrono::steady_clock::now(); -} - -CSampleReceiver::~CSampleReceiver() = default; - -int CSampleReceiver::Receive(eCAL::CUDPReceiver* sample_receiver_, int timeout_) -{ - if(sample_receiver_ == nullptr) return(-1); - - // wait for any incoming message - const size_t recv_len = sample_receiver_->Receive(m_msg_buffer.data(), m_msg_buffer.size(), timeout_); - if(recv_len > 0) - { - return(Process(m_msg_buffer.data(), recv_len)); - } - - return(0); -} - -int CSampleReceiver::Process(const char* sample_buffer_, size_t sample_buffer_len_) -{ - // we need at least the header information to start - if (sample_buffer_len_ < sizeof(SUDPMessageHead)) return(0); - - // cast buffer to udp message struct - struct SUDPMessage* ecal_message = (struct SUDPMessage*)sample_buffer_; - - // check for eCAL 4.x header - if ((ecal_message->header.head[0] == 'e') - && (ecal_message->header.head[1] == 'C') - && (ecal_message->header.head[2] == 'A') - && (ecal_message->header.head[3] == 'L') - ) - { - eCAL::Logging::Log(log_level_warning, "Received eCAL 4 traffic"); - return(0); - } - - // check for valid header - else if ( (ecal_message->header.head[0] != 'E') - || (ecal_message->header.head[1] != 'C') - || (ecal_message->header.head[2] != 'A') - || (ecal_message->header.head[3] != 'L') - ) - { - eCAL::Logging::Log(log_level_warning, "Received invalid traffic (eCAL Header missing)"); - return(0); - } - - // check integrity - switch (ecal_message->header.type) - { - case msg_type_header: - break; - case msg_type_content: - case msg_type_header_with_content: - if (sample_buffer_len_ < sizeof(SUDPMessageHead) + static_cast(ecal_message->header.len)) - return(0); - break; - default: - return(0); - } - -#ifndef NDEBUG - // log it - switch (ecal_message->header.type) - { - case msg_type_header_with_content: - eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - HEADER_WITH_CONTENT"); - break; - case msg_type_header: - eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - HEADER"); - break; - case msg_type_content: - eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - CONTENT"); - break; - } -#endif - - switch (ecal_message->header.type) - { - case msg_type_header_with_content: - { - // read sample_name size - unsigned short sample_name_size = 0; - memcpy(&sample_name_size, ecal_message->payload, 2); - // read sample_name - const std::string sample_name = ecal_message->payload + sizeof(sample_name_size); - - if (HasSample(sample_name)) - { - // read sample - if (!m_ecal_sample.ParseFromArray(ecal_message->payload + static_cast(sizeof(sample_name_size) + sample_name_size), static_cast(static_cast(ecal_message->header.len) - (sizeof(sample_name_size) + sample_name_size)))) return(0); - -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug3, sample_name + "::UDP Sample Completed"); - - // log it - switch (m_ecal_sample.cmd_type()) - { - case eCAL::pb::bct_none: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - NONE"); - break; - case eCAL::pb::bct_set_sample: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - SAMPLE"); - break; - case eCAL::pb::bct_reg_publisher: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PUBLISHER"); - break; - case eCAL::pb::bct_reg_subscriber: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SUBSCRIBER"); - break; - case eCAL::pb::bct_reg_process: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PROCESS"); - break; - case eCAL::pb::bct_reg_service: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SERVICE"); - break; - case eCAL::pb::bct_reg_client: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER CLIENT"); - break; - default: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - UNKNOWN"); - break; - } -#endif - // get layer if this is a payload sample - eCAL::pb::eTLayerType layer = eCAL::pb::eTLayerType::tl_none; - if (m_ecal_sample.cmd_type() == eCAL::pb::eCmdType::bct_set_sample) - { - if (m_ecal_sample.topic().tlayer_size() > 0) - { - layer = m_ecal_sample.topic().tlayer(0).type(); - } - } - // apply sample - ApplySample(m_ecal_sample, layer); - } - } - break; - // if we have a header only package - // we create a receive slot and apply it to the receive slot map - // to process the following data packages - // we do not know the name here unfortunately - // so we have to wait for the first payload package :-( - case msg_type_header: - { - // create new receive slot - std::shared_ptr receive_slot(nullptr); - receive_slot = std::make_shared(CSampleReceiveSlot(this)); - m_receive_slot_map[ecal_message->header.id] = receive_slot; - // apply message - receive_slot->ApplyMessage(*ecal_message); - } - break; - // if we have a payload package - // we check for an existing receive slot and apply the data to it - case msg_type_content: - { - // first data package ? - if (ecal_message->header.num == 0) - { - // read sample_name size - unsigned short sample_name_size = 0; - memcpy(&sample_name_size, ecal_message->payload, 2); - // read sample_name - const std::string sample_name = ecal_message->payload + sizeof(sample_name_size); - - // remove the matching slot if we are not interested in this sample - if (!HasSample(sample_name)) - { - auto riter = m_receive_slot_map.find(ecal_message->header.id); - if (riter != m_receive_slot_map.end()) - { -#ifndef NDEBUG - // log timeouted slot - eCAL::Logging::Log(log_level_debug3, "CSampleReceiver::Receive - DISCARD PACKAGE FOR TOPIC: " + sample_name); -#endif - m_receive_slot_map.erase(riter); - break; - } - } - } - - // process data package - auto iter = m_receive_slot_map.find(ecal_message->header.id); - if (iter != m_receive_slot_map.end()) - { - // apply message - iter->second->ApplyMessage(*ecal_message); - } - } - break; - default: - break; - } - - // cleanup finished or zombie received slots - auto diff_time = std::chrono::steady_clock::now() - m_cleanup_start; - const std::chrono::duration step_time = std::chrono::milliseconds(NET_UDP_RECBUFFER_CLEANUP); - if (diff_time > step_time) - { - m_cleanup_start = std::chrono::steady_clock::now(); - - for (ReceiveSlotMapT::iterator riter = m_receive_slot_map.begin(); riter != m_receive_slot_map.end();) - { - const bool finished = riter->second->HasFinished(); - const bool timeouted = riter->second->HasTimedOut(step_time); - if (finished || timeouted) - { -#ifndef NDEBUG - const int32_t total_len = riter->second->GetMessageTotalLength(); - const int32_t current_len = riter->second->GetMessageCurrentLength(); -#endif - riter = m_receive_slot_map.erase(riter); -#ifndef NDEBUG - // log timeouted slot - if (timeouted) - { - eCAL::Logging::Log(log_level_debug3, "CSampleReceiver::Receive - TIMEOUT (TotalLength / CurrentLength): " + std::to_string(total_len) + " / " + std::to_string(current_len)); - } -#endif - } - else - { - ++riter; - } - } - } - - return(static_cast(sample_buffer_len_)); -} diff --git a/ecal/core/src/io/udp/rcv_sample.h b/ecal/core/src/io/udp/rcv_sample.h deleted file mode 100644 index b3cb2b02d0..0000000000 --- a/ecal/core/src/io/udp/rcv_sample.h +++ /dev/null @@ -1,116 +0,0 @@ -/* ========================= 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 Receiver thread for ecal samples -**/ - -#pragma once - -#include "ecal_def.h" -#include "udp_receiver.h" -#include "util/ecal_thread.h" -#include "msg_type.h" - -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -class CReceiveSlot -{ -public: - CReceiveSlot(); - virtual ~CReceiveSlot(); - - int ApplyMessage(const struct SUDPMessage& ecal_message_); - bool HasFinished() {return((m_recv_mode == rcm_aborted) || (m_recv_mode == rcm_completed));}; - bool HasTimedOut(const std::chrono::duration& diff_time_) {m_timeout += diff_time_; return(m_timeout >= std::chrono::milliseconds(NET_UDP_RECBUFFER_TIMEOUT));}; - int32_t GetMessageTotalLength() {return(m_message_total_len);}; - int32_t GetMessageCurrentLength() {return(m_message_curr_len);}; - - virtual int OnMessageCompleted(std::vector &&msg_buffer_) = 0; - -protected: - int OnMessageStart(const struct SUDPMessage& ecal_message_); - int OnMessageData(const struct SUDPMessage& ecal_message_); - - enum eReceiveMode - { - rcm_waiting = 1, - rcm_reading, - rcm_aborted, - rcm_completed - }; - - std::chrono::duration m_timeout; - std::vector m_recv_buffer; - eReceiveMode m_recv_mode; - - int32_t m_message_id; - int32_t m_message_total_num; - int32_t m_message_total_len; - - int32_t m_message_curr_num; - int32_t m_message_curr_len; - - eCAL::pb::Sample m_ecal_sample; -}; - - -class CSampleReceiver -{ - class CSampleReceiveSlot : public CReceiveSlot - { - public: - explicit CSampleReceiveSlot(CSampleReceiver* sample_receiver_); - virtual ~CSampleReceiveSlot(); - - virtual int OnMessageCompleted(std::vector &&msg_buffer_); - - protected: - CSampleReceiver* m_sample_receiver; - }; - -public: - CSampleReceiver(); - virtual ~CSampleReceiver(); - - virtual bool HasSample(const std::string& sample_name_) = 0; - virtual bool ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType layer_) = 0; - - int Receive(eCAL::CUDPReceiver* sample_receiver_, int timeout_); - int Process(const char* sample_buffer_, size_t sample_buffer_len_); - -protected: - using ReceiveSlotMapT = std::unordered_map>; - ReceiveSlotMapT m_receive_slot_map; - std::vector m_msg_buffer; - eCAL::pb::Sample m_ecal_sample; - - std::chrono::steady_clock::time_point m_cleanup_start; -}; diff --git a/ecal/core/src/io/udp/sendreceive/linux/socket_os.h b/ecal/core/src/io/udp/sendreceive/linux/socket_os.h new file mode 100644 index 0000000000..a436413216 --- /dev/null +++ b/ecal/core/src/io/udp/sendreceive/linux/socket_os.h @@ -0,0 +1,88 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2022 - Teknique Limited + * + * 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 ================================= +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + + +namespace IO +{ + namespace UDP + { + inline static std::vector get_interface_index_list() + { + std::vector interface_index_list; + ifaddrs* ifa = nullptr; + ifaddrs* ifap = nullptr; + + // get a list of network interfaces + getifaddrs(&ifap); + + // create a list of network interfaces indexes + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_PACKET) + { + int index = if_nametoindex(ifa->ifa_name); + if (index) + { + interface_index_list.push_back(index); + } + } + } + + freeifaddrs(ifap); + + return interface_index_list; + } + + inline static bool set_socket_mcast_group_option(int socket, const char* ipaddr_, int option) + { + // set the multicast socket option on all interfaces + for (int iface : get_interface_index_list()) + { + group_req group_req = {}; + sockaddr_in* group = nullptr; + + memset(&group_req, 0, sizeof(group_req)); + group_req.gr_interface = iface; + group = reinterpret_cast(&group_req.gr_group); + group->sin_family = AF_INET; + group->sin_addr.s_addr = inet_addr(ipaddr_); + group->sin_port = 0; + + int rc = setsockopt(socket, IPPROTO_IP, option, &group_req, sizeof(group_source_req)); + if (rc != 0) + { + std::cerr << "setsockopt failed. Unable to set multicast group option: " << strerror(errno) << std::endl; + return(false); + } + } + + return(true); + } + } +} diff --git a/ecal/core/src/io/udp/sendreceive/udp_receiver.cpp b/ecal/core/src/io/udp/sendreceive/udp_receiver.cpp new file mode 100644 index 0000000000..9eb89afc8f --- /dev/null +++ b/ecal/core/src/io/udp/sendreceive/udp_receiver.cpp @@ -0,0 +1,106 @@ +/* ========================= 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 UDP receiver class +**/ + +#include "udp_receiver.h" +#include "io/udp/ecal_udp_configurations.h" + +#include "udp_receiver_asio.h" +#ifdef ECAL_NPCAP_SUPPORT +#include "udp_receiver_npcap.h" +#endif + +#include + +namespace IO +{ + namespace UDP + { + //////////////////////////////////////////////////////// + // udp receiver class + //////////////////////////////////////////////////////// + CUDPReceiver::CUDPReceiver() + : m_use_npcap(false) + { +#ifdef ECAL_NPCAP_SUPPORT + if (eCAL::UDP::IsNpcapEnabled()) + { + m_use_npcap = Udpcap::Initialize(); // Only use NPCAP if we can initialize it (or it has already been initialized successfully) + if (!m_use_npcap) + { + std::cerr << "Npcap is enabled, but cannot be initialized. Using socket fallback mode." << std::endl; + } + } +#endif //ECAL_NPCAP_SUPPORT + } + + bool CUDPReceiver::Create(const SReceiverAttr& attr_) + { + if (m_socket_impl) return false; + +#ifdef ECAL_NPCAP_SUPPORT + if (m_use_npcap) + { + m_socket_impl = std::make_shared(attr_); + return true; + } +#endif // ECAL_NPCAP_SUPPORT + + m_socket_impl = std::make_shared(attr_); + return(true); + } + + bool CUDPReceiver::Destroy() + { + if (!m_socket_impl) return(false); + + const std::lock_guard lock(m_socket_mtx); + m_socket_impl.reset(); + + return(true); + } + + bool CUDPReceiver::AddMultiCastGroup(const char* ipaddr_) + { + if (!m_socket_impl) return(false); + + const std::lock_guard lock(m_socket_mtx); + return(m_socket_impl->AddMultiCastGroup(ipaddr_)); + } + + bool CUDPReceiver::RemMultiCastGroup(const char* ipaddr_) + { + if (!m_socket_impl) return(false); + + const std::lock_guard lock(m_socket_mtx); + return(m_socket_impl->RemMultiCastGroup(ipaddr_)); + } + + size_t CUDPReceiver::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ /* = nullptr */) + { + if (!m_socket_impl) return(0); + + const std::lock_guard lock(m_socket_mtx); + return(m_socket_impl->Receive(buf_, len_, timeout_, address_)); + } + } +} diff --git a/ecal/core/src/io/udp/sendreceive/udp_receiver.h b/ecal/core/src/io/udp/sendreceive/udp_receiver.h new file mode 100644 index 0000000000..35467b8cdd --- /dev/null +++ b/ecal/core/src/io/udp/sendreceive/udp_receiver.h @@ -0,0 +1,93 @@ +/* ========================= 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 UDP receiver class +**/ + +#pragma once + +#include + +#ifdef ECAL_OS_WINDOWS +#include "win32/socket_os.h" +#endif + +#ifdef ECAL_OS_LINUX +#include +#include +#include +#endif + +#include +#include +#include + +namespace IO +{ + namespace UDP + { + struct SReceiverAttr + { + std::string address; + int port = 0; + bool broadcast = false; + bool loopback = true; + int rcvbuf = 1024 * 1024; + }; + + class CUDPReceiverImpl + { + public: + CUDPReceiverImpl(const SReceiverAttr& /*attr_*/) {}; + // We don't technically need a virtual destructor, if we are working with shared_ptrs... + virtual ~CUDPReceiverImpl() = default; + + // Delete copy / move operations to prevent slicing + CUDPReceiverImpl(CUDPReceiverImpl&&) = delete; + CUDPReceiverImpl& operator=(CUDPReceiverImpl&&) = delete; + CUDPReceiverImpl(const CUDPReceiverImpl&) = delete; + CUDPReceiverImpl& operator=(const CUDPReceiverImpl&) = delete; + + virtual bool AddMultiCastGroup(const char* ipaddr_) = 0; + virtual bool RemMultiCastGroup(const char* ipaddr_) = 0; + + virtual size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) = 0; + }; + + class CUDPReceiver + { + public: + CUDPReceiver(); + + bool Create(const SReceiverAttr& attr_); + bool Destroy(); + + bool AddMultiCastGroup(const char* ipaddr_); + bool RemMultiCastGroup(const char* ipaddr_); + + size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr); + + protected: + bool m_use_npcap; + std::mutex m_socket_mtx; + std::shared_ptr m_socket_impl; + }; + } +} diff --git a/ecal/core/src/io/udp/sendreceive/udp_receiver_asio.cpp b/ecal/core/src/io/udp/sendreceive/udp_receiver_asio.cpp new file mode 100644 index 0000000000..df4c0f3677 --- /dev/null +++ b/ecal/core/src/io/udp/sendreceive/udp_receiver_asio.cpp @@ -0,0 +1,229 @@ +/* ========================= 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 "udp_receiver_asio.h" + +#include "io/udp/ecal_udp_configurations.h" + +#ifdef __linux__ +#include "linux/socket_os.h" +#endif + +#include + +namespace IO +{ + namespace UDP + { + //////////////////////////////////////////////////////// + // Default ASIO based receiver class implementation + //////////////////////////////////////////////////////// + CUDPReceiverAsio::CUDPReceiverAsio(const SReceiverAttr& attr_) : + CUDPReceiverImpl(attr_), + m_created(false), + m_broadcast(attr_.broadcast), + m_socket(m_iocontext) + { + // create socket + const asio::ip::udp::endpoint listen_endpoint(asio::ip::udp::v4(), static_cast(attr_.port)); + { + asio::error_code ec; + m_socket.open(listen_endpoint.protocol(), ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to open socket: " << ec.message() << std::endl; + return; + } + } + + // set socket reuse + { + asio::error_code ec; + m_socket.set_option(asio::ip::udp::socket::reuse_address(true), ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to set reuse-address option: " << ec.message() << std::endl; + } + } + + // bind socket + { + asio::error_code ec; + m_socket.bind(listen_endpoint, ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to bind socket to " << listen_endpoint.address().to_string() << ":" << listen_endpoint.port() << ": " << ec.message() << std::endl; + return; + } + } + + // set loopback option + { + const asio::ip::multicast::enable_loopback loopback(attr_.loopback); + asio::error_code ec; + m_socket.set_option(loopback, ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to enable loopback: " << ec.message() << std::endl; + } + } + + // set receive buffer size (default = 1 MB) + { + int rcvbuf = 1024 * 1024; + if (attr_.rcvbuf > 0) rcvbuf = attr_.rcvbuf; + const asio::socket_base::receive_buffer_size recbufsize(rcvbuf); + asio::error_code ec; + m_socket.set_option(recbufsize, ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to set receive buffer size: " << ec.message() << std::endl; + } + } + + // join multicast group + AddMultiCastGroup(attr_.address.c_str()); + + // state successful creation + m_created = true; + } + + CUDPReceiverAsio::~CUDPReceiverAsio() + { + // close the socket + m_socket.close(); + + // state successful destruction + m_created = false; + } + + bool CUDPReceiverAsio::AddMultiCastGroup(const char* ipaddr_) + { + if (!m_broadcast) + { + // join multicast group +#ifdef __linux__ + if (eCAL::UDP::IsUdpMulticastJoinAllIfEnabled()) + { + if (!IO::UDP::set_socket_mcast_group_option(m_socket.native_handle(), ipaddr_, MCAST_JOIN_GROUP)) + { + return(false); + } + } + else +#endif + { + asio::error_code ec; + m_socket.set_option(asio::ip::multicast::join_group(asio::ip::make_address(ipaddr_)), ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to join multicast group: " << ec.message() << std::endl; + return(false); + } + } + } + return(true); + } + + bool CUDPReceiverAsio::RemMultiCastGroup(const char* ipaddr_) + { + if (!m_broadcast) + { + // Leave multicast group +#ifdef __linux__ + if (eCAL::UDP::IsUdpMulticastJoinAllIfEnabled()) + { + if (!IO::UDP::set_socket_mcast_group_option(m_socket.native_handle(), ipaddr_, MCAST_LEAVE_GROUP)) + { + return(false); + } + } + else +#endif + { + asio::error_code ec; + m_socket.set_option(asio::ip::multicast::leave_group(asio::ip::make_address(ipaddr_)), ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to leave multicast group: " << ec.message() << std::endl; + return(false); + } + } + } + return(true); + } + + size_t CUDPReceiverAsio::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ /* = nullptr */) + { + if (!m_created) return 0; + + size_t reclen(0); + m_socket.async_receive_from(asio::buffer(buf_, len_), m_sender_endpoint, + [&reclen](std::error_code ec, std::size_t length) + { + if (!ec) + { + reclen = length; + } + }); + + // run for timeout ms + RunIOContext(asio::chrono::milliseconds(timeout_)); + + // retrieve underlaying raw socket informations + if (address_ != nullptr) + { + if (m_sender_endpoint.address().is_v4()) + { + asio::detail::sockaddr_in4_type* in4 = reinterpret_cast(m_sender_endpoint.data()); + address_->sin_addr = in4->sin_addr; + address_->sin_family = in4->sin_family; + address_->sin_port = in4->sin_port; + memset(&(address_->sin_zero), 0, 8); + } + else + { + std::cout << "CUDPReceiverAsio: ipv4 address conversion failed." << std::endl; + } + } + + return (reclen); + } + + void CUDPReceiverAsio::RunIOContext(const asio::chrono::steady_clock::duration& timeout) + { + // restart the io_context, as it may have been left in the "stopped" state by a previous operation + m_iocontext.restart(); + + // block until the asynchronous operation has completed, or timed out + m_iocontext.run_for(timeout); + + // stop the context if even the operation was not successful completed + if (!m_iocontext.stopped()) + { + // cancel the outstanding asynchronous operation + m_socket.cancel(); + + // run the io_context again until the operation completes + m_iocontext.run(); + } + } + } +} diff --git a/ecal/core/src/io/udp/udp_receiver_asio.h b/ecal/core/src/io/udp/sendreceive/udp_receiver_asio.h similarity index 51% rename from ecal/core/src/io/udp/udp_receiver_asio.h rename to ecal/core/src/io/udp/sendreceive/udp_receiver_asio.h index 4fcded03ce..91cc91dcd0 100644 --- a/ecal/core/src/io/udp/udp_receiver_asio.h +++ b/ecal/core/src/io/udp/sendreceive/udp_receiver_asio.h @@ -19,7 +19,7 @@ #pragma once -#include "io/udp/udp_receiver_base.h" +#include "udp_receiver.h" #ifdef _MSC_VER #pragma warning(push) @@ -30,27 +30,31 @@ #pragma warning(pop) #endif -namespace eCAL +namespace IO { - class CUDPReceiverAsio : public CUDPReceiverBase + namespace UDP { - public: - CUDPReceiverAsio(const SReceiverAttr& attr_); - - // this virtual function is called during construction/destruction, - // so, mark it as final to ensure that no derived classes override it. - bool AddMultiCastGroup(const char* ipaddr_) final override; - bool RemMultiCastGroup(const char* ipaddr_) override; - - size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) override; - - protected: - void RunIOContext(const asio::chrono::steady_clock::duration& timeout); - - bool m_created; - bool m_broadcast; - asio::io_context m_iocontext; - asio::ip::udp::socket m_socket; - asio::ip::udp::endpoint m_sender_endpoint; - }; -} \ No newline at end of file + class CUDPReceiverAsio : public CUDPReceiverImpl + { + public: + CUDPReceiverAsio(const SReceiverAttr& attr_); + ~CUDPReceiverAsio(); + + // this virtual function is called during construction/destruction, + // so, mark it as final to ensure that no derived classes override it. + bool AddMultiCastGroup(const char* ipaddr_) final; + bool RemMultiCastGroup(const char* ipaddr_) override; + + size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) override; + + protected: + void RunIOContext(const asio::chrono::steady_clock::duration& timeout); + + bool m_created; + bool m_broadcast; + asio::io_context m_iocontext; + asio::ip::udp::socket m_socket; + asio::ip::udp::endpoint m_sender_endpoint; + }; + } +} diff --git a/ecal/core/src/io/udp/sendreceive/udp_receiver_npcap.cpp b/ecal/core/src/io/udp/sendreceive/udp_receiver_npcap.cpp new file mode 100644 index 0000000000..1e6fe3147f --- /dev/null +++ b/ecal/core/src/io/udp/sendreceive/udp_receiver_npcap.cpp @@ -0,0 +1,130 @@ +/* ========================= 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 "udp_receiver_npcap.h" + +#include + +namespace IO +{ + namespace UDP + { + //////////////////////////////////////////////////////// + // Npcap based receiver class implementation + //////////////////////////////////////////////////////// + CUDPReceiverPcap::CUDPReceiverPcap(const SReceiverAttr& attr_) + : CUDPReceiverImpl(attr_) + , m_created(false) + , m_broadcast(attr_.broadcast) + { + // set receive buffer size (default = 1 MB) + int rcvbuf = 1024 * 1024; + if (attr_.rcvbuf > 0) + { + rcvbuf = attr_.rcvbuf; + } + if (!m_socket.setReceiveBufferSize(rcvbuf)) + { + std::cerr << "CUDPReceiverPcap: Unable to set receive buffer size." << std::endl; + } + + // bind socket + if (!m_socket.bind(Udpcap::HostAddress::Any(), static_cast(attr_.port))) + { + std::cerr << "CUDPReceiverPcap: Unable to bind socket." << std::endl; + return; + } + + // set loopback option + if (!m_broadcast) + { + m_socket.setMulticastLoopbackEnabled(attr_.loopback); + } + + // join multicast group + AddMultiCastGroup(attr_.address.c_str()); + + // state successful creation + m_created = true; + } + + CUDPReceiverPcap::~CUDPReceiverPcap() + { + // close socket + m_socket.close(); + + // state successful destruction + m_created = false; + } + + bool CUDPReceiverPcap::AddMultiCastGroup(const char* ipaddr_) + { + if (!m_broadcast) + { + // join multicast group + if (!m_socket.joinMulticastGroup(Udpcap::HostAddress(ipaddr_))) + { + std::cerr << "CUDPReceiverPcap: Unable to join multicast group." << std::endl; + return(false); + } + } + return(true); + } + + bool CUDPReceiverPcap::RemMultiCastGroup(const char* ipaddr_) + { + if (!m_broadcast) + { + // leave multicast group + if (!m_socket.leaveMulticastGroup(Udpcap::HostAddress(ipaddr_))) + { + std::cerr << "CUDPReceiverPcap: Unable to leave multicast group." << std::endl; + return(false); + } + } + return(true); + } + + size_t CUDPReceiverPcap::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ /* = nullptr */) + { + if (!m_created) return 0; + + size_t bytes_received; + if (address_) + { + Udpcap::HostAddress source_address; + uint16_t source_port; + bytes_received = m_socket.receiveDatagram(buf_, len_, static_cast(timeout_), &source_address, &source_port); + + if (bytes_received && source_address.isValid()) + { + address_->sin_addr.s_addr = source_address.toInt(); + address_->sin_family = AF_INET; + address_->sin_port = source_port; + memset(&(address_->sin_zero), 0, 8); + } + } + else + { + bytes_received = m_socket.receiveDatagram(buf_, len_, static_cast(timeout_)); + } + return bytes_received; + } + } +} diff --git a/ecal/core/src/io/udp/udp_receiver_npcap.h b/ecal/core/src/io/udp/sendreceive/udp_receiver_npcap.h similarity index 55% rename from ecal/core/src/io/udp/udp_receiver_npcap.h rename to ecal/core/src/io/udp/sendreceive/udp_receiver_npcap.h index c678dd1613..77278102f2 100644 --- a/ecal/core/src/io/udp/udp_receiver_npcap.h +++ b/ecal/core/src/io/udp/sendreceive/udp_receiver_npcap.h @@ -20,31 +20,34 @@ #pragma once -#include "io/udp/udp_receiver_base.h" - +#include "udp_receiver.h" #include "config/ecal_config_reader_hlp.h" + #include #include -namespace eCAL +namespace IO { - //////////////////////////////////////////////////////// - // Npcap based receiver class implementation - //////////////////////////////////////////////////////// - class CUDPReceiverPcap : public CUDPReceiverBase + namespace UDP { - public: - CUDPReceiverPcap(const SReceiverAttr& attr_); - - bool AddMultiCastGroup(const char* ipaddr_) override; - bool RemMultiCastGroup(const char* ipaddr_) override; - - size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) override; - - protected: - bool m_created; - bool m_broadcast; - Udpcap::UdpcapSocket m_socket; - }; - -} \ No newline at end of file + //////////////////////////////////////////////////////// + // Npcap based receiver class implementation + //////////////////////////////////////////////////////// + class CUDPReceiverPcap : public CUDPReceiverImpl + { + public: + CUDPReceiverPcap(const SReceiverAttr& attr_); + ~CUDPReceiverPcap(); + + bool AddMultiCastGroup(const char* ipaddr_) override; + bool RemMultiCastGroup(const char* ipaddr_) override; + + size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) override; + + protected: + bool m_created; + bool m_broadcast; + Udpcap::UdpcapSocket m_socket; + }; + } +} diff --git a/ecal/core/src/io/udp/sendreceive/udp_sender.cpp b/ecal/core/src/io/udp/sendreceive/udp_sender.cpp new file mode 100644 index 0000000000..5bca08c93d --- /dev/null +++ b/ecal/core/src/io/udp/sendreceive/udp_sender.cpp @@ -0,0 +1,132 @@ +/* ========================= 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 UDP sender class +**/ + +#include "udp_sender.h" + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4834) +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace IO +{ + namespace UDP + { + //////////////////////////////////////////////////////// + // udp sender class implementation + //////////////////////////////////////////////////////// + class CUDPSenderImpl + { + public: + CUDPSenderImpl(const SSenderAttr& attr_); + size_t Send(const void* buf_, size_t len_, const char* ipaddr_ = nullptr); + + protected: + bool m_broadcast; + asio::io_context m_iocontext; + asio::ip::udp::endpoint m_endpoint; + asio::ip::udp::socket m_socket; + unsigned short m_port; + }; + + CUDPSenderImpl::CUDPSenderImpl(const SSenderAttr& attr_) : + m_broadcast(attr_.broadcast), + m_endpoint(asio::ip::make_address(attr_.address), static_cast(attr_.port)), + m_socket(m_iocontext, m_endpoint.protocol()), + m_port(static_cast(attr_.port)) + { + if (m_broadcast) + { + // set unicast packet TTL + const asio::ip::unicast::hops ttl(attr_.ttl); + asio::error_code ec; + m_socket.set_option(ttl, ec); + if (ec) + std::cerr << "CUDPSender: Setting TTL failed: " << ec.message() << std::endl; + } + else + { + // set multicast packet TTL + { + const asio::ip::multicast::hops ttl(attr_.ttl); + asio::error_code ec; + m_socket.set_option(ttl, ec); + if (ec) + std::cerr << "CUDPSender: Setting TTL failed: " << ec.message() << std::endl; + } + + // set loopback option + { + const asio::ip::multicast::enable_loopback loopback(attr_.loopback); + asio::error_code ec; + m_socket.set_option(loopback, ec); + if (ec) + std::cerr << "CUDPSender: Error setting loopback option: " << ec.message() << std::endl; + } + } + + if (m_broadcast) + { + asio::error_code ec; + m_socket.set_option(asio::socket_base::broadcast(true), ec); + if (ec) + std::cerr << "CUDPSender: Setting broadcast mode failed: " << ec.message() << std::endl; + } + } + + size_t CUDPSenderImpl::Send(const void* buf_, const size_t len_, const char* ipaddr_) + { + const asio::socket_base::message_flags flags(0); + asio::error_code ec; + size_t sent(0); + if ((ipaddr_ != nullptr) && (ipaddr_[0] != '\0')) sent = m_socket.send_to(asio::buffer(buf_, len_), asio::ip::udp::endpoint(asio::ip::make_address(ipaddr_), m_port), flags, ec); + else sent = m_socket.send_to(asio::buffer(buf_, len_), m_endpoint, flags, ec); + if (ec) + { + std::cout << "CUDPSender::Send failed with: \'" << ec.message() << "\'" << std::endl; + return (0); + } + return(sent); + } + + //////////////////////////////////////////////////////// + // udp sender class + //////////////////////////////////////////////////////// + CUDPSender::CUDPSender(const SSenderAttr& attr_) + { + m_socket_impl = std::make_shared(attr_); + } + + size_t CUDPSender::Send(const void* buf_, const size_t len_, const char* ipaddr_) + { + if (!m_socket_impl) return(0); + return(m_socket_impl->Send(buf_, len_, ipaddr_)); + } + } +} diff --git a/ecal/core/src/io/udp/sendreceive/udp_sender.h b/ecal/core/src/io/udp/sendreceive/udp_sender.h new file mode 100644 index 0000000000..0d8d3eec48 --- /dev/null +++ b/ecal/core/src/io/udp/sendreceive/udp_sender.h @@ -0,0 +1,67 @@ +/* ========================= 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 UDP sender class +**/ + +#pragma once + +#include + +#ifdef ECAL_OS_WINDOWS +#include "win32/socket_os.h" +#endif + +#ifdef ECAL_OS_LINUX +#include +#include +#include +#endif + +#include +#include + +namespace IO +{ + namespace UDP + { + struct SSenderAttr + { + std::string address; + int port = 0; + int ttl = 0; + bool broadcast = false; + bool loopback = true; + int sndbuf = 1024 * 1024; + }; + + class CUDPSenderImpl; + + class CUDPSender + { + public: + CUDPSender(const SSenderAttr& attr_); + size_t Send(const void* buf_, size_t len_, const char* ipaddr_ = nullptr); + + protected: + std::shared_ptr m_socket_impl; + }; + } +} diff --git a/ecal/core/src/ecal_win_socket.h b/ecal/core/src/io/udp/sendreceive/win32/socket_os.h similarity index 100% rename from ecal/core/src/ecal_win_socket.h rename to ecal/core/src/io/udp/sendreceive/win32/socket_os.h diff --git a/ecal/core/src/io/udp/snd_raw_buffer.cpp b/ecal/core/src/io/udp/snd_raw_buffer.cpp deleted file mode 100644 index 2ab8c15e2f..0000000000 --- a/ecal/core/src/io/udp/snd_raw_buffer.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* ========================= 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 raw message buffer handling -**/ - -#include -#include -#include - -#include "ecal_process.h" - -#include "snd_raw_buffer.h" -#include "io/udp/msg_type.h" - -namespace -{ - // random number generator - unsigned long xorshf96(unsigned long& x, unsigned long& y, unsigned long& z) // period 2^96-1 - { - unsigned long t; - x ^= x << 16; - x ^= x >> 5; - x ^= x << 1; - - t = x; - x = y; - y = z; - z = t ^ x ^ y; - - return z; - } -} - -namespace eCAL -{ - size_t CreateSampleBuffer(const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, std::vector& payload_) - { - const unsigned short sample_name_size = (unsigned short)sample_name_.size() + 1; -#if GOOGLE_PROTOBUF_VERSION >= 3001000 - const size_t sample_size = ecal_sample_.ByteSizeLong(); -#else - size_t sample_size = ecal_sample_.ByteSize(); -#endif - const size_t data_size = sizeof(sample_name_size) + sample_name_size + sample_size; - - // create payload buffer with reserved space for first message head - payload_.resize(data_size + sizeof(struct SUDPMessageHead)); - char* payload_data = payload_.data() + sizeof(struct SUDPMessageHead); - - // write topic name size - ((unsigned short*)payload_data)[0] = sample_name_size; - // write topic name - memcpy(payload_data + sizeof(sample_name_size), sample_name_.c_str(), sample_name_size); - - // write payload - if (ecal_sample_.SerializeWithCachedSizesToArray((google::protobuf::uint8*)payload_data + sizeof(sample_name_size) + sample_name_size)) - { - return data_size; - } - - return (0); - } - - size_t SendSampleBuffer(char* buf_, size_t buf_len_, long bandwidth_, TransmitCallbackT transmit_cb_) - { - if (buf_ == nullptr) return(0); - - size_t sent(0); - size_t sent_sum(0); - - int32_t total_packet_num = int32_t(buf_len_ / MSG_PAYLOAD_SIZE); - if (buf_len_%MSG_PAYLOAD_SIZE) total_packet_num++; - - // create message header - struct SUDPMessageHead msg_header; - - switch (total_packet_num) - { - case 1: - { - // create start packet - msg_header.type = msg_type_header_with_content; - msg_header.id = -1; // not needed for combined header / data message - msg_header.num = 1; - msg_header.len = int32_t(buf_len_); - - // copy msg_header in send buffer - memcpy(buf_, &msg_header, sizeof(struct SUDPMessageHead)); - - // send single header + data package - sent = transmit_cb_(buf_, sizeof(struct SUDPMessageHead) + buf_len_); - if (sent == 0) return(sent); - sent_sum += sent; - -#ifndef NDEBUG - // log it - //std::cout << "SendRawBuffer Packet Sent - HEADER_WITH_CONTENT (" + std::to_string(sent) + " Bytes)" << std::endl; -#endif - } - break; - default: - { - // calculate bandwidth timing parameter - long long send_sleep_us(0); - if (bandwidth_ > 0) - { - send_sleep_us = MSG_BUFFER_SIZE; - send_sleep_us *= 1000 * 1000; - send_sleep_us /= bandwidth_; - } - - // create start package - msg_header.type = msg_type_header; - { - // create random number for message id - { - static std::mutex xorshf96_mtx; - const std::lock_guard lock(xorshf96_mtx); - - static unsigned long x = static_cast(std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()).count() - ); - static unsigned long y = 362436069; - static unsigned long z = 521288629; - - msg_header.id = xorshf96(x, y, z); - } - } - msg_header.num = total_packet_num; - msg_header.len = int32_t(buf_len_); - - // send start package - sent = transmit_cb_(&msg_header, sizeof(struct SUDPMessageHead)); - if (sent == 0) return(sent); - sent_sum += sent; - -#ifndef NDEBUG - // log it - //std::cout << "SendRawBuffer Packet Sent - HEADER (" + std::to_string(sent) + " Bytes)" << std::endl; -#endif - - // send data packages - msg_header.type = msg_type_content; - for (int32_t current_packet_num = 0; current_packet_num < total_packet_num; current_packet_num++) - { - // calculate current payload - size_t current_snd_len = buf_len_; - if (current_snd_len > MSG_PAYLOAD_SIZE) current_snd_len = MSG_PAYLOAD_SIZE; - - if (current_snd_len > 0) - { - // reduce total send len - buf_len_ -= current_snd_len; - - // create data packet numbering - msg_header.num = current_packet_num; - msg_header.len = int32_t(current_snd_len); - - // copy msg_header in send buffer - memcpy(buf_ + static_cast(current_packet_num)*MSG_PAYLOAD_SIZE, &msg_header, sizeof(struct SUDPMessageHead)); - - // send data package - sent = transmit_cb_(buf_ + static_cast(current_packet_num)*MSG_PAYLOAD_SIZE, sizeof(struct SUDPMessageHead) + current_snd_len); - if (sent == 0) return(sent); - if (send_sleep_us != 0) - eCAL::Process::SleepFor(std::chrono::microseconds(send_sleep_us)); - -#ifndef NDEBUG - // log it - //std::cout << "SendRawBuffer Packet Sent - CONTENT (" + std::to_string(sent) + " Bytes)" << std::endl; -#endif - sent_sum += sent; - } - } - } - break; - } - - return(sent_sum); - } -} diff --git a/ecal/core/src/io/udp/snd_sample.cpp b/ecal/core/src/io/udp/snd_sample.cpp deleted file mode 100644 index 2a71e0af36..0000000000 --- a/ecal/core/src/io/udp/snd_sample.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* ========================= 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 Sender thread for ecal samples -**/ - -#include - -#include "snd_sample.h" -#include "snd_raw_buffer.h" - -namespace -{ - size_t TransmitToUDP(const void* buf_, const size_t len_, const std::shared_ptr& sample_sender_, const std::string& mcast_address_) - { - return (sample_sender_->Send(buf_, len_, mcast_address_.c_str())); - } -} - -namespace eCAL -{ - CSampleSender::CSampleSender(const SSenderAttr& attr_) - { - m_udp_sender = std::make_shared(attr_); - } - - size_t CSampleSender::SendSample(const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, long bandwidth_) - { - if (!m_udp_sender) return(0); - - // return value - size_t sent_sum(0); - - const size_t data_size = CreateSampleBuffer(sample_name_, ecal_sample_, m_payload); - if (data_size > 0) - { - // and send it - sent_sum = SendSampleBuffer(m_payload.data(), data_size, bandwidth_, std::bind(TransmitToUDP, std::placeholders::_1, std::placeholders::_2, m_udp_sender, m_attr.address)); - -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug4, "UDP Sample Buffer Sent (" + std::to_string(sent_sum) + " Bytes)"); -#endif - } - - // return bytes sent - return(sent_sum); - } -} diff --git a/ecal/core/src/io/udp/udp_init.cpp b/ecal/core/src/io/udp/udp_init.cpp deleted file mode 100644 index 1636cd86b2..0000000000 --- a/ecal/core/src/io/udp/udp_init.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* ========================= 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 UDP initialization -**/ - -#include - -#include -#include - -#ifdef ECAL_OS_WINDOWS -#include "ecal_win_socket.h" -#endif /* ECAL_OS_WINDOWS */ - -static std::atomic g_socket_init_refcnt(0); - -namespace eCAL -{ - namespace Net - { - int Initialize() - { - g_socket_init_refcnt++; - if(g_socket_init_refcnt == 1) - { -#ifdef ECAL_OS_WINDOWS - const WORD wVersionRequested = MAKEWORD(2, 2); - - WSADATA wsaData; - const int err = WSAStartup(wVersionRequested, &wsaData); - if (err != 0) - { - /* Tell the user that we could not find a usable */ - /* Winsock DLL. */ - printf("WSAStartup failed with error: %d\n", err); - return(-1); - } - - /* Confirm that the WinSock DLL supports 2.2.*/ - /* Note that if the DLL supports versions greater */ - /* than 2.2 in addition to 2.2, it will still return */ - /* 2.2 in wVersion since that is the version we */ - /* requested. */ - - if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) - { - /* Tell the user that we could not find a usable */ - /* WinSock DLL. */ - printf("Could not find a usable version of Winsock.dll\n"); - WSACleanup(); - } -#endif /* ECAL_OS_WINDOWS */ - } - return(0); - } - - int Finalize() - { - if(g_socket_init_refcnt == 0) return(0); - - g_socket_init_refcnt--; - if(g_socket_init_refcnt == 0) - { -#ifdef ECAL_OS_WINDOWS - WSACleanup(); -#endif /* ECAL_OS_WINDOWS */ - } - - return(0); - } - } -} diff --git a/ecal/core/src/io/udp/udp_receiver.cpp b/ecal/core/src/io/udp/udp_receiver.cpp deleted file mode 100644 index 0ddf70ab4f..0000000000 --- a/ecal/core/src/io/udp/udp_receiver.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* ========================= 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 UDP receiver class -**/ - -#include - -#include "udp_receiver.h" - -#include - -#include "io/udp/udp_receiver_base.h" -#include "io/udp/udp_receiver_asio.h" -#ifdef ECAL_NPCAP_SUPPORT -#include "io/udp/udp_receiver_npcap.h" -#endif - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // udp receiver class - //////////////////////////////////////////////////////// - CUDPReceiver::CUDPReceiver() - : CReceiver(CReceiver::SType_ReceiverUDP) - , m_use_npcap(false) - { -#ifdef ECAL_NPCAP_SUPPORT - if (Config::IsNpcapEnabled()) - { - m_use_npcap = Udpcap::Initialize(); // Only use NPCAP if we can initialize it (or it has already been initialized successfully) - if (!m_use_npcap) - { - std::cerr << "Npcap is enabled, but cannot be initialized. Using socket fallback mode." << std::endl; - } - } -#endif //ECAL_NPCAP_SUPPORT - } - - bool CUDPReceiver::Create(const SReceiverAttr& attr_) - { - if (m_socket_impl) return false; - -#ifdef ECAL_NPCAP_SUPPORT - if (m_use_npcap) - { - m_socket_impl = std::make_shared(attr_); - return true; - } -#endif // ECAL_NPCAP_SUPPORT - - m_socket_impl = std::make_shared(attr_); - return(true); - } - - bool CUDPReceiver::Destroy() - { - if (!m_socket_impl) return(false); - m_socket_impl.reset(); - return(true); - } - - bool CUDPReceiver::AddMultiCastGroup(const char* ipaddr_) - { - if (!m_socket_impl) return(false); - return(m_socket_impl->AddMultiCastGroup(ipaddr_)); - } - - bool CUDPReceiver::RemMultiCastGroup(const char* ipaddr_) - { - if (!m_socket_impl) return(false); - return(m_socket_impl->RemMultiCastGroup(ipaddr_)); - } - - size_t CUDPReceiver::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ /* = nullptr */) - { - if (!m_socket_impl) return(0); - return(m_socket_impl->Receive(buf_, len_, timeout_, address_)); - } -} diff --git a/ecal/core/src/io/udp/udp_receiver_asio.cpp b/ecal/core/src/io/udp/udp_receiver_asio.cpp deleted file mode 100644 index 2e1c22bee7..0000000000 --- a/ecal/core/src/io/udp/udp_receiver_asio.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/* ========================= 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 "io/udp/udp_receiver_asio.h" - -#ifdef __linux__ -#include "linux/ecal_socket_option_linux.h" -#endif - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // Default ASIO based receiver class implementation - //////////////////////////////////////////////////////// - CUDPReceiverAsio::CUDPReceiverAsio(const SReceiverAttr& attr_) : - CUDPReceiverBase(attr_), - m_created(false), - m_broadcast(attr_.broadcast), - m_socket(m_iocontext) - { - // create socket - const asio::ip::udp::endpoint listen_endpoint(asio::ip::udp::v4(), static_cast(attr_.port)); - { - asio::error_code ec; - m_socket.open(listen_endpoint.protocol(), ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to open socket: " << ec.message() << std::endl; - return; - } - } - - // set socket reuse - { - asio::error_code ec; - m_socket.set_option(asio::ip::udp::socket::reuse_address(true), ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to set reuse-address option: " << ec.message() << std::endl; - } - } - - // bind socket - { - asio::error_code ec; - m_socket.bind(listen_endpoint, ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to bind socket to " << listen_endpoint.address().to_string() << ":" << listen_endpoint.port() << ": " << ec.message() << std::endl; - return; - } - } - - // set loopback option - { - const asio::ip::multicast::enable_loopback loopback(attr_.loopback); - asio::error_code ec; - m_socket.set_option(loopback, ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to enable loopback: " << ec.message() << std::endl; - } - } - - // set receive buffer size (default = 1 MB) - { - int rcvbuf = 1024 * 1024; - if (attr_.rcvbuf > 0) rcvbuf = attr_.rcvbuf; - const asio::socket_base::receive_buffer_size recbufsize(rcvbuf); - asio::error_code ec; - m_socket.set_option(recbufsize, ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to set receive buffer size: " << ec.message() << std::endl; - } - } - - // join multicast group - AddMultiCastGroup(attr_.address.c_str()); - - // state successful creation - m_created = true; - } - - bool CUDPReceiverAsio::AddMultiCastGroup(const char* ipaddr_) - { - if (!m_broadcast) - { - // join multicast group -#ifdef __linux__ - if (eCAL::Config::IsUdpMulticastJoinAllIfEnabled()) - { - if (!set_socket_mcast_group_option(m_socket.native_handle(), ipaddr_, MCAST_JOIN_GROUP)) - { - return(false); - } - } - else -#endif - { - asio::error_code ec; - m_socket.set_option(asio::ip::multicast::join_group(asio::ip::make_address(ipaddr_)), ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to join multicast group: " << ec.message() << std::endl; - return(false); - } - } - -#ifdef ECAL_JOIN_MULTICAST_TWICE - // this is a very bad workaround because of an identified bug on a specific embedded device - // we join the multicast group multiple times otherwise the socket will not receive any data - std::cerr << "eCAL was compiled with ECAL_JOIN_MULTICAST_TWICE" << std::endl; - m_socket.set_option(asio::ip::multicast::leave_group(asio::ip::make_address(ipaddr_))); - m_socket.set_option(asio::ip::multicast::join_group(asio::ip::make_address(ipaddr_))); -#endif // ECAL_JOIN_MULTICAST_TWICE - } - return(true); - } - - bool CUDPReceiverAsio::RemMultiCastGroup(const char* ipaddr_) - { - if (!m_broadcast) - { - // Leave multicast group -#ifdef __linux__ - if (eCAL::Config::IsUdpMulticastJoinAllIfEnabled()) - { - if (!set_socket_mcast_group_option(m_socket.native_handle(), ipaddr_, MCAST_LEAVE_GROUP)) - { - return(false); - } - } - else -#endif - { - asio::error_code ec; - m_socket.set_option(asio::ip::multicast::leave_group(asio::ip::make_address(ipaddr_)), ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to leave multicast group: " << ec.message() << std::endl; - return(false); - } - } - } - return(true); - } - - size_t CUDPReceiverAsio::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ /* = nullptr */) - { - if (!m_created) return 0; - - size_t reclen(0); - m_socket.async_receive_from(asio::buffer(buf_, len_), m_sender_endpoint, - [&reclen](std::error_code ec, std::size_t length) - { - if (!ec) - { - reclen = length; - } - }); - - // run for timeout ms - RunIOContext(asio::chrono::milliseconds(timeout_)); - - // retrieve underlaying raw socket informations - if (address_ != nullptr) - { - if (m_sender_endpoint.address().is_v4()) - { - asio::detail::sockaddr_in4_type* in4 = reinterpret_cast(m_sender_endpoint.data()); - address_->sin_addr = in4->sin_addr; - address_->sin_family = in4->sin_family; - address_->sin_port = in4->sin_port; - memset(&(address_->sin_zero), 0, 8); - } - else - { - std::cout << "CUDPReceiverAsio: ipv4 address conversion failed." << std::endl; - } - } - - return (reclen); - } - - void CUDPReceiverAsio::RunIOContext(const asio::chrono::steady_clock::duration& timeout) - { - // restart the io_context, as it may have been left in the "stopped" state by a previous operation - m_iocontext.restart(); - - // block until the asynchronous operation has completed, or timed out - m_iocontext.run_for(timeout); - - // stop the context if even the operation was not successful completed - if (!m_iocontext.stopped()) - { - // cancel the outstanding asynchronous operation - m_socket.cancel(); - - // run the io_context again until the operation completes - m_iocontext.run(); - } - } -} diff --git a/ecal/core/src/io/udp/udp_receiver_base.h b/ecal/core/src/io/udp/udp_receiver_base.h deleted file mode 100644 index 5cd7fc9d17..0000000000 --- a/ecal/core/src/io/udp/udp_receiver_base.h +++ /dev/null @@ -1,46 +0,0 @@ -/* ========================= 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 ================================= -*/ - -#pragma once - -#include "io/udp/udp_receiver.h" - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // udp receiver class implementation - //////////////////////////////////////////////////////// - class CUDPReceiverBase - { - public: - CUDPReceiverBase(const SReceiverAttr& /*attr_*/) {}; - // We don't technically need a virtual destructor, if we are working with shared_ptrs... - virtual ~CUDPReceiverBase() = default; - // Delete copy / move operations to prevent slicing - CUDPReceiverBase(CUDPReceiverBase&&) = delete; - CUDPReceiverBase& operator=(CUDPReceiverBase&&) = delete; - CUDPReceiverBase(const CUDPReceiverBase&) = delete; - CUDPReceiverBase& operator=(const CUDPReceiverBase&) = delete; - - virtual bool AddMultiCastGroup(const char* ipaddr_) = 0; - virtual bool RemMultiCastGroup(const char* ipaddr_) = 0; - - virtual size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) = 0; - }; -} \ No newline at end of file diff --git a/ecal/core/src/io/udp/udp_receiver_npcap.cpp b/ecal/core/src/io/udp/udp_receiver_npcap.cpp deleted file mode 100644 index a697ff7e55..0000000000 --- a/ecal/core/src/io/udp/udp_receiver_npcap.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* ========================= 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 "io/udp/udp_receiver_npcap.h" - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // Npcap based receiver class implementation - //////////////////////////////////////////////////////// - CUDPReceiverPcap::CUDPReceiverPcap(const SReceiverAttr& attr_) - : CUDPReceiverBase(attr_) - , m_created(false) - , m_broadcast(attr_.broadcast) - { - // set receive buffer size (default = 1 MB) - int rcvbuf = 1024 * 1024; - if (attr_.rcvbuf > 0) - { - rcvbuf = attr_.rcvbuf; - } - if (!m_socket.setReceiveBufferSize(rcvbuf)) - { - std::cerr << "CUDPReceiverPcap: Unable to set receive buffer size." << std::endl; - } - - // bind socket - if (!m_socket.bind(Udpcap::HostAddress::Any(), static_cast(attr_.port))) - { - std::cerr << "CUDPReceiverPcap: Unable to bind socket." << std::endl; - return; - } - - // set loopback option - if (!m_broadcast) - { - m_socket.setMulticastLoopbackEnabled(attr_.loopback); - } - - // join multicast group - AddMultiCastGroup(attr_.address.c_str()); - - // state successful creation - m_created = true; - } - - bool CUDPReceiverPcap::AddMultiCastGroup(const char* ipaddr_) - { - if (!m_broadcast) - { - // join multicast group - if (!m_socket.joinMulticastGroup(Udpcap::HostAddress(ipaddr_))) - { - std::cerr << "CUDPReceiverPcap: Unable to join multicast group." << std::endl; - return(false); - } - } - return(true); - } - - bool CUDPReceiverPcap::RemMultiCastGroup(const char* ipaddr_) - { - if (!m_broadcast) - { - // leave multicast group - if (!m_socket.leaveMulticastGroup(Udpcap::HostAddress(ipaddr_))) - { - std::cerr << "CUDPReceiverPcap: Unable to leave multicast group." << std::endl; - return(false); - } - } - return(true); - } - - size_t CUDPReceiverPcap::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ /* = nullptr */) - { - if (!m_created) return 0; - - size_t bytes_received; - if (address_) - { - Udpcap::HostAddress source_address; - uint16_t source_port; - bytes_received = m_socket.receiveDatagram(buf_, len_, static_cast(timeout_), &source_address, &source_port); - - if (bytes_received && source_address.isValid()) - { - address_->sin_addr.s_addr = source_address.toInt(); - address_->sin_family = AF_INET; - address_->sin_port = source_port; - memset(&(address_->sin_zero), 0, 8); - } - } - else - { - bytes_received = m_socket.receiveDatagram(buf_, len_, static_cast(timeout_)); - } - return bytes_received; - } -} diff --git a/ecal/core/src/io/udp/udp_sender.cpp b/ecal/core/src/io/udp/udp_sender.cpp deleted file mode 100644 index 6a89a0f7d5..0000000000 --- a/ecal/core/src/io/udp/udp_sender.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* ========================= 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 UDP sender class -**/ - -#include -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4834) -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "udp_sender.h" - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // udp sender class implementation - //////////////////////////////////////////////////////// - class CUDPSenderImpl - { - public: - CUDPSenderImpl(const SSenderAttr& attr_); - size_t Send(const void* buf_, size_t len_, const char* ipaddr_ = nullptr); - - protected: - bool m_broadcast; - asio::io_context m_iocontext; - asio::ip::udp::endpoint m_endpoint; - asio::ip::udp::socket m_socket; - unsigned short m_port; - }; - - CUDPSenderImpl::CUDPSenderImpl(const SSenderAttr& attr_) : - m_broadcast(attr_.broadcast), - m_endpoint(asio::ip::make_address(attr_.address), static_cast(attr_.port)), - m_socket(m_iocontext, m_endpoint.protocol()), - m_port(static_cast(attr_.port)) - { - if (m_broadcast) - { - // set unicast packet TTL - const asio::ip::unicast::hops ttl(attr_.ttl); - asio::error_code ec; - m_socket.set_option(ttl, ec); - if (ec) - std::cerr << "CUDPSender: Setting TTL failed: " << ec.message() << std::endl; - } - else - { - // set multicast packet TTL - { - const asio::ip::multicast::hops ttl(attr_.ttl); - asio::error_code ec; - m_socket.set_option(ttl, ec); - if (ec) - std::cerr << "CUDPSender: Setting TTL failed: " << ec.message() << std::endl; - } - - // set loopback option - { - const asio::ip::multicast::enable_loopback loopback(attr_.loopback); - asio::error_code ec; - m_socket.set_option(loopback, ec); - if (ec) - std::cerr << "CUDPSender: Error setting loopback option: " << ec.message() << std::endl; - } - } - - if (m_broadcast) - { - asio::error_code ec; - m_socket.set_option(asio::socket_base::broadcast(true), ec); - if (ec) - std::cerr << "CUDPSender: Setting broadcast mode failed: " << ec.message() << std::endl; - } - } - - size_t CUDPSenderImpl::Send(const void* buf_, const size_t len_, const char* ipaddr_) - { - const asio::socket_base::message_flags flags(0); - asio::error_code ec; - size_t sent(0); - if ((ipaddr_ != nullptr) && (ipaddr_[0] != '\0')) sent = m_socket.send_to(asio::buffer(buf_, len_), asio::ip::udp::endpoint(asio::ip::make_address(ipaddr_), m_port), flags, ec); - else sent = m_socket.send_to(asio::buffer(buf_, len_), m_endpoint, flags, ec); - if (ec) - { - std::cout << "CUDPSender::Send failed with: \'" << ec.message() << "\'" << std::endl; - return (0); - } - return(sent); - } - - //////////////////////////////////////////////////////// - // udp sender class - //////////////////////////////////////////////////////// - CUDPSender::CUDPSender(const SSenderAttr& attr_) - { - m_socket_impl = std::make_shared(attr_); - } - - size_t CUDPSender::Send(const void* buf_, const size_t len_, const char* ipaddr_) - { - if (!m_socket_impl) return(0); - return(m_socket_impl->Send(buf_, len_, ipaddr_)); - } -} diff --git a/ecal/core/src/logging/ecal_log_impl.cpp b/ecal/core/src/logging/ecal_log_impl.cpp index a0e7944fac..2ef0f14cc1 100644 --- a/ecal/core/src/logging/ecal_log_impl.cpp +++ b/ecal/core/src/logging/ecal_log_impl.cpp @@ -26,7 +26,7 @@ #include #include "ecal_log_impl.h" -#include "io/udp/udp_configurations.h" +#include "io/udp/ecal_udp_configurations.h" #include #include @@ -145,21 +145,33 @@ namespace eCAL m_logfile = fopen(m_logfile_name.c_str(), "w"); } - // set network attributes if(m_filter_mask_udp != 0) { - SSenderAttr attr; + // set logging send network attributes + IO::UDP::SSenderAttr attr; attr.address = UDP::GetLoggingAddress(); attr.port = UDP::GetLoggingPort(); attr.ttl = UDP::GetMulticastTtl(); - attr.broadcast = !Config::IsNetworkEnabled(); + attr.broadcast = UDP::IsBroadcast(); attr.loopback = true; attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); // create udp logging sender - m_udp_sender = std::make_unique(attr); + m_udp_logging_sender = std::make_unique(attr); } + // set logging receive network attributes + IO::UDP::SReceiverAttr attr; + attr.address = UDP::GetLoggingAddress(); + attr.port = UDP::GetLoggingPort(); + attr.broadcast = UDP::IsBroadcast(); + attr.loopback = true; + attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); + + // start logging receiver + const UDP::CLoggingReceiver::LogMessageCallbackT log_message_callback = std::bind(&CLog::RegisterLogMessage, this, std::placeholders::_1); + m_log_receiver = std::make_shared(attr, log_message_callback); + m_created = true; } @@ -169,7 +181,7 @@ namespace eCAL const std::lock_guard lock(m_log_sync); - m_udp_sender.reset(); + m_udp_logging_sender.reset(); if(m_logfile != nullptr) fclose(m_logfile); m_logfile = nullptr; @@ -259,8 +271,9 @@ namespace eCAL fflush(m_logfile); } - if((log_udp != 0) && m_udp_sender) + if((log_udp != 0) && m_udp_logging_sender) { + // set up log message ecal_msg.Clear(); ecal_msg.set_time(std::chrono::duration_cast(log_time.time_since_epoch()).count()); ecal_msg.set_hname(m_hname); @@ -270,11 +283,8 @@ namespace eCAL ecal_msg.set_level(level_); ecal_msg.set_content(msg_); - ecal_msg_s = ecal_msg.SerializeAsString(); - if(!ecal_msg_s.empty()) - { - m_udp_sender->Send((void*)ecal_msg_s.data(), ecal_msg_s.size()); - } + // sent it + m_udp_logging_sender->Send(ecal_msg); } } @@ -310,4 +320,39 @@ namespace eCAL return(m_core_time); } + + void CLog::GetLogging(eCAL::pb::Logging& logging_) + { + // clear protobuf object + logging_.Clear(); + + // acquire access + const std::lock_guard lock(m_log_msglist_sync); + + LogMessageListT::const_iterator siter = m_log_msglist.begin(); + while (siter != m_log_msglist.end()) + { + // add log message + eCAL::pb::LogMessage* pMonLogMessage = logging_.add_logs(); + + // copy content + pMonLogMessage->CopyFrom(*siter); + + ++siter; + } + + // empty message list + m_log_msglist.clear(); + } + + void CLog::RegisterLogMessage(const eCAL::pb::LogMessage& log_msg_) + { + // in "network mode" we accept all log messages + // in "local mode" we accept log messages from this host only + if ((m_hname == log_msg_.hname()) || Config::IsNetworkEnabled()) + { + const std::lock_guard lock(m_log_msglist_sync); + m_log_msglist.emplace_back(log_msg_); + } + } } diff --git a/ecal/core/src/logging/ecal_log_impl.h b/ecal/core/src/logging/ecal_log_impl.h index 44580d022c..75c7fb65c7 100644 --- a/ecal/core/src/logging/ecal_log_impl.h +++ b/ecal/core/src/logging/ecal_log_impl.h @@ -23,7 +23,8 @@ #pragma once -#include "io/udp/udp_sender.h" +#include "io/udp/ecal_udp_logging_receiver.h" +#include "io/udp/ecal_udp_logging_sender.h" #include #include @@ -33,6 +34,7 @@ #include "ecal_global_accessors.h" #include +#include #include #include #include @@ -115,31 +117,41 @@ namespace eCAL **/ std::chrono::duration GetCoreTime(); + void GetLogging(eCAL::pb::Logging& logging_); + private: - char ParseLogLevel(const std::string& filter_); + void RegisterLogMessage(const eCAL::pb::LogMessage& log_msg_); CLog(const CLog&); // prevent copy-construction CLog& operator=(const CLog&); // prevent assignment - std::mutex m_log_sync; + std::mutex m_log_sync; + + std::atomic m_created; + std::unique_ptr m_udp_logging_sender; + + // log messages + using LogMessageListT = std::list; + std::mutex m_log_msglist_sync; + LogMessageListT m_log_msglist; - std::atomic m_created; - std::unique_ptr m_udp_sender; + // udp logging receiver + std::shared_ptr m_log_receiver; - std::string m_hname; - int m_pid; - std::string m_pname; + std::string m_hname; + int m_pid; + std::string m_pname; - std::string m_logfile_name; - FILE* m_logfile; + std::string m_logfile_name; + FILE* m_logfile; - eCAL_Logging_eLogLevel m_level; - eCAL_Logging_Filter m_filter_mask_con; - eCAL_Logging_Filter m_filter_mask_file; - eCAL_Logging_Filter m_filter_mask_udp; + eCAL_Logging_eLogLevel m_level; + eCAL_Logging_Filter m_filter_mask_con; + eCAL_Logging_Filter m_filter_mask_file; + eCAL_Logging_Filter m_filter_mask_udp; - std::chrono::duration m_core_time; + std::chrono::duration m_core_time; - std::chrono::steady_clock::time_point m_core_time_start; + std::chrono::steady_clock::time_point m_core_time_start; }; } diff --git a/ecal/core/src/monitoring/ecal_monitoring_def.cpp b/ecal/core/src/monitoring/ecal_monitoring_def.cpp index ba9a8c4114..e6adcab65c 100644 --- a/ecal/core/src/monitoring/ecal_monitoring_def.cpp +++ b/ecal/core/src/monitoring/ecal_monitoring_def.cpp @@ -23,6 +23,7 @@ #include "ecal_monitoring_def.h" #include "ecal_monitoring_impl.h" +#include "logging/ecal_log_impl.h" #include "ecal_global_accessors.h" namespace eCAL @@ -73,27 +74,6 @@ namespace eCAL m_monitoring_impl->GetMonitoringStructs(monitoring_, entities_); } - void CMonitoring::GetLogging(eCAL::pb::Logging& logging_) - { - m_monitoring_impl->GetLogging(logging_); - } - - int CMonitoring::PubMonitoring(bool state_, std::string& name_) - { - return(m_monitoring_impl->PubMonitoring(state_, name_)); - } - - int CMonitoring::PubLogging(bool state_, std::string& name_) - { - return(m_monitoring_impl->PubLogging(state_, name_)); - } - - bool CMonitoring::ApplySample(const eCAL::pb::Sample & ecal_sample_) - { - if(m_monitoring_impl != nullptr) return m_monitoring_impl->ApplySample(ecal_sample_, eCAL::pb::eTLayerType::tl_none); - return false; - } - namespace Monitoring { //////////////////////////////////////////////////////// @@ -148,22 +128,22 @@ namespace eCAL int GetLogging(std::string& log_) { eCAL::pb::Logging logging; - if (g_monitoring() != nullptr) g_monitoring()->GetLogging(logging); + if (g_log() != nullptr) g_log()->GetLogging(logging); log_ = logging.SerializeAsString(); return((int)log_.size()); } - int PubMonitoring(bool state_, std::string name_ /* = "ecal.monitoring"*/) + int PubMonitoring(bool /*state_*/, std::string /*name_*/) { - if (g_monitoring() != nullptr) return(g_monitoring()->PubMonitoring(state_, name_)); - return -1; + // TODO: Remove this function from the API + return 0; } - int PubLogging(bool state_, std::string name_ /* = "ecal.logging"*/) + int PubLogging(bool /*state_*/, std::string /*name_*/) { - if (g_monitoring() != nullptr) return(g_monitoring()->PubLogging(state_, name_)); - return -1; + // TODO: Remove this function from the API + return 0; } } } diff --git a/ecal/core/src/monitoring/ecal_monitoring_def.h b/ecal/core/src/monitoring/ecal_monitoring_def.h index 047d962744..0f59f70849 100644 --- a/ecal/core/src/monitoring/ecal_monitoring_def.h +++ b/ecal/core/src/monitoring/ecal_monitoring_def.h @@ -56,12 +56,6 @@ namespace eCAL void GetMonitoring(eCAL::pb::Monitoring& monitoring_, unsigned int entities_ = Monitoring::Entity::All); void GetMonitoring(eCAL::Monitoring::SMonitoring& monitoring_, unsigned int entities_ = Monitoring::Entity::All); - void GetLogging(eCAL::pb::Logging& logging_); - - int PubMonitoring(bool state_, std::string& name_); - int PubLogging(bool state_, std::string& name_); - - bool ApplySample(const eCAL::pb::Sample& ecal_sample_); protected: CMonitoringImpl* m_monitoring_impl = nullptr; diff --git a/ecal/core/src/monitoring/ecal_monitoring_impl.cpp b/ecal/core/src/monitoring/ecal_monitoring_impl.cpp index addaff80fa..1e46f5dcab 100644 --- a/ecal/core/src/monitoring/ecal_monitoring_impl.cpp +++ b/ecal/core/src/monitoring/ecal_monitoring_impl.cpp @@ -24,6 +24,7 @@ #include #include +#include "io/udp/ecal_udp_configurations.h" #include "config/ecal_config_reader_hlp.h" #include "ecal_monitoring_impl.h" @@ -57,17 +58,6 @@ namespace eCAL // utilize registration receiver to enrich monitor information g_registration_receiver()->SetCustomApplySampleCallback([this](const auto& ecal_sample_){this->ApplySample(ecal_sample_, eCAL::pb::tl_none);}); - // start logging receive thread - const CLoggingReceiveThread::LogMessageCallbackT logmsg_cb = std::bind(&CMonitoringImpl::RegisterLogMessage, this, std::placeholders::_1); - m_log_rcv_threadcaller = std::make_shared(logmsg_cb); - m_log_rcv_threadcaller->SetNetworkMode(Config::IsNetworkEnabled()); - - // start monitoring and logging publishing thread - // we really need to remove this feature ! - const CMonLogPublishingThread::MonitoringCallbackT mon_cb = std::bind(&CMonitoringImpl::GetMonitoringPb, this, std::placeholders::_1, Monitoring::Entity::All); - const CMonLogPublishingThread::LoggingCallbackT log_cb = std::bind(&CMonitoringImpl::GetLogging, this, std::placeholders::_1); - m_pub_threadcaller = std::make_shared(mon_cb, log_cb); - // setup blacklist and whitelist filter strings# m_topic_filter_excl_s = Config::GetMonitoringFilterExcludeList(); m_topic_filter_incl_s = Config::GetMonitoringFilterIncludeList(); @@ -542,12 +532,6 @@ namespace eCAL return(true); } - void CMonitoringImpl::RegisterLogMessage(const eCAL::pb::LogMessage& log_msg_) - { - const std::lock_guard lock(m_log_msglist_sync); - m_log_msglist.emplace_back(log_msg_); - } - CMonitoringImpl::STopicMonMap* CMonitoringImpl::GetMap(enum ePubSub pubsub_type_) { STopicMonMap* pHostMap = nullptr; @@ -692,44 +676,6 @@ namespace eCAL } } - void CMonitoringImpl::GetLogging(eCAL::pb::Logging& logging_) - { - // clear protobuf object - logging_.Clear(); - - // acquire access - const std::lock_guard lock(m_log_msglist_sync); - - LogMessageListT::const_iterator siter = m_log_msglist.begin(); - while (siter != m_log_msglist.end()) - { - // add log message - eCAL::pb::LogMessage* pMonLogMessage = logging_.add_logs(); - - // copy content - pMonLogMessage->CopyFrom(*siter); - - ++siter; - } - - // empty message list - m_log_msglist.clear(); - } - - int CMonitoringImpl::PubMonitoring(bool state_, std::string & name_) - { - // (de)activate monitor publisher - m_pub_threadcaller->SetMonState(state_, name_); - return 0; - } - - int CMonitoringImpl::PubLogging(bool state_, std::string & name_) - { - // (de)activate logging publisher - m_pub_threadcaller->SetLogState(state_, name_); - return 0; - } - void CMonitoringImpl::MonitorProcs(eCAL::pb::Monitoring& monitoring_) { // acquire access diff --git a/ecal/core/src/monitoring/ecal_monitoring_impl.h b/ecal/core/src/monitoring/ecal_monitoring_impl.h index 8377b35c54..cb8dac3e5d 100644 --- a/ecal/core/src/monitoring/ecal_monitoring_impl.h +++ b/ecal/core/src/monitoring/ecal_monitoring_impl.h @@ -25,9 +25,17 @@ #include -#include "ecal_monitoring_threads.h" +#include "ecal_def.h" #include "util/ecal_expmap.h" -#include "io/udp/rcv_sample.h" + +#ifdef _MSC_VER +#pragma warning(push, 0) // disable proto warnings +#endif +#include +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif #include #include @@ -38,11 +46,11 @@ namespace eCAL //////////////////////////////////////// // Monitoring Declaration //////////////////////////////////////// - class CMonitoringImpl : public CSampleReceiver + class CMonitoringImpl { public: CMonitoringImpl(); - ~CMonitoringImpl() override = default; + ~CMonitoringImpl() = default; void Create(); void Destroy(); @@ -53,15 +61,9 @@ namespace eCAL void GetMonitoringPb(eCAL::pb::Monitoring& monitoring_, unsigned int entities_); void GetMonitoringStructs(eCAL::Monitoring::SMonitoring& monitoring_, unsigned int entities_); - void GetLogging(eCAL::pb::Logging& logging_); - - int PubMonitoring(bool state_, std::string& name_); - int PubLogging(bool state_, std::string& name_); - - bool ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType /*layer_*/) override; protected: - bool HasSample(const std::string& /* sample_name_ */) override { return(true); }; + bool ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType /*layer_*/); bool RegisterProcess(const eCAL::pb::Sample& sample_); bool UnregisterProcess(const eCAL::pb::Sample& sample_); @@ -81,8 +83,6 @@ namespace eCAL bool RegisterTopic(const eCAL::pb::Sample& sample_, enum ePubSub pubsub_type_); bool UnregisterTopic(const eCAL::pb::Sample& sample_, enum ePubSub pubsub_type_); - void RegisterLogMessage(const eCAL::pb::LogMessage& log_msg_); - using TopicMonMapT = eCAL::Util::CExpMap; struct STopicMonMap { @@ -167,14 +167,5 @@ namespace eCAL STopicMonMap m_subscriber_map; SServerMonMap m_server_map; SClientMonMap m_clients_map; - - // logging - using LogMessageListT = std::list; - std::mutex m_log_msglist_sync; - LogMessageListT m_log_msglist; - - // worker threads - std::shared_ptr m_log_rcv_threadcaller; - std::shared_ptr m_pub_threadcaller; }; } diff --git a/ecal/core/src/monitoring/ecal_monitoring_threads.cpp b/ecal/core/src/monitoring/ecal_monitoring_threads.cpp deleted file mode 100644 index 933941a39a..0000000000 --- a/ecal/core/src/monitoring/ecal_monitoring_threads.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* ========================= 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 Global database class -**/ - -#include -#include - -#include "ecal_def.h" -#include "io/udp/msg_type.h" -#include "io/udp/udp_configurations.h" - -#include "ecal_monitoring_threads.h" - -namespace eCAL -{ - static bool IsLocalHost(const eCAL::pb::LogMessage& ecal_message_) - { - const std::string host_name = ecal_message_.hname(); - if (host_name.empty()) return(false); - if (host_name == Process::GetHostName()) return(true); - return(false); - } - - CLoggingReceiveThread::CLoggingReceiveThread(LogMessageCallbackT log_cb_) : - m_network_mode(false), m_log_cb(log_cb_) - { - // set network attributes - SReceiverAttr attr; - attr.address = UDP::GetLoggingAddress(); - attr.port = UDP::GetLoggingPort(); - attr.broadcast = !Config::IsNetworkEnabled(); - attr.loopback = true; - attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); - - // create udp logging receiver - m_log_rcv.Create(attr); - - // start logging receiver thread - m_log_rcv_thread.Start(0, std::bind(&CLoggingReceiveThread::ThreadFun, this)); - - // allocate receive buffer - m_msg_buffer.resize(MSG_BUFFER_SIZE); - } - - CLoggingReceiveThread::~CLoggingReceiveThread() - { - m_log_rcv_thread.Stop(); - m_log_rcv.Destroy(); - } - - void CLoggingReceiveThread::SetNetworkMode(bool network_mode_) - { - m_network_mode = network_mode_; - } - - int CLoggingReceiveThread::ThreadFun() - { - // wait for any incoming message - const size_t recv_len = m_log_rcv.Receive(m_msg_buffer.data(), m_msg_buffer.size(), CMN_LOGGING_RECEIVE_THREAD_CYCLE_TIME_MS); - if (recv_len > 0) - { - m_log_ecal_msg.Clear(); - if (m_log_ecal_msg.ParseFromArray(m_msg_buffer.data(), static_cast(recv_len))) - { - if (IsLocalHost(m_log_ecal_msg) || m_network_mode) - { - m_log_cb(m_log_ecal_msg); - } - } - } - - return(0); - } - - CMonLogPublishingThread::CMonLogPublishingThread(MonitoringCallbackT mon_cb_, LoggingCallbackT log_cb_) : - m_mon_cb(mon_cb_), m_log_cb(log_cb_) - { - m_pub_thread.Start(Config::GetRegistrationRefreshMs(), std::bind(&CMonLogPublishingThread::ThreadFun, this)); - } - - CMonLogPublishingThread::~CMonLogPublishingThread() - { - m_pub_thread.Stop(); - m_mon_pub.pub.Destroy(); - m_log_pub.pub.Destroy(); - } - - void CMonLogPublishingThread::SetMonState(bool state_, const std::string& name_) - { - m_mon_pub.state = state_; - m_mon_pub.name = name_; - if (state_) - { - m_mon_pub.pub.Create(name_); - } - else - { - m_mon_pub.pub.Destroy(); - } - } - - void CMonLogPublishingThread::SetLogState(bool state_, const std::string& name_) - { - m_log_pub.state = state_; - m_log_pub.name = name_; - if (state_) - { - m_log_pub.pub.Create(name_); - } - else - { - m_log_pub.pub.Destroy(); - } - } - - int CMonLogPublishingThread::ThreadFun() - { - if (m_mon_pub.state) - { - // get monitoring - eCAL::pb::Monitoring monitoring; - m_mon_cb(monitoring); - // publish monitoring - m_mon_pub.pub.Send(monitoring); - } - - if (m_log_pub.state) - { - // get logging - eCAL::pb::Logging logging; - m_log_cb(logging); - // publish logging - m_log_pub.pub.Send(logging); - } - return 0; - } -} diff --git a/ecal/core/src/monitoring/ecal_monitoring_threads.h b/ecal/core/src/monitoring/ecal_monitoring_threads.h deleted file mode 100644 index b139180c83..0000000000 --- a/ecal/core/src/monitoring/ecal_monitoring_threads.h +++ /dev/null @@ -1,100 +0,0 @@ -/* ========================= 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 Monitoring worker threads -**/ - -#pragma once - -#include -#include - -#include "util/ecal_thread.h" -#include "io/udp/udp_receiver.h" - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include -#include - -namespace eCAL -{ - class CLoggingReceiveThread - { - public: - using LogMessageCallbackT = std::function; - - CLoggingReceiveThread(LogMessageCallbackT log_cb_); - virtual ~CLoggingReceiveThread(); - - void SetNetworkMode(bool network_mode_); - - protected: - int ThreadFun(); - - CUDPReceiver m_log_rcv; - class CThread m_log_rcv_thread; - - bool m_network_mode; - std::vector m_msg_buffer; - eCAL::pb::LogMessage m_log_ecal_msg; - LogMessageCallbackT m_log_cb; - }; - - class CMonLogPublishingThread - { - public: - using MonitoringCallbackT = std::function; - using LoggingCallbackT = std::function; - - CMonLogPublishingThread(MonitoringCallbackT mon_cb_, LoggingCallbackT log_cb_); - virtual ~CMonLogPublishingThread(); - - void SetMonState(bool state_, const std::string& name_); - void SetLogState(bool state_, const std::string& name_); - - protected: - int ThreadFun(); - - template - struct SProtoPub - { - SProtoPub() : state(false) {} - bool state; - std::string name; - - protobuf::CPublisher pub; - }; - - class CThread m_pub_thread; - struct SProtoPub m_mon_pub; - struct SProtoPub m_log_pub; - - MonitoringCallbackT m_mon_cb; - LoggingCallbackT m_log_cb; - }; -} diff --git a/ecal/core/src/pubsub/ecal_subgate.cpp b/ecal/core/src/pubsub/ecal_subgate.cpp index df75cb5782..6ae3998f2f 100644 --- a/ecal/core/src/pubsub/ecal_subgate.cpp +++ b/ecal/core/src/pubsub/ecal_subgate.cpp @@ -22,6 +22,7 @@ **/ #include +#include #include @@ -76,7 +77,9 @@ namespace eCAL CDataReader::InitializeLayers(); // start timeout thread - m_subtimeout_thread.Start(CMN_DATAREADER_TIMEOUT_RESOLUTION_MS, std::bind(&CSubGate::CheckTimeouts, this)); + m_subtimeout_thread = std::make_shared(std::bind(&CSubGate::CheckTimeouts, this)); + m_subtimeout_thread->start(std::chrono::milliseconds(CMN_DATAREADER_TIMEOUT_RESOLUTION_MS)); + m_created = true; } @@ -85,7 +88,7 @@ namespace eCAL if(!m_created) return; // stop timeout thread - m_subtimeout_thread.Stop(); + m_subtimeout_thread->stop(); // destroy all remaining subscriber const std::unique_lock lock(m_topic_name_datareader_sync); @@ -367,10 +370,8 @@ namespace eCAL } } - int CSubGate::CheckTimeouts() + void CSubGate::CheckTimeouts() { - if (!m_created) return(0); - // check subscriber timeouts const std::shared_lock lock(m_topic_name_datareader_sync); for (auto iter = m_topic_name_datareader_map.begin(); iter != m_topic_name_datareader_map.end(); ++iter) @@ -385,7 +386,8 @@ namespace eCAL g_shutdown = 1; } - return(0); + // idle thread + std::this_thread::sleep_for(std::chrono::milliseconds(CMN_DATAREADER_TIMEOUT_RESOLUTION_MS)); } bool CSubGate::ApplyTopicToDescGate(const std::string& topic_name_, const SDataTypeInformation& topic_info_) diff --git a/ecal/core/src/pubsub/ecal_subgate.h b/ecal/core/src/pubsub/ecal_subgate.h index 0593179f75..f08af23838 100644 --- a/ecal/core/src/pubsub/ecal_subgate.h +++ b/ecal/core/src/pubsub/ecal_subgate.h @@ -23,12 +23,12 @@ #pragma once -#include "util/ecal_thread.h" - #include "readwrite/ecal_reader.h" +#include "util/ecal_thread.h" #include #include +#include #include #include @@ -59,16 +59,16 @@ namespace eCAL void RefreshRegistrations(); protected: - int CheckTimeouts(); + void CheckTimeouts(); bool ApplyTopicToDescGate(const std::string& topic_name_, const SDataTypeInformation& topic_info_); - static std::atomic m_created; + static std::atomic m_created; // database data reader using TopicNameDataReaderMapT = std::unordered_multimap>; - std::shared_timed_mutex m_topic_name_datareader_sync; - TopicNameDataReaderMapT m_topic_name_datareader_map; + std::shared_timed_mutex m_topic_name_datareader_sync; + TopicNameDataReaderMapT m_topic_name_datareader_map; - eCAL::CThread m_subtimeout_thread; + std::shared_ptr m_subtimeout_thread; }; } diff --git a/ecal/core/src/readwrite/udp/ecal_reader_udp_mc.cpp b/ecal/core/src/readwrite/udp/ecal_reader_udp_mc.cpp index 1f242bb821..f5133fc10a 100644 --- a/ecal/core/src/readwrite/udp/ecal_reader_udp_mc.cpp +++ b/ecal/core/src/readwrite/udp/ecal_reader_udp_mc.cpp @@ -26,94 +26,87 @@ #include "ecal_global_accessors.h" #include "pubsub/ecal_subgate.h" -#include "io/udp/udp_configurations.h" +#include "io/udp/ecal_udp_configurations.h" namespace eCAL { - //////////////// - // READER - //////////////// - bool CDataReaderUDP::HasSample(const std::string& sample_name_) - { - if (g_subgate() == nullptr) return(false); - return(g_subgate()->HasSample(sample_name_)); - } - - bool CDataReaderUDP::ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType layer_) - { - if (g_subgate() == nullptr) return false; - return g_subgate()->ApplySample(ecal_sample_, layer_); - } - //////////////// // LAYER //////////////// CUDPReaderLayer::CUDPReaderLayer() : - started(false), - local_mode(false) + m_started(false), + m_local_mode(false) {} - CUDPReaderLayer::~CUDPReaderLayer() - { - thread.Stop(); - } + CUDPReaderLayer::~CUDPReaderLayer() = default; void CUDPReaderLayer::Initialize() { - // set local mode - local_mode = !Config::IsNetworkEnabled(); - - // set network attributes - SReceiverAttr attr; - attr.address = UDP::GetPayloadAddress(); - attr.port = UDP::GetPayloadPort(); - attr.broadcast = !Config::IsNetworkEnabled(); - attr.loopback = true; - attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); - - // create udp receiver - rcv.Create(attr); } void CUDPReaderLayer::AddSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/, QOS::SReaderQOS /*qos_*/) { - if (!started) + if (!m_started) { - thread.Start(0, std::bind(&CDataReaderUDP::Receive, &reader, &rcv, CMN_PAYLOAD_RECEIVE_THREAD_CYCLE_TIME_MS)); - started = true; + // set local mode + m_local_mode = UDP::IsBroadcast(); + + // set network attributes + IO::UDP::SReceiverAttr attr; + attr.address = UDP::GetPayloadAddress(); + attr.port = UDP::GetPayloadPort(); + attr.broadcast = UDP::IsBroadcast(); + attr.loopback = true; + attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); + + // start payload sample receiver + m_payload_receiver = std::make_shared(attr, std::bind(&CUDPReaderLayer::HasSample, this, std::placeholders::_1), std::bind(&CUDPReaderLayer::ApplySample, this, std::placeholders::_1)); + + m_started = true; } // we use udp broadcast in local mode - if (local_mode) return; + if (m_local_mode) return; // add topic name based multicast address const std::string mcast_address = UDP::GetTopicPayloadAddress(topic_name_); - if (topic_name_mcast_map.find(mcast_address) == topic_name_mcast_map.end()) + if (m_topic_name_mcast_map.find(mcast_address) == m_topic_name_mcast_map.end()) { - topic_name_mcast_map.emplace(std::pair(mcast_address, 0)); - rcv.AddMultiCastGroup(mcast_address.c_str()); + m_topic_name_mcast_map.emplace(std::pair(mcast_address, 0)); + m_payload_receiver->AddMultiCastGroup(mcast_address.c_str()); } - topic_name_mcast_map[mcast_address]++; + m_topic_name_mcast_map[mcast_address]++; } void CUDPReaderLayer::RemSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/) { // we use udp broadcast in local mode - if (local_mode) return; + if (m_local_mode) return; const std::string mcast_address = UDP::GetTopicPayloadAddress(topic_name_); - if (topic_name_mcast_map.find(mcast_address) == topic_name_mcast_map.end()) + if (m_topic_name_mcast_map.find(mcast_address) == m_topic_name_mcast_map.end()) { // this should never happen } else { - topic_name_mcast_map[mcast_address]--; - if (topic_name_mcast_map[mcast_address] == 0) + m_topic_name_mcast_map[mcast_address]--; + if (m_topic_name_mcast_map[mcast_address] == 0) { - rcv.RemMultiCastGroup(mcast_address.c_str()); - topic_name_mcast_map.erase(mcast_address); + m_payload_receiver->RemMultiCastGroup(mcast_address.c_str()); + m_topic_name_mcast_map.erase(mcast_address); } } } + bool CUDPReaderLayer::HasSample(const std::string& sample_name_) + { + if (g_subgate() == nullptr) return(false); + return(g_subgate()->HasSample(sample_name_)); + } + + bool CUDPReaderLayer::ApplySample(const eCAL::pb::Sample& ecal_sample_) + { + if (g_subgate() == nullptr) return false; + return g_subgate()->ApplySample(ecal_sample_, eCAL::pb::eTLayerType::tl_ecal_udp_mc); + } } diff --git a/ecal/core/src/readwrite/udp/ecal_reader_udp_mc.h b/ecal/core/src/readwrite/udp/ecal_reader_udp_mc.h index 411c15652a..6a19e784b5 100644 --- a/ecal/core/src/readwrite/udp/ecal_reader_udp_mc.h +++ b/ecal/core/src/readwrite/udp/ecal_reader_udp_mc.h @@ -25,7 +25,7 @@ #include "readwrite/ecal_reader_layer.h" -#include "io/udp/rcv_sample.h" +#include "io/udp/ecal_udp_sample_receiver.h" #include #include @@ -33,16 +33,6 @@ namespace eCAL { - //////////////// - // READER - //////////////// - class CDataReaderUDP : public CSampleReceiver - { - public: - bool HasSample(const std::string& sample_name_) override; - bool ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType layer_) override; - }; - //////////////// // LAYER //////////////// @@ -60,11 +50,12 @@ namespace eCAL void SetConnectionParameter(SReaderLayerPar& /*par_*/) override {} private: - bool started; - bool local_mode; - CUDPReceiver rcv; - CThread thread; - CDataReaderUDP reader; - std::map topic_name_mcast_map; + bool HasSample(const std::string& sample_name_); + bool ApplySample(const eCAL::pb::Sample& ecal_sample_); + + bool m_started; + bool m_local_mode; + std::shared_ptr m_payload_receiver; + std::map m_topic_name_mcast_map; }; } diff --git a/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.cpp b/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.cpp index 189c06cc3b..c0510f9ff3 100644 --- a/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.cpp +++ b/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.cpp @@ -25,7 +25,7 @@ #include #include "ecal_writer_udp_mc.h" -#include "io/udp/udp_configurations.h" +#include "io/udp/ecal_udp_configurations.h" namespace eCAL { @@ -61,20 +61,20 @@ namespace eCAL m_topic_id = topic_id_; // set network attributes - SSenderAttr attr; + IO::UDP::SSenderAttr attr; attr.address = UDP::GetTopicPayloadAddress(topic_name_); attr.port = UDP::GetPayloadPort(); attr.ttl = UDP::GetMulticastTtl(); - attr.broadcast = !Config::IsNetworkEnabled(); + attr.broadcast = UDP::IsBroadcast(); attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); // create udp/sample sender with activated loop-back attr.loopback = true; - m_sample_sender_loopback = std::make_shared(attr); + m_sample_sender_loopback = std::make_shared(attr); // create udp/sample sender without activated loop-back attr.loopback = false; - m_sample_sender_no_loopback = std::make_shared(attr); + m_sample_sender_no_loopback = std::make_shared(attr); m_created = true; return true; @@ -123,14 +123,14 @@ namespace eCAL { if (m_sample_sender_loopback) { - sent = m_sample_sender_loopback->SendSample(m_ecal_sample.topic().tname(), m_ecal_sample, attr_.bandwidth); + sent = m_sample_sender_loopback->Send(m_ecal_sample.topic().tname(), m_ecal_sample, attr_.bandwidth); } } else { if (m_sample_sender_no_loopback) { - sent = m_sample_sender_no_loopback->SendSample(m_ecal_sample.topic().tname(), m_ecal_sample, attr_.bandwidth); + sent = m_sample_sender_no_loopback->Send(m_ecal_sample.topic().tname(), m_ecal_sample, attr_.bandwidth); } } diff --git a/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.h b/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.h index 08bf23cd72..4cb4dcd37c 100644 --- a/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.h +++ b/ecal/core/src/readwrite/udp/ecal_writer_udp_mc.h @@ -33,8 +33,7 @@ #pragma warning(pop) #endif -#include "io/udp/snd_sample.h" -#include "io/udp/udp_sender.h" +#include "io/udp/ecal_udp_sample_sender.h" #include "readwrite/ecal_writer_base.h" #include @@ -56,9 +55,9 @@ namespace eCAL bool Write(const void* buf_, const SWriterAttr& attr_) override; protected: - eCAL::pb::Sample m_ecal_sample; + eCAL::pb::Sample m_ecal_sample; - std::shared_ptr m_sample_sender_loopback; - std::shared_ptr m_sample_sender_no_loopback; + std::shared_ptr m_sample_sender_loopback; + std::shared_ptr m_sample_sender_no_loopback; }; } diff --git a/ecal/core/src/registration/ecal_registration_provider.cpp b/ecal/core/src/registration/ecal_registration_provider.cpp index c322ab2c02..22c24950c8 100644 --- a/ecal/core/src/registration/ecal_registration_provider.cpp +++ b/ecal/core/src/registration/ecal_registration_provider.cpp @@ -33,8 +33,10 @@ #include "ecal_registration_provider.h" #include "ecal_descgate.h" -#include "io/udp/udp_configurations.h" -#include "io/udp/snd_sample.h" +#include "io/udp/ecal_udp_configurations.h" +#include "io/udp/ecal_udp_sample_sender.h" + +#include namespace eCAL { @@ -81,16 +83,16 @@ namespace eCAL if (m_use_network_monitoring) { // set network attributes - SSenderAttr attr; + IO::UDP::SSenderAttr attr; attr.address = UDP::GetRegistrationAddress(); attr.port = UDP::GetRegistrationPort(); attr.ttl = UDP::GetMulticastTtl(); - attr.broadcast = !Config::IsNetworkEnabled(); + attr.broadcast = UDP::IsBroadcast(); attr.loopback = true; attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); // create udp registration sender - m_reg_sample_snd = std::make_shared(attr); + m_reg_sample_snd = std::make_shared(attr); } else { @@ -105,7 +107,8 @@ namespace eCAL } // start cyclic registration thread - m_reg_sample_snd_thread.Start(Config::GetRegistrationRefreshMs(), std::bind(&CRegistrationProvider::RegisterSendThread, this)); + m_reg_sample_snd_thread = std::make_shared(std::bind(&CRegistrationProvider::RegisterSendThread, this)); + m_reg_sample_snd_thread->start(std::chrono::milliseconds(Config::GetRegistrationRefreshMs())); m_created = true; } @@ -115,7 +118,7 @@ namespace eCAL if(!m_created) return; // stop cyclic registration thread - m_reg_sample_snd_thread.Stop(); + m_reg_sample_snd_thread->stop(); // send one last (un)registration message to the world // thank you and goodbye :-) @@ -433,7 +436,7 @@ namespace eCAL bool return_value {true}; if (m_use_network_monitoring && m_reg_sample_snd) - return_value &= (m_reg_sample_snd->SendSample(sample_name_, sample_, -1) != 0); + return_value &= (m_reg_sample_snd->Send(sample_name_, sample_, -1) != 0); if(m_use_shm_monitoring) { @@ -465,16 +468,14 @@ namespace eCAL return return_value; } - int CRegistrationProvider::RegisterSendThread() + void CRegistrationProvider::RegisterSendThread() { - if(!m_created) return(0); - // calculate average receive bytes - g_process_rbytes = static_cast(((double)g_process_rbytes_sum / m_reg_refresh)*1000.0); + g_process_rbytes = static_cast(((double)g_process_rbytes_sum / m_reg_refresh) * 1000.0); g_process_rbytes_sum = 0; // calculate average write bytes - g_process_wbytes = static_cast(((double)g_process_wbytes_sum / m_reg_refresh)*1000.0); + g_process_wbytes = static_cast(((double)g_process_wbytes_sum / m_reg_refresh) * 1000.0); g_process_wbytes_sum = 0; // refresh subscriber registration @@ -503,9 +504,7 @@ namespace eCAL // write sample list to shared memory SendSampleList(); - - return(0); - } + } bool CRegistrationProvider::ApplyTopicToDescGate(const std::string& topic_name_ , const SDataTypeInformation& topic_info_ diff --git a/ecal/core/src/registration/ecal_registration_provider.h b/ecal/core/src/registration/ecal_registration_provider.h index 435b38ddc4..22a596096b 100644 --- a/ecal/core/src/registration/ecal_registration_provider.h +++ b/ecal/core/src/registration/ecal_registration_provider.h @@ -28,18 +28,19 @@ #pragma once -#include "util/ecal_thread.h" -#include "io/udp/snd_sample.h" -#include "io/udp/udp_sender.h" +#include "io/udp/ecal_udp_sample_sender.h" #include "io/shm/ecal_memfile_broadcast.h" #include "io/shm/ecal_memfile_broadcast_writer.h" +#include "util/ecal_thread.h" + #include +#include #include #include +#include #include -#include #ifdef _MSC_VER #pragma warning(push, 0) // disable proto warnings @@ -79,7 +80,7 @@ namespace eCAL bool ApplySample(const std::string& sample_name_, const eCAL::pb::Sample& sample_); - int RegisterSendThread(); + void RegisterSendThread(); bool ApplyTopicToDescGate(const std::string& topic_name_ , const SDataTypeInformation& topic_info_ @@ -92,33 +93,33 @@ namespace eCAL bool SendSampleList(bool reset_sample_list_ = true); - static std::atomic m_created; - int m_reg_refresh; - bool m_reg_topics; - bool m_reg_services; - bool m_reg_process; + static std::atomic m_created; + int m_reg_refresh; + bool m_reg_topics; + bool m_reg_services; + bool m_reg_process; - CThread m_reg_sample_snd_thread; - std::shared_ptr m_reg_sample_snd; + std::shared_ptr m_reg_sample_snd; + std::shared_ptr m_reg_sample_snd_thread; using SampleMapT = std::unordered_map; - std::mutex m_topics_map_sync; - SampleMapT m_topics_map; + std::mutex m_topics_map_sync; + SampleMapT m_topics_map; - std::mutex m_server_map_sync; - SampleMapT m_server_map; + std::mutex m_server_map_sync; + SampleMapT m_server_map; - std::mutex m_client_map_sync; - SampleMapT m_client_map; + std::mutex m_client_map_sync; + SampleMapT m_client_map; - std::mutex m_sample_list_sync; - eCAL::pb::SampleList m_sample_list; - std::string m_sample_list_buffer; + std::mutex m_sample_list_sync; + eCAL::pb::SampleList m_sample_list; + std::string m_sample_list_buffer; - eCAL::CMemoryFileBroadcast m_memfile_broadcast; - eCAL::CMemoryFileBroadcastWriter m_memfile_broadcast_writer; + eCAL::CMemoryFileBroadcast m_memfile_broadcast; + eCAL::CMemoryFileBroadcastWriter m_memfile_broadcast_writer; - bool m_use_network_monitoring; - bool m_use_shm_monitoring; + bool m_use_network_monitoring; + bool m_use_shm_monitoring; }; } diff --git a/ecal/core/src/registration/ecal_registration_receiver.cpp b/ecal/core/src/registration/ecal_registration_receiver.cpp index 44759f1b88..dd6d07cc23 100644 --- a/ecal/core/src/registration/ecal_registration_receiver.cpp +++ b/ecal/core/src/registration/ecal_registration_receiver.cpp @@ -32,69 +32,58 @@ #include "service/ecal_clientgate.h" #include "service/ecal_servicegate.h" -#include "io/udp/udp_configurations.h" +#include "io/udp/ecal_udp_configurations.h" #include "ecal_sample_to_topicinfo.h" +#include + namespace eCAL { - bool CUdpRegistrationReceiver::ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType /*layer_*/) - { - if (g_registration_receiver() == nullptr) return false; - return g_registration_receiver()->ApplySample(ecal_sample_); - } - ////////////////////////////////////////////////////////////////// // CMemfileRegistrationReceiver ////////////////////////////////////////////////////////////////// - bool CMemfileRegistrationReceiver::Create(eCAL::CMemoryFileBroadcastReader* memfile_broadcast_reader_) + void CMemfileRegistrationReceiver::Create(eCAL::CMemoryFileBroadcastReader* memfile_broadcast_reader_) { - if (m_created) return false; + if (m_created) return; + + // start memfile broadcast receive thread m_memfile_broadcast_reader = memfile_broadcast_reader_; + m_memfile_broadcast_reader_thread = std::make_shared(std::bind(&CMemfileRegistrationReceiver::Receive, this)); + m_memfile_broadcast_reader_thread->start(std::chrono::milliseconds(Config::GetRegistrationRefreshMs()/2)); + m_created = true; - return true; } - bool CMemfileRegistrationReceiver::Receive() + void CMemfileRegistrationReceiver::Destroy() { - if (!m_created) return false; + if (!m_created) return; - MemfileBroadcastMessageListT message_list; - if(!m_memfile_broadcast_reader->Read(message_list, 0)) - return false; + // stop memfile broadcast receive thread + m_memfile_broadcast_reader_thread->stop(); + m_memfile_broadcast_reader = nullptr; - eCAL::pb::SampleList sample_list; - bool return_value {true}; + m_created = false; + } - for(const auto& message: message_list) + void CMemfileRegistrationReceiver::Receive() + { + MemfileBroadcastMessageListT message_list; + if (m_memfile_broadcast_reader->Read(message_list, 0)) { - if(sample_list.ParseFromArray(message.data, static_cast(message.size))) + eCAL::pb::SampleList sample_list; + + for (const auto& message : message_list) { - for(const auto& sample: sample_list.samples()) + if (sample_list.ParseFromArray(message.data, static_cast(message.size))) { - return_value &= ApplySample(sample); + for (const auto& sample : sample_list.samples()) + { + if (g_registration_receiver()) g_registration_receiver()->ApplySample(sample); + } } } - else - { - return_value = false; - } } - return return_value; - } - - bool CMemfileRegistrationReceiver::Destroy() - { - if (!m_created) return false; - m_memfile_broadcast_reader = nullptr; - m_created = false; - return true; - } - - bool CMemfileRegistrationReceiver::ApplySample(const eCAL::pb::Sample& ecal_sample_) - { - if (g_registration_receiver() == nullptr) return false; - return g_registration_receiver()->ApplySample(ecal_sample_); } ////////////////////////////////////////////////////////////////// @@ -136,18 +125,15 @@ namespace eCAL if (m_use_network_monitoring) { // set network attributes - SReceiverAttr attr; + IO::UDP::SReceiverAttr attr; attr.address = UDP::GetRegistrationAddress(); attr.port = UDP::GetRegistrationPort(); - attr.broadcast = !Config::IsNetworkEnabled(); + attr.broadcast = UDP::IsBroadcast(); attr.loopback = true; attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); - // create udp registration receiver - m_reg_rcv.Create(attr); - - // start registration receiver thread - m_reg_rcv_thread.Start(0, std::bind(&CUdpRegistrationReceiver::Receive, &m_reg_rcv_process, &m_reg_rcv, CMN_REGISTRATION_RECEIVE_THREAD_CYCLE_TIME_MS)); + // start registration sample receiver + m_registration_receiver = std::make_shared(attr, std::bind(&CRegistrationReceiver::HasSample, this, std::placeholders::_1), std::bind(&CRegistrationReceiver::ApplySample, this, std::placeholders::_1)); } if (m_use_shm_monitoring) @@ -157,7 +143,6 @@ namespace eCAL m_memfile_broadcast_reader.Bind(&m_memfile_broadcast); m_memfile_reg_rcv.Create(&m_memfile_broadcast_reader); - m_memfile_reg_rcv_thread.Start(Config::GetRegistrationRefreshMs() / 2 , std::bind(&CMemfileRegistrationReceiver::Receive, &m_memfile_reg_rcv)); } m_created = true; @@ -167,14 +152,15 @@ namespace eCAL { if(!m_created) return; + // stop network registration receive thread if(m_use_network_monitoring) - // stop network registration receive thread - m_reg_rcv_thread.Stop(); + { + m_registration_receiver = nullptr; + } if(m_use_shm_monitoring) { // stop memfile registration receive thread and unbind reader - m_memfile_reg_rcv_thread.Stop(); m_memfile_broadcast_reader.Unbind(); m_memfile_broadcast.Destroy(); } diff --git a/ecal/core/src/registration/ecal_registration_receiver.h b/ecal/core/src/registration/ecal_registration_receiver.h index a3d1ee85cf..c949ba9794 100644 --- a/ecal/core/src/registration/ecal_registration_receiver.h +++ b/ecal/core/src/registration/ecal_registration_receiver.h @@ -30,15 +30,16 @@ #include #include "ecal_def.h" -#include "util/ecal_thread.h" -#include "io/udp/rcv_sample.h" +#include "io/udp/ecal_udp_sample_receiver.h" #include "io/shm/ecal_memfile_broadcast.h" #include "io/shm/ecal_memfile_broadcast_reader.h" -#include #include +#include +#include +#include #ifdef _MSC_VER #pragma warning(push, 0) // disable proto warnings @@ -50,24 +51,19 @@ namespace eCAL { - class CUdpRegistrationReceiver : public CSampleReceiver - { - bool HasSample(const std::string& /*sample_name_*/) override { return(true); }; - bool ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType layer_) override; - }; - class CMemfileRegistrationReceiver { public: - bool Create(eCAL::CMemoryFileBroadcastReader* memfile_broadcast_reader_); - bool Receive(); - bool Destroy(); - - bool ApplySample(const eCAL::pb::Sample& ecal_sample_); + void Create(CMemoryFileBroadcastReader* memfile_broadcast_reader_); + void Destroy(); private: + void Receive(); + + CMemoryFileBroadcastReader* m_memfile_broadcast_reader = nullptr; + std::shared_ptr m_memfile_broadcast_reader_thread; + bool m_created = false; - eCAL::CMemoryFileBroadcastReader* m_memfile_broadcast_reader = nullptr; }; using ApplySampleCallbackT = std::function; @@ -84,6 +80,7 @@ namespace eCAL void EnableLoopback(bool state_); bool LoopBackEnabled() const { return m_loopback; }; + bool HasSample(const std::string& /*sample_name_*/) { return(true); }; bool ApplySample(const eCAL::pb::Sample& ecal_sample_); bool AddRegistrationCallback(enum eCAL_Registration_Event event_, const RegistrationCallbackT& callback_); @@ -92,37 +89,34 @@ namespace eCAL void SetCustomApplySampleCallback(const ApplySampleCallbackT& callback_); void RemCustomApplySampleCallback(); - protected: void ApplySubscriberRegistration(const eCAL::pb::Sample& ecal_sample_); void ApplyPublisherRegistration(const eCAL::pb::Sample& ecal_sample_); bool IsHostGroupMember(const eCAL::pb::Sample & ecal_sample_); - static std::atomic m_created; - bool m_network; - bool m_loopback; + static std::atomic m_created; + bool m_network; + bool m_loopback; - RegistrationCallbackT m_callback_pub; - RegistrationCallbackT m_callback_sub; - RegistrationCallbackT m_callback_service; - RegistrationCallbackT m_callback_client; - RegistrationCallbackT m_callback_process; + RegistrationCallbackT m_callback_pub; + RegistrationCallbackT m_callback_sub; + RegistrationCallbackT m_callback_service; + RegistrationCallbackT m_callback_client; + RegistrationCallbackT m_callback_process; - CUDPReceiver m_reg_rcv; - CThread m_reg_rcv_thread; - CUdpRegistrationReceiver m_reg_rcv_process; + std::shared_ptr m_registration_receiver; + + CMemoryFileBroadcast m_memfile_broadcast; + CMemoryFileBroadcastReader m_memfile_broadcast_reader; - eCAL::CMemoryFileBroadcast m_memfile_broadcast; - eCAL::CMemoryFileBroadcastReader m_memfile_broadcast_reader; - CMemfileRegistrationReceiver m_memfile_reg_rcv; - CThread m_memfile_reg_rcv_thread; + CMemfileRegistrationReceiver m_memfile_reg_rcv; - bool m_use_network_monitoring; - bool m_use_shm_monitoring; + bool m_use_network_monitoring; + bool m_use_shm_monitoring; - ApplySampleCallbackT m_callback_custom_apply_sample; + ApplySampleCallbackT m_callback_custom_apply_sample; - std::string m_host_group_name; + std::string m_host_group_name; }; } diff --git a/ecal/core/src/util/ecal_thread.cpp b/ecal/core/src/util/ecal_thread.cpp deleted file mode 100644 index 2ece2fc9e0..0000000000 --- a/ecal/core/src/util/ecal_thread.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* ========================= 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 thread interface - windows platform -**/ - -#include - -#include "ecal_event_internal.h" -#include "ecal_thread.h" -#include - -namespace eCAL -{ - CThread::CThread() - { - } - - CThread::~CThread() - { - try { Stop(); } catch(...) { /*??*/ } - } - - int CThread::Start(int period_, std::function ext_caller_) - { - if(m_tdata.is_started) return(0); - - gOpenUnnamedEvent(&m_tdata.event); - m_tdata.do_stop = false; - m_tdata.period = period_; - m_tdata.ext_caller = ext_caller_; - m_tdata.thread = std::thread(CThread::HelperThread, (void*)&m_tdata); - m_tdata.is_started = true; - - gSetEvent(m_tdata.event); - - return(1); - } - - int CThread::Stop() - { - if(m_tdata.is_started) - { - // signal thread to stop - m_tdata.do_stop = true; - - // release thread wait barrier - Fire(); - - // join thread if joinable and wait for return - if(m_tdata.thread.joinable()) m_tdata.thread.join(); - - // ok the thread is stopped - m_tdata.is_started = false; - - // close event - gCloseEvent(m_tdata.event); - } - - return(1); - } - - int CThread::Fire() - { - gSetEvent(m_tdata.event); - return(1); - } - - void CThread::HelperThread(void* par_) - { - if(par_ == nullptr) return; - - struct ThreadData* tdata = static_cast(par_); - if(!gEventIsValid(tdata->event)) return; - - // mark as running - tdata->is_running = true; - - int state = 0; - while(!tdata->do_stop) - { - // wait for timeout 'period' - if(tdata->period > 0) gWaitForEvent(tdata->event, tdata->period); - - // call external code - if(!tdata->do_stop && gEventIsValid(tdata->event) && tdata->ext_caller) - { - state = (tdata->ext_caller)(); - } - else - { - state = -1; - } - - if(state < 0) break; - } - - // mark as stopped - tdata->is_running = false; - - return; - } -} diff --git a/ecal/core/src/util/ecal_thread.h b/ecal/core/src/util/ecal_thread.h index 7c5fa3c65f..c6493c3e76 100644 --- a/ecal/core/src/util/ecal_thread.h +++ b/ecal/core/src/util/ecal_thread.h @@ -21,48 +21,93 @@ * @brief eCAL threading helper class **/ -#pragma once - -#include - -#include -#include +#include +#include #include +#include +#include + +#pragma once namespace eCAL { - class CThread + /** + * @brief A class that encapsulates threaded functionality with a callback interface. + */ + class CCallbackThread { public: - CThread(); - virtual ~CThread(); + /** + * @brief Constructor for the CallbackThread class. + * @param callback A callback function to be executed in the CallbackThread thread. + */ + CCallbackThread(std::function callback) + : callback_(callback) {} + + /** + * @brief Start the callback thread with a specified timeout. + * @param timeout The timeout duration for waiting in the callback thread. + */ + template + void start(DurationType timeout) + { + callbackThread_ = std::thread(&CCallbackThread::callbackFunction, this, timeout); + } + + /** + * @brief Stop the callback thread. + * Waits for the callback thread to finish its work. + */ + void stop() + { + { + const std::unique_lock lock(mtx_); + // Set the flag to signal the callback thread to stop + stopThread_ = true; + // Notify the callback thread to wake up and check the flag + cv_.notify_one(); + } - int Start(int period, std::function ext_caller_); - int Stop(); - int Fire(); + // Wait for the callback thread to finish + if (callbackThread_.joinable()) { + callbackThread_.join(); + } + } - bool IsRunning() {return(m_tdata.is_running);}; + private: + std::thread callbackThread_; /**< The callback thread object. */ + std::function callback_; /**< The callback function to be executed in the callback thread. */ + std::mutex mtx_; /**< Mutex for thread synchronization. */ + std::condition_variable cv_; /**< Condition variable for signaling between threads. */ + bool stopThread_{false}; /**< Flag to indicate whether the callback thread should stop. */ - protected: - struct ThreadData + /** + * @brief Callback function that runs in the callback thread. + * Periodically checks the stopThread flag and executes the callback function. + * @tparam DurationType The type of the timeout duration (e.g., std::chrono::seconds, std::chrono::milliseconds). + * @param timeout The timeout duration for waiting in the callback thread. + */ + template + void callbackFunction(DurationType timeout) { - ThreadData() : - period(0) - , is_running(false) - , is_started(false) - , do_stop(false) + while (true) { - }; - std::thread thread; - int period; - EventHandleT event; - std::atomic is_running; - std::atomic is_started; - std::atomic do_stop; - std::function ext_caller; - }; - struct ThreadData m_tdata; + { + std::unique_lock lock(mtx_); + // Wait for a signal or a timeout + if (cv_.wait_for(lock, timeout, [this] { return stopThread_; })) + { + // If the stopThread flag is true, break out of the loop + break; + } + } - static void HelperThread(void* par_); + // Do some work in the callback thread + if (callback_) + { + callback_(); + } + } + } }; } diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 8c0e178d60..9a67fc8058 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -97,6 +97,7 @@ add_subdirectory(cpp/pubsub/flatbuffer/monster_snd) endif(HAS_FLATBUFFERS) #add_subdirectory(cpp/pubsub/msgpack/address_rec) #add_subdirectory(cpp/pubsub/msgpack/address_snd) +add_subdirectory(cpp/pubsub/protobuf/person_loopback) add_subdirectory(cpp/pubsub/protobuf/person_rec) add_subdirectory(cpp/pubsub/protobuf/person_rec_events) add_subdirectory(cpp/pubsub/protobuf/person_rec_lambda_in_class) diff --git a/samples/cpp/pubsub/protobuf/person_loopback/CMakeLists.txt b/samples/cpp/pubsub/protobuf/person_loopback/CMakeLists.txt new file mode 100644 index 0000000000..e56cb98dd0 --- /dev/null +++ b/samples/cpp/pubsub/protobuf/person_loopback/CMakeLists.txt @@ -0,0 +1,49 @@ +# ========================= 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_loopback) + +find_package(eCAL REQUIRED) +find_package(Protobuf REQUIRED) + +set(person_loopback_src + src/person_loopback.cpp +) + +set(person_loopback_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_loopback_src}) +PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_loopback_proto}) + +target_link_libraries(${PROJECT_NAME} + eCAL::core + protobuf::libprotobuf +) + +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/samples/cpp/pubsub/protobuf/person_loopback/src/person_loopback.cpp b/samples/cpp/pubsub/protobuf/person_loopback/src/person_loopback.cpp new file mode 100644 index 0000000000..afbcc2b45d --- /dev/null +++ b/samples/cpp/pubsub/protobuf/person_loopback/src/person_loopback.cpp @@ -0,0 +1,97 @@ +/* ========================= 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 "person.pb.h" + +int main(int argc, char **argv) +{ + // initialize eCAL API + eCAL::Initialize(argc, argv, "person publisher"); + + // set process state + eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); + + // enable to receive our own messages + eCAL::Util::EnableLoopback(true); + + // create a publisher (topic name "person") + eCAL::protobuf::CPublisher pub("person"); + + // generate a class instance of Person + pb::People::Person person; + + eCAL::protobuf::CSubscriber sub("person"); + auto receive_lambda = [&sub](const char* /*topic_name_*/, const pb::People::Person& person_, const long long /*time_*/, const long long /*clock_*/, const long long /*id_*/){ + std::cout << "------------------------------------------" << std::endl; + std::cout << " RECEIVED " << std::endl; + std::cout << "------------------------------------------" << std::endl; + 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; + std::cout << std::endl; + + }; + sub.AddReceiveCallback(receive_lambda); + + // 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 << "------------------------------------------" << std::endl; + std::cout << " SENT " << std::endl; + std::cout << "------------------------------------------" << std::endl; + 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/core/src/io/udp/udp_init.h b/samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/animal.proto similarity index 85% rename from ecal/core/src/io/udp/udp_init.h rename to samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/animal.proto index a967838fe4..625a78db82 100644 --- a/ecal/core/src/io/udp/udp_init.h +++ b/samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/animal.proto @@ -17,17 +17,12 @@ * ========================= eCAL LICENSE ================================= */ -/** - * @brief UDP initialization -**/ +syntax = "proto3"; -#pragma once +package pb.Animal; -namespace eCAL +message Dog { - namespace Net - { - int Initialize(); - int Finalize(); - } + string name = 1; + string colour = 2; } diff --git a/ecal/core/src/io/udp/udp_receiver.h b/samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/house.proto similarity index 56% rename from ecal/core/src/io/udp/udp_receiver.h rename to samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/house.proto index 500aaeec6c..fbe8cce4c7 100644 --- a/ecal/core/src/io/udp/udp_receiver.h +++ b/samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/house.proto @@ -17,34 +17,11 @@ * ========================= eCAL LICENSE ================================= */ -/** - * @brief UDP receiver class -**/ +syntax = "proto3"; -#pragma once +package pb.Environment; -#include -#include "ecal_receiver.h" - -namespace eCAL +message House { - class CUDPReceiverBase; - - class CUDPReceiver : public CReceiver - { - public: - CUDPReceiver(); - - bool Create(const SReceiverAttr& attr_) override; - bool Destroy() override; - - bool AddMultiCastGroup(const char* ipaddr_); - bool RemMultiCastGroup(const char* ipaddr_); - - size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) override; - - protected: - bool m_use_npcap; - std::shared_ptr m_socket_impl; - }; + int32 rooms = 1; } diff --git a/ecal/core/src/io/udp/udp_sender.h b/samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/person.proto similarity index 58% rename from ecal/core/src/io/udp/udp_sender.h rename to samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/person.proto index 3c49743d4d..4200a433f0 100644 --- a/ecal/core/src/io/udp/udp_sender.h +++ b/samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/person.proto @@ -17,36 +17,26 @@ * ========================= eCAL LICENSE ================================= */ -/** - * @brief UDP sender class -**/ +syntax = "proto3"; -#pragma once +import "animal.proto"; +import "house.proto"; -#include -#include +package pb.People; -namespace eCAL +message Person { - struct SSenderAttr + enum SType { - std::string address; - int port = 0; - int ttl = 0; - bool broadcast = false; - bool loopback = true; - int sndbuf = 1024 * 1024; - }; + MALE = 0; + FEMALE = 1; + } - class CUDPSenderImpl; + int32 id = 1; + string name = 2; + SType stype = 3; + string email = 4; - class CUDPSender - { - public: - CUDPSender(const SSenderAttr& attr_); - size_t Send(const void* buf_, size_t len_, const char* ipaddr_ = nullptr); - - protected: - std::shared_ptr m_socket_impl; - }; + Animal.Dog dog = 5; + Environment.House house = 6; } diff --git a/testing/ecal/pubsub_test/src/pubsub_gettopics.cpp b/testing/ecal/pubsub_test/src/pubsub_gettopics.cpp index 6806596fe0..fd59090114 100644 --- a/testing/ecal/pubsub_test/src/pubsub_gettopics.cpp +++ b/testing/ecal/pubsub_test/src/pubsub_gettopics.cpp @@ -126,7 +126,7 @@ TEST(PubSub, GetTopics) } // let's unregister them - eCAL::Process::SleepMS(CMN_MONITORING_TIMEOUT + 1000); + eCAL::Process::SleepMS(CMN_MONITORING_TIMEOUT + 2000); // get all topics again, now all topics // should be removed from the map diff --git a/testing/ecal/topic2mcast_test/src/topic2mcast_test.cpp b/testing/ecal/topic2mcast_test/src/topic2mcast_test.cpp index 425e2b4008..d85b1699ac 100644 --- a/testing/ecal/topic2mcast_test/src/topic2mcast_test.cpp +++ b/testing/ecal/topic2mcast_test/src/topic2mcast_test.cpp @@ -18,7 +18,7 @@ */ #include -#include "topic2mcast.h" +#include "io/udp/ecal_udp_topic2mcast.h" #include #include