Files
StandardSence/StandardScene.Core/Scheduler/RegionalTrafficControlMission.cs
T
2026-06-14 11:19:15 +08:00

433 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using SimpleLite.RCS;
using SimpleLite.RCS.CarTypes;
using SimpleCore;
using Newtonsoft.Json;
using SimpleLite.CADTools;
using SimpleLite.Props;
using SimpleLite.UI;
using SimpleCore.Library;
using System.Threading.Tasks;
using System.Diagnostics;
using SimpleCore.Traffic;
using SimpleCore.PropType;
using LessokajiWeaverUtilities.Utilities;
namespace StandardScene.Scheduler
{
[MissionType(Name = "区域流量监控", editor = typeof(RegionalTrafficControlMission))]
[I18N.DocumentTranslation(Name = "Regional Traffic Control Mission", locale = "en")]
public class RegionalTrafficControlMission : Mission
{
public static Mission Create()
{
return new RegionalTrafficControlMission();
}
/// <summary>
/// 区域流量监控状态
/// </summary>
public class RegionalTrafficControlMissionStatus : MissionStatus
{
/// <summary>
/// 区域车辆统计信息:区域编号 -> 当前车辆数量
/// </summary>
public Dictionary<string, int> RegionVehicleCounts { get; set; } = new Dictionary<string, int>();
/// <summary>
/// 区域限制信息:区域编号 -> 最大允许车辆数
/// </summary>
public Dictionary<string, int> RegionMaxCounts { get; set; } = new Dictionary<string, int>();
/// <summary>
/// 被阻止的车辆统计:区域编号 -> 被阻止次数
/// </summary>
public Dictionary<string, int> BlockedCounts { get; set; } = new Dictionary<string, int>();
}
public override MissionStatus status { get; set; } = new RegionalTrafficControlMissionStatus();
[JsonIgnore] private bool started = false;
[JsonIgnore] private object sync = new object();
/// <summary>
/// 区域内的车辆记录:区域编号 -> 车辆ID列表
/// </summary>
[JsonIgnore] private readonly Dictionary<string, HashSet<int>> carsInRegions = new Dictionary<string, HashSet<int>>();
/// <summary>
/// 启动进程
/// </summary>
[MethodMember(Name = "启动进程", Description = "开始区域流量监控")]
public override void Execute()
{
if (started)
{
Diagnosis.Post("区域流量监控已在运行中", "区域流量监控");
return;
}
started = true;
status.status = "运行中";
// 订阅交通控制事件
TrafficControl.BeforeLock += BeforeLockEvent;
TrafficControl.AfterLeave += AfterLeaveEvent;
TrafficControl.OnLockAcquired += OnLockAcquiredEvent;
// 启动统计更新任务
Task.Factory.StartNew(() =>
{
while (started)
{
try
{
UpdateRegionStatistics();
Thread.Sleep(500);
}
catch (Exception e)
{
Diagnosis.Post($"{ExceptionFormatter.FormatEx(e)}", "区域流量监控");
}
}
});
Diagnosis.Post("区域流量监控已启动", "区域流量监控", true);
}
/// <summary>
/// 停止进程
/// </summary>
[MethodMember(Name = "停止进程", Description = "停止区域流量监控")]
public void Stop()
{
if (!started)
{
return;
}
started = false;
// 取消事件订阅
TrafficControl.BeforeLock -= BeforeLockEvent;
TrafficControl.AfterLeave -= AfterLeaveEvent;
TrafficControl.OnLockAcquired -= OnLockAcquiredEvent;
status.status = "已停止";
Diagnosis.Post("区域流量监控已停止", "区域流量监控", true);
}
/// <summary>
/// 获取站点的区域信息
/// </summary>
/// <param name="site">站点</param>
/// <returns>区域标识和限制数量,如果站点不属于任何区域则返回null</returns>
private (string regionId, int maxCount)? GetRegionInfo(Site site)
{
if (site == null || site.fields == null)
{
return null;
}
// 查找所有以Region开头的字段
var regionField = site.fields.FirstOrDefault(kvp => kvp.Key.StartsWith("Region", StringComparison.OrdinalIgnoreCase));
if (regionField.Key == null)
{
return null; // 站点不属于任何区域
}
string regionId = regionField.Key; // 例如 "Region1"
// 字段值就是限制数量
if (!int.TryParse(regionField.Value, out int maxCount))
{
return null; // 无法解析限制数量
}
return (regionId, maxCount);
}
/// <summary>
/// 检查车辆是否已经在指定区域内
/// </summary>
private bool IsCarAlreadyInRegion(AbstractCar car, string regionId)
{
if (car?.status == null)
{
return false;
}
// 检查车辆当前占用的站点是否属于该区域
if (car.status.holdingLocks != null)
{
foreach (var lockedSiteId in car.status.holdingLocks)
{
var lockedSite = SimpleLib.GetSite(lockedSiteId);
var regionInfo = GetRegionInfo(lockedSite);
if (regionInfo.HasValue && regionInfo.Value.regionId == regionId)
{
return true; // 车辆已经在区域内
}
}
}
return false;
}
/// <summary>
/// BeforeLock事件处理:检查车辆是否可以进入区域
/// </summary>
private bool BeforeLockEvent(AbstractCar car, int siteId)
{
try
{
var site = SimpleLib.GetSite(siteId);
if (site == null)
{
return true;
}
// 获取站点的区域信息
var regionInfo = GetRegionInfo(site);
if (!regionInfo.HasValue)
{
return true; // 站点不属于任何区域,允许通过
}
var (regionId, maxCount) = regionInfo.Value;
// 如果最大数量为0或负数,表示不限制
if (maxCount <= 0)
{
return true;
}
// 检查车辆是否已经在区域内
lock (sync)
{
if (IsCarAlreadyInRegion(car, regionId))
{
// 车辆已经在区域内,允许通行
return true;
}
// 车辆即将进入区域,需要检查是否超过限制
// 排除当前车辆本身,因为它还没有进入区域
int currentCount = CountCarsInRegion(regionId, excludeCarId: car.id);
// 如果当前车辆数量已达到或超过限制,阻止进入
if (currentCount >= maxCount)
{
var statusObj = (RegionalTrafficControlMissionStatus)status;
if (!statusObj.BlockedCounts.ContainsKey(regionId))
{
statusObj.BlockedCounts[regionId] = 0;
}
statusObj.BlockedCounts[regionId]++;
Diagnosis.Log($"车辆 {car.id} 尝试进入区域 {regionId} 被阻止,当前车辆数: {currentCount}/{maxCount}", "区域流量监控", true);
return false; // 阻止进入
}
return true; // 允许进入
}
}
catch (Exception e)
{
Diagnosis.Post($"BeforeLockEvent错误: {ExceptionFormatter.FormatEx(e)}", "区域流量监控");
return true; // 发生错误时允许通过,避免阻塞系统
}
}
/// <summary>
/// AfterLeave事件处理:车辆离开区域时更新统计
/// </summary>
private void AfterLeaveEvent(AbstractCar car, int siteId)
{
try
{
var site = SimpleLib.GetSite(siteId);
if (site == null)
{
return;
}
// 获取站点的区域信息
var regionInfo = GetRegionInfo(site);
if (!regionInfo.HasValue)
{
return; // 站点不属于任何区域
}
string regionId = regionInfo.Value.regionId;
// 从区域车辆列表中移除该车辆
lock (sync)
{
if (carsInRegions.TryGetValue(regionId, out var carSet))
{
carSet.Remove(car.id);
if (carSet.Count == 0)
{
carsInRegions.Remove(regionId);
}
}
}
}
catch (Exception e)
{
Diagnosis.Post($"AfterLeaveEvent错误: {ExceptionFormatter.FormatEx(e)}", "区域流量监控");
}
}
/// <summary>
/// OnLockAcquired事件处理:车辆成功锁定站点时,将其加入区域统计
/// </summary>
private void OnLockAcquiredEvent(AbstractCar car, int siteId)
{
try
{
var site = SimpleLib.GetSite(siteId);
if (site == null)
{
return;
}
// 获取站点的区域信息
var regionInfo = GetRegionInfo(site);
if (!regionInfo.HasValue)
{
return; // 站点不属于任何区域
}
string regionId = regionInfo.Value.regionId;
// 将车辆添加到区域车辆列表中
lock (sync)
{
if (!carsInRegions.TryGetValue(regionId, out var carSet))
{
carSet = new HashSet<int>();
carsInRegions[regionId] = carSet;
}
carSet.Add(car.id);
}
}
catch (Exception e)
{
Diagnosis.Post($"OnLockAcquiredEvent错误: {ExceptionFormatter.FormatEx(e)}", "区域流量监控");
}
}
/// <summary>
/// 统计指定区域内的车辆数量
/// </summary>
/// <param name="regionId">区域标识</param>
/// <param name="excludeCarId">要排除的车辆ID(可选,用于排除即将进入的车辆)</param>
/// <returns>区域内的车辆数量</returns>
private int CountCarsInRegion(string regionId, int excludeCarId = -1)
{
lock (sync)
{
// 首先从已记录的车辆列表中统计
int count = 0;
if (carsInRegions.TryGetValue(regionId, out var recordedCars))
{
count = recordedCars.Count(carId => carId != excludeCarId);
}
// 同时检查所有站点,统计属于该区域且当前有车辆占用的站点
var regionSites = SimpleLib.GetAllSites()
.Where(s =>
{
var regionInfo = GetRegionInfo(s);
return regionInfo.HasValue && regionInfo.Value.regionId == regionId;
})
.ToList();
// 统计这些站点上的车辆(只统计已锁定的,不包括正在前往的)
// 因为正在前往的车辆还没有真正进入区域
var carsInRegionSites = SimpleLib.GetAllCars()
.OfType<Car>()
.Where(car =>
{
if (car?.status == null)
return false;
// 排除指定车辆
if (car.id == excludeCarId)
return false;
// 只统计已经在区域内的站点(holdingLocks
// 不统计pendingLocks,因为那些车辆还没有真正进入区域
bool inRegionSite = car.status.holdingLocks?.Any(siteId =>
{
var site = SimpleLib.GetSite(siteId);
var regionInfo = GetRegionInfo(site);
return regionInfo.HasValue && regionInfo.Value.regionId == regionId;
}) == true;
return inRegionSite;
})
.Select(car => car.id)
.ToHashSet();
// 返回两者中的较大值,确保统计准确
return Math.Max(count, carsInRegionSites.Count);
}
}
/// <summary>
/// 更新区域统计信息
/// </summary>
private void UpdateRegionStatistics()
{
try
{
lock (sync)
{
var statusObj = (RegionalTrafficControlMissionStatus)status;
statusObj.RegionVehicleCounts.Clear();
statusObj.RegionMaxCounts.Clear();
// 获取所有有区域字段的站点,按区域分组
var regionSites = SimpleLib.GetAllSites()
.Select(s =>
{
var regionInfo = GetRegionInfo(s);
return regionInfo.HasValue ? (site: s, regionId: regionInfo.Value.regionId, maxCount: regionInfo.Value.maxCount) : (site: (Site)null, regionId: (string)null, maxCount: 0);
})
.Where(x => x.site != null && !string.IsNullOrEmpty(x.regionId))
.GroupBy(x => x.regionId)
.ToList();
foreach (var group in regionSites)
{
string regionId = group.Key;
var firstItem = group.First();
// 获取最大允许数量(从第一个站点的字段值获取)
int maxCount = firstItem.maxCount;
// 统计当前车辆数量
int currentCount = CountCarsInRegion(regionId);
statusObj.RegionMaxCounts[regionId] = maxCount;
statusObj.RegionVehicleCounts[regionId] = currentCount;
}
}
}
catch (Exception e)
{
Diagnosis.Post($"UpdateRegionStatistics错误: {ExceptionFormatter.FormatEx(e)}", "区域流量监控");
}
}
}
}