40e8a23e7c
v2-ci / build (push) Failing after 37s
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
The OPC UA address-space build pipeline was named after a v2-roadmap milestone number rather than its domain. Rename the family to describe what it does (build/diff/apply the OPC UA address space): Phase7Composer -> AddressSpaceComposer Phase7CompositionResult -> AddressSpaceComposition Phase7Planner -> AddressSpacePlanner Phase7Plan -> AddressSpacePlan Phase7Applier -> AddressSpaceApplier Phase7ApplyOutcome -> AddressSpaceApplyOutcome The 9 Phase7*Tests suites follow suit; Phase7ScriptingEntitiesTests -> ScriptingEntitiesTests (it tests the scripting migration, not the pipeline). Log-message prefixes move to the new class names. Pure mechanical rename, no behavioral change. EF migration classes/IDs (AddPhase7ScriptingTables, ExtendComputeGenerationDiffWithPhase7) are immutable and left untouched, as are historical design docs. Build clean; OpcUaServer 261/261, Runtime 272/272, ScriptingEntities 12/12 green.
86 lines
6.4 KiB
C#
86 lines
6.4 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
|
|
|
/// <summary>
|
|
/// Wrapper <see cref="IOpcUaAddressSpaceSink"/> that defers to an inner sink swapped in at
|
|
/// runtime. Needed because the production sink (<c>SdkAddressSpaceSink</c>) wraps an
|
|
/// <c>OtOpcUaNodeManager</c> that only exists after the SDK <c>StandardServer</c> has
|
|
/// started — but Akka actors resolve their sink dependency at construction time, before
|
|
/// the hosted service has booted the SDK.
|
|
///
|
|
/// Bound as a singleton in DI on driver-role hosts; the OPC UA hosted service calls
|
|
/// <see cref="SetSink"/> once the server is up. Until that swap happens, every call is a
|
|
/// no-op against <see cref="NullOpcUaAddressSpaceSink"/>, so the actor stays safe to
|
|
/// receive messages from the moment it boots.
|
|
/// </summary>
|
|
public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink, ISurgicalAddressSpaceSink
|
|
{
|
|
private volatile IOpcUaAddressSpaceSink _inner = NullOpcUaAddressSpaceSink.Instance;
|
|
|
|
/// <summary>Swap in the production sink. Pass <c>null</c> to revert to the null sink
|
|
/// (used during graceful shutdown so post-stop writes don't hit a half-disposed manager).</summary>
|
|
/// <param name="sink">The sink implementation to use, or null to use the null sink.</param>
|
|
public void SetSink(IOpcUaAddressSpaceSink? sink) =>
|
|
_inner = sink ?? NullOpcUaAddressSpaceSink.Instance;
|
|
|
|
/// <summary>Writes a value to the OPC UA address space through the inner sink.</summary>
|
|
/// <param name="nodeId">The node ID of the variable.</param>
|
|
/// <param name="value">The value to write.</param>
|
|
/// <param name="quality">The OPC UA quality value.</param>
|
|
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
|
|
public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc)
|
|
=> _inner.WriteValue(nodeId, value, quality, sourceTimestampUtc);
|
|
|
|
/// <summary>Writes a full alarm-condition state through the inner sink.</summary>
|
|
/// <param name="alarmNodeId">The node ID of the alarm condition.</param>
|
|
/// <param name="state">The full condition state to project onto the node.</param>
|
|
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
|
|
public void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc)
|
|
=> _inner.WriteAlarmCondition(alarmNodeId, state, sourceTimestampUtc);
|
|
|
|
/// <summary>Materialises a real Part 9 alarm-condition node through the inner sink.</summary>
|
|
/// <param name="alarmNodeId">The alarm node ID (== ScriptedAlarmId).</param>
|
|
/// <param name="equipmentNodeId">The equipment folder node ID the condition parents under.</param>
|
|
/// <param name="displayName">The human-readable condition name.</param>
|
|
/// <param name="alarmType">The domain alarm type.</param>
|
|
/// <param name="severity">The domain severity.</param>
|
|
/// <param name="isNative">True for a driver-fed (native) equipment-tag alarm; false (default) for a scripted alarm.</param>
|
|
public void MaterialiseAlarmCondition(string alarmNodeId, string equipmentNodeId, string displayName, string alarmType, int severity, bool isNative = false)
|
|
=> _inner.MaterialiseAlarmCondition(alarmNodeId, equipmentNodeId, displayName, alarmType, severity, isNative);
|
|
|
|
/// <summary>Ensures a folder exists in the address space through the inner sink.</summary>
|
|
/// <param name="folderNodeId">The node ID of the folder.</param>
|
|
/// <param name="parentNodeId">The node ID of the parent folder, or null for root.</param>
|
|
/// <param name="displayName">The display name of the folder.</param>
|
|
public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName)
|
|
=> _inner.EnsureFolder(folderNodeId, parentNodeId, displayName);
|
|
|
|
/// <summary>Ensures a variable exists in the address space through the inner sink.</summary>
|
|
/// <param name="variableNodeId">The node ID of the variable.</param>
|
|
/// <param name="parentFolderNodeId">The node ID of the parent folder, or null for root.</param>
|
|
/// <param name="displayName">The display name of the variable.</param>
|
|
/// <param name="dataType">The OPC UA data type of the variable.</param>
|
|
/// <param name="writable">When true the node is created read/write; otherwise read-only.</param>
|
|
/// <param name="historianTagname">null ⇒ not historized; non-null ⇒ create Historizing with the
|
|
/// HistoryRead access bit and register the historian tagname.</param>
|
|
/// <param name="isArray">When true the node is created as a 1-D array; when false (default) scalar.</param>
|
|
/// <param name="arrayLength">The declared length of the 1-D array when <paramref name="isArray"/> is true.</param>
|
|
public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType, bool writable, string? historianTagname = null, bool isArray = false, uint? arrayLength = null)
|
|
=> _inner.EnsureVariable(variableNodeId, parentFolderNodeId, displayName, dataType, writable, historianTagname, isArray, arrayLength);
|
|
|
|
/// <summary>Rebuilds the address space through the inner sink.</summary>
|
|
public void RebuildAddressSpace() => _inner.RebuildAddressSpace();
|
|
|
|
/// <summary>Forwards an in-place tag-attribute update (F10b) to the inner sink when it supports the
|
|
/// surgical capability. Returns false otherwise — before the real <c>SdkAddressSpaceSink</c> is
|
|
/// swapped in (inner is still the null sink), or any inner sink that isn't surgical — so the caller
|
|
/// (AddressSpaceApplier) falls back to a full rebuild. Without this forward the surgical optimization is
|
|
/// inert on every driver-role host, because actors inject THIS wrapper, not the inner sink.</summary>
|
|
/// <param name="variableNodeId">The node ID of the variable to update in place.</param>
|
|
/// <param name="writable">Whether the node should be read/write.</param>
|
|
/// <param name="historianTagname">null ⇒ not historized; non-null ⇒ Historizing + historian binding.</param>
|
|
/// <returns>True when the inner sink applied the update; false when it lacks the capability or the node is missing.</returns>
|
|
public bool UpdateTagAttributes(string variableNodeId, bool writable, string? historianTagname)
|
|
=> _inner is ISurgicalAddressSpaceSink surgical
|
|
&& surgical.UpdateTagAttributes(variableNodeId, writable, historianTagname);
|
|
}
|