Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions rcljava/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,7 @@ set(${PROJECT_NAME}_sources
"src/main/java/org/ros2/rcljava/events/PublisherEventStatus.java"
"src/main/java/org/ros2/rcljava/events/SubscriptionEventStatus.java"
"src/main/java/org/ros2/rcljava/graph/NameAndTypes.java"
"src/main/java/org/ros2/rcljava/publisher/statuses/LivelinessLost.java"
"src/main/java/org/ros2/rcljava/publisher/statuses/OfferedDeadlineMissed.java"
"src/main/java/org/ros2/rcljava/publisher/statuses/OfferedQosIncompatible.java"
"src/main/java/org/ros2/rcljava/graph/NodeNameInfo.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"
Expand Down Expand Up @@ -173,6 +171,9 @@ set(${PROJECT_NAME}_sources
"src/main/java/org/ros2/rcljava/parameters/service/ParameterServiceImpl.java"
"src/main/java/org/ros2/rcljava/publisher/Publisher.java"
"src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java"
"src/main/java/org/ros2/rcljava/publisher/statuses/LivelinessLost.java"
"src/main/java/org/ros2/rcljava/publisher/statuses/OfferedDeadlineMissed.java"
"src/main/java/org/ros2/rcljava/publisher/statuses/OfferedQosIncompatible.java"
"src/main/java/org/ros2/rcljava/qos/policies/Durability.java"
"src/main/java/org/ros2/rcljava/qos/policies/History.java"
"src/main/java/org/ros2/rcljava/qos/policies/Liveliness.java"
Expand Down
9 changes: 9 additions & 0 deletions rcljava/include/org_ros2_rcljava_node_NodeImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ JNIEXPORT jlong
JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateTimerHandle(
JNIEnv *, jclass, jlong, jlong, jlong);

/*
* Class: org_ros2_rcljava_node_NodeImpl
* Method: nativeGetNodeNames
* Signature: (JLjava/util/List;)V
*/
JNIEXPORT void
JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeGetNodeNames(
JNIEnv *, jclass, jlong, jobject);

/*
* Class: org_ros2_rcljava_node_NodeImpl
* Method: nativeGetTopicNamesAndTypes
Expand Down
78 changes: 78 additions & 0 deletions rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,84 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreateTimerHandle(
return jtimer;
}

JNIEXPORT void JNICALL
Java_org_ros2_rcljava_node_NodeImpl_nativeGetNodeNames(
JNIEnv * env, jclass, jlong handle, jobject jnode_names_info)
{
rcl_node_t * node = reinterpret_cast<rcl_node_t *>(handle);
if (!node) {
rcljava_throw_exception(env, "java/lang/IllegalArgumentException", "node handle is NULL");
return;
}

jclass list_clazz = env->GetObjectClass(jnode_names_info);
jmethodID list_add_mid = env->GetMethodID(list_clazz, "add", "(Ljava/lang/Object;)Z");
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
jclass node_info_clazz = env->FindClass("org/ros2/rcljava/graph/NodeNameInfo");
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
jmethodID node_info_init_mid = env->GetMethodID(node_info_clazz, "<init>", "()V");
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
jfieldID name_fid = env->GetFieldID(node_info_clazz, "name", "Ljava/lang/String;");
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
jfieldID namespace_fid = env->GetFieldID(node_info_clazz, "namespace", "Ljava/lang/String;");
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
jfieldID enclave_fid = env->GetFieldID(node_info_clazz, "enclave", "Ljava/lang/String;");
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);

rcl_allocator_t allocator = rcl_get_default_allocator();
rcutils_string_array_t node_names = rcutils_get_zero_initialized_string_array();
rcutils_string_array_t node_namespaces = rcutils_get_zero_initialized_string_array();
rcutils_string_array_t enclaves = rcutils_get_zero_initialized_string_array();

rcl_ret_t ret = rcl_get_node_names_with_enclaves(
node,
allocator,
&node_names,
&node_namespaces,
&enclaves);
RCLJAVA_COMMON_THROW_FROM_RCL(env, ret, "rcl_get_node_names_with_enclaves failed");
auto on_scope_exit = rcpputils::make_scope_exit(
[pnames = &node_names, pnamespaces = &node_namespaces, penclaves = &enclaves, env]() {
rcl_ret_t ret = rcutils_string_array_fini(pnames);
if (!env->ExceptionCheck() && RCL_RET_OK != ret) {
rcljava_throw_rclexception(env, ret, "failed to fini node names string array");
}
ret = rcutils_string_array_fini(pnamespaces);
if (!env->ExceptionCheck() && RCL_RET_OK != ret) {
rcljava_throw_rclexception(env, ret, "failed to fini node namespaces string array");
}
ret = rcutils_string_array_fini(penclaves);
if (!env->ExceptionCheck() && RCL_RET_OK != ret) {
rcljava_throw_rclexception(env, ret, "failed to fini enclaves string array");
}
}
);

if (node_names.size != node_namespaces.size || node_names.size != enclaves.size) {
rcljava_throw_exception(
env,
"java/lang/IllegalStateException",
"names, namespaces and enclaves array leghts don't match");
return;
}

for (size_t i = 0; i < node_names.size; i++) {
jstring jnode_name = env->NewStringUTF(node_names.data[i]);
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
jstring jnode_namespace = env->NewStringUTF(node_namespaces.data[i]);
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
jstring jenclave = env->NewStringUTF(enclaves.data[i]);
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
jobject jitem = env->NewObject(node_info_clazz, node_info_init_mid);
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
env->SetObjectField(jitem, name_fid, jnode_name);
env->SetObjectField(jitem, namespace_fid, jnode_namespace);
env->SetObjectField(jitem, enclave_fid, jenclave);
env->CallBooleanMethod(jnode_names_info, list_add_mid, jitem);
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
}
}

JNIEXPORT void JNICALL
Java_org_ros2_rcljava_node_NodeImpl_nativeGetTopicNamesAndTypes(
JNIEnv * env, jclass, jlong handle, jobject jnames_and_types)
Expand Down
68 changes: 68 additions & 0 deletions rcljava/src/main/java/org/ros2/rcljava/graph/NodeNameInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* 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.graph;

import java.util.Objects;

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

import org.ros2.rcljava.common.JNIUtils;

/**
* Class that represents the queried information of a node in the graph.
*/
public class NodeNameInfo {
/// The node name
public String name;
/// The node namespace
public String namespace;
/// The security enclave of the node.
/**
* For further details, see:
* @{link http://design.ros2.org/articles/ros2_security_enclaves.html}
*/
public String enclave;

/// Constructor
public NodeNameInfo(final String name, final String namespace, final String enclave) {
this.name = name;
this.namespace = namespace;
this.enclave = enclave;
}

/// Default constructor, used from jni.
private NodeNameInfo() {}

@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof NodeNameInfo)) {
return false;
}
NodeNameInfo other = (NodeNameInfo) o;
return Objects.equals(this.name, other.name) &&
Objects.equals(this.namespace, other.namespace) &&
Objects.equals(this.enclave, other.enclave);
}

