From f861280c504d311991c4152635778edea711592f Mon Sep 17 00:00:00 2001 From: Esteve Fernandez Date: Fri, 18 Dec 2020 07:44:25 +0100 Subject: [PATCH 01/33] Pin version of rcl_interfaces --- ros2_java_desktop.repos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ros2_java_desktop.repos b/ros2_java_desktop.repos index 9cedbc35..1ff6490e 100644 --- a/ros2_java_desktop.repos +++ b/ros2_java_desktop.repos @@ -54,7 +54,7 @@ repositories: ros2/rcl_interfaces: type: git url: https://github.com/ros2/rcl_interfaces.git - version: master + version: db27f0e8619460848d80c1442f7fec0c56ee63e5 ros2/rclpy: type: git url: https://github.com/ros2/rclpy.git From b4e64f71538a057b3a1dbb49315aabeeedab3c88 Mon Sep 17 00:00:00 2001 From: Esteve Fernandez Date: Fri, 18 Dec 2020 07:45:04 +0100 Subject: [PATCH 02/33] Pin version of rcl_interfaces --- ros2_java_android.repos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ros2_java_android.repos b/ros2_java_android.repos index 35a3475b..74effa53 100644 --- a/ros2_java_android.repos +++ b/ros2_java_android.repos @@ -38,7 +38,7 @@ repositories: ros2/rcl_interfaces: type: git url: https://github.com/ros2/rcl_interfaces.git - version: master + version: db27f0e8619460848d80c1442f7fec0c56ee63e5 ros2/rmw: type: git url: https://github.com/ros2/rmw.git From 94d1eff4e1a689ffdf3838f5a69e2c1bdd29de71 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Fri, 10 Jan 2020 05:44:50 -0800 Subject: [PATCH 03/33] Update rosidl_generator_java for Dashing compatibility (#66) * Update rosidl_generator_java for new IDL pipeline Supporting ROS Dashing or later. Signed-off-by: Jacob Perron * Fix name JNI name mangling Accidentally broken during update. Signed-off-by: Jacob Perron * Remove java compile flags This remove some compile warnings. Signed-off-by: Jacob Perron * Refactor to support services Signed-off-by: Jacob Perron * Wide string support Signed-off-by: Jacob Perron * Avoid duplicate includes Signed-off-by: Jacob Perron * Use test_interface_files Signed-off-by: Jacob Perron * Support for actions Signed-off-by: Jacob Perron * Add suffix for long literals Signed-off-by: Jacob Perron * Handle unsigned literals Though Java supports unsigned values, it doesn't support unsigned literals (ie. literals that are larger than the max signed value). As a workaround, we can convert the literal to it's negative equivalent. Signed-off-by: Jacob Perron * Fix escape string function Signed-off-by: Jacob Perron * Use cast to workaround integer literals Otherwise the compiler complains about potential loss of data. Signed-off-by: Jacob Perron * Minor refactor Signed-off-by: Jacob Perron * Remove TODO Signed-off-by: Jacob Perron --- rcljava_common/CMakeLists.txt | 1 + .../rcljava/interfaces/ActionDefinition.java | 18 + rosidl_generator_java/CMakeLists.txt | 10 +- .../bin/rosidl_generator_java | 6 +- .../cmake/custom_command.cmake | 20 +- .../cmake/register_java.cmake | 4 +- ...l_generator_java_generate_interfaces.cmake | 125 ++--- rosidl_generator_java/package.xml | 5 +- rosidl_generator_java/resource/action.cpp.em | 48 ++ rosidl_generator_java/resource/action.java.em | 79 +++ rosidl_generator_java/resource/idl.cpp.em | 110 ++++ rosidl_generator_java/resource/idl.java.em | 87 +++ rosidl_generator_java/resource/msg.cpp.em | 529 +++++++++--------- rosidl_generator_java/resource/msg.java.em | 170 +++--- rosidl_generator_java/resource/srv.cpp.em | 37 +- rosidl_generator_java/resource/srv.java.em | 51 +- .../rosidl_generator_java/__init__.py | 310 +++++----- .../org/ros2/generator/InterfacesTest.java | 4 +- 18 files changed, 994 insertions(+), 620 deletions(-) create mode 100644 rcljava_common/src/main/java/org/ros2/rcljava/interfaces/ActionDefinition.java create mode 100644 rosidl_generator_java/resource/action.cpp.em create mode 100644 rosidl_generator_java/resource/action.java.em create mode 100644 rosidl_generator_java/resource/idl.cpp.em create mode 100644 rosidl_generator_java/resource/idl.java.em diff --git a/rcljava_common/CMakeLists.txt b/rcljava_common/CMakeLists.txt index 822a6bb3..3bc04fde 100644 --- a/rcljava_common/CMakeLists.txt +++ b/rcljava_common/CMakeLists.txt @@ -36,6 +36,7 @@ set(${PROJECT_NAME}_java_sources "src/main/java/org/ros2/rcljava/common/JNIUtils.java" "src/main/java/org/ros2/rcljava/exceptions/RCLException.java" "src/main/java/org/ros2/rcljava/exceptions/RCLReturn.java" + "src/main/java/org/ros2/rcljava/interfaces/ActionDefinition.java" "src/main/java/org/ros2/rcljava/interfaces/Disposable.java" "src/main/java/org/ros2/rcljava/interfaces/MessageDefinition.java" "src/main/java/org/ros2/rcljava/interfaces/ServiceDefinition.java" diff --git a/rcljava_common/src/main/java/org/ros2/rcljava/interfaces/ActionDefinition.java b/rcljava_common/src/main/java/org/ros2/rcljava/interfaces/ActionDefinition.java new file mode 100644 index 00000000..9681463b --- /dev/null +++ b/rcljava_common/src/main/java/org/ros2/rcljava/interfaces/ActionDefinition.java @@ -0,0 +1,18 @@ +/* Copyright 2019 Open Source Robotics Foundation, Inc. + * + * 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. + */ + +package org.ros2.rcljava.interfaces; + +public interface ActionDefinition {} diff --git a/rosidl_generator_java/CMakeLists.txt b/rosidl_generator_java/CMakeLists.txt index 62705679..13b0c00d 100644 --- a/rosidl_generator_java/CMakeLists.txt +++ b/rosidl_generator_java/CMakeLists.txt @@ -24,19 +24,17 @@ endif() include(UseJava) include(JavaExtra) -set(CMAKE_JAVA_COMPILE_FLAGS "-source" "1.6" "-target" "1.6") - ament_index_register_resource("rosidl_generator_packages") if(BUILD_TESTING) find_package(rosidl_cmake REQUIRED) find_package(rosidl_generator_c REQUIRED) - - find_package(ament_lint_auto REQUIRED) + find_package(test_interface_files REQUIRED) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() + # TODO(jacobperron): Use test_interface_files instead and update tests set(message_files "msg/Bool.msg" "msg/Byte.msg" @@ -70,7 +68,9 @@ if(BUILD_TESTING) "${CMAKE_CURRENT_SOURCE_DIR}/resource" ) - rosidl_generate_interfaces(${PROJECT_NAME} ${message_files} + rosidl_generate_interfaces(${PROJECT_NAME} + ${message_files} + ${test_interface_files_SRV_FILES} SKIP_INSTALL ) diff --git a/rosidl_generator_java/bin/rosidl_generator_java b/rosidl_generator_java/bin/rosidl_generator_java index 95ca2a86..a0f676d4 100755 --- a/rosidl_generator_java/bin/rosidl_generator_java +++ b/rosidl_generator_java/bin/rosidl_generator_java @@ -27,17 +27,13 @@ def main(argv=sys.argv[1:]): '--generator-arguments-file', required=True, help='The location of the file containing the generator arguments') - parser.add_argument( - '--typesupport-impl', - required=True, - help='The typesupport implementation targeted by the C interface') parser.add_argument( '--typesupport-impls', required=True, help='All the available typesupport implementations') args = parser.parse_args(argv) - return generate_java(args.generator_arguments_file, args.typesupport_impl, args.typesupport_impls) + return generate_java(args.generator_arguments_file, args.typesupport_impls.split(';')) if __name__ == '__main__': diff --git a/rosidl_generator_java/cmake/custom_command.cmake b/rosidl_generator_java/cmake/custom_command.cmake index 753c0835..db7d1cc1 100644 --- a/rosidl_generator_java/cmake/custom_command.cmake +++ b/rosidl_generator_java/cmake/custom_command.cmake @@ -11,19 +11,14 @@ # 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. - - add_custom_command( OUTPUT - ${_generated_msg_java_files} - ${_generated_msg_cpp_files} - ${_generated_srv_java_files} - ${_generated_srv_cpp_files} + ${_generated_extension_files} + ${_generated_java_files} COMMAND ${PYTHON_EXECUTABLE} ${rosidl_generator_java_BIN} --generator-arguments-file "${generator_arguments_file}" - --typesupport-impl "${_typesupport_impl}" --typesupport-impls "${_typesupport_impls}" - DEPENDS ${target_dependencies} + DEPENDS ${target_dependencies} ${rosidl_generate_interfaces_TARGET} COMMENT "Generating Java code for ROS interfaces" VERBATIM ) @@ -34,17 +29,14 @@ else() add_custom_target( ${rosidl_generate_interfaces_TARGET}${_target_suffix} DEPENDS - ${_generated_msg_java_files} - ${_generated_msg_cpp_files} - ${_generated_srv_java_files} - ${_generated_srv_cpp_files} + ${_generated_extension_files} + ${_generated_java_files} ) endif() add_jar("${PROJECT_NAME}_messages_jar" SOURCES - ${_generated_msg_java_files} - ${_generated_srv_java_files} + ${_generated_java_files} OUTPUT_NAME ${PROJECT_NAME}_messages INCLUDE_JARS diff --git a/rosidl_generator_java/cmake/register_java.cmake b/rosidl_generator_java/cmake/register_java.cmake index d3e0b659..bcc41108 100644 --- a/rosidl_generator_java/cmake/register_java.cmake +++ b/rosidl_generator_java/cmake/register_java.cmake @@ -13,9 +13,9 @@ # limitations under the License. macro(rosidl_generator_java_extras BIN GENERATOR_FILES TEMPLATE_DIR) - find_package(ament_cmake_core QUIET REQUIRED) + find_package(ament_cmake_core REQUIRED) ament_register_extension( - "rosidl_generate_interfaces" + "rosidl_generate_idl_interfaces" "rosidl_generator_java" "rosidl_generator_java_generate_interfaces.cmake") diff --git a/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake b/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake index deacaa6f..e0b0ff17 100644 --- a/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake +++ b/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake @@ -39,8 +39,6 @@ if(NOT WIN32) endif() endif() -set(CMAKE_JAVA_COMPILE_FLAGS "-source" "1.6" "-target" "1.6") - # Get a list of typesupport implementations from valid rmw implementations. rosidl_generator_java_get_typesupports(_typesupport_impls) @@ -51,48 +49,53 @@ endif() set(_output_path "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_java/${PROJECT_NAME}") -set(_generated_cpp_files "") -set(_generated_msg_java_files "") -set(_generated_msg_cpp_files "") -set(_generated_srv_java_files "") -set(_generated_srv_cpp_files "") +set(_generated_extension_files "") +set(_generated_java_files "") -foreach(_idl_file ${rosidl_generate_interfaces_IDL_FILES}) - get_filename_component(_parent_folder "${_idl_file}" DIRECTORY) - get_filename_component(_parent_folder "${_parent_folder}" NAME) - get_filename_component(_module_name "${_idl_file}" NAME_WE) +foreach(_typesupport_impl ${_typesupport_impls}) + set(_generated_extension_${_typesupport_impl}_files "") +endforeach() - if(_parent_folder STREQUAL "msg") - list(APPEND _generated_msg_java_files - "${_output_path}/${_parent_folder}/${_module_name}.java" +foreach(_abs_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) + get_filename_component(_parent_folder "${_abs_idl_file}" DIRECTORY) + get_filename_component(_parent_folder "${_parent_folder}" NAME) + get_filename_component(_idl_name "${_abs_idl_file}" NAME_WE) + list(APPEND _generated_java_files + "${_output_path}/${_parent_folder}/${_idl_name}.java" + ) + # TODO(jacobperron): Is there a more robust way of detecting services and actions + # Services generate extra files + if(_parent_folder STREQUAL "srv") + list(APPEND _generated_java_files + "${_output_path}/${_parent_folder}/${_idl_name}_Request.java" + "${_output_path}/${_parent_folder}/${_idl_name}_Response.java" ) - - foreach(_typesupport_impl ${_typesupport_impls}) - list_append_unique(_generated_msg_cpp_files - "${_output_path}/${_parent_folder}/${_module_name}.ep.${_typesupport_impl}.cpp" - ) - list(APPEND _type_support_by_generated_msg_cpp_files "${_typesupport_impl}") - endforeach() - elseif(_parent_folder STREQUAL "srv") - list(APPEND _generated_srv_java_files - "${_output_path}/${_parent_folder}/${_module_name}.java" + endif() + # Actions generate extra files + if(_parent_folder STREQUAL "action") + list(APPEND _generated_java_files + "${_output_path}/${_parent_folder}/${_idl_name}_Goal.java" + "${_output_path}/${_parent_folder}/${_idl_name}_Result.java" + "${_output_path}/${_parent_folder}/${_idl_name}_Feedback.java" ) - - foreach(_typesupport_impl ${_typesupport_impls}) - list_append_unique(_generated_srv_cpp_files - "${_output_path}/${_parent_folder}/${_module_name}.ep.${_typesupport_impl}.cpp" - ) - list(APPEND _type_support_by_generated_srv_cpp_files "${_typesupport_impl}") - endforeach() - else() - message(FATAL_ERROR "Interface file with unknown parent folder: ${_idl_file}") endif() + + foreach(_typesupport_impl ${_typesupport_impls}) + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}.ep.${_typesupport_impl}.cpp") + endforeach() +endforeach() + +foreach(_typesupport_impl ${_typesupport_impls}) + foreach(_generated_file ${_generated_extension_${_typesupport_impl}_files}) + list(APPEND _generated_extension_files "${_generated_file}") + list(APPEND _typesupport_by_generated_cpp_files "${_typesupport_impl}") + endforeach() endforeach() set(_dependency_files "") set(_dependencies "") foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) - foreach(_idl_file ${${_pkg_name}_INTERFACE_FILES}) + foreach(_idl_file ${${_pkg_name}_IDL_FILES}) set(_abs_idl_file "${${_pkg_name}_DIR}/../${_idl_file}") normalize_path(_abs_idl_file "${_abs_idl_file}") list(APPEND _dependency_files "${_abs_idl_file}") @@ -103,11 +106,15 @@ endforeach() set(target_dependencies "${rosidl_generator_java_BIN}" ${rosidl_generator_java_GENERATOR_FILES} + "${rosidl_generator_java_TEMPLATE_DIR}/action.cpp.em" + "${rosidl_generator_java_TEMPLATE_DIR}/idl.cpp.em" "${rosidl_generator_java_TEMPLATE_DIR}/msg.cpp.em" "${rosidl_generator_java_TEMPLATE_DIR}/srv.cpp.em" + "${rosidl_generator_java_TEMPLATE_DIR}/action.java.em" + "${rosidl_generator_java_TEMPLATE_DIR}/idl.java.em" "${rosidl_generator_java_TEMPLATE_DIR}/msg.java.em" "${rosidl_generator_java_TEMPLATE_DIR}/srv.java.em" - ${rosidl_generate_interfaces_IDL_FILES} + ${rosidl_generate_interfaces_ABS_IDL_FILES} ${_dependency_files}) foreach(dep ${target_dependencies}) if(NOT EXISTS "${dep}") @@ -119,7 +126,7 @@ set(generator_arguments_file "${CMAKE_BINARY_DIR}/rosidl_generator_java__argumen rosidl_write_generator_arguments( "${generator_arguments_file}" PACKAGE_NAME "${PROJECT_NAME}" - ROS_INTERFACE_FILES "${rosidl_generate_interfaces_IDL_FILES}" + IDL_TUPLES "${rosidl_generate_interfaces_IDL_TUPLES}" ROS_INTERFACE_DEPENDENCIES "${_dependencies}" OUTPUT_DIR "${_output_path}" TEMPLATE_DIR "${rosidl_generator_java_TEMPLATE_DIR}" @@ -128,7 +135,6 @@ rosidl_write_generator_arguments( file(MAKE_DIRECTORY "${_output_path}") -set(_generated_extension_files "") set(_extension_dependencies "") set(_target_suffix "__java") @@ -156,10 +162,8 @@ add_subdirectory("${_subdir}" ${rosidl_generate_interfaces_TARGET}${_target_suff set_property( SOURCE - ${_generated_msg_java_files} - ${_generated_msg_cpp_files} - ${_generated_srv_java_files} - ${_generated_srv_cpp_files} + ${_generated_extension_files} + ${_generated_java_files} PROPERTY GENERATED 1) macro(set_properties _build_type) @@ -172,28 +176,21 @@ macro(set_properties _build_type) OUTPUT_NAME "${_library_path}") endmacro() -set(_type_support_by_generated_cpp_files ${_type_support_by_generated_msg_cpp_files} ${_type_support_by_generated_srv_cpp_files}) -set(_generated_cpp_files ${_generated_msg_cpp_files} ${_generated_srv_cpp_files}) - -set(_javaext_suffix "__javaext") -foreach(_generated_cpp_file ${_generated_cpp_files}) - get_filename_component(_full_folder "${_generated_cpp_file}" DIRECTORY) - get_filename_component(_package_folder "${_full_folder}" DIRECTORY) - get_filename_component(_package_name "${_package_folder}" NAME) - get_filename_component(_parent_folder "${_full_folder}" NAME) - get_filename_component(_base_msg_name "${_generated_cpp_file}" NAME_WE) - list(FIND _generated_cpp_files ${_generated_cpp_file} _file_index) - list(GET _type_support_by_generated_cpp_files ${_file_index} _typesupport_impl) +foreach(_generated_cpp_file ${_generated_extension_files}) + list(FIND _generated_extension_files ${_generated_cpp_file} _file_index) + list(GET _typesupport_by_generated_cpp_files ${_file_index} _typesupport_impl) find_package(${_typesupport_impl} REQUIRED) - set(_generated_msg_cpp_common_file "${_full_folder}/${_base_msg_name}.cpp") + get_filename_component(_parent_folder "${_generated_cpp_file}" DIRECTORY) + get_filename_component(_parent_folder "${_parent_folder}" NAME) + get_filename_component(_parent_folder "${_parent_folder}" NAME) + get_filename_component(_base_name "${_generated_cpp_file}" NAME_WE) string(REGEX REPLACE "^rosidl_typesupport_" "" _short_typesupport_impl ${_typesupport_impl}) - set(_library_name - "${_parent_folder}${_base_msg_name}${_short_typesupport_impl}" - ) + set(_library_name "${_parent_folder}${_base_name}${_short_typesupport_impl}") set(_library_path - "${_package_name}_${_parent_folder}_${_base_msg_name}__jni__${_typesupport_impl}" + "${rosidl_generate_interfaces_TARGET}_${_parent_folder}_${_base_name}__jni__${_typesupport_impl}" ) string_camel_case_to_lower_case_underscore(${_library_path} _library_path) + add_library(${_library_name} SHARED ${_generated_cpp_file} ) @@ -201,6 +198,7 @@ foreach(_generated_cpp_file ${_generated_cpp_files}) ${_library_name} ${rosidl_generate_interfaces_TARGET}${_target_suffix} ${rosidl_generate_interfaces_TARGET}__rosidl_typesupport_c + ${_target_dependencies} ) set(_extension_compile_flags "") if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -248,9 +246,6 @@ foreach(_generated_cpp_file ${_generated_cpp_files}) ) target_link_libraries(${_library_name} ${${_pkg_name}_JNI_LIBRARIES}) endforeach() - add_dependencies(${_library_name} - ${rosidl_generate_interfaces_TARGET}__${_typesupport_impl} - ) if(NOT rosidl_generate_interfaces_SKIP_INSTALL) install(TARGETS ${_library_name} ARCHIVE DESTINATION lib/jni @@ -277,7 +272,7 @@ add_custom_command( if(NOT rosidl_generate_interfaces_SKIP_INSTALL) set(_install_jar_dir "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}") - if(NOT _generated_msg_java_files STREQUAL "" OR NOT _generated_srv_java_files STREQUAL "") + if(NOT _generated_java_files STREQUAL "") install_jar("${PROJECT_NAME}_messages_jar" "share/${PROJECT_NAME}/java") ament_export_jars("share/${PROJECT_NAME}/java/${PROJECT_NAME}_messages.jar") endif() @@ -285,10 +280,8 @@ endif() if(BUILD_TESTING AND rosidl_generate_interfaces_ADD_LINTER_TESTS) if( - NOT _generated_msg_java_files STREQUAL "" OR - NOT _generated_msg_cpp_files STREQUAL "" OR - NOT _generated_srv_java_files STREQUAL "" OR - NOT _generated_srv_cpp_files STREQUAL "" + NOT _generated_java_files STREQUAL "" OR + NOT _generated_extension_files STREQUAL "" ) find_package(ament_cmake_cppcheck REQUIRED) ament_cppcheck( diff --git a/rosidl_generator_java/package.xml b/rosidl_generator_java/package.xml index 9d47a6c7..deffa059 100644 --- a/rosidl_generator_java/package.xml +++ b/rosidl_generator_java/package.xml @@ -35,15 +35,18 @@ rmw_implementation_cmake rosidl_generator_c - rosidl_parser rosidl_cmake + rosidl_parser rosidl_typesupport_c rosidl_typesupport_connext_c + rosidl_typesupport_fastrtps_c rosidl_typesupport_introspection_c rosidl_typesupport_opensplice_c + test_interface_files rosidl_generator_packages + rosidl_runtime_packages ament_cmake diff --git a/rosidl_generator_java/resource/action.cpp.em b/rosidl_generator_java/resource/action.cpp.em new file mode 100644 index 00000000..17e456bf --- /dev/null +++ b/rosidl_generator_java/resource/action.cpp.em @@ -0,0 +1,48 @@ +@# Included from rosidl_generator_java/resource/idl.cpp.em +@{ +from rosidl_generator_c import idl_structure_type_to_c_include_prefix + +action_includes = [ + 'rosidl_generator_c/action_type_support_struct.h', +] +}@ +@[for include in action_includes]@ +@[ if include in include_directives]@ +// already included above +// @ +@[ else]@ +@{include_directives.add(include)}@ +@[ end if]@ +#include "@(include)" +@[end for]@ + +#include "@(idl_structure_type_to_c_include_prefix(action.namespaced_type)).h" + +#ifdef __cplusplus +extern "C" { +#endif + +@{ +from rosidl_generator_java import get_jni_mangled_name + +action_fqn = action.namespaced_type.namespaced_name() +underscore_separated_type_name = '_'.join(action_fqn) +underscore_separated_jni_type_name = get_jni_mangled_name(action_fqn) +}@ +/* + * Class: @(underscore_separated_type_name) + * Method: getActionTypeSupport + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getActionTypeSupport(JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif + +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getActionTypeSupport(JNIEnv *, jclass) +{ + const rosidl_action_type_support_t * ts = ROSIDL_GET_ACTION_TYPE_SUPPORT( + @(package_name), @(action.namespaced_type.name)); + return reinterpret_cast(ts); +} diff --git a/rosidl_generator_java/resource/action.java.em b/rosidl_generator_java/resource/action.java.em new file mode 100644 index 00000000..070ddd61 --- /dev/null +++ b/rosidl_generator_java/resource/action.java.em @@ -0,0 +1,79 @@ +@# Generation triggered from rosidl_generator_java/resource/idl.java.em +// generated from rosidl_generator_java/resource/action.java.em +// with input from @(package_name):@(interface_path) +// generated code does not contain a copyright notice + +package @(package_name + '.' + interface_path.parts[0]); +@{ +import os +from rosidl_cmake import expand_template + +namespaces = action.namespaced_type.namespaces +type_name = action.namespaced_type.name +goal_type_name = action.goal.structure.namespaced_type.name +result_type_name = action.result.structure.namespaced_type.name +feedback_type_name = action.feedback.structure.namespaced_type.name + +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} +data.update({'message': action.goal}) +output_file = os.path.join(output_dir, *namespaces[1:], goal_type_name + '.java') +expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) + +data.update({'message': action.result}) +output_file = os.path.join(output_dir, *namespaces[1:], result_type_name + '.java') +expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) + +data.update({'message': action.feedback}) +output_file = os.path.join(output_dir, *namespaces[1:], feedback_type_name + '.java') +expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) + +action_imports = [ + 'org.ros2.rcljava.common.JNIUtils', + 'org.ros2.rcljava.interfaces.ActionDefinition', + 'org.slf4j.Logger', + 'org.slf4j.LoggerFactory', +] +}@ + +@[for action_import in action_imports]@ +import @(action_import); +@[end for]@ + +public class @(type_name) implements ActionDefinition { + + private static final Logger logger = LoggerFactory.getLogger(@(type_name).class); + + static { + try { + JNIUtils.loadTypesupport(@(type_name).class); + } catch (UnsatisfiedLinkError ule) { + logger.error("Native code library failed to load.\n" + ule); + System.exit(1); + } + } + + public static native long getActionTypeSupport(); + + public static final Class<@(type_name)_Goal> GoalType = @(type_name)_Goal.class; + + public static final Class<@(type_name)_Result> ResultType = @(type_name)_Result.class; + + public static final Class<@(type_name)_Feedback> FeedbackType = @(type_name)_Feedback.class; +} diff --git a/rosidl_generator_java/resource/idl.cpp.em b/rosidl_generator_java/resource/idl.cpp.em new file mode 100644 index 00000000..48098dd9 --- /dev/null +++ b/rosidl_generator_java/resource/idl.cpp.em @@ -0,0 +1,110 @@ +// generated from rosidl_generator_java/resource/idl.cpp.em +// with input from @(package_name):@(interface_path) +// generated code does not contain a copyright notice +@ +@####################################################################### +@# EmPy template for generating .cpp files +@# +@# Context: +@# - package_name (string) +@# - interface_path (Path relative to the directory named after the package) +@# - content (IdlContent, list of elements, e.g. Messages or Services) +@####################################################################### +@{ +include_directives = set() + +jni_includes = [ + 'jni.h', +] +include_directives.update(jni_includes) +std_includes = [ + 'cassert', + 'cstdint', + 'string', +] +include_directives.update(std_includes) +rosidl_includes = [ + 'rosidl_generator_c/message_type_support_struct.h', +] +include_directives.update(rosidl_includes) +rcljava_includes = [ + 'rcljava_common/exceptions.h', + 'rcljava_common/signatures.h', +] +include_directives.update(rcljava_includes) +}@ +@[for include in jni_includes]@ +#include <@(include)> +@[end for]@ + +@[for include in std_includes]@ +#include <@(include)> +@[end for]@ + +@[for include in rosidl_includes]@ +#include "@(include)" +@[end for]@ + +@[for include in rcljava_includes]@ +#include "@(include)" +@[end for]@ + +// Ensure that a jlong is big enough to store raw pointers +static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); + +using rcljava_common::exceptions::rcljava_throw_exception; + +@{ +jni_package_name = package_name.replace('_', '_1') +}@ +@ +@####################################################################### +@# Handle messages +@####################################################################### +@{ +from rosidl_parser.definition import Message +}@ +@[for message in content.get_elements_of_type(Message)]@ +@{ +TEMPLATE( + 'msg.cpp.em', + package_name=package_name, + jni_package_name=jni_package_name, + message=message, + include_directives=include_directives) +}@ +@[end for]@ +@ +@####################################################################### +@# Handle services +@####################################################################### +@{ +from rosidl_parser.definition import Service +}@ +@[for service in content.get_elements_of_type(Service)]@ +@{ +TEMPLATE( + 'srv.cpp.em', + package_name=package_name, + jni_package_name=jni_package_name, + service=service, + include_directives=include_directives) +}@ +@[end for]@ +@ +@####################################################################### +@# Handle actions +@####################################################################### +@{ +from rosidl_parser.definition import Action +}@ +@[for action in content.get_elements_of_type(Action)]@ +@{ +TEMPLATE( + 'action.cpp.em', + package_name=package_name, + jni_package_name=jni_package_name, + action=action, + include_directives=include_directives) +}@ +@[end for]@ diff --git a/rosidl_generator_java/resource/idl.java.em b/rosidl_generator_java/resource/idl.java.em new file mode 100644 index 00000000..f34d75c7 --- /dev/null +++ b/rosidl_generator_java/resource/idl.java.em @@ -0,0 +1,87 @@ +@####################################################################### +@# EmPy template for generating .java files +@# +@# Context: +@# - package_name (string) +@# - interface_path (Path relative to the directory named after the package) +@# - content (IdlContent, list of elements, e.g. Messages or Services) +@# Additional context: +@# - output_dir (Path) +@# - template_basepath (Path) +@####################################################################### +@ +@####################################################################### +@# Handle messages +@####################################################################### +@{ +import os + +from rosidl_cmake import expand_template +from rosidl_parser.definition import Action +from rosidl_parser.definition import Message +from rosidl_parser.definition import Service + +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +for message in content.get_elements_of_type(Message): + data.update({'message': message}) + type_name = message.structure.namespaced_type.name + namespaces = message.structure.namespaced_type.namespaces + output_file = os.path.join(output_dir, *namespaces[1:], type_name + '.java') + expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) +}@ +@ +@####################################################################### +@# Handle services +@####################################################################### +@{ +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +for service in content.get_elements_of_type(Service): + data.update({'service': service}) + type_name = service.namespaced_type.name + namespaces = service.namespaced_type.namespaces + output_file = os.path.join(output_dir, *namespaces[1:], type_name + '.java') + expand_template( + 'srv.java.em', + data, + output_file, + template_basepath=template_basepath) +}@ +@ +@####################################################################### +@# Handle actions +@####################################################################### +@{ +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +for action in content.get_elements_of_type(Action): + data.update({'action': action}) + type_name = action.namespaced_type.name + namespaces = action.namespaced_type.namespaces + output_file = os.path.join(output_dir, *namespaces[1:], type_name + '.java') + expand_template( + 'action.java.em', + data, + output_file, + template_basepath=template_basepath) +}@ diff --git a/rosidl_generator_java/resource/msg.cpp.em b/rosidl_generator_java/resource/msg.cpp.em index e1d449d4..6d1f7877 100644 --- a/rosidl_generator_java/resource/msg.cpp.em +++ b/rosidl_generator_java/resource/msg.cpp.em @@ -1,85 +1,26 @@ -// generated from rosidl_generator_java/resource/msg.cpp.em -// generated code does not contain a copyright notice - -#include - -#include -#include -#include - -// Ensure that a jlong is big enough to store raw pointers -static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); - -#include "@(spec.base_type.pkg_name)/@(subfolder)/@(module_name).h" -#include "rosidl_generator_c/message_type_support_struct.h" - -#include "rosidl_generator_c/string.h" -#include "rosidl_generator_c/string_functions.h" - -#include "rosidl_generator_c/primitives_array.h" -#include "rosidl_generator_c/primitives_array_functions.h" - -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" - -@#JNI performance tips taken from http://planet.jboss.org/post/jni_performance_the_saga_continues - -using rcljava_common::exceptions::rcljava_throw_exception; - +@# Included from rosidl_generator_java/resource/idl.cpp.em @{ - from collections import defaultdict -def get_normalized_type(type_, subfolder='msg'): - return get_java_type(type_, use_primitives=False, subfolder=subfolder).replace('.', '__') - -def get_jni_type(type_, subfolder='msg'): - return get_java_type(type_, use_primitives=False, subfolder=subfolder).replace('.', '/') - -constructor_signatures = defaultdict(lambda: "()V") -constructor_signatures['java/lang/Boolean'] = "(Z)V" -constructor_signatures['java/lang/Byte'] = "(B)V" -constructor_signatures['java/lang/Character'] = "(C)V" -constructor_signatures['java/lang/Double'] = "(D)V" -constructor_signatures['java/lang/Float'] = "(F)V" -constructor_signatures['java/lang/Integer'] = "(I)V" -constructor_signatures['java/lang/Long'] = "(J)V" -constructor_signatures['java/lang/Short'] = "(S)V" -constructor_signatures['java/util/List'] = None - -value_methods = {} -value_methods['java/lang/Boolean'] = ("booleanValue", "()Z") -value_methods['java/lang/Byte'] = ("byteValue", "()B") -value_methods['java/lang/Character'] = ("charValue", "()C") -value_methods['java/lang/Double'] = ("doubleValue", "()D") -value_methods['java/lang/Float'] = ("floatValue", "()F") -value_methods['java/lang/Integer'] = ("intValue", "()I") -value_methods['java/lang/Long'] = ("longValue", "()J") -value_methods['java/lang/Short'] = ("shortValue", "()S") - -jni_signatures = {} -jni_signatures['java/lang/Boolean'] = "Z" -jni_signatures['java/lang/Byte'] = "B" -jni_signatures['java/lang/Character'] = "C" -jni_signatures['java/lang/Double'] = "D" -jni_signatures['java/lang/Float'] = "F" -jni_signatures['java/lang/Integer'] = "I" -jni_signatures['java/lang/Long'] = "J" -jni_signatures['java/lang/Short'] = "S" - -non_primitive_types = set() - -def get_constructor_signature(type_, subfolder='msg'): - return constructor_signatures[get_jni_type(type_, subfolder=subfolder)] - -def get_value_method(type_, subfolder='msg'): - return value_methods.get(get_jni_type(type_, subfolder=subfolder)) - -def get_jni_signature(type_, subfolder='msg'): - return jni_signatures.get(get_jni_type(type_, subfolder=subfolder)) - -msg_normalized_type = get_normalized_type(spec.base_type, subfolder=subfolder) -msg_jni_type = get_jni_type(spec.base_type, subfolder=subfolder) +from rosidl_generator_c import basetype_to_c +from rosidl_generator_c import idl_structure_type_to_c_include_prefix +from rosidl_generator_java import constructor_signatures +from rosidl_generator_java import get_java_type +from rosidl_generator_java import get_jni_signature +from rosidl_generator_java import get_jni_type +from rosidl_generator_java import get_normalized_type +from rosidl_generator_java import value_methods +from rosidl_parser.definition import AbstractGenericString +from rosidl_parser.definition import AbstractString +from rosidl_parser.definition import AbstractWString +from rosidl_parser.definition import AbstractNestedType +from rosidl_parser.definition import AbstractSequence +from rosidl_parser.definition import Array +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import NamespacedType + +msg_normalized_type = '__'.join(message.structure.namespaced_type.namespaced_name()) +msg_jni_type = '/'.join(message.structure.namespaced_type.namespaced_name()) list_java_type = "java.util.List" array_list_java_type = "java.util.ArrayList" @@ -90,31 +31,49 @@ list_jni_type = "java/util/List" array_list_normalized_type = "java__util__ArrayList" array_list_jni_type = "java/util/ArrayList" +# Collect JNI types and includes cache = defaultdict(lambda: False) - cache[msg_normalized_type] = msg_jni_type - -# We do not cache strings because java.lang.String behaves differently - -unique_fields = set() - -for field in spec.fields: - if field.type.is_array: +namespaced_types = set() +includes = set() +for member in message.structure.members: + type_ = member.type + if isinstance(type_, AbstractNestedType): cache[list_normalized_type] = list_jni_type cache[array_list_normalized_type] = array_list_jni_type - - if not field.type.type == 'string': - cache[get_normalized_type(field.type)] = get_jni_type(field.type) - - if not field.type.is_primitive_type(): - non_primitive_types.add(get_jni_type(field.type)) - unique_fields.add((field.type.pkg_name, field.type.type)) + type_ = type_.value_type + if isinstance(type_, BasicType): + includes.add('rosidl_generator_c/primitives_sequence.h') + includes.add('rosidl_generator_c/primitives_sequence_functions.h') + + # We do not cache strings because java.lang.String behaves differently + if not isinstance(type_, AbstractGenericString): + cache[get_normalized_type(type_)] = get_jni_type(type_) + + if isinstance(type_, AbstractString): + includes.add('rosidl_generator_c/string.h') + includes.add('rosidl_generator_c/string_functions.h') + + if isinstance(type_, AbstractWString): + includes.add('rosidl_generator_c/u16string.h') + includes.add('rosidl_generator_c/u16string_functions.h') + + if isinstance(type_, NamespacedType): + namespaced_types.add(get_jni_type(type_)) + includes.add(idl_structure_type_to_c_include_prefix(type_) + '.h') }@ - -@[for field_pkg_name, field_type in unique_fields]@ -#include "@(field_pkg_name)/msg/@(convert_camel_case_to_lower_case_underscore(field_type)).h" -#include "@(field_pkg_name)/msg/@(convert_camel_case_to_lower_case_underscore(field_type))__functions.h" +@[for include in includes]@ +@[ if include in include_directives]@ +// already included above +// @ +@[ else]@ +@{include_directives.add(include)}@ +@[ end if]@ +#include "@(include)" @[end for]@ + +#include "@(idl_structure_type_to_c_include_prefix(message.structure.namespaced_type)).h" + #ifdef __cplusplus extern "C" { #endif @@ -126,15 +85,15 @@ JavaVM * g_vm = nullptr; @[for normalized_type, jni_type in cache.items()]@ jclass _j@(normalized_type)_class_global = nullptr; -@[if constructor_signatures[jni_type]]@ +@[ if constructor_signatures[jni_type]]@ jmethodID _j@(normalized_type)_constructor_global = nullptr; -@[end if]@ +@[ end if]@ -@[ if value_methods.get(jni_type)]@ +@[ if value_methods.get(jni_type)]@ jmethodID _j@(normalized_type)_value_global = nullptr; -@[ end if]@ +@[ end if]@ -@[ if jni_type in non_primitive_types]@ +@[ if jni_type in namespaced_types]@ jmethodID _j@(normalized_type)_from_java_converter_global = nullptr; using _j@(normalized_type)_from_java_signature = @(normalized_type) * (*)(jobject, @(normalized_type) *); jlong _j@(normalized_type)_from_java_converter_ptr_global = 0; @@ -144,255 +103,283 @@ jmethodID _j@(normalized_type)_to_java_converter_global = nullptr; using _j@(normalized_type)_to_java_signature = jobject (*)(@(normalized_type) *, jobject); jlong _j@(normalized_type)_to_java_converter_ptr_global = 0; _j@(normalized_type)_to_java_signature _j@(normalized_type)_to_java_function = nullptr; -@[ end if]@ +@[ end if]@ @[end for]@ } // namespace +@{ +from rosidl_generator_java import get_jni_mangled_name + +message_fqn = message.structure.namespaced_type.namespaced_name() +underscore_separated_type_name = '_'.join(message_fqn) +underscore_separated_jni_type_name = get_jni_mangled_name(message_fqn) +}@ /* - * Class: @(jni_package_name)_@(subfolder)_@(type_name) + * Class: @(underscore_separated_type_name) * Method: getFromJavaConverter * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getFromJavaConverter +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getFromJavaConverter (JNIEnv *, jclass); /* - * Class: @(jni_package_name)_@(subfolder)_@(type_name) + * Class: @(underscore_separated_type_name) * Method: getToJavaConverter * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getToJavaConverter +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getToJavaConverter (JNIEnv *, jclass); /* - * Class: @(jni_package_name)_@(subfolder)_@(type_name) + * Class: @(underscore_separated_type_name) * Method: getTypeSupport * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getTypeSupport +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getTypeSupport (JNIEnv *, jclass); /* - * Class: @(jni_package_name)_@(subfolder)_@(type_name) + * Class: @(underscore_separated_type_name) * Method: getDestructor * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getDestructor +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getDestructor (JNIEnv *, jclass); #ifdef __cplusplus } #endif -@# Avoid warnings about unused arguments if the message definition does not contain any fields -@[if spec.fields]@ -@(msg_normalized_type) * @(spec.base_type.pkg_name)_@(type_name)__convert_from_java(jobject _jmessage_obj, @(msg_normalized_type) * ros_message) +@# Avoid warnings about unused arguments if the message definition does not contain any members +@[if message.structure.members]@ +@(msg_normalized_type) * @(underscore_separated_type_name)__convert_from_java(jobject _jmessage_obj, @(msg_normalized_type) * ros_message) @[else]@ -@(msg_normalized_type) * @(spec.base_type.pkg_name)_@(type_name)__convert_from_java(jobject, @(msg_normalized_type) * ros_message) +@(msg_normalized_type) * @(underscore_separated_type_name)__convert_from_java(jobject, @(msg_normalized_type) * ros_message) @[end if]@ { JNIEnv * env = nullptr; // TODO(esteve): check return status assert(g_vm != nullptr); - g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8); assert(env != nullptr); if (ros_message == nullptr) { ros_message = @(msg_normalized_type)__create(); } -@[for field in spec.fields]@ +@[for member in message.structure.members]@ @{ -normalized_type = get_normalized_type(field.type) +normalized_type = get_normalized_type(member.type) }@ -@[ if field.type.is_array] - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "L@(list_jni_type);"); - jobject _jlist_@(field.name)_object = env->GetObjectField(_jmessage_obj, _jfield_@(field.name)_fid); - - if (_jlist_@(field.name)_object != nullptr) { - jmethodID _jlist_@(field.name)_get_mid = env->GetMethodID(_j@(list_normalized_type)_class_global, "get", "(I)Ljava/lang/Object;"); -@[ if field.type.array_size is None or field.type.is_upper_bound]@ - jmethodID _jlist_@(field.name)_size_mid = env->GetMethodID(_j@(list_normalized_type)_class_global, "size", "()I"); - jint _jlist_@(field.name)_size = env->CallIntMethod(_jlist_@(field.name)_object, _jlist_@(field.name)_size_mid); -@[ if field.type.type == 'string']@ - if (!rosidl_generator_c__String__Array__init(&(ros_message->@(field.name)), _jlist_@(field.name)_size)) { +@[ if isinstance(member.type, AbstractNestedType)] + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "L@(list_jni_type);"); + jobject _jlist_@(member.name)_object = env->GetObjectField(_jmessage_obj, _jfield_@(member.name)_fid); + + if (_jlist_@(member.name)_object != nullptr) { + jmethodID _jlist_@(member.name)_get_mid = env->GetMethodID(_j@(list_normalized_type)_class_global, "get", "(I)Ljava/lang/Object;"); +@[ if isinstance(member.type, AbstractSequence)]@ + jmethodID _jlist_@(member.name)_size_mid = env->GetMethodID(_j@(list_normalized_type)_class_global, "size", "()I"); + jint _jlist_@(member.name)_size = env->CallIntMethod(_jlist_@(member.name)_object, _jlist_@(member.name)_size_mid); +@[ if isinstance(member.type.value_type, AbstractString)]@ + if (!rosidl_generator_c__String__Sequence__init(&(ros_message->@(member.name)), _jlist_@(member.name)_size)) { rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create String__Array ros_message"); } -@[ else]@ -@[ if field.type.is_primitive_type()]@ - if (!rosidl_generator_c__@(field.type.type)__Array__init(&(ros_message->@(field.name)), _jlist_@(field.name)_size)) { - rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create @(field.type.type)__Array ros_message"); +@[ elif isinstance(member.type.value_type, AbstractWString)]@ + if (!rosidl_generator_c__U16String__Sequence__init(&(ros_message->@(member.name)), _jlist_@(member.name)_size)) { + rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create U16String__Array ros_message"); } -@[ else]@ - if (!@(field.type.pkg_name)__msg__@(field.type.type)__Array__init(&(ros_message->@(field.name)), _jlist_@(field.name)_size)) { - rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create @(field.type.type)__Array ros_message"); +@[ else]@ +@[ if isinstance(member.type.value_type, BasicType)]@ + if (!rosidl_generator_c__@(member.type.value_type.typename)__Sequence__init(&(ros_message->@(member.name)), _jlist_@(member.name)_size)) { + rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create @(member.type.value_type)__Array ros_message"); } - -@[ end if]@ -@[ end if]@ - auto _dest_@(field.name) = ros_message->@(field.name).data; @[ else]@ - jint _jlist_@(field.name)_size = @(field.type.array_size); + if (!@('__'.join(member.type.value_type.namespaced_name()))__Sequence__init(&(ros_message->@(member.name)), _jlist_@(member.name)_size)) { + rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create @(member.type.value_type)__Array ros_message"); + } - auto _dest_@(field.name) = ros_message->@(field.name); @[ end if]@ +@[ end if]@ + auto _dest_@(member.name) = ros_message->@(member.name).data; +@[ else]@ + jint _jlist_@(member.name)_size = @(member.type.size); + + auto _dest_@(member.name) = ros_message->@(member.name); +@[ end if]@ - for (jint i = 0; i < _jlist_@(field.name)_size; ++i) { - auto element = env->CallObjectMethod(_jlist_@(field.name)_object, _jlist_@(field.name)_get_mid, i); -@[ if field.type.is_primitive_type()]@ -@[ if field.type.type == 'string']@ - jstring _jfield_@(field.name)_value = static_cast(element); - if (_jfield_@(field.name)_value != nullptr) { - const char * _str@(field.name) = env->GetStringUTFChars(_jfield_@(field.name)_value, 0); + for (jint i = 0; i < _jlist_@(member.name)_size; ++i) { + auto element = env->CallObjectMethod(_jlist_@(member.name)_object, _jlist_@(member.name)_get_mid, i); +@[ if isinstance(member.type.value_type, AbstractString)]@ + jstring _jfield_@(member.name)_value = static_cast(element); + if (_jfield_@(member.name)_value != nullptr) { + const char * _str@(member.name) = env->GetStringUTFChars(_jfield_@(member.name)_value, 0); rosidl_generator_c__String__assign( - &_dest_@(field.name)[i], _str@(field.name)); - env->ReleaseStringUTFChars(_jfield_@(field.name)_value, _str@(field.name)); + &_dest_@(member.name)[i], _str@(member.name)); + env->ReleaseStringUTFChars(_jfield_@(member.name)_value, _str@(member.name)); + } +@[ elif isinstance(member.type.value_type, AbstractWString)]@ + jstring _jfield_@(member.name)_value = static_cast(element); + if (_jfield_@(member.name)_value != nullptr) { + const jchar * _str@(member.name) = env->GetStringChars(_jfield_@(member.name)_value, 0); + rosidl_generator_c__U16String__assign( + &_dest_@(member.name)[i], _str@(member.name)); + env->ReleaseStringChars(_jfield_@(member.name)_value, _str@(member.name)); } -@[ else]@ +@[ elif isinstance(member.type.value_type, BasicType)]@ @{ -call_method_name = 'Call%sMethod' % get_java_type(field.type, use_primitives=True).capitalize() +call_method_name = 'Call%sMethod' % get_java_type(member.type.value_type, use_primitives=True).capitalize() }@ - _dest_@(field.name)[i] = env->@(call_method_name)(element, _j@(normalized_type)_value_global); -@[ end if]@ -@[ else]@ - _dest_@(field.name)[i] = *_j@(normalized_type)_from_java_function(element, nullptr); -@[ end if]@ + _dest_@(member.name)[i] = env->@(call_method_name)(element, _j@(normalized_type)_value_global); +@[ else]@ + _dest_@(member.name)[i] = *_j@(normalized_type)_from_java_function(element, nullptr); +@[ end if]@ env->DeleteLocalRef(element); } } -@[ else]@ -@[ if field.type.is_primitive_type()]@ -@[ if field.type.type == 'string']@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "Ljava/lang/String;"); - jstring _jvalue@(field.name) = static_cast(env->GetObjectField(_jmessage_obj, _jfield_@(field.name)_fid)); - - if (_jvalue@(field.name) != nullptr) { - const char * _str@(field.name) = env->GetStringUTFChars(_jvalue@(field.name), 0); +@[ else]@ +@[ if isinstance(member.type, AbstractGenericString)]@ + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "Ljava/lang/String;"); + jstring _jvalue@(member.name) = static_cast(env->GetObjectField(_jmessage_obj, _jfield_@(member.name)_fid)); + + if (_jvalue@(member.name) != nullptr) { +@[ if isinstance(member.type, AbstractString)]@ + const char * _str@(member.name) = env->GetStringUTFChars(_jvalue@(member.name), 0); rosidl_generator_c__String__assign( - &ros_message->@(field.name), _str@(field.name)); - env->ReleaseStringUTFChars(_jvalue@(field.name), _str@(field.name)); + &ros_message->@(member.name), _str@(member.name)); + env->ReleaseStringUTFChars(_jvalue@(member.name), _str@(member.name)); +@[ else]@ + const jchar * _str@(member.name) = env->GetStringChars(_jvalue@(member.name), 0); + rosidl_generator_c__U16String__assign( + &ros_message->@(member.name), _str@(member.name)); + env->ReleaseStringChars(_jvalue@(member.name), _str@(member.name)); +@[ end if]@ } -@[ else]@ +@[ elif isinstance(member.type, BasicType)]@ @{ -jni_signature = get_jni_signature(field.type) -get_method_name = 'Get%sField' % get_java_type(field.type, use_primitives=True).capitalize() +jni_signature = get_jni_signature(member.type) +get_method_name = 'Get%sField' % get_java_type(member.type, use_primitives=True).capitalize() }@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "@(jni_signature)"); - ros_message->@(field.name) = env->@(get_method_name)(_jmessage_obj, _jfield_@(field.name)_fid); + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "@(jni_signature)"); + ros_message->@(member.name) = env->@(get_method_name)(_jmessage_obj, _jfield_@(member.name)_fid); -@[ end if]@ -@[ else]@ - auto _jfield_@(field.name)_fid = env->GetFieldID( - _j@(msg_normalized_type)_class_global, "@(field.name)", "L@(field.type.pkg_name)/msg/@(field.type.type);"); - assert(_jfield_@(field.name)_fid != nullptr); +@[ else]@ + auto _jfield_@(member.name)_fid = env->GetFieldID( + _j@(msg_normalized_type)_class_global, "@(member.name)", "L@('/'.join(member.type.namespaced_name()));"); + assert(_jfield_@(member.name)_fid != nullptr); - jobject _jfield_@(field.name)_obj = env->GetObjectField(_jmessage_obj, _jfield_@(field.name)_fid); + jobject _jfield_@(member.name)_obj = env->GetObjectField(_jmessage_obj, _jfield_@(member.name)_fid); - if (_jfield_@(field.name)_obj != nullptr) { - ros_message->@(field.name) = *_j@(normalized_type)_from_java_function(_jfield_@(field.name)_obj, nullptr); + if (_jfield_@(member.name)_obj != nullptr) { + ros_message->@(member.name) = *_j@(normalized_type)_from_java_function(_jfield_@(member.name)_obj, nullptr); } - env->DeleteLocalRef(_jfield_@(field.name)_obj); -@[ end if]@ + env->DeleteLocalRef(_jfield_@(member.name)_obj); @[ end if]@ +@[ end if]@ @[end for]@ assert(ros_message != nullptr); return ros_message; } @# Avoid warnings about unused arguments if the message definition does not contain any fields -@[if spec.fields]@ -jobject @(spec.base_type.pkg_name)_@(type_name)__convert_to_java(@(msg_normalized_type) * _ros_message, jobject _jmessage_obj) +@[if message.structure.members]@ +jobject @(underscore_separated_type_name)__convert_to_java(@(msg_normalized_type) * _ros_message, jobject _jmessage_obj) @[else]@ -jobject @(spec.base_type.pkg_name)_@(type_name)__convert_to_java(@(msg_normalized_type) *, jobject _jmessage_obj) +jobject @(underscore_separated_type_name)__convert_to_java(@(msg_normalized_type) *, jobject _jmessage_obj) @[end if]@ { JNIEnv * env = nullptr; // TODO(esteve): check return status assert(g_vm != nullptr); - g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8); assert(env != nullptr); if (_jmessage_obj == nullptr) { _jmessage_obj = env->NewObject(_j@(msg_normalized_type)_class_global, _j@(msg_normalized_type)_constructor_global); } -@[for field in spec.fields]@ +@[for member in message.structure.members]@ @{ -normalized_type = get_normalized_type(field.type) +normalized_type = get_normalized_type(member.type) }@ -@[ if field.type.is_array]@ -@[ if field.type.is_primitive_type()]@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "L@(list_jni_type);"); - jobject _jarray_list_@(field.name)_obj = env->NewObject(_j@(array_list_normalized_type)_class_global, _j@(array_list_normalized_type)_constructor_global); -@[ if field.type.array_size and not field.type.is_upper_bound]@ - for (size_t i = 0; i < @(field.type.array_size); ++i) { - auto _ros_@(field.name)_element = _ros_message->@(field.name)[i]; -@[ else]@ - for (size_t i = 0; i < _ros_message->@(field.name).size; ++i) { - auto _ros_@(field.name)_element = _ros_message->@(field.name).data[i]; -@[ end if]@ -@[ if field.type.type == 'string']@ - jobject _jlist_@(field.name)_element = nullptr; - if (_ros_@(field.name)_element.data != nullptr) { - _jlist_@(field.name)_element = env->NewStringUTF(_ros_@(field.name)_element.data); +@[ if isinstance(member.type, AbstractNestedType)]@ +@[ if isinstance(member.type.value_type, (BasicType, AbstractGenericString))]@ + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "L@(list_jni_type);"); + jobject _jarray_list_@(member.name)_obj = env->NewObject(_j@(array_list_normalized_type)_class_global, _j@(array_list_normalized_type)_constructor_global); +@[ if isinstance(member.type, Array)]@ + for (size_t i = 0; i < @(member.type.size); ++i) { + auto _ros_@(member.name)_element = _ros_message->@(member.name)[i]; +@[ else]@ + for (size_t i = 0; i < _ros_message->@(member.name).size; ++i) { + auto _ros_@(member.name)_element = _ros_message->@(member.name).data[i]; +@[ end if]@ +@[ if isinstance(member.type.value_type, AbstractGenericString)]@ + jobject _jlist_@(member.name)_element = nullptr; + if (_ros_@(member.name)_element.data != nullptr) { +@[ if isinstance(member.type.value_type, AbstractString)]@ + _jlist_@(member.name)_element = env->NewStringUTF(_ros_@(member.name)_element.data); +@[ else]@ + _jlist_@(member.name)_element = env->NewString(_ros_@(member.name)_element.data, _ros_@(member.name)_element.size); +@[ end if]@ } -@[ else]@ - jobject _jlist_@(field.name)_element = env->NewObject( - _j@(normalized_type)_class_global, _j@(normalized_type)_constructor_global, _ros_@(field.name)_element); -@[ end if]@ +@[ else]@ + jobject _jlist_@(member.name)_element = env->NewObject( + _j@(normalized_type)_class_global, _j@(normalized_type)_constructor_global, _ros_@(member.name)_element); +@[ end if]@ // TODO(esteve): replace ArrayList with a jobjectArray to initialize the array beforehand - jmethodID _jlist_@(field.name)_add_mid = env->GetMethodID( + jmethodID _jlist_@(member.name)_add_mid = env->GetMethodID( _j@(array_list_normalized_type)_class_global, "add", "(Ljava/lang/Object;)Z"); - if (_jlist_@(field.name)_element != nullptr) { - jboolean _jlist_@(field.name)_add_result = env->CallBooleanMethod(_jarray_list_@(field.name)_obj, _jlist_@(field.name)_add_mid, _jlist_@(field.name)_element); - assert(_jlist_@(field.name)_add_result); + if (_jlist_@(member.name)_element != nullptr) { + jboolean _jlist_@(member.name)_add_result = env->CallBooleanMethod(_jarray_list_@(member.name)_obj, _jlist_@(member.name)_add_mid, _jlist_@(member.name)_element); + assert(_jlist_@(member.name)_add_result); } } -@[ else]@ +@[ else]@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "L@(list_jni_type);"); - jobject _jarray_list_@(field.name)_obj = env->NewObject(_j@(array_list_normalized_type)_class_global, _j@(array_list_normalized_type)_constructor_global); + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "L@(list_jni_type);"); + jobject _jarray_list_@(member.name)_obj = env->NewObject(_j@(array_list_normalized_type)_class_global, _j@(array_list_normalized_type)_constructor_global); -@[ if field.type.array_size and not field.type.is_upper_bound]@ - for (size_t i = 0; i < @(field.type.array_size); ++i) { - jobject _jlist_@(field.name)_element = _j@(normalized_type)_to_java_function(&(_ros_message->@(field.name)[i]), nullptr); -@[ else]@ - for (size_t i = 0; i < _ros_message->@(field.name).size; ++i) { - jobject _jlist_@(field.name)_element = _j@(normalized_type)_to_java_function(&(_ros_message->@(field.name).data[i]), nullptr); -@[ end if]@ +@[ if isinstance(member.type, Array)]@ + for (size_t i = 0; i < @(member.type.size); ++i) { + jobject _jlist_@(member.name)_element = _j@(normalized_type)_to_java_function(&(_ros_message->@(member.name)[i]), nullptr); +@[ else]@ + for (size_t i = 0; i < _ros_message->@(member.name).size; ++i) { + jobject _jlist_@(member.name)_element = _j@(normalized_type)_to_java_function(&(_ros_message->@(member.name).data[i]), nullptr); +@[ end if]@ // TODO(esteve): replace ArrayList with a jobjectArray to initialize the array beforehand - jmethodID _jlist_@(field.name)_add_mid = env->GetMethodID(_j@(array_list_normalized_type)_class_global, "add", "(Ljava/lang/Object;)Z"); - if (_jlist_@(field.name)_element != nullptr) { - jboolean _jlist_@(field.name)_add_result = env->CallBooleanMethod(_jarray_list_@(field.name)_obj, _jlist_@(field.name)_add_mid, _jlist_@(field.name)_element); - assert(_jlist_@(field.name)_add_result); + jmethodID _jlist_@(member.name)_add_mid = env->GetMethodID(_j@(array_list_normalized_type)_class_global, "add", "(Ljava/lang/Object;)Z"); + if (_jlist_@(member.name)_element != nullptr) { + jboolean _jlist_@(member.name)_add_result = env->CallBooleanMethod(_jarray_list_@(member.name)_obj, _jlist_@(member.name)_add_mid, _jlist_@(member.name)_element); + assert(_jlist_@(member.name)_add_result); } } -@[ end if]@ - env->SetObjectField(_jmessage_obj, _jfield_@(field.name)_fid, _jarray_list_@(field.name)_obj); - env->DeleteLocalRef(_jarray_list_@(field.name)_obj); -@[ else]@ -@[ if field.type.is_primitive_type()]@ -@[ if field.type.type == 'string']@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "Ljava/lang/String;"); - if (_ros_message->@(field.name).data != nullptr) { - env->SetObjectField(_jmessage_obj, _jfield_@(field.name)_fid, env->NewStringUTF(_ros_message->@(field.name).data)); +@[ end if]@ + env->SetObjectField(_jmessage_obj, _jfield_@(member.name)_fid, _jarray_list_@(member.name)_obj); + env->DeleteLocalRef(_jarray_list_@(member.name)_obj); +@[ else]@ +@[ if isinstance(member.type, AbstractGenericString)]@ + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "Ljava/lang/String;"); + if (_ros_message->@(member.name).data != nullptr) { +@[ if isinstance(member.type, AbstractString)]@ + env->SetObjectField(_jmessage_obj, _jfield_@(member.name)_fid, env->NewStringUTF(_ros_message->@(member.name).data)); +@[ else]@ + env->SetObjectField(_jmessage_obj, _jfield_@(member.name)_fid, env->NewString(_ros_message->@(member.name).data, _ros_message->@(member.name).size)); +@[ end if]@ } -@[ else]@ +@[ elif isinstance(member.type, BasicType)]@ @{ -jni_signature = get_jni_signature(field.type) -set_method_name = 'Set%sField' % get_java_type(field.type, use_primitives=True).capitalize() +jni_signature = get_jni_signature(member.type) +set_method_name = 'Set%sField' % get_java_type(member.type, use_primitives=True).capitalize() }@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "@(jni_signature)"); - env->@(set_method_name)(_jmessage_obj, _jfield_@(field.name)_fid, _ros_message->@(field.name)); -@[ end if]@ -@[ else]@ - auto _jfield_@(field.name)_fid = env->GetFieldID( - _j@(msg_normalized_type)_class_global, "@(field.name)", "L@(field.type.pkg_name)/msg/@(field.type.type);"); - assert(_jfield_@(field.name)_fid != nullptr); + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "@(jni_signature)"); + env->@(set_method_name)(_jmessage_obj, _jfield_@(member.name)_fid, _ros_message->@(member.name)); +@[ else]@ + auto _jfield_@(member.name)_fid = env->GetFieldID( + _j@(msg_normalized_type)_class_global, "@(member.name)", "L@('/'.join(member.type.namespaced_name()));"); + assert(_jfield_@(member.name)_fid != nullptr); - jobject _jfield_@(field.name)_obj = _j@(normalized_type)_to_java_function(&(_ros_message->@(field.name)), nullptr); + jobject _jfield_@(member.name)_obj = _j@(normalized_type)_to_java_function(&(_ros_message->@(member.name)), nullptr); - env->SetObjectField(_jmessage_obj, _jfield_@(field.name)_fid, _jfield_@(field.name)_obj); -@[ end if]@ + env->SetObjectField(_jmessage_obj, _jfield_@(member.name)_fid, _jfield_@(member.name)_obj); @[ end if]@ +@[ end if]@ @[end for]@ assert(_jmessage_obj != nullptr); return _jmessage_obj; @@ -406,7 +393,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * vm, void *) } JNIEnv * env; - if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8) != JNI_OK) { return JNI_ERR; } else { @[for normalized_type, jni_type in cache.items()]@ @@ -415,20 +402,20 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * vm, void *) _j@(normalized_type)_class_global = static_cast(env->NewGlobalRef(_j@(normalized_type)_class_local)); env->DeleteLocalRef(_j@(normalized_type)_class_local); assert(_j@(normalized_type)_class_global != nullptr); -@[if constructor_signatures[jni_type]]@ +@[ if constructor_signatures[jni_type]]@ _j@(normalized_type)_constructor_global = env->GetMethodID(_j@(normalized_type)_class_global, "", "@(constructor_signatures[jni_type])"); assert(_j@(normalized_type)_constructor_global != nullptr); -@[end if]@ +@[ end if]@ @{ value_method = value_methods.get(jni_type) if value_method: value_method_name, value_method_signature = value_method }@ -@[ if value_method]@ +@[ if value_method]@ _j@(normalized_type)_value_global = env->GetMethodID(_j@(normalized_type)_class_global, "@(value_method_name)", "@(value_method_signature)"); assert(_j@(normalized_type)_value_global != nullptr); -@[ end if]@ -@[ if jni_type in non_primitive_types]@ +@[ end if]@ +@[ if jni_type in namespaced_types]@ _j@(normalized_type)_from_java_converter_global = env->GetStaticMethodID( _j@(normalized_type)_class_global, "getFromJavaConverter", "()J"); assert(_j@(normalized_type)_from_java_converter_global != nullptr); @@ -452,10 +439,10 @@ if value_method: _j@(normalized_type)_to_java_function = reinterpret_cast<_j@(normalized_type)_to_java_signature>(_j@(normalized_type)_to_java_converter_ptr_global); assert(_j@(normalized_type)_to_java_function != nullptr); -@[ end if]@ +@[ end if]@ @[end for]@ } - return JNI_VERSION_1_6; + return JNI_VERSION_1_8; } JNIEXPORT void JNICALL JNI_OnUnload(JavaVM * vm, void *) @@ -464,18 +451,18 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM * vm, void *) assert(g_vm == vm); JNIEnv * env; - if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) == JNI_OK) { + if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8) == JNI_OK) { @[for normalized_type, jni_type in cache.items()]@ if (_j@(normalized_type)_class_global != nullptr) { env->DeleteGlobalRef(_j@(normalized_type)_class_global); _j@(normalized_type)_class_global = nullptr; -@[ if constructor_signatures[jni_type]]@ +@[ if constructor_signatures[jni_type]]@ _j@(normalized_type)_constructor_global = nullptr; -@[ end if]@ -@[ if value_methods.get(jni_type)]@ +@[ end if]@ +@[ if value_methods.get(jni_type)]@ _j@(normalized_type)_value_global = nullptr; -@[ end if]@ -@[ if jni_type in non_primitive_types]@ +@[ end if]@ +@[ if jni_type in namespaced_types]@ _j@(normalized_type)_from_java_converter_global = nullptr; _j@(normalized_type)_from_java_converter_ptr_global = 0; _j@(normalized_type)_from_java_function = nullptr; @@ -483,31 +470,31 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM * vm, void *) _j@(normalized_type)_to_java_converter_global = nullptr; _j@(normalized_type)_to_java_converter_ptr_global = 0; _j@(normalized_type)_to_java_function = nullptr; -@[ end if]@ +@[ end if]@ } @[end for]@ } } -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getFromJavaConverter(JNIEnv *, jclass) +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getFromJavaConverter(JNIEnv *, jclass) { - jlong ptr = reinterpret_cast(&@(spec.base_type.pkg_name)_@(type_name)__convert_from_java); + jlong ptr = reinterpret_cast(&@(underscore_separated_type_name)__convert_from_java); return ptr; } -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getToJavaConverter(JNIEnv *, jclass) +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getToJavaConverter(JNIEnv *, jclass) { - jlong ptr = reinterpret_cast(@(spec.base_type.pkg_name)_@(type_name)__convert_to_java); + jlong ptr = reinterpret_cast(@(underscore_separated_type_name)__convert_to_java); return ptr; } -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getTypeSupport(JNIEnv *, jclass) +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getTypeSupport(JNIEnv *, jclass) { - jlong ptr = reinterpret_cast(ROSIDL_GET_MSG_TYPE_SUPPORT(@(spec.base_type.pkg_name), @(subfolder), @(spec.msg_name))); + jlong ptr = reinterpret_cast(ROSIDL_GET_MSG_TYPE_SUPPORT(@(','.join(message.structure.namespaced_type.namespaced_name())))); return ptr; } -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getDestructor(JNIEnv *, jclass) +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getDestructor(JNIEnv *, jclass) { jlong ptr = reinterpret_cast(@(msg_normalized_type)__destroy); return ptr; diff --git a/rosidl_generator_java/resource/msg.java.em b/rosidl_generator_java/resource/msg.java.em index 4409c1f3..cb6e1b3a 100644 --- a/rosidl_generator_java/resource/msg.java.em +++ b/rosidl_generator_java/resource/msg.java.em @@ -1,18 +1,41 @@ -package @(package_name).@(subfolder); - -import org.ros2.rcljava.common.JNIUtils; -import org.ros2.rcljava.interfaces.MessageDefinition; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; +@# Generation triggered from rosidl_generator_java/resource/idl.java.em +// generated from rosidl_generator_java/resource/msg.java.em +// with input from @(package_name):@(interface_path) +// generated code does not contain a copyright notice + +package @(package_name + '.' + interface_path.parts[0]); +@{ +from rosidl_generator_java import convert_lower_case_underscore_to_camel_case +from rosidl_generator_java import get_java_type +from rosidl_generator_java import primitive_value_to_java +from rosidl_generator_java import value_to_java +from rosidl_parser.definition import AbstractGenericString +from rosidl_parser.definition import AbstractNestedType +from rosidl_parser.definition import Array +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import BoundedSequence +from rosidl_parser.definition import NamespacedType + +type_name = message.structure.namespaced_type.name + +message_imports = [ + 'org.apache.commons.lang3.builder.EqualsBuilder', + 'org.apache.commons.lang3.builder.HashCodeBuilder', + 'org.ros2.rcljava.common.JNIUtils', + 'org.ros2.rcljava.interfaces.MessageDefinition', + 'org.slf4j.Logger', + 'org.slf4j.LoggerFactory', +] +}@ +@[for message_import in message_imports]@ +import @(message_import); +@[end for]@ -@[for field in spec.fields]@ -@[ if not field.type.is_primitive_type()]@ -import @(field.type.pkg_name).msg.@(field.type.type); -@[ end if]@ +@[for member in message.structure.members]@ +@[ if isinstance(member.type, NamespacedType)]@ +// Member '@(member.name)' +import @('.'.join(member.type.namespaced_name())); +@[ end if]@ @[end for]@ public final class @(type_name) implements MessageDefinition { @@ -49,85 +72,84 @@ public final class @(type_name) implements MessageDefinition { return @(type_name).getTypeSupport(); } -@[for constant in spec.constants]@ - public static final @(get_builtin_java_type(constant.type)) @(constant.name) = @(constant_value_to_java(constant.type, constant.value)); +@[for constant in message.constants]@ + public static final @(get_java_type(constant.type)) @(constant.name) = @(primitive_value_to_java(constant.type, constant.value)); @[end for]@ -@[for field in spec.fields]@ - -@[ if field.type.is_array]@ -@[ if field.default_value is not None]@ - private java.util.List<@(get_java_type(field.type, use_primitives=False))> @(field.name) = java.util.Arrays.asList(new @(get_java_type(field.type, use_primitives=False))[] @(value_to_java(field.type, field.default_value))); -@[ else]@ -@[ if field.type.array_size]@ - private java.util.List<@(get_java_type(field.type, use_primitives=False))> @(field.name); -@[ else]@ - private java.util.List<@(get_java_type(field.type, use_primitives=False))> @(field.name) = new java.util.ArrayList<@(get_java_type(field.type, use_primitives=False))>(); -@[ end if]@ -@[ end if]@ - - public final @(type_name) set@(convert_lower_case_underscore_to_camel_case(field.name))(final java.util.List<@(get_java_type(field.type, use_primitives=False))> @(field.name)) { -@[ if field.type.array_size]@ -@[ if field.type.is_upper_bound]@ - if(@(field.name).size() > @(field.type.array_size)) { - throw new IllegalArgumentException("List too big, maximum size allowed: @(field.type.array_size)"); -@[ else]@ - if(@(field.name).size() != @(field.type.array_size)) { - throw new IllegalArgumentException("Invalid size for fixed array, must be exactly: @(field.type.array_size)"); -@[ end if]@ +@[for member in message.structure.members]@ + +@[ if isinstance(member.type, AbstractNestedType)]@ +@[ if member.has_annotation('default')]@ + private java.util.List<@(get_java_type(member.type, use_primitives=False))> @(member.name) = java.util.Arrays.asList(new @(get_java_type(member.type, use_primitives=False))[] @(value_to_java(member.type, member.get_annotation_value('default')['value']))); +@[ else]@ +@[ if isinstance(member.type, Array)]@ + private java.util.List<@(get_java_type(member.type, use_primitives=False))> @(member.name); +@[ else]@ + private java.util.List<@(get_java_type(member.type, use_primitives=False))> @(member.name) = new java.util.ArrayList<@(get_java_type(member.type, use_primitives=False))>(); +@[ end if]@ +@[ end if]@ + + public final @(type_name) set@(convert_lower_case_underscore_to_camel_case(member.name))(final java.util.List<@(get_java_type(member.type, use_primitives=False))> @(member.name)) { +@[ if isinstance(member.type, BoundedSequence)]@ + if(@(member.name).size() > @(member.type.maximum_size)) { + throw new IllegalArgumentException("List too big, maximum size allowed: @(member.type.maximum_size)"); + } +@[ elif isinstance(member.type, Array)]@ + if(@(member.name).size() != @(member.type.size)) { + throw new IllegalArgumentException("Invalid size for fixed array, must be exactly: @(member.type.size)"); } -@[ end if]@ - this.@(field.name) = @(field.name); +@[ end if]@ + this.@(member.name) = @(member.name); return this; } -@[ if field.type.is_primitive_type()]@ - public final @(type_name) set@(convert_lower_case_underscore_to_camel_case(field.name))(final @(get_java_type(field.type, use_primitives=True))[] @(field.name)) { - java.util.List<@(get_java_type(field.type, use_primitives=False))> @(field.name)_tmp = new java.util.ArrayList<@(get_java_type(field.type, use_primitives=False))>(); - for(@(get_java_type(field.type, use_primitives=True)) @(field.name)_value : @(field.name)) { - @(field.name)_tmp.add(@(field.name)_value); +@[ if isinstance(member.type.value_type, (BasicType, AbstractGenericString))]@ + public final @(type_name) set@(convert_lower_case_underscore_to_camel_case(member.name))(final @(get_java_type(member.type, use_primitives=True))[] @(member.name)) { + java.util.List<@(get_java_type(member.type, use_primitives=False))> @(member.name)_tmp = new java.util.ArrayList<@(get_java_type(member.type, use_primitives=False))>(); + for(@(get_java_type(member.type, use_primitives=True)) @(member.name)_value : @(member.name)) { + @(member.name)_tmp.add(@(member.name)_value); } - return set@(convert_lower_case_underscore_to_camel_case(field.name))(@(field.name)_tmp); + return set@(convert_lower_case_underscore_to_camel_case(member.name))(@(member.name)_tmp); } -@[ end if]@ +@[ end if]@ - public final java.util.List<@(get_java_type(field.type, use_primitives=False))> get@(convert_lower_case_underscore_to_camel_case(field.name))() { - return this.@(field.name); + public final java.util.List<@(get_java_type(member.type, use_primitives=False))> get@(convert_lower_case_underscore_to_camel_case(member.name))() { + return this.@(member.name); } +@[ else]@ +@[ if member.has_annotation('default')]@ + private @(get_java_type(member.type)) @(member.name) = @(value_to_java(member.type, member.get_annotation_value('default')['value'])); @[ else]@ -@[ if field.default_value is not None]@ - private @(get_java_type(field.type)) @(field.name) = @(value_to_java(field.type, field.default_value)); -@[ else]@ -@[ if field.type.type == 'string']@ - private @(get_java_type(field.type)) @(field.name) = ""; -@[ elif field.type.is_primitive_type()]@ - private @(get_java_type(field.type)) @(field.name); -@[ else]@ - private @(get_java_type(field.type)) @(field.name) = new @(get_java_type(field.type))(); -@[ end if]@ -@[ end if]@ - - public @(type_name) set@(convert_lower_case_underscore_to_camel_case(field.name))(final @(get_java_type(field.type)) @(field.name)) { -@[ if field.type.string_upper_bound]@ - if(@(field.name).length() > @(field.type.string_upper_bound)) { - throw new IllegalArgumentException("String too long, maximum size allowed: @(field.type.string_upper_bound)"); +@[ if isinstance(member.type, AbstractGenericString)]@ + private @(get_java_type(member.type)) @(member.name) = ""; +@[ elif isinstance(member.type, BasicType)]@ + private @(get_java_type(member.type)) @(member.name); +@[ else]@ + private @(get_java_type(member.type)) @(member.name) = new @(get_java_type(member.type))(); +@[ end if]@ +@[ end if]@ + + public @(type_name) set@(convert_lower_case_underscore_to_camel_case(member.name))(final @(get_java_type(member.type)) @(member.name)) { +@[ if isinstance(member.type, AbstractGenericString) and member.type.has_maximum_size()]@ + if(@(member.name).length() > @(member.type.maximum_size)) { + throw new IllegalArgumentException("String too long, maximum size allowed: @(member.type.maximum_size)"); } -@[ end if]@ +@[ end if]@ - this.@(field.name) = @(field.name); + this.@(member.name) = @(member.name); return this; } - public @(get_java_type(field.type)) get@(convert_lower_case_underscore_to_camel_case(field.name))() { - return this.@(field.name); + public @(get_java_type(member.type)) get@(convert_lower_case_underscore_to_camel_case(member.name))() { + return this.@(member.name); } -@[ end if]@ +@[ end if]@ @[end for]@ public int hashCode() { return new HashCodeBuilder(17, 37) -@[for field in spec.fields]@ - .append(this.@(field.name)) +@[for member in message.structure.members]@ + .append(this.@(member.name)) @[end for]@ .toHashCode(); } @@ -140,8 +162,8 @@ public final class @(type_name) implements MessageDefinition { } @(type_name) rhs = (@(type_name)) obj; return new EqualsBuilder() -@[for field in spec.fields]@ - .append(this.@(field.name), rhs.@(field.name)) +@[for member in message.structure.members]@ + .append(this.@(member.name), rhs.@(member.name)) @[end for]@ .isEquals(); } diff --git a/rosidl_generator_java/resource/srv.cpp.em b/rosidl_generator_java/resource/srv.cpp.em index 71014762..5c2d2d0b 100644 --- a/rosidl_generator_java/resource/srv.cpp.em +++ b/rosidl_generator_java/resource/srv.cpp.em @@ -1,29 +1,48 @@ -// generated from rosidl_generator_java/resource/srv.cpp.em -// generated code does not contain a copyright notice +@# Included from rosidl_generator_java/resource/idl.cpp.em +@{ +from rosidl_generator_c import idl_structure_type_to_c_include_prefix -#include +service_includes = [ + 'rosidl_generator_c/service_type_support_struct.h', +] +}@ +@[for include in service_includes]@ +@[ if include in include_directives]@ +// already included above +// @ +@[ else]@ +@{include_directives.add(include)}@ +@[ end if]@ +#include "@(include)" +@[end for]@ -#include "rosidl_generator_c/service_type_support_struct.h" -#include "@(spec.pkg_name)/@(subfolder)/@(module_name).h" +#include "@(idl_structure_type_to_c_include_prefix(service.namespaced_type)).h" #ifdef __cplusplus extern "C" { #endif +@{ +from rosidl_generator_java import get_jni_mangled_name + +service_fqn = service.namespaced_type.namespaced_name() +underscore_separated_type_name = '_'.join(service_fqn) +underscore_separated_jni_type_name = get_jni_mangled_name(service_fqn) +}@ /* - * Class: @(jni_package_name)_@(subfolder)_@(type_name) + * Class: @(underscore_separated_type_name) * Method: getServiceTypeSupport * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getServiceTypeSupport(JNIEnv *, jclass); +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getServiceTypeSupport(JNIEnv *, jclass); #ifdef __cplusplus } #endif -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getServiceTypeSupport(JNIEnv *, jclass) +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getServiceTypeSupport(JNIEnv *, jclass) { const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT( - @(spec.pkg_name), @(spec.srv_name)); + @(','.join(service_fqn))); return reinterpret_cast(ts); } diff --git a/rosidl_generator_java/resource/srv.java.em b/rosidl_generator_java/resource/srv.java.em index d7e992f4..9be7a6a7 100644 --- a/rosidl_generator_java/resource/srv.java.em +++ b/rosidl_generator_java/resource/srv.java.em @@ -1,10 +1,51 @@ -package @(package_name).@(subfolder); +@# Generation triggered from rosidl_generator_java/resource/idl.java.em +// generated from rosidl_generator_java/resource/srv.java.em +// with input from @(package_name):@(interface_path) +// generated code does not contain a copyright notice -import org.ros2.rcljava.common.JNIUtils; -import org.ros2.rcljava.interfaces.ServiceDefinition; +package @(package_name + '.' + interface_path.parts[0]); +@{ +import os +from rosidl_cmake import expand_template -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +namespaces = service.namespaced_type.namespaces +type_name = service.namespaced_type.name +request_type_name = service.request_message.structure.namespaced_type.name +response_type_name = service.response_message.structure.namespaced_type.name + +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} +data.update({'message': service.request_message}) +output_file = os.path.join(output_dir, *namespaces[1:], request_type_name + '.java') +expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) + +data.update({'message': service.response_message}) +output_file = os.path.join(output_dir, *namespaces[1:], response_type_name + '.java') +expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) + +service_imports = [ + 'org.ros2.rcljava.common.JNIUtils', + 'org.ros2.rcljava.interfaces.ServiceDefinition', + 'org.slf4j.Logger', + 'org.slf4j.LoggerFactory', +] +}@ + +@[for service_import in service_imports]@ +import @(service_import); +@[end for]@ public class @(type_name) implements ServiceDefinition { diff --git a/rosidl_generator_java/rosidl_generator_java/__init__.py b/rosidl_generator_java/rosidl_generator_java/__init__.py index 8d9163f3..7941ab69 100644 --- a/rosidl_generator_java/rosidl_generator_java/__init__.py +++ b/rosidl_generator_java/rosidl_generator_java/__init__.py @@ -1,4 +1,5 @@ # Copyright 2016-2017 Esteve Fernandez +# Copyright 2019 Open Source Robotics Foundation, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,15 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +from ast import literal_eval from collections import defaultdict -import os +import pathlib -from rosidl_cmake import convert_camel_case_to_lower_case_underscore -from rosidl_cmake import expand_template -from rosidl_cmake import get_newest_modification_time +from rosidl_cmake import generate_files from rosidl_cmake import read_generator_arguments -from rosidl_parser import parse_message_file -from rosidl_parser import parse_service_file +from rosidl_parser.definition import AbstractGenericString +from rosidl_parser.definition import AbstractNestedType +from rosidl_parser.definition import BASIC_TYPES +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import NamespacedType # Taken from http://stackoverflow.com/a/6425628 @@ -28,201 +31,176 @@ def convert_lower_case_underscore_to_camel_case(word): return ''.join(x.capitalize() or '_' for x in word.split('_')) -def generate_java(generator_arguments_file, typesupport_impl, typesupport_impls): +def generate_java(generator_arguments_file, typesupport_impls): args = read_generator_arguments(generator_arguments_file) - typesupport_impls = typesupport_impls.split(';') - - template_dir = args['template_dir'] - type_support_impl_by_filename = { - '%s.ep.{0}.cpp'.format(impl): impl - for impl in typesupport_impls - } - mapping_msgs = { - os.path.join(template_dir, 'msg.java.em'): ['%s.java'], - os.path.join(template_dir, 'msg.cpp.em'): type_support_impl_by_filename.keys(), + additional_context = { + 'output_dir': pathlib.Path(args['output_dir']), + 'template_basepath': pathlib.Path(args['template_dir']), } - - mapping_srvs = { - os.path.join(template_dir, 'srv.java.em'): ['%s.java'], - os.path.join(template_dir, 'srv.cpp.em'): type_support_impl_by_filename.keys(), + mapping = { + 'idl.java.em': '_%s.java', } - - for template_file in mapping_msgs.keys(): - assert os.path.exists(template_file), \ - 'Messages template file %s not found' % template_file - for template_file in mapping_srvs.keys(): - assert os.path.exists(template_file), \ - 'Services template file %s not found' % template_file - - functions = {'get_java_type': get_java_type, } - latest_target_timestamp = get_newest_modification_time(args['target_dependencies']) - - modules = defaultdict(list) - for ros_interface_file in args['ros_interface_files']: - extension = os.path.splitext(ros_interface_file)[1] - subfolder = os.path.basename(os.path.dirname(ros_interface_file)) - if extension == '.msg': - spec = parse_message_file(args['package_name'], ros_interface_file) - mapping = mapping_msgs - type_name = spec.base_type.type - elif extension == '.srv': - spec = parse_service_file(args['package_name'], ros_interface_file) - mapping = mapping_srvs - type_name = spec.srv_name - else: - continue - - module_name = convert_camel_case_to_lower_case_underscore(type_name) - modules[subfolder].append((module_name, type_name)) - package_name = args['package_name'] - jni_package_name = package_name.replace('_', '_1') - jni_type_name = type_name.replace('_', '_1') - for template_file, generated_filenames in mapping.items(): - for generated_filename in generated_filenames: - data = { - 'constant_value_to_java': constant_value_to_java, - 'value_to_java': value_to_java, - 'convert_camel_case_to_lower_case_underscore': - convert_camel_case_to_lower_case_underscore, - 'convert_lower_case_underscore_to_camel_case': - convert_lower_case_underscore_to_camel_case, - 'get_builtin_java_type': get_builtin_java_type, - 'module_name': module_name, - 'package_name': package_name, - 'jni_package_name': jni_package_name, - 'jni_type_name': jni_type_name, - 'spec': spec, - 'subfolder': subfolder, - 'typesupport_impl': type_support_impl_by_filename.get(generated_filename, ''), - 'typesupport_impls': typesupport_impls, - 'type_name': type_name, - } - data.update(functions) - generated_file = os.path.join(args['output_dir'], subfolder, - generated_filename % type_name) - expand_template( - template_file, data, generated_file, minimum_timestamp=latest_target_timestamp) - + generate_files( + generator_arguments_file, mapping, additional_context=additional_context, keep_case=True) + + for impl in typesupport_impls: + mapping = { + 'idl.cpp.em': '%s.ep.{0}.cpp'.format(impl), + } + generate_files( + generator_arguments_file, + mapping, + additional_context=additional_context, + keep_case=True) return 0 def escape_string(s): s = s.replace('\\', '\\\\') - s = s.replace("'", "\\'") + s = s.replace('"', '\\"') return s +def escape_wstring(s): + return escape_string(s) + + def value_to_java(type_, value): - assert type_.is_primitive_type() - assert value is not None + assert not isinstance(type_, NamespacedType), \ + "Could not convert non-basic type '{}' to Java".format(type_) + assert value is not None, "Value for for type '{}' must not be None".format(type_) - if not type_.is_array: + if not isinstance(type_, AbstractNestedType): return primitive_value_to_java(type_, value) java_values = [] - for single_value in value: - java_value = primitive_value_to_java(type_, single_value) + for single_value in literal_eval(value): + java_value = primitive_value_to_java(type_.value_type, single_value) java_values.append(java_value) return '{%s}' % ', '.join(java_values) def primitive_value_to_java(type_, value): - assert type_.is_primitive_type() - assert value is not None + assert isinstance(type_, (BasicType, AbstractGenericString)), \ + "Could not convert non-basic type '{}' to Java".format(type_) + assert value is not None, "Value for for type '{}' must not be None".format(type_) - if type_.type == 'bool': - return 'true' if value else 'false' - - if type_.type in [ - 'byte', - 'char', - 'int8', - 'uint8', - 'int16', - 'uint16', - 'int32', - 'uint32', - 'int64', - 'uint64', - 'float64', - ]: - return str(value) - - if type_.type == 'float32': - return '%sf' % value - - if type_.type == 'string': + if isinstance(type_, AbstractGenericString): return '"%s"' % escape_string(value) - assert False, "unknown primitive type '%s'" % type_ - - -def constant_value_to_java(type_, value): - assert value is not None - - if type_ == 'bool': + if type_.typename == 'boolean': return 'true' if value else 'false' - if type_ in [ - 'byte', - 'char', - 'int8', - 'uint8', - 'int16', - 'uint16', - 'int32', - 'uint32', - 'int64', - 'uint64', - 'float64', - ]: - return str(value) - - if type_ == 'float32': + if type_.typename == 'float': return '%sf' % value - if type_ == 'string': - return '"%s"' % escape_string(value) - - assert False, "unknown constant type '%s'" % type_ - + if type_.typename in ('octet', 'uint8', 'int8'): + # literal is treated as an integer so we must cast + return '(byte) %s' % value -def get_builtin_java_type(type_, use_primitives=True): - if type_ == 'bool': - return 'boolean' if use_primitives else 'java.lang.Boolean' + if type_.typename in ('uint16', 'int16'): + # literal is treated as an integer so we must cast + return '(short) %s' % value - if type_ == 'byte': - return 'byte' if use_primitives else 'java.lang.Byte' + # Java doesn't support unsigned literals (values over 2^31-1) + # Instead we should convert to the corresponding negative number + if type_.typename == 'uint32' and int(value) > 2**31-1: + negative_value = int(value) - 2**32 + return str(negative_value) - if type_ == 'char': - return 'char' if use_primitives else 'java.lang.Character' + if type_.typename in ('uint64', 'int64'): + # Java doesn't support unsigned literals (values over 2^63-1) + # Instead we should convert to the corresponding negative number + if type_.typename == 'uint64' and int(value) > 2**63-1: + value = int(value) - 2**64 + return '%sL' % value - if type_ == 'float32': - return 'float' if use_primitives else 'java.lang.Float' - - if type_ == 'float64': - return 'double' if use_primitives else 'java.lang.Double' - - if type_ in ['int8', 'uint8']: - return 'byte' if use_primitives else 'java.lang.Byte' - - if type_ in ['int16', 'uint16']: - return 'short' if use_primitives else 'java.lang.Short' - - if type_ in ['int32', 'uint32']: - return 'int' if use_primitives else 'java.lang.Integer' - - if type_ in ['int64', 'uint64']: - return 'long' if use_primitives else 'java.lang.Long' + if type_.typename in BASIC_TYPES: + return str(value) - if type_ == 'string': + assert False, "unknown primitive type '%s'" % type_.typename + + +# Map IDL types to Java primitive types +# Maps to a tuple: (primitive, class type) +IDL_TYPE_TO_JAVA_PRIMITIVE = { + 'boolean': ('boolean', 'java.lang.Boolean'), + 'char': ('char', 'java.lang.Char'), + 'octet': ('byte', 'java.lang.Byte'), + 'float': ('float', 'java.lang.Float'), + 'double': ('double', 'java.lang.Double'), + 'long double': ('double', 'java.lang.Double'), + 'uint8': ('byte', 'java.lang.Byte'), + 'int8': ('byte', 'java.lang.Byte'), + 'uint16': ('short', 'java.lang.Short'), + 'int16': ('short', 'java.lang.Short'), + 'uint32': ('int', 'java.lang.Integer'), + 'int32': ('int', 'java.lang.Integer'), + 'uint64': ('long', 'java.lang.Long'), + 'int64': ('long', 'java.lang.Long'), +} + + +def get_java_type(type_, use_primitives=True): + if isinstance(type_, AbstractNestedType): + type_ = type_.value_type + if isinstance(type_, NamespacedType): + return '.'.join(type_.namespaced_name()) + if isinstance(type_, BasicType): + return IDL_TYPE_TO_JAVA_PRIMITIVE[type_.typename][0 if use_primitives else 1] + if isinstance(type_, AbstractGenericString): return 'java.lang.String' assert False, "unknown type '%s'" % type_ -def get_java_type(type_, use_primitives=True, subfolder='msg'): - if not type_.is_primitive_type(): - return '%s.%s.%s' % (type_.pkg_name, subfolder, type_.type) - - return get_builtin_java_type(type_.type, use_primitives=use_primitives) +def get_normalized_type(type_): + return get_java_type(type_, use_primitives=False).replace('.', '__') + + +def get_jni_type(type_): + return get_java_type(type_, use_primitives=False).replace('.', '/') + + +# JNI performance tips taken from http://planet.jboss.org/post/jni_performance_the_saga_continues +constructor_signatures = defaultdict(lambda: '()V') +constructor_signatures['java/lang/Boolean'] = '(Z)V' +constructor_signatures['java/lang/Byte'] = '(B)V' +constructor_signatures['java/lang/Character'] = '(C)V' +constructor_signatures['java/lang/Double'] = '(D)V' +constructor_signatures['java/lang/Float'] = '(F)V' +constructor_signatures['java/lang/Integer'] = '(I)V' +constructor_signatures['java/lang/Long'] = '(J)V' +constructor_signatures['java/lang/Short'] = '(S)V' +constructor_signatures['java/util/List'] = None + +value_methods = {} +value_methods['java/lang/Boolean'] = ('booleanValue', '()Z') +value_methods['java/lang/Byte'] = ('byteValue', '()B') +value_methods['java/lang/Character'] = ('charValue', '()C') +value_methods['java/lang/Double'] = ('doubleValue', '()D') +value_methods['java/lang/Float'] = ('floatValue', '()F') +value_methods['java/lang/Integer'] = ('intValue', '()I') +value_methods['java/lang/Long'] = ('longValue', '()J') +value_methods['java/lang/Short'] = ('shortValue', '()S') + +jni_signatures = {} +jni_signatures['java/lang/Boolean'] = 'Z' +jni_signatures['java/lang/Byte'] = 'B' +jni_signatures['java/lang/Character'] = 'C' +jni_signatures['java/lang/Double'] = 'D' +jni_signatures['java/lang/Float'] = 'F' +jni_signatures['java/lang/Integer'] = 'I' +jni_signatures['java/lang/Long'] = 'J' +jni_signatures['java/lang/Short'] = 'S' + + +def get_jni_signature(type_): + global jni_signatures + return jni_signatures.get(get_jni_type(type_)) + + +def get_jni_mangled_name(fully_qualified_name): + # JNI name mangling: + # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names + return fully_qualified_name[0].replace('_', '_1') + '_' + '_'.join(fully_qualified_name[1:]) diff --git a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java index 03a5011d..d1b0fada 100644 --- a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java +++ b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java @@ -68,12 +68,12 @@ public final void testByte() { @Test public final void testChar() { - char expected1 = 'a'; + byte expected1 = 'a'; rosidl_generator_java.msg.Char charOne = new rosidl_generator_java.msg.Char(); charOne.setEmptyChar(expected1); assertEquals(expected1, charOne.getEmptyChar()); - char expected2 = 'b'; + byte expected2 = 'b'; rosidl_generator_java.msg.Char charTwo = new rosidl_generator_java.msg.Char(); charTwo.setEmptyChar(expected2); assertEquals(expected2, charTwo.getEmptyChar()); From 6e571872ed5d292186a6805a00311d70b1114e47 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Tue, 11 Feb 2020 07:48:56 -0800 Subject: [PATCH 04/33] Update rcljava for Dashing (#75) * Update API for getting rcl error string Signed-off-by: Jacob Perron * Add implementation for ROS Context A context represents an init/shutdown cycle and is used in the creation of top level entities like nodes and guard conditions. For convenience, a default context is created when rcljava is initialized. Signed-off-by: Jacob Perron * Add implementation for Clock Signed-off-by: Jacob Perron * Update for Dashing support * Update wait set API calls * Update entity creation API calls * Use Context objects to check 'ok()' status * Add Clock and Context members to NodeImpl * Fix static member reference: 'this.defaultContext' -> 'RCLJava.defaultContext' Signed-off-by: Jacob Perron * Avoid hiding errors when cleaning up init options Signed-off-by: Jacob Perron * Disable tests Signed-off-by: Jacob Perron * Fix typos in JNI library files Signed-off-by: Jacob Perron * Fix native node method signature Signed-off-by: Jacob Perron * Populate missing QoS settings with defaults Otherwise we run into a runtime error about Fast-RTPS not supporting liveliness. Signed-off-by: Jacob Perron * Fix issues with Clock class * Load with JNIUtils * Rename native create method for consistency * Fix bug in native implementation Signed-off-by: Jacob Perron * Enable linter tests Signed-off-by: Jacob Perron * Fix lint errors Signed-off-by: Jacob Perron * Enable RCLJava test and fix bugs * Use Context.ok() and deprecate RCLJava.isInitialized(). * Move implementation loading to static initialization code. Otherwise, calls to getDefaultContext() fail if called before rclJavaInit(). It wasn't clear to me why the implementation should be loaded in a separate function call. We can probably refactor the code to avoid the error if we want to move the loading back into rclJavaInit(). * Refactor test into one init/shutdown test. Previously, not calling RCLJava.shutdown() was leaving a context around between tests. Signed-off-by: Jacob Perron * Fix NodeTest Signed-off-by: Jacob Perron * Enable most tests Tests that involve services are broken due to issues related to interface generation. There's a separate PR for a fix: https://github.com/ros2-java/ros2_java/pull/76. Signed-off-by: Jacob Perron --- rcljava/CMakeLists.txt | 18 ++- rcljava/include/org_ros2_rcljava_RCLJava.h | 38 +++--- rcljava/include/org_ros2_rcljava_Time.h | 6 +- .../org_ros2_rcljava_client_ClientImpl.h | 7 +- .../org_ros2_rcljava_contexts_ContextImpl.h | 59 +++++++++ .../org_ros2_rcljava_executors_BaseExecutor.h | 88 ++++++------- .../include/org_ros2_rcljava_node_NodeImpl.h | 22 ++-- ...org_ros2_rcljava_publisher_PublisherImpl.h | 8 +- .../org_ros2_rcljava_service_ServiceImpl.h | 4 +- ...s2_rcljava_subscription_SubscriptionImpl.h | 5 +- rcljava/include/org_ros2_rcljava_time_Clock.h | 42 +++++++ .../org_ros2_rcljava_timer_WallTimerImpl.h | 41 +++--- .../src/main/cpp/org_ros2_rcljava_RCLJava.cpp | 46 +++---- .../src/main/cpp/org_ros2_rcljava_Time.cpp | 4 +- .../org_ros2_rcljava_client_ClientImpl.cpp | 4 +- .../org_ros2_rcljava_contexts_ContextImpl.cpp | 108 ++++++++++++++++ ...rg_ros2_rcljava_executors_BaseExecutor.cpp | 87 ++++--------- .../cpp/org_ros2_rcljava_node_NodeImpl.cpp | 20 +-- ...g_ros2_rcljava_publisher_PublisherImpl.cpp | 6 +- .../org_ros2_rcljava_service_ServiceImpl.cpp | 2 +- ..._rcljava_subscription_SubscriptionImpl.cpp | 2 +- .../main/cpp/org_ros2_rcljava_time_Clock.cpp | 85 +++++++++++++ .../org_ros2_rcljava_timer_WallTimerImpl.cpp | 20 +-- .../main/java/org/ros2/rcljava/RCLJava.java | 107 +++++++++++----- .../org/ros2/rcljava/contexts/Context.java | 49 ++++++++ .../ros2/rcljava/contexts/ContextImpl.java | 118 ++++++++++++++++++ .../ros2/rcljava/executors/BaseExecutor.java | 25 ++-- .../java/org/ros2/rcljava/node/NodeImpl.java | 21 +++- .../java/org/ros2/rcljava/time/Clock.java | 93 ++++++++++++++ .../java/org/ros2/rcljava/RCLJavaTest.java | 18 +-- .../java/org/ros2/rcljava/node/NodeTest.java | 14 +-- 31 files changed, 860 insertions(+), 307 deletions(-) create mode 100644 rcljava/include/org_ros2_rcljava_contexts_ContextImpl.h create mode 100644 rcljava/include/org_ros2_rcljava_time_Clock.h create mode 100644 rcljava/src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp create mode 100644 rcljava/src/main/cpp/org_ros2_rcljava_time_Clock.cpp create mode 100644 rcljava/src/main/java/org/ros2/rcljava/contexts/Context.java create mode 100644 rcljava/src/main/java/org/ros2/rcljava/contexts/ContextImpl.java create mode 100644 rcljava/src/main/java/org/ros2/rcljava/time/Clock.java diff --git a/rcljava/CMakeLists.txt b/rcljava/CMakeLists.txt index 2e6005fb..a6b4211f 100644 --- a/rcljava/CMakeLists.txt +++ b/rcljava/CMakeLists.txt @@ -57,11 +57,13 @@ set(${PROJECT_NAME}_jni_sources "src/main/cpp/org_ros2_rcljava_RCLJava.cpp" "src/main/cpp/org_ros2_rcljava_Time.cpp" "src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp" + "src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp" "src/main/cpp/org_ros2_rcljava_executors_BaseExecutor.cpp" "src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp" "src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp" "src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp" "src/main/cpp/org_ros2_rcljava_subscription_SubscriptionImpl.cpp" + "src/main/cpp/org_ros2_rcljava_time_Clock.cpp" "src/main/cpp/org_ros2_rcljava_timer_WallTimerImpl.cpp" ) @@ -118,6 +120,8 @@ set(${PROJECT_NAME}_sources "src/main/java/org/ros2/rcljava/client/ClientImpl.java" "src/main/java/org/ros2/rcljava/concurrent/Callback.java" "src/main/java/org/ros2/rcljava/concurrent/RCLFuture.java" + "src/main/java/org/ros2/rcljava/contexts/Context.java" + "src/main/java/org/ros2/rcljava/contexts/ContextImpl.java" "src/main/java/org/ros2/rcljava/consumers/BiConsumer.java" "src/main/java/org/ros2/rcljava/consumers/Consumer.java" "src/main/java/org/ros2/rcljava/consumers/TriConsumer.java" @@ -151,6 +155,7 @@ set(${PROJECT_NAME}_sources "src/main/java/org/ros2/rcljava/service/ServiceImpl.java" "src/main/java/org/ros2/rcljava/subscription/Subscription.java" "src/main/java/org/ros2/rcljava/subscription/SubscriptionImpl.java" + "src/main/java/org/ros2/rcljava/time/Clock.java" "src/main/java/org/ros2/rcljava/time/ClockType.java" "src/main/java/org/ros2/rcljava/timer/Timer.java" "src/main/java/org/ros2/rcljava/timer/WallTimer.java" @@ -194,12 +199,15 @@ if(BUILD_TESTING) "srv/AddTwoInts.srv" ) + rosidl_generator_java_get_typesupports(_java_type_supports) + rosidl_generate_interfaces(${PROJECT_NAME} ${${PROJECT_NAME}_message_files} ${${PROJECT_NAME}_service_files} DEPENDENCIES builtin_interfaces rcl_interfaces + ${_java_type_supports} SKIP_INSTALL ) @@ -214,10 +222,10 @@ if(BUILD_TESTING) set(${PROJECT_NAME}_test_sources "src/test/java/org/ros2/rcljava/RCLJavaTest.java" "src/test/java/org/ros2/rcljava/TimeTest.java" - "src/test/java/org/ros2/rcljava/client/ClientTest.java" + # "src/test/java/org/ros2/rcljava/client/ClientTest.java" "src/test/java/org/ros2/rcljava/node/NodeTest.java" - "src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java" - "src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java" + # "src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java" + # "src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java" "src/test/java/org/ros2/rcljava/publisher/PublisherTest.java" "src/test/java/org/ros2/rcljava/subscription/SubscriptionTest.java" "src/test/java/org/ros2/rcljava/timer/TimerTest.java" @@ -226,9 +234,9 @@ if(BUILD_TESTING) set(${PROJECT_NAME}_testsuites "org.ros2.rcljava.RCLJavaTest" "org.ros2.rcljava.TimeTest" - "org.ros2.rcljava.client.ClientTest" + # "org.ros2.rcljava.client.ClientTest" "org.ros2.rcljava.node.NodeTest" - "org.ros2.rcljava.parameters.SyncParametersClientTest" + # "org.ros2.rcljava.parameters.SyncParametersClientTest" "org.ros2.rcljava.publisher.PublisherTest" "org.ros2.rcljava.subscription.SubscriptionTest" "org.ros2.rcljava.timer.TimerTest" diff --git a/rcljava/include/org_ros2_rcljava_RCLJava.h b/rcljava/include/org_ros2_rcljava_RCLJava.h index e064bcd0..5ef6aff1 100644 --- a/rcljava/include/org_ros2_rcljava_RCLJava.h +++ b/rcljava/include/org_ros2_rcljava_RCLJava.h @@ -22,46 +22,36 @@ extern "C" { #endif /* * Class: org_ros2_rcljava_RCLJava - * Method: nativeRCLJavaInit - * Signature: ()V + * Method: nativeCreateContextHandle + * Signature: ()J */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeRCLJavaInit(JNIEnv *, jclass); +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_RCLJava_nativeCreateContextHandle(JNIEnv *, jclass); /* * Class: org_ros2_rcljava_RCLJava * Method: nativeCreateNodeHandle - * Signature: (Ljava/lang/String;Ljava/lang/String;)J + * Signature: (Ljava/lang/String;Ljava/lang/String;J)J */ -JNIEXPORT jlong JNICALL - Java_org_ros2_rcljava_RCLJava_nativeCreateNodeHandle(JNIEnv *, jclass, jstring, jstring); +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_RCLJava_nativeCreateNodeHandle( + JNIEnv *, jclass, jstring, jstring, jlong); /* * Class: org_ros2_rcljava_RCLJava * Method: nativeGetRMWIdentifier * Signature: ()Ljava/lang/String; */ -JNIEXPORT jstring JNICALL Java_org_ros2_rcljava_RCLJava_nativeGetRMWIdentifier(JNIEnv *, jclass); - -/* - * Class: org_ros2_rcljava_RCLJava - * Method: nativeOk - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_org_ros2_rcljava_RCLJava_nativeOk(JNIEnv *, jclass); - -/* - * Class: org_ros2_rcljava_RCLJava - * Method: nativeShutdown - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeShutdown(JNIEnv *, jclass); +JNIEXPORT jstring +JNICALL Java_org_ros2_rcljava_RCLJava_nativeGetRMWIdentifier(JNIEnv *, jclass); /* * Class: org_ros2_rcljava_RCLJava * Method: nativeConvertQoSProfileToHandle * Signature: (IIIIZ)J */ -JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_RCLJava_nativeConvertQoSProfileToHandle( +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_RCLJava_nativeConvertQoSProfileToHandle( JNIEnv *, jclass, jint, jint, jint, jint, jboolean); /* @@ -69,8 +59,8 @@ JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_RCLJava_nativeConvertQoSProfileToH * Method: nativeDisposeQoSProfile * Signature: (J)V */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_RCLJava_nativeDisposeQoSProfile(JNIEnv *, jclass, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_RCLJava_nativeDisposeQoSProfile(JNIEnv *, jclass, jlong); #ifdef __cplusplus } diff --git a/rcljava/include/org_ros2_rcljava_Time.h b/rcljava/include/org_ros2_rcljava_Time.h index a65b1866..9528f4db 100644 --- a/rcljava/include/org_ros2_rcljava_Time.h +++ b/rcljava/include/org_ros2_rcljava_Time.h @@ -25,14 +25,16 @@ extern "C" { * Method: nativeRCLSystemTimeNow * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_Time_nativeRCLSystemTimeNow(JNIEnv *, jclass); +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_Time_nativeRCLSystemTimeNow(JNIEnv *, jclass); /* * Class: org_ros2_rcljava_Time * Method: nativeRCLSteadyTimeNow * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_Time_nativeRCLSteadyTimeNow(JNIEnv *, jclass); +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_Time_nativeRCLSteadyTimeNow(JNIEnv *, jclass); #ifdef __cplusplus } diff --git a/rcljava/include/org_ros2_rcljava_client_ClientImpl.h b/rcljava/include/org_ros2_rcljava_client_ClientImpl.h index 49018e3e..ab71b022 100644 --- a/rcljava/include/org_ros2_rcljava_client_ClientImpl.h +++ b/rcljava/include/org_ros2_rcljava_client_ClientImpl.h @@ -25,7 +25,8 @@ extern "C" { * Method: nativeSendClientRequest * Signature: (JJJJJLorg/ros2/rcljava/interfaces/MessageDefinition;)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_client_ClientImpl_nativeSendClientRequest( +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_client_ClientImpl_nativeSendClientRequest( JNIEnv *, jclass, jlong, jlong, jlong, jlong, jlong, jobject); /* @@ -33,8 +34,8 @@ JNIEXPORT void JNICALL Java_org_ros2_rcljava_client_ClientImpl_nativeSendClientR * Method: nativeDispose * Signature: (JJ)V */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_client_ClientImpl_nativeDispose(JNIEnv *, jclass, jlong, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_client_ClientImpl_nativeDispose(JNIEnv *, jclass, jlong, jlong); #ifdef __cplusplus } diff --git a/rcljava/include/org_ros2_rcljava_contexts_ContextImpl.h b/rcljava/include/org_ros2_rcljava_contexts_ContextImpl.h new file mode 100644 index 00000000..049891d6 --- /dev/null +++ b/rcljava/include/org_ros2_rcljava_contexts_ContextImpl.h @@ -0,0 +1,59 @@ +// Copyright 2019 Open Source Robotics Foundation, Inc. +// +// 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. + +#include +/* Header for class org_ros2_rcljava_contexts_ContextImpl */ + +#ifndef ORG_ROS2_RCLJAVA_CONTEXTS_CONTEXTIMPL_H_ +#define ORG_ROS2_RCLJAVA_CONTEXTS_CONTEXTIMPL_H_ +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: org_ros2_rcljava_contexts_ContextImpl + * Method: nativeInit + * Signature: (J)V + */ +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_contexts_ContextImpl_nativeInit(JNIEnv *, jclass, jlong); + +/* + * Class: org_ros2_rcljava_contexts_ContextImpl + * Method: nativeShutdown + * Signature: (J)V + */ +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_contexts_ContextImpl_nativeShutdown(JNIEnv *, jclass, jlong); + +/* + * Class: org_ros2_rcljava_contexts_ContextImpl + * Method: nativeIsValid + * Signature: (J)Z + */ +JNIEXPORT jboolean +JNICALL Java_org_ros2_rcljava_contexts_ContextImpl_nativeIsValid(JNIEnv *, jclass, jlong); + +/* + * Class: org_ros2_rcljava_contexts_ContextImpl + * Method: nativeDispose + * Signature: (J)V + */ +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_contexts_ContextImpl_nativeDispose(JNIEnv *, jclass, jlong); + +#ifdef __cplusplus +} +#endif +#endif // ORG_ROS2_RCLJAVA_CONTEXTS_CONTEXTIMPL_H_ diff --git a/rcljava/include/org_ros2_rcljava_executors_BaseExecutor.h b/rcljava/include/org_ros2_rcljava_executors_BaseExecutor.h index f9cefbbd..c94a7959 100644 --- a/rcljava/include/org_ros2_rcljava_executors_BaseExecutor.h +++ b/rcljava/include/org_ros2_rcljava_executors_BaseExecutor.h @@ -26,55 +26,42 @@ extern "C" { * Method: nativeGetZeroInitializedWaitSet * Signature: ()J */ -JNIEXPORT jlong JNICALL - Java_org_ros2_rcljava_executors_BaseExecutor_nativeGetZeroInitializedWaitSet(JNIEnv *, jclass); +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeGetZeroInitializedWaitSet( + JNIEnv *, jclass); /* * Class: org_ros2_rcljava_executors_BaseExecutor * Method: nativeWaitSetInit - * Signature: (JIIIII)V + * Signature: (JJIIIIII)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetInit( - JNIEnv *, jclass, jlong, jint, jint, jint, jint, jint); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetInit( + JNIEnv *, jclass, jlong, jlong, jint, jint, jint, jint, jint, jint); /* * Class: org_ros2_rcljava_executors_BaseExecutor * Method: nativeDisposeWaitSet * Signature: (J)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeDisposeWaitSet( - JNIEnv *, jclass, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeDisposeWaitSet(JNIEnv *, jclass, jlong); /* * Class: org_ros2_rcljava_executors_BaseExecutor - * Method: nativeWaitSetClearSubscriptions + * Method: nativeWaitSetClear * Signature: (J)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClearSubscriptions( - JNIEnv *, jclass, jlong); - -/* - * Class: org_ros2_rcljava_executors_BaseExecutor - * Method: nativeWaitSetClearTimers - * Signature: (J)V - */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClearTimers(JNIEnv *, jclass, jlong); - -/* - * Class: org_ros2_rcljava_executors_BaseExecutor - * Method: nativeWaitSetClearServices - * Signature: (J)V - */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClearServices(JNIEnv *, jclass, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClear(JNIEnv *, jclass, jlong); /* * Class: org_ros2_rcljava_executors_BaseExecutor * Method: nativeWaitSetAddSubscription * Signature: (JJ)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddSubscription( +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddSubscription( JNIEnv *, jclass, jlong, jlong); /* @@ -82,39 +69,33 @@ JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSe * Method: nativeWait * Signature: (JJ)V */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_executors_BaseExecutor_nativeWait(JNIEnv *, jclass, jlong, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWait(JNIEnv *, jclass, jlong, jlong); /* * Class: org_ros2_rcljava_executors_BaseExecutor * Method: nativeTake * Signature: (JLjava/lang/Class;)Lorg/ros2/rcljava/interfaces/MessageDefinition; */ -JNIEXPORT jobject JNICALL - Java_org_ros2_rcljava_executors_BaseExecutor_nativeTake(JNIEnv *, jclass, jlong, jclass); +JNIEXPORT jobject +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeTake(JNIEnv *, jclass, jlong, jclass); /* * Class: org_ros2_rcljava_executors_BaseExecutor * Method: nativeWaitSetAddService * Signature: (JJ)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddService( +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddService( JNIEnv *, jclass, jlong, jlong); -/* - * Class: org_ros2_rcljava_executors_BaseExecutor - * Method: nativeWaitSetClearClients - * Signature: (J)V - */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClearClients(JNIEnv *, jclass, jlong); - /* * Class: org_ros2_rcljava_executors_BaseExecutor * Method: nativeWaitSetAddClient * Signature: (JJ)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddClient( +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddClient( JNIEnv *, jclass, jlong, jlong); /* @@ -122,7 +103,8 @@ JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSe * Method: nativeTakeRequest * Signature: (JJJJLorg/ros2/rcljava/interfaces/MessageDefinition;)Lorg/ros2/rcljava/RMWRequestId; */ -JNIEXPORT jobject JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeTakeRequest( +JNIEXPORT jobject +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeTakeRequest( JNIEnv *, jclass, jlong, jlong, jlong, jlong, jobject); /* @@ -130,7 +112,8 @@ JNIEXPORT jobject JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeTak * Method: nativeSendServiceResponse * Signature: (JLorg/ros2/rcljava/RMWRequestId;JJJLorg/ros2/rcljava/interfaces/MessageDefinition;)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeSendServiceResponse( +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeSendServiceResponse( JNIEnv *, jclass, jlong, jobject, jlong, jlong, jlong, jobject); /* @@ -138,7 +121,8 @@ JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeSendSe * Method: nativeTakeResponse * Signature: (JJJJLorg/ros2/rcljava/interfaces/MessageDefinition;)Lorg/ros2/rcljava/RMWRequestId; */ -JNIEXPORT jobject JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeTakeResponse( +JNIEXPORT jobject +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeTakeResponse( JNIEnv *, jclass, jlong, jlong, jlong, jlong, jobject); /* @@ -146,7 +130,8 @@ JNIEXPORT jobject JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeTak * Method: nativeWaitSetAddTimer * Signature: (JJ)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddTimer( +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddTimer( JNIEnv *, jclass, jlong, jlong); /* @@ -154,8 +139,8 @@ JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSe * Method: nativeWaitSetSubscriptionIsReady * Signature: (JJ)Z */ -JNIEXPORT jboolean JNICALL - Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetSubscriptionIsReady( +JNIEXPORT jboolean +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetSubscriptionIsReady( JNIEnv *, jclass, jlong, jlong); /* @@ -163,7 +148,8 @@ JNIEXPORT jboolean JNICALL * Method: nativeWaitSetTimerIsReady * Signature: (JJ)Z */ -JNIEXPORT jboolean JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetTimerIsReady( +JNIEXPORT jboolean +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetTimerIsReady( JNIEnv *, jclass, jlong, jlong); /* @@ -171,7 +157,8 @@ JNIEXPORT jboolean JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWa * Method: nativeWaitSetServiceIsReady * Signature: (JJ)Z */ -JNIEXPORT jboolean JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetServiceIsReady( +JNIEXPORT jboolean +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetServiceIsReady( JNIEnv *, jclass, jlong, jlong); /* @@ -179,7 +166,8 @@ JNIEXPORT jboolean JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWa * Method: nativeWaitSetClientIsReady * Signature: (JJ)Z */ -JNIEXPORT jboolean JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClientIsReady( +JNIEXPORT jboolean +JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClientIsReady( JNIEnv *, jclass, jlong, jlong); #ifdef __cplusplus diff --git a/rcljava/include/org_ros2_rcljava_node_NodeImpl.h b/rcljava/include/org_ros2_rcljava_node_NodeImpl.h index 260efc68..181c0c30 100644 --- a/rcljava/include/org_ros2_rcljava_node_NodeImpl.h +++ b/rcljava/include/org_ros2_rcljava_node_NodeImpl.h @@ -25,7 +25,8 @@ extern "C" { * Method: nativeCreatePublisherHandle * Signature: (JLjava/lang/Class;Ljava/lang/String;J)J */ -JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreatePublisherHandle( +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreatePublisherHandle( JNIEnv *, jclass, jlong, jclass, jstring, jlong); /* @@ -33,7 +34,8 @@ JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreatePublishe * Method: nativeCreateSubscriptionHandle * Signature: (JLjava/lang/Class;Ljava/lang/String;J)J */ -JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateSubscriptionHandle( +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateSubscriptionHandle( JNIEnv *, jclass, jlong, jclass, jstring, jlong); /* @@ -41,7 +43,8 @@ JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateSubscrip * Method: nativeCreateServiceHandle * Signature: (JLjava/lang/Class;Ljava/lang/String;J)J */ -JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateServiceHandle( +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateServiceHandle( JNIEnv *, jclass, jlong, jclass, jstring, jlong); /* @@ -49,7 +52,8 @@ JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateServiceH * Method: nativeCreateClientHandle * Signature: (JLjava/lang/Class;Ljava/lang/String;J)J */ -JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateClientHandle( +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateClientHandle( JNIEnv *, jclass, jlong, jclass, jstring, jlong); /* @@ -57,15 +61,17 @@ JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateClientHa * Method: nativeDispose * Signature: (J)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeDispose(JNIEnv *, jclass, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeDispose(JNIEnv *, jclass, jlong); /* * Class: org_ros2_rcljava_node_NodeImpl * Method: nativeCreateTimerHandle - * Signature: (J)J + * Signature: (JJJ)J */ -JNIEXPORT jlong JNICALL - Java_org_ros2_rcljava_node_NodeImpl_nativeCreateTimerHandle(JNIEnv *, jclass, jlong); +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateTimerHandle( + JNIEnv *, jclass, jlong, jlong, jlong); #ifdef __cplusplus } diff --git a/rcljava/include/org_ros2_rcljava_publisher_PublisherImpl.h b/rcljava/include/org_ros2_rcljava_publisher_PublisherImpl.h index 6a4b09e7..befe7447 100644 --- a/rcljava/include/org_ros2_rcljava_publisher_PublisherImpl.h +++ b/rcljava/include/org_ros2_rcljava_publisher_PublisherImpl.h @@ -25,7 +25,8 @@ extern "C" { * Method: nativePublish * Signature: (JJLorg/ros2/rcljava/interfaces/MessageDefinition;)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_publisher_PublisherImpl_nativePublish( +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_publisher_PublisherImpl_nativePublish( JNIEnv *, jclass, jlong, jlong, jobject); /* @@ -33,8 +34,9 @@ JNIEXPORT void JNICALL Java_org_ros2_rcljava_publisher_PublisherImpl_nativePubli * Method: nativeDispose * Signature: (JJ)V */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_publisher_PublisherImpl_nativeDispose(JNIEnv *, jclass, jlong, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_publisher_PublisherImpl_nativeDispose( + JNIEnv *, jclass, jlong, jlong); #ifdef __cplusplus } diff --git a/rcljava/include/org_ros2_rcljava_service_ServiceImpl.h b/rcljava/include/org_ros2_rcljava_service_ServiceImpl.h index 27457dcf..d83cf74c 100644 --- a/rcljava/include/org_ros2_rcljava_service_ServiceImpl.h +++ b/rcljava/include/org_ros2_rcljava_service_ServiceImpl.h @@ -25,8 +25,8 @@ extern "C" { * Method: nativeDispose * Signature: (JJ)V */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_service_ServiceImpl_nativeDispose(JNIEnv *, jclass, jlong, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_service_ServiceImpl_nativeDispose(JNIEnv *, jclass, jlong, jlong); #ifdef __cplusplus } diff --git a/rcljava/include/org_ros2_rcljava_subscription_SubscriptionImpl.h b/rcljava/include/org_ros2_rcljava_subscription_SubscriptionImpl.h index 8cb7b141..afb3456c 100644 --- a/rcljava/include/org_ros2_rcljava_subscription_SubscriptionImpl.h +++ b/rcljava/include/org_ros2_rcljava_subscription_SubscriptionImpl.h @@ -25,8 +25,9 @@ extern "C" { * Method: nativeDispose * Signature: (JJ)V */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_subscription_SubscriptionImpl_nativeDispose(JNIEnv *, jclass, jlong, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_subscription_SubscriptionImpl_nativeDispose( + JNIEnv *, jclass, jlong, jlong); #ifdef __cplusplus } diff --git a/rcljava/include/org_ros2_rcljava_time_Clock.h b/rcljava/include/org_ros2_rcljava_time_Clock.h new file mode 100644 index 00000000..430ca4b0 --- /dev/null +++ b/rcljava/include/org_ros2_rcljava_time_Clock.h @@ -0,0 +1,42 @@ +// Copyright 2019 Open Source Robotics Foundation, Inc. +// +// 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. + +#include +/* Header for class org_ros2_rcljava_time_Clock */ + +#ifndef ORG_ROS2_RCLJAVA_TIME_CLOCK_H_ +#define ORG_ROS2_RCLJAVA_TIME_CLOCK_H_ +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_ros2_rcljava_time_Clock + * Method: nativeCreateClock + * Signature: (Lorg/ros2/rcljava/time/ClockType;)J + */ +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_time_Clock_nativeCreateClockHandle(JNIEnv *, jclass, jobject); + +/* + * Class: org_ros2_rcljava_time_Clock + * Method: nativeDispose + * Signature: (J)V + */ +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_time_Clock_nativeDispose(JNIEnv *, jclass, jlong); + +#ifdef __cplusplus +} +#endif +#endif // ORG_ROS2_RCLJAVA_TIME_CLOCK_H_ diff --git a/rcljava/include/org_ros2_rcljava_timer_WallTimerImpl.h b/rcljava/include/org_ros2_rcljava_timer_WallTimerImpl.h index 3094dc16..88c9daeb 100644 --- a/rcljava/include/org_ros2_rcljava_timer_WallTimerImpl.h +++ b/rcljava/include/org_ros2_rcljava_timer_WallTimerImpl.h @@ -26,80 +26,81 @@ extern "C" { * Method: nativeDispose * Signature: (J)V */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_timer_WallTimerImpl_nativeDispose(JNIEnv *, jclass, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_timer_WallTimerImpl_nativeDispose(JNIEnv *, jclass, jlong); /* * Class: org_ros2_rcljava_timer_WallTimerImpl * Method: nativeIsReady * Signature: (J)Z */ -JNIEXPORT jboolean JNICALL - Java_org_ros2_rcljava_timer_WallTimerImpl_nativeIsReady(JNIEnv *, jclass, jlong); +JNIEXPORT jboolean +JNICALL Java_org_ros2_rcljava_timer_WallTimerImpl_nativeIsReady(JNIEnv *, jclass, jlong); /* * Class: org_ros2_rcljava_timer_WallTimerImpl * Method: nativeIsCanceled * Signature: (J)Z */ -JNIEXPORT jboolean JNICALL - Java_org_ros2_rcljava_timer_WallTimerImpl_nativeIsCanceled(JNIEnv *, jclass, jlong); +JNIEXPORT jboolean +JNICALL Java_org_ros2_rcljava_timer_WallTimerImpl_nativeIsCanceled(JNIEnv *, jclass, jlong); /* * Class: org_ros2_rcljava_timer_WallTimerImpl * Method: nativeReset * Signature: (J)Z */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_timer_WallTimerImpl_nativeReset(JNIEnv *, jclass, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_timer_WallTimerImpl_nativeReset(JNIEnv *, jclass, jlong); /* * Class: org_ros2_rcljava_timer_WallTimerImpl * Method: nativeCancel * Signature: (J)Z */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_timer_WallTimerImpl_nativeCancel(JNIEnv *, jclass, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_timer_WallTimerImpl_nativeCancel(JNIEnv *, jclass, jlong); /* * Class: org_ros2_rcljava_timer_WallTimerImpl * Method: nativeTimeUntilNextCall * Signature: (J)J */ -JNIEXPORT jlong JNICALL - Java_org_ros2_rcljava_timer_WallTimerImpl_nativeTimeUntilNextCall(JNIEnv *, jclass, jlong); +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_timer_WallTimerImpl_nativeTimeUntilNextCall(JNIEnv *, jclass, jlong); /* * Class: org_ros2_rcljava_timer_WallTimerImpl * Method: nativeTimeSinceLastCall * Signature: (J)J */ -JNIEXPORT jlong JNICALL - Java_org_ros2_rcljava_timer_WallTimerImpl_nativeTimeSinceLastCall(JNIEnv *, jclass, jlong); +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_timer_WallTimerImpl_nativeTimeSinceLastCall(JNIEnv *, jclass, jlong); /* * Class: org_ros2_rcljava_timer_WallTimerImpl * Method: nativeGetTimerPeriodNS * Signature: (J)J */ -JNIEXPORT jlong JNICALL - Java_org_ros2_rcljava_timer_WallTimerImpl_nativeGetTimerPeriodNS(JNIEnv *, jclass, jlong); +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_timer_WallTimerImpl_nativeGetTimerPeriodNS(JNIEnv *, jclass, jlong); /* * Class: org_ros2_rcljava_timer_WallTimerImpl * Method: nativeSetTimerPeriodNS * Signature: (JJ)Z */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_timer_WallTimerImpl_nativeSetTimerPeriodNS(JNIEnv *, jclass, jlong, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_timer_WallTimerImpl_nativeSetTimerPeriodNS( + JNIEnv *, jclass, jlong, jlong); /* * Class: org_ros2_rcljava_timer_WallTimerImpl * Method: nativeCallTimer * Signature: (J)Z */ -JNIEXPORT void JNICALL - Java_org_ros2_rcljava_timer_WallTimerImpl_nativeCallTimer(JNIEnv *, jclass, jlong); +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_timer_WallTimerImpl_nativeCallTimer(JNIEnv *, jclass, jlong); #ifdef __cplusplus } diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp index 3604b22c..00249c2b 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp @@ -35,21 +35,18 @@ using rcljava_common::signatures::convert_from_java_signature; using rcljava_common::signatures::convert_to_java_signature; using rcljava_common::signatures::destroy_ros_message_signature; -JNIEXPORT void JNICALL -Java_org_ros2_rcljava_RCLJava_nativeRCLJavaInit(JNIEnv * env, jclass) +JNIEXPORT jlong JNICALL +Java_org_ros2_rcljava_RCLJava_nativeCreateContextHandle(JNIEnv *, jclass) { - // TODO(esteve): parse args - rcl_ret_t ret = rcl_init(0, nullptr, rcl_get_default_allocator()); - if (ret != RCL_RET_OK) { - std::string msg = "Failed to init: " + std::string(rcl_get_error_string_safe()); - rcl_reset_error(); - rcljava_throw_rclexception(env, ret, msg); - } + rcl_context_t * context = static_cast(malloc(sizeof(rcl_context_t))); + *context = rcl_get_zero_initialized_context(); + jlong context_handle = reinterpret_cast(context); + return context_handle; } JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_RCLJava_nativeCreateNodeHandle( - JNIEnv * env, jclass, jstring jnode_name, jstring jnamespace) + JNIEnv * env, jclass, jstring jnode_name, jstring jnamespace, jlong context_handle) { const char * node_name_tmp = env->GetStringUTFChars(jnode_name, 0); std::string node_name(node_name_tmp); @@ -59,13 +56,16 @@ Java_org_ros2_rcljava_RCLJava_nativeCreateNodeHandle( std::string namespace_(namespace_tmp); env->ReleaseStringUTFChars(jnamespace, namespace_tmp); + rcl_context_t * context = reinterpret_cast(context_handle); + rcl_node_t * node = static_cast(malloc(sizeof(rcl_node_t))); *node = rcl_get_zero_initialized_node(); rcl_node_options_t default_options = rcl_node_get_default_options(); - rcl_ret_t ret = rcl_node_init(node, node_name.c_str(), namespace_.c_str(), &default_options); + rcl_ret_t ret = rcl_node_init( + node, node_name.c_str(), namespace_.c_str(), context, &default_options); if (ret != RCL_RET_OK) { - std::string msg = "Failed to create node: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to create node: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); return 0; @@ -82,23 +82,6 @@ Java_org_ros2_rcljava_RCLJava_nativeGetRMWIdentifier(JNIEnv * env, jclass) return env->NewStringUTF(rmw_implementation_identifier); } -JNIEXPORT jboolean JNICALL -Java_org_ros2_rcljava_RCLJava_nativeOk(JNIEnv *, jclass) -{ - return rcl_ok(); -} - -JNIEXPORT void JNICALL -Java_org_ros2_rcljava_RCLJava_nativeShutdown(JNIEnv * env, jclass) -{ - rcl_ret_t ret = rcl_shutdown(); - if (ret != RCL_RET_OK) { - std::string msg = "Failed to shutdown: " + std::string(rcl_get_error_string_safe()); - rcl_reset_error(); - rcljava_throw_rclexception(env, ret, msg); - } -} - JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_RCLJava_nativeConvertQoSProfileToHandle( JNIEnv *, jclass, jint history, jint depth, jint reliability, jint durability, @@ -110,6 +93,11 @@ Java_org_ros2_rcljava_RCLJava_nativeConvertQoSProfileToHandle( qos_profile->depth = depth; qos_profile->reliability = static_cast(reliability); qos_profile->durability = static_cast(durability); + // TODO(jacobperron): Expose deadline, lifespan, and liveliness settings as parameters + qos_profile->deadline = rmw_qos_profile_default.deadline; + qos_profile->lifespan = rmw_qos_profile_default.lifespan; + qos_profile->liveliness = rmw_qos_profile_default.liveliness; + qos_profile->liveliness_lease_duration = rmw_qos_profile_default.liveliness_lease_duration; qos_profile->avoid_ros_namespace_conventions = avoidROSNamespaceConventions; return reinterpret_cast(qos_profile); } diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_Time.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_Time.cpp index fb9f1de1..1f287b7f 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_Time.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_Time.cpp @@ -32,7 +32,7 @@ Java_org_ros2_rcljava_Time_nativeRCLSystemTimeNow(JNIEnv * env, jclass) rcutils_ret_t ret = RCUTILS_RET_ERROR; ret = rcutils_system_time_now(&rcutils_now); if (ret != RCUTILS_RET_OK) { - std::string msg = "Could not get current time: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Could not get current time: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -46,7 +46,7 @@ Java_org_ros2_rcljava_Time_nativeRCLSteadyTimeNow(JNIEnv * env, jclass) rcutils_ret_t ret = RCUTILS_RET_ERROR; ret = rcutils_steady_time_now(&rcutils_now); if (ret != RCUTILS_RET_OK) { - std::string msg = "Could not get current time: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Could not get current time: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp index 9dd0c724..aec896e9 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp @@ -60,7 +60,7 @@ Java_org_ros2_rcljava_client_ClientImpl_nativeSendClientRequest( if (ret != RCL_RET_OK) { std::string msg = - "Failed to send request from a client: " + std::string(rcl_get_error_string_safe()); + "Failed to send request from a client: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -91,7 +91,7 @@ Java_org_ros2_rcljava_client_ClientImpl_nativeDispose( rcl_ret_t ret = rcl_client_fini(client, node); if (ret != RCL_RET_OK) { - std::string msg = "Failed to destroy client: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to destroy client: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp new file mode 100644 index 00000000..4b2f8695 --- /dev/null +++ b/rcljava/src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp @@ -0,0 +1,108 @@ +// Copyright 2019 Open Source Robotics Foundation, Inc. +// +// 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. + +#include + +#include + +#include "rcl/context.h" +#include "rcl/error_handling.h" +#include "rcl/init.h" + +#include "rcljava_common/exceptions.h" +#include "rcljava_common/signatures.h" + +#include "org_ros2_rcljava_contexts_ContextImpl.h" + +using rcljava_common::exceptions::rcljava_throw_rclexception; + +JNIEXPORT jboolean JNICALL +Java_org_ros2_rcljava_contexts_ContextImpl_nativeIsValid(JNIEnv *, jclass, jlong context_handle) +{ + rcl_context_t * context = reinterpret_cast(context_handle); + bool is_valid = rcl_context_is_valid(context); + return is_valid; +} + +JNIEXPORT void JNICALL +Java_org_ros2_rcljava_contexts_ContextImpl_nativeInit(JNIEnv * env, jclass, jlong context_handle) +{ + // TODO(jacobperron): Encapsulate init options into a Java class + rcl_init_options_t init_options = rcl_get_zero_initialized_init_options(); + rcl_ret_t ret = rcl_init_options_init(&init_options, rcl_get_default_allocator()); + if (RCL_RET_OK != ret) { + std::string msg = "Failed to init context options: " + std::string(rcl_get_error_string().str); + rcl_reset_error(); + rcljava_throw_rclexception(env, ret, msg); + return; + } + + rcl_context_t * context = reinterpret_cast(context_handle); + // TODO(esteve): parse args + ret = rcl_init(0, nullptr, &init_options, context); + if (RCL_RET_OK != ret) { + std::string msg = "Failed to init context: " + std::string(rcl_get_error_string().str); + rcl_ret_t ignored_ret = rcl_init_options_fini(&init_options); + (void)ignored_ret; + rcl_reset_error(); + rcljava_throw_rclexception(env, ret, msg); + return; + } + + rcl_ret_t fini_ret = rcl_init_options_fini(&init_options); + if (RCL_RET_OK != fini_ret) { + std::string msg = "Failed to init context: " + std::string(rcl_get_error_string().str); + rcl_ret_t ignored_ret = rcl_shutdown(context); + (void)ignored_ret; + rcl_reset_error(); + rcljava_throw_rclexception(env, ret, msg); + return; + } +} + +JNIEXPORT void JNICALL +Java_org_ros2_rcljava_contexts_ContextImpl_nativeShutdown( + JNIEnv * env, jclass, jlong context_handle) +{ + rcl_context_t * context = reinterpret_cast(context_handle); + // Only attempt shutdown if the context is valid + if (!rcl_context_is_valid(context)) { + return; + } + rcl_ret_t ret = rcl_shutdown(context); + if (RCL_RET_OK != ret) { + std::string msg = "Failed to shutdown context: " + std::string(rcl_get_error_string().str); + rcl_reset_error(); + rcljava_throw_rclexception(env, ret, msg); + } +} + +JNIEXPORT void JNICALL +Java_org_ros2_rcljava_contexts_ContextImpl_nativeDispose(JNIEnv * env, jclass, jlong context_handle) +{ + if (0 == context_handle) { + // already destroyed + return; + } + + rcl_context_t * context = reinterpret_cast(context_handle); + + rcl_ret_t ret = rcl_context_fini(context); + + if (RCL_RET_OK != ret) { + std::string msg = "Failed to destroy context: " + std::string(rcl_get_error_string().str); + rcl_reset_error(); + rcljava_throw_rclexception(env, ret, msg); + } +} diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_executors_BaseExecutor.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_executors_BaseExecutor.cpp index 62ac4af3..3f948b1b 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_executors_BaseExecutor.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_executors_BaseExecutor.cpp @@ -104,17 +104,18 @@ Java_org_ros2_rcljava_executors_BaseExecutor_nativeGetZeroInitializedWaitSet(JNI JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetInit( - JNIEnv * env, jclass, jlong wait_set_handle, jint number_of_subscriptions, + JNIEnv * env, jclass, jlong wait_set_handle, jlong context_handle, jint number_of_subscriptions, jint number_of_guard_conditions, jint number_of_timers, jint number_of_clients, - jint number_of_services) + jint number_of_services, jint number_of_events) { rcl_wait_set_t * wait_set = reinterpret_cast(wait_set_handle); + rcl_context_t * context = reinterpret_cast(context_handle); rcl_ret_t ret = rcl_wait_set_init( wait_set, number_of_subscriptions, number_of_guard_conditions, number_of_timers, - number_of_clients, number_of_services, rcl_get_default_allocator()); + number_of_clients, number_of_services, number_of_events, context, rcl_get_default_allocator()); if (ret != RCL_RET_OK) { - std::string msg = "Failed to initialize wait set: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to initialize wait set: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -128,21 +129,21 @@ Java_org_ros2_rcljava_executors_BaseExecutor_nativeDisposeWaitSet( rcl_ret_t ret = rcl_wait_set_fini(wait_set); if (ret != RCL_RET_OK) { - std::string msg = "Failed to destroy timer: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to destroy timer: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } } JNIEXPORT void JNICALL -Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClearSubscriptions( +Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClear( JNIEnv * env, jclass, jlong wait_set_handle) { rcl_wait_set_t * wait_set = reinterpret_cast(wait_set_handle); - rcl_ret_t ret = rcl_wait_set_clear_subscriptions(wait_set); + rcl_ret_t ret = rcl_wait_set_clear(wait_set); if (ret != RCL_RET_OK) { std::string msg = - "Failed to clear subscriptions from wait set: " + std::string(rcl_get_error_string_safe()); + "Failed to clear wait set: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -154,10 +155,10 @@ Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddSubscription( { rcl_wait_set_t * wait_set = reinterpret_cast(wait_set_handle); rcl_subscription_t * subscription = reinterpret_cast(subscription_handle); - rcl_ret_t ret = rcl_wait_set_add_subscription(wait_set, subscription); + rcl_ret_t ret = rcl_wait_set_add_subscription(wait_set, subscription, nullptr); if (ret != RCL_RET_OK) { std::string msg = - "Failed to add subscription to wait set: " + std::string(rcl_get_error_string_safe()); + "Failed to add subscription to wait set: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -170,7 +171,7 @@ Java_org_ros2_rcljava_executors_BaseExecutor_nativeWait( rcl_wait_set_t * wait_set = reinterpret_cast(wait_set_handle); rcl_ret_t ret = rcl_wait(wait_set, timeout); if (ret != RCL_RET_OK && ret != RCL_RET_TIMEOUT) { - std::string msg = "Failed to wait on wait set: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to wait on wait set: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -199,13 +200,13 @@ Java_org_ros2_rcljava_executors_BaseExecutor_nativeTake( void * taken_msg = convert_from_java(jmsg, nullptr); - rcl_ret_t ret = rcl_take(subscription, taken_msg, nullptr); + rcl_ret_t ret = rcl_take(subscription, taken_msg, nullptr, nullptr); if (ret != RCL_RET_OK && ret != RCL_RET_SUBSCRIPTION_TAKE_FAILED) { destroy_ros_message(taken_msg); std::string msg = - "Failed to take from a subscription: " + std::string(rcl_get_error_string_safe()); + "Failed to take from a subscription: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); return nullptr; @@ -230,58 +231,16 @@ Java_org_ros2_rcljava_executors_BaseExecutor_nativeTake( return nullptr; } -JNIEXPORT void JNICALL -Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClearTimers( - JNIEnv * env, jclass, jlong wait_set_handle) -{ - rcl_wait_set_t * wait_set = reinterpret_cast(wait_set_handle); - rcl_ret_t ret = rcl_wait_set_clear_timers(wait_set); - if (ret != RCL_RET_OK) { - std::string msg = - "Failed to clear timers from wait set: " + std::string(rcl_get_error_string_safe()); - rcl_reset_error(); - rcljava_throw_rclexception(env, ret, msg); - } -} - -JNIEXPORT void JNICALL -Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClearServices( - JNIEnv * env, jclass, jlong wait_set_handle) -{ - rcl_wait_set_t * wait_set = reinterpret_cast(wait_set_handle); - rcl_ret_t ret = rcl_wait_set_clear_services(wait_set); - if (ret != RCL_RET_OK) { - std::string msg = - "Failed to clear services from wait set: " + std::string(rcl_get_error_string_safe()); - rcl_reset_error(); - rcljava_throw_rclexception(env, ret, msg); - } -} - JNIEXPORT void JNICALL Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddService( JNIEnv * env, jclass, jlong wait_set_handle, jlong service_handle) { rcl_wait_set_t * wait_set = reinterpret_cast(wait_set_handle); rcl_service_t * service = reinterpret_cast(service_handle); - rcl_ret_t ret = rcl_wait_set_add_service(wait_set, service); - if (ret != RCL_RET_OK) { - std::string msg = - "Failed to add service to wait set: " + std::string(rcl_get_error_string_safe()); - rcl_reset_error(); - rcljava_throw_rclexception(env, ret, msg); - } -} - -JNIEXPORT void JNICALL -Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetClearClients( - JNIEnv * env, jclass, jlong wait_set_handle) -{ - rcl_wait_set_t * wait_set = reinterpret_cast(wait_set_handle); - rcl_ret_t ret = rcl_wait_set_clear_clients(wait_set); + rcl_ret_t ret = rcl_wait_set_add_service(wait_set, service, nullptr); if (ret != RCL_RET_OK) { std::string msg = - "Failed to clear clients from wait set: " + std::string(rcl_get_error_string_safe()); + "Failed to add service to wait set: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -293,10 +252,10 @@ Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddClient( { rcl_wait_set_t * wait_set = reinterpret_cast(wait_set_handle); rcl_client_t * client = reinterpret_cast(client_handle); - rcl_ret_t ret = rcl_wait_set_add_client(wait_set, client); + rcl_ret_t ret = rcl_wait_set_add_client(wait_set, client, nullptr); if (ret != RCL_RET_OK) { std::string msg = - "Failed to add client to wait set: " + std::string(rcl_get_error_string_safe()); + "Failed to add client to wait set: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -308,10 +267,10 @@ Java_org_ros2_rcljava_executors_BaseExecutor_nativeWaitSetAddTimer( { rcl_wait_set_t * wait_set = reinterpret_cast(wait_set_handle); rcl_timer_t * timer = reinterpret_cast(timer_handle); - rcl_ret_t ret = rcl_wait_set_add_timer(wait_set, timer); + rcl_ret_t ret = rcl_wait_set_add_timer(wait_set, timer, nullptr); if (ret != RCL_RET_OK) { std::string msg = - "Failed to add timer to wait set: " + std::string(rcl_get_error_string_safe()); + "Failed to add timer to wait set: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -348,7 +307,7 @@ Java_org_ros2_rcljava_executors_BaseExecutor_nativeTakeRequest( destroy_ros_message(taken_msg); std::string msg = - "Failed to take request from a service: " + std::string(rcl_get_error_string_safe()); + "Failed to take request from a service: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); return nullptr; @@ -399,7 +358,7 @@ Java_org_ros2_rcljava_executors_BaseExecutor_nativeSendServiceResponse( if (ret != RCL_RET_OK) { std::string msg = - "Failed to send response from a service: " + std::string(rcl_get_error_string_safe()); + "Failed to send response from a service: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -438,7 +397,7 @@ Java_org_ros2_rcljava_executors_BaseExecutor_nativeTakeResponse( destroy_ros_message(taken_msg); std::string msg = - "Failed to take request from a service: " + std::string(rcl_get_error_string_safe()); + "Failed to take request from a service: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); return nullptr; diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp index a5b7a78a..16db9d04 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp @@ -59,7 +59,7 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreatePublisherHandle( rcl_ret_t ret = rcl_publisher_init(publisher, node, ts, topic.c_str(), &publisher_ops); if (ret != RCL_RET_OK) { - std::string msg = "Failed to create publisher: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to create publisher: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); return 0; @@ -98,7 +98,7 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreateSubscriptionHandle( rcl_ret_t ret = rcl_subscription_init(subscription, node, ts, topic.c_str(), &subscription_ops); if (ret != RCL_RET_OK) { - std::string msg = "Failed to create subscription: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to create subscription: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); return 0; @@ -141,7 +141,7 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreateServiceHandle( rcl_ret_t ret = rcl_service_init(service, node, ts, service_name.c_str(), &service_ops); if (ret != RCL_RET_OK) { - std::string msg = "Failed to create service: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to create service: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); return 0; @@ -184,7 +184,7 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreateClientHandle( rcl_ret_t ret = rcl_client_init(client, node, ts, service_name.c_str(), &client_ops); if (ret != RCL_RET_OK) { - std::string msg = "Failed to create client: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to create client: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); return 0; @@ -207,7 +207,7 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeDispose(JNIEnv * env, jclass, jlong no rcl_ret_t ret = rcl_node_fini(node); if (ret != RCL_RET_OK) { - std::string msg = "Failed to destroy node: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to destroy node: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -215,15 +215,19 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeDispose(JNIEnv * env, jclass, jlong no JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateTimerHandle( - JNIEnv * env, jclass, jlong timer_period) + JNIEnv * env, jclass, jlong clock_handle, jlong context_handle, jlong timer_period) { + rcl_clock_t * clock = reinterpret_cast(clock_handle); + rcl_context_t * context = reinterpret_cast(context_handle); + rcl_timer_t * timer = static_cast(malloc(sizeof(rcl_timer_t))); *timer = rcl_get_zero_initialized_timer(); - rcl_ret_t ret = rcl_timer_init(timer, timer_period, NULL, rcl_get_default_allocator()); + rcl_ret_t ret = rcl_timer_init( + timer, clock, context, timer_period, NULL, rcl_get_default_allocator()); if (ret != RCL_RET_OK) { - std::string msg = "Failed to create timer: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to create timer: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); return 0; diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp index d4ffcc9f..fef34631 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp @@ -49,14 +49,14 @@ Java_org_ros2_rcljava_publisher_PublisherImpl_nativePublish( void * raw_ros_message = convert_from_java(jmsg, nullptr); - rcl_ret_t ret = rcl_publish(publisher, raw_ros_message); + rcl_ret_t ret = rcl_publish(publisher, raw_ros_message, nullptr); destroy_ros_message_signature destroy_ros_message = reinterpret_cast(jmsg_destructor_handle); destroy_ros_message(raw_ros_message); if (ret != RCL_RET_OK) { - std::string msg = "Failed to publish: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to publish: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } @@ -85,7 +85,7 @@ Java_org_ros2_rcljava_publisher_PublisherImpl_nativeDispose( rcl_ret_t ret = rcl_publisher_fini(publisher, node); if (ret != RCL_RET_OK) { - std::string msg = "Failed to destroy publisher: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to destroy publisher: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp index fcfbdbe0..e56289d8 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp @@ -56,7 +56,7 @@ Java_org_ros2_rcljava_service_ServiceImpl_nativeDispose( rcl_ret_t ret = rcl_service_fini(service, node); if (ret != RCL_RET_OK) { - std::string msg = "Failed to destroy service: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to destroy service: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_subscription_SubscriptionImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_subscription_SubscriptionImpl.cpp index b2a30ac2..d0e84bdf 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_subscription_SubscriptionImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_subscription_SubscriptionImpl.cpp @@ -56,7 +56,7 @@ Java_org_ros2_rcljava_subscription_SubscriptionImpl_nativeDispose( rcl_ret_t ret = rcl_subscription_fini(subscription, node); if (ret != RCL_RET_OK) { - std::string msg = "Failed to destroy subscription: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to destroy subscription: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_rclexception(env, ret, msg); } diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_time_Clock.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_time_Clock.cpp new file mode 100644 index 00000000..fe54ed1b --- /dev/null +++ b/rcljava/src/main/cpp/org_ros2_rcljava_time_Clock.cpp @@ -0,0 +1,85 @@ +// Copyright 2019 Open Source Robotics Foundation, Inc. +// +// 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. + +#include + +#include +#include + +#include "rcl/error_handling.h" +#include "rcl/time.h" + +#include "rcljava_common/exceptions.h" +#include "rcljava_common/signatures.h" + +#include "org_ros2_rcljava_time_Clock.h" + +using rcljava_common::exceptions::rcljava_throw_exception; +using rcljava_common::exceptions::rcljava_throw_rclexception; + +JNIEXPORT jlong JNICALL +Java_org_ros2_rcljava_time_Clock_nativeCreateClockHandle(JNIEnv * env, jclass, jobject jclock_type) +{ + // Convert from Java ClockType to rcl_clock_type_t + jclass clock_type_enum = env->FindClass("org/ros2/rcljava/time/ClockType"); + jmethodID get_name_method = env->GetMethodID(clock_type_enum, "name", "()Ljava/lang/String;"); + jstring clock_type_name = (jstring)env->CallObjectMethod(jclock_type, get_name_method); + const char * clock_type_name_native = env->GetStringUTFChars(clock_type_name, 0); + rcl_clock_type_t clock_type; + if (strcmp(clock_type_name_native, "TIME_SOURCE_UNINITIALIZED") == 0) { + clock_type = RCL_CLOCK_UNINITIALIZED; + } else if (strcmp(clock_type_name_native, "ROS_TIME") == 0) { + clock_type = RCL_ROS_TIME; + } else if (strcmp(clock_type_name_native, "SYSTEM_TIME") == 0) { + clock_type = RCL_SYSTEM_TIME; + } else if (strcmp(clock_type_name_native, "STEADY_TIME") == 0) { + clock_type = RCL_STEADY_TIME; + } else { + std::string msg = "Unexpected clock type: " + std::string(clock_type_name_native); + rcljava_throw_exception(env, "java/lang/EnumConstantNotPresentException", msg); + return 0; + } + + // Initialize clock + rcl_clock_t * clock = static_cast(malloc(sizeof(rcl_clock_t))); + rcl_allocator_t allocator = rcl_get_default_allocator(); + rcl_ret_t ret = rcl_clock_init(clock_type, clock, &allocator); + if (RCL_RET_OK != ret) { + std::string msg = "Failed to init clock: " + std::string(rcl_get_error_string().str); + rcl_reset_error(); + rcljava_throw_rclexception(env, ret, msg); + return 0; + } + + jlong clock_handle = reinterpret_cast(clock); + return clock_handle; +} + +JNIEXPORT void JNICALL +Java_org_ros2_rcljava_time_Clock_nativeDispose(JNIEnv * env, jclass, jlong clock_handle) +{ + if (0 == clock_handle) { + // already destroyed + return; + } + + rcl_clock_t * clock = reinterpret_cast(clock_handle); + + rcl_ret_t ret = rcl_clock_fini(clock); + if (RCL_RET_OK != ret) { + std::string msg = "Failed to destroy clock: " + std::string(rcl_get_error_string().str); + rcl_reset_error(); + rcljava_throw_rclexception(env, ret, msg); + } +} diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_timer_WallTimerImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_timer_WallTimerImpl.cpp index 0db954e6..dd0ed3a3 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_timer_WallTimerImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_timer_WallTimerImpl.cpp @@ -45,7 +45,7 @@ Java_org_ros2_rcljava_timer_WallTimerImpl_nativeIsReady( rcl_ret_t ret = rcl_timer_is_ready(timer, &is_ready); if (ret != RCL_RET_OK) { - std::string msg = "Failed to check timer ready: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to check timer ready: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_exception(env, "java/lang/IllegalStateException", msg); } @@ -65,7 +65,7 @@ Java_org_ros2_rcljava_timer_WallTimerImpl_nativeIsCanceled( rcl_ret_t ret = rcl_timer_is_canceled(timer, &is_canceled); if (ret != RCL_RET_OK) { - std::string msg = "Failed to check timer canceled: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to check timer canceled: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_exception(env, "java/lang/IllegalStateException", msg); } @@ -89,7 +89,7 @@ Java_org_ros2_rcljava_timer_WallTimerImpl_nativeDispose( rcl_ret_t ret = rcl_timer_fini(timer); if (ret != RCL_RET_OK) { - std::string msg = "Failed to destroy timer: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to destroy timer: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_exception(env, "java/lang/IllegalStateException", msg); } @@ -107,7 +107,7 @@ Java_org_ros2_rcljava_timer_WallTimerImpl_nativeReset(JNIEnv * env, jclass, jlon rcl_ret_t ret = rcl_timer_reset(timer); if (ret != RCL_RET_OK) { - std::string msg = "Failed to reset timer: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to reset timer: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_exception(env, "java/lang/IllegalStateException", msg); } @@ -126,7 +126,7 @@ Java_org_ros2_rcljava_timer_WallTimerImpl_nativeCancel( rcl_ret_t ret = rcl_timer_cancel(timer); if (ret != RCL_RET_OK) { - std::string msg = "Failed to cancel timer: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to cancel timer: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_exception(env, "java/lang/IllegalStateException", msg); } @@ -147,7 +147,7 @@ Java_org_ros2_rcljava_timer_WallTimerImpl_nativeTimeUntilNextCall( if (ret != RCL_RET_OK) { std::string msg = - "Failed to get time until next timer call: " + std::string(rcl_get_error_string_safe()); + "Failed to get time until next timer call: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_exception(env, "java/lang/IllegalStateException", msg); return 0; @@ -171,7 +171,7 @@ Java_org_ros2_rcljava_timer_WallTimerImpl_nativeTimeSinceLastCall( if (ret != RCL_RET_OK) { std::string msg = - "Failed to get time until next timer call: " + std::string(rcl_get_error_string_safe()); + "Failed to get time until next timer call: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_exception(env, "java/lang/IllegalStateException", msg); return 0; @@ -194,7 +194,7 @@ Java_org_ros2_rcljava_timer_WallTimerImpl_nativeGetTimerPeriodNS( rcl_ret_t ret = rcl_timer_get_period(timer, &timer_period); if (ret != RCL_RET_OK) { - std::string msg = "Failed to get timer period: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to get timer period: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_exception(env, "java/lang/IllegalStateException", msg); return 0; @@ -217,7 +217,7 @@ Java_org_ros2_rcljava_timer_WallTimerImpl_nativeSetTimerPeriodNS( rcl_ret_t ret = rcl_timer_exchange_period(timer, timer_period, &old_period); if (ret != RCL_RET_OK) { - std::string msg = "Failed to set timer period: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to set timer period: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_exception(env, "java/lang/IllegalStateException", msg); } @@ -236,7 +236,7 @@ Java_org_ros2_rcljava_timer_WallTimerImpl_nativeCallTimer( rcl_ret_t ret = rcl_timer_call(timer); if (ret != RCL_RET_OK) { - std::string msg = "Failed to call timer: " + std::string(rcl_get_error_string_safe()); + std::string msg = "Failed to call timer: " + std::string(rcl_get_error_string().str); rcl_reset_error(); rcljava_throw_exception(env, "java/lang/IllegalStateException", msg); } diff --git a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java index 666ffcd7..ef58b7ae 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java +++ b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java @@ -25,6 +25,8 @@ import org.ros2.rcljava.client.Client; import org.ros2.rcljava.common.JNIUtils; +import org.ros2.rcljava.contexts.Context; +import org.ros2.rcljava.contexts.ContextImpl; import org.ros2.rcljava.executors.SingleThreadedExecutor; import org.ros2.rcljava.interfaces.MessageDefinition; import org.ros2.rcljava.interfaces.ServiceDefinition; @@ -60,6 +62,17 @@ private static SingleThreadedExecutor getGlobalExecutor() { */ private RCLJava() {} + /** + * The default context. + */ + private static Context defaultContext; + + /** + * All the @{link Context}s that have been created. + * Does not include the default context. + */ + private static Collection contexts; + /** * All the @{link Node}s that have been created. */ @@ -89,10 +102,21 @@ private static void cleanup() { node.dispose(); } + for (Context context : contexts) { + context.dispose(); + } } static { + try { + JNIUtils.loadImplementation(RCLJava.class); + } catch (UnsatisfiedLinkError ule) { + logger.error("Native code library failed to load.\n" + ule); + System.exit(1); + } + nodes = new LinkedBlockingQueue(); + contexts = new LinkedBlockingQueue(); // NOTE(esteve): disabling shutdown hook for now to avoid JVM crashes // Runtime.getRuntime().addShutdownHook(new Thread() { @@ -103,52 +127,57 @@ private static void cleanup() { } /** - * Flag to indicate if RCLJava has been fully initialized, with a valid RMW - * implementation. + * Get the default context. */ - private static boolean initialized = false; + public static synchronized Context getDefaultContext() { + if (RCLJava.defaultContext == null) { + long contextHandle = nativeCreateContextHandle(); + RCLJava.defaultContext = new ContextImpl(contextHandle); + } + return RCLJava.defaultContext; + } /** * @return true if RCLJava has been fully initialized, false otherwise. */ + @Deprecated public static boolean isInitialized() { - return RCLJava.initialized; + return RCLJava.ok(); } /** * Initialize the RCLJava API. If successful, a valid RMW implementation will * be loaded and accessible, enabling the creating of ROS2 entities * (@{link Node}s, @{link Publisher}s and @{link Subscription}s. + * This also initializes the default context. */ - public static void rclJavaInit() { - synchronized (RCLJava.class) { - if (!RCLJava.initialized) { - try { - JNIUtils.loadImplementation(RCLJava.class); - } catch (UnsatisfiedLinkError ule) { - logger.error("Native code library failed to load.\n" + ule); - System.exit(1); - } - RCLJava.nativeRCLJavaInit(); - logger.info("Using RMW implementation: {}", RCLJava.getRMWIdentifier()); - initialized = true; - } + public static synchronized void rclJavaInit() { + if (RCLJava.ok()) { + return; } + + // Initialize default context + getDefaultContext().init(); + + logger.info("Using RMW implementation: {}", RCLJava.getRMWIdentifier()); } /** - * Initialize the underlying rcl layer. + * Create a context (rcl_context_t) and return a pointer to it as an integer. + * + * @return A pointer to the underlying context structure. */ - private static native void nativeRCLJavaInit(); + private static native long nativeCreateContextHandle(); /** * Create a ROS2 node (rcl_node_t) and return a pointer to it as an integer. * * @param nodeName The name that will identify this node in a ROS2 graph. * @param namespace The namespace of the node. + * @param contextHandle Pointer to a context (rcl_context_t) with which to associated the node. * @return A pointer to the underlying ROS2 node structure. */ - private static native long nativeCreateNodeHandle(String nodeName, String namespace); + private static native long nativeCreateNodeHandle(String nodeName, String namespace, long contextHandle); /** * @return The identifier of the currently active RMW implementation via the @@ -175,7 +204,26 @@ public static String getRMWIdentifier() { * @return true if RCLJava hasn't been shut down, false otherwise. */ public static boolean ok() { - return nativeOk(); + return RCLJava.getDefaultContext().isValid(); + } + + /** + * @return true if RCLJava hasn't been shut down, false otherwise. + */ + public static boolean ok(final Context context) { + return context.isValid(); + } + + /** + * Create a @{link Context}. + * + * @return A @{link Context} that represents the underlying context structure. + */ + public static Context createContext() { + long contextHandle = nativeCreateContextHandle(); + Context context = new ContextImpl(contextHandle); + contexts.add(context); + return context; } /** @@ -186,7 +234,7 @@ public static boolean ok() { * structure. */ public static Node createNode(final String nodeName) { - return createNode(nodeName, ""); + return createNode(nodeName, "", RCLJava.getDefaultContext()); } /** @@ -197,9 +245,9 @@ public static Node createNode(final String nodeName) { * @return A @{link Node} that represents the underlying ROS2 node * structure. */ - public static Node createNode(final String nodeName, final String namespace) { - long nodeHandle = nativeCreateNodeHandle(nodeName, namespace); - Node node = new NodeImpl(nodeHandle, nodeName); + public static Node createNode(final String nodeName, final String namespace, final Context context) { + long nodeHandle = nativeCreateNodeHandle(nodeName, namespace, context.getHandle()); + Node node = new NodeImpl(nodeHandle, nodeName, context); nodes.add(node); return node; } @@ -255,11 +303,12 @@ public static void spinSome(final ComposableNode composableNode) { getGlobalExecutor().removeNode(composableNode); } - private static native void nativeShutdown(); - - public static void shutdown() { + public static synchronized void shutdown() { cleanup(); - nativeShutdown(); + if (RCLJava.defaultContext != null) { + RCLJava.defaultContext.dispose(); + RCLJava.defaultContext = null; + } } public static long convertQoSProfileToHandle(final QoSProfile qosProfile) { diff --git a/rcljava/src/main/java/org/ros2/rcljava/contexts/Context.java b/rcljava/src/main/java/org/ros2/rcljava/contexts/Context.java new file mode 100644 index 00000000..717eea77 --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/contexts/Context.java @@ -0,0 +1,49 @@ +/* Copyright 2019 Open Source Robotics Foundation, Inc. + * + * 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. + */ + +package org.ros2.rcljava.contexts; + +import org.ros2.rcljava.interfaces.Disposable; + +/** + * Encapsulates the non-global state of a ROS init/shutdown cycle. + * + * Generally, after a context is created it is initialized with @{link #init()} + * and before it is disposed @{link #shutdown()} is called. + * + * For more information on the lifecylce of a context, see the RCL documentation: + * http://docs.ros2.org/dashing/api/rcl/context_8h.html#ae051a6821ad89dc78910a557029c8ae2 + * + * This class serves as a bridge between a rcl_context_t and RCLJava. + * A Context must be created via @{link RCLJava#createContext()} + */ +public interface Context extends Disposable { + /** + * Initialize the context. + * // TODO(jacobperron): Pass arguments for parsing + * // TODO(jacobperron): Pass in InitOptions object + */ + void init(); + + /** + * Shutdown the context. + */ + void shutdown(); + + /** + * return true if the Context is valid, false otherwise. + */ + boolean isValid(); +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/contexts/ContextImpl.java b/rcljava/src/main/java/org/ros2/rcljava/contexts/ContextImpl.java new file mode 100644 index 00000000..3d6bf9f0 --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/contexts/ContextImpl.java @@ -0,0 +1,118 @@ +/* Copyright 2019 Open Source Robotics Foundation, Inc. + * + * 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. + */ + +package org.ros2.rcljava.contexts; + +import org.ros2.rcljava.common.JNIUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@inheritDoc} + */ +public class ContextImpl implements Context { + private static final Logger logger = LoggerFactory.getLogger(ContextImpl.class); + + static { + try { + JNIUtils.loadImplementation(ContextImpl.class); + } catch (UnsatisfiedLinkError ule) { + logger.error("Native code library failed to load.\n" + ule); + System.exit(1); + } + } + + /** + * An integer that represents a pointer to the underlying context structure (rcl_context_t). + */ + private long handle; + + /** + * Constructor. + * + * @param handle A pointer to the underlying context structure. + * Must not be zero. + */ + public ContextImpl(final long handle) { + this.handle = handle; + } + + /** + * Destroy a context (rcl_context_t). + * + * @param handle A pointer to the underlying context structure. + */ + private static native void nativeDispose(long handle); + + /** + * {@inheritDoc} + */ + public final void dispose() { + // Ensure the context is shutdown first + shutdown(); + nativeDispose(this.handle); + this.handle = 0; + } + + /** + * {@inheritDoc} + */ + public final long getHandle() { + return this.handle; + } + + /** + * Initialize a rcl_context_t. + * + * @param contextHandle The pointer to the context structure. + */ + private static native void nativeInit(long contextHandle); + + /** + * {@inheritDoc} + */ + public final void init() { + nativeInit(this.handle); + } + + /** + * Shutdown a rcl_context_t. + * + * @param contextHandle The pointer to the context structure. + */ + private static native void nativeShutdown(long contextHandle); + + /** + * {@inheritDoc} + */ + public final void shutdown() { + nativeShutdown(this.handle); + } + + /** + * Check if a context is valid (rcl_context_t). + * + * @param handle A pointer to the underlying context structure. + */ + private static native boolean nativeIsValid(long handle); + + /** + * {@inheritDoc} + */ + public final boolean isValid() { + return nativeIsValid(this.handle); + } +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/executors/BaseExecutor.java b/rcljava/src/main/java/org/ros2/rcljava/executors/BaseExecutor.java index 4d7beaac..536e417b 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/executors/BaseExecutor.java +++ b/rcljava/src/main/java/org/ros2/rcljava/executors/BaseExecutor.java @@ -204,15 +204,10 @@ protected void waitForWork(long timeout) { } long waitSetHandle = nativeGetZeroInitializedWaitSet(); - nativeWaitSetInit(waitSetHandle, subscriptionsSize, 0, timersSize, clientsSize, servicesSize); + long contextHandle = RCLJava.getDefaultContext().getHandle(); + nativeWaitSetInit(waitSetHandle, contextHandle, subscriptionsSize, 0, timersSize, clientsSize, servicesSize, 0); - nativeWaitSetClearSubscriptions(waitSetHandle); - - nativeWaitSetClearTimers(waitSetHandle); - - nativeWaitSetClearServices(waitSetHandle); - - nativeWaitSetClearClients(waitSetHandle); + nativeWaitSetClear(waitSetHandle); for (Map.Entry entry : this.subscriptionHandles) { nativeWaitSetAddSubscription(waitSetHandle, entry.getKey()); @@ -362,10 +357,12 @@ protected void spinOnce(long timeout) { private static native long nativeGetZeroInitializedWaitSet(); - private static native void nativeWaitSetInit(long waitSetHandle, int numberOfSubscriptions, - int numberOfGuardConditions, int numberOfTimers, int numberOfClients, int numberOfServices); + private static native void nativeWaitSetInit( + long waitSetHandle, long contextHandle, int numberOfSubscriptions, + int numberOfGuardConditions, int numberOfTimers, int numberOfClients, + int numberOfServices, int numberOfEvents); - private static native void nativeWaitSetClearSubscriptions(long waitSetHandle); + private static native void nativeWaitSetClear(long waitSetHandle); private static native void nativeWaitSetAddSubscription( long waitSetHandle, long subscriptionHandle); @@ -375,14 +372,8 @@ private static native void nativeWaitSetAddSubscription( private static native MessageDefinition nativeTake( long subscriptionHandle, Class messageType); - private static native void nativeWaitSetClearTimers(long waitSetHandle); - - private static native void nativeWaitSetClearServices(long waitSetHandle); - private static native void nativeWaitSetAddService(long waitSetHandle, long serviceHandle); - private static native void nativeWaitSetClearClients(long waitSetHandle); - private static native void nativeWaitSetAddClient(long waitSetHandle, long clientHandle); private static native void nativeWaitSetAddTimer(long waitSetHandle, long timerHandle); diff --git a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java index 96797b97..2235fda0 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java @@ -22,6 +22,7 @@ import org.ros2.rcljava.concurrent.Callback; import org.ros2.rcljava.consumers.Consumer; import org.ros2.rcljava.consumers.TriConsumer; +import org.ros2.rcljava.contexts.Context; import org.ros2.rcljava.qos.QoSProfile; import org.ros2.rcljava.interfaces.MessageDefinition; import org.ros2.rcljava.interfaces.ServiceDefinition; @@ -34,6 +35,8 @@ import org.ros2.rcljava.service.ServiceImpl; import org.ros2.rcljava.subscription.Subscription; import org.ros2.rcljava.subscription.SubscriptionImpl; +import org.ros2.rcljava.time.Clock; +import org.ros2.rcljava.time.ClockType; import org.ros2.rcljava.timer.Timer; import org.ros2.rcljava.timer.WallTimer; import org.ros2.rcljava.timer.WallTimerImpl; @@ -75,6 +78,16 @@ public class NodeImpl implements Node { */ private long handle; + /** + * The context that this node is associated with. + */ + private Context context; + + /** + * The clock used by this node. + */ + private Clock clock; + /** * All the @{link Subscription}s that have been created through this instance. */ @@ -112,9 +125,11 @@ public class NodeImpl implements Node { * @param handle A pointer to the underlying ROS2 node structure. Must not * be zero. */ - public NodeImpl(final long handle, final String name) { + public NodeImpl(final long handle, final String name, final Context context) { + this.clock = new Clock(ClockType.SYSTEM_TIME); this.handle = handle; this.name = name; + this.context = context; this.publishers = new LinkedBlockingQueue(); this.subscriptions = new LinkedBlockingQueue(); this.services = new LinkedBlockingQueue(); @@ -315,12 +330,12 @@ public final long getHandle() { return this.handle; } - private static native long nativeCreateTimerHandle(long timerPeriod); + private static native long nativeCreateTimerHandle(long clockHandle, long contextHandle, long timerPeriod); public WallTimer createWallTimer( final long period, final TimeUnit unit, final Callback callback) { long timerPeriodNS = TimeUnit.NANOSECONDS.convert(period, unit); - long timerHandle = nativeCreateTimerHandle(timerPeriodNS); + long timerHandle = nativeCreateTimerHandle(clock.getHandle(), context.getHandle(), timerPeriodNS); WallTimer timer = new WallTimerImpl(new WeakReference(this), timerHandle, callback, timerPeriodNS); this.timers.add(timer); diff --git a/rcljava/src/main/java/org/ros2/rcljava/time/Clock.java b/rcljava/src/main/java/org/ros2/rcljava/time/Clock.java new file mode 100644 index 00000000..69936c33 --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/time/Clock.java @@ -0,0 +1,93 @@ +/* Copyright 2019 Open Source Robotics Foundation, Inc. + * + * 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. + */ + +package org.ros2.rcljava.time; + +import org.ros2.rcljava.common.JNIUtils; + +import org.ros2.rcljava.interfaces.Disposable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Clock implements Disposable { + private static final Logger logger = LoggerFactory.getLogger(Clock.class); + + static { + try { + JNIUtils.loadImplementation(Clock.class); + } catch (UnsatisfiedLinkError ule) { + logger.error("Native code library failed to load.\n" + ule); + System.exit(1); + } + } + + /** + * The underlying clock handle (rcl_clock_t). + */ + private long handle; + + /** + * The clock type. + */ + private final ClockType clockType; + + /** + * Create an RCL clock (rcl_clock_t). + * + * @param clockType The RCL clock type. + * @return A pointer to the underlying clock structure as an integer. + */ + private static native long nativeCreateClockHandle(ClockType clockType); + + /** + * Constructor. + * + * @param clockType The type of clock to create. + */ + public Clock(ClockType clockType) { + this.clockType = clockType; + this.handle = Clock.nativeCreateClockHandle(clockType); + } + + /** + * @return The clock type. + */ + public ClockType getClockType() { + return clockType; + } + + /** + * Destroy an RCL clock (rcl_clock_t). + * + * @param handle A pointer to the underlying clock structure. + */ + private static native void nativeDispose(long handle); + + /** + * {@inheritDoc} + */ + public final void dispose() { + Clock.nativeDispose(this.handle); + this.handle = 0; + } + + /** + * {@inheritDoc} + */ + public final long getHandle() { + return this.handle; + } +} diff --git a/rcljava/src/test/java/org/ros2/rcljava/RCLJavaTest.java b/rcljava/src/test/java/org/ros2/rcljava/RCLJavaTest.java index 349e95ee..dba322cf 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/RCLJavaTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/RCLJavaTest.java @@ -15,24 +15,18 @@ package org.ros2.rcljava; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import org.junit.Test; public class RCLJavaTest { @Test - public final void testInit() { - assertEquals(false, RCLJava.isInitialized()); + public final void testInitShutdown() { + assertFalse(RCLJava.ok()); RCLJava.rclJavaInit(); - assertEquals(true, RCLJava.isInitialized()); - } - - @Test - public final void testOk() { - RCLJava.rclJavaInit(); - assertEquals(true, RCLJava.ok()); + assertTrue(RCLJava.ok()); RCLJava.shutdown(); - assertEquals(false, RCLJava.ok()); + assertFalse(RCLJava.ok()); } } diff --git a/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java b/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java index 7c6a2010..8dd5aa30 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java @@ -47,7 +47,7 @@ public class NodeTest { private boolean boolValue1, boolValue2; private byte byteValue1, byteValue2; - private char charValue1, charValue2; + private byte charValue1, charValue2; private float float32Value1, float32Value2; private double float64Value1, float64Value2; private byte int8Value1, int8Value2; @@ -61,7 +61,7 @@ public class NodeTest { private String stringValue1, stringValue2; boolean checkPrimitives(rcljava.msg.Primitives primitives, boolean booleanValue, byte byteValue, - char charValue, float float32Value, double float64Value, byte int8Value, byte uint8Value, + byte charValue, float float32Value, double float64Value, byte int8Value, byte uint8Value, short int16Value, short uint16Value, int int32Value, int uint32Value, long int64Value, long uint64Value, String stringValue) { boolean result = true; @@ -112,7 +112,7 @@ public void setUp() { boolValue1 = true; byteValue1 = (byte) 123; - charValue1 = '\u0012'; + charValue1 = (byte) '\u0012'; float32Value1 = 12.34f; float64Value1 = 43.21; int8Value1 = (byte) -12; @@ -142,7 +142,7 @@ public void setUp() { boolValue2 = false; byteValue2 = (byte) 42; - charValue2 = '\u0021'; + charValue2 = (byte) '\u0021'; float32Value2 = 13.34f; float64Value2 = 44.21; int8Value2 = (byte) -13; @@ -275,7 +275,7 @@ public final void testPubSubBoundedArrayPrimitives() throws Exception { List boolValues = Arrays.asList(new Boolean[] {true, false, true}); List byteValues = Arrays.asList(new Byte[] {123, 42}); - List charValues = Arrays.asList(new Character[] {'\u0012', '\u0021'}); + List charValues = Arrays.asList(new Byte[] {'\u0012', '\u0021'}); List float32Values = Arrays.asList(new Float[] {12.34f, 13.34f}); List float64Values = Arrays.asList(new Double[] {43.21, 44.21}); List int8Values = Arrays.asList(new Byte[] {-12, -13}); @@ -437,7 +437,7 @@ public final void testPubSubDynamicArrayPrimitives() throws Exception { List boolValues = Arrays.asList(new Boolean[] {true, false, true}); List byteValues = Arrays.asList(new Byte[] {123, 42}); - List charValues = Arrays.asList(new Character[] {'\u0012', '\u0021'}); + List charValues = Arrays.asList(new Byte[] {'\u0012', '\u0021'}); List float32Values = Arrays.asList(new Float[] {12.34f, 13.34f}); List float64Values = Arrays.asList(new Double[] {43.21, 44.21}); List int8Values = Arrays.asList(new Byte[] {-12, -13}); @@ -703,7 +703,7 @@ public final void testPubSubStaticArrayPrimitives() throws Exception { List boolValues = Arrays.asList(new Boolean[] {true, false, true}); List byteValues = Arrays.asList(new Byte[] {123, 42, 24}); - List charValues = Arrays.asList(new Character[] {'\u0012', '\u0021', '\u0008'}); + List charValues = Arrays.asList(new Byte[] {'\u0012', '\u0021', '\u0008'}); List float32Values = Arrays.asList(new Float[] {12.34f, 13.34f, 14.34f}); List float64Values = Arrays.asList(new Double[] {43.21, 54.21, 65.21}); List int8Values = Arrays.asList(new Byte[] {-12, -13, -14}); From ef2805b1c2ad229b199cc6059e859306658f6ac8 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Tue, 11 Feb 2020 07:50:16 -0800 Subject: [PATCH 05/33] Update rosidl_generator_java tests (#68) * Update rosidl_generator_java tests Now using messages from test_interface_files. Signed-off-by: Jacob Perron * Remove old interface files Signed-off-by: Jacob Perron --- rosidl_generator_java/CMakeLists.txt | 24 +- rosidl_generator_java/msg/Bool.msg | 1 - rosidl_generator_java/msg/Byte.msg | 1 - rosidl_generator_java/msg/Char.msg | 1 - rosidl_generator_java/msg/Constants.msg | 5 - rosidl_generator_java/msg/Empty.msg | 1 - rosidl_generator_java/msg/Float32.msg | 1 - rosidl_generator_java/msg/Float64.msg | 1 - rosidl_generator_java/msg/Int16.msg | 1 - rosidl_generator_java/msg/Int32.msg | 1 - rosidl_generator_java/msg/Int64.msg | 1 - rosidl_generator_java/msg/Int8.msg | 1 - rosidl_generator_java/msg/Nested.msg | 6 - rosidl_generator_java/msg/Primitives.msg | 18 - rosidl_generator_java/msg/Strings.msg | 4 - rosidl_generator_java/msg/Uint16.msg | 1 - rosidl_generator_java/msg/Uint32.msg | 1 - rosidl_generator_java/msg/Uint64.msg | 1 - rosidl_generator_java/msg/Uint8.msg | 1 - rosidl_generator_java/msg/Various.msg | 23 - .../org/ros2/generator/InterfacesTest.java | 712 +++++++++++------- 21 files changed, 460 insertions(+), 346 deletions(-) delete mode 100644 rosidl_generator_java/msg/Bool.msg delete mode 100644 rosidl_generator_java/msg/Byte.msg delete mode 100644 rosidl_generator_java/msg/Char.msg delete mode 100644 rosidl_generator_java/msg/Constants.msg delete mode 100644 rosidl_generator_java/msg/Empty.msg delete mode 100644 rosidl_generator_java/msg/Float32.msg delete mode 100644 rosidl_generator_java/msg/Float64.msg delete mode 100644 rosidl_generator_java/msg/Int16.msg delete mode 100644 rosidl_generator_java/msg/Int32.msg delete mode 100644 rosidl_generator_java/msg/Int64.msg delete mode 100644 rosidl_generator_java/msg/Int8.msg delete mode 100644 rosidl_generator_java/msg/Nested.msg delete mode 100644 rosidl_generator_java/msg/Primitives.msg delete mode 100644 rosidl_generator_java/msg/Strings.msg delete mode 100644 rosidl_generator_java/msg/Uint16.msg delete mode 100644 rosidl_generator_java/msg/Uint32.msg delete mode 100644 rosidl_generator_java/msg/Uint64.msg delete mode 100644 rosidl_generator_java/msg/Uint8.msg delete mode 100644 rosidl_generator_java/msg/Various.msg diff --git a/rosidl_generator_java/CMakeLists.txt b/rosidl_generator_java/CMakeLists.txt index 13b0c00d..4b1c2ea5 100644 --- a/rosidl_generator_java/CMakeLists.txt +++ b/rosidl_generator_java/CMakeLists.txt @@ -34,29 +34,6 @@ if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() - # TODO(jacobperron): Use test_interface_files instead and update tests - set(message_files - "msg/Bool.msg" - "msg/Byte.msg" - "msg/Char.msg" - "msg/Constants.msg" - "msg/Empty.msg" - "msg/Float32.msg" - "msg/Float64.msg" - "msg/Int16.msg" - "msg/Int32.msg" - "msg/Int64.msg" - "msg/Int8.msg" - "msg/Nested.msg" - "msg/Primitives.msg" - "msg/Strings.msg" - "msg/Uint16.msg" - "msg/Uint32.msg" - "msg/Uint64.msg" - "msg/Uint8.msg" - "msg/Various.msg" - ) - include(cmake/register_java.cmake) include(cmake/rosidl_generator_java_get_typesupports.cmake) @@ -70,6 +47,7 @@ if(BUILD_TESTING) rosidl_generate_interfaces(${PROJECT_NAME} ${message_files} + ${test_interface_files_MSG_FILES} ${test_interface_files_SRV_FILES} SKIP_INSTALL ) diff --git a/rosidl_generator_java/msg/Bool.msg b/rosidl_generator_java/msg/Bool.msg deleted file mode 100644 index b1578abb..00000000 --- a/rosidl_generator_java/msg/Bool.msg +++ /dev/null @@ -1 +0,0 @@ -bool empty_bool diff --git a/rosidl_generator_java/msg/Byte.msg b/rosidl_generator_java/msg/Byte.msg deleted file mode 100644 index 2e6fa040..00000000 --- a/rosidl_generator_java/msg/Byte.msg +++ /dev/null @@ -1 +0,0 @@ -byte empty_byte diff --git a/rosidl_generator_java/msg/Char.msg b/rosidl_generator_java/msg/Char.msg deleted file mode 100644 index 0f2e0c90..00000000 --- a/rosidl_generator_java/msg/Char.msg +++ /dev/null @@ -1 +0,0 @@ -char empty_char diff --git a/rosidl_generator_java/msg/Constants.msg b/rosidl_generator_java/msg/Constants.msg deleted file mode 100644 index d9e404e8..00000000 --- a/rosidl_generator_java/msg/Constants.msg +++ /dev/null @@ -1,5 +0,0 @@ -int32 X=123 -int32 Y=-123 -string FOO=foo -char TOTO=127 -byte TATA=48 diff --git a/rosidl_generator_java/msg/Empty.msg b/rosidl_generator_java/msg/Empty.msg deleted file mode 100644 index 8b137891..00000000 --- a/rosidl_generator_java/msg/Empty.msg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/rosidl_generator_java/msg/Float32.msg b/rosidl_generator_java/msg/Float32.msg deleted file mode 100644 index a8bb0cb6..00000000 --- a/rosidl_generator_java/msg/Float32.msg +++ /dev/null @@ -1 +0,0 @@ -float32 empty_float32 diff --git a/rosidl_generator_java/msg/Float64.msg b/rosidl_generator_java/msg/Float64.msg deleted file mode 100644 index d6efd991..00000000 --- a/rosidl_generator_java/msg/Float64.msg +++ /dev/null @@ -1 +0,0 @@ -float64 empty_float64 diff --git a/rosidl_generator_java/msg/Int16.msg b/rosidl_generator_java/msg/Int16.msg deleted file mode 100644 index 14426ca1..00000000 --- a/rosidl_generator_java/msg/Int16.msg +++ /dev/null @@ -1 +0,0 @@ -int16 empty_int16 diff --git a/rosidl_generator_java/msg/Int32.msg b/rosidl_generator_java/msg/Int32.msg deleted file mode 100644 index 51c88f0e..00000000 --- a/rosidl_generator_java/msg/Int32.msg +++ /dev/null @@ -1 +0,0 @@ -int32 empty_int32 diff --git a/rosidl_generator_java/msg/Int64.msg b/rosidl_generator_java/msg/Int64.msg deleted file mode 100644 index 75a1c238..00000000 --- a/rosidl_generator_java/msg/Int64.msg +++ /dev/null @@ -1 +0,0 @@ -int64 empty_int64 diff --git a/rosidl_generator_java/msg/Int8.msg b/rosidl_generator_java/msg/Int8.msg deleted file mode 100644 index 76167eb9..00000000 --- a/rosidl_generator_java/msg/Int8.msg +++ /dev/null @@ -1 +0,0 @@ -int8 empty_int8 diff --git a/rosidl_generator_java/msg/Nested.msg b/rosidl_generator_java/msg/Nested.msg deleted file mode 100644 index a7386b58..00000000 --- a/rosidl_generator_java/msg/Nested.msg +++ /dev/null @@ -1,6 +0,0 @@ -uint8 ANSWER=42 - -Primitives primitives -Primitives[2] two_primitives -Primitives[<=3] up_to_three_primitives -Primitives[] unbounded_primitives diff --git a/rosidl_generator_java/msg/Primitives.msg b/rosidl_generator_java/msg/Primitives.msg deleted file mode 100644 index ec13f6ad..00000000 --- a/rosidl_generator_java/msg/Primitives.msg +++ /dev/null @@ -1,18 +0,0 @@ -bool bool_value true -byte byte_value -char char_value -float32 float32_value 1.125 -float64 float64_value -int8 int8_value -5 -uint8 uint8_value 23 -int16 int16_value -uint16 uint16_value -int32 int32_value -uint32 uint32_value -int64 int64_value -uint64 uint64_value -string string_value -string string_value_with_default 'default' -#string<=5[3] fixed_length_string_value -#string<=5[<=10] upper_bound_string_value -string unbound_string_value diff --git a/rosidl_generator_java/msg/Strings.msg b/rosidl_generator_java/msg/Strings.msg deleted file mode 100644 index f81e029b..00000000 --- a/rosidl_generator_java/msg/Strings.msg +++ /dev/null @@ -1,4 +0,0 @@ -string empty_string -string def_string "Hello world!" -string<=22 ub_string -string<=22 ub_def_string "Upper bounded string." diff --git a/rosidl_generator_java/msg/Uint16.msg b/rosidl_generator_java/msg/Uint16.msg deleted file mode 100644 index 9dd741c4..00000000 --- a/rosidl_generator_java/msg/Uint16.msg +++ /dev/null @@ -1 +0,0 @@ -uint16 empty_uint16 diff --git a/rosidl_generator_java/msg/Uint32.msg b/rosidl_generator_java/msg/Uint32.msg deleted file mode 100644 index cb65d083..00000000 --- a/rosidl_generator_java/msg/Uint32.msg +++ /dev/null @@ -1 +0,0 @@ -uint32 empty_uint32 diff --git a/rosidl_generator_java/msg/Uint64.msg b/rosidl_generator_java/msg/Uint64.msg deleted file mode 100644 index 9e7c0f80..00000000 --- a/rosidl_generator_java/msg/Uint64.msg +++ /dev/null @@ -1 +0,0 @@ -uint64 empty_uint64 diff --git a/rosidl_generator_java/msg/Uint8.msg b/rosidl_generator_java/msg/Uint8.msg deleted file mode 100644 index 6b0876e0..00000000 --- a/rosidl_generator_java/msg/Uint8.msg +++ /dev/null @@ -1 +0,0 @@ -uint8 empty_uint8 diff --git a/rosidl_generator_java/msg/Various.msg b/rosidl_generator_java/msg/Various.msg deleted file mode 100644 index efe284ae..00000000 --- a/rosidl_generator_java/msg/Various.msg +++ /dev/null @@ -1,23 +0,0 @@ -bool bool_value false -byte byte_value 1 -char char_value 1 -float32 float32_value 1.23 -float64 float64_value -int8 int8_value -5 -uint16[2] two_uint16_value [5, 23] -int32[<=3] up_to_three_int32_values -int32[<=3] up_to_three_int32_values_with_default_values [5, 23] -uint64[] unbounded_uint64_values - -#string[2] two_string_value ['foo', 'bar'] -string[<=3] up_to_three_string_values -string[] unbounded_string_values - -Empty empty -Empty[2] two_empty -Empty[] unbounded_empty - -Nested nested -Nested[2] two_nested -Nested[<=3] up_to_three_nested -Nested[] unbounded_nested diff --git a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java index d1b0fada..dfea4a97 100644 --- a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java +++ b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java @@ -17,8 +17,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import java.util.concurrent.Callable; import java.util.Arrays; import java.util.List; import org.junit.BeforeClass; @@ -34,61 +35,18 @@ public static void setupOnce() { @Rule public ExpectedException thrown = ExpectedException.none(); - @Test - public final void testBool() { - boolean expected1 = false; - rosidl_generator_java.msg.Bool boolOne = new rosidl_generator_java.msg.Bool(); - boolOne.setEmptyBool(expected1); - assertEquals(expected1, boolOne.getEmptyBool()); - - boolean expected2 = true; - rosidl_generator_java.msg.Bool boolTwo = new rosidl_generator_java.msg.Bool(); - boolTwo.setEmptyBool(expected2); - assertEquals(expected2, boolTwo.getEmptyBool()); - - boolOne.setEmptyBool(expected2); - assertEquals(expected2, boolOne.getEmptyBool()); - } - - @Test - public final void testByte() { - byte expected1 = 123; - rosidl_generator_java.msg.Byte byteOne = new rosidl_generator_java.msg.Byte(); - byteOne.setEmptyByte(expected1); - assertEquals(expected1, byteOne.getEmptyByte()); - - byte expected2 = -42; - rosidl_generator_java.msg.Byte byteTwo = new rosidl_generator_java.msg.Byte(); - byteTwo.setEmptyByte(expected2); - assertEquals(expected2, byteTwo.getEmptyByte()); - - byteOne.setEmptyByte(expected2); - assertEquals(expected2, byteOne.getEmptyByte()); - } - - @Test - public final void testChar() { - byte expected1 = 'a'; - rosidl_generator_java.msg.Char charOne = new rosidl_generator_java.msg.Char(); - charOne.setEmptyChar(expected1); - assertEquals(expected1, charOne.getEmptyChar()); - - byte expected2 = 'b'; - rosidl_generator_java.msg.Char charTwo = new rosidl_generator_java.msg.Char(); - charTwo.setEmptyChar(expected2); - assertEquals(expected2, charTwo.getEmptyChar()); - - charOne.setEmptyChar(expected2); - assertEquals(expected2, charOne.getEmptyChar()); - } - - @Test - public final void testConstants() { - assertEquals(123, rosidl_generator_java.msg.Constants.X); - assertEquals(-123, rosidl_generator_java.msg.Constants.Y); - assertEquals("foo", rosidl_generator_java.msg.Constants.FOO); - assertEquals('\u007f', rosidl_generator_java.msg.Constants.TOTO); - assertEquals(48, rosidl_generator_java.msg.Constants.TATA); + // TODO(jacobperron): Replace with JUnit's assertThrows method when we switch to JUnit 5 + // See: https://junit.org/junit5/docs/5.0.1/api/org/junit/jupiter/api/Assertions.html + private static void assertThrows(Class expectedException, Callable func) { + try { + func.call(); + } + catch(Exception exception) { + if (expectedException.isInstance(exception)) { + return; + } + } + assertTrue("Callable did not throw the expected exception", false); } @Test @@ -98,232 +56,480 @@ public final void testEmpty() { } @Test - public final void testFloat32() { - float expected1 = 12.34f; - rosidl_generator_java.msg.Float32 float32One = new rosidl_generator_java.msg.Float32(); - float32One.setEmptyFloat32(expected1); - assertEquals(expected1, float32One.getEmptyFloat32(), 0.01); - - float expected2 = -43.21f; - rosidl_generator_java.msg.Float32 float32Two = new rosidl_generator_java.msg.Float32(); - float32Two.setEmptyFloat32(expected2); - assertEquals(expected2, float32Two.getEmptyFloat32(), 0.01); - - float32One.setEmptyFloat32(expected2); - assertEquals(expected2, float32One.getEmptyFloat32(), 0.01); - } - - @Test - public final void testFloat64() { - double expected1 = 12.34; - rosidl_generator_java.msg.Float64 float64One = new rosidl_generator_java.msg.Float64(); - float64One.setEmptyFloat64(expected1); - assertEquals(expected1, float64One.getEmptyFloat64(), 0.01); - - double expected2 = -43.21; - rosidl_generator_java.msg.Float64 float64Two = new rosidl_generator_java.msg.Float64(); - float64Two.setEmptyFloat64(expected2); - assertEquals(expected2, float64Two.getEmptyFloat64(), 0.01); - - float64One.setEmptyFloat64(expected2); - assertEquals(expected2, float64One.getEmptyFloat64(), 0.01); - } - - @Test - public final void testInt8() { - byte expected1 = 123; - rosidl_generator_java.msg.Int8 byteOne = new rosidl_generator_java.msg.Int8(); - byteOne.setEmptyInt8(expected1); - assertEquals(expected1, byteOne.getEmptyInt8()); - - byte expected2 = -42; - rosidl_generator_java.msg.Int8 byteTwo = new rosidl_generator_java.msg.Int8(); - byteTwo.setEmptyInt8(expected2); - assertEquals(expected2, byteTwo.getEmptyInt8()); - - byteOne.setEmptyInt8(expected2); - assertEquals(expected2, byteOne.getEmptyInt8()); - } - - @Test - public final void testInt16() { - short expected1 = 1230; - rosidl_generator_java.msg.Int16 shortOne = new rosidl_generator_java.msg.Int16(); - shortOne.setEmptyInt16(expected1); - assertEquals(expected1, shortOne.getEmptyInt16()); - - short expected2 = -420; - rosidl_generator_java.msg.Int16 shortTwo = new rosidl_generator_java.msg.Int16(); - shortTwo.setEmptyInt16(expected2); - assertEquals(expected2, shortTwo.getEmptyInt16()); - - shortOne.setEmptyInt16(expected2); - assertEquals(expected2, shortOne.getEmptyInt16()); + public final void testBasicTypes() { + // Test setting/getting positive values + rosidl_generator_java.msg.BasicTypes basicTypesOne = new rosidl_generator_java.msg.BasicTypes(); + boolean expectedBool1 = true; + basicTypesOne.setBoolValue(expectedBool1); + byte expectedByte1 = 123; + basicTypesOne.setByteValue(expectedByte1); + byte expectedChar1 = 'a'; + basicTypesOne.setCharValue(expectedChar1); + float expectedFloat1 = 12.34f; + basicTypesOne.setFloat32Value(expectedFloat1); + double expectedDouble1 = 12.34; + basicTypesOne.setFloat64Value(expectedDouble1); + byte expectedInt81 = 123; + basicTypesOne.setInt8Value(expectedInt81); + short expectedInt161 = 1230; + basicTypesOne.setInt16Value(expectedInt161); + int expectedInt321 = 123000; + basicTypesOne.setInt32Value(expectedInt321); + long expectedInt641 = 42949672960L; + basicTypesOne.setInt64Value(expectedInt641); + + assertEquals(expectedBool1, basicTypesOne.getBoolValue()); + assertEquals(expectedByte1, basicTypesOne.getByteValue()); + assertEquals(expectedChar1, basicTypesOne.getCharValue()); + assertEquals(expectedFloat1, basicTypesOne.getFloat32Value(), 0.01f); + assertEquals(expectedDouble1, basicTypesOne.getFloat64Value(), 0.01); + assertEquals(expectedInt81, basicTypesOne.getInt8Value()); + assertEquals(expectedInt161, basicTypesOne.getInt16Value()); + assertEquals(expectedInt321, basicTypesOne.getInt32Value()); + assertEquals(expectedInt641, basicTypesOne.getInt64Value()); + + // Test setting/getting negative values + rosidl_generator_java.msg.BasicTypes basicTypesTwo = new rosidl_generator_java.msg.BasicTypes(); + boolean expectedBool2 = false; + basicTypesTwo.setBoolValue(expectedBool2); + byte expectedByte2 = -42; + basicTypesTwo.setByteValue(expectedByte2); + byte expectedChar2 = ' '; + basicTypesTwo.setCharValue(expectedChar2); + float expectedFloat2 = -43.21f; + basicTypesTwo.setFloat32Value(expectedFloat2); + double expectedDouble2 = -43.21; + basicTypesTwo.setFloat64Value(expectedDouble2); + byte expectedInt82 = -42; + basicTypesTwo.setInt8Value(expectedInt82); + short expectedInt162 = -420; + basicTypesTwo.setInt16Value(expectedInt162); + int expectedInt322 = -42000; + basicTypesTwo.setInt32Value(expectedInt322); + long expectedInt642 = -4200000L; + basicTypesTwo.setInt64Value(expectedInt642); + + assertEquals(expectedBool2, basicTypesTwo.getBoolValue()); + assertEquals(expectedByte2, basicTypesTwo.getByteValue()); + assertEquals(expectedChar2, basicTypesTwo.getCharValue()); + assertEquals(expectedFloat2, basicTypesTwo.getFloat32Value(), 0.01f); + assertEquals(expectedDouble2, basicTypesTwo.getFloat64Value(), 0.01); + assertEquals(expectedInt82, basicTypesTwo.getInt8Value()); + assertEquals(expectedInt162, basicTypesTwo.getInt16Value()); + assertEquals(expectedInt322, basicTypesTwo.getInt32Value()); + assertEquals(expectedInt642, basicTypesTwo.getInt64Value()); } @Test - public final void testInt32() { - int expected1 = 123000; - rosidl_generator_java.msg.Int32 intOne = new rosidl_generator_java.msg.Int32(); - intOne.setEmptyInt32(expected1); - assertEquals(expected1, intOne.getEmptyInt32()); - - int expected2 = -42000; - rosidl_generator_java.msg.Int32 intTwo = new rosidl_generator_java.msg.Int32(); - intTwo.setEmptyInt32(expected2); - assertEquals(expected2, intTwo.getEmptyInt32()); - - intOne.setEmptyInt32(expected2); - assertEquals(expected2, intOne.getEmptyInt32()); - } - - @Test - public final void testInt64() { - long expected1 = 42949672960L; - rosidl_generator_java.msg.Int64 longOne = new rosidl_generator_java.msg.Int64(); - longOne.setEmptyInt64(expected1); - assertEquals(expected1, longOne.getEmptyInt64()); - - long expected2 = -4200000L; - rosidl_generator_java.msg.Int64 longTwo = new rosidl_generator_java.msg.Int64(); - longTwo.setEmptyInt64(expected2); - assertEquals(expected2, longTwo.getEmptyInt64()); - - longOne.setEmptyInt64(expected2); - assertEquals(expected2, longOne.getEmptyInt64()); + public final void testConstants() { + assertEquals(true, rosidl_generator_java.msg.Constants.BOOL_CONST); + assertEquals(50, rosidl_generator_java.msg.Constants.BYTE_CONST); + assertEquals(100, rosidl_generator_java.msg.Constants.CHAR_CONST); + assertEquals(1.125f, rosidl_generator_java.msg.Constants.FLOAT32_CONST, 0.01f); + assertEquals(1.125, rosidl_generator_java.msg.Constants.FLOAT64_CONST, 0.01); + assertEquals(-50, rosidl_generator_java.msg.Constants.INT8_CONST); + assertEquals((byte) 200, rosidl_generator_java.msg.Constants.UINT8_CONST); + assertEquals(-1000, rosidl_generator_java.msg.Constants.INT16_CONST); + assertEquals(2000, rosidl_generator_java.msg.Constants.UINT16_CONST); + assertEquals(-30000, rosidl_generator_java.msg.Constants.INT32_CONST); + assertEquals(60000, rosidl_generator_java.msg.Constants.UINT32_CONST); + assertEquals(-40000000, rosidl_generator_java.msg.Constants.INT64_CONST); + assertEquals(50000000, rosidl_generator_java.msg.Constants.UINT64_CONST); + + assertEquals("Hello world!", rosidl_generator_java.msg.Strings.STRING_CONST); } @Test public final void testDefaultValues() { - rosidl_generator_java.msg.Strings a = new rosidl_generator_java.msg.Strings(); - - assertEquals("", a.getEmptyString()); - assertEquals("Hello world!", a.getDefString()); - a.setDefString("Bye world"); - assertEquals("Bye world", a.getDefString()); - - rosidl_generator_java.msg.Various b = new rosidl_generator_java.msg.Various(); - assertEquals(Arrays.asList(new Short[] {5, 23}), b.getTwoUint16Value()); - assertEquals( - Arrays.asList(new Integer[] {5, 23}), b.getUpToThreeInt32ValuesWithDefaultValues()); - - assertEquals('\u0001', b.getCharValue()); - assertNotEquals('1', b.getCharValue()); - assertEquals((byte) '\u0001', b.getByteValue()); - assertNotEquals((byte) '1', b.getByteValue()); + rosidl_generator_java.msg.Defaults defaults = new rosidl_generator_java.msg.Defaults(); + assertEquals(true, defaults.getBoolValue()); + assertEquals(50, defaults.getByteValue()); + assertEquals(100, defaults.getCharValue()); + assertEquals(1.125f, defaults.getFloat32Value(), 0.01f); + assertEquals(1.125, defaults.getFloat64Value(), 0.01); + assertEquals(-50, defaults.getInt8Value()); + assertEquals((byte) 200, defaults.getUint8Value()); + assertEquals(-1000, defaults.getInt16Value()); + assertEquals(2000, defaults.getUint16Value()); + assertEquals(-30000, defaults.getInt32Value()); + assertEquals(60000, defaults.getUint32Value()); + assertEquals(-40000000, defaults.getInt64Value()); + assertEquals(50000000, defaults.getUint64Value()); + + rosidl_generator_java.msg.Strings strings = new rosidl_generator_java.msg.Strings(); + assertEquals("Hello world!", strings.getStringValueDefault1()); + assertEquals("Hello'world!", strings.getStringValueDefault2()); + assertEquals("Hello\"world!", strings.getStringValueDefault3()); + assertEquals("Hello'world!", strings.getStringValueDefault4()); + assertEquals("Hello\"world!", strings.getStringValueDefault5()); + assertEquals("Hello world!", strings.getBoundedStringValueDefault1()); + assertEquals("Hello'world!", strings.getBoundedStringValueDefault2()); + assertEquals("Hello\"world!", strings.getBoundedStringValueDefault3()); + assertEquals("Hello'world!", strings.getBoundedStringValueDefault4()); + assertEquals("Hello\"world!", strings.getBoundedStringValueDefault5()); } @Test public final void testCheckStringConstraints() { - rosidl_generator_java.msg.Strings a = new rosidl_generator_java.msg.Strings(); - a.setEmptyString("test"); - assertEquals("test", a.getEmptyString()); + rosidl_generator_java.msg.Strings strings = new rosidl_generator_java.msg.Strings(); + strings.setStringValue("test"); + assertEquals("test", strings.getStringValue()); char[] chars22 = new char[22]; Arrays.fill(chars22, 'a'); String chars22String = new String(chars22); - a.setUbString(chars22String); - assertEquals(chars22String, a.getUbString()); + strings.setBoundedStringValue(chars22String); + assertEquals(chars22String, strings.getBoundedStringValue()); char[] chars23 = new char[23]; Arrays.fill(chars23, 'a'); String chars23String = new String(chars23); thrown.expect(IllegalArgumentException.class); - a.setUbString(chars23String); + strings.setBoundedStringValue(chars23String); } @Test - public final void testCheckFixedArrayConstraints() { - rosidl_generator_java.msg.Nested b = new rosidl_generator_java.msg.Nested(); - rosidl_generator_java.msg.Primitives primitives = new rosidl_generator_java.msg.Primitives(); - b.setPrimitives(primitives); - assertEquals(primitives, b.getPrimitives()); - - List listOfPrimitives = - Arrays.asList(new rosidl_generator_java.msg.Primitives[] {primitives, primitives}); - b.setTwoPrimitives(listOfPrimitives); - assertEquals(listOfPrimitives, b.getTwoPrimitives()); - - thrown.expect(IllegalArgumentException.class); - b.setTwoPrimitives(Arrays.asList(new rosidl_generator_java.msg.Primitives[] {primitives})); + public final void testArrays() { + rosidl_generator_java.msg.Arrays arrays = new rosidl_generator_java.msg.Arrays(); + + // This value should not change and is asserted at end of test + arrays.setAlignmentCheck(42); + + // Test setting/getting fixed length arrays of primitive types + List boolList = Arrays.asList(true, false, true); + arrays.setBoolValues(boolList); + assertEquals(boolList, arrays.getBoolValues()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setBoolValues(Arrays.asList(true, false, true, false))); + List byteList = Arrays.asList((byte) 0, (byte) 1, (byte) 255); + arrays.setByteValues(byteList); + assertEquals(byteList, arrays.getByteValues()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setByteValues(Arrays.asList((byte) 1, (byte) 2))); + List charList = Arrays.asList(' ', 'a', 'Z'); + arrays.setCharValues(charList); + assertEquals(charList, arrays.getCharValues()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setCharValues(Arrays.asList((byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd'))); + List float32List = Arrays.asList(0.0f, -1.125f, 1.125f); + arrays.setFloat32Values(float32List); + assertEquals(float32List, arrays.getFloat32Values()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setFloat32Values(Arrays.asList(1.0f, 2.0f))); + List float64List = Arrays.asList(0.0f, -3.1415, 3.1415); + arrays.setFloat64Values(float64List); + assertEquals(float64List, arrays.getFloat64Values()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setFloat64Values(Arrays.asList(1.0, 2.0, 3.0, 4.0))); + List int8List = Arrays.asList(0, -128, 127); + arrays.setInt8Values(int8List); + assertEquals(int8List, arrays.getInt8Values()); + assertThrows(IllegalArgumentException.class, + () ->arrays.setInt8Values(Arrays.asList((byte) 1, (byte) 2))); + List uint8List = Arrays.asList(0, 1, 255); + arrays.setUint8Values(uint8List); + assertEquals(uint8List, arrays.getUint8Values()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setUint8Values(Arrays.asList((byte) 1, (byte) 2, (byte) 3, (byte) 4))); + List int16List = Arrays.asList(0, -32768, 32767); + arrays.setInt16Values(int16List); + assertEquals(int16List, arrays.getInt16Values()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setInt16Values(Arrays.asList((short) 1, (short) 2))); + List uint16List = Arrays.asList(0, 1, 65535); + arrays.setUint16Values(uint16List); + assertEquals(uint16List, arrays.getUint16Values()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setUint16Values(Arrays.asList((short) 1, (short) 2, (short) 3, (short) 4))); + List int32List = Arrays.asList(0, -2147483648, 2147483647); + arrays.setInt32Values(int32List); + assertEquals(int32List, arrays.getInt32Values()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setInt32Values(Arrays.asList(1, 2))); + List uint32List = Arrays.asList(0, 1, 4294967295L); + arrays.setUint32Values(uint32List); + assertEquals(uint32List, arrays.getUint32Values()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setUint32Values(Arrays.asList(1, 2, 3, 4))); + List int64List = Arrays.asList(0, -9223372036854775808L, 9223372036854775807L); + arrays.setInt64Values(int64List); + assertEquals(int64List, arrays.getInt64Values()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setInt64Values(Arrays.asList(1L, 2L))); + List uint64List = Arrays.asList(0, 1, -1); + arrays.setUint64Values(uint64List); + assertEquals(uint64List, arrays.getUint64Values()); + assertThrows(IllegalArgumentException.class, + () ->arrays.setUint64Values(Arrays.asList(1L, 2L, 3L, 4L))); + + // Test setting/getting fixed length arrays of strings + List stringList = Arrays.asList("", "min value", "max_value"); + arrays.setStringValues(stringList); + assertEquals(stringList, arrays.getStringValues()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setStringValues(Arrays.asList("too", "few"))); + + // Test setting/getting fixed length arrays of nested types + rosidl_generator_java.msg.BasicTypes basicTypes = new rosidl_generator_java.msg.BasicTypes(); + List basicTypesList = Arrays.asList( + new rosidl_generator_java.msg.BasicTypes[] {basicTypes, basicTypes, basicTypes}); + arrays.setBasicTypesValues(basicTypesList); + assertEquals(basicTypesList, arrays.getBasicTypesValues()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setBasicTypesValues(Arrays.asList(new rosidl_generator_java.msg.BasicTypes[] {basicTypes}))); + rosidl_generator_java.msg.Constants constants = new rosidl_generator_java.msg.Constants(); + List constantsList = Arrays.asList( + new rosidl_generator_java.msg.Constants[] {constants, constants, constants}); + arrays.setConstantsValues(constantsList); + assertEquals(constantsList, arrays.getConstantsValues()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setConstantsValues(Arrays.asList(new rosidl_generator_java.msg.Constants[] {constants}))); + rosidl_generator_java.msg.Defaults defaults = new rosidl_generator_java.msg.Defaults(); + List defaultsList = Arrays.asList( + new rosidl_generator_java.msg.Defaults[] {defaults, defaults, defaults}); + arrays.setDefaultsValues(defaultsList); + assertEquals(defaultsList, arrays.getDefaultsValues()); + assertThrows(IllegalArgumentException.class, + () -> arrays.setDefaultsValues(Arrays.asList(new rosidl_generator_java.msg.Defaults[] {defaults}))); + + assertEquals(42, arrays.getAlignmentCheck()); } @Test - public final void testCheckUpperboundArrayConstraints() { - rosidl_generator_java.msg.Nested b = new rosidl_generator_java.msg.Nested(); - rosidl_generator_java.msg.Primitives primitives = new rosidl_generator_java.msg.Primitives(); - b.setUpToThreePrimitives(Arrays.asList(new rosidl_generator_java.msg.Primitives[] {})); - assertEquals( - Arrays.asList(new rosidl_generator_java.msg.Primitives[] {}), b.getUpToThreePrimitives()); - b.setUpToThreePrimitives( - Arrays.asList(new rosidl_generator_java.msg.Primitives[] {primitives})); - assertEquals(Arrays.asList(new rosidl_generator_java.msg.Primitives[] {primitives}), - b.getUpToThreePrimitives()); - b.setUpToThreePrimitives( - Arrays.asList(new rosidl_generator_java.msg.Primitives[] {primitives, primitives})); - assertEquals(Arrays.asList(new rosidl_generator_java.msg.Primitives[] {primitives, primitives}), - b.getUpToThreePrimitives()); - b.setUpToThreePrimitives(Arrays.asList( - new rosidl_generator_java.msg.Primitives[] {primitives, primitives, primitives})); - assertEquals(Arrays.asList(new rosidl_generator_java.msg.Primitives[] { - primitives, primitives, primitives}), - b.getUpToThreePrimitives()); - - thrown.expect(IllegalArgumentException.class); - b.setUpToThreePrimitives(Arrays.asList(new rosidl_generator_java.msg.Primitives[] { - primitives, primitives, primitives, primitives})); + public final void testBoundedSequences() { + rosidl_generator_java.msg.BoundedSequences bounded_seq = new rosidl_generator_java.msg.BoundedSequences(); + + // This value should not change and is asserted at end of test + bounded_seq.setAlignmentCheck(42); + + // Test setting/getting fixed length bounded_seq of primitive types + List boolList = Arrays.asList(true, false, true); + bounded_seq.setBoolValues(boolList); + assertEquals(boolList, bounded_seq.getBoolValues()); + List boolListShort = Arrays.asList(false); + bounded_seq.setBoolValues(boolListShort); + assertEquals(boolListShort, bounded_seq.getBoolValues()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setBoolValues(Arrays.asList(true, false, true, false))); + List byteList = Arrays.asList((byte) 0, (byte) 1, (byte) 255); + bounded_seq.setByteValues(byteList); + assertEquals(byteList, bounded_seq.getByteValues()); + List byteListShort = Arrays.asList((byte) 1); + bounded_seq.setByteValues(byteListShort); + assertEquals(byteListShort, bounded_seq.getByteValues()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setByteValues(Arrays.asList((byte) 1, (byte) 2, (byte) 3, (byte) 4))); + List charList = Arrays.asList(' ', 'a', 'Z'); + bounded_seq.setCharValues(charList); + assertEquals(charList, bounded_seq.getCharValues()); + List charListShort = Arrays.asList('z', 'A'); + bounded_seq.setCharValues(charListShort); + assertEquals(charListShort, bounded_seq.getCharValues()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setCharValues(Arrays.asList((byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd'))); + List float32List = Arrays.asList(0.0f, -1.125f, 1.125f); + bounded_seq.setFloat32Values(float32List); + assertEquals(float32List, bounded_seq.getFloat32Values()); + List float32ListShort = Arrays.asList(1.125f, -1.125f); + bounded_seq.setFloat32Values(float32ListShort); + assertEquals(float32ListShort, bounded_seq.getFloat32Values()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setFloat32Values(Arrays.asList(1.0f, 2.0f, 3.0f, 4.0f))); + List float64List = Arrays.asList(0.0f, -3.1415, 3.1415); + bounded_seq.setFloat64Values(float64List); + assertEquals(float64List, bounded_seq.getFloat64Values()); + List float64ListShort = Arrays.asList(3.1415, -3.1415); + bounded_seq.setFloat64Values(float64ListShort); + assertEquals(float64ListShort, bounded_seq.getFloat64Values()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setFloat64Values(Arrays.asList(1.0, 2.0, 3.0, 4.0))); + List int8List = Arrays.asList(0, -128, 127); + bounded_seq.setInt8Values(int8List); + assertEquals(int8List, bounded_seq.getInt8Values()); + List int8ListShort = Arrays.asList(127, -128); + bounded_seq.setInt8Values(int8ListShort); + assertEquals(int8ListShort, bounded_seq.getInt8Values()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setInt8Values(Arrays.asList((byte) 1, (byte) 2, (byte) 3, (byte) 4))); + List uint8List = Arrays.asList(0, 1, 255); + bounded_seq.setUint8Values(uint8List); + assertEquals(uint8List, bounded_seq.getUint8Values()); + List uint8ListShort = Arrays.asList(255, 1); + bounded_seq.setUint8Values(uint8ListShort); + assertEquals(uint8ListShort, bounded_seq.getUint8Values()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setUint8Values(Arrays.asList((byte) 1, (byte) 2, (byte) 3, (byte) 4))); + List int16List = Arrays.asList(0, -32768, 32767); + bounded_seq.setInt16Values(int16List); + assertEquals(int16List, bounded_seq.getInt16Values()); + List int16ListShort = Arrays.asList(32767, -32768); + bounded_seq.setInt16Values(int16ListShort); + assertEquals(int16ListShort, bounded_seq.getInt16Values()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setInt16Values(Arrays.asList((short) 1, (short) 2, (short) 3, (short) 4))); + List uint16List = Arrays.asList(0, 1, 65535); + bounded_seq.setUint16Values(uint16List); + assertEquals(uint16List, bounded_seq.getUint16Values()); + List uint16ListShort = Arrays.asList(0, 1, 65535); + bounded_seq.setUint16Values(uint16ListShort); + assertEquals(uint16ListShort, bounded_seq.getUint16Values()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setUint16Values(Arrays.asList((short) 1, (short) 2, (short) 3, (short) 4))); + List int32List = Arrays.asList(0, -2147483648, 2147483647); + bounded_seq.setInt32Values(int32List); + assertEquals(int32List, bounded_seq.getInt32Values()); + List int32ListShort = Arrays.asList(2147483647, -2147483648); + bounded_seq.setInt32Values(int32ListShort); + assertEquals(int32ListShort, bounded_seq.getInt32Values()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setInt32Values(Arrays.asList(1, 2, 3, 4))); + List uint32List = Arrays.asList(0, 1, 4294967295L); + bounded_seq.setUint32Values(uint32List); + assertEquals(uint32List, bounded_seq.getUint32Values()); + List uint32ListShort = Arrays.asList(4294967295L, 1); + bounded_seq.setUint32Values(uint32ListShort); + assertEquals(uint32ListShort, bounded_seq.getUint32Values()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setUint32Values(Arrays.asList(1, 2, 3, 4))); + List int64List = Arrays.asList(0, -9223372036854775808L, 9223372036854775807L); + bounded_seq.setInt64Values(int64List); + assertEquals(int64List, bounded_seq.getInt64Values()); + List int64ListShort = Arrays.asList(0, -9223372036854775808L, 9223372036854775807L); + bounded_seq.setInt64Values(int64ListShort); + assertEquals(int64ListShort, bounded_seq.getInt64Values()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setInt64Values(Arrays.asList(1L, 2L, 3L, 4L))); + List uint64List = Arrays.asList(0, 1, -1); + bounded_seq.setUint64Values(uint64List); + assertEquals(uint64List, bounded_seq.getUint64Values()); + List uint64ListShort = Arrays.asList(0, 1, -1); + bounded_seq.setUint64Values(uint64ListShort); + assertEquals(uint64ListShort, bounded_seq.getUint64Values()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setUint64Values(Arrays.asList(1L, 2L, 3L, 4L))); + + // Test setting/getting fixed length bounded_seq of strings + List stringList = Arrays.asList("", "min value", "max_value"); + bounded_seq.setStringValues(stringList); + assertEquals(stringList, bounded_seq.getStringValues()); + List stringListShort = Arrays.asList("max_value", ""); + bounded_seq.setStringValues(stringListShort); + assertEquals(stringListShort, bounded_seq.getStringValues()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setStringValues(Arrays.asList("too", "many", "values", "!"))); + + // Test setting/getting fixed length bounded_seq of nested types + rosidl_generator_java.msg.BasicTypes basicTypes = new rosidl_generator_java.msg.BasicTypes(); + List basicTypesList = Arrays.asList( + new rosidl_generator_java.msg.BasicTypes[] {basicTypes, basicTypes, basicTypes}); + bounded_seq.setBasicTypesValues(basicTypesList); + assertEquals(basicTypesList, bounded_seq.getBasicTypesValues()); + List basicTypesListShort = Arrays.asList( + new rosidl_generator_java.msg.BasicTypes[] {basicTypes}); + bounded_seq.setBasicTypesValues(basicTypesListShort); + assertEquals(basicTypesListShort, bounded_seq.getBasicTypesValues()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setBasicTypesValues( + Arrays.asList(new rosidl_generator_java.msg.BasicTypes[] {basicTypes, basicTypes, basicTypes, basicTypes}))); + rosidl_generator_java.msg.Constants constants = new rosidl_generator_java.msg.Constants(); + List constantsList = Arrays.asList( + new rosidl_generator_java.msg.Constants[] {constants, constants, constants}); + bounded_seq.setConstantsValues(constantsList); + assertEquals(constantsList, bounded_seq.getConstantsValues()); + List constantsListShort = Arrays.asList( + new rosidl_generator_java.msg.Constants[] {constants}); + bounded_seq.setConstantsValues(constantsListShort); + assertEquals(constantsListShort, bounded_seq.getConstantsValues()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setConstantsValues( + Arrays.asList(new rosidl_generator_java.msg.Constants[] {constants, constants, constants, constants}))); + rosidl_generator_java.msg.Defaults defaults = new rosidl_generator_java.msg.Defaults(); + List defaultsList = Arrays.asList( + new rosidl_generator_java.msg.Defaults[] {defaults, defaults, defaults}); + bounded_seq.setDefaultsValues(defaultsList); + assertEquals(defaultsList, bounded_seq.getDefaultsValues()); + List defaultsListShort = Arrays.asList( + new rosidl_generator_java.msg.Defaults[] {defaults, defaults, defaults}); + bounded_seq.setDefaultsValues(defaultsListShort); + assertEquals(defaultsListShort, bounded_seq.getDefaultsValues()); + assertThrows(IllegalArgumentException.class, + () -> bounded_seq.setDefaultsValues( + Arrays.asList(new rosidl_generator_java.msg.Defaults[] {defaults, defaults, defaults, defaults}))); + + assertEquals(42, bounded_seq.getAlignmentCheck()); } @Test - public final void testCheckUnboundedArrays() { - rosidl_generator_java.msg.Nested b = new rosidl_generator_java.msg.Nested(); - rosidl_generator_java.msg.Primitives primitives = new rosidl_generator_java.msg.Primitives(); - b.setUnboundedPrimitives( - Arrays.asList(new rosidl_generator_java.msg.Primitives[] {primitives, primitives})); - assertEquals(Arrays.asList(new rosidl_generator_java.msg.Primitives[] {primitives, primitives}), - b.getUnboundedPrimitives()); - } - - @Test - public final void testCheckIntArraysConstraints() { - rosidl_generator_java.msg.Various c = new rosidl_generator_java.msg.Various(); - c.setUpToThreeInt32Values(Arrays.asList(new Integer[] {})); - assertEquals(Arrays.asList(new Integer[] {}), c.getUpToThreeInt32Values()); - c.setUpToThreeInt32Values(Arrays.asList(new Integer[] {12345})); - assertEquals(Arrays.asList(new Integer[] {12345}), c.getUpToThreeInt32Values()); - c.setUpToThreeInt32Values(Arrays.asList(new Integer[] {12345, -12345})); - assertEquals(Arrays.asList(new Integer[] {12345, -12345}), c.getUpToThreeInt32Values()); - c.setUpToThreeInt32Values(Arrays.asList(new Integer[] {12345, -12345, 6789})); - assertEquals(Arrays.asList(new Integer[] {12345, -12345, 6789}), c.getUpToThreeInt32Values()); - - // Test that arrays of primitives are also accepted - c.setUpToThreeInt32Values(new int[] {}); - assertEquals(Arrays.asList(new Integer[] {}), c.getUpToThreeInt32Values()); - c.setUpToThreeInt32Values(new int[] {12345}); - assertEquals(Arrays.asList(new Integer[] {12345}), c.getUpToThreeInt32Values()); - c.setUpToThreeInt32Values(new int[] {12345, -12345}); - assertEquals(Arrays.asList(new Integer[] {12345, -12345}), c.getUpToThreeInt32Values()); - c.setUpToThreeInt32Values(new int[] {12345, -12345, 6789}); - assertEquals(Arrays.asList(new Integer[] {12345, -12345, 6789}), c.getUpToThreeInt32Values()); - - thrown.expect(IllegalArgumentException.class); - c.setUpToThreeInt32Values(Arrays.asList(new Integer[] {12345, -12345, 6789, -6789})); - } - - @Test - public final void testCheckUpperboundStringConstraints() { - rosidl_generator_java.msg.Various c = new rosidl_generator_java.msg.Various(); - c.setUpToThreeStringValues(Arrays.asList(new String[] {})); - assertEquals(Arrays.asList(new String[] {}), c.getUpToThreeStringValues()); - c.setUpToThreeStringValues(Arrays.asList(new String[] {"foo"})); - assertEquals(Arrays.asList(new String[] {"foo"}), c.getUpToThreeStringValues()); - c.setUpToThreeStringValues(Arrays.asList(new String[] {"foo", "bar"})); - assertEquals(Arrays.asList(new String[] {"foo", "bar"}), c.getUpToThreeStringValues()); - c.setUpToThreeStringValues(Arrays.asList(new String[] {"foo", "bar", "baz"})); - assertEquals(Arrays.asList(new String[] {"foo", "bar", "baz"}), c.getUpToThreeStringValues()); - - thrown.expect(IllegalArgumentException.class); - c.setUpToThreeStringValues(Arrays.asList(new String[] {"foo", "bar", "baz", "hello"})); + public final void testUnboundedSequences() { + rosidl_generator_java.msg.UnboundedSequences unbounded_seq = new rosidl_generator_java.msg.UnboundedSequences(); + + // This value should not change and is asserted at end of test + unbounded_seq.setAlignmentCheck(42); + + // Test setting/getting fixed length unbounded_seq of primitive types + List boolList = Arrays.asList(true, false, true); + unbounded_seq.setBoolValues(boolList); + assertEquals(boolList, unbounded_seq.getBoolValues()); + List byteList = Arrays.asList((byte) 0, (byte) 1, (byte) 255); + unbounded_seq.setByteValues(byteList); + assertEquals(byteList, unbounded_seq.getByteValues()); + List charList = Arrays.asList(' ', 'a', 'Z'); + unbounded_seq.setCharValues(charList); + assertEquals(charList, unbounded_seq.getCharValues()); + List float32List = Arrays.asList(0.0f, -1.125f, 1.125f); + unbounded_seq.setFloat32Values(float32List); + assertEquals(float32List, unbounded_seq.getFloat32Values()); + List float64List = Arrays.asList(0.0f, -3.1415, 3.1415); + unbounded_seq.setFloat64Values(float64List); + assertEquals(float64List, unbounded_seq.getFloat64Values()); + List int8List = Arrays.asList(0, -128, 127); + unbounded_seq.setInt8Values(int8List); + assertEquals(int8List, unbounded_seq.getInt8Values()); + List uint8List = Arrays.asList(0, 1, 255); + unbounded_seq.setUint8Values(uint8List); + assertEquals(uint8List, unbounded_seq.getUint8Values()); + List int16List = Arrays.asList(0, -32768, 32767); + unbounded_seq.setInt16Values(int16List); + assertEquals(int16List, unbounded_seq.getInt16Values()); + List uint16List = Arrays.asList(0, 1, 65535); + unbounded_seq.setUint16Values(uint16List); + assertEquals(uint16List, unbounded_seq.getUint16Values()); + List int32List = Arrays.asList(0, -2147483648, 2147483647); + unbounded_seq.setInt32Values(int32List); + assertEquals(int32List, unbounded_seq.getInt32Values()); + List uint32List = Arrays.asList(0, 1, 4294967295L); + unbounded_seq.setUint32Values(uint32List); + assertEquals(uint32List, unbounded_seq.getUint32Values()); + List int64List = Arrays.asList(0, -9223372036854775808L, 9223372036854775807L); + unbounded_seq.setInt64Values(int64List); + assertEquals(int64List, unbounded_seq.getInt64Values()); + List uint64List = Arrays.asList(0, 1, -1); + unbounded_seq.setUint64Values(uint64List); + assertEquals(uint64List, unbounded_seq.getUint64Values()); + + // Test setting/getting fixed length unbounded_seq of strings + List stringList = Arrays.asList("", "min value", "max_value"); + unbounded_seq.setStringValues(stringList); + assertEquals(stringList, unbounded_seq.getStringValues()); + + // Test setting/getting fixed length unbounded_seq of nested types + rosidl_generator_java.msg.BasicTypes basicTypes = new rosidl_generator_java.msg.BasicTypes(); + List basicTypesList = Arrays.asList( + new rosidl_generator_java.msg.BasicTypes[] {basicTypes, basicTypes, basicTypes}); + unbounded_seq.setBasicTypesValues(basicTypesList); + assertEquals(basicTypesList, unbounded_seq.getBasicTypesValues()); + rosidl_generator_java.msg.Constants constants = new rosidl_generator_java.msg.Constants(); + List constantsList = Arrays.asList( + new rosidl_generator_java.msg.Constants[] {constants, constants, constants}); + unbounded_seq.setConstantsValues(constantsList); + assertEquals(constantsList, unbounded_seq.getConstantsValues()); + rosidl_generator_java.msg.Defaults defaults = new rosidl_generator_java.msg.Defaults(); + List defaultsList = Arrays.asList( + new rosidl_generator_java.msg.Defaults[] {defaults, defaults, defaults}); + unbounded_seq.setDefaultsValues(defaultsList); + assertEquals(defaultsList, unbounded_seq.getDefaultsValues()); + + assertEquals(42, unbounded_seq.getAlignmentCheck()); } } From e1945af3b38422c764fbd8b30a50d6782e7d8ca0 Mon Sep 17 00:00:00 2001 From: talregev Date: Tue, 17 Mar 2020 22:42:36 +0200 Subject: [PATCH 06/33] Update Android repos for dashing (#80) --- ros2_java_android.repos | 44 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/ros2_java_android.repos b/ros2_java_android.repos index 74effa53..f6b76ae2 100644 --- a/ros2_java_android.repos +++ b/ros2_java_android.repos @@ -2,80 +2,84 @@ repositories: eProsima/Fast-CDR: type: git url: https://github.com/eProsima/Fast-CDR.git - version: v1.0.7 + version: v1.0.11 eProsima/Fast-RTPS: type: git url: https://github.com/eProsima/Fast-RTPS.git - version: 7a0b0fe7ca8d2c4ea41e36744c6024c263a6505a + version: v1.8.2 ros2/ament_cmake_ros: type: git url: https://github.com/ros2/ament_cmake_ros.git - version: 0.5.0 + version: dashing ros2/rcutils: type: git url: https://github.com/ros2/rcutils.git - version: 0.5.1 + version: dashing ros2/common_interfaces: type: git url: https://github.com/ros2/common_interfaces.git - version: 0.5.0 + version: dashing ros2/example_interfaces: type: git url: https://github.com/ros2/example_interfaces.git - version: 0.5.0 + version: dashing ros2/libyaml_vendor: type: git url: https://github.com/ros2/libyaml_vendor.git - version: 1.0.0 + version: dashing ros2/poco_vendor: type: git url: https://github.com/ros2/poco_vendor.git - version: 1.1.1 + version: dashing ros2/rcl: type: git url: https://github.com/ros2/rcl.git - version: 0.5.1 + version: dashing ros2/rcl_interfaces: type: git url: https://github.com/ros2/rcl_interfaces.git - version: db27f0e8619460848d80c1442f7fec0c56ee63e5 + version: dashing ros2/rmw: type: git url: https://github.com/ros2/rmw.git - version: 0.5.0 + version: dashing ros2/rmw_fastrtps: type: git url: https://github.com/ros2/rmw_fastrtps.git - version: 0.5.1 + version: dashing ros2/rmw_implementation: type: git url: https://github.com/ros2/rmw_implementation.git - version: 0.5.1 + version: dashing ros2/rosidl: type: git url: https://github.com/ros2/rosidl.git - version: 0.5.1 + version: dashing ros2/rosidl_dds: type: git url: https://github.com/ros2/rosidl_dds.git - version: 0.5.0 + version: dashing ros2/rosidl_defaults: type: git url: https://github.com/ros2/rosidl_defaults.git - version: 0.5.0 + version: dashing ros2/rosidl_typesupport: type: git url: https://github.com/ros2/rosidl_typesupport.git - version: 0.5.0 + version: dashing + ros2/osrf_testing_tools_cpp: + type: git + url: https://github.com/osrf/osrf_testing_tools_cpp + version: dashing ros2_java/ros2_java: type: git - url: https://github.com/esteve/ros2_java.git - version: master + url: https://github.com/ros2-java/ros2_java + version: dashing ros2_java/ros2_android: type: git url: https://github.com/esteve/ros2_android.git version: master ros2_java/ros2_android_examples: type: git - url: https://github.com/esteve/ros2_android_examples.git + url: https://github.com/ros2-java/ros2_android_examples version: master From 3da311123d591c768ecc0178692dc5750433b064 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Wed, 18 Mar 2020 08:35:48 -0700 Subject: [PATCH 07/33] Remove non-existent dependency on rosidl_typesupport_java (#97) The package does not exist. Signed-off-by: Jacob Perron --- rcljava/package.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/rcljava/package.xml b/rcljava/package.xml index a61b7b3f..90da562f 100644 --- a/rcljava/package.xml +++ b/rcljava/package.xml @@ -19,7 +19,6 @@ rmw rosidl_generator_c rosidl_typesupport_c - rosidl_typesupport_java builtin_interfaces rcl_interfaces rmw From dcc6a54c0194f36ba4745de55f219d1645ee7c91 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Wed, 18 Mar 2020 08:36:23 -0700 Subject: [PATCH 08/33] Shutdown default context after tests (#95) This fixes some fatal runtime errors during testing. Signed-off-by: Jacob Perron --- .../src/test/java/org/ros2/rcljava/publisher/PublisherTest.java | 1 + .../java/org/ros2/rcljava/subscription/SubscriptionTest.java | 1 + rcljava/src/test/java/org/ros2/rcljava/timer/TimerTest.java | 1 + 3 files changed, 3 insertions(+) diff --git a/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java b/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java index f06cc0fb..bfcba4e0 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java @@ -33,5 +33,6 @@ public final void testCreate() { assertEquals(node.getHandle(), publisher.getNodeReference().get().getHandle()); assertNotEquals(0, publisher.getNodeReference().get().getHandle()); assertNotEquals(0, publisher.getHandle()); + RCLJava.shutdown(); } } diff --git a/rcljava/src/test/java/org/ros2/rcljava/subscription/SubscriptionTest.java b/rcljava/src/test/java/org/ros2/rcljava/subscription/SubscriptionTest.java index c7fbbf5d..ee23ff47 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/subscription/SubscriptionTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/subscription/SubscriptionTest.java @@ -36,5 +36,6 @@ public void accept(final std_msgs.msg.String msg) {} assertEquals(node.getHandle(), subscription.getNodeReference().get().getHandle()); assertNotEquals(0, subscription.getNodeReference().get().getHandle()); assertNotEquals(0, subscription.getHandle()); + RCLJava.shutdown(); } } diff --git a/rcljava/src/test/java/org/ros2/rcljava/timer/TimerTest.java b/rcljava/src/test/java/org/ros2/rcljava/timer/TimerTest.java index 222c6251..28378805 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/timer/TimerTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/timer/TimerTest.java @@ -79,5 +79,6 @@ public final void testCreate() { assertTrue(timer.isCanceled()); assertEquals(4, timerCallback.getCounter()); + RCLJava.shutdown(); } } From 530552254f02a7c9c2603d03c038c5f8fb966691 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Wed, 18 Mar 2020 08:39:37 -0700 Subject: [PATCH 09/33] Fix service and action interface generation (#76) * Fix service and action interface generation Before, we were not generating code for the messages and services that make up service and action interfaces. Due to issues with duplicate definitions caused by instantiating the msg.cpp.em template multiple times, I've opted to generate separate files for each service, action, and the interfaces that they are made of. This is similar to what we are doing with the generated Java code. I've added a test confirming that generated service code can be used. Adding a test for actions is difficult at the moment due to a circular dependency with action_msgs. Signed-off-by: Jacob Perron * Add missing header include Signed-off-by: Jacob Perron * Rename top level generated cpp file This avoids name clashing with other generated files. Similar to what we do with generated Java files. Signed-off-by: Jacob Perron * Fix JNI name mangling function so it works for service and action subtypes For example, 'example_interfaces/srv/AddTwoInts_Request' should be mangled to 'example_1interfaces_srv_AddTwoInts_1Request'. Signed-off-by: Jacob Perron * Remove vestigal references to jni_package_name Signed-off-by: Jacob Perron * Add comment about action and service headers Signed-off-by: Jacob Perron * Simplify include logic Signed-off-by: Jacob Perron --- rosidl_generator_java/CMakeLists.txt | 1 + ...l_generator_java_generate_interfaces.cmake | 9 ++ rosidl_generator_java/resource/action.cpp.em | 94 ++++++++++-- rosidl_generator_java/resource/idl.cpp.em | 140 ++++++++---------- rosidl_generator_java/resource/msg.cpp.em | 74 +++++++-- rosidl_generator_java/resource/srv.cpp.em | 51 +++++-- .../rosidl_generator_java/__init__.py | 5 +- .../org/ros2/generator/InterfacesTest.java | 69 +++++++++ 8 files changed, 325 insertions(+), 118 deletions(-) diff --git a/rosidl_generator_java/CMakeLists.txt b/rosidl_generator_java/CMakeLists.txt index 4b1c2ea5..1e6c8539 100644 --- a/rosidl_generator_java/CMakeLists.txt +++ b/rosidl_generator_java/CMakeLists.txt @@ -67,6 +67,7 @@ if(BUILD_TESTING) set(_deps_library_dirs "") list_append_unique(_deps_library_dirs ${CMAKE_CURRENT_BINARY_DIR}) list_append_unique(_deps_library_dirs ${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_java/rosidl_generator_java/msg/) + list_append_unique(_deps_library_dirs ${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_java/rosidl_generator_java/srv/) foreach(testsuite ${${PROJECT_NAME}_testsuites}) ament_add_junit_tests("${PROJECT_NAME}_tests_${testsuite}" diff --git a/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake b/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake index e0b0ff17..6ca9c9ff 100644 --- a/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake +++ b/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake @@ -70,6 +70,10 @@ foreach(_abs_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) "${_output_path}/${_parent_folder}/${_idl_name}_Request.java" "${_output_path}/${_parent_folder}/${_idl_name}_Response.java" ) + foreach(_typesupport_impl ${_typesupport_impls}) + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Request.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Response.ep.${_typesupport_impl}.cpp") + endforeach() endif() # Actions generate extra files if(_parent_folder STREQUAL "action") @@ -78,6 +82,11 @@ foreach(_abs_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) "${_output_path}/${_parent_folder}/${_idl_name}_Result.java" "${_output_path}/${_parent_folder}/${_idl_name}_Feedback.java" ) + foreach(_typesupport_impl ${_typesupport_impls}) + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Goal.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Result.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Feedback.ep.${_typesupport_impl}.cpp") + endforeach() endif() foreach(_typesupport_impl ${_typesupport_impls}) diff --git a/rosidl_generator_java/resource/action.cpp.em b/rosidl_generator_java/resource/action.cpp.em index 17e456bf..1fcdd7fb 100644 --- a/rosidl_generator_java/resource/action.cpp.em +++ b/rosidl_generator_java/resource/action.cpp.em @@ -1,23 +1,93 @@ @# Included from rosidl_generator_java/resource/idl.cpp.em @{ +import os + +from rosidl_cmake import expand_template from rosidl_generator_c import idl_structure_type_to_c_include_prefix -action_includes = [ - 'rosidl_generator_c/action_type_support_struct.h', -] +namespaces = action.namespaced_type.namespaces +type_name = action.namespaced_type.name +goal_type_name = action.goal.structure.namespaced_type.name +result_type_name = action.result.structure.namespaced_type.name +feedback_type_name = action.feedback.structure.namespaced_type.name +feedback_message_type_name = action.feedback_message.structure.namespaced_type.name +send_goal_type_name = action.send_goal_service.namespaced_type.name +get_result_type_name = action.get_result_service.namespaced_type.name + +data = { + 'package_name': package_name, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +# Generate Goal message type +data.update({'message': action.goal}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(goal_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate Result message type +data.update({'message': action.result}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(result_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate Feedback message type +data.update({'message': action.feedback}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(feedback_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate FeedbackMessage message type +data.update({'message': action.feedback_message}) +output_file = os.path.join( + output_dir, + *namespaces[1:], + '{0}.ep.{1}.cpp'.format(feedback_message_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate SendGoal service type +data.update({'service': action.send_goal_service}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(send_goal_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate SendGoal service type +data.update({'service': action.get_result_service}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(get_result_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) }@ -@[for include in action_includes]@ -@[ if include in include_directives]@ -// already included above -// @ -@[ else]@ -@{include_directives.add(include)}@ -@[ end if]@ -#include "@(include)" -@[end for]@ + +#include + +#include + +#include "rosidl_generator_c/action_type_support_struct.h" #include "@(idl_structure_type_to_c_include_prefix(action.namespaced_type)).h" +// Ensure that a jlong is big enough to store raw pointers +static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); + #ifdef __cplusplus extern "C" { #endif diff --git a/rosidl_generator_java/resource/idl.cpp.em b/rosidl_generator_java/resource/idl.cpp.em index 48098dd9..c909006a 100644 --- a/rosidl_generator_java/resource/idl.cpp.em +++ b/rosidl_generator_java/resource/idl.cpp.em @@ -9,102 +9,90 @@ @# - package_name (string) @# - interface_path (Path relative to the directory named after the package) @# - content (IdlContent, list of elements, e.g. Messages or Services) +@# - output_dir (Path) +@# - template_basepath (Path) +@# - typesupport_impl (string, the typesupport identifier of the generated code) @####################################################################### @{ -include_directives = set() +import os -jni_includes = [ - 'jni.h', -] -include_directives.update(jni_includes) -std_includes = [ - 'cassert', - 'cstdint', - 'string', -] -include_directives.update(std_includes) -rosidl_includes = [ - 'rosidl_generator_c/message_type_support_struct.h', -] -include_directives.update(rosidl_includes) -rcljava_includes = [ - 'rcljava_common/exceptions.h', - 'rcljava_common/signatures.h', -] -include_directives.update(rcljava_includes) -}@ -@[for include in jni_includes]@ -#include <@(include)> -@[end for]@ - -@[for include in std_includes]@ -#include <@(include)> -@[end for]@ - -@[for include in rosidl_includes]@ -#include "@(include)" -@[end for]@ - -@[for include in rcljava_includes]@ -#include "@(include)" -@[end for]@ - -// Ensure that a jlong is big enough to store raw pointers -static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); - -using rcljava_common::exceptions::rcljava_throw_exception; +from rosidl_cmake import expand_template +from rosidl_parser.definition import Action +from rosidl_parser.definition import Message +from rosidl_parser.definition import Service -@{ -jni_package_name = package_name.replace('_', '_1') }@ @ @####################################################################### @# Handle messages @####################################################################### @{ -from rosidl_parser.definition import Message -}@ -@[for message in content.get_elements_of_type(Message)]@ -@{ -TEMPLATE( - 'msg.cpp.em', - package_name=package_name, - jni_package_name=jni_package_name, - message=message, - include_directives=include_directives) +data = { + 'package_name': package_name, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +for message in content.get_elements_of_type(Message): + data.update({'message': message}) + type_name = message.structure.namespaced_type.name + namespaces = message.structure.namespaced_type.namespaces + output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(type_name, typesupport_impl)) + expand_template( + 'msg.cpp.em', + data, + output_file, + template_basepath=template_basepath) }@ -@[end for]@ @ @####################################################################### @# Handle services @####################################################################### @{ -from rosidl_parser.definition import Service -}@ -@[for service in content.get_elements_of_type(Service)]@ -@{ -TEMPLATE( - 'srv.cpp.em', - package_name=package_name, - jni_package_name=jni_package_name, - service=service, - include_directives=include_directives) +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, + 'typesupport_impl': typesupport_impl, +} + +for service in content.get_elements_of_type(Service): + data.update({'service': service}) + type_name = service.namespaced_type.name + namespaces = service.namespaced_type.namespaces + output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(type_name, typesupport_impl)) + expand_template( + 'srv.cpp.em', + data, + output_file, + template_basepath=template_basepath) + }@ -@[end for]@ @ @####################################################################### @# Handle actions @####################################################################### @{ -from rosidl_parser.definition import Action -}@ -@[for action in content.get_elements_of_type(Action)]@ -@{ -TEMPLATE( - 'action.cpp.em', - package_name=package_name, - jni_package_name=jni_package_name, - action=action, - include_directives=include_directives) +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, + 'typesupport_impl': typesupport_impl, +} + +for action in content.get_elements_of_type(Action): + data.update({'action': action}) + type_name = action.namespaced_type.name + namespaces = action.namespaced_type.namespaces + output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(type_name, typesupport_impl)) + expand_template( + 'action.cpp.em', + data, + output_file, + template_basepath=template_basepath) }@ -@[end for]@ diff --git a/rosidl_generator_java/resource/msg.cpp.em b/rosidl_generator_java/resource/msg.cpp.em index 6d1f7877..4907c5d0 100644 --- a/rosidl_generator_java/resource/msg.cpp.em +++ b/rosidl_generator_java/resource/msg.cpp.em @@ -35,7 +35,7 @@ array_list_jni_type = "java/util/ArrayList" cache = defaultdict(lambda: False) cache[msg_normalized_type] = msg_jni_type namespaced_types = set() -includes = set() +member_includes = set() for member in message.structure.members: type_ = member.type if isinstance(type_, AbstractNestedType): @@ -43,36 +43,78 @@ for member in message.structure.members: cache[array_list_normalized_type] = array_list_jni_type type_ = type_.value_type if isinstance(type_, BasicType): - includes.add('rosidl_generator_c/primitives_sequence.h') - includes.add('rosidl_generator_c/primitives_sequence_functions.h') + member_includes.add('rosidl_generator_c/primitives_sequence.h') + member_includes.add('rosidl_generator_c/primitives_sequence_functions.h') # We do not cache strings because java.lang.String behaves differently if not isinstance(type_, AbstractGenericString): cache[get_normalized_type(type_)] = get_jni_type(type_) if isinstance(type_, AbstractString): - includes.add('rosidl_generator_c/string.h') - includes.add('rosidl_generator_c/string_functions.h') + member_includes.add('rosidl_generator_c/string.h') + member_includes.add('rosidl_generator_c/string_functions.h') if isinstance(type_, AbstractWString): - includes.add('rosidl_generator_c/u16string.h') - includes.add('rosidl_generator_c/u16string_functions.h') + member_includes.add('rosidl_generator_c/u16string.h') + member_includes.add('rosidl_generator_c/u16string_functions.h') if isinstance(type_, NamespacedType): namespaced_types.add(get_jni_type(type_)) - includes.add(idl_structure_type_to_c_include_prefix(type_) + '.h') + include_prefix = idl_structure_type_to_c_include_prefix(type_) + # TODO(jacobperron): Remove this logic after https://github.com/ros2/rosidl/pull/432 (Foxy) + # Strip off any service or action suffix + # There are several types that actions and services are composed of, but they are included + # a common header that is based on the action or service name + # ie. there are not separate headers for each type + if include_prefix.endswith('__request'): + include_prefix = include_prefix[:-9] + elif include_prefix.endswith('__response'): + include_prefix = include_prefix[:-10] + if include_prefix.endswith('__goal'): + include_prefix = include_prefix[:-6] + elif include_prefix.endswith('__result'): + include_prefix = include_prefix[:-8] + elif include_prefix.endswith('__feedback'): + include_prefix = include_prefix[:-10] + member_includes.add(include_prefix + '.h') }@ -@[for include in includes]@ -@[ if include in include_directives]@ -// already included above -// @ -@[ else]@ -@{include_directives.add(include)}@ -@[ end if]@ +@{ +# TODO(jacobperron): Remove this logic after https://github.com/ros2/rosidl/pull/432 (Foxy) +message_c_include_prefix = idl_structure_type_to_c_include_prefix(message.structure.namespaced_type) +# Strip off any service or action suffix +if message_c_include_prefix.endswith('__request'): + message_c_include_prefix = message_c_include_prefix[:-9] +elif message_c_include_prefix.endswith('__response'): + message_c_include_prefix = message_c_include_prefix[:-10] +if message_c_include_prefix.endswith('__goal'): + message_c_include_prefix = message_c_include_prefix[:-6] +elif message_c_include_prefix.endswith('__result'): + message_c_include_prefix = message_c_include_prefix[:-8] +elif message_c_include_prefix.endswith('__feedback'): + message_c_include_prefix = message_c_include_prefix[:-10] +}@ + +#include + +#include +#include +#include + +#include "rosidl_generator_c/message_type_support_struct.h" + +#include "rcljava_common/exceptions.h" +#include "rcljava_common/signatures.h" + +@[for include in member_includes]@ #include "@(include)" @[end for]@ -#include "@(idl_structure_type_to_c_include_prefix(message.structure.namespaced_type)).h" +#include "@(message_c_include_prefix).h" + +// Ensure that a jlong is big enough to store raw pointers +static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); + +using rcljava_common::exceptions::rcljava_throw_exception; #ifdef __cplusplus extern "C" { diff --git a/rosidl_generator_java/resource/srv.cpp.em b/rosidl_generator_java/resource/srv.cpp.em index 5c2d2d0b..c7bd40a6 100644 --- a/rosidl_generator_java/resource/srv.cpp.em +++ b/rosidl_generator_java/resource/srv.cpp.em @@ -1,23 +1,50 @@ @# Included from rosidl_generator_java/resource/idl.cpp.em @{ +import os +from rosidl_cmake import expand_template from rosidl_generator_c import idl_structure_type_to_c_include_prefix -service_includes = [ - 'rosidl_generator_c/service_type_support_struct.h', -] +namespaces = service.namespaced_type.namespaces +type_name = service.namespaced_type.name +request_type_name = service.request_message.structure.namespaced_type.name +response_type_name = service.response_message.structure.namespaced_type.name + +data = { + 'package_name': package_name, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +# Generate request message +data.update({'message': service.request_message}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(request_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate response message +data.update({'message': service.response_message}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(response_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) }@ -@[for include in service_includes]@ -@[ if include in include_directives]@ -// already included above -// @ -@[ else]@ -@{include_directives.add(include)}@ -@[ end if]@ -#include "@(include)" -@[end for]@ + +#include + +#include + +#include "rosidl_generator_c/service_type_support_struct.h" #include "@(idl_structure_type_to_c_include_prefix(service.namespaced_type)).h" +// Ensure that a jlong is big enough to store raw pointers +static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); + #ifdef __cplusplus extern "C" { #endif diff --git a/rosidl_generator_java/rosidl_generator_java/__init__.py b/rosidl_generator_java/rosidl_generator_java/__init__.py index 7941ab69..32a8e366 100644 --- a/rosidl_generator_java/rosidl_generator_java/__init__.py +++ b/rosidl_generator_java/rosidl_generator_java/__init__.py @@ -45,8 +45,9 @@ def generate_java(generator_arguments_file, typesupport_impls): for impl in typesupport_impls: mapping = { - 'idl.cpp.em': '%s.ep.{0}.cpp'.format(impl), + 'idl.cpp.em': '_%s.cpp', } + additional_context.update(typesupport_impl=impl) generate_files( generator_arguments_file, mapping, @@ -203,4 +204,4 @@ def get_jni_signature(type_): def get_jni_mangled_name(fully_qualified_name): # JNI name mangling: # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names - return fully_qualified_name[0].replace('_', '_1') + '_' + '_'.join(fully_qualified_name[1:]) + return '_'.join(list(map(lambda name: name.replace('_', '_1'), fully_qualified_name))) diff --git a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java index dfea4a97..00638e09 100644 --- a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java +++ b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java @@ -532,4 +532,73 @@ public final void testUnboundedSequences() { assertEquals(42, unbounded_seq.getAlignmentCheck()); } + + @Test + public final void testBasicTypesService() { + rosidl_generator_java.srv.BasicTypes_Request basicTypesRequest = + new rosidl_generator_java.srv.BasicTypes_Request(); + rosidl_generator_java.srv.BasicTypes_Response basicTypesResponse = + new rosidl_generator_java.srv.BasicTypes_Response(); + // Set request fields + boolean expectedBool1 = true; + basicTypesRequest.setBoolValue(expectedBool1); + byte expectedByte1 = 123; + basicTypesRequest.setByteValue(expectedByte1); + byte expectedChar1 = 'a'; + basicTypesRequest.setCharValue(expectedChar1); + float expectedFloat1 = 12.34f; + basicTypesRequest.setFloat32Value(expectedFloat1); + double expectedDouble1 = 12.34; + basicTypesRequest.setFloat64Value(expectedDouble1); + byte expectedInt81 = 123; + basicTypesRequest.setInt8Value(expectedInt81); + short expectedInt161 = 1230; + basicTypesRequest.setInt16Value(expectedInt161); + int expectedInt321 = 123000; + basicTypesRequest.setInt32Value(expectedInt321); + long expectedInt641 = 42949672960L; + basicTypesRequest.setInt64Value(expectedInt641); + + // Set response fields + boolean expectedBool2 = false; + basicTypesResponse.setBoolValue(expectedBool2); + byte expectedByte2 = -42; + basicTypesResponse.setByteValue(expectedByte2); + byte expectedChar2 = ' '; + basicTypesResponse.setCharValue(expectedChar2); + float expectedFloat2 = -43.21f; + basicTypesResponse.setFloat32Value(expectedFloat2); + double expectedDouble2 = -43.21; + basicTypesResponse.setFloat64Value(expectedDouble2); + byte expectedInt82 = -42; + basicTypesResponse.setInt8Value(expectedInt82); + short expectedInt162 = -420; + basicTypesResponse.setInt16Value(expectedInt162); + int expectedInt322 = -42000; + basicTypesResponse.setInt32Value(expectedInt322); + long expectedInt642 = -4200000L; + basicTypesResponse.setInt64Value(expectedInt642); + + // Get request fields + assertEquals(expectedBool1, basicTypesRequest.getBoolValue()); + assertEquals(expectedByte1, basicTypesRequest.getByteValue()); + assertEquals(expectedChar1, basicTypesRequest.getCharValue()); + assertEquals(expectedFloat1, basicTypesRequest.getFloat32Value(), 0.01f); + assertEquals(expectedDouble1, basicTypesRequest.getFloat64Value(), 0.01); + assertEquals(expectedInt81, basicTypesRequest.getInt8Value()); + assertEquals(expectedInt161, basicTypesRequest.getInt16Value()); + assertEquals(expectedInt321, basicTypesRequest.getInt32Value()); + assertEquals(expectedInt641, basicTypesRequest.getInt64Value()); + + // Get response fields + assertEquals(expectedBool2, basicTypesResponse.getBoolValue()); + assertEquals(expectedByte2, basicTypesResponse.getByteValue()); + assertEquals(expectedChar2, basicTypesResponse.getCharValue()); + assertEquals(expectedFloat2, basicTypesResponse.getFloat32Value(), 0.01f); + assertEquals(expectedDouble2, basicTypesResponse.getFloat64Value(), 0.01); + assertEquals(expectedInt82, basicTypesResponse.getInt8Value()); + assertEquals(expectedInt162, basicTypesResponse.getInt16Value()); + assertEquals(expectedInt322, basicTypesResponse.getInt32Value()); + assertEquals(expectedInt642, basicTypesResponse.getInt64Value()); + } } From a86bd0fc7e694d344fa2bc8cf79873be3f87f5f7 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Wed, 18 Mar 2020 08:41:47 -0700 Subject: [PATCH 10/33] Rename rcljava_common cpp headers to have .hpp suffix (#93) * Fix service and action interface generation Before, we were not generating code for the messages and services that make up service and action interfaces. Due to issues with duplicate definitions caused by instantiating the msg.cpp.em template multiple times, I've opted to generate separate files for each service, action, and the interfaces that they are made of. This is similar to what we are doing with the generated Java code. I've added a test confirming that generated service code can be used. Adding a test for actions is difficult at the moment due to a circular dependency with action_msgs. Signed-off-by: Jacob Perron * Add missing header include Signed-off-by: Jacob Perron * Rename top level generated cpp file This avoids name clashing with other generated files. Similar to what we do with generated Java files. Signed-off-by: Jacob Perron * Fix JNI name mangling function so it works for service and action subtypes For example, 'example_interfaces/srv/AddTwoInts_Request' should be mangled to 'example_1interfaces_srv_AddTwoInts_1Request'. Signed-off-by: Jacob Perron * Remove vestigal references to jni_package_name Signed-off-by: Jacob Perron * Add comment about action and service headers Signed-off-by: Jacob Perron * Simplify include logic Signed-off-by: Jacob Perron * Rename cpp headers to have .hpp suffix Signed-off-by: Jacob Perron * Update files to reflect new header suffix This resolves a cppcheck linter error complaining about "is invalid C code". Changing the suffix causes cppcheck to treat the files as C++ code. Signed-off-by: Jacob Perron Co-authored-by: Esteve Fernandez --- rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp | 4 ++-- rcljava/src/main/cpp/org_ros2_rcljava_Time.cpp | 2 +- .../src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp | 4 ++-- .../main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp | 4 ++-- .../main/cpp/org_ros2_rcljava_executors_BaseExecutor.cpp | 4 ++-- rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp | 4 ++-- .../main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp | 4 ++-- .../src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp | 4 ++-- .../org_ros2_rcljava_subscription_SubscriptionImpl.cpp | 4 ++-- rcljava/src/main/cpp/org_ros2_rcljava_time_Clock.cpp | 4 ++-- .../src/main/cpp/org_ros2_rcljava_timer_WallTimerImpl.cpp | 4 ++-- .../rcljava_common/{exceptions.h => exceptions.hpp} | 8 ++++---- .../rcljava_common/{signatures.h => signatures.hpp} | 6 +++--- .../{visibility_control.h => visibility_control.hpp} | 6 +++--- rcljava_common/src/main/cpp/rcljava_common.cpp | 2 +- rosidl_generator_java/resource/msg.cpp.em | 4 ++-- 16 files changed, 34 insertions(+), 34 deletions(-) rename rcljava_common/include/rcljava_common/{exceptions.h => exceptions.hpp} (85%) rename rcljava_common/include/rcljava_common/{signatures.h => signatures.hpp} (88%) rename rcljava_common/include/rcljava_common/{visibility_control.h => visibility_control.hpp} (92%) diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp index 00249c2b..d4251ee6 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp @@ -25,8 +25,8 @@ #include "rmw/rmw.h" #include "rosidl_generator_c/message_type_support_struct.h" -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" #include "org_ros2_rcljava_RCLJava.h" diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_Time.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_Time.cpp index 1f287b7f..fcd332a2 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_Time.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_Time.cpp @@ -19,7 +19,7 @@ #include "rcl/error_handling.h" #include "rcl/rcl.h" -#include "rcljava_common/exceptions.h" +#include "rcljava_common/exceptions.hpp" #include "org_ros2_rcljava_Time.h" diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp index aec896e9..9277fc3c 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp @@ -24,8 +24,8 @@ #include "rmw/rmw.h" #include "rosidl_generator_c/message_type_support_struct.h" -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" #include "org_ros2_rcljava_client_ClientImpl.h" diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp index 4b2f8695..8f70afad 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp @@ -20,8 +20,8 @@ #include "rcl/error_handling.h" #include "rcl/init.h" -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" #include "org_ros2_rcljava_contexts_ContextImpl.h" diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_executors_BaseExecutor.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_executors_BaseExecutor.cpp index 3f948b1b..e87daaa4 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_executors_BaseExecutor.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_executors_BaseExecutor.cpp @@ -25,8 +25,8 @@ #include "rmw/rmw.h" #include "rosidl_generator_c/message_type_support_struct.h" -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" #include "org_ros2_rcljava_executors_BaseExecutor.h" diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp index 16db9d04..8df9b5b9 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp @@ -24,8 +24,8 @@ #include "rmw/rmw.h" #include "rosidl_generator_c/message_type_support_struct.h" -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" #include "org_ros2_rcljava_node_NodeImpl.h" diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp index fef34631..6cef82c2 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp @@ -24,8 +24,8 @@ #include "rcl/rcl.h" #include "rmw/rmw.h" -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" #include "org_ros2_rcljava_publisher_PublisherImpl.h" diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp index e56289d8..f4708f6c 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp @@ -24,8 +24,8 @@ #include "rmw/rmw.h" #include "rosidl_generator_c/message_type_support_struct.h" -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" #include "org_ros2_rcljava_service_ServiceImpl.h" diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_subscription_SubscriptionImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_subscription_SubscriptionImpl.cpp index d0e84bdf..eedb9242 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_subscription_SubscriptionImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_subscription_SubscriptionImpl.cpp @@ -24,8 +24,8 @@ #include "rmw/rmw.h" #include "rosidl_generator_c/message_type_support_struct.h" -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" #include "org_ros2_rcljava_subscription_SubscriptionImpl.h" diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_time_Clock.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_time_Clock.cpp index fe54ed1b..3a4638b2 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_time_Clock.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_time_Clock.cpp @@ -20,8 +20,8 @@ #include "rcl/error_handling.h" #include "rcl/time.h" -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" #include "org_ros2_rcljava_time_Clock.h" diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_timer_WallTimerImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_timer_WallTimerImpl.cpp index dd0ed3a3..8a7559af 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_timer_WallTimerImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_timer_WallTimerImpl.cpp @@ -24,8 +24,8 @@ #include "rcl/rcl.h" #include "rmw/rmw.h" -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" #include "org_ros2_rcljava_timer_WallTimerImpl.h" diff --git a/rcljava_common/include/rcljava_common/exceptions.h b/rcljava_common/include/rcljava_common/exceptions.hpp similarity index 85% rename from rcljava_common/include/rcljava_common/exceptions.h rename to rcljava_common/include/rcljava_common/exceptions.hpp index d24bea92..8a148611 100644 --- a/rcljava_common/include/rcljava_common/exceptions.h +++ b/rcljava_common/include/rcljava_common/exceptions.hpp @@ -11,13 +11,13 @@ // 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. -#ifndef RCLJAVA_COMMON__EXCEPTIONS_H_ -#define RCLJAVA_COMMON__EXCEPTIONS_H_ +#ifndef RCLJAVA_COMMON__EXCEPTIONS_HPP_ +#define RCLJAVA_COMMON__EXCEPTIONS_HPP_ #include #include -#include "rcljava_common/visibility_control.h" +#include "rcljava_common/visibility_control.hpp" namespace rcljava_common { @@ -31,4 +31,4 @@ void rcljava_throw_exception(JNIEnv *, const char *, const std::string &); } // namespace exceptions } // namespace rcljava_common -#endif // RCLJAVA_COMMON__EXCEPTIONS_H_ +#endif // RCLJAVA_COMMON__EXCEPTIONS_HPP_ diff --git a/rcljava_common/include/rcljava_common/signatures.h b/rcljava_common/include/rcljava_common/signatures.hpp similarity index 88% rename from rcljava_common/include/rcljava_common/signatures.h rename to rcljava_common/include/rcljava_common/signatures.hpp index 35d9e6d8..11576474 100644 --- a/rcljava_common/include/rcljava_common/signatures.h +++ b/rcljava_common/include/rcljava_common/signatures.hpp @@ -11,8 +11,8 @@ // 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. -#ifndef RCLJAVA_COMMON__SIGNATURES_H_ -#define RCLJAVA_COMMON__SIGNATURES_H_ +#ifndef RCLJAVA_COMMON__SIGNATURES_HPP_ +#define RCLJAVA_COMMON__SIGNATURES_HPP_ #include #include @@ -29,4 +29,4 @@ using destroy_ros_message_signature = void (*)(void *); } // namespace signatures } // namespace rcljava_common -#endif // RCLJAVA_COMMON__SIGNATURES_H_ +#endif // RCLJAVA_COMMON__SIGNATURES_HPP_ diff --git a/rcljava_common/include/rcljava_common/visibility_control.h b/rcljava_common/include/rcljava_common/visibility_control.hpp similarity index 92% rename from rcljava_common/include/rcljava_common/visibility_control.h rename to rcljava_common/include/rcljava_common/visibility_control.hpp index b0c65d3b..d4895c85 100644 --- a/rcljava_common/include/rcljava_common/visibility_control.h +++ b/rcljava_common/include/rcljava_common/visibility_control.hpp @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef RCLJAVA_COMMON__VISIBILITY_CONTROL_H_ -#define RCLJAVA_COMMON__VISIBILITY_CONTROL_H_ +#ifndef RCLJAVA_COMMON__VISIBILITY_CONTROL_HPP_ +#define RCLJAVA_COMMON__VISIBILITY_CONTROL_HPP_ #ifdef __cplusplus extern "C" @@ -55,4 +55,4 @@ extern "C" } #endif -#endif // RCLJAVA_COMMON__VISIBILITY_CONTROL_H_ +#endif // RCLJAVA_COMMON__VISIBILITY_CONTROL_HPP_ diff --git a/rcljava_common/src/main/cpp/rcljava_common.cpp b/rcljava_common/src/main/cpp/rcljava_common.cpp index 25accf78..4fb1797d 100644 --- a/rcljava_common/src/main/cpp/rcljava_common.cpp +++ b/rcljava_common/src/main/cpp/rcljava_common.cpp @@ -16,7 +16,7 @@ #include #include -#include "rcljava_common/exceptions.h" +#include "rcljava_common/exceptions.hpp" namespace rcljava_common { diff --git a/rosidl_generator_java/resource/msg.cpp.em b/rosidl_generator_java/resource/msg.cpp.em index 4907c5d0..9c868e60 100644 --- a/rosidl_generator_java/resource/msg.cpp.em +++ b/rosidl_generator_java/resource/msg.cpp.em @@ -102,8 +102,8 @@ elif message_c_include_prefix.endswith('__feedback'): #include "rosidl_generator_c/message_type_support_struct.h" -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" @[for include in member_includes]@ #include "@(include)" From 9d591b1976b14fce5b3eac1f010dfe101635335c Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Wed, 18 Mar 2020 08:50:08 -0700 Subject: [PATCH 11/33] Update desktop repos file (#94) We don't need to build everything from source, only some of the ROS interface packages. Signed-off-by: Jacob Perron --- ros2_java_desktop.repos | 110 +++++----------------------------------- 1 file changed, 13 insertions(+), 97 deletions(-) diff --git a/ros2_java_desktop.repos b/ros2_java_desktop.repos index 1ff6490e..2fc3529d 100644 --- a/ros2_java_desktop.repos +++ b/ros2_java_desktop.repos @@ -1,113 +1,29 @@ repositories: - eProsima/Fast-CDR: + ament/ament_java: type: git - url: https://github.com/eProsima/Fast-CDR.git - version: v1.0.7 - eProsima/Fast-RTPS: - type: git - url: https://github.com/eProsima/Fast-RTPS.git - version: 7a0b0fe7ca8d2c4ea41e36744c6024c263a6505a - ros/class_loader: - type: git - url: https://github.com/ros/class_loader.git - version: 1.1.0 - ros/console_bridge: - type: git - url: https://github.com/ros/console_bridge.git - version: ad25f7307da76be2857545e7e5c2a20727eee542 - ros2/console_bridge_vendor: - type: git - url: https://github.com/ros2/console_bridge_vendor.git + url: https://github.com/ros2-java/ament_java.git version: master - ros2/ament_cmake_ros: - type: git - url: https://github.com/ros2/ament_cmake_ros.git - version: 0.5.0 - ros2/rcutils: - type: git - url: https://github.com/ros2/rcutils.git - version: 0.5.1 ros2/common_interfaces: type: git - url: https://github.com/ros2/common_interfaces.git - version: 0.5.0 - ros2/example_interfaces: - type: git - url: https://github.com/ros2/example_interfaces.git - version: 0.5.0 - ros2/launch: - type: git - url: https://github.com/ros2/launch.git - version: 0.5.2 - ros2/libyaml_vendor: - type: git - url: https://github.com/ros2/libyaml_vendor.git - version: 1.0.0 - ros2/poco_vendor: - type: git - url: https://github.com/ros2/poco_vendor.git - version: 1.1.1 - ros2/rcl: - type: git - url: https://github.com/ros2/rcl.git - version: 0.5.1 + url: https://github.com/ros2/common_interfaces + version: dashing ros2/rcl_interfaces: type: git url: https://github.com/ros2/rcl_interfaces.git - version: db27f0e8619460848d80c1442f7fec0c56ee63e5 - ros2/rclpy: - type: git - url: https://github.com/ros2/rclpy.git - version: 0.5.3 - ros2/rmw: - type: git - url: https://github.com/ros2/rmw.git - version: 0.5.0 - ros2/rmw_fastrtps: - type: git - url: https://github.com/ros2/rmw_fastrtps.git - version: 0.5.1 - ros2/rmw_implementation: - type: git - url: https://github.com/ros2/rmw_implementation.git - version: 0.5.1 - ros2/rmw_opensplice: - type: git - url: https://github.com/ros2/rmw_opensplice.git - version: 0.5.2 - ros2/rosidl_typesupport_opensplice: - type: git - url: https://github.com/ros2/rosidl_typesupport_opensplice.git - version: 0.5.0 - ros2/ros2cli: - type: git - url: https://github.com/ros2/ros2cli.git - version: 0.5.3 - ros2/rosidl: - type: git - url: https://github.com/ros2/rosidl.git - version: 0.5.1 - ros2/rosidl_dds: - type: git - url: https://github.com/ros2/rosidl_dds.git - version: 0.5.0 + version: dashing ros2/rosidl_defaults: type: git - url: https://github.com/ros2/rosidl_defaults.git - version: 0.5.0 - ros2/rosidl_python: + url: https://github.com/ros2/rosidl_defaults + version: dashing + ros2/unique_identifier_msgs: type: git - url: https://github.com/ros2/rosidl_python.git - version: 0.5.2 - ros2/rosidl_typesupport: - type: git - url: https://github.com/ros2/rosidl_typesupport.git - version: 0.5.0 + url: https://github.com/ros2/unique_identifier_msgs + version: dashing ros2_java/ros2_java: type: git - url: https://github.com/esteve/ros2_java.git - version: master + url: https://github.com/ros2-java/ros2_java.git + version: dashing ros2_java/ros2_java_examples: type: git - url: https://github.com/esteve/ros2_java_examples.git + url: https://github.com/ros2-java/ros2_java_examples.git version: master From 2357c33035830269924199ed32e32ea4eba8baf1 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Wed, 18 Mar 2020 08:50:38 -0700 Subject: [PATCH 12/33] Workaround dependency error when generating test message (#91) Signed-off-by: Jacob Perron --- rosidl_generator_java/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rosidl_generator_java/CMakeLists.txt b/rosidl_generator_java/CMakeLists.txt index 1e6c8539..1b55ad93 100644 --- a/rosidl_generator_java/CMakeLists.txt +++ b/rosidl_generator_java/CMakeLists.txt @@ -37,6 +37,8 @@ if(BUILD_TESTING) include(cmake/register_java.cmake) include(cmake/rosidl_generator_java_get_typesupports.cmake) + # Trick ament_target_dependencies() into thinking this package has been found + set(rosidl_generator_java_FOUND "1") set(rosidl_generator_java_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake") rosidl_generator_java_extras( From 8b144dbfdd77f4193d3e49e01c8e3d5c8629db11 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Tue, 7 Apr 2020 11:36:44 -0700 Subject: [PATCH 13/33] Add GitHub workflow for Dashing (#96) * Add dashing workflow Using the ros-tooling custom GitHub actions Signed-off-by: Jacob Perron * Update repos file URL Signed-off-by: Jacob Perron --- .github/workflows/build_and_test.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/build_and_test.yml diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml new file mode 100644 index 00000000..19734367 --- /dev/null +++ b/.github/workflows/build_and_test.yml @@ -0,0 +1,20 @@ +name: CI + +on: [push] + +jobs: + build_and_test: + runs-on: ubuntu-18.04 + steps: + - name: Install Java + run: | + sudo apt update -qq + sudo apt install -y default-jdk + - uses: ros-tooling/setup-ros@0.0.14 + with: + required-ros-distributions: dashing + - uses: ros-tooling/action-ros-ci@8d58122 + with: + package-name: rosidl_generator_java rcljava_common rcljava + source-ros-binary-installation: dashing + vcs-repo-file-url: https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_desktop.repos From 601efedee69463d32f9b854e9063e2ded0a13493 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Wed, 6 May 2020 08:59:08 -0700 Subject: [PATCH 14/33] Add example_interfaces to desktop repos file (#101) * Add example_interfaces to desktop repos file This package is required by rcljava_examples. Signed-off-by: Jacob Perron * Append .git to repository URLs for consistency Signed-off-by: Jacob Perron --- ros2_java_desktop.repos | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ros2_java_desktop.repos b/ros2_java_desktop.repos index 2fc3529d..78ef6a0e 100644 --- a/ros2_java_desktop.repos +++ b/ros2_java_desktop.repos @@ -5,7 +5,11 @@ repositories: version: master ros2/common_interfaces: type: git - url: https://github.com/ros2/common_interfaces + url: https://github.com/ros2/common_interfaces.git + version: dashing + ros2/example_interfaces: + type: git + url: https://github.com/ros2/example_interfaces.git version: dashing ros2/rcl_interfaces: type: git @@ -13,11 +17,11 @@ repositories: version: dashing ros2/rosidl_defaults: type: git - url: https://github.com/ros2/rosidl_defaults + url: https://github.com/ros2/rosidl_defaults.git version: dashing ros2/unique_identifier_msgs: type: git - url: https://github.com/ros2/unique_identifier_msgs + url: https://github.com/ros2/unique_identifier_msgs.git version: dashing ros2_java/ros2_java: type: git From 603a8f4c2486918c098584a54fe05d58cb512b0c Mon Sep 17 00:00:00 2001 From: nielstiben Date: Thu, 7 May 2020 13:37:43 +0200 Subject: [PATCH 15/33] Readme instruction fix (#106) * Fix README with correct branch in the instructions. * Update README instructions repository links. Changed the repository links from a private github account to ROS2 Organisation repositories links. Co-authored-by: Niels Tiben --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 99ed10c8..e8e683be 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ Desktop ``` mkdir -p ros2_java_ws/src cd ros2_java_ws -curl -skL https://raw.githubusercontent.com/esteve/ros2_java/master/ros2_java_desktop.repos -o ros2_java_desktop.repos +curl -skL https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_desktop.repos -o ros2_java_desktop.repos vcs import src < ros2_java_desktop.repos . ../ament_ws/install_isolated/local_setup.sh ament build --symlink-install --isolated @@ -144,7 +144,7 @@ export ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang # pull and build ros2 for android mkdir -p ${ROS2_ANDROID_WORKSPACE}/src cd ${ROS2_ANDROID_WORKSPACE} -wget https://raw.githubusercontent.com/esteve/ros2_java/master/ros2_java_android.repos +wget https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_desktop.repos vcs import ${ROS2_ANDROID_WORKSPACE}/src < ros2_java_android.repos source ${AMENT_WORKSPACE}/install_isolated/local_setup.sh ament build --isolated --skip-packages test_msgs \ From 81678abc53a7ab3d6b61e0d87e071cb0901809b8 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Thu, 7 May 2020 05:28:38 -0700 Subject: [PATCH 16/33] Add Client methods for checking and waiting for service availability (#85) * Re-enable tests related to services Signed-off-by: Jacob Perron * Add Client methods for checking and waiting for service availability These methods are very useful for allowing a client to wait for a service to be available before making requests. Signed-off-by: Jacob Perron * Refactor ClientTest to avoid repeatedly sending requests Signed-off-by: Jacob Perron --- rcljava/CMakeLists.txt | 10 ++-- .../org_ros2_rcljava_client_ClientImpl.h | 10 ++++ .../org_ros2_rcljava_client_ClientImpl.cpp | 27 +++++++++ .../java/org/ros2/rcljava/client/Client.java | 30 ++++++++++ .../org/ros2/rcljava/client/ClientImpl.java | 56 +++++++++++++++++++ .../org/ros2/rcljava/client/ClientTest.java | 24 +++++--- 6 files changed, 145 insertions(+), 12 deletions(-) diff --git a/rcljava/CMakeLists.txt b/rcljava/CMakeLists.txt index a6b4211f..694a2beb 100644 --- a/rcljava/CMakeLists.txt +++ b/rcljava/CMakeLists.txt @@ -222,10 +222,10 @@ if(BUILD_TESTING) set(${PROJECT_NAME}_test_sources "src/test/java/org/ros2/rcljava/RCLJavaTest.java" "src/test/java/org/ros2/rcljava/TimeTest.java" - # "src/test/java/org/ros2/rcljava/client/ClientTest.java" + "src/test/java/org/ros2/rcljava/client/ClientTest.java" "src/test/java/org/ros2/rcljava/node/NodeTest.java" - # "src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java" - # "src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java" + "src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java" + "src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java" "src/test/java/org/ros2/rcljava/publisher/PublisherTest.java" "src/test/java/org/ros2/rcljava/subscription/SubscriptionTest.java" "src/test/java/org/ros2/rcljava/timer/TimerTest.java" @@ -234,9 +234,9 @@ if(BUILD_TESTING) set(${PROJECT_NAME}_testsuites "org.ros2.rcljava.RCLJavaTest" "org.ros2.rcljava.TimeTest" - # "org.ros2.rcljava.client.ClientTest" + "org.ros2.rcljava.client.ClientTest" "org.ros2.rcljava.node.NodeTest" - # "org.ros2.rcljava.parameters.SyncParametersClientTest" + "org.ros2.rcljava.parameters.SyncParametersClientTest" "org.ros2.rcljava.publisher.PublisherTest" "org.ros2.rcljava.subscription.SubscriptionTest" "org.ros2.rcljava.timer.TimerTest" diff --git a/rcljava/include/org_ros2_rcljava_client_ClientImpl.h b/rcljava/include/org_ros2_rcljava_client_ClientImpl.h index ab71b022..c59635e0 100644 --- a/rcljava/include/org_ros2_rcljava_client_ClientImpl.h +++ b/rcljava/include/org_ros2_rcljava_client_ClientImpl.h @@ -37,6 +37,16 @@ JNICALL Java_org_ros2_rcljava_client_ClientImpl_nativeSendClientRequest( JNIEXPORT void JNICALL Java_org_ros2_rcljava_client_ClientImpl_nativeDispose(JNIEnv *, jclass, jlong, jlong); +/* + * Class: org_ros2_rcljava_client_ClientImpl + * Method: nativeIsServiceAvailable + * Signature: (JJ)Z + */ +JNIEXPORT jboolean +JNICALL Java_org_ros2_rcljava_client_ClientImpl_nativeIsServiceAvailable( + JNIEnv *, jclass, jlong, jlong); + + #ifdef __cplusplus } #endif diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp index 9277fc3c..0455c306 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_client_ClientImpl.cpp @@ -19,6 +19,7 @@ #include #include "rcl/error_handling.h" +#include "rcl/graph.h" #include "rcl/node.h" #include "rcl/rcl.h" #include "rmw/rmw.h" @@ -96,3 +97,29 @@ Java_org_ros2_rcljava_client_ClientImpl_nativeDispose( rcljava_throw_rclexception(env, ret, msg); } } + +JNIEXPORT jboolean JNICALL +Java_org_ros2_rcljava_client_ClientImpl_nativeIsServiceAvailable( + JNIEnv * env, jclass, jlong node_handle, jlong client_handle) +{ + rcl_node_t * node = reinterpret_cast(node_handle); + assert(node != NULL); + rcl_client_t * client = reinterpret_cast(client_handle); + assert(client != NULL); + + bool is_ready; + rcl_ret_t ret = rcl_service_server_is_available(node, client, &is_ready); + if (RCL_RET_NODE_INVALID == ret) { + if (node && !rcl_context_is_valid(node->context)) { + // context is shutdown, do a soft failure + return false; + } + } + if (ret != RCL_RET_OK) { + std::string msg = + "Failed to check if service is available: " + std::string(rcl_get_error_string().str); + rcl_reset_error(); + rcljava_throw_rclexception(env, ret, msg); + } + return is_ready; +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/client/Client.java b/rcljava/src/main/java/org/ros2/rcljava/client/Client.java index 79ef137e..774ecec6 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/client/Client.java +++ b/rcljava/src/main/java/org/ros2/rcljava/client/Client.java @@ -15,6 +15,7 @@ package org.ros2.rcljava.client; +import java.time.Duration; import java.util.concurrent.Future; import org.ros2.rcljava.concurrent.RCLFuture; @@ -37,5 +38,34 @@ Future asyncSendRe Future asyncSendRequest( final U request, final Consumer> callback); + /** + * Check if the service server is available. + * + * @return true if the client can talk to the service, false otherwise. + */ + boolean isServiceAvailable(); + + /** + * Wait for the service server to be available. + * + * Blocks until the service is available or the ROS context is invalidated. + * + * @return true if the service is available, false if the ROS context was shutdown. + */ + boolean waitForService(); + + /** + * Wait for the service server to be available. + * + * Blocks until the service is available or a timeout occurs. + * Also returns if the ROS context is invalidated. + * + * @param timeout Time to wait for the service to be available. + * A zero value causes this method to check if the service is available and return immediately. + * A negative value is treated as an infinite timeout. + * @return true if the service is available, false otherwise. + */ + boolean waitForService(Duration timeout); + String getServiceName(); } diff --git a/rcljava/src/main/java/org/ros2/rcljava/client/ClientImpl.java b/rcljava/src/main/java/org/ros2/rcljava/client/ClientImpl.java index eb1b2754..7b2a8557 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/client/ClientImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/client/ClientImpl.java @@ -15,11 +15,15 @@ package org.ros2.rcljava.client; +import java.time.Duration; import java.lang.ref.WeakReference; +import java.lang.InterruptedException; +import java.lang.Long; import java.util.AbstractMap; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import org.ros2.rcljava.RCLJava; import org.ros2.rcljava.common.JNIUtils; @@ -139,6 +143,58 @@ public final long getHandle() { return this.handle; } + private static native boolean nativeIsServiceAvailable(long nodeHandle, long handle); + + /** + * {@inheritDoc} + */ + public boolean isServiceAvailable() { + Node node = this.nodeReference.get(); + if (node == null) { + return false; + } + return nativeIsServiceAvailable(node.getHandle(), this.handle); + } + + /** + * {@inheritDoc} + */ + public final boolean waitForService() { + return waitForService(Duration.ofNanos(-1)); + } + + /** + * {@inheritDoc} + */ + public final boolean waitForService(Duration timeout) { + long timeoutNano = timeout.toNanos(); + if (0L == timeoutNano) { + return isServiceAvailable(); + } + long startTime = System.nanoTime(); + long timeToWait = (timeoutNano >= 0L) ? timeoutNano : Long.MAX_VALUE; + while (RCLJava.ok() && timeToWait > 0L) { + // TODO(jacobperron): Wake up whenever graph changes instead of sleeping for a fixed duration + try { + TimeUnit.MILLISECONDS.sleep(10); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + return false; + } + + if (isServiceAvailable()) { + return true; + } + + // If timeout is negative, timeToWait will always be greater than zero + if (timeoutNano > 0L) { + timeToWait = timeoutNano - (System.nanoTime() - startTime); + } + } + + return false; + } + public String getServiceName() { return this.serviceName; } diff --git a/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java b/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java index dd41e228..df7d2a6c 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java @@ -26,9 +26,12 @@ import org.junit.Test; import java.lang.ref.WeakReference; +import java.time.Duration; import java.util.Arrays; import java.util.List; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import org.ros2.rcljava.RCLJava; import org.ros2.rcljava.concurrent.RCLFuture; @@ -81,10 +84,10 @@ public static void tearDownOnce() { @Test public final void testAdd() throws Exception { - RCLFuture future = + RCLFuture consumerFuture = new RCLFuture(new WeakReference(node)); - TestClientConsumer clientConsumer = new TestClientConsumer(future); + TestClientConsumer clientConsumer = new TestClientConsumer(consumerFuture); Service service = node.createService( rcljava.srv.AddTwoInts.class, "add_two_ints", clientConsumer); @@ -96,12 +99,19 @@ public final void testAdd() throws Exception { Client client = node.createClient(rcljava.srv.AddTwoInts.class, "add_two_ints"); - while (RCLJava.ok() && !future.isDone()) { - client.asyncSendRequest(request); - RCLJava.spinOnce(node); - } + assertTrue(client.waitForService(Duration.ofSeconds(10))); + + Future responseFuture = client.asyncSendRequest(request); + + rcljava.srv.AddTwoInts_Response response = responseFuture.get(10, TimeUnit.SECONDS); + + // Check that the message was received by the service + assertTrue(consumerFuture.isDone()); + + // Check the contents of the response + assertEquals(5, response.getSum()); - assertEquals(5, future.get().getSum()); + // Cleanup client.dispose(); assertEquals(0, client.getHandle()); service.dispose(); From 30303d139e95b942c050e4106455dd202f510b35 Mon Sep 17 00:00:00 2001 From: jayhou Date: Sat, 9 May 2020 17:13:17 +0800 Subject: [PATCH 17/33] fix README.md android part (#107) * Android part pul command use the wrong reops Signed-off-by: Jay Hou --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e8e683be..330d52f9 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ export ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang # pull and build ros2 for android mkdir -p ${ROS2_ANDROID_WORKSPACE}/src cd ${ROS2_ANDROID_WORKSPACE} -wget https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_desktop.repos +wget https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_android.repos vcs import ${ROS2_ANDROID_WORKSPACE}/src < ros2_java_android.repos source ${AMENT_WORKSPACE}/install_isolated/local_setup.sh ament build --isolated --skip-packages test_msgs \ From 0506afbd85311920c722b8290b96529b2ac74d36 Mon Sep 17 00:00:00 2001 From: Niels Tiben Date: Tue, 9 Jun 2020 11:49:38 +0200 Subject: [PATCH 18/33] Fix action generation (#108) Generate action code for: * `_SendGoal_Request` * `_SendGoal_Response` * `_GetResult_Request` * `_GetResult_Response` --- rosidl_generator_java/resource/action.cpp.em | 30 ++++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/rosidl_generator_java/resource/action.cpp.em b/rosidl_generator_java/resource/action.cpp.em index 1fcdd7fb..e6291779 100644 --- a/rosidl_generator_java/resource/action.cpp.em +++ b/rosidl_generator_java/resource/action.cpp.em @@ -58,21 +58,45 @@ expand_template( data, output_file) +# Generate SendGoal message type +data.update({'msg': action.send_goal_service.request_message}) +output_file = os.path.join(output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(send_goal_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate GetResult message type +data.update({'msg': action.get_result_service.request_message}) +output_file = os.path.join(output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(send_goal_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, + 'typesupport_impl': typesupport_impl, +} + # Generate SendGoal service type data.update({'service': action.send_goal_service}) output_file = os.path.join( output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(send_goal_type_name, typesupport_impl)) expand_template( - 'msg.cpp.em', + 'srv.cpp.em', data, output_file) -# Generate SendGoal service type +# Generate GetResult service type data.update({'service': action.get_result_service}) output_file = os.path.join( output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(get_result_type_name, typesupport_impl)) expand_template( - 'msg.cpp.em', + 'srv.cpp.em', data, output_file) }@ From 52f5159f0306d9977ebf78d8deceeabee3da0743 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Wed, 24 Jun 2020 14:08:06 -0700 Subject: [PATCH 19/33] Disable flake8 (#111) This fixes CI while we wait for an upstream issue to be resolved. See https://github.com/ament/ament_lint/pull/252 Signed-off-by: Jacob Perron --- rosidl_generator_java/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rosidl_generator_java/CMakeLists.txt b/rosidl_generator_java/CMakeLists.txt index 1b55ad93..bd4c94b4 100644 --- a/rosidl_generator_java/CMakeLists.txt +++ b/rosidl_generator_java/CMakeLists.txt @@ -31,6 +31,10 @@ if(BUILD_TESTING) find_package(rosidl_generator_c REQUIRED) find_package(test_interface_files REQUIRED) + # Disable flake8 linter + # TODO(jacobperron): Re-enable after https://github.com/ament/ament_lint/pull/252 + set(ament_cmake_flake8_FOUND TRUE) + find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() From 31a6bacf6d5d8b732c89079334249d44ae59e64b Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Tue, 30 Jun 2020 14:09:42 -0700 Subject: [PATCH 20/33] Add methods to remove entities from Node (#110) * Add methods to remove entities from Node When an entity is disposed, make sure to also remove it from the Node. This resolves an issue where invalid entities may be used by other classes or users. For example, a disposed Subscription being accessed by the executor. Fixes #105 Signed-off-by: Jacob Perron * Add tests for disposing publishers, services, and clients Signed-off-by: Jacob Perron * Rename test Signed-off-by: Jacob Perron --- .../org/ros2/rcljava/client/ClientImpl.java | 1 + .../main/java/org/ros2/rcljava/node/Node.java | 49 +++++++++++++++++++ .../java/org/ros2/rcljava/node/NodeImpl.java | 28 +++++++++++ .../ros2/rcljava/publisher/PublisherImpl.java | 1 + .../org/ros2/rcljava/service/ServiceImpl.java | 1 + .../subscription/SubscriptionImpl.java | 1 + .../org/ros2/rcljava/client/ClientTest.java | 8 ++- .../ros2/rcljava/publisher/PublisherTest.java | 10 +++- .../subscription/SubscriptionTest.java | 10 +++- 9 files changed, 106 insertions(+), 3 deletions(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/client/ClientImpl.java b/rcljava/src/main/java/org/ros2/rcljava/client/ClientImpl.java index 7b2a8557..076b99cc 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/client/ClientImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/client/ClientImpl.java @@ -131,6 +131,7 @@ public final Class getResponseType() { public final void dispose() { Node node = this.nodeReference.get(); if (node != null) { + node.removeClient(this); nativeDispose(node.getHandle(), this.handle); this.handle = 0; } diff --git a/rcljava/src/main/java/org/ros2/rcljava/node/Node.java b/rcljava/src/main/java/org/ros2/rcljava/node/Node.java index 39dab4b2..0a3ba7ad 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/node/Node.java +++ b/rcljava/src/main/java/org/ros2/rcljava/node/Node.java @@ -122,6 +122,55 @@ Client createClient( Client createClient(final Class serviceType, final String serviceName) throws NoSuchFieldException, IllegalAccessException; + /** + * Remove a Subscription created by this Node. + * + * Calling this method effectively invalidates the passed @{link Subscription}. + * If the subscription was not created by this Node, then nothing happens. + * + * @param subscription The object to remove from this node. + * @return true if the subscription was removed, false if the subscription was already + * removed or was never created by this Node. + */ + boolean removeSubscription(final Subscription subscription); + + /** + * Remove a Publisher created by this Node. + * + * Calling this method effectively invalidates the passed @{link Publisher}. + * If the publisher was not created by this Node, then nothing happens. + * + * @param publisher The object to remove from this node. + * @return true if the publisher was removed, false if the publisher was already + * removed or was never created by this Node. + */ + boolean removePublisher(final Publisher publisher); + + /** + * Remove a Service created by this Node. + * + * Calling this method effectively invalidates the passed @{link Service}. + * If the service was not created by this Node, then nothing happens. + * + * @param service The object to remove from this node. + * @return true if the service was removed, false if the service was already + * removed or was never created by this Node. + */ + boolean removeService(final Service service); + + /** + * Remove a Client created by this Node. + * + * Calling this method effectively invalidates the passed @{link Client}. + * If the client was not created by this Node, then nothing happens. + * + * @param client The object to remove from this node. + * @return true if the client was removed, false if the client was already + * removed or was never created by this Node. + */ + boolean removeClient(final Client client); + + WallTimer createWallTimer(final long period, final TimeUnit unit, final Callback callback); String getName(); diff --git a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java index 2235fda0..68018fa8 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java @@ -221,6 +221,20 @@ public final Subscription createSubscription( return this.createSubscription(messageType, topic, callback, QoSProfile.DEFAULT); } + /** + * {@inheritDoc} + */ + public boolean removeSubscription(final Subscription subscription) { + return this.subscriptions.remove(subscription); + } + + /** + * {@inheritDoc} + */ + public boolean removePublisher(final Publisher publisher) { + return this.publishers.remove(publisher); + } + /** * {@inheritDoc} */ @@ -300,6 +314,20 @@ public Client createClient(final Class servi private static native long nativeCreateClientHandle( long handle, Class cls, String serviceName, long qosProfileHandle); + /** + * {@inheritDoc} + */ + public boolean removeService(final Service service) { + return this.services.remove(service); + } + + /** + * {@inheritDoc} + */ + public boolean removeClient(final Client client) { + return this.clients.remove(client); + } + /** * {@inheritDoc} */ diff --git a/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java b/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java index f0b3cb6b..f3fa717b 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java @@ -117,6 +117,7 @@ public final WeakReference getNodeReference() { public final void dispose() { Node node = this.nodeReference.get(); if (node != null) { + node.removePublisher(this); nativeDispose(node.getHandle(), this.handle); this.handle = 0; } diff --git a/rcljava/src/main/java/org/ros2/rcljava/service/ServiceImpl.java b/rcljava/src/main/java/org/ros2/rcljava/service/ServiceImpl.java index a438e72b..e45d06ec 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/service/ServiceImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/service/ServiceImpl.java @@ -85,6 +85,7 @@ public final Class getResponseType() { public final void dispose() { Node node = this.nodeReference.get(); if (node != null) { + node.removeService(this); nativeDispose(node.getHandle(), this.handle); this.handle = 0; } diff --git a/rcljava/src/main/java/org/ros2/rcljava/subscription/SubscriptionImpl.java b/rcljava/src/main/java/org/ros2/rcljava/subscription/SubscriptionImpl.java index 1cdd48cf..47fce59a 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/subscription/SubscriptionImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/subscription/SubscriptionImpl.java @@ -129,6 +129,7 @@ public final WeakReference getNodeReference() { public final void dispose() { Node node = this.nodeReference.get(); if (node != null) { + node.removeSubscription(this); nativeDispose(node.getHandle(), this.handle); this.handle = 0; } diff --git a/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java b/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java index df7d2a6c..f4724d9b 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java @@ -111,10 +111,16 @@ public final void testAdd() throws Exception { // Check the contents of the response assertEquals(5, response.getSum()); - // Cleanup + assertEquals(1, node.getClients().size()); + assertEquals(1, node.getServices().size()); + + // We expect that calling dispose should result in a zero handle + // and the reference is dropped from the Node client.dispose(); assertEquals(0, client.getHandle()); + assertEquals(0, node.getClients().size()); service.dispose(); assertEquals(0, service.getHandle()); + assertEquals(0, node.getServices().size()); } } diff --git a/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java b/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java index bfcba4e0..2ce379fd 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java @@ -25,7 +25,7 @@ public class PublisherTest { @Test - public final void testCreate() { + public final void testCreateAndDispose() { RCLJava.rclJavaInit(); Node node = RCLJava.createNode("test_node"); Publisher publisher = @@ -33,6 +33,14 @@ public final void testCreate() { assertEquals(node.getHandle(), publisher.getNodeReference().get().getHandle()); assertNotEquals(0, publisher.getNodeReference().get().getHandle()); assertNotEquals(0, publisher.getHandle()); + assertEquals(1, node.getPublishers().size()); + + // We expect that calling dispose should result in a zero handle + // and the reference is dropped from the Node + publisher.dispose(); + assertEquals(0, publisher.getHandle()); + assertEquals(0, node.getPublishers().size()); + RCLJava.shutdown(); } } diff --git a/rcljava/src/test/java/org/ros2/rcljava/subscription/SubscriptionTest.java b/rcljava/src/test/java/org/ros2/rcljava/subscription/SubscriptionTest.java index ee23ff47..8c990480 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/subscription/SubscriptionTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/subscription/SubscriptionTest.java @@ -26,7 +26,7 @@ public class SubscriptionTest { @Test - public final void testCreate() { + public final void testCreateAndDispose() { RCLJava.rclJavaInit(); Node node = RCLJava.createNode("test_node"); Subscription subscription = node.createSubscription( @@ -36,6 +36,14 @@ public void accept(final std_msgs.msg.String msg) {} assertEquals(node.getHandle(), subscription.getNodeReference().get().getHandle()); assertNotEquals(0, subscription.getNodeReference().get().getHandle()); assertNotEquals(0, subscription.getHandle()); + assertEquals(1, node.getSubscriptions().size()); + + // We expect that calling dispose should result in a zero handle + // and the reference is dropped from the Node + subscription.dispose(); + assertEquals(0, subscription.getHandle()); + assertEquals(0, node.getSubscriptions().size()); + RCLJava.shutdown(); } } From 3cc2e2265d90907dc75767eba53f78a7f61af959 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Wed, 30 Sep 2020 08:07:16 -0700 Subject: [PATCH 21/33] Trigger CI workflow on pull requests (#135) Signed-off-by: Jacob Perron --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 19734367..40f1cc7b 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -1,6 +1,6 @@ name: CI -on: [push] +on: [push, pull_request] jobs: build_and_test: From 353166eeaea7373aad72344f801aaedebe952f4a Mon Sep 17 00:00:00 2001 From: sung-goo-kim <69021132+sung-goo-kim@users.noreply.github.com> Date: Thu, 1 Oct 2020 00:08:01 +0900 Subject: [PATCH 22/33] (RclJava) fix BaseExecutor.spinSome logic - run the executable even if it found (#134) --- .../org/ros2/rcljava/executors/BaseExecutor.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/executors/BaseExecutor.java b/rcljava/src/main/java/org/ros2/rcljava/executors/BaseExecutor.java index 536e417b..2233a5d0 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/executors/BaseExecutor.java +++ b/rcljava/src/main/java/org/ros2/rcljava/executors/BaseExecutor.java @@ -332,12 +332,12 @@ protected void spinSome() { AnyExecutable anyExecutable = getNextExecutable(); if (anyExecutable == null) { waitForWork(0); - do { - anyExecutable = getNextExecutable(); - if (anyExecutable != null) { - executeAnyExecutable(anyExecutable); - } - } while (anyExecutable != null); + anyExecutable = getNextExecutable(); + } + + while (anyExecutable != null) { + executeAnyExecutable(anyExecutable); + anyExecutable = getNextExecutable(); } } From bd10dab40e2a7a96324ccc6433ca42a9e8fcc25a Mon Sep 17 00:00:00 2001 From: sung-goo-kim <69021132+sung-goo-kim@users.noreply.github.com> Date: Thu, 1 Oct 2020 00:08:30 +0900 Subject: [PATCH 23/33] (RclJava) Fix Node.testPubUInt32MultipleNodes() error (#133) --- rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java b/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java index 8dd5aa30..92cd7ec6 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java @@ -837,7 +837,7 @@ public Node getNode() { executor.addNode(composableSubscriptionNodeOne); executor.addNode(composableSubscriptionNodeTwo); - while (RCLJava.ok() && !futureOne.isDone() && !futureTwo.isDone()) { + while (RCLJava.ok() && !(futureOne.isDone() && futureTwo.isDone())) { publisher.publish(msg); executor.spinSome(); } From b3a25b10cc7e693a247dbdadb6541890acdcfd07 Mon Sep 17 00:00:00 2001 From: sung-goo-kim <69021132+sung-goo-kim@users.noreply.github.com> Date: Thu, 1 Oct 2020 00:08:58 +0900 Subject: [PATCH 24/33] clear node/context handle on RclJava.cleanup() (#130) * clear node/context handle on RclJava.cleanup() * clear publishers, subscriptions, clients, services and timers on Node.dispose() --- .../main/java/org/ros2/rcljava/RCLJava.java | 23 +++---------------- .../java/org/ros2/rcljava/node/NodeImpl.java | 17 ++++++++++++++ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java index ef58b7ae..75e2148d 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java +++ b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java @@ -80,31 +80,14 @@ private RCLJava() {} private static void cleanup() { for (Node node : nodes) { - for (Subscription subscription : node.getSubscriptions()) { - subscription.dispose(); - } - - for (Publisher publisher : node.getPublishers()) { - publisher.dispose(); - } - - for (Timer timer : node.getTimers()) { - timer.dispose(); - } - - for (Service service : node.getServices()) { - service.dispose(); - } - - for (Client client : node.getClients()) { - client.dispose(); - } - node.dispose(); } + nodes.clear(); + for (Context context : contexts) { context.dispose(); } + contexts.clear(); } static { diff --git a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java index 68018fa8..9fdc1c27 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java @@ -24,6 +24,7 @@ import org.ros2.rcljava.consumers.TriConsumer; import org.ros2.rcljava.contexts.Context; import org.ros2.rcljava.qos.QoSProfile; +import org.ros2.rcljava.interfaces.Disposable; import org.ros2.rcljava.interfaces.MessageDefinition; import org.ros2.rcljava.interfaces.ServiceDefinition; import org.ros2.rcljava.parameters.ParameterType; @@ -343,10 +344,26 @@ public final Collection getClients() { */ private static native void nativeDispose(long handle); + private void cleanupDisposables(Collection disposables) { + for (Disposable disposable : disposables) { + disposable.dispose(); + } + disposables.clear(); + } + + private void cleanup() { + cleanupDisposables(subscriptions); + cleanupDisposables(publishers); + cleanupDisposables(timers); + cleanupDisposables(services); + cleanupDisposables(clients); + } + /** * {@inheritDoc} */ public final void dispose() { + cleanup(); nativeDispose(this.handle); this.handle = 0; } From 885fa3fc838d4d94529192e2e7685595482a6b74 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Mon, 9 Nov 2020 14:30:33 -0800 Subject: [PATCH 25/33] Compile generated action-related services (#140) * Remove redundant code generation The request and response messages are already generated as part of the srv template. Signed-off-by: Jacob Perron * Strip action service suffixes from C include prefix The generated C headers for actions are included in a single header named after the action. Signed-off-by: Jacob Perron * Generate Java code for SendGoal and GetResult service definitions Though not strictly necessary, it is nice to have definitions for these action-specific services for the purpose of writing unit tests. Signed-off-by: Jacob Perron * Compile generated service definitions for actions Previously, though we were generated service definitions for actions we were not compiling them. Signed-off-by: Jacob Perron --- ...dl_generator_java_generate_interfaces.cmake | 12 ++++++++++++ rosidl_generator_java/resource/action.cpp.em | 17 ----------------- rosidl_generator_java/resource/action.java.em | 18 ++++++++++++++++++ rosidl_generator_java/resource/msg.cpp.em | 10 ++++++++++ rosidl_generator_java/resource/srv.cpp.em | 15 ++++++++++++++- 5 files changed, 54 insertions(+), 18 deletions(-) diff --git a/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake b/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake index 6ca9c9ff..31dc1a59 100644 --- a/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake +++ b/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake @@ -81,11 +81,23 @@ foreach(_abs_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) "${_output_path}/${_parent_folder}/${_idl_name}_Goal.java" "${_output_path}/${_parent_folder}/${_idl_name}_Result.java" "${_output_path}/${_parent_folder}/${_idl_name}_Feedback.java" + "${_output_path}/${_parent_folder}/${_idl_name}_SendGoal.java" + "${_output_path}/${_parent_folder}/${_idl_name}_SendGoal_Request.java" + "${_output_path}/${_parent_folder}/${_idl_name}_SendGoal_Response.java" + "${_output_path}/${_parent_folder}/${_idl_name}_GetResult.java" + "${_output_path}/${_parent_folder}/${_idl_name}_GetResult_Request.java" + "${_output_path}/${_parent_folder}/${_idl_name}_GetResult_Response.java" ) foreach(_typesupport_impl ${_typesupport_impls}) list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Goal.ep.${_typesupport_impl}.cpp") list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Result.ep.${_typesupport_impl}.cpp") list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Feedback.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_SendGoal.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_SendGoal_Request.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_SendGoal_Response.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_GetResult.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_GetResult_Request.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_GetResult_Response.ep.${_typesupport_impl}.cpp") endforeach() endif() diff --git a/rosidl_generator_java/resource/action.cpp.em b/rosidl_generator_java/resource/action.cpp.em index e6291779..b093b313 100644 --- a/rosidl_generator_java/resource/action.cpp.em +++ b/rosidl_generator_java/resource/action.cpp.em @@ -58,25 +58,8 @@ expand_template( data, output_file) -# Generate SendGoal message type -data.update({'msg': action.send_goal_service.request_message}) -output_file = os.path.join(output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(send_goal_type_name, typesupport_impl)) -expand_template( - 'msg.cpp.em', - data, - output_file) - -# Generate GetResult message type -data.update({'msg': action.get_result_service.request_message}) -output_file = os.path.join(output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(send_goal_type_name, typesupport_impl)) -expand_template( - 'msg.cpp.em', - data, - output_file) - data = { 'package_name': package_name, - 'interface_path': interface_path, 'output_dir': output_dir, 'template_basepath': template_basepath, 'typesupport_impl': typesupport_impl, diff --git a/rosidl_generator_java/resource/action.java.em b/rosidl_generator_java/resource/action.java.em index 070ddd61..5e7ebbe9 100644 --- a/rosidl_generator_java/resource/action.java.em +++ b/rosidl_generator_java/resource/action.java.em @@ -13,6 +13,8 @@ type_name = action.namespaced_type.name goal_type_name = action.goal.structure.namespaced_type.name result_type_name = action.result.structure.namespaced_type.name feedback_type_name = action.feedback.structure.namespaced_type.name +send_goal_type_name = action.send_goal_service.namespaced_type.name +get_result_type_name = action.get_result_service.namespaced_type.name data = { 'package_name': package_name, @@ -44,6 +46,22 @@ expand_template( output_file, template_basepath=template_basepath) +data.update({'service': action.send_goal_service}) +output_file = os.path.join(output_dir, *namespaces[1:], send_goal_type_name + '.java') +expand_template( + 'srv.java.em', + data, + output_file, + template_basepath=template_basepath) + +data.update({'service': action.get_result_service}) +output_file = os.path.join(output_dir, *namespaces[1:], get_result_type_name + '.java') +expand_template( + 'srv.java.em', + data, + output_file, + template_basepath=template_basepath) + action_imports = [ 'org.ros2.rcljava.common.JNIUtils', 'org.ros2.rcljava.interfaces.ActionDefinition', diff --git a/rosidl_generator_java/resource/msg.cpp.em b/rosidl_generator_java/resource/msg.cpp.em index 9c868e60..fc7e764c 100644 --- a/rosidl_generator_java/resource/msg.cpp.em +++ b/rosidl_generator_java/resource/msg.cpp.em @@ -62,6 +62,7 @@ for member in message.structure.members: namespaced_types.add(get_jni_type(type_)) include_prefix = idl_structure_type_to_c_include_prefix(type_) # TODO(jacobperron): Remove this logic after https://github.com/ros2/rosidl/pull/432 (Foxy) + # and https://github.com/ros2/rosidl/pull/538 # Strip off any service or action suffix # There are several types that actions and services are composed of, but they are included # a common header that is based on the action or service name @@ -76,10 +77,15 @@ for member in message.structure.members: include_prefix = include_prefix[:-8] elif include_prefix.endswith('__feedback'): include_prefix = include_prefix[:-10] + elif include_prefix.endswith('__send_goal'): + include_prefix = include_prefix[:-11] + elif include_prefix.endswith('__get_result'): + include_prefix = include_prefix[:-12] member_includes.add(include_prefix + '.h') }@ @{ # TODO(jacobperron): Remove this logic after https://github.com/ros2/rosidl/pull/432 (Foxy) +# and https://github.com/ros2/rosidl/pull/538 message_c_include_prefix = idl_structure_type_to_c_include_prefix(message.structure.namespaced_type) # Strip off any service or action suffix if message_c_include_prefix.endswith('__request'): @@ -92,6 +98,10 @@ elif message_c_include_prefix.endswith('__result'): message_c_include_prefix = message_c_include_prefix[:-8] elif message_c_include_prefix.endswith('__feedback'): message_c_include_prefix = message_c_include_prefix[:-10] +elif message_c_include_prefix.endswith('__send_goal'): + message_c_include_prefix = message_c_include_prefix[:-11] +elif message_c_include_prefix.endswith('__get_result'): + message_c_include_prefix = message_c_include_prefix[:-12] }@ #include diff --git a/rosidl_generator_java/resource/srv.cpp.em b/rosidl_generator_java/resource/srv.cpp.em index c7bd40a6..7bf6209b 100644 --- a/rosidl_generator_java/resource/srv.cpp.em +++ b/rosidl_generator_java/resource/srv.cpp.em @@ -40,7 +40,20 @@ expand_template( #include "rosidl_generator_c/service_type_support_struct.h" -#include "@(idl_structure_type_to_c_include_prefix(service.namespaced_type)).h" +@{ +include_prefix = idl_structure_type_to_c_include_prefix(service.namespaced_type) +# TODO(jacobperron): Remove this logic after https://github.com/ros2/rosidl/pull/538 +# Strip off any service suffix +# There are a couple service types that actions are composed of, but they are included +# a common header that is based on the action name +# ie. there are not separate headers for each type +if include_prefix.endswith('__send_goal'): + include_prefix = include_prefix[:-11] +elif include_prefix.endswith('__get_result'): + include_prefix = include_prefix[:-12] +}@ + +#include "@(include_prefix).h" // Ensure that a jlong is big enough to store raw pointers static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); From b3c888abc01df2f408b81d14ec8848c9a21e67a5 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Thu, 3 Dec 2020 17:08:46 -0800 Subject: [PATCH 26/33] Update build instructions in README and CI (#119) * Use curl for consistency Signed-off-by: Jacob Perron * Update build badges Currently, we only have CI for Dashing on Linux. Signed-off-by: Jacob Perron * Update URLs Signed-off-by: Jacob Perron * Minor formatting changes Signed-off-by: Jacob Perron * Update build instructions for desktop Signed-off-by: Jacob Perron * Use different header markdown syntax For more granularity in header levels. Signed-off-by: Jacob Perron * Fix whitespace formatting Signed-off-by: Jacob Perron * Formatting Signed-off-by: Jacob Perron * Add ref to ticket for ROS time Signed-off-by: Jacob Perron * 'merge install' for Windows Signed-off-by: Jacob Perron * Fix typo Signed-off-by: Jacob Perron * Update Android instructions with CI (#125) * Update Android instructions This is a work in progress, still troubleshooting build issues. Signed-off-by: Jacob Perron * Update Android repos file Now the repos file contains all packages in the upstream dashing repos file, without the Qt packages: https://github.com/ros2/ros2/blob/dashing/ros2.repos Signed-off-by: Jacob Perron * Added job for Android * Only build the rcljava subset of dependencies * Fix URL * Replace ros-tooling scripts with plain commands * Fix typo * Install vcstool for vcs * Install Android NDK * Add ros2_java workspace to CMake find root path * Disable building tests * Do not build osrf_testing_tools_cpp as it uses execinfo.h, which is not available on Android * Workaround for Android * Reenable building tests * Skip cyclonedds for now * Install lark parser * Install python3-dev * Pass PYTHON_LIBRARY and PYTHON_INCLUDE_DIR to the CMake Python module * Skip rcl_logging_log4cxx * Use rmw fork for Android. Skip CycloneDDS packages * Revert to 1.6 * Configure log4j dynamically to avoid errors when building for Android * Configure log4j dynamically to avoid errors when building for Android * Switch to jacob/instructions temporarily * Fix compilation error * Trim repos file for Android * Readd googletest repo * Fix missing import * Build entire workspace * Only build up to the Android examples * Install colcon extensions for Gradle * Revert changes for desktop * Move ros2_android repo to ros2-java org * Install Gradle * Build rcljava_examples * Install Gradle * Do not build rcljava_examples temporarily * Update README.md * Do not use fork * Use repos files from GitHub workspace Signed-off-by: Jacob Perron * Add checkout action for cloning the branch to test Signed-off-by: Jacob Perron * Use version of ros2_java in the branch being tested Signed-off-by: Jacob Perron * Switch back to dashing branch Signed-off-by: Jacob Perron Co-authored-by: Esteve Fernandez Co-authored-by: Esteve Fernandez --- .github/workflows/build_and_test.yml | 73 +++++- README.md | 224 +++++++++--------- .../java/org/ros2/rcljava/node/NodeImpl.java | 2 - .../org/ros2/rcljava/client/ClientTest.java | 14 +- .../java/org/ros2/rcljava/node/NodeTest.java | 14 +- .../parameters/AsyncParametersClientTest.java | 14 +- .../parameters/SyncParametersClientTest.java | 14 +- ros2_java_android.repos | 102 +++++++- rosidl_generator_java/resource/msg.cpp.em | 10 +- .../org/ros2/generator/InterfacesTest.java | 14 +- 10 files changed, 339 insertions(+), 142 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 40f1cc7b..8f8a1adf 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -8,8 +8,9 @@ jobs: steps: - name: Install Java run: | - sudo apt update -qq - sudo apt install -y default-jdk + sudo apt-get update -qq + sudo apt-get install -y default-jdk gradle + - uses: actions/checkout@v2 - uses: ros-tooling/setup-ros@0.0.14 with: required-ros-distributions: dashing @@ -17,4 +18,70 @@ jobs: with: package-name: rosidl_generator_java rcljava_common rcljava source-ros-binary-installation: dashing - vcs-repo-file-url: https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_desktop.repos + vcs-repo-file-url: ${{ github.workspace }}/ros2_java_desktop.repos + + build_android: + runs-on: ubuntu-18.04 + steps: + - name: Install Java + run: | + sudo apt-get update -qq + sudo apt-get install -y default-jdk gradle + - uses: actions/checkout@v2 + - name: Setup locale for ROS 2 + run: | + sudo locale-gen en_US en_US.UTF-8 + sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 + export LANG=en_US.UTF-8 + - name: Setup sources for ROS 2 + run: | + sudo apt-get update && sudo apt-get install -y curl gnupg2 lsb-release + curl -sL https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add - + sudo sh -c 'echo "deb [arch=$(dpkg --print-architecture)] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/ros2-latest.list' + - name: Install ROS 2 dependencies + run: | + sudo apt-get update && sudo apt-get install -y python3-colcon-common-extensions python3-vcstool python3-lark-parser python3-dev + - name: Install colcon extensions for Gradle + run: | + sudo pip3 install git+git://github.com/colcon/colcon-gradle.git + sudo pip3 install git+git://github.com/colcon/colcon-ros-gradle.git + - name: Install Android NDK + run: | + curl -LO https://dl.google.com/android/repository/android-ndk-r21d-linux-x86_64.zip + unzip android-ndk-r21d-linux-x86_64.zip + - name: Setup workspace with VCS repo file + run: | + mkdir -p ros2_java_ws/src + cd ros2_java_ws + curl -sL file://${{ github.workspace }}/ros2_java_android.repos | vcs import src + # Use checked out version of ros2_java + rm -rf src/ros2_java/ros2_java + ln --symbolic ${{ github.workspace }} src/ros2_java + - name: Build ros2_java for Android + run: | + export PYTHON3_EXEC="$( which python3 )" + export PYTHON3_LIBRARY="$( ${PYTHON3_EXEC} -c 'import os.path; from distutils import sysconfig; print(os.path.realpath(os.path.join(sysconfig.get_config_var("LIBPL"), sysconfig.get_config_var("LDLIBRARY"))))' )" + export PYTHON3_INCLUDE_DIR="$( ${PYTHON3_EXEC} -c 'from distutils import sysconfig; print(sysconfig.get_config_var("INCLUDEPY"))' )" + export ANDROID_ABI=armeabi-v7a + export ANDROID_NATIVE_API_LEVEL=android-21 + export ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang + export ANDROID_NDK=${PWD}/android-ndk-r21d + + cd ros2_java_ws + colcon build \ + --packages-ignore cyclonedds rcl_logging_log4cxx rosidl_generator_py \ + --packages-up-to rcljava \ + --cmake-args \ + -DPYTHON_EXECUTABLE=${PYTHON3_EXEC} \ + -DPYTHON_LIBRARY=${PYTHON3_LIBRARY} \ + -DPYTHON_INCLUDE_DIR=${PYTHON3_INCLUDE_DIR} \ + -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ + -DANDROID_FUNCTION_LEVEL_LINKING=OFF \ + -DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} \ + -DANDROID_TOOLCHAIN_NAME=${ANDROID_TOOLCHAIN_NAME} \ + -DANDROID_STL=c++_shared \ + -DANDROID_ABI=${ANDROID_ABI} \ + -DANDROID_NDK=${ANDROID_NDK} \ + -DTHIRDPARTY=ON \ + -DCOMPILE_EXAMPLES=OFF \ + -DCMAKE_FIND_ROOT_PATH="${PWD}/install" diff --git a/README.md b/README.md index 330d52f9..20787538 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,31 @@ -ROS2 for Java -============= +# ROS 2 Java client library -Build status ------------- +### Build status -| Target | Status | -|----------|--------| -| **Ubuntu Xenial (OpenJDK)** | [![Build Status](http://vsts-matrix-badges.herokuapp.com/repos/ros2-java/ros2-java/1/branches/master/1)](https://dev.azure.com/ros2-java/ros2-java/_build?definitionId=1) | -| **Ubuntu Xenial (Android)** | [![Build Status](http://vsts-matrix-badges.herokuapp.com/repos/ros2-java/ros2-java/1/branches/master/2)](https://dev.azure.com/ros2-java/ros2-java/_build?definitionId=1) | +| Target | Status | +|-------------------------------------------|---------------| +| **ROS Dashing - Ubuntu Bionic (OpenJDK)** | ![Build Status](https://github.com/ros2-java/ros2_java/workflows/CI/badge.svg?branch=dashing) | -Introduction ------------- +## Introduction -This is a set of projects (bindings, code generator, examples and more) that enables developers to write ROS2 +This is a set of projects (bindings, code generator, examples and more) that enables developers to write ROS 2 applications for the JVM and Android. Besides this repository itself, there's also: -- https://github.com/esteve/ament_java, which adds support for Gradle to Ament -- https://github.com/esteve/ament_gradle_plugin, a Gradle plugin that makes it easier to use ROS2 in Java and Android project. The Gradle plugin can be installed from Gradle Central https://plugins.gradle.org/plugin/org.ros2.tools.gradle -- https://github.com/esteve/ros2_java_examples, examples for the Java Runtime Environment -- https://github.com/esteve/ros2_android_examples, examples for Android +- https://github.com/ros2-java/ament_java, which adds support for Gradle to Ament +- https://github.com/ros2-java/ament_gradle_plugin, a Gradle plugin that makes it easier to use ROS 2 in Java and Android project. The Gradle plugin can be installed from Gradle Central https://plugins.gradle.org/plugin/org.ros2.tools.gradle +- https://github.com/ros2-java/ros2_java_examples, examples for the Java Runtime Environment +- https://github.com/ros2-java/ros2_android_examples, examples for Android -Is this Java only? ------------------- +### Is this Java only? -No, any language that targets the JVM can be used to write ROS2 applications. +No, any language that targets the JVM can be used to write ROS 2 applications. -Including Android? ------------------- +### Including Android? Yep! Make sure to use Fast-RTPS as your DDS vendor and at least [this revision](https://github.com/eProsima/Fast-RTPS/commit/5301ef203d45528a083821c3ba582164d782360b). -Features --------- +### Features The current set of features include: - Generation of all builtin and complex ROS types, including arrays, strings, nested types, constants, etc. @@ -41,76 +34,80 @@ The current set of features include: - Clients and services - Timers - Composition (i.e. more than one node per process) -- Time handling (system and steady, ROS time not yet supported https://github.com/ros2/ros2/issues/350) +- Time handling (system and steady, ROS time not yet supported https://github.com/ros2-java/ros2_java/issues/122) - Support for Android - Parameters services and clients (both asynchronous and synchronous) -Sounds great, how can I try this out? -------------------------------------- +## Sounds great, how can I try this out? + +### Install dependencies > Note: While the following instructions use a Linux shell the same can be done on other platforms like Windows with slightly adjusted commands. -> -> For Windows and Mac, first follow the steps for installing prerequisites on the binary installation page: https://github.com/ros2/ros2/wiki/Installation -> -> Stop and return here when you reach the "Downloading ROS 2" section. -Download the ament repositories in a separate workspace: +1. [Install ROS 2](https://index.ros.org/doc/ros2/Installation). + +1. Install Java and a JDK. + + On Ubuntu, you can install OpenJDK with: + + sudo apt install default-jdk + +1. Install Gradle. +Make sure you have Gradle 3.2 (or later) installed. + + *Ubuntu Bionic or later* + + sudo apt install gradle + + *macOS* + + brew install gradle + + Note: if run into compatibily issues between gradle 3.x and Java 9, try using Java 8, + + brew tap caskroom/versions + brew cask install java8 + export JAVA_HOME=/Library/Java/JavaVirtualMachines/1.8.0.jdk/Contents/Home + + *Windows* + + choco install gradle + +1. Install build tools: + + sudo apt install curl python3-colcon-common-extensions python3-pip python3-vcstool -``` -mkdir -p ament_ws/src -cd ament_ws -curl -skL https://raw.githubusercontent.com/esteve/ament_java/master/ament_java.repos -o ament_java.repos -vcs import src < ament_java.repos -src/ament/ament_tools/scripts/ament.py build --symlink-install --isolated -``` +1. Install Gradle extensions for colcon: -> Note: On Windows, use `python src/ament/ament_tools/scripts/ament.py build`, as `*.py` scripts must be prefixed with `python`, `--symlink-install` is not supported due to a bug in Python symlinks, and `--isolated` creates paths that are too long. -> Additionally, you may need to call `call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"` if you have not run from a VS 2017 terminal. + python3 -m pip install -U git+https://github.com/colcon/colcon-gradle + python3 -m pip install --no-deps -U git+https://github.com/colcon/colcon-ros-gradle -We need to split the build process between Ament and the rest of `ros2_java` workspace so that the additional build type for Gradle projects is picked up by Ament. +### Download and Build ROS 2 Java for Desktop -Make sure you have Gradle 3.2 (or later) installed. Ubuntu 16.04 ships with Gradle 2.10, you can install a more recent version of Gradle from the following PPA: +1. Source your ROS 2 installation, for example: -``` -$ sudo add-apt-repository ppa:cwchien/gradle -$ sudo apt install -y gradle -``` -For OSX, use homebrew command to install gradle: + source /opt/ros/dashing/setup.bash -``` -$ brew install gradle -``` +1. Download the ROS 2 Java repositories into a workspace: -For OSX users, there some compatibily issues have been reported between gradle 3.x and Java 9. Currently only the combination of Gradle 3.x + Java 8 has been tested successfully. Make sure to use install and use Java 8 for the building process: + mkdir -p ros2_java_ws/src + cd ros2_java_ws + curl -skL https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_desktop.repos | vcs import src -``` -$ brew tap caskroom/versions -$ brew cask install java8 -$ export JAVA_HOME=/Library/Java/JavaVirtualMachines/1.8.0.jdk/Contents/Home -``` +1. **Linux only** Install ROS dependencies: -> Note: On Windows, you may use `choco install -y gradle` + rosdep install --from-paths src -y -i --skip-keys "ament_tools" -The following sections deal with building the `ros2_java` codebase for the desktop Java runtime and for Android. +1. Build desktop packages: -Desktop -------- + colcon build --symlink-install -``` -mkdir -p ros2_java_ws/src -cd ros2_java_ws -curl -skL https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_desktop.repos -o ros2_java_desktop.repos -vcs import src < ros2_java_desktop.repos -. ../ament_ws/install_isolated/local_setup.sh -ament build --symlink-install --isolated -``` + *Note, on Windows we have to use `--merge-install`* -> On Windows, if you would like to use OpenSplice, call `call "C:\opensplice67\HDE\x86_64.win64\release.bat"` before building. + colcon build --merge-install -Now you can just run a bunch of examples, head over to https://github.com/esteve/ros2_java_examples for more information. -Android -------- +### Download and Build ROS 2 Java for Android The Android setup is slightly more complex, you'll need the SDK and NDK installed, and an Android device where you can run the examples. @@ -122,48 +119,43 @@ We'll also need to have the [Android SDK](https://developer.android.com/studio/# Although the `ros2_java_android.repos` file contains all the repositories for the Android bindings to compile, we'll have to disable certain packages (`python_cmake_module`, `rosidl_generator_py`, `test_msgs`) that are included the repositories and that we either don't need or can't cross-compile properly (e.g. the Python generator) -``` -# define paths -ROOT_DIR = ${HOME} -AMENT_WORKSPACE=${ROOT_DIR}/ament_ws -ROS2_ANDROID_WORKSPACE=${ROOT_DIR}/ros2_android_ws - -# pull and build ament -mkdir -p ${AMENT_WORKSPACE}/src -cd ${AMENT_WORKSPACE} -wget https://raw.githubusercontent.com/esteve/ament_java/master/ament_java.repos -vcs import ${AMENT_WORKSPACE}/src < ament_java.repos -src/ament/ament_tools/scripts/ament.py build --symlink-install --isolated - -# android build configuration -export PYTHON3_EXEC="$( which python3 )" -export ANDROID_ABI=armeabi-v7a -export ANDROID_NATIVE_API_LEVEL=android-21 -export ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang - -# pull and build ros2 for android -mkdir -p ${ROS2_ANDROID_WORKSPACE}/src -cd ${ROS2_ANDROID_WORKSPACE} -wget https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_android.repos -vcs import ${ROS2_ANDROID_WORKSPACE}/src < ros2_java_android.repos -source ${AMENT_WORKSPACE}/install_isolated/local_setup.sh -ament build --isolated --skip-packages test_msgs \ - --cmake-args \ - -DPYTHON_EXECUTABLE=${PYTHON3_EXEC} \ - -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \ - -DANDROID_FUNCTION_LEVEL_LINKING=OFF \ - -DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} \ - -DANDROID_TOOLCHAIN_NAME=${ANDROID_TOOLCHAIN_NAME} \ - -DANDROID_STL=gnustl_shared \ - -DANDROID_ABI=${ANDROID_ABI} \ - -DANDROID_NDK=${ANDROID_NDK} \ - -DTHIRDPARTY=ON \ - -DCOMPILE_EXAMPLES=OFF \ - -DCMAKE_FIND_ROOT_PATH="$AMENT_WORKSPACE/install_isolated;$ROS2_ANDROID_WORKSPACE/install_isolated" \ - -- \ - --parallel \ - --ament-gradle-args \ - -Pament.android_stl=gnustl_shared -Pament.android_abi=$ANDROID_ABI -Pament.android_ndk=$ANDROID_NDK -- -``` - -You can find more information about the Android examples at https://github.com/esteve/ros2_android_examples +1. Download the [Android NDK](https://developer.android.com/ndk/downloads/index.html) and set the environment variable `ANDROID_NDK` to the path where it is extracted. + +1. Download the [Android SDK](https://developer.android.com/studio/#downloads) and set the environment variable `ANDROID_HOME` to the path where it is extracted. + +1. Clone ROS 2 and ROS 2 Java source code: + + mkdir -p $HOME/ros2_android_ws/src + cd $HOME/ros2_android_ws + curl https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_android.repos | vcs import src + +1. Set Android build configuration: + + export PYTHON3_EXEC="$( which python3 )" + export PYTHON3_LIBRARY="$( ${PYTHON3_EXEC} -c 'import os.path; from distutils import sysconfig; print(os.path.realpath(os.path.join(sysconfig.get_config_var("LIBPL"), sysconfig.get_config_var("LDLIBRARY"))))' )" + export PYTHON3_INCLUDE_DIR="$( ${PYTHON3_EXEC} -c 'from distutils import sysconfig; print(sysconfig.get_config_var("INCLUDEPY"))' )" + export ANDROID_ABI=armeabi-v7a + export ANDROID_NATIVE_API_LEVEL=android-21 + export ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang + +1. Build (skipping packages that we don't need or can't cross-compile): + + colcon build \ + --packages-ignore cyclonedds rcl_logging_log4cxx rosidl_generator_py \ + --packages-up-to rcljava \ + --cmake-args \ + -DPYTHON_EXECUTABLE=${PYTHON3_EXEC} \ + -DPYTHON_LIBRARY=${PYTHON3_LIBRARY} \ + -DPYTHON_INCLUDE_DIR=${PYTHON3_INCLUDE_DIR} \ + -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ + -DANDROID_FUNCTION_LEVEL_LINKING=OFF \ + -DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} \ + -DANDROID_TOOLCHAIN_NAME=${ANDROID_TOOLCHAIN_NAME} \ + -DANDROID_STL=c++_shared \ + -DANDROID_ABI=${ANDROID_ABI} \ + -DANDROID_NDK=${ANDROID_NDK} \ + -DTHIRDPARTY=ON \ + -DCOMPILE_EXAMPLES=OFF \ + -DCMAKE_FIND_ROOT_PATH="${PWD}/install" + +You can find more information about the Android examples at https://github.com/ros2-java/ros2_android_examples diff --git a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java index 9fdc1c27..8bf5e69d 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java @@ -47,8 +47,6 @@ import java.lang.ref.WeakReference; -import java.lang.reflect.Method; - import java.util.ArrayList; import java.util.Collection; import java.util.List; diff --git a/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java b/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java index f4724d9b..cf01c02e 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import java.lang.ref.WeakReference; +import java.lang.reflect.Method; import java.time.Duration; import java.util.Arrays; @@ -46,7 +47,18 @@ public class ClientTest { @BeforeClass public static void setupOnce() throws Exception { RCLJava.rclJavaInit(); - org.apache.log4j.BasicConfigurator.configure(); + try + { + // Configure log4j. Doing this dynamically so that Android does not complain about missing + // the log4j JARs, SLF4J uses Android's native logging mechanism instead. + Class c = Class.forName("org.apache.log4j.BasicConfigurator"); + Method m = c.getDeclaredMethod("configure", (Class[]) null); + Object o = m.invoke(null, (Object[]) null); + } + catch (Exception e) + { + e.printStackTrace(); + } } public class TestClientConsumer implements TriConsumer[]) null); + Object o = m.invoke(null, (Object[]) null); + } + catch (Exception e) + { + e.printStackTrace(); + } } public class TestConsumer implements Consumer { diff --git a/rcljava/src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java b/rcljava/src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java index a458c6a2..1c94bce5 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import java.lang.ref.WeakReference; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -69,7 +70,18 @@ public TestConsumer(RCLFuture resultFuture) { @BeforeClass public static void setupOnce() throws Exception { RCLJava.rclJavaInit(); - org.apache.log4j.BasicConfigurator.configure(); + try + { + // Configure log4j. Doing this dynamically so that Android does not complain about missing + // the log4j JARs, SLF4J uses Android's native logging mechanism instead. + Class c = Class.forName("org.apache.log4j.BasicConfigurator"); + Method m = c.getDeclaredMethod("configure", (Class[]) null); + Object o = m.invoke(null, (Object[]) null); + } + catch (Exception e) + { + e.printStackTrace(); + } } @Before diff --git a/rcljava/src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java b/rcljava/src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java index 8ec4266a..197e5cef 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import java.lang.ref.WeakReference; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -52,7 +53,18 @@ public class SyncParametersClientTest { @BeforeClass public static void setupOnce() throws Exception { RCLJava.rclJavaInit(); - org.apache.log4j.BasicConfigurator.configure(); + try + { + // Configure log4j. Doing this dynamically so that Android does not complain about missing + // the log4j JARs, SLF4J uses Android's native logging mechanism instead. + Class c = Class.forName("org.apache.log4j.BasicConfigurator"); + Method m = c.getDeclaredMethod("configure", (Class[]) null); + Object o = m.invoke(null, (Object[]) null); + } + catch (Exception e) + { + e.printStackTrace(); + } } @Before diff --git a/ros2_java_android.repos b/ros2_java_android.repos index f6b76ae2..e1dd0920 100644 --- a/ros2_java_android.repos +++ b/ros2_java_android.repos @@ -1,19 +1,51 @@ repositories: + ament/ament_cmake: + type: git + url: https://github.com/ament/ament_cmake.git + version: dashing + ament/ament_index: + type: git + url: https://github.com/ament/ament_index.git + version: dashing + ament/ament_lint: + type: git + url: https://github.com/ament/ament_lint.git + version: dashing + ament/ament_package: + type: git + url: https://github.com/ament/ament_package.git + version: dashing + ament/googletest: + type: git + url: https://github.com/ament/googletest.git + version: dashing + ament/uncrustify_vendor: + type: git + url: https://github.com/ament/uncrustify_vendor.git + version: dashing + ament/ament_java: + type: git + url: https://github.com/ros2-java/ament_java.git + version: master eProsima/Fast-CDR: type: git url: https://github.com/eProsima/Fast-CDR.git version: v1.0.11 - eProsima/Fast-RTPS: + eProsima/Fast-DDS: type: git - url: https://github.com/eProsima/Fast-RTPS.git + url: https://github.com/eProsima/Fast-DDS.git version: v1.8.2 - ros2/ament_cmake_ros: + osrf/osrf_pycommon: type: git - url: https://github.com/ros2/ament_cmake_ros.git + url: https://github.com/osrf/osrf_pycommon.git version: dashing - ros2/rcutils: + osrf/osrf_testing_tools_cpp: type: git - url: https://github.com/ros2/rcutils.git + url: https://github.com/ros2-java/osrf_testing_tools_cpp.git + version: dashing-android + ros2/ament_cmake_ros: + type: git + url: https://github.com/ros2/ament_cmake_ros.git version: dashing ros2/common_interfaces: type: git @@ -23,9 +55,9 @@ repositories: type: git url: https://github.com/ros2/example_interfaces.git version: dashing - ros2/libyaml_vendor: + ros2/launch: type: git - url: https://github.com/ros2/libyaml_vendor.git + url: https://github.com/ros2/launch.git version: dashing ros2/poco_vendor: type: git @@ -39,10 +71,30 @@ repositories: type: git url: https://github.com/ros2/rcl_interfaces.git version: dashing + ros2/rcl_logging: + type: git + url: https://github.com/ros2/rcl_logging.git + version: dashing + ros2/rcpputils: + type: git + url: https://github.com/ros2/rcpputils.git + version: dashing + ros2/rcutils: + type: git + url: https://github.com/ros2/rcutils.git + version: dashing ros2/rmw: type: git url: https://github.com/ros2/rmw.git version: dashing + ros2/rmw_connext: + type: git + url: https://github.com/ros2/rmw_connext.git + version: dashing + ros2/rmw_cyclonedds: + type: git + url: https://github.com/ros2/rmw_cyclonedds.git + version: dashing-eloquent ros2/rmw_fastrtps: type: git url: https://github.com/ros2/rmw_fastrtps.git @@ -51,6 +103,10 @@ repositories: type: git url: https://github.com/ros2/rmw_implementation.git version: dashing + ros2/rmw_opensplice: + type: git + url: https://github.com/ros2/rmw_opensplice.git + version: dashing ros2/rosidl: type: git url: https://github.com/ros2/rosidl.git @@ -63,13 +119,37 @@ repositories: type: git url: https://github.com/ros2/rosidl_defaults.git version: dashing + ros2/rosidl_python: + type: git + url: https://github.com/ros2/rosidl_python.git + version: dashing ros2/rosidl_typesupport: type: git url: https://github.com/ros2/rosidl_typesupport.git version: dashing - ros2/osrf_testing_tools_cpp: + ros2/rosidl_typesupport_connext: + type: git + url: https://github.com/ros2/rosidl_typesupport_connext.git + version: dashing + ros2/rosidl_typesupport_fastrtps: + type: git + url: https://github.com/ros2/rosidl_typesupport_fastrtps.git + version: dashing + ros2/rosidl_typesupport_opensplice: + type: git + url: https://github.com/ros2/rosidl_typesupport_opensplice.git + version: dashing + ros2/test_interface_files: + type: git + url: https://github.com/ros2/test_interface_files.git + version: dashing + ros2/tinydir_vendor: + type: git + url: https://github.com/ros2/tinydir_vendor.git + version: dashing + ros2/unique_identifier_msgs: type: git - url: https://github.com/osrf/osrf_testing_tools_cpp + url: https://github.com/ros2/unique_identifier_msgs.git version: dashing ros2_java/ros2_java: type: git @@ -77,7 +157,7 @@ repositories: version: dashing ros2_java/ros2_android: type: git - url: https://github.com/esteve/ros2_android.git + url: https://github.com/ros2-java/ros2_android.git version: master ros2_java/ros2_android_examples: type: git diff --git a/rosidl_generator_java/resource/msg.cpp.em b/rosidl_generator_java/resource/msg.cpp.em index fc7e764c..4ee1b0f7 100644 --- a/rosidl_generator_java/resource/msg.cpp.em +++ b/rosidl_generator_java/resource/msg.cpp.em @@ -212,7 +212,7 @@ JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getDestructor JNIEnv * env = nullptr; // TODO(esteve): check return status assert(g_vm != nullptr); - g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8); + g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); assert(env != nullptr); if (ros_message == nullptr) { @@ -341,7 +341,7 @@ jobject @(underscore_separated_type_name)__convert_to_java(@(msg_normalized_type JNIEnv * env = nullptr; // TODO(esteve): check return status assert(g_vm != nullptr); - g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8); + g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); assert(env != nullptr); if (_jmessage_obj == nullptr) { @@ -445,7 +445,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * vm, void *) } JNIEnv * env; - if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8) != JNI_OK) { + if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { return JNI_ERR; } else { @[for normalized_type, jni_type in cache.items()]@ @@ -494,7 +494,7 @@ if value_method: @[ end if]@ @[end for]@ } - return JNI_VERSION_1_8; + return JNI_VERSION_1_6; } JNIEXPORT void JNICALL JNI_OnUnload(JavaVM * vm, void *) @@ -503,7 +503,7 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM * vm, void *) assert(g_vm == vm); JNIEnv * env; - if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8) == JNI_OK) { + if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) == JNI_OK) { @[for normalized_type, jni_type in cache.items()]@ if (_j@(normalized_type)_class_global != nullptr) { env->DeleteGlobalRef(_j@(normalized_type)_class_global); diff --git a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java index 00638e09..d64af3eb 100644 --- a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java +++ b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import java.lang.reflect.Method; import java.util.concurrent.Callable; import java.util.Arrays; import java.util.List; @@ -30,7 +31,18 @@ public class InterfacesTest { @BeforeClass public static void setupOnce() { - org.apache.log4j.BasicConfigurator.configure(); + try + { + // Configure log4j. Doing this dynamically so that Android does not complain about missing + // the log4j JARs, SLF4J uses Android's native logging mechanism instead. + Class c = Class.forName("org.apache.log4j.BasicConfigurator"); + Method m = c.getDeclaredMethod("configure", (Class[]) null); + Object o = m.invoke(null, (Object[]) null); + } + catch (Exception e) + { + e.printStackTrace(); + } } @Rule public ExpectedException thrown = ExpectedException.none(); From 6ebafe1825c084f7573c9ac97107e97c5ca614db Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Mon, 7 Dec 2020 10:45:54 -0800 Subject: [PATCH 27/33] Bump versions of ros-tooling actions in CI (#154) * Bump version of ros-tooling/setup-ros to 0.1.0 Signed-off-by: Jacob Perron * Bump version of ros-tooling/action-ros-ci to 0.1.0 Signed-off-by: Jacob Perron --- .github/workflows/build_and_test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 8f8a1adf..a8629bff 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -11,13 +11,13 @@ jobs: sudo apt-get update -qq sudo apt-get install -y default-jdk gradle - uses: actions/checkout@v2 - - uses: ros-tooling/setup-ros@0.0.14 + - uses: ros-tooling/setup-ros@0.1.0 with: required-ros-distributions: dashing - - uses: ros-tooling/action-ros-ci@8d58122 + - uses: ros-tooling/action-ros-ci@0.1.0 with: package-name: rosidl_generator_java rcljava_common rcljava - source-ros-binary-installation: dashing + target-ros2-distro: dashing vcs-repo-file-url: ${{ github.workspace }}/ros2_java_desktop.repos build_android: From 12e3407ef678e2154e49699ddde0d99aa5199dde Mon Sep 17 00:00:00 2001 From: pluris Date: Wed, 9 Dec 2020 04:29:33 +0900 Subject: [PATCH 28/33] change code order(methodId 'add' in for loop) (#148) --- rosidl_generator_java/resource/msg.cpp.em | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/rosidl_generator_java/resource/msg.cpp.em b/rosidl_generator_java/resource/msg.cpp.em index 4ee1b0f7..681083e6 100644 --- a/rosidl_generator_java/resource/msg.cpp.em +++ b/rosidl_generator_java/resource/msg.cpp.em @@ -355,6 +355,8 @@ normalized_type = get_normalized_type(member.type) @[ if isinstance(member.type.value_type, (BasicType, AbstractGenericString))]@ auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "L@(list_jni_type);"); jobject _jarray_list_@(member.name)_obj = env->NewObject(_j@(array_list_normalized_type)_class_global, _j@(array_list_normalized_type)_constructor_global); + // TODO(esteve): replace ArrayList with a jobjectArray to initialize the array beforehand + jmethodID _jlist_@(member.name)_add_mid = env->GetMethodID(_j@(array_list_normalized_type)_class_global, "add", "(Ljava/lang/Object;)Z"); @[ if isinstance(member.type, Array)]@ for (size_t i = 0; i < @(member.type.size); ++i) { auto _ros_@(member.name)_element = _ros_message->@(member.name)[i]; @@ -375,9 +377,6 @@ normalized_type = get_normalized_type(member.type) jobject _jlist_@(member.name)_element = env->NewObject( _j@(normalized_type)_class_global, _j@(normalized_type)_constructor_global, _ros_@(member.name)_element); @[ end if]@ - // TODO(esteve): replace ArrayList with a jobjectArray to initialize the array beforehand - jmethodID _jlist_@(member.name)_add_mid = env->GetMethodID( - _j@(array_list_normalized_type)_class_global, "add", "(Ljava/lang/Object;)Z"); if (_jlist_@(member.name)_element != nullptr) { jboolean _jlist_@(member.name)_add_result = env->CallBooleanMethod(_jarray_list_@(member.name)_obj, _jlist_@(member.name)_add_mid, _jlist_@(member.name)_element); assert(_jlist_@(member.name)_add_result); @@ -387,6 +386,8 @@ normalized_type = get_normalized_type(member.type) auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "L@(list_jni_type);"); jobject _jarray_list_@(member.name)_obj = env->NewObject(_j@(array_list_normalized_type)_class_global, _j@(array_list_normalized_type)_constructor_global); + // TODO(esteve): replace ArrayList with a jobjectArray to initialize the array beforehand + jmethodID _jlist_@(member.name)_add_mid = env->GetMethodID(_j@(array_list_normalized_type)_class_global, "add", "(Ljava/lang/Object;)Z"); @[ if isinstance(member.type, Array)]@ for (size_t i = 0; i < @(member.type.size); ++i) { @@ -395,8 +396,6 @@ normalized_type = get_normalized_type(member.type) for (size_t i = 0; i < _ros_message->@(member.name).size; ++i) { jobject _jlist_@(member.name)_element = _j@(normalized_type)_to_java_function(&(_ros_message->@(member.name).data[i]), nullptr); @[ end if]@ - // TODO(esteve): replace ArrayList with a jobjectArray to initialize the array beforehand - jmethodID _jlist_@(member.name)_add_mid = env->GetMethodID(_j@(array_list_normalized_type)_class_global, "add", "(Ljava/lang/Object;)Z"); if (_jlist_@(member.name)_element != nullptr) { jboolean _jlist_@(member.name)_add_result = env->CallBooleanMethod(_jarray_list_@(member.name)_obj, _jlist_@(member.name)_add_mid, _jlist_@(member.name)_element); assert(_jlist_@(member.name)_add_result); From 120be3b8047132ee4b9631cbc28fffc38a9ce9e4 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Mon, 14 Dec 2020 11:58:07 -0800 Subject: [PATCH 29/33] Remove dependency on Connext (#161) * Remove test dependency on rosidl_typesupport_connext_c Signed-off-by: Jacob Perron * Remove Connext packages from Android repos file Signed-off-by: Jacob Perron --- ros2_java_android.repos | 8 -------- rosidl_generator_java/package.xml | 1 - 2 files changed, 9 deletions(-) diff --git a/ros2_java_android.repos b/ros2_java_android.repos index e1dd0920..af23ec53 100644 --- a/ros2_java_android.repos +++ b/ros2_java_android.repos @@ -87,10 +87,6 @@ repositories: type: git url: https://github.com/ros2/rmw.git version: dashing - ros2/rmw_connext: - type: git - url: https://github.com/ros2/rmw_connext.git - version: dashing ros2/rmw_cyclonedds: type: git url: https://github.com/ros2/rmw_cyclonedds.git @@ -127,10 +123,6 @@ repositories: type: git url: https://github.com/ros2/rosidl_typesupport.git version: dashing - ros2/rosidl_typesupport_connext: - type: git - url: https://github.com/ros2/rosidl_typesupport_connext.git - version: dashing ros2/rosidl_typesupport_fastrtps: type: git url: https://github.com/ros2/rosidl_typesupport_fastrtps.git diff --git a/rosidl_generator_java/package.xml b/rosidl_generator_java/package.xml index deffa059..ebf566eb 100644 --- a/rosidl_generator_java/package.xml +++ b/rosidl_generator_java/package.xml @@ -39,7 +39,6 @@ rosidl_parser rosidl_typesupport_c - rosidl_typesupport_connext_c rosidl_typesupport_fastrtps_c rosidl_typesupport_introspection_c rosidl_typesupport_opensplice_c From fdfe1134c4a9d69789a543edf2ce85a9f6c4d372 Mon Sep 17 00:00:00 2001 From: Esteve Fernandez Date: Mon, 14 Dec 2020 21:08:52 +0100 Subject: [PATCH 30/33] Remove dependency on OpenSplice (#163) --- ros2_java_android.repos | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ros2_java_android.repos b/ros2_java_android.repos index af23ec53..3967832d 100644 --- a/ros2_java_android.repos +++ b/ros2_java_android.repos @@ -99,10 +99,6 @@ repositories: type: git url: https://github.com/ros2/rmw_implementation.git version: dashing - ros2/rmw_opensplice: - type: git - url: https://github.com/ros2/rmw_opensplice.git - version: dashing ros2/rosidl: type: git url: https://github.com/ros2/rosidl.git @@ -127,10 +123,6 @@ repositories: type: git url: https://github.com/ros2/rosidl_typesupport_fastrtps.git version: dashing - ros2/rosidl_typesupport_opensplice: - type: git - url: https://github.com/ros2/rosidl_typesupport_opensplice.git - version: dashing ros2/test_interface_files: type: git url: https://github.com/ros2/test_interface_files.git From 29539eb9fb4fb92bdfd046cc0e3e8828ba8975f6 Mon Sep 17 00:00:00 2001 From: Esteve Fernandez Date: Fri, 18 Dec 2020 07:26:55 +0100 Subject: [PATCH 31/33] Removed rmw_cyclonedds repository --- ros2_java_android.repos | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ros2_java_android.repos b/ros2_java_android.repos index 3967832d..5d285cab 100644 --- a/ros2_java_android.repos +++ b/ros2_java_android.repos @@ -87,10 +87,6 @@ repositories: type: git url: https://github.com/ros2/rmw.git version: dashing - ros2/rmw_cyclonedds: - type: git - url: https://github.com/ros2/rmw_cyclonedds.git - version: dashing-eloquent ros2/rmw_fastrtps: type: git url: https://github.com/ros2/rmw_fastrtps.git From c259468ff826748d73264c0e0c4ebf09c1f28211 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Fri, 8 Jan 2021 15:45:44 -0800 Subject: [PATCH 32/33] Avoid unnecessary string copies (#171) Signed-off-by: Jacob Perron --- .../src/main/cpp/org_ros2_rcljava_RCLJava.cpp | 14 +++----- .../cpp/org_ros2_rcljava_node_NodeImpl.cpp | 36 +++++++------------ 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp index d4251ee6..0f2ef0d4 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp @@ -48,13 +48,8 @@ JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_RCLJava_nativeCreateNodeHandle( JNIEnv * env, jclass, jstring jnode_name, jstring jnamespace, jlong context_handle) { - const char * node_name_tmp = env->GetStringUTFChars(jnode_name, 0); - std::string node_name(node_name_tmp); - env->ReleaseStringUTFChars(jnode_name, node_name_tmp); - - const char * namespace_tmp = env->GetStringUTFChars(jnamespace, 0); - std::string namespace_(namespace_tmp); - env->ReleaseStringUTFChars(jnamespace, namespace_tmp); + const char * node_name = env->GetStringUTFChars(jnode_name, 0); + const char * node_namespace = env->GetStringUTFChars(jnamespace, 0); rcl_context_t * context = reinterpret_cast(context_handle); @@ -62,8 +57,9 @@ Java_org_ros2_rcljava_RCLJava_nativeCreateNodeHandle( *node = rcl_get_zero_initialized_node(); rcl_node_options_t default_options = rcl_node_get_default_options(); - rcl_ret_t ret = rcl_node_init( - node, node_name.c_str(), namespace_.c_str(), context, &default_options); + rcl_ret_t ret = rcl_node_init(node, node_name, node_namespace, context, &default_options); + env->ReleaseStringUTFChars(jnode_name, node_name); + env->ReleaseStringUTFChars(jnamespace, node_namespace); if (ret != RCL_RET_OK) { std::string msg = "Failed to create node: " + std::string(rcl_get_error_string().str); rcl_reset_error(); diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp index 8df9b5b9..618c8c03 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp @@ -39,11 +39,7 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreatePublisherHandle( jmethodID mid = env->GetStaticMethodID(jmessage_class, "getTypeSupport", "()J"); jlong jts = env->CallStaticLongMethod(jmessage_class, mid); - const char * topic_tmp = env->GetStringUTFChars(jtopic, 0); - - std::string topic(topic_tmp); - - env->ReleaseStringUTFChars(jtopic, topic_tmp); + const char * topic = env->GetStringUTFChars(jtopic, 0); rcl_node_t * node = reinterpret_cast(node_handle); @@ -56,7 +52,8 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreatePublisherHandle( rmw_qos_profile_t * qos_profile = reinterpret_cast(qos_profile_handle); publisher_ops.qos = *qos_profile; - rcl_ret_t ret = rcl_publisher_init(publisher, node, ts, topic.c_str(), &publisher_ops); + rcl_ret_t ret = rcl_publisher_init(publisher, node, ts, topic, &publisher_ops); + env->ReleaseStringUTFChars(jtopic, topic); if (ret != RCL_RET_OK) { std::string msg = "Failed to create publisher: " + std::string(rcl_get_error_string().str); @@ -77,11 +74,7 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreateSubscriptionHandle( jmethodID mid = env->GetStaticMethodID(jmessage_class, "getTypeSupport", "()J"); jlong jts = env->CallStaticLongMethod(jmessage_class, mid); - const char * topic_tmp = env->GetStringUTFChars(jtopic, 0); - - std::string topic(topic_tmp); - - env->ReleaseStringUTFChars(jtopic, topic_tmp); + const char * topic = env->GetStringUTFChars(jtopic, 0); rcl_node_t * node = reinterpret_cast(node_handle); @@ -95,7 +88,8 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreateSubscriptionHandle( rmw_qos_profile_t * qos_profile = reinterpret_cast(qos_profile_handle); subscription_ops.qos = *qos_profile; - rcl_ret_t ret = rcl_subscription_init(subscription, node, ts, topic.c_str(), &subscription_ops); + rcl_ret_t ret = rcl_subscription_init(subscription, node, ts, topic, &subscription_ops); + env->ReleaseStringUTFChars(jtopic, topic); if (ret != RCL_RET_OK) { std::string msg = "Failed to create subscription: " + std::string(rcl_get_error_string().str); @@ -121,11 +115,7 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreateServiceHandle( assert(jts != 0); - const char * service_name_tmp = env->GetStringUTFChars(jservice_name, 0); - - std::string service_name(service_name_tmp); - - env->ReleaseStringUTFChars(jservice_name, service_name_tmp); + const char * service_name = env->GetStringUTFChars(jservice_name, 0); rcl_node_t * node = reinterpret_cast(node_handle); @@ -138,7 +128,8 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreateServiceHandle( rmw_qos_profile_t * qos_profile = reinterpret_cast(qos_profile_handle); service_ops.qos = *qos_profile; - rcl_ret_t ret = rcl_service_init(service, node, ts, service_name.c_str(), &service_ops); + rcl_ret_t ret = rcl_service_init(service, node, ts, service_name, &service_ops); + env->ReleaseStringUTFChars(jservice_name, service_name); if (ret != RCL_RET_OK) { std::string msg = "Failed to create service: " + std::string(rcl_get_error_string().str); @@ -164,11 +155,7 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreateClientHandle( assert(jts != 0); - const char * service_name_tmp = env->GetStringUTFChars(jservice_name, 0); - - std::string service_name(service_name_tmp); - - env->ReleaseStringUTFChars(jservice_name, service_name_tmp); + const char * service_name = env->GetStringUTFChars(jservice_name, 0); rcl_node_t * node = reinterpret_cast(node_handle); @@ -181,7 +168,8 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreateClientHandle( rmw_qos_profile_t * qos_profile = reinterpret_cast(qos_profile_handle); client_ops.qos = *qos_profile; - rcl_ret_t ret = rcl_client_init(client, node, ts, service_name.c_str(), &client_ops); + rcl_ret_t ret = rcl_client_init(client, node, ts, service_name, &client_ops); + env->ReleaseStringUTFChars(jservice_name, service_name); if (ret != RCL_RET_OK) { std::string msg = "Failed to create client: " + std::string(rcl_get_error_string().str); From 3ebac767c5f67c99ed3c233fc581bc24991e48e8 Mon Sep 17 00:00:00 2001 From: Jacob Perron Date: Mon, 11 Jan 2021 11:33:17 -0800 Subject: [PATCH 33/33] Simplify access to service callback (#172) This also works around the problem of assigning to an 'incompatible type', but looks much cleaner. Signed-off-by: Jacob Perron --- .../src/main/java/org/ros2/rcljava/service/ServiceImpl.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/service/ServiceImpl.java b/rcljava/src/main/java/org/ros2/rcljava/service/ServiceImpl.java index e45d06ec..8d7a3254 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/service/ServiceImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/service/ServiceImpl.java @@ -100,9 +100,7 @@ public final long getHandle() { public void executeCallback( RMWRequestId rmwRequestId, MessageDefinition request, MessageDefinition response) { - TriConsumer callback = - ((ServiceImpl) this).callback; - + TriConsumer callback = this.callback; callback.accept(rmwRequestId, request, response); }