Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
'-lrcl_lifecycle',
'-lrcutils',
'-lrcl_yaml_param_parser',
'-lrcpputils',
'-lrmw',
'-lrosidl_runtime_c',
],
Expand Down
26 changes: 26 additions & 0 deletions lib/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,32 @@ class Node extends rclnodejs.ShadowNode {
return rclnodejs.getServiceNamesAndTypes(this.handle);
}

/**
* Get a list of publishers on a given topic.
* @param {string} topic - the topic name to get the publishers for.
* @param {boolean} noDemangle - if `true`, `topic_name` needs to be a valid middleware topic name,
* otherwise it should be a valid ROS topic name.
* @returns {Array} - list of publishers
*/
getPublishersInfoByTopic(topic, noDemangle) {
return rclnodejs.getPublishersInfoByTopic(this.handle, topic, noDemangle);
}

/**
* Get a list of subscriptions on a given topic.
* @param {string} topic - the topic name to get the subscriptions for.
* @param {boolean} noDemangle - if `true`, `topic_name` needs to be a valid middleware topic name,
* otherwise it should be a valid ROS topic name.
* @returns {Array} - list of subscriptions
*/
getSubscriptionsInfoByTopic(topic, noDemangle) {
return rclnodejs.getSubscriptionsInfoByTopic(
this.handle,
topic,
noDemangle
);
}

