diff --git a/CMakeLists.txt b/CMakeLists.txt index 709c8f91..ab2176a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,21 +88,44 @@ set(generated_path "${CMAKE_BINARY_DIR}/generated") set(generated_files "${generated_path}/get_factory.cpp") list(APPEND generated_files "${generated_path}/get_mappings.cpp") -# generate per package compilation units to keep the memory usage low -ament_index_get_resources(ros2_message_packages "rosidl_interfaces") -foreach(message_package ${ros2_message_packages}) - find_package(${message_package} QUIET REQUIRED) - message(STATUS "Found ${message_package}: ${${message_package}_VERSION} (${${message_package}_DIR})") - if(NOT "${message_package}" STREQUAL "builtin_interfaces") - list(APPEND generated_files "${generated_path}/${message_package}_factories.cpp") +# generate per interface compilation units to keep the memory usage low +ament_index_get_resources(ros2_interface_packages "rosidl_interfaces") +foreach(package_name ${ros2_interface_packages}) + find_package(${package_name} QUIET REQUIRED) + message(STATUS "Found ${package_name}: ${${package_name}_VERSION} (${${package_name}_DIR})") + if(NOT "${package_name}" STREQUAL "builtin_interfaces") + list(APPEND generated_files "${generated_path}/${package_name}_factories.cpp") + list(APPEND generated_files "${generated_path}/${package_name}_factories.hpp") + foreach(interface_file ${${package_name}_INTERFACE_FILES}) + file(TO_CMAKE_PATH "${interface_file}" interface_name) + get_filename_component(interface_basename "${interface_name}" NAME_WE) + # skipping actions and request and response messages of services + if(NOT "${interface_name}" MATCHES "^(msg|srv)/" OR "${interface_basename}" MATCHES "_(Request|Response)$") + continue() + endif() + string(REPLACE "/" "__" interface_name "${interface_name}") + get_filename_component(interface_name "${interface_name}" NAME_WE) + list(APPEND generated_files "${generated_path}/${package_name}__${interface_name}__factories.cpp") + endforeach() endif() endforeach() +set(target_dependencies + "bin/ros1_bridge_generate_factories" + "resource/get_factory.cpp.em" + "resource/get_mappings.cpp.em" + "resource/interface_factories.cpp.em" + "resource/pkg_factories.cpp.em" + "resource/pkg_factories.hpp.em" + "ros1_bridge/__init__.py") + add_custom_command( OUTPUT ${generated_files} COMMAND ${PYTHON_EXECUTABLE} bin/ros1_bridge_generate_factories --output-path "${generated_path}" --template-dir resource - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + DEPENDS ${target_dependencies} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating factories for interface types") if(NOT WIN32) # ignore warning in ROS 1 message headers @@ -152,7 +175,7 @@ add_library(${PROJECT_NAME} SHARED ${generated_files}) ament_target_dependencies(${PROJECT_NAME} ${prefixed_ros1_message_packages} - ${ros2_message_packages} + ${ros2_interface_packages} "rclcpp" "ros1_roscpp" "ros1_std_msgs") @@ -165,7 +188,7 @@ install(TARGETS ${PROJECT_NAME} custom_executable(static_bridge "src/static_bridge.cpp" ROS1_DEPENDENCIES - TARGET_DEPENDENCIES ${ros2_message_packages}) + TARGET_DEPENDENCIES ${ros2_interface_packages}) target_link_libraries(static_bridge ${PROJECT_NAME}) @@ -179,7 +202,7 @@ target_link_libraries(parameter_bridge custom_executable(dynamic_bridge "src/dynamic_bridge.cpp" ROS1_DEPENDENCIES - TARGET_DEPENDENCIES ${ros2_message_packages}) + TARGET_DEPENDENCIES ${ros2_interface_packages}) target_link_libraries(dynamic_bridge ${PROJECT_NAME}) diff --git a/README.md b/README.md index 8bfd3892..1ce9f47b 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,6 @@ Before continuing you should have the prerequisites for building ROS 2 from sour In the past, building this package required patches to ROS 1, but in the latest releases that is no longer the case. If you run into trouble first make sure you have at least version `1.11.16` of `ros_comm` and `rosbag`. -Also, building the ROS 1 bridge can consume a tremendous amount of memory (almost 4 GB of free RAM per thread while compiling) to the point that it can easily overwhelm a computer if done with parallel compilation enabled. -As such, we recommend first building everything else as usual, then coming back to build the ROS 1 bridge without parallel compilation. - The bridge uses `pkg-config` to find ROS 1 packages. ROS 2 packages are found through CMake using `find_package()`. Therefore the `CMAKE_PREFIX_PATH` must not contain paths from ROS 1 which would overlay ROS 2 packages. diff --git a/resource/interface_factories.cpp.em b/resource/interface_factories.cpp.em new file mode 100644 index 00000000..57f71a4b --- /dev/null +++ b/resource/interface_factories.cpp.em @@ -0,0 +1,300 @@ +// generated from ros1_bridge/resource/interface_factories.cpp.em + +@############################################### +@# +@# Factory template specializations for a +@# specific message type +@# +@# EmPy template for generating +@# ______factories.cpp +@# +@############################################### +@# Start of Template +@# +@# Context: +@# - ros2_package_name (str) +@# The ROS 2 package name of this file +@# - mappings (list of ros1_bridge.Mapping) +@# Mapping between messages as well as their fields +@############################################### +@ +@{ +from ros1_bridge import camel_case_to_lower_case_underscore +}@ +#include "@(ros2_package_name)_factories.hpp" + +#include + +#include "rclcpp/rclcpp.hpp" + +// include builtin interfaces +#include + +// include ROS 1 services +@[for service in mapped_services]@ +#include <@(service["ros1_package"])/@(service["ros1_name"]).h> +@[end for]@ + +// include ROS 2 services +@[for service in mapped_services]@ +#include <@(service["ros2_package"])/srv/@(camel_case_to_lower_case_underscore(service["ros2_name"])).hpp> +@[end for]@ + +namespace ros1_bridge +{ + +std::shared_ptr +get_factory_@(ros2_package_name)__@(interface_type)__@(interface.message_name)(const std::string & ros1_type_name, const std::string & ros2_type_name) +{ +@[if not mapped_msgs]@ + (void)ros1_type_name; + (void)ros2_type_name; +@[else]@ + // mapping from string to specialized template +@[end if]@ +@[for m in mapped_msgs]@ + if ( + (ros1_type_name == "@(m.ros1_msg.package_name)/@(m.ros1_msg.message_name)" || + ros1_type_name == "") && + ros2_type_name == "@(m.ros2_msg.package_name)/@(m.ros2_msg.message_name)") + { + return std::make_shared< + Factory< + @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name), + @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) + > + >("@(m.ros1_msg.package_name)/@(m.ros1_msg.message_name)", ros2_type_name); + } +@[end for]@ + return std::shared_ptr(); +} + +std::unique_ptr +get_service_factory_@(ros2_package_name)__@(interface_type)__@(interface.message_name)(const std::string & ros_id, const std::string & package_name, const std::string & service_name) +{ +@[if not mapped_services]@ + (void)ros_id; + (void)package_name; + (void)service_name; +@[end if]@ +@[for service in mapped_services]@ + if ( + ( + ros_id == "ros1" && + package_name == "@(service["ros1_package"])" && + service_name == "@(service["ros1_name"])" + ) || ( + ros_id == "ros2" && + package_name == "@(service["ros2_package"])" && + service_name == "@(service["ros2_name"])" + ) + ) { + return std::unique_ptr(new ServiceFactory< + @(service["ros1_package"])::@(service["ros1_name"]), + @(service["ros2_package"])::srv::@(service["ros2_name"]) + >); + } +@[end for]@ + return nullptr; +} +@ +// conversion functions for available interfaces +@[for m in mapped_msgs]@ + +template<> +void +Factory< + @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name), + @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) +>::convert_1_to_2( + const @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name) & ros1_msg, + @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) & ros2_msg) +{ +@[ if not m.fields_1_to_2]@ + (void)ros1_msg; + (void)ros2_msg; +@[ end if]@ +@[ for ros1_field, ros2_field in m.fields_1_to_2.items()]@ +@[ if not ros2_field.type.is_array]@ + // convert non-array field +@[ if not ros2_field.type.pkg_name]@ + // convert primitive field + ros2_msg.@(ros2_field.name) = ros1_msg.@(ros1_field.name); +@[ elif ros2_field.type.pkg_name == 'builtin_interfaces']@ + // convert builtin field + ros1_bridge::convert_1_to_2(ros1_msg.@(ros1_field.name), ros2_msg.@(ros2_field.name)); +@[ else]@ + // convert sub message field + Factory< + @(ros1_field.pkg_name)::@(ros1_field.msg_name), + @(ros2_field.type.pkg_name)::msg::@(ros2_field.type.type) + >::convert_1_to_2( + ros1_msg.@(ros1_field.name), ros2_msg.@(ros2_field.name)); +@[ end if]@ +@[ else]@ + // convert array field +@[ if not ros2_field.type.array_size or ros2_field.type.is_upper_bound]@ + // ensure array size +@[ if ros2_field.type.is_upper_bound]@ + // check boundary + assert(ros1_msg.@(ros1_field.name).size() <= @(ros2_field.type.array_size)); +@[ end if]@ + // dynamic arrays must be resized + ros2_msg.@(ros2_field.name).resize(ros1_msg.@(ros1_field.name).size()); +@[ end if]@ +@[ if not ros2_field.type.pkg_name]@ + // convert primitive array elements + std::copy( + ros1_msg.@(ros1_field.name).begin(), + ros1_msg.@(ros1_field.name).end(), + ros2_msg.@(ros2_field.name).begin()); +@[ else]@ + // copy element wise since the type is different + { + auto ros1_it = ros1_msg.@(ros1_field.name).begin(); + auto ros2_it = ros2_msg.@(ros2_field.name).begin(); + for ( + ; + ros1_it != ros1_msg.@(ros1_field.name).end() && + ros2_it != ros2_msg.@(ros2_field.name).end(); + ++ros1_it, ++ros2_it + ) + { + // convert sub message element +@[ if ros2_field.type.pkg_name == 'builtin_interfaces']@ + ros1_bridge::convert_1_to_2(*ros1_it, *ros2_it); +@[ else]@ + Factory< + @(ros1_field.pkg_name)::@(ros1_field.msg_name), + @(ros2_field.type.pkg_name)::msg::@(ros2_field.type.type) + >::convert_1_to_2( + *ros1_it, *ros2_it); +@[ end if]@ + } + } +@[ end if]@ +@[ end if]@ +@[ end for]@ +} + +template<> +void +Factory< + @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name), + @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) +>::convert_2_to_1( + const @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) & ros2_msg, + @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name) & ros1_msg) +{ +@[ if not m.fields_2_to_1]@ + (void)ros2_msg; + (void)ros1_msg; +@[ end if]@ +@[ for ros2_field, ros1_field in m.fields_2_to_1.items()]@ +@[ if not ros2_field.type.is_array]@ + // convert non-array field +@[ if not ros2_field.type.pkg_name]@ + // convert primitive field + ros1_msg.@(ros1_field.name) = ros2_msg.@(ros2_field.name); +@[ elif ros2_field.type.pkg_name == 'builtin_interfaces']@ + // convert builtin field + ros1_bridge::convert_2_to_1(ros2_msg.@(ros2_field.name), ros1_msg.@(ros1_field.name)); +@[ else]@ + // convert sub message field + Factory< + @(ros1_field.pkg_name)::@(ros1_field.msg_name), + @(ros2_field.type.pkg_name)::msg::@(ros2_field.type.type) + >::convert_2_to_1( + ros2_msg.@(ros2_field.name), ros1_msg.@(ros1_field.name)); +@[ end if]@ +@[ else]@ + // convert array field +@[ if not ros2_field.type.array_size or ros2_field.type.is_upper_bound]@ + // ensure array size + // dynamic arrays must be resized + ros1_msg.@(ros1_field.name).resize(ros2_msg.@(ros2_field.name).size()); +@[ end if]@ +@[ if not ros2_field.type.pkg_name]@ + // convert primitive array elements + std::copy( + ros2_msg.@(ros2_field.name).begin(), + ros2_msg.@(ros2_field.name).end(), + ros1_msg.@(ros1_field.name).begin()); +@[ else]@ + // copy element wise since the type is different + { + auto ros2_it = ros2_msg.@(ros2_field.name).begin(); + auto ros1_it = ros1_msg.@(ros1_field.name).begin(); + for ( + ; + ros2_it != ros2_msg.@(ros2_field.name).end() && + ros1_it != ros1_msg.@(ros1_field.name).end(); + ++ros2_it, ++ros1_it + ) + { + // convert sub message element +@[ if ros2_field.type.pkg_name == 'builtin_interfaces']@ + ros1_bridge::convert_2_to_1(*ros2_it, *ros1_it); +@[ else]@ + Factory< + @(ros1_field.pkg_name)::@(ros1_field.msg_name), + @(ros2_field.type.pkg_name)::msg::@(ros2_field.type.type) + >::convert_2_to_1( + *ros2_it, *ros1_it); +@[ end if]@ + } + } +@[ end if]@ +@[ end if]@ +@[ end for]@ +} +@[end for]@ +@ +@[for service in mapped_services]@ + +@[ for frm, to in [("1", "2"), ("2", "1")]]@ +@[ for type in ["Request", "Response"]]@ +template <> +void ServiceFactory< + @(service["ros1_package"])::@(service["ros1_name"]), + @(service["ros2_package"])::srv::@(service["ros2_name"]) +>::translate_@(frm)_to_@(to)( +@[ if frm == "1"]@ + const @(service["ros1_package"])::@(service["ros1_name"])::@(type)& req1, + @(service["ros2_package"])::srv::@(service["ros2_name"])::@(type)& req2 +@[ else]@ + const @(service["ros2_package"])::srv::@(service["ros2_name"])::@(type)& req2, + @(service["ros1_package"])::@(service["ros1_name"])::@(type)& req1 +@[ end if]@ +) { +@[ for field in service["fields"][type.lower()]]@ +@[ if field["array"]]@ + req@(to).@(field["ros" + to]["name"]).resize(req@(frm).@(field["ros" + frm]["name"]).size()); + auto @(field["ros1"]["name"])1_it = req1.@(field["ros1"]["name"]).begin(); + auto @(field["ros2"]["name"])2_it = req2.@(field["ros2"]["name"]).begin(); + while ( + @(field["ros1"]["name"])1_it != req1.@(field["ros1"]["name"]).end() && + @(field["ros2"]["name"])2_it != req2.@(field["ros2"]["name"]).end() + ) { + auto & @(field["ros1"]["name"])1 = *(@(field["ros1"]["name"])1_it++); + auto & @(field["ros2"]["name"])2 = *(@(field["ros2"]["name"])2_it++); +@[ else]@ + auto & @(field["ros1"]["name"])1 = req1.@(field["ros1"]["name"]); + auto & @(field["ros2"]["name"])2 = req2.@(field["ros2"]["name"]); +@[ end if]@ +@[ if field["basic"]]@ + @(field["ros2"]["name"])@(to) = @(field["ros1"]["name"])@(frm); +@[ else]@ + Factory<@(field["ros1"]["cpptype"]),@(field["ros2"]["cpptype"])>::convert_@(frm)_to_@(to)(@ +@(field["ros2"]["name"])@(frm), @(field["ros1"]["name"])@(to)); +@[ end if]@ +@[ if field["array"]]@ + } +@[ end if]@ +@[ end for]@ +} + +@[ end for]@ +@[ end for]@ +@[end for]@ +} // namespace ros1_bridge diff --git a/resource/pkg_factories.cpp.em b/resource/pkg_factories.cpp.em index d79ed2a1..c8d2ab21 100644 --- a/resource/pkg_factories.cpp.em +++ b/resource/pkg_factories.cpp.em @@ -17,277 +17,43 @@ @# Mapping between messages as well as their fields @############################################### @ -@{ -from ros1_bridge import camel_case_to_lower_case_underscore -}@ -#include "rclcpp/rclcpp.hpp" #include "@(ros2_package_name)_factories.hpp" -// include builtin interfaces -#include - -// include ROS 1 services -@[for service in services]@ -#include <@(service["ros1_package"])/@(service["ros1_name"]).h> -@[end for]@ - -// include ROS 2 services -@[for service in services]@ -#include <@(service["ros2_package"])/srv/@(camel_case_to_lower_case_underscore(service["ros2_name"])).hpp> -@[end for]@ - namespace ros1_bridge { std::shared_ptr get_factory_@(ros2_package_name)(const std::string & ros1_type_name, const std::string & ros2_type_name) { -@[if not mappings]@ +@[if not ros2_msg_types]@ (void)ros1_type_name; (void)ros2_type_name; +@[else]@ + std::shared_ptr factory; @[end if]@ - // mapping from string to specialized template -@[for m in mappings]@ - if ( - (ros1_type_name == "@(m.ros1_msg.package_name)/@(m.ros1_msg.message_name)" || - ros1_type_name == "") && - ros2_type_name == "@(m.ros2_msg.package_name)/@(m.ros2_msg.message_name)") - { - return std::make_shared< - Factory< - @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name), - @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) - > - >("@(m.ros1_msg.package_name)/@(m.ros1_msg.message_name)", ros2_type_name); +@[for m in ros2_msg_types]@ + factory = get_factory_@(ros2_package_name)__msg__@(m.message_name)(ros1_type_name, ros2_type_name); + if (factory) { + return factory; } @[end for]@ return std::shared_ptr(); } -// conversion functions for available interfaces -@[for m in mappings]@ - -template<> -void -Factory< - @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name), - @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) ->::convert_1_to_2( - const @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name) & ros1_msg, - @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) & ros2_msg) -{ -@[ if not m.fields_1_to_2]@ - (void)ros1_msg; - (void)ros2_msg; -@[ end if]@ -@[ for ros1_field, ros2_field in m.fields_1_to_2.items()]@ -@[ if not ros2_field.type.is_array]@ - // convert non-array field -@[ if not ros2_field.type.pkg_name]@ - // convert primitive field - ros2_msg.@(ros2_field.name) = ros1_msg.@(ros1_field.name); -@[ elif ros2_field.type.pkg_name == 'builtin_interfaces']@ - // convert builtin field - ros1_bridge::convert_1_to_2(ros1_msg.@(ros1_field.name), ros2_msg.@(ros2_field.name)); -@[ else]@ - // convert sub message field - Factory< - @(ros1_field.pkg_name)::@(ros1_field.msg_name), - @(ros2_field.type.pkg_name)::msg::@(ros2_field.type.type) - >::convert_1_to_2( - ros1_msg.@(ros1_field.name), ros2_msg.@(ros2_field.name)); -@[ end if]@ -@[ else]@ - // convert array field -@[ if not ros2_field.type.array_size or ros2_field.type.is_upper_bound]@ - // ensure array size -@[ if ros2_field.type.is_upper_bound]@ - // check boundary - assert(ros1_msg.@(ros1_field.name).size() <= @(ros2_field.type.array_size)); -@[ end if]@ - // dynamic arrays must be resized - ros2_msg.@(ros2_field.name).resize(ros1_msg.@(ros1_field.name).size()); -@[ end if]@ -@[ if not ros2_field.type.pkg_name]@ - // convert primitive array elements - std::copy( - ros1_msg.@(ros1_field.name).begin(), - ros1_msg.@(ros1_field.name).end(), - ros2_msg.@(ros2_field.name).begin()); -@[ else]@ - // copy element wise since the type is different - { - auto ros1_it = ros1_msg.@(ros1_field.name).begin(); - auto ros2_it = ros2_msg.@(ros2_field.name).begin(); - for ( - ; - ros1_it != ros1_msg.@(ros1_field.name).end() && - ros2_it != ros2_msg.@(ros2_field.name).end(); - ++ros1_it, ++ros2_it - ) - { - // convert sub message element -@[ if ros2_field.type.pkg_name == 'builtin_interfaces']@ - ros1_bridge::convert_1_to_2(*ros1_it, *ros2_it); -@[ else]@ - Factory< - @(ros1_field.pkg_name)::@(ros1_field.msg_name), - @(ros2_field.type.pkg_name)::msg::@(ros2_field.type.type) - >::convert_1_to_2( - *ros1_it, *ros2_it); -@[ end if]@ - } - } -@[ end if]@ -@[ end if]@ -@[ end for]@ -} - -template<> -void -Factory< - @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name), - @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) ->::convert_2_to_1( - const @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) & ros2_msg, - @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name) & ros1_msg) -{ -@[ if not m.fields_2_to_1]@ - (void)ros2_msg; - (void)ros1_msg; -@[ end if]@ -@[ for ros2_field, ros1_field in m.fields_2_to_1.items()]@ -@[ if not ros2_field.type.is_array]@ - // convert non-array field -@[ if not ros2_field.type.pkg_name]@ - // convert primitive field - ros1_msg.@(ros1_field.name) = ros2_msg.@(ros2_field.name); -@[ elif ros2_field.type.pkg_name == 'builtin_interfaces']@ - // convert builtin field - ros1_bridge::convert_2_to_1(ros2_msg.@(ros2_field.name), ros1_msg.@(ros1_field.name)); -@[ else]@ - // convert sub message field - Factory< - @(ros1_field.pkg_name)::@(ros1_field.msg_name), - @(ros2_field.type.pkg_name)::msg::@(ros2_field.type.type) - >::convert_2_to_1( - ros2_msg.@(ros2_field.name), ros1_msg.@(ros1_field.name)); -@[ end if]@ -@[ else]@ - // convert array field -@[ if not ros2_field.type.array_size or ros2_field.type.is_upper_bound]@ - // ensure array size - // dynamic arrays must be resized - ros1_msg.@(ros1_field.name).resize(ros2_msg.@(ros2_field.name).size()); -@[ end if]@ -@[ if not ros2_field.type.pkg_name]@ - // convert primitive array elements - std::copy( - ros2_msg.@(ros2_field.name).begin(), - ros2_msg.@(ros2_field.name).end(), - ros1_msg.@(ros1_field.name).begin()); -@[ else]@ - // copy element wise since the type is different - { - auto ros2_it = ros2_msg.@(ros2_field.name).begin(); - auto ros1_it = ros1_msg.@(ros1_field.name).begin(); - for ( - ; - ros2_it != ros2_msg.@(ros2_field.name).end() && - ros1_it != ros1_msg.@(ros1_field.name).end(); - ++ros2_it, ++ros1_it - ) - { - // convert sub message element -@[ if ros2_field.type.pkg_name == 'builtin_interfaces']@ - ros1_bridge::convert_2_to_1(*ros2_it, *ros1_it); -@[ else]@ - Factory< - @(ros1_field.pkg_name)::@(ros1_field.msg_name), - @(ros2_field.type.pkg_name)::msg::@(ros2_field.type.type) - >::convert_2_to_1( - *ros2_it, *ros1_it); -@[ end if]@ - } - } -@[ end if]@ -@[ end if]@ -@[ end for]@ -} - -@[end for]@ - -@[for service in services]@ -@[ for frm, to in [("1", "2"), ("2", "1")]]@ -@[ for type in ["Request", "Response"]]@ -template <> -void ServiceFactory< - @(service["ros1_package"])::@(service["ros1_name"]), - @(service["ros2_package"])::srv::@(service["ros2_name"]) ->::translate_@(frm)_to_@(to)( -@[ if frm == "1"]@ - const @(service["ros1_package"])::@(service["ros1_name"])::@(type)& req1, - @(service["ros2_package"])::srv::@(service["ros2_name"])::@(type)& req2 -@[ else]@ - const @(service["ros2_package"])::srv::@(service["ros2_name"])::@(type)& req2, - @(service["ros1_package"])::@(service["ros1_name"])::@(type)& req1 -@[ end if]@ -) { -@[ for field in service["fields"][type.lower()]]@ -@[ if field["array"]]@ - req@(to).@(field["ros" + to]["name"]).resize(req@(frm).@(field["ros" + frm]["name"]).size()); - auto @(field["ros1"]["name"])1_it = req1.@(field["ros1"]["name"]).begin(); - auto @(field["ros2"]["name"])2_it = req2.@(field["ros2"]["name"]).begin(); - while ( - @(field["ros1"]["name"])1_it != req1.@(field["ros1"]["name"]).end() && - @(field["ros2"]["name"])2_it != req2.@(field["ros2"]["name"]).end() - ) { - auto & @(field["ros1"]["name"])1 = *(@(field["ros1"]["name"])1_it++); - auto & @(field["ros2"]["name"])2 = *(@(field["ros2"]["name"])2_it++); -@[ else]@ - auto & @(field["ros1"]["name"])1 = req1.@(field["ros1"]["name"]); - auto & @(field["ros2"]["name"])2 = req2.@(field["ros2"]["name"]); -@[ end if]@ -@[ if field["basic"]]@ - @(field["ros2"]["name"])@(to) = @(field["ros1"]["name"])@(frm); -@[ else]@ - Factory<@(field["ros1"]["cpptype"]),@(field["ros2"]["cpptype"])>::convert_@(frm)_to_@(to)(@ -@(field["ros2"]["name"])@(frm), @(field["ros1"]["name"])@(to)); -@[ end if]@ -@[ if field["array"]]@ - } -@[ end if]@ -@[ end for]@ -} - -@[ end for]@ -@[ end for]@ -@[end for]@ - std::unique_ptr get_service_factory_@(ros2_package_name)(const std::string & ros_id, const std::string & package_name, const std::string & service_name) { -@[if not services]@ +@[if not ros2_srv_types]@ (void)ros_id; (void)package_name; (void)service_name; +@[else]@ + std::unique_ptr factory; @[end if]@ -@[for service in services]@ - if ( - ( - ros_id == "ros1" && - package_name == "@(service["ros1_package"])" && - service_name == "@(service["ros1_name"])" - ) || ( - ros_id == "ros2" && - package_name == "@(service["ros2_package"])" && - service_name == "@(service["ros2_name"])" - ) - ) { - return std::unique_ptr(new ServiceFactory< - @(service["ros1_package"])::@(service["ros1_name"]), - @(service["ros2_package"])::srv::@(service["ros2_name"]) - >); +@[for s in ros2_srv_types]@ + factory = get_service_factory_@(ros2_package_name)__srv__@(s.message_name)(ros_id, package_name, service_name); + if (factory) { + return factory; } @[end for]@ return nullptr; diff --git a/resource/pkg_factories.hpp.em b/resource/pkg_factories.hpp.em index 2d19955d..670d4fd2 100644 --- a/resource/pkg_factories.hpp.em +++ b/resource/pkg_factories.hpp.em @@ -24,15 +24,18 @@ @{ from ros1_bridge import camel_case_to_lower_case_underscore }@ +#include +#include + #include // include ROS 1 messages -@[for ros1_msg in ros1_msgs]@ +@[for ros1_msg in mapped_ros1_msgs]@ #include <@(ros1_msg.package_name)/@(ros1_msg.message_name).h> @[end for]@ // include ROS 2 messages -@[for ros2_msg in ros2_msgs]@ +@[for ros2_msg in mapped_ros2_msgs]@ #include <@(ros2_msg.package_name)/msg/@(camel_case_to_lower_case_underscore(ros2_msg.message_name)).hpp> @[end for]@ @@ -41,9 +44,19 @@ namespace ros1_bridge std::shared_ptr get_factory_@(ros2_package_name)(const std::string & ros1_type_name, const std::string & ros2_type_name); +@[for m in ros2_msg_types]@ + +std::shared_ptr +get_factory_@(ros2_package_name)__msg__@(m.message_name)(const std::string & ros1_type_name, const std::string & ros2_type_name); +@[end for]@ std::unique_ptr get_service_factory_@(ros2_package_name)(const std::string & ros_id, const std::string & package_name, const std::string & service_name); +@[for s in ros2_srv_types]@ + +std::unique_ptr +get_service_factory_@(ros2_package_name)__srv__@(s.message_name)(const std::string & ros_id, const std::string & package_name, const std::string & service_name); +@[end for]@ // conversion functions for available interfaces @[for m in mappings]@ diff --git a/ros1_bridge/__init__.py b/ros1_bridge/__init__.py index 68ee91f9..14f3ec32 100644 --- a/ros1_bridge/__init__.py +++ b/ros1_bridge/__init__.py @@ -85,29 +85,67 @@ def generate_cpp(output_path, template_dir): expand_template(template_file, data, output_file) for ros2_package_name in data['ros2_package_names']: - for extension in ['cpp', 'hpp']: - data_pkg = { - 'ros2_package_name': ros2_package_name, - 'mappings': [ - m for m in data['mappings'] - if m.ros2_msg.package_name == ros2_package_name], - 'services': [ - s for s in data['services'] - if s['ros2_package'] == ros2_package_name] - } - if extension == 'hpp': - data_pkg.update({ - 'ros1_msgs': [ - m.ros1_msg for m in data['mappings'] - if m.ros2_msg.package_name == ros2_package_name], - 'ros2_msgs': [ - m.ros2_msg for m in data['mappings'] - if m.ros2_msg.package_name == ros2_package_name], - }) - template_file = os.path.join(template_dir, 'pkg_factories.%s.em' % extension) - output_file = os.path.join( - output_path, '%s_factories.%s' % (ros2_package_name, extension)) - expand_template(template_file, data_pkg, output_file) + data_pkg_hpp = { + 'ros2_package_name': ros2_package_name, + # include directives and template types + 'mapped_ros1_msgs': [ + m.ros1_msg for m in data['mappings'] + if m.ros2_msg.package_name == ros2_package_name], + 'mapped_ros2_msgs': [ + m.ros2_msg for m in data['mappings'] + if m.ros2_msg.package_name == ros2_package_name], + # forward declaration of factory functions + 'ros2_msg_types': [ + m for m in data['all_ros2_msgs'] + if m.package_name == ros2_package_name], + 'ros2_srv_types': [ + s for s in data['all_ros2_srvs'] + if s.package_name == ros2_package_name], + # forward declaration of template specializations + 'mappings': [ + m for m in data['mappings'] + if m.ros2_msg.package_name == ros2_package_name], + } + template_file = os.path.join(template_dir, 'pkg_factories.hpp.em') + output_file = os.path.join( + output_path, '%s_factories.hpp' % ros2_package_name) + expand_template(template_file, data_pkg_hpp, output_file) + + data_pkg_cpp = { + 'ros2_package_name': ros2_package_name, + # call interface specific factory functions + 'ros2_msg_types': data_pkg_hpp['ros2_msg_types'], + 'ros2_srv_types': data_pkg_hpp['ros2_srv_types'], + } + template_file = os.path.join(template_dir, 'pkg_factories.cpp.em') + output_file = os.path.join( + output_path, '%s_factories.cpp' % ros2_package_name) + expand_template(template_file, data_pkg_cpp, output_file) + + for interface_type, interfaces in zip( + ['msg', 'srv'], [data['all_ros2_msgs'], data['all_ros2_srvs']] + ): + for interface in interfaces: + if interface.package_name != ros2_package_name: + continue + data_idl_cpp = { + 'ros2_package_name': ros2_package_name, + 'interface_type': interface_type, + 'interface': interface, + 'mapped_msgs': [ + m for m in data['mappings'] + if m.ros2_msg.package_name == ros2_package_name and + m.ros2_msg.message_name == interface.message_name], + 'mapped_services': [ + s for s in data['services'] + if s['ros2_package'] == ros2_package_name and + s['ros2_name'] == interface.message_name], + } + template_file = os.path.join(template_dir, 'interface_factories.cpp.em') + output_file = os.path.join( + output_path, '%s__%s__%s__factories.cpp' % + (ros2_package_name, interface_type, interface.message_name)) + expand_template(template_file, data_idl_cpp, output_file) def generate_messages(rospack=None): @@ -167,7 +205,8 @@ def generate_messages(rospack=None): 'ros1_msgs': [m.ros1_msg for m in ordered_mappings], 'ros2_msgs': [m.ros2_msg for m in ordered_mappings], 'mappings': ordered_mappings, - 'ros2_package_names_msg': ros2_package_names + 'ros2_package_names_msg': ros2_package_names, + 'all_ros2_msgs': ros2_msgs, } @@ -177,7 +216,8 @@ def generate_services(rospack=None): services = determine_common_services(ros1_srvs, ros2_srvs, mapping_rules) return { 'services': services, - 'ros2_package_names_srv': ros2_pkgs + 'ros2_package_names_srv': ros2_pkgs, + 'all_ros2_srvs': ros2_srvs, }