rclnodejs 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +1 -1
  2. package/binding.gyp +1 -0
  3. package/index.js +26 -3
  4. package/lib/context.js +4 -2
  5. package/lib/lifecycle.js +9 -0
  6. package/lib/node.js +106 -18
  7. package/lib/serialization.js +60 -0
  8. package/lib/type_description_service.js +27 -1
  9. package/package.json +3 -1
  10. package/rosidl_convertor/README.md +298 -0
  11. package/rosidl_convertor/idl_convertor.js +49 -0
  12. package/rosidl_convertor/idl_convertor.py +1176 -0
  13. package/rosidl_gen/generator.json +2 -2
  14. package/rosidl_gen/index.js +21 -4
  15. package/rosidl_gen/packages.js +65 -32
  16. package/rosidl_gen/templates/message.dot +1 -1
  17. package/scripts/npmjs-readme.md +1 -1
  18. package/src/addon.cpp +2 -0
  19. package/src/macros.h +17 -1
  20. package/src/rcl_action_client_bindings.cpp +3 -2
  21. package/src/rcl_action_goal_bindings.cpp +3 -2
  22. package/src/rcl_action_server_bindings.cpp +7 -5
  23. package/src/rcl_client_bindings.cpp +4 -3
  24. package/src/rcl_context_bindings.cpp +29 -19
  25. package/src/rcl_guard_condition_bindings.cpp +3 -2
  26. package/src/rcl_lifecycle_bindings.cpp +18 -4
  27. package/src/rcl_node_bindings.cpp +105 -3
  28. package/src/rcl_publisher_bindings.cpp +4 -3
  29. package/src/rcl_serialization_bindings.cpp +116 -0
  30. package/src/rcl_serialization_bindings.h +26 -0
  31. package/src/rcl_service_bindings.cpp +4 -3
  32. package/src/rcl_subscription_bindings.cpp +3 -2
  33. package/src/rcl_time_point_bindings.cpp +3 -2
  34. package/src/rcl_timer_bindings.cpp +8 -6
  35. package/src/rcl_utilities.cpp +31 -0
  36. package/src/rcl_utilities.h +7 -0
  37. package/tsconfig.json +2 -2
  38. package/types/context.d.ts +3 -2
  39. package/types/index.d.ts +26 -1
  40. package/types/lifecycle.d.ts +7 -0
  41. package/types/node.d.ts +48 -8
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rosidl-generator",
3
- "version": "0.5.1",
4
- "description": "Generate JavaScript object from ROS IDL(.msg) files",
3
+ "version": "1.0.0",
4
+ "description": "Generate JavaScript object from ROS IDL(.msg/.srv/.action/.idl) files",
5
5
  "main": "index.js",
