12 KiB
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 -
Step 1: 先写托管侧失败测试,锁定缺少原生库时的报错边界
[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);
}
- Step 2: 运行单测确认当前失败
Run: dotnet test tests/Flyshot.Core.Tests/Flyshot.Core.Tests.csproj --filter RustIcspPlanner_ThrowsClearError_WhenNativeLibraryIsMissing -v minimal
Expected: FAIL,提示 RustIcspPlanner 或互操作类型不存在
- Step 3: 创建 Rust
cdylib和托管互操作项目骨架
[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"
<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>
- Step 4: 添加最小库解析与异常包装实现
public sealed class NativePlanningException : Exception
{
public NativePlanningException(string message, int errorCode, Exception? innerException = null)
: base(message, innerException)
{
ErrorCodeValue = errorCode;
}
public int ErrorCodeValue { get; }
}
public sealed class NativePlanningLibraryResolver
{
private readonly Func<RuntimeInformation, string> _resolveLibraryPath;
}
- 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 -
Step 1: 先写 Rust 失败测试,锁定 clamped-zero 样条边界和值/导数接口
#[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(×, &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);
}
- 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,提示样条类型或构造函数不存在
- Step 3: 实现三对角求解、样条系数和每段导数峰值接口
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] { /* ... */ }
}
- 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 -
Step 1: 先写 Rust 失败测试,锁定初始段时长、scale 计算和未收敛路径
#[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);
}
- 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 或结果结构不存在
- Step 3: 实现最小 ICSP 纯算法结构,逐项复刻 C# 收敛语义
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> { /* ... */ }
- 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 -
Step 1: 先写托管失败测试,锁定数组布局、结果复制和释放路径
[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);
}
- Step 2: 运行单测确认失败
Run: dotnet test tests/Flyshot.Core.Tests/Flyshot.Core.Tests.csproj --filter RustIcspPlanner_ReturnsManagedPlannedTrajectory_FromNativeResult -v minimal
Expected: FAIL,提示 P/Invoke 签名、结果结构或释放逻辑未实现
- Step 3: 实现 C ABI 入口、线程局部错误文本和
SafeHandle释放
#[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 { /* ... */ }
[LibraryImport(NativeLibraryName, EntryPoint = "flyshot_icsp_plan")]
internal static unsafe partial int FlyshotIcspPlan(/* ... */);
- 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 -
Step 1: 先写失败测试,锁定 C# 与 Rust 在基础样本上的逐项一致
[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);
}
- Step 2: 运行对拍测试确认失败
Run: dotnet test tests/Flyshot.Core.Tests/Flyshot.Core.Tests.csproj --filter RustIcspPlanner_MatchesManagedIcspPlanner_ForCompatibilityFixture -v minimal
Expected: FAIL,提示数值未对齐或原生库复制失败
- Step 3: 补齐原生库复制脚本、测试输出复制规则和对拍辅助断言
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
- 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 -
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 失败,本计划未修改该行为。
- Step 3: 运行解决方案构建
Run: dotnet build FlyshotReplacement.sln -v minimal
Expected: PASS
- Step 4: 更新文档,记录本地构建、库复制和当前上线边界
- Rust 规划器当前仅显式使用,不替换默认 `ICspPlanner`
- Windows 需要 `flyshot_planning.dll`
- Linux 需要 `libflyshot_planning.so`