using SimpleCore.PropType; using StandardScene.Magnetic.Protocol; using System; using System.Collections.Generic; namespace StandardScene.Magnetic.Tasking { public enum Fass2TaskPhase { Idle, Planning, Dispatching, Moving, AtStation, SegmentDone, Complete, Fault, Cancelled } /// /// 一次 FASS 2.0 任务执行上下文,供状态机推进与重启恢复使用。 /// public sealed class Fass2TaskContext { public Fass2TaskPhase Phase { get; set; } = Fass2TaskPhase.Idle; public int StartSiteId { get; set; } public int GoalSiteId { get; set; } public int CurrentIndex { get; set; } public ulong TaskId { get; set; } public Fass2TaskPlan Plan { get; set; } public string FieldsSignature { get; set; } = string.Empty; public double DefaultSpeed { get; set; } = -1; public string FaultReason { get; set; } public DateTime StartedAt { get; set; } public DateTime LastDispatchAt { get; set; } = DateTime.MinValue; public ulong LastActionId { get; set; } public DateTime LastActionSentAt { get; set; } = DateTime.MinValue; /// 交管窗口未齐、正在等锁;此期间不消耗移动超时预算。 public bool WaitingForTraffic { get; set; } /// 停在管控停止点等待按车放行;此期间不消耗移动超时预算。 public bool WaitingForRelease { get; set; } /// 本任务已对哪个路径下标发过放行报文;-1 表示尚未放行。 public int ControlReleasedIndex { get; set; } = -1; /// 最近一次规划的站点链路,供断线重连判断是否仍在原路径上。 public int[] RouteSiteIds { get; set; } = Array.Empty(); } public static class Fass2TaskPersistence { public const string TagPhase = "Fass2Task_Phase"; public const string TagStartSite = "Fass2Task_StartSite"; public const string TagGoalSite = "Fass2Task_GoalSite"; public const string TagCurrentIndex = "Fass2Task_CurrentIndex"; public const string TagTaskId = "Fass2Task_TaskId"; public const string TagFieldsSignature = "Fass2Task_FieldsSig"; public const string TagDefaultSpeed = "Fass2Task_DefaultSpeed"; public const string TagRouteSites = "Fass2Task_RouteSites"; public static void Save(TagSet tags, Fass2TaskContext context) { if (tags == null || context == null) { return; } SetTag(tags, TagPhase, context.Phase.ToString()); SetTag(tags, TagStartSite, context.StartSiteId.ToString()); SetTag(tags, TagGoalSite, context.GoalSiteId.ToString()); SetTag(tags, TagCurrentIndex, context.CurrentIndex.ToString()); SetTag(tags, TagTaskId, context.TaskId.ToString()); SetTag(tags, TagFieldsSignature, context.FieldsSignature ?? string.Empty); SetTag(tags, TagDefaultSpeed, context.DefaultSpeed.ToString(System.Globalization.CultureInfo.InvariantCulture)); SetTag(tags, TagRouteSites, FormatIntList(context.RouteSiteIds)); } public static bool TryLoad(TagSet tags, out Fass2TaskContext context) { context = null; if (tags == null || !tags.TryGetValue(TagPhase, out var phaseText) || !Enum.TryParse(phaseText, out Fass2TaskPhase phase)) { return false; } if (phase is Fass2TaskPhase.Idle or Fass2TaskPhase.Complete or Fass2TaskPhase.Cancelled) { return false; } if (!TryReadInt(tags, TagStartSite, out var startSite) || !TryReadInt(tags, TagGoalSite, out var goalSite) || !TryReadInt(tags, TagCurrentIndex, out var currentIndex)) { return false; } tags.TryGetValue(TagFieldsSignature, out var fieldsSignature); ulong.TryParse(tags.TryGetValue(TagTaskId, out var taskIdText) ? taskIdText : "0", out var taskId); double.TryParse( tags.TryGetValue(TagDefaultSpeed, out var speedText) ? speedText : "-1", System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var defaultSpeed); tags.TryGetValue(TagRouteSites, out var routeSitesText); context = new Fass2TaskContext { Phase = phase, StartSiteId = startSite, GoalSiteId = goalSite, CurrentIndex = currentIndex, TaskId = taskId, FieldsSignature = fieldsSignature ?? string.Empty, DefaultSpeed = defaultSpeed, RouteSiteIds = ParseIntList(routeSitesText) }; return true; } public static void Clear(TagSet tags) { if (tags == null) { return; } RemoveTag(tags, TagPhase); RemoveTag(tags, TagStartSite); RemoveTag(tags, TagGoalSite); RemoveTag(tags, TagCurrentIndex); RemoveTag(tags, TagTaskId); RemoveTag(tags, TagFieldsSignature); RemoveTag(tags, TagDefaultSpeed); RemoveTag(tags, TagRouteSites); } private static void SetTag(TagSet tags, string key, string value) { if (tags.Contains(key)) { tags.Remove(key); } tags.Add(key, value); } private static void RemoveTag(TagSet tags, string key) { if (tags.Contains(key)) { tags.Remove(key); } } private static bool TryReadInt(TagSet tags, string key, out int value) { value = 0; return tags.TryGetValue(key, out var text) && int.TryParse(text, out value); } private static string FormatIntList(int[] values) { return values == null || values.Length == 0 ? string.Empty : string.Join(",", values); } private static int[] ParseIntList(string text) { if (string.IsNullOrWhiteSpace(text)) { return Array.Empty(); } var parts = text.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var ids = new List(parts.Length); foreach (var part in parts) { if (int.TryParse(part.Trim(), out var id) && id > 0) { ids.Add(id); } } return ids.ToArray(); } } }