/// <summary> /// Finds the alarm identified by the name. /// </summary> /// <param name="alarmName">Name of the alarm.</param> /// <param name="recordNumber">The record number associated with the alarm.</param> /// <returns>The alarm if null; otherwise null.</returns> private UnderlyingSystemAlarm FindAlarm(string alarmName, uint recordNumber) { lock (m_alarms) { // look up archived alarm. if (recordNumber != 0) { UnderlyingSystemAlarm alarm = null; if (!m_archive.TryGetValue(recordNumber, out alarm)) { return(null); } return(alarm); } // look up alarm. for (int ii = 0; ii < m_alarms.Count; ii++) { UnderlyingSystemAlarm alarm = m_alarms[ii]; if (alarm.Name == alarmName) { return(alarm); } } return(null); } }
/// <summary> /// Acknowledges an alarm. /// </summary> /// <param name="alarmName">Name of the alarm.</param> /// <param name="recordNumber">The record number.</param> /// <param name="comment">The comment.</param> /// <param name="userName">Name of the user.</param> public void AcknowledgeAlarm(string alarmName, uint recordNumber, LocalizedText comment, string userName) { UnderlyingSystemAlarm snapshot = null; lock (m_alarms) { UnderlyingSystemAlarm alarm = FindAlarm(alarmName, recordNumber); if (alarm != null) { if (alarm.SetStateBits(UnderlyingSystemAlarmStates.Acknowledged, true)) { alarm.Time = DateTime.UtcNow; alarm.Reason = "The alarm was acknoweledged."; alarm.Comment = Utils.Format("{0}", comment); alarm.UserName = userName; alarm.SetStateBits(UnderlyingSystemAlarmStates.Confirmed, false); } snapshot = alarm.CreateSnapshot(); } } if (snapshot != null) { ReportAlarmChange(snapshot); } }
/// <summary> /// Sets the state of the source (surpresses any active alarms). /// </summary> /// <param name="offline">if set to <c>true</c> the source is offline.</param> public void SetOfflineState(bool offline) { m_isOffline = offline; List <UnderlyingSystemAlarm> snapshots = new List <UnderlyingSystemAlarm>(); lock (m_alarms) { for (int ii = 0; ii < m_alarms.Count; ii++) { UnderlyingSystemAlarm alarm = m_alarms[ii]; if (alarm.SetStateBits(UnderlyingSystemAlarmStates.Suppressed, offline)) { alarm.Time = alarm.EnableTime = DateTime.UtcNow; alarm.Reason = "The alarm was " + ((offline)?"suppressed.":"unsuppressed."); // check if the alarm change should be reported. if ((alarm.State & UnderlyingSystemAlarmStates.Enabled) != 0) { snapshots.Add(alarm.CreateSnapshot()); } } } } // report any alarm changes after releasing the lock. for (int ii = 0; ii < snapshots.Count; ii++) { ReportAlarmChange(snapshots[ii]); } }
/// <summary> /// Adds a comment to an alarm. /// </summary> /// <param name="alarmName">Name of the alarm.</param> /// <param name="recordNumber">The record number.</param> /// <param name="comment">The comment.</param> /// <param name="userName">Name of the user.</param> public void CommentAlarm(string alarmName, uint recordNumber, LocalizedText comment, string userName) { UnderlyingSystemAlarm snapshot = null; lock (m_alarms) { UnderlyingSystemAlarm alarm = FindAlarm(alarmName, recordNumber); if (alarm != null) { alarm.Time = DateTime.UtcNow; alarm.Reason = "A comment was added."; alarm.UserName = userName; // only change the comment if a non-null comment was provided. if (comment != null && (!String.IsNullOrEmpty(comment.Text) || !String.IsNullOrEmpty(comment.Locale))) { alarm.Comment = Utils.Format("{0}", comment); } snapshot = alarm.CreateSnapshot(); } } if (snapshot != null) { ReportAlarmChange(snapshot); } }
/// <summary> /// Reports a change to an alarm record. /// </summary> /// <param name="alarm">The alarm.</param> private void ReportAlarmChange(UnderlyingSystemAlarm alarm) { if (OnAlarmChanged != null) { try { OnAlarmChanged(alarm); } catch (Exception e) { Utils.Trace(e, "Unexpected error reporting change to an Alarm for Source {0}.", m_sourcePath); } } }
/// <summary> /// Reports the current state of all conditions. /// </summary> public void Refresh() { List <UnderlyingSystemAlarm> snapshots = new List <UnderlyingSystemAlarm>(); lock (m_alarms) { for (int ii = 0; ii < m_alarms.Count; ii++) { UnderlyingSystemAlarm alarm = m_alarms[ii]; snapshots.Add(alarm.CreateSnapshot()); } } // report any alarm changes after releasing the lock. for (int ii = 0; ii < snapshots.Count; ii++) { ReportAlarmChange(snapshots[ii]); } }
/// <summary> /// Called when the state of an alarm for the source has changed. /// </summary> private void OnAlarmChanged(UnderlyingSystemAlarm alarm) { lock (m_nodeManager.Lock) { // ignore archived alarms for now. if (alarm.RecordNumber != 0) { NodeId branchId = new NodeId(alarm.RecordNumber, this.NodeId.NamespaceIndex); // find the alarm branch. AlarmConditionState branch = null; if (!m_branches.TryGetValue(alarm.Name, out 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. AlarmConditionState node = null; if (!m_alarms.TryGetValue(alarm.Name, out node)) { m_alarms[alarm.Name] = node = CreateAlarm(alarm, null); } // map the system information to the UA defined alarm. UpdateAlarm(node, alarm); ReportChanges(node); } }
/// <summary> /// Enables or disables the alarm. /// </summary> /// <param name="alarmName">Name of the alarm.</param> /// <param name="enabling">if set to <c>true</c> the alarm is enabled.</param> public void EnableAlarm(string alarmName, bool enabling) { List <UnderlyingSystemAlarm> snapshots = new List <UnderlyingSystemAlarm>(); lock (m_alarms) { UnderlyingSystemAlarm alarm = FindAlarm(alarmName, 0); if (alarm != null) { // enable/disable the alarm. if (alarm.SetStateBits(UnderlyingSystemAlarmStates.Enabled, enabling)) { alarm.Time = alarm.EnableTime = DateTime.UtcNow; alarm.Reason = "The alarm was " + ((enabling)?"enabled.":"disabled."); snapshots.Add(alarm.CreateSnapshot()); } // enable/disable any archived records for the alarm. foreach (UnderlyingSystemAlarm record in m_archive.Values) { if (record.Name != alarmName) { continue; } if (record.SetStateBits(UnderlyingSystemAlarmStates.Enabled, enabling)) { record.Time = alarm.EnableTime = DateTime.UtcNow; record.Reason = "The alarm was " + ((enabling)?"enabled.":"disabled."); snapshots.Add(alarm.CreateSnapshot()); } } } } // report any alarm changes after releasing the lock. for (int ii = 0; ii < snapshots.Count; ii++) { ReportAlarmChange(snapshots[ii]); } }
/// <summary> /// Confirms an alarm. /// </summary> /// <param name="alarmName">Name of the alarm.</param> /// <param name="recordNumber">The record number.</param> /// <param name="comment">The comment.</param> /// <param name="userName">Name of the user.</param> public void ConfirmAlarm(string alarmName, uint recordNumber, LocalizedText comment, string userName) { UnderlyingSystemAlarm snapshot = null; lock (m_alarms) { UnderlyingSystemAlarm alarm = FindAlarm(alarmName, recordNumber); if (alarm != null) { if (alarm.SetStateBits(UnderlyingSystemAlarmStates.Confirmed, true)) { alarm.Time = DateTime.UtcNow; alarm.Reason = "The alarm was confirmed."; alarm.Comment = Utils.Format("{0}", comment); alarm.UserName = userName; // remove branch. if (recordNumber != 0) { m_archive.Remove(recordNumber); alarm.SetStateBits(UnderlyingSystemAlarmStates.Deleted, true); } // de-activate alarm. else { alarm.SetStateBits(UnderlyingSystemAlarmStates.Active, false); } } snapshot = alarm.CreateSnapshot(); } } if (snapshot != null) { ReportAlarmChange(snapshot); } }
/// <summary> /// Creates a new active alarm for the source. /// </summary> /// <param name="alarmName">Name of the alarm.</param> /// <param name="alarmType">Type of the alarm.</param> public void CreateAlarm(string alarmName, string alarmType) { UnderlyingSystemAlarm alarm = new UnderlyingSystemAlarm(); alarm.Source = this; alarm.Name = alarmName; alarm.AlarmType = alarmType; alarm.RecordNumber = 0; alarm.Reason = "Alarm created."; alarm.Time = DateTime.UtcNow; alarm.Severity = EventSeverity.Low; alarm.Comment = null; alarm.UserName = null; alarm.State = UnderlyingSystemAlarmStates.Active | UnderlyingSystemAlarmStates.Enabled; alarm.EnableTime = DateTime.UtcNow; alarm.ActiveTime = DateTime.UtcNow; switch (alarmType) { case "HighAlarm": { alarm.Limits = new double[] { 80 }; alarm.State |= UnderlyingSystemAlarmStates.High; break; } case "HighLowAlarm": { alarm.Limits = new double[] { 90, 70, 30, 10 }; alarm.State |= UnderlyingSystemAlarmStates.High; break; } } lock (m_alarms) { m_alarms.Add(alarm); } }
/// <summary> /// Updates the state of an alarm. /// </summary> private void UpdateAlarm(UnderlyingSystemAlarm alarm, long counter, int index, List <UnderlyingSystemAlarm> snapshots) { string reason = null; // ignore disabled alarms. if ((alarm.State & UnderlyingSystemAlarmStates.Enabled) == 0) { return; } // check if the alarm needs to be updated this cycle. if (counter % (8 + (index % 4)) == 0) { // check if it is time to activate. if ((alarm.State & UnderlyingSystemAlarmStates.Active) == 0) { reason = "The alarm is active."; alarm.SetStateBits(UnderlyingSystemAlarmStates.Active, true); alarm.SetStateBits(UnderlyingSystemAlarmStates.Acknowledged | UnderlyingSystemAlarmStates.Confirmed, false); alarm.Severity = EventSeverity.Low; alarm.ActiveTime = DateTime.UtcNow; switch (alarm.AlarmType) { case "HighAlarm": { alarm.SetStateBits(UnderlyingSystemAlarmStates.Limits, false); alarm.SetStateBits(UnderlyingSystemAlarmStates.High, true); break; } case "HighLowAlarm": { alarm.SetStateBits(UnderlyingSystemAlarmStates.Limits, false); alarm.SetStateBits(UnderlyingSystemAlarmStates.Low, true); break; } } } // bump the severity. else if ((alarm.State & UnderlyingSystemAlarmStates.Acknowledged) == 0) { if (alarm.Severity < EventSeverity.High) { reason = "The alarm severity has increased."; Array values = Enum.GetValues(typeof(EventSeverity)); for (int ii = 0; ii < values.Length; ii++) { EventSeverity severity = (EventSeverity)values.GetValue(ii); if (severity > alarm.Severity) { alarm.Severity = severity; break; } } if (alarm.Severity > EventSeverity.Medium) { switch (alarm.AlarmType) { case "HighLowAlarm": { alarm.SetStateBits(UnderlyingSystemAlarmStates.Limits, false); alarm.SetStateBits(UnderlyingSystemAlarmStates.LowLow, true); break; } } } } // give up on the alarm. else { // create an archived state that needs to be acknowledged. if (alarm.AlarmType == "TripAlarm") { // check the number of archived states. int count = 0; foreach (UnderlyingSystemAlarm record in m_archive.Values) { if (record.Name == alarm.Name) { count++; } } // limit the number of archived states to avoid filling up the display. if (count < 2) { // archive the current state. UnderlyingSystemAlarm snapshot = alarm.CreateSnapshot(); snapshot.RecordNumber = ++m_nextRecordNumber; snapshot.Severity = EventSeverity.Low; m_archive.Add(snapshot.RecordNumber, snapshot); snapshots.Add(snapshot); } } reason = "The alarm was deactivated by the system."; alarm.SetStateBits(UnderlyingSystemAlarmStates.Active, false); //alarm.SetStateBits(UnderlyingSystemAlarmStates.Acknowledged | UnderlyingSystemAlarmStates.Confirmed, true); alarm.Severity = EventSeverity.Low; } } } // update the reason. if (reason != null) { alarm.Time = DateTime.UtcNow; alarm.Reason = reason; // return a snapshot used to report the state change. snapshots.Add(alarm.CreateSnapshot()); } // no change so nothing to report. }
/// <summary> /// Updates the alarm with a new state. /// </summary> /// <param name="node">The node.</param> /// <param name="alarm">The alarm.</param> 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 = Guid.NewGuid().ToByteArray(); 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. ExclusiveLimitAlarmState highAlarm = node as ExclusiveLimitAlarmState; if (highAlarm != null) { highAlarm.HighLimit.Value = alarm.Limits[0]; if ((alarm.State & UnderlyingSystemAlarmStates.High) != 0) { highAlarm.SetLimitState(context, LimitAlarmStates.High); } } // handle high-low alarms. NonExclusiveLimitAlarmState highLowAlarm = node as NonExclusiveLimitAlarmState; if (highLowAlarm != null) { 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; } }
/// <summary> /// Creates a new alarm for the source. /// </summary> /// <param name="alarm">The alarm.</param> /// <param name="branchId">The branch id.</param> /// <returns>The new alarm.</returns> 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 ExclusiveDeviationAlarmState(this); node = node2; node2.HighLimit = new PropertyState <double>(node2); break; } case "HighLowAlarm": { NonExclusiveLevelAlarmState node2 = new NonExclusiveLevelAlarmState(this); node = node2; node2.HighHighLimit = new PropertyState <double>(node2); node2.HighLimit = new PropertyState <double>(node2); node2.LowLimit = new PropertyState <double>(node2); node2.LowLowLimit = new PropertyState <double>(node2); node2.HighHighState = new TwoStateVariableState(node2); node2.HighState = new TwoStateVariableState(node2); node2.LowState = new TwoStateVariableState(node2); 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 <LocalizedText>(node); node.ClientUserId = new PropertyState <string>(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 initilization 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 <DateTime>(node.EnabledState); node.EnabledState.EffectiveDisplayName = new PropertyState <LocalizedText>(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 <DateTime>(node.ActiveState); node.ActiveState.EffectiveDisplayName = new PropertyState <LocalizedText>(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 requried 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, this.BrowseName.NamespaceIndex), null, true); // don't add branches to the address space. if (NodeId.IsNull(branchId)) { this.AddChild(node); } // initialize event information.node node.EventType.Value = node.TypeDefinitionId; node.SourceNode.Value = this.NodeId; node.SourceName.Value = this.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); }