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
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 @@ -155,6 +155,15 @@ JNIEXPORT void
JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeGetSubscriptionNamesAndTypesByNode(
JNIEnv *, jclass, jlong, jstring, jstring, jobject);

/*
* Class: org_ros2_rcljava_node_NodeImpl
* Method: nativeServiceNamesAndTypesByNode
* Signature: (JLjava/lang/String;Ljava/lang/String;Ljava/util/Collection;)V
*/
JNIEXPORT void
JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeGetServiceNamesAndTypesByNode(
JNIEnv *, jclass, jlong, jstring, jstring, jobject);

#ifdef __cplusplus
}
#endif
Expand Down
36 changes: 36 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 @@ -575,3 +575,39 @@ Java_org_ros2_rcljava_node_NodeImpl_nativeGetSubscriptionNamesAndTypesByNode(
RCLJAVA_COMMON_THROW_FROM_RCL(env, ret, "failed to get subscription names and types");
fill_jnames_and_types(env, subscription_names_and_types, jnames_and_types);
}

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

const char * name = env->GetStringUTFChars(jname, NULL);
auto release_jname = rcpputils::make_scope_exit(
[jname, name, env]() {env->ReleaseStringUTFChars(jname, name);});
const char * namespace_ = env->GetStringUTFChars(jnamespace, NULL);
auto release_jnamespace = rcpputils::make_scope_exit(
[jnamespace, namespace_, env]() {env->ReleaseStringUTFChars(jnamespace, namespace_);});
rcl_allocator_t allocator = rcl_get_default_allocator();
rcl_names_and_types_t service_names_and_types = rcl_get_zero_initialized_names_and_types();
auto fini_names_and_types = rcpputils::make_scope_exit(
[pnames_and_types = &service_names_and_types, env]() {
rcl_ret_t ret = rcl_names_and_types_fini(pnames_and_types);
if (!env->ExceptionCheck() && RCL_RET_OK != ret) {
rcljava_throw_rclexception(
env, ret, "failed to fini service names and types structure");
}
});

rcl_ret_t ret = rcl_get_service_names_and_types_by_node(
node,
&allocator,
name,
namespace_,
&service_names_and_types);
RCLJAVA_COMMON_THROW_FROM_RCL(env, ret, "failed to get service names and types");
fill_jnames_and_types(env, service_names_and_types, jnames_and_types);
}
12 changes: 12 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 @@ -649,4 +649,16 @@ <T extends ServiceDefinition> Client<T> createClient(final Class<T> serviceType,
*/
Collection<NameAndTypes> getSubscriptionNamesAndTypesByNode(
String nodeName, String nodeNamespace);

/**
* Return the service server names and types that were created from the node specified by the
* given node name and namespace.
* See @{link graph#NameAndTypes} for more information about the returned value.
*
* @param nodeName name of the node we want to know its services.
* @param nodeNamespace namespace of the node we want to know its services.
* @return the detected service server names and types.
*/
Collection<NameAndTypes> getServiceNamesAndTypesByNode(
String nodeName, String nodeNamespace);
}
11 changes: 11 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 @@ -838,4 +838,15 @@ public final Collection<NameAndTypes> getSubscriptionNamesAndTypesByNode(

private static native final Collection<NameAndTypes> nativeGetSubscriptionNamesAndTypesByNode(
long handle, String nodeName, String nodeNamespace, Collection<NameAndTypes> namesAndTypes);

public final Collection<NameAndTypes> getServiceNamesAndTypesByNode(
String nodeName, String nodeNamespace)
{
Collection<NameAndTypes> namesAndTypes = new ArrayList();
nativeGetServiceNamesAndTypesByNode(this.handle, nodeName, nodeNamespace, namesAndTypes);
return namesAndTypes;
}

private static native final Collection<NameAndTypes> nativeGetServiceNamesAndTypesByNode(
long handle, String nodeName, String nodeNamespace, Collection<NameAndTypes> namesAndTypes);
}
118 changes: 118 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 @@ -1354,4 +1354,122 @@ public void accept(final Collection<NameAndTypes> local, Collection<NameAndTypes
publisher.dispose();
remoteNode.dispose();
}

