664 lines · 25.7 KB · C#
RawDownload
//******************************************************************************************************//  AlarmMonitor.cs - Gbtc////  Copyright © 2012, 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 MIT License (MIT), 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/MIT////  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://  ----------------------------------------------------------------------------------------------------//  02/16/2012 - Stephen C. Wills//       Generated original version of source code.//  12/20/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Data;using System.Linq;using System.Net.Sockets;using System.Threading;using System.Timers;using GSF.Data;using GSF.TimeSeries.Transport;using GSF.TimeSeries.UI.DataModels;using Timer = System.Timers.Timer; namespace GSF.TimeSeries.UI{    /// <summary>    /// Represents an alarm monitor, for use inside a management UI application, that gets initial state of raised alarms    /// from an invoke based console connection command then maintains state with a data subscription to all alarms.    /// </summary>    public class AlarmMonitor : IDisposable    {        #region [ Members ]         // Constants         /// <summary>        /// Default refresh interval for alarm monitor.        /// </summary>        public const int DefaultRefreshInterval = 10;         // For future use we may want an alarm sound for a given severity        //private const string DefaultSoundFile = "";        //private const int DefaultAlertSeverity = 850;         // Events         /// <summary>        /// Event raised when alarms are refreshed.        /// </summary>        public event EventHandler RefreshedAlarms;         // Fields        private AlarmStatusQuery m_alarmStatusQuery;        private DataSubscriber m_dataSubscriber;        private readonly UnsynchronizedSubscriptionInfo m_subscriptionInfo;        private volatile Dictionary<int, Alarm> m_definedAlarmsByID;        private volatile Dictionary<Guid, Alarm> m_definedAlarmsByMeasurement;        private readonly object m_currentAlarmsLock;        private readonly ISet<Alarm> m_currentAlarms;        private int m_alarmsChanged;        private Timer m_refreshTimer;        private int m_refreshInterval;        private bool m_disposed;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new instance of the <see cref="AlarmMonitor"/> class.        /// </summary>        /// <param name="singleton">Indicates whether this instance should update the global reference to become the singleton.</param>        public AlarmMonitor(bool singleton = false)        {            object refreshInterval = IsolatedStorageManager.ReadFromIsolatedStorage("AlarmStatusRefreshInterval") ?? DefaultRefreshInterval;             if (!int.TryParse(refreshInterval.ToString(), out m_refreshInterval))                m_refreshInterval = DefaultRefreshInterval;             m_currentAlarmsLock = new object();            m_currentAlarms = new HashSet<Alarm>();             m_refreshTimer = new Timer(m_refreshInterval * 1000);            m_refreshTimer.Elapsed += RefreshTimer_Elapsed;             m_alarmStatusQuery = new AlarmStatusQuery();            m_alarmStatusQuery.RaisedAlarmStates += m_alarmStatusQuery_RaisedAlarmStates;            m_alarmStatusQuery.ProcessException += m_alarmStatusQuery_ProcessException;             // Load all alarms defined in the database            UpdateDefinedAlarms();             // Setup subscription to subscribe to all alarm measurements            m_subscriptionInfo = new UnsynchronizedSubscriptionInfo(false)            {                FilterExpression = "FILTER ActiveMeasurements WHERE SignalType='ALRM'"            };             m_dataSubscriber = new DataSubscriber();            m_dataSubscriber.ConnectionEstablished += m_dataSubscriber_ConnectionEstablished;            m_dataSubscriber.ReceivedServerResponse += m_dataSubscriber_ReceivedServerResponse;            m_dataSubscriber.NewMeasurements += m_dataSubscriber_NewMeasurements;            m_dataSubscriber.ProcessException += m_dataSubscriber_ProcessException;            m_dataSubscriber.ConnectionString = GetDataPublisherConnectionString();            m_dataSubscriber.OperationalModes |= OperationalModes.UseCommonSerializationFormat | OperationalModes.CompressSignalIndexCache;            m_dataSubscriber.DataLossInterval = -1;            m_dataSubscriber.Initialize();             if (singleton)                Default = this;        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets the interval, in seconds,        /// between refreshing the alarm list.        /// </summary>        public int RefreshInterval        {            get            {                return m_refreshInterval;            }            set            {                m_refreshTimer.Interval = value * 1000;                m_refreshInterval = value;            }        }         /// <summary>        /// Gets the current list of raised alarms.        /// </summary>        /// <returns>Current <see cref="RaisedAlarm"/> list.</returns>        public ObservableCollection<RaisedAlarm> GetAlarmList()        {            IEnumerable<RaisedAlarm> currentHighestSeverityAlarms;             lock (m_currentAlarmsLock)            {                currentHighestSeverityAlarms = m_currentAlarms                    .GroupBy(alarm => alarm.SignalID)                    .SelectMany(GetHighestSeverityAlarms)                    .Select(CreateRaisedAlarm);                 return new ObservableCollection<RaisedAlarm>(currentHighestSeverityAlarms);            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Performs application-defined tasks associated with freeing, releasing, or        /// resetting unmanaged resources.        /// </summary>        public void Dispose()        {            Dispose(true);            GC.SuppressFinalize(this);        }         /// <summary>        /// Releases the unmanaged resources used by the <see cref="AlarmMonitor"/> object and optionally releases the managed resources.        /// </summary>        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>        protected virtual void Dispose(bool disposing)        {            if (!m_disposed)            {                try                {                    if (disposing)                    {                        if ((object)m_refreshTimer != null)                        {                            m_refreshTimer.Dispose();                            m_refreshTimer = null;                        }                         if ((object)m_alarmStatusQuery != null)                        {                            m_alarmStatusQuery.RaisedAlarmStates -= m_alarmStatusQuery_RaisedAlarmStates;                            m_alarmStatusQuery.ProcessException -= m_alarmStatusQuery_ProcessException;                            m_alarmStatusQuery.Dispose();                            m_alarmStatusQuery = null;                        }                         if ((object)m_dataSubscriber != null)                        {                            m_dataSubscriber.ConnectionEstablished -= m_dataSubscriber_ConnectionEstablished;                            m_dataSubscriber.ReceivedServerResponse -= m_dataSubscriber_ReceivedServerResponse;                            m_dataSubscriber.NewMeasurements -= m_dataSubscriber_NewMeasurements;                            m_dataSubscriber.ProcessException -= m_dataSubscriber_ProcessException;                            m_dataSubscriber.Dispose();                            m_dataSubscriber = null;                        }                    }                }                finally                {                    m_disposed = true;          // Prevent duplicate dispose.                }            }        }         /// <summary>        /// Starts the refresh timer that notifies consumer about the current alarm status.        /// </summary>        public void Start()        {            if (!m_refreshTimer.Enabled)            {                // Start subscriber connection cycle                m_dataSubscriber.Start();                 // Start the refresh timer                m_refreshTimer.Start();            }        }         /// <summary>        /// Updates the collection of defined system alarms.        /// </summary>        public void UpdateDefinedAlarms()        {            IEnumerable<Alarm> definedAlarms = LoadDefinedAlarms();            List<Alarm> oldCurrentAlarms;            Alarm newCurrentAlarm;             // Initialize lookup table for defined alarms by ID            m_definedAlarmsByID = definedAlarms.ToDictionary(alarm => alarm.ID);             // Initialize lookup table for defined alarms by associated measurement ID            m_definedAlarmsByMeasurement = definedAlarms                .Where(alarm => alarm.AssociatedMeasurementID.HasValue)                .GroupBy(alarm => alarm.AssociatedMeasurementID.GetValueOrDefault())                .ToDictionary(group => group.Key, group => group.First());             lock (m_currentAlarmsLock)            {                // Copy current alarms set to a list so we can iterate while removing                oldCurrentAlarms = new List<Alarm>(m_currentAlarms);                 foreach (Alarm oldCurrentAlarm in oldCurrentAlarms)                {                    // Remove the old alarm from the set of current alarms                    m_currentAlarms.Remove(oldCurrentAlarm);                     // If the old alarm is still defined in the updated alarms table,                    // add the equivalent new defined alarm to the set of current alarms                    if (m_definedAlarmsByID.TryGetValue(oldCurrentAlarm.ID, out newCurrentAlarm))                    {                        newCurrentAlarm.TimeRaised = oldCurrentAlarm.TimeRaised;                        m_currentAlarms.Add(newCurrentAlarm);                    }                }            }             // Set alarms changed state to false            Interlocked.Exchange(ref m_alarmsChanged, 0);             // Notify that the alarms have been updated            OnRefreshedAlarms();        }         /// <summary>        /// Stops the refresh timer.        /// </summary>        public void Stop()        {            if (m_refreshTimer.Enabled)            {                // Stop the refresh timer                m_refreshTimer.Stop();                 // Stop data subscriber                m_dataSubscriber.Stop();            }        }         // Processing for the Elapsed event of the refresh timer.        private void RefreshTimer_Elapsed(object sender, ElapsedEventArgs e)        {            // No need to raise event unless current alarm state has changed            if (Interlocked.CompareExchange(ref m_alarmsChanged, 0, 1) != 0)                OnRefreshedAlarms();        }         // Gets the current data publisher connection string        private string GetDataPublisherConnectionString()        {            AdoDataConnection database = null;             try            {                // Create database connection to get the current data publisher connection string                database = new AdoDataConnection(CommonFunctions.DefaultSettingsCategory);                return database.DataPublisherConnectionString();            }            finally            {                if ((object)database != null)                    database.Dispose();            }        }         // Loads existing alarms defined in the database        private IEnumerable<Alarm> LoadDefinedAlarms()        {            // Create alarms using definitions from the database            AdoDataConnection database = null;             try            {                // Create database connection to get currently defined alarms                database = new AdoDataConnection(CommonFunctions.DefaultSettingsCategory);                string query = database.ParameterizedQueryString("SELECT * FROM Alarm WHERE NodeID = {0} AND Enabled <> 0", "nodeID");                DataTable alarms = database.Connection.RetrieveData(database.AdapterType, query, database.CurrentNodeID());                 return alarms.Rows.Cast<DataRow>()                    .Select(CreateAlarm)                    .ToList();            }            finally            {                if ((object)database != null)                    database.Dispose();            }        }         // Creates an alarm using a data row queried from the database        private Alarm CreateAlarm(DataRow row)        {            object associatedMeasurementId = row.Field<object>("AssociatedMeasurementID");             return new Alarm            {                ID = row.ConvertField<int>("ID"),                TagName = row.Field<object>("TagName").ToString(),                SignalID = Guid.Parse(row.Field<object>("SignalID").ToString()),                AssociatedMeasurementID = ((object)associatedMeasurementId != null) ? Guid.Parse(associatedMeasurementId.ToString()) : (Guid?)null,                Description = row.Field<object>("Description").ToNonNullString(),                Severity = row.ConvertField<int>("Severity").GetEnumValueOrDefault<AlarmSeverity>(AlarmSeverity.None),                Operation = row.ConvertField<int>("Operation").GetEnumValueOrDefault<AlarmOperation>(AlarmOperation.Equal),                SetPoint = row.ConvertNullableField<double>("SetPoint"),                Tolerance = row.ConvertNullableField<double>("Tolerance"),                Delay = row.ConvertNullableField<double>("Delay"),                Hysteresis = row.ConvertNullableField<double>("Hysteresis")            };        }         // Creates a data model based "RaisedAlarm" from an alarm        private RaisedAlarm CreateRaisedAlarm(Alarm alarm)        {            return new RaisedAlarm            {                ID = alarm.ID,                Severity = (int)alarm.Severity,                TimeRaised = ((DateTime)alarm.TimeRaised).ToString("MM/dd/yyyy HH:mm:ss.fff"),                TagName = alarm.TagName,                Description = alarm.Description            };        }         // Get the defined alarm for the given alarm event        private Alarm GetDefinedAlarm(IMeasurement alarmEvent)        {            Dictionary<Guid, Alarm> definedAlarmsByMeasurement;            Alarm definedAlarm;             definedAlarmsByMeasurement = m_definedAlarmsByMeasurement;             if (definedAlarmsByMeasurement.TryGetValue(alarmEvent.ID, out definedAlarm))                definedAlarm.TimeRaised = alarmEvent.Timestamp;             return definedAlarm;        }         // Get all the highest severity alarms in a list of alarms        private IEnumerable<Alarm> GetHighestSeverityAlarms(IEnumerable<Alarm> alarms)        {            AlarmSeverity highestSeverity = alarms.Max(alarm => alarm.Severity);            return alarms.Where(alarm => alarm.Severity == highestSeverity);        }         // Triggers the RefreshedAlarms event.        private void OnRefreshedAlarms()        {            if (RefreshedAlarms != null)                RefreshedAlarms(this, EventArgs.Empty);        }         private void m_alarmStatusQuery_RaisedAlarmStates(object sender, EventArgs<ICollection<Alarm>> e)        {            Dictionary<int, Alarm> definedAlarmsByID;            Alarm definedAlarm;             definedAlarmsByID = m_definedAlarmsByID;             // Received the initial set of alarms - cache them as the "current" raised alarm state            lock (m_currentAlarmsLock)            {                foreach (Alarm raisedAlarm in e.Argument)                {                    // We can only add defined alarms to the set of current                    // alarms to allow for fast set-based operations later                    if (definedAlarmsByID.TryGetValue(raisedAlarm.ID, out definedAlarm))                    {                        definedAlarm.TimeRaised = raisedAlarm.TimeRaised;                        m_currentAlarms.Add(definedAlarm);                    }                }            }             // Set alarms changed state to false            Interlocked.Exchange(ref m_alarmsChanged, 0);             // Notify that the alarms have been updated            OnRefreshedAlarms();        }         private void m_alarmStatusQuery_ProcessException(object sender, EventArgs<Exception> e)        {            CommonFunctions.LogException(null, "AlarmMonitor", e.Argument);        }         private void m_dataSubscriber_ConnectionEstablished(object sender, EventArgs e)        {            lock (m_currentAlarmsLock)            {                m_currentAlarms.Clear();            }             m_dataSubscriber.Subscribe(m_subscriptionInfo);        }         private void m_dataSubscriber_ReceivedServerResponse(object sender, EventArgs<ServerResponse, ServerCommand> e)        {            // Request the latest alarm states after subscription is established to ensure all alarm states are up-to-date            if (e.Argument1 == ServerResponse.Succeeded && e.Argument2 == ServerCommand.Subscribe)                m_alarmStatusQuery.RequestRaisedAlarmStates();        }         private void m_dataSubscriber_NewMeasurements(object sender, EventArgs<ICollection<IMeasurement>> e)        {            Dictionary<Guid, Alarm> definedAlarmsByMeasurement = m_definedAlarmsByMeasurement;            ICollection<IMeasurement> latestMeasurements;            Alarm definedAlarm = null;             IEnumerable<Alarm> clearedAlarms;            IEnumerable<Alarm> raisedAlarms;             // Get the most recent measurement for each alarm signal            latestMeasurements = e.Argument                .GroupBy(measurement => measurement.ID)                .Select(group => group.Last())                .ToList();             // Get alarms for measurements that have been cleared (i.e., value == 0)            clearedAlarms = latestMeasurements                .Where(measurement => measurement.AdjustedValue == 0.0D)                .Where(measurement => definedAlarmsByMeasurement.TryGetValue(measurement.ID, out definedAlarm))                .Select(measurement => definedAlarm)                .ToList();             // Get alarms for measurements that have been raised (i.e., value == 1)            raisedAlarms = latestMeasurements                .Where(measurement => measurement.AdjustedValue != 0.0D)                .Select(GetDefinedAlarm)                .Where(alarm => (object)alarm != null)                .ToList();             // Handle any changes to current raised alarm list            if (raisedAlarms.Any() || clearedAlarms.Any())            {                // Update current alarms - removing then adding alarms                lock (m_currentAlarmsLock)                {                    m_currentAlarms.ExceptWith(clearedAlarms);                    m_currentAlarms.UnionWith(raisedAlarms);

Lines 1–500 of 664.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t