Files
lmxopcua/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDependencyConsumer.cs
T
Joseph Doherty 53d222e0f7 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
2026-07-16 04:29:37 -04:00

45 lines
2.6 KiB
C#

namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
/// <summary>
/// Optional composable driver capability: the driver consumes <b>other</b> tags' live values.
/// The host registers the declared <see cref="DependencyRefs"/> with its per-node dependency mux
/// and forwards every matching value change into <see cref="OnDependencyValue"/> — the seam that
/// lets a host-blind driver (e.g. the <c>Calculation</c> pseudo-driver) read the address space's
/// live values without any cross-driver plumbing of its own.
/// </summary>
/// <remarks>
/// <para>
/// The mechanism mirrors how a <c>VirtualTagActor</c> registers interest on the
/// <c>DependencyMuxActor</c>: the mux already receives every driver's
/// <c>AttributeValuePublished</c> keyed by wire-ref (under v3, RawPath), so a driver that
/// implements this interface simply piggy-backs on that fan-out. A calc tag's computed output
/// re-enters the mux via the ordinary driver publish path, so calc-of-calc chains work with no
/// extra machinery — which is exactly why the deploy-time cycle gate is mandatory.
/// </para>
/// <para>
/// Implementations of <see cref="OnDependencyValue"/> are invoked from an actor context and
/// <b>must be non-blocking</b> — do the work inline and cheaply (the compile cache makes
/// steady-state calc evaluation a bounded method invocation), never block on I/O.
/// </para>
/// </remarks>
public interface IDependencyConsumer
{
/// <summary>
/// Wire-refs (RawPaths) this driver needs fed to it, derived from its authored tags. The host
/// re-reads this after <see cref="IDriver.InitializeAsync"/> / <see cref="IDriver.ReinitializeAsync"/>
/// and (re)registers the set with its dependency mux on every apply, so a tag/script edit that
/// changes the set converges without a bespoke notification.
/// </summary>
IReadOnlyCollection<string> DependencyRefs { get; }
/// <summary>
/// Host push of a single dependency value change. Called from an actor context, so the
/// implementation must be non-blocking.
/// </summary>
/// <param name="rawPath">The changed dependency's wire-ref (RawPath).</param>
/// <param name="value">The new value (may be null).</param>
/// <param name="statusCode">The OPC UA status code carried with the value (0 = Good).</param>
/// <param name="timestampUtc">The source timestamp of the change.</param>
void OnDependencyValue(string rawPath, object? value, uint statusCode, DateTime timestampUtc);
}