feat(driver-calc): B2-WP7 Calculation driver — IDependencyConsumer + triggers + deploy cycle gate
Adds the Calculation pseudo-driver (tags computed by C# scripts over other tags' live values) plus the host seam that feeds it: - IDependencyConsumer capability interface (Core.Abstractions) + DriverTypeNames.Calculation. - CalculationDriver : IDriver, ISubscribable, IReadable, IDependencyConsumer — change-gate + dedupe (VirtualTagActor parity), timer-group scheduler, Bad-transition + script-log emission, Good recovery; reuses the T0-4 CalculationEvaluator. - DriverHostActor spawns a DependencyConsumerMuxAdapter per dependency-consuming driver, registering its refs on the per-node DependencyMuxActor and forwarding DependencyValueChanged → OnDependencyValue; re-registers on every apply, torn down with the driver. - Factory + probe registered in DriverFactoryBootstrap (keeps the T0-3 guard green); guard test csproj references the driver so the bin-scan discovers the factory. - DeploymentArtifact injects the resolved scriptSource (scriptId → SourceCode) into a calc tag's delivered TagConfig so the host-blind driver can compile it. - DraftValidator deploy gates: CalculationScriptMissing / CalculationScriptNotFound (scriptId existence) + CalculationDependencyCycle (DependencyGraph.DetectCycles over calc→calc edges). - Tests: driver units (dep-ref extraction, change-gate, dedupe, Bad+script-log+recovery, timer, read), tag-config parsing, DraftValidator gates (self/2-cycle/cross-driver-terminal), and a Runtime integration test proving values FLOW mux → adapter → driver → calc-of-calc end-to-end. Claude-Session: https://claude.ai/code/session_01LVneM3eh1UtJxEisFXgmox
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Calculation;
|
||||
|
||||
/// <summary>
|
||||
/// Test-connect probe for the <c>Calculation</c> pseudo-driver. There is no backend to connect to —
|
||||
/// the driver computes tags from other tags' values — so the probe just confirms the driver config
|
||||
/// parses (mini-design §7). Per-script compile verification belongs to the editor diagnostics + the
|
||||
/// tag editor's inline panel, not the probe (which receives only the driver config). Never throws;
|
||||
/// returns Ok with negligible latency.
|
||||
/// </summary>
|
||||
public sealed class CalculationDriverProbe : IDriverProbe
|
||||
{
|
||||
private static readonly JsonSerializerOptions Opts = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
|
||||
Converters = { new JsonStringEnumConverter() },
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DriverType => CalculationDriverFactoryExtensions.DriverTypeName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DriverProbeResult> ProbeAsync(string configJson, TimeSpan timeout, CancellationToken ct)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(configJson))
|
||||
return Task.FromResult(new DriverProbeResult(false, "Config JSON is empty.", null));
|
||||
|
||||
try
|
||||
{
|
||||
// Parse-only: a Calculation driver's config is well-formed as long as it deserialises to an
|
||||
// object (the RawTags array is optional — a driver with no calc tags is still valid).
|
||||
_ = JsonSerializer.Deserialize<CalculationDriverFactoryExtensions.CalculationDriverConfigDto>(configJson, Opts);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(new DriverProbeResult(false, $"Config JSON is invalid: {ex.Message}", null));
|
||||
}
|
||||
|
||||
return Task.FromResult(new DriverProbeResult(true, null, TimeSpan.Zero));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user