//******************************************************************************************************
// CommonFunctions.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:
// ----------------------------------------------------------------------------------------------------
// 03/31/2011 - Mehulbhai P Thakkar
// Generated original version of source code.
// 12/20/2012 - Starlynn Danyelle Gilliam
// Modified Header.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Principal;
using System.Threading;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Threading;
using GSF.Communication;
using GSF.Data;
using GSF.Identity;
using GSF.IO;
using GSF.Security;
using GSF.TimeSeries.UI.DataModels;
using Timer = System.Timers.Timer;
namespace GSF.TimeSeries.UI
{
///
/// Represents a static class containing common methods.
///
public static class CommonFunctions
{
#region [ Members ]
///
/// Defines the default settings category for TimeSeriesFramework data connections.
///
public const string DefaultSettingsCategory = "systemSettings";
///
/// Defines the maximum number of pages to be stored in page history.
///
public const int MaxPageHistory = 10;
#endregion
#region [ Static ]
// Static Fields
private static SecurityPrincipal s_currentPrincipal;
private static Guid s_currentNodeID;
private static string s_serviceConnectionString;
private static string s_dataPublisherConnectionString;
private static string s_realTimeStatisticServiceUrl;
private static string s_timeSeriesDataServiceUrl;
private static WindowsServiceClient s_windowsServiceClient;
private static bool s_retryServiceConnection;
private static LinkedList> s_pageHistory;
private static LinkedListNode> s_currentPage;
private static Timer s_principalRefreshTimer;
// Static Constructor
static CommonFunctions()
{
s_principalRefreshTimer = new Timer();
s_principalRefreshTimer.AutoReset = false;
s_principalRefreshTimer.Interval = 1000;
s_principalRefreshTimer.Elapsed += PrincipalRefreshTimer_Elapsed;
s_principalRefreshTimer.Start();
}
// Static Properties
///
/// Defines the current principal for the thread owning the common functions.
///
public static SecurityPrincipal CurrentPrincipal
{
get
{
if ((object)s_currentPrincipal == null)
s_currentPrincipal = Thread.CurrentPrincipal as SecurityPrincipal;
return s_currentPrincipal;
}
set
{
if ((object)value == null || (object)value.Identity == null || string.IsNullOrEmpty(value.Identity.Name))
return;
ISecurityProvider cachedProvider;
ISecurityProvider provider;
if (SecurityProviderCache.TryGetCachedProvider(value.Identity.Name, out cachedProvider))
{
provider = SecurityProviderUtility.CreateProvider(value.Identity.Name);
provider.UserData.Clone(cachedProvider.UserData);
provider.Password = cachedProvider.Password;
s_currentPrincipal = new SecurityPrincipal(new SecurityIdentity(provider));
}
}
}
///
/// Defines the current user name as defined in the Thread.CurrentPrincipal.Identity.
///
public static string CurrentUser = Thread.CurrentPrincipal.Identity.Name;
///
/// Gets the flag that indicates whether we can go forward to the next user control in history.
///
public static bool CanGoForward
{
get
{
SecurityPrincipal currentPrincipal = CurrentPrincipal;
ISecurityProvider securityProvider;
if (!SecurityProviderCache.TryGetCachedProvider(currentPrincipal.Identity.Name, out securityProvider))
securityProvider = SecurityProviderCache.CurrentProvider;
return ((object)s_currentPage != null) && ((object)s_currentPage.Next != null) && currentPrincipal.Identity.IsAuthenticated && securityProvider.UserData.Roles.Any();
}
}
///
/// Gets the flag that indicates whether we can go back to the previous user control in history.
///
public static bool CanGoBack
{
get
{
SecurityPrincipal currentPrincipal = CurrentPrincipal;
ISecurityProvider securityProvider;
if (!SecurityProviderCache.TryGetCachedProvider(currentPrincipal.Identity.Name, out securityProvider))
securityProvider = SecurityProviderCache.CurrentProvider;
return ((object)s_currentPage != null) && ((object)s_currentPage.Previous != null) && currentPrincipal.Identity.IsAuthenticated && securityProvider.UserData.Roles.Any();
}
}
// Events
///
/// Used to notify main window that connection to service has changed.
///
public static event EventHandler ServiceConnectionRefreshed = delegate
{
};
///
/// Event raised when the property changes as
/// a result of its security provider expiring from the security provider cache.
///
public static event EventHandler CurrentPrincipalRefreshed;
///
/// Triggered when the flag that indicates whether we can move forward in page history changes.
///
public static event EventHandler CanGoForwardChanged;
///
/// Triggered when the flag that indicates whether we can move backward in page history changes.
///
public static event EventHandler CanGoBackChanged;
// Static Methods
#region [ AdoDataConnection Extension Methods ]
///
/// Sets the current user context for the database.
///
///
/// Purpose of this method is to supply current user information from the UI to DELETE trigger for change logging.
/// This method must be called before any delete operation on the database in order to log who deleted this record.
/// For SQL server it sets user name into CONTEXT_INFO().
/// For MySQL server it sets user name into session variable @context.
/// For Oracle server it sets user name into context package.
/// MS Access is not supported for change logging.
/// For any other database in the future, such as Oracle, this logic must be extended to support change log in the database.
///
/// used to set user context before any delete operation.
public static void SetCurrentUserContext(AdoDataConnection database)
{
bool createdConnection = false;
try
{
if (!string.IsNullOrEmpty(CurrentUser))
{
if ((object)database == null)
{
database = new AdoDataConnection(DefaultSettingsCategory);
createdConnection = true;
}
IDbCommand command;
string connectionType = database.Connection.GetType().Name.ToLower();
// Set Current User for the database session for this connection.
switch (connectionType)
{
case "sqlconnection":
const string contextSql = "DECLARE @context VARBINARY(128)\n SELECT @context = CONVERT(VARBINARY(128), CONVERT(VARCHAR(128), @userName))\n SET CONTEXT_INFO @context";
command = database.Connection.CreateCommand();
command.CommandText = contextSql;
command.AddParameterWithValue("@userName", CurrentUser);
command.ExecuteNonQuery();
break;
case "mysqlconnection":
command = database.Connection.CreateCommand();
command.CommandText = "SET @context = '" + CurrentUser + "';";
command.ExecuteNonQuery();
break;
case "oracleconnection":
command = database.Connection.CreateCommand();
command.CommandText = "BEGIN context.set_current_user('" + CurrentUser + "'); END;";
command.ExecuteNonQuery();
break;
}
}
}
finally
{
if (createdConnection)
database.Dispose();
}
}
/////
///// Retrieves connection string to connect to backend windows service.
/////
///// to database.
///// IP address and port on which backend windows service is running.
//public static string RemoteStatusServerConnectionString(this AdoDataConnection database)
//{
// if (string.IsNullOrEmpty(s_remoteStatusServerConnectionString))
// database.GetNodeSettings();
// return s_remoteStatusServerConnectionString;
//}
///
/// Retrieves web service url to query real time statistics values.
///
/// to database.
/// string, url to web service.
public static string RealTimeStatisticServiceUrl(this AdoDataConnection database)
{
if (string.IsNullOrEmpty(s_realTimeStatisticServiceUrl))
database.GetNodeSettings();
return s_realTimeStatisticServiceUrl;
}
/////
///// Retrieves a port number on which back end service is publishing data.
/////
///// to database.
///// port number on which data is being published.
//public static string DataPublisherPort(this AdoDataConnection database)
//{
// if (string.IsNullOrEmpty(s_dataPublisherPort))
// database.GetNodeSettings();
// return s_dataPublisherPort;
//}
///
/// Retrieves web service url to query real time data.
///
/// to database.
/// string, url to web service.
public static string TimeSeriesDataServiceUrl(this AdoDataConnection database)
{
if (string.IsNullOrEmpty(s_timeSeriesDataServiceUrl))
database.GetNodeSettings();
return s_timeSeriesDataServiceUrl;
}
///
/// Retrieves connection string information to connect to backed windows service from UI.
///
/// to database.
/// Flag that determines if cached connection string should be overwritten.
/// Connection string to connect to backend windows service.
public static string ServiceConnectionString(this AdoDataConnection database, bool overwrite = false)
{
if (string.IsNullOrEmpty(s_serviceConnectionString) || overwrite)
database.GetNodeSettings();
return s_serviceConnectionString;
}
///
/// Retrieves connection string to subscribe data from data published hosted by the backend windows service.
///
/// to database.
/// Connection string to subscribe from data publisher.
public static string DataPublisherConnectionString(this AdoDataConnection database)
{
if (string.IsNullOrEmpty(s_dataPublisherConnectionString))
database.GetNodeSettings();
return s_dataPublisherConnectionString;
}
///
/// Method to parse Settings field value for current node defined in the database and extract various parameters to communicate with backend windows service.
///
/// to database.
private static void GetNodeSettings(this AdoDataConnection database)
{
Node node = Node.GetCurrentNode(database);
if (node != null)
{
string interfaceValue = string.Empty;
Dictionary settings = node.Settings.ToLower().ParseKeyValuePairs();
if (settings.ContainsKey("realtimestatisticserviceurl"))
s_realTimeStatisticServiceUrl = settings["realtimestatisticserviceurl"];
if (settings.ContainsKey("timeseriesdataserviceurl"))
s_timeSeriesDataServiceUrl = settings["timeseriesdataserviceurl"];
if (settings.ContainsKey("interface"))
interfaceValue = settings["interface"];
if (settings.ContainsKey("remotestatusserverconnectionstring"))
{
Dictionary serviceSettings;
s_serviceConnectionString = settings["remotestatusserverconnectionstring"];
serviceSettings = s_serviceConnectionString.ParseKeyValuePairs();
if (serviceSettings.ContainsKey("interface"))
interfaceValue = serviceSettings["interface"];
if (serviceSettings.ContainsKey("server"))
{
string server = serviceSettings["server"];
if (serviceSettings.ContainsKey("datapublisherport"))
{
s_dataPublisherConnectionString = "server=" + server.Substring(0, server.LastIndexOf(":", StringComparison.OrdinalIgnoreCase) + 1) + serviceSettings["datapublisherport"];
if (!string.IsNullOrEmpty(interfaceValue))
s_dataPublisherConnectionString += ";interface=" + interfaceValue;
}
else if (settings.ContainsKey("datapublisherport"))
{
s_dataPublisherConnectionString = "server=" + server.Substring(0, server.LastIndexOf(":", StringComparison.OrdinalIgnoreCase) + 1) + settings["datapublisherport"];
if (!string.IsNullOrEmpty(interfaceValue))
s_dataPublisherConnectionString += ";interface=" + interfaceValue;
}
}
}
}
}
///
/// Returns current node id UI is connected to.
///
/// Current Node ID.
public static Guid CurrentNodeID()
{
return s_currentNodeID;
}
///
/// Returns current node id UI is connected to.
///
/// Connected
/// Proper implementation for current node id.
public static object CurrentNodeID(this AdoDataConnection database)
{
return database.Guid(s_currentNodeID);
}
#endregion
///
/// Assigns based ID of currently active node.
///
/// Current node ID to assign.
public static void SetAsCurrentNodeID(this Guid nodeID)
{
if (s_currentNodeID != nodeID)
{
s_currentNodeID = nodeID;
// When node selection changes, reset other static members related to node.
s_serviceConnectionString = string.Empty;
s_dataPublisherConnectionString = string.Empty;
s_realTimeStatisticServiceUrl = string.Empty;
s_timeSeriesDataServiceUrl = string.Empty;
SetRetryServiceConnection(true);
DisconnectWindowsServiceClient();
ConnectWindowsServiceClient();
}
}
///
/// Returns if given is null.
///
/// Value to test for null.
/// if is null; otherwise .
public static object ToNotNull(this object value)
{
if ((object)value == null)
return (object)DBNull.Value;
if (value is int && (int)value == 0)
return (object)DBNull.Value;
return value;
}
///
/// Returns a collection of down sampling methods.
///
/// type collection of down sampling methods.
public static Dictionary GetDownsamplingMethodLookupList()
{
Dictionary downsamplingLookupList = new Dictionary();
downsamplingLookupList.Add("LastReceived", "LastReceived");
downsamplingLookupList.Add("Closest", "Closest");
downsamplingLookupList.Add("Filtered", "Filtered");
downsamplingLookupList.Add("BestQuality", "BestQuality");
return downsamplingLookupList;
}
///
/// Returns a collection of system time zones.
///
/// Indicates if selection on UI is optional for this collection.
/// type collection of system time zones.
public static Dictionary GetTimeZones(bool isOptional)
{
Dictionary timeZonesList = new Dictionary();
if (isOptional)
timeZonesList.Add("", "Select Time Zone");
foreach (TimeZoneInfo timeZoneInfo in TimeZoneInfo.GetSystemTimeZones())
{
if (!timeZonesList.ContainsKey(timeZoneInfo.Id))
timeZonesList.Add(timeZoneInfo.Id, timeZoneInfo.DisplayName);
}
return timeZonesList;
}
///
/// Retrieves children of an UIElement based on type.
///
/// Parent UIElement.
/// Type of child UIElement looking for within parent UIElement.
/// Reference parameter to return child collection.
public static void GetChildren(UIElement parent, Type targetType, ref List children)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
if (count > 0)
{
for (int i = 0; i < count; i++)
{
UIElement child = (UIElement)VisualTreeHelper.GetChild(parent, i);
if (child.GetType() == targetType)
{
children.Add(child);
}
GetChildren(child, targetType, ref children);
}
}
}
///
/// Retrieves first child of an UIElement based on type.
///
/// Parent UIElement
/// Type of the child UIElement.
/// Reference parameter to return UIElement.
public static void GetFirstChild(UIElement parent, Type targetType, ref UIElement element)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
if (count > 0)
{
for (int i = 0; i < count; i++)
{
UIElement child = (UIElement)VisualTreeHelper.GetChild(parent, i);
if (child.GetType() == targetType)
{
element = child;
break;
}
GetFirstChild(child, targetType, ref element);
}
}
}
///
/// Retrieves runtime id for an object.
///
/// Table where object has been defined.
/// ID of an object in source table.
/// Existing database connection, if available.
/// string, id of an object in Runtime table.
public static string GetRuntimeID(string sourceTable, int sourceID, AdoDataConnection database = null)
{
string runtimeID = string.Empty;
bool createdConnection = false;
try
{
if ((object)database == null)
{
database = new AdoDataConnection(DefaultSettingsCategory);
createdConnection = true;
}
string query = database.ParameterizedQueryString("SELECT ID FROM Runtime WHERE SourceTable = {0} AND SourceID = {1}", "sourceTable", "sourceID");
object id = database.Connection.ExecuteScalar(query, sourceTable, sourceID);
if (id != null)
runtimeID = id.ToString();
return runtimeID;
}
finally
{
if (createdConnection)
database.Dispose();
}
}
///
/// Sets a boolean flag indicating if connection cycle should be continued.
///
///
public static void SetRetryServiceConnection(bool retry)
{
s_retryServiceConnection = retry;
if (!retry)
DisconnectWindowsServiceClient();
}
///
/// Retrieves object.
///
/// object.
public static WindowsServiceClient GetWindowsServiceClient()
{
ConnectWindowsServiceClient();
return s_windowsServiceClient;
}
///
/// Connects to backend windows service.
///
public static void ConnectWindowsServiceClient(bool overwrite = false)
{
TlsClient remotingClient;
ISecurityProvider provider;
UserData userData;
if (overwrite)
{
DisconnectWindowsServiceClient();
ServiceConnectionRefreshed(null, EventArgs.Empty);
}
else if (s_windowsServiceClient == null || s_windowsServiceClient.Helper.RemotingClient.CurrentState != ClientState.Connected)
{
if (s_windowsServiceClient != null)
DisconnectWindowsServiceClient();
AdoDataConnection database = new AdoDataConnection(DefaultSettingsCategory);
try
{
string connectionString = database.ServiceConnectionString(true);
if (!string.IsNullOrWhiteSpace(connectionString))
{
s_windowsServiceClient = new WindowsServiceClient(connectionString);
if (SecurityProviderCache.TryGetCachedProvider(CurrentPrincipal.Identity.Name, out provider))
{
userData = provider.UserData;
if ((object)userData != null)
{
s_windowsServiceClient.Helper.Username = userData.LoginID;
s_windowsServiceClient.Helper.Password = SecurityProviderUtility.EncryptPassword(provider.Password);
remotingClient = s_windowsServiceClient.Helper.RemotingClient as TlsClient;
if ((object)remotingClient != null && (object)provider.SecurePassword != null && provider.SecurePassword.Length > 0)
remotingClient.NetworkCredential = new NetworkCredential(userData.LoginID, provider.SecurePassword);
}
}
s_windowsServiceClient.Helper.RemotingClient.MaxConnectionAttempts = -1;
s_windowsServiceClient.Helper.RemotingClient.ConnectionEstablished += RemotingClient_ConnectionEstablished;
s_windowsServiceClient.Helper.RemotingClient.ConnectionException += RemotingClient_ConnectionException;
ConnectAsync();
}
}
finally
{
database.Dispose();
}
}
}
private static void RemotingClient_ConnectionEstablished(object sender, EventArgs e)
{
ServiceConnectionRefreshed(null, EventArgs.Empty);
}
private static void RemotingClient_ConnectionException(object sender, EventArgs e)
{
bool logException = true;
SocketException ex = e.Argument as SocketException;
if ((object)ex == null && (object)e.Argument.InnerException != null)
ex = e.Argument.InnerException as SocketException;
if ((object)ex != null)
logException = (ex.SocketErrorCode != SocketError.ConnectionRefused);
if (logException)
LogException(null, "Remoting Client Connect", e.Argument);
}
///
/// Displays a status message in the unobtrusive status message popup.
///
/// The message to be displayed.
public static void DisplayStatusMessage(string message)
{
Dispatcher dispatcher = Application.Current.Dispatcher;
TextBlock statusTextBlock;
Popup statusPopup;
if (dispatcher.Thread != Thread.CurrentThread)
{
dispatcher.BeginInvoke(new Action(DisplayStatusMessage), message);
return;
}
statusTextBlock = Application.Current.MainWindow.FindName("TextBlockResult") as TextBlock;
statusPopup = Application.Current.MainWindow.FindName("PopupStatus") as Popup;
if ((object)statusTextBlock != null && (object)statusPopup != null)
{
statusTextBlock.Text = message;
statusPopup.IsOpen = true;
statusPopup.InvalidateVisual();
}
}
///
/// Hides the unobtrusive status message popup.
///
public static void HideStatusMessage()
{
Dispatcher dispatcher = Application.Current.Dispatcher;
Popup statusPopup;
if (dispatcher.Thread != Thread.CurrentThread)
{
dispatcher.BeginInvoke(new Action(HideStatusMessage));
return;
}
statusPopup = Application.Current.MainWindow.FindName("PopupStatus") as Popup;
if ((object)statusPopup != null)
statusPopup.IsOpen = false;
}
///
/// Gets a message box to display message to users.
///
public static Action Popup
{
get
{
return (Action)((message, caption, messageBoxImage) =>
MessageBox.Show(Application.Current.MainWindow, message, caption, MessageBoxButton.OK, messageBoxImage));
}
}
///
/// Connects asynchronously to backend windows service.
///
private static void ConnectAsync()
{
ThreadPool.QueueUserWorkItem(state =>
{
try
{
if ((object)s_windowsServiceClient != null && s_retryServiceConnection)
{
s_windowsServiceClient.Helper.Connect();
// If connection attempt failed with provided credentials, try once more with direct authentication
// but only when transport is secured
if (!s_windowsServiceClient.Authenticated && s_windowsServiceClient.Helper.RemotingClient is TlsClient)
{
ISecurityProvider provider;
if ((object)CurrentPrincipal != null && (object)CurrentPrincipal.Identity != null &&
SecurityProviderCache.TryGetCachedProvider(CurrentPrincipal.Identity.Name, out provider) &&
!string.IsNullOrEmpty(provider.Password))
{
s_windowsServiceClient.Helper.Disconnect();
s_windowsServiceClient.Helper.Password = provider.Password;
s_windowsServiceClient.Helper.Connect();
}
}
}
}
catch (Exception ex)
{
LogException(null, "RemoteConsoleConnection", ex);
}
});
}
///
/// Disconnects from backend windows service.
///
public static void DisconnectWindowsServiceClient()
{
try
{
if (s_windowsServiceClient != null)
{
s_windowsServiceClient.Dispose();
s_windowsServiceClient = null;
}
}
catch (Exception ex)
{
LogException(null, "RemoteConsoleConnection", ex);
}
}
///
/// Sends command to backend windows service via object.
///
/// command to be sent.
/// string, indicating success.
public static string SendCommandToService(string command)
{
if (s_windowsServiceClient != null)
{
// Make sure requests are serialized
lock (s_windowsServiceClient)
{
if (s_windowsServiceClient.Helper.RemotingClient.CurrentState == ClientState.Connected)
s_windowsServiceClient.Helper.SendRequest(command);
else
throw new ApplicationException("Application is currently disconnected from service.");
}
return "Successfully sent " + command + " command.";
}
throw new ApplicationException("Application is currently disconnected from service.");
}
///
/// Retrieves a list of .
///
/// Collection of as a .
public static List GetStopBits()
{
List stopBitsList = new List();
foreach (string stopBit in Enum.GetNames(typeof(StopBits)))
stopBitsList.Add(stopBit);
return stopBitsList;
}
///
/// Retrieves a list of .
///
/// Collection of as a .
public static List GetParities()
{
List parityList = new List();
foreach (string parity in Enum.GetNames(typeof(Parity)))
parityList.Add(parity);
return parityList;
}
///
/// Converts xml element to datatype
///
///
///
///
public static object ConvertValueToType(string xmlValue, string xmlDataType)
{
Type dataType = Type.GetType(xmlDataType);
float value;
if (float.TryParse(xmlValue, out value))
{
switch (xmlDataType)
{
case "System.DateTime":
return new DateTime((long)value);
default:
return Convert.ChangeType(value, dataType);
}
}
return "".ConvertToType