rclnodejs 1.5.1 → 1.6.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 (64) hide show
  1. package/README.md +25 -2
  2. package/binding.gyp +2 -0
  3. package/index.js +21 -4
  4. package/lib/action/client.js +1 -1
  5. package/lib/action/graph.js +1 -1
  6. package/lib/action/server.js +1 -1
  7. package/lib/action/server_goal_handle.js +1 -1
  8. package/lib/client.js +1 -1
  9. package/lib/clock.js +1 -1
  10. package/lib/context.js +1 -1
  11. package/lib/duration.js +1 -1
  12. package/lib/event_handler.js +1 -1
  13. package/lib/guard_condition.js +1 -1
  14. package/lib/lifecycle.js +1 -1
  15. package/lib/lifecycle_publisher.js +1 -1
  16. package/lib/logging.js +1 -1
  17. package/lib/message_serialization.js +171 -0
  18. package/lib/native_loader.js +173 -0
  19. package/lib/node.js +16 -1
  20. package/lib/parameter.js +4 -10
  21. package/lib/publisher.js +1 -1
  22. package/lib/serialization.js +1 -1
  23. package/lib/service.js +1 -1
  24. package/lib/subscription.js +17 -2
  25. package/lib/time.js +1 -1
  26. package/lib/time_source.js +1 -1
  27. package/lib/timer.js +1 -1
  28. package/lib/type_description_service.js +1 -1
  29. package/lib/utils.js +324 -0
  30. package/lib/validator.js +1 -1
  31. package/package.json +14 -18
  32. package/prebuilds/linux-arm64/humble-jammy-arm64-rclnodejs.node +0 -0
  33. package/prebuilds/linux-arm64/jazzy-noble-arm64-rclnodejs.node +0 -0
  34. package/prebuilds/linux-arm64/kilted-noble-arm64-rclnodejs.node +0 -0
  35. package/prebuilds/linux-x64/humble-jammy-x64-rclnodejs.node +0 -0
  36. package/prebuilds/linux-x64/jazzy-noble-x64-rclnodejs.node +0 -0
  37. package/prebuilds/linux-x64/kilted-noble-x64-rclnodejs.node +0 -0
  38. package/rosidl_convertor/idl_convertor.js +3 -2
  39. package/rosidl_gen/deallocator.js +1 -1
  40. package/rosidl_gen/generate_worker.js +1 -1
  41. package/rosidl_gen/generator.json +1 -1
  42. package/rosidl_gen/idl_generator.js +11 -24
  43. package/rosidl_gen/index.js +1 -1
  44. package/rosidl_gen/primitive_types.js +2 -2
  45. package/rosidl_gen/templates/action-template.js +68 -0
  46. package/rosidl_gen/templates/message-template.js +1113 -0
  47. package/rosidl_gen/templates/service-event-template.js +31 -0
  48. package/rosidl_gen/templates/service-template.js +44 -0
  49. package/rosidl_parser/rosidl_parser.js +2 -2
  50. package/scripts/install.js +113 -0
  51. package/scripts/tag_prebuilds.js +70 -0
  52. package/src/addon.cpp +3 -0
  53. package/third_party/ref-napi/index.js +15 -0
  54. package/third_party/ref-napi/lib/ref.js +1696 -0
  55. package/third_party/ref-napi/src/ref_napi_bindings.cpp +736 -0
  56. package/third_party/ref-napi/src/ref_napi_bindings.h +26 -0
  57. package/types/index.d.ts +17 -0
  58. package/types/node.d.ts +16 -1
  59. package/rosidl_gen/templates/CMakeLists.dot +0 -40
  60. package/rosidl_gen/templates/action.dot +0 -50
  61. package/rosidl_gen/templates/message.dot +0 -851
  62. package/rosidl_gen/templates/package.dot +0 -16
  63. package/rosidl_gen/templates/service.dot +0 -26
  64. package/rosidl_gen/templates/service_event.dot +0 -10
