17
17
using System . Collections . Generic ;
18
18
using UnityEngine ;
19
19
using UnityEditor ;
20
+ using System . Xml ;
20
21
21
22
namespace ROS2
22
23
{
@@ -28,14 +29,16 @@ internal class ROS2ForUnity
28
29
{
29
30
private static bool isInitialized = false ;
30
31
private static string ros2ForUnityAssetFolderName = "Ros2ForUnity" ;
32
+ private XmlDocument ros2csMetadata = new XmlDocument ( ) ;
33
+ private XmlDocument ros2ForUnityMetadata = new XmlDocument ( ) ;
31
34
32
35
enum Platform
33
36
{
34
37
Windows ,
35
38
Linux
36
39
}
37
40
38
- private Platform GetOS ( )
41
+ private static Platform GetOS ( )
39
42
{
40
43
if ( Application . platform == RuntimePlatform . LinuxEditor || Application . platform == RuntimePlatform . LinuxPlayer )
41
44
{
@@ -48,11 +51,11 @@ private Platform GetOS()
48
51
throw new System . NotSupportedException ( "Only Linux and Windows are supported" ) ;
49
52
}
50
53
51
- private bool InEditor ( ) {
54
+ private static bool InEditor ( ) {
52
55
return Application . isEditor ;
53
56
}
54
57
55
- private string GetOSName ( )
58
+ private static string GetOSName ( )
56
59
{
57
60
switch ( GetOS ( ) )
58
61
{
@@ -80,7 +83,7 @@ private string GetEnvPathVariableValue()
80
83
return Environment . GetEnvironmentVariable ( GetEnvPathVariableName ( ) ) ;
81
84
}
82
85
83
- private string GetPluginPath ( )
86
+ public static string GetRos2ForUnityPath ( )
84
87
{
85
88
char separator = Path . DirectorySeparatorChar ;
86
89
string appDataPath = Application . dataPath ;
@@ -89,6 +92,14 @@ private string GetPluginPath()
89
92
if ( InEditor ( ) ) {
90
93
pluginPath += separator + ros2ForUnityAssetFolderName ;
91
94
}
95
+ return pluginPath ;
96
+ }
97
+
98
+ public static string GetPluginPath ( )
99
+ {
100
+ char separator = Path . DirectorySeparatorChar ;
101
+ string ros2ForUnityPath = GetRos2ForUnityPath ( ) ;
102
+ string pluginPath = ros2ForUnityPath ;
92
103
93
104
pluginPath += separator + "Plugins" ;
94
105
@@ -116,7 +127,7 @@ private string GetPluginPath()
116
127
/// Note that on Linux, LD_LIBRARY_PATH as used for dlopen() is determined on process start and this change won't
117
128
/// affect it. Ros2 looks for rmw implementation based on this variable (independently) and the change
118
129
/// is effective for this process, however rmw implementation's dependencies itself are loaded by dynamic linker
119
- /// anyway so setting it for Linux is pointless.
130
+ /// anyway so setting it for Linux is pointless.
120
131
/// </description>
121
132
private void SetEnvPathVariable ( )
122
133
{
@@ -132,16 +143,69 @@ private void SetEnvPathVariable()
132
143
Environment . SetEnvironmentVariable ( GetEnvPathVariableName ( ) , pluginPath + envPathSep + currentPath ) ;
133
144
}
134
145
146
+ public bool IsStandalone ( ) {
147
+ return Convert . ToBoolean ( Convert . ToInt16 ( GetMetadataValue ( ros2csMetadata , "/ros2cs/standalone" ) ) ) ;
148
+ }
149
+
150
+ public string GetROSVersion ( )
151
+ {
152
+ string ros2SourcedCodename = GetROSVersionSourced ( ) ;
153
+ string ros2FromRos4UMetadata = GetMetadataValue ( ros2ForUnityMetadata , "/ros2_for_unity/ros2" ) ;
154
+
155
+ // Sourced ROS2 libs takes priority
156
+ if ( string . IsNullOrEmpty ( ros2SourcedCodename ) ) {
157
+ return ros2FromRos4UMetadata ;
158
+ }
159
+
160
+ return ros2SourcedCodename ;
161
+ }
162
+
163
+ /// <summary>
164
+ /// Checks if both ros2cs and ros2-for-unity were build for the same ros version as well as
165
+ /// the current sourced ros version matches ros2cs binaries.
166
+ /// </summary>
167
+ public void CheckIntegrity ( )
168
+ {
169
+ string ros2SourcedCodename = GetROSVersionSourced ( ) ;
170
+ string ros2FromRos2csMetadata = GetMetadataValue ( ros2csMetadata , "/ros2cs/ros2" ) ;
171
+ string ros2FromRos4UMetadata = GetMetadataValue ( ros2ForUnityMetadata , "/ros2_for_unity/ros2" ) ;
172
+
173
+ if ( ros2FromRos4UMetadata != ros2FromRos2csMetadata ) {
174
+ Debug . LogError (
175
+ "ROS2 versions in 'ros2cs' and 'ros2-for-unity' metadata files are not the same. " +
176
+ "This is caused by mixing versions/builds. Plugin might not work correctly."
177
+ ) ;
178
+ }
179
+
180
+ if ( ! IsStandalone ( ) && ros2SourcedCodename != ros2FromRos2csMetadata ) {
181
+ Debug . LogError (
182
+ "ROS2 version in 'ros2cs' metadata doesn't match currently sourced version. " +
183
+ "This is caused by mixing versions/builds. Plugin might not work correctly."
184
+ ) ;
185
+ }
186
+
187
+ if ( IsStandalone ( ) && ! string . IsNullOrEmpty ( ros2SourcedCodename ) ) {
188
+ Debug . LogError (
189
+ "You should not source ROS2 in 'ros2-for-unity' standalone build. " +
190
+ "Plugin might not work correctly."
191
+ ) ;
192
+ }
193
+ }
194
+
195
+ public string GetROSVersionSourced ( )
196
+ {
197
+ return Environment . GetEnvironmentVariable ( "ROS_DISTRO" ) ;
198
+ }
199
+
135
200
/// <summary>
136
201
/// Check if the ros version is supported, only applicable to non-standalone plugin versions
137
202
/// (i. e. without ros2 libraries included in the plugin).
138
203
/// </summary>
139
- private string CheckROSVersionSourced ( )
204
+ private void CheckROSSupport ( string ros2Codename )
140
205
{
141
- string currentVersion = Environment . GetEnvironmentVariable ( "ROS_DISTRO" ) ;
142
206
List < string > supportedVersions = new List < string > ( ) { "foxy" , "galactic" } ;
143
207
var supportedVersionsString = String . Join ( ", " , supportedVersions ) ;
144
- if ( string . IsNullOrEmpty ( currentVersion ) )
208
+ if ( string . IsNullOrEmpty ( ros2Codename ) )
145
209
{
146
210
string errMessage = "No ROS environment sourced. You need to source your ROS2 " + supportedVersionsString
147
211
+ " environment before launching Unity (ROS_DISTRO env variable not found)" ;
@@ -155,9 +219,9 @@ private string CheckROSVersionSourced()
155
219
#endif
156
220
}
157
221
158
- if ( ! supportedVersions . Contains ( currentVersion ) )
222
+ if ( ! supportedVersions . Contains ( ros2Codename ) )
159
223
{
160
- string errMessage = "Currently sourced ROS version differs from supported one. Sourced: " + currentVersion
224
+ string errMessage = "Currently sourced ROS version differs from supported one. Sourced: " + ros2Codename
161
225
+ ", supported: " + supportedVersionsString + "." ;
162
226
Debug . LogError ( errMessage ) ;
163
227
#if UNITY_EDITOR
@@ -168,8 +232,6 @@ private string CheckROSVersionSourced()
168
232
Application . Quit ( ROS_BAD_VERSION_CODE ) ;
169
233
#endif
170
234
}
171
- Debug . Log ( "Running with a supported ROS 2 version: " + currentVersion ) ;
172
- return currentVersion ;
173
235
}
174
236
175
237
private void RegisterCtrlCHandler ( )
@@ -192,28 +254,65 @@ private void ConnectLoggers()
192
254
Ros2csLogger . LogLevel = LogLevel . WARNING ;
193
255
}
194
256
257
+ private string GetMetadataValue ( XmlDocument doc , string valuePath )
258
+ {
259
+ return doc . DocumentElement . SelectSingleNode ( valuePath ) . InnerText ;
260
+ }
261
+
262
+ private void LoadMetadata ( )
263
+ {
264
+ char separator = Path . DirectorySeparatorChar ;
265
+ try
266
+ {
267
+ ros2csMetadata . Load ( GetPluginPath ( ) + separator + "metadata_ros2cs.xml" ) ;
268
+ ros2ForUnityMetadata . Load ( GetRos2ForUnityPath ( ) + separator + "metadata_ros2_for_unity.xml" ) ;
269
+ }
270
+ catch ( System . IO . FileNotFoundException )
271
+ {
272
+ var errMessage = "Could not find metadata files." ;
273
+ #if UNITY_EDITOR
274
+ EditorApplication . isPlaying = false ;
275
+ throw new System . IO . FileNotFoundException ( errMessage ) ;
276
+ #else
277
+ const int NO_METADATA = 1 ;
278
+ Application . Quit ( NO_METADATA ) ;
279
+ #endif
280
+ }
281
+ }
282
+
195
283
internal ROS2ForUnity ( )
196
284
{
197
- // TODO: Find a way to determine whether we run standalone build
285
+ // Load metadata
286
+ LoadMetadata ( ) ;
287
+ string currentRos2Version = GetROSVersion ( ) ;
288
+ string standalone = IsStandalone ( ) ? "standalone" : "non-standalone" ;
289
+
290
+ // Self checks
291
+ CheckROSSupport ( currentRos2Version ) ;
292
+ CheckIntegrity ( ) ;
293
+
294
+ // Library loading
198
295
if ( GetOS ( ) == Platform . Windows ) {
199
296
// Windows version can run standalone, modifies PATH to ensure all plugins visibility
200
297
SetEnvPathVariable ( ) ;
201
298
} else {
202
- // Linux version needs to have ros2 sourced, which is checked here. It also loads plugins by absolute path
203
- // since LD_LIBRARY_PATH cannot be set dynamically within the process for dlopen() which is used under the hood.
204
- // Since libraries are built with -rpath=".", dependencies will be correcly located within plugins directory.
205
- // For foxy, it is also necessary to use modified version of librcpputils to resolve custom msgs packages.
206
- string currentRosVersion = CheckROSVersionSourced ( ) ;
299
+ // For foxy, it is necessary to use modified version of librcpputils to resolve custom msgs packages.
207
300
ROS2 . GlobalVariables . absolutePath = GetPluginPath ( ) + "/" ;
208
- if ( currentRosVersion == "foxy" ) {
301
+ if ( currentRos2Version == "foxy" ) {
209
302
ROS2 . GlobalVariables . preloadLibrary = true ;
210
303
ROS2 . GlobalVariables . preloadLibraryName = "librcpputils.so" ;
211
304
}
212
305
}
306
+
307
+ // Initialize
213
308
ConnectLoggers ( ) ;
214
309
Ros2cs . Init ( ) ;
215
310
RegisterCtrlCHandler ( ) ;
216
311
312
+ string rmwImpl = Ros2cs . GetRMWImplementation ( ) ;
313
+
314
+ Debug . Log ( "ROS2 version: " + currentRos2Version + ". Build type: " + standalone + ". RMW: " + rmwImpl ) ;
315
+
217
316
#if UNITY_EDITOR
218
317
EditorApplication . playModeStateChanged += this . EditorPlayStateChanged ;
219
318
EditorApplication . quitting += this . DestroyROS2ForUnity ;
0 commit comments