Skip to content

Commit d21707b

Browse files
committed
Add get subscription names and types by node method to Node class (#35)
Signed-off-by: Ivan Santiago Paunovic <[email protected]>
1 parent 7739871 commit d21707b

File tree

5 files changed

+164
-0
lines changed

5 files changed

+164
-0
lines changed

rcljava/include/org_ros2_rcljava_node_NodeImpl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,15 @@ JNIEXPORT void
146146
JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeGetPublisherNamesAndTypesByNode(
147147
JNIEnv *, jclass, jlong, jstring, jstring, jobject);
148148

149+
/*
150+
* Class: org_ros2_rcljava_node_NodeImpl
151+
* Method: nativeGetSubscriptionNamesAndTypesByNode
152+
* Signature: (JLjava/lang/String;Ljava/lang/String;Ljava/util/Collection;)V
153+
*/
154+
JNIEXPORT void
155+
JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeGetSubscriptionNamesAndTypesByNode(
156+
JNIEnv *, jclass, jlong, jstring, jstring, jobject);
157+
149158
#ifdef __cplusplus
150159
}
151160
#endif

rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,3 +525,41 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeGetPublisherNamesAndTypesByNode(
525525
RCLJAVA_COMMON_THROW_FROM_RCL(env, ret, "failed to get publisher names and types");
526526
fill_jnames_and_types(env, publisher_names_and_types, jnames_and_types);
527527
}
528+
529+
JNIEXPORT void JNICALL
530+
Java_org_ros2_rcljava_node_NodeImpl_nativeGetSubscriptionNamesAndTypesByNode(
531+
JNIEnv * env, jclass, jlong handle, jstring jname, jstring jnamespace, jobject jnames_and_types)
532+
{
533+
rcl_node_t * node = reinterpret_cast<rcl_node_t *>(handle);
534+
if (!node) {
535+
rcljava_throw_exception(env, "java/lang/IllegalArgumentException", "node handle is NULL");
536+
return;
537+
}
538+
539+
const char * name = env->GetStringUTFChars(jname, NULL);
540+
auto release_jname = rcpputils::make_scope_exit(
541+
[jname, name, env]() {env->ReleaseStringUTFChars(jname, name);});
542+
const char * namespace_ = env->GetStringUTFChars(jnamespace, NULL);
543+
auto release_jnamespace = rcpputils::make_scope_exit(
544+
[jnamespace, namespace_, env]() {env->ReleaseStringUTFChars(jnamespace, namespace_);});
545+
rcl_allocator_t allocator = rcl_get_default_allocator();
546+
rcl_names_and_types_t subscription_names_and_types = rcl_get_zero_initialized_names_and_types();
547+
auto fini_names_and_types = rcpputils::make_scope_exit(
548+
[pnames_and_types = &subscription_names_and_types, env]() {
549+
rcl_ret_t ret = rcl_names_and_types_fini(pnames_and_types);
550+
if (!env->ExceptionCheck() && RCL_RET_OK != ret) {
551+
rcljava_throw_rclexception(
552+
env, ret, "failed to fini subscription names and types structure");
553+
}
554+
});
555+
556+
rcl_ret_t ret = rcl_get_subscriber_names_and_types_by_node(
557+
node,
558+
&allocator,
559+
false,
560+
name,
561+
namespace_,
562+
&subscription_names_and_types);
563+
RCLJAVA_COMMON_THROW_FROM_RCL(env, ret, "failed to get subscription names and types");
564+
fill_jnames_and_types(env, subscription_names_and_types, jnames_and_types);
565+
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,4 +637,16 @@ <T extends ServiceDefinition> Client<T> createClient(final Class<T> serviceType,
637637
* @return the detected publisher names and types.
638638
*/
639639
Collection<NameAndTypes> getPublisherNamesAndTypesByNode(String nodeName, String nodeNamespace);
640+
641+
/**
642+
* Return the subscription names and types that were created from the node specified by the given
643+
* node name and namespace.
644+
* See @{link graph#NameAndTypes} for more information about the returned value.
645+
*
646+
* @param nodeName name of the node we want to know its subscriptions.
647+
* @param nodeNamespace namespace of the node we want to know its subscriptions.
648+
* @return the detected subscription names and types.
649+
*/
650+
Collection<NameAndTypes> getSubscriptionNamesAndTypesByNode(
651+
String nodeName, String nodeNamespace);
640652
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,4 +841,15 @@ public final Collection<NameAndTypes> getPublisherNamesAndTypesByNode(
841841

842842
private static native final Collection<NameAndTypes> nativeGetPublisherNamesAndTypesByNode(
843843
long handle, String nodeName, String nodeNamespace, Collection<NameAndTypes> namesAndTypes);
844+
845+
public final Collection<NameAndTypes> getSubscriptionNamesAndTypesByNode(
846+
String nodeName, String nodeNamespace)
847+
{
848+
Collection<NameAndTypes> namesAndTypes = new ArrayList();
849+
nativeGetSubscriptionNamesAndTypesByNode(this.handle, nodeName, nodeNamespace, namesAndTypes);
850+
return namesAndTypes;
851+
}
852+
853+
private static native final Collection<NameAndTypes> nativeGetSubscriptionNamesAndTypesByNode(
854+
long handle, String nodeName, String nodeNamespace, Collection<NameAndTypes> namesAndTypes);
844855
}

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

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,4 +1261,98 @@ public void accept(final Collection<NameAndTypes> local, Collection<NameAndTypes
12611261
subscription.dispose();
12621262
remoteNode.dispose();
12631263
}
1264+
1265+
@Test
1266+
public final void testGetSubscriptionNamesAndTypesByNode() throws Exception {
1267+
final Node remoteNode = RCLJava.createNode("test_get_subscription_names_and_types_remote_node");
1268+
Subscription<rcljava.msg.Empty> subscription1 = node.<rcljava.msg.Empty>createSubscription(
1269+
rcljava.msg.Empty.class, "test_get_subscription_names_and_types_one",
1270+
new Consumer<rcljava.msg.Empty>() {
1271+
public void accept(final rcljava.msg.Empty msg) {}
1272+
});
1273+
Subscription<rcljava.msg.Empty> subscription2 = node.<rcljava.msg.Empty>createSubscription(
1274+
rcljava.msg.Empty.class, "test_get_subscription_names_and_types_two",
1275+
new Consumer<rcljava.msg.Empty>() {
1276+
public void accept(final rcljava.msg.Empty msg) {}
1277+
});
1278+
Subscription<rcljava.msg.Empty> subscription3 = remoteNode.<rcljava.msg.Empty>createSubscription(
1279+
rcljava.msg.Empty.class, "test_get_subscription_names_and_types_two",
1280+
new Consumer<rcljava.msg.Empty>() {
1281+
public void accept(final rcljava.msg.Empty msg) {}
1282+
});
1283+
Subscription<rcljava.msg.Empty> subscription4 = remoteNode.<rcljava.msg.Empty>createSubscription(
1284+
rcljava.msg.Empty.class, "test_get_subscription_names_and_types_three",
1285+
new Consumer<rcljava.msg.Empty>() {
1286+
public void accept(final rcljava.msg.Empty msg) {}
1287+
});
1288+
Publisher<rcljava.msg.UInt32> publisher = node.<rcljava.msg.UInt32>createPublisher(
1289+
rcljava.msg.UInt32.class, "test_get_topic_names_and_types_this_should_not_appear");
1290+
1291+
BiConsumer<Collection<NameAndTypes>, Collection<NameAndTypes>> validateNameAndTypes =
1292+
new BiConsumer<Collection<NameAndTypes>, Collection<NameAndTypes>>() {
1293+
public void accept(final Collection<NameAndTypes> local, Collection<NameAndTypes> remote) {
1294+
// TODO(ivanpauno): Using assertj may help a lot here https://assertj.github.io/doc/.
1295+
assertEquals(local.size(), 2);
1296+
assertTrue(
1297+
"topic 'test_get_subscription_names_and_types_one' was not discovered for local node",
1298+
local.contains(
1299+
new NameAndTypes(
1300+
"/test_get_subscription_names_and_types_one",
1301+
new ArrayList(Arrays.asList("rcljava/msg/Empty")))));
1302+
assertTrue(
1303+
"topic 'test_get_subscription_names_and_types_two' was not discovered for local node",
1304+
local.contains(
1305+
new NameAndTypes(
1306+
"/test_get_subscription_names_and_types_two",
1307+
new ArrayList(Arrays.asList("rcljava/msg/Empty")))));
1308+
1309+
assertEquals(remote.size(), 2);
1310+
assertTrue(
1311+
"topic 'test_get_subscription_names_and_types_two' was not discovered for remote node",
1312+
remote.contains(
1313+
new NameAndTypes(
1314+
"/test_get_subscription_names_and_types_two",
1315+
new ArrayList(Arrays.asList("rcljava/msg/Empty")))));
1316+
assertTrue(
1317+
"topic 'test_get_subscription_names_and_types_three' was not discovered for remote node",
1318+
remote.contains(
1319+
new NameAndTypes(
1320+
"/test_get_subscription_names_and_types_three",
1321+
new ArrayList(Arrays.asList("rcljava/msg/Empty")))));
1322+
}
1323+
};
1324+
1325+
long start = System.currentTimeMillis();
1326+
boolean ok = false;
1327+
Collection<NameAndTypes> local = null;
1328+
Collection<NameAndTypes> remote = null;
1329+
do {
1330+
local = this.node.getSubscriptionNamesAndTypesByNode("test_node", "/");
1331+
remote = this.node.getSubscriptionNamesAndTypesByNode(
1332+
"test_get_subscription_names_and_types_remote_node", "/");
1333+
try {
1334+
validateNameAndTypes.accept(local, remote);
1335+
ok = true;
1336+
} catch (AssertionError err) {
1337+
// ignore here, it's going to be validated again at the end.
1338+
}
1339+
// TODO(ivanpauno): We could wait for the graph guard condition to be triggered if that
1340+
// would be available.
1341+
try {
1342+
TimeUnit.MILLISECONDS.sleep(100);
1343+
} catch (InterruptedException err) {
1344+
// ignore
1345+
}
1346+
} while (!ok && System.currentTimeMillis() < start + 1000);
1347+
assertNotNull(local);
1348+
assertNotNull(remote);
1349+
validateNameAndTypes.accept(local, remote);
1350+
1351+
subscription1.dispose();
1352+
subscription2.dispose();
1353+
subscription3.dispose();
1354+
subscription4.dispose();
1355+
publisher.dispose();
1356+
remoteNode.dispose();
1357+
}
12641358
}

0 commit comments

Comments
 (0)