190 lines
6.3 KiB
C#
190 lines
6.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using SimpleLite.RCS.CarTypes;
|
|
|
|
namespace StandardScene.Signal.Logic
|
|
{
|
|
/// <summary>
|
|
/// 磁条管控区占用判定:他车占用「当前策略」管控站点列表的数量 ≥ 容量则不放行;本车不计入。
|
|
/// 磁条交管进程只对停在触发点的 MagCar 调用,满足后发启动。
|
|
/// </summary>
|
|
public static class MagControlAreaGate
|
|
{
|
|
public const byte StartStopPass = 1;
|
|
public const byte StartStopControlStart = 11;
|
|
|
|
private static readonly char[] SiteIdSeparators = { ',', ';', '|', ' ', '\t', '[', ']', '(', ')', '{', '}' };
|
|
|
|
public static byte NormalizeReleaseStartStop(byte value)
|
|
{
|
|
if (value == StartStopPass || value == StartStopControlStart)
|
|
return value;
|
|
return StartStopControlStart;
|
|
}
|
|
|
|
public static int NormalizeCapacity(int capacity)
|
|
{
|
|
return capacity < 1 ? 1 : capacity;
|
|
}
|
|
|
|
public static int[] ParseSiteIds(string text, int fallbackSiteId)
|
|
{
|
|
var ids = new List<int>();
|
|
if (!string.IsNullOrWhiteSpace(text))
|
|
{
|
|
var parts = text.Split(SiteIdSeparators, StringSplitOptions.RemoveEmptyEntries);
|
|
foreach (var part in parts)
|
|
{
|
|
if (int.TryParse(part.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var id) &&
|
|
id > 0 && !ids.Contains(id))
|
|
{
|
|
ids.Add(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ids.Count == 0 && fallbackSiteId > 0)
|
|
ids.Add(fallbackSiteId);
|
|
|
|
return ids.ToArray();
|
|
}
|
|
|
|
public static string NormalizeCompatGroup(string value)
|
|
{
|
|
return (value ?? string.Empty).Trim();
|
|
}
|
|
|
|
public static bool SitesIntersect(IReadOnlyList<int> a, IReadOnlyList<int> b)
|
|
{
|
|
if (a == null || b == null || a.Count == 0 || b.Count == 0)
|
|
return false;
|
|
for (var i = 0; i < a.Count; i++)
|
|
{
|
|
var left = a[i];
|
|
if (left <= 0)
|
|
continue;
|
|
for (var j = 0; j < b.Count; j++)
|
|
{
|
|
if (b[j] == left)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static MagControlReleaseDecision Evaluate(
|
|
int selfCarId,
|
|
int triggerSit,
|
|
IReadOnlyList<int> areaSiteIds,
|
|
int capacity,
|
|
byte releaseStartStop,
|
|
IReadOnlyList<(int CarId, IReadOnlyList<int> OccupiedSiteIds)> occupancies)
|
|
{
|
|
var sites = areaSiteIds == null || areaSiteIds.Count == 0
|
|
? (triggerSit > 0 ? new[] { triggerSit } : Array.Empty<int>())
|
|
: areaSiteIds;
|
|
capacity = NormalizeCapacity(capacity);
|
|
releaseStartStop = NormalizeReleaseStartStop(releaseStartStop);
|
|
|
|
var others = 0;
|
|
if (occupancies != null)
|
|
{
|
|
foreach (var occupancy in occupancies)
|
|
{
|
|
if (occupancy.CarId == selfCarId)
|
|
continue;
|
|
if (OccupiesListedSites(occupancy.OccupiedSiteIds, sites))
|
|
others++;
|
|
}
|
|
}
|
|
|
|
var canRelease = others < capacity;
|
|
var areaId = string.Join(",", sites);
|
|
return new MagControlReleaseDecision
|
|
{
|
|
RuleFound = true,
|
|
CanRelease = canRelease,
|
|
AreaId = areaId,
|
|
Capacity = capacity,
|
|
OthersInArea = others,
|
|
ReleaseStartStop = releaseStartStop,
|
|
Reason = canRelease
|
|
? "area clear"
|
|
: $"area occupied cars={others}, capacity={capacity}"
|
|
};
|
|
}
|
|
|
|
/// <summary>统计占用管控区内部站点的车辆数,可排除排队车和在途车。</summary>
|
|
public static int CountOccupyingCars(
|
|
IReadOnlyList<(int CarId, IReadOnlyList<int> OccupiedSiteIds)> occupancies,
|
|
IReadOnlyList<int> areaSiteIds,
|
|
HashSet<int> excludeCarIds)
|
|
{
|
|
if (occupancies == null || occupancies.Count == 0 ||
|
|
areaSiteIds == null || areaSiteIds.Count == 0)
|
|
return 0;
|
|
|
|
var others = 0;
|
|
foreach (var occupancy in occupancies)
|
|
{
|
|
if (excludeCarIds != null && excludeCarIds.Contains(occupancy.CarId))
|
|
continue;
|
|
if (OccupiesListedSites(occupancy.OccupiedSiteIds, areaSiteIds))
|
|
others++;
|
|
}
|
|
|
|
return others;
|
|
}
|
|
|
|
public static bool OccupiesListedSites(IReadOnlyList<int> occupiedSiteIds, IReadOnlyList<int> areaSiteIds)
|
|
{
|
|
if (occupiedSiteIds == null || occupiedSiteIds.Count == 0 ||
|
|
areaSiteIds == null || areaSiteIds.Count == 0)
|
|
return false;
|
|
|
|
for (var i = 0; i < occupiedSiteIds.Count; i++)
|
|
{
|
|
var occupied = occupiedSiteIds[i];
|
|
if (occupied <= 0)
|
|
continue;
|
|
for (var j = 0; j < areaSiteIds.Count; j++)
|
|
{
|
|
if (areaSiteIds[j] == occupied)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>统计一辆车占用的站点:物理 siteID + holdingLocks。</summary>
|
|
public static IReadOnlyList<int> CollectOccupiedSiteIds(Car car)
|
|
{
|
|
var ids = new List<int>();
|
|
if (car == null)
|
|
return ids;
|
|
try
|
|
{
|
|
if (car.siteID > 0)
|
|
ids.Add(car.siteID);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
var holding = car.status?.holdingLocks;
|
|
if (holding == null)
|
|
return ids;
|
|
foreach (var lockId in holding)
|
|
{
|
|
if (lockId > 0 && !ids.Contains(lockId))
|
|
ids.Add(lockId);
|
|
}
|
|
|
|
return ids;
|
|
}
|
|
}
|
|
}
|