* 新增 Rust 原生 ICSP 规划库 (Cargo 项目, CubicSpline 算法, FFI 接口) * 新增 Flyshot.Native.Planning C# 互操作项目 (P/Invoke 封装) * 更新 ControllerClientCompat 适配新的规划接口 * 添加构建脚本 (Copy-NativePlanningArtifacts.ps1) * 新增互操作测试及一致性对比测试 * 更新解决方案文件和设计文档
131 lines
5.0 KiB
C#
131 lines
5.0 KiB
C#
using System.Runtime.InteropServices;
|
|
|
|
namespace Flyshot.Native.Planning;
|
|
|
|
/// <summary>
|
|
/// 声明 Rust 原生规划库导出的稳定 C ABI 方法。
|
|
/// </summary>
|
|
internal static partial class NativeIcspMethods
|
|
{
|
|
/// <summary>
|
|
/// 保护原生库句柄首次初始化,避免并发规划重复加载。
|
|
/// </summary>
|
|
private static readonly object LibraryLoadLock = new();
|
|
|
|
/// <summary>
|
|
/// 保存已加载原生库句柄,使 P/Invoke 生命周期覆盖整个进程。
|
|
/// </summary>
|
|
private static nint _libraryHandle;
|
|
|
|
/// <summary>
|
|
/// 保存当前进程已绑定到 P/Invoke 逻辑名称的实际库路径。
|
|
/// </summary>
|
|
private static string? _loadedLibraryPath;
|
|
|
|
/// <summary>
|
|
/// 初始化程序集级 DllImport 解析器,把逻辑库名绑定到显式加载句柄。
|
|
/// </summary>
|
|
static NativeIcspMethods()
|
|
{
|
|
NativeLibrary.SetDllImportResolver(
|
|
typeof(NativeIcspMethods).Assembly,
|
|
ResolveImportedLibrary);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 使用指定解析器确保原生规划库已加载。
|
|
/// </summary>
|
|
/// <param name="resolver">负责生成平台路径并加载原生库的解析器。</param>
|
|
internal static void EnsureLibraryLoaded(NativePlanningLibraryResolver resolver)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(resolver);
|
|
string requestedPath = resolver.ResolveLibraryPath();
|
|
|
|
lock (LibraryLoadLock)
|
|
{
|
|
if (_libraryHandle != nint.Zero)
|
|
{
|
|
StringComparison comparison = OperatingSystem.IsWindows()
|
|
? StringComparison.OrdinalIgnoreCase
|
|
: StringComparison.Ordinal;
|
|
if (!string.Equals(_loadedLibraryPath, requestedPath, comparison))
|
|
{
|
|
throw new NativePlanningException(
|
|
$"Rust 原生规划库已绑定到“{_loadedLibraryPath}”,不能在同一进程中改为“{requestedPath}”。",
|
|
errorCode: -1008);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
_libraryHandle = resolver.LoadLibrary();
|
|
_loadedLibraryPath = requestedPath;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 调用 Rust ICSP 入口并返回错误码和结果指针。
|
|
/// </summary>
|
|
/// <param name="waypoints">行优先连续路点数组。</param>
|
|
/// <param name="waypointCount">路点数量。</param>
|
|
/// <param name="degreesOfFreedom">每个路点的关节自由度。</param>
|
|
/// <param name="velocityLimits">逐关节速度上限。</param>
|
|
/// <param name="accelerationLimits">逐关节加速度上限。</param>
|
|
/// <param name="jerkLimits">逐关节 jerk 上限。</param>
|
|
/// <param name="options">与 Rust C ABI 对齐的规划选项。</param>
|
|
/// <param name="result">接收 Rust 分配的结果指针。</param>
|
|
/// <returns>零表示成功,非零表示固定类别的原生错误。</returns>
|
|
[LibraryImport(
|
|
NativePlanningLibraryResolver.LogicalLibraryName,
|
|
EntryPoint = "flyshot_icsp_plan")]
|
|
internal static unsafe partial int FlyshotIcspPlan(
|
|
double* waypoints,
|
|
int waypointCount,
|
|
int degreesOfFreedom,
|
|
double* velocityLimits,
|
|
double* accelerationLimits,
|
|
double* jerkLimits,
|
|
NativeIcspOptions* options,
|
|
out nint result);
|
|
|
|
/// <summary>
|
|
/// 释放由 Rust 分配的 ICSP 结果对象。
|
|
/// </summary>
|
|
/// <param name="result">Rust 返回的结果对象指针;允许为空指针。</param>
|
|
[LibraryImport(
|
|
NativePlanningLibraryResolver.LogicalLibraryName,
|
|
EntryPoint = "flyshot_icsp_result_free")]
|
|
internal static partial void FlyshotIcspResultFree(nint result);
|
|
|
|
/// <summary>
|
|
/// 获取当前线程最近一次 Rust 调用保存的 UTF-8 错误文本。
|
|
/// </summary>
|
|
/// <returns>指向线程局部 C 字符串的只读指针。</returns>
|
|
[LibraryImport(
|
|
NativePlanningLibraryResolver.LogicalLibraryName,
|
|
EntryPoint = "flyshot_last_error_message")]
|
|
internal static partial nint FlyshotLastErrorMessage();
|
|
|
|
/// <summary>
|
|
/// 将固定逻辑库名解析为已经显式加载的系统句柄。
|
|
/// </summary>
|
|
/// <param name="libraryName">P/Invoke 请求的逻辑库名。</param>
|
|
/// <param name="assembly">发起请求的托管程序集。</param>
|
|
/// <param name="searchPath">调用方提供的可选搜索路径。</param>
|
|
/// <returns>逻辑名称匹配时返回已加载句柄,否则返回零交给系统继续解析。</returns>
|
|
private static nint ResolveImportedLibrary(
|
|
string libraryName,
|
|
System.Reflection.Assembly assembly,
|
|
DllImportSearchPath? searchPath)
|
|
{
|
|
_ = assembly;
|
|
_ = searchPath;
|
|
return string.Equals(
|
|
libraryName,
|
|
NativePlanningLibraryResolver.LogicalLibraryName,
|
|
StringComparison.Ordinal)
|
|
? _libraryHandle
|
|
: nint.Zero;
|
|
}
|
|
}
|