@@ -0,0 +1,1113 @@
1
+ // Copyright (c) 2025, The Robot Web Tools Contributors
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ 'use strict';
16
+
17
+ /**
18
+ * Generate JavaScript message class code using native template literals
19
+ * This replaces the doT template engine with pure JavaScript
20
+ */
21
+
22
+ // Helper functions (same as in message.dot template)
23
+ function getPrimitiveNameByType(type) {
24
+ if (type.type === 'bool') {
25
+ return 'Bool';
26
+ } else if (type.type === 'int8') {
27
+ return 'Int8';
28
+ } else if (type.type === 'uint8') {
29
+ return 'UInt8';
30
+ } else if (type.type === 'int16') {
31
+ return 'Int16';
32
+ } else if (type.type === 'uint16') {
33
+ return 'UInt16';
34
+ } else if (type.type === 'int32') {
35
+ return 'Int32';
36
+ } else if (type.type === 'uint32') {
37
+ return 'UInt32';
38
+ } else if (type.type === 'int64') {
39
+ return 'Int64';
40
+ } else if (type.type === 'uint64') {
41
+ return 'UInt64';
42
+ } else if (type.type === 'float64') {
43
+ return 'Float64';
44
+ } else if (type.type === 'float32') {
45
+ return 'Float32';
46
+ } else if (type.type === 'char') {
47
+ return 'Char';
48
+ } else if (type.type === 'byte') {
49
+ return 'Byte';
50
+ } else if (type.type === 'string' || type.type === 'wstring') {
51
+ return 'String';
52
+ } else {
53
+ return '';
54
+ }
55
+ }
56
+
57
+ function getTypedArrayName(type) {
58
+ const t = type.type.toLowerCase();
59
+ let typedArrayName = null;
60
+
61
+ switch (t) {
62
+ case 'byte':
63
+ case 'octet':
64
+ case 'uint8':
65
+ typedArrayName = 'Uint8Array';
66
+ break;
67
+ case 'char':
68
+ case 'int8':
69
+ typedArrayName = 'Int8Array';
70
+ break;
71
+ case 'int16':
72
+ case 'short':
73
+ typedArrayName = 'Int16Array';
74
+ break;
75
+ case 'uint16':
76
+ case 'unsigned short':
77
+ typedArrayName = 'Uint16Array';
78
+ break;
79
+ case 'int32':
80
+ case 'long':
81
+ typedArrayName = 'Int32Array';
82
+ break;
83
+ case 'uint32':
84
+ case 'unsigned long':
85
+ typedArrayName = 'Uint32Array';
86
+ break;
87
+ case 'float':
88
+ case 'float32':
89
+ typedArrayName = 'Float32Array';
90
+ break;
91
+ case 'double':
92
+ case 'float64':
93
+ typedArrayName = 'Float64Array';
94
+ break;
95
+ default:
96
+ typedArrayName = '';
97
+ }
98
+ return typedArrayName;
99
+ }
100
+
101
+ function getTypedArrayElementName(type) {
102
+ const t = type.type.toLowerCase();
103
+ if (t === 'int8') {
104
+ return 'int8';
105
+ } else if (t === 'uint8') {
106
+ return 'uint8';
107
+ } else if (t === 'int16') {
108
+ return 'int16';
109
+ } else if (t === 'uint16') {
110
+ return 'uint16';
111
+ } else if (t === 'int32') {
112
+ return 'int32';
113
+ } else if (t === 'uint32') {
114
+ return 'uint32';
115
+ } else if (t === 'float64') {
116
+ return 'double';
117
+ } else if (t === 'float32') {
118
+ return 'float';
119
+ } else if (t === 'char') {
120
+ return 'int8';
121
+ } else if (t === 'byte') {
122
+ return 'uint8';
123
+ } else {
124
+ return '';
125
+ }
126
+ }
127
+
128
+ const primitiveBaseType = [
129
+ 'Bool',
130
+ 'Int8',
131
+ 'UInt8',
132
+ 'Int16',
133
+ 'UInt16',
134
+ 'Int32',
135
+ 'UInt32',
136
+ 'Int64',
137
+ 'UInt64',
138
+ 'Float64',
139
+ 'Float32',
140
+ 'Char',
141
+ 'Byte',
142
+ 'String',
143
+ ];
144
+
145
+ const typedArrayType = [
146
+ 'int8',
147
+ 'uint8',
148
+ 'int16',
149
+ 'uint16',
150
+ 'int32',
151
+ 'uint32',
152
+ 'float64',
153
+ 'float32',
154
+ 'char',
155
+ 'byte',
156
+ ];
157
+
158
+ function isPrimitivePackage(baseType) {
159
+ return primitiveBaseType.indexOf(baseType.type) !== -1;
160
+ }
161
+
162
+ function isTypedArrayType(type) {
163
+ return typedArrayType.indexOf(type.type.toLowerCase()) !== -1;
164
+ }
165
+
166
+ function isBigInt(type) {
167
+ return ['int64', 'uint64'].indexOf(type.type.toLowerCase()) !== -1;
168
+ }
169
+
170
+ function getWrapperNameByType(type) {
171
+ if (type.isPrimitiveType) {
172
+ return getPrimitiveNameByType(type) + 'Wrapper';
173
+ } else {
174
+ return type.type + 'Wrapper';
175
+ }
176
+ }
177
+
178
+ function getModulePathByType(type, messageInfo) {
179
+ if (type.isPrimitiveType) {
180
+ return 'std_msgs__msg__' + getPrimitiveNameByType(type) + '.js';
181
+ }
182
+
183
+ if (
184
+ messageInfo &&
185
+ messageInfo.subFolder === 'action' &&
186
+ messageInfo.pkgName === type.pkgName &&
187
+ (type.type.endsWith('_Goal') ||
188
+ type.type.endsWith('_Result') ||
189
+ type.type.endsWith('_Feedback'))
190
+ ) {
191
+ return type.pkgName + '__action__' + type.type + '.js';
192
+ }
193
+
194
+ /* We should use '__msg__' to require "service_msgs/msg/ServiceEventInfo.msg" for service event message. */
195
+ if (
196
+ messageInfo &&
197
+ messageInfo.isServiceEvent &&
198
+ type.type !== 'ServiceEventInfo'
199
+ ) {
200
+ return type.pkgName + `__${messageInfo.subFolder}__` + type.type + '.js';
201
+ }
202
+ return type.pkgName + '__msg__' + type.type + '.js';
203
+ }
204
+
205
+ function getPackageNameByType(type) {
206
+ if (type.isPrimitiveType) {
207
+ return 'std_msgs';
208
+ } else {
209
+ return type.pkgName;
210
+ }
211
+ }
212
+
213
+ function extractMemberNames(fields) {
214
+ let memberNames = [];
215
+ fields.forEach((field) => {
216
+ memberNames.push(field.name);
217
+ });
218
+ return JSON.stringify(memberNames);
219
+ }
220
+
221
+ /**
222
+ * Main template generation function
223
+ * @param {Object} data - Template data
224
+ * @param {Object} data.spec - Message specification
225
+ * @param {Object} data.messageInfo - Message information (pkgName, subFolder, interfaceName, isServiceEvent)
226
+ * @param {string} data.json - JSON string of spec
227
+ * @param {boolean} data.isDebug - Debug flag
228
+ * @returns {string} Generated JavaScript code
229
+ */
230
+ function generateMessage(data) {
231
+ const spec = data.spec;
232
+ const messageInfo = data.messageInfo;
233
+ const json = data.json;
234
+ const isDebug = data.isDebug;
235
+
236
+ // Initialize variables
237
+ const objectWrapper = spec.msgName + 'Wrapper';
238
+ const arrayWrapper = spec.msgName + 'ArrayWrapper';
239
+ const refObjectType = spec.msgName + 'RefStruct';
240
+ const refObjectArrayType = spec.msgName + 'RefStructArray';
241
+ const refArrayType = spec.msgName + 'RefArray';
242
+ const compactMsgDefJSON = JSON.stringify(JSON.parse(json));
243
+
244
+ // Add dummy field if empty (following rosidl_generator_c style)
245
+ if (spec.fields.length === 0) {
246
+ spec.isEmpty = true;
247
+ spec.fields.push({
248
+ type: {
249
+ isArray: false,
250
+ pkgName: null,
251
+ type: 'bool',
252
+ isDynamicArray: false,
253
+ stringUpperBound: null,
254
+ isUpperBound: false,
255
+ isPrimitiveType: true,
256
+ isFixedSizeArray: false,
257
+ arraySize: null,
258
+ },
259
+ name: '_dummy',
260
+ });
261
+ }
262
+
263
+ // Check if will use TypedArray
264
+ const willUseTypedArray = isTypedArrayType(spec.baseType);
265
+ const currentTypedArray = getTypedArrayName(spec.baseType);
266
+ const currentTypedArrayElementType = getTypedArrayElementName(spec.baseType);
267
+
268
+ // Track required modules
269
+ let existedModules = [];
270
+ function shouldRequire(baseType, fieldType) {
271
+ let requiredModule = `${getPackageNameByType(fieldType)}/${getModulePathByType(fieldType, messageInfo)}`;
272
+ let shouldReq =
273
+ !isPrimitivePackage(baseType) &&
274
+ (fieldType.isArray ||
275
+ !fieldType.isPrimitiveType ||
276
+ fieldType.type === 'string');
277
+
278
+ if (shouldReq && existedModules.indexOf(requiredModule) === -1) {
279
+ existedModules.push(requiredModule);
280
+ return true;
281
+ } else {
282
+ return false;
283
+ }
284
+ }
285
+
286
+ // Start generating the code
287
+ return `// This file is automatically generated by rclnodejs
288
+ //
289
+ // *** DO NOT EDIT directly
290
+ //
291
+
292
+ 'use strict';
293
+
294
+ ${willUseTypedArray ? "const rclnodejs = require('../../lib/native_loader.js');" : ''}
295
+ const ref = require('../../third_party/ref-napi');
296
+ const StructType = require('@rclnodejs/ref-struct-di')(ref);
297
+ const ArrayType = require('@rclnodejs/ref-array-di')(ref);
298
+ const primitiveTypes = require('../../rosidl_gen/primitive_types.js');
299
+ const deallocator = require('../../rosidl_gen/deallocator.js');
300
+ const translator = require('../../rosidl_gen/message_translator.js');
301
+
302
+ ${spec.fields
303
+ .map((field) => {
304
+ if (shouldRequire(spec.baseType, field.type)) {
305
+ return `const ${getWrapperNameByType(field.type)} = require('../../generated/${getPackageNameByType(field.type)}/${getModulePathByType(field.type, messageInfo)}');`;
306
+ }
307
+ return '';
308
+ })
309
+ .filter((line) => line)
310
+ .join('\n')}
311
+
312
+ ${
313
+ spec.msgName === 'String'
314
+ ? `const ${refObjectType} = primitiveTypes.string;`
315
+ : `const ${refObjectType} = StructType({
316
+ ${spec.fields
317
+ .map((field) => {
318
+ if (field.type.isPrimitiveType && !field.type.isArray) {
319
+ return ` ${field.name}: primitiveTypes.${field.type.type},`;
320
+ } else if (
321
+ field.type.isPrimitiveType &&
322
+ field.type.isArray &&
323
+ field.type.isFixedSizeArray
324
+ ) {
325
+ return ` ${field.name}: ArrayType(primitiveTypes.${field.type.type}, ${field.type.arraySize}),`;
326
+ } else if (
327
+ !field.type.isPrimitiveType &&
328
+ field.type.isArray &&
329
+ field.type.isFixedSizeArray
330
+ ) {
331
+ return ` ${field.name}: ArrayType(${getWrapperNameByType(field.type)}.refObjectType, ${field.type.arraySize}),`;
332
+ } else if (field.type.isArray) {
333
+ return ` ${field.name}: ${getWrapperNameByType(field.type)}.refObjectArrayType,`;
334
+ } else {
335
+ return ` ${field.name}: ${getWrapperNameByType(field.type)}.refObjectType,`;
336
+ }
337
+ })
338
+ .join('\n')}
339
+ });`
340
+ }
341
+
342
+ const ${refArrayType} = ArrayType(${refObjectType});
343
+ const ${refObjectArrayType} = StructType({
344
+ ${
345
+ willUseTypedArray
346
+ ? ` data: ref.refType(ref.types.${currentTypedArrayElementType}),`
347
+ : ` data: ${refArrayType},`
348
+ }
349
+ size: ref.types.size_t,
350
+ capacity: ref.types.size_t
351
+ });
352
+
353
+ ${generateWrapperClass()}
354
+
355
+ ${generateArrayWrapperClass()}
356
+
357
+ ${generateConstants()}
358
+
359
+ module.exports = ${objectWrapper};
360
+
361
+ ${isDebug ? `/*\n * The following is the original spec object coming from parser:\n${json}\n*/` : ''}
362
+ `;
363
+
364
+ // Helper function to generate the wrapper class
365
+ function generateWrapperClass() {
366
+ return `class ${objectWrapper} {
367
+ constructor(other) {
368
+ this._initialize();
369
+ this._setDefaults();
370
+
371
+ ${
372
+ spec.fields
373
+ .map(
374
+ (field) => ` if (typeof other === 'object' && other._refObject) {
375
+ this._refObject = new ${refObjectType}(other._refObject.toObject());
376
+ ${
377
+ field.type.isArray
378
+ ? ` this._wrapperFields.${field.name} = ${getWrapperNameByType(field.type)}.createArray();
379
+ this._wrapperFields.${field.name}.copy(other._wrapperFields.${field.name});
380
+ ${field.type.isPrimitiveType && !isTypedArrayType(field.type) ? ` this.${field.name} = other.${field.name};` : ''}`
381
+ : !field.type.isPrimitiveType ||
382
+ (field.type.type === 'string' && spec.msgName !== 'String')
383
+ ? ` this._wrapperFields.${field.name} = new ${getWrapperNameByType(field.type)}(other._wrapperFields.${field.name});`
384
+ : ''
385
+ }`
386
+ )
387
+ .filter((line) => line.trim())
388
+ .join('\n')[0] || ''
389
+ } if (typeof other === 'object' && other._refObject) {
390
+ this._refObject = new ${refObjectType}(other._refObject.toObject());
391
+ ${spec.fields
392
+ .map((field) => {
393
+ if (field.type.isArray) {
394
+ return ` this._wrapperFields.${field.name} = ${getWrapperNameByType(field.type)}.createArray();
395
+ this._wrapperFields.${field.name}.copy(other._wrapperFields.${field.name});
396
+ ${field.type.isPrimitiveType && !isTypedArrayType(field.type) ? ` this.${field.name} = other.${field.name};` : ''}`;
397
+ } else if (
398
+ !field.type.isPrimitiveType ||
399
+ (field.type.type === 'string' && spec.msgName !== 'String')
400
+ ) {
401
+ return ` this._wrapperFields.${field.name} = new ${getWrapperNameByType(field.type)}(other._wrapperFields.${field.name});`;
402
+ }
403
+ return '';
404
+ })
405
+ .filter((line) => line)
406
+ .join('\n')}
407
+ } else if (typeof other !== 'undefined') {
408
+ translator.constructFromPlanObject(this, other);
409
+ }
410
+ this.freeze(/*own=*/false);
411
+ }
412
+
413
+ _setDefaults() {
414
+ ${spec.fields
415
+ .map((field) => {
416
+ if (
417
+ field.type.isPrimitiveType &&
418
+ !field.type.isArray &&
419
+ field.default_value
420
+ ) {
421
+ if (field.type.type === 'string' || field.type.type === 'wstring') {
422
+ return ` this._refObject.${field.name} = "${field.default_value.replace(/"/g, '\\"')}";`;
423
+ } else {
424
+ return ` this._refObject.${field.name} = ${field.default_value};`;
425
+ }
426
+ } else if (
427
+ field.type.isPrimitiveType &&
428
+ !isTypedArrayType(field.type) &&
429
+ field.default_value
430
+ ) {
431
+ if (isBigInt(field.type)) {
432
+ return ` this._${field.name}Array = ${JSON.stringify(field.default_value)}.map(num => BigInt(num));`;
433
+ } else {
434
+ return ` this._${field.name}Array = ${JSON.stringify(field.default_value)};`;
435
+ }
436
+ } else if (
437
+ field.type.isPrimitiveType &&
438
+ isTypedArrayType(field.type) &&
439
+ field.default_value
440
+ ) {
441
+ return ` this._wrapperFields.${field.name}.fill(${getTypedArrayName(field.type)}.from(${JSON.stringify(field.default_value)}));`;
442
+ }
443
+ return '';
444
+ })
445
+ .filter((line) => line)
446
+ .join('\n')}
447
+ }
448
+
449
+ _initialize() {
450
+ this._wrapperFields = {};
451
+ this._refObject = new ${refObjectType}();
452
+ ${spec.fields
453
+ .map((field) => {
454
+ if (field.type.isArray) {
455
+ let result = ` this._wrapperFields.${field.name} = ${getWrapperNameByType(field.type)}.createArray();`;
456
+ if (field.type.type === 'string' && field.type.isFixedSizeArray) {
457
+ result += `\n for (let i = 0; i < ${field.type.arraySize}; i++) {
458
+ primitiveTypes.initString(this._refObject.${field.name}[i]);
459
+ }`;
460
+ }
461
+ if (
462
+ field.type.isArray &&
463
+ field.type.isPrimitiveType &&
464
+ !isTypedArrayType(field.type)
465
+ ) {
466
+ result += `\n this._${field.name}Array = [];`;
467
+ }
468
+ return result;
469
+ } else if (
470
+ !field.type.isPrimitiveType ||
471
+ (field.type.type === 'string' && spec.msgName !== 'String')
472
+ ) {
473
+ return ` this._wrapperFields.${field.name} = new ${getWrapperNameByType(field.type)}();`;
474
+ } else if (spec.msgName === 'String') {
475
+ return ` primitiveTypes.initString(this._refObject);`;
476
+ }
477
+ return '';
478
+ })
479
+ .filter((line) => line)
480
+ .join('\n')}
481
+ }
482
+
483
+ static createFromRefObject(refObject) {
484
+ let self = new ${objectWrapper}();
485
+ self.copyRefObject(refObject);
486
+ return self;
487
+ }
488
+
489
+ static createArray() {
490
+ return new ${arrayWrapper}();
491
+ }
492
+
493
+ static get ArrayType() {
494
+ return ${arrayWrapper};
495
+ }
496
+
497
+ static get refObjectArrayType() {
498
+ return ${refObjectArrayType};
499
+ }
500
+
501
+ static get refObjectType() {
502
+ return ${refObjectType};
503
+ }
504
+
505
+ toRawROS() {
506
+ this.freeze(/*own=*/true);
507
+ return this._refObject.ref();
508
+ }
509
+
510
+ freeze(own) {
511
+ ${generateFreezeMethod()}
512
+ }
513
+
514
+ serialize() {
515
+ this.freeze(/*own=*/false);
516
+ return this._refObject.ref();
517
+ }
518
+
519
+ deserialize(refObject) {
520
+ ${spec.fields
521
+ .map((field) => {
522
+ if (
523
+ field.type.isArray &&
524
+ field.type.isPrimitiveType &&
525
+ field.type.isFixedSizeArray &&
526
+ isTypedArrayType(field.type)
527
+ ) {
528
+ return ` this._wrapperFields.${field.name}.fill(refObject.${field.name}.toArray());`;
529
+ } else if (
530
+ field.type.isArray &&
531
+ field.type.isPrimitiveType &&
532
+ field.type.isFixedSizeArray &&
533
+ !isTypedArrayType(field.type)
534
+ ) {
535
+ if (field.type.type === 'string') {
536
+ return ` for (let index = 0; index < ${field.type.arraySize}; index++) {
537
+ this._${field.name}Array[index] = refObject.${field.name}[index].data;
538
+ }`;
539
+ } else {
540
+ return ` this._${field.name}Array = refObject.${field.name}.toArray();`;
541
+ }
542
+ } else if (
543
+ field.type.isArray &&
544
+ field.type.isPrimitiveType &&
545
+ !isTypedArrayType(field.type)
546
+ ) {
547
+ return ` refObject.${field.name}.data.length = refObject.${field.name}.size;
548
+ for (let index = 0; index < refObject.${field.name}.size; index++) {
549
+ this._${field.name}Array[index] = refObject.${field.name}.data[index].data;
550
+ }`;
551
+ } else if (
552
+ field.type.isArray &&
553
+ !field.type.isPrimitiveType &&
554
+ field.type.isFixedSizeArray
555
+ ) {
556
+ return ` this._refObject.${field.name} = refObject.${field.name};
557
+ this._wrapperFields.${field.name}.size = ${field.type.arraySize}
558
+ for (let i = 0; i < ${field.type.arraySize}; i++) {
559
+ this._wrapperFields.${field.name}.data[i].copyRefObject(refObject.${field.name}[i]);
560
+ }`;
561
+ } else if (field.type.isArray || !field.type.isPrimitiveType) {
562
+ return ` this._wrapperFields.${field.name}.copyRefObject(refObject.${field.name});`;
563
+ } else if (field.type.type === 'string' && spec.msgName !== 'String') {
564
+ return ` this._wrapperFields.${field.name}.data = refObject.${field.name}.data;`;
565
+ } else if (field.type.isPrimitiveType) {
566
+ return ` this._refObject.${field.name} = refObject.${field.name};`;
567
+ }
568
+ return '';
569
+ })
570
+ .filter((line) => line)
571
+ .join('\n')}
572
+ }
573
+
574
+ toPlainObject(enableTypedArray) {
575
+ return translator.toPlainObject(this, enableTypedArray);
576
+ }
577
+
578
+ static freeStruct(refObject) {
579
+ ${generateFreeStructMethod()}
580
+ }
581
+
582
+ static destroyRawROS(msg) {
583
+ ${objectWrapper}.freeStruct(msg.refObject);
584
+ }
585
+
586
+ static type() {
587
+ return {pkgName: '${messageInfo.pkgName}', subFolder: '${messageInfo.subFolder}', interfaceName: '${messageInfo.interfaceName}'};
588
+ }
589
+
590
+ static isPrimitive() {
591
+ return ${isPrimitivePackage(spec.baseType)};
592
+ }
593
+
594
+ static get isROSArray() {
595
+ return false;
596
+ }
597
+
598
+ get refObject() {
599
+ return this._refObject;
600
+ }
601
+
602
+ ${generateGettersSetters()}
603
+
604
+ copyRefObject(refObject) {
605
+ this._refObject = new ${refObjectType}(refObject.toObject());
606
+
607
+ ${spec.fields
608
+ .map((field) => {
609
+ if (
610
+ field.type.isArray &&
611
+ field.type.isPrimitiveType &&
612
+ !isTypedArrayType(field.type)
613
+ ) {
614
+ return ` refObject.${field.name}.data.length = refObject.${field.name}.size;
615
+ for (let index = 0; index < refObject.${field.name}.size; index++) {
616
+ this._${field.name}Array[index] = refObject.${field.name}.data[index].data;
617
+ }`;
618
+ } else if (
619
+ field.type.isArray &&
620
+ field.type.isPrimitiveType &&
621
+ field.type.isFixedSizeArray
622
+ ) {
623
+ return ` this._wrapperFields.${field.name}.fill(refObject.${field.name}.toArray());`;
624
+ } else if (
625
+ field.type.isArray &&
626
+ !field.type.isPrimitiveType &&
627
+ field.type.isFixedSizeArray
628
+ ) {
629
+ return ` this._refObject.${field.name} = refObject.${field.name};
630
+ this._wrapperFields.${field.name}.size = ${field.type.arraySize}
631
+ for (let i = 0; i < ${field.type.arraySize}; i++) {
632
+ this._wrapperFields.${field.name}.data[i].copyRefObject(refObject.${field.name}[i]);
633
+ }`;
634
+ } else if (
635
+ !field.type.isPrimitiveType ||
636
+ field.type.isArray ||
637
+ (field.type.type === 'string' && spec.msgName !== 'String')
638
+ ) {
639
+ return ` this._wrapperFields.${field.name}.copyRefObject(this._refObject.${field.name});`;
640
+ }
641
+ return '';
642
+ })
643
+ .filter((line) => line)
644
+ .join('\n')}
645
+ }
646
+
647
+ copy(other) {
648
+ this._refObject = new ${refObjectType}(other._refObject.toObject());
649
+
650
+ ${spec.fields
651
+ .map((field) => {
652
+ if (
653
+ field.type.isArray &&
654
+ field.type.isPrimitiveType &&
655
+ !isTypedArrayType(field.type)
656
+ ) {
657
+ return ` this._${field.name}Array = other._${field.name}Array.slice();`;
658
+ } else if (
659
+ !field.type.isPrimitiveType ||
660
+ field.type.isArray ||
661
+ (field.type.type === 'string' && spec.msgName !== 'String')
662
+ ) {
663
+ return ` this._wrapperFields.${field.name}.copy(other._wrapperFields.${field.name});`;
664
+ }
665
+ return '';
666
+ })
667
+ .filter((line) => line)
668
+ .join('\n')}
669
+ }
670
+
671
+ static get classType() {
672
+ return ${objectWrapper};
673
+ }
674
+
675
+ static get ROSMessageDef() {
676
+ return ${compactMsgDefJSON};
677
+ }
678
+
679
+ hasMember(name) {
680
+ let memberNames = ${extractMemberNames(spec.fields)};
681
+ return memberNames.indexOf(name) !== -1;
682
+ }
683
+ }`;
684
+ }
685
+
686
+ // Helper function to generate freeze method
687
+ function generateFreezeMethod() {
688
+ return spec.fields
689
+ .map((field) => {
690
+ if (
691
+ field.type.isArray &&
692
+ field.type.isPrimitiveType &&
693
+ !isTypedArrayType(field.type) &&
694
+ field.type.isFixedSizeArray
695
+ ) {
696
+ if (field.type.type === 'string') {
697
+ return ` for (let i = 0; i < ${field.type.arraySize}; i++) {
698
+ if (own) {
699
+ primitiveTypes.initString(this._refObject.${field.name}[i].ref(), own);
700
+ } else {
701
+ if (this._${field.name}Array.length === ${field.type.arraySize}) {
702
+ const value = this._${field.name}Array[i];
703
+ this._refObject.${field.name}[i].data = value;
704
+ this._refObject.${field.name}[i].size = Buffer.byteLength(value);
705
+ this._refObject.${field.name}[i].capacity = Buffer.byteLength(value) + 1;
706
+ }
707
+ }
708
+ }`;
709
+ } else if (isBigInt(field.type)) {
710
+ return ` this._refObject.${field.name} = this._${field.name}Array.map(num => num.toString());`;
711
+ } else {
712
+ return ` this._refObject.${field.name} = this._${field.name}Array;`;
713
+ }
714
+ } else if (
715
+ field.type.isArray &&
716
+ field.type.isPrimitiveType &&
717
+ isTypedArrayType(field.type) &&
718
+ field.type.isFixedSizeArray
719
+ ) {
720
+ return ` this._refObject.${field.name} = Array.from(this._wrapperFields.${field.name}.data);`;
721
+ } else if (
722
+ field.type.isArray &&
723
+ field.type.isPrimitiveType &&
724
+ !isTypedArrayType(field.type)
725
+ ) {
726
+ return ` if (own) {
727
+ this._wrapperFields.${field.name}.fill([]);
728
+ } else {
729
+ this._wrapperFields.${field.name}.fill(this._${field.name}Array);
730
+ }
731
+ this._wrapperFields.${field.name}.freeze(own);
732
+ this._refObject.${field.name} = this._wrapperFields.${field.name}.refObject;`;
733
+ } else if (
734
+ field.type.isArray &&
735
+ field.type.isPrimitiveType &&
736
+ isTypedArrayType(field.type)
737
+ ) {
738
+ return ` if (own) {
739
+ this._wrapperFields.${field.name}.fill(${getTypedArrayName(field.type)}.from([]));
740
+ }
741
+ this._wrapperFields.${field.name}.freeze(own);
742
+ this._refObject.${field.name} = this._wrapperFields.${field.name}.refObject;`;
743
+ } else if (
744
+ field.type.isArray &&
745
+ !field.type.isPrimitiveType &&
746
+ field.type.isFixedSizeArray
747
+ ) {
748
+ return ` for (let i = 0; i < ${field.type.arraySize}; i++) {
749
+ if (this._wrapperFields.${field.name}.data[i]) {
750
+ this._wrapperFields.${field.name}.data[i].freeze(own);
751
+ this._refObject.${field.name}[i] = this._wrapperFields.${field.name}.data[i].refObject;
752
+ }
753
+ }`;
754
+ } else if (field.type.isArray || !field.type.isPrimitiveType) {
755
+ let result = '';
756
+ if (field.type.isArray) {
757
+ result += ` if (own) {
758
+ this._wrapperFields.${field.name}.fill([]);
759
+ }
760
+ `;
761
+ }
762
+ result += ` this._wrapperFields.${field.name}.freeze(own);
763
+ this._refObject.${field.name} = this._wrapperFields.${field.name}.refObject;`;
764
+ return result;
765
+ } else if (field.type.type === 'string' && spec.msgName !== 'String') {
766
+ return ` if (own) {
767
+ this._wrapperFields.${field.name}.freeze(own);
768
+ }
769
+ this._refObject.${field.name} = this._wrapperFields.${field.name}.refObject;`;
770
+ } else if (spec.msgName === 'String') {
771
+ return ` if (own) {
772
+ primitiveTypes.initString(this._refObject.ref(), own);
773
+ }`;
774
+ }
775
+ return '';
776
+ })
777
+ .filter((line) => line)
778
+ .join('\n');
779
+ }
780
+
781
+ // Helper function to generate freeStruct method
782
+ function generateFreeStructMethod() {
783
+ return spec.fields
784
+ .map((field) => {
785
+ if (field.type.isArray && !field.type.isFixedSizeArray) {
786
+ return ` if (refObject.${field.name}.size != 0) {
787
+ ${getWrapperNameByType(field.type)}.ArrayType.freeArray(refObject.${field.name});
788
+ if (!${getWrapperNameByType(field.type)}.ArrayType.useTypedArray) {
789
+ deallocator.freeStructMember(refObject.${field.name}, ${getWrapperNameByType(field.type)}.refObjectArrayType, 'data');
790
+ }
791
+ }`;
792
+ } else if (
793
+ field.type.isArray &&
794
+ !field.type.isPrimitiveType &&
795
+ field.type.isFixedSizeArray
796
+ ) {
797
+ return ` for (let i = 0; i < ${field.type.arraySize}; i++) {
798
+ ${getWrapperNameByType(field.type)}.freeStruct(refObject.${field.name}[i]);
799
+ }`;
800
+ } else if (
801
+ field.type.isArray &&
802
+ field.type.type === 'string' &&
803
+ field.type.isFixedSizeArray
804
+ ) {
805
+ return ` for (let i = 0; i < ${field.type.arraySize}; i++) {
806
+ ${getWrapperNameByType(field.type)}.freeStruct(refObject.${field.name}[i]);
807
+ }`;
808
+ } else if (
809
+ !field.type.isPrimitiveType ||
810
+ (field.type.type === 'string' && spec.msgName !== 'String')
811
+ ) {
812
+ return ` ${getWrapperNameByType(field.type)}.freeStruct(refObject.${field.name});`;
813
+ } else if (spec.msgName === 'String') {
814
+ return ` deallocator.freeStructMember(refObject, ${getWrapperNameByType(field.type)}.refObjectType, '${field.name}');`;
815
+ }
816
+ return '';
817
+ })
818
+ .filter((line) => line)
819
+ .join('\n');
820
+ }
821
+
822
+ // Helper function to generate getters and setters
823
+ function generateGettersSetters() {
824
+ return spec.fields
825
+ .map(
826
+ (field) => ` get ${field.name}() {
827
+ ${generateGetter(field)}
828
+ }
829
+
830
+ set ${field.name}(value) {
831
+ ${generateSetter(field)}
832
+ }`
833
+ )
834
+ .join('\n\n');
835
+ }
836
+
837
+ function generateGetter(field) {
838
+ if (field.type.isArray && field.type.type === 'Constants') {
839
+ return ` return [];`;
840
+ } else if (field.type.isArray && isTypedArrayType(field.type)) {
841
+ return ` return this._wrapperFields['${field.name}'].data;`;
842
+ } else if (field.type.isArray && field.type.isPrimitiveType) {
843
+ return ` return this._${field.name}Array;`;
844
+ } else if (field.type.isArray && !field.type.isPrimitiveType) {
845
+ return ` return this._wrapperFields.${field.name};`;
846
+ } else if (!field.type.isPrimitiveType && !field.type.isArray) {
847
+ return ` return this._wrapperFields.${field.name};`;
848
+ } else if (
849
+ !field.type.isArray &&
850
+ field.type.type === 'string' &&
851
+ spec.msgName !== 'String'
852
+ ) {
853
+ return ` return this._wrapperFields.${field.name}.data;`;
854
+ } else if (isBigInt(field.type)) {
855
+ return ` return BigInt(this._refObject.${field.name});`;
856
+ } else {
857
+ return ` return this._refObject.${field.name};`;
858
+ }
859
+ }
860
+
861
+ function generateSetter(field) {
862
+ let result = '';
863
+
864
+ if (field.type.isArray && field.type.isFixedSizeArray) {
865
+ result += ` if (value.length !== ${field.type.arraySize}) {
866
+ throw new RangeError('The length of the array must be ${field.type.arraySize}.');
867
+ }
868
+
869
+ `;
870
+ }
871
+ if (field.type.isArray && field.type.isUpperBound) {
872
+ result += ` if (value.length > ${field.type.arraySize}) {
873
+ throw new RangeError('The length of array ${field.name} must be <= ${field.type.arraySize}.');
874
+ }
875
+ `;
876
+ }
877
+
878
+ if (field.type.isArray && isTypedArrayType(field.type)) {
879
+ result += ` this._wrapperFields['${field.name}'].fill(value);`;
880
+ } else if (field.type.isArray && field.type.isPrimitiveType) {
881
+ result += ` this._${field.name}Array = value;`;
882
+ } else if (field.type.isArray && !field.type.isPrimitiveType) {
883
+ result += ` this._wrapperFields.${field.name}.fill(value);`;
884
+ } else if (!field.type.isPrimitiveType && !field.type.isArray) {
885
+ result += ` if (value instanceof ${getWrapperNameByType(field.type)}) {
886
+ this._wrapperFields.${field.name}.copy(value);
887
+ } else {
888
+ this._wrapperFields.${field.name}.copy(new ${getWrapperNameByType(field.type)}(value));
889
+ }`;
890
+ } else if (
891
+ !field.type.isArray &&
892
+ field.type.type === 'string' &&
893
+ spec.msgName !== 'String'
894
+ ) {
895
+ result += ` this._wrapperFields.${field.name}.data = value;`;
896
+ } else if (isBigInt(field.type)) {
897
+ result += ` if (typeof value !== "bigint") {
898
+ throw new TypeError('${field.name} must be type of bigint');
899
+ }
900
+ this._refObject.${field.name} = value.toString();`;
901
+ } else {
902
+ if (spec.msgName === 'String') {
903
+ result += ` this._refObject.size = Buffer.byteLength(value);
904
+ this._refObject.capacity = Buffer.byteLength(value) + 1;
905
+ `;
906
+ }
907
+ result += ` this._refObject.${field.name} = value;`;
908
+ }
909
+
910
+ return result;
911
+ }
912
+
913
+ // Helper function to generate array wrapper class
914
+ function generateArrayWrapperClass() {
915
+ return `class ${arrayWrapper} {
916
+ constructor(size = 0) {
917
+ this._resize(size);
918
+ }
919
+
920
+ toRawROS() {
921
+ return this._refObject.ref();
922
+ }
923
+
924
+ fill(values) {
925
+ ${
926
+ willUseTypedArray
927
+ ? ` if (Array.isArray(values)) {
928
+ this._wrappers = new ${currentTypedArray}(values);
929
+ } else {
930
+ this._wrappers = values;
931
+ }`
932
+ : isPrimitivePackage(spec.baseType)
933
+ ? ` const length = values.length;
934
+ this._resize(length);
935
+ for (let i = 0; i < length; ++i) {
936
+ let wrapper = new ${objectWrapper}();
937
+ wrapper.data = values[i];
938
+ this._wrappers[i] = wrapper;
939
+ }`
940
+ : ` const length = values.length;
941
+ this._resize(length);
942
+ values.forEach((value, index) => {
943
+ if (value instanceof ${objectWrapper}) {
944
+ this._wrappers[index].copy(value);
945
+ } else {
946
+ this._wrappers[index] = new ${objectWrapper}(value);
947
+ }
948
+ });`
949
+ }
950
+ }
951
+
952
+ freeze(own) {
953
+ ${
954
+ !willUseTypedArray
955
+ ? ` this._wrappers.forEach((wrapper, index) => {
956
+ wrapper.freeze(own);
957
+ this._refArray[index] = wrapper.refObject;
958
+ });
959
+ `
960
+ : ''
961
+ }
962
+ this._refObject.size = this._wrappers.length;
963
+ this._refObject.capacity = this._wrappers.length;
964
+
965
+ if (this._refObject.capacity === 0) {
966
+ this._refObject.data = null
967
+ } else {
968
+ ${
969
+ willUseTypedArray
970
+ ? ` const buffer = Buffer.from(new Uint8Array(this._wrappers.buffer));
971
+ this._refObject.data = buffer;`
972
+ : ` this._refObject.data = this._refArray.buffer;`
973
+ }
974
+ }
975
+ }
976
+
977
+ get refObject() {
978
+ return this._refObject;
979
+ }
980
+
981
+ get data() {
982
+ return this._wrappers;
983
+ }
984
+
985
+ get size() {
986
+ return this._wrappers.length;
987
+ }
988
+
989
+ set size(value) {
990
+ if (typeof value != 'number') {
991
+ throw new TypeError('Invalid argument: should provide a number to ${arrayWrapper}.size setter');
992
+ }
993
+ return this._resize(value);
994
+ }
995
+
996
+ get capacity() {
997
+ return this._wrappers.length;
998
+ }
999
+
1000
+ set capacity(value) {
1001
+ if (typeof value != 'number') {
1002
+ throw new TypeError('Invalid argument: should provide a number to ${arrayWrapper}.capacity setter');
1003
+ }
1004
+ return this._resize(value);
1005
+ }
1006
+
1007
+ _resize(size) {
1008
+ if (size < 0) {
1009
+ throw new RangeError('Invalid argument: should provide a positive number');
1010
+ }
1011
+ ${
1012
+ willUseTypedArray
1013
+ ? ` this._refArray = undefined;`
1014
+ : ` this._refArray = new ${refArrayType}(size);`
1015
+ }
1016
+
1017
+ this._refObject = new ${refObjectArrayType}();
1018
+ this._refObject.size = size;
1019
+ this._refObject.capacity = size;
1020
+
1021
+ ${
1022
+ willUseTypedArray
1023
+ ? ` this._wrappers = new ${currentTypedArray}(size);`
1024
+ : ` this._wrappers = new Array();
1025
+ for (let i = 0; i < size; i++) {
1026
+ this._wrappers.push(new ${objectWrapper}());
1027
+ }`
1028
+ }
1029
+ }
1030
+
1031
+ copyRefObject(refObject) {
1032
+ this._refObject = refObject;
1033
+
1034
+ ${
1035
+ willUseTypedArray
1036
+ ? ` const byteLen = refObject.size * ref.types.${currentTypedArrayElementType}.size;
1037
+ const arrayBuffer = refObject.data.length !== 0 ?
1038
+ rclnodejs.createArrayBufferFromAddress(refObject.data.hexAddress(), byteLen) :
1039
+ Buffer.alloc(0);
1040
+ this._wrappers = new ${currentTypedArray}(arrayBuffer);`
1041
+ : ` let refObjectArray = this._refObject.data;
1042
+ refObjectArray.length = this._refObject.size;
1043
+ this._resize(this._refObject.size);
1044
+ for (let index = 0; index < this._refObject.size; index++) {
1045
+ this._wrappers[index].copyRefObject(refObjectArray[index]);
1046
+ }`
1047
+ }
1048
+ }
1049
+
1050
+ copy(other) {
1051
+ if (! (other instanceof ${arrayWrapper})) {
1052
+ throw new TypeError('Invalid argument: should provide "${arrayWrapper}".');
1053
+ }
1054
+
1055
+ this._resize(other.size);
1056
+ ${
1057
+ willUseTypedArray
1058
+ ? ` this._wrappers = other._wrappers.slice();`
1059
+ : ` other._wrappers.forEach((wrapper, index) => {
1060
+ this._wrappers[index].copy(wrapper);
1061
+ });`
1062
+ }
1063
+ }
1064
+
1065
+ static freeArray(refObject) {
1066
+ ${
1067
+ !willUseTypedArray
1068
+ ? ` let refObjectArray = refObject.data;
1069
+ refObjectArray.length = refObject.size;
1070
+ for (let index = 0; index < refObject.size; index++) {
1071
+ ${objectWrapper}.freeStruct(refObjectArray[index]);
1072
+ }`
1073
+ : ''
1074
+ }
1075
+ }
1076
+
1077
+ static get elementType() {
1078
+ return ${objectWrapper};
1079
+ }
1080
+
1081
+ static get isROSArray() {
1082
+ return true;
1083
+ }
1084
+
1085
+ static get useTypedArray() {
1086
+ return ${willUseTypedArray};
1087
+ }
1088
+
1089
+ get classType() {
1090
+ return ${arrayWrapper};
1091
+ }
1092
+
1093
+ toPlainObject(enableTypedArray) {
1094
+ return translator.toPlainObject(this, enableTypedArray);
1095
+ }
1096
+ }`;
1097
+ }
1098
+
1099
+ // Helper function to generate constants
1100
+ function generateConstants() {
1101
+ if (spec.constants && spec.constants.length > 0) {
1102
+ return spec.constants
1103
+ .map((c) => {
1104
+ const value = c.type === 'string' ? `"${c.value}"` : c.value;
1105
+ return `Object.defineProperty(${objectWrapper}, "${c.name}", {value: ${value}, writable: false, enumerable: true, configurable: true});`;
1106
+ })
1107
+ .join('\n');
1108
+ }
1109
+ return '';
1110
+ }
1111
+ }
1112
+
1113
+ module.exports = generateMessage;