Skip to content

Commit

Permalink
Merge pull request #183 from ros2/interface_specific_compilation_units
Browse files Browse the repository at this point in the history
Interface specific compilation units
  • Loading branch information
dirk-thomas authored Apr 16, 2019
2 parents f684d95 + cd0ef16 commit c9911be
Show file tree
Hide file tree
Showing 6 changed files with 428 additions and 289 deletions.
45 changes: 34 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand All @@ -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})

Expand All @@ -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})

Expand Down
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
300 changes: 300 additions & 0 deletions resource/interface_factories.cpp.em
Original file line number Diff line number Diff line change
@@ -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
@# <pkgname>__<type>__<interfacename>__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 <algorithm>

#include "rclcpp/rclcpp.hpp"

// include builtin interfaces
#include <ros1_bridge/convert_builtin_interfaces.hpp>

// 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<FactoryInterface>
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<FactoryInterface>();
}

std::unique_ptr<ServiceFactoryInterface>
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<ServiceFactoryInterface>(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
Loading

0 comments on commit c9911be

Please sign in to comment.