using StandardScene.Magnetic.Protocol; using System; using System.Collections.Generic; using System.Text; namespace StandardScene.Magnetic.Tasking { /// /// 比对期望节点与上报节点,判定到站与动作是否完成(对齐 backend CarResponseService)。 /// public static class Fass2ActionResolver { private static readonly (string Name, Func Get, Action Set)[] ActionFields = { ("StartStop", n => n.StartStop, (n, v) => n.StartStop = v), ("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), ("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) }; public static bool IsAtNode(Fass2StateReport report, ushort targetNode) { return report?.Node != null && report.Node.Node == targetNode; } public static bool IsVehicleMoving(byte vehicleState) { return vehicleState == 1; } public static bool RequiresActionWait(Fass2NodeMessage expected) { if (expected == null) { return false; } if (expected.StartStop is Fass2TaskBuilder.StartStopStop or Fass2TaskBuilder.StartStopPrecision) { return true; } foreach (var field in ActionFields) { 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; } // 过站(StartStop=1) 只要求到点且非运行态,不要求 Orientation 等轨迹字段到位。 if (expected.StartStop == Fass2TaskBuilder.StartStopPass) { return true; } 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 (expected.Speed != 0 && actual.Speed != expected.Speed) { pending.Add("Speed"); } foreach (var field in ActionFields) { if (field.Name == "StartStop") { continue; } 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 (expected.Speed != 0 && (actual == null || actual.Speed != expected.Speed)) { patch.Speed = expected.Speed; hasPatch = true; } foreach (var field in ActionFields) { if (field.Name == "StartStop") { continue; } 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(); } } }