Files

211 lines
7.0 KiB
C#

using SimpleCore;
using SimpleCore.Compiler;
using SimpleCore.Library;
using SimpleCore.PropType;
using System;
using System.Collections.Generic;
namespace StandardScene.Magnetic.Tasking
{
/// <summary>
/// 从站点序列构建 <see cref="SegmentPlan"/>,供交管 Forecast 与任务下发共用同一路径。
/// </summary>
internal static class Fass2RouteHelper
{
public static List<int> GetSitesBetween(int startSiteId, int endSiteId, AbstractCar car)
{
if (startSiteId == endSiteId)
{
return new List<int> { startSiteId };
}
if (car != null)
{
var startSite = SimpleLib.GetSite(startSiteId);
var endSite = SimpleLib.GetSite(endSiteId);
if (startSite != null && endSite != null)
{
var plan = new SegmentPlan { usingCar = car };
plan.fields["allow_destination_on_route"] = "true";
plan.FindRoute(startSite, endSite);
var sites = ExtractSiteIds(plan);
if (sites.Count > 0)
{
return sites;
}
}
}
return Fass2PathFinder.GetSitesBetweenBfs(startSiteId, endSiteId);
}
public static void ForecastTrafficSequence(AbstractCar car, IReadOnlyList<int> siteIds, int fromIndex)
{
if (car == null || siteIds == null || siteIds.Count == 0)
{
throw new ArgumentException("traffic forecast requires car and siteIds");
}
fromIndex = Math.Max(0, Math.Min(fromIndex, siteIds.Count - 1));
if (fromIndex >= siteIds.Count - 1)
{
return;
}
var routeSiteIds = new List<int>();
for (var i = fromIndex; i < siteIds.Count; i++)
{
routeSiteIds.Add(siteIds[i]);
}
try
{
var plan = BuildSegmentPlan(car, routeSiteIds);
var program = plan.Compile("fass2-traffic", false);
program.Forecast();
}
catch (Exception ex) when (IsRecoverableForecastFailure(ex))
{
// 双车 Multi-car search Exhausted 时降级:只建 pending/seqScope,交由 TryLock 现场占点
ApplyManualTrafficSequence(car, routeSiteIds);
Diagnosis.Post(
$"Mag2Car forecast fallback manual sequence car={car.id}: {ex.Message}",
"Mag2Car",
true);
}
}
/// <summary>
/// 不走 SimpleCore 多车 Forecast,直接写入交管序列(持锁起点已由 TrafficReset 保证)。
/// </summary>
public static void ApplyManualTrafficSequence(AbstractCar car, IReadOnlyList<int> routeSiteIds)
{
if (car == null || routeSiteIds == null || routeSiteIds.Count == 0)
{
return;
}
var scope = new int[routeSiteIds.Count];
for (var i = 0; i < routeSiteIds.Count; i++)
{
scope[i] = routeSiteIds[i];
}
var pending = new int[Math.Max(0, scope.Length - 1)];
for (var i = 0; i < pending.Length; i++)
{
pending[i] = scope[i + 1];
}
car.status.seqScope = scope;
car.status.pendingLocks = pending;
car.status.seqPtr = scope.Length > 1 ? 1 : 0;
car.status.escape = Array.Empty<int>();
}
private static bool IsRecoverableForecastFailure(Exception ex)
{
for (var cur = ex; cur != null; cur = cur.InnerException)
{
var msg = cur.Message ?? string.Empty;
if (msg.IndexOf("Multi-car traffic search", StringComparison.OrdinalIgnoreCase) >= 0 ||
msg.IndexOf("Exhausted", StringComparison.OrdinalIgnoreCase) >= 0 ||
msg.IndexOf("BadForecast", StringComparison.OrdinalIgnoreCase) >= 0 ||
cur.GetType().Name.IndexOf("Forecast", StringComparison.OrdinalIgnoreCase) >= 0)
{
return true;
}
}
return false;
}
public static SegmentPlan BuildSegmentPlan(AbstractCar car, IReadOnlyList<int> siteIds)
{
if (car == null || siteIds == null || siteIds.Count == 0)
{
throw new ArgumentException("segment plan requires car and siteIds");
}
var plan = new SegmentPlan { usingCar = car };
plan.fields["allow_destination_on_route"] = "true";
var firstSite = SimpleLib.GetSite(siteIds[0]);
if (firstSite == null)
{
throw new InvalidOperationException($"site {siteIds[0]} not found");
}
plan.segments.Add(firstSite);
for (var i = 0; i < siteIds.Count - 1; i++)
{
var dstSite = SimpleLib.GetSite(siteIds[i + 1]);
if (dstSite == null)
{
throw new InvalidOperationException($"site {siteIds[i + 1]} not found");
}
var track = FindTrack(siteIds[i], siteIds[i + 1]);
if (track == null)
{
throw new InvalidOperationException($"no track from site {siteIds[i]} to {siteIds[i + 1]}");
}
plan.segments.Add(track);
plan.segments.Add(dstSite);
}
return plan;
}
public static List<int> ExtractSiteIds(SegmentPlan plan)
{
var sites = new List<int>();
if (plan?.segments == null)
{
return sites;
}
foreach (var segment in plan.segments)
{
if (segment is Site site)
{
sites.Add(site.id);
}
}
return sites;
}
public static Track FindTrack(int fromSiteId, int toSiteId)
{
foreach (var track in SimpleLib.GetAllTracks())
{
if (track.direction == 0)
{
if ((track.siteA == fromSiteId && track.siteB == toSiteId) ||
(track.siteB == fromSiteId && track.siteA == toSiteId))
{
return track;
}
continue;
}
if (track.direction == 1 && track.siteA == fromSiteId && track.siteB == toSiteId)
{
return track;
}
if (track.direction == 2 && track.siteB == fromSiteId && track.siteA == toSiteId)
{
return track;
}
}
return null;
}
}
}