797 lines · 34.7 KB · C#
RawDownload
//******************************************************************************************************//  Adapters.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://  ----------------------------------------------------------------------------------------------------//  05/05/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.ComponentModel;using System.IO;using System.Linq;using System.Reflection;using System.Text.RegularExpressions;using System.Windows;using System.Windows.Input;using GSF.IO;using GSF.Reflection;using GSF.TimeSeries.Adapters;using GSF.TimeSeries.UI.Commands;using GSF.TimeSeries.UI.DataModels; // ReSharper disable VirtualMemberCallInConstructornamespace GSF.TimeSeries.UI.ViewModels{    /// <summary>    /// Class to hold bindable <see cref="Adapter"/> collection and current selection information for UI.    /// </summary>    internal class Adapters : PagedViewModelBase<Adapter, int>    {        #region [ Members ]         // Nested Types        public class AdapterTypeDescription        {            public string Header { get; set; }             public string Description { get; set; }             public Visibility HeaderVisibility =>                 Header is null ? Visibility.Collapsed : Visibility.Visible;             public override string ToString() =>                 Header is null ? Description : $"{Header}: {Description}";        }         // Fields        private List<Tuple<Type, AdapterTypeDescription>> m_adapterTypeList;        private List<AdapterConnectionStringParameter> m_parameterList;        private readonly AdapterType m_adapterType;        private string m_searchDirectory;        private AdapterConnectionStringParameter m_selectedParameter;        private RelayCommand m_initializeCommand;        private string m_runtimeID;         private bool m_suppressParameterValueUpdates;        private bool m_suppressConnectionStringUpdates;         #endregion         #region [ Constructor ]         /// <summary>        /// Creates an instance of <see cref="Adapters"/> class.        /// </summary>        /// <param name="itemsPerPage">Integer value to determine number of items per page.</param>        /// <param name="autoSave">Boolean value to determine is user changes should be saved automatically.</param>        /// <param name="adapterType"><see cref="AdapterType"/> to determine type.</param>        public Adapters(int itemsPerPage, AdapterType adapterType, bool autoSave = true)            : base(0, autoSave) // Set items per page to zero to avoid load in the base class.        {            AppDomain.CurrentDomain.AssemblyResolve += AppDomain_AssemblyResolve;            ItemsPerPage = itemsPerPage;             // Must attach to PropertyChanged event after setting m_adapterType!            // Do not override OnPropertyChanged or you will regret it!            m_adapterType = adapterType;            PropertyChanged += Adapters_PropertyChanged;             SearchDirectory = FilePath.GetAbsolutePath("").EnsureEnd(Path.DirectorySeparatorChar);            Load();        }         #endregion         #region [ Properties ]         public string RuntimeID        {            get => m_runtimeID;            set            {                m_runtimeID = value;                OnPropertyChanged("RuntimeID");            }        }         /// <summary>        /// Gets flag that determines if <see cref="PagedViewModelBase{T1, T2}.CurrentItem"/> is a new record.        /// </summary>        public override bool IsNewRecord => CurrentItem.ID == 0;         /// <summary>        /// Gets or sets the collection containing the adapter types found        /// in the assemblies residing in the <see cref="SearchDirectory"/>.        /// </summary>        public List<Tuple<Type, AdapterTypeDescription>> AdapterTypeList        {            get => m_adapterTypeList;            set            {                m_adapterTypeList = value;                OnPropertyChanged("AdapterTypeList");            }        }         /// <summary>        /// Gets or sets the index of the selected item in the <see cref="AdapterTypeList"/>.        /// </summary>        public int AdapterTypeSelectedIndex        {            get            {                int index = m_adapterTypeList                    .Select(tuple => tuple.Item1)                    .TakeWhile(type => type.FullName != CurrentItem.TypeName)                    .Count();                 if (index == m_adapterTypeList.Count)                    index = -1;                 return index;            }            set            {                if (value >= 0 && value < AdapterTypeList.Count)                    CurrentItem.TypeName = AdapterTypeList[value].Item1.FullName;                 OnAdapterTypeSelectedIndexChanged();            }        }         /// <summary>        /// Gets additional information about the type selected from the drop-down.        /// </summary>        public string TypeInfo => string.IsNullOrEmpty(CurrentItem.TypeName) ? "Adapter Type" :             $"Adapter Type: {CurrentItem.TypeName} from {Path.GetFileName(CurrentItem.AssemblyName)}";         /// <summary>        /// Gets or sets the list of <see cref="Adapter.ConnectionString"/> parameters found by        /// searching the class defined by <see cref="Adapter.AssemblyName"/> and        /// <see cref="Adapter.TypeName"/> plus the parameters defined by the ConnectionString.        /// </summary>        public List<AdapterConnectionStringParameter> ParameterList        {            get => m_parameterList;            set            {                try                {                    // Attempting to update parameter values without suppressing connection                    // string updates would result in an infinite loop. We should not be modifying                    // the connection string directly at the same time the user is, anyway.                    m_suppressConnectionStringUpdates = true;                     // Getting this ahead of time allows us to reselect the previously                    // selected parameter if it still exists in the parameter list.                    AdapterConnectionStringParameter selectedParameter = m_selectedParameter;                     m_parameterList = value;                    OnPropertyChanged("ParameterList");                     // Reselect the previously selected parameter, and update                    // the connection string parameters if necessary.                    UpdateConnectionStringParameters(m_parameterList, CurrentItem.ConnectionString.ToNonNullString().ParseKeyValuePairs());                    SelectedParameter = selectedParameter;                }                finally                {                    // Indicate that we want to stop suppressing connection string updates.                    m_suppressConnectionStringUpdates = false;                }            }        }         /// <summary>        /// Gets or sets the directory in which the application will search for        /// assemblies containing adapters. The resulting list of adapters populates        /// the <see cref="AdapterTypeList"/>.        /// </summary>        public string SearchDirectory        {            get => m_searchDirectory;            set            {                if (m_searchDirectory?.Equals(value, StringComparison.OrdinalIgnoreCase) ?? false)                    return;                 m_searchDirectory = value;                OnPropertyChanged("SearchDirectory");                 if (string.IsNullOrEmpty(m_searchDirectory) || Directory.Exists(m_searchDirectory))                {                    // When the adapter type list is updated, the selected type will change.                    // Since that is bound to the type name which in turn updates the                    // assembly name, we need to store the current values ahead of time                    // and restore them after updating the adapter type list.                    string assemblyName = CurrentItem.AssemblyName;                    string typeName = CurrentItem.TypeName;                    string ext = string.IsNullOrEmpty(m_searchDirectory) ? string.Empty : Path.DirectorySeparatorChar.ToString();                     AdapterTypeList = GetAdapterTypeList(m_searchDirectory + ext, GetAdapterInterfaceType(m_adapterType));                    CurrentItem.TypeName = typeName;                    CurrentItem.AssemblyName = assemblyName;                }            }        }         /// <summary>        /// Gets or sets the currently selected parameter from the        /// <see cref="ParameterList"/>. This is used to reselect previously        /// selected parameters when the ParameterList is updated.        /// </summary>        public AdapterConnectionStringParameter SelectedParameter        {            get => m_selectedParameter;            set            {                if (m_selectedParameter is not null)                    m_selectedParameter.PropertyChanged -= ConnectionStringParameter_PropertyChanged;                 m_selectedParameter = value;                 if (m_selectedParameter is not null)                    m_selectedParameter.PropertyChanged += ConnectionStringParameter_PropertyChanged;                 OnPropertyChanged("SelectedParameter");            }        }         /// <summary>        /// Gets the command whose action is evoked when the user clicks the initialize button.        /// </summary>        public ICommand InitializeCommand => m_initializeCommand ?? (m_initializeCommand = new RelayCommand(InitializeAdapter, () => CanSave));         /// <summary>        /// Determines whether the custom configuration button is visible.        /// </summary>        public Visibility CustomConfigurationButtonVisibility => AdapterTypeSelectedIndex >= 0 &&             AdapterTypeList[AdapterTypeSelectedIndex].Item1.TryGetAttribute(out CustomConfigurationEditorAttribute _) ?                 Visibility.Visible :                 Visibility.Collapsed;         #endregion         #region [ Methods ]         /// <summary>        /// Gets the primary key value of the <see cref="PagedViewModelBase{T1, T2}.CurrentItem"/>.        /// </summary>        /// <returns>The primary key value of the <see cref="PagedViewModelBase{T1, T2}.CurrentItem"/>.</returns>        public override int GetCurrentItemKey() =>             CurrentItem.ID;         /// <summary>        /// Gets the string based named identifier of the <see cref="PagedViewModelBase{T1, T2}.CurrentItem"/>.        /// </summary>        /// <returns>The string based named identifier of the <see cref="PagedViewModelBase{T1, T2}.CurrentItem"/>.</returns>        public override string GetCurrentItemName() =>             CurrentItem.AdapterName;         /// <summary>        /// Creates a new instance of <see cref="Historian"/> and assigns it to CurrentItem.        /// </summary>        public override void Clear()        {            base.Clear();            CurrentItem.Type = m_adapterType;            ParameterList = null;        }         /// <summary>        /// Loads collection of <see cref="Adapter"/> defined in the database.        /// </summary>        public override void Load()        {            Mouse.OverrideCursor = Cursors.Wait;             try            {                if (ItemsKeys is null)                {                    ItemsKeys = Adapter.LoadIDs(null, m_adapterType, SortMember, SortDirection);                     if (SortSelector is not null)                    {                        ItemsKeys = SortDirection == "ASC" ?                             ItemsKeys.OrderBy(SortSelector).ToList() :                             ItemsKeys.OrderByDescending(SortSelector).ToList();                    }                }                 List<int> pageKeys = ItemsKeys.Skip((CurrentPageNumber - 1) * ItemsPerPage).Take(ItemsPerPage).ToList();                ItemsSource = Adapter.Load(null, m_adapterType, pageKeys);                CurrentItem.Type = m_adapterType;            }            catch (Exception ex)            {                if (ex.InnerException is null)                {                    Popup(ex.Message, $"Load {DataModelName} Exception:", MessageBoxImage.Error);                    CommonFunctions.LogException(null, $"Load {DataModelName}", ex);                }                else                {                    Popup($"{ex.Message}{Environment.NewLine}Inner Exception: {ex.InnerException.Message}", $"Load {DataModelName} Exception:", MessageBoxImage.Error);                    CommonFunctions.LogException(null, $"Load {DataModelName}", ex.InnerException);                }            }            finally            {                Mouse.OverrideCursor = null;            }        }         /// <summary>        /// Deletes associated <see cref="Adapter"/> record.        /// </summary>        public override void Delete()        {            if (!CanDelete || !Confirm($"Are you sure you want to delete '{GetCurrentItemName()}'?", $"Delete {DataModelName}"))                return;             try            {                if (OnBeforeDeleteCanceled())                    throw new OperationCanceledException("Delete was canceled.");                 int currentItemKey = GetCurrentItemKey();                string result = Adapter.Delete(null, m_adapterType, GetCurrentItemKey());                ItemsKeys.Remove(currentItemKey);                 OnDeleted();                 Load();                DisplayStatusMessage(result);            }            catch (Exception ex)            {                if (ex.InnerException is null)                {                    Popup(ex.Message, $"Delete {DataModelName} Exception:", MessageBoxImage.Error);                    CommonFunctions.LogException(null, $"Delete {DataModelName}", ex);                }                else                {                    Popup($"{ex.Message}{Environment.NewLine}Inner Exception: {ex.InnerException.Message}", $"Delete {DataModelName} Exception:", MessageBoxImage.Error);                    CommonFunctions.LogException(null, $"Delete {DataModelName}", ex.InnerException);                }            }        }         /// <summary>        /// Unloads the <see cref="Adapters"/> class.        /// </summary>        public void Unload() =>             AppDomain.CurrentDomain.AssemblyResolve -= AppDomain_AssemblyResolve;         /// <summary>        /// Handles PropertyChanged event on CurrentItem.        /// </summary>        /// <param name="sender">Source of the event.</param>        /// <param name="e">Event arguments.</param>        protected override void m_currentItem_PropertyChanged(object sender, PropertyChangedEventArgs e)        {            base.m_currentItem_PropertyChanged(sender, e);             switch (e.PropertyName)            {                // Occurs when CurrentItem.TypeName changes.                case "TypeName":                {                    // If there exists a type in the adapter type list that                    // matches the type name, also update the assembly name.                    Type selectedType = m_adapterTypeList                                        .Select(tuple => tuple.Item1)                                        .SingleOrDefault(type => type.FullName == CurrentItem.TypeName);                     if (selectedType is not null)                    {                        string pattern = Regex.Escape(FilePath.GetAbsolutePath("").EnsureEnd(Path.DirectorySeparatorChar));                        CurrentItem.AssemblyName = Regex.Replace(selectedType.Assembly.Location, $"^{pattern}", "", RegexOptions.IgnoreCase);                    }                     // Also update the parameter list. Since the type has changed,                    // the current list of parameters generated by searching the                    // selected type is no longer valid.                    ParameterList = GetParameterList(CurrentItem.AssemblyName, CurrentItem.TypeName);                     OnAdapterTypeSelectedIndexChanged();                    OnPropertyChanged("TypeInfo");                    break;                }                // Occurs when CurrentItem.AssemblyName changes.                case "AssemblyName":                // Occurs when CurrentItem.ConnectionString changes.                // When the connection string changes, we need to keep all related                // elements synchronized. Since we can't easily determine exactly                // what has changed, simply update everything.                case "ConnectionString":                    // When the assembly name changes, we may be able to                    // find connection string parameters in the class defined                    // by the assembly name and type name.                    ParameterList = GetParameterList(CurrentItem.AssemblyName, CurrentItem.TypeName);                    break;            }        }         /// <summary>        /// Handles PropertyChanged event on <see cref="SelectedParameter"/>.        /// </summary>        /// <param name="sender">Source of the event.</param>        /// <param name="e">Event arguments.</param>        protected virtual void ConnectionStringParameter_PropertyChanged(object sender, PropertyChangedEventArgs e)        {            if (m_suppressConnectionStringUpdates || sender is not AdapterConnectionStringParameter parameter || parameter.Value is null || e.PropertyName != "Value")                return;             try            {                m_suppressParameterValueUpdates = true;                 // The easiest way to update is to break the connection string into key                // value pairs, update the value of the pair corresponding to the parameter                // that fired the event, and then rejoin the key value pairs.                Dictionary<string, string> settings = CurrentItem.ConnectionString.ToNonNullString().ParseKeyValuePairs();                settings[parameter.Name] = parameter.Value.ToString();                 // Build the new connection string and validate that it can still be parsed                string connectionString = settings.JoinKeyValuePairs();                connectionString.ParseKeyValuePairs();                 CurrentItem.ConnectionString = connectionString;            }            finally            {                m_suppressParameterValueUpdates = false;            }        }         private void Adapters_PropertyChanged(object sender, PropertyChangedEventArgs e)        {            if (e.PropertyName != "CurrentItem")                return;             if (CurrentItem is null)            {                RuntimeID = string.Empty;            }            else            {                switch (m_adapterType)                {                    case AdapterType.Action:                        RuntimeID = CommonFunctions.GetRuntimeID("CustomActionAdapter", CurrentItem.ID);                        break;                    case AdapterType.Filter:                        RuntimeID = CommonFunctions.GetRuntimeID("CustomFilterAdapter", CurrentItem.ID);                        break;                    case AdapterType.Input:                        RuntimeID = CommonFunctions.GetRuntimeID("CustomInputAdapter", CurrentItem.ID);                        break;                    case AdapterType.Output:                        RuntimeID = CommonFunctions.GetRuntimeID("CustomOutputAdapter", CurrentItem.ID);                        break;                    default:                        RuntimeID = string.Empty;                        break;                }                 // Modify search path to the path of the assembly of the selected item                SearchDirectory = FilePath.GetDirectoryName(FilePath.GetAbsolutePath(CurrentItem.AssemblyName ?? ""));

Lines 1–500 of 797.

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