Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions recipes/ros2_csharp/activate.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@echo off
set AMENT_PREFIX_PATH_BACKUP=%AMENT_PREFIX_PATH%
set AMENT_PREFIX_PATH=%CONDA_PREFIX%\Library
set PATH=%CONDA_PREFIX%\Library\bin;%PATH%
set CMAKE_PREFIX_PATH=%CONDA_PREFIX%\Library;%CMAKE_PREFIX_PATH%
set PYTHONPATH=%CONDA_PREFIX%\Lib\site-packages;%PYTHONPATH%
6 changes: 6 additions & 0 deletions recipes/ros2_csharp/deactivate.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@echo off
set AMENT_PREFIX_PATH=%AMENT_PREFIX_PATH_BACKUP%
set AMENT_PREFIX_PATH_BACKUP=
set PATH=%PATH:%CONDA_PREFIX%\Library\bin;=%
set CMAKE_PREFIX_PATH=%CMAKE_PREFIX_PATH:%CONDA_PREFIX%\Library;=%
set PYTHONPATH=%PYTHONPATH:%CONDA_PREFIX%\Lib\site-packages;=%
42 changes: 42 additions & 0 deletions recipes/ros2_csharp/meta.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package:
name: ros2_csharp
version: 0.0.1

source:
path: {{ 你的安装打包目录 }}

build:
number: 0
script:
# 创建基本目录
- mkdir %PREFIX%\Library\bin 2>nul
- mkdir %PREFIX%\Library\include 2>nul
- mkdir %PREFIX%\Library\lib 2>nul
- mkdir %PREFIX%\Library\share 2>nul
# 复制文件
- xcopy /E /I /Y %SRC_DIR%\bin %PREFIX%\Library\bin
- xcopy /E /I /Y %SRC_DIR%\include %PREFIX%\Library\include
- xcopy /E /I /Y %SRC_DIR%\lib %PREFIX%\Library\lib
- xcopy /E /I /Y %SRC_DIR%\share %PREFIX%\Library\share

# 关键:注册ROS 2包
- mkdir %PREFIX%\Library\share\ament_index\resource_index\packages 2>nul
- copy %RECIPE_DIR%\ros_driver %PREFIX%\Library\share\ament_index\resource_index\packages\ros_driver

# 设置环境变量
- mkdir %PREFIX%\etc\conda\activate.d 2>nul
- mkdir %PREFIX%\etc\conda\deactivate.d 2>nul
# activate脚本
- copy %RECIPE_DIR%\activate.bat %PREFIX%\etc\conda\activate.d\ros_csharp.bat
- copy %RECIPE_DIR%\deactivate.bat %PREFIX%\etc\conda\deactivate.d\ros_csharp.bat


requirements:
host:
run:

about:
home: https://www.ros.org/
license: BSD-3-Clause
summary: |
Robot Operating System
Empty file added recipes/ros2_csharp/ros_driver
Empty file.
38 changes: 38 additions & 0 deletions ros2_csharp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## ros2 C# 开发指南

## 背景
该项目是一个示例项目,帮您快速的配置 C# 开发环境。具体硬件设备驱动需要您自行编写。


