新增磁导航内部交管和信号交互界面
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
}
|
||||
Reference in New Issue
Block a user