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 { /// /// 磁条交管进程:只针对 MagCar。同区名排队后,按「本线 + 冲突对向线」站点并集查占用,容量允许则发启动。 /// /// /// 配置 Config/Signal/mag-control-areas.json。 /// 同「管控区名称」绑成一个路口排队;放行时合并本线与冲突对向线的管控站点列表统计当前占用。 /// 不使用 pending 在途占额。不处理 Mag2Car。 /// [MissionType(Name = "磁条交管进程", editor = typeof(MagTrafficMission))] [I18N.DocumentTranslation(Name = "Magnetic Traffic Mission", locale = "en")] public class MagTrafficMission : Mission { /// 扫车周期(毫秒)。停在触发点的 MagCar 每拍排队判定一次。 [FieldMember] public int TickMs = 1000; /// 管控区表路径。相对路径只解析工作目录 Config/Signal [FieldMember] public string AreasPath = @"Config\Signal\mag-control-areas.json"; /// 放行日志根目录。实际按车名落在其下 mag-traffic/car{name}/release_yyyyMMdd.log。 [FieldMember] public string LogDirectory = "log"; /// 是否把交管放行写入本地文件留档。 [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(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.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; } } } } }