Skip to content
Merged
2 changes: 2 additions & 0 deletions rcljava/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ set(${PROJECT_NAME}_jni_sources
"src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp"
"src/main/cpp/org_ros2_rcljava_subscription_SubscriptionImpl.cpp"
"src/main/cpp/org_ros2_rcljava_subscription_statuses_LivelinessChanged.cpp"
"src/main/cpp/org_ros2_rcljava_subscription_statuses_MessageLost.cpp"
"src/main/cpp/org_ros2_rcljava_subscription_statuses_RequestedDeadlineMissed.cpp"
"src/main/cpp/org_ros2_rcljava_subscription_statuses_RequestedQosIncompatible.cpp"
"src/main/cpp/org_ros2_rcljava_time_Clock.cpp"
Expand Down Expand Up @@ -180,6 +181,7 @@ set(${PROJECT_NAME}_sources
"src/main/java/org/ros2/rcljava/subscription/Subscription.java"
"src/main/java/org/ros2/rcljava/subscription/SubscriptionImpl.java"
"src/main/java/org/ros2/rcljava/subscription/statuses/LivelinessChanged.java"
"src/main/java/org/ros2/rcljava/subscription/statuses/MessageLost.java"
"src/main/java/org/ros2/rcljava/subscription/statuses/RequestedDeadlineMissed.java"
"src/main/java/org/ros2/rcljava/subscription/statuses/RequestedQosIncompatible.java"
"src/main/java/org/ros2/rcljava/time/Clock.java"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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 <jni.h>
/* Header for class org_ros2_rcljava_subscription_statuses_MessageLost */

#ifndef ORG_ROS2_RCLJAVA_SUBSCRIPTION_STATUSES_MESSAGELOST_H_
#define ORG_ROS2_RCLJAVA_SUBSCRIPTION_STATUSES_MESSAGELOST_H_
#ifdef __cplusplus
extern "C" {
#endif

/*
* Class: org_ros2_rcljava_subscription_statuses_MessageLost
* Method: nativeAllocateRCLStatusEvent
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_org_ros2_rcljava_subscription_statuses_MessageLost_nativeAllocateRCLStatusEvent(
JNIEnv *, jclass);

/*
* Class: org_ros2_rcljava_subscription_statuses_MessageLost
* Method: nativeDeallocateRCLStatusEvent
* Signature: (J)V
*/
JNIEXPORT void JNICALL
Java_org_ros2_rcljava_subscription_statuses_MessageLost_nativeDeallocateRCLStatusEvent(
JNIEnv *, jclass, jlong);

/*
* Class: org_ros2_rcljava_subscription_statuses_MessageLost
* Method: nativeFromRCLEvent
* Signature: (J)V
*/
JNIEXPORT void JNICALL
Java_org_ros2_rcljava_subscription_statuses_MessageLost_nativeFromRCLEvent(
JNIEnv *, jobject, jlong);

/*
* Class: org_ros2_rcljava_subscription_statuses_MessageLost
* Method: nativeGetsubscriptionEventType
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_org_ros2_rcljava_subscription_statuses_MessageLost_nativeGetSubscriptionEventType(
JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif // ORG_ROS2_RCLJAVA_SUBSCRIPTION_STATUSES_MESSAGELOST_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 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 "org_ros2_rcljava_subscription_statuses_MessageLost.h"

#include <jni.h>
#include <stdlib.h>

#include "rmw/types.h"
#include "rcl/event.h"
#include "rcljava_common/exceptions.hpp"

using rcljava_common::exceptions::rcljava_throw_exception;

JNIEXPORT jlong JNICALL
Java_org_ros2_rcljava_subscription_statuses_MessageLost_nativeAllocateRCLStatusEvent(
JNIEnv * env, jclass)
{
void * p = malloc(sizeof(rmw_message_lost_status_t));
if (!p) {
rcljava_throw_exception(
env, "java/lang/OutOfMemoryError", "failed to allocate missed deadline status");
}
return reinterpret_cast<jlong>(p);
}

JNIEXPORT void JNICALL
Java_org_ros2_rcljava_subscription_statuses_MessageLost_nativeDeallocateRCLStatusEvent(
JNIEnv *, jclass, jlong handle)
{
free(reinterpret_cast<void *>(handle));
}

JNIEXPORT void JNICALL
Java_org_ros2_rcljava_subscription_statuses_MessageLost_nativeFromRCLEvent(
JNIEnv * env, jobject self, jlong handle)
{
auto * p = reinterpret_cast<rmw_message_lost_status_t *>(handle);
if (!p) {
rcljava_throw_exception(
env, "java/lang/IllegalArgumentException", "passed rmw object handle is NULL");
return;
}
// TODO(ivanpauno): class and field lookup could be done at startup time
jclass clazz = env->GetObjectClass(self);
jfieldID total_count_fid = env->GetFieldID(clazz, "totalCount", "I");
if (env->ExceptionCheck()) {
return;
}
jfieldID total_count_change_fid = env->GetFieldID(clazz, "totalCountChange", "I");
if (env->ExceptionCheck()) {
return;
}
env->SetIntField(self, total_count_fid, p->total_count);
env->SetIntField(self, total_count_change_fid, p->total_count_change);
}

JNIEXPORT jint JNICALL
Java_org_ros2_rcljava_subscription_statuses_MessageLost_nativeGetSubscriptionEventType(
JNIEnv *, jclass)
{
return RCL_SUBSCRIPTION_MESSAGE_LOST;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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.subscription.statuses;

import java.util.function.Supplier;

import org.ros2.rcljava.common.JNIUtils;
import org.ros2.rcljava.events.SubscriptionEventStatus;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This class serves as a bridge between a rmw_message_lost_status_t and RCLJava.
*/
public class MessageLost implements SubscriptionEventStatus {
public int totalCount;
public int totalCountChange;

public final long allocateRCLStatusEvent() {
return nativeAllocateRCLStatusEvent();
}
public final void deallocateRCLStatusEvent(long handle) {
nativeDeallocateRCLStatusEvent(handle);
}
public final void fromRCLEvent(long handle) {
nativeFromRCLEvent(handle);
}
public final int getSubscriptionEventType() {
return nativeGetSubscriptionEventType();
}
// TODO(ivanpauno): Remove this when -source 8 can be used (method references for the win)
public static final Supplier<MessageLost> factory = new Supplier<MessageLost>() {
public MessageLost get() {
return new MessageLost();
}
};

private static native long nativeAllocateRCLStatusEvent();
private static native void nativeDeallocateRCLStatusEvent(long handle);
private native void nativeFromRCLEvent(long handle);
private static native int nativeGetSubscriptionEventType();

private static final Logger logger = LoggerFactory.getLogger(MessageLost.class);
static {
try {
JNIUtils.loadImplementation(MessageLost.class);
} catch (UnsatisfiedLinkError ule) {
logger.error("Native code library failed to load.\n" + ule);
System.exit(1);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.ros2.rcljava.events.EventHandler;
import org.ros2.rcljava.node.Node;
import org.ros2.rcljava.subscription.statuses.LivelinessChanged;
import org.ros2.rcljava.subscription.statuses.MessageLost;
import org.ros2.rcljava.subscription.statuses.RequestedDeadlineMissed;
import org.ros2.rcljava.subscription.statuses.RequestedQosIncompatible;

Expand Down Expand Up @@ -149,4 +150,32 @@ public void accept(final RequestedQosIncompatible status) {
RCLJava.shutdown();
assertEquals(0, eventHandler.getHandle());
}

@Test
public final void testCreateMessageLost() {
String identifier = RCLJava.getRMWIdentifier();
if (identifier.equals("rmw_fastrtps_cpp") || identifier.equals("rmw_fastrtps_dynamic_cpp")) {
// event not supported in these implementations
return;
}
RCLJava.rclJavaInit();
Node node = RCLJava.createNode("test_node");
Subscription<std_msgs.msg.String> subscription = node.<std_msgs.msg.String>createSubscription(
std_msgs.msg.String.class, "test_topic", new Consumer<std_msgs.msg.String>() {
public void accept(final std_msgs.msg.String msg) {}
});
EventHandler eventHandler = subscription.createEventHandler(
MessageLost.factory, new Consumer<MessageLost>() {
public void accept(final MessageLost status) {
assertEquals(status.totalCount, 0);
assertEquals(status.totalCountChange, 0);
}
}
);
assertNotEquals(0, eventHandler.getHandle());
// force executing the callback, so we check that taking an event works
eventHandler.executeCallback();
RCLJava.shutdown();
assertEquals(0, eventHandler.getHandle());
}
}
51 changes: 51 additions & 0 deletions rcljava_common/include/rcljava_common/exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,57 @@

#include "rcljava_common/visibility_control.hpp"

/// Execute \a error_statement if an exception has happened.
/**
* \param env a JNIEnv pointer, used to check for exceptions.
* \param error_statement statement executed if an exception has happened.
*/
#define RCLJAVA_COMMON_CHECK_FOR_EXCEPTION_WITH_ERROR_STATEMENT(env, error_statement) \
do { \
if (env->ExceptionCheck()) { \
error_statement; \
} \
} while (0)

/// Return from the current function if a java exception has happened.
/**
* \param env a JNIEnv pointer, used to check for exceptions.
*/
#define RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env) \
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION_WITH_ERROR_STATEMENT(env, return )

/// Call \ref rcljava_throw_rclexception if \a ret is not RCL_RET_OK,
/// and execute \a error_statement in that case.
/**
* The rcl error message will be added to \a base_message, and the rcl error state will be reset.
*
* \param env a JNIEnv pointer, used to throw a java exception from the rcl error.
* \param ret rcl_ret_t error that will be checked.
* \param base_message error message that will be passed to the thrown exception.
* The complete error will be "${base_message}: ${rcl_error_string}".
* \a base_message can be either a `const char *` or as `std::string`.
* \param error_statement statement executed if ret was not RCL_RET_OK.
*/
#define RCLJAVA_COMMON_THROW_FROM_RCL_WITH_ERROR_STATEMENT( \
env, ret, base_message, error_statement) \
do { \
if (RCL_RET_OK != ret) { \
rcljava_common::exceptions::rcljava_throw_rclexception( \
env, ret, static_cast<std::string>(base_message) + ": " + rcl_get_error_string().str); \
rcl_reset_error(); \
error_statement; \
} \
} while (0)

/// Call \ref rcljava_throw_rclexception if \a ret is not RCL_RET_OK and return.
/**
* \param env a JNIEnv pointer, used to check for exceptions.
* \param ret rcl_ret_t error that will be checked.
* \param base_message error message that will be passed to the thrown exception.
*/
#define RCLJAVA_COMMON_THROW_FROM_RCL(env, ret, base_message) \
RCLJAVA_COMMON_THROW_FROM_RCL_WITH_ERROR_STATEMENT(env, ret, base_message, return )

namespace rcljava_common
{
namespace exceptions
Expand Down