@@ -90,6 +90,95 @@ let interfaceLoader = {
9090 return pkg ;
9191 } ,
9292
93+ _isRos2InstallationPath ( pkgPath ) {
94+ // Use "which ros2" to dynamically find the ROS2 installation root
95+ try {
96+ const whichResult = require ( 'child_process' ) . spawnSync (
97+ 'which' ,
98+ [ 'ros2' ] ,
99+ {
100+ encoding : 'utf8' ,
101+ timeout : 5000 ,
102+ }
103+ ) ;
104+
105+ if ( whichResult . status === 0 && whichResult . stdout ) {
106+ const ros2BinPath = whichResult . stdout . trim ( ) ;
107+ // Get the ROS2 installation root (typically /opt/ros/<distro> or similar)
108+ const ros2Root = path . dirname ( path . dirname ( ros2BinPath ) ) ;
109+
110+ return pkgPath . includes ( ros2Root ) ;
111+ }
112+ } catch ( err ) {
113+ console . error ( 'Error running which ros2:' , err . message ) ;
114+ // If "which ros2" fails, fall back to hardcoded check
115+ return pkgPath . includes ( 'ros2-linux' ) ;
116+ }
117+
118+ return false ;
119+ } ,
120+
121+ _searchAndGenerateInterface ( packageName , type , messageName , filePath ) {
122+ // Check if it's a valid package
123+ for ( const pkgPath of generator . getInstalledPackagePaths ( ) ) {
124+ // We are going to ignore the path where ROS2 is installed.
125+ if ( this . _isRos2InstallationPath ( pkgPath ) ) {
126+ continue ;
127+ }
128+
129+ // Recursively search for files named messageName.* under pkgPath/
130+ if ( fs . existsSync ( pkgPath ) ) {
131+ // Recursive function to search for files
132+ function searchForFile ( dir ) {
133+ try {
134+ const items = fs . readdirSync ( dir , { withFileTypes : true } ) ;
135+ for ( const item of items ) {
136+ const fullPath = path . join ( dir , item . name ) ;
137+
138+ if ( item . isFile ( ) ) {
139+ const baseName = path . parse ( item . name ) . name ;
140+ // Check if the base filename matches messageName
141+ if ( baseName === messageName ) {
142+ return fullPath ;
143+ }
144+ } else if ( item . isDirectory ( ) ) {
145+ // Recursively search subdirectories
146+ const result = searchForFile ( fullPath ) ;
147+ if ( result ) {
148+ return result ;
149+ }
150+ }
151+ }
152+ } catch ( err ) {
153+ // Skip directories we can't read
154+ console . error ( 'Error reading directory:' , dir , err . message ) ;
155+ }
156+ return null ;
157+ }
158+
159+ const foundFilePath = searchForFile (
160+ path . join ( pkgPath , 'share' , packageName )
161+ ) ;
162+
163+ if ( foundFilePath && foundFilePath . length > 0 ) {
164+ // Use worker thread to generate interfaces synchronously
165+ try {
166+ generator . generateInPathSyncWorker ( pkgPath ) ;
167+ // Now try to load the interface again from the generated files
168+ if ( fs . existsSync ( filePath ) ) {
169+ return require ( filePath ) ;
170+ }
171+ } catch ( err ) {
172+ console . error ( 'Error in interface generation:' , err ) ;
173+ }
174+ }
175+ }
176+ }
177+ throw new Error (
178+ `The message required does not exist: ${ packageName } , ${ type } , ${ messageName } at ${ generator . generatedRoot } `
179+ ) ;
180+ } ,
181+
93182 loadInterface ( packageName , type , messageName ) {
94183 if ( arguments . length === 1 ) {
95184 const type = arguments [ 0 ] ;
@@ -100,7 +189,6 @@ let interfaceLoader = {
100189 }
101190 throw new Error ( `The message required does not exist: ${ type } ` ) ;
102191 }
103-
104192 if ( packageName && type && messageName ) {
105193 let filePath = path . join (
106194 generator . generatedRoot ,
@@ -110,8 +198,16 @@ let interfaceLoader = {
110198
111199 if ( fs . existsSync ( filePath ) ) {
112200 return require ( filePath ) ;
201+ } else {
202+ return this . _searchAndGenerateInterface (
203+ packageName ,
204+ type ,
205+ messageName ,
206+ filePath
207+ ) ;
113208 }
114209 }
210+ // We cannot parse `packageName`, `type` and `messageName` from the string passed.
115211 throw new Error (
116212 `The message required does not exist: ${ packageName } , ${ type } , ${ messageName } at ${ generator . generatedRoot } `
117213 ) ;
0 commit comments