feat(launcher): SimpleLite 进程独立化与 DLL 热更新
- FollowParent=false 时在 Windows 用 cmd /c start 脱离父进程组,关闭 MiGu.Server 不再连带结束 SimpleLite - 新增 SimpleLiteBuildSync:SimpleLite 未运行时把 obj/Debug 最新 DLL 同步到 bin/Debug,并探测「前往站点」API 是否可用 - 新增 RestartForUpdate 与 POST /api/health/simplelite/restart-for-update:一键关闭、同步、重启以加载新 API - 诊断增加 FollowParent / GotoSiteApiAvailable / DeployHint;新增 scripts/redeploy-simplelite.ps1 部署脚本 - Dispose 增加 _disposed 幂等保护,避免重复释放
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MiGu.Server.Launcher;
|
||||
|
||||
/// <summary>
|
||||
/// 将 <c>obj/Debug</c> 下最新编译的 SimpleLite 同步到 <c>bin/Debug</c>(仅当 SimpleLite 未运行时)。
|
||||
/// </summary>
|
||||
public static class SimpleLiteBuildSync
|
||||
{
|
||||
public static bool TrySyncFromObjToBin(string contentRoot, ILogger? log = null)
|
||||
{
|
||||
if (!TryResolvePaths(contentRoot, out var objDll, out var objExe, out var binDir))
|
||||
return false;
|
||||
|
||||
var binDll = Path.Combine(binDir, "SimpleLite.dll");
|
||||
var binExe = Path.Combine(binDir, "SimpleLite.exe");
|
||||
|
||||
if (!File.Exists(objDll))
|
||||
{
|
||||
log?.LogDebug("[SimpleLiteBuildSync] obj DLL 不存在: {Path}", objDll);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Process.GetProcessesByName("SimpleLite").Any(p => !p.HasExited))
|
||||
{
|
||||
log?.LogWarning("[SimpleLiteBuildSync] SimpleLite 仍在运行,跳过 DLL 同步。请先关闭 SimpleLite 窗口。");
|
||||
return false;
|
||||
}
|
||||
|
||||
var objTime = File.GetLastWriteTimeUtc(objDll);
|
||||
if (File.Exists(binDll) && File.GetLastWriteTimeUtc(binDll) >= objTime)
|
||||
{
|
||||
log?.LogDebug("[SimpleLiteBuildSync] bin 已是最新,无需同步");
|
||||
return false;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(binDir);
|
||||
File.Copy(objDll, binDll, true);
|
||||
log?.LogInformation("[SimpleLiteBuildSync] 已同步 {Src} → {Dst}", objDll, binDll);
|
||||
|
||||
if (File.Exists(objExe))
|
||||
{
|
||||
File.Copy(objExe, binExe, true);
|
||||
log?.LogInformation("[SimpleLiteBuildSync] 已同步 {Src} → {Dst}", objExe, binExe);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool? ProbeGotoSiteRoute(int port = 8222)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(2) };
|
||||
using var resp = client.PostAsync(
|
||||
$"http://127.0.0.1:{port}/projection/reflection/car/0/goto-site?siteId=0",
|
||||
null).GetAwaiter().GetResult();
|
||||
var body = resp.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
||||
if (resp.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
return body.Contains("<html", StringComparison.OrdinalIgnoreCase) ? false : true;
|
||||
return body.Contains("\"success\"", StringComparison.Ordinal);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryResolvePaths(string contentRoot, out string objDll, out string objExe, out string binDir)
|
||||
{
|
||||
objDll = objExe = "";
|
||||
binDir = "";
|
||||
var repo = FindRepoRoot(contentRoot);
|
||||
if (repo == null) return false;
|
||||
var sl = Path.Combine(repo, "Simple", "SimpleLite");
|
||||
objDll = Path.Combine(sl, "obj", "Debug", "SimpleLite.dll");
|
||||
objExe = Path.Combine(sl, "obj", "Debug", "SimpleLite.exe");
|
||||
binDir = Path.Combine(sl, "bin", "Debug");
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string? FindRepoRoot(string contentRoot)
|
||||
{
|
||||
var dir = new DirectoryInfo(contentRoot);
|
||||
while (dir != null)
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(dir.FullName, "Simple", "SimpleLite")))
|
||||
return dir.FullName;
|
||||
dir = dir.Parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user