diff --git a/src/AlarmCondition/AlarmConditionNodeManager.cs b/src/AlarmCondition/AlarmConditionNodeManager.cs index 186b5b27..c60ad583 100644 --- a/src/AlarmCondition/AlarmConditionNodeManager.cs +++ b/src/AlarmCondition/AlarmConditionNodeManager.cs @@ -29,9 +29,11 @@ using System; using System.Collections.Generic; +using System.Reflection.Emit; using System.Threading; using Opc.Ua; using Opc.Ua.Server; +using Opc.Ua.Test; namespace AlarmCondition { @@ -271,7 +273,8 @@ private AreaState CreateAndIndexAreas(AreaState parent, AreaConfiguration config if (!m_sources.TryGetValue(sourcePath, out SourceState source)) { NodeId sourceId = ModelUtils.ConstructIdForSource(sourcePath, NamespaceIndex); - m_sources[sourcePath] = source = new SourceState(this, sourceId, sourcePath); + ResetRandomGenerator(ii); + m_sources[sourcePath] = source = new SourceState(this, sourceId, sourcePath, m_generator); } // HasEventSource and HasNotifier control the propagation of event notifications so @@ -456,6 +459,14 @@ protected override NodeState ValidateNode( cache?.Add(handle.NodeId, target); } } + + private void ResetRandomGenerator(int seed, int boundaryValueFrequency = 0) + { + m_randomSource = new RandomSource(seed); + m_generator = new DataGenerator(m_randomSource); + m_generator.BoundaryValueFrequency = boundaryValueFrequency; + } + #endregion #region Private Fields @@ -463,6 +474,8 @@ protected override NodeState ValidateNode( private readonly Dictionary m_areas; private readonly Dictionary m_sources; private Timer m_simulationTimer; + private RandomSource m_randomSource; + private DataGenerator m_generator; #endregion } } diff --git a/src/AlarmCondition/Model/SourceState.cs b/src/AlarmCondition/Model/SourceState.cs index b0ad52b7..729d03bb 100644 --- a/src/AlarmCondition/Model/SourceState.cs +++ b/src/AlarmCondition/Model/SourceState.cs @@ -27,9 +27,12 @@ * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ +using Microsoft.AspNetCore.Hosting.Server; using Opc.Ua; +using Opc.Ua.Test; using System; using System.Collections.Generic; +using System.Reflection.Emit; namespace AlarmCondition { @@ -45,7 +48,8 @@ public partial class SourceState : BaseObjectState public SourceState( AlarmConditionServerNodeManager nodeManager, NodeId nodeId, - string sourcePath) + string sourcePath, + DataGenerator generator) : base(null) { @@ -68,6 +72,7 @@ public SourceState( EventNotifier = EventNotifiers.None; // create a dialog. + m_generator = generator; m_dialog = CreateDialog("OnlineState"); // create the table of conditions. @@ -223,7 +228,7 @@ private DialogConditionState CreateDialog(string dialogName) AddChild(node); // initialize event information. - node.EventId.Value = Guid.NewGuid().ToByteArray(); + node.EventId.Value = GetNextGuidAsByteArray(); node.EventType.Value = node.TypeDefinitionId; node.SourceNode.Value = NodeId; node.SourceName.Value = SymbolicName; @@ -256,6 +261,16 @@ private DialogConditionState CreateDialog(string dialogName) return node; } + private byte[] GetNextGuidAsByteArray() + { + // Unpack the object to Uuid and then explicitly cast to Guid to access the byte[] + // using a random generator with a known seed to get reproducible results. + return ((Guid)((Uuid)m_generator.GetRandom( + NodeId.Parse($"i={(int)BuiltInType.Guid}"), + ValueRanks.Scalar, new uint[] { 1 }, + m_nodeManager.Server.TypeTree))).ToByteArray(); + } + /// /// The responses used with the dialog condition. /// @@ -411,7 +426,7 @@ private void UpdateAlarm(AlarmConditionState node, UnderlyingSystemAlarm alarm) } // update the basic event information (include generating a unique id for the event). - node.EventId.Value = Guid.NewGuid().ToByteArray(); + node.EventId.Value = GetNextGuidAsByteArray(); node.Time.Value = DateTime.UtcNow; node.ReceiveTime.Value = node.Time.Value; @@ -727,6 +742,7 @@ private string GetUserName(ISystemContext context) private readonly Dictionary m_events; private readonly Dictionary m_branches; private readonly DialogConditionState m_dialog; + private DataGenerator m_generator; #endregion } }