diff --git a/rcljava/CMakeLists.txt b/rcljava/CMakeLists.txt index 684f2275..a3b0c297 100644 --- a/rcljava/CMakeLists.txt +++ b/rcljava/CMakeLists.txt @@ -59,6 +59,7 @@ set(${PROJECT_NAME}_jni_sources "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_events_EventHandlerImpl.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" @@ -125,6 +126,9 @@ set(${PROJECT_NAME}_sources "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" + "src/main/java/org/ros2/rcljava/events/EventHandler.java" + "src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java" + "src/main/java/org/ros2/rcljava/events/EventStatus.java" "src/main/java/org/ros2/rcljava/executors/AnyExecutable.java" "src/main/java/org/ros2/rcljava/executors/BaseExecutor.java" "src/main/java/org/ros2/rcljava/executors/Executor.java" diff --git a/rcljava/include/org_ros2_rcljava_events_EventHandlerImpl.h b/rcljava/include/org_ros2_rcljava_events_EventHandlerImpl.h new file mode 100644 index 00000000..2416ca08 --- /dev/null +++ b/rcljava/include/org_ros2_rcljava_events_EventHandlerImpl.h @@ -0,0 +1,44 @@ +// Copyright 2020 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_events_EventHandlerImpl */ + +#ifndef ORG_ROS2_RCLJAVA_EVENTS_EVENTHANDLERIMPL_H_ +#define ORG_ROS2_RCLJAVA_EVENTS_EVENTHANDLERIMPL_H_ +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: org_ros2_rcljava_events_EventHandlerImpl + * Method: nativeDispose + * Signature: (J)V + */ +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_events_EventHandlerImpl_nativeDispose(JNIEnv *, jclass, jlong event_handle); + +/* + * Class: org_ros2_rcljava_events_EventHandlerImpl + * Method: nativeTake + * Signature: (JJ)V + */ +JNIEXPORT void +JNICALL Java_org_ros2_rcljava_events_EventHandlerImpl_nativeTake( + JNIEnv *, jclass, jlong event_handle, jlong event_status_handle); + +#ifdef __cplusplus +} +#endif +#endif // ORG_ROS2_RCLJAVA_EVENTS_EVENTHANDLERIMPL_H_ diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_events_EventHandlerImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_events_EventHandlerImpl.cpp new file mode 100644 index 00000000..c23fd533 --- /dev/null +++ b/rcljava/src/main/cpp/org_ros2_rcljava_events_EventHandlerImpl.cpp @@ -0,0 +1,71 @@ +// Copyright 2020 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/error_handling.h" +#include "rcl/event.h" + +#include "rcljava_common/exceptions.hpp" + +#include "org_ros2_rcljava_events_EventHandlerImpl.h" + +using rcljava_common::exceptions::rcljava_throw_exception; +using rcljava_common::exceptions::rcljava_throw_rclexception; + +JNIEXPORT void JNICALL +Java_org_ros2_rcljava_events_EventHandlerImpl_nativeDispose( + JNIEnv * env, jclass, jlong event_handle) +{ + if (event_handle == 0) { + // everything is ok, already destroyed + return; + } + + auto * event = reinterpret_cast(event_handle); + + rcl_ret_t ret = rcl_event_fini(event); + + if (RCL_RET_OK != ret) { + std::string msg = "Failed to destroy event: " + std::string(rcl_get_error_string().str); + rcl_reset_error(); + rcljava_throw_exception(env, "java/lang/IllegalStateException", msg); + } +} + +JNIEXPORT void JNICALL +Java_org_ros2_rcljava_events_EventHandlerImpl_nativeTake(JNIEnv *env, jclass, jlong event_handle, jlong event_status_handle) +{ + auto * event = reinterpret_cast(event_handle); + if (!event) { + rcljava_throw_exception( + env, + "java/lang/IllegalArgumentException", + "The underlying rcl_event_t has been already disposed"); + } + void * event_status = reinterpret_cast(event_status_handle); + if (!event_status) { + rcljava_throw_exception( + env, + "java/lang/IllegalArgumentException", + "The passed event status is NULL"); + } + rcl_ret_t ret = rcl_take_event(event, event_status); + if (RCL_RET_OK != ret) { + rcljava_throw_rclexception(env, ret, rcl_get_error_string().str); + rcl_reset_error(); + } +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/EventHandler.java b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandler.java new file mode 100644 index 00000000..7df0f4fb --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandler.java @@ -0,0 +1,51 @@ +// Copyright 2020 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.events; + +import java.lang.ref.WeakReference; + +import org.ros2.rcljava.interfaces.Disposable; +import org.ros2.rcljava.events.EventStatus; + +/** + * This class serves as a bridge between a rcl_event_t and RCLJava. + * An EventHandler must be created via + * @{link Publisher#createEventHandler(Class<T>, Consumer<T>)} + * @{link Subscription#createEventHandler(Class<T>, Consumer<T>)} + * + * @param The event status type. + * @param The parent class type. + */ +public interface EventHandler extends Disposable { + /** + * @return The event status type. + */ + Class getEventStatusType(); + + /** + * @return The parent entity type. + */ + Class getParentType(); + + /** + * @return A weak reference to the parent. + */ + WeakReference getParentReference(); + + /** + * @return Execute the registered event callback + */ + void executeCallback(); +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java new file mode 100644 index 00000000..013ec29a --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java @@ -0,0 +1,165 @@ +// Copyright 2020 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.events; + +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; + +import org.ros2.rcljava.common.JNIUtils; +import org.ros2.rcljava.consumers.Consumer; +import org.ros2.rcljava.events.EventHandler; +import org.ros2.rcljava.events.EventStatus; +import org.ros2.rcljava.interfaces.Disposable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class serves as a bridge between a rcl_event_t and RCLJava. + * An EventHandler must be created via + * @{link Publisher#createEventHandler(Class<T>, Consumer<T>)} + * @{link Subscription#createEventHandler(Class<T>, Consumer<T>)} + * + * @param The status event type. + * @param The parent class type. + */ +public class EventHandlerImpl< + T extends EventStatus, + ParentT extends Disposable> +implements EventHandler { + private static final Logger logger = LoggerFactory.getLogger(EventHandlerImpl.class); + + static { + try { + JNIUtils.loadImplementation(EventHandlerImpl.class); + } catch (UnsatisfiedLinkError ule) { + logger.error("Native code library failed to load.\n" + ule); + System.exit(1); + } + } + + /** + * Constructor. + * + * @param parentType The Class type of the parent. + * It can be either a @{link org.ros2.rcljava.Publisher} or a + * @{link org.ros2.rcljava.Subscription} class. + * @param parentReference A {@link java.lang.ref.WeakReference} to the + * @{link org.ros2.rcljava.Publisher} or @{link org.ros2.rcljava.Subscription} + * that created this event handler. + * @param handle A pointer to the underlying ROS 2 event structure, as an integer. + * Must not be zero. + * @param eventStatusType The Class of the messages that this + * subscription will receive. We need this because of Java's type erasure, + * which doesn't allow us to use the generic parameter of + * @{link org.ros2.rcljava.Subscription} directly. + * @param callback The callback function that will be called when the event + * is triggered. + */ + public EventHandlerImpl( + final Class parentType, + final WeakReference parentReference, + final long handle, + final Class eventStatusType, + final Consumer callback) { + this.parentType = parentType; + this.parentReference = parentReference; + this.handle = handle; + this.eventStatusType = eventStatusType; + this.callback = callback; + } + + /** + * {@inheritDoc} + */ + public final Class getEventStatusType() { + return this.eventStatusType; + } + + /** + * {@inheritDoc} + */ + public final Class getParentType() { + return this.parentType; + } + + /** + * {@inheritDoc} + */ + public final WeakReference getParentReference() { + return this.parentReference; + } + + /** + * {@inheritDoc} + */ + public final long getHandle() { + return this.handle; + } + + /** + * Destroy a ROS 2 event (rcl_event_t). + * + * @param handle A pointer to the underlying ROS 2 event structure, + * as an integer. Must not be zero. + */ + private static native void nativeDispose(long handle); + + /** + * {@inheritDoc} + */ + public final void dispose() { + nativeDispose(this.handle); + this.handle = 0; + } + + /** + * Takes the RCL event status and returns a pointer to it. + * + * @param event_handle A pointer to the underlying ROS 2 event (rcl_event_t). + * @param event_status_handle A pointer to the underlying ROS 2 event status (void *). + * Must not be zero. + */ + private static native void nativeTake(long event_handle, long event_status_handle); + + /** + * {@inheritDoc} + */ + public final void executeCallback() { + T eventStatus = null; + try { + eventStatus = this.eventStatusType.getDeclaredConstructor().newInstance(); + } catch (NoSuchMethodException nme) { + nme.printStackTrace(); + } catch (InvocationTargetException ite) { + ite.printStackTrace(); + } catch (InstantiationException ie) { + ie.printStackTrace(); + } catch (IllegalAccessException iae) { + iae.printStackTrace(); + } + long nativeEventStatusHandle = eventStatus.allocateRCLStatusEvent(); + nativeTake(this.handle, nativeEventStatusHandle); + eventStatus.fromRCLEvent(nativeEventStatusHandle); + eventStatus.deallocateRCLStatusEvent(nativeEventStatusHandle); + callback.accept(eventStatus); + } + + private final Class eventStatusType; + private final Class parentType; + private final WeakReference parentReference; + private long handle; + private final Consumer callback; +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/EventStatus.java b/rcljava/src/main/java/org/ros2/rcljava/events/EventStatus.java new file mode 100644 index 00000000..4b129826 --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/events/EventStatus.java @@ -0,0 +1,43 @@ +// Copyright 2020 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.events; + +import org.ros2.rcljava.interfaces.Disposable; + +/** + * This class serves as a bridge between ROS2's rcl event status types and RCLJava. + */ +public interface EventStatus { + /** + * Allocates an rcl event status. + * + * @return A pointer to the allocated status. + */ + long allocateRCLStatusEvent(); + + /** + * Deallocates an rcl event status. + * + * @param handle Pointer to a previously allocated event status. + */ + void deallocateRCLStatusEvent(long handle); + + /** + * Loads the event with the data of the rcl event status. + * + * @param handle A pointer to the underlying event status. + */ + void fromRCLEvent(long handle); +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/PublisherEventStatus.java b/rcljava/src/main/java/org/ros2/rcljava/events/PublisherEventStatus.java new file mode 100644 index 00000000..b6c9881b --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/events/PublisherEventStatus.java @@ -0,0 +1,28 @@ +// Copyright 2020 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.events; + +import org.ros2.rcljava.events.EventStatus; + +/** + * This class serves as a bridge between ROS2's rcl publisher event + * status types and RCLJava. + */ +public interface PublisherEventStatus extends EventStatus { + /** + * @return The status event type. + */ + int get_publisher_event_type(); +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/SubscriptionEventStatus.java b/rcljava/src/main/java/org/ros2/rcljava/events/SubscriptionEventStatus.java new file mode 100644 index 00000000..224e9e53 --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/events/SubscriptionEventStatus.java @@ -0,0 +1,28 @@ +// Copyright 2020 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.events; + +import org.ros2.rcljava.events.EventStatus; + +/** + * This class serves as a bridge between ROS2's rcl subscription event + * status types and RCLJava. + */ +public interface SubscriptionEventStatus extends EventStatus { + /** + * @return The status event type. + */ + int get_subscription_event_type(); +}