433 lines
15 KiB
C#
433 lines
15 KiB
C#
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)}", "区域流量监控");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|