diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc54d822..f840977a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,6 @@ jobs: - uses: actions/checkout@v3 with: path: src/integration-service - submodules: 'true' - name: Download required dependencies run: | diff --git a/.gitignore b/.gitignore index 005319ed..cfbad01c 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,6 @@ ENV/ .catkin_workspace devel/ build/ + +# vscode +.txt.user diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 9b26033c..99b165b0 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -28,6 +28,8 @@ option(IS_COMPILE_DEBUG "Compile the Integration Service project in debug mode." option(BUILD_LIBRARY "Compile the Integration Service" ON) +option(IS_XTYPES_THIRDPARTY "Allow to download thirdparty xtypes repository when needed." ON) + if(DEFINED CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE_LOWERCASE "" CACHE STRING "Build type to lowercase") string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWERCASE) @@ -56,7 +58,33 @@ endif() # External dependencies for the Integration Service Core library ############################################################################### if(BUILD_LIBRARY) - find_package(xtypes REQUIRED) + find_package(xtypes QUIET) + + # if not externally provided use the thirdparty as external project + if(IS_XTYPES_THIRDPARTY AND NOT xtypes_FOUND) + message(STATUS "Updating submodules") + execute_process( + COMMAND git submodule update --init + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../ + RESULT_VARIABLE EXECUTE_RESULT + ) + + if(NOT EXECUTE_RESULT EQUAL 0) + message(ERROR "Cannot update git submodules and xtypes are missing") + else() + include(ExternalProject) + + ExternalProject_Add(xtypes-ext + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/xtypes + INSTALL_DIR ${CMAKE_INSTALL_PREFIX} + CMAKE_ARGS + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} + -DCMAKE_INSTALL_PREFIX:PATH=/xtypes + ) + endif() + endif() find_package(yaml-cpp REQUIRED) @@ -82,6 +110,36 @@ if(BUILD_LIBRARY) src/Instance.cpp ) + # if xtypes is not available use the ExternalProject + if(TARGET xtypes-ext) + + # force the build + add_dependencies(${PROJECT_NAME} xtypes-ext) + + # mimick xtypes target properties + # propagation is required for tools, utils and System Handles + target_compile_features(${PROJECT_NAME} + INTERFACE + cxx_std_17 + cxx_variadic_macros + ) + + ExternalProject_Get_Property(xtypes-ext INSTALL_DIR) + + target_include_directories(${PROJECT_NAME} + PUBLIC + ${INSTALL_DIR}/xtypes/include + ${INSTALL_DIR}/xtypes/thirdparty/cpp-peglib/include) + + if(MSVC) + target_compile_options(${PROJECT_NAME} + PUBLIC + /Zc:preprocessor + /execution-charset:UTF-8) + endif(MSVC) + + endif() + if (Sanitizers_FOUND) add_sanitizers(${PROJECT_NAME}) endif() @@ -127,7 +185,7 @@ if(BUILD_LIBRARY) target_link_libraries(${PROJECT_NAME} PUBLIC yaml-cpp - xtypes + $ PRIVATE Boost::program_options $<$:dl> @@ -139,7 +197,6 @@ if(BUILD_LIBRARY) $ $ $ - #$ #propagate the xtypes headers ) target_compile_definitions(${PROJECT_NAME} @@ -313,4 +370,4 @@ if(BUILD_API_REFERENCE) DESTINATION ${CMAKE_INSTALL_PREFIX} ) -endif() \ No newline at end of file +endif() diff --git a/core/cmake/common/gtest.cmake b/core/cmake/common/gtest.cmake index d814ca63..0a7e319c 100644 --- a/core/cmake/common/gtest.cmake +++ b/core/cmake/common/gtest.cmake @@ -77,7 +77,7 @@ macro(add_gtest) endif() foreach(GTEST_SOURCE_FILE ${GTEST_SOURCES}) - file(STRINGS ${GTEST_SOURCE_FILE} GTEST_TEST_NAMES REGEX "^TEST") + file(STRINGS ${GTEST_SOURCE_FILE} GTEST_TEST_NAMES REGEX "^TEST_F|^TEST") foreach(GTEST_TEST_NAME ${GTEST_TEST_NAMES}) string(REGEX REPLACE ["\) \(,"] ";" GTEST_TEST_NAME ${GTEST_TEST_NAME}) list(GET GTEST_TEST_NAME 1 GTEST_GROUP_NAME) @@ -85,6 +85,7 @@ macro(add_gtest) add_test(NAME ${GTEST_GROUP_NAME}.${GTEST_TEST_NAME} COMMAND ${command} --gtest_filter=${GTEST_GROUP_NAME}.${GTEST_TEST_NAME}:*/${GTEST_GROUP_NAME}.${GTEST_TEST_NAME}/*) + # Add environment set(GTEST_ENVIRONMENT "") if(WIN32) diff --git a/core/include/is/core/Config.hpp b/core/include/is/core/Config.hpp index d85a58fc..802422ce 100644 --- a/core/include/is/core/Config.hpp +++ b/core/include/is/core/Config.hpp @@ -57,6 +57,7 @@ struct MiddlewareConfig std::string type; std::vector types_from; YAML::Node config_node; + YAML::Node types_node; }; /** diff --git a/core/include/is/systemhandle/SystemHandle.hpp b/core/include/is/systemhandle/SystemHandle.hpp index a5be35fb..ba2f262d 100644 --- a/core/include/is/systemhandle/SystemHandle.hpp +++ b/core/include/is/systemhandle/SystemHandle.hpp @@ -178,6 +178,20 @@ class SystemHandle * @returns `true` if the SystemHandle is still working; `false` otherwise. */ virtual bool spin_once() = 0; + + /** + * @brief Perform additional actions prior to configure the System Handle regarding types. + * + * @param[in] types_node A *YAML* node containing the types definition. This may be an empty node. + * + * @returns `true` if the preprocess stage ends successfully; `false` otherwise. + */ + virtual bool preprocess_types( + const YAML::Node& types_node) + { + (void) types_node; + return true; + } }; /** diff --git a/core/src/Config.cpp b/core/src/Config.cpp index c7de6493..c6fc1d90 100644 --- a/core/src/Config.cpp +++ b/core/src/Config.cpp @@ -19,6 +19,7 @@ #include #include +#include #include namespace eprosima { @@ -406,8 +407,8 @@ bool add_topic_or_service_config( const YAML::Node& node, const std::map& predefined_routes, std::map& config_map, - std::function set_type, - std::function set_reply_type, + std::function set_type, + std::function set_reply_type, std::function set_route, std::function(const YAML::Node&)> parse_route) { @@ -845,7 +846,7 @@ bool Config::parse( _m_middlewares.insert( std::make_pair( - middleware_alias, MiddlewareConfig{middleware, types_from, config})); + middleware_alias, MiddlewareConfig{middleware, types_from, config, config_node["types"]})); } if (_m_middlewares.size() < 2) @@ -1135,13 +1136,11 @@ bool Config::load_middlewares( */ is::internal::SystemHandleInfo info = is::internal::Register::get(middleware_type); - if (!info) + if (!info || !info.handle->preprocess_types(mw_config.types_node)) { return false; } - bool configured = true; - /** * Now, it iterates the middleware required types map. * For each middleware, it checks which types it needs, and places them into @@ -1245,24 +1244,40 @@ bool Config::load_middlewares( } } } - - /** - * Finally, now that the SystemHandleInfo struct is filled with all its types, it - * calls to the SystemHandle::configure override function for the selected middleware. - */ - configured = info.handle->configure( - requirements->second, mw_config.config_node, info.types); + } + else + { + // Check if another middleware relies on types builtin or dynamically loaded by the middleware but not + // explicit in the configuration file + if ( middlewares.end() == + std::find_if(middlewares.begin(), middlewares.end(), [&mw_name](const Entry& mw) + { + auto& from = mw.second.types_from; + return mw_name != mw.first && std::find(from.begin(), from.end(), mw_name) != from.end(); + })) + { + logger << utils::Logger::Level::ERROR + << "The middleware '" << mw_name + << "' has no types associated" << std::endl; + return false; + } } /** - * If the middleware was correctly configured, it inserts it within the info_map. + * Finally, now that the SystemHandleInfo struct is filled with all its types, it + * calls to the SystemHandle::configure override function for the selected middleware. */ - if (configured) + if ( info.handle->configure( + requirements->second, mw_config.config_node, info.types)) { + // If the middleware was correctly configured, it inserts it within the info_map. info_map.insert(std::make_pair(mw_name, std::move(info))); } else { + logger << utils::Logger::Level::ERROR + << "The middleware '" << mw_name + << "' configuration step failed" << std::endl; return false; } } diff --git a/examples/basic/fastdds_ros2_dynamic__helloworld.yaml b/examples/basic/fastdds_ros2_dynamic__helloworld.yaml new file mode 100644 index 00000000..ceb29ef7 --- /dev/null +++ b/examples/basic/fastdds_ros2_dynamic__helloworld.yaml @@ -0,0 +1,37 @@ +types: + idls: + - > + #include + + module custom_msgs + { + module msg + { + struct HelloWorld2 + { + custom_msgs::msg::HelloWorld h; + }; + }; + }; + + paths: ["/is_ws/src/is/examples/utils/dds/DDSHelloWorld/"] + +systems: + dds_3: + type: fastdds + participant: + domain_id: 3 + + dds_5: + type: ros2_dynamic + namespace: foo/ + node_name: "my_ros_node" + domain: 0 + +routes: + domain_3_to_5: { from: dds_3, to: dds_5 } + domain_5_to_3: { from: dds_5, to: dds_3 } + +topics: + hello_domain_5: { type: "custom_msgs::msg::HelloWorld2", route: domain_3_to_5} + hello_domain_3: { type: "custom_msgs::msg::HelloWorld2", route: domain_5_to_3} diff --git a/thirdparty/COLCON_IGNORE b/thirdparty/COLCON_IGNORE new file mode 100644 index 00000000..e69de29b