340 lines · 13.3 KB · C#
RawDownload
//******************************************************************************************************//  AlarmStatusQuery.cs - Gbtc////  Copyright © 2013, 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://  ----------------------------------------------------------------------------------------------------//  05/16/2013 - J. Ritchie Carroll//       Generated original version of source code.////****************************************************************************************************** using System;using System.Collections.Generic;using System.Threading;using GSF.Communication;using GSF.Console;using GSF.ServiceProcess;using GSF.Threading; namespace GSF.TimeSeries.UI{    /// <summary>    /// Represents as object that will query current alarm state.    /// </summary>    public class AlarmStatusQuery : IDisposable    {        #region [ Members ]         // Constants         /// <summary>        /// Default value for <see cref="ResponseTimeout"/>.        /// </summary>        public const int DefaultResponseTimeout = 20000; // Default to waiting twenty seconds, could be lots of alarms         // Events         /// <summary>        /// Provides collection containing raised alarms with the highest severity for each signal in the system.        /// </summary>        /// <remarks>        /// <see cref="EventArgs{T}.Argument"/> is a collection of the current high-severity <see cref="Alarm"/> states.        /// </remarks>        public event EventHandler<EventArgs<ICollection<Alarm>>> RaisedAlarmStates;         /// <summary>        /// Event is raised when there is an exception encountered while processing.        /// </summary>        /// <remarks>        /// <see cref="EventArgs{T}.Argument"/> is the exception that was thrown.        /// </remarks>        public event EventHandler<EventArgs<Exception>> ProcessException;         // Fields        private WindowsServiceClient m_serviceClient;        private ICollection<Alarm> m_raisedAlarms;        private readonly LongSynchronizedOperation m_alarmStateQueryOperation;        private AutoResetEvent m_responseComplete;        private int m_responseTimeout;        private bool m_connectedToService;        private bool m_disposed;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new <see cref="AlarmStatusQuery"/>.        /// </summary>        public AlarmStatusQuery()        {            // Attach to service connected events            CommonFunctions.ServiceConnectionRefreshed += CommonFunctions_ServiceConnectionRefreshed;             // Determine initial state of connectivity            UpdateServiceConnectivity();             m_alarmStateQueryOperation = new LongSynchronizedOperation(ExecuteAlarmStateQuery)            {                IsBackground = true            };            m_responseComplete = new AutoResetEvent(false);            m_responseTimeout = DefaultResponseTimeout;        }         /// <summary>        /// Releases the unmanaged resources before the <see cref="AlarmStatusQuery"/> object is reclaimed by <see cref="GC"/>.        /// </summary>        ~AlarmStatusQuery()        {            Dispose(false);        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets allowed timeout, in milliseconds, for a command request response.        /// </summary>        public int ResponseTimeout        {            get            {                return m_responseTimeout;            }            set            {                m_responseTimeout = value;            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Releases all the resources used by the <see cref="AlarmStatusQuery"/> object.        /// </summary>        public void Dispose()        {            Dispose(true);            GC.SuppressFinalize(this);        }         /// <summary>        /// Releases the unmanaged resources used by the <see cref="AlarmStatusQuery"/> 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_serviceClient != null && (object)m_serviceClient.Helper != null)                            m_serviceClient.Helper.ReceivedServiceResponse -= Helper_ReceivedServiceResponse;                         m_serviceClient = null;                         if ((object)m_responseComplete != null)                        {                            // Release any waiting threads before disposing wait handle                            m_responseComplete.Set();                            m_responseComplete.Dispose();                        }                         m_responseComplete = null;                         // Detach from service connected events                        CommonFunctions.ServiceConnectionRefreshed -= CommonFunctions_ServiceConnectionRefreshed;                    }                }                finally                {                    m_disposed = true;  // Prevent duplicate dispose.                }            }        }         private void UpdateServiceConnectivity(bool refreshRaisedAlarms = false)        {            // Detach from original service response if refreshing service connection            if ((object)m_serviceClient != null && (object)m_serviceClient.Helper != null)                m_serviceClient.Helper.ReceivedServiceResponse -= Helper_ReceivedServiceResponse;             // Get new service client            m_serviceClient = CommonFunctions.GetWindowsServiceClient();            ClientHelper clientHelper = ((object)m_serviceClient != null) ? m_serviceClient.Helper : null;            IClient remotingClient = ((object)clientHelper != null) ? clientHelper.RemotingClient : null;             if ((object)clientHelper != null)                clientHelper.ReceivedServiceResponse += Helper_ReceivedServiceResponse;             if ((object)remotingClient == null || remotingClient.CurrentState != ClientState.Connected)            {                // Remoting client is not connected to service                m_connectedToService = false;                 // Release any waiting threads if service has been disconnected                if ((object)m_responseComplete != null)                    m_responseComplete.Set();            }            else            {                // If remoting client is connected, refresh raised alarms if requested and attach to connection terminated event                m_connectedToService = true;                 if (refreshRaisedAlarms)                    RequestRaisedAlarmStates();                 remotingClient.ConnectionTerminated += RemotingClient_ConnectionTerminated;            }        }         private void CommonFunctions_ServiceConnectionRefreshed(object sender, EventArgs eventArgs)        {            // Determine new state of connectivity - since we are reconnecting and this could be to a            // new node an/or a restarted system, re-request raised alarm states            UpdateServiceConnectivity(true);        }         private void RemotingClient_ConnectionTerminated(object sender, EventArgs eventArgs)        {            IClient remotingClient = sender as IClient;             // Attempt to detach from the event that just occurred            if ((object)remotingClient != null)                remotingClient.ConnectionTerminated -= RemotingClient_ConnectionTerminated;             // Determine new state of connectivity            UpdateServiceConnectivity();        }         /// <summary>        /// Requests the current state of highest severity alarms.        /// </summary>        public void RequestRaisedAlarmStates()        {            // Create single item operation queue so that            // multiple requests will be handled one at a time            m_alarmStateQueryOperation.RunOnceAsync();        }         // Execute alarm state query        private void ExecuteAlarmStateQuery()        {            // Nothing to do if not connected to service            if (!m_connectedToService)                return;             try            {                // Clear any existing raised alarms                m_raisedAlarms = null;                 // Reset state of response complete event (in case a prior event set never completed)                if ((object)m_responseComplete != null)                    m_responseComplete.Reset();                 // Send command to alarm services to get highest severity alarms                CommonFunctions.SendCommandToService("INVOKE ALARM!SERVICES GetRaisedAlarms");                 // Wait for command response allowing for processing time                if ((object)m_responseComplete != null)                {                    if (!m_responseComplete.WaitOne(m_responseTimeout))                        OnProcessException(new TimeoutException(string.Format("Timed-out after {0} seconds waiting for service response{1}.", (m_responseTimeout / 1000.0D).ToString("0.00"), m_connectedToService ? "" : " - service is disconnected")));                }                 // If alarms were returned, expose them to consumer                if ((object)m_raisedAlarms != null)                    OnRaisedAlarmStates(m_raisedAlarms);            }            catch (Exception ex)            {                OnProcessException(new InvalidOperationException("Alarm state query error: " + ex.Message, ex));            }        }         /// <summary>        /// Raises <see cref="RaisedAlarmStates"/> event.        /// </summary>        /// <param name="raisedAlarms">Alarm states.</param>        protected virtual void OnRaisedAlarmStates(ICollection<Alarm> raisedAlarms)        {            if ((object)RaisedAlarmStates != null)                RaisedAlarmStates(this, new EventArgs<ICollection<Alarm>>(raisedAlarms));        }         /// <summary>        /// Raises <see cref="ProcessException"/> event.        /// </summary>        /// <param name="ex">Processing <see cref="Exception"/>.</param>        protected virtual void OnProcessException(Exception ex)        {            if ((object)ProcessException != null)                ProcessException(this, new EventArgs<Exception>(ex));        }         /// <summary>        /// Handles ReceivedServiceResponse event.        /// </summary>        /// <param name="sender">Source of the event.</param>        /// <param name="e">Event arguments.</param>        private void Helper_ReceivedServiceResponse(object sender, EventArgs<ServiceResponse> e)        {            if ((object)e != null)            {                ServiceResponse response = e.Argument;                 if ((object)response != null)                {                    string sourceCommand;                    bool responseSuccess;                     if (ClientHelper.TryParseActionableResponse(response, out sourceCommand, out responseSuccess) && responseSuccess)                    {                        if (!string.IsNullOrWhiteSpace(sourceCommand) && string.Compare(sourceCommand.Trim(), "INVOKE", true) == 0)                        {                            List<object> attachments = response.Attachments;                             // A GetHighestSeverityAlarms INVOKE will have two attachments: an alarm array, item 0, and the original command arguments, item 1                            if ((object)attachments != null && attachments.Count > 1)                            {                                Arguments arguments = attachments[1] as Arguments;                                 // Check the method that was invoked - the second argument after the adapter ID                                if ((object)arguments != null && string.Compare(arguments["OrderedArg2"], "GetRaisedAlarms", true) == 0)                                {                                    m_raisedAlarms = attachments[0] as ICollection<Alarm>;                                     // Release waiting thread once desired response has been received                                    if ((object)m_responseComplete != null)                                        m_responseComplete.Set();                                }                            }                        }                    }                }            }        }         #endregion    }}

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