From cab5655769bc00a874418df3d777eee28e9e8384 Mon Sep 17 00:00:00 2001 From: HiepLM Date: Wed, 4 Feb 2026 14:33:51 +0700 Subject: [PATCH] update c_api --- examples/CSharpExample.cs | 240 +++++ examples/NavigationExample/Program.cs | 240 +++++ examples/NavigationExample/libnav_c_api.so | Bin 125696 -> 173512 bytes src/APIs/c_api/include/nav_c_api.h | 1000 ++++++++++++++------ src/APIs/c_api/src/nav_c_api.cpp | 610 ++++++++++++ 5 files changed, 1785 insertions(+), 305 deletions(-) diff --git a/examples/CSharpExample.cs b/examples/CSharpExample.cs index 6ffb99a..2c363c6 100644 --- a/examples/CSharpExample.cs +++ b/examples/CSharpExample.cs @@ -128,12 +128,98 @@ namespace NavigationExample public bool is_ready; } + + [StructLayout(LayoutKind.Sequential)] + public struct NamedOccupancyGrid + { + public IntPtr name; // char* + public IntPtr map; // OccupancyGridHandle + } + + [StructLayout(LayoutKind.Sequential)] + public struct NamedLaserScan + { + public IntPtr name; // char* + public IntPtr scan; // LaserScanHandle + } + + [StructLayout(LayoutKind.Sequential)] + public struct NamedPointCloud + { + public IntPtr name; // char* + public IntPtr cloud; // PointCloudHandle + } + + [StructLayout(LayoutKind.Sequential)] + public struct NamedPointCloud2 + { + public IntPtr name; // char* + public IntPtr cloud; // PointCloud2Handle + } + + [StructLayout(LayoutKind.Sequential)] + public struct PlannerDataOutput + { + public IntPtr plan; // Path2DHandle + public IntPtr costmap; // OccupancyGridHandle + public IntPtr costmap_update; // OccupancyGridUpdateHandle + [MarshalAs(UnmanagedType.I1)] + public bool is_costmap_updated; + public IntPtr footprint; // PolygonStampedHandle + } + // ============================================================================ // String Management // ============================================================================ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern void nav_c_api_free_string(IntPtr str); + + // ============================================================================ + // Complex Message Handle Management + // ============================================================================ + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_occupancy_grid(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_occupancy_grid_update(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_laser_scan(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_point_cloud(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_point_cloud2(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_odometry(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_path2d(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_polygon_stamped(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_order(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_named_occupancy_grids(IntPtr maps, UIntPtr count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_named_laser_scans(IntPtr scans, UIntPtr count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_named_point_clouds(IntPtr clouds, UIntPtr count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_named_point_cloud2s(IntPtr clouds, UIntPtr count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_planner_data(ref PlannerDataOutput data); + // ============================================================================ // State Conversion // ============================================================================ @@ -209,12 +295,24 @@ namespace NavigationExample IntPtr handle, ref PoseStamped goal, double xy_goal_tolerance, double yaw_goal_tolerance); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_move_to_order( + IntPtr handle, IntPtr order, ref PoseStamped goal, + double xy_goal_tolerance, double yaw_goal_tolerance); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool navigation_dock_to( IntPtr handle, string marker, ref PoseStamped goal, double xy_goal_tolerance, double yaw_goal_tolerance); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_dock_to_order( + IntPtr handle, IntPtr order, ref PoseStamped goal, + double xy_goal_tolerance, double yaw_goal_tolerance); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool navigation_move_straight_to( @@ -267,6 +365,118 @@ namespace NavigationExample [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern void navigation_free_feedback(ref NavFeedback feedback); + + // ============================================================================ + // Navigation Data Management + // ============================================================================ + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_add_static_map(IntPtr handle, string map_name, IntPtr map); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_add_laser_scan(IntPtr handle, string laser_scan_name, IntPtr laser_scan); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_add_point_cloud(IntPtr handle, string point_cloud_name, IntPtr point_cloud); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_add_point_cloud2(IntPtr handle, string point_cloud2_name, IntPtr point_cloud2); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_add_odometry(IntPtr handle, string odometry_name, IntPtr odometry); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_static_map(IntPtr handle, string map_name, out IntPtr out_map); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_laser_scan(IntPtr handle, string laser_scan_name, out IntPtr out_scan); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_point_cloud(IntPtr handle, string point_cloud_name, out IntPtr out_cloud); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_point_cloud2(IntPtr handle, string point_cloud2_name, out IntPtr out_cloud); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_all_static_maps(IntPtr handle, out IntPtr out_maps, out UIntPtr out_count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_all_laser_scans(IntPtr handle, out IntPtr out_scans, out UIntPtr out_count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_all_point_clouds(IntPtr handle, out IntPtr out_clouds, out UIntPtr out_count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_all_point_cloud2s(IntPtr handle, out IntPtr out_clouds, out UIntPtr out_count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_static_map(IntPtr handle, string map_name); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_laser_scan(IntPtr handle, string laser_scan_name); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_point_cloud(IntPtr handle, string point_cloud_name); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_point_cloud2(IntPtr handle, string point_cloud2_name); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_all_static_maps(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_all_laser_scans(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_all_point_clouds(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_all_point_cloud2s(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_all_data(IntPtr handle); + + + // ============================================================================ + // Planner Data + // ============================================================================ + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_global_data(IntPtr handle, ref PlannerDataOutput out_data); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_local_data(IntPtr handle, ref PlannerDataOutput out_data); + + + // ============================================================================ + // Navigation Commands with Order + // ============================================================================ + + + + // ============================================================================ // Helper Methods for String Conversion // ============================================================================ @@ -379,6 +589,36 @@ namespace NavigationExample NavigationAPI.navigation_free_feedback(ref feedback); } + + // Get global planner data (opaque handles) + NavigationAPI.PlannerDataOutput globalData = new NavigationAPI.PlannerDataOutput(); + if (NavigationAPI.navigation_get_global_data(navHandle, ref globalData)) + { + Console.WriteLine($"Global data received (costmap_updated={globalData.is_costmap_updated})"); + NavigationAPI.navigation_free_planner_data(ref globalData); + } + + // Get all static maps (names + opaque handles) + IntPtr mapsPtr; + UIntPtr mapsCount; + if (NavigationAPI.navigation_get_all_static_maps(navHandle, out mapsPtr, out mapsCount)) + { + ulong count = mapsCount.ToUInt64(); + Console.WriteLine($"Static maps: {count}"); + if (mapsPtr != IntPtr.Zero && count > 0) + { + int itemSize = Marshal.SizeOf(); + for (ulong i = 0; i < count; i++) + { + IntPtr itemPtr = IntPtr.Add(mapsPtr, checked((int)(i * (ulong)itemSize))); + var item = Marshal.PtrToStructure(itemPtr); + string name = NavigationAPI.MarshalString(item.name); + Console.WriteLine($"- {name}"); + } + NavigationAPI.navigation_free_named_occupancy_grids(mapsPtr, (UIntPtr)count); + } + } + // Cleanup NavigationAPI.navigation_destroy(navHandle); NavigationAPI.tf_listener_destroy(tfHandle); diff --git a/examples/NavigationExample/Program.cs b/examples/NavigationExample/Program.cs index 6ffb99a..2c363c6 100644 --- a/examples/NavigationExample/Program.cs +++ b/examples/NavigationExample/Program.cs @@ -128,12 +128,98 @@ namespace NavigationExample public bool is_ready; } + + [StructLayout(LayoutKind.Sequential)] + public struct NamedOccupancyGrid + { + public IntPtr name; // char* + public IntPtr map; // OccupancyGridHandle + } + + [StructLayout(LayoutKind.Sequential)] + public struct NamedLaserScan + { + public IntPtr name; // char* + public IntPtr scan; // LaserScanHandle + } + + [StructLayout(LayoutKind.Sequential)] + public struct NamedPointCloud + { + public IntPtr name; // char* + public IntPtr cloud; // PointCloudHandle + } + + [StructLayout(LayoutKind.Sequential)] + public struct NamedPointCloud2 + { + public IntPtr name; // char* + public IntPtr cloud; // PointCloud2Handle + } + + [StructLayout(LayoutKind.Sequential)] + public struct PlannerDataOutput + { + public IntPtr plan; // Path2DHandle + public IntPtr costmap; // OccupancyGridHandle + public IntPtr costmap_update; // OccupancyGridUpdateHandle + [MarshalAs(UnmanagedType.I1)] + public bool is_costmap_updated; + public IntPtr footprint; // PolygonStampedHandle + } + // ============================================================================ // String Management // ============================================================================ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern void nav_c_api_free_string(IntPtr str); + + // ============================================================================ + // Complex Message Handle Management + // ============================================================================ + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_occupancy_grid(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_occupancy_grid_update(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_laser_scan(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_point_cloud(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_point_cloud2(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_odometry(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_path2d(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_polygon_stamped(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_order(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_named_occupancy_grids(IntPtr maps, UIntPtr count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_named_laser_scans(IntPtr scans, UIntPtr count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_named_point_clouds(IntPtr clouds, UIntPtr count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_named_point_cloud2s(IntPtr clouds, UIntPtr count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern void navigation_free_planner_data(ref PlannerDataOutput data); + // ============================================================================ // State Conversion // ============================================================================ @@ -209,12 +295,24 @@ namespace NavigationExample IntPtr handle, ref PoseStamped goal, double xy_goal_tolerance, double yaw_goal_tolerance); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_move_to_order( + IntPtr handle, IntPtr order, ref PoseStamped goal, + double xy_goal_tolerance, double yaw_goal_tolerance); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool navigation_dock_to( IntPtr handle, string marker, ref PoseStamped goal, double xy_goal_tolerance, double yaw_goal_tolerance); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_dock_to_order( + IntPtr handle, IntPtr order, ref PoseStamped goal, + double xy_goal_tolerance, double yaw_goal_tolerance); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool navigation_move_straight_to( @@ -267,6 +365,118 @@ namespace NavigationExample [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern void navigation_free_feedback(ref NavFeedback feedback); + + // ============================================================================ + // Navigation Data Management + // ============================================================================ + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_add_static_map(IntPtr handle, string map_name, IntPtr map); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_add_laser_scan(IntPtr handle, string laser_scan_name, IntPtr laser_scan); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_add_point_cloud(IntPtr handle, string point_cloud_name, IntPtr point_cloud); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_add_point_cloud2(IntPtr handle, string point_cloud2_name, IntPtr point_cloud2); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_add_odometry(IntPtr handle, string odometry_name, IntPtr odometry); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_static_map(IntPtr handle, string map_name, out IntPtr out_map); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_laser_scan(IntPtr handle, string laser_scan_name, out IntPtr out_scan); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_point_cloud(IntPtr handle, string point_cloud_name, out IntPtr out_cloud); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_point_cloud2(IntPtr handle, string point_cloud2_name, out IntPtr out_cloud); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_all_static_maps(IntPtr handle, out IntPtr out_maps, out UIntPtr out_count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_all_laser_scans(IntPtr handle, out IntPtr out_scans, out UIntPtr out_count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_all_point_clouds(IntPtr handle, out IntPtr out_clouds, out UIntPtr out_count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_all_point_cloud2s(IntPtr handle, out IntPtr out_clouds, out UIntPtr out_count); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_static_map(IntPtr handle, string map_name); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_laser_scan(IntPtr handle, string laser_scan_name); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_point_cloud(IntPtr handle, string point_cloud_name); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_point_cloud2(IntPtr handle, string point_cloud2_name); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_all_static_maps(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_all_laser_scans(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_all_point_clouds(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_all_point_cloud2s(IntPtr handle); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_remove_all_data(IntPtr handle); + + + // ============================================================================ + // Planner Data + // ============================================================================ + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_global_data(IntPtr handle, ref PlannerDataOutput out_data); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool navigation_get_local_data(IntPtr handle, ref PlannerDataOutput out_data); + + + // ============================================================================ + // Navigation Commands with Order + // ============================================================================ + + + + // ============================================================================ // Helper Methods for String Conversion // ============================================================================ @@ -379,6 +589,36 @@ namespace NavigationExample NavigationAPI.navigation_free_feedback(ref feedback); } + + // Get global planner data (opaque handles) + NavigationAPI.PlannerDataOutput globalData = new NavigationAPI.PlannerDataOutput(); + if (NavigationAPI.navigation_get_global_data(navHandle, ref globalData)) + { + Console.WriteLine($"Global data received (costmap_updated={globalData.is_costmap_updated})"); + NavigationAPI.navigation_free_planner_data(ref globalData); + } + + // Get all static maps (names + opaque handles) + IntPtr mapsPtr; + UIntPtr mapsCount; + if (NavigationAPI.navigation_get_all_static_maps(navHandle, out mapsPtr, out mapsCount)) + { + ulong count = mapsCount.ToUInt64(); + Console.WriteLine($"Static maps: {count}"); + if (mapsPtr != IntPtr.Zero && count > 0) + { + int itemSize = Marshal.SizeOf(); + for (ulong i = 0; i < count; i++) + { + IntPtr itemPtr = IntPtr.Add(mapsPtr, checked((int)(i * (ulong)itemSize))); + var item = Marshal.PtrToStructure(itemPtr); + string name = NavigationAPI.MarshalString(item.name); + Console.WriteLine($"- {name}"); + } + NavigationAPI.navigation_free_named_occupancy_grids(mapsPtr, (UIntPtr)count); + } + } + // Cleanup NavigationAPI.navigation_destroy(navHandle); NavigationAPI.tf_listener_destroy(tfHandle); diff --git a/examples/NavigationExample/libnav_c_api.so b/examples/NavigationExample/libnav_c_api.so index 8c6aee65fab080395b23f7b40c6f11be92bf56fd..d57761c1bd6d3c98705de3dacb6154fa923522ea 100755 GIT binary patch literal 173512 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 #include #include -// Forward declarations -typedef void* NavigationHandle; -typedef void* TFListenerHandle; + // Forward declarations + typedef void *NavigationHandle; + typedef void *TFListenerHandle; + typedef void *OccupancyGridHandle; + typedef void *OccupancyGridUpdateHandle; + typedef void *LaserScanHandle; + typedef void *PointCloudHandle; + typedef void *PointCloud2Handle; + typedef void *OdometryHandle; + typedef void *Path2DHandle; + typedef void *PolygonStampedHandle; + typedef void *OrderHandle; -// ============================================================================ -// Enums -// ============================================================================ + // ============================================================================ + // Enums + // ============================================================================ -/** - * @brief Navigation states, including planning and controller status - */ -typedef enum { + /** + * @brief Navigation states, including planning and controller status + */ + typedef enum + { NAV_STATE_PENDING = 0, NAV_STATE_ACTIVE = 1, NAV_STATE_PREEMPTED = 2, @@ -35,380 +46,759 @@ typedef enum { NAV_STATE_CONTROLLING = 11, NAV_STATE_CLEARING = 12, NAV_STATE_PAUSED = 13 -} NavigationState; + } NavigationState; -// ============================================================================ -// Structures -// ============================================================================ + // ============================================================================ + // Structures + // ============================================================================ -/** - * @brief Point structure (x, y, z) - */ -typedef struct { + /** + * @brief Point structure (x, y, z) + */ + typedef struct + { double x; double y; double z; -} Point; + } Point; -/** - * @brief Pose2D structure (x, y, theta) - */ -typedef struct { + /** + * @brief Pose2D structure (x, y, theta) + */ + typedef struct + { double x; double y; double theta; -} Pose2D; + } Pose2D; -/** - * @brief Twist2D structure (x, y, theta velocities) - */ -typedef struct { + /** + * @brief Twist2D structure (x, y, theta velocities) + */ + typedef struct + { double x; double y; double theta; -} Twist2D; + } Twist2D; -/** - * @brief Quaternion structure - */ -typedef struct { + /** + * @brief Quaternion structure + */ + typedef struct + { double x; double y; double z; double w; -} Quaternion; + } Quaternion; -/** - * @brief Position structure - */ -typedef struct { + /** + * @brief Position structure + */ + typedef struct + { double x; double y; double z; -} Position; + } Position; -/** - * @brief Pose structure - */ -typedef struct { + /** + * @brief Pose structure + */ + typedef struct + { Position position; Quaternion orientation; -} Pose; + } Pose; -/** - * @brief Header structure - */ -typedef struct { + /** + * @brief Header structure + */ + typedef struct + { uint32_t seq; int64_t sec; uint32_t nsec; - char* frame_id; -} Header; + char *frame_id; + } Header; -/** - * @brief PoseStamped structure - */ -typedef struct { + /** + * @brief PoseStamped structure + */ + typedef struct + { Header header; Pose pose; -} PoseStamped; + } PoseStamped; -/** - * @brief Twist2DStamped structure - */ -typedef struct { + /** + * @brief Twist2DStamped structure + */ + typedef struct + { Header header; Twist2D velocity; -} Twist2DStamped; + } Twist2DStamped; -/** - * @brief Vector3 structure - */ -typedef struct { + /** + * @brief Vector3 structure + */ + typedef struct + { double x; double y; double z; -} Vector3; + } Vector3; -/** - * @brief Navigation feedback structure - */ -typedef struct { + /** + * @brief Navigation feedback structure + */ + typedef struct + { NavigationState navigation_state; - char* feed_back_str; + char *feed_back_str; Pose2D current_pose; bool goal_checked; bool is_ready; -} NavFeedback; + } NavFeedback; -// ============================================================================ -// String Management -// ============================================================================ + /** + * @brief Named OccupancyGrid structure + * @note map is an opaque handle to a C++ robot_nav_msgs::OccupancyGrid + */ + typedef struct + { + char *name; + OccupancyGridHandle map; + } NamedOccupancyGrid; -/** - * @brief Free a string allocated by the library - * @param str String to free - */ -void nav_c_api_free_string(char* str); + /** + * @brief Named LaserScan structure + * @note scan is an opaque handle to a C++ robot_sensor_msgs::LaserScan + */ + typedef struct + { + char *name; + LaserScanHandle scan; + } NamedLaserScan; -// ============================================================================ -// State Conversion -// ============================================================================ + /** + * @brief Named PointCloud structure + * @note cloud is an opaque handle to a C++ robot_sensor_msgs::PointCloud + */ + typedef struct + { + char *name; + PointCloudHandle cloud; + } NamedPointCloud; -/** - * @brief Convert a State enum to its string representation - * @param state Enum value of NavigationState - * @return String representation (caller must free with nav_c_api_free_string) - */ -char* navigation_state_to_string(NavigationState state); + /** + * @brief Named PointCloud2 structure + * @note cloud is an opaque handle to a C++ robot_sensor_msgs::PointCloud2 + */ + typedef struct + { + char *name; + PointCloud2Handle cloud; + } NamedPointCloud2; -// ============================================================================ -// Helper Functions -// ============================================================================ + /** + * @brief Planner data output structure (opaque message handles) + */ + typedef struct + { + Path2DHandle plan; + OccupancyGridHandle costmap; + OccupancyGridUpdateHandle costmap_update; + bool is_costmap_updated; + PolygonStampedHandle footprint; + } PlannerDataOutput; -/** - * @brief Creates a target pose by offsetting a given 2D pose along its heading direction - * @param pose_x X coordinate of the original pose - * @param pose_y Y coordinate of the original pose - * @param pose_theta Heading angle in radians - * @param frame_id The coordinate frame ID (null-terminated string) - * @param offset_distance Distance to offset along heading (positive = forward, negative = backward) - * @param out_goal Output parameter for the offset pose - * @return true on success, false on failure - */ -bool navigation_offset_goal_2d(double pose_x, double pose_y, double pose_theta, - const char* frame_id, double offset_distance, - PoseStamped* out_goal); + // ============================================================================ + // String Management + // ============================================================================ -/** - * @brief Creates an offset target pose from a given PoseStamped - * @param in_pose Input pose - * @param offset_distance Distance to offset along heading direction - * @param out_goal Output parameter for the offset pose - * @return true on success, false on failure - */ -bool navigation_offset_goal_stamped(const PoseStamped* in_pose, double offset_distance, - PoseStamped* out_goal); + /** + * @brief Free a string allocated by the library + * @param str String to free + */ + void nav_c_api_free_string(char *str); -// ============================================================================ -// Navigation Handle Management -// ============================================================================ + // ============================================================================ + // Complex Message Handle Management + // ============================================================================ -/** - * @brief Create a new navigation instance - * @return Navigation handle, or NULL on failure - */ -NavigationHandle navigation_create(void); + /** + * @brief Free an occupancy grid handle + * @param handle Occupancy grid handle to free + */ + void navigation_free_occupancy_grid(OccupancyGridHandle handle); + /** + * @brief Free an occupancy grid update handle + * @param handle Occupancy grid update handle to free + */ + void navigation_free_occupancy_grid_update(OccupancyGridUpdateHandle handle); -/** - * @brief Destroy a navigation instance - * @param handle Navigation handle to destroy - */ -void navigation_destroy(NavigationHandle handle); + /** + * @brief Free a laser scan handle + * @param handle Laser scan handle to free + */ + void navigation_free_laser_scan(LaserScanHandle handle); -// ============================================================================ -// TF Listener Management -// ============================================================================ + /** + * @brief Free a point cloud handle + * @param handle Point cloud handle to free + */ + void navigation_free_point_cloud(PointCloudHandle handle); -/** - * @brief Create a TF listener instance - * @return TF listener handle, or NULL on failure - */ -TFListenerHandle tf_listener_create(void); + /** + * @brief Free a point cloud2 handle + * @param handle Point cloud2 handle to free + */ + void navigation_free_point_cloud2(PointCloud2Handle handle); -/** - * @brief Destroy a TF listener instance - * @param handle TF listener handle to destroy - */ -void tf_listener_destroy(TFListenerHandle handle); + /** + * @brief Free an odometry handle + * @param handle Odometry handle to free + */ + void navigation_free_odometry(OdometryHandle handle); -/** - * @brief Inject a static transform into the TF buffer. - * - * This is a convenience for standalone usage where no external TF publisher exists yet. - * It will create/ensure the frames exist and become transformable. - * - * @param tf_handle TF listener handle - * @param parent_frame Parent frame id (e.g. "map") - * @param child_frame Child frame id (e.g. "base_link") - * @param x Translation x (meters) - * @param y Translation y (meters) - * @param z Translation z (meters) - * @param qx Rotation quaternion x - * @param qy Rotation quaternion y - * @param qz Rotation quaternion z - * @param qw Rotation quaternion w - * @return true on success, false on failure - */ -bool tf_listener_set_static_transform(TFListenerHandle tf_handle, - const char* parent_frame, - const char* child_frame, - double x, double y, double z, - double qx, double qy, double qz, double qw); + /** + * @brief Free a path2d handle + * @param handle Path2d handle to free + */ + void navigation_free_path2d(Path2DHandle handle); -// ============================================================================ -// Navigation Interface Methods -// ============================================================================ + /** + * @brief Free a polygon stamped handle + * @param handle Polygon stamped handle to free + */ + void navigation_free_polygon_stamped(PolygonStampedHandle handle); -/** - * @brief Initialize the navigation system - * @param handle Navigation handle - * @param tf_handle TF listener handle - * @return true on success, false on failure - */ -bool navigation_initialize(NavigationHandle handle, TFListenerHandle tf_handle); + /** + * @brief Free an order handle + * @param handle Order handle to free + */ + void navigation_free_order(OrderHandle handle); -/** - * @brief Set the robot's footprint (outline shape) - * @param handle Navigation handle - * @param points Array of points representing the footprint polygon - * @param point_count Number of points in the array - * @return true on success, false on failure - */ -bool navigation_set_robot_footprint(NavigationHandle handle, const Point* points, size_t point_count); + /** + * @brief Free an array of named occupancy grids + * @param maps Array of named occupancy grids to free + * @param count Number of named occupancy grids in the array + */ + void navigation_free_named_occupancy_grids(NamedOccupancyGrid *maps, size_t count); -/** - * @brief Get the robot's footprint (outline shape) - * @param handle Navigation handle - * @param out_points Output array of points (allocated by library, free with navigation_free_points) - * @param out_count Output number of points in the array - * @return true on success, false on failure - */ -bool navigation_get_robot_footprint(NavigationHandle handle, Point** out_points, size_t* out_count); + /** + * @brief Free an array of named laser scans + * @param scans Array of named laser scans to free + * @param count Number of named laser scans in the array + */ + void navigation_free_named_laser_scans(NamedLaserScan *scans, size_t count); -/** - * @brief Free a points array allocated by navigation_get_robot_footprint - * @param points Pointer to point array - */ -void navigation_free_points(Point* points); + /** + * @brief Free an array of named point clouds + * @param clouds Array of named point clouds to free + * @param count Number of named point clouds in the array + */ + void navigation_free_named_point_clouds(NamedPointCloud *clouds, size_t count); -/** - * @brief Send a goal for the robot to navigate to - * @param handle Navigation handle - * @param goal Target pose in the global frame - * @param xy_goal_tolerance Acceptable error in X/Y (meters) - * @param yaw_goal_tolerance Acceptable angular error (radians) - * @return true if goal was accepted and sent successfully - */ -bool navigation_move_to(NavigationHandle handle, const PoseStamped* goal, - double xy_goal_tolerance, double yaw_goal_tolerance); + /** + * @brief Free an array of named point cloud2s + * @param clouds Array of named point cloud2s to free + * @param count Number of named point cloud2s in the array + */ + void navigation_free_named_point_cloud2s(NamedPointCloud2 *clouds, size_t count); -/** - * @brief Send a docking goal to a predefined marker - * @param handle Navigation handle - * @param marker Marker name or ID (null-terminated string) - * @param goal Target pose for docking - * @param xy_goal_tolerance Acceptable XY error (meters) - * @param yaw_goal_tolerance Acceptable heading error (radians) - * @return true if docking command succeeded - */ -bool navigation_dock_to(NavigationHandle handle, const char* marker, - const PoseStamped* goal, - double xy_goal_tolerance, double yaw_goal_tolerance); + /** + * @brief Free a planner data output + * @param data Planner data output to free + */ + void navigation_free_planner_data(PlannerDataOutput *data); -/** - * @brief Move straight toward the target position - * @param handle Navigation handle - * @param goal Target pose - * @param xy_goal_tolerance Acceptable positional error (meters) - * @return true if command issued successfully - */ -bool navigation_move_straight_to(NavigationHandle handle, const PoseStamped* goal, - double xy_goal_tolerance); + // ============================================================================ + // State Conversion + // ============================================================================ -/** - * @brief Rotate in place to align with target orientation - * @param handle Navigation handle - * @param goal Pose containing desired heading (only Z-axis used) - * @param yaw_goal_tolerance Acceptable angular error (radians) - * @return true if rotation command was sent successfully - */ -bool navigation_rotate_to(NavigationHandle handle, const PoseStamped* goal, - double yaw_goal_tolerance); + /** + * @brief Convert a State enum to its string representation + * @param state Enum value of NavigationState + * @return String representation (caller must free with nav_c_api_free_string) + */ + char *navigation_state_to_string(NavigationState state); -/** - * @brief Pause the robot's movement - * @param handle Navigation handle - */ -void navigation_pause(NavigationHandle handle); + // ============================================================================ + // Helper Functions + // ============================================================================ -/** - * @brief Resume motion after a pause - * @param handle Navigation handle - */ -void navigation_resume(NavigationHandle handle); + /** + * @brief Creates a target pose by offsetting a given 2D pose along its heading direction + * @param pose_x X coordinate of the original pose + * @param pose_y Y coordinate of the original pose + * @param pose_theta Heading angle in radians + * @param frame_id The coordinate frame ID (null-terminated string) + * @param offset_distance Distance to offset along heading (positive = forward, negative = backward) + * @param out_goal Output parameter for the offset pose + * @return true on success, false on failure + */ + bool navigation_offset_goal_2d(double pose_x, double pose_y, double pose_theta, + const char *frame_id, double offset_distance, + PoseStamped *out_goal); -/** - * @brief Cancel the current goal and stop the robot - * @param handle Navigation handle - */ -void navigation_cancel(NavigationHandle handle); + /** + * @brief Creates an offset target pose from a given PoseStamped + * @param in_pose Input pose + * @param offset_distance Distance to offset along heading direction + * @param out_goal Output parameter for the offset pose + * @return true on success, false on failure + */ + bool navigation_offset_goal_stamped(const PoseStamped *in_pose, double offset_distance, + PoseStamped *out_goal); -/** - * @brief Send limited linear velocity command - * @param handle Navigation handle - * @param linear_x Linear velocity in X direction - * @param linear_y Linear velocity in Y direction - * @param linear_z Linear velocity in Z direction - * @return true if the command was accepted - */ -bool navigation_set_twist_linear(NavigationHandle handle, - double linear_x, double linear_y, double linear_z); + // ============================================================================ + // Navigation Handle Management + // ============================================================================ -/** - * @brief Send limited angular velocity command - * @param handle Navigation handle - * @param angular_x Angular velocity around X axis - * @param angular_y Angular velocity around Y axis - * @param angular_z Angular velocity around Z axis - * @return true if the command was accepted - */ -bool navigation_set_twist_angular(NavigationHandle handle, - double angular_x, double angular_y, double angular_z); + /** + * @brief Create a new navigation instance + * @return Navigation handle, or NULL on failure + */ + NavigationHandle navigation_create(void); -/** - * @brief Get the robot's pose as a PoseStamped - * @param handle Navigation handle - * @param out_pose Output parameter with the robot's current pose - * @return true if pose was successfully retrieved - */ -bool navigation_get_robot_pose_stamped(NavigationHandle handle, PoseStamped* out_pose); + /** + * @brief Destroy a navigation instance + * @param handle Navigation handle to destroy + */ + void navigation_destroy(NavigationHandle handle); -/** - * @brief Get the robot's pose as a 2D pose - * @param handle Navigation handle - * @param out_pose Output parameter with the robot's current 2D pose - * @return true if pose was successfully retrieved - */ -bool navigation_get_robot_pose_2d(NavigationHandle handle, Pose2D* out_pose); + // ============================================================================ + // TF Listener Management + // ============================================================================ -/** - * @brief Get the robot's current twist - * @param handle Navigation handle - * @param out_twist Output parameter with the robot's current twist - * @return true if twist was successfully retrieved - * @note out_twist->header.frame_id must be freed using nav_c_api_free_string - */ -bool navigation_get_twist(NavigationHandle handle, Twist2DStamped* out_twist); + /** + * @brief Create a TF listener instance + * @return TF listener handle, or NULL on failure + */ + TFListenerHandle tf_listener_create(void); -/** - * @brief Get navigation feedback - * @param handle Navigation handle - * @param out_feedback Output parameter with navigation feedback - * @return true if feedback was successfully retrieved - * @note The feed_back_str field must be freed using nav_c_api_free_string - */ -bool navigation_get_feedback(NavigationHandle handle, NavFeedback* out_feedback); + /** + * @brief Destroy a TF listener instance + * @param handle TF listener handle to destroy + */ + void tf_listener_destroy(TFListenerHandle handle); -/** - * @brief Free navigation feedback structure - * @param feedback Feedback structure to free - */ -void navigation_free_feedback(NavFeedback* feedback); + /** + * @brief Inject a static transform into the TF buffer. + * + * This is a convenience for standalone usage where no external TF publisher exists yet. + * It will create/ensure the frames exist and become transformable. + * + * @param tf_handle TF listener handle + * @param parent_frame Parent frame id (e.g. "map") + * @param child_frame Child frame id (e.g. "base_link") + * @param x Translation x (meters) + * @param y Translation y (meters) + * @param z Translation z (meters) + * @param qx Rotation quaternion x + * @param qy Rotation quaternion y + * @param qz Rotation quaternion z + * @param qw Rotation quaternion w + * @return true on success, false on failure + */ + bool tf_listener_set_static_transform(TFListenerHandle tf_handle, + const char *parent_frame, + const char *child_frame, + double x, double y, double z, + double qx, double qy, double qz, double qw); + + // ============================================================================ + // Navigation Interface Methods + // ============================================================================ + + /** + * @brief Initialize the navigation system + * @param handle Navigation handle + * @param tf_handle TF listener handle + * @return true on success, false on failure + */ + bool navigation_initialize(NavigationHandle handle, TFListenerHandle tf_handle); + + /** + * @brief Set the robot's footprint (outline shape) + * @param handle Navigation handle + * @param points Array of points representing the footprint polygon + * @param point_count Number of points in the array + * @return true on success, false on failure + */ + bool navigation_set_robot_footprint(NavigationHandle handle, const Point *points, size_t point_count); + + /** + * @brief Get the robot's footprint (outline shape) + * @param handle Navigation handle + * @param out_points Output array of points (allocated by library, free with navigation_free_points) + * @param out_count Output number of points in the array + * @return true on success, false on failure + */ + bool navigation_get_robot_footprint(NavigationHandle handle, Point **out_points, size_t *out_count); + + /** + * @brief Free a points array allocated by navigation_get_robot_footprint + * @param points Pointer to point array + */ + void navigation_free_points(Point *points); + + /** + * @brief Send a goal for the robot to navigate to + * @param handle Navigation handle + * @param goal Target pose in the global frame + * @param xy_goal_tolerance Acceptable error in X/Y (meters) + * @param yaw_goal_tolerance Acceptable angular error (radians) + * @return true if goal was accepted and sent successfully + */ + bool navigation_move_to(NavigationHandle handle, const PoseStamped *goal, + double xy_goal_tolerance, double yaw_goal_tolerance); + + /** + * @brief Send a goal for the robot to navigate to + * @param handle Navigation handle + * @param order Order message + * @param goal Target pose in the global frame + * @param xy_goal_tolerance Acceptable error in X/Y (meters) + * @param yaw_goal_tolerance Acceptable angular error (radians) + * @return true if goal was accepted and sent successfully + */ + bool navigation_move_to_order(NavigationHandle handle, const OrderHandle order, + const PoseStamped *goal, + double xy_goal_tolerance, double yaw_goal_tolerance); + + /** + * @brief Send a docking goal to a predefined marker + * @param handle Navigation handle + * @param marker Marker name or ID (null-terminated string) + * @param goal Target pose for docking + * @param xy_goal_tolerance Acceptable XY error (meters) + * @param yaw_goal_tolerance Acceptable heading error (radians) + * @return true if docking command succeeded + */ + bool navigation_dock_to(NavigationHandle handle, const char *marker, + const PoseStamped *goal, + double xy_goal_tolerance, double yaw_goal_tolerance); + + /** + * @brief Send a docking goal to a predefined marker + * @param handle Navigation handle + * @param order Order message + * @param goal Target pose for docking + * @param xy_goal_tolerance Acceptable XY error (meters) + * @param yaw_goal_tolerance Acceptable heading error (radians) + * @return true if docking command succeeded + */ + bool navigation_dock_to_order(NavigationHandle handle, const OrderHandle order, + const PoseStamped *goal, + double xy_goal_tolerance, double yaw_goal_tolerance); + + /** + * @brief Move straight toward the target position + * @param handle Navigation handle + * @param goal Target pose + * @param xy_goal_tolerance Acceptable positional error (meters) + * @return true if command issued successfully + */ + bool navigation_move_straight_to(NavigationHandle handle, const PoseStamped *goal, + double xy_goal_tolerance); + + /** + * @brief Rotate in place to align with target orientation + * @param handle Navigation handle + * @param goal Pose containing desired heading (only Z-axis used) + * @param yaw_goal_tolerance Acceptable angular error (radians) + * @return true if rotation command was sent successfully + */ + bool navigation_rotate_to(NavigationHandle handle, const PoseStamped *goal, + double yaw_goal_tolerance); + + /** + * @brief Pause the robot's movement + * @param handle Navigation handle + */ + void navigation_pause(NavigationHandle handle); + + /** + * @brief Resume motion after a pause + * @param handle Navigation handle + */ + void navigation_resume(NavigationHandle handle); + + /** + * @brief Cancel the current goal and stop the robot + * @param handle Navigation handle + */ + void navigation_cancel(NavigationHandle handle); + + /** + * @brief Send limited linear velocity command + * @param handle Navigation handle + * @param linear_x Linear velocity in X direction + * @param linear_y Linear velocity in Y direction + * @param linear_z Linear velocity in Z direction + * @return true if the command was accepted + */ + bool navigation_set_twist_linear(NavigationHandle handle, + double linear_x, double linear_y, double linear_z); + + /** + * @brief Send limited angular velocity command + * @param handle Navigation handle + * @param angular_x Angular velocity around X axis + * @param angular_y Angular velocity around Y axis + * @param angular_z Angular velocity around Z axis + * @return true if the command was accepted + */ + bool navigation_set_twist_angular(NavigationHandle handle, + double angular_x, double angular_y, double angular_z); + + /** + * @brief Get the robot's pose as a PoseStamped + * @param handle Navigation handle + * @param out_pose Output parameter with the robot's current pose + * @return true if pose was successfully retrieved + */ + bool navigation_get_robot_pose_stamped(NavigationHandle handle, PoseStamped *out_pose); + + /** + * @brief Get the robot's pose as a 2D pose + * @param handle Navigation handle + * @param out_pose Output parameter with the robot's current 2D pose + * @return true if pose was successfully retrieved + */ + bool navigation_get_robot_pose_2d(NavigationHandle handle, Pose2D *out_pose); + + /** + * @brief Get the robot's current twist + * @param handle Navigation handle + * @param out_twist Output parameter with the robot's current twist + * @return true if twist was successfully retrieved + * @note out_twist->header.frame_id must be freed using nav_c_api_free_string + */ + bool navigation_get_twist(NavigationHandle handle, Twist2DStamped *out_twist); + + /** + * @brief Get navigation feedback + * @param handle Navigation handle + * @param out_feedback Output parameter with navigation feedback + * @return true if feedback was successfully retrieved + * @note The feed_back_str field must be freed using nav_c_api_free_string + */ + bool navigation_get_feedback(NavigationHandle handle, NavFeedback *out_feedback); + + /** + * @brief Free navigation feedback structure + * @param feedback Feedback structure to free + */ + void navigation_free_feedback(NavFeedback *feedback); + + /** + * @brief Add a static map to the navigation system + * @param handle Navigation handle + * @param map_name Name of the map + * @param map Occupancy grid handle + * @return true if the map was added successfully + */ + bool navigation_add_static_map(NavigationHandle handle, const char *map_name, OccupancyGridHandle map); + + /** + * @brief Add a laser scan to the navigation system + * @param handle Navigation handle + * @param laser_scan_name Name of the laser scan + * @param laser_scan Laser scan handle + * @return true if the laser scan was added successfully + */ + bool navigation_add_laser_scan(NavigationHandle handle, const char *laser_scan_name, LaserScanHandle laser_scan); + + /** + * @brief Add a point cloud to the navigation system + * @param handle Navigation handle + * @param point_cloud_name Name of the point cloud + * @param point_cloud Point cloud handle + * @return true if the point cloud was added successfully + */ + bool navigation_add_point_cloud(NavigationHandle handle, const char *point_cloud_name, PointCloudHandle point_cloud); + + /** + * @brief Add a point cloud2 to the navigation system + * @param handle Navigation handle + * @param point_cloud2_name Name of the point cloud2 + * @param point_cloud2 Point cloud2 handle + * @return true if the point cloud2 was added successfully + */ + bool navigation_add_point_cloud2(NavigationHandle handle, const char *point_cloud2_name, PointCloud2Handle point_cloud2); + + /** + * @brief Add an odometry to the navigation system + * @param handle Navigation handle + * @param odometry_name Name of the odometry + * @param odometry Odometry handle + * @return true if the odometry was added successfully + */ + bool navigation_add_odometry(NavigationHandle handle, const char *odometry_name, OdometryHandle odometry); + + /** + * @brief Get a static map from the navigation system + * @param handle Navigation handle + * @param map_name Name of the map + * @param out_map Output parameter for the map handle + * @return true if the map was retrieved successfully + */ + bool navigation_get_static_map(NavigationHandle handle, const char *map_name, OccupancyGridHandle *out_map); + + /** + * @brief Get a laser scan from the navigation system + * @param handle Navigation handle + * @param laser_scan_name Name of the laser scan + * @param out_scan Output parameter for the laser scan handle + * @return true if the laser scan was retrieved successfully + */ + bool navigation_get_laser_scan(NavigationHandle handle, const char *laser_scan_name, LaserScanHandle *out_scan); + + /** + * @brief Get a point cloud from the navigation system + * @param handle Navigation handle + * @param point_cloud_name Name of the point cloud + * @param out_cloud Output parameter for the point cloud handle + * @return true if the point cloud was retrieved successfully + */ + bool navigation_get_point_cloud(NavigationHandle handle, const char *point_cloud_name, PointCloudHandle *out_cloud); + + /** + * @brief Get a point cloud2 from the navigation system + * @param handle Navigation handle + * @param point_cloud2_name Name of the point cloud2 + * @param out_cloud Output parameter for the point cloud2 handle + * @return true if the point cloud2 was retrieved successfully + */ + bool navigation_get_point_cloud2(NavigationHandle handle, const char *point_cloud2_name, PointCloud2Handle *out_cloud); + + /** + * @brief Get all static maps from the navigation system + * @param handle Navigation handle + * @param out_maps Output parameter for the maps array + * @param out_count Output parameter for the number of maps + * @return true if the maps were retrieved successfully + */ + bool navigation_get_all_static_maps(NavigationHandle handle, NamedOccupancyGrid **out_maps, size_t *out_count); + + /** + * @brief Get all laser scans from the navigation system + * @param handle Navigation handle + * @param out_scans Output parameter for the scans array + * @param out_count Output parameter for the number of scans + * @return true if the scans were retrieved successfully + */ + bool navigation_get_all_laser_scans(NavigationHandle handle, NamedLaserScan **out_scans, size_t *out_count); + + /** + * @brief Get all point clouds from the navigation system + * @param handle Navigation handle + * @param out_clouds Output parameter for the clouds array + * @param out_count Output parameter for the number of clouds + * @return true if the clouds were retrieved successfully + */ + bool navigation_get_all_point_clouds(NavigationHandle handle, NamedPointCloud **out_clouds, size_t *out_count); + + /** + * @brief Get all point cloud2s from the navigation system + * @param handle Navigation handle + * @param out_clouds Output parameter for the clouds array + * @param out_count Output parameter for the number of clouds + * @return true if the clouds were retrieved successfully + */ + bool navigation_get_all_point_cloud2s(NavigationHandle handle, NamedPointCloud2 **out_clouds, size_t *out_count); + + /** + * @brief Remove a static map from the navigation system + * @param handle Navigation handle + * @param map_name Name of the map + * @return true if the map was removed successfully + */ + bool navigation_remove_static_map(NavigationHandle handle, const char *map_name); + + /** + * @brief Remove a laser scan from the navigation system + * @param handle Navigation handle + * @param laser_scan_name Name of the laser scan + * @return true if the laser scan was removed successfully + */ + bool navigation_remove_laser_scan(NavigationHandle handle, const char *laser_scan_name); + + /** + * @brief Remove a point cloud from the navigation system + * @param handle Navigation handle + * @param point_cloud_name Name of the point cloud + * @return true if the point cloud was removed successfully + */ + bool navigation_remove_point_cloud(NavigationHandle handle, const char *point_cloud_name); + + /** + * @brief Remove a point cloud2 from the navigation system + * @param handle Navigation handle + * @param point_cloud2_name Name of the point cloud2 + * @return true if the point cloud2 was removed successfully + */ + bool navigation_remove_point_cloud2(NavigationHandle handle, const char *point_cloud2_name); + + /** + * @brief Remove all static maps from the navigation system + * @param handle Navigation handle + * @return true if the maps were removed successfully + */ + bool navigation_remove_all_static_maps(NavigationHandle handle); + + /** + * @brief Remove all laser scans from the navigation system + * @param handle Navigation handle + * @return true if the scans were removed successfully + */ + bool navigation_remove_all_laser_scans(NavigationHandle handle); + + /** + * @brief Remove all point clouds from the navigation system + * @param handle Navigation handle + * @return true if the clouds were removed successfully + */ + bool navigation_remove_all_point_clouds(NavigationHandle handle); + + /** + * @brief Remove all point cloud2s from the navigation system + * @param handle Navigation handle + * @return true if the cloud2s were removed successfully + */ + bool navigation_remove_all_point_cloud2s(NavigationHandle handle); + + /** + * @brief Remove all data from the navigation system + * @param handle Navigation handle + * @return true if the data was removed successfully + */ + bool navigation_remove_all_data(NavigationHandle handle); + + /** + * @brief Get the global data from the navigation system + * @param handle Navigation handle + * @param out_data Output parameter for the data + * @return true if the data was retrieved successfully + */ + bool navigation_get_global_data(NavigationHandle handle, PlannerDataOutput *out_data); + + /** + * @brief Get the local data from the navigation system + * @param handle Navigation handle + * @param out_data Output parameter for the data + * @return true if the data was retrieved successfully + */ + bool navigation_get_local_data(NavigationHandle handle, PlannerDataOutput *out_data); #ifdef __cplusplus } #endif #endif // NAVIGATION_C_API_H - diff --git a/src/APIs/c_api/src/nav_c_api.cpp b/src/APIs/c_api/src/nav_c_api.cpp index 7cd747d..0d836b5 100644 --- a/src/APIs/c_api/src/nav_c_api.cpp +++ b/src/APIs/c_api/src/nav_c_api.cpp @@ -6,14 +6,24 @@ #include #include #include +#include #include +#include +#include +#include +#include #include +#include +#include +#include +#include #include #include #include #include #include #include +#include #include @@ -100,6 +110,26 @@ namespace { c_feedback->goal_checked = cpp_feedback.goal_checked; c_feedback->is_ready = cpp_feedback.is_ready; } + + template + T* clone_message(const T& src) { + return new (std::nothrow) T(src); + } + + void clear_planner_data(PlannerDataOutput* data) { + if (!data) { + return; + } + delete static_cast(data->plan); + delete static_cast(data->costmap); + delete static_cast(data->costmap_update); + delete static_cast(data->footprint); + data->plan = nullptr; + data->costmap = nullptr; + data->costmap_update = nullptr; + data->footprint = nullptr; + data->is_costmap_updated = false; + } } // ============================================================================ @@ -533,3 +563,583 @@ extern "C" void navigation_free_feedback(NavFeedback* feedback) { } } +// ============================================================================ +// Complex Message Handle Management +// ============================================================================ + +extern "C" void navigation_free_occupancy_grid(OccupancyGridHandle handle) { + delete static_cast(handle); +} + +extern "C" void navigation_free_occupancy_grid_update(OccupancyGridUpdateHandle handle) { + delete static_cast(handle); +} + +extern "C" void navigation_free_laser_scan(LaserScanHandle handle) { + delete static_cast(handle); +} + +extern "C" void navigation_free_point_cloud(PointCloudHandle handle) { + delete static_cast(handle); +} + +extern "C" void navigation_free_point_cloud2(PointCloud2Handle handle) { + delete static_cast(handle); +} + +extern "C" void navigation_free_odometry(OdometryHandle handle) { + delete static_cast(handle); +} + +extern "C" void navigation_free_path2d(Path2DHandle handle) { + delete static_cast(handle); +} + +extern "C" void navigation_free_polygon_stamped(PolygonStampedHandle handle) { + delete static_cast(handle); +} + +extern "C" void navigation_free_order(OrderHandle handle) { + delete static_cast(handle); +} + +extern "C" void navigation_free_named_occupancy_grids(NamedOccupancyGrid* maps, size_t count) { + if (!maps) { + return; + } + for (size_t i = 0; i < count; ++i) { + free(maps[i].name); + delete static_cast(maps[i].map); + } + free(maps); +} + +extern "C" void navigation_free_named_laser_scans(NamedLaserScan* scans, size_t count) { + if (!scans) { + return; + } + for (size_t i = 0; i < count; ++i) { + free(scans[i].name); + delete static_cast(scans[i].scan); + } + free(scans); +} + +extern "C" void navigation_free_named_point_clouds(NamedPointCloud* clouds, size_t count) { + if (!clouds) { + return; + } + for (size_t i = 0; i < count; ++i) { + free(clouds[i].name); + delete static_cast(clouds[i].cloud); + } + free(clouds); +} + +extern "C" void navigation_free_named_point_cloud2s(NamedPointCloud2* clouds, size_t count) { + if (!clouds) { + return; + } + for (size_t i = 0; i < count; ++i) { + free(clouds[i].name); + delete static_cast(clouds[i].cloud); + } + free(clouds); +} + +extern "C" void navigation_free_planner_data(PlannerDataOutput* data) { + clear_planner_data(data); +} + +// ============================================================================ +// Navigation Data Management +// ============================================================================ + +extern "C" bool navigation_add_static_map(NavigationHandle handle, const char* map_name, OccupancyGridHandle map) { + if (!handle || !map_name || !map) { + return false; + } + try { + auto* nav = static_cast(handle); + auto* map_ptr = static_cast(map); + nav->addStaticMap(std::string(map_name), *map_ptr); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_add_laser_scan(NavigationHandle handle, const char* laser_scan_name, LaserScanHandle laser_scan) { + if (!handle || !laser_scan_name || !laser_scan) { + return false; + } + try { + auto* nav = static_cast(handle); + auto* scan_ptr = static_cast(laser_scan); + nav->addLaserScan(std::string(laser_scan_name), *scan_ptr); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_add_point_cloud(NavigationHandle handle, const char* point_cloud_name, PointCloudHandle point_cloud) { + if (!handle || !point_cloud_name || !point_cloud) { + return false; + } + try { + auto* nav = static_cast(handle); + auto* cloud_ptr = static_cast(point_cloud); + nav->addPointCloud(std::string(point_cloud_name), *cloud_ptr); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_add_point_cloud2(NavigationHandle handle, const char* point_cloud2_name, PointCloud2Handle point_cloud2) { + if (!handle || !point_cloud2_name || !point_cloud2) { + return false; + } + try { + auto* nav = static_cast(handle); + auto* cloud_ptr = static_cast(point_cloud2); + nav->addPointCloud2(std::string(point_cloud2_name), *cloud_ptr); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_add_odometry(NavigationHandle handle, const char* odometry_name, OdometryHandle odometry) { + if (!handle || !odometry_name || !odometry) { + return false; + } + try { + auto* nav = static_cast(handle); + auto* odom_ptr = static_cast(odometry); + nav->addOdometry(std::string(odometry_name), *odom_ptr); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_get_static_map(NavigationHandle handle, const char* map_name, OccupancyGridHandle* out_map) { + if (!handle || !map_name || !out_map) { + return false; + } + try { + auto* nav = static_cast(handle); + robot_nav_msgs::OccupancyGrid map = nav->getStaticMap(std::string(map_name)); + auto* map_ptr = clone_message(map); + if (!map_ptr) { + return false; + } + *out_map = static_cast(map_ptr); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_get_laser_scan(NavigationHandle handle, const char* laser_scan_name, LaserScanHandle* out_scan) { + if (!handle || !laser_scan_name || !out_scan) { + return false; + } + try { + auto* nav = static_cast(handle); + robot_sensor_msgs::LaserScan scan = nav->getLaserScan(std::string(laser_scan_name)); + auto* scan_ptr = clone_message(scan); + if (!scan_ptr) { + return false; + } + *out_scan = static_cast(scan_ptr); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_get_point_cloud(NavigationHandle handle, const char* point_cloud_name, PointCloudHandle* out_cloud) { + if (!handle || !point_cloud_name || !out_cloud) { + return false; + } + try { + auto* nav = static_cast(handle); + robot_sensor_msgs::PointCloud cloud = nav->getPointCloud(std::string(point_cloud_name)); + auto* cloud_ptr = clone_message(cloud); + if (!cloud_ptr) { + return false; + } + *out_cloud = static_cast(cloud_ptr); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_get_point_cloud2(NavigationHandle handle, const char* point_cloud2_name, PointCloud2Handle* out_cloud) { + if (!handle || !point_cloud2_name || !out_cloud) { + return false; + } + try { + auto* nav = static_cast(handle); + robot_sensor_msgs::PointCloud2 cloud = nav->getPointCloud2(std::string(point_cloud2_name)); + auto* cloud_ptr = clone_message(cloud); + if (!cloud_ptr) { + return false; + } + *out_cloud = static_cast(cloud_ptr); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_get_all_static_maps(NavigationHandle handle, NamedOccupancyGrid** out_maps, size_t* out_count) { + if (!handle || !out_maps || !out_count) { + return false; + } + try { + auto* nav = static_cast(handle); + auto maps = nav->getAllStaticMaps(); + if (maps.empty()) { + *out_maps = nullptr; + *out_count = 0; + return true; + } + + NamedOccupancyGrid* result = static_cast(malloc(sizeof(NamedOccupancyGrid) * maps.size())); + if (!result) { + return false; + } + + size_t index = 0; + for (const auto& entry : maps) { + result[index].name = strdup(entry.first.c_str()); + result[index].map = static_cast(clone_message(entry.second)); + if (!result[index].name || !result[index].map) { + navigation_free_named_occupancy_grids(result, index + 1); + return false; + } + ++index; + } + + *out_maps = result; + *out_count = maps.size(); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_get_all_laser_scans(NavigationHandle handle, NamedLaserScan** out_scans, size_t* out_count) { + if (!handle || !out_scans || !out_count) { + return false; + } + try { + auto* nav = static_cast(handle); + auto scans = nav->getAllLaserScans(); + if (scans.empty()) { + *out_scans = nullptr; + *out_count = 0; + return true; + } + + NamedLaserScan* result = static_cast(malloc(sizeof(NamedLaserScan) * scans.size())); + if (!result) { + return false; + } + + size_t index = 0; + for (const auto& entry : scans) { + result[index].name = strdup(entry.first.c_str()); + result[index].scan = static_cast(clone_message(entry.second)); + if (!result[index].name || !result[index].scan) { + navigation_free_named_laser_scans(result, index + 1); + return false; + } + ++index; + } + + *out_scans = result; + *out_count = scans.size(); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_get_all_point_clouds(NavigationHandle handle, NamedPointCloud** out_clouds, size_t* out_count) { + if (!handle || !out_clouds || !out_count) { + return false; + } + try { + auto* nav = static_cast(handle); + auto clouds = nav->getAllPointClouds(); + if (clouds.empty()) { + *out_clouds = nullptr; + *out_count = 0; + return true; + } + + NamedPointCloud* result = static_cast(malloc(sizeof(NamedPointCloud) * clouds.size())); + if (!result) { + return false; + } + + size_t index = 0; + for (const auto& entry : clouds) { + result[index].name = strdup(entry.first.c_str()); + result[index].cloud = static_cast(clone_message(entry.second)); + if (!result[index].name || !result[index].cloud) { + navigation_free_named_point_clouds(result, index + 1); + return false; + } + ++index; + } + + *out_clouds = result; + *out_count = clouds.size(); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_get_all_point_cloud2s(NavigationHandle handle, NamedPointCloud2** out_clouds, size_t* out_count) { + if (!handle || !out_clouds || !out_count) { + return false; + } + try { + auto* nav = static_cast(handle); + auto clouds = nav->getAllPointCloud2s(); + if (clouds.empty()) { + *out_clouds = nullptr; + *out_count = 0; + return true; + } + + NamedPointCloud2* result = static_cast(malloc(sizeof(NamedPointCloud2) * clouds.size())); + if (!result) { + return false; + } + + size_t index = 0; + for (const auto& entry : clouds) { + result[index].name = strdup(entry.first.c_str()); + result[index].cloud = static_cast(clone_message(entry.second)); + if (!result[index].name || !result[index].cloud) { + navigation_free_named_point_cloud2s(result, index + 1); + return false; + } + ++index; + } + + *out_clouds = result; + *out_count = clouds.size(); + return true; + } catch (...) { + return false; + } +} + +extern "C" bool navigation_remove_static_map(NavigationHandle handle, const char* map_name) { + if (!handle || !map_name) { + return false; + } + try { + auto* nav = static_cast(handle); + return nav->removeStaticMap(std::string(map_name)); + } catch (...) { + return false; + } +} + +extern "C" bool navigation_remove_laser_scan(NavigationHandle handle, const char* laser_scan_name) { + if (!handle || !laser_scan_name) { + return false; + } + try { + auto* nav = static_cast(handle); + return nav->removeLaserScan(std::string(laser_scan_name)); + } catch (...) { + return false; + } +} + +extern "C" bool navigation_remove_point_cloud(NavigationHandle handle, const char* point_cloud_name) { + if (!handle || !point_cloud_name) { + return false; + } + try { + auto* nav = static_cast(handle); + return nav->removePointCloud(std::string(point_cloud_name)); + } catch (...) { + return false; + } +} + +extern "C" bool navigation_remove_point_cloud2(NavigationHandle handle, const char* point_cloud2_name) { + if (!handle || !point_cloud2_name) { + return false; + } + try { + auto* nav = static_cast(handle); + return nav->removePointCloud2(std::string(point_cloud2_name)); + } catch (...) { + return false; + } +} + +extern "C" bool navigation_remove_all_static_maps(NavigationHandle handle) { + if (!handle) { + return false; + } + try { + auto* nav = static_cast(handle); + return nav->removeAllStaticMaps(); + } catch (...) { + return false; + } +} + +extern "C" bool navigation_remove_all_laser_scans(NavigationHandle handle) { + if (!handle) { + return false; + } + try { + auto* nav = static_cast(handle); + return nav->removeAllLaserScans(); + } catch (...) { + return false; + } +} + +extern "C" bool navigation_remove_all_point_clouds(NavigationHandle handle) { + if (!handle) { + return false; + } + try { + auto* nav = static_cast(handle); + return nav->removeAllPointClouds(); + } catch (...) { + return false; + } +} + +extern "C" bool navigation_remove_all_point_cloud2s(NavigationHandle handle) { + if (!handle) { + return false; + } + try { + auto* nav = static_cast(handle); + return nav->removeAllPointCloud2s(); + } catch (...) { + return false; + } +} + +extern "C" bool navigation_remove_all_data(NavigationHandle handle) { + if (!handle) { + return false; + } + try { + auto* nav = static_cast(handle); + return nav->removeAllData(); + } catch (...) { + return false; + } +} + +// ============================================================================ +// Planner Data +// ============================================================================ + +extern "C" bool navigation_get_global_data(NavigationHandle handle, PlannerDataOutput* out_data) { + if (!handle || !out_data) { + return false; + } + clear_planner_data(out_data); + try { + auto* nav = static_cast(handle); + robot::move_base_core::PlannerDataOutput data = nav->getGlobalData(); + out_data->plan = static_cast(clone_message(data.plan)); + out_data->costmap = static_cast(clone_message(data.costmap)); + out_data->costmap_update = static_cast(clone_message(data.costmap_update)); + out_data->footprint = static_cast(clone_message(data.footprint)); + out_data->is_costmap_updated = data.is_costmap_updated; + + if (!out_data->plan || !out_data->costmap || !out_data->costmap_update || !out_data->footprint) { + clear_planner_data(out_data); + return false; + } + return true; + } catch (...) { + clear_planner_data(out_data); + return false; + } +} + +extern "C" bool navigation_get_local_data(NavigationHandle handle, PlannerDataOutput* out_data) { + if (!handle || !out_data) { + return false; + } + clear_planner_data(out_data); + try { + auto* nav = static_cast(handle); + robot::move_base_core::PlannerDataOutput data = nav->getLocalData(); + out_data->plan = static_cast(clone_message(data.plan)); + out_data->costmap = static_cast(clone_message(data.costmap)); + out_data->costmap_update = static_cast(clone_message(data.costmap_update)); + out_data->footprint = static_cast(clone_message(data.footprint)); + out_data->is_costmap_updated = data.is_costmap_updated; + + if (!out_data->plan || !out_data->costmap || !out_data->costmap_update || !out_data->footprint) { + clear_planner_data(out_data); + return false; + } + return true; + } catch (...) { + clear_planner_data(out_data); + return false; + } +} + +// ============================================================================ +// Navigation Commands with Order +// ============================================================================ + +extern "C" bool navigation_move_to_order(NavigationHandle handle, const OrderHandle order, + const PoseStamped* goal, + double xy_goal_tolerance, double yaw_goal_tolerance) { + if (!handle || !order || !goal) { + return false; + } + try { + auto* nav = static_cast(handle); + auto* order_ptr = static_cast(order); + robot_geometry_msgs::PoseStamped cpp_goal = c_to_cpp_pose_stamped(goal); + return nav->moveTo(*order_ptr, cpp_goal, xy_goal_tolerance, yaw_goal_tolerance); + } catch (...) { + return false; + } +} + +extern "C" bool navigation_dock_to_order(NavigationHandle handle, const OrderHandle order, + const PoseStamped* goal, + double xy_goal_tolerance, double yaw_goal_tolerance) { + if (!handle || !order || !goal) { + return false; + } + try { + auto* nav = static_cast(handle); + auto* order_ptr = static_cast(order); + robot_geometry_msgs::PoseStamped cpp_goal = c_to_cpp_pose_stamped(goal); + return nav->dockTo(*order_ptr, cpp_goal, xy_goal_tolerance, yaw_goal_tolerance); + } catch (...) { + return false; + } +} +