Files
StandardSence/StandardScene.Signal/Mission/MagTrafficMission.cs
T

194 lines
7.7 KiB
C#

using System;
using System.IO;
using System.Threading;
using LessokajiWeaverUtilities.Utilities;
using Newtonsoft.Json;
using SimpleCore;
using SimpleCore.Library;
using SimpleLite.RCS;
using StandardScene.Signal.Logic;
using StandardScene.Signal.Model;
using StandardScene.Signal.Ui;
using StandardScene.Utils;
namespace StandardScene.Signal
{
/// <summary>
/// 磁条交管进程:只针对 MagCar。同区名排队后,按「本线 + 冲突对向线」站点并集查占用,容量允许则发启动。
/// </summary>
/// <remarks>
/// 配置 <c>Config/Signal/mag-control-areas.json</c>。
/// 同「管控区名称」绑成一个路口排队;放行时合并本线与冲突对向线的管控站点列表统计当前占用。
/// 不使用 pending 在途占额。不处理 Mag2Car。
/// </remarks>
[MissionType(Name = "磁条交管进程", editor = typeof(MagTrafficMission))]
[I18N.DocumentTranslation(Name = "Magnetic Traffic Mission", locale = "en")]
public class MagTrafficMission : Mission
{
/// <summary>扫车周期(毫秒)。停在触发点的 MagCar 每拍排队判定一次。</summary>
[FieldMember] public int TickMs = 1000;
/// <summary>管控区表路径。相对路径只解析工作目录 <c>Config/Signal</c>。</summary>
[FieldMember] public string AreasPath = @"Config\Signal\mag-control-areas.json";
/// <summary>放行日志根目录。实际按车名落在其下 mag-traffic/car{name}/release_yyyyMMdd.log。</summary>
[FieldMember] public string LogDirectory = "log";
/// <summary>是否把交管放行写入本地文件留档。</summary>
[FieldMember] public bool EnableFileLog = true;
[JsonIgnore] private bool _started;
[JsonIgnore] private readonly MagControlRuntime _runtime = new MagControlRuntime();
[JsonIgnore] private CancellationTokenSource _cts;
[JsonIgnore] private Thread _thread;
public class MagTrafficMissionStatus : MissionStatus
{
public int RuleCount { get; set; }
public int WaitingAtTrigger { get; set; }
public int PendingInFlight { get; set; }
public int ScanTickCount { get; set; }
public string AreasFile { get; set; }
}
public override MissionStatus status { get; set; } = new MagTrafficMissionStatus();
public static Mission Create() => new MagTrafficMission();
[MethodMember(Name = "启动进程", Description = "加载管控区表,按区名排队后对 MagCar 发放行")]
public override void Execute()
{
try
{
MagTrafficFileLogger.Configure(LogDirectory, EnableFileLog);
ReloadConfig();
_started = true;
StartWatch();
MagTrafficFileLogger.WriteSystem($"进程启动 规则={((MagTrafficMissionStatus)status).RuleCount} 配置={((MagTrafficMissionStatus)status).AreasFile}");
Diagnosis.Post(
$"磁条交管进程已启动,规则={((MagTrafficMissionStatus)status).RuleCount}(仅 MagCar),放行日志={MagTrafficFileLogger.GetLogDirectory()}",
"MagTraffic", true);
}
catch (Exception ex)
{
status.status = "启动失败";
Diagnosis.Post($"磁条交管进程启动失败:{ExceptionFormatter.FormatEx(ex)}", "MagTraffic", true);
throw;
}
}
[MethodMember(Name = "重新加载配置", Description = "重新读取 mag-control-areas.json")]
public void ReloadConfig()
{
var file = SignalConfigStore.Resolve(AreasPath);
var rows = SignalConfigStore.Load<MagControlAreaModel>(file);
_runtime.Replace(rows);
var st = (MagTrafficMissionStatus)status;
st.AreasFile = file;
st.RuleCount = _runtime.RuleCount;
st.PendingInFlight = _runtime.PendingCount;
status.status = _started ? "运行中" : "配置已加载";
MagTrafficFileLogger.Configure(LogDirectory, EnableFileLog);
MagTrafficFileLogger.WriteSystem($"重新加载配置 规则={st.RuleCount} 文件={file}");
Diagnosis.Post($"磁条交管配置已加载:启用规则 {st.RuleCount} @ {file}", "MagTraffic", true);
}
[MethodMember(Name = "停止进程")]
public new void Stop()
{
_started = false;
StopWatch();
status.status = "已停止";
MagTrafficFileLogger.WriteSystem("进程停止");
Diagnosis.Post("磁条交管进程已停止", "MagTraffic", true);
}
[MethodMember(Name = "打开放行日志目录", Description = "打开 mag-traffic 目录(按 car{name} 分子目录),便于按车查阅放行记录")]
public void OpenReleaseLogFolder()
{
try
{
MagTrafficFileLogger.Configure(LogDirectory, EnableFileLog);
var dir = MagTrafficFileLogger.GetLogDirectory();
Directory.CreateDirectory(dir);
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
{
FileName = dir,
UseShellExecute = true
});
}
catch (Exception ex)
{
CycleUiHelper.Alert("错误", $"打开放行日志目录失败:{ex.Message}");
}
}
[MethodMember(Name = "打开管控区列表", Description = "编辑区名 / 兼容组 / 优先级 / 等待时长 / 触发点 / 区内站点 / 容量")]
public void OpenAreas()
{
try
{
var path = SignalConfigStore.Resolve(AreasPath);
ModelListPanel<MagControlAreaModel>.Open(
"磁条管控区配置",
path,
x => x == null ? "" : x.TriggerSit.ToString());
}
catch (Exception ex)
{
CycleUiHelper.Alert("错误", $"打开管控区列表失败:{ex.Message}");
}
}
private void StartWatch()
{
StopWatch();
_cts = new CancellationTokenSource();
_thread = new Thread(WatchLoop)
{
IsBackground = true,
Name = "MagTrafficWatch"
};
_thread.Start();
}
private void StopWatch()
{
try { _cts?.Cancel(); }
catch { }
try { _thread?.Join(2000); }
catch { }
_thread = null;
}
private void WatchLoop()
{
var token = _cts.Token;
var tick = 0;
var interval = Math.Max(200, TickMs);
while (!token.IsCancellationRequested)
{
try
{
var (waiting, _) = _runtime.ScanAndRelease();
var st = (MagTrafficMissionStatus)status;
st.WaitingAtTrigger = waiting;
st.PendingInFlight = _runtime.PendingCount;
st.RuleCount = _runtime.RuleCount;
st.ScanTickCount = ++tick;
if (_started)
status.status = $"运行中 规则{st.RuleCount} 等待{waiting}";
}
catch (Exception ex)
{
Diagnosis.Log($"磁条交管扫车异常:{ex.Message}", "MagTraffic", true);
}
try { Thread.Sleep(interval); }
catch (ThreadInterruptedException) { break; }
}
}
}
}