diff --git a/StandardScene.Magnetic/Scheduler/EventCarMission.cs b/StandardScene.Magnetic/Scheduler/EventCarMission.cs
index 451f462..2d92eaf 100644
--- a/StandardScene.Magnetic/Scheduler/EventCarMission.cs
+++ b/StandardScene.Magnetic/Scheduler/EventCarMission.cs
@@ -8,6 +8,7 @@ using SimpleCore.PropType;
using StandardScene;
using StandardScene.Scheduler.FassEvent;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -68,6 +69,9 @@ namespace StandardScene.Scheduler
/// 记录每辆车上一周期的 CurrentNode,用于 PrevNode 触发与地标变化时清除任务防重复标记。
[JsonIgnore] private readonly Dictionary _prevNodeByCarId = new Dictionary();
+ /// 每车最多一个在飞 Tick,避免断网重试把 Task.Run 堆爆、同一车并发下发。
+ [JsonIgnore] private readonly ConcurrentDictionary _tickInFlight = new ConcurrentDictionary();
+
/// 进程运行状态扩展字段。
public class EventCarMissionStatus : MissionStatus
{
@@ -140,9 +144,9 @@ namespace StandardScene.Scheduler
Diagnosis.Post($"FASS EventCar 配置已加载:{path},事件数={_eventCars.Count}", "FassEventCar", true);
}
- /// 停止轮询并等待工作线程结束(最多 2 秒)。
+ /// 停止轮询并等待工作线程结束(最多 2 秒)。覆盖基类 Stop,避免全量重载后线程仍在对全场车下发。
[MethodMember(Name = "停止进程", Description = "停止 FASS 车辆事件轮询")]
- public void Stop()
+ public override void Stop()
{
_started = false;
try
@@ -153,8 +157,10 @@ namespace StandardScene.Scheduler
{
}
- status.status = "已停止";
+ _workerThread = null;
Diagnosis.Post("FASS车辆事件进程已停止", "FassEventCar", true);
+ base.Stop();
+ status.status = "已停止";
}
///
@@ -182,14 +188,18 @@ namespace StandardScene.Scheduler
}
///
- /// 遍历所有 Car,构建快照并异步调用 Handler(与 FASS Task.Run 行为一致)。
- ///
- /// 跳过 status 为空或 usage.refreshing=false 的车辆(未参与调度刷新的车不处理)。
- /// 当地标变化时清除 ,允许在新地标重新触发事件。
- ///
+ /// 遍历所有 Car,构建快照后 Task.Run 处理:一车断网不堵其它车。
+ /// 同一车上一拍未结束则跳过本拍,禁止无界堆积。
///
private void ProcessAllCars()
{
+ var handler = _handler;
+ var eventCars = _eventCars;
+ if (handler == null || eventCars == null)
+ {
+ return;
+ }
+
foreach (var car in SimpleLib.GetAllCars().OfType())
{
if (car.status == null)
@@ -207,13 +217,37 @@ namespace StandardScene.Scheduler
_prevNodeByCarId.TryGetValue(car.id, out var prevNode);
var snapshot = FassEventCarSnapshotFactory.Create(car, prevNode, FassEventCarSnapshotFactory.GetNextNodeCode(car));
- Task.Run(() => _handler.HandleCarTimerTick(car, snapshot, _eventCars));
-
if (!string.Equals(prevNode, currentNode, StringComparison.OrdinalIgnoreCase))
{
_prevNodeByCarId[car.id] = currentNode;
ClearTaskTags(car);
}
+
+ if (!_tickInFlight.TryAdd(car.id, 0))
+ {
+ continue;
+ }
+
+ var carId = car.id;
+ Task.Run(() =>
+ {
+ try
+ {
+ if (_started)
+ handler.HandleCarTimerTick(car, snapshot, eventCars);
+ }
+ catch (Exception ex)
+ {
+ Diagnosis.Post(
+ $"FASS车辆事件处理异常 car={carId}:{ExceptionFormatter.FormatEx(ex)}",
+ "FassEventCar",
+ true);
+ }
+ finally
+ {
+ _tickInFlight.TryRemove(carId, out _);
+ }
+ });
}
}
diff --git a/StandardScene.Magnetic/Scheduler/FassEvent/FassEventCarHandler.cs b/StandardScene.Magnetic/Scheduler/FassEvent/FassEventCarHandler.cs
index 4786507..fce9010 100644
--- a/StandardScene.Magnetic/Scheduler/FassEvent/FassEventCarHandler.cs
+++ b/StandardScene.Magnetic/Scheduler/FassEvent/FassEventCarHandler.cs
@@ -43,7 +43,7 @@ namespace StandardScene.Scheduler.FassEvent
}
///
- /// 单次 TimerTick 对一辆车的处理入口(通常在 Task.Run 中调用)。
+ /// 单次 TimerTick 对一辆车的处理入口(由 EventCarMission 按车 Task.Run,同车同时只跑一拍)。
/// 仅处理 TriggerEvent=TimerTick 的配置项。
///
public void HandleCarTimerTick(Car car, FassEventCarSnapshot snapshot, IReadOnlyList eventCars)