diff --git a/ClumsyPilot/ParkrobTrajplanner/EMPlanner/README.md b/ClumsyPilot/ParkrobTrajplanner/EMPlanner/README.md index 1691cc1..c0f26c5 100644 --- a/ClumsyPilot/ParkrobTrajplanner/EMPlanner/README.md +++ b/ClumsyPilot/ParkrobTrajplanner/EMPlanner/README.md @@ -213,6 +213,7 @@ dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerifi ```text plugins/ ├── ClumsyPilot.dll +├── TrajectoryPlanningVisualization.dll ├── osqp.dll └── licenses/ ├── OSQP-LICENSE.txt @@ -220,7 +221,11 @@ plugins/ └── OSQP-VERSION.txt ``` -发布器会检查 64 位 PowerShell、原生 DLL 的 x64 PE 类型和固定 SHA-256。OSQP 加载器从托管程序集目录以绝对路径加载同级 `osqp.dll`;它不依赖 `PATH` 或当前工作目录。部署和执行职责的更多说明见 [TrajectoryExecution](../TrajectoryExecution/README.md)。 +发布器只从显式 `ManagedDll` 的同级目录解析 `TrajectoryPlanningVisualization.dll`,先复制完整包到 staging, +再原子替换 `plugins` 目录;它不会搜索 `PATH` 或当前工作目录。发布器会检查 64 位 PowerShell、原生 DLL 的 x64 PE +类型和固定 SHA-256,并保留既有恢复行为。OSQP 加载器从托管程序集目录以绝对路径加载同级 `osqp.dll`;它不依赖 +`PATH` 或当前工作目录。部署和执行职责的更多说明见 [TrajectoryExecution](../TrajectoryExecution/README.md),网页 +观察的开关、token URL、图表语义和停止清理见 [EM trajectory observation MovementTest](../tarjplanner_movementtest/README.md)。 ## 常见错误(Common Errors) diff --git a/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/README.md b/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/README.md index 2c3d463..ec4c923 100644 --- a/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/README.md +++ b/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/README.md @@ -76,6 +76,30 @@ rejected before EM planning. and must never be forwarded directly to a vehicle motion interface. This MovementTest performs no coordinate conversion for driving and has no driving capability; any future execution mode requires a separate safety-reviewed design. +## 网页看板与可选 Painter + +将 `EnableWebVisualization=true` 可启用本机网页看板;它只绑定 `127.0.0.1`,启动日志会给出完整的、带随机 +会话令牌的 URL。端口为 `0` 时由系统选择可用回环端口;浏览器必须使用该完整 URL,不能删掉 token。默认 +`AutoOpenWebVisualization=true` 会尝试打开默认浏览器;无桌面会话或打开失败只记录 URL,服务和观察循环继续运行。 +网页默认以 `10 Hz` 发布不可变动态快照,网页关闭、断开或慢客户端只会丢弃网页帧,绝不会阻塞、序列化等待或改变 +规划周期。 + +`EnableNativePainterVisualization=true` 才会为本会话创建 `TrajectoryObserver.World`、`LS`、`ST` Painter;默认关闭。 +网页与 Painter 可以同时开启,也可全部关闭:全部关闭时仍保留 `OBSERVE_ONLY` 规划、控制台和 UI 状态。网页的启动、 +快照或服务出现异常时会只记录一次中文诊断并熔断本次网页输出,不自动循环重启,也不会停止规划观察。 + +网页采用中文说明配合科研绘图约定:白底、细灰网格和细曲线;当前轨迹为蓝色实线,上一轮为灰色虚线,换向与交接点 +为橙色,红色仅表示实际越界或失败。路径总览把已完成方向段画为灰色细实线、活动段画为蓝色细实线、未来段画为浅灰 +虚线,并以深蓝覆盖当前规划视界。页面的 `ReferenceS (m)` 是将轨迹投影到**完整活动方向段**后的共享参考站; +`PathS (m)` 是每轮 ST 求解使用、从该轮局部起点累计的实际路径弧长,二者不可互相替代或跨轮直接相减。 + +运动学页中 `j[i]` 仅表示真实区间 `[tᵢ, tᵢ₊₁)`,所以 jerk 只有 `N-1` 个样本;末点之后没有虚构的 `j=0`。 +状态页将滚动续航、接近停车边界和精确停车边界分别标为 `RollingContinuation`、 +`ApproachStopBoundary`、`ExactStopAtBoundary`:前两者允许非零末端速度,只有真实边界才是精确停车锚点。换向高亮 +仅会在投影位置满足容差、已连续停车保持 `GearSwitchStopHoldSeconds`,并取得 +`DirectionConfirmationSamples` 个满足 `DirectionConfirmationSpeedMetersPerSecond` 阈值的下一方向带符号速度样本后, +严格从 `N -> N+1` 前进。 + ## Operator launch and stop checklist The vehicle UI entry is `EM轨迹规划观察闭环测试`. It is an observation session only; it has no chassis, @@ -97,6 +121,8 @@ command is diagnostic information only and must not be copied into a vehicle-con To stop, use the vehicle UI's normal MovementTest stop action. Confirm the status says `Observation stop requested; all observer layers were cleared.` The cancellation request stops observer work and clears -all three layers. A current-session runtime fault also clears all three layers so stale active diagnostics are not left -on screen; an intentional bootstrap failure preserves its diagnostic world view. Stopping, running, or starting this -test must not issue chassis, motor, steering, brake, or gear output. +an existing Painter, stops the web publisher/HTTP/SSE service, and releases its port. A current-session runtime fault +also clears an existing Painter and stops the web session so stale diagnostics are not left on screen; an intentional +bootstrap failure still reports through UI/console even when both visualization modes are off. Closing a browser tab does +not stop the session; `TestStop()` owns final web and Painter cleanup. Stopping, running, or starting this test must not +issue chassis, motor, steering, brake, or gear output. diff --git a/ClumsyPilot/scripts/Publish-ClumsyPilotPlugin.ps1 b/ClumsyPilot/scripts/Publish-ClumsyPilotPlugin.ps1 index c124f93..57790e4 100644 --- a/ClumsyPilot/scripts/Publish-ClumsyPilotPlugin.ps1 +++ b/ClumsyPilot/scripts/Publish-ClumsyPilotPlugin.ps1 @@ -47,6 +47,10 @@ if (Test-SamePath $outputDirectoryPath $workspaceRoot) { if (-not [System.IO.File]::Exists($managedDllPath)) { throw "Managed DLL does not exist: $managedDllPath" } +$visualizationDll = Join-Path ([System.IO.Path]::GetDirectoryName($managedDllPath)) 'TrajectoryPlanningVisualization.dll' +if (-not [System.IO.File]::Exists($visualizationDll)) { + throw "Visualization DLL does not exist beside managed DLL: $visualizationDll" +} if (-not [System.Environment]::Is64BitProcess) { throw 'Refusing to publish from a non-x64 PowerShell host.' } @@ -89,6 +93,7 @@ $movedExistingPlugins = $false try { [System.IO.Directory]::CreateDirectory((Join-Path $stagingDirectory 'licenses')) | Out-Null [System.IO.File]::Copy($managedDllPath, (Join-Path $stagingDirectory 'ClumsyPilot.dll'), $false) + [System.IO.File]::Copy($visualizationDll, (Join-Path $stagingDirectory 'TrajectoryPlanningVisualization.dll'), $false) [System.IO.File]::Copy($nativeDll, (Join-Path $stagingDirectory 'osqp.dll'), $false) [System.IO.File]::Copy($license, (Join-Path $stagingDirectory 'licenses\OSQP-LICENSE.txt'), $false) [System.IO.File]::Copy($notice, (Join-Path $stagingDirectory 'licenses\OSQP-NOTICE.txt'), $false) diff --git a/ClumsyPilot/tests/EMPlannerVerificationHost/PluginPackagingChecks.cs b/ClumsyPilot/tests/EMPlannerVerificationHost/PluginPackagingChecks.cs index acca903..942ee5b 100644 --- a/ClumsyPilot/tests/EMPlannerVerificationHost/PluginPackagingChecks.cs +++ b/ClumsyPilot/tests/EMPlannerVerificationHost/PluginPackagingChecks.cs @@ -83,14 +83,20 @@ internal static class PluginPackagingChecks relativeFiles.Add(Path.GetRelativePath(packageRoot, files[index]).Replace('\\', '/')); relativeFiles.Sort(StringComparer.Ordinal); - Verification.Equal("plugins/ClumsyPilot.dll|plugins/licenses/OSQP-LICENSE.txt|plugins/licenses/OSQP-NOTICE.txt|" + - "plugins/licenses/OSQP-VERSION.txt|plugins/osqp.dll", string.Join("|", relativeFiles), - "plugin tree contains exactly the managed DLL, native DLL, and OSQP license files"); + Verification.Equal("plugins/ClumsyPilot.dll|plugins/TrajectoryPlanningVisualization.dll|" + + "plugins/licenses/OSQP-LICENSE.txt|plugins/licenses/OSQP-NOTICE.txt|plugins/licenses/OSQP-VERSION.txt|" + + "plugins/osqp.dll", string.Join("|", relativeFiles), + "plugin tree contains exactly the managed DLLs, native DLL, and OSQP license files"); string managedPlugin = Path.Combine(pluginsDirectory, "ClumsyPilot.dll"); AssemblyName managedAssembly = AssemblyName.GetAssemblyName(managedPlugin); Verification.True(!string.IsNullOrWhiteSpace(managedAssembly.Name), "packaged ClumsyPilot.dll is managed"); + string visualizationPlugin = Path.Combine(pluginsDirectory, "TrajectoryPlanningVisualization.dll"); + AssemblyName visualizationAssembly = AssemblyName.GetAssemblyName(visualizationPlugin); + Verification.Equal("TrajectoryPlanningVisualization", visualizationAssembly.Name, + "packaged visualization DLL has the expected managed assembly name"); + string nativePlugin = Path.Combine(pluginsDirectory, "osqp.dll"); Verification.Equal((ushort)0x8664, ReadPeMachine(nativePlugin), "packaged OSQP binary is x64"); Verification.Equal(ReadExpectedOsqpHash(repositoryRoot), GetSha256(nativePlugin),