//******************************************************************************************************
// Alarm.cs - Gbtc
//
// Copyright © 2010, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain a copy of the License at:
//
// http://www.opensource.org/licenses/eclipse-1.0.php
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 01/31/2012 - Stephen C. Wills
// Generated original version of source code.
// 02/10/2012 - Stephen C. Wills
// Moved to TimeSeriesFramework project.
//
//******************************************************************************************************
using System;
namespace GSF.TimeSeries
{
#region [ Enumerations ]
///
/// Represents the two states that an alarm can be in: raised or cleared.
///
public enum AlarmState
{
///
/// Indicates that an alarm is cleared.
///
Cleared = 0,
///
/// Indicates that an alarm has been raised.
///
Raised = 1
}
///
/// Represents the severity of alarms.
///
public enum AlarmSeverity
{
///
/// Indicates that an alarm is of no importance.
///
None = 0,
///
/// Indicates that an alarm is informative, but not dangerous.
///
Information = 50,
///
/// Indicates that an alarm is not very important.
///
Low = 150,
///
/// Indicates that an alarm is somewhat important.
///
MediumLow = 300,
///
/// Indicates that an alarm is moderately importance.
///
Medium = 500,
///
/// Indicates that an alarm is important.
///
MediumHigh = 700,
///
/// Indicates that an alarm is very important.
///
High = 850,
///
/// Indicates than an alarm signifies a dangerous situation.
///
Critical = 950,
///
/// Indicates that an alarm reports bad data.
///
Error = 1000
}
///
/// Represents the operation to be performed
/// when testing values from an incoming signal.
///
public enum AlarmOperation
{
///
/// Internal range test
///
Equal = 1,
///
/// External range test
///
NotEqual = 2,
///
/// Upper bound
///
GreaterOrEqual = 11,
///
/// Lower bound
///
LessOrEqual = 21,
///
/// Upper limit
///
GreaterThan = 12,
///
/// Lower limit
///
LessThan = 22,
///
/// Latched value
///
Flatline = 3
}
#endregion
///
/// Represents an alarm that tests the values of
/// an incoming signal to determine the state of alarm.
///
public class Alarm
{
#region [ Members ]
// Fields
private Ticks m_lastNegative;
private double m_lastValue;
private Ticks m_lastChanged;
#endregion
#region [ Constructors ]
///
/// Creates a new instance of the class.
///
public Alarm()
{
State = 0;
}
#endregion
#region [ Properties ]
///
/// Gets or sets the identification number of the alarm.
///
public int ID
{
get;
set;
}
///
/// Gets or sets the tag name of the alarm.
///
public string TagName
{
get;
set;
}
///
/// Gets or sets the identification number of the
/// signal whose value is monitored by the alarm.
///
public Guid SignalID
{
get;
set;
}
///
/// Gets or sets the identification number of
/// the measurements generated for alarm events.
///
public Guid? AssociatedMeasurementID
{
get;
set;
}
///
/// Gets or sets the description of the alarm.
///
public string Description
{
get;
set;
}
///
/// Gets or sets the severity of the alarm.
///
public AlarmSeverity Severity
{
get;
set;
}
///
/// Gets or sets the operation to be performed
/// when testing values from the incoming signal.
///
public AlarmOperation Operation
{
get;
set;
}
///
/// Gets or sets the value to be compared against
/// the signal to determine whether to raise the
/// alarm. This value is irrelevant for the
/// operation.
///
public double? SetPoint
{
get;
set;
}
///
/// Gets or sets a tolerance window around the
/// to use when comparing
/// against the value of the signal. This value
/// is only relevant for the
/// and operations.
///
///
/// The equal and not equal operations are actually
/// internal and external range tests based on the setpoint
/// and the tolerance. The two tests are performed as follows.
///
///
/// - Equal: (value >= SetPoint - Tolerance) && (value <= SetPoint + Tolerance)
/// - Not equal: (value < SetPoint - Tolerance) || (value > SetPoint + Tolerance)
///
///
public double? Tolerance
{
get;
set;
}
///
/// Gets or sets the amount of time that the
/// signal must be exhibiting alarming behavior
/// before the alarm is raised.
///
public double? Delay
{
get;
set;
}
///
/// Gets or sets the hysteresis used when clearing
/// alarms. This value is only relevant in greater
/// than (or equal) and less than (or equal) operations.
///
///
/// The hysteresis is an offset that provides padding between
/// the point at which the alarm is raised and the point at
/// which the alarm is cleared. For example, in the case of the
/// operation:
///
///
/// - Raised: value >= SetPoint
/// - Cleared: value < SetPoint - Hysteresis
///
///
/// The direction of the offset depends on whether the
/// operation is greater than (or equal) or less than (or equal).
/// The hysteresis must be greater than zero.
///
public double? Hysteresis
{
get;
set;
}
///
/// Gets or sets the state of the alarm (raised or cleared).
///
public AlarmState State
{
get;
set;
}
///
/// Gets or sets the most recent measurement
/// that caused the alarm to be raised.
///
public IMeasurement Cause
{
get;
set;
}
#endregion
#region [ Methods ]
///
/// Tests the value of the given signal to determine
/// whether the alarm should be raised or cleared.
///
/// The signal whose value is to be tested.
/// true if the alarm's state changed; false otherwise
public bool Test(IMeasurement signal)
{
AlarmState previousState = State;
if (State == AlarmState.Raised && ClearsAlarm(signal))
{
State = AlarmState.Cleared;
}
else if (State == AlarmState.Cleared && RaisesAlarm(signal))
{
State = AlarmState.Raised;
Cause = signal;
}
return State != previousState;
}
// Tests the given measurement to determine whether
// its value triggers an alarm raised event.
private bool RaisesAlarm(IMeasurement signal)
{
if (Operation != AlarmOperation.Flatline)
return CheckRange(signal);
return CheckFlatline(signal);
}
// Tests the given measurement to determine whether
// its value indicates that the signal has been in
// the alarming range for the configured delay time.
private bool CheckRange(IMeasurement signal)
{
bool result = GetTestResult(signal);
Ticks dist;
if (!result)
{
// Keep track of the last time
// the signal was in range
m_lastNegative = signal.Timestamp;
}
else
{
// Get the amount of time since the
// last time the value was in range
dist = signal.Timestamp - m_lastNegative;
// If the amount of time is larger than
// the delay threshold, raise the alarm
if (dist >= Ticks.FromSeconds(Delay.Value))
return true;
}
return false;
}
// Tests the signal to determine whether its
// value is within the configured alarming range.
private bool GetTestResult(IMeasurement signal)
{
double value = signal.Value;
switch (Operation)
{
case AlarmOperation.Equal:
return (value <= SetPoint + Tolerance) && (value >= SetPoint - Tolerance);
case AlarmOperation.NotEqual:
return (value < SetPoint - Tolerance) || (value > SetPoint + Tolerance);
case AlarmOperation.GreaterOrEqual:
return value >= SetPoint;
case AlarmOperation.LessOrEqual:
return value <= SetPoint;
case AlarmOperation.GreaterThan:
return value > SetPoint;
case AlarmOperation.LessThan:
return value < SetPoint;
}
return false;
}
// Tests the given measurement to determine whether
// its value triggers an alarm cleared event.
private bool ClearsAlarm(IMeasurement signal)
{
if (Operation != AlarmOperation.Flatline)
return CheckRangeClear(signal);
return !CheckFlatline(signal);
}
// Tests the given measurement to determine whether its value
// indicates that the signal has left the alarming range.
private bool CheckRangeClear(IMeasurement signal)
{
double value = signal.Value;
switch (Operation)
{
case AlarmOperation.GreaterOrEqual:
return (value < SetPoint - Hysteresis);
case AlarmOperation.LessOrEqual:
return (value > SetPoint + Hysteresis);
case AlarmOperation.GreaterThan:
return (value <= SetPoint - Hysteresis);
case AlarmOperation.LessThan:
return (value >= SetPoint + Hysteresis);
default:
return !GetTestResult(signal);
}
}
// Determines whether the given value has
// flatlined over the configured delay interval.
private bool CheckFlatline(IMeasurement signal)
{
long dist, diff;
if (signal.Value != m_lastValue)
{
m_lastChanged = signal.Timestamp;
m_lastValue = signal.Value;
}
dist = Ticks.FromSeconds(Delay.Value);
diff = signal.Timestamp - m_lastChanged;
return diff >= dist;
}
#endregion
}
}