Skip to content

Commit 4405557

Browse files
authored
Add missing methods for node (#1140)
Adds support for querying node names with enclaves and retrieving a node’s fully qualified name. - Introduce `getNodeNamesAndNamespacesWithEnclaves()` and `getFullyQualifiedName()` in the JS API - Implement optional enclave retrieval and new fully qualified name binding in C++ - Add corresponding unit and type-check tests Fix: #1139
1 parent 67e5203 commit 4405557

File tree

5 files changed

+113
-9
lines changed

5 files changed

+113
-9
lines changed

lib/node.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1053,7 +1053,15 @@ class Node extends rclnodejs.ShadowNode {
10531053
* @return {Array<{name: string, namespace: string}>} An array of the names and namespaces.
10541054
*/
10551055
getNodeNamesAndNamespaces() {
1056-
return rclnodejs.getNodeNames(this.handle);
1056+
return rclnodejs.getNodeNames(this.handle, /*getEnclaves=*/ false);
1057+
}
1058+
1059+
/**
1060+
* Get the list of nodes and their namespaces with enclaves discovered by the provided node.
1061+
* @return {Array<{name: string, namespace: string, enclave: string}>} An array of the names, namespaces and enclaves.
1062+
*/
1063+
getNodeNamesAndNamespacesWithEnclaves() {
1064+
return rclnodejs.getNodeNames(this.handle, /*getEnclaves=*/ true);
10571065
}
10581066

10591067
/**
@@ -1610,6 +1618,15 @@ class Node extends rclnodejs.ShadowNode {
16101618
}
16111619
}
16121620

1621+
/**
1622+
* Get the fully qualified name of the node.
1623+
*
1624+
* @returns {string} - String containing the fully qualified name of the node.
1625+
*/
1626+
getFullyQualifiedName() {
1627+
return rclnodejs.getFullyQualifiedName(this.handle);
1628+
}
1629+
16131630
// returns on 1st error or result {successful, reason}
16141631
_validateParameters(parameters = [], declareParameterMode = false) {
16151632
for (const parameter of parameters) {

src/rcl_node_bindings.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -385,40 +385,67 @@ Napi::Value GetNodeNames(const Napi::CallbackInfo& info) {
385385

386386
RclHandle* node_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
387387
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
388+
bool get_enclaves = info[1].As<Napi::Boolean>().Value();
388389
rcutils_string_array_t node_names =
389390
rcutils_get_zero_initialized_string_array();
390391
rcutils_string_array_t node_namespaces =
391392
rcutils_get_zero_initialized_string_array();
393+
rcutils_string_array_t enclaves = rcutils_get_zero_initialized_string_array();
392394
rcl_allocator_t allocator = rcl_get_default_allocator();
393395

394-
THROW_ERROR_IF_NOT_EQUAL(
395-
RCL_RET_OK,
396-
rcl_get_node_names(node, allocator, &node_names, &node_namespaces),
397-
"Failed to get_node_names.");
396+
if (get_enclaves) {
397+
THROW_ERROR_IF_NOT_EQUAL(
398+
RCL_RET_OK,
399+
rcl_get_node_names_with_enclaves(node, allocator, &node_names,
400+
&node_namespaces, &enclaves),
401+
"Failed to get_node_names.");
402+
} else {
403+
THROW_ERROR_IF_NOT_EQUAL(
404+
RCL_RET_OK,
405+
rcl_get_node_names(node, allocator, &node_names, &node_namespaces),
406+
"Failed to get_node_names.");
407+
}
398408

399409
Napi::Array result_list = Napi::Array::New(env, node_names.size);
400410

401411
for (size_t i = 0; i < node_names.size; ++i) {
402412
Napi::Object item = Napi::Object::New(env);
403-
404413
item.Set("name", Napi::String::New(env, node_names.data[i]));
405414
item.Set("namespace", Napi::String::New(env, node_namespaces.data[i]));
406-
415+
if (get_enclaves) {
416+
item.Set("enclave", Napi::String::New(env, enclaves.data[i]));
417+
}
407418
result_list.Set(i, item);
408419
}
409420

410421
rcutils_ret_t fini_names_ret = rcutils_string_array_fini(&node_names);
411422
rcutils_ret_t fini_namespaces_ret =
412423
rcutils_string_array_fini(&node_namespaces);
413-
424+
rcutils_ret_t fini_enclaves_ret = rcutils_string_array_fini(&enclaves);
414425
THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, fini_names_ret,
415426
"Failed to destroy node_names");
416427
THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, fini_namespaces_ret,
417428
"Failed to destroy node_namespaces");
418-
429+
THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, fini_enclaves_ret,
430+
"Failed to fini enclaves string array");
419431
return result_list;
420432
}
421433

434+
Napi::Value GetFullyQualifiedName(const Napi::CallbackInfo& info) {
435+
Napi::Env env = info.Env();
436+
437+
RclHandle* node_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
438+
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
439+
const char* fully_qualified_node_name =
440+
rcl_node_get_fully_qualified_name(node);
441+
if (!fully_qualified_node_name) {
442+
Napi::Error::New(env, "Fully qualified name not set")
443+
.ThrowAsJavaScriptException();
444+
return env.Undefined();
445+
}
446+
return Napi::String::New(env, fully_qualified_node_name);
447+
}
448+
422449
Napi::Object InitNodeBindings(Napi::Env env, Napi::Object exports) {
423450
exports.Set("getParameterOverrides",
424451
Napi::Function::New(env, GetParameterOverrides));
@@ -439,6 +466,8 @@ Napi::Object InitNodeBindings(Napi::Env env, Napi::Object exports) {
439466
exports.Set("countServices", Napi::Function::New(env, CountServices));
440467
#endif
441468
exports.Set("getNodeNames", Napi::Function::New(env, GetNodeNames));
469+
exports.Set("getFullyQualifiedName",
470+
Napi::Function::New(env, GetFullyQualifiedName));
442471
return exports;
443472
}
444473

test/test-node.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@ describe('rclnodejs node test suite', function () {
4848
assert.deepStrictEqual(node.namespace(), '/ns');
4949
});
5050

51+
it('Get fully qualified name', function () {
52+
let nodeName = 'example_node_with_ns',
53+
nodeNamespace = '/ns';
54+
55+
var node = rclnodejs.createNode(nodeName, nodeNamespace);
56+
assert.deepStrictEqual(
57+
node.getFullyQualifiedName(),
58+
'/ns/example_node_with_ns'
59+
);
60+
});
61+
5162
it('Try creating a node with the empty namespace', function () {
5263
let nodeName = 'example_node_with_empty_ns',
5364
nodeNamespace = '';
@@ -398,6 +409,17 @@ describe('rcl node methods testing', function () {
398409
assert.strictEqual(currentNode.namespace, '/my_ns');
399410
});
400411

412+
it('node.getNodeNamesAndNamespacesWithEnclaves', function () {
413+
var nodeNames = node.getNodeNamesAndNamespacesWithEnclaves();
414+
var currentNode = nodeNames.find(function (nodeName) {
415+
return nodeName.name === 'my_node';
416+
});
417+
418+
assert.strictEqual(currentNode.name, 'my_node');
419+
assert.strictEqual(currentNode.namespace, '/my_ns');
420+
assert.strictEqual(currentNode.enclave, '/');
421+
});
422+
401423
it('node.countPublishers', async () => {
402424
assert.strictEqual(node.countPublishers('chatter'), 0);
403425

test/types/index.test-d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ expectType<rclnodejs.NamesAndTypesQueryResult[]>(
6767
expectType<rclnodejs.NamesAndTypesQueryResult[]>(node.getTopicNamesAndTypes());
6868
expectType<string[]>(node.getNodeNames());
6969
expectType<rclnodejs.NodeNamesQueryResult[]>(node.getNodeNamesAndNamespaces());
70+
expectType<rclnodejs.NodeNamesQueryResultWithEnclaves[]>(
71+
node.getNodeNamesAndNamespacesWithEnclaves()
72+
);
7073
expectType<Array<object>>(node.getPublishersInfoByTopic('topic', false));
7174
expectType<Array<object>>(node.getSubscriptionsInfoByTopic('topic', false));
7275
expectType<number>(node.countPublishers(TOPIC));
@@ -76,6 +79,7 @@ expectType<number>(node.countServices(SERVICE_NAME));
7679
expectType<rclnodejs.Options<string | rclnodejs.QoS>>(
7780
rclnodejs.Node.getDefaultOptions()
7881
);
82+
expectType<string>(node.getFullyQualifiedName());
7983

8084
// ---- LifecycleNode ----
8185
const lifecycleNode = rclnodejs.createLifecycleNode(LIFECYCLE_NODE_NAME);

types/node.d.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,24 @@ declare module 'rclnodejs' {
142142
namespace: string;
143143
};
144144

145+
/**
146+
* Result of Node.getNodeNames() query
147+
*
148+
* @example
149+
* ```
150+
* [
151+
* { name: 'gazebo', namespace: '/', enclave: '/'},
152+
* { name: 'robot_state_publisher', namespace: '/', enclave: '/' },
153+
* { name: 'cam2image', namespace: '/demo' , enclave: '/'}
154+
* ]
155+
* ```
156+
*/
157+
type NodeNamesQueryResultWithEnclaves = {
158+
name: string;
159+
namespace: string;
160+
enclave: string;
161+
};
162+
145163
/**
146164
* Node is the primary entrypoint in a ROS system for communication.
147165
* It can be used to create ROS entities such as publishers, subscribers,
@@ -769,6 +787,13 @@ declare module 'rclnodejs' {
769787
*/
770788
getNodeNamesAndNamespaces(): Array<NodeNamesQueryResult>;
771789

790+
/**
791+
* Get the list of nodes and their namespaces with enclaves discovered by the provided node.
792+
*
793+
* @returns An array of the names, namespaces and enclaves.
794+
*/
795+
getNodeNamesAndNamespacesWithEnclaves(): Array<NodeNamesQueryResultWithEnclaves>;
796+
772797
/**
773798
* Return the number of publishers on a given topic.
774799
* @param topic - The name of the topic.
@@ -796,5 +821,12 @@ declare module 'rclnodejs' {
796821
* @returns Number of services.
797822
*/
798823
countServices(serviceName: string): number;
824+
825+
/**
826+
* Get the fully qualified name of the node.
827+
*
828+
* @returns String containing the fully qualified name of the node.
829+
*/
830+
getFullyQualifiedName(): string;
799831
}
800832
}

0 commit comments

Comments
 (0)