Skip to content

Commit 9be7780

Browse files
committed
Add get node names method to Node class (osrf#31)
* Add NodeNameInfo class * Implement getNodeNames Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
1 parent 9b326a3 commit 9be7780

File tree

7 files changed

+234
-3
lines changed

7 files changed

+234
-3
lines changed

rcljava/CMakeLists.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,7 @@ set(${PROJECT_NAME}_sources
143143
"src/main/java/org/ros2/rcljava/events/PublisherEventStatus.java"
144144
"src/main/java/org/ros2/rcljava/events/SubscriptionEventStatus.java"
145145
"src/main/java/org/ros2/rcljava/graph/NameAndTypes.java"
146-
"src/main/java/org/ros2/rcljava/publisher/statuses/LivelinessLost.java"
147-
"src/main/java/org/ros2/rcljava/publisher/statuses/OfferedDeadlineMissed.java"
148-
"src/main/java/org/ros2/rcljava/publisher/statuses/OfferedQosIncompatible.java"
146+
"src/main/java/org/ros2/rcljava/graph/NodeNameInfo.java"
149147
"src/main/java/org/ros2/rcljava/executors/AnyExecutable.java"
150148
"src/main/java/org/ros2/rcljava/executors/BaseExecutor.java"
151149
"src/main/java/org/ros2/rcljava/executors/Executor.java"
@@ -173,6 +171,9 @@ set(${PROJECT_NAME}_sources
173171
"src/main/java/org/ros2/rcljava/parameters/service/ParameterServiceImpl.java"
174172
"src/main/java/org/ros2/rcljava/publisher/Publisher.java"
175173
"src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java"
174+
"src/main/java/org/ros2/rcljava/publisher/statuses/LivelinessLost.java"
175+
"src/main/java/org/ros2/rcljava/publisher/statuses/OfferedDeadlineMissed.java"
176+
"src/main/java/org/ros2/rcljava/publisher/statuses/OfferedQosIncompatible.java"
176177
"src/main/java/org/ros2/rcljava/qos/policies/Durability.java"
177178
"src/main/java/org/ros2/rcljava/qos/policies/History.java"
178179
"src/main/java/org/ros2/rcljava/qos/policies/Liveliness.java"

rcljava/include/org_ros2_rcljava_node_NodeImpl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,15 @@ JNIEXPORT jlong
9292
JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeCreateTimerHandle(
9393
JNIEnv *, jclass, jlong, jlong, jlong);
9494

95+
/*
96+
* Class: org_ros2_rcljava_node_NodeImpl
97+
* Method: nativeGetNodeNames
98+
* Signature: (JLjava/util/List;)V
99+
*/
100+
JNIEXPORT void
101+
JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeGetNodeNames(
102+
JNIEnv *, jclass, jlong, jobject);
103+
95104
/*
96105
* Class: org_ros2_rcljava_node_NodeImpl
97106
* Method: nativeGetTopicNamesAndTypes

rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,84 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeCreateTimerHandle(
242242
return jtimer;
243243
}
244244

245+
JNIEXPORT void JNICALL
246+
Java_org_ros2_rcljava_node_NodeImpl_nativeGetNodeNames(
247+
JNIEnv * env, jclass, jlong handle, jobject jnode_names_info)
248+
{
249+
rcl_node_t * node = reinterpret_cast<rcl_node_t *>(handle);
250+
if (!node) {
251+
rcljava_throw_exception(env, "java/lang/IllegalArgumentException", "node handle is NULL");
252+
return;
253+
}
254+
255+
jclass list_clazz = env->GetObjectClass(jnode_names_info);
256+
jmethodID list_add_mid = env->GetMethodID(list_clazz, "add", "(Ljava/lang/Object;)Z");
257+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
258+
jclass node_info_clazz = env->FindClass("org/ros2/rcljava/graph/NodeNameInfo");
259+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
260+
jmethodID node_info_init_mid = env->GetMethodID(node_info_clazz, "<init>", "()V");
261+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
262+
jfieldID name_fid = env->GetFieldID(node_info_clazz, "name", "Ljava/lang/String;");
263+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
264+
jfieldID namespace_fid = env->GetFieldID(node_info_clazz, "namespace", "Ljava/lang/String;");
265+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
266+
jfieldID enclave_fid = env->GetFieldID(node_info_clazz, "enclave", "Ljava/lang/String;");
267+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
268+
269+
rcl_allocator_t allocator = rcl_get_default_allocator();
270+
rcutils_string_array_t node_names = rcutils_get_zero_initialized_string_array();
271+
rcutils_string_array_t node_namespaces = rcutils_get_zero_initialized_string_array();
272+
rcutils_string_array_t enclaves = rcutils_get_zero_initialized_string_array();
273+
274+
rcl_ret_t ret = rcl_get_node_names_with_enclaves(
275+
node,
276+
allocator,
277+
&node_names,
278+
&node_namespaces,
279+
&enclaves);
280+
RCLJAVA_COMMON_THROW_FROM_RCL(env, ret, "rcl_get_node_names_with_enclaves failed");
281+
auto on_scope_exit = rcpputils::make_scope_exit(
282+
[pnames = &node_names, pnamespaces = &node_namespaces, penclaves = &enclaves, env]() {
283+
rcl_ret_t ret = rcutils_string_array_fini(pnames);
284+
if (!env->ExceptionCheck() && RCL_RET_OK != ret) {
285+
rcljava_throw_rclexception(env, ret, "failed to fini node names string array");
286+
}
287+
ret = rcutils_string_array_fini(pnamespaces);
288+
if (!env->ExceptionCheck() && RCL_RET_OK != ret) {
289+
rcljava_throw_rclexception(env, ret, "failed to fini node namespaces string array");
290+
}
291+
ret = rcutils_string_array_fini(penclaves);
292+
if (!env->ExceptionCheck() && RCL_RET_OK != ret) {
293+
rcljava_throw_rclexception(env, ret, "failed to fini enclaves string array");
294+
}
295+
}
296+
);
297+
298+
if (node_names.size != node_namespaces.size || node_names.size != enclaves.size) {
299+
rcljava_throw_exception(
300+
env,
301+
"java/lang/IllegalStateException",
302+
"names, namespaces and enclaves array leghts don't match");
303+
return;
304+
}
305+
306+
for (size_t i = 0; i < node_names.size; i++) {
307+
jstring jnode_name = env->NewStringUTF(node_names.data[i]);
308+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
309+
jstring jnode_namespace = env->NewStringUTF(node_namespaces.data[i]);
310+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
311+
jstring jenclave = env->NewStringUTF(enclaves.data[i]);
312+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
313+
jobject jitem = env->NewObject(node_info_clazz, node_info_init_mid);
314+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
315+
env->SetObjectField(jitem, name_fid, jnode_name);
316+
env->SetObjectField(jitem, namespace_fid, jnode_namespace);
317+
env->SetObjectField(jitem, enclave_fid, jenclave);
318+
env->CallBooleanMethod(jnode_names_info, list_add_mid, jitem);
319+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
320+
}
321+
}
322+
245323
JNIEXPORT void JNICALL
246324
Java_org_ros2_rcljava_node_NodeImpl_nativeGetTopicNamesAndTypes(
247325
JNIEnv * env, jclass, jlong handle, jobject jnames_and_types)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* Copyright 2020 Open Source Robotics Foundation, Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
package org.ros2.rcljava.graph;
17+
18+
import java.util.Objects;
19+
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
23+
import org.ros2.rcljava.common.JNIUtils;
24+
25+
/**
26+
* Class that represents the queried information of a node in the graph.
27+
*/
28+
public class NodeNameInfo {
29+
/// The node name
30+
public String name;
31+
/// The node namespace
32+
public String namespace;
33+
/// The security enclave of the node.
34+
/**
35+
* For further details, see:
36+
* @{link http://design.ros2.org/articles/ros2_security_enclaves.html}
37+
*/
38+
public String enclave;
39+
40+
/// Constructor
41+
public NodeNameInfo(final String name, final String namespace, final String enclave) {
42+
this.name = name;
43+
this.namespace = namespace;
44+
this.enclave = enclave;
45+
}
46+
47+
/// Default constructor, used from jni.
48+
private NodeNameInfo() {}
49+
50+
@Override
51+
public boolean equals(Object o) {
52+
if (o == this) {
53+
return true;
54+
}
55+
if (!(o instanceof NodeNameInfo)) {
56+
return false;
57+
}
58+
NodeNameInfo other = (NodeNameInfo) o;
59+
return Objects.equals(this.name, other.name) &&
60+
Objects.equals(this.namespace, other.namespace) &&
61+
Objects.equals(this.enclave, other.enclave);
62+
}
63+
64+
@Override
65+
public int hashCode() {
66+
return Objects.hash(this.name, this.namespace, this.enclave);
67+
}
68+
}

rcljava/src/main/java/org/ros2/rcljava/node/Node.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.ros2.rcljava.consumers.TriConsumer;
2727
import org.ros2.rcljava.graph.EndpointInfo;
2828
import org.ros2.rcljava.graph.NameAndTypes;
29+
import org.ros2.rcljava.graph.NodeNameInfo;
2930
import org.ros2.rcljava.interfaces.Disposable;
3031
import org.ros2.rcljava.interfaces.MessageDefinition;
3132
import org.ros2.rcljava.interfaces.ServiceDefinition;
@@ -552,6 +553,12 @@ <T extends ServiceDefinition> Client<T> createClient(final Class<T> serviceType,
552553
*/
553554
rcl_interfaces.msg.ListParametersResult listParameters(List<String> prefixes, long depth);
554555

556+
/**
557+
* Returns a collection of node names that were detected in the ROS graph.
558+
* See @{link NodeNameInfo} for more information about the return value.
559+
*/
560+
Collection<NodeNameInfo> getNodeNames();
561+
555562
/**
556563
* Return the topics names and types that were detected in the graph.
557564
* See @{link graph#NameAndTypes} for more information about the returned value.

rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.ros2.rcljava.concurrent.Callback;
2323
import org.ros2.rcljava.consumers.Consumer;
2424
import org.ros2.rcljava.consumers.TriConsumer;
25+
import org.ros2.rcljava.graph.NodeNameInfo;
2526
import org.ros2.rcljava.contexts.Context;
2627
import org.ros2.rcljava.graph.EndpointInfo;
2728
import org.ros2.rcljava.graph.NameAndTypes;
@@ -760,6 +761,14 @@ public rcl_interfaces.msg.ListParametersResult listParameters(
760761
}
761762
}
762763

764+
public final Collection<NodeNameInfo> getNodeNames() {
765+
ArrayList<NodeNameInfo> nodeNames = new ArrayList();
766+
nativeGetNodeNames(this.handle, nodeNames);
767+
return nodeNames;
768+
}
769+
770+
private native static final void nativeGetNodeNames(long handle, ArrayList<NodeNameInfo> nodeNames);
771+
763772
public final Collection<NameAndTypes> getTopicNamesAndTypes() {
764773
Collection<NameAndTypes> namesAndTypes = new ArrayList();
765774
nativeGetTopicNamesAndTypes(this.handle, namesAndTypes);

rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.ros2.rcljava.executors.SingleThreadedExecutor;
4747
import org.ros2.rcljava.graph.EndpointInfo;
4848
import org.ros2.rcljava.graph.NameAndTypes;
49+
import org.ros2.rcljava.graph.NodeNameInfo;
4950
import org.ros2.rcljava.node.Node;
5051
import org.ros2.rcljava.publisher.Publisher;
5152
import org.ros2.rcljava.qos.policies.Reliability;
@@ -879,6 +880,64 @@ public Node getNode() {
879880
assertEquals(0, subscriptionOne.getHandle());
880881
subscriptionTwo.dispose();
881882
assertEquals(0, subscriptionTwo.getHandle());
883+
publisherNode.dispose();
884+
assertEquals(0, publisherNode.getHandle());
885+
subscriptionNodeOne.dispose();
886+
assertEquals(0, subscriptionNodeOne.getHandle());
887+
subscriptionNodeTwo.dispose();
888+
assertEquals(0, subscriptionNodeTwo.getHandle());
889+
}
890+
891+
@Test
892+
public final void testGetNodeNames() throws Exception {
893+
final Node node1 = RCLJava.createNode("test_get_node_names_1");
894+
final Node node2 = RCLJava.createNode("test_get_node_names_2");
895+
final Node node3 = RCLJava.createNode("test_get_node_names_3");
896+
897+
Consumer<Collection<NodeNameInfo>> validateNodeNameInfo =
898+
new Consumer<Collection<NodeNameInfo>>() {
899+
public void accept(final Collection<NodeNameInfo> nodeNamesInfo) {
900+
assertEquals(4, nodeNamesInfo.size());
901+
assertTrue(
902+
"node 'test_node' was not discovered",
903+
nodeNamesInfo.contains(new NodeNameInfo("test_node", "/", "/")));
904+
assertTrue(
905+
"node 'test_get_node_names_1' was not discovered",
906+
nodeNamesInfo.contains(new NodeNameInfo("test_get_node_names_1", "/", "/")));
907+
assertTrue(
908+
"node 'test_get_node_names_2' was not discovered",
909+
nodeNamesInfo.contains(new NodeNameInfo("test_get_node_names_1", "/", "/")));
910+
assertTrue(
911+
"node 'test_get_node_names_3' was not discovered",
912+
nodeNamesInfo.contains(new NodeNameInfo("test_get_node_names_1", "/", "/")));
913+
}
914+
};
915+
916+
long start = System.currentTimeMillis();
917+
boolean ok = false;
918+
Collection<NodeNameInfo> nodeNamesInfo = null;
919+
do {
920+
nodeNamesInfo = this.node.getNodeNames();
921+
try {
922+
validateNodeNameInfo.accept(nodeNamesInfo);
923+
ok = true;
924+
} catch (AssertionError err) {
925+
// ignore here, it's going to be validated again at the end.
926+
}
927+
// TODO(ivanpauno): We could wait for the graph guard condition to be triggered if that
928+
// would be available.
929+
try {
930+
TimeUnit.MILLISECONDS.sleep(100);
931+
} catch (InterruptedException err) {
932+
// ignore
933+
}
934+
} while (!ok && System.currentTimeMillis() < start + 1000);
935+
assertNotNull(nodeNamesInfo);
936+
validateNodeNameInfo.accept(nodeNamesInfo);
937+
938+
node1.dispose();
939+
node2.dispose();
940+
node3.dispose();
882941
}
883942

884943
@Test

0 commit comments

Comments
 (0)