Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interface specific compilation units #183

Merged
merged 3 commits into from
Apr 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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