feat(central-ui): FlowDebugService — management-command bridge + predicted graph
IFlowDebugService/FlowDebugService (scoped, registered beside IDataConnectionMoveService). Run/replay go to the per-node ManagementActor through ManagementActorHolder with the Blazor principal projected to AuthenticatedUser, exactly as DataConnectionMoveService does; the FlowDebugRunResult payload is deserialised (camelCase) and fed to FlowGraphBuilder, while ManagementError/Unauthorized/holder-null/transport faults collapse into FlowDebugOutcome with the server's error code verbatim (STANDBY_NODE reaches the page untouched). Prediction is DB-only: PredictMethodAsync hands FlowShapePredictor a memoised resolver that maps a route target → Instance.UniqueName → template parent chain (nearest definition wins, cycle guarded) → TemplateScript.Code; PredictValueChangeAsync roots at the write and appends each script whose value-change trigger names the attribute (same attributeName/attribute keys the site's TriggerRouting parser reads). Claude-Session: https://claude.ai/code/session_01GSLkkHKz9JrAtmJjmQK3cP
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
using System.Text.Json;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Debugging;
|
||||
using ZB.MOM.WW.ScadaBridge.ScriptAnalysis;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Outcome of a Flow Debugger run or replay as the page consumes it: the built
|
||||
/// <see cref="FlowGraph"/> (null on failure), the raw server result (null when the command
|
||||
/// itself was refused), and a classified error.
|
||||
/// </summary>
|
||||
/// <param name="Graph">The flow graph built from the returned audit events; null when the command failed outright.</param>
|
||||
/// <param name="Raw">The server's <see cref="FlowDebugRunResult"/>; null when no result came back.</param>
|
||||
/// <param name="Error">Human-readable failure, null on success.</param>
|
||||
/// <param name="ErrorCode">Machine-readable failure code (<c>STANDBY_NODE</c>, <c>NO_TRIGGER_WITHIN_WINDOW</c>, <c>UNAUTHORIZED</c>, <c>SERVICE_UNAVAILABLE</c>, …), null on success.</param>
|
||||
public sealed record FlowDebugOutcome(FlowGraph? Graph, FlowDebugRunResult? Raw, string? Error, string? ErrorCode)
|
||||
{
|
||||
/// <summary>True when a graph was built (the run may still have finished incomplete — see <see cref="Raw"/>).</summary>
|
||||
public bool Success => Graph != null && Error == null;
|
||||
|
||||
/// <summary>Creates a failed outcome.</summary>
|
||||
public static FlowDebugOutcome Fail(string error, string errorCode) => new(null, null, error, errorCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI facade over the Flow Debugger (design 2026-09-05 §5.5/§7.1): static shape
|
||||
/// prediction from script syntax, and the three management commands (Live run of a method,
|
||||
/// Live run of a value change, Replay) dispatched to the per-node <c>ManagementActor</c>
|
||||
/// through the in-process <c>ManagementActorHolder</c> — the SAME Ask path the HTTP
|
||||
/// <c>/management</c> endpoint uses, so the server stays the single enforcer of the Deployer
|
||||
/// gate and the active-node refusal (<c>STANDBY_NODE</c>).
|
||||
/// </summary>
|
||||
public interface IFlowDebugService
|
||||
{
|
||||
/// <summary>Predicts the flow shape of an inbound API method from its script and the site scripts its constant routes resolve to.</summary>
|
||||
/// <param name="apiMethodId">The method id.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The predicted flow; <see cref="PredictedFlow.Empty"/> when the method does not exist.</returns>
|
||||
Task<PredictedFlow> PredictMethodAsync(int apiMethodId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Predicts the flow a value change on <paramref name="attributeName"/> would trigger: the
|
||||
/// instance's template scripts whose value-change trigger names that attribute, each predicted
|
||||
/// as a site script.
|
||||
/// </summary>
|
||||
/// <param name="instanceId">The instance id.</param>
|
||||
/// <param name="attributeName">Canonical attribute name.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
Task<PredictedFlow> PredictValueChangeAsync(int instanceId, string attributeName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Runs an inbound API method with tracing on the active central node and returns the flow.</summary>
|
||||
/// <param name="apiMethodId">The method id.</param>
|
||||
/// <param name="parameters">Flat JSON object of parameters, or null.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
Task<FlowDebugOutcome> RunMethodAsync(int apiMethodId, JsonElement? parameters, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Writes an attribute through the traced path and returns the flow the change triggered.</summary>
|
||||
/// <param name="instanceId">The instance id.</param>
|
||||
/// <param name="attributeName">Canonical attribute name.</param>
|
||||
/// <param name="value">Value in canonical string form.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
Task<FlowDebugOutcome> RunValueChangeAsync(int instanceId, string attributeName, string value, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Rebuilds a past flow from the central audit store.</summary>
|
||||
/// <param name="executionId">Any execution id in the chain.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
Task<FlowDebugOutcome> ReplayAsync(Guid executionId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
Reference in New Issue
Block a user