diff --git a/rcljava/CMakeLists.txt b/rcljava/CMakeLists.txt index 6e30e56d..055c5ca1 100644 --- a/rcljava/CMakeLists.txt +++ b/rcljava/CMakeLists.txt @@ -32,7 +32,7 @@ macro(target) get_rcl_information("${rmw_implementation}" "rcl${target_suffix}") endif() - set(_jni_classes "RCLJava;Node;Publisher") + set(_jni_classes "RCLJava;Node;Publisher;Client") foreach(_jni_class ${_jni_classes}) @@ -80,11 +80,17 @@ macro(target) endmacro() set(${PROJECT_NAME}_sources + "src/main/java/org/ros2/rcljava/BiConsumer.java" + "src/main/java/org/ros2/rcljava/Client.java" "src/main/java/org/ros2/rcljava/Consumer.java" "src/main/java/org/ros2/rcljava/Node.java" "src/main/java/org/ros2/rcljava/Publisher.java" + "src/main/java/org/ros2/rcljava/RCLFuture.java" "src/main/java/org/ros2/rcljava/RCLJava.java" + "src/main/java/org/ros2/rcljava/RMWRequestId.java" + "src/main/java/org/ros2/rcljava/Service.java" "src/main/java/org/ros2/rcljava/Subscription.java" + "src/main/java/org/ros2/rcljava/TriConsumer.java" ) add_jar("${PROJECT_NAME}_jar" diff --git a/rcljava/include/org_ros2_rcljava_Client.h b/rcljava/include/org_ros2_rcljava_Client.h new file mode 100644 index 00000000..49106b6c --- /dev/null +++ b/rcljava/include/org_ros2_rcljava_Client.h @@ -0,0 +1,36 @@ +// Copyright 2016 Esteve Fernandez +// +// 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. + +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_ros2_rcljava_Client */ + +#ifndef ORG_ROS2_RCLJAVA_CLIENT_H_ +#define ORG_ROS2_RCLJAVA_CLIENT_H_ + +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_ros2_rcljava_Client + * Method: nativeSendClientRequest + * Signature: (JJJJLjava/lang/Object;)V + */ +JNIEXPORT void JNICALL Java_org_ros2_rcljava_Client_nativeSendClientRequest + (JNIEnv *, jclass, jlong, jlong, jlong, jlong, jobject); +#ifdef __cplusplus +} +#endif + +#endif // ORG_ROS2_RCLJAVA_CLIENT_H_ diff --git a/rcljava/include/org_ros2_rcljava_Node.h b/rcljava/include/org_ros2_rcljava_Node.h index e094e82d..9b8800b2 100644 --- a/rcljava/include/org_ros2_rcljava_Node.h +++ b/rcljava/include/org_ros2_rcljava_Node.h @@ -37,6 +37,22 @@ JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_Node_nativeCreatePublisherHandle JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_Node_nativeCreateSubscriptionHandle (JNIEnv *, jclass, jlong, jclass, jstring); +/* + * Class: org_ros2_rcljava_Node + * Method: nativeCreateServiceHandle + * Signature: (JLjava/lang/Class;Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_Node_nativeCreateServiceHandle + (JNIEnv *, jclass, jlong, jclass, jstring); + +/* + * Class: org_ros2_rcljava_Node + * Method: nativeCreateClientHandle + * Signature: (JLjava/lang/Class;Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_Node_nativeCreateClientHandle + (JNIEnv *, jclass, jlong, jclass, jstring); + #ifdef __cplusplus } #endif diff --git a/rcljava/include/org_ros2_rcljava_RCLJava.h b/rcljava/include/org_ros2_rcljava_RCLJava.h index 84bbd726..ea2bcf92 100644 --- a/rcljava/include/org_ros2_rcljava_RCLJava.h +++ b/rcljava/include/org_ros2_rcljava_RCLJava.h @@ -18,6 +18,7 @@ #ifndef ORG_ROS2_RCLJAVA_RCLJAVA_H_ #define ORG_ROS2_RCLJAVA_RCLJAVA_H_ + #ifdef __cplusplus extern "C" { #endif @@ -53,6 +54,14 @@ JNIEXPORT jstring JNICALL Java_org_ros2_rcljava_RCLJava_nativeGetRMWIdentifier 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); + /* * Class: org_ros2_rcljava_RCLJava * Method: nativeGetZeroInitializedWaitSet @@ -64,10 +73,10 @@ JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_RCLJava_nativeGetZeroInitializedWa /* * Class: org_ros2_rcljava_RCLJava * Method: nativeWaitSetInit - * Signature: (JIII)V + * Signature: (JIIIII)V */ JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeWaitSetInit - (JNIEnv *, jclass, jlong, jint, jint, jint); + (JNIEnv *, jclass, jlong, jint, jint, jint, jint, jint); /* * Class: org_ros2_rcljava_RCLJava @@ -103,13 +112,70 @@ JNIEXPORT jobject JNICALL Java_org_ros2_rcljava_RCLJava_nativeTake /* * Class: org_ros2_rcljava_RCLJava - * Method: nativeShutdown - * Signature: ()V + * Method: nativeWaitSetClearServices + * Signature: (J)V */ -JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeShutdown - (JNIEnv *, jclass); +JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeWaitSetClearServices + (JNIEnv *, jclass, jlong); + +/* + * Class: org_ros2_rcljava_RCLJava + * Method: nativeWaitSetAddService + * Signature: (JJ)V + */ +JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeWaitSetAddService + (JNIEnv *, jclass, jlong, jlong); + +/* + * Class: org_ros2_rcljava_RCLJava + * Method: nativeWaitSetClearClients + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeWaitSetClearClients + (JNIEnv *, jclass, jlong); + +/* + * Class: org_ros2_rcljava_RCLJava + * Method: nativeWaitSetAddClient + * Signature: (JJ)V + */ +JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeWaitSetAddClient + (JNIEnv *, jclass, jlong, jlong); + +/* + * Class: org_ros2_rcljava_RCLJava + * Method: nativeTakeRequest + * Signature: (JJJLjava/lang/Object;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_org_ros2_rcljava_RCLJava_nativeTakeRequest + (JNIEnv *, jclass, jlong, jlong, jlong, jobject); + +/* + * Class: org_ros2_rcljava_RCLJava + * Method: nativeSendServiceResponse + * Signature: (JLjava/lang/Object;JJLjava/lang/Object;)V + */ +JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeSendServiceResponse + (JNIEnv *, jclass, jlong, jobject, jlong, jlong, jobject); + +/* + * Class: org_ros2_rcljava_RCLJava + * Method: nativeSendClientRequest + * Signature: (JJLjava/lang/Object;JJ)V + */ +JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeSendClientRequest + (JNIEnv *, jclass, jlong, jlong, jlong, jlong, jobject); + +/* + * Class: org_ros2_rcljava_RCLJava + * Method: nativeTakeResponse + * Signature: (JJJLjava/lang/Object;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_org_ros2_rcljava_RCLJava_nativeTakeResponse + (JNIEnv *, jclass, jlong, jlong, jlong, jobject); #ifdef __cplusplus } #endif + #endif // ORG_ROS2_RCLJAVA_RCLJAVA_H_ diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_Client.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_Client.cpp new file mode 100644 index 00000000..0cb57130 --- /dev/null +++ b/rcljava/src/main/cpp/org_ros2_rcljava_Client.cpp @@ -0,0 +1,55 @@ +// Copyright 2016 Esteve Fernandez +// +// 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 + +#include "rmw/rmw.h" +#include "rcl/error_handling.h" +#include "rcl/rcl.h" +#include "rcl/node.h" +#include "rosidl_generator_c/message_type_support.h" + +#include "rcljava_common/exceptions.h" +#include "rcljava_common/signatures.h" + +#include "org_ros2_rcljava_Client.h" + +JNIEXPORT void JNICALL Java_org_ros2_rcljava_Client_nativeSendClientRequest(JNIEnv * env, jclass, + jlong client_handle, jlong sequence_number, jlong jrequest_from_java_converter_handle, + jlong jrequest_to_java_converter_handle, jobject jrequest_msg) +{ + assert(client_handle != 0); + assert(jrequest_from_java_converter_handle != 0); + assert(jrequest_to_java_converter_handle != 0); + assert(jrequest_msg != nullptr); + + rcl_client_t * client = reinterpret_cast(client_handle); + + convert_from_java_signature convert_from_java = + reinterpret_cast(jrequest_from_java_converter_handle); + + void * request_msg = convert_from_java(jrequest_msg, nullptr); + + rcl_ret_t ret = rcl_send_request(client, request_msg, &sequence_number); + + if (ret != RCL_RET_OK) { + rcljava_throw_exception( + env, "java/lang/IllegalStateException", + "Failed to send request from a client: " + std::string(rcl_get_error_string_safe())); + } +} diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_Node.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_Node.cpp index 2b441625..05f33927 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_Node.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_Node.cpp @@ -102,3 +102,81 @@ JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_Node_nativeCreateSubscriptionHandl jlong jsubscription = reinterpret_cast(subscription); return jsubscription; } + +JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_Node_nativeCreateServiceHandle(JNIEnv * env, jclass, + jlong node_handle, jclass jservice_class, jstring jservice_name) +{ + jmethodID mid = env->GetStaticMethodID(jservice_class, "getServiceTypeSupport", "()J"); + + assert(mid != NULL); + + jlong jts = env->CallStaticLongMethod(jservice_class, mid); + + 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); + + rcl_node_t * node = reinterpret_cast(node_handle); + + rosidl_service_type_support_t * ts = + reinterpret_cast(jts); + + rcl_service_t * service = static_cast(malloc(sizeof(rcl_service_t))); + service->impl = NULL; + rcl_service_options_t service_ops = rcl_service_get_default_options(); + + rcl_ret_t ret = rcl_service_init(service, node, ts, service_name.c_str(), &service_ops); + + if (ret != RCL_RET_OK) { + rcljava_throw_exception( + env, "java/lang/IllegalStateException", + "Failed to create service: " + std::string(rcl_get_error_string_safe())); + return 0; + } + + jlong jservice = reinterpret_cast(service); + return jservice; +} + +JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_Node_nativeCreateClientHandle(JNIEnv * env, jclass, + jlong node_handle, jclass jservice_class, jstring jservice_name) +{ + jmethodID mid = env->GetStaticMethodID(jservice_class, "getServiceTypeSupport", "()J"); + + assert(mid != NULL); + + jlong jts = env->CallStaticLongMethod(jservice_class, mid); + + 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); + + rcl_node_t * node = reinterpret_cast(node_handle); + + rosidl_service_type_support_t * ts = + reinterpret_cast(jts); + + rcl_client_t * client = static_cast(malloc(sizeof(rcl_client_t))); + client->impl = NULL; + rcl_client_options_t client_ops = rcl_client_get_default_options(); + + rcl_ret_t ret = rcl_client_init(client, node, ts, service_name.c_str(), &client_ops); + + if (ret != RCL_RET_OK) { + rcljava_throw_exception( + env, "java/lang/IllegalStateException", + "Failed to create client: " + std::string(rcl_get_error_string_safe())); + return 0; + } + + jlong jclient = reinterpret_cast(client); + return jclient; +} diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp index 34fe4804..e923305c 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp @@ -29,6 +29,10 @@ #include "org_ros2_rcljava_RCLJava.h" +jobject convert_rmw_request_id_to_java(JNIEnv *, rmw_request_id_t *); + +rmw_request_id_t * convert_rmw_request_id_from_java(JNIEnv *, jobject); + JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeRCLJavaInit(JNIEnv * env, jclass) { // TODO(esteve): parse args @@ -75,7 +79,7 @@ JNIEXPORT jboolean JNICALL Java_org_ros2_rcljava_RCLJava_nativeOk(JNIEnv *, jcla return rcl_ok(); } -JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_RCLJava_nativeGetZeroInitializedWaitSet(JNIEnv * env, +JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_RCLJava_nativeGetZeroInitializedWaitSet(JNIEnv *, jclass) { rcl_wait_set_t * wait_set = static_cast(malloc(sizeof(rcl_wait_set_t))); @@ -97,11 +101,9 @@ JNIEXPORT jlong JNICALL Java_org_ros2_rcljava_RCLJava_nativeGetZeroInitializedWa JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeWaitSetInit(JNIEnv * env, jclass, jlong wait_set_handle, jint number_of_subscriptions, - jint number_of_guard_conditions, jint number_of_timers) + jint number_of_guard_conditions, jint number_of_timers, + jint number_of_clients, jint number_of_services) { - jint number_of_clients = 0; - jint number_of_services = 0; - rcl_wait_set_t * wait_set = reinterpret_cast(wait_set_handle); rcl_ret_t ret = rcl_wait_set_init( @@ -204,3 +206,229 @@ JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeShutdown(JNIEnv * env "Failed to shutdown: " + std::string(rcl_get_error_string_safe())); } } + +JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_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) { + rcljava_throw_exception( + env, "java/lang/IllegalStateException", + "Failed to clear services from wait set: " + std::string(rcl_get_error_string_safe())); + } +} + +JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_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) { + rcljava_throw_exception( + env, "java/lang/IllegalStateException", + "Failed to add service to wait set: " + std::string(rcl_get_error_string_safe())); + } +} + +JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_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); + if (ret != RCL_RET_OK) { + rcljava_throw_exception( + env, "java/lang/IllegalStateException", + "Failed to clear clients from wait set: " + std::string(rcl_get_error_string_safe())); + } +} + +JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeWaitSetAddClient(JNIEnv * env, jclass, + jlong wait_set_handle, + jlong client_handle) +{ + 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); + if (ret != RCL_RET_OK) { + rcljava_throw_exception( + env, "java/lang/IllegalStateException", + "Failed to add client to wait set: " + std::string(rcl_get_error_string_safe())); + } +} + +JNIEXPORT jobject JNICALL Java_org_ros2_rcljava_RCLJava_nativeTakeRequest(JNIEnv * env, jclass, + jlong service_handle, + jlong jrequest_from_java_converter_handle, + jlong jrequest_to_java_converter_handle, jobject jrequest_msg) +{ + assert(service_handle != 0); + assert(jrequest_from_java_converter_handle != 0); + assert(jrequest_to_java_converter_handle != 0); + assert(jrequest_msg != nullptr); + + rcl_service_t * service = reinterpret_cast(service_handle); + + convert_from_java_signature convert_from_java = + reinterpret_cast(jrequest_from_java_converter_handle); + + convert_to_java_signature convert_to_java = + reinterpret_cast(jrequest_to_java_converter_handle); + + void * taken_msg = convert_from_java(jrequest_msg, nullptr); + + rmw_request_id_t header; + + rcl_ret_t ret = rcl_take_request(service, &header, taken_msg); + + if (ret != RCL_RET_OK && ret != RCL_RET_SERVICE_TAKE_FAILED) { + rcljava_throw_exception( + env, "java/lang/IllegalStateException", + "Failed to take request from a service: " + std::string(rcl_get_error_string_safe())); + return nullptr; + } + + if (ret != RCL_RET_SERVICE_TAKE_FAILED) { + jobject jtaken_msg = convert_to_java(taken_msg, jrequest_msg); + + assert(jtaken_msg != nullptr); + + jobject jheader = convert_rmw_request_id_to_java(env, &header); + return jheader; + } + + return nullptr; +} + +JNIEXPORT void JNICALL Java_org_ros2_rcljava_RCLJava_nativeSendServiceResponse(JNIEnv * env, jclass, + jlong service_handle, + jobject jrequest_id, + jlong jresponse_from_java_converter_handle, jlong jresponse_to_java_converter_handle, + jobject jresponse_msg) +{ + assert(service_handle != 0); + assert(jresponse_from_java_converter_handle != 0); + assert(jresponse_to_java_converter_handle != 0); + assert(jresponse_msg != nullptr); + + rcl_service_t * service = reinterpret_cast(service_handle); + + convert_from_java_signature convert_from_java = + reinterpret_cast(jresponse_from_java_converter_handle); + + void * response_msg = convert_from_java(jresponse_msg, nullptr); + + rmw_request_id_t * request_id = convert_rmw_request_id_from_java(env, jrequest_id); + + rcl_ret_t ret = rcl_send_response(service, request_id, response_msg); + + if (ret != RCL_RET_OK) { + rcljava_throw_exception( + env, "java/lang/IllegalStateException", + "Failed to send response from a service: " + std::string(rcl_get_error_string_safe())); + } +} + +JNIEXPORT jobject JNICALL Java_org_ros2_rcljava_RCLJava_nativeTakeResponse(JNIEnv * env, jclass, + jlong client_handle, + jlong jresponse_from_java_converter_handle, + jlong jresponse_to_java_converter_handle, jobject jresponse_msg) +{ + assert(client_handle != 0); + assert(jresponse_from_java_converter_handle != 0); + assert(jresponse_to_java_converter_handle != 0); + assert(jresponse_msg != nullptr); + + rcl_client_t * client = reinterpret_cast(client_handle); + + convert_from_java_signature convert_from_java = + reinterpret_cast(jresponse_from_java_converter_handle); + + convert_to_java_signature convert_to_java = + reinterpret_cast(jresponse_to_java_converter_handle); + + void * taken_msg = convert_from_java(jresponse_msg, nullptr); + + rmw_request_id_t header; + + rcl_ret_t ret = rcl_take_response(client, &header, taken_msg); + + if (ret != RCL_RET_OK && ret != RCL_RET_CLIENT_TAKE_FAILED) { + rcljava_throw_exception( + env, "java/lang/IllegalStateException", + "Failed to take request from a service: " + std::string(rcl_get_error_string_safe())); + return nullptr; + } + + if (ret != RCL_RET_CLIENT_TAKE_FAILED) { + jobject jtaken_msg = convert_to_java(taken_msg, jresponse_msg); + + assert(jtaken_msg != nullptr); + + jobject jheader = convert_rmw_request_id_to_java(env, &header); + return jheader; + } + + return nullptr; +} + + +jobject convert_rmw_request_id_to_java(JNIEnv * env, rmw_request_id_t * request_id) +{ + jclass jrequest_id_class = env->FindClass("org/ros2/rcljava/RMWRequestId"); + assert(jrequest_id_class != nullptr); + + jmethodID jconstructor = env->GetMethodID(jrequest_id_class, "", "()V"); + assert(jconstructor != nullptr); + + jobject jrequest_id = env->NewObject(jrequest_id_class, jconstructor); + + jfieldID jsequence_number_field_id = env->GetFieldID(jrequest_id_class, "sequenceNumber", "J"); + jfieldID jwriter_guid_field_id = env->GetFieldID(jrequest_id_class, "writerGUID", "[B"); + + assert(jsequence_number_field_id != nullptr); + assert(jwriter_guid_field_id != nullptr); + + int8_t * writer_guid = request_id->writer_guid; + int64_t sequence_number = request_id->sequence_number; + + env->SetLongField(jrequest_id, jsequence_number_field_id, sequence_number); + + jsize writer_guid_len = 16; // See rmw/rmw/include/rmw/types.h + + jbyteArray jwriter_guid = env->NewByteArray(writer_guid_len); + env->SetByteArrayRegion(jwriter_guid, 0, writer_guid_len, reinterpret_cast(writer_guid)); + env->SetObjectField(jrequest_id, jwriter_guid_field_id, jwriter_guid); + + return jrequest_id; +} + +rmw_request_id_t * convert_rmw_request_id_from_java(JNIEnv * env, jobject jrequest_id) +{ + assert(jrequest_id != nullptr); + + jclass jrequest_id_class = env->GetObjectClass(jrequest_id); + assert(jrequest_id_class != nullptr); + + jfieldID jsequence_number_field_id = env->GetFieldID(jrequest_id_class, "sequenceNumber", "J"); + jfieldID jwriter_guid_field_id = env->GetFieldID(jrequest_id_class, "writerGUID", "[B"); + + assert(jsequence_number_field_id != nullptr); + assert(jwriter_guid_field_id != nullptr); + + rmw_request_id_t * request_id = static_cast(malloc(sizeof(rmw_request_id_t))); + + int8_t * writer_guid = request_id->writer_guid; + request_id->sequence_number = env->GetLongField(jrequest_id, jsequence_number_field_id); + + jsize writer_guid_len = 16; // See rmw/rmw/include/rmw/types.h + + jbyteArray jwriter_guid = (jbyteArray)env->GetObjectField(jrequest_id, jwriter_guid_field_id); + env->GetByteArrayRegion(jwriter_guid, 0, writer_guid_len, reinterpret_cast(writer_guid)); + + return request_id; +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/BiConsumer.java b/rcljava/src/main/java/org/ros2/rcljava/BiConsumer.java new file mode 100644 index 00000000..8b9fc635 --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/BiConsumer.java @@ -0,0 +1,33 @@ +/* Copyright 2016 Esteve Fernandez + * + * 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; + +/** + * This is a copy of {@link java.util.funcion.BiConsumer} for platforms that don't + * support Java 8's API (e.g. Android < 7.0) + * + * @param - the type of the first input to the operation + * @param - the type of the second input to the operation + */ +public interface BiConsumer { + /** + * Performs this operation on the given argument. + * + * @param input1 - the first input argument + * @param input2 - the second input argument + */ + void accept(T input1, U input2); +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/Client.java b/rcljava/src/main/java/org/ros2/rcljava/Client.java new file mode 100644 index 00000000..e9ba7a4d --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/Client.java @@ -0,0 +1,124 @@ +/* Copyright 2016 Esteve Fernandez + * + * 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; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Future; + +public class Client { + static { + try { + System.loadLibrary("rcljavaClient__" + RCLJava.getRMWIdentifier()); + } catch (UnsatisfiedLinkError ule) { + System.err.println("Native code library failed to load.\n" + ule); + System.exit(1); + } + } + + private final WeakReference nodeReference; + private final long nodeHandle; + private final long clientHandle; + private final Class serviceType; + private final String serviceName; + private long sequenceNumber = 0; + private Map pendingRequests; + + private long requestFromJavaConverterHandle = 0; + private long requestToJavaConverterHandle = 0; + + private long responseFromJavaConverterHandle = 0; + private long responseToJavaConverterHandle = 0; + + private final Class requestType; + private final Class responseType; + + public Client(final WeakReference nodeReference, + final long nodeHandle, final long clientHandle, final Class serviceType, final String serviceName, + final Class requestType, final Class responseType, + final long requestFromJavaConverterHandle, final long requestToJavaConverterHandle, + final long responseFromJavaConverterHandle, final long responseToJavaConverterHandle) { + this.nodeReference = nodeReference; + this.nodeHandle = nodeHandle; + this.clientHandle = clientHandle; + this.serviceType = serviceType; + this.serviceName = serviceName; + this.requestType = requestType; + this.responseType = responseType; + this.requestFromJavaConverterHandle = requestFromJavaConverterHandle; + this.requestToJavaConverterHandle = requestToJavaConverterHandle; + this.responseFromJavaConverterHandle = responseFromJavaConverterHandle; + this.responseToJavaConverterHandle = responseToJavaConverterHandle; + this.pendingRequests = new HashMap(); + } + + public Future sendRequest(U request) { + synchronized(pendingRequests) { + sequenceNumber++; + nativeSendClientRequest(clientHandle, sequenceNumber, + requestFromJavaConverterHandle, requestToJavaConverterHandle, request); + RCLFuture future = new RCLFuture(this.nodeReference); + pendingRequests.put(sequenceNumber, future); + return future; + } + } + + public void handleResponse(RMWRequestId header, U response) { + synchronized(pendingRequests) { + long sequenceNumber = header.sequenceNumber; + RCLFuture future = pendingRequests.remove(sequenceNumber); + future.set(response); + } + } + + public final Class getServiceType() { + return serviceType; + } + + public final long getClientHandle() { + return clientHandle; + } + + private static native void nativeSendClientRequest(long clientHandle, long sequenceNumber, + long requestFromJavaConverterHandle, long requestToJavaConverterHandle, Object requestMessage); + + + public final long getRequestFromJavaConverterHandle() { + return this.requestFromJavaConverterHandle; + } + + public final long getRequestToJavaConverterHandle() { + return this.requestToJavaConverterHandle; + } + + public final long getResponseFromJavaConverterHandle() { + return this.responseFromJavaConverterHandle; + } + + public final long getResponseToJavaConverterHandle() { + return this.responseToJavaConverterHandle; + } + + public final Class getRequestType() { + return this.requestType; + } + + public final Class getResponseType() { + return this.responseType; + } +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/Node.java b/rcljava/src/main/java/org/ros2/rcljava/Node.java index 8aa0b9ff..e667a69a 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/Node.java +++ b/rcljava/src/main/java/org/ros2/rcljava/Node.java @@ -15,6 +15,9 @@ package org.ros2.rcljava; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; @@ -48,6 +51,16 @@ public class Node { */ private final Queue publishers; + /** + * All the @{link Service}s that have been created through this instance. + */ + private final Queue services; + + /** + * All the @{link Client}s that have been created through this instance. + */ + private final Queue clients; + /** * Constructor. * @@ -58,6 +71,8 @@ public Node(final long nodeHandle) { this.nodeHandle = nodeHandle; this.publishers = new LinkedBlockingQueue(); this.subscriptions = new LinkedBlockingQueue(); + this.services = new LinkedBlockingQueue(); + this.clients = new LinkedBlockingQueue(); } /** @@ -166,4 +181,74 @@ public final void dispose() { public final long getNodeHandle() { return this.nodeHandle; } + + private static native long nativeCreateServiceHandle( + long nodeHandle, Class cls, String serviceName); + + public Service createService(Class serviceType, String serviceName, + TriConsumer callback) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + + Class requestType = (Class)serviceType.getField("RequestType").get(null); + + Method requestFromJavaConverterMethod = requestType.getDeclaredMethod("getFromJavaConverter", (Class []) null); + long requestFromJavaConverterHandle = (Long)requestFromJavaConverterMethod.invoke(null, (Class []) null); + + Method requestToJavaConverterMethod = requestType.getDeclaredMethod("getToJavaConverter", (Class []) null); + long requestToJavaConverterHandle = (Long)requestToJavaConverterMethod.invoke(null, (Class []) null); + + Class responseType = (Class)serviceType.getField("ResponseType").get(null); + + Method responseFromJavaConverterMethod = responseType.getDeclaredMethod("getFromJavaConverter", (Class []) null); + long responseFromJavaConverterHandle = (Long)responseFromJavaConverterMethod.invoke(null, (Class []) null); + + Method responseToJavaConverterMethod = responseType.getDeclaredMethod("getToJavaConverter", (Class []) null); + long responseToJavaConverterHandle = (Long)responseToJavaConverterMethod.invoke(null, (Class []) null); + + long serviceHandle = nativeCreateServiceHandle(this.nodeHandle, serviceType, serviceName); + Service service = new Service(this.nodeHandle, serviceHandle, serviceType, serviceName, callback, requestType, responseType, + requestFromJavaConverterHandle, requestToJavaConverterHandle, responseFromJavaConverterHandle, + responseToJavaConverterHandle); + this.services.add(service); + return service; + } + + public final Queue getServices() { + return this.services; + } + + public Client createClient(Class serviceType, String serviceName) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + + Class requestType = (Class)serviceType.getField("RequestType").get(null); + + Method requestFromJavaConverterMethod = requestType.getDeclaredMethod("getFromJavaConverter", (Class []) null); + long requestFromJavaConverterHandle = (Long)requestFromJavaConverterMethod.invoke(null, (Class []) null); + + Method requestToJavaConverterMethod = requestType.getDeclaredMethod("getToJavaConverter", (Class []) null); + long requestToJavaConverterHandle = (Long)requestToJavaConverterMethod.invoke(null, (Class []) null); + + Class responseType = (Class)serviceType.getField("ResponseType").get(null); + + Method responseFromJavaConverterMethod = responseType.getDeclaredMethod("getFromJavaConverter", (Class []) null); + long responseFromJavaConverterHandle = (Long)responseFromJavaConverterMethod.invoke(null, (Class []) null); + + Method responseToJavaConverterMethod = responseType.getDeclaredMethod("getToJavaConverter", (Class []) null); + long responseToJavaConverterHandle = (Long)responseToJavaConverterMethod.invoke(null, (Class []) null); + + + long clientHandle = nativeCreateClientHandle(this.nodeHandle, serviceType, serviceName); + Client client = new Client(new WeakReference(this), + this.nodeHandle, clientHandle, serviceType, serviceName, + requestType, responseType, + requestFromJavaConverterHandle, requestToJavaConverterHandle, responseFromJavaConverterHandle, + responseToJavaConverterHandle); + this.clients.add(client); + return client; + } + + private static native long nativeCreateClientHandle( + long nodeHandle, Class cls, String serviceName); + + public final Queue getClients() { + return this.clients; + } } diff --git a/rcljava/src/main/java/org/ros2/rcljava/RCLFuture.java b/rcljava/src/main/java/org/ros2/rcljava/RCLFuture.java new file mode 100644 index 00000000..4b207fb6 --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/RCLFuture.java @@ -0,0 +1,69 @@ +/* Copyright 2016 Esteve Fernandez + * + * 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; + +import java.lang.InterruptedException; +import java.lang.ref.WeakReference; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + + +public class RCLFuture implements Future { + private WeakReference nodeReference; + private boolean done = false; + private V value = null; + + public RCLFuture(WeakReference nodeReference) { + this.nodeReference = nodeReference; + } + + public V get() throws InterruptedException, ExecutionException { + while (RCLJava.ok() && !isDone()) { + Node node = nodeReference.get(); + if (node != null) { + RCLJava.spinOnce(node); + } else { + return null; // TODO(esteve) do something + } + } + return value; + } + + public V get(long timeout, TimeUnit unit) throws InterruptedException, + ExecutionException, TimeoutException { + return null; + } + + public boolean isDone() { + return done; + } + + public boolean isCancelled() { + return false; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + public synchronized void set(V value) { + this.value = value; + done = true; + } +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java index 9874b0a0..10d8eba0 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java +++ b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java @@ -15,6 +15,8 @@ package org.ros2.rcljava; +import java.lang.reflect.Method; + import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentSkipListMap; @@ -104,6 +106,7 @@ public static void rclJavaInit() { System.exit(1); } else { nativeRCLJavaInit(); + System.out.println("Using RMW implementation: " + getRMWIdentifier()); initialized = true; } } @@ -181,15 +184,27 @@ public static Node createNode(final String nodeName) { public static void spinOnce(final Node node) { long waitSetHandle = nativeGetZeroInitializedWaitSet(); - nativeWaitSetInit(waitSetHandle, node.getSubscriptions().size(), 0, 0); + nativeWaitSetInit(waitSetHandle, node.getSubscriptions().size(), 0, 0, node.getClients().size(), node.getServices().size()); nativeWaitSetClearSubscriptions(waitSetHandle); + nativeWaitSetClearServices(waitSetHandle); + + nativeWaitSetClearClients(waitSetHandle); + for (Subscription subscription : node.getSubscriptions()) { nativeWaitSetAddSubscription( waitSetHandle, subscription.getSubscriptionHandle()); } + for (Service service : node.getServices()) { + nativeWaitSetAddService(waitSetHandle, service.getServiceHandle()); + } + + for (Client client : node.getClients()) { + nativeWaitSetAddClient(waitSetHandle, client.getClientHandle()); + } + nativeWait(waitSetHandle); for (Subscription subscription : node.getSubscriptions()) { @@ -200,6 +215,68 @@ public static void spinOnce(final Node node) { subscription.getCallback().accept(message); } } + + for (Service service : node.getServices()) { + long requestFromJavaConverterHandle = service.getRequestFromJavaConverterHandle(); + long requestToJavaConverterHandle = service.getRequestToJavaConverterHandle(); + long responseFromJavaConverterHandle = service.getResponseFromJavaConverterHandle(); + long responseToJavaConverterHandle = service.getResponseToJavaConverterHandle(); + + Class requestType = service.getRequestType(); + Class responseType = service.getResponseType(); + + Object requestMessage = null; + Object responseMessage = null; + + try { + requestMessage = requestType.newInstance(); + responseMessage = responseType.newInstance(); + } catch (InstantiationException ie) { + ie.printStackTrace(); + continue; + } catch (IllegalAccessException iae) { + iae.printStackTrace(); + continue; + } + + RMWRequestId rmwRequestId = (RMWRequestId)nativeTakeRequest(service.getServiceHandle(), requestFromJavaConverterHandle, requestToJavaConverterHandle, requestMessage); + if (rmwRequestId != null) { + service.getCallback().accept(rmwRequestId, requestMessage, responseMessage); + nativeSendServiceResponse(service.getServiceHandle(), rmwRequestId, responseFromJavaConverterHandle, responseToJavaConverterHandle, responseMessage); + } + } + + for (Client client : node.getClients()) { + long requestFromJavaConverterHandle = client.getRequestFromJavaConverterHandle(); + long requestToJavaConverterHandle = client.getRequestToJavaConverterHandle(); + long responseFromJavaConverterHandle = client.getResponseFromJavaConverterHandle(); + long responseToJavaConverterHandle = client.getResponseToJavaConverterHandle(); + + Class requestType = client.getRequestType(); + Class responseType = client.getResponseType(); + + Object requestMessage = null; + Object responseMessage = null; + + try { + requestMessage = requestType.newInstance(); + responseMessage = responseType.newInstance(); + } catch (InstantiationException ie) { + ie.printStackTrace(); + continue; + } catch (IllegalAccessException iae) { + iae.printStackTrace(); + continue; + } + + RMWRequestId rmwRequestId = (RMWRequestId)nativeTakeResponse( + client.getClientHandle(), responseFromJavaConverterHandle, + responseToJavaConverterHandle, responseMessage); + + if (rmwRequestId != null) { + client.handleResponse(rmwRequestId, responseMessage); + } + } } private static native void nativeShutdown(); @@ -212,7 +289,8 @@ public static void shutdown() { private static native void nativeWaitSetInit( long waitSetHandle, int numberOfSubscriptions, - int numberOfGuardConditions, int numberOfTimers); + int numberOfGuardConditions, int numberOfTimers, + int numberOfClients, int numberOfServices); private static native void nativeWaitSetClearSubscriptions( long waitSetHandle); @@ -224,4 +302,24 @@ private static native void nativeWaitSetAddSubscription( private static native Object nativeTake(long subscriptionHandle, Class messageType); + + 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 Object nativeTakeRequest( + long serviceHandle, long requestFromJavaConverterHandle, + long requestToJavaConverterHandle, Object requestMessage); + + private static native void nativeSendServiceResponse(long serviceHandle, Object header, + long responseFromJavaConverterHandle, long responseToJavaConverterHandle, + Object responseMessage); + + private static native Object nativeTakeResponse( + long clientHandle, long responseFromJavaConverterHandle, + long responseToJavaConverterHandle, Object responseMessage); } diff --git a/rcljava/src/main/java/org/ros2/rcljava/RMWRequestId.java b/rcljava/src/main/java/org/ros2/rcljava/RMWRequestId.java new file mode 100644 index 00000000..3e1278de --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/RMWRequestId.java @@ -0,0 +1,21 @@ +/* Copyright 2016 Esteve Fernandez + * + * 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; + +public class RMWRequestId { + public byte writerGUID[] = new byte[16]; + public long sequenceNumber; +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/Service.java b/rcljava/src/main/java/org/ros2/rcljava/Service.java new file mode 100644 index 00000000..2f979f57 --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/Service.java @@ -0,0 +1,89 @@ +/* Copyright 2016 Esteve Fernandez + * + * 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; + +public class Service { + + private final long nodeHandle; + private final long serviceHandle; + private final Class serviceType; + private final String serviceName; + private final TriConsumer callback; + + private long requestFromJavaConverterHandle = 0; + private long requestToJavaConverterHandle = 0; + + private long responseFromJavaConverterHandle = 0; + private long responseToJavaConverterHandle = 0; + + private final Class requestType; + private final Class responseType; + + public Service(final long nodeHandle, final long serviceHandle, + final Class serviceType, final String serviceName, + final TriConsumer callback, + final Class requestType, final Class responseType, + final long requestFromJavaConverterHandle, final long requestToJavaConverterHandle, + final long responseFromJavaConverterHandle, final long responseToJavaConverterHandle) { + this.nodeHandle = nodeHandle; + this.serviceHandle = serviceHandle; + this.serviceType = serviceType; + this.serviceName = serviceName; + this.callback = callback; + this.requestType = requestType; + this.responseType = responseType; + this.requestFromJavaConverterHandle = requestFromJavaConverterHandle; + this.requestToJavaConverterHandle = requestToJavaConverterHandle; + this.responseFromJavaConverterHandle = responseFromJavaConverterHandle; + this.responseToJavaConverterHandle = responseToJavaConverterHandle; + } + + public final TriConsumer getCallback() { + return callback; + } + + public final Class getServiceType() { + return serviceType; + } + + public final long getServiceHandle() { + return serviceHandle; + } + + public final long getRequestFromJavaConverterHandle() { + return this.requestFromJavaConverterHandle; + } + + public final long getRequestToJavaConverterHandle() { + return this.requestToJavaConverterHandle; + } + + public final long getResponseFromJavaConverterHandle() { + return this.responseFromJavaConverterHandle; + } + + public final long getResponseToJavaConverterHandle() { + return this.responseToJavaConverterHandle; + } + + public final Class getRequestType() { + return this.requestType; + } + + public final Class getResponseType() { + return this.responseType; + } +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/TriConsumer.java b/rcljava/src/main/java/org/ros2/rcljava/TriConsumer.java new file mode 100644 index 00000000..0e26d7c8 --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/TriConsumer.java @@ -0,0 +1,34 @@ +/* Copyright 2016 Esteve Fernandez + * + * 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; + +/** + * Based on {@link java.util.funcion.BiConsumer} + * + * @param - the type of the first input to the operation + * @param - the type of the second input to the operation + * @param - the type of the third input to the operation + */ +public interface TriConsumer { + /** + * Performs this operation on the given argument. + * + * @param input1 - the first input argument + * @param input2 - the second input argument + * @param input3 - the third input argument + */ + void accept(T input1, U input2, V input3); +} 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 fe962392..f9008deb 100644 --- a/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake +++ b/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake @@ -46,7 +46,7 @@ set(_generated_msg_java_files "") #set(_generated_msg_cpp_files "") #set(_generated_msg_cpp_common_files "") set(_generated_msg_cpp_ts_files "") -set(_generated_srv_files "") +set(_generated_srv_java_files "") foreach(_idl_file ${rosidl_generate_interfaces_IDL_FILES}) get_filename_component(_parent_folder "${_idl_file}" DIRECTORY) @@ -59,18 +59,22 @@ foreach(_idl_file ${rosidl_generate_interfaces_IDL_FILES}) ) foreach(_typesupport_impl ${_typesupport_impls}) -# list_append_unique(_generated_msg_cpp_files -# "${_output_path}/${_parent_folder}/${_module_name}_s.ep.${_typesupport_impl}.cpp" -# ) list_append_unique(_generated_msg_cpp_ts_files "${_output_path}/${_parent_folder}/${_module_name}_s.ep.${_typesupport_impl}.cpp" ) list(APPEND _type_support_by_generated_msg_cpp_files "${_typesupport_impl}") endforeach() elseif("${_parent_folder} " STREQUAL "srv ") - list(APPEND _generated_srv_files + list(APPEND _generated_srv_java_files "${_output_path}/${_parent_folder}/${_module_name}.java" ) + + foreach(_typesupport_impl ${_typesupport_impls}) + list_append_unique(_generated_srv_cpp_ts_files + "${_output_path}/${_parent_folder}/${_module_name}_s.ep.${_typesupport_impl}.cpp" + ) + list(APPEND _type_support_by_generated_srv_cpp_files "${_typesupport_impl}") + endforeach() else() message(FATAL_ERROR "Interface file with unknown parent folder: ${_idl_file}") endif() @@ -91,6 +95,7 @@ set(target_dependencies "${rosidl_generator_java_BIN}" ${rosidl_generator_java_GENERATOR_FILES} "${rosidl_generator_java_TEMPLATE_DIR}/msg_support.entry_point.cpp.template" + "${rosidl_generator_java_TEMPLATE_DIR}/srv_support.entry_point.cpp.template" "${rosidl_generator_java_TEMPLATE_DIR}/msg.java.template" "${rosidl_generator_java_TEMPLATE_DIR}/srv.java.template" ${rosidl_generate_interfaces_IDL_FILES} @@ -120,12 +125,12 @@ set(_target_suffix "__java") set_property( SOURCE - ${_generated_msg_java_files} ${_generated_msg_cpp_ts_files} ${_generated_srv_files} + ${_generated_msg_java_files} ${_generated_msg_cpp_ts_files} ${_generated_srv_java_files} ${_generated_srv_cpp_ts_files} PROPERTY GENERATED 1) add_custom_command( -# OUTPUT ${_generated_msg_cpp_common_files} ${_generated_msg_java_files} ${_generated_msg_cpp_ts_files} ${_generated_msg_cpp_files} ${_generated_srv_files} - OUTPUT ${_generated_msg_java_files} ${_generated_msg_cpp_ts_files} ${_generated_srv_files} +# OUTPUT ${_generated_msg_cpp_common_files} ${_generated_msg_java_files} ${_generated_msg_cpp_ts_files} ${_generated_msg_cpp_files} ${_generated_srv_java_files} + OUTPUT ${_generated_msg_java_files} ${_generated_msg_cpp_ts_files} ${_generated_srv_java_files} ${_generated_srv_cpp_ts_files} COMMAND ${PYTHON_EXECUTABLE} ${rosidl_generator_java_BIN} --generator-arguments-file "${generator_arguments_file}" --typesupport-impl "${_typesupport_impl}" @@ -143,29 +148,32 @@ else() DEPENDS ${_generated_msg_java_files} ${_generated_msg_cpp_ts_files} - ${_generated_srv_files} + ${_generated_srv_java_files} + ${_generated_srv_cpp_ts_files} ) endif() -foreach(_generated_msg_cpp_ts_file ${_generated_msg_cpp_ts_files}) - get_filename_component(_full_folder "${_generated_msg_cpp_ts_file}" DIRECTORY) +function(build_jni_libraries generated_source_files type_support_by_generated_files) + +foreach(generated_source_file ${generated_source_files}) + get_filename_component(_full_folder "${generated_source_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_msg_cpp_ts_file}" NAME_WE) - get_filename_component(_full_extension_msg_name "${_generated_msg_cpp_ts_file}" EXT) + get_filename_component(_base_msg_name "${generated_source_file}" NAME_WE) + get_filename_component(_full_extension_msg_name "${generated_source_file}" EXT) set(_msg_name "${_base_msg_name}${_full_extension_msg_name}") - list(FIND _generated_msg_cpp_ts_files ${_generated_msg_cpp_ts_file} _file_index) - list(GET _type_support_by_generated_msg_cpp_files ${_file_index} _typesupport_impl) + list(FIND generated_source_files ${generated_source_file} _file_index) + list(GET type_support_by_generated_files ${_file_index} _typesupport_impl) find_package(${_typesupport_impl} REQUIRED) set(_generated_msg_cpp_common_file "${_full_folder}/${_base_msg_name}.cpp") set(_javaext_suffix "__javaext") add_library(${_package_name}_${_base_msg_name}__${_typesupport_impl} SHARED - "${_generated_msg_cpp_ts_file}" + "${generated_source_file}" ) set_target_properties(${_package_name}_${_base_msg_name}__${_typesupport_impl} PROPERTIES @@ -253,6 +261,11 @@ foreach(_generated_msg_cpp_ts_file ${_generated_msg_cpp_ts_files}) endforeach() +endfunction() + +build_jni_libraries("${_generated_msg_cpp_ts_files}" "${_type_support_by_generated_msg_cpp_files}") +build_jni_libraries("${_generated_srv_cpp_ts_files}" "${_type_support_by_generated_srv_cpp_files}") + set(_jar_deps "") find_package(rcljava_common REQUIRED) foreach(_jar_dep ${rcljava_common_JARS}) @@ -268,12 +281,15 @@ endforeach() add_jar("${PROJECT_NAME}_jar" "${_generated_msg_java_files}" + "${_generated_srv_java_files}" OUTPUT_NAME "${PROJECT_NAME}" INCLUDE_JARS "${_jar_deps}" ) +add_dependencies("${PROJECT_NAME}_jar" "${rosidl_generate_interfaces_TARGET}${_target_suffix}") + get_property(_jar_file TARGET "${PROJECT_NAME}_jar" PROPERTY "JAR_FILE") if(NOT rosidl_generate_interfaces_SKIP_INSTALL) diff --git a/rosidl_generator_java/resource/msg.java.template b/rosidl_generator_java/resource/msg.java.template index 988e13c7..c27bbc51 100644 --- a/rosidl_generator_java/resource/msg.java.template +++ b/rosidl_generator_java/resource/msg.java.template @@ -4,9 +4,7 @@ import org.ros2.rcljava.common.RCLJavaProxy; @[for field in spec.fields]@ @[ if not field.type.is_primitive_type()]@ -@[ if field.type.pkg_name != spec.base_type.pkg_name]@ import @(field.type.pkg_name).msg.@(field.type.type); -@[ end if]@ @[ end if]@ @[end for]@ diff --git a/rosidl_generator_java/resource/msg_support.entry_point.cpp.template b/rosidl_generator_java/resource/msg_support.entry_point.cpp.template index 66bcb123..c0e87402 100644 --- a/rosidl_generator_java/resource/msg_support.entry_point.cpp.template +++ b/rosidl_generator_java/resource/msg_support.entry_point.cpp.template @@ -31,7 +31,7 @@ extern "C" { * Method: getFromJavaConverter * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(type_name)_getFromJavaConverter +JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getFromJavaConverter (JNIEnv *, jclass); /* @@ -39,7 +39,7 @@ JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(type_name)_getFr * Method: getToJavaConverter * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(type_name)_getToJavaConverter +JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getToJavaConverter (JNIEnv *, jclass); /* @@ -47,7 +47,7 @@ JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(type_name)_getTo * Method: getTypeSupport * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(type_name)_getTypeSupport +JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getTypeSupport (JNIEnv *, jclass); #ifdef __cplusplus @@ -390,23 +390,23 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { return JNI_VERSION_1_6; } -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(type_name)_getFromJavaConverter +JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getFromJavaConverter (JNIEnv *, jclass) { jlong ptr = reinterpret_cast(&@(spec.base_type.pkg_name)_@(type_name)__convert_from_java); return ptr; } -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(type_name)_getToJavaConverter +JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getToJavaConverter (JNIEnv *, jclass) { jlong ptr = reinterpret_cast(@(spec.base_type.pkg_name)_@(type_name)__convert_to_java); return ptr; } -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(type_name)_getTypeSupport +JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getTypeSupport (JNIEnv *, jclass) { - jlong ptr = reinterpret_cast(ROSIDL_GET_MSG_TYPE_SUPPORT(@(spec.base_type.pkg_name), @(spec.msg_name))); + jlong ptr = reinterpret_cast(ROSIDL_GET_TYPE_SUPPORT(@(spec.base_type.pkg_name), @(subfolder), @(spec.msg_name))); return ptr; } diff --git a/rosidl_generator_java/resource/srv.java.template b/rosidl_generator_java/resource/srv.java.template index d15abba5..897c2891 100644 --- a/rosidl_generator_java/resource/srv.java.template +++ b/rosidl_generator_java/resource/srv.java.template @@ -1 +1,20 @@ -// Empty +package @(package_name).@(subfolder); + +import org.ros2.rcljava.common.RCLJavaProxy; + +public class @(type_name) { + static { + try { + System.loadLibrary("@(spec.pkg_name)_@(type_name)_s__" + RCLJavaProxy.getTypesupportIdentifier()); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load.\n" + e); + System.exit(1); + } + } + + public static native long getServiceTypeSupport(); + + public static final Class<@(type_name)_Request> RequestType = @(type_name)_Request.class; + + public static final Class<@(type_name)_Response> ResponseType = @(type_name)_Response.class; +} diff --git a/rosidl_generator_java/resource/srv_support.entry_point.cpp.template b/rosidl_generator_java/resource/srv_support.entry_point.cpp.template new file mode 100644 index 00000000..96ad99e6 --- /dev/null +++ b/rosidl_generator_java/resource/srv_support.entry_point.cpp.template @@ -0,0 +1,28 @@ +#include + +#include +#include <@(spec.pkg_name)/@(subfolder)/@(module_name).h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: @(jni_package_name)_@(subfolder)_@(type_name) + * Method: getServiceTypeSupport + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getServiceTypeSupport + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif + +JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getServiceTypeSupport + (JNIEnv *, jclass) +{ + const rosidl_service_type_support_t * ts = ROSIDL_GET_TYPE_SUPPORT( + @(spec.pkg_name), @(subfolder), @(spec.srv_name)); + return reinterpret_cast(ts); +} diff --git a/rosidl_generator_java/rosidl_generator_java/__init__.py b/rosidl_generator_java/rosidl_generator_java/__init__.py index 9e824eda..091f73c0 100644 --- a/rosidl_generator_java/rosidl_generator_java/__init__.py +++ b/rosidl_generator_java/rosidl_generator_java/__init__.py @@ -39,6 +39,8 @@ def generate_java(generator_arguments_file, typesupport_impl, typesupport_impls) mapping_srvs = { os.path.join(template_dir, 'srv.java.template'): ['%s.java'], + os.path.join(template_dir, 'srv_support.entry_point.cpp.template'): + type_support_impl_by_filename.keys(), } for template_file in mapping_msgs.keys(): @@ -72,6 +74,7 @@ def generate_java(generator_arguments_file, typesupport_impl, typesupport_impls) 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 = { @@ -81,6 +84,7 @@ def generate_java(generator_arguments_file, typesupport_impl, typesupport_impls) '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,