## C# 开发环境配置
### 1. 安装 dotnet 环境
dotnet [下载地址](https://dotnet.microsoft.com/zh-cn/download)
安装完成后执行一下命令,如果安装成功会有如下展示。
```
dotnet --version
8.0.406
```

### 2. ros2-humble 安装
请参照官方[安装文档](https://docs.ros.org/en/humble/Installation/Windows-Install-Binary.html)

### 3. 开发配置
1. ros2_dotnet 环境配置请参阅[配置文档](https://github.com/ros2-dotnet/ros2_dotnet/blob/main/README.md)。
2. 参照项目下的 ros_driver 创建您自己的项目。
3. 编写相应的代码。
4. 构建流程
```
打开 windows 搜索 Developer Command Prompt 启动 visual studio
cd {本地ros2-humble} 目录
call ./local_setup.bat
cd {Uni-Lab-OS 项目目录}/ros_csharp
vcs import ./src < ros2_humble.repos
colcon build --merge-install --packages-up-to {your package name} --event-handlers console_cohesion+
```
5. 拷贝编译完成的包到conda 对应的虚拟环境目录下。
6. 执行如下命令运行测试

```
参照 ros_driver 项目,需要配置 CONFIG_FILE_PATH 环境变量,对应的值是 {Uni-Lab-OS 路径}/unilabos/registry/devices 下的 yaml 文件路径。
ros2 run {项目包名} {可执行程序名}
```
33 changes: 33 additions & 0 deletions ros2_csharp/ros2_humble.repos
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
repositories:
ament_dotnet/ament_cmake_export_assemblies:
type: git
url: https://github.com/ros2-dotnet/ament_cmake_export_assemblies.git
version: main
ros2/common_interfaces:
type: git
url: https://github.com/ros2/common_interfaces.git
version: 4.2.4
ros2/rcl_interfaces:
type: git
url: https://github.com/ros2/rcl_interfaces.git
version: 1.2.1
ros2/rosidl_defaults:
type: git
url: https://github.com/ros2/rosidl_defaults.git
version: 1.2.0
ros2/test_interface_files:
type: git
url: https://github.com/ros2/test_interface_files.git
version: humble
ros2/unique_identifier_msgs:
type: git
url: https://github.com/ros2/unique_identifier_msgs.git
version: 2.2.1
ros2_dotnet/dotnet_cmake_module:
type: git
url: https://github.com/ros2-dotnet/dotnet_cmake_module.git
version: main
ros2_dotnet/ros2_dotnet:
type: git
url: https://github.com/ros2-dotnet/ros2_dotnet.git
version: main
65 changes: 65 additions & 0 deletions ros2_csharp/src/ros_driver/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
cmake_minimum_required(VERSION 3.5)

project(ros_driver C)

find_package(ament_cmake REQUIRED)
find_package(rcldotnet REQUIRED)

find_package(rcldotnet_common REQUIRED)

find_package(builtin_interfaces REQUIRED)
find_package(std_msgs REQUIRED)
find_package(std_srvs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(test_msgs REQUIRED)
find_package(action_msgs REQUIRED) # Add this line
find_package(rcldotnet REQUIRED)
find_package(ilabos_msgs REQUIRED)



find_package(dotnet_cmake_module REQUIRED)

set(CSHARP_TARGET_FRAMEWORK "netcoreapp6.0")
find_package(DotNETExtra REQUIRED)


# 使用相对路径定位 YamlDotNet.dll
set(YAMLDOTNET_DLL "${CMAKE_CURRENT_SOURCE_DIR}/deps/YamlDotNet/YamlDotNet.dll")

# 检查文件是否存在
if(NOT EXISTS ${YAMLDOTNET_DLL})
message(FATAL_ERROR "YamlDotNet.dll not found at: ${YAMLDOTNET_DLL}")
endif()


set(_assemblies_dep_dlls
${rcldotnet_common_ASSEMBLIES_DLL}
${rcldotnet_ASSEMBLIES_DLL}
${builtin_interfaces_ASSEMBLIES_DLL}
${std_msgs_ASSEMBLIES_DLL}
${std_srvs_ASSEMBLIES_DLL}
${geometry_msgs_ASSEMBLIES_DLL}
${test_msgs_ASSEMBLIES_DLL}
${action_msgs_ASSEMBLIES_DLL}
${ilabos_msgs_ASSEMBLIES_DLL}
${YAMLDOTNET_DLL}
)

add_dotnet_executable(ros_driver
Program.cs
DeviceNode.cs
DeviceActionServer.cs
PropertyPublisher.cs
MockGripper.cs
utils/Yaml.cs
utils/MessageConverter.cs
INCLUDE_DLLS
${_assemblies_dep_dlls}
)


install_dotnet(ros_driver DESTINATION lib/${PROJECT_NAME}/dotnet)


ament_package()
133 changes: 133 additions & 0 deletions ros2_csharp/src/ros_driver/DeviceActionServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using ROS2;
using test_msgs.action;
using System.Collections.Generic;
using ilabos_msgs.action;
using RosDriver.Utils;

namespace ros_driver
{
/// <summary>
/// Generic action server that can be used with any ROS2 action type
/// </summary>
public class DeviceActionServer<TAction, TGoal, TResult, TFeedback> : IDisposable
where TAction : IRosActionDefinition<TGoal, TResult, TFeedback>
where TGoal : IRosMessage, new()
where TResult : IRosMessage, new()
where TFeedback : IRosMessage, new()
{
private readonly Node _node;
private readonly string _actionName;
private bool _disposed = false;
private ActionValueMapping _actionValue = null;
private readonly object _device = null;


public DeviceActionServer(
Node node,
object device,
string actionName,
ActionValueMapping actionValue)
{
_node = node;
_actionName = actionName;
_actionValue = actionValue;
_device = device;

_node.CreateActionServer<TAction, TGoal, TResult, TFeedback>(
_actionName,
HandleAccepted,
cancelCallback: HandleCancel);

Console.WriteLine($"Created Action Server: {_actionName}");
}


private void HandleAccepted(ActionServerGoalHandle<TAction, TGoal, TResult, TFeedback> goalHandle)
{
// Don't block in the callback.
// -> Don't wait for the returned Task.
_ = DoWorkWithGoal(goalHandle);
}

private CancelResponse HandleCancel(ActionServerGoalHandle<TAction, TGoal, TResult, TFeedback> goalHandle)
{
return CancelResponse.Accept;
}

private async Task DoWorkWithGoal(ActionServerGoalHandle<TAction, TGoal, TResult, TFeedback> goalHandle)
{

Console.WriteLine("Executing goal...");
// 检查 TAction 是否是 Fibonacci 类型
if (typeof(TAction) == typeof(Fibonacci))
{
Console.WriteLine("确认 TAction 是 Fibonacci 类型");
// 注意:这种转换是不安全的,因为泛型参数在编译时已确定
// 这里仅作为示例,实际使用时需要谨慎
var fibonacciGoalHandle = goalHandle as ActionServerGoalHandle<Fibonacci, Fibonacci_Goal, Fibonacci_Result, Fibonacci_Feedback>;
if (fibonacciGoalHandle != null)
{
Console.WriteLine("成功转换为 Fibonacci 类型的 GoalHandle");
var feedback = new Fibonacci_Feedback();

feedback.Sequence = new List<int> { 0, 1 };

for (int i = 1; i < fibonacciGoalHandle.Goal.Order; i++)
{
if (fibonacciGoalHandle.IsCanceling)
{
var cancelResult = new Fibonacci_Result();
cancelResult.Sequence = feedback.Sequence;

Console.WriteLine($"Canceled Result: {string.Join(", ", cancelResult.Sequence)}");
fibonacciGoalHandle.Canceled(cancelResult);
return;
}

feedback.Sequence.Add(feedback.Sequence[i] + feedback.Sequence[i - 1]);

Console.WriteLine($"Feedback: {string.Join(", ", feedback.Sequence)}");
fibonacciGoalHandle.PublishFeedback(feedback);

// NOTE: This causes the code to resume in an background worker Thread.
// Consider this when copying code from the example if additional synchronization is needed.
await Task.Delay(1000);
}

var result = new Fibonacci_Result();
result.Sequence = feedback.Sequence;

Console.WriteLine($"Result: {string.Join(", ", result.Sequence)}");
fibonacciGoalHandle.Succeed(result);
}
}
else
{
Console.WriteLine($"TAction 不是 Fibonacci 类型,而是 {typeof(TAction).Name}");
return;
}
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// Release managed resources if any
}

_disposed = true;
}
}
}
}
Loading