feat(ui): structured editors for script schemas and alarm triggers

Replace raw-JSON text inputs with rich UI: script parameter/return types use
a JSON Schema builder (SchemaBuilder + JsonSchemaShapeParser, with a migration
to convert existing definitions); alarm trigger config uses a type-aware
editor with a flattened attribute picker (AlarmTriggerEditor). AlarmActor
gains optional direction (rising/falling/either) on RateOfChange triggers.
This commit is contained in:
Joseph Doherty
2026-05-13 00:33:00 -04:00
parent 57f477fd28
commit 783da8e21a
25 changed files with 3609 additions and 861 deletions
@@ -1,59 +1,17 @@
using System.Text.Json;
namespace ScadaLink.CentralUI.ScriptAnalysis;
/// <summary>
/// Parses the parameter-definitions and return-definition JSON written by
/// ParameterListEditor / ReturnTypeEditor into a <see cref="ScriptShape"/>.
/// Lenient: malformed JSON yields an empty parameter list, not an exception.
/// Parses the parameter-definitions and return-definition JSON Schema written
/// by SchemaBuilder into a <see cref="ScriptShape"/>. Delegates to
/// <see cref="JsonSchemaShapeParser"/>, which also handles legacy flat-shape
/// rows during the transition window.
/// </summary>
public static class ScriptShapeParser
{
public static ScriptShape Parse(string name, string? parametersJson, string? returnJson)
{
var parameters = ParseParameters(parametersJson);
var returnType = ParseReturnType(returnJson);
var parameters = JsonSchemaShapeParser.ParseParameters(parametersJson);
var returnType = JsonSchemaShapeParser.ParseReturnType(returnJson);
return new ScriptShape(name, parameters, returnType);
}
private static IReadOnlyList<ParameterShape> ParseParameters(string? json)
{
if (string.IsNullOrWhiteSpace(json)) return Array.Empty<ParameterShape>();
try
{
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.ValueKind != JsonValueKind.Array) return Array.Empty<ParameterShape>();
return doc.RootElement.EnumerateArray()
.Select(el => new ParameterShape(
Name: el.TryGetProperty("name", out var n) ? n.GetString() ?? "" : "",
Type: el.TryGetProperty("type", out var t) ? t.GetString() ?? "String" : "String",
Required: !el.TryGetProperty("required", out var rq) || rq.ValueKind != JsonValueKind.False))
.Where(p => !string.IsNullOrEmpty(p.Name))
.ToList();
}
catch
{
return Array.Empty<ParameterShape>();
}
}
private static string? ParseReturnType(string? json)
{
if (string.IsNullOrWhiteSpace(json)) return null;
try
{
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.ValueKind != JsonValueKind.Object) return null;
if (!doc.RootElement.TryGetProperty("type", out var t)) return null;
var type = t.GetString();
if (string.IsNullOrEmpty(type)) return null;
if (type == "List" && doc.RootElement.TryGetProperty("itemType", out var it))
return $"List<{it.GetString() ?? "Object"}>";
return type;
}
catch
{
return null;
}
}
}