using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading; using Netch.Models; using Netch.Utils; using Timer = System.Timers.Timer; namespace Netch.Controllers { public abstract class Guard { public abstract string Name { get; protected set; } /// /// 主程序名 /// public abstract string MainFile { get; protected set; } protected State State { get; set; } = State.Waiting; public abstract void Stop(); /// /// 成功启动关键词 /// protected readonly List StartedKeywords = new List(); /// /// 启动失败关键词 /// protected readonly List StoppedKeywords = new List(); /// /// 进程是否可以重定向输出 /// protected bool RedirectStd { get; set; } = true; /// /// 进程实例 /// public Process Instance { get; private set; } /// /// 日志文件(重定向输出文件) /// private string _logPath; private readonly StringBuilder _logBuffer = new StringBuilder(); /// /// 程序输出的编码, /// 调用于基类的 /// protected Encoding InstanceOutputEncoding { get; set; } = Encoding.GetEncoding("gbk"); /// /// 停止进程 /// protected void StopInstance() { try { if (Instance == null || Instance.HasExited) return; Instance.Kill(); Instance.WaitForExit(); } catch (Win32Exception e) { Logging.Error($"停止 {MainFile} 错误:\n" + e); } catch { // ignored } } /// /// 仅初始化 ,不设定事件处理方法 /// /// protected void InitInstance(string argument) { Instance = new Process { StartInfo = { FileName = Path.GetFullPath($"bin\\{MainFile}"), WorkingDirectory = $"{Global.NetchDir}\\bin", Arguments = argument, CreateNoWindow = true, UseShellExecute = !RedirectStd, RedirectStandardOutput = RedirectStd, StandardOutputEncoding = RedirectStd ? InstanceOutputEncoding : null, RedirectStandardError = RedirectStd, StandardErrorEncoding = RedirectStd ? InstanceOutputEncoding : null, WindowStyle = ProcessWindowStyle.Hidden } }; } /// /// 默认行为启动主程序 /// /// 主程序启动参数 /// 进程优先级 /// 是否成功启动 protected bool StartInstanceAuto(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal) { State = State.Starting; try { // 初始化程序 InitInstance(argument); Instance.EnableRaisingEvents = true; if (RedirectStd) { // 清理日志 _logPath ??= Path.Combine(Global.NetchDir, $"logging\\{Name}.log"); if (File.Exists(_logPath)) File.Delete(_logPath); Instance.OutputDataReceived += OnOutputDataReceived; Instance.ErrorDataReceived += OnOutputDataReceived; } Instance.Exited += OnExited; // 启动程序 Instance.Start(); if (priority != ProcessPriorityClass.Normal) Instance.PriorityClass = priority; if (!RedirectStd) return true; // 启动日志重定向 Instance.BeginOutputReadLine(); Instance.BeginErrorReadLine(); SaveBufferTimer.Elapsed += SaveBufferTimerEvent; SaveBufferTimer.Enabled = true; if (StartedKeywords.Count == 0) return true; // 等待启动 for (var i = 0; i < 1000; i++) { Thread.Sleep(10); switch (State) { case State.Started: return true; case State.Stopped: Logging.Error($"{Name} 控制器启动失败"); Stop(); return false; } } Logging.Error($"{Name} 控制器启动超时"); Stop(); return false; } catch (Exception e) { Logging.Error($"{Name} 控制器启动失败:\n {e}"); return false; } } private static readonly Timer SaveBufferTimer = new Timer(300) {AutoReset = true}; private void OnExited(object sender, EventArgs e) { if (RedirectStd) { SaveBufferTimer.Enabled = false; } SaveBufferTimerEvent(null, null); State = State.Stopped; } /// /// 接收输出数据 /// /// 发送者 /// 数据 protected void OnOutputDataReceived(object sender, DataReceivedEventArgs e) { // 程序结束, 接收到 null if (e.Data == null) return; Write(e.Data); // 检查启动 if (State == State.Starting) { if (StartedKeywords.Any(s => e.Data.Contains(s))) State = State.Started; else if (StoppedKeywords.Any(s => e.Data.Contains(s))) State = State.Stopped; } } /// /// 计时器存储日志 /// /// /// private void SaveBufferTimerEvent(object sender, EventArgs e) { try { if (_logPath != null && _logBuffer != null) { File.AppendAllText(_logPath, _logBuffer.ToString()); _logBuffer.Clear(); } } catch (Exception exception) { Logging.Warning($"写入 {Name} 日志错误:\n" + exception.Message); } } /// /// 写入日志文件缓冲 /// /// /// 转码后的字符串 private void Write(string info) { _logBuffer.Append(info + Global.EOF); } } }