@Test
public final void testGetServiceNamesAndTypesByNode() throws Exception {
final Node remoteNode = RCLJava.createNode("test_get_service_names_and_types_remote_node");
Service<rcljava.srv.AddTwoInts> service1 = node.<rcljava.srv.AddTwoInts>createService(
rcljava.srv.AddTwoInts.class, "test_get_service_names_and_types_one",
new TriConsumer<
RMWRequestId, rcljava.srv.AddTwoInts_Request, rcljava.srv.AddTwoInts_Response>()
{
public final void accept(
final RMWRequestId header,
final rcljava.srv.AddTwoInts_Request request,
final rcljava.srv.AddTwoInts_Response response)
{}
});
Service<rcljava.srv.AddTwoInts> service2 = node.<rcljava.srv.AddTwoInts>createService(
rcljava.srv.AddTwoInts.class, "test_get_service_names_and_types_two",
new TriConsumer<
RMWRequestId, rcljava.srv.AddTwoInts_Request, rcljava.srv.AddTwoInts_Response>()
{
public final void accept(
final RMWRequestId header,
final rcljava.srv.AddTwoInts_Request request,
final rcljava.srv.AddTwoInts_Response response)
{}
});
Service<rcljava.srv.AddTwoInts> service3 = remoteNode.<rcljava.srv.AddTwoInts>createService(
rcljava.srv.AddTwoInts.class, "test_get_service_names_and_types_two",
new TriConsumer<
RMWRequestId, rcljava.srv.AddTwoInts_Request, rcljava.srv.AddTwoInts_Response>()
{
public final void accept(
final RMWRequestId header,
final rcljava.srv.AddTwoInts_Request request,
final rcljava.srv.AddTwoInts_Response response)
{}
});
Service<rcljava.srv.AddTwoInts> service4 = remoteNode.<rcljava.srv.AddTwoInts>createService(
rcljava.srv.AddTwoInts.class, "test_get_service_names_and_types_three",
new TriConsumer<
RMWRequestId, rcljava.srv.AddTwoInts_Request, rcljava.srv.AddTwoInts_Response>()
{
public final void accept(
final RMWRequestId header,
final rcljava.srv.AddTwoInts_Request request,
final rcljava.srv.AddTwoInts_Response response)
{}
});
Client<rcljava.srv.AddTwoInts> client = node.<rcljava.srv.AddTwoInts>createClient(
rcljava.srv.AddTwoInts.class, "test_get_service_names_and_types_this_should_not_appear");

BiConsumer<Collection<NameAndTypes>, Collection<NameAndTypes>> validateNameAndTypes =
new BiConsumer<Collection<NameAndTypes>, Collection<NameAndTypes>>() {
public void accept(final Collection<NameAndTypes> local, Collection<NameAndTypes> remote) {
// TODO(ivanpauno): Using assertj may help a lot here https://assertj.github.io/doc/.
assertEquals(local.size(), 2);
assertTrue(
"service 'test_get_service_names_and_types_one' was not discovered for local node",
local.contains(
new NameAndTypes(
"/test_get_service_names_and_types_one",
new ArrayList(Arrays.asList("rcljava/srv/AddTwoInts")))));
assertTrue(
"service 'test_get_service_names_and_types_two' was not discovered for local node",
local.contains(
new NameAndTypes(
"/test_get_service_names_and_types_two",
new ArrayList(Arrays.asList("rcljava/srv/AddTwoInts")))));

assertEquals(remote.size(), 2);
assertTrue(
"service 'test_get_service_names_and_types_two' was not discovered for remote node",
remote.contains(
new NameAndTypes(
"/test_get_service_names_and_types_two",
new ArrayList(Arrays.asList("rcljava/srv/AddTwoInts")))));
assertTrue(
"service 'test_get_service_names_and_types_three' was not discovered for remote node",
remote.contains(
new NameAndTypes(
"/test_get_service_names_and_types_three",
new ArrayList(Arrays.asList("rcljava/srv/AddTwoInts")))));
}
};

long start = System.currentTimeMillis();
boolean ok = false;
Collection<NameAndTypes> local = null;
Collection<NameAndTypes> remote = null;
do {
local = this.node.getServiceNamesAndTypesByNode("test_node", "/");
remote = this.node.getServiceNamesAndTypesByNode(
"test_get_service_names_and_types_remote_node", "/");
try {
validateNameAndTypes.accept(local, remote);
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(local);
assertNotNull(remote);
validateNameAndTypes.accept(local, remote);

service1.dispose();
service2.dispose();
service3.dispose();
service4.dispose();
client.dispose();
remoteNode.dispose();
}
}