Files
lmxopcua/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftSnapshot.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

71 lines
4.0 KiB
C#

using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
namespace ZB.MOM.WW.OtOpcUa.Configuration.Validation;
/// <summary>
/// Inputs for draft validation. Contains the draft's rows plus the minimum prior-generation
/// rows needed for cross-generation invariants (EquipmentUuid stability, UnsArea identity).
/// </summary>
public sealed class DraftSnapshot
{
/// <summary>Gets the draft generation identifier.</summary>
public required long GenerationId { get; init; }
/// <summary>Gets the cluster identifier.</summary>
public required string ClusterId { get; init; }
/// <summary>
/// Cluster's Enterprise segment (UNS level 1). When set, <see cref="DraftValidator"/> uses
/// the actual length for path-length checks instead of a conservative 32-char upper bound.
/// </summary>
public string? Enterprise { get; init; }
/// <summary>
/// Cluster's Site segment (UNS level 2). When set, <see cref="DraftValidator"/> uses the
/// actual length for path-length checks instead of a conservative 32-char upper bound.
/// </summary>
public string? Site { get; init; }
// v3: the Namespace entity is retired (the two OPC UA namespaces are implicit). The Raw tree
// (RawFolders → DriverInstances → Devices → TagGroups → Tags) + the UNS projection
// (UnsTagReferences) feed the v3 validator rules (raw-name charset, historized-tagname length, UNS
// effective-leaf uniqueness).
/// <summary>Gets the list of Raw-tree folders (drive the raw-name charset rule + RawPath computation).</summary>
public IReadOnlyList<RawFolder> RawFolders { get; init; } = [];
/// <summary>Gets the list of driver instances.</summary>
public IReadOnlyList<DriverInstance> DriverInstances { get; init; } = [];
/// <summary>Gets the list of devices.</summary>
public IReadOnlyList<Device> Devices { get; init; } = [];
/// <summary>Gets the list of tag-groups (drive the raw-name charset rule + RawPath computation).</summary>
public IReadOnlyList<TagGroup> TagGroups { get; init; } = [];
/// <summary>Gets the list of UNS areas.</summary>
public IReadOnlyList<UnsArea> UnsAreas { get; init; } = [];
/// <summary>Gets the list of UNS lines.</summary>
public IReadOnlyList<UnsLine> UnsLines { get; init; } = [];
/// <summary>Gets the list of equipment.</summary>
public IReadOnlyList<Equipment> Equipment { get; init; } = [];
/// <summary>Gets the list of raw tags.</summary>
public IReadOnlyList<Tag> Tags { get; init; } = [];
/// <summary>Gets the UNS tag references (raw tag → equipment projection). Drives the UNS effective-leaf
/// uniqueness rule (effective name = DisplayNameOverride else the backing raw tag's Name).</summary>
public IReadOnlyList<UnsTagReference> UnsTagReferences { get; init; } = [];
/// <summary>Equipment-bound VirtualTags (script-derived signals). Part of the UNS effective-leaf
/// uniqueness set within an equipment (references + VirtualTags + ScriptedAlarms).</summary>
public IReadOnlyList<VirtualTag> VirtualTags { get; init; } = [];
/// <summary>Equipment-bound scripted alarms. Part of the UNS effective-leaf uniqueness set.</summary>
public IReadOnlyList<ScriptedAlarm> ScriptedAlarms { get; init; } = [];
/// <summary>User-authored scripts (shared by VirtualTags, ScriptedAlarms, and Calculation tags). Drives
/// the WP7 Calculation gates: <c>scriptId</c> existence + the calc→calc dependency-cycle check.</summary>
public IReadOnlyList<Script> Scripts { get; init; } = [];
/// <summary>Gets the list of poll groups.</summary>
public IReadOnlyList<PollGroup> PollGroups { get; init; } = [];
/// <summary>Prior Equipment rows (any generation, same cluster) for stability checks.</summary>
public IReadOnlyList<Equipment> PriorEquipment { get; init; } = [];
/// <summary>Active reservations (<c>ReleasedAt IS NULL</c>) for pre-flight.</summary>
public IReadOnlyList<ExternalIdReservation> ActiveReservations { get; init; } = [];
}