This is just a place to continue the discussion that started in https://github.com/esteve/ros2_java/issues/1
It’s great to have better place for discussions than the github issue tracker.
As already said before I would like to join efforts regarding a .Net wrapper. In the mean time I will go on with my own wrapper.
I was able to make progress with my wrapper ( https://github.com/firesurfer/rclcs) the last few days and I’m trying to implement services tomorrow. Nevertheless as mentioned on github I’m currently running into some major issues with arrays.
But I still believe that a fairly flat wrapper without some glue code should work fine in C# and might might also provide better performance. .Net provides a nice mechanism called pinning which allows for a certain set of data types to pass a pointer of a managed memory section to unmanaged code without doing the usual copy.
From a usability point of view I think it would be nice be create an api with the same names and usage like the rclcpp. So users can easily switch from c++ to c# or the other way round.
That sounds great, I fully agree that a single wrapper is much better for users, and please continue with your efforts! I don’t know if the way I wrote the wrapper is the most .NET idiomatic way, I’m fairly new to the platform/language. My only advice is that you could reuse empy, the templating engine used in rclpy and rcljava (and pretty much all of ROS2), since that’ll help get you more contributors and to better integrate rclcs with ROS2.
I’m far from being a .NET expert, so given that you’re more knowledgeable about this, I trust your judgement My wrapper is just a .NETified version of my Java wrapper, so I don’t know of any better way of passing structures around.
That’d be great, though the recommended path is DDS implementation <-> RMW <-> RCL <-> Language-specific library. rclcpp is meant to be the C++ client library, not a foundation library upon which other libraries can be built. For example, rcljava is built on top of RCL, despite the bindings being written in C++, because that way there’s a clear separation between the base layers and the client libraries.
My intention is to finaly use the templating engine. But when I took a first look at how it is used in the rosidl_generator_c it looked terrible complicated. So I decided to hack my own message generator.
I wouldn’t call myself a .Net expert, I actually spend the most time the last two year coding in C++, but there’s some stuff that much easier to do in C# than in C++.
My .Net wrapper codes is built upon the RCL. I just wanted to say that it would be nice to have roughly the same naming/usage pattern like in the rclcpp. Building a wrapper upon C++ code would be very very difficult.
I was able to make some progress with my wrapper. As already said on my post on the mailinglist ( https://groups.google.com/forum/#!topic/ros-sig-ng-ros/uxpnNy71MRY) most of the essential features are working now.
I even wrote some examples: https://github.com/firesurfer/rclcs_testing_ws
But nevertheless I’m running into some problems with the memory management when using arrays. I wrote a fairly detailed article about that in the documentation of my rclcs ( https://github.com/firesurfer/rclcs/blob/master/doc/MemoryHandling.md ). Could you perhaps take a look at it and/or tell me how you solved the problem, when to free manually allocated memory inside the struct that wraps an array?
My idea was to create something like a Message wrapper around the struct, which prevents the struct from beeing copied. The usage of a message would then be:
Message<test_msgs.msg.Dummy> msg = new Message<test_msgs.msg.Dummy>();
msg.Data.field name = …
But I think this isn’t very elegant. But having to free your memory by hand also isn’t very nice.
Sorry for the slow response.
I just had a look at https://github.com/firesurfer/rclcs/blob/master/doc/MemoryHandling.md I see you found a way to solve this, that’s great!
The way I solved it is a byproduct of converting between C and Java structures, since the Java messages are pure Java and are only converted to their C counterparts when they are published/received, I can control how and when to allocate/deallocate.
Do you still have to explicitly deallocate the wrapped structures in C#? Are structs automatically destroyed in C#?
my solution was to create a (autogenerated) class wrapper around the message struct. The wrapper contains a single instance of struct. For simple types and arrays the code generator generates getter and setter properties which directly operate on the struct (with some nice enhancements for dealing with strings and arrays). Only nested types were a bit nasty to implement.
In order to solve the memory allocation/deallocation problem the wrapper implements the IDisposable interface. This means at destruction (and on calling .Dispose() ) the wrapper calls .Free() on the struct. In the Free method the struct steps via reflection through all fields check if they also have Free method and calls it.
So it’s still necessary to free unmanaged memory, because I need to allocate/deallocate unamanged in order to be able to pass unbounded arrays. The main difference is: the user doesn’t have to call .Free() manually.
I think it might get a bit clearer by having a look at the generated message:
@esteve could you perhaps put your .Net wrapper code on github? I’m still running into some troubles with my code on windows and wanted to have look at your code. (Even though we are using different approaches it would be nice to see if the problems I’m running in are due to ros2 on windows or are caused by my own code)
@firesurfer hey, sorry for the slow response, I totally forgot about this. I just made the repo public ( https://github.com/esteve/ros2_dotnet ), it’s not as complete as yours, but hopefully you may find it useful. Things you might want to consider:
Instead of using
dlopen(see https://github.com/esteve/ros2_dotnet/blob/devel/ros2_dotnet_utils/DllLoadUtils.cs). The reason is that with
DllImportyou can’t pick which RMW to load at runtime, so right now
rclcsuses whatever RMW implementation is the default. It works fine if you only have one RMW implementation (such as FastRTPS), but it won’t if you have additional ones.
The CMake cruft I had to add is rather ugly. .NET Core will again switch to a new build system, so I added support for dotnet’s
project.json, but also for the current
msbuildformat on Windows. Eventually everything will consolidate into only one build system ( http://ivanz.com/2016/05/17/farewell-project-json-hello-msbuild-and-csproj and https://blogs.msdn.microsoft.com/dotnet/2016/10/19/net-core-tooling-in-visual-studio-15/), but for the time being I added support for both
Works on Windows (tested on Windows 10 and Visual Studio Community). I originally added support for
dotnet, but I’m afraid that after a few changes for supporting Windows, I broke the support for Linux. It should be fairly straightforward to fix, just a few tweaks to the CMake scripts, I think.
Let me know if you need anything else, and of course, feel free to take any code you may find useful, it’s all Apache licensed.
I don’t really see the advantage of using dlopen directly. The .net way would be to create a class that contains the DllImport statements foreach RMW implementation and that is implemented against an interface or baseclass. Than you have some logic that decides at runtime which concrete class should be used and create your code against the interface.
Otherwise you are likely to mix some code that decides which rmw implementation to use with the code that actual call the native function. I think that would be a rather ugly design.
Also I couldn’t experience any problems with multiple RMW implementations (I have FastRTPS and OpenSplice installed). I would be suprised if there where any problems because “normal” enduser applications just link to the default rmw too.
The implementation into the buildsystem is still something I’m kind of afraid of. I will have a deeper look into your CMake code, but I think that’s a point I could really use some help.
I need to do some further debugging with my implementation on windows. I’m running into some troubles with strings on Windows, which aren’t passed correctly into the native code.
Thanks for publishing your code. I’m sure I will find some useful parts in it.
Help wanted for rclcs
Sure, that’s doable, but the way
rclcs is currently laid out only works with a single RMW implementation. For example,
librcl.so ( https://github.com/firesurfer/rclcs/blob/master/src/RCL.cs#L19 ), over which the user doesn’t have any control. If you have both OpenSplice and FastRTPS installed, you can’t pick which one to load.
I guess that’s a matter of different tastes, but that’s the design we did for
rclpy and thusly, the one I followed for
rcljava. But given that the .NET mechanism for using native libraries is more powerful, it may not make sense to use the same design with .NET, so I don’t really know. The approach you described sounds really clean and should work fine.
How do you choose which RMW implementation to use? I might have missed something, but it seems to be me that
librcl.so directly and you can’t pick which one to load at runtime.
From my current understanding also a c++ program written by an end user will usually link only against the default rmw. I’m aware that it is possible to link against a non-default rmw but I’m not sure that this is the intended use for endusers. (There’s an issue tracker on github where we discussed this matter and my understanding was that linking against non-default rmw is usually used for testing purposes)
So the way I’m switching the rmw implementation at the moment is simply recompiling ros2 with the RMW_IMPLEMENTATION variable defined.
If desired I will implemented a mechanism that allows choosing the rmw implementation in C# at runtime. Perhaps @dirk-thomas or @wjwwood could comment on this.
Implementing the pattern I described in the previous post, will also have the advantage that it’s possible to have special code paths for different plattforms. (For example different library names on windows) So I will probably go for that.
At the moment there is unfortunatly a lot of other stuff to do for the wrapper. For example I’m currently refactoring my message generator to use the C# CodeDom (or in future the Roselyn code synthesize interface), furthermore I need to fix the issue relating to windows. And there are still a lot of unit-tests that needs to be written. So it might be a while before I will implement a mechanism for choosing the rmw in the C# code.
Update on rclcs!
I finally found some time (university is more time intensive than one might think) to work at the rclcs again.
- The rclcs finally works again with the current rcl
- I moved to the dotnet_cmake_module esteve provided (Will merge the corresponding branches of the rclcs after @esteve merged my PR to the cmake module)
- The did a complete rewrite of the message generator using the C# CodeDom interface
** To be fair: I’m still running into major trouble regarding nested messages. It’s really hard to generate them and provide a memory save wrapping interface without to much overhead. (But I’m working on this)
The next big thing I’m going to do is provide a simple abstraction layer inside the rclcs to implement different codepaths for linux and windows.
I’m still looking for helpers who might want to help porting to Windows!
I implemented multiple codepaths for linux and windows that get chosen at runtime. That should make a working implementation on windows much easier.
Furthermore I fixed some memory cleaning bugs and cleaned up some classes.
The demonstration workspace now also uses the dotnet_cmake_module.
I implemented support for msbuild projects. That means the user has almost the same workflow like developing a standard .net application (Code and debug in visual studio or monodevelop) . It also means that I don’t need that many different code paths in the cmake part of the build system.
Then I managed to compile ros2 on windows and my testing workspace without any problems. Running some simple testprograms that simply creates a node and spins it seem to work fine now.
In order to solve that I needed to force 64bit builds. (otherwise windows thinks - ohh let’s run this .net application as 32bit program and crashes when I try to call functions the the rcl)
Next I’ll try setting up a simple publish and subscribe example on windows.
@firesurfer you are my hero - thanks for developing a C# sharp library for ROS. Will this be based on .Net Core?
I’m new to ROS and trying to pick up Python but for me, C# is the most beautiful language out there and the IDEs for C# are always great. Having to learn another language has been putting me off ROS for ages. Can’t wait to test drive your binding.
I’m happy to see that someone is interessted in the C# wrapper. But first - I didn’t put any work in this for 2 month or so. It probably won’t work out of the box. Also I couldn’t get it to work on windows the last time I tried.
Regarding .net core. It is based on the normal .net framework but the classes used should also be available in .net core. So it would just be building against .net core instead of normal .net.
For serious developments I recommend using C++ or Python with ROS2. You will have to put more work into this wrapper before it can be used properly.