diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml new file mode 100644 index 00000000..a8629bff --- /dev/null +++ b/.github/workflows/build_and_test.yml @@ -0,0 +1,87 @@ +name: CI + +on: [push, pull_request] + +jobs: + build_and_test: + 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 + - uses: ros-tooling/setup-ros@0.1.0 + with: + required-ros-distributions: dashing + - uses: ros-tooling/action-ros-ci@0.1.0 + with: + package-name: rosidl_generator_java rcljava_common rcljava + target-ros2-distro: dashing + 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 99ed10c8..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/esteve/ros2_java/master/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/esteve/ros2_java/master/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/CMakeLists.txt b/rcljava/CMakeLists.txt index 2e6005fb..694a2beb 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 ) 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..c59635e0 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,18 @@ 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); + +/* + * 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 } 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/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 diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp index 3604b22c..0f2ef0d4 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" @@ -35,37 +35,33 @@ 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); - env->ReleaseStringUTFChars(jnode_name, node_name_tmp); + const char * node_name = env->GetStringUTFChars(jnode_name, 0); + const char * node_namespace = env->GetStringUTFChars(jnamespace, 0); - const char * namespace_tmp = env->GetStringUTFChars(jnamespace, 0); - 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, 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_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 +78,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 +89,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..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" @@ -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..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,13 +19,14 @@ #include #include "rcl/error_handling.h" +#include "rcl/graph.h" #include "rcl/node.h" #include "rcl/rcl.h" #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" @@ -60,7 +61,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,8 +92,34 @@ 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); } } + +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/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp new file mode 100644 index 00000000..8f70afad --- /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.hpp" +#include "rcljava_common/signatures.hpp" + +#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..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" @@ -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..618c8c03 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" @@ -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,10 +52,11 @@ 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_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; @@ -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,10 +88,11 @@ 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_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; @@ -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,10 +128,11 @@ 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_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; @@ -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,10 +168,11 @@ 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_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 +195,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 +203,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..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" @@ -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..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" @@ -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..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" @@ -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..3a4638b2 --- /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.hpp" +#include "rcljava_common/signatures.hpp" + +#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..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" @@ -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..75e2148d 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. */ @@ -67,32 +80,26 @@ 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 { + 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 +110,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 +187,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 +217,7 @@ public static boolean ok() { * structure. */ public static Node createNode(final String nodeName) { - return createNode(nodeName, ""); + return createNode(nodeName, "", RCLJava.getDefaultContext()); } /** @@ -197,9 +228,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 +286,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/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..076b99cc 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; @@ -127,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; } @@ -139,6 +144,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/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..2233a5d0 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()); @@ -337,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(); } } @@ -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/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 96797b97..8bf5e69d 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java @@ -22,7 +22,9 @@ 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.Disposable; import org.ros2.rcljava.interfaces.MessageDefinition; import org.ros2.rcljava.interfaces.ServiceDefinition; import org.ros2.rcljava.parameters.ParameterType; @@ -34,6 +36,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; @@ -43,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; @@ -75,6 +77,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 +124,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(); @@ -206,6 +220,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} */ @@ -285,6 +313,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} */ @@ -300,10 +342,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; } @@ -315,12 +373,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/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..8d7a3254 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; } @@ -99,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); } 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/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/client/ClientTest.java b/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java index dd41e228..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,9 +26,13 @@ import org.junit.Test; import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +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; @@ -43,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 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,15 +111,28 @@ 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(1, node.getClients().size()); + assertEquals(1, node.getServices().size()); - assertEquals(5, future.get().getSum()); + // 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/node/NodeTest.java b/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java index 7c6a2010..2287b9de 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import java.lang.ref.WeakReference; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; @@ -47,7 +48,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 +62,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; @@ -86,7 +87,18 @@ boolean checkPrimitives(rcljava.msg.Primitives primitives, boolean booleanValue, @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 TestConsumer implements Consumer { @@ -112,7 +124,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 +154,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 +287,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 +449,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 +715,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}); @@ -837,7 +849,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(); } 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/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java b/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java index f06cc0fb..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,5 +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 c7fbbf5d..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,5 +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(); } } 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(); } } 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/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/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/ros2_java_android.repos b/ros2_java_android.repos index 35a3475b..5d285cab 100644 --- a/ros2_java_android.repos +++ b/ros2_java_android.repos @@ -1,81 +1,145 @@ 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.7 - eProsima/Fast-RTPS: + version: v1.0.11 + eProsima/Fast-DDS: + type: git + url: https://github.com/eProsima/Fast-DDS.git + version: v1.8.2 + osrf/osrf_pycommon: + type: git + url: https://github.com/osrf/osrf_pycommon.git + version: dashing + osrf/osrf_testing_tools_cpp: type: git - url: https://github.com/eProsima/Fast-RTPS.git - version: 7a0b0fe7ca8d2c4ea41e36744c6024c263a6505a + 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: 0.5.0 - 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 - ros2/libyaml_vendor: + version: dashing + ros2/launch: type: git - url: https://github.com/ros2/libyaml_vendor.git - version: 1.0.0 + url: https://github.com/ros2/launch.git + 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: master + 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: 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_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: 0.5.0 + version: dashing + ros2/rosidl_typesupport_fastrtps: + type: git + url: https://github.com/ros2/rosidl_typesupport_fastrtps.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/ros2/unique_identifier_msgs.git + 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 + url: https://github.com/ros2-java/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 diff --git a/ros2_java_desktop.repos b/ros2_java_desktop.repos index 9cedbc35..78ef6a0e 100644 --- a/ros2_java_desktop.repos +++ b/ros2_java_desktop.repos @@ -1,113 +1,33 @@ 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 + version: dashing 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 + version: dashing ros2/rcl_interfaces: type: git url: https://github.com/ros2/rcl_interfaces.git - version: master - 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: + 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.git + 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 diff --git a/rosidl_generator_java/CMakeLists.txt b/rosidl_generator_java/CMakeLists.txt index 62705679..bd4c94b4 100644 --- a/rosidl_generator_java/CMakeLists.txt +++ b/rosidl_generator_java/CMakeLists.txt @@ -24,44 +24,25 @@ 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(test_interface_files REQUIRED) - find_package(ament_lint_auto 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() - 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) + # 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( @@ -70,7 +51,10 @@ 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_MSG_FILES} + ${test_interface_files_SRV_FILES} SKIP_INSTALL ) @@ -89,6 +73,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/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..31dc1a59 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,74 @@ 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}") + 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() - 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" + "${_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_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}") + 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() - 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 +127,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 +147,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 +156,6 @@ rosidl_write_generator_arguments( file(MAKE_DIRECTORY "${_output_path}") -set(_generated_extension_files "") set(_extension_dependencies "") set(_target_suffix "__java") @@ -156,10 +183,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 +197,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 +219,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 +267,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 +293,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 +301,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/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/package.xml b/rosidl_generator_java/package.xml index 9d47a6c7..ebf566eb 100644 --- a/rosidl_generator_java/package.xml +++ b/rosidl_generator_java/package.xml @@ -35,15 +35,17 @@ 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..b093b313 --- /dev/null +++ b/rosidl_generator_java/resource/action.cpp.em @@ -0,0 +1,125 @@ +@# 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 + +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) + +data = { + 'package_name': package_name, + '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( + 'srv.cpp.em', + data, + output_file) + +# 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( + 'srv.cpp.em', + data, + output_file) +}@ + +#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 + +@{ +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..5e7ebbe9 --- /dev/null +++ b/rosidl_generator_java/resource/action.java.em @@ -0,0 +1,97 @@ +@# 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 +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, + '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) + +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', + '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..c909006a --- /dev/null +++ b/rosidl_generator_java/resource/idl.cpp.em @@ -0,0 +1,98 @@ +// 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) +@# - output_dir (Path) +@# - template_basepath (Path) +@# - typesupport_impl (string, the typesupport identifier of the generated code) +@####################################################################### +@{ +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 + +}@ +@ +@####################################################################### +@# Handle messages +@####################################################################### +@{ +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) +}@ +@ +@####################################################################### +@# Handle services +@####################################################################### +@{ +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) + +}@ +@ +@####################################################################### +@# Handle actions +@####################################################################### +@{ +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) +}@ 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..681083e6 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,101 @@ 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() +member_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 + type_ = type_.value_type + if isinstance(type_, BasicType): + 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): + member_includes.add('rosidl_generator_c/string.h') + member_includes.add('rosidl_generator_c/string_functions.h') + + if isinstance(type_, AbstractWString): + 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_)) + 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 + # 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] + 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'): + 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] +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] +}@ - if not field.type.type == 'string': - cache[get_normalized_type(field.type)] = get_jni_type(field.type) +#include - 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)) -}@ +#include +#include +#include + +#include "rosidl_generator_c/message_type_support_struct.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" +#include "rcljava_common/exceptions.hpp" +#include "rcljava_common/signatures.hpp" + +@[for include in member_includes]@ +#include "@(include)" @[end for]@ + +#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" { #endif @@ -126,15 +137,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,51 +155,58 @@ 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; @@ -200,109 +218,124 @@ JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_g 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)); } -@[ else]@ +@[ 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)); + } +@[ 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; @@ -314,85 +347,90 @@ jobject @(spec.base_type.pkg_name)_@(type_name)__convert_to_java(@(msg_normalize 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); + // 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]; +@[ 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]@ - // 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); +@[ else]@ + jobject _jlist_@(member.name)_element = env->NewObject( + _j@(normalized_type)_class_global, _j@(normalized_type)_constructor_global, _ros_@(member.name)_element); +@[ end if]@ + 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); - -@[ 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]@ - // 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); + 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) { + 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]@ + 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; @@ -415,20 +453,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,7 +490,7 @@ 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; @@ -469,13 +507,13 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM * vm, void *) 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 +521,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..7bf6209b 100644 --- a/rosidl_generator_java/resource/srv.cpp.em +++ b/rosidl_generator_java/resource/srv.cpp.em @@ -1,29 +1,88 @@ -// 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 +@{ +import os +from rosidl_cmake import expand_template +from rosidl_generator_c import idl_structure_type_to_c_include_prefix + +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) +}@ #include +#include + #include "rosidl_generator_c/service_type_support_struct.h" -#include "@(spec.pkg_name)/@(subfolder)/@(module_name).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"); #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..32a8e366 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,177 @@ 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.cpp', + } + additional_context.update(typesupport_impl=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 '_'.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 03a5011d..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 @@ -17,8 +17,10 @@ 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.lang.reflect.Method; +import java.util.concurrent.Callable; import java.util.Arrays; import java.util.List; import org.junit.BeforeClass; @@ -29,66 +31,34 @@ 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(); - @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() { - char expected1 = 'a'; - rosidl_generator_java.msg.Char charOne = new rosidl_generator_java.msg.Char(); - charOne.setEmptyChar(expected1); - assertEquals(expected1, charOne.getEmptyChar()); - - char 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 +68,549 @@ 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()); - } - - @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()); + 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 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); - } - - @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})); + strings.setBoundedStringValue(chars23String); } @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 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 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()); + 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 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})); + 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()); } @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 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()); } }