using StandardScene.Magnetic.Protocol; using System; using System.Collections.Generic; using System.Text; namespace StandardScene.Magnetic.Tasking { /// /// 比对期望节点与上报节点,判定到站与动作是否完成(对齐 backend CarResponseService)。 /// 默认只闭环机构类动作;轨迹字段(Speed/Orientation 等)需显式开启。 /// public static class Fass2ActionResolver { /// 是否在停车站比对 Speed/Orientation/Direction/Byroad 等轨迹字段。 public static bool VerifyTrajectoryFields { get; set; } private static readonly (string Name, Func Get, Action Set)[] MechanismFields = { ("Obstacle", n => n.Obstacle, (n, v) => n.Obstacle = v), ("Audio", n => n.Audio, (n, v) => n.Audio = v), ("Light", n => n.Light, (n, v) => n.Light = v), ("Charge", n => n.Charge, (n, v) => n.Charge = v), ("Rest", n => n.Rest, (n, v) => n.Rest = v), ("Lift", n => n.Lift, (n, v) => n.Lift = v), ("Clamp", n => n.Clamp, (n, v) => n.Clamp = v), ("Tray", n => n.Tray, (n, v) => n.Tray = v), ("Roll", n => n.Roll, (n, v) => n.Roll = v), ("Shutdown", n => n.Shutdown, (n, v) => n.Shutdown = v) }; private static readonly (string Name, Func Get, Action Set)[] TrajectoryFields = { ("Direction", n => n.Direction, (n, v) => n.Direction = v), ("Orientation", n => n.Orientation, (n, v) => n.Orientation = v), ("Byroad", n => n.Byroad, (n, v) => n.Byroad = v) }; public static bool IsAtNode(Fass2StateReport report, ushort targetNode) { return report?.Node != null && report.Node.Node == targetNode; } public static bool IsVehicleMoving(byte vehicleState) { return vehicleState is 1 or 5; } public static bool RequiresActionWait(Fass2NodeMessage expected) { if (expected == null) { return false; } if (expected.StartStop is Fass2TaskBuilder.StartStopStop or Fass2TaskBuilder.StartStopPrecision or Fass2TaskBuilder.StartStopControlStop or Fass2TaskBuilder.StartStopControlStart) { return true; } foreach (var field in MechanismFields) { if (field.Get(expected) != 0) { return true; } } if (!VerifyTrajectoryFields) { return false; } foreach (var field in TrajectoryFields) { if (field.Get(expected) != 0) { return true; } } return expected.Speed != 0; } public static bool IsStationActionComplete(Fass2NodeMessage expected, Fass2NodeMessage actual, byte vehicleState) { if (expected == null || actual == null) { return false; } if (actual.Node != expected.Node) { return false; } if (IsVehicleMoving(vehicleState)) { return false; } if (!RequiresActionWait(expected)) { return true; } if (expected.StartStop == Fass2TaskBuilder.StartStopPass) { return true; } if (expected.StartStop == Fass2TaskBuilder.StartStopControlStop) { return false; } return ListPendingFields(expected, actual).Count == 0; } public static IReadOnlyList ListPendingFields(Fass2NodeMessage expected, Fass2NodeMessage actual) { var pending = new List(); if (expected == null || actual == null) { return pending; } if (expected.StartStop != 0 && actual.StartStop != expected.StartStop) { pending.Add("StartStop"); } if (VerifyTrajectoryFields) { if (expected.Speed != 0 && actual.Speed != expected.Speed) { pending.Add("Speed"); } foreach (var field in TrajectoryFields) { var expectedValue = field.Get(expected); if (expectedValue != 0 && field.Get(actual) != expectedValue) { pending.Add(field.Name); } } } foreach (var field in MechanismFields) { var expectedValue = field.Get(expected); if (expectedValue != 0 && field.Get(actual) != expectedValue) { pending.Add(field.Name); } } return pending; } public static Fass2NodeMessage BuildActionPatch(Fass2NodeMessage expected, Fass2NodeMessage actual) { if (expected == null) { return null; } var patch = new Fass2NodeMessage { Node = expected.Node }; var hasPatch = false; if (expected.StartStop != 0 && (actual == null || actual.StartStop != expected.StartStop)) { patch.StartStop = expected.StartStop; hasPatch = true; } if (VerifyTrajectoryFields) { if (expected.Speed != 0 && (actual == null || actual.Speed != expected.Speed)) { patch.Speed = expected.Speed; hasPatch = true; } foreach (var field in TrajectoryFields) { var expectedValue = field.Get(expected); if (expectedValue != 0 && (actual == null || field.Get(actual) != expectedValue)) { field.Set(patch, expectedValue); hasPatch = true; } } } foreach (var field in MechanismFields) { var expectedValue = field.Get(expected); if (expectedValue != 0 && (actual == null || field.Get(actual) != expectedValue)) { field.Set(patch, expectedValue); hasPatch = true; } } return hasPatch ? patch : null; } public static string DescribePending(Fass2NodeMessage expected, Fass2NodeMessage actual) { var pending = ListPendingFields(expected, actual); if (pending.Count == 0) { return string.Empty; } var builder = new StringBuilder(); for (var i = 0; i < pending.Count; i++) { if (i > 0) { builder.Append(','); } builder.Append(pending[i]); } return builder.ToString(); } } }