@Override
public int hashCode() {
return Objects.hash(this.name, this.namespace, this.enclave);
}
}
7 changes: 7 additions & 0 deletions rcljava/src/main/java/org/ros2/rcljava/node/Node.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.ros2.rcljava.consumers.TriConsumer;
import org.ros2.rcljava.graph.EndpointInfo;
import org.ros2.rcljava.graph.NameAndTypes;
import org.ros2.rcljava.graph.NodeNameInfo;
import org.ros2.rcljava.interfaces.Disposable;
import org.ros2.rcljava.interfaces.MessageDefinition;
import org.ros2.rcljava.interfaces.ServiceDefinition;
Expand Down Expand Up @@ -552,6 +553,12 @@ <T extends ServiceDefinition> Client<T> createClient(final Class<T> serviceType,
*/
rcl_interfaces.msg.ListParametersResult listParameters(List<String> prefixes, long depth);

/**
* Returns a collection of node names that were detected in the ROS graph.
* See @{link NodeNameInfo} for more information about the return value.
*/
Collection<NodeNameInfo> getNodeNames();

/**
* Return the topics names and types that were detected in the graph.
* See @{link graph#NameAndTypes} for more information about the returned value.
Expand Down
9 changes: 9 additions & 0 deletions rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.ros2.rcljava.concurrent.Callback;
import org.ros2.rcljava.consumers.Consumer;
import org.ros2.rcljava.consumers.TriConsumer;
import org.ros2.rcljava.graph.NodeNameInfo;
import org.ros2.rcljava.contexts.Context;
import org.ros2.rcljava.graph.EndpointInfo;
import org.ros2.rcljava.graph.NameAndTypes;
Expand Down Expand Up @@ -741,6 +742,14 @@ public rcl_interfaces.msg.ListParametersResult listParameters(
}
}

public final Collection<NodeNameInfo> getNodeNames() {
ArrayList<NodeNameInfo> nodeNames = new ArrayList();
nativeGetNodeNames(this.handle, nodeNames);
return nodeNames;
}

private native static final void nativeGetNodeNames(long handle, ArrayList<NodeNameInfo> nodeNames);

public final Collection<NameAndTypes> getTopicNamesAndTypes() {
Collection<NameAndTypes> namesAndTypes = new ArrayList();
nativeGetTopicNamesAndTypes(this.handle, namesAndTypes);
Expand Down
59 changes: 59 additions & 0 deletions rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.ros2.rcljava.executors.SingleThreadedExecutor;
import org.ros2.rcljava.graph.EndpointInfo;
import org.ros2.rcljava.graph.NameAndTypes;
import org.ros2.rcljava.graph.NodeNameInfo;
import org.ros2.rcljava.node.Node;
import org.ros2.rcljava.publisher.Publisher;
import org.ros2.rcljava.qos.policies.Reliability;
Expand Down Expand Up @@ -867,6 +868,64 @@ public Node getNode() {
assertEquals(0, subscriptionOne.getHandle());
subscriptionTwo.dispose();
assertEquals(0, subscriptionTwo.getHandle());
publisherNode.dispose();
assertEquals(0, publisherNode.getHandle());
subscriptionNodeOne.dispose();
assertEquals(0, subscriptionNodeOne.getHandle());
subscriptionNodeTwo.dispose();
assertEquals(0, subscriptionNodeTwo.getHandle());
}

@Test
public final void testGetNodeNames() throws Exception {
final Node node1 = RCLJava.createNode("test_get_node_names_1");
final Node node2 = RCLJava.createNode("test_get_node_names_2");
final Node node3 = RCLJava.createNode("test_get_node_names_3");

Consumer<Collection<NodeNameInfo>> validateNodeNameInfo =
new Consumer<Collection<NodeNameInfo>>() {
public void accept(final Collection<NodeNameInfo> nodeNamesInfo) {
assertEquals(4, nodeNamesInfo.size());
assertTrue(
"node 'test_node' was not discovered",
nodeNamesInfo.contains(new NodeNameInfo("test_node", "/", "/")));
assertTrue(
"node 'test_get_node_names_1' was not discovered",
nodeNamesInfo.contains(new NodeNameInfo("test_get_node_names_1", "/", "/")));
assertTrue(
"node 'test_get_node_names_2' was not discovered",
nodeNamesInfo.contains(new NodeNameInfo("test_get_node_names_1", "/", "/")));
assertTrue(
"node 'test_get_node_names_3' was not discovered",
nodeNamesInfo.contains(new NodeNameInfo("test_get_node_names_1", "/", "/")));
}
};

long start = System.currentTimeMillis();
boolean ok = false;
Collection<NodeNameInfo> nodeNamesInfo = null;
do {
nodeNamesInfo = this.node.getNodeNames();
try {
validateNodeNameInfo.accept(nodeNamesInfo);
ok = true;
} catch (AssertionError err) {
// ignore here, it's going to be validated again at the end.
}
// TODO(ivanpauno): We could wait for the graph guard condition to be triggered if that
// would be available.
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException err) {
// ignore
}
} while (!ok && System.currentTimeMillis() < start + 1000);
assertNotNull(nodeNamesInfo);
validateNodeNameInfo.accept(nodeNamesInfo);

node1.dispose();
node2.dispose();
node3.dispose();
}

@Test
Expand Down