129 lines
8.4 KiB
C#
129 lines
8.4 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>
|
|
/// <param name="nodeId">The OPC UA node ID of the variable.</param>
|
|
/// <param name="value">The value to write.</param>
|
|
/// <param name="quality">The quality status of the value.</param>
|
|
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
|
|
void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc);
|
|
|
|
/// <summary>Write an alarm-condition's full Part 9 state. When a real condition node has been
|
|
/// materialised for <paramref name="alarmNodeId"/> via <see cref="MaterialiseAlarmCondition"/>,
|
|
/// this projects the whole <see cref="AlarmConditionSnapshot"/>
|
|
/// (Enabled/Active/Acked/Confirmed/Shelving/Severity/Message) onto it and recomputes Retain;
|
|
/// otherwise it falls back to the legacy two-element <c>[Active, Acknowledged]</c> placeholder
|
|
/// variable. A materialised condition also fires a Part 9 condition event on each transition (T16)
|
|
/// so subscribed clients receive the alarm event, not just the changed attributes.</summary>
|
|
/// <param name="alarmNodeId">The OPC UA node ID of the alarm (== ScriptedAlarmId for materialised conditions).</param>
|
|
/// <param name="state">The full condition state to project onto the node.</param>
|
|
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
|
|
void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc);
|
|
|
|
/// <summary>
|
|
/// Materialise a real OPC UA Part 9 alarm-condition node under its equipment folder so clients
|
|
/// can browse it as a proper condition (with basic Active/Ack state). The node id equals the
|
|
/// alarm node id (the ScriptedAlarmId) so subsequent <see cref="WriteAlarmCondition"/> calls update
|
|
/// it. Used by <c>AddressSpaceApplier.MaterialiseScriptedAlarms</c>. Idempotent.
|
|
/// </summary>
|
|
/// <param name="alarmNodeId">The alarm node ID (== ScriptedAlarmId); becomes the condition's NodeId.</param>
|
|
/// <param name="equipmentNodeId">The equipment folder node ID the condition parents under.</param>
|
|
/// <param name="displayName">Human-readable condition name (BrowseName / DisplayName / Message).</param>
|
|
/// <param name="alarmType">Domain alarm type — mapped to the SDK condition subtype by the sink.</param>
|
|
/// <param name="severity">Domain severity (OPC UA 1..1000 scale); mapped to the SDK severity buckets.</param>
|
|
/// <param name="isNative">When true this is a driver-fed (native, e.g. Galaxy) equipment-tag alarm; when
|
|
/// false (default) it is a scripted alarm. The node manager tracks native condition node ids so a later
|
|
/// task can route a native condition's inbound Acknowledge to the driver instead of the scripted engine.</param>
|
|
void MaterialiseAlarmCondition(string alarmNodeId, string equipmentNodeId, string displayName, string alarmType, int severity, bool isNative = false);
|
|
|
|
/// <summary>
|
|
/// Ensure a folder node exists under the given parent. Used by <c>AddressSpaceApplier</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>
|
|
/// <param name="folderNodeId">The OPC UA node ID for the folder.</param>
|
|
/// <param name="parentNodeId">The parent folder node ID, or null for namespace root.</param>
|
|
/// <param name="displayName">The display name for the folder.</param>
|
|
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>AddressSpaceApplier</c> to materialise equipment-namespace tags ahead of any
|
|
/// driver-side subscribe so OPC UA clients can browse them. Idempotent.
|
|
/// </summary>
|
|
/// <param name="variableNodeId">The OPC UA node ID for the variable.</param>
|
|
/// <param name="parentFolderNodeId">The parent folder node ID, or null for namespace root.</param>
|
|
/// <param name="displayName">The display name for the variable.</param>
|
|
/// <param name="dataType">OPC UA built-in type name ("Boolean" / "Int32" / "Float" / etc.).</param>
|
|
/// <param name="writable">When true the node is created <c>CurrentReadWrite</c> (an authored
|
|
/// ReadWrite equipment tag); when false it stays <c>CurrentRead</c> (read-only). Non-equipment-tag
|
|
/// variables (folders' children, alarm placeholders) always pass <c>false</c>.</param>
|
|
/// <param name="historianTagname">null ⇒ the variable is not historized; non-null ⇒ create it
|
|
/// Historizing with the HistoryRead access bit and register the (already default-resolved)
|
|
/// historian tagname.</param>
|
|
/// <param name="isArray">When true the node is created as a 1-D array (<c>ValueRank=OneDimension</c>
|
|
/// with <c>ArrayDimensions=[arrayLength]</c>); when false (default) it stays scalar
|
|
/// (<c>ValueRank=Scalar</c>). Array elements share the scalar's base <paramref name="dataType"/> —
|
|
/// rank + dimensions carry the array-ness.</param>
|
|
/// <param name="arrayLength">The declared length of the 1-D array when <paramref name="isArray"/> is
|
|
/// true; ignored for scalars. Null ⇒ length 0 (unbounded).</param>
|
|
void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType, bool writable, string? historianTagname = null, bool isArray = false, uint? arrayLength = null);
|
|
|
|
/// <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>
|
|
/// Announce that nodes were added at runtime (discovered-node injection) under
|
|
/// <paramref name="affectedNodeId"/> so subscribed clients refresh their browse
|
|
/// (Part 3 GeneralModelChangeEvent, verb NodeAdded).
|
|
/// </summary>
|
|
/// <param name="affectedNodeId">The node under which discovered nodes were added.</param>
|
|
void RaiseNodesAddedModelChange(string affectedNodeId);
|
|
}
|
|
|
|
/// <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() { }
|
|
|
|
/// <inheritdoc />
|
|
public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) { }
|
|
|
|
/// <inheritdoc />
|
|
public void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc) { }
|
|
|
|
/// <inheritdoc />
|
|
public void MaterialiseAlarmCondition(string alarmNodeId, string equipmentNodeId, string displayName, string alarmType, int severity, bool isNative = false) { }
|
|
|
|
/// <inheritdoc />
|
|
public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) { }
|
|
|
|
/// <inheritdoc />
|
|
public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType, bool writable, string? historianTagname = null, bool isArray = false, uint? arrayLength = null) { }
|
|
|
|
/// <inheritdoc />
|
|
public void RebuildAddressSpace() { }
|
|
|
|
/// <inheritdoc />
|
|
public void RaiseNodesAddedModelChange(string affectedNodeId) { }
|
|
}
|