210 lines
6.6 KiB
C#
210 lines
6.6 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Newtonsoft.Json;
|
||
using SimpleCore;
|
||
using SimpleCore.Library;
|
||
using SimpleLite;
|
||
using SimpleLite.Props;
|
||
using SimpleLite.RCS;
|
||
using SimpleLite.RCS.CarTypes;
|
||
using StandardScene.CarTypes;
|
||
using StandardScene.Chained;
|
||
using StandardScene.Magnetic.Tasking;
|
||
using StandardScene.Model;
|
||
|
||
namespace StandardScene
|
||
{
|
||
/// <summary>
|
||
/// 磁导航 FASS 2.0 环线任务进程。
|
||
/// <para>任务分配/触发/流量逻辑继承 Core <see cref="LoopMission"/>。</para>
|
||
/// <para>导航执行由 <see cref="Mag2Car"/> 拦截 Loop 编译脚本(含 <c>Mag2Go</c> + <c>goalSite</c>),
|
||
/// 改为单次全程 <c>0xB1</c> 状态机任务。</para>
|
||
/// </summary>
|
||
[MissionType(Name = "磁导航FASS2环线", editor = typeof(Mag2LoopMission))]
|
||
public class Mag2LoopMission : LoopMission
|
||
{
|
||
[JsonIgnore] public override MissionStatus status { get; set; } = new LoopMissionStatus();
|
||
|
||
/// <summary>
|
||
/// Mag2 空闲时可能尚未 TrafficReset(holdingLocks 为空),仍按上报站点/siteID 匹配在站车辆,
|
||
/// 否则 AutoLoop 永远找不到车、加不上 goalSite。
|
||
/// </summary>
|
||
protected override Car FindCarArrivedAtSite(int siteId)
|
||
{
|
||
var byLock = base.FindCarArrivedAtSite(siteId);
|
||
if (byLock != null)
|
||
return byLock;
|
||
|
||
try
|
||
{
|
||
return SimpleLib.GetAllCars()
|
||
.OfType<Mag2Car>()
|
||
.FirstOrDefault(car =>
|
||
{
|
||
if (car?.tags == null || car.tags.Contains("occupied") || !car.tags.Contains("Online"))
|
||
return false;
|
||
if (car.status?.pendingLocks != null && car.status.pendingLocks.Length > 0)
|
||
return false;
|
||
if (car.status?.holdingLocks != null && car.status.holdingLocks.Length > 1)
|
||
return false;
|
||
|
||
var physical = car.siteID > 0 ? car.siteID : car.GetLastSite();
|
||
return physical == siteId;
|
||
});
|
||
}
|
||
catch
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分配前尽量补上本站交管锁,保证 SelectCar/GoSite 的 GetLastSite 可用。
|
||
/// </summary>
|
||
protected override void AssignCarToTarget(Car car, int targetSiteId)
|
||
{
|
||
if (car is Mag2Car mag2)
|
||
{
|
||
EnsureMag2HoldingCurrentSite(mag2);
|
||
}
|
||
|
||
base.AssignCarToTarget(car, targetSiteId);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 用当前占点匹配全部 LoopTask 展开路径(不过滤 IsViaPoint)。
|
||
/// 路径用 BFS,避免 FindRoute 污染交管锁。
|
||
/// </summary>
|
||
public static List<Fass2ChainMatch> FindChainsContaining(int siteId)
|
||
{
|
||
var matches = new List<Fass2ChainMatch>();
|
||
if (siteId <= 0)
|
||
{
|
||
return matches;
|
||
}
|
||
|
||
IEnumerable<AbstractLoopMission> missions = null;
|
||
try
|
||
{
|
||
missions = SimpleProject.proj?.Missions?.OfType<AbstractLoopMission>();
|
||
}
|
||
catch
|
||
{
|
||
missions = null;
|
||
}
|
||
|
||
if (missions == null)
|
||
{
|
||
return matches;
|
||
}
|
||
|
||
foreach (var mission in missions)
|
||
{
|
||
IReadOnlyList<LoopTask> tasks;
|
||
try
|
||
{
|
||
tasks = mission.GetTasks();
|
||
}
|
||
catch
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (tasks == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
foreach (var task in tasks)
|
||
{
|
||
var match = TryMatchTask(task, siteId);
|
||
if (match != null)
|
||
{
|
||
matches.Add(match);
|
||
}
|
||
}
|
||
}
|
||
|
||
return matches;
|
||
}
|
||
|
||
public static bool TryResolveBelongingChain(
|
||
int siteId,
|
||
int? preferGoalSiteId,
|
||
out Fass2ChainMatch selected,
|
||
out string reason)
|
||
{
|
||
var matches = FindChainsContaining(siteId);
|
||
return Fass2ReconnectChainSelector.TrySelect(matches, preferGoalSiteId, out selected, out reason);
|
||
}
|
||
|
||
private static Fass2ChainMatch TryMatchTask(LoopTask task, int siteId)
|
||
{
|
||
if (task == null || task.CurrentStationId <= 0 || task.TargetStationId <= 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
List<int> fullPath;
|
||
try
|
||
{
|
||
fullPath = Fass2RouteHelper.GetSitesBetween(task.CurrentStationId, task.TargetStationId, null);
|
||
}
|
||
catch
|
||
{
|
||
return null;
|
||
}
|
||
|
||
if (fullPath == null || fullPath.Count == 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var index = fullPath.IndexOf(siteId);
|
||
if (index < 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var remaining = fullPath.Skip(index).ToList();
|
||
return new Fass2ChainMatch
|
||
{
|
||
TaskId = task.Id,
|
||
TaskPriority = task.Priority,
|
||
CurrentSiteId = siteId,
|
||
TargetSiteId = task.TargetStationId,
|
||
IndexInPath = index,
|
||
DistanceToTarget = Math.Max(0, fullPath.Count - index - 1),
|
||
IsAtStartPoint = index == 0,
|
||
IsAtEndPoint = index == fullPath.Count - 1,
|
||
FullPath = fullPath,
|
||
RemainingPath = remaining
|
||
};
|
||
}
|
||
|
||
private static void EnsureMag2HoldingCurrentSite(Mag2Car car)
|
||
{
|
||
try
|
||
{
|
||
if (car.status?.holdingLocks != null && car.status.holdingLocks.Length == 1)
|
||
return;
|
||
|
||
var siteId = car.siteID > 0 ? car.siteID : car.GetLastSite();
|
||
if (siteId <= 0)
|
||
return;
|
||
|
||
var site = SimpleLib.GetSite(siteId);
|
||
if (site == null)
|
||
return;
|
||
|
||
car.TrafficReset(site, true, strict: false);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// 交管失败时仍允许打上 goalSite,后续 GoSite/状态机会再处理
|
||
}
|
||
}
|
||
}
|
||
}
|