Some checks failed
v2-ci / build (push) Failing after 47s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Closes the gap where Tag rows with EquipmentId=NULL + Namespace.Kind=SystemPlatform (Galaxy hierarchy) existed in ConfigDb but were never surfaced in the OPC UA address space. Now they materialise as Variable nodes under a folder named for their FolderPath, browseable through any OPC UA client. Layers touched: - IOpcUaAddressSpaceSink: new EnsureVariable(nodeId, parentFolderId, displayName, dataType) signature on the sink interface, NullSink, DeferredSink, SdkSink. - OtOpcUaNodeManager.EnsureVariable: creates a BaseDataVariableState parented under the named folder (or root), initial Value=null + StatusCode=BadWaitingForInitialData; resolves Tag.DataType strings to the matching OPC UA built-in NodeId. Idempotent. - Phase7CompositionResult: new GalaxyTags collection of GalaxyTagPlan records carrying (TagId, DriverInstanceId, FolderPath, DisplayName, DataType, MxAccessRef). Constructor overloads keep existing call sites compiling. - Phase7Composer.Compose: now takes Tag + Namespace inputs, filters for SystemPlatform-namespace tags with EquipmentId=NULL, emits GalaxyTagPlan rows with MXAccess ref "FolderPath.Name". - Phase7Plan: new AddedGalaxyTags / RemovedGalaxyTags / ChangedGalaxyTags collections + GalaxyTagDelta record; IsEmpty + needsRebuild updated. - Phase7Planner.Compute: diffs GalaxyTags by TagId via existing DiffById helper. - DeploymentArtifact.ParseComposition: reads the Tags + Namespaces + DriverInstances arrays the ConfigComposer already emits, applies the same SystemPlatform filter, returns the same GalaxyTagPlan list as the composer so artifact-side and compose-side plans agree. - Phase7Applier: new MaterialiseGalaxyTags pass that ensures one folder per distinct FolderPath then one Variable per tag. NodeId for the variable is "<FolderPath>.<Name>" matching the MXAccess ref so the future Galaxy SubscribeBulk wiring can address them directly. - OpcUaPublishActor.RebuildAddressSpace: invokes MaterialiseGalaxyTags after MaterialiseHierarchy. _lastApplied initialiser updated for the new ctor. - seed-clusters.sql: pre-existing TestMachine_001.TestAlarm001..003 rows needed no change — the composer/applier now picks them up automatically. Verified end-to-end via docker-dev: deploy click → driver-a logs "Phase7Applier: Galaxy tags materialised (tags=3, folders=1)" → OPC UA Client CLI browses the three Variable nodes under TestMachine_001 folder. Reads return BadWaitingForInitialData status (expected — Galaxy driver's SubscribeBulk wiring to push values into the nodes is the remaining follow-up).
58 lines
3.3 KiB
C#
58 lines
3.3 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
|
|
|
/// <summary>
|
|
/// Abstraction over the OPC UA SDK's address space. <c>OpcUaPublishActor</c> consumes this
|
|
/// so the Runtime project doesn't reference <c>Opc.Ua.Server</c> directly — production
|
|
/// binds a real SDK-backed sink in the fused Host's wiring, dev/Mac binds the
|
|
/// <see cref="NullOpcUaAddressSpaceSink"/> no-op.
|
|
/// </summary>
|
|
public interface IOpcUaAddressSpaceSink
|
|
{
|
|
/// <summary>Write a Variable node's current value + quality + source timestamp.</summary>
|
|
void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc);
|
|
|
|
/// <summary>Write an alarm-condition Variable's active/acknowledged state.</summary>
|
|
void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc);
|
|
|
|
/// <summary>
|
|
/// Ensure a folder node exists under the given parent. Used by <c>Phase7Applier</c> to
|
|
/// materialise the UNS Area/Line/Equipment hierarchy in the address space. When
|
|
/// <paramref name="parentNodeId"/> is null the folder is parented under the namespace
|
|
/// root. Idempotent: calling twice with the same id is safe.
|
|
/// </summary>
|
|
void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName);
|
|
|
|
/// <summary>
|
|
/// Ensure a Variable node exists at <paramref name="variableNodeId"/>, parented under
|
|
/// <paramref name="parentFolderNodeId"/> (or the namespace root when null). Created with
|
|
/// Bad quality + null value; subsequent <see cref="WriteValue"/> calls update both.
|
|
/// Used by <c>Phase7Applier</c> to materialise Galaxy / SystemPlatform tags ahead of any
|
|
/// driver-side subscribe so OPC UA clients can browse them. Idempotent.
|
|
/// </summary>
|
|
/// <param name="dataType">OPC UA built-in type name ("Boolean" / "Int32" / "Float" / etc.).</param>
|
|
void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType);
|
|
|
|
/// <summary>
|
|
/// Tear down + repopulate the address space. Called by <c>OpcUaPublishActor</c> after a
|
|
/// successful deployment apply so the node manager reflects the new config. Idempotent.
|
|
/// </summary>
|
|
void RebuildAddressSpace();
|
|
}
|
|
|
|
/// <summary>OPC UA status code projection — Good / Uncertain / Bad. Real SDK has finer-grained
|
|
/// codes; the engine actors only need this 3-state classification.</summary>
|
|
public enum OpcUaQuality { Good, Uncertain, Bad }
|
|
|
|
/// <summary>No-op sink. Bound by default so the actors are safe to run in dev / Mac /
|
|
/// integration tests without a real SDK behind them.</summary>
|
|
public sealed class NullOpcUaAddressSpaceSink : IOpcUaAddressSpaceSink
|
|
{
|
|
public static readonly NullOpcUaAddressSpaceSink Instance = new();
|
|
private NullOpcUaAddressSpaceSink() { }
|
|
public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) { }
|
|
public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc) { }
|
|
public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) { }
|
|
public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType) { }
|
|
public void RebuildAddressSpace() { }
|
|
}
|