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