Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/image performance #128

Open
wants to merge 74 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
9401929
Remove unnecessary if
hoffmann-stefan Aug 19, 2021
52363eb
Formating and improve formating of generated code
hoffmann-stefan Aug 19, 2021
e4eeb2c
Add support for arrays of strings
hoffmann-stefan Aug 19, 2021
cb0f23f
Uncomment test for arrays of strings
hoffmann-stefan Aug 19, 2021
2b198f4
Generate array size constants
hoffmann-stefan Aug 19, 2021
8209665
Add initial support for sequences
hoffmann-stefan Aug 20, 2021
0820c78
Add tests for sequences
hoffmann-stefan Aug 20, 2021
fb9a25f
Initialize arrays and change mapped type
hoffmann-stefan Aug 26, 2021
922c24d
Add size checks for array and bounded sequences
hoffmann-stefan Aug 24, 2021
7dc4f12
Use `int32_t` as size type to the c# side
hoffmann-stefan Aug 24, 2021
ebbd965
Generate service request/response types
hoffmann-stefan Aug 25, 2021
2229e71
Generate service definition classes
hoffmann-stefan Aug 26, 2021
41f46a0
Add initial support for services
hoffmann-stefan Aug 26, 2021
8722462
Add service example
hoffmann-stefan Aug 26, 2021
1e8521d
Add initial support for clients
hoffmann-stefan Aug 31, 2021
0c21977
Add client example
hoffmann-stefan Aug 31, 2021
262ffea
Add unittest for services
hoffmann-stefan Aug 31, 2021
680e3db
Add Client.ServiceIsReady() mehtod
hoffmann-stefan Sep 1, 2021
bd37a93
Remove Interfaces that expose internal implementation details
hoffmann-stefan Sep 6, 2021
a65e5e9
Add SafeHandles for Node and related types
hoffmann-stefan Sep 8, 2021
3b33504
Add SafeHandles for request id and wait set
hoffmann-stefan Sep 8, 2021
ca71795
Add SafeHandles to generated message types
hoffmann-stefan Sep 9, 2021
c3d3797
Add static member caches
hoffmann-stefan Sep 9, 2021
f87e169
Improve error handling
hoffmann-stefan Sep 9, 2021
91f8e01
Rename request variable to solve a CS0136 error:
LoyVanBeek Oct 12, 2021
67a732b
Use `const` instead of `static readonly` for constants
hoffmann-stefan Jan 26, 2022
849eca5
Add licence headers and entry in NOTICE
hoffmann-stefan Apr 19, 2022
79a338d
Update README.md
hoffmann-stefan Apr 19, 2022
06f231e
Use CamelCase for property names
hoffmann-stefan Jun 14, 2022
fe31b3f
Add support for field default values for non-collections
hoffmann-stefan Jun 15, 2022
6d3790b
Add support for field default values for collections
hoffmann-stefan Jun 15, 2022
a337ee5
reduce warnings: Only use cdecl calling convention on i386 architectures
hoffmann-stefan Jun 15, 2022
73975d2
reduce warnings: remove debug message in cmake script
hoffmann-stefan Jun 15, 2022
6b51d27
codestyle: add .editorconfig
hoffmann-stefan Jun 15, 2022
534d2a3
codestyle: Format code using `dotnet format`
hoffmann-stefan Jun 15, 2022
fc68df1
codestyle: fix naming of internal variables
hoffmann-stefan Jun 15, 2022
be8439d
codestyle: fix xunit waring
hoffmann-stefan Jun 15, 2022
093c045
guard conditions: only build one native library
hoffmann-stefan Jun 17, 2022
b7713c5
guard conditions: add guard conditions
hoffmann-stefan Jun 17, 2022
664ee0b
guard conditions: only try to take waitables if they are ready
hoffmann-stefan Jun 21, 2022
adebb26
actions: initial action idl generation
hoffmann-stefan Jun 21, 2022
2aa60ce
actions: extend idl generation to include interfaces for "action wrap…
hoffmann-stefan Jun 21, 2022
37845b0
actions: add accessors to `UUID` and `Time` properties for "action wr…
hoffmann-stefan Jun 22, 2022
ab8aff4
actions: add ActionDefinitionStaticMemberCache
hoffmann-stefan Jun 22, 2022
0b85387
actions: add public action client API without implementation
hoffmann-stefan Jun 22, 2022
eec1f7f
actions: rename `ServiceIsReady()` to `ServerIsReady() `and move to b…
hoffmann-stefan Jun 21, 2022
71ad79b
actions: add helper for converting guid <-> uuid
hoffmann-stefan Jun 23, 2022
83746eb
actions: implement basic action client
hoffmann-stefan Jun 24, 2022
e1b38da
actions: add public action server API without implementation
hoffmann-stefan Jun 29, 2022
41cf05b
actions: add missing license header
hoffmann-stefan Jun 29, 2022
fdab894
guard conditions: Improve exception message in GuardCondition.
hoffmann-stefan Jul 19, 2022
3b71121
actions: Change IsCancelRequested to IsCanceling, add IsExecuting
hoffmann-stefan Jul 19, 2022
b2e46d9
actions: Add ReadFromMessageHandle/WriteToMessageHandle helper mehtods
hoffmann-stefan Jul 19, 2022
e6639a6
actions: implement basic action server
hoffmann-stefan Jul 19, 2022
3ffbd2d
actions: add example action client and server
hoffmann-stefan Jul 19, 2022
04b2019
actions: make local variable name more obvious
hoffmann-stefan Jul 22, 2022
02cf5a5
actions: use return value from Interlocked.CompareExchange
hoffmann-stefan Jul 22, 2022
459c229
actions: reorder HandleGoalResponse, HandleStatusMessage and HandleFe…
hoffmann-stefan Jul 28, 2022
15156b0
actions: handle action specific take failed error codes
hoffmann-stefan Jul 28, 2022
866834a
actions: publish status before sending the result response
hoffmann-stefan Jul 28, 2022
5fda852
actions: fix typo in dependency
hoffmann-stefan Aug 17, 2022
0019358
bool pInvoke: Fix bool pInvokes in rcldotnet
hoffmann-stefan Sep 13, 2022
275dc2b
bool pInvoke: Fix bool pInvokes in generated messages
hoffmann-stefan Oct 11, 2022
2f857a3
Add quality of service for publishers and subscribers.
hoffmann-stefan Oct 13, 2022
bb16831
missing
pabloinigoblasco Nov 30, 2022
328145e
first attempt
pabloinigoblasco Jun 9, 2023
fd7b86d
image performance
pabloinigoblasco Jun 13, 2023
5788f59
merge ros2_dotnet original repo
cmv-81 Feb 20, 2024
d2b7553
solvin merge errors
cmv-81 Feb 20, 2024
58b970b
fixing unknown platform error
cmv-81 Feb 21, 2024
e328135
update net7.0
cmv-81 Mar 19, 2024
30bdf4f
pointcloud performance
pabloinigoblasco Nov 9, 2024
08ec73a
safe bytes
pabloinigoblasco Nov 9, 2024
db91259
minor
pabloinigoblasco Nov 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ Copyright 2016-2018 Esteve Fernandez ([email protected])

