refactor(management): FlowDebugRunner — extract WriteAndPollAsync from RunValueChangeAsync (no behaviour change)
Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
@@ -3,6 +3,8 @@ using System.Text.Json;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using ZB.MOM.WW.Audit;
|
using ZB.MOM.WW.Audit;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Messages.InboundApi;
|
using ZB.MOM.WW.ScadaBridge.Commons.Messages.InboundApi;
|
||||||
@@ -211,6 +213,22 @@ public sealed class FlowDebugRunner
|
|||||||
.GetSiteByIdAsync(instance.SiteId, ct).ConfigureAwait(false)
|
.GetSiteByIdAsync(instance.SiteId, ct).ConfigureAwait(false)
|
||||||
?? throw new ManagementCommandException($"Site with ID {instance.SiteId} not found.", NotFoundCode);
|
?? throw new ManagementCommandException($"Site with ID {instance.SiteId} not found.", NotFoundCode);
|
||||||
|
|
||||||
|
return await WriteAndPollAsync(
|
||||||
|
sp, user, instance, site, cmd.AttributeName, cmd.Value, cmd.WaitSeconds, triggerScript: null, ct)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The traced attribute write + spawned-run poll shared by <see cref="RunValueChangeAsync"/> and
|
||||||
|
/// the write-kind branch of the script entry's <c>RunScriptAsync</c> (design 2026-09-08 §4.3). The
|
||||||
|
/// caller has already gated the active node, loaded and scope-checked the instance, and loaded the
|
||||||
|
/// site. <paramref name="triggerScript"/> is null for the attribute-level command and the script's
|
||||||
|
/// canonical name for the script entry — it only changes the NO_TRIGGER message.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<FlowDebugRunResult> WriteAndPollAsync(
|
||||||
|
IServiceProvider sp, AuthenticatedUser user, Instance instance, Site site,
|
||||||
|
string attributeName, string? value, int waitSeconds, string? triggerScript, CancellationToken ct)
|
||||||
|
{
|
||||||
var root = Guid.NewGuid();
|
var root = Guid.NewGuid();
|
||||||
var startedAt = DateTimeOffset.UtcNow;
|
var startedAt = DateTimeOffset.UtcNow;
|
||||||
var sw = Stopwatch.StartNew();
|
var sw = Stopwatch.StartNew();
|
||||||
@@ -223,7 +241,7 @@ public sealed class FlowDebugRunner
|
|||||||
site.SiteIdentifier,
|
site.SiteIdentifier,
|
||||||
new RouteToSetAttributesRequest(
|
new RouteToSetAttributesRequest(
|
||||||
Guid.NewGuid().ToString(), instance.UniqueName,
|
Guid.NewGuid().ToString(), instance.UniqueName,
|
||||||
new Dictionary<string, string> { [cmd.AttributeName] = cmd.Value ?? string.Empty },
|
new Dictionary<string, string> { [attributeName] = value ?? string.Empty },
|
||||||
DateTimeOffset.UtcNow, ParentExecutionId: root, Trace: true),
|
DateTimeOffset.UtcNow, ParentExecutionId: root, Trace: true),
|
||||||
ct).ConfigureAwait(false);
|
ct).ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -232,18 +250,18 @@ public sealed class FlowDebugRunner
|
|||||||
write.Success ? AuditStatus.Delivered : AuditStatus.Failed,
|
write.Success ? AuditStatus.Delivered : AuditStatus.Failed,
|
||||||
occurredAtUtc: startedAt.UtcDateTime,
|
occurredAtUtc: startedAt.UtcDateTime,
|
||||||
actor: user.Username,
|
actor: user.Username,
|
||||||
target: cmd.AttributeName,
|
target: attributeName,
|
||||||
sourceNode: sp.GetService<INodeIdentityProvider>()?.NodeName,
|
sourceNode: sp.GetService<INodeIdentityProvider>()?.NodeName,
|
||||||
executionId: root,
|
executionId: root,
|
||||||
sourceSiteId: site.SiteIdentifier,
|
sourceSiteId: site.SiteIdentifier,
|
||||||
sourceInstanceId: instance.UniqueName,
|
sourceInstanceId: instance.UniqueName,
|
||||||
durationMs: (int)Math.Min(sw.ElapsedMilliseconds, int.MaxValue),
|
durationMs: (int)Math.Min(sw.ElapsedMilliseconds, int.MaxValue),
|
||||||
errorMessage: write.ErrorMessage,
|
errorMessage: write.ErrorMessage,
|
||||||
requestSummary: JsonSerializer.Serialize(new { attribute = cmd.AttributeName, value = cmd.Value }, ScriptTraceExtra.JsonOptions),
|
requestSummary: JsonSerializer.Serialize(new { attribute = attributeName, value = value }, ScriptTraceExtra.JsonOptions),
|
||||||
extra: new ScriptTraceExtra
|
extra: new ScriptTraceExtra
|
||||||
{
|
{
|
||||||
Source = SourceTag,
|
Source = SourceTag,
|
||||||
Writes = [new AttributeWriteDetail(cmd.AttributeName, cmd.Value, write.Success, write.ErrorMessage)],
|
Writes = [new AttributeWriteDetail(attributeName, value, write.Success, write.ErrorMessage)],
|
||||||
}.ToJson());
|
}.ToJson());
|
||||||
await WriteCentralAsync(sp, rootRow, ct).ConfigureAwait(false);
|
await WriteCentralAsync(sp, rootRow, ct).ConfigureAwait(false);
|
||||||
var redactedRoot = redactor?.Apply(rootRow) ?? rootRow;
|
var redactedRoot = redactor?.Apply(rootRow) ?? rootRow;
|
||||||
@@ -258,7 +276,7 @@ public sealed class FlowDebugRunner
|
|||||||
// == root, then their children — until a ScriptRun parented to root exists AND the
|
// == root, then their children — until a ScriptRun parented to root exists AND the
|
||||||
// set has been quiet for SettleWindow, or WaitSeconds elapses.
|
// set has been quiet for SettleWindow, or WaitSeconds elapses.
|
||||||
var puller = sp.GetRequiredService<IPullTraceEventsClient>();
|
var puller = sp.GetRequiredService<IPullTraceEventsClient>();
|
||||||
var deadline = _now() + TimeSpan.FromSeconds(Math.Max(1, cmd.WaitSeconds));
|
var deadline = _now() + TimeSpan.FromSeconds(Math.Max(1, waitSeconds));
|
||||||
var known = new Dictionary<Guid, AuditEvent>();
|
var known = new Dictionary<Guid, AuditEvent>();
|
||||||
DateTimeOffset? lastNew = null;
|
DateTimeOffset? lastNew = null;
|
||||||
var since = startedAt.UtcDateTime.AddSeconds(-5);
|
var since = startedAt.UtcDateTime.AddSeconds(-5);
|
||||||
@@ -298,7 +316,9 @@ public sealed class FlowDebugRunner
|
|||||||
});
|
});
|
||||||
return new FlowDebugRunResult(
|
return new FlowDebugRunResult(
|
||||||
root, completed,
|
root, completed,
|
||||||
completed ? null : $"No traced run was spawned within {cmd.WaitSeconds}s (no value-change trigger on '{cmd.AttributeName}', or no device echo).",
|
completed ? null : triggerScript is null
|
||||||
|
? $"No traced run was spawned within {waitSeconds}s (no value-change trigger on '{attributeName}', or no device echo)."
|
||||||
|
: $"No traced run of '{triggerScript}' was spawned within {waitSeconds}s after writing '{attributeName}' (trigger condition not met, or no device echo).",
|
||||||
completed ? null : NoTriggerCode,
|
completed ? null : NoTriggerCode,
|
||||||
ordered, false, startedAt, DateTimeOffset.UtcNow);
|
ordered, false, startedAt, DateTimeOffset.UtcNow);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user