6
6
  "authors": [
7
7
  "Minggang Wang <minggang.wang@intel.com>",
@@ -18,18 +18,35 @@ const fse = require('fs-extra');
18
18
  const generateJSStructFromIDL = require('./idl_generator.js');
19
19
  const packages = require('./packages.js');
20
20
  const path = require('path');
21
-
21
+ const idlConvertor = require('../rosidl_convertor/idl_convertor.js');
22
22
  const generatedRoot = path.join(__dirname, '../generated/');
23
23
  const serviceMsgPath = path.join(generatedRoot, 'srv_msg');
24
+ const idlPath = path.join(generatedRoot, 'share');
25
+ const useIDL = !!process.argv.find((arg) => arg === '--idl');
24
26
 
25
27
  function getInstalledPackagePaths() {
26
28
  return process.env.AMENT_PREFIX_PATH.split(path.delimiter);
27
29
  }
28
30
 
29
31
  async function generateInPath(path) {
30
- const pkgs = await packages.findPackagesInDirectory(path);
31
-
32
- const pkgsInfo = Array.from(pkgs.values());
32
+ let pkgsInfo = null;
33
+ if (!useIDL) {
34
+ pkgsInfo = Array.from(
35
+ (await packages.findPackagesInDirectory(path)).values()
36
+ );
37
+ } else {
38
+ const idlPkgs = await packages.findPackagesInDirectory(path, useIDL);
39
+ await fse.ensureDir(idlPath);
40
+ const promises = [];
41
+ idlPkgs.forEach((pkg) => {
42
+ pkg.idls.forEach((idl) => {
43
+ promises.push(idlConvertor(idl.pkgName, idl.filePath, idlPath));
44
+ });
45
+ });
46
+ await Promise.all(promises);
47
+ const pkgsFromIdl = await packages.findPackagesInDirectory(idlPath, false);
48
+ pkgsInfo = Array.from(pkgsFromIdl.values());
49
+ }
33
50
 
34
51
  await Promise.all(
35
52
  pkgsInfo.map((pkgInfo) => generateJSStructFromIDL(pkgInfo, generatedRoot))
@@ -70,7 +70,13 @@ function grabInterfaceInfo(filePath, amentExecuted) {
70
70
  function addInterfaceInfo(info, type, pkgMap) {
71
71
  let pkgName = info.pkgName;
72
72
  if (!pkgMap.has(pkgName)) {
73
- pkgMap.set(pkgName, { messages: [], services: [], actions: [], pkgName });
73
+ pkgMap.set(pkgName, {
74
+ messages: [],
75
+ services: [],
76
+ actions: [],
77
+ idls: [],
78
+ pkgName,
79
+ });
74
80
  }
75
81
  let pkg = pkgMap.get(pkgName);
76
82
  pkg[type].push(info);
@@ -157,26 +163,32 @@ async function generateMsgForSrv(filePath, interfaceInfo, pkgMap) {
157
163
  }
158
164
  }
159
165
 
160
- async function addInterfaceInfos(filePath, dir, pkgMap) {
161
- const interfaceInfo = grabInterfaceInfo(filePath, true);
166
+ async function addInterfaceInfos(filePath, dir, pkgMap, useIDL) {
167
+ const interfaceInfo = grabInterfaceInfo(filePath, /*amentExecuted=*/ true);
162
168
  const ignore = pkgFilters.matchesAny(interfaceInfo);
163
169
  if (!ignore) {
164
- if (path.extname(filePath) === '.msg') {
165
- // Some .msg files were generated prior to 0.3.2 for .action files,
166
- // which has been disabled. So these files should be ignored here.
167
- if (path.dirname(dir).split(path.sep).pop() !== 'action') {
168
- addInterfaceInfo(interfaceInfo, 'messages', pkgMap);
169
- }
170
- } else if (path.extname(filePath) === '.srv') {
171
- const requestMsgName = `${path.parse(filePath).name}_Request.msg`;
172
- if (!fs.existsSync(path.join(path.dirname(filePath), requestMsgName))) {
173
- await generateMsgForSrv(filePath, interfaceInfo, pkgMap);
170
+ if (useIDL) {
171
+ if (path.extname(filePath) === '.idl') {
172
+ addInterfaceInfo(interfaceInfo, 'idls', pkgMap);
174
173
  }
175
- addInterfaceInfo(interfaceInfo, 'services', pkgMap);
176
- } else if (path.extname(filePath) === '.action') {
177
- addInterfaceInfo(interfaceInfo, 'actions', pkgMap);
178
174
  } else {
179
- // we ignore all other files
175
+ if (path.extname(filePath) === '.msg') {
176
+ // Some .msg files were generated prior to 0.3.2 for .action files,
177
+ // which has been disabled. So these files should be ignored here.
178
+ if (path.dirname(dir).split(path.sep).pop() !== 'action') {
179
+ addInterfaceInfo(interfaceInfo, 'messages', pkgMap);
180
+ }
181
+ } else if (path.extname(filePath) === '.srv') {
182
+ const requestMsgName = `${path.parse(filePath).name}_Request.msg`;
183
+ if (!fs.existsSync(path.join(path.dirname(filePath), requestMsgName))) {
184
+ await generateMsgForSrv(filePath, interfaceInfo, pkgMap);
185
+ }
186
+ addInterfaceInfo(interfaceInfo, 'services', pkgMap);
187
+ } else if (path.extname(filePath) === '.action') {
188
+ addInterfaceInfo(interfaceInfo, 'actions', pkgMap);
189
+ } else {
190
+ // we ignore all other files.
191
+ }
180
192
  }
181
193
  }
182
194
  }
@@ -186,7 +198,7 @@ async function addInterfaceInfos(filePath, dir, pkgMap) {
186
198
  * @param {string} dir - the directory to search in
187
199
  * @return {Promise<Map<string, object>>} A mapping from the package name to some info about it.
188
200
  */
189
- async function findAmentPackagesInDirectory(dir) {
201
+ async function findAmentPackagesInDirectory(dir, useIDL) {
190
202
  const pkgs = await getAmentPackages(dir);
191
203
  const files = await Promise.all(
192
204
  pkgs.map((pkg) => getPackageDefinitionsFiles(pkg, dir))
@@ -195,7 +207,7 @@ async function findAmentPackagesInDirectory(dir) {
195
207
  const rosFiles = files.flat();
196
208
  const pkgMap = new Map();
197
209
  await Promise.all(
198
- rosFiles.map((filePath) => addInterfaceInfos(filePath, dir, pkgMap))
210
+ rosFiles.map((filePath) => addInterfaceInfos(filePath, dir, pkgMap, useIDL))
199
211
  );
200
212
  return pkgMap;
201
213
  }
@@ -205,7 +217,7 @@ async function findAmentPackagesInDirectory(dir) {
205
217
  * @param {string} dir - the directory to search in
206
218
  * @return {Promise<Map<string, object>>} A mapping from the package name to some info about it.
207
219
  */
208
- async function findPackagesInDirectory(dir) {
220
+ async function findPackagesInDirectory(dir, useIDL) {
209
221
  return new Promise((resolve, reject) => {
210
222
  let amentExecuted = true;
211
223
 
@@ -217,30 +229,51 @@ async function findPackagesInDirectory(dir) {
217
229
  }
218
230
 
219
231
  if (amentExecuted) {
220
- return resolve(findAmentPackagesInDirectory(dir));
232
+ return resolve(findAmentPackagesInDirectory(dir, useIDL));
221
233
  }
222
234
 
223
235
  let walker = walk.walk(dir, { followLinks: true });
224
236
  let pkgMap = new Map();
225
- walker.on('file', (root, file, next) => {
237
+ walker.on('file', async (root, file, next) => {
226
238
  const interfaceInfo = grabInterfaceInfo(
227
239
  path.join(root, file.name),
228
240
  amentExecuted
229
241
  );
230
242
  const ignore = pkgFilters.matchesAny(interfaceInfo);
231
243
  if (!ignore) {
232
- if (path.extname(file.name) === '.msg') {
233
- // Some .msg files were generated prior to 0.3.2 for .action files,
234
- // which has been disabled. So these files should be ignored here.
235
- if (path.dirname(root).split(path.sep).pop() !== 'action') {
236
- addInterfaceInfo(interfaceInfo, 'messages', pkgMap);
244
+ if (useIDL) {
245
+ if (path.extname(file.name) === '.idl') {
246
+ addInterfaceInfo(interfaceInfo, 'idls', pkgMap);
237
247
  }
238
- } else if (path.extname(file.name) === '.srv') {
239
- addInterfaceInfo(interfaceInfo, 'services', pkgMap);
240
- } else if (path.extname(file.name) === '.action') {
241
- addInterfaceInfo(interfaceInfo, 'actions', pkgMap);
242
248
  } else {
243
- // we ignore all other files
249
+ if (path.extname(file.name) === '.msg') {
250
+ // Some .msg files were generated prior to 0.3.2 for .action files,
251
+ // which has been disabled. So these files should be ignored here.
252
+ if (path.dirname(root).split(path.sep).pop() !== 'action') {
253
+ addInterfaceInfo(interfaceInfo, 'messages', pkgMap);
254
+ }
255
+ } else if (path.extname(file.name) === '.srv') {
256
+ const requestMsgName = `${path.parse(interfaceInfo.filePath).name}_Request.msg`;
257
+ if (
258
+ !fs.existsSync(
259
+ path.join(
260
+ path.dirname(interfaceInfo.filePath),
261
+ requestMsgName
262
+ )
263
+ )
264
+ ) {
265
+ await generateMsgForSrv(
266
+ interfaceInfo.filePath,
267
+ interfaceInfo,
268
+ pkgMap
269
+ );
270
+ }
271
+ addInterfaceInfo(interfaceInfo, 'services', pkgMap);
272
+ } else if (path.extname(file.name) === '.action') {
273
+ addInterfaceInfo(interfaceInfo, 'actions', pkgMap);
274
+ } else {
275
+ // we ignore all other files
276
+ }
244
277
  }
245
278
  }
246
279
  next();
@@ -506,7 +506,7 @@ class {{=objectWrapper}} {
506
506
  {{~}}
507
507
  }
508
508
 
509
- static destoryRawROS(msg) {
509
+ static destroyRawROS(msg) {
510
510
  {{=objectWrapper}}.freeStruct(msg.refObject);
511
511
  }
512
512
 
@@ -45,7 +45,7 @@ npm i rclnodejs@x.y.z
45
45
 
46
46
  | RCLNODEJS Version | Compatible ROS 2 LTS |
47
47
  | :----------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
48
- | latest version (currently [v1.2.0](https://github.com/RobotWebTools/rclnodejs/tree/1.2.0)) | [Kilted](https://github.com/RobotWebTools/rclnodejs/tree/kilted)<br>[Jazzy](https://github.com/RobotWebTools/rclnodejs/tree/jazzy)<br>[Humble](https://github.com/RobotWebTools/rclnodejs/tree/humble-hawksbill) |
48
+ | latest version (currently [v1.4.0](https://github.com/RobotWebTools/rclnodejs/tree/1.4.0)) | [Kilted](https://github.com/RobotWebTools/rclnodejs/tree/kilted)<br>[Jazzy](https://github.com/RobotWebTools/rclnodejs/tree/jazzy)<br>[Humble](https://github.com/RobotWebTools/rclnodejs/tree/humble-hawksbill) |
49
49
 
50
50
  ## Documentation
51
51
 
package/src/addon.cpp CHANGED
@@ -30,6 +30,7 @@
30
30
  #include "rcl_names_bindings.h"
31
31
  #include "rcl_node_bindings.h"
32
32
  #include "rcl_publisher_bindings.h"
33
+ #include "rcl_serialization_bindings.h"
33
34
  #include "rcl_service_bindings.h"
34
35
  #include "rcl_subscription_bindings.h"
35
36
  #include "rcl_time_point_bindings.h"
@@ -88,6 +89,7 @@ Napi::Object InitModule(Napi::Env env, Napi::Object exports) {
88
89
  rclnodejs::InitEventHandleBindings(env, exports);
89
90
  #endif
90
91
  rclnodejs::InitLifecycleBindings(env, exports);
92
+ rclnodejs::InitSerializationBindings(env, exports);
91
93
  rclnodejs::ShadowNode::Init(env, exports);
92
94
  rclnodejs::RclHandle::Init(env, exports);
93
95
 
package/src/macros.h CHANGED
@@ -23,17 +23,33 @@
23
23
  { \
24
24
  if (lhs op rhs) { \
25
25
  rcl_reset_error(); \
26
- Napi::Error::New(rclnodejs::GetEnv(), message) \
26
+ Napi::Error::New(env, message) \
27
27
  .ThrowAsJavaScriptException(); \
28
+ return env.Undefined(); \
28
29
  } \
29
30
  }
30
31
 
32
+ #define CHECK_OP_AND_THROW_ERROR_IF_NOT_TRUE_NO_RETURN(op, lhs, rhs, message) \
33
+ { \
34
+ if (lhs op rhs) { \
35
+ rcl_reset_error(); \
36
+ Napi::Error::New(env, message) \
37
+ .ThrowAsJavaScriptException(); \
38
+ } \
39
+ }
40
+
31
41
  #define THROW_ERROR_IF_NOT_EQUAL(lhs, rhs, message) \
32
42
  CHECK_OP_AND_THROW_ERROR_IF_NOT_TRUE(!=, lhs, rhs, message)
33
43
 
34
44
  #define THROW_ERROR_IF_EQUAL(lhs, rhs, message) \
35
45
  CHECK_OP_AND_THROW_ERROR_IF_NOT_TRUE(==, lhs, rhs, message)
36
46
 
47
+ #define THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(lhs, rhs, message) \
48
+ CHECK_OP_AND_THROW_ERROR_IF_NOT_TRUE_NO_RETURN(!=, lhs, rhs, message)
49
+
50
+ #define THROW_ERROR_IF_EQUAL_NO_RETURN(lhs, rhs, message) \
51
+ CHECK_OP_AND_THROW_ERROR_IF_NOT_TRUE_NO_RETURN(==, lhs, rhs, message)
52
+
37
53
  #define PACKAGE_NAME "rclnodejs"
38
54
 
39
55
  #ifdef DEBUG_ON
@@ -75,12 +75,13 @@ Napi::Value ActionCreateClient(const Napi::CallbackInfo& info) {
75
75
  &action_client_ops),
76
76
  RCL_RET_OK, rcl_get_error_string().str);
77
77
  auto js_obj = RclHandle::NewInstance(
78
- env, action_client, node_handle, [node](void* ptr) {
78
+ env, action_client, node_handle, [node, env](void* ptr) {
79
79
  rcl_action_client_t* action_client =
80
80
  reinterpret_cast<rcl_action_client_t*>(ptr);
81
81
  rcl_ret_t ret = rcl_action_client_fini(action_client, node);
82
82
  free(ptr);
83
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, ret, rcl_get_error_string().str);
83
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
84
+ rcl_get_error_string().str);
84
85
  });
85
86
 
86
87
  return js_obj;
@@ -49,12 +49,13 @@ Napi::Value ActionAcceptNewGoal(const Napi::CallbackInfo& info) {
49
49
  malloc(sizeof(rcl_action_goal_handle_t)));
50
50
  *goal_handle = *new_goal;
51
51
  auto js_obj =
52
- RclHandle::NewInstance(env, goal_handle, nullptr, [](void* ptr) {
52
+ RclHandle::NewInstance(env, goal_handle, nullptr, [env](void* ptr) {
53
53
  rcl_action_goal_handle_t* goal_handle =
54
54
  reinterpret_cast<rcl_action_goal_handle_t*>(ptr);
55
55
  rcl_ret_t ret = rcl_action_goal_handle_fini(goal_handle);
56
56
  free(ptr);
57
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, ret, rcl_get_error_string().str);
57
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
58
+ rcl_get_error_string().str);
58
59
  });
59
60
 
60
61
  return js_obj;
@@ -81,12 +81,13 @@ Napi::Value ActionCreateServer(const Napi::CallbackInfo& info) {
81
81
  action_name.c_str(), &action_server_ops),
82
82
  RCL_RET_OK, rcl_get_error_string().str);
83
83
  auto js_obj = RclHandle::NewInstance(
84
- env, action_server, node_handle, [node](void* ptr) {
84
+ env, action_server, node_handle, [node, env](void* ptr) {
85
85
  rcl_action_server_t* action_server =
86
86
  reinterpret_cast<rcl_action_server_t*>(ptr);
87
87
  rcl_ret_t ret = rcl_action_server_fini(action_server, node);
88
88
  free(ptr);
89
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, ret, rcl_get_error_string().str);
89
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
90
+ rcl_get_error_string().str);
90
91
  });
91
92
 
92
93
  return js_obj;
@@ -390,13 +391,14 @@ Napi::Value ActionProcessCancelRequest(const Napi::CallbackInfo& info) {
390
391
  }
391
392
 
392
393
  *response = cancel_response_ptr->msg;
393
- auto js_obj =
394
- RclHandle::NewInstance(env, cancel_response_ptr, nullptr, [](void* ptr) {
394
+ auto js_obj = RclHandle::NewInstance(
395
+ env, cancel_response_ptr, nullptr, [env](void* ptr) {
395
396
  rcl_action_cancel_response_t* cancel_response_ptr =
396
397
  reinterpret_cast<rcl_action_cancel_response_t*>(ptr);
397
398
  rcl_ret_t ret = rcl_action_cancel_response_fini(cancel_response_ptr);
398
399
  free(ptr);
399
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, ret, rcl_get_error_string().str);
400
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
401
+ rcl_get_error_string().str);
400
402
  });
401
403
  return js_obj;
402
404
  }
@@ -53,12 +53,13 @@ Napi::Value CreateClient(const Napi::CallbackInfo& info) {
53
53
  rcl_client_init(client, node, ts, service_name.c_str(), &client_ops),
54
54
  RCL_RET_OK, rcl_get_error_string().str);
55
55
 
56
- auto js_obj =
57
- RclHandle::NewInstance(env, client, node_handle, [node](void* ptr) {
56
+ auto js_obj = RclHandle::NewInstance(
57
+ env, client, node_handle, [node, env](void* ptr) {
58
58
  rcl_client_t* client = reinterpret_cast<rcl_client_t*>(ptr);
59
59
  rcl_ret_t ret = rcl_client_fini(client, node);
60
60
  free(ptr);
61
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, ret, rcl_get_error_string().str);
61
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
62
+ rcl_get_error_string().str);
62
63
  });
63
64
 
64
65
  return js_obj;
@@ -17,7 +17,8 @@
17
17
  #include <rcl/logging.h>
18
18
  #include <rcl/rcl.h>
19
19
 
20
- #include <cstdio>
20
+ #include <rcpputils/scope_exit.hpp>
21
+ // NOLINTNEXTLINE
21
22
  #include <string>
22
23
 
23
24
  #include "macros.h"
@@ -35,6 +36,15 @@ Napi::Value Init(const Napi::CallbackInfo& info) {
35
36
  rcl_init_options_init(&init_options, allocator),
36
37
  rcl_get_error_string().str);
37
38
 
39
+ RCPPUTILS_SCOPE_EXIT({
40
+ rcl_ret_t fini_ret = rcl_init_options_fini(&init_options);
41
+ if (RCL_RET_OK != fini_ret) {
42
+ Napi::Error::New(env, rcl_get_error_string().str)
43
+ .ThrowAsJavaScriptException();
44
+ rcl_reset_error();
45
+ }
46
+ });
47
+
38
48
  // Preprocess Context
39
49
  RclHandle* context_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
40
50
  rcl_context_t* context =
@@ -42,17 +52,20 @@ Napi::Value Init(const Napi::CallbackInfo& info) {
42
52
 
43
53
  // Preprocess argc & argv
44
54
  Napi::Array jsArgv = info[1].As<Napi::Array>();
45
- int argc = jsArgv.Length();
46
- char** argv = nullptr;
47
-
48
- if (argc > 0) {
49
- argv = reinterpret_cast<char**>(malloc(argc * sizeof(char*)));
50
- for (int i = 0; i < argc; i++) {
51
- std::string arg = jsArgv.Get(i).As<Napi::String>().Utf8Value();
52
- int len = arg.length() + 1;
53
- argv[i] = reinterpret_cast<char*>(malloc(len * sizeof(char)));
54
- snprintf(argv[i], len, "%s", arg.c_str());
55
- }
55
+ size_t argc = jsArgv.Length();
56
+ char** argv = AbstractArgsFromNapiArray(jsArgv);
57
+
58
+ // Set up the domain id.
59
+ size_t domain_id = RCL_DEFAULT_DOMAIN_ID;
60
+ if (info.Length() > 2 && info[2].IsBigInt()) {
61
+ bool lossless;
62
+ domain_id = info[2].As<Napi::BigInt>().Uint64Value(&lossless);
63
+ }
64
+ rcl_ret_t ret = rcl_init_options_set_domain_id(&init_options, domain_id);
65
+ if (RCL_RET_OK != ret) {
66
+ Napi::Error::New(env, "failed to set domain id to init options")
67
+ .ThrowAsJavaScriptException();
68
+ return env.Undefined();
56
69
  }
57
70
 
58
71
  THROW_ERROR_IF_NOT_EQUAL(
@@ -64,11 +77,7 @@ Napi::Value Init(const Napi::CallbackInfo& info) {
64
77
  RCL_RET_OK, rcl_logging_configure(&context->global_arguments, &allocator),
65
78
  rcl_get_error_string().str);
66
79
 
67
- for (int i = 0; i < argc; i++) {
68
- free(argv[i]);
69
- }
70
- free(argv);
71
-
80
+ RCPPUTILS_SCOPE_EXIT({ FreeArgs(argv, argc); });
72
81
  return env.Undefined();
73
82
  }
74
83
 
@@ -111,7 +120,8 @@ Napi::Value CreateContext(const Napi::CallbackInfo& info) {
111
120
  rcl_context_t* context = reinterpret_cast<rcl_context_t*>(ptr);
112
121
  rcl_ret_t ret = DestroyContext(env, context);
113
122
  free(ptr);
114
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, ret, rcl_get_error_string().str);
123
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
124
+ rcl_get_error_string().str);
115
125
  });
116
126
 
117
127
  return js_obj;
@@ -141,7 +151,7 @@ Napi::Value GetDomainId(const Napi::CallbackInfo& info) {
141
151
  return env.Undefined();
142
152
  }
143
153
 
144
- return Napi::Number::New(env, domain_id);
154
+ return Napi::BigInt::New(env, domain_id);
145
155
  }
146
156
 
147
157
  Napi::Object InitContextBindings(Napi::Env env, Napi::Object exports) {
@@ -41,11 +41,12 @@ Napi::Value CreateGuardCondition(const Napi::CallbackInfo& info) {
41
41
  rcl_guard_condition_init(gc, context, gc_options),
42
42
  rcl_get_error_string().str);
43
43
 
44
- auto handle = RclHandle::NewInstance(env, gc, nullptr, [](void* ptr) {
44
+ auto handle = RclHandle::NewInstance(env, gc, nullptr, [env](void* ptr) {
45
45
  rcl_guard_condition_t* gc = reinterpret_cast<rcl_guard_condition_t*>(ptr);
46
46
  rcl_ret_t ret = rcl_guard_condition_fini(gc);
47
47
  free(ptr);
48
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, ret, rcl_get_error_string().str);
48
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
49
+ rcl_get_error_string().str);
49
50
  });
50
51
 
51
52
  return handle;
@@ -83,12 +83,13 @@ Napi::Value CreateLifecycleStateMachine(const Napi::CallbackInfo& info) {
83
83
  rcl_get_error_string().str);
84
84
 
85
85
  auto js_obj = RclHandle::NewInstance(
86
- env, state_machine, node_handle, [node](void* ptr) {
86
+ env, state_machine, node_handle, [node, env](void* ptr) {
87
87
  rcl_lifecycle_state_machine_t* state_machine =
88
88
  reinterpret_cast<rcl_lifecycle_state_machine_t*>(ptr);
89
89
  rcl_ret_t ret = rcl_lifecycle_state_machine_fini(state_machine, node);
90
90
  free(ptr);
91
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, ret, rcl_get_error_string().str);
91
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
92
+ rcl_get_error_string().str);
92
93
  });
93
94
  #else
94
95
  const rcl_node_options_t* node_options =
@@ -101,13 +102,14 @@ Napi::Value CreateLifecycleStateMachine(const Napi::CallbackInfo& info) {
101
102
  rcl_get_error_string().str);
102
103
 
103
104
  auto js_obj = RclHandle::NewInstance(
104
- env, state_machine, node_handle, [node, node_options](void* ptr) {
105
+ env, state_machine, node_handle, [node, node_options, env](void* ptr) {
105
106
  rcl_lifecycle_state_machine_t* state_machine =
106
107
  reinterpret_cast<rcl_lifecycle_state_machine_t*>(ptr);
107
108
  rcl_ret_t ret = rcl_lifecycle_state_machine_fini(
108
109
  state_machine, node, &node_options->allocator);
109
110
  free(ptr);
110
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, ret, rcl_get_error_string().str);
111
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
112
+ rcl_get_error_string().str);
111
113
  });
112
114
  #endif
113
115
 
@@ -372,6 +374,17 @@ Napi::Value IsInitialized(const Napi::CallbackInfo& info) {
372
374
  return Napi::Boolean::New(env, is_initialized);
373
375
  }
374
376
 
377
+ Napi::Value Print(const Napi::CallbackInfo& info) {
378
+ Napi::Env env = info.Env();
379
+ RclHandle* state_machine_handle =
380
+ RclHandle::Unwrap(info[0].As<Napi::Object>());
381
+ rcl_lifecycle_state_machine_t* state_machine =
382
+ reinterpret_cast<rcl_lifecycle_state_machine_t*>(
383
+ state_machine_handle->ptr());
384
+ rcl_print_state_machine(state_machine);
385
+ return env.Undefined();
386
+ }
387
+
375
388
  Napi::Object InitLifecycleBindings(Napi::Env env, Napi::Object exports) {
376
389
  exports.Set("createLifecycleStateMachine",
377
390
  Napi::Function::New(env, CreateLifecycleStateMachine));
@@ -396,6 +409,7 @@ Napi::Object InitLifecycleBindings(Napi::Env env, Napi::Object exports) {
396
409
  exports.Set("getLifecycleShutdownTransitionLabel",
397
410
  Napi::Function::New(env, GetLifecycleShutdownTransitionLabel));
398
411
  exports.Set("isInitialized", Napi::Function::New(env, IsInitialized));
412
+ exports.Set("print", Napi::Function::New(env, Print));
399
413
  return exports;
400
414
  }
401
415