94 lines
3.8 KiB
C#
94 lines
3.8 KiB
C#
using System.Text.Json;
|
|
|
|
namespace Zcbot.WindowsNode;
|
|
|
|
internal interface INodeAdapter
|
|
{
|
|
string Capability { get; }
|
|
string DisplayName { get; }
|
|
string AdapterVersion { get; }
|
|
IReadOnlyList<string> Features { get; }
|
|
bool HasActiveJobs { get; }
|
|
string RunningDetail { get; }
|
|
AdapterRuntimeStatus DetectRuntime();
|
|
bool ValidateRequest(JsonElement request);
|
|
Task RunAsync(RecoverableJob job);
|
|
void Cancel(Guid jobId);
|
|
}
|
|
|
|
internal sealed class OriginPlotNodeAdapter(JobInboxStore inbox) : INodeAdapter
|
|
{
|
|
internal const string CurrentAdapterVersion = "0.5.0";
|
|
internal const string ContractFilename = "origin.plot.v2.json";
|
|
private static readonly NodeAdapterContract Contract =
|
|
NodeAdapterContract.Load(ContractFilename, CurrentAdapterVersion);
|
|
private readonly OriginWorkerRunner runner = new(inbox);
|
|
|
|
internal static string CapabilityName => Contract.Capability;
|
|
|
|
public string Capability => Contract.Capability;
|
|
public string DisplayName => Contract.DisplayName;
|
|
public string AdapterVersion => CurrentAdapterVersion;
|
|
public IReadOnlyList<string> Features => Contract.Features;
|
|
public bool HasActiveJobs => runner.HasActiveJobs;
|
|
public string RunningDetail => "Origin 正在生成图形";
|
|
public AdapterRuntimeStatus DetectRuntime() => OriginRuntimeProbe.Detect();
|
|
public bool ValidateRequest(JsonElement request) =>
|
|
JobInboxStore.IsValidOriginRequest(request, Features);
|
|
public Task RunAsync(RecoverableJob job) => runner.RunAsync(job);
|
|
public void Cancel(Guid jobId) => runner.Cancel(jobId);
|
|
}
|
|
|
|
internal sealed record NodeAdapterContract(
|
|
string Capability, string DisplayName, IReadOnlyList<string> Features)
|
|
{
|
|
internal static NodeAdapterContract Load(string filename, string adapterVersion)
|
|
{
|
|
var path = Path.GetFullPath(
|
|
Path.Combine(AppContext.BaseDirectory, "software-contracts", filename));
|
|
using var document = JsonDocument.Parse(File.ReadAllBytes(path));
|
|
var root = document.RootElement;
|
|
var capability = root.GetProperty("capability").GetString()
|
|
?? throw new InvalidDataException("Adapter contract capability is missing.");
|
|
var displayName = root.GetProperty("display_name").GetString()
|
|
?? throw new InvalidDataException("Adapter contract display name is missing.");
|
|
var current = ParseVersion(adapterVersion);
|
|
var features = root.GetProperty("features").EnumerateObject()
|
|
.Where(item => ParseVersion(item.Value.GetString() ?? "0.0.0") <= current)
|
|
.Select(item => item.Name)
|
|
.ToArray();
|
|
return new NodeAdapterContract(capability, displayName, features);
|
|
}
|
|
|
|
private static Version ParseVersion(string value) =>
|
|
Version.TryParse(value, out var version) ? version : new Version(0, 0, 0);
|
|
}
|
|
|
|
internal sealed class NodeAdapterRegistry
|
|
{
|
|
private readonly IReadOnlyDictionary<string, INodeAdapter> adapters;
|
|
|
|
internal NodeAdapterRegistry(IEnumerable<INodeAdapter> values)
|
|
{
|
|
adapters = values.ToDictionary(item => item.Capability, StringComparer.Ordinal);
|
|
}
|
|
|
|
internal static NodeAdapterRegistry CreateDefault(JobInboxStore inbox) =>
|
|
new([new OriginPlotNodeAdapter(inbox)]);
|
|
|
|
internal static IReadOnlyList<NodeAdapterContract> InstalledContracts { get; } =
|
|
[
|
|
NodeAdapterContract.Load(
|
|
OriginPlotNodeAdapter.ContractFilename,
|
|
OriginPlotNodeAdapter.CurrentAdapterVersion),
|
|
];
|
|
|
|
internal static IReadOnlyList<string> InstalledCapabilities { get; } =
|
|
InstalledContracts.Select(item => item.Capability).ToArray();
|
|
|
|
internal IReadOnlyCollection<INodeAdapter> All => adapters.Values.ToArray();
|
|
|
|
internal INodeAdapter? Find(string capability) =>
|
|
adapters.TryGetValue(capability, out var adapter) ? adapter : null;
|
|
}
|