fix: EventCar 同车 Tick 串行并覆盖基类 Stop

上一拍未结束则跳过本拍,避免断网重试堆积;Stop 调用 base.Stop 并清空工作线程。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
黄兆尉
2026-08-26 23:03:55 +08:00
co-authored by Cursor
parent d9bf8b9c84
commit 756dea82f8
2 changed files with 45 additions and 11 deletions
@@ -8,6 +8,7 @@ using SimpleCore.PropType;
using StandardScene; using StandardScene;
using StandardScene.Scheduler.FassEvent; using StandardScene.Scheduler.FassEvent;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -68,6 +69,9 @@ namespace StandardScene.Scheduler
/// <summary>记录每辆车上一周期的 CurrentNode,用于 PrevNode 触发与地标变化时清除任务防重复标记。</summary> /// <summary>记录每辆车上一周期的 CurrentNode,用于 PrevNode 触发与地标变化时清除任务防重复标记。</summary>
[JsonIgnore] private readonly Dictionary<int, string> _prevNodeByCarId = new Dictionary<int, string>(); [JsonIgnore] private readonly Dictionary<int, string> _prevNodeByCarId = new Dictionary<int, string>();
/// <summary>每车最多一个在飞 Tick,避免断网重试把 Task.Run 堆爆、同一车并发下发。</summary>
[JsonIgnore] private readonly ConcurrentDictionary<int, byte> _tickInFlight = new ConcurrentDictionary<int, byte>();
/// <summary>进程运行状态扩展字段。</summary> /// <summary>进程运行状态扩展字段。</summary>
public class EventCarMissionStatus : MissionStatus public class EventCarMissionStatus : MissionStatus
{ {
@@ -140,9 +144,9 @@ namespace StandardScene.Scheduler
Diagnosis.Post($"FASS EventCar 配置已加载:{path},事件数={_eventCars.Count}", "FassEventCar", true); Diagnosis.Post($"FASS EventCar 配置已加载:{path},事件数={_eventCars.Count}", "FassEventCar", true);
} }
/// <summary>停止轮询并等待工作线程结束(最多 2 秒)。</summary> /// <summary>停止轮询并等待工作线程结束(最多 2 秒)。覆盖基类 Stop,避免全量重载后线程仍在对全场车下发。</summary>
[MethodMember(Name = "停止进程", Description = "停止 FASS 车辆事件轮询")] [MethodMember(Name = "停止进程", Description = "停止 FASS 车辆事件轮询")]
public void Stop() public override void Stop()
{ {
_started = false; _started = false;
try try
@@ -153,8 +157,10 @@ namespace StandardScene.Scheduler
{ {
} }
status.status = "已停止"; _workerThread = null;
Diagnosis.Post("FASS车辆事件进程已停止", "FassEventCar", true); Diagnosis.Post("FASS车辆事件进程已停止", "FassEventCar", true);
base.Stop();
status.status = "已停止";
} }
/// <summary> /// <summary>
@@ -182,14 +188,18 @@ namespace StandardScene.Scheduler
} }
/// <summary> /// <summary>
/// 遍历所有 Car,构建快照并异步调用 Handler(与 FASS Task.Run 行为一致) /// 遍历所有 Car,构建快照后 Task.Run 处理:一车断网不堵其它车
/// <para> /// 同一车上一拍未结束则跳过本拍,禁止无界堆积。
/// 跳过 status 为空或 usage.refreshing=false 的车辆(未参与调度刷新的车不处理)。
/// 当地标变化时清除 <see cref="FassEventCarHandler.ActiveTaskTag"/>,允许在新地标重新触发事件。
/// </para>
/// </summary> /// </summary>
private void ProcessAllCars() private void ProcessAllCars()
{ {
var handler = _handler;
var eventCars = _eventCars;
if (handler == null || eventCars == null)
{
return;
}
foreach (var car in SimpleLib.GetAllCars().OfType<Car>()) foreach (var car in SimpleLib.GetAllCars().OfType<Car>())
{ {
if (car.status == null) if (car.status == null)
@@ -207,13 +217,37 @@ namespace StandardScene.Scheduler
_prevNodeByCarId.TryGetValue(car.id, out var prevNode); _prevNodeByCarId.TryGetValue(car.id, out var prevNode);
var snapshot = FassEventCarSnapshotFactory.Create(car, prevNode, FassEventCarSnapshotFactory.GetNextNodeCode(car)); var snapshot = FassEventCarSnapshotFactory.Create(car, prevNode, FassEventCarSnapshotFactory.GetNextNodeCode(car));
Task.Run(() => _handler.HandleCarTimerTick(car, snapshot, _eventCars));
if (!string.Equals(prevNode, currentNode, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(prevNode, currentNode, StringComparison.OrdinalIgnoreCase))
{ {
_prevNodeByCarId[car.id] = currentNode; _prevNodeByCarId[car.id] = currentNode;
ClearTaskTags(car); 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 _);
}
});
} }
} }
@@ -43,7 +43,7 @@ namespace StandardScene.Scheduler.FassEvent
} }
/// <summary> /// <summary>
/// 单次 TimerTick 对一辆车的处理入口(通常在 Task.Run 中调用)。 /// 单次 TimerTick 对一辆车的处理入口(由 EventCarMission 按车 Task.Run,同车同时只跑一拍)。
/// 仅处理 TriggerEvent=TimerTick 的配置项。 /// 仅处理 TriggerEvent=TimerTick 的配置项。
/// </summary> /// </summary>
public void HandleCarTimerTick(Car car, FassEventCarSnapshot snapshot, IReadOnlyList<FassEventCarConfig> eventCars) public void HandleCarTimerTick(Car car, FassEventCarSnapshot snapshot, IReadOnlyList<FassEventCarConfig> eventCars)