This product includes software developed by
Esteve Fernandez ([email protected])
Stefan Hoffmann ([email protected])
6 changes: 5 additions & 1 deletion rcldotnet/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ find_package(rmw REQUIRED)
find_package(rmw_implementation REQUIRED)
find_package(rmw_implementation_cmake REQUIRED)
find_package(rosidl_generator_c REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(std_msgs REQUIRED)

find_package(dotnet_cmake_module REQUIRED)
find_package(DotNETExtra REQUIRED)
Expand Down Expand Up @@ -76,6 +78,8 @@ set(_assemblies_dep_dlls
${builtin_interfaces_ASSEMBLIES_DLL}
${rcldotnet_common_ASSEMBLIES_DLL}
${unique_identifier_msgs_ASSEMBLIES_DLL}
${sensor_msgs_ASSEMBLIES_DLL}
${std_msgs_ASSEMBLIES_DLL}
)

add_dotnet_library(${PROJECT_NAME}_assemblies
Expand Down Expand Up @@ -141,7 +145,7 @@ if(BUILD_TESTING)
if(DEFINED ENV{RCLDOTNET_TEST_TARGET_FRAMEWORK})
set(RCLDOTNET_TEST_TARGET_FRAMEWORK $ENV{RCLDOTNET_TEST_TARGET_FRAMEWORK})
else()
set(RCLDOTNET_TEST_TARGET_FRAMEWORK "net6.0")
set(RCLDOTNET_TEST_TARGET_FRAMEWORK "net7.0")
endif()

add_dotnet_test(test_messages
Expand Down
230 changes: 229 additions & 1 deletion rcldotnet/RCLdotnet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
using action_msgs.msg;
using action_msgs.srv;
using ROS2.Utils;
using sensor_msgs.msg;
using System.Collections.Generic;
using System.Diagnostics;

namespace ROS2
{
Expand Down Expand Up @@ -898,7 +901,21 @@ private static bool Take(Subscription subscription, IRosMessage message)
switch (ret)
{
case RCLRet.Ok:
ReadFromMessageHandle(message, messageHandle);
if (message is sensor_msgs.msg.Image)
{
HackedReadFromImageMessageHandle(subscription, message, messageHandle);

}
else if (message is sensor_msgs.msg.PointCloud2)
{
// Console.WriteLine("PointCloud2");
HackedReadFromPointCloudMessageHandle(subscription, message, messageHandle);
}
else
{
ReadFromMessageHandle(message, messageHandle);
}

return true;

case RCLRet.SubscriptionTakeFailed:
Expand Down Expand Up @@ -1485,6 +1502,217 @@ public static string GetRMWIdentifier()
return rmw_identifier;
}


// WORKAROUND IMAGES
// public static Dictionary<Subscription, sensor_msgs.msg.Image> lastImages =new Dictionary<Subscription, sensor_msgs.msg.Image>();

public static Dictionary<Subscription, sensor_msgs.msg.PointCloud2> pointCloudSubscription = new Dictionary<Subscription, sensor_msgs.msg.PointCloud2>();

public static Dictionary<Subscription, sensor_msgs.msg.Image> imageSubscription = new Dictionary<Subscription, sensor_msgs.msg.Image>();

public static Dictionary<sensor_msgs.msg.Image, byte[]> lastImagesBytes = new Dictionary<Image, byte[]>();

public static Dictionary<sensor_msgs.msg.PointCloud2, byte[]> lastPointCloudsBytes = new Dictionary<sensor_msgs.msg.PointCloud2, byte[]>();

public static object IMG_DATA_LOCK = new object();
public static object POINTCLOUD_DATA_LOCK = new object();


public static void GetLastPointCloudCopy(sensor_msgs.msg.PointCloud2 pointCloud2, ref byte[] pointCloudData)
{
lock (POINTCLOUD_DATA_LOCK)
{
if (lastPointCloudsBytes.ContainsKey(pointCloud2))
{
var cloudData = lastPointCloudsBytes[pointCloud2];

// cloudData = lastPointCloudsBytes[pointCloud2];
if(pointCloudData == null || pointCloudData.Length != cloudData.Length)
{
pointCloudData = new byte[lastPointCloudsBytes[pointCloud2].Length];
}

Buffer.BlockCopy(cloudData, 0, pointCloudData, 0, cloudData.Length);
// Marshal.Copy(lastPointCloudsBytes[pointCloud2], 0, pointCloudData, cloudData.Length);

}
else
{
pointCloudData = null;
}
}
}

// public static byte[] GetLastPointCloudBytes(sensor_msgs.msg.PointCloud2 pointCloud2)
// {
// lock (POINTCLOUD_DATA_LOCK)
// {
// if (lastPointCloudsBytes.ContainsKey(pointCloud2))
// {
// return lastPointCloudsBytes[pointCloud2];
// }
// return null;
// }
// }

public static void HackedPointCloud__ReadFromHandle(Subscription subscription, global::System.IntPtr messageHandle, sensor_msgs.msg.PointCloud2 pointCloud2)
{
Debug.WriteLine("HackedPointCloud__ReadFromHandle");
var sw = System.Diagnostics.Stopwatch.StartNew();
lock(POINTCLOUD_DATA_LOCK)
{
pointCloud2.Header.__ReadFromHandle(sensor_msgs.msg.PointCloud2.native_get_field_header_HANDLE(messageHandle));
pointCloud2.Height = sensor_msgs.msg.PointCloud2.native_read_field_height(messageHandle);
pointCloud2.Width = sensor_msgs.msg.PointCloud2.native_read_field_width(messageHandle);
pointCloud2.PointStep = sensor_msgs.msg.PointCloud2.native_read_field_point_step(messageHandle);
pointCloud2.RowStep = sensor_msgs.msg.PointCloud2.native_read_field_row_step(messageHandle);
pointCloud2.IsBigendian = sensor_msgs.msg.PointCloud2.native_read_field_is_bigendian(messageHandle);
{
int size = sensor_msgs.msg.PointCloud2.native_getsize_field_data_message(messageHandle);
HackedPointCloudMemoryCopy(subscription, messageHandle, pointCloud2, size);
}
}
}

public static void HackedImage__ReadFromHandle(Subscription subscription, global::System.IntPtr messageHandle, sensor_msgs.msg.Image image)
{
// Debug.WriteLine("HackedImage__ReadFromHandle");
// var sw = System.Diagnostics.Stopwatch.StartNew();

lock(IMG_DATA_LOCK)
{
image.Header.__ReadFromHandle(sensor_msgs.msg.Image.native_get_field_header_HANDLE(messageHandle));
image.Height = sensor_msgs.msg.Image.native_read_field_height(messageHandle);
image.Width = sensor_msgs.msg.Image.native_read_field_width(messageHandle);
IntPtr pStr_Encoding = sensor_msgs.msg.Image.native_read_field_encoding(messageHandle);
image.Encoding = Marshal.PtrToStringAnsi(pStr_Encoding);

image.IsBigendian = sensor_msgs.msg.Image.native_read_field_is_bigendian(messageHandle);
image.Step = sensor_msgs.msg.Image.native_read_field_step(messageHandle);
{
int size__local_variable = sensor_msgs.msg.Image.native_getsize_field_data_message(messageHandle);

HackedMemoryCopy(subscription, messageHandle, image, size__local_variable);

// sw.Stop();

// var sw2 = System.Diagnostics.Stopwatch.StartNew();

// image.Data = new System.Collections.Generic.List<byte>(size__local_variable);
// for (int i__local_variable = 0; i__local_variable < size__local_variable; i__local_variable++)
// {
// image.Data.Add(sensor_msgs.msg.Image.native_read_field_data(sensor_msgs.msg.Image.native_get_field_data_message(messageHandle, i__local_variable)));
// }

// sw2.Stop();
// Console.WriteLine($"copying image took: {sw.ElapsedTicks} ticks, {sw2.ElapsedTicks} ticks: {(float)(sw.ElapsedTicks - sw2.ElapsedTicks)/(float)sw.ElapsedTicks}");

}
}

// sw.Stop();
// Debug.WriteLine($"HackedImage__ReadFromHandle END: {sw.ElapsedMilliseconds} ms");
}

private static void HackedPointCloudMemoryCopy(Subscription subscription, IntPtr messageHandle, sensor_msgs.msg.PointCloud2 pointCloud2, int size__local_variable)
{
IntPtr first = sensor_msgs.msg.PointCloud2.native_get_field_data_message(messageHandle, 0);
byte[] bytes = null;
if (pointCloudSubscription.ContainsKey(subscription))
{
var lastPointCloud = pointCloudSubscription[subscription];
bytes = lastPointCloudsBytes[lastPointCloud];
lastPointCloudsBytes.Remove(lastPointCloud);
}

if (bytes == null || bytes.Length != size__local_variable)
{
bytes = new byte[size__local_variable];
lastPointCloudsBytes[pointCloud2] = bytes;
}

Marshal.Copy(first, bytes, 0, (int)(size__local_variable));
lastPointCloudsBytes[pointCloud2] = bytes;
pointCloudSubscription[subscription] = pointCloud2;
}

private static void HackedMemoryCopy(Subscription subscription, IntPtr messageHandle, sensor_msgs.msg.Image image, int size__local_variable)
{
IntPtr first = sensor_msgs.msg.Image.native_get_field_data_message(messageHandle, 0);

// lastImagesHandles[image] = new Tuple<IntPtr, uint>(first, (uint)size__local_variable);

byte[] bytes = null;
if (imageSubscription.ContainsKey(subscription))
{
var lastImage = imageSubscription[subscription];
bytes = lastImagesBytes[lastImage];
lastImagesBytes.Remove(lastImage);
}

if (bytes == null || bytes.Length != size__local_variable)
{
// Debug.WriteLine("HackedImage__ReadFromHandle: new byte buffer created");

bytes = new byte[size__local_variable];
// Console.WriteLine($"makmustReleaseing new size for image subscription: {size__local_variable} subscription: {subscription.GetHashCode()}");
}


Marshal.Copy(first,bytes, 0, (int)(size__local_variable));

lastImagesBytes[image] = bytes;
imageSubscription[subscription] = image;
}

internal static void HackedReadFromPointCloudMessageHandle(Subscription subscription, IRosMessage message, SafeHandle messageHandle)
{
bool mustRelease = false;
try
{
messageHandle.DangerousAddRef(ref mustRelease);
HackedPointCloud__ReadFromHandle(subscription, messageHandle.DangerousGetHandle(),message as sensor_msgs.msg.PointCloud2);
}
finally
{
if (mustRelease)
{
messageHandle.DangerousRelease();
}
}
}



internal static void HackedReadFromImageMessageHandle(Subscription subscription, IRosMessage message, SafeHandle messageHandle)
{
bool mustRelease = false;
try
{
// Using SafeHandles for __ReadFromHandle() is very tedious as
// this needs to be handled in generated code across multiple
// assemblies. Array and collection indexing would need to
// create SafeHandles everywhere. It's not worth it, especially
// considering the extra allocations for SafeHandles in arrays
// or collections that don't really represent their own native
// resource.
messageHandle.DangerousAddRef(ref mustRelease);
HackedImage__ReadFromHandle(subscription, messageHandle.DangerousGetHandle(),message as sensor_msgs.msg.Image);
//message.__ReadFromHandle(messageHandle.DangerousGetHandle());
}
catch(Exception e)
{
// Debug.WriteLine("HackedImage__ReadFromHandle: exception");
}
finally
{
if (mustRelease)
{
messageHandle.DangerousRelease();
}
}
}

internal static void ReadFromMessageHandle(IRosMessage message, SafeHandle messageHandle)
{
bool mustRelease = false;
Expand Down
2 changes: 1 addition & 1 deletion rcldotnet/SafeSubscriptionHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace ROS2
/// of the subscription handle and node handle. This also applies to publisher, service and client handles,
/// which point to a rcl_publisher_t, rcl_service_t or rcl_client_t.
/// </summary>
internal sealed class SafeSubscriptionHandle : SafeHandleZeroOrMinusOneIsInvalid
public sealed class SafeSubscriptionHandle : SafeHandleZeroOrMinusOneIsInvalid
{
// Trick with parent handles taken from https://github.com/dotnet/corefx/pull/6366
// Commit from early 2016, but still in current .NET as of september 2021:
Expand Down
4 changes: 2 additions & 2 deletions rcldotnet/Subscription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal Subscription()
// internal handle is disposed.
// By relying on the GC/Finalizer of SafeHandle the handle only gets
// Disposed if the subscription is not live anymore.
internal abstract SafeSubscriptionHandle Handle { get; }
public abstract SafeSubscriptionHandle Handle { get; }

internal abstract IRosMessage CreateMessage();

Expand All @@ -54,7 +54,7 @@ internal Subscription(SafeSubscriptionHandle handle, Action<T> callback)
_callback = callback;
}

internal override SafeSubscriptionHandle Handle { get; }
public override SafeSubscriptionHandle Handle { get; }

internal override IRosMessage CreateMessage() => (IRosMessage)new T();

Expand Down
4 changes: 4 additions & 0 deletions rcldotnet/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<exec_depend>rmw_implementation_cmake</exec_depend>
<exec_depend>rosidl_generator_c</exec_depend>
<exec_depend>rosidl_parser</exec_depend>
<exec_depend>std_msgs</exec_depend>

<build_depend>rmw_implementation_cmake</build_depend>
<build_depend>rcl</build_depend>
Expand All @@ -25,13 +26,16 @@
<build_depend>action_msgs</build_depend>
<build_depend>builtin_interfaces</build_depend>
<build_depend>unique_identifier_msgs</build_depend>
<build_depend>sensor_msgs</build_depend>
<build_depend>std_msgs</build_depend>

<exec_depend>rcl</exec_depend>
<exec_depend>rcl_interfaces</exec_depend>
<exec_depend>rcl_action</exec_depend>
<exec_depend>action_msgs</exec_depend>
<exec_depend>builtin_interfaces</exec_depend>
<exec_depend>unique_identifier_msgs</exec_depend>
<build_depend>unique_identifier_msgs</build_depend>

<test_depend>std_msgs</test_depend>
<test_depend>test_msgs</test_depend>
Expand Down
2 changes: 2 additions & 0 deletions rcldotnet/rcldotnet.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "rcldotnet.h"

static rcl_context_t context;
static rcl_clock_t clock;

int32_t native_rcl_init(int argc, const char *argv[]) {
context = rcl_get_zero_initialized_context();
Expand All @@ -40,6 +41,7 @@ int32_t native_rcl_init(int argc, const char *argv[]) {
return ret;
}

ret = rcl_clock_init(RCL_STEADY_TIME, &clock, &allocator);
return ret;
}

Expand Down
3 changes: 1 addition & 2 deletions rcldotnet/rcldotnet_node.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ int32_t native_rcl_action_create_server_handle(void **action_server_handle,
rcl_action_server_options_t action_server_ops =
rcl_action_server_get_default_options();


rcl_ret_t ret =
rcl_action_server_init(action_server, node, clock, ts, action_name, &action_server_ops);

Expand Down Expand Up @@ -258,4 +257,4 @@ const char * native_rcl_node_get_fully_qualified_name_handle(void *node_handle)
rcl_node_t *node = (rcl_node_t *)node_handle;

return rcl_node_get_fully_qualified_name(node);
}
}
Loading