From 6262a4573a6abafd2ed27c407f0f05e7503a8718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E8=96=84=E4=BA=91?= Date: Wed, 5 Aug 2026 09:57:38 +0800 Subject: [PATCH] feat: report EM observation planning cycles --- .../MovementTest.TrajectoryObservationTest.cs | 14 ++++++++---- .../TrajectoryObservationPipeline.cs | 22 ++++++++++++++----- .../TrajectoryObservationChecks.cs | 5 +++++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs b/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs index 8316899..4586d0d 100644 --- a/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs +++ b/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs @@ -271,6 +271,9 @@ internal static class TrajectoryObservationMovementTestRunner DateTimeOffset now = state.CapturedAtUtc; TrajectoryObservationLoopTick tick = observationLoop.Tick(now, state, token); TrajectoryObservationObservation observation = tick.Observation; + TrajectoryObservationDiagnostic diagnostic = TrajectoryObservationDiagnostics.Create( + tick.LatestCycle, tick.LatestPlanningElapsed, tick.PlanningInFlight, + observation.PublishedTrajectory); TrajectoryObservationCharts charts = observation.PublishedTrajectory == null ? null : TrajectoryObservationCharts.Build(observation.PublishedTrajectory, segment, @@ -284,9 +287,10 @@ internal static class TrajectoryObservationMovementTestRunner gearSwitchWaitingPrinted = true; } - if (tick.ShouldLog) - LogIfCurrent(sessionId, CreateTickStatus(sessionId, observation, charts, - tick.LatestCycle, tick.LatestPlanningElapsed, runtimeState.WaitingAtGearSwitch)); + if (tick.PlanningStarted) + LogIfCurrent(sessionId, "planning status=pending"); + if (tick.PlanningCompleted) + LogIfCurrent(sessionId, diagnostic.Text); } } @@ -383,7 +387,9 @@ internal static class TrajectoryObservationMovementTestRunner private static void PrintStatus(string message) { - Hedingben.ToastText(ObserveOnlyNotice + "\n" + message, StatusChannel); + string text = ObserveOnlyNotice + "\n" + message; + Hedingben.ToastText(text, StatusChannel); + Console.WriteLine("[TrajectoryObserver] " + text); } private static void Finish(long sessionId, CancellationTokenSource cancellation, Task completed) diff --git a/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPipeline.cs b/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPipeline.cs index 9c0b5d9..019e6df 100644 --- a/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPipeline.cs +++ b/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPipeline.cs @@ -255,12 +255,15 @@ public sealed class TrajectoryObservationController public sealed class TrajectoryObservationLoopTick { internal TrajectoryObservationLoopTick(TrajectoryObservationObservation observation, - PlanningCycleResult latestCycle, TimeSpan latestPlanningElapsed, bool planningInFlight) + PlanningCycleResult latestCycle, TimeSpan latestPlanningElapsed, bool planningInFlight, + bool planningStarted, bool planningCompleted) { Observation = observation ?? throw new ArgumentNullException(nameof(observation)); LatestCycle = latestCycle; LatestPlanningElapsed = latestPlanningElapsed; PlanningInFlight = planningInFlight; + PlanningStarted = planningStarted; + PlanningCompleted = planningCompleted; } public TrajectoryObservationObservation Observation { get; } @@ -271,6 +274,10 @@ public sealed class TrajectoryObservationLoopTick public bool PlanningInFlight { get; } + public bool PlanningStarted { get; } + + public bool PlanningCompleted { get; } + public bool ShouldLog => true; } @@ -293,28 +300,31 @@ public sealed class TrajectoryObservationLoop if (state == null) throw new ArgumentNullException(nameof(state)); cancellationToken.ThrowIfCancellationRequested(); - ConsumeCompletedPlanning(now); + bool planningCompleted = ConsumeCompletedPlanning(now); + bool planningStarted = false; if (planningTask == null && controller.ShouldStartCycle(now)) { + planningStarted = true; planningStartedAtUtc = now; planningTask = controller.StartCycle(now, state, cancellationToken); - ConsumeCompletedPlanning(now); + planningCompleted |= ConsumeCompletedPlanning(now); } TrajectoryObservationObservation observation = controller.Observe(now, state); return new TrajectoryObservationLoopTick(observation, latestCycle, latestPlanningElapsed, - planningTask != null); + planningTask != null, planningStarted, planningCompleted); } - private void ConsumeCompletedPlanning(DateTimeOffset observedAtUtc) + private bool ConsumeCompletedPlanning(DateTimeOffset observedAtUtc) { Task completed = planningTask; - if (completed == null || !completed.IsCompleted) return; + if (completed == null || !completed.IsCompleted) return false; latestCycle = completed.GetAwaiter().GetResult(); TimeSpan elapsed = observedAtUtc - planningStartedAtUtc; latestPlanningElapsed = elapsed < TimeSpan.Zero ? TimeSpan.Zero : elapsed; planningTask = null; + return true; } } diff --git a/ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs b/ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs index 355ac4d..df4b7b1 100644 --- a/ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs +++ b/ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs @@ -72,6 +72,8 @@ internal static class TrajectoryObservationChecks Verification.Equal("等待真实档位/方向确认;观察模式不会推进下一方向段", TrajectoryObservationRuntimeState.GearSwitchWaitingNotice, "observation runtime uses required gear-switch notice"); + Verification.True(source.Contains("Console.WriteLine(\"[TrajectoryObserver] \" + text);"), + "observer status is mirrored to the host terminal"); } private static void VerifiesOperatorDocumentationUsesExactUiEntry() @@ -238,6 +240,8 @@ internal static class TrajectoryObservationChecks TrajectoryObservationLoopTick firstTick = loop.Tick(startedAt, firstState, CancellationToken.None); + Verification.True(firstTick.PlanningStarted, "observer first tick reports a planning-cycle start"); + Verification.True(!firstTick.PlanningCompleted, "observer first tick has no completed cycle"); Verification.True(firstTick.ShouldLog, "observer first cadence tick is eligible for session-guarded logging"); Verification.Equal(10L, firstTick.Observation.VehicleState.SequenceId, @@ -298,6 +302,7 @@ internal static class TrajectoryObservationChecks Verification.True(completed, "observer delayed planning completes deterministically"); Verification.True(completedTick != null, "observer delayed planning produces a completion tick"); TrajectoryObservationLoopTick finalTick = completedTick!; + Verification.True(finalTick.PlanningCompleted, "observer completion tick reports cycle completion"); Verification.True(finalTick.LatestCycle != null && finalTick.LatestCycle.Published, "observer delayed planning result is consumed without a continuation"); Verification.Equal(14L, finalTick.Observation.VehicleState.SequenceId,