using SimpleLite.RCS; using SimpleCore; using SimpleCore.PropType; using System; using System.Linq; using SimpleLite.RCS.CarTypes; namespace StandardScene.Scheduler.FassEvent { /// /// 节点(地标)交管判断辅助类,替代 FASS AlgorithmService 中与 EventCar 相关的方法。 /// /// “锁定”判定依据 StandardScene 交通锁:holdingLocks、pendingLocks、aquiringLock, /// 以及站点 tags 中的 unavailable。 /// /// internal static class FassEventNodeHelper { /// 指定节点列表是否全部未被任何车辆占用。 public static bool NodeAllUnlocked(string[] nodeCodes) { return nodeCodes == null || nodeCodes.All(node => !IsNodeLocked(node, -1)); } /// 指定节点列表是否全部未被占用(排除当前车辆自身锁)。 public static bool NodeAllUnlockedNotCar(string carCode, string[] nodeCodes) { var excludeCarId = ResolveCarId(carCode); return nodeCodes == null || nodeCodes.All(node => !IsNodeLocked(node, excludeCarId)); } /// 指定节点列表中是否存在任意一个被占用的节点。 public static bool NodeAnyLocked(string[] nodeCodes) { return nodeCodes != null && nodeCodes.Any(node => IsNodeLocked(node, -1)); } /// 指定节点列表中是否存在被其他车辆占用的节点(排除自身)。 public static bool NodeAnyLockedNotCar(string carCode, string[] nodeCodes) { var excludeCarId = ResolveCarId(carCode); return nodeCodes != null && nodeCodes.Any(node => IsNodeLocked(node, excludeCarId)); } /// /// 节点全部解锁(排除自身)且指定节点范围内不存在处于运行态的其他车辆。 /// 对应 FASS NodeAllUnlockedNotCarNotEndCarState。 /// public static bool NodeAllUnlockedNotCarNotEndCarState(string carCode, string runningState, string[] nodeCodes) { if (!NodeAllUnlockedNotCar(carCode, nodeCodes)) { return false; } return !ExistsRunningCarOnNodes(carCode, runningState, nodeCodes); } /// /// 判断除指定车辆外,是否有车辆在给定节点列表上处于“运行中”状态。 /// 逻辑对齐 FASS IsExistRunningStateCar:支持首参 Contains 匹配及 Skip(1) 尾节点匹配。 /// public static bool ExistsRunningCarOnNodes(string excludeCarCode, string runningState, string[] nodeCodes) { if (nodeCodes == null || nodeCodes.Length == 0) { return false; } var excludeCarId = ResolveCarId(excludeCarCode); foreach (var car in SimpleLib.GetAllCars().OfType()) { if (car.id == excludeCarId) { continue; } var currentNode = FassEventCarSnapshotFactory.GetCurrentNodeCode(car); if (string.IsNullOrWhiteSpace(currentNode)) { continue; } var state = FassEventCarSnapshotFactory.GetFassState(car); if (!IsRunningState(state, runningState)) { continue; } // 与 FASS 一致:当前地标包含参数首项,或等于后续任一项。 if (nodeCodes.Any(node => string.Equals(node, currentNode, StringComparison.OrdinalIgnoreCase) || currentNode.Contains(node))) { return true; } if (nodeCodes.Length > 1) { var tailNodes = nodeCodes.Skip(1).ToArray(); if (tailNodes.Any(node => string.Equals(node, currentNode, StringComparison.OrdinalIgnoreCase))) { return true; } } } return false; } /// /// 判断单个地标是否被锁定。 /// 节点号可解析为站点 ID,或通过 fields["TagValue"] 匹配地标标签。 /// private static bool IsNodeLocked(string nodeCode, int excludeCarId) { if (!TryResolveSiteId(nodeCode, out var siteId)) { return false; } var site = SimpleLib.GetSite(siteId); if (site?.tags?.Contains("unavailable") == true) { return true; } return SimpleLib.GetAllCars().Any(car => car.id != excludeCarId && (car.status.holdingLocks?.Contains(siteId) == true || car.status.pendingLocks?.Contains(siteId) == true || car.status.aquiringLock == siteId)); } /// 根据 FASS 车辆编号解析场景内 car.id。 private static int ResolveCarId(string carCode) { if (string.IsNullOrWhiteSpace(carCode)) { return -1; } var car = SimpleLib.GetAllCars() .OfType() .FirstOrDefault(item => string.Equals(FassEventCarSnapshotFactory.GetCarCode(item), carCode, StringComparison.OrdinalIgnoreCase)); return car?.id ?? -1; } /// /// 将地标编码解析为站点 ID:纯数字直接查站;否则按 TagValue 字段匹配。 /// private static bool TryResolveSiteId(string nodeCode, out int siteId) { siteId = 0; if (string.IsNullOrWhiteSpace(nodeCode)) { return false; } if (int.TryParse(nodeCode, out siteId)) { return SimpleLib.GetSite(siteId) != null; } var site = SimpleLib.GetAllSites().FirstOrDefault(item => item.fields.TryGetValue("TagValue", out var tag) && string.Equals(tag, nodeCode, StringComparison.OrdinalIgnoreCase)); if (site == null) { return false; } siteId = site.id; return true; } /// /// 判断状态是否视为“运行中”。兼容 FASS 历史拼写 Runing 与 Running。 /// private static bool IsRunningState(string state, string runningState) { if (string.IsNullOrWhiteSpace(state)) { return false; } if (string.Equals(state, runningState, StringComparison.OrdinalIgnoreCase)) { return true; } return string.Equals(state, "Running", StringComparison.OrdinalIgnoreCase) && (string.Equals(runningState, "Runing", StringComparison.OrdinalIgnoreCase) || string.Equals(runningState, "Running", StringComparison.OrdinalIgnoreCase)); } } }