using StandardScene.Magnetic.Protocol; using System; using System.Collections.Generic; using System.Globalization; namespace StandardScene.Magnetic.Tasking { /// /// 管控停止(StartStop=12)按车放行:地图站点保持 12,仅改本车任务报文。 /// public sealed class Fass2ControlAreaInfo { public int SiteId { get; set; } public string AreaId { get; set; } = string.Empty; public int[] AreaSiteIds { get; set; } = Array.Empty(); public int Capacity { get; set; } = 1; public byte ReleaseStartStop { get; set; } = Fass2TaskBuilder.StartStopControlStart; } public sealed class Fass2ControlOccupancy { public Fass2ControlOccupancy(int carId, IReadOnlyList occupiedSiteIds) { CarId = carId; OccupiedSiteIds = occupiedSiteIds ?? Array.Empty(); } public int CarId { get; } public IReadOnlyList OccupiedSiteIds { get; } } public sealed class Fass2ControlReleaseCheck { public bool CanRelease { get; set; } public string AreaId { get; set; } = string.Empty; public int Capacity { get; set; } = 1; public int OthersInArea { get; set; } public byte ReleaseStartStop { get; set; } = Fass2TaskBuilder.StartStopControlStart; public string Reason { get; set; } = string.Empty; } public static class Fass2ControlAreaGate { private static readonly char[] SiteIdSeparators = { ',', ';', '|', ' ', '\t', '[', ']', '(', ')', '{', '}' }; public static bool IsControlStop(byte startStop) { return startStop == Fass2TaskBuilder.StartStopControlStop; } public static bool AllowsLeave(byte startStop) { return startStop == Fass2TaskBuilder.StartStopPass || startStop == Fass2TaskBuilder.StartStopControlStart; } public static Fass2ControlAreaInfo Resolve(Fass2SiteActionData data, int siteId) { data ??= new Fass2SiteActionData(); var areaSites = ParseSiteIds(data.ControlArea, siteId); var capacity = data.ControlCapacity.GetValueOrDefault(1); if (capacity < 1) { capacity = 1; } var release = data.ControlReleaseStartStop ?? Fass2TaskBuilder.StartStopControlStart; if (release != Fass2TaskBuilder.StartStopPass && release != Fass2TaskBuilder.StartStopControlStart) { release = Fass2TaskBuilder.StartStopControlStart; } return new Fass2ControlAreaInfo { SiteId = siteId, AreaSiteIds = areaSites, AreaId = string.Join(",", areaSites), Capacity = capacity, ReleaseStartStop = release }; } 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 int LimitWindowCount( IReadOnlyList nodes, int startIndex, int count, int releasedIndex) { if (nodes == null || count <= 0 || startIndex < 0 || startIndex >= nodes.Count) { return 0; } var limited = Math.Min(count, nodes.Count - startIndex); for (var i = 0; i < limited; i++) { var node = nodes[startIndex + i]; if (node != null && IsControlStop(node.StartStop) && releasedIndex != startIndex + i) { return i + 1; } } return limited; } public static Fass2ControlReleaseCheck Evaluate( int selfCarId, Fass2ControlAreaInfo area, IReadOnlyList occupancies) { area ??= new Fass2ControlAreaInfo(); if (area.AreaSiteIds == null || area.AreaSiteIds.Length == 0) { area.AreaSiteIds = area.SiteId > 0 ? new[] { area.SiteId } : Array.Empty(); } if (string.IsNullOrEmpty(area.AreaId)) { area.AreaId = string.Join(",", area.AreaSiteIds); } if (area.Capacity < 1) { area.Capacity = 1; } var others = 0; if (occupancies != null) { foreach (var occupancy in occupancies) { if (occupancy == null || occupancy.CarId == selfCarId) { continue; } if (OccupiesListedSites(occupancy.OccupiedSiteIds, area.AreaSiteIds)) { others++; } } } var canRelease = others < area.Capacity; return new Fass2ControlReleaseCheck { CanRelease = canRelease, AreaId = area.AreaId, Capacity = area.Capacity, OthersInArea = others, ReleaseStartStop = area.ReleaseStartStop == 0 ? Fass2TaskBuilder.StartStopControlStart : area.ReleaseStartStop, Reason = canRelease ? "area clear" : $"area occupied cars={others}, capacity={area.Capacity}" }; } 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; } } }