You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
3.7 KiB
C#
133 lines
3.7 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Sln.Wcs.UI.Services;
|
|
|
|
public interface IEngineProcessService
|
|
{
|
|
bool IsRunning { get; }
|
|
string StatusText { get; }
|
|
string? LastError { get; }
|
|
event Action<string>? OutputReceived;
|
|
event Action? StateChanged;
|
|
Task StartAsync();
|
|
void Stop();
|
|
}
|
|
|
|
public class EngineProcessService : IEngineProcessService
|
|
{
|
|
private Process? _process;
|
|
private readonly HttpClient _http = new() { Timeout = TimeSpan.FromSeconds(3) };
|
|
private const string BaseUrl = "http://localhost:5100";
|
|
private readonly StringBuilder _outputBuffer = new();
|
|
|
|
public bool IsRunning { get; private set; }
|
|
public string StatusText => IsRunning ? "运行中" : "已停止";
|
|
public string? LastError { get; private set; }
|
|
public event Action<string>? OutputReceived;
|
|
public event Action? StateChanged;
|
|
|
|
public async Task StartAsync()
|
|
{
|
|
if (IsRunning) return;
|
|
LastError = null;
|
|
_outputBuffer.Clear();
|
|
|
|
var dllPath = Path.Combine(AppContext.BaseDirectory, "Sln.Wcs.HoistServer.dll");
|
|
if (!File.Exists(dllPath))
|
|
throw new InvalidOperationException($"找不到 {dllPath}");
|
|
|
|
_process = new Process
|
|
{
|
|
StartInfo = new ProcessStartInfo
|
|
{
|
|
FileName = "dotnet",
|
|
Arguments = $"\"{dllPath}\" --urls {BaseUrl}",
|
|
UseShellExecute = false,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
CreateNoWindow = true,
|
|
},
|
|
EnableRaisingEvents = true,
|
|
};
|
|
|
|
_process.OutputDataReceived += (_, e) =>
|
|
{
|
|
if (e.Data != null) { _outputBuffer.AppendLine(e.Data); OutputReceived?.Invoke(e.Data); }
|
|
};
|
|
_process.ErrorDataReceived += (_, e) =>
|
|
{
|
|
if (e.Data != null) { _outputBuffer.AppendLine(e.Data); OutputReceived?.Invoke(e.Data); }
|
|
};
|
|
|
|
_process.Exited += (_, _) =>
|
|
{
|
|
IsRunning = false;
|
|
StateChanged?.Invoke();
|
|
_process?.Dispose();
|
|
_process = null;
|
|
};
|
|
|
|
_process.Start();
|
|
_process.BeginOutputReadLine();
|
|
_process.BeginErrorReadLine();
|
|
|
|
// 等待健康检查,最多 20 秒
|
|
for (int i = 0; i < 40; i++)
|
|
{
|
|
await Task.Delay(500);
|
|
if (_process.HasExited)
|
|
{
|
|
LastError = _outputBuffer.ToString();
|
|
_process.Dispose();
|
|
_process = null;
|
|
throw new InvalidOperationException($"进程异常退出:\n{LastError}");
|
|
}
|
|
if (await HealthCheckAsync())
|
|
{
|
|
IsRunning = true;
|
|
StateChanged?.Invoke();
|
|
return;
|
|
}
|
|
}
|
|
|
|
LastError = _outputBuffer.ToString();
|
|
Stop();
|
|
throw new InvalidOperationException($"启动超时 (20s):\n{LastError}");
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if (_process == null) return;
|
|
IsRunning = false;
|
|
|
|
try
|
|
{
|
|
if (!_process.HasExited)
|
|
{
|
|
_process.Kill();
|
|
_process.WaitForExit(3000);
|
|
}
|
|
_process.Dispose();
|
|
}
|
|
catch { }
|
|
finally { _process = null; }
|
|
|
|
StateChanged?.Invoke();
|
|
}
|
|
|
|
private async Task<bool> HealthCheckAsync()
|
|
{
|
|
try
|
|
{
|
|
var res = await _http.GetAsync($"{BaseUrl}/api/health");
|
|
return res.IsSuccessStatusCode;
|
|
}
|
|
catch { return false; }
|
|
}
|
|
}
|