Files
Migu2.0/MiGu.Server/Signal/SignalTableManifestLoader.cs
T

175 lines
5.9 KiB
C#

using System.Text.Json;
using MiGu.Server.Launcher;
namespace MiGu.Server.Signal;
/// <summary>
/// 优先从 plugins/StandardScene.Signal.dll 反射表和列(Model 上的 SignalTable / DisplayName)。
/// 没有插件 DLL 时才读 signal-tables.json 或内置清单。
/// </summary>
public static class SignalTableManifestLoader
{
private const string ManifestFileName = "signal-tables.json";
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true
};
public static IReadOnlyList<SignalTableDef> Load(SimpleLiteLauncher launcher)
{
var workingDirectory = launcher.ResolveWorkingDirectory();
var pluginsDir = launcher.ResolvePluginsDir();
var fromPlugin = SignalModelSchemaResolver.ResolveTables(pluginsDir, workingDirectory);
if (fromPlugin.Count > 0)
return fromPlugin;
var manifest = TryLoadManifest(launcher);
return manifest.Tables
.Where(t => !string.IsNullOrWhiteSpace(t.Id) && !string.IsNullOrWhiteSpace(t.FileName))
.Select(e => ToDef(e, pluginsDir, workingDirectory))
.ToList();
}
public static long ComputeManifestSignature(SimpleLiteLauncher launcher)
{
long sig = 0;
foreach (var path in ResolveManifestPaths(launcher))
{
if (!File.Exists(path)) continue;
try
{
var info = new FileInfo(path);
sig ^= info.LastWriteTimeUtc.Ticks;
sig ^= info.Length;
}
catch { /* ignore */ }
}
var pluginsDir = launcher.ResolvePluginsDir();
var dll = pluginsDir == null ? null : Path.Combine(pluginsDir, "StandardScene.Signal.dll");
if (dll != null && File.Exists(dll))
{
try
{
var info = new FileInfo(dll);
sig ^= info.LastWriteTimeUtc.Ticks;
}
catch { /* ignore */ }
}
return sig;
}
private static SignalTableManifest TryLoadManifest(SimpleLiteLauncher launcher)
{
foreach (var path in ResolveManifestPaths(launcher))
{
if (!File.Exists(path)) continue;
try
{
var json = File.ReadAllText(path);
var manifest = JsonSerializer.Deserialize<SignalTableManifest>(json, JsonOptions);
if (manifest?.Tables is { Count: > 0 })
return manifest;
}
catch { /* try next */ }
}
return JsonSerializer.Deserialize<SignalTableManifest>(EmbeddedFallbackJson, JsonOptions)
?? new SignalTableManifest();
}
private static IEnumerable<string> ResolveManifestPaths(SimpleLiteLauncher launcher)
{
var wd = launcher.ResolveWorkingDirectory();
if (!string.IsNullOrWhiteSpace(wd))
yield return Path.Combine(wd, "Config", "Signal", ManifestFileName);
var plugins = launcher.ResolvePluginsDir();
if (string.IsNullOrWhiteSpace(plugins)) yield break;
yield return Path.Combine(plugins, ManifestFileName);
yield return Path.Combine(plugins, "Config", "Signal", ManifestFileName);
}
private static SignalTableDef ToDef(SignalTableManifestEntry entry, string? pluginsDir, string? workingDirectory)
{
var columns = BuildColumns(entry, pluginsDir, workingDirectory);
return new SignalTableDef(
entry.Id.Trim(),
string.IsNullOrWhiteSpace(entry.Title) ? entry.Id.Trim() : entry.Title.Trim(),
entry.Category ?? "",
entry.FileName.Trim(),
columns);
}
private static IReadOnlyList<SignalColumn> BuildColumns(
SignalTableManifestEntry entry,
string? pluginsDir,
string? workingDirectory)
{
var reflected = SignalModelSchemaResolver.ResolveColumns(entry.Model, pluginsDir, workingDirectory);
if (reflected.Count > 0)
return reflected;
if (entry.Columns is { Count: > 0 })
{
return entry.Columns
.Where(c => !string.IsNullOrWhiteSpace(c.Key))
.Select(c => new SignalColumn(
c.Key.Trim(),
string.IsNullOrWhiteSpace(c.Label) ? c.Key.Trim() : c.Label.Trim(),
string.IsNullOrWhiteSpace(c.Type) ? "string" : c.Type.Trim(),
c.Options,
string.IsNullOrWhiteSpace(c.Group) ? null : c.Group.Trim()))
.ToList();
}
return Array.Empty<SignalColumn>();
}
private const string EmbeddedFallbackJson = """
{
"version": 1,
"tables": [
{
"id": "stations",
"title": "PLC机构",
"category": "PLC数据管理",
"fileName": "stations.json",
"model": "PlcStationModel"
},
{
"id": "docks",
"title": "机构工位",
"category": "PLC数据管理",
"fileName": "station-docks.json",
"model": "PlcStationDockModel"
},
{
"id": "handshake",
"title": "握手点",
"category": "握手点数据管理",
"fileName": "handshake-points.json",
"model": "HandshakePointModel"
},
{
"id": "release",
"title": "放行点",
"category": "放行点数据管理",
"fileName": "release-points.json",
"model": "ReleasePointModel"
},
{
"id": "mag-control",
"title": "磁条管控区",
"category": "磁条交管",
"fileName": "mag-control-areas.json",
"model": "MagControlAreaModel"
}
]
}
""";
}