diff --git a/src/AlarmCondition/Model/SourceState.cs b/src/AlarmCondition/Model/SourceState.cs
index dc3f6a78..cefb71d2 100644
--- a/src/AlarmCondition/Model/SourceState.cs
+++ b/src/AlarmCondition/Model/SourceState.cs
@@ -1,109 +1,109 @@
-/* ========================================================================
- * Copyright (c) 2005-2019 The OPC Foundation, Inc. All rights reserved.
- *
- * OPC Foundation MIT License 1.00
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following
- * conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * The complete license agreement can be found here:
- * 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
-{
- ///
- /// Maps an alarm source to a UA object node.
- ///
- public partial class SourceState : BaseObjectState
- {
- #region Constructors
- ///
- /// Initializes the area.
- ///
- public SourceState(
- AlarmConditionServerNodeManager nodeManager,
- NodeId nodeId,
+/* ========================================================================
+ * Copyright (c) 2005-2019 The OPC Foundation, Inc. All rights reserved.
+ *
+ * OPC Foundation MIT License 1.00
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The complete license agreement can be found here:
+ * 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
+{
+ ///
+ /// Maps an alarm source to a UA object node.
+ ///
+ public partial class SourceState : BaseObjectState
+ {
+ #region Constructors
+ ///
+ /// Initializes the area.
+ ///
+ public SourceState(
+ AlarmConditionServerNodeManager nodeManager,
+ NodeId nodeId,
string sourcePath,
- DataGenerator generator)
- :
- base(null)
- {
- Initialize(nodeManager.SystemContext);
-
- // save the node manager that owns the source.
- m_nodeManager = nodeManager;
-
- // create the source with the underlying system.
- m_source = ((UnderlyingSystem)nodeManager.SystemContext.SystemHandle).CreateSource(sourcePath, OnAlarmChanged);
-
- // initialize the area with the fixed metadata.
- SymbolicName = m_source.Name;
- NodeId = nodeId;
- BrowseName = new QualifiedName(Utils.Format("{0}", m_source.Name), nodeId.NamespaceIndex);
- DisplayName = BrowseName.Name;
- Description = null;
- ReferenceTypeId = null;
- TypeDefinitionId = ObjectTypeIds.BaseObjectType;
- EventNotifier = EventNotifiers.None;
-
- // create a dialog.
- m_generator = generator;
- m_dialog = CreateDialog("OnlineState");
-
- // create the table of conditions.
- m_alarms = new Dictionary();
- m_events = new Dictionary();
- m_branches = new Dictionary();
-
- // request an updated for all alarms.
- //m_source.Refresh();
- }
- #endregion
-
- #region Public Interface
- ///
- /// Returns the last event produced for any conditions belonging to the node or its children.
- ///
- /// The system context.
- /// The list of condition events to return.
- /// Whether to recursively report events for the children.
- public override void ConditionRefresh(ISystemContext context, List events, bool includeChildren)
- {
- // need to check if this source has already been processed during this refresh operation.
- for (int ii = 0; ii < events.Count; ii++)
- {
- if (events[ii] is InstanceStateSnapshot e && ReferenceEquals(e.Handle, this))
- {
- return;
- }
- }
-
- // report the dialog.
+ DataGenerator generator)
+ :
+ base(null)
+ {
+ Initialize(nodeManager.SystemContext);
+
+ // save the node manager that owns the source.
+ m_nodeManager = nodeManager;
+
+ // create the source with the underlying system.
+ m_source = ((UnderlyingSystem)nodeManager.SystemContext.SystemHandle).CreateSource(sourcePath, OnAlarmChanged);
+
+ // initialize the area with the fixed metadata.
+ SymbolicName = m_source.Name;
+ NodeId = nodeId;
+ BrowseName = new QualifiedName(Utils.Format("{0}", m_source.Name), nodeId.NamespaceIndex);
+ DisplayName = BrowseName.Name;
+ Description = null;
+ ReferenceTypeId = null;
+ TypeDefinitionId = ObjectTypeIds.BaseObjectType;
+ EventNotifier = EventNotifiers.None;
+
+ // create a dialog.
+ m_generator = generator;
+ m_dialog = CreateDialog("OnlineState");
+
+ // create the table of conditions.
+ m_alarms = new Dictionary();
+ m_events = new Dictionary();
+ m_branches = new Dictionary();
+
+ // request an updated for all alarms.
+ //m_source.Refresh();
+ }
+ #endregion
+
+ #region Public Interface
+ ///
+ /// Returns the last event produced for any conditions belonging to the node or its children.
+ ///
+ /// The system context.
+ /// The list of condition events to return.
+ /// Whether to recursively report events for the children.
+ public override void ConditionRefresh(ISystemContext context, List events, bool includeChildren)
+ {
+ // need to check if this source has already been processed during this refresh operation.
+ for (int ii = 0; ii < events.Count; ii++)
+ {
+ if (events[ii] is InstanceStateSnapshot e && ReferenceEquals(e.Handle, this))
+ {
+ return;
+ }
+ }
+
+ // report the dialog.
if (m_dialog != null && m_dialog.Retain.Value)
{
// create a snapshot.
@@ -114,196 +114,196 @@ public override void ConditionRefresh(ISystemContext context, List
- /// Called when the state of an alarm for the source has changed.
- ///
- private void OnAlarmChanged(UnderlyingSystemAlarm alarm)
- {
- lock (m_nodeManager.Lock)
- {
- // ignore archived alarms for now.
- if (alarm.RecordNumber != 0)
- {
- var branchId = new NodeId(alarm.RecordNumber, NodeId.NamespaceIndex);
-
- // find the alarm branch.
- if (!m_branches.TryGetValue(new NodeId(alarm.Name, NodeId.NamespaceIndex), out AlarmConditionState branch))
- {
- m_branches[branchId] = branch = CreateAlarm(alarm, branchId);
- }
-
- // map the system information to the UA defined alarm.
- UpdateAlarm(branch, alarm);
- ReportChanges(branch);
-
- // delete the branch.
- if ((alarm.State & UnderlyingSystemAlarmStates.Deleted) != 0)
- {
- m_branches.Remove(branchId);
- }
-
- return;
- }
-
- // find the alarm node.
- if (!m_alarms.TryGetValue(alarm.Name, out AlarmConditionState node))
- {
- m_alarms[alarm.Name] = node = CreateAlarm(alarm, null);
- }
-
- // map the system information to the UA defined alarm.
- UpdateAlarm(node, alarm);
- ReportChanges(node);
- }
- }
-
- ///
- /// Creates a new dialog condition
- ///
- private DialogConditionState CreateDialog(string dialogName)
- {
- ISystemContext context = m_nodeManager.SystemContext;
-
+ }
+
+ // the alarm objects act as a cache for the last known state and are used to generate refresh events.
+ foreach (AlarmConditionState alarm in m_alarms.Values)
+ {
+ // do not refresh alarms that are not in an interesting state.
+ if (!alarm.Retain.Value)
+ {
+ continue;
+ }
+
+ // create a snapshot.
+ InstanceStateSnapshot e = new();
+ e.Initialize(context, alarm);
+
+ // set the handle of the snapshot to check for duplicates.
+ e.Handle = this;
+
+ events.Add(e);
+ }
+
+ // report any active branches.
+ foreach (AlarmConditionState alarm in m_branches.Values)
+ {
+ // create a snapshot.
+ var e = new InstanceStateSnapshot();
+ e.Initialize(context, alarm);
+
+ // set the handle of the snapshot to check for duplicates.
+ e.Handle = this;
+
+ events.Add(e);
+ }
+ }
+ #endregion
+
+ #region Private Methods
+ ///
+ /// Called when the state of an alarm for the source has changed.
+ ///
+ private void OnAlarmChanged(UnderlyingSystemAlarm alarm)
+ {
+ lock (m_nodeManager.Lock)
+ {
+ // ignore archived alarms for now.
+ if (alarm.RecordNumber != 0)
+ {
+ var branchId = new NodeId(alarm.RecordNumber, NodeId.NamespaceIndex);
+
+ // find the alarm branch.
+ if (!m_branches.TryGetValue(new NodeId(alarm.Name, NodeId.NamespaceIndex), out AlarmConditionState branch))
+ {
+ m_branches[branchId] = branch = CreateAlarm(alarm, branchId);
+ }
+
+ // map the system information to the UA defined alarm.
+ UpdateAlarm(branch, alarm);
+ ReportChanges(branch);
+
+ // delete the branch.
+ if ((alarm.State & UnderlyingSystemAlarmStates.Deleted) != 0)
+ {
+ m_branches.Remove(branchId);
+ }
+
+ return;
+ }
+
+ // find the alarm node.
+ if (!m_alarms.TryGetValue(alarm.Name, out AlarmConditionState node))
+ {
+ m_alarms[alarm.Name] = node = CreateAlarm(alarm, null);
+ }
+
+ // map the system information to the UA defined alarm.
+ UpdateAlarm(node, alarm);
+ ReportChanges(node);
+ }
+ }
+
+ ///
+ /// Creates a new dialog condition
+ ///
+ private DialogConditionState CreateDialog(string dialogName)
+ {
+ ISystemContext context = m_nodeManager.SystemContext;
+
var node = new DialogConditionState(this)
{
SymbolicName = dialogName,
- };
-
- // specify optional fields.
- node.EnabledState = new TwoStateVariableState(node);
- node.EnabledState.TransitionTime = new PropertyState(node.EnabledState);
- node.EnabledState.EffectiveDisplayName = new PropertyState(node.EnabledState);
- node.EnabledState.Create(context, null, BrowseNames.EnabledState, null, false);
-
- // specify reference type between the source and the alarm.
- node.ReferenceTypeId = ReferenceTypeIds.HasComponent;
-
- // This call initializes the condition from the type model (i.e. creates all of the objects
- // and variables required to store its state). The information about the type model was
- // incorporated into the class when the class was created.
- node.Create(
- context,
- null,
- new QualifiedName(dialogName, BrowseName.NamespaceIndex),
- null,
- true);
-
- AddChild(node);
-
- // initialize event information.
- node.EventId.Value = GetNextGuidAsByteArray();
- node.EventType.Value = node.TypeDefinitionId;
- node.SourceNode.Value = NodeId;
- node.SourceName.Value = SymbolicName;
- node.ConditionName.Value = node.SymbolicName;
- node.Time.Value = DateTime.UtcNow;
- node.ReceiveTime.Value = node.Time.Value;
- node.Message.Value = "The dialog was activated";
- node.Retain.Value = true;
-
- node.SetEnableState(context, true);
- node.SetSeverity(context, EventSeverity.Low);
-
- // initialize the dialog information.
- node.Prompt.Value = "Please specify a new state for the source.";
- node.ResponseOptionSet.Value = s_ResponseOptions;
- node.DefaultResponse.Value = 2;
- node.CancelResponse.Value = 2;
- node.OkResponse.Value = 0;
-
- // set up method handlers.
- node.OnRespond = OnRespond;
-
- // this flag needs to be set because the underlying system does not produce these events.
- node.AutoReportStateChanges = true;
-
- // activate the dialog.
- node.Activate(context);
-
- // return the new node.
- return node;
- }
-
- private byte[] GetNextGuidAsByteArray()
- {
- // unpack the object to Uuid and then explicit cast to Guid to access the byte[]
- // using RandomGenerator with known 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.
- ///
- private readonly LocalizedText[] s_ResponseOptions =
- [
- "Online",
- "Offline",
- "No Change"
- ];
-
- ///
- /// Creates a new alarm for the source.
- ///
- /// The alarm.
- /// The branch id.
- /// The new alarm.
- private AlarmConditionState CreateAlarm(UnderlyingSystemAlarm alarm, NodeId branchId)
- {
- ISystemContext context = m_nodeManager.SystemContext;
-
- AlarmConditionState node = null;
-
- // need to map the alarm type to a UA defined alarm type.
- switch (alarm.AlarmType)
- {
+ };
+
+ // specify optional fields.
+ node.EnabledState = new TwoStateVariableState(node);
+ node.EnabledState.TransitionTime = new PropertyState(node.EnabledState);
+ node.EnabledState.EffectiveDisplayName = new PropertyState(node.EnabledState);
+ node.EnabledState.Create(context, null, BrowseNames.EnabledState, null, false);
+
+ // specify reference type between the source and the alarm.
+ node.ReferenceTypeId = ReferenceTypeIds.HasComponent;
+
+ // This call initializes the condition from the type model (i.e. creates all of the objects
+ // and variables required to store its state). The information about the type model was
+ // incorporated into the class when the class was created.
+ node.Create(
+ context,
+ null,
+ new QualifiedName(dialogName, BrowseName.NamespaceIndex),
+ null,
+ true);
+
+ AddChild(node);
+
+ // initialize event information.
+ node.EventId.Value = GetNextGuidAsByteArray();
+ node.EventType.Value = node.TypeDefinitionId;
+ node.SourceNode.Value = NodeId;
+ node.SourceName.Value = SymbolicName;
+ node.ConditionName.Value = node.SymbolicName;
+ node.Time.Value = DateTime.UtcNow;
+ node.ReceiveTime.Value = node.Time.Value;
+ node.Message.Value = "The dialog was activated";
+ node.Retain.Value = true;
+
+ node.SetEnableState(context, true);
+ node.SetSeverity(context, EventSeverity.Low);
+
+ // initialize the dialog information.
+ node.Prompt.Value = "Please specify a new state for the source.";
+ node.ResponseOptionSet.Value = s_ResponseOptions;
+ node.DefaultResponse.Value = 2;
+ node.CancelResponse.Value = 2;
+ node.OkResponse.Value = 0;
+
+ // set up method handlers.
+ node.OnRespond = OnRespond;
+
+ // this flag needs to be set because the underlying system does not produce these events.
+ node.AutoReportStateChanges = true;
+
+ // activate the dialog.
+ node.Activate(context);
+
+ // return the new node.
+ return node;
+ }
+
+ private byte[] GetNextGuidAsByteArray()
+ {
+ // Unpack the object to Uuid and then explicitly cast to Guid to access the byte[]
+ // using RandomGenerator with known 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.
+ ///
+ private readonly LocalizedText[] s_ResponseOptions =
+ [
+ "Online",
+ "Offline",
+ "No Change"
+ ];
+
+ ///
+ /// Creates a new alarm for the source.
+ ///
+ /// The alarm.
+ /// The branch id.
+ /// The new alarm.
+ private AlarmConditionState CreateAlarm(UnderlyingSystemAlarm alarm, NodeId branchId)
+ {
+ ISystemContext context = m_nodeManager.SystemContext;
+
+ AlarmConditionState node = null;
+
+ // need to map the alarm type to a UA defined alarm type.
+ switch (alarm.AlarmType)
+ {
case "HighAlarm":
{
ExclusiveDeviationAlarmState node2 = new(this);
node = node2;
node2.HighLimit = new PropertyState(node2);
break;
- }
-
+ }
+
case "HighLowAlarm":
{
NonExclusiveLevelAlarmState node2 = new(this);
@@ -320,429 +320,429 @@ private AlarmConditionState CreateAlarm(UnderlyingSystemAlarm alarm, NodeId bran
node2.LowLowState = new TwoStateVariableState(node2);
break;
- }
-
+ }
+
case "TripAlarm":
{
node = new TripAlarmState(this);
break;
- }
-
+ }
+
default:
{
node = new AlarmConditionState(this);
break;
- }
- }
-
- node.SymbolicName = alarm.Name;
-
- // add optional components.
- node.Comment = new ConditionVariableState(node);
- node.ClientUserId = new PropertyState(node);
- node.AddComment = new AddCommentMethodState(node);
- node.ConfirmedState = new TwoStateVariableState(node);
- node.Confirm = new AddCommentMethodState(node);
-
- if (NodeId.IsNull(branchId))
- {
- node.SuppressedState = new TwoStateVariableState(node);
- node.ShelvingState = new ShelvedStateMachineState(node);
- }
-
- // adding optional components to children is a little more complicated since the
- // necessary initialization strings defined by the class that represents the child.
- // in this case we pre-create the child, add the optional components
- // and call create without assigning NodeIds. The NodeIds will be assigned when the
- // parent object is created.
- node.EnabledState = new TwoStateVariableState(node);
- node.EnabledState.TransitionTime = new PropertyState(node.EnabledState);
- node.EnabledState.EffectiveDisplayName = new PropertyState(node.EnabledState);
- node.EnabledState.Create(context, null, BrowseNames.EnabledState, null, false);
-
- // same procedure add optional components to the ActiveState component.
- node.ActiveState = new TwoStateVariableState(node);
- node.ActiveState.TransitionTime = new PropertyState(node.ActiveState);
- node.ActiveState.EffectiveDisplayName = new PropertyState(node.ActiveState);
- node.ActiveState.Create(context, null, BrowseNames.ActiveState, null, false);
-
- // specify reference type between the source and the alarm.
- node.ReferenceTypeId = ReferenceTypeIds.HasComponent;
-
- // This call initializes the condition from the type model (i.e. creates all of the objects
- // and variables required to store its state). The information about the type model was
- // incorporated into the class when the class was created.
- //
- // This method also assigns new NodeIds to all of the components by calling the INodeIdFactory.New
- // method on the INodeIdFactory object which is part of the system context. The NodeManager provides
- // the INodeIdFactory implementation used here.
- node.Create(
- context,
- null,
- new QualifiedName(alarm.Name, BrowseName.NamespaceIndex),
- null,
- true);
-
- // don't add branches to the address space.
- if (NodeId.IsNull(branchId))
- {
- AddChild(node);
- }
-
- // initialize event information.node
- node.EventType.Value = node.TypeDefinitionId;
- node.SourceNode.Value = NodeId;
- node.SourceName.Value = SymbolicName;
- node.ConditionName.Value = node.SymbolicName;
- node.Time.Value = DateTime.UtcNow;
- node.ReceiveTime.Value = node.Time.Value;
- node.BranchId.Value = branchId;
-
- // set up method handlers.
- node.OnEnableDisable = OnEnableDisableAlarm;
- node.OnAcknowledge = OnAcknowledge;
- node.OnAddComment = OnAddComment;
- node.OnConfirm = OnConfirm;
- node.OnShelve = OnShelve;
- node.OnTimedUnshelve = OnTimedUnshelve;
-
- // return the new node.
- return node;
- }
-
- ///
- /// Updates the alarm with a new state.
- ///
- /// The node.
- /// The alarm.
- private void UpdateAlarm(AlarmConditionState node, UnderlyingSystemAlarm alarm)
- {
- ISystemContext context = m_nodeManager.SystemContext;
-
- // remove old event.
- if (node.EventId.Value != null)
- {
- m_events.Remove(Utils.ToHexString(node.EventId.Value));
- }
-
- // update the basic event information (include generating a unique id for the event).
- node.EventId.Value = GetNextGuidAsByteArray();
- node.Time.Value = DateTime.UtcNow;
- node.ReceiveTime.Value = node.Time.Value;
-
- // save the event for later lookup.
- m_events[Utils.ToHexString(node.EventId.Value)] = node;
-
- // determine the retain state.
- node.Retain.Value = true;
-
- if (alarm != null)
- {
- node.Time.Value = alarm.Time;
- node.Message.Value = new LocalizedText(alarm.Reason);
-
- // update the states.
- node.SetEnableState(context, (alarm.State & UnderlyingSystemAlarmStates.Enabled) != 0);
- node.SetAcknowledgedState(context, (alarm.State & UnderlyingSystemAlarmStates.Acknowledged) != 0);
- node.SetConfirmedState(context, (alarm.State & UnderlyingSystemAlarmStates.Confirmed) != 0);
- node.SetActiveState(context, (alarm.State & UnderlyingSystemAlarmStates.Active) != 0);
- node.SetSuppressedState(context, (alarm.State & UnderlyingSystemAlarmStates.Suppressed) != 0);
-
- // update other information.
- node.SetComment(context, alarm.Comment, alarm.UserName);
- node.SetSeverity(context, alarm.Severity);
-
- node.EnabledState.TransitionTime.Value = alarm.EnableTime;
- node.ActiveState.TransitionTime.Value = alarm.ActiveTime;
-
- // check for deleted items.
- if ((alarm.State & UnderlyingSystemAlarmStates.Deleted) != 0)
- {
- node.Retain.Value = false;
- }
-
- // handle high alarms.
- if (node is ExclusiveLimitAlarmState highAlarm)
- {
- highAlarm.HighLimit.Value = alarm.Limits[0];
-
- if ((alarm.State & UnderlyingSystemAlarmStates.High) != 0)
- {
- highAlarm.SetLimitState(context, LimitAlarmStates.High);
- }
- }
-
- // handle high-low alarms.
- if (node is NonExclusiveLimitAlarmState highLowAlarm)
- {
- highLowAlarm.HighHighLimit.Value = alarm.Limits[0];
- highLowAlarm.HighLimit.Value = alarm.Limits[1];
- highLowAlarm.LowLimit.Value = alarm.Limits[2];
- highLowAlarm.LowLowLimit.Value = alarm.Limits[3];
-
- LimitAlarmStates limit = LimitAlarmStates.Inactive;
-
- if ((alarm.State & UnderlyingSystemAlarmStates.HighHigh) != 0)
- {
- limit |= LimitAlarmStates.HighHigh;
- }
-
- if ((alarm.State & UnderlyingSystemAlarmStates.High) != 0)
- {
- limit |= LimitAlarmStates.High;
- }
-
- if ((alarm.State & UnderlyingSystemAlarmStates.Low) != 0)
- {
- limit |= LimitAlarmStates.Low;
- }
-
- if ((alarm.State & UnderlyingSystemAlarmStates.LowLow) != 0)
- {
- limit |= LimitAlarmStates.LowLow;
- }
-
- highLowAlarm.SetLimitState(context, limit);
- }
- }
-
- // not interested in disabled or inactive alarms.
- if (!node.EnabledState.Id.Value || !node.ActiveState.Id.Value)
- {
- node.Retain.Value = false;
- }
- }
-
- ///
- /// Called when the alarm is enabled or disabled.
- ///
- private ServiceResult OnEnableDisableAlarm(
- ISystemContext context,
- ConditionState condition,
- bool enabling)
- {
- m_source.EnableAlarm(condition.SymbolicName, enabling);
- return ServiceResult.Good;
- }
-
- ///
- /// Called when the alarm has a comment added.
- ///
- private ServiceResult OnAddComment(
- ISystemContext context,
- ConditionState condition,
- byte[] eventId,
- LocalizedText comment)
- {
- AlarmConditionState alarm = FindAlarmByEventId(eventId);
-
- if (alarm == null)
- {
- return StatusCodes.BadEventIdUnknown;
- }
-
- m_source.CommentAlarm(alarm.SymbolicName, GetRecordNumber(alarm), comment, GetUserName(context));
-
- return ServiceResult.Good;
- }
-
- ///
- /// Called when the alarm is acknowledged.
- ///
- private ServiceResult OnAcknowledge(
- ISystemContext context,
- ConditionState condition,
- byte[] eventId,
- LocalizedText comment)
- {
- AlarmConditionState alarm = FindAlarmByEventId(eventId);
-
- if (alarm == null)
- {
- return StatusCodes.BadEventIdUnknown;
- }
-
- m_source.AcknowledgeAlarm(alarm.SymbolicName, GetRecordNumber(alarm), comment, GetUserName(context));
-
- return ServiceResult.Good;
- }
-
- ///
- /// Called when the alarm is confirmed.
- ///
- private ServiceResult OnConfirm(
- ISystemContext context,
- ConditionState condition,
- byte[] eventId,
- LocalizedText comment)
- {
- AlarmConditionState alarm = FindAlarmByEventId(eventId);
-
- if (alarm == null)
- {
- return StatusCodes.BadEventIdUnknown;
- }
-
- m_source.ConfirmAlarm(alarm.SymbolicName, GetRecordNumber(alarm), comment, GetUserName(context));
-
- return ServiceResult.Good;
- }
-
- ///
- /// Called when the alarm is shelved.
- ///
- private ServiceResult OnShelve(
- ISystemContext context,
- AlarmConditionState alarm,
- bool shelving,
- bool oneShot,
- double shelvingTime)
- {
- alarm.SetShelvingState(context, shelving, oneShot, shelvingTime);
- alarm.Message.Value = "The alarm shelved.";
-
- UpdateAlarm(alarm, null);
- ReportChanges(alarm);
-
- return ServiceResult.Good;
- }
-
- ///
- /// Called when the alarm is shelved.
- ///
- private ServiceResult OnTimedUnshelve(
- ISystemContext context,
- AlarmConditionState alarm)
- {
- // update the alarm state and produce and event.
- alarm.SetShelvingState(context, false, false, 0);
- alarm.Message.Value = "The timed shelving period expired.";
-
- UpdateAlarm(alarm, null);
- ReportChanges(alarm);
-
- return ServiceResult.Good;
- }
-
- ///
- /// Called when the dialog receives a response.
- ///
- private ServiceResult OnRespond(
- ISystemContext context,
- DialogConditionState dialog,
- int selectedResponse)
- {
- // response 0 means set the source online.
- if (selectedResponse == 0)
- {
- m_source.SetOfflineState(false);
- }
-
- // response 1 means set the source offline.
- if (selectedResponse == 1)
- {
- m_source.SetOfflineState(true);
- }
-
- // other responses mean do nothing.
- dialog.SetResponse(context, selectedResponse);
-
- // dialog no longer interesting once it is deactivated.
- dialog.Message.Value = "The dialog was deactivated";
- dialog.Retain.Value = false;
-
- return ServiceResult.Good;
- }
-
- ///
- /// Reports the changes to the alarm.
- ///
- private void ReportChanges(AlarmConditionState alarm)
- {
- // report changes to node attributes.
- alarm.ClearChangeMasks(m_nodeManager.SystemContext, true);
-
- // check if events are being monitored for the source.
- if (AreEventsMonitored)
- {
- // create a snapshot.
- InstanceStateSnapshot e = new();
- e.Initialize(m_nodeManager.SystemContext, alarm);
-
- // report the event.
- alarm.ReportEvent(m_nodeManager.SystemContext, e);
- }
- }
-
- ///
- /// Finds the alarm by event id.
- ///
- /// The event id.
- /// The alarm. Null if not found.
- private AlarmConditionState FindAlarmByEventId(byte[] eventId)
- {
- if (eventId == null)
- {
- return null;
- }
-
- if (!m_events.TryGetValue(Utils.ToHexString(eventId), out AlarmConditionState alarm))
- {
- return null;
- }
-
- return alarm;
- }
-
- ///
- /// Gets the record number associated with tge alarm.
- ///
- /// The alarm.
- /// The record number; 0 if the alarm is not an archived alarm.
- private uint GetRecordNumber(AlarmConditionState alarm)
- {
- if (alarm == null)
- {
- return 0;
- }
-
- if (alarm.BranchId == null || alarm.BranchId.Value == null)
- {
- return 0;
- }
-
- uint? recordNumber = alarm.BranchId.Value.Identifier as uint?;
-
- if (recordNumber != null)
- {
- return recordNumber.Value;
- }
-
- return 0;
- }
-
- ///
- /// Gets the user name associated with the context.
- ///
- private string GetUserName(ISystemContext context)
- {
- if (context.UserIdentity != null)
- {
- return context.UserIdentity.DisplayName;
- }
-
- return null;
- }
- #endregion
-
- #region Private Fields
- private readonly AlarmConditionServerNodeManager m_nodeManager;
- private readonly UnderlyingSystemSource m_source;
- private readonly Dictionary m_alarms;
- private readonly Dictionary m_events;
- private readonly Dictionary m_branches;
- private readonly DialogConditionState m_dialog;
- private DataGenerator m_generator;
- #endregion
- }
-}
+ }
+ }
+
+ node.SymbolicName = alarm.Name;
+
+ // add optional components.
+ node.Comment = new ConditionVariableState(node);
+ node.ClientUserId = new PropertyState(node);
+ node.AddComment = new AddCommentMethodState(node);
+ node.ConfirmedState = new TwoStateVariableState(node);
+ node.Confirm = new AddCommentMethodState(node);
+
+ if (NodeId.IsNull(branchId))
+ {
+ node.SuppressedState = new TwoStateVariableState(node);
+ node.ShelvingState = new ShelvedStateMachineState(node);
+ }
+
+ // adding optional components to children is a little more complicated since the
+ // necessary initialization strings defined by the class that represents the child.
+ // in this case we pre-create the child, add the optional components
+ // and call create without assigning NodeIds. The NodeIds will be assigned when the
+ // parent object is created.
+ node.EnabledState = new TwoStateVariableState(node);
+ node.EnabledState.TransitionTime = new PropertyState(node.EnabledState);
+ node.EnabledState.EffectiveDisplayName = new PropertyState(node.EnabledState);
+ node.EnabledState.Create(context, null, BrowseNames.EnabledState, null, false);
+
+ // same procedure add optional components to the ActiveState component.
+ node.ActiveState = new TwoStateVariableState(node);
+ node.ActiveState.TransitionTime = new PropertyState(node.ActiveState);
+ node.ActiveState.EffectiveDisplayName = new PropertyState(node.ActiveState);
+ node.ActiveState.Create(context, null, BrowseNames.ActiveState, null, false);
+
+ // specify reference type between the source and the alarm.
+ node.ReferenceTypeId = ReferenceTypeIds.HasComponent;
+
+ // This call initializes the condition from the type model (i.e. creates all of the objects
+ // and variables required to store its state). The information about the type model was
+ // incorporated into the class when the class was created.
+ //
+ // This method also assigns new NodeIds to all of the components by calling the INodeIdFactory.New
+ // method on the INodeIdFactory object which is part of the system context. The NodeManager provides
+ // the INodeIdFactory implementation used here.
+ node.Create(
+ context,
+ null,
+ new QualifiedName(alarm.Name, BrowseName.NamespaceIndex),
+ null,
+ true);
+
+ // don't add branches to the address space.
+ if (NodeId.IsNull(branchId))
+ {
+ AddChild(node);
+ }
+
+ // initialize event information.node
+ node.EventType.Value = node.TypeDefinitionId;
+ node.SourceNode.Value = NodeId;
+ node.SourceName.Value = SymbolicName;
+ node.ConditionName.Value = node.SymbolicName;
+ node.Time.Value = DateTime.UtcNow;
+ node.ReceiveTime.Value = node.Time.Value;
+ node.BranchId.Value = branchId;
+
+ // set up method handlers.
+ node.OnEnableDisable = OnEnableDisableAlarm;
+ node.OnAcknowledge = OnAcknowledge;
+ node.OnAddComment = OnAddComment;
+ node.OnConfirm = OnConfirm;
+ node.OnShelve = OnShelve;
+ node.OnTimedUnshelve = OnTimedUnshelve;
+
+ // return the new node.
+ return node;
+ }
+
+ ///
+ /// Updates the alarm with a new state.
+ ///
+ /// The node.
+ /// The alarm.
+ private void UpdateAlarm(AlarmConditionState node, UnderlyingSystemAlarm alarm)
+ {
+ ISystemContext context = m_nodeManager.SystemContext;
+
+ // remove old event.
+ if (node.EventId.Value != null)
+ {
+ m_events.Remove(Utils.ToHexString(node.EventId.Value));
+ }
+
+ // update the basic event information (include generating a unique id for the event).
+ node.EventId.Value = GetNextGuidAsByteArray();
+ node.Time.Value = DateTime.UtcNow;
+ node.ReceiveTime.Value = node.Time.Value;
+
+ // save the event for later lookup.
+ m_events[Utils.ToHexString(node.EventId.Value)] = node;
+
+ // determine the retain state.
+ node.Retain.Value = true;
+
+ if (alarm != null)
+ {
+ node.Time.Value = alarm.Time;
+ node.Message.Value = new LocalizedText(alarm.Reason);
+
+ // update the states.
+ node.SetEnableState(context, (alarm.State & UnderlyingSystemAlarmStates.Enabled) != 0);
+ node.SetAcknowledgedState(context, (alarm.State & UnderlyingSystemAlarmStates.Acknowledged) != 0);
+ node.SetConfirmedState(context, (alarm.State & UnderlyingSystemAlarmStates.Confirmed) != 0);
+ node.SetActiveState(context, (alarm.State & UnderlyingSystemAlarmStates.Active) != 0);
+ node.SetSuppressedState(context, (alarm.State & UnderlyingSystemAlarmStates.Suppressed) != 0);
+
+ // update other information.
+ node.SetComment(context, alarm.Comment, alarm.UserName);
+ node.SetSeverity(context, alarm.Severity);
+
+ node.EnabledState.TransitionTime.Value = alarm.EnableTime;
+ node.ActiveState.TransitionTime.Value = alarm.ActiveTime;
+
+ // check for deleted items.
+ if ((alarm.State & UnderlyingSystemAlarmStates.Deleted) != 0)
+ {
+ node.Retain.Value = false;
+ }
+
+ // handle high alarms.
+ if (node is ExclusiveLimitAlarmState highAlarm)
+ {
+ highAlarm.HighLimit.Value = alarm.Limits[0];
+
+ if ((alarm.State & UnderlyingSystemAlarmStates.High) != 0)
+ {
+ highAlarm.SetLimitState(context, LimitAlarmStates.High);
+ }
+ }
+
+ // handle high-low alarms.
+ if (node is NonExclusiveLimitAlarmState highLowAlarm)
+ {
+ highLowAlarm.HighHighLimit.Value = alarm.Limits[0];
+ highLowAlarm.HighLimit.Value = alarm.Limits[1];
+ highLowAlarm.LowLimit.Value = alarm.Limits[2];
+ highLowAlarm.LowLowLimit.Value = alarm.Limits[3];
+
+ LimitAlarmStates limit = LimitAlarmStates.Inactive;
+
+ if ((alarm.State & UnderlyingSystemAlarmStates.HighHigh) != 0)
+ {
+ limit |= LimitAlarmStates.HighHigh;
+ }
+
+ if ((alarm.State & UnderlyingSystemAlarmStates.High) != 0)
+ {
+ limit |= LimitAlarmStates.High;
+ }
+
+ if ((alarm.State & UnderlyingSystemAlarmStates.Low) != 0)
+ {
+ limit |= LimitAlarmStates.Low;
+ }
+
+ if ((alarm.State & UnderlyingSystemAlarmStates.LowLow) != 0)
+ {
+ limit |= LimitAlarmStates.LowLow;
+ }
+
+ highLowAlarm.SetLimitState(context, limit);
+ }
+ }
+
+ // not interested in disabled or inactive alarms.
+ if (!node.EnabledState.Id.Value || !node.ActiveState.Id.Value)
+ {
+ node.Retain.Value = false;
+ }
+ }
+
+ ///
+ /// Called when the alarm is enabled or disabled.
+ ///
+ private ServiceResult OnEnableDisableAlarm(
+ ISystemContext context,
+ ConditionState condition,
+ bool enabling)
+ {
+ m_source.EnableAlarm(condition.SymbolicName, enabling);
+ return ServiceResult.Good;
+ }
+
+ ///
+ /// Called when the alarm has a comment added.
+ ///
+ private ServiceResult OnAddComment(
+ ISystemContext context,
+ ConditionState condition,
+ byte[] eventId,
+ LocalizedText comment)
+ {
+ AlarmConditionState alarm = FindAlarmByEventId(eventId);
+
+ if (alarm == null)
+ {
+ return StatusCodes.BadEventIdUnknown;
+ }
+
+ m_source.CommentAlarm(alarm.SymbolicName, GetRecordNumber(alarm), comment, GetUserName(context));
+
+ return ServiceResult.Good;
+ }
+
+ ///
+ /// Called when the alarm is acknowledged.
+ ///
+ private ServiceResult OnAcknowledge(
+ ISystemContext context,
+ ConditionState condition,
+ byte[] eventId,
+ LocalizedText comment)
+ {
+ AlarmConditionState alarm = FindAlarmByEventId(eventId);
+
+ if (alarm == null)
+ {
+ return StatusCodes.BadEventIdUnknown;
+ }
+
+ m_source.AcknowledgeAlarm(alarm.SymbolicName, GetRecordNumber(alarm), comment, GetUserName(context));
+
+ return ServiceResult.Good;
+ }
+
+ ///
+ /// Called when the alarm is confirmed.
+ ///
+ private ServiceResult OnConfirm(
+ ISystemContext context,
+ ConditionState condition,
+ byte[] eventId,
+ LocalizedText comment)
+ {
+ AlarmConditionState alarm = FindAlarmByEventId(eventId);
+
+ if (alarm == null)
+ {
+ return StatusCodes.BadEventIdUnknown;
+ }
+
+ m_source.ConfirmAlarm(alarm.SymbolicName, GetRecordNumber(alarm), comment, GetUserName(context));
+
+ return ServiceResult.Good;
+ }
+
+ ///
+ /// Called when the alarm is shelved.
+ ///
+ private ServiceResult OnShelve(
+ ISystemContext context,
+ AlarmConditionState alarm,
+ bool shelving,
+ bool oneShot,
+ double shelvingTime)
+ {
+ alarm.SetShelvingState(context, shelving, oneShot, shelvingTime);
+ alarm.Message.Value = "The alarm shelved.";
+
+ UpdateAlarm(alarm, null);
+ ReportChanges(alarm);
+
+ return ServiceResult.Good;
+ }
+
+ ///
+ /// Called when the alarm is shelved.
+ ///
+ private ServiceResult OnTimedUnshelve(
+ ISystemContext context,
+ AlarmConditionState alarm)
+ {
+ // update the alarm state and produce and event.
+ alarm.SetShelvingState(context, false, false, 0);
+ alarm.Message.Value = "The timed shelving period expired.";
+
+ UpdateAlarm(alarm, null);
+ ReportChanges(alarm);
+
+ return ServiceResult.Good;
+ }
+
+ ///
+ /// Called when the dialog receives a response.
+ ///
+ private ServiceResult OnRespond(
+ ISystemContext context,
+ DialogConditionState dialog,
+ int selectedResponse)
+ {
+ // response 0 means set the source online.
+ if (selectedResponse == 0)
+ {
+ m_source.SetOfflineState(false);
+ }
+
+ // response 1 means set the source offline.
+ if (selectedResponse == 1)
+ {
+ m_source.SetOfflineState(true);
+ }
+
+ // other responses mean do nothing.
+ dialog.SetResponse(context, selectedResponse);
+
+ // dialog no longer interesting once it is deactivated.
+ dialog.Message.Value = "The dialog was deactivated";
+ dialog.Retain.Value = false;
+
+ return ServiceResult.Good;
+ }
+
+ ///
+ /// Reports the changes to the alarm.
+ ///
+ private void ReportChanges(AlarmConditionState alarm)
+ {
+ // report changes to node attributes.
+ alarm.ClearChangeMasks(m_nodeManager.SystemContext, true);
+
+ // check if events are being monitored for the source.
+ if (AreEventsMonitored)
+ {
+ // create a snapshot.
+ InstanceStateSnapshot e = new();
+ e.Initialize(m_nodeManager.SystemContext, alarm);
+
+ // report the event.
+ alarm.ReportEvent(m_nodeManager.SystemContext, e);
+ }
+ }
+
+ ///
+ /// Finds the alarm by event id.
+ ///
+ /// The event id.
+ /// The alarm. Null if not found.
+ private AlarmConditionState FindAlarmByEventId(byte[] eventId)
+ {
+ if (eventId == null)
+ {
+ return null;
+ }
+
+ if (!m_events.TryGetValue(Utils.ToHexString(eventId), out AlarmConditionState alarm))
+ {
+ return null;
+ }
+
+ return alarm;
+ }
+
+ ///
+ /// Gets the record number associated with tge alarm.
+ ///
+ /// The alarm.
+ /// The record number; 0 if the alarm is not an archived alarm.
+ private uint GetRecordNumber(AlarmConditionState alarm)
+ {
+ if (alarm == null)
+ {
+ return 0;
+ }
+
+ if (alarm.BranchId == null || alarm.BranchId.Value == null)
+ {
+ return 0;
+ }
+
+ uint? recordNumber = alarm.BranchId.Value.Identifier as uint?;
+
+ if (recordNumber != null)
+ {
+ return recordNumber.Value;
+ }
+
+ return 0;
+ }
+
+ ///
+ /// Gets the user name associated with the context.
+ ///
+ private string GetUserName(ISystemContext context)
+ {
+ if (context.UserIdentity != null)
+ {
+ return context.UserIdentity.DisplayName;
+ }
+
+ return null;
+ }
+ #endregion
+
+ #region Private Fields
+ private readonly AlarmConditionServerNodeManager m_nodeManager;
+ private readonly UnderlyingSystemSource m_source;
+ private readonly Dictionary m_alarms;
+ private readonly Dictionary m_events;
+ private readonly Dictionary m_branches;
+ private readonly DialogConditionState m_dialog;
+ private DataGenerator m_generator;
+ #endregion
+ }
+}