using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using LessokajiWeaverUtilities.Utilities; using SimpleCore; using SimpleCore.Library; using Simple3; using Simple3.Props; using Simple3.RCS; using Simple3.RCS.CarTypes; using StandardScene.CarTypes; using StandardScene.Chained; using StandardScene.Magnetic.Tasking; using StandardScene.Model; namespace StandardScene { /// /// 磁导航 FASS 2.0 环线任务进程。 /// 任务分配/触发/流量逻辑继承 Core /// 导航执行由 拦截 Loop 编译脚本(含 Mag2Go + goalSite), /// 改为单次全程 0xB1 状态机任务。 /// [MissionType(Name = "磁导航FASS2环线", editor = typeof(Mag2LoopMission))] public class Mag2LoopMission : LoopMission { [JsonIgnore] public override MissionStatus status { get; set; } = new LoopMissionStatus(); /// /// Mag2 空闲时可能尚未 TrafficReset(holdingLocks 为空),仍按上报站点/siteID 匹配在站车辆, /// 否则 AutoLoop 永远找不到车、加不上 goalSite。 /// protected override Car FindCarArrivedAtSite(int siteId) { var byLock = base.FindCarArrivedAtSite(siteId); if (byLock != null) return byLock; try { return SimpleLib.GetAllCars() .OfType() .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; } } /// /// 分配前尽量补上本站交管锁,保证 SelectCar/GoSite 的 GetLastSite 可用。 /// protected override void AssignCarToTarget(Car car, int targetSiteId) { if (car is Mag2Car mag2 && !EnsureMag2HoldingCurrentSite(mag2)) { return; } base.AssignCarToTarget(car, targetSiteId); } /// /// 用当前占点匹配全部 LoopTask 展开路径(不过滤 IsViaPoint)。 /// 路径用 BFS,避免 FindRoute 污染交管锁。 /// public static List FindChainsContaining(int siteId) { var matches = new List(); if (siteId <= 0) { return matches; } IEnumerable missions = null; try { missions = SimpleProject.proj?.Missions?.OfType(); } catch { missions = null; } if (missions == null) { return matches; } foreach (var mission in missions) { IReadOnlyList 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 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 bool EnsureMag2HoldingCurrentSite(Mag2Car car) { try { if (car.status?.holdingLocks != null && car.status.holdingLocks.Length == 1) return true; var siteId = car.siteID > 0 ? car.siteID : car.GetLastSite(); if (siteId <= 0) return false; var site = SimpleLib.GetSite(siteId); if (site == null) return false; car.TrafficReset(site, true, strict: false); return true; } catch (Exception ex) { Diagnosis.Post( $"Mag2 补交管锁失败,跳过派车 car={car?.id}:{ex.Message}", "Mag2Loop", true); return false; } } } }