2.0协议完善加简单交管配置
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace StandardScene.Magnetic.Tasking
|
||||
{
|
||||
/// <summary>
|
||||
/// 断线重连后,用当前占点匹配 tasklist 链路的结果。
|
||||
/// </summary>
|
||||
public sealed class Fass2ChainMatch
|
||||
{
|
||||
public int TaskId { get; set; }
|
||||
public int TaskPriority { get; set; }
|
||||
public int CurrentSiteId { get; set; }
|
||||
public int TargetSiteId { get; set; }
|
||||
public int IndexInPath { get; set; }
|
||||
public int DistanceToTarget { get; set; }
|
||||
public bool IsAtStartPoint { get; set; }
|
||||
public bool IsAtEndPoint { get; set; }
|
||||
public IReadOnlyList<int> FullPath { get; set; } = new List<int>();
|
||||
public IReadOnlyList<int> RemainingPath { get; set; } = new List<int>();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var position = IsAtStartPoint ? "start" : (IsAtEndPoint ? "end" : "mid");
|
||||
return $"task={TaskId} {CurrentSiteId}->{TargetSiteId} pos={position} remain={DistanceToTarget} prio={TaskPriority}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一站多链路时的固定裁决:原 goal 仍在剩余路径 → 起点 → 优先级 → 距目标更近。
|
||||
/// </summary>
|
||||
public static class Fass2ReconnectChainSelector
|
||||
{
|
||||
public static bool TrySelect(
|
||||
IReadOnlyList<Fass2ChainMatch> matches,
|
||||
int? preferGoalSiteId,
|
||||
out Fass2ChainMatch selected,
|
||||
out string reason)
|
||||
{
|
||||
selected = null;
|
||||
reason = "no chain";
|
||||
if (matches == null || matches.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var candidates = matches.Where(m => m != null && m.TargetSiteId > 0).ToList();
|
||||
if (candidates.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preferGoalSiteId != null && preferGoalSiteId.Value > 0)
|
||||
{
|
||||
var keepGoal = candidates
|
||||
.Where(m => RemainingContains(m, preferGoalSiteId.Value))
|
||||
.ToList();
|
||||
if (keepGoal.Count == 1)
|
||||
{
|
||||
selected = keepGoal[0];
|
||||
reason = $"preferGoal={preferGoalSiteId.Value}";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keepGoal.Count > 1)
|
||||
{
|
||||
candidates = keepGoal;
|
||||
reason = $"preferGoal={preferGoalSiteId.Value} narrowed={keepGoal.Count}";
|
||||
}
|
||||
}
|
||||
|
||||
var ranked = candidates
|
||||
.OrderByDescending(m => m.IsAtStartPoint ? 1 : 0)
|
||||
.ThenByDescending(m => m.TaskPriority)
|
||||
.ThenBy(m => m.DistanceToTarget)
|
||||
.ThenBy(m => m.TaskId)
|
||||
.ToList();
|
||||
|
||||
if (ranked.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var best = ranked[0];
|
||||
if (ranked.Count > 1)
|
||||
{
|
||||
var second = ranked[1];
|
||||
var tied = best.IsAtStartPoint == second.IsAtStartPoint &&
|
||||
best.TaskPriority == second.TaskPriority &&
|
||||
best.DistanceToTarget == second.DistanceToTarget;
|
||||
if (tied && best.TargetSiteId != second.TargetSiteId)
|
||||
{
|
||||
reason = $"ambiguous {best} vs {second}";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
selected = best;
|
||||
if (string.IsNullOrEmpty(reason) || reason == "no chain")
|
||||
{
|
||||
reason = $"selected {best}";
|
||||
}
|
||||
else
|
||||
{
|
||||
reason = $"{reason}; selected {best}";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool RemainingContains(Fass2ChainMatch match, int siteId)
|
||||
{
|
||||
if (match.RemainingPath != null)
|
||||
{
|
||||
for (var i = 0; i < match.RemainingPath.Count; i++)
|
||||
{
|
||||
if (match.RemainingPath[i] == siteId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return match.TargetSiteId == siteId;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user