namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
///
/// Abstraction over the OPC UA SDK's address space. OpcUaPublishActor consumes this
/// so the Runtime project doesn't reference Opc.Ua.Server directly — production
/// binds a real SDK-backed sink in the fused Host's wiring, dev/Mac binds the
/// no-op.
///
public interface IOpcUaAddressSpaceSink
{
/// Write a Variable node's current value + quality + source timestamp.
/// The OPC UA node ID of the variable.
/// The value to write.
/// The quality status of the value.
/// The source timestamp in UTC.
void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc);
/// Write an alarm-condition's full Part 9 state. When a real condition node has been
/// materialised for via ,
/// this projects the whole
/// (Enabled/Active/Acked/Confirmed/Shelving/Severity/Message) onto it and recomputes Retain;
/// otherwise it falls back to the legacy two-element [Active, Acknowledged] 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.
/// The OPC UA node ID of the alarm (== ScriptedAlarmId for materialised conditions).
/// The full condition state to project onto the node.
/// The source timestamp in UTC.
void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc);
///
/// 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 calls update
/// it. Used by AddressSpaceApplier.MaterialiseScriptedAlarms. Idempotent.
///
/// The alarm node ID (== ScriptedAlarmId); becomes the condition's NodeId.
/// The equipment folder node ID the condition parents under.
/// Human-readable condition name (BrowseName / DisplayName / Message).
/// Domain alarm type — mapped to the SDK condition subtype by the sink.
/// Domain severity (OPC UA 1..1000 scale); mapped to the SDK severity buckets.
/// 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.
void MaterialiseAlarmCondition(string alarmNodeId, string equipmentNodeId, string displayName, string alarmType, int severity, bool isNative = false);
///
/// Ensure a folder node exists under the given parent. Used by AddressSpaceApplier to
/// materialise the UNS Area/Line/Equipment hierarchy in the address space. When
/// is null the folder is parented under the namespace
/// root. Idempotent: calling twice with the same id is safe.
///
/// The OPC UA node ID for the folder.
/// The parent folder node ID, or null for namespace root.
/// The display name for the folder.
void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName);
///
/// Ensure a Variable node exists at , parented under
/// (or the namespace root when null). Created with
/// Bad quality + null value; subsequent calls update both.
/// Used by AddressSpaceApplier to materialise equipment-namespace tags ahead of any
/// driver-side subscribe so OPC UA clients can browse them. Idempotent.
///
/// The OPC UA node ID for the variable.
/// The parent folder node ID, or null for namespace root.
/// The display name for the variable.
/// OPC UA built-in type name ("Boolean" / "Int32" / "Float" / etc.).
/// When true the node is created CurrentReadWrite (an authored
/// ReadWrite equipment tag); when false it stays CurrentRead (read-only). Non-equipment-tag
/// variables (folders' children, alarm placeholders) always pass false.
/// null ⇒ the variable is not historized; non-null ⇒ create it
/// Historizing with the HistoryRead access bit and register the (already default-resolved)
/// historian tagname.
/// When true the node is created as a 1-D array (ValueRank=OneDimension
/// with ArrayDimensions=[arrayLength]); when false (default) it stays scalar
/// (ValueRank=Scalar). Array elements share the scalar's base —
/// rank + dimensions carry the array-ness.
/// The declared length of the 1-D array when is
/// true; ignored for scalars. Null ⇒ length 0 (unbounded).
void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType, bool writable, string? historianTagname = null, bool isArray = false, uint? arrayLength = null);
///
/// Tear down + repopulate the address space. Called by OpcUaPublishActor after a
/// successful deployment apply so the node manager reflects the new config. Idempotent.
///
void RebuildAddressSpace();
///
/// Announce that nodes were added at runtime (discovered-node injection) under
/// so subscribed clients refresh their browse
/// (Part 3 GeneralModelChangeEvent, verb NodeAdded).
///
/// The node under which discovered nodes were added.
void RaiseNodesAddedModelChange(string affectedNodeId);
}
/// OPC UA status code projection — Good / Uncertain / Bad. Real SDK has finer-grained
/// codes; the engine actors only need this 3-state classification.
public enum OpcUaQuality { Good, Uncertain, Bad }
/// No-op sink. Bound by default so the actors are safe to run in dev / Mac /
/// integration tests without a real SDK behind them.
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 WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc) { }
///
public void MaterialiseAlarmCondition(string alarmNodeId, string equipmentNodeId, string displayName, string alarmType, int severity, bool isNative = false) { }
///
public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) { }
///
public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType, bool writable, string? historianTagname = null, bool isArray = false, uint? arrayLength = null) { }
///
public void RebuildAddressSpace() { }
///
public void RaiseNodesAddedModelChange(string affectedNodeId) { }
}