Skip to content

Commit

Permalink
Added support for subscribers. Added support for Linux and Windows (D…
Browse files Browse the repository at this point in the history
…esktop and and UWP)
  • Loading branch information
esteve committed Apr 12, 2018
1 parent 31074ee commit 30a05b3
Show file tree
Hide file tree
Showing 56 changed files with 1,809 additions and 1,221 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
[Xx]86/
[Bb]uild/
bld/
[Bb]in/
#[Bb]in/
[Oo]bj/

# Visual Studio 2015 cache/options directory
Expand Down
5 changes: 5 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ros2_rust
Copyright 2018 Esteve Fernandez ([email protected])

This product includes software developed by
Esteve Fernandez ([email protected])
75 changes: 60 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,65 @@
Warning!
========
ROS2 for .NET
=============

This is far from complete, if you want fully functional bindings .NET for ROS2, check out @firesurfer 's excellent [rclcs](https://github.com/firesurfer/rclcs).
Introduction
------------

What works so far
-----------------
This is a collection of projects (bindings, code generator, examples and more) for writing ROS2
applications for .NET Core and .NET Standard 2.0

- Generation of non-nested messages
- Support for Windows (tested on Windows 10 and Visual Studio Community)
- Publishers (see https://github.com/esteve/ros2_dotnet/blob/devel/rcldotnet_examples/Program.cs)
Features
--------

TODO
----
The current set of features include:
- Generation of all builtin ROS types
- Support for publishers and subscriptions
- Cross-platform support (Linux, Windows, Windows IoT Core, UWP)

- The first version of this had support for .NET Core on Linux, but when I added support for Windows, I most certainly broke it.
- Nested messages (i.e. custom messages containing other custom messages)
- Subscriptions
- Clients
- Services
What's missing?
---------------

Lots of things!
- Nested types
- Component nodes
- Clients and services
- Tests
- Documentation
- More examples (e.g. IoT, VB, UWP, HoloLens, etc.)

Sounds great, how can I try this out?
-------------------------------------

The following steps show how to build the examples:

```
mkdir -p ~/ros2_dotnet_ws/src
cd ~/ros2_dotnet_ws
wget https://raw.githubusercontent.com/esteve/ros2_dotnet/master/ros2_dotnet.repos
vcs import ~/ros2_dotnet_ws/src < ros2_dotnet.repos
cd ~/ros2_dotnet_ws/src/ros2/rosidl_typesupport
patch -p1 < ../../ros2_dotnet/ros2_dotnet/rosidl_typesupport_ros2_dotnet.patch
cd ~/ros2_dotnet_ws
src/ament/ament_tools/scripts/ament.py build --isolated
```

Now you can just run a bunch of examples.

### Publisher and subscriber

Publisher:

```
. ~/ros2_dotnet_ws/install_isolated/local_setup.sh
ros2 run rcldotnet_examples rcldotnet_talker
```

Subscriber:

```
. ~/ros2_dotnet_ws/install_isolated/local_setup.sh
ros2 run rcldotnet_examples rcldotnet_listener
```

Enjoy!
120 changes: 63 additions & 57 deletions rcldotnet/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,78 +6,84 @@ find_package(ament_cmake_export_assemblies REQUIRED)

find_package(ament_cmake REQUIRED)
find_package(rcl REQUIRED)
find_package(rcl_interfaces REQUIRED)
find_package(rmw REQUIRED)
find_package(rmw_implementation REQUIRED)
find_package(rmw_implementation_cmake REQUIRED)
find_package(rosidl_generator_c REQUIRED)

find_package(dotnet_cmake_module REQUIRED)
find_package(DotNETExtra MODULE)
find_package(DotNETExtra REQUIRED)

find_package(ros2_dotnet_utils REQUIRED)
find_package(rcldotnet_common REQUIRED)

if(NOT WIN32)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-undefined,error")
endif()
endif()

set(CS_SOURCES
INode.cs
IPublisher.cs
ISubscription.cs
Node.cs
Publisher.cs
RCLdotnet.cs
Subscription.cs
)

add_assemblies("${PROJECT_NAME}_assemblies"
find_package(rcldotnet_common REQUIRED)
foreach(_assembly_dep ${rcldotnet_common_ASSEMBLIES_DLL})
list(APPEND _assembly_deps_dll "${_assembly_dep}")
endforeach()

add_dotnet_library(${PROJECT_NAME}_assemblies
SOURCES
${CS_SOURCES}
OUTPUT_NAME
"${PROJECT_NAME}"
INCLUDE_ASSEMBLIES_DLL
${ros2_dotnet_utils_ASSEMBLIES_DLL}
INCLUDE_ASSEMBLIES_NUGET
${ros2_dotnet_utils_ASSEMBLIES_NUGET}
INCLUDE_DLLS
${_assembly_deps_dll}
)

install_assemblies("${PROJECT_NAME}_assemblies" "share/${PROJECT_NAME}/dotnet")
ament_export_assemblies_dll("share/${PROJECT_NAME}/dotnet/${PROJECT_NAME}.dll")
ament_export_assemblies_nuget("share/${PROJECT_NAME}/dotnet/${PROJECT_NAME}.1.0.0.nupkg")

macro(target)
if(NOT target_suffix STREQUAL "")
get_rcl_information("${rmw_implementation}" "rcl${target_suffix}")
endif()
# Only build the library if a C typesupport exists for the rmw_implementation
get_rmw_typesupport(typesupport_impls "${rmw_implementation}" LANGUAGE "c")
if(typesupport_impls STREQUAL "")
message(STATUS "Skipping rcldotnet for '${rmw_implementation}' because no C "
"typesupport library was found.")
return()
endif()

message("Building native library for ${target_suffix}")

set(_native_sources "rcldotnet;rcldotnet_node;rcldotnet_publisher")

foreach(_native_source ${_native_sources})

set(_target_name "${_native_source}${target_suffix}")

add_library(
${_target_name}
SHARED ${_native_source}.c
)
target_compile_definitions(${_target_name}
PRIVATE "RMW_IMPLEMENTATION_SUFFIX=${target_suffix}")

ament_target_dependencies(${_target_name}
"rcl${target_suffix}"
)

install(TARGETS ${_target_name}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)

# ament_export_libraries(${_target_name})

endforeach()

endmacro()

call_for_each_rmw_implementation(target)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/netcoreapp2.0/publish/${PROJECT_NAME}_assemblies.dll
DESTINATION lib/${PROJECT_NAME}/dotnet/)
ament_export_assemblies_dll("lib/${PROJECT_NAME}/dotnet/${PROJECT_NAME}_assemblies.dll")

set(_native_sources "rcldotnet;rcldotnet_node;rcldotnet_publisher")

foreach(_target_name ${_native_sources})
add_library(${_target_name} SHARED
${_target_name}.c
)
set_target_properties(${_target_name}
PROPERTIES
OUTPUT_NAME ${_target_name}_native)
ament_target_dependencies(${_target_name}
"builtin_interfaces"
"rcl"
"rosidl_generator_c"
"rosidl_typesupport_c"
)
ament_export_libraries(${_target_name}_native)

install(TARGETS ${_target_name}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
endforeach()

ament_export_dependencies(ament_cmake)
ament_export_dependencies(builtin_interfaces)
ament_export_dependencies(rcl)
ament_export_dependencies(rosidl_generator_c)
ament_export_dependencies(rosidl_typesupport_c)

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()

ament_package()
5 changes: 5 additions & 0 deletions rcldotnet/INode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using ROS2.Interfaces;

namespace ROS2 {
public interface INode : IDisposable { }
}
5 changes: 5 additions & 0 deletions rcldotnet/IPublisher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using ROS2.Interfaces;

namespace ROS2 {
public interface IPublisher : IDisposable { }
}
8 changes: 8 additions & 0 deletions rcldotnet/ISubscription.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using ROS2.Interfaces;

namespace ROS2 {
public interface ISubscription : IDisposable {
IMessage CreateMessage();
void CallbackFN(IMessage message);
}
}
116 changes: 73 additions & 43 deletions rcldotnet/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,63 +15,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Reflection;

using ROS2dotnetUtils;
using ROS2.Common;
using ROS2.Interfaces;
using ROS2.Utils;

namespace ROS2 {
internal class NodeDelegates {
private static readonly DllLoadUtils dllLoadUtils;

[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
internal delegate int NativeRCLCreatePublisherHandleType (
ref IntPtr publisherHandle, IntPtr node_handle, [MarshalAs (UnmanagedType.LPStr)] string node_name, IntPtr typesupport_ptr);

internal static NativeRCLCreatePublisherHandleType native_rcl_create_publisher_handle = null;

[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
internal delegate int NativeRCLCreateSubscriptionHandleType (
ref IntPtr subscriptionHandle, IntPtr node_handle, [MarshalAs (UnmanagedType.LPStr)] string node_name, IntPtr typesupport_ptr);

namespace rcldotnet
{
public class Node
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr NativeRCLCreatePublisherHandleType(IntPtr node_handle, [MarshalAs(UnmanagedType.LPStr)] string lpString, IntPtr typesupport_ptr);
internal static NativeRCLCreateSubscriptionHandleType native_rcl_create_subscription_handle = null;

private static NativeRCLCreatePublisherHandleType native_rcl_create_publisher_handle = null;
static NodeDelegates () {
dllLoadUtils = DllLoadUtilsFactory.GetDllLoadUtils ();
try {
IntPtr nativelibrary = dllLoadUtils.LoadLibrary ("rcldotnet_node");

private static readonly DllLoadUtils dllLoadUtils;
IntPtr native_rcl_create_publisher_handle_ptr = dllLoadUtils.GetProcAddress (
nativelibrary, "native_rcl_create_publisher_handle");

static Node()
{
dllLoadUtils = DllLoadUtilsFactory.GetDllLoadUtils();
try {
IntPtr nativelibrary = dllLoadUtils.LoadLibrary("rcldotnet_node__" + RCLdotnet.GetRMWIdentifier());
NodeDelegates.native_rcl_create_publisher_handle =
(NativeRCLCreatePublisherHandleType) Marshal.GetDelegateForFunctionPointer (
native_rcl_create_publisher_handle_ptr, typeof (NativeRCLCreatePublisherHandleType));

IntPtr native_rcl_create_publisher_handle_ptr = dllLoadUtils.GetProcAddress(nativelibrary, "native_rcl_create_publisher_handle");
IntPtr native_rcl_create_subscription_handle_ptr = dllLoadUtils.GetProcAddress (
nativelibrary, "native_rcl_create_subscription_handle");

Node.native_rcl_create_publisher_handle = Marshal.GetDelegateForFunctionPointer<NativeRCLCreatePublisherHandleType>(native_rcl_create_publisher_handle_ptr);
} catch (UnsatisfiedLinkError e) {
System.Console.WriteLine("Native code library failed to load.\n" + e);
Environment.Exit(1);
NodeDelegates.native_rcl_create_subscription_handle =
(NativeRCLCreateSubscriptionHandleType) Marshal.GetDelegateForFunctionPointer (
native_rcl_create_subscription_handle_ptr, typeof (NativeRCLCreateSubscriptionHandleType));
} catch (UnsatisfiedLinkError e) {
System.Console.WriteLine ("Native code library failed to load.\n" + e);
Environment.Exit (1);
}
}
}

private IntPtr node_handle_;
public class Node : INode {

public Node(IntPtr node_handle)
{
node_handle_ = node_handle;
}
private IList<ISubscription> subscriptions_;

public Publisher<T> CreatePublisher<T>(string topic) where T : IMessage
{
Type typeParametertype = typeof(T);
MethodInfo m = typeParametertype.GetMethod("getTypeSupport");
private IntPtr nodeHandle_;

IntPtr typesupport = (IntPtr)m.Invoke(null, new object[] {});
IntPtr publisher_handle = native_rcl_create_publisher_handle(node_handle_, topic, typesupport);
Publisher<T> publisher = new Publisher<T>(node_handle_, publisher_handle);
return publisher;
}
public Node (IntPtr node_handle) {
nodeHandle_ = node_handle;
subscriptions_ = new List<ISubscription> ();
}

public IList<ISubscription> Subscriptions { get { return subscriptions_; } }

public IntPtr Handle { get { return nodeHandle_; } }

public Subscription<T> CreateSubscription<T>(string topic)
{
Type typeParametertype = typeof(T);
Subscription<T> subscription = new Subscription<T>(node_handle_);
return subscription;
public Publisher<T> CreatePublisher<T> (string topic) where T : IMessage {
Type typeParametertype = typeof (T);
MethodInfo m = typeParametertype.GetMethod ("_GET_TYPE_SUPPORT");

IntPtr typesupport = (IntPtr) m.Invoke (null, new object[] { });
IntPtr publisherHandle = IntPtr.Zero;
RCLRet ret = (RCLRet) NodeDelegates.native_rcl_create_publisher_handle (ref publisherHandle, nodeHandle_, topic, typesupport);
Publisher<T> publisher = new Publisher<T> (publisherHandle);
return publisher;
}

public Subscription<T> CreateSubscription<T> (string topic, Action<T> callback) where T : IMessage, new () {
Type typeParametertype = typeof (T);
MethodInfo m = typeParametertype.GetMethod ("_GET_TYPE_SUPPORT");

IntPtr typesupport = (IntPtr) m.Invoke (null, new object[] { });
IntPtr subscriptionHandle = IntPtr.Zero;
RCLRet ret = (RCLRet) NodeDelegates.native_rcl_create_subscription_handle (ref subscriptionHandle, nodeHandle_, topic, typesupport);
Subscription<T> subscription = new Subscription<T> (subscriptionHandle, callback);
this.subscriptions_.Add (subscription);
return subscription;
}
}
}
}
}
Loading

0 comments on commit 30a05b3

Please sign in to comment.