Files
FlyShotHost/docs/superpowers/plans/2026-06-22-rust-icsp-native-implementation.md
yunxiao.zhu bcbf8017ad 📝 docs(planning): 统一 Rust 规划库命名
将 Rust 原生规划库设计口径统一为 flyshot_planning。

同步更新 Windows 和 Linux 产物名以及复制命令示例。
2026-06-27 15:10:06 +08:00

331 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Rust ICSP Native Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 为现有 `ICspPlanner` 增加一套基于 Rust `cdylib` 的原生 ICSP 规划实现,并通过 C ABI + C# P/Invoke 完成可验证的对拍接入,同时不改变当前默认生产路径。
**Architecture:** 新增 `native/flyshot-native-planning` Rust 原生库承载 clamped-zero 三次样条与 ICSP 迭代核心;新增 `src/Flyshot.Native.Planning` 托管互操作项目负责库加载、数组编排、错误转换和结果复制。现有 `Flyshot.Core.Planning.ICspPlanner` 保持不变,`RustIcspPlanner` 仅作为显式新入口并在测试中与托管实现逐项对拍。
**Tech Stack:** Rust `cdylib`、C ABI、.NET 8、`LibraryImport`、`SafeHandle`、xUnit、PowerShell、Cargo
---
### Task 1: 建立原生与托管骨架
**Files:**
- Create: `native/flyshot-native-planning/Cargo.toml`
- Create: `native/flyshot-native-planning/src/lib.rs`
- Create: `native/flyshot-native-planning/src/error.rs`
- Create: `native/flyshot-native-planning/src/ffi.rs`
- Create: `native/flyshot-native-planning/src/icsp.rs`
- Create: `native/flyshot-native-planning/src/spline.rs`
- Create: `src/Flyshot.Native.Planning/Flyshot.Native.Planning.csproj`
- Create: `src/Flyshot.Native.Planning/NativeIcspMethods.cs`
- Create: `src/Flyshot.Native.Planning/NativeIcspSafeHandle.cs`
- Create: `src/Flyshot.Native.Planning/NativePlanningException.cs`
- Create: `src/Flyshot.Native.Planning/NativePlanningLibraryResolver.cs`
- Modify: `FlyshotReplacement.sln`
- Modify: `tests/Flyshot.Core.Tests/Flyshot.Core.Tests.csproj`
- [x] **Step 1: 先写托管侧失败测试,锁定缺少原生库时的报错边界**
```csharp
[Fact]
public void RustIcspPlanner_ThrowsClearError_WhenNativeLibraryIsMissing()
{
var planner = new RustIcspPlanner(
options: NativeIcspPlannerOptions.Default,
libraryResolver: new NativePlanningLibraryResolver(_ => "missing-library"));
var exception = Assert.Throws<NativePlanningException>(() => planner.Plan(CreateSimpleRequest()));
Assert.Contains("missing-library", exception.Message);
}
```
- [x] **Step 2: 运行单测确认当前失败**
Run: `dotnet test tests/Flyshot.Core.Tests/Flyshot.Core.Tests.csproj --filter RustIcspPlanner_ThrowsClearError_WhenNativeLibraryIsMissing -v minimal`
Expected: FAIL,提示 `RustIcspPlanner` 或互操作类型不存在
- [x] **Step 3: 创建 Rust `cdylib` 和托管互操作项目骨架**
```toml
[package]
name = "flyshot_planning"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[profile.release]
lto = true
codegen-units = 1
strip = "symbols"
panic = "abort"
```
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Flyshot.Core.Domain\Flyshot.Core.Domain.csproj" />
<ProjectReference Include="..\Flyshot.Core.Planning\Flyshot.Core.Planning.csproj" />
</ItemGroup>
</Project>
```
- [x] **Step 4: 添加最小库解析与异常包装实现**
```csharp
public sealed class NativePlanningException : Exception
{
public NativePlanningException(string message, int errorCode, Exception? innerException = null)
: base(message, innerException)
{
ErrorCodeValue = errorCode;
}
public int ErrorCodeValue { get; }
}
```
```csharp
public sealed class NativePlanningLibraryResolver
{
private readonly Func<RuntimeInformation, string> _resolveLibraryPath;
}
```
- [x] **Step 5: 重新运行单测,确认错误已变为清晰的原生库缺失异常**
Run: `dotnet test tests/Flyshot.Core.Tests/Flyshot.Core.Tests.csproj --filter RustIcspPlanner_ThrowsClearError_WhenNativeLibraryIsMissing -v minimal`
Expected: PASS
### Task 2: 先做样条基础能力
**Files:**
- Modify: `native/flyshot-native-planning/src/spline.rs`
- Modify: `native/flyshot-native-planning/src/lib.rs`
- Create: `native/flyshot-native-planning/tests/spline_tests.rs`
- [x] **Step 1: 先写 Rust 失败测试,锁定 clamped-zero 样条边界和值/导数接口**
```rust
#[test]
fn clamped_zero_spline_hits_waypoints_and_zero_boundary_velocity() {
let times = [0.0, 1.0, 2.0, 3.0];
let values = [0.0, 1.0, 0.5, 2.0];
let spline = CubicSpline1D::new_clamped_zero(&times, &values).unwrap();
assert_abs_diff_eq!(spline.position(0.0), 0.0, epsilon = 1e-12);
assert_abs_diff_eq!(spline.position(3.0), 2.0, epsilon = 1e-12);
assert_abs_diff_eq!(spline.velocity(0.0), 0.0, epsilon = 1e-12);
assert_abs_diff_eq!(spline.velocity(3.0), 0.0, epsilon = 1e-12);
}
```
- [x] **Step 2: 运行 Rust 测试确认失败**
Run: `cargo test --manifest-path native/flyshot-native-planning/Cargo.toml spline_tests::clamped_zero_spline_hits_waypoints_and_zero_boundary_velocity -- --exact`
Expected: FAIL,提示样条类型或构造函数不存在
- [x] **Step 3: 实现三对角求解、样条系数和每段导数峰值接口**
```rust
pub struct CubicSpline1D {
knots: Vec<f64>,
coefficients: Vec<[f64; 4]>,
}
impl CubicSpline1D {
pub fn new_clamped_zero(times: &[f64], values: &[f64]) -> Result<Self, NativePlanningError> { /* ... */ }
pub fn position(&self, time: f64) -> f64 { /* ... */ }
pub fn velocity(&self, time: f64) -> f64 { /* ... */ }
pub fn acceleration(&self, time: f64) -> f64 { /* ... */ }
pub fn jerk(&self, time: f64) -> f64 { /* ... */ }
pub fn segment_peak_derivatives(&self, segment_index: usize) -> [f64; 3] { /* ... */ }
}
```
- [x] **Step 4: 重新运行 Rust 样条测试**
Run: `cargo test --manifest-path native/flyshot-native-planning/Cargo.toml spline_tests::clamped_zero_spline_hits_waypoints_and_zero_boundary_velocity -- --exact`
Expected: PASS
### Task 3: 实现纯 Rust ICSP 核心
**Files:**
- Modify: `native/flyshot-native-planning/src/icsp.rs`
- Create: `native/flyshot-native-planning/tests/icsp_tests.rs`
- [x] **Step 1: 先写 Rust 失败测试,锁定初始段时长、scale 计算和未收敛路径**
```rust
#[test]
fn icsp_returns_same_iteration_count_as_managed_baseline_fixture() {
let input = test_fixture_three_segment_case();
let result = plan_icsp(&input).unwrap();
assert_eq!(result.iterations, 2);
assert_abs_diff_eq!(result.threshold, 0.0, epsilon = 1e-12);
}
```
- [x] **Step 2: 运行 Rust ICSP 测试确认失败**
Run: `cargo test --manifest-path native/flyshot-native-planning/Cargo.toml icsp_tests::icsp_returns_same_iteration_count_as_managed_baseline_fixture -- --exact`
Expected: FAIL,提示 `plan_icsp` 或结果结构不存在
- [x] **Step 3: 实现最小 ICSP 纯算法结构,逐项复刻 C# 收敛语义**
```rust
pub struct IcspInput<'a> {
pub waypoints: &'a [f64],
pub waypoint_count: usize,
pub dof: usize,
pub velocity_limits: &'a [f64],
pub acceleration_limits: &'a [f64],
pub jerk_limits: &'a [f64],
pub options: IcspOptions,
}
pub fn plan_icsp(input: &IcspInput<'_>) -> Result<IcspPlanResult, NativePlanningError> { /* ... */ }
```
- [x] **Step 4: 重新运行 Rust ICSP 测试**
Run: `cargo test --manifest-path native/flyshot-native-planning/Cargo.toml icsp_tests::icsp_returns_same_iteration_count_as_managed_baseline_fixture -- --exact`
Expected: PASS
### Task 4: 接通 C ABI 和托管结果复制
**Files:**
- Modify: `native/flyshot-native-planning/src/ffi.rs`
- Modify: `native/flyshot-native-planning/src/error.rs`
- Modify: `src/Flyshot.Native.Planning/NativeIcspMethods.cs`
- Modify: `src/Flyshot.Native.Planning/NativeIcspSafeHandle.cs`
- Create: `src/Flyshot.Native.Planning/NativeIcspInteropModels.cs`
- Create: `src/Flyshot.Native.Planning/RustIcspPlanner.cs`
- Create: `tests/Flyshot.Core.Tests/RustIcspPlannerInteropTests.cs`
- [x] **Step 1: 先写托管失败测试,锁定数组布局、结果复制和释放路径**
```csharp
[Fact]
public void RustIcspPlanner_ReturnsManagedPlannedTrajectory_FromNativeResult()
{
var planner = CreateRustPlannerAgainstBuiltNativeLibrary();
var result = planner.Plan(CreateSimpleRequest());
Assert.Equal(PlanningMethod.Icsp, result.Method);
Assert.Equal(result.PlannedWaypointCount, result.WaypointTimes.Count);
}
```
- [x] **Step 2: 运行单测确认失败**
Run: `dotnet test tests/Flyshot.Core.Tests/Flyshot.Core.Tests.csproj --filter RustIcspPlanner_ReturnsManagedPlannedTrajectory_FromNativeResult -v minimal`
Expected: FAIL,提示 P/Invoke 签名、结果结构或释放逻辑未实现
- [x] **Step 3: 实现 C ABI 入口、线程局部错误文本和 `SafeHandle` 释放**
```rust
#[no_mangle]
pub extern "C" fn flyshot_icsp_plan(/* ... */) -> i32 { /* ... */ }
#[no_mangle]
pub extern "C" fn flyshot_icsp_result_free(result: *mut FlyshotIcspResult) { /* ... */ }
#[no_mangle]
pub extern "C" fn flyshot_last_error_message() -> *const c_char { /* ... */ }
```
```csharp
[LibraryImport(NativeLibraryName, EntryPoint = "flyshot_icsp_plan")]
internal static unsafe partial int FlyshotIcspPlan(/* ... */);
```
- [x] **Step 4: 重新运行托管互操作测试**
Run: `dotnet test tests/Flyshot.Core.Tests/Flyshot.Core.Tests.csproj --filter RustIcspPlanner_ReturnsManagedPlannedTrajectory_FromNativeResult -v minimal`
Expected: PASS
### Task 5: 增加 C#/Rust 对拍测试和构建复制脚本
**Files:**
- Modify: `tests/Flyshot.Core.Tests/PlanningCompatibilityTests.cs`
- Create: `tests/Flyshot.Core.Tests/RustIcspPlannerParityTests.cs`
- Create: `build/Copy-NativePlanningArtifacts.ps1`
- Modify: `README.md`
- [x] **Step 1: 先写失败测试,锁定 C# 与 Rust 在基础样本上的逐项一致**
```csharp
[Fact]
public void RustIcspPlanner_MatchesManagedIcspPlanner_ForCompatibilityFixture()
{
var request = CreateCompatibilityRequest();
var managed = new ICspPlanner().Plan(request);
var native = CreateRustPlannerAgainstBuiltNativeLibrary().Plan(request);
AssertTrajectoryMatches(managed, native, tolerance: 1e-9);
}
```
- [x] **Step 2: 运行对拍测试确认失败**
Run: `dotnet test tests/Flyshot.Core.Tests/Flyshot.Core.Tests.csproj --filter RustIcspPlanner_MatchesManagedIcspPlanner_ForCompatibilityFixture -v minimal`
Expected: FAIL,提示数值未对齐或原生库复制失败
- [x] **Step 3: 补齐原生库复制脚本、测试输出复制规则和对拍辅助断言**
```powershell
param(
[string]$Configuration = "Release"
)
cargo build --manifest-path native/flyshot-native-planning/Cargo.toml --release
Copy-Item native/flyshot-native-planning/target/release/flyshot_planning.dll tests/Flyshot.Core.Tests/bin/$Configuration/net8.0/ -Force
```
- [x] **Step 4: 重新运行对拍测试**
Run: `dotnet test tests/Flyshot.Core.Tests/Flyshot.Core.Tests.csproj --filter RustIcspPlanner_MatchesManagedIcspPlanner_ForCompatibilityFixture -v minimal`
Expected: PASS
### Task 6: 解决方案级验证
**Files:**
- Modify: `README.md`
- Modify: `docs/superpowers/specs/2026-06-22-rust-icsp-native-design.md`
- [x] **Step 1: 运行 Rust 全量测试**
Run: `cargo test --manifest-path native/flyshot-native-planning/Cargo.toml`
Expected: PASS
- [ ] **Step 2: 运行 Core 测试**
Run: `dotnet test tests/Flyshot.Core.Tests/Flyshot.Core.Tests.csproj -v minimal`
Expected: PASS
当前验证:新增 Rust 相关测试全部通过;Core 全套仍保留实施前已存在的两项
`FlyshotExecutionSendSequenceBuilder` speed-ratio 失败,本计划未修改该行为。
- [x] **Step 3: 运行解决方案构建**
Run: `dotnet build FlyshotReplacement.sln -v minimal`
Expected: PASS
- [x] **Step 4: 更新文档,记录本地构建、库复制和当前上线边界**
```markdown
- Rust 规划器当前仅显式使用,不替换默认 `ICspPlanner`
- Windows 需要 `flyshot_planning.dll`
- Linux 需要 `libflyshot_planning.so`
```