using System; using System.Collections.Generic; using System.Globalization; using SimpleLite.RCS.CarTypes; namespace StandardScene.Signal.Logic { /// /// 磁条管控区占用判定:他车占用「当前策略」管控站点列表的数量 ≥ 容量则不放行;本车不计入。 /// 磁条交管进程只对停在触发点的 MagCar 调用,满足后发启动。 /// 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(); 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 a, IReadOnlyList 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 areaSiteIds, int capacity, byte releaseStartStop, IReadOnlyList<(int CarId, IReadOnlyList OccupiedSiteIds)> occupancies) { var sites = areaSiteIds == null || areaSiteIds.Count == 0 ? (triggerSit > 0 ? new[] { triggerSit } : Array.Empty()) : 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}" }; } /// 统计占用管控区内部站点的车辆数,可排除排队车和在途车。 public static int CountOccupyingCars( IReadOnlyList<(int CarId, IReadOnlyList OccupiedSiteIds)> occupancies, IReadOnlyList areaSiteIds, HashSet 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 occupiedSiteIds, IReadOnlyList 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; } /// 统计一辆车占用的站点:物理 siteID + holdingLocks。 public static IReadOnlyList CollectOccupiedSiteIds(Car car) { var ids = new List(); 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; } } }