/**
* Get the list of nodes discovered by the provided node.
* @return {Array<string>} - An array of the names.
Expand Down
1 change: 1 addition & 0 deletions scripts/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const dependencies = [
'builtin_interfaces',
'rcl_lifecycle',
'lifecycle_msgs',
'rcpputils',
'rosidl_runtime_c',
'rosidl_dynamic_typesupport',
'type_description_interfaces',
Expand Down
18 changes: 18 additions & 0 deletions src/rcl_client_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,30 @@ Napi::Value GetClientServiceName(const Napi::CallbackInfo& info) {
return Napi::String::New(env, service_name);
}

Napi::Value ServiceServerIsAvailable(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();

RclHandle* node_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
RclHandle* client_handle = RclHandle::Unwrap(info[1].As<Napi::Object>());
rcl_client_t* client = reinterpret_cast<rcl_client_t*>(client_handle->ptr());

bool is_available;
THROW_ERROR_IF_NOT_EQUAL(
RCL_RET_OK, rcl_service_server_is_available(node, client, &is_available),
"Failed to get service state.");

return Napi::Boolean::New(env, is_available);
}

Napi::Object InitClientBindings(Napi::Env env, Napi::Object exports) {
exports.Set("createClient", Napi::Function::New(env, CreateClient));
exports.Set("sendRequest", Napi::Function::New(env, SendRequest));
exports.Set("rclTakeResponse", Napi::Function::New(env, RclTakeResponse));
exports.Set("getClientServiceName",
Napi::Function::New(env, GetClientServiceName));
exports.Set("serviceServerIsAvailable",
Napi::Function::New(env, ServiceServerIsAvailable));
return exports;
}

Expand Down
103 changes: 56 additions & 47 deletions src/rcl_graph_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <rcl/graph.h>
#include <rcl/rcl.h>

#include <rcpputils/scope_exit.hpp>
#include <string>

#include "macros.h"
Expand All @@ -26,6 +27,11 @@

namespace rclnodejs {

typedef rcl_ret_t (*rcl_get_info_by_topic_func_t)(
const rcl_node_t* node, rcutils_allocator_t* allocator,
const char* topic_name, bool no_mangle,
rcl_topic_endpoint_info_array_t* info_array);

Napi::Value GetPublisherNamesAndTypesByNode(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();

Expand Down Expand Up @@ -193,59 +199,61 @@ Napi::Value GetServiceNamesAndTypes(const Napi::CallbackInfo& info) {
return result_list;
}

Napi::Value GetNodeNames(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();

RclHandle* node_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
rcutils_string_array_t node_names =
rcutils_get_zero_initialized_string_array();
rcutils_string_array_t node_namespaces =
rcutils_get_zero_initialized_string_array();
rcl_allocator_t allocator = rcl_get_default_allocator();

THROW_ERROR_IF_NOT_EQUAL(
RCL_RET_OK,
rcl_get_node_names(node, allocator, &node_names, &node_namespaces),
"Failed to get_node_names.");

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

for (size_t i = 0; i < node_names.size; ++i) {
Napi::Object item = Napi::Object::New(env);

item.Set("name", Napi::String::New(env, node_names.data[i]));
item.Set("namespace", Napi::String::New(env, node_namespaces.data[i]));

result_list.Set(i, item);
Napi::Value GetInfoByTopic(Napi::Env env, rcl_node_t* node,
const char* topic_name, bool no_mangle,
const char* type,
rcl_get_info_by_topic_func_t rcl_get_info_by_topic) {
rcutils_allocator_t allocator = rcutils_get_default_allocator();
rcl_topic_endpoint_info_array_t info_array =
rcl_get_zero_initialized_topic_endpoint_info_array();

RCPPUTILS_SCOPE_EXIT({
rcl_ret_t fini_ret =
rcl_topic_endpoint_info_array_fini(&info_array, &allocator);
if (RCL_RET_OK != fini_ret) {
Napi::Error::New(env, rcl_get_error_string().str)
.ThrowAsJavaScriptException();
rcl_reset_error();
}
});

rcl_ret_t ret = rcl_get_info_by_topic(node, &allocator, topic_name, no_mangle,
&info_array);
if (RCL_RET_OK != ret) {
if (RCL_RET_UNSUPPORTED == ret) {
Napi::Error::New(
env, std::string("Failed to get information by topic for ") + type +
": function not supported by RMW_IMPLEMENTATION")
.ThrowAsJavaScriptException();
return env.Undefined();
}
Napi::Error::New(
env, std::string("Failed to get information by topic for ") + type)
.ThrowAsJavaScriptException();
return env.Undefined();
}

rcutils_ret_t fini_names_ret = rcutils_string_array_fini(&node_names);
rcutils_ret_t fini_namespaces_ret =
rcutils_string_array_fini(&node_namespaces);
return ConvertToJSTopicEndpointInfoList(env, &info_array);
}

THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, fini_names_ret,
"Failed to destroy node_names");
THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, fini_namespaces_ret,
"Failed to destroy node_namespaces");
Napi::Value GetPublishersInfoByTopic(const Napi::CallbackInfo& info) {
RclHandle* node_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
std::string topic_name = info[1].As<Napi::String>().Utf8Value();
bool no_mangle = info[2].As<Napi::Boolean>();

return result_list;
return GetInfoByTopic(info.Env(), node, topic_name.c_str(), no_mangle,
"publishers", rcl_get_publishers_info_by_topic);
}

Napi::Value ServiceServerIsAvailable(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();

Napi::Value GetSubscriptionsInfoByTopic(const Napi::CallbackInfo& info) {
RclHandle* node_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
RclHandle* client_handle = RclHandle::Unwrap(info[1].As<Napi::Object>());
rcl_client_t* client = reinterpret_cast<rcl_client_t*>(client_handle->ptr());

bool is_available;
THROW_ERROR_IF_NOT_EQUAL(
RCL_RET_OK, rcl_service_server_is_available(node, client, &is_available),
"Failed to get service state.");
std::string topic_name = info[1].As<Napi::String>().Utf8Value();
bool no_mangle = info[2].As<Napi::Boolean>();

return Napi::Boolean::New(env, is_available);
return GetInfoByTopic(info.Env(), node, topic_name.c_str(), no_mangle,
"subscriptions", rcl_get_subscriptions_info_by_topic);
}

Napi::Object InitGraphBindings(Napi::Env env, Napi::Object exports) {
Expand All @@ -261,9 +269,10 @@ Napi::Object InitGraphBindings(Napi::Env env, Napi::Object exports) {
Napi::Function::New(env, GetTopicNamesAndTypes));
exports.Set("getServiceNamesAndTypes",
Napi::Function::New(env, GetServiceNamesAndTypes));
exports.Set("getNodeNames", Napi::Function::New(env, GetNodeNames));
exports.Set("serviceServerIsAvailable",
Napi::Function::New(env, ServiceServerIsAvailable));
exports.Set("getPublishersInfoByTopic",
Napi::Function::New(env, GetPublishersInfoByTopic));
exports.Set("getSubscriptionsInfoByTopic",
Napi::Function::New(env, GetSubscriptionsInfoByTopic));
return exports;
}

Expand Down
40 changes: 40 additions & 0 deletions src/rcl_node_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,45 @@ Napi::Value CountServices(const Napi::CallbackInfo& info) {
return Napi::Number::New(env, count);
}

Napi::Value GetNodeNames(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();

RclHandle* node_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
rcutils_string_array_t node_names =
rcutils_get_zero_initialized_string_array();
rcutils_string_array_t node_namespaces =
rcutils_get_zero_initialized_string_array();
rcl_allocator_t allocator = rcl_get_default_allocator();

THROW_ERROR_IF_NOT_EQUAL(
RCL_RET_OK,
rcl_get_node_names(node, allocator, &node_names, &node_namespaces),
"Failed to get_node_names.");

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

for (size_t i = 0; i < node_names.size; ++i) {
Napi::Object item = Napi::Object::New(env);

item.Set("name", Napi::String::New(env, node_names.data[i]));
item.Set("namespace", Napi::String::New(env, node_namespaces.data[i]));

result_list.Set(i, item);
}

rcutils_ret_t fini_names_ret = rcutils_string_array_fini(&node_names);
rcutils_ret_t fini_namespaces_ret =
rcutils_string_array_fini(&node_namespaces);

THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, fini_names_ret,
"Failed to destroy node_names");
THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, fini_namespaces_ret,
"Failed to destroy node_namespaces");

return result_list;
}

Napi::Object InitNodeBindings(Napi::Env env, Napi::Object exports) {
exports.Set("getParameterOverrides",
Napi::Function::New(env, GetParameterOverrides));
Expand All @@ -395,6 +434,7 @@ Napi::Object InitNodeBindings(Napi::Env env, Napi::Object exports) {
exports.Set("countSubscribers", Napi::Function::New(env, CountSubscribers));
exports.Set("countClients", Napi::Function::New(env, CountClients));
exports.Set("countServices", Napi::Function::New(env, CountServices));
exports.Set("getNodeNames", Napi::Function::New(env, GetNodeNames));
return exports;
}

Expand Down
73 changes: 73 additions & 0 deletions src/rcl_utilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <rcl/rcl.h>
#include <rcl_action/rcl_action.h>
#include <rmw/topic_endpoint_info.h>
#include <uv.h>

#include <memory>
Expand Down Expand Up @@ -69,6 +70,68 @@ std::unique_ptr<rmw_qos_profile_t> GetQosProfileFromObject(
return qos_profile;
}

Napi::Value ConvertRMWTimeToDuration(Napi::Env env,
const rmw_time_t* duration) {
Napi::Object obj = Napi::Object::New(env);
obj.Set("seconds", Napi::BigInt::New(env, duration->sec));
obj.Set("nanoseconds", Napi::Number::New(env, duration->nsec));
return obj;
}

Napi::Value ConvertToHashObject(Napi::Env env,
const rosidl_type_hash_t* type_hash) {
Napi::Object obj = Napi::Object::New(env);
obj.Set("version", Napi::Number::New(env, type_hash->version));
obj.Set("value", Napi::Buffer<char>::Copy(
env, reinterpret_cast<const char*>(type_hash->value),
ROSIDL_TYPE_HASH_SIZE));
return obj;
}

Napi::Value ConvertToQoS(Napi::Env env, const rmw_qos_profile_t* qos_profile) {
Napi::Object qos = Napi::Object::New(env);
qos.Set("depth", Napi::Number::New(env, qos_profile->depth));
qos.Set("history", Napi::Number::New(env, qos_profile->history));
qos.Set("reliability", Napi::Number::New(env, qos_profile->reliability));
qos.Set("durability", Napi::Number::New(env, qos_profile->durability));
qos.Set("lifespan", ConvertRMWTimeToDuration(env, &qos_profile->lifespan));
qos.Set("deadline", ConvertRMWTimeToDuration(env, &qos_profile->deadline));
qos.Set("liveliness", Napi::Number::New(env, qos_profile->liveliness));
qos.Set(
"liveliness_lease_duration",
ConvertRMWTimeToDuration(env, &qos_profile->liveliness_lease_duration));
qos.Set(
"avoid_ros_namespace_conventions",
Napi::Boolean::New(env, qos_profile->avoid_ros_namespace_conventions));
return qos;
}

Napi::Value ConvertToJSTopicEndpoint(
Napi::Env env, const rmw_topic_endpoint_info_t* topic_endpoint_info) {
Napi::Array endpoint_gid = Napi::Array::New(env, RMW_GID_STORAGE_SIZE);
for (size_t i = 0; i < RMW_GID_STORAGE_SIZE; i++) {
endpoint_gid.Set(
i, Napi::Number::New(env, topic_endpoint_info->endpoint_gid[i]));
}

Napi::Object endpoint = Napi::Object::New(env);
endpoint.Set("node_name",
Napi::String::New(env, topic_endpoint_info->node_name));
endpoint.Set("node_namespace",
Napi::String::New(env, topic_endpoint_info->node_namespace));
endpoint.Set("topic_type",
Napi::String::New(env, topic_endpoint_info->topic_type));
endpoint.Set("topic_type_hash",
ConvertToHashObject(env, &topic_endpoint_info->topic_type_hash));
endpoint.Set("endpoint_type",
Napi::Number::New(
env, static_cast<int>(topic_endpoint_info->endpoint_type)));
endpoint.Set("endpoint_gid", endpoint_gid);
endpoint.Set("qos_profile",
ConvertToQoS(env, &topic_endpoint_info->qos_profile));
return endpoint;
}

uv_lib_t g_lib;
Napi::Env g_env = nullptr;

Expand Down Expand Up @@ -183,4 +246,14 @@ void ExtractNamesAndTypes(rcl_names_and_types_t names_and_types,
}
}

Napi::Array ConvertToJSTopicEndpointInfoList(
Napi::Env env, const rmw_topic_endpoint_info_array_t* info_array) {
Napi::Array list = Napi::Array::New(env, info_array->size);
for (size_t i = 0; i < info_array->size; ++i) {
rmw_topic_endpoint_info_t topic_endpoint_info = info_array->info_array[i];
list.Set(i, ConvertToJSTopicEndpoint(env, &topic_endpoint_info));
}
return list;
}

} // namespace rclnodejs
3 changes: 3 additions & 0 deletions src/rcl_utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ std::unique_ptr<rmw_qos_profile_t> GetQoSProfile(Napi::Value qos);
void ExtractNamesAndTypes(rcl_names_and_types_t names_and_types,
Napi::Array* result_list);

Napi::Array ConvertToJSTopicEndpointInfoList(
Napi::Env env, const rmw_topic_endpoint_info_array_t* info_array);

} // namespace rclnodejs

#endif // SRC_RCL_UTILITIES_H_
Loading
Loading