UNPKG

rclnodejs

Version:
218 lines (193 loc) 6.76 kB
// Copyright (c) 2017 Intel Corporation. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. 'use strict'; const path = require('path'); const fs = require('fs'); const generator = require('../rosidl_gen/index.js'); let interfaceLoader = { loadInterfaceByObject(obj) { // // `obj` param structure // // { // package: 'std_msgs', // type: 'msg', // name: 'String', // } // if ( typeof obj !== 'object' || !obj || !obj.package || !obj.type || !obj.name ) { throw new TypeError( 'Should provide an object argument to get ROS message class' ); } return this.loadInterface(obj.package, obj.type, obj.name); }, loadInterfaceByString(name) { if (typeof name !== 'string') { throw new TypeError( 'Should provide a string argument to get ROS message class' ); } // TODO(Kenny): more checks of the string argument if (name.indexOf('/') !== -1) { let [packageName, type, messageName] = name.split('/'); return this.loadInterface(packageName, type, messageName); } // Suppose the name is a package, and traverse the path to collect the IDL files. let packagePath = path.join(generator.generatedRoot, name); let interfaces = fs.readdirSync(packagePath); if (interfaces.length > 0) { return this.loadInterfaceByPath(packagePath, interfaces); } throw new TypeError( 'A string argument in expected in "package/type/message" format' ); }, loadInterfaceByPath(packagePath, interfaces) { let interfaceInfos = []; interfaces.forEach((file) => { let results = file.match(/\w+__(\w+)__(\w+).js$/); let type = results[1]; let name = results[2]; let filePath = path.join(packagePath, file).normalize(); interfaceInfos.push({ name, type, filePath }); }); let pkg = { srv: {}, msg: {}, action: {} }; interfaceInfos.forEach((info) => { Object.defineProperty(pkg[info.type], info.name, { value: require(info.filePath), }); }); return pkg; }, _isRos2InstallationPath(pkgPath) { // Use "which ros2" to dynamically find the ROS2 installation root try { const whichResult = require('child_process').spawnSync( 'which', ['ros2'], { encoding: 'utf8', timeout: 5000, } ); if (whichResult.status === 0 && whichResult.stdout) { const ros2BinPath = whichResult.stdout.trim(); // Get the ROS2 installation root (typically /opt/ros/<distro> or similar) const ros2Root = path.dirname(path.dirname(ros2BinPath)); return pkgPath.includes(ros2Root); } } catch (err) { console.error('Error running which ros2:', err.message); // If "which ros2" fails, fall back to hardcoded check return pkgPath.includes('ros2-linux'); } return false; }, _searchAndGenerateInterface(packageName, type, messageName, filePath) { // Check if it's a valid package for (const pkgPath of generator.getInstalledPackagePaths()) { // We are going to ignore the path where ROS2 is installed. if (this._isRos2InstallationPath(pkgPath)) { continue; } // Recursively search for files named messageName.* under pkgPath/ if (fs.existsSync(pkgPath)) { // Recursive function to search for files function searchForFile(dir) { try { const items = fs.readdirSync(dir, { withFileTypes: true }); for (const item of items) { const fullPath = path.join(dir, item.name); if (item.isFile()) { const baseName = path.parse(item.name).name; // Check if the base filename matches messageName if (baseName === messageName) { return fullPath; } } else if (item.isDirectory()) { // Recursively search subdirectories const result = searchForFile(fullPath); if (result) { return result; } } } } catch (err) { // Skip directories we can't read console.error('Error reading directory:', dir, err.message); } return null; } const foundFilePath = searchForFile( path.join(pkgPath, 'share', packageName) ); if (foundFilePath && foundFilePath.length > 0) { // Use worker thread to generate interfaces synchronously try { generator.generateInPathSyncWorker(pkgPath); // Now try to load the interface again from the generated files if (fs.existsSync(filePath)) { return require(filePath); } } catch (err) { console.error('Error in interface generation:', err); } } } } throw new Error( `The message required does not exist: ${packageName}, ${type}, ${messageName} at ${generator.generatedRoot}` ); }, loadInterface(packageName, type, messageName) { if (arguments.length === 1) { const type = arguments[0]; if (typeof type === 'object') { return this.loadInterfaceByObject(type); } else if (typeof type === 'string') { return this.loadInterfaceByString(type); } throw new Error(`The message required does not exist: ${type}`); } if (packageName && type && messageName) { let filePath = path.join( generator.generatedRoot, packageName, packageName + '__' + type + '__' + messageName + '.js' ); if (fs.existsSync(filePath)) { return require(filePath); } else { return this._searchAndGenerateInterface( packageName, type, messageName, filePath ); } } // We cannot parse `packageName`, `type` and `messageName` from the string passed. throw new Error( `The message required does not exist: ${packageName}, ${type}, ${messageName} at ${generator.generatedRoot}` ); }, }; module.exports = interfaceLoader;