✨ feat(native-planning): 更新 Rust ICSP 原生库及构建脚本

* 将 Rust ICSP 原生库名称更改为 `flyshot_planning`,更新相关引用。
* 修改构建脚本以支持新的库名称,并确保库文件复制到正确的目录。
* 添加对 Rust ICSP 原生库的依赖检查,确保发布时包含必要的库文件。
* 更新测试项目以加载新的原生库,确保兼容性和功能完整性。
This commit is contained in:
2026-06-26 17:47:04 +08:00
parent 9c56ee825e
commit 32cc740df5
10 changed files with 91 additions and 91 deletions

View File

@@ -17,7 +17,7 @@
- 宿主只保留 ASP.NET Core HTTP 控制器层,以及其后端 `Flyshot.ControllerClientCompat` 兼容服务。
- `ExecuteTrajectory` 与 `ExecuteFlyShotTraj` 已经接入 `Planning + Triggering + Runtime` 链路;Web 状态页已通过 `/status` 和 `/api/status/snapshot` 暴露当前兼容层与运行时状态。
- `Flyshot.Core.Planning` 的 ICSP / self-adapt-icsp 轨迹已经完成旧系统导出轨迹对齐;`doubles` 仍未实现。
- `Flyshot.Native.Planning` 已提供显式 Rust ICSP 规划入口,通过 C ABI 调用 `flyshot_native_planning`;普通轨迹和飞拍轨迹可使用准确参数 `method="icsp_rust"` 选择该后端,默认 `icsp` 仍使用 C# `ICspPlanner`。
- `Flyshot.Native.Planning` 已提供显式 Rust ICSP 规划入口,通过 C ABI 调用 `flyshot_planning`;普通轨迹和飞拍轨迹可使用准确参数 `method="icsp_rust"` 选择该后端,默认 `icsp` 仍使用 C# `ICspPlanner`。
- `Flyshot.Runtime.Fanuc` 已固化 `10010 / 10012 / 60015` 基础协议帧编解码。`10010` 状态通道以 `j519 协议.pcap` 和 `Rvbust/uttc-20260428/20260428.pcap` 真机抓包确认为 90B 固定帧。
- 2026-04-28 UTTC 抓包确认:UDP 60015 命令 `target[0..5]` 为关节角度制 `deg`,`JointDetialTraj.txt` 为弧度制 `rad`,`speed_ratio=0.7` 体现为 UDP 下发时间轴约 `1.427730x` 拉伸;2026-04-30 实体机确认 `speed_ratio` 不影响生成的 `JointTraj.txt` 规划时长,当前实际生成约 `7.4s` 轨迹。
- 2026-04-30 本机 `50001/TCP+JSON` 抓包确认:`ExecuteFlyShotTraj(save_traj=true,use_cache=false)` 请求只显式携带规划方法、保存、缓存和等待参数,不携带 `JointLimits / acc_limit / jerk_limit / velocity / acceleration / jerk`。因此旧系统不可见的有效规划限制不再继续假设来自公开链路,新系统按 replacement-only 内部参数限制规划加速度。
@@ -96,13 +96,15 @@ Windows x64 需要安装:
.\build\Copy-NativePlanningArtifacts.ps1 -Configuration Release
```
脚本会通过 `vcvars64.bat` 初始化 MSVC 环境,构建
`native/flyshot-native-planning/target/release/flyshot_native_planning.dll`,
并默认复制到 `tests/Flyshot.Core.Tests/bin/Release/net8.0/`。发布服务时可通过
脚本会执行 `cargo build --release`,构建
`native/flyshot-native-planning/target/release/flyshot_planning.dll`,
并默认复制到 `src/Libs/flyshot_planning.dll` 与
`tests/Flyshot.Core.Tests/bin/Release/net8.0/`。发布服务时可通过
`-DestinationDirectories` 把原生库复制到实际发布目录。
Linux x64 在已安装 `stable-x86_64-unknown-linux-gnu` 工具链的 Linux 环境执行同一脚本,
产物为 `libflyshot_native_planning.so`。Windows 第一阶段不要求交叉编译 Linux 原生库。
产物为 `libflyshot_planning.so`,默认写入 `src/Libs/libflyshot_planning.so`。
Windows 第一阶段不要求交叉编译 Linux 原生库。
剩余 Todo:

View File

@@ -1,94 +1,76 @@
<#
.SYNOPSIS
构建 Rust ICSP 原生规划库并复制到 src/Libs 与可选目标目录。
.DESCRIPTION
在 Windows x64 上通过 cargo release 构建 flyshot_planning,默认写入
src/Libs/flyshot_planning.dll;Linux x64 写入 src/Libs/libflyshot_planning.so。
可通过 -DestinationDirectories 额外复制到测试或发布输出目录。
.PARAMETER Configuration
传递给 dotnet 相关路径的构建配置名,默认 Release。
.PARAMETER DestinationDirectories
除 src/Libs 外需要覆盖复制的额外目标目录列表。
#>
[CmdletBinding()]
param(
[ValidateSet("Debug", "Release")]
[string]$Configuration = "Release",
[ValidateSet('Debug', 'Release')]
[string]$Configuration = 'Release',
[string[]]$DestinationDirectories = @()
)
$ErrorActionPreference = "Stop"
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
# 从脚本位置定位仓库根目录,确保从任意工作目录调用都得到一致结果。
$repositoryRoot = Split-Path -Parent $PSScriptRoot
$manifestPath = Join-Path $repositoryRoot "native/flyshot-native-planning/Cargo.toml"
$cargoExecutable = if ($IsWindows) { "cargo.exe" } else { "cargo" }
$cargoPath = Join-Path $HOME ".cargo/bin/$cargoExecutable"
if (-not (Test-Path -LiteralPath $cargoPath)) {
throw "未找到 Rust Cargo:$cargoPath。请先通过 rustup 安装 stable-x86_64-pc-windows-msvc。"
$repositoryRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
$cargoManifest = Join-Path $repositoryRoot 'native\flyshot-native-planning\Cargo.toml'
$libsDirectory = Join-Path $repositoryRoot 'src\Libs'
$cargoProfile = if ($Configuration -eq 'Debug') { 'debug' } else { 'release' }
$targetDirectory = Join-Path $repositoryRoot "native\flyshot-native-planning\target\$cargoProfile"
if ($IsLinux -or (-not $IsWindows -and -not $IsMacOS))
{
$artifactFileName = 'libflyshot_planning.so'
}
else
{
$artifactFileName = 'flyshot_planning.dll'
}
$cargoArguments = @("build", "--manifest-path", $manifestPath)
if ($Configuration -eq "Release") {
$cargoArguments += "--release"
$builtArtifactPath = Join-Path $targetDirectory $artifactFileName
$libsArtifactPath = Join-Path $libsDirectory $artifactFileName
Write-Host "正在构建 Rust 原生规划库 ($Configuration) ..."
& cargo build --manifest-path $cargoManifest --$Configuration
if ($LASTEXITCODE -ne 0)
{
throw "cargo build 失败,退出码:$LASTEXITCODE"
}
if ($IsWindows) {
# Windows MSVC 目标必须在 VS C++ 开发者环境中运行,避免 PATH 中其他 link.exe 抢占。
$vswherePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
if (-not (Test-Path -LiteralPath $vswherePath)) {
throw "未找到 vswhere.exe,无法定位 Visual Studio C++ Build Tools。"
if (-not (Test-Path -LiteralPath $builtArtifactPath -PathType Leaf))
{
throw "未找到构建产物:$builtArtifactPath"
}
New-Item -ItemType Directory -Path $libsDirectory -Force | Out-Null
Copy-Item -LiteralPath $builtArtifactPath -Destination $libsArtifactPath -Force
Write-Host "已复制到:$libsArtifactPath"
$defaultTestOutput = Join-Path $repositoryRoot "tests\Flyshot.Core.Tests\bin\$Configuration\net8.0"
$allDestinations = @($DestinationDirectories) + @($defaultTestOutput)
foreach ($destination in $allDestinations | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique)
{
$absoluteDestination = [System.IO.Path]::GetFullPath($destination)
if (-not (Test-Path -LiteralPath $absoluteDestination -PathType Container))
{
Write-Host "跳过不存在的目标目录:$absoluteDestination"
continue
}
$installationPath = & $vswherePath -latest -products * `
-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
-property installationPath
if ([string]::IsNullOrWhiteSpace($installationPath)) {
throw "未找到已安装的 Visual Studio C++ x64 编译工具。"
}
$vcvarsPath = Join-Path $installationPath "VC/Auxiliary/Build/vcvars64.bat"
if (-not (Test-Path -LiteralPath $vcvarsPath)) {
throw "未找到 VS x64 开发者环境脚本:$vcvarsPath"
}
$quotedArguments = $cargoArguments | ForEach-Object { '"' + $_.Replace('"', '\"') + '"' }
$command = 'call "{0}" >nul && "{1}" {2}' -f `
$vcvarsPath, $cargoPath, ($quotedArguments -join " ")
& cmd.exe /d /c $command
if ($LASTEXITCODE -ne 0) {
throw "Rust 原生规划库构建失败,退出码:$LASTEXITCODE"
}
}
else {
& $cargoPath @cargoArguments
if ($LASTEXITCODE -ne 0) {
throw "Rust 原生规划库构建失败,退出码:$LASTEXITCODE"
}
}
$profileDirectory = if ($Configuration -eq "Release") { "release" } else { "debug" }
$libraryName = if ($IsWindows) {
"flyshot_native_planning.dll"
}
elseif ($IsLinux) {
"libflyshot_native_planning.so"
}
else {
throw "当前脚本仅支持 Windows x64 和 Linux x64。"
}
$sourcePath = Join-Path $repositoryRoot "native/flyshot-native-planning/target/$profileDirectory/$libraryName"
if (-not (Test-Path -LiteralPath $sourcePath)) {
throw "Rust 构建已结束,但未找到原生库:$sourcePath"
}
# 未显式指定目标时,默认复制到对应配置的 Core 测试输出目录。
if ($DestinationDirectories.Count -eq 0) {
$DestinationDirectories = @(
(Join-Path $repositoryRoot "tests/Flyshot.Core.Tests/bin/$Configuration/net8.0")
)
}
foreach ($destinationDirectory in $DestinationDirectories) {
$destinationPath = if ([System.IO.Path]::IsPathRooted($destinationDirectory)) {
$destinationDirectory
}
else {
Join-Path $repositoryRoot $destinationDirectory
}
$resolvedDestination = [System.IO.Path]::GetFullPath(
$destinationPath)
New-Item -ItemType Directory -Force -Path $resolvedDestination | Out-Null
Copy-Item -LiteralPath $sourcePath `
-Destination (Join-Path $resolvedDestination $libraryName) `
-Force
Write-Host "已复制原生规划库到:$resolvedDestination"
Copy-Item -LiteralPath $builtArtifactPath -Destination (Join-Path $absoluteDestination $artifactFileName) -Force
Write-Host "已复制到:$absoluteDestination\$artifactFileName"
}

View File

@@ -3,5 +3,5 @@
version = 4
[[package]]
name = "flyshot-native-planning"
name = "flyshot_planning"
version = "0.1.0"

View File

@@ -1,5 +1,5 @@
[package]
name = "flyshot-native-planning"
name = "flyshot_planning"
version = "0.1.0"
edition = "2021"

View File

@@ -427,6 +427,13 @@ function Install-ObfuscatedAssemblies
$_.Name -like 'Flyshot*.pdb'
} |
Remove-Item -Force
# Rust ICSP 原生库必须随框架依赖包一起交付,否则 method=icsp_rust 会在运行时加载失败。
$nativePlanningLibraryPath = Join-Path $PublishedDirectory 'flyshot_planning.dll'
if (-not (Test-Path -LiteralPath $nativePlanningLibraryPath -PathType Leaf))
{
throw "发布目录缺少 Rust 原生规划库 flyshot_planning.dll:$nativePlanningLibraryPath"
}
}
<#

View File

@@ -17,7 +17,7 @@ public sealed class NativePlanningLibraryResolver
/// <summary>
/// 原生规划库的逻辑名称。
/// </summary>
public const string LogicalLibraryName = "flyshot_native_planning";
public const string LogicalLibraryName = "flyshot_planning";
private readonly Func<NativePlanningPlatform, string> _resolveLibraryPath;
@@ -92,8 +92,8 @@ public sealed class NativePlanningLibraryResolver
string fileName = platform.OperatingSystem switch
{
"Windows" => "flyshot_native_planning.dll",
"Linux" => "libflyshot_native_planning.so",
"Windows" => "flyshot_planning.dll",
"Linux" => "libflyshot_planning.so",
_ => throw new NativePlanningException(
$"Rust 原生规划库不支持当前操作系统:{platform.OperatingSystem}。",
errorCode: -1003)

View File

@@ -16,6 +16,9 @@
<Content Include="..\..\NLog.config" Link="NLog.config" CopyToOutputDirectory="PreserveNewest" />
<!-- 运行时配置根目录固定为输出目录 Config,调试和发布都复制仓库内固化配置。 -->
<Content Include="..\..\Config\**\*" Link="Config\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
<!-- Rust ICSP 原生规划库:从 src/Libs 固定复制到运行与发布目录,供 method=icsp_rust 加载。 -->
<Content Include="..\Libs\flyshot_planning.dll" Link="flyshot_planning.dll" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" Condition="Exists('..\Libs\flyshot_planning.dll')" />
<Content Include="..\Libs\libflyshot_planning.so" Link="libflyshot_planning.so" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" Condition="Exists('..\Libs\libflyshot_planning.so')" />
</ItemGroup>
<ItemGroup>

Binary file not shown.

Binary file not shown.

View File

@@ -32,4 +32,10 @@
<ProjectReference Include="..\..\src\Flyshot.Runtime.Fanuc\Flyshot.Runtime.Fanuc.csproj" />
</ItemGroup>
<ItemGroup>
<!-- Rust ICSP 原生规划库:测试运行时需要与托管程序集同目录加载。 -->
<Content Include="..\..\src\Libs\flyshot_planning.dll" Link="flyshot_planning.dll" CopyToOutputDirectory="PreserveNewest" Condition="Exists('..\..\src\Libs\flyshot_planning.dll')" />
<Content Include="..\..\src\Libs\libflyshot_planning.so" Link="libflyshot_planning.so" CopyToOutputDirectory="PreserveNewest" Condition="Exists('..\..\src\Libs\libflyshot_planning.so')" />
</ItemGroup>
</Project>