Skip to content

Commit 5e5d647

Browse files
committed
feat: add descriptor namespaces
1 parent baba669 commit 5e5d647

File tree

1 file changed

+167
-88
lines changed

1 file changed

+167
-88
lines changed

rostsd_gen/index.js

Lines changed: 167 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const fs = require('fs');
3131
const loader = require('../lib/interface_loader.js');
3232
const pkgFilters = require('../rosidl_gen/filter.js');
3333

34+
const descriptorInterfaceNamespace = 'descriptor';
35+
3436
async function generateAll() {
3537
// load pkg and interface info (msgs and srvs)
3638
const generatedPath = path.join(__dirname, '../generated/');
@@ -119,47 +121,30 @@ function savePkgInfoAsTSD(pkgInfos, fd) {
119121
for (const subfolder of pkgInfo.subfolders.keys()) {
120122
fs.writeSync(fd, ` namespace ${subfolder} {\n`);
121123

122-
for (const rosInterface of pkgInfo.subfolders.get(subfolder)) {
123-
const type = rosInterface.type();
124-
const fullInterfaceName = `${type.pkgName}/${type.subFolder}/${type.interfaceName}`;
125-
const fullInterfacePath = `${type.pkgName}.${type.subFolder}.${type.interfaceName}`;
126-
const fullInterfaceConstructor = fullInterfacePath + 'Constructor';
127-
128-
if (isMsgInterface(rosInterface)) {
129-
// create message interface
130-
saveMsgAsTSD(rosInterface, fd);
131-
saveMsgConstructorAsTSD(rosInterface, fd);
132-
messagesMap[fullInterfaceName] = fullInterfacePath;
133-
} else if (isSrvInterface(rosInterface)) {
134-
if (
135-
!isValidService(rosInterface, pkgInfo.subfolders.get(subfolder))
136-
) {
137-
let type = rosInterface.type();
138-
console.log(
139-
`Incomplete service: ${type.pkgName}.${type.subFolder}.${type.interfaceName}.`
140-
);
141-
continue;
142-
}
143-
144-
// create service interface
145-
saveSrvAsTSD(rosInterface, fd);
146-
if (!isInternalActionSrvInterface(rosInterface)) {
147-
servicesMap[fullInterfaceName] = fullInterfaceConstructor;
148-
}
149-
} else if (isActionInterface(rosInterface)) {
150-
if (!isValidAction(rosInterface, pkgInfo.subfolders.get(subfolder))) {
151-
let type = rosInterface.type();
152-
console.log(
153-
`Incomplete action: ${type.pkgName}.${type.subFolder}.${type.interfaceName}.`
154-
);
155-
continue;
156-
}
157-
158-
// create action interface
159-
saveActionAsTSD(rosInterface, fd);
160-
actionsMap[fullInterfaceName] = fullInterfaceConstructor;
161-
}
162-
}
124+
// generate real msg/srv/action interfaces
125+
generateRosMsgInterfaces(
126+
pkgInfo,
127+
subfolder,
128+
messagesMap,
129+
servicesMap,
130+
actionsMap,
131+
fd
132+
);
133+
134+
// generate descriptor msg/srv/action interfaces
135+
fs.writeSync(fd, ` namespace ${descriptorInterfaceNamespace} {\n`);
136+
const descriptorInterfaceType = true;
137+
generateRosMsgInterfaces(
138+
pkgInfo,
139+
subfolder,
140+
messagesMap,
141+
servicesMap,
142+
actionsMap,
143+
fd,
144+
descriptorInterfaceType
145+
);
146+
// close namespace descriptor declare
147+
fs.writeSync(fd, ' }\n');
163148

164149
// close namespace declare
165150
fs.writeSync(fd, ' }\n');
@@ -238,16 +223,78 @@ function savePkgInfoAsTSD(pkgInfos, fd) {
238223
fs.writeSync(fd, '}\n');
239224
}
240225

241-
function saveMsgAsTSD(rosMsgInterface, fd) {
242-
fs.writeSync(
243-
fd,
244-
` export interface ${rosMsgInterface.type().interfaceName} {\n`
226+
function generateRosMsgInterfaces(
227+
pkgInfo,
228+
subfolder,
229+
messagesMap,
230+
servicesMap,
231+
actionsMap,
232+
fd,
233+
descriptorInterfaceType = false
234+
) {
235+
for (const rosInterface of pkgInfo.subfolders.get(subfolder)) {
236+
const type = rosInterface.type();
237+
const fullInterfaceName = `${type.pkgName}/${type.subFolder}/${type.interfaceName}`;
238+
const fullInterfacePath = `${type.pkgName}.${type.subFolder}.${type.interfaceName}`;
239+
const fullInterfaceConstructor = fullInterfacePath + 'Constructor';
240+
241+
if (isMsgInterface(rosInterface)) {
242+
// create message interface
243+
saveMsgAsTSD(rosInterface, fd, descriptorInterfaceType);
244+
saveMsgConstructorAsTSD(rosInterface, fd, descriptorInterfaceType);
245+
messagesMap[fullInterfaceName] = fullInterfacePath;
246+
} else if (isSrvInterface(rosInterface)) {
247+
if (!isValidService(rosInterface, pkgInfo.subfolders.get(subfolder))) {
248+
let type = rosInterface.type();
249+
console.log(
250+
`Incomplete service: ${type.pkgName}.${type.subFolder}.${type.interfaceName}.`
251+
);
252+
continue;
253+
}
254+
255+
// create service interface
256+
saveSrvAsTSD(rosInterface, fd, descriptorInterfaceType);
257+
if (!isInternalActionSrvInterface(rosInterface)) {
258+
servicesMap[fullInterfaceName] = fullInterfaceConstructor;
259+
}
260+
} else if (isActionInterface(rosInterface)) {
261+
if (!isValidAction(rosInterface, pkgInfo.subfolders.get(subfolder))) {
262+
let type = rosInterface.type();
263+
console.log(
264+
`Incomplete action: ${type.pkgName}.${type.subFolder}.${type.interfaceName}.`
265+
);
266+
continue;
267+
}
268+
269+
// create action interface
270+
saveActionAsTSD(rosInterface, fd, descriptorInterfaceType);
271+
actionsMap[fullInterfaceName] = fullInterfaceConstructor;
272+
}
273+
}
274+
}
275+
276+
function saveMsgAsTSD(rosMsgInterface, fd, descriptorInterfaceType = false) {
277+
const indentlevel = descriptorInterfaceType ? 8 : 6;
278+
const tmpl = indentString(
279+
`export interface ${rosMsgInterface.type().interfaceName} {\n`,
280+
indentlevel
245281
);
282+
fs.writeSync(fd, tmpl);
246283
const useSamePkg =
247284
isInternalActionMsgInterface(rosMsgInterface) ||
248285
isInternalServiceEventMsgInterface(rosMsgInterface);
249-
saveMsgFieldsAsTSD(rosMsgInterface, fd, 8, ';', '', useSamePkg);
250-
fs.writeSync(fd, ' }\n');
286+
const indentLevel = descriptorInterfaceType ? 10 : 8;
287+
saveMsgFieldsAsTSD(
288+
rosMsgInterface,
289+
fd,
290+
indentLevel,
291+
';',
292+
'',
293+
useSamePkg,
294+
descriptorInterfaceType
295+
);
296+
const tmplEnd = indentString('}\n', indentlevel);
297+
fs.writeSync(fd, tmplEnd);
251298
}
252299

253300
/**
@@ -261,6 +308,7 @@ function saveMsgAsTSD(rosMsgInterface, fd) {
261308
* @param {string} typePrefix The prefix to put before the type name for
262309
* non-primitive types
263310
* @param {boolean} useSamePackageSubFolder Indicates if the sub folder name should be taken from the message
311+
* @param {boolean} descriptorInterfaceType Indicates if descriptor interface is being generated
264312
* when the field type comes from the same package. This is needed for action interfaces. Defaults to false.
265313
* @returns {undefined}
266314
*/
@@ -270,7 +318,8 @@ function saveMsgFieldsAsTSD(
270318
indent = 0,
271319
lineEnd = ',',
272320
typePrefix = '',
273-
useSamePackageSubFolder = false
321+
useSamePackageSubFolder = false,
322+
descriptorInterfaceType = false
274323
) {
275324
let type = rosMsgInterface.type();
276325
let fields = rosMsgInterface.ROSMessageDef.fields;
@@ -280,75 +329,96 @@ function saveMsgFieldsAsTSD(
280329
useSamePackageSubFolder && field.type.pkgName === type.pkgName
281330
? type.subFolder
282331
: 'msg';
283-
let fieldType = fieldType2JSName(field, subFolder);
332+
let fieldType = fieldType2JSName(field, subFolder, descriptorInterfaceType);
284333
let tp = field.type.isPrimitiveType ? '' : typePrefix;
285334
if (typePrefix === 'rclnodejs.') {
286335
fieldType = 'any';
287336
tp = '';
288337
}
289338

290-
const tmpl = indentString(`${field.name}: ${tp}${fieldType}`, indent);
291-
fs.writeSync(fd, tmpl);
339+
let arrayString = '';
292340
if (field.type.isArray) {
293-
fs.writeSync(fd, '[]');
341+
arrayString = '[]';
294342

295-
if (fieldType === 'number') {
343+
if (field.type.isFixedSizeArray && descriptorInterfaceType) {
344+
arrayString = `[${field.type.arraySize}]`;
345+
}
346+
347+
if (fieldType === 'number' && !descriptorInterfaceType) {
296348
// for number[] include alternate typed-array types, e.g., number[] | uint8[]
297349
let jsTypedArrayName = fieldTypeArray2JSTypedArrayName(field.type.type);
298350

299351
if (jsTypedArrayName) {
300-
fs.writeSync(fd, ` | ${jsTypedArrayName}`);
352+
arrayString += ` | ${jsTypedArrayName}`;
301353
}
302354
}
303355
}
356+
const fieldString = descriptorInterfaceType
357+
? `${field.name}: '${tp}${fieldType}${arrayString}'`
358+
: `${field.name}: ${tp}${fieldType}${arrayString}`;
359+
const tmpl = indentString(fieldString, indent);
360+
fs.writeSync(fd, tmpl);
304361

305362
fs.writeSync(fd, lineEnd);
306363
fs.writeSync(fd, '\n');
307364
}
308365
}
309366

310-
function saveMsgConstructorAsTSD(rosMsgInterface, fd) {
367+
function saveMsgConstructorAsTSD(
368+
rosMsgInterface,
369+
fd,
370+
descriptorInterfaceType = false
371+
) {
311372
const type = rosMsgInterface.type();
312373
const msgName = type.interfaceName;
374+
if (!descriptorInterfaceType) {
375+
fs.writeSync(fd, ` export interface ${msgName}Constructor {\n`);
313376

314-
fs.writeSync(fd, ` export interface ${msgName}Constructor {\n`);
377+
for (const constant of rosMsgInterface.ROSMessageDef.constants) {
378+
const constantType = primitiveType2JSName(constant.type);
379+
fs.writeSync(fd, ` readonly ${constant.name}: ${constantType};\n`);
380+
}
315381

316-
for (const constant of rosMsgInterface.ROSMessageDef.constants) {
317-
const constantType = primitiveType2JSName(constant.type);
318-
fs.writeSync(fd, ` readonly ${constant.name}: ${constantType};\n`);
382+
fs.writeSync(fd, ` new(other?: ${msgName}): ${msgName};\n`);
383+
fs.writeSync(fd, ' }\n');
319384
}
320-
321-
fs.writeSync(fd, ` new(other?: ${msgName}): ${msgName};\n`);
322-
fs.writeSync(fd, ' }\n');
323385
}
324386

325-
function saveSrvAsTSD(rosSrvInterface, fd) {
326-
const serviceName = rosSrvInterface.type().interfaceName;
327-
328-
const interfaceTemplate = [
329-
`export interface ${serviceName}Constructor extends ROSService {`,
330-
` readonly Request: ${serviceName}_RequestConstructor;`,
331-
` readonly Response: ${serviceName}_ResponseConstructor;`,
332-
'}',
333-
'',
334-
];
335-
336-
fs.writeSync(fd, indentLines(interfaceTemplate, 6).join('\n'));
387+
function saveSrvAsTSD(rosSrvInterface, fd, descriptorInterfaceType = false) {
388+
if (!descriptorInterfaceType) {
389+
const serviceName = rosSrvInterface.type().interfaceName;
390+
391+
const interfaceTemplate = [
392+
`export interface ${serviceName}Constructor extends ROSService {`,
393+
` readonly Request: ${serviceName}_RequestConstructor;`,
394+
` readonly Response: ${serviceName}_ResponseConstructor;`,
395+
'}',
396+
'',
397+
];
398+
const indentLevel = 6;
399+
fs.writeSync(fd, indentLines(interfaceTemplate, indentLevel).join('\n'));
400+
}
337401
}
338402

339-
function saveActionAsTSD(rosActionInterface, fd) {
340-
const actionName = rosActionInterface.type().interfaceName;
341-
342-
const interfaceTemplate = [
343-
`export interface ${actionName}Constructor {`,
344-
` readonly Goal: ${actionName}_GoalConstructor;`,
345-
` readonly Result: ${actionName}_ResultConstructor;`,
346-
` readonly Feedback: ${actionName}_FeedbackConstructor;`,
347-
'}',
348-
'',
349-
];
350-
351-
fs.writeSync(fd, indentLines(interfaceTemplate, 6).join('\n'));
403+
function saveActionAsTSD(
404+
rosActionInterface,
405+
fd,
406+
descriptorInterfaceType = false
407+
) {
408+
if (!descriptorInterfaceNamespace) {
409+
const actionName = rosActionInterface.type().interfaceName;
410+
411+
const interfaceTemplate = [
412+
`export interface ${actionName}Constructor {`,
413+
` readonly Goal: ${actionName}_GoalConstructor;`,
414+
` readonly Result: ${actionName}_ResultConstructor;`,
415+
` readonly Feedback: ${actionName}_FeedbackConstructor;`,
416+
'}',
417+
'',
418+
];
419+
const indentLevel = 6;
420+
fs.writeSync(fd, indentLines(interfaceTemplate, indentLevel).join('\n'));
421+
}
352422
}
353423

354424
function isMsgInterface(rosInterface) {
@@ -451,7 +521,16 @@ function isValidAction(rosActionInterface, infos) {
451521
return matches === SUCCESS_MATCH_COUNT;
452522
}
453523

454-
function fieldType2JSName(fieldInfo, subFolder = 'msg') {
524+
function fieldType2JSName(
525+
fieldInfo,
526+
subFolder = 'msg',
527+
descriptorInterfaceType = false
528+
) {
529+
if (descriptorInterfaceType) {
530+
return fieldInfo.type.isPrimitiveType
531+
? `${fieldInfo.type.type}`
532+
: `${fieldInfo.type.pkgName}/${subFolder}/${fieldInfo.type.type}`;
533+
}
455534
return fieldInfo.type.isPrimitiveType
456535
? primitiveType2JSName(fieldInfo.type.type)
457536
: `${fieldInfo.type.pkgName}.${subFolder}.${fieldInfo.type.type}`;

0 commit comments

Comments
 (0)