using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using XP.Common.Logging.Interfaces;
using XP.Hardware.RaySource.Config;
namespace XP.Hardware.RaySource.Implementations
{
///
/// Host 进程生命周期管理器
/// 负责启动、监控和关闭 .NET Framework 4.8 Host 子进程
///
public class CometHostManager : IDisposable
{
private readonly ILoggerService _logger;
private readonly RaySourceConfig _config;
private Process _hostProcess;
private bool _isDisposed;
///
/// Host 可执行文件名
///
private const string HostExeName = "XP.Hardware.RaySource.Comet.Host.exe";
///
/// 命令管道名称(客户端写 → Host 读)
///
private const string CmdPipeName = "XP.Hardware.RaySource.Comet.Cmd";
///
/// 响应管道名称(Host 写 → 客户端读)
///
private const string RspPipeName = "XP.Hardware.RaySource.Comet.Rsp";
///
/// 等待管道就绪的超时时间(毫秒)
///
private const int PipeReadyTimeoutMs = 10000;
///
/// 等待 Host 进程退出的超时时间(毫秒)
///
private const int ShutdownTimeoutMs = 5000;
///
/// 构造函数
///
/// 日志服务
/// 射线源配置
public CometHostManager(ILoggerService loggerService, RaySourceConfig config)
{
_logger = loggerService?.ForModule() ?? throw new ArgumentNullException(nameof(loggerService));
_config = config ?? throw new ArgumentNullException(nameof(config));
}
///
/// Host 进程是否正在运行
///
public bool IsRunning => _hostProcess != null && !_hostProcess.HasExited;
///
/// 确保 Host 进程正在运行
/// 优先检查是否已有同路径的 Host 进程在运行,有则直接关联;否则启动新进程
///
public void EnsureRunning()
{
if (IsRunning)
{
_logger.Debug("Host 进程已在运行,PID={Pid}", _hostProcess.Id);
return;
}
var hostExePath = GetHostExePath();
// 检查是否已有 Host 进程在运行(例如上次异常退出后残留的进程),杀掉后重新启动
var hostProcessName = Path.GetFileNameWithoutExtension(HostExeName);
var existingProcesses = Process.GetProcessesByName(hostProcessName);
foreach (var proc in existingProcesses)
{
try
{
_logger.Info("发现残留的 Host 进程,正在终止,PID={Pid}", proc.Id);
proc.Kill();
proc.WaitForExit(1500);
}
catch (Exception ex)
{
_logger.Warn("终止残留 Host 进程失败,PID={Pid},{Message}", proc.Id, ex.Message);
}
}
_logger.Info("启动 Host 进程:{Path}", hostExePath);
if (!File.Exists(hostExePath))
{
throw new FileNotFoundException($"Host 可执行文件未找到:{hostExePath}", hostExePath);
}
var startInfo = new ProcessStartInfo
{
FileName = hostExePath,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true
};
_hostProcess = new Process { StartInfo = startInfo, EnableRaisingEvents = true };
_hostProcess.Exited += OnHostProcessExited;
_hostProcess.ErrorDataReceived += OnHostErrorDataReceived;
_hostProcess.Start();
_hostProcess.BeginErrorReadLine();
_logger.Info("Host 进程已启动,PID={Pid}", _hostProcess.Id);
// 等待 NamedPipe 就绪
WaitForPipeReady();
}
///
/// 安全关闭 Host 进程
/// 等待进程退出,超时则强制终止(DisconnectCommand 已由 CometIpcClient 发送)
///
public void Shutdown()
{
if (!IsRunning)
{
_logger.Debug("Host 进程未运行,无需关闭");
return;
}
_logger.Info("正在关闭 Host 进程,PID={Pid}", _hostProcess.Id);
try
{
// 等待进程退出(DisconnectCommand 已由 CometIpcClient 发送)
if (!_hostProcess.WaitForExit(ShutdownTimeoutMs))
{
_logger.Warn("Host 进程未在超时时间内退出,强制终止");
_hostProcess.Kill();
_hostProcess.WaitForExit(1000);
}
_logger.Info("Host 进程已关闭");
}
catch (Exception ex)
{
_logger.Warn("关闭 Host 进程时发生异常:{Message}", ex.Message);
}
finally
{
CleanupProcess();
}
}
///
/// 获取 Host 可执行文件路径
/// 优先使用配置的路径,为空时默认在主程序输出目录下查找
///
private string GetHostExePath()
{
// 优先使用配置的路径
if (!string.IsNullOrWhiteSpace(_config.HostExePath))
{
return _config.HostExePath;
}
// 默认在主程序输出目录的 Host 子目录下查找
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
return Path.Combine(baseDir, "Host", HostExeName);
}
///
/// 等待 NamedPipe 就绪
/// 通过短暂延迟和进程存活检查确认 Host 进程已启动并创建了管道
/// 使用临时 NamedPipeClientStream 尝试连接来检测管道是否就绪
///
private void WaitForPipeReady()
{
var startTime = DateTime.UtcNow;
while ((DateTime.UtcNow - startTime).TotalMilliseconds < PipeReadyTimeoutMs)
{
if (_hostProcess.HasExited)
{
throw new InvalidOperationException($"Host 进程启动后立即退出,退出码={_hostProcess.ExitCode}");
}
// 短暂等待,让 Host 进程有时间创建管道
Thread.Sleep(200);
// 进程仍在运行,认为管道即将就绪或已就绪
if (!_hostProcess.HasExited)
{
_logger.Info("Host 进程正在运行,管道应已就绪");
return;
}
}
// 超时但进程仍在运行,假设管道已就绪
if (!_hostProcess.HasExited)
{
_logger.Warn("等待管道就绪超时,但 Host 进程仍在运行,继续");
return;
}
throw new TimeoutException($"等待 Host 进程 NamedPipe 就绪超时({PipeReadyTimeoutMs}ms)");
}
///
/// Host 进程退出事件处理
///
private void OnHostProcessExited(object sender, EventArgs e)
{
if (_hostProcess != null)
{
_logger.Warn("Host 进程已退出,退出码={ExitCode}", _hostProcess.ExitCode);
}
}
///
/// Host 进程 stderr 输出事件处理,转发到主进程日志
///
private void OnHostErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
_logger.Debug("[Host stderr] {Message}", e.Data);
}
}
///
/// 清理进程资源
///
private void CleanupProcess()
{
if (_hostProcess != null)
{
_hostProcess.Exited -= OnHostProcessExited;
_hostProcess.ErrorDataReceived -= OnHostErrorDataReceived;
_hostProcess.Dispose();
_hostProcess = null;
}
}
///
/// 释放资源
///
public void Dispose()
{
if (!_isDisposed)
{
Shutdown();
_isDisposed = true;
}
}
}
}