diff --git a/CHANGELOG.md b/CHANGELOG.md index 075bccdb3a..3e7cf80983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ **Features:** - Enhance MacOS performance with timed{send,receive} functionality in unix domain socket[\#903](https://github.com/eclipse-iceoryx/iceoryx/issues/903) +- Multi-Publisher support for DDS gateway and generic gateway class [\#900](https://github.com/eclipse-iceoryx/iceoryx/issues/900) +- Replace `iox-gw-iceoryx2dds` and `iox-gw-dds2iceoryx` gateways with `iox-dds-gateway` [\#900](https://github.com/eclipse-iceoryx/iceoryx/issues/900) - Enhance posixCall[\#805](https://github.com/eclipse-iceoryx/iceoryx/issues/805) - New chunk available callback for the new C++[\#391](https://github.com/eclipse-iceoryx/iceoryx/issues/391) - Git Hooks on iceoryx[\#486](https://github.com/eclipse-iceoryx/iceoryx/issues/486) diff --git a/cmake/cyclonedds/CMakeLists.txt b/cmake/cyclonedds/CMakeLists.txt index 0bdf78bc41..fb2796a70e 100644 --- a/cmake/cyclonedds/CMakeLists.txt +++ b/cmake/cyclonedds/CMakeLists.txt @@ -28,12 +28,6 @@ if(NOT N EQUAL 0) endif() endif() -if(WIN32) - set(CREATE_PATH_COMMAND mkdir) -else() - set(CREATE_PATH_COMMAND mkdir -p) -endif(WIN32) - if(DEFINED CMAKE_TOOLCHAIN_FILE) set(TOOLCHAIN_FILE "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") endif() @@ -67,12 +61,7 @@ function(fetch_and_install name) endif() # Build - execute_process(COMMAND ${CREATE_PATH_COMMAND} "${BUILD_DIR}" - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) - if(result) - message(FATAL_ERROR "CMake step [create build dir] for ${name} failed: ${result}") - endif() + file(MAKE_DIRECTORY "${BUILD_DIR}") # Parse additional CMake flags set(ADDITIONAL_CMAKE_FLAGS "") diff --git a/iceoryx_binding_c/CMakeLists.txt b/iceoryx_binding_c/CMakeLists.txt index f610cc4a4b..7eeeae0f86 100644 --- a/iceoryx_binding_c/CMakeLists.txt +++ b/iceoryx_binding_c/CMakeLists.txt @@ -14,7 +14,7 @@ # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.5) set(IOX_VERSION_STRING "1.90.0") diff --git a/iceoryx_dds/CMakeLists.txt b/iceoryx_dds/CMakeLists.txt index 467c76bf07..17a7bb7bb1 100644 --- a/iceoryx_dds/CMakeLists.txt +++ b/iceoryx_dds/CMakeLists.txt @@ -124,33 +124,21 @@ endif() target_compile_options(iceoryx_dds PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) # -########## build gateway apps ########## +########## build gateway app ########## # -add_executable(iox-gw-iceoryx2dds - source/iceoryx2dds_app/main.cpp +add_executable(iox-dds-gateway + source/gateway/main.cpp ) -target_link_libraries(iox-gw-iceoryx2dds +target_link_libraries(iox-dds-gateway iceoryx_posh::iceoryx_posh iceoryx_posh::iceoryx_posh_gateway iceoryx_posh::iceoryx_posh_config ${PROJECT_NAMESPACE}::iceoryx_dds ) -target_compile_options(iox-gw-iceoryx2dds PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) +target_compile_options(iox-dds-gateway PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) -add_executable(iox-gw-dds2iceoryx - source/dds2iceoryx_app/main.cpp -) -target_link_libraries(iox-gw-dds2iceoryx - iceoryx_posh::iceoryx_posh - iceoryx_posh::iceoryx_posh_gateway - iceoryx_posh::iceoryx_posh_config - ${PROJECT_NAMESPACE}::iceoryx_dds -) - -target_compile_options(iox-gw-dds2iceoryx PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) - -set_target_properties(iox-gw-iceoryx2dds iox-gw-dds2iceoryx iceoryx_dds +set_target_properties(iox-dds-gateway iceoryx_dds PROPERTIES CXX_STANDARD_REQUIRED ON CXX_STANDARD ${ICEORYX_CXX_STANDARD} @@ -167,7 +155,7 @@ endif() ########## export library ########## # setup_install_directories_and_export_package( - TARGETS iceoryx_dds iox-gw-iceoryx2dds iox-gw-dds2iceoryx + TARGETS iceoryx_dds iox-dds-gateway INCLUDE_DIRECTORY include/ ) diff --git a/iceoryx_dds/examples/docker/publisher_node.entrypoint b/iceoryx_dds/examples/docker/publisher_node.entrypoint index 83b9de8741..024196bcd4 100755 --- a/iceoryx_dds/examples/docker/publisher_node.entrypoint +++ b/iceoryx_dds/examples/docker/publisher_node.entrypoint @@ -29,5 +29,5 @@ EOF fi /usr/bin/iox-roudi & -/usr/bin/iox-gw-iceoryx2dds & +/usr/bin/iox-dds-gateway & /usr/bin/iox-cpp-publisher-untyped diff --git a/iceoryx_dds/examples/docker/subscriber_node.entrypoint b/iceoryx_dds/examples/docker/subscriber_node.entrypoint index fea3c6fb6a..261b0b8d94 100755 --- a/iceoryx_dds/examples/docker/subscriber_node.entrypoint +++ b/iceoryx_dds/examples/docker/subscriber_node.entrypoint @@ -29,5 +29,5 @@ EOF fi /usr/bin/iox-roudi & -/usr/bin/iox-gw-dds2iceoryx & +/usr/bin/iox-dds-gateway & /usr/bin/iox-cpp-subscriber-untyped diff --git a/iceoryx_dds/source/dds2iceoryx_app/main.cpp b/iceoryx_dds/source/dds2iceoryx_app/main.cpp deleted file mode 100644 index 373deb669b..0000000000 --- a/iceoryx_dds/source/dds2iceoryx_app/main.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. -// -// 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. -// -// SPDX-License-Identifier: Apache-2.0 - -#include "iceoryx_dds/dds/data_reader.hpp" -#include "iceoryx_dds/gateway/dds_to_iox.hpp" -#include "iceoryx_dds/internal/log/logging.hpp" -#include "iceoryx_hoofs/platform/signal.hpp" -#include "iceoryx_hoofs/posix_wrapper/semaphore.hpp" -#include "iceoryx_hoofs/posix_wrapper/signal_handler.hpp" -#include "iceoryx_posh/gateway/toml_gateway_config_parser.hpp" -#include "iceoryx_posh/runtime/posh_runtime.hpp" - -#include -#include -#include - -class ShutdownManager -{ - public: - static void scheduleShutdown(int num) - { - char reason; - psignal(num, &reason); - s_shutdownRequested.store(true, std::memory_order_relaxed); - s_semaphore.post().or_else([](auto) { - std::cerr << "failed to call post on shutdown semaphore" << std::endl; - std::terminate(); - }); - } - static void waitUntilShutdown() - { - s_semaphore.wait().or_else([](auto) { - std::cerr << "failed to call wait on shutdown semaphore" << std::endl; - std::terminate(); - }); - } - static bool shouldShutdown() - { - return s_shutdownRequested.load(std::memory_order_relaxed); - } - - private: - static iox::posix::Semaphore s_semaphore; - static std::atomic_bool s_shutdownRequested; - ShutdownManager() = default; -}; -iox::posix::Semaphore ShutdownManager::s_semaphore = - iox::posix::Semaphore::create(iox::posix::CreateUnnamedSingleProcessSemaphore, 0U).value(); -std::atomic_bool ShutdownManager::s_shutdownRequested{false}; - -int main() -{ - // Set OS signal handlers - auto signalGuardInt = iox::posix::registerSignalHandler(iox::posix::Signal::INT, ShutdownManager::scheduleShutdown); - auto signalGuardTerm = - iox::posix::registerSignalHandler(iox::posix::Signal::TERM, ShutdownManager::scheduleShutdown); - - // Start application - iox::runtime::PoshRuntime::initRuntime("iox-gw-dds2iceoryx"); - - iox::dds::DDS2IceoryxGateway<> gw; - - iox::config::TomlGatewayConfigParser::parse() - .and_then([&](auto config) { gw.loadConfiguration(config); }) - .or_else([&](auto err) { - iox::dds::LogWarn() << "[Main] Failed to parse gateway config with error: " - << iox::config::TOML_GATEWAY_CONFIG_FILE_PARSE_ERROR_STRINGS[err]; - iox::dds::LogWarn() << "[Main] Using default configuration."; - iox::config::GatewayConfig defaultConfig; - defaultConfig.setDefaults(); - gw.loadConfiguration(defaultConfig); - }); - - gw.runMultithreaded(); - - // Run until SIGINT or SIGTERM - ShutdownManager::waitUntilShutdown(); - - return 0; -} diff --git a/iceoryx_dds/source/iceoryx2dds_app/main.cpp b/iceoryx_dds/source/gateway/main.cpp similarity index 83% rename from iceoryx_dds/source/iceoryx2dds_app/main.cpp rename to iceoryx_dds/source/gateway/main.cpp index f7a313588c..a085cf52d0 100644 --- a/iceoryx_dds/source/iceoryx2dds_app/main.cpp +++ b/iceoryx_dds/source/gateway/main.cpp @@ -15,8 +15,7 @@ // // SPDX-License-Identifier: Apache-2.0 -#include - +#include "iceoryx_dds/gateway/dds_to_iox.hpp" #include "iceoryx_dds/gateway/iox_to_dds.hpp" #include "iceoryx_dds/internal/log/logging.hpp" #include "iceoryx_hoofs/cxx/helplets.hpp" @@ -63,22 +62,26 @@ int main() iox::posix::registerSignalHandler(iox::posix::Signal::TERM, ShutdownManager::scheduleShutdown); // Start application - iox::runtime::PoshRuntime::initRuntime("iox-gw-iceoryx2dds"); + iox::runtime::PoshRuntime::initRuntime("iox-dds-gateway"); - iox::dds::Iceoryx2DDSGateway<> gw; + iox::config::GatewayConfig gatewayConfig; + iox::dds::Iceoryx2DDSGateway<> iox2ddsGateway; + iox::dds::DDS2IceoryxGateway<> dds2ioxGateway; iox::config::TomlGatewayConfigParser::parse() - .and_then([&](auto config) { gw.loadConfiguration(config); }) + .and_then([&](auto config) { gatewayConfig = config; }) .or_else([&](auto err) { iox::dds::LogWarn() << "[Main] Failed to parse gateway config with error: " << iox::config::TOML_GATEWAY_CONFIG_FILE_PARSE_ERROR_STRINGS[err]; iox::dds::LogWarn() << "[Main] Using default configuration."; - iox::config::GatewayConfig defaultConfig; - defaultConfig.setDefaults(); - gw.loadConfiguration(defaultConfig); + gatewayConfig.setDefaults(); }); - gw.runMultithreaded(); + iox2ddsGateway.loadConfiguration(gatewayConfig); + dds2ioxGateway.loadConfiguration(gatewayConfig); + + iox2ddsGateway.runMultithreaded(); + dds2ioxGateway.runMultithreaded(); // Run until SIGINT or SIGTERM ShutdownManager::waitUntilShutdown(); diff --git a/iceoryx_dds/source/iceoryx_dds/dds/cyclone_data_reader.cpp b/iceoryx_dds/source/iceoryx_dds/dds/cyclone_data_reader.cpp index 09719c1f90..9f39b955c0 100644 --- a/iceoryx_dds/source/iceoryx_dds/dds/cyclone_data_reader.cpp +++ b/iceoryx_dds/source/iceoryx_dds/dds/cyclone_data_reader.cpp @@ -45,6 +45,17 @@ void iox::dds::CycloneDataReader::connect() noexcept auto subscriber = ::dds::sub::Subscriber(CycloneContext::getParticipant()); auto qos = ::dds::sub::qos::DataReaderQos(); + + /// Is required for the Gateway. When two iceoryx publisher are publishing on the same + /// topic and one publisher is located on a remote iceoryx instance connected via a + /// bidirectional dds gateway (iceoryx2dds & dds2iceoryx) then every sample is delivered + /// twice to the local subscriber. + /// Once via the local iceoryx publisher and once via dds2iceoryx which received the + /// sample from the iceoryx2dds gateway. But when we ignore the local dds writer the + /// sample is not forwarded to the local dds gateway and delivered a second time. + auto* cqos = qos.delegate().ddsc_qos(); + dds_qset_ignorelocal(cqos, DDS_IGNORELOCAL_PROCESS); + qos.delegate().ddsc_qos(cqos); qos << ::dds::core::policy::History::KeepAll(); m_impl = ::dds::sub::DataReader(subscriber, topic, qos); @@ -52,6 +63,7 @@ void iox::dds::CycloneDataReader::connect() noexcept LogDebug() << "[CycloneDataReader] Connected to topic: " << topicString; m_isConnected.store(true, std::memory_order_relaxed); + free(cqos); } } diff --git a/iceoryx_hoofs/testing/include/iceoryx_hoofs/testing/watch_dog.hpp b/iceoryx_hoofs/testing/include/iceoryx_hoofs/testing/watch_dog.hpp index 00cbeff345..dc02d2f9e3 100644 --- a/iceoryx_hoofs/testing/include/iceoryx_hoofs/testing/watch_dog.hpp +++ b/iceoryx_hoofs/testing/include/iceoryx_hoofs/testing/watch_dog.hpp @@ -30,12 +30,22 @@ using namespace iox::units::duration_literals; class Watchdog { public: - Watchdog(const iox::units::Duration& timeToWait) noexcept + explicit Watchdog(const iox::units::Duration& timeToWait) noexcept : m_timeToWait(timeToWait) { } + Watchdog(const Watchdog&) = delete; + Watchdog(Watchdog&&) = delete; + Watchdog& operator=(const Watchdog&) = delete; + Watchdog& operator=(Watchdog&&) = delete; + ~Watchdog() noexcept + { + reset(); + } + + void reset() noexcept { if (m_watchdog.joinable()) { @@ -44,14 +54,25 @@ class Watchdog } } - void watchAndActOnFailure(std::function f) noexcept + void watchAndActOnFailure(const std::function& actionOnFailure = std::function()) noexcept { + reset(); + m_watchdog = std::thread([=] { m_watchdogSemaphore.timedWait(m_timeToWait) .and_then([&](auto& result) { if (result == iox::posix::SemaphoreWaitState::TIMEOUT) { - f(); + std::cerr << "Watchdog observed no reaction after " << m_timeToWait << ". Taking measures!" + << std::endl; + if (actionOnFailure) + { + actionOnFailure(); + } + else + { + std::terminate(); + } EXPECT_TRUE(false); } }) diff --git a/iceoryx_posh/include/iceoryx_posh/gateway/gateway_base.hpp b/iceoryx_posh/include/iceoryx_posh/gateway/gateway_base.hpp index e73a35b650..d05ffd484d 100644 --- a/iceoryx_posh/include/iceoryx_posh/gateway/gateway_base.hpp +++ b/iceoryx_posh/include/iceoryx_posh/gateway/gateway_base.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -59,6 +60,7 @@ class GatewayBase protected: // Needed for unit testing GatewayBase() noexcept = default; + capro::Interfaces getInterface() const noexcept; protected: popo::InterfacePort m_interfaceImpl{nullptr}; diff --git a/iceoryx_posh/include/iceoryx_posh/gateway/toml_gateway_config_parser.hpp b/iceoryx_posh/include/iceoryx_posh/gateway/toml_gateway_config_parser.hpp index 3334f70710..7e16a88b4a 100644 --- a/iceoryx_posh/include/iceoryx_posh/gateway/toml_gateway_config_parser.hpp +++ b/iceoryx_posh/include/iceoryx_posh/gateway/toml_gateway_config_parser.hpp @@ -61,8 +61,8 @@ static constexpr const char GATEWAY_CONFIG_SERVICE_EVENT_NAME[] = "event"; class TomlGatewayConfigParser { public: - static cxx::expected parse(); - static cxx::expected parse(const roudi::ConfigFilePathString_t& path); + static cxx::expected + parse(const roudi::ConfigFilePathString_t& path = roudi::ConfigFilePathString_t(DEFAULT_CONFIG_FILE_PATH)); protected: static cxx::expected validate(const cpptoml::table& parsedToml) noexcept; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/gateway/gateway_generic.inl b/iceoryx_posh/include/iceoryx_posh/internal/gateway/gateway_generic.inl index fe14d15ec8..2f712f55b2 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/gateway/gateway_generic.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/gateway/gateway_generic.inl @@ -94,7 +94,12 @@ GatewayGeneric::addChannel(const capro::ServiceDescription } else { - auto result = channel_t::create(service, options); + auto result = channel_t::create({service.getServiceIDString(), + service.getInstanceIDString(), + service.getEventIDString(), + {0U, 0U, 0U, 0U}, + this->getInterface()}, + options); if (result.has_error()) { return cxx::error(GatewayError::UNSUCCESSFUL_CHANNEL_CREATION); diff --git a/iceoryx_posh/source/gateway/gateway_base.cpp b/iceoryx_posh/source/gateway/gateway_base.cpp index 051283723c..d62877d8bc 100644 --- a/iceoryx_posh/source/gateway/gateway_base.cpp +++ b/iceoryx_posh/source/gateway/gateway_base.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -35,6 +36,11 @@ GatewayBase::~GatewayBase() noexcept } } +capro::Interfaces GatewayBase::getInterface() const noexcept +{ + return m_interfaceImpl.getCaProServiceDescription().getSourceInterface(); +} + bool GatewayBase::getCaProMessage(CaproMessage& msg) noexcept { auto maybeCaproMessage = m_interfaceImpl.tryGetCaProMessage(); @@ -49,6 +55,5 @@ bool GatewayBase::getCaProMessage(CaproMessage& msg) noexcept return false; } } - } // namespace gw } // namespace iox diff --git a/iceoryx_posh/source/gateway/toml_gateway_config_parser.cpp b/iceoryx_posh/source/gateway/toml_gateway_config_parser.cpp index b42673174c..29a89e6ab0 100644 --- a/iceoryx_posh/source/gateway/toml_gateway_config_parser.cpp +++ b/iceoryx_posh/source/gateway/toml_gateway_config_parser.cpp @@ -20,12 +20,6 @@ #include -iox::cxx::expected -iox::config::TomlGatewayConfigParser::parse() -{ - return iox::config::TomlGatewayConfigParser::parse(DEFAULT_CONFIG_FILE_PATH); -} - iox::cxx::expected iox::config::TomlGatewayConfigParser::parse(const roudi::ConfigFilePathString_t& path) { diff --git a/iceoryx_posh/source/popo/ports/interface_port.cpp b/iceoryx_posh/source/popo/ports/interface_port.cpp index 76aef9b91c..d4bdf025a5 100644 --- a/iceoryx_posh/source/popo/ports/interface_port.cpp +++ b/iceoryx_posh/source/popo/ports/interface_port.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -33,6 +34,17 @@ cxx::optional InterfacePort::tryGetCaProMessage() noexcept void InterfacePort::dispatchCaProMessage(const capro::CaproMessage& caProMessage) noexcept { + auto messageInterface = caProMessage.m_serviceDescription.getSourceInterface(); + auto myInterface = getMembers()->m_serviceDescription.getSourceInterface(); + + // Do only forward messages for internal ports or if the ports interface is different + // than the messageInterface otherwise it is possible that a gateway subscribes to its + // own services. This would lead to running messages in cycles. + if (myInterface != iox::capro::Interfaces::INTERNAL && myInterface == messageInterface) + { + return; + } + if (!getMembers()->m_caproMessageFiFo.push(caProMessage)) { // information loss for this interface port diff --git a/iceoryx_posh/source/roudi/port_manager.cpp b/iceoryx_posh/source/roudi/port_manager.cpp index 16ecdef6a9..881ad87e6b 100644 --- a/iceoryx_posh/source/roudi/port_manager.cpp +++ b/iceoryx_posh/source/roudi/port_manager.cpp @@ -365,6 +365,17 @@ bool PortManager::sendToAllMatchingPublisherPorts(const capro::CaproMessage& mes for (auto publisherPortData : m_portPool->getPublisherPortDataList()) { PublisherPortRouDiType publisherPort(publisherPortData); + + auto messageInterface = message.m_serviceDescription.getSourceInterface(); + auto publisherInterface = publisherPort.getCaProServiceDescription().getSourceInterface(); + + // internal publisher receive all messages all other publishers receive only messages if + // they do not have the same interface otherwise we have cyclic connections in gateways + if (publisherInterface != capro::Interfaces::INTERNAL && publisherInterface == messageInterface) + { + break; + } + if (subscriberSource.getCaProServiceDescription() == publisherPort.getCaProServiceDescription() && !(publisherPort.getSubscriberTooSlowPolicy() == popo::SubscriberTooSlowPolicy::DISCARD_OLDEST_DATA && subscriberSource.getQueueFullPolicy() == popo::QueueFullPolicy::BLOCK_PUBLISHER)) @@ -394,6 +405,17 @@ void PortManager::sendToAllMatchingSubscriberPorts(const capro::CaproMessage& me for (auto subscriberPortData : m_portPool->getSubscriberPortDataList()) { SubscriberPortType subscriberPort(subscriberPortData); + + auto messageInterface = message.m_serviceDescription.getSourceInterface(); + auto subscriberInterface = subscriberPort.getCaProServiceDescription().getSourceInterface(); + + // internal subscriber receive all messages all other subscribers receive only messages if + // they do not have the same interface otherwise we have cyclic connections in gateways + if (subscriberInterface != capro::Interfaces::INTERNAL && subscriberInterface == messageInterface) + { + break; + } + if (subscriberPort.getCaProServiceDescription() == publisherSource.getCaProServiceDescription() && !(publisherSource.getSubscriberTooSlowPolicy() == popo::SubscriberTooSlowPolicy::DISCARD_OLDEST_DATA && subscriberPort.getQueueFullPolicy() == popo::QueueFullPolicy::BLOCK_PUBLISHER)) diff --git a/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp b/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp index ad21d3f2b7..0cc72f23c9 100644 --- a/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp +++ b/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp @@ -57,22 +57,36 @@ class PublisherSubscriberCommunication_test : public RouDi_GTest template std::unique_ptr> - createPublisher(const SubscriberTooSlowPolicy policy = SubscriberTooSlowPolicy::DISCARD_OLDEST_DATA) + createPublisher(const SubscriberTooSlowPolicy policy = SubscriberTooSlowPolicy::DISCARD_OLDEST_DATA, + const capro::Interfaces interface = capro::Interfaces::INTERNAL) { iox::popo::PublisherOptions options; options.subscriberTooSlowPolicy = policy; - return std::make_unique>(m_serviceDescription, options); + return std::make_unique>( + capro::ServiceDescription{m_serviceDescription.getServiceIDString(), + m_serviceDescription.getInstanceIDString(), + m_serviceDescription.getEventIDString(), + {0U, 0U, 0U, 0U}, + interface}, + options); } template std::unique_ptr> createSubscriber(const QueueFullPolicy policy = QueueFullPolicy::DISCARD_OLDEST_DATA, - const uint64_t queueCapacity = SubscriberPortData::ChunkQueueData_t::MAX_CAPACITY) + const uint64_t queueCapacity = SubscriberPortData::ChunkQueueData_t::MAX_CAPACITY, + const capro::Interfaces interface = capro::Interfaces::INTERNAL) { iox::popo::SubscriberOptions options; options.queueFullPolicy = policy; options.queueCapacity = queueCapacity; - return std::make_unique>(m_serviceDescription, options); + return std::make_unique>( + capro::ServiceDescription{m_serviceDescription.getServiceIDString(), + m_serviceDescription.getInstanceIDString(), + m_serviceDescription.getEventIDString(), + {0U, 0U, 0U, 0U}, + interface}, + options); } @@ -81,6 +95,88 @@ class PublisherSubscriberCommunication_test : public RouDi_GTest "PublisherSubscriberCommunication", "IntegrationTest", "AllHailHypnotoad"}; }; +TEST_F(PublisherSubscriberCommunication_test, AllSubscriberInterfacesCanBeSubscribedToPublisherWithInternalInterface) +{ + auto publisher = createPublisher(); + this->InterOpWait(); + + std::vector>> subscribers; + for (uint16_t interface = 0U; interface < static_cast(capro::Interfaces::INTERFACE_END); ++interface) + { + subscribers.emplace_back(createSubscriber(QueueFullPolicy::DISCARD_OLDEST_DATA, + SubscriberPortData::ChunkQueueData_t::MAX_CAPACITY, + static_cast(interface))); + } + this->InterOpWait(); + + constexpr int TRANSMISSION_DATA = 1337; + ASSERT_FALSE(publisher->loan() + .and_then([&](auto& sample) { + *sample = TRANSMISSION_DATA; + sample.publish(); + }) + .has_error()); + + for (auto& subscriber : subscribers) + { + EXPECT_FALSE(subscriber->take() + .and_then([&](auto& sample) { EXPECT_THAT(*sample, Eq(TRANSMISSION_DATA)); }) + .has_error()); + } +} + +TEST_F(PublisherSubscriberCommunication_test, SubscriberCanOnlyBeSubscribedWhenInterfaceDiffersFromPublisher) +{ + for (uint16_t publisherInterface = 0U; publisherInterface < static_cast(capro::Interfaces::INTERFACE_END); + ++publisherInterface) + { + if (static_cast(publisherInterface) == capro::Interfaces::INTERNAL) + { + continue; + } + + m_watchdog.watchAndActOnFailure(); + + auto publisher = createPublisher(SubscriberTooSlowPolicy::DISCARD_OLDEST_DATA, + static_cast(publisherInterface)); + this->InterOpWait(); + + std::vector>> subscribers; + for (uint16_t subscriberInterface = 0U; + subscriberInterface < static_cast(capro::Interfaces::INTERFACE_END); + ++subscriberInterface) + { + subscribers.emplace_back(createSubscriber(QueueFullPolicy::DISCARD_OLDEST_DATA, + SubscriberPortData::ChunkQueueData_t::MAX_CAPACITY, + static_cast(subscriberInterface))); + } + this->InterOpWait(); + + constexpr int TRANSMISSION_DATA = 1337; + ASSERT_FALSE(publisher->loan() + .and_then([&](auto& sample) { + *sample = TRANSMISSION_DATA; + sample.publish(); + }) + .has_error()); + + for (auto& subscriber : subscribers) + { + if (subscriber->getServiceDescription().getSourceInterface() + == static_cast(publisherInterface)) + { + EXPECT_TRUE(subscriber->take().has_error()); + } + else + { + EXPECT_FALSE(subscriber->take() + .and_then([&](auto& sample) { EXPECT_THAT(*sample, Eq(TRANSMISSION_DATA)); }) + .has_error()); + } + } + } +} + TEST_F(PublisherSubscriberCommunication_test, SendingComplexDataType_forward_list) { using Type_t = ComplexDataType, 5>>; diff --git a/iceoryx_posh/test/mocks/gateway_base_mock.hpp b/iceoryx_posh/test/mocks/gateway_base_mock.hpp index 590da2c44d..424a443374 100644 --- a/iceoryx_posh/test/mocks/gateway_base_mock.hpp +++ b/iceoryx_posh/test/mocks/gateway_base_mock.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,6 +26,9 @@ class MockGatewayBase public: MockGatewayBase(const iox::capro::Interfaces){}; MOCK_METHOD1(getCaProMessage, bool(iox::capro::CaproMessage&)); + + protected: + MOCK_METHOD0(getInterface, iox::capro::Interfaces()); }; #endif // IOX_POSH_MOCKS_GATEWAY_BASE_HPP diff --git a/iceoryx_posh/test/moduletests/test_popo_interface_port.cpp b/iceoryx_posh/test/moduletests/test_popo_interface_port.cpp new file mode 100644 index 0000000000..937d12ed4b --- /dev/null +++ b/iceoryx_posh/test/moduletests/test_popo_interface_port.cpp @@ -0,0 +1,105 @@ +// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// +// 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. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "iceoryx_posh/internal/popo/ports/interface_port.hpp" + +#include "test.hpp" + +namespace +{ +using namespace iox; +using namespace iox::popo; +using namespace ::testing; +using ::testing::_; + +class InterfacePort_test : public Test +{ + public: + void SetUp() + { + } + + void TearDown() + { + } + + capro::CaproMessage generateMessage(const capro::Interfaces interface) noexcept + { + return {capro::CaproMessageType::ACK, {"Cheri", "Cheri", "Hypnotoad", {0U, 0U, 0U, 0U}, interface}}; + } +}; + + +TEST_F(InterfacePort_test, EveryMessageCanBeDispatchedWhenInterfacePortIsInternal) +{ + InterfacePortData interfacePortData("", capro::Interfaces::INTERNAL); + + for (uint16_t interface = 0; interface < static_cast(capro::Interfaces::INTERFACE_END); ++interface) + { + auto message = generateMessage(static_cast(interface)); + InterfacePort(&interfacePortData).dispatchCaProMessage(message); + + auto maybeMessage = InterfacePort(&interfacePortData).tryGetCaProMessage(); + ASSERT_TRUE(maybeMessage.has_value()); + EXPECT_THAT(message.m_serviceDescription, Eq(maybeMessage->m_serviceDescription)); + } +} + +TEST_F(InterfacePort_test, MessageDispatchedIfInterfacesDifferWhenInterfacePortIsNotInternal) +{ + for (uint16_t myInterface = 0; myInterface < static_cast(capro::Interfaces::INTERFACE_END); ++myInterface) + { + if (static_cast(myInterface) == capro::Interfaces::INTERNAL) + { + continue; + } + + InterfacePortData interfacePortData("", static_cast(myInterface)); + + for (uint16_t interface = 0; interface < static_cast(capro::Interfaces::INTERFACE_END); ++interface) + { + if (interface != myInterface) + { + auto message = generateMessage(static_cast(interface)); + InterfacePort(&interfacePortData).dispatchCaProMessage(message); + + auto maybeMessage = InterfacePort(&interfacePortData).tryGetCaProMessage(); + ASSERT_TRUE(maybeMessage.has_value()); + EXPECT_THAT(message.m_serviceDescription, Eq(maybeMessage->m_serviceDescription)); + } + } + } +} + +TEST_F(InterfacePort_test, MessageDiscaredIfInterfacesAreEqualWhenInterfacePortIsNotInternal) +{ + for (uint16_t myInterface = 0; myInterface < static_cast(capro::Interfaces::INTERFACE_END); ++myInterface) + { + if (static_cast(myInterface) == capro::Interfaces::INTERNAL) + { + continue; + } + + InterfacePortData interfacePortData("", static_cast(myInterface)); + + auto message = generateMessage(static_cast(myInterface)); + InterfacePort(&interfacePortData).dispatchCaProMessage(message); + + auto maybeMessage = InterfacePort(&interfacePortData).tryGetCaProMessage(); + ASSERT_FALSE(maybeMessage.has_value()); + } +} +} // namespace diff --git a/iceoryx_posh/test/moduletests/test_popo_toml_gateway_config_parser.cpp b/iceoryx_posh/test/moduletests/test_popo_toml_gateway_config_parser.cpp index 60feeb0f9a..2f92cd6658 100644 --- a/iceoryx_posh/test/moduletests/test_popo_toml_gateway_config_parser.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_toml_gateway_config_parser.cpp @@ -204,7 +204,9 @@ TEST_F(TomlGatewayConfigParserSuiteTest, NoServicesInConfigReturnIncompleteConfi EXPECT_EQ(TomlGatewayConfigParseError::INCOMPLETE_CONFIGURATION, result.get_error()); } -TEST_F(TomlGatewayConfigParserSuiteTest, ParseWithoutParameterTakeDefaultPathReturnNoError) +// Without argument the iceoryx default config in /etc/iceoryx/gateway_config.toml is used. Then this +// test fails on every machine which is using such a config. +TEST_F(TomlGatewayConfigParserSuiteTest, DISABLED_ParseWithoutParameterTakeDefaultPathReturnNoError) { auto result = TomlGatewayConfigParser::parse(); ASSERT_FALSE(result.has_error()); diff --git a/tools/ci/build-test-macos-with-sanitizers.sh b/tools/ci/build-test-macos-with-sanitizers.sh index 46b62fa9d4..7f53a82aa3 100755 --- a/tools/ci/build-test-macos-with-sanitizers.sh +++ b/tools/ci/build-test-macos-with-sanitizers.sh @@ -31,7 +31,7 @@ $(clang --version)" msg "building and installing dependencies" # tinfo library which is required by iceoryx_introspection isn't available in mac -brew install ncurses +brew install ncurses wget mkdir -p bison_build cd bison_build wget https://ftp.gnu.org/gnu/bison/bison-3.5.1.tar.gz diff --git a/tools/ci/build-test-macos.sh b/tools/ci/build-test-macos.sh index 81272910b6..6d3cf1c5b8 100755 --- a/tools/ci/build-test-macos.sh +++ b/tools/ci/build-test-macos.sh @@ -31,7 +31,7 @@ $(clang --version)" msg "building and installing dependencies" # tinfo library which is required by iceoryx_introspection isn't available in mac -brew install ncurses +brew install ncurses wget mkdir -p bison_build cd bison_build wget https://ftp.gnu.org/gnu/bison/bison-3.5.1.tar.gz