732 lines · 31.2 KB · C#
RawDownload
//******************************************************************************************************//  AdoInputAdapter.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://  ----------------------------------------------------------------------------------------------------//  11/18/2010 - Stephen C. Wills//       Generated original version of source code.//  12/13/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Globalization;using System.IO;using System.Linq;using System.Reflection;using System.Threading;using GSF;using GSF.Diagnostics;using GSF.IO;using GSF.Parsing;using GSF.TimeSeries;using GSF.TimeSeries.Adapters;using GSF.TimeSeries.Transport;using SerializationFormat = GSF.SerializationFormat; namespace AdoAdapters{    /// <summary>    /// Represents an input adapter that reads measurements from a database table.    /// </summary>    [Description("ADO: Reads measurements from an ADO data source")]    public class AdoInputAdapter : InputAdapterBase    {        #region [ Members ]         // Fields        private readonly Dictionary<string, string> m_fieldNames;        private string m_dbTableName;        private string m_dbConnectionString;        private string m_dataProviderString;        private string m_timestampFormat;        private int m_framesPerSecond;        private bool m_simulateTimestamps;        private IList<IMeasurement> m_dbMeasurements;        private string m_cacheFileName;        private int m_nextIndex;         #endregion         #region [ Constructors ]         /// <summary>        /// Initializes a new instance of the <see cref="AdoInputAdapter"/> class.        /// </summary>        public AdoInputAdapter()        {            m_fieldNames = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);            m_dbMeasurements = new List<IMeasurement>();        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets the table name in the data source from which measurements are read.        /// </summary>        [ConnectionStringParameter,        Description("Define the table name in the data source from which measurements are read."),        DefaultValue("PICOMP")]        public string TableName        {            get            {                return m_dbTableName;            }            set            {                m_dbTableName = value;            }        }         /// <summary>        /// Gets or sets the connection string used to connect to the data source.        /// </summary>        [ConnectionStringParameter,        Description("Define the connection string used to connect to the data source."),        DefaultValue("")]        public string DbConnectionString        {            get            {                return m_dbConnectionString;            }            set            {                m_dbConnectionString = value;            }        }         /// <summary>        /// Gets or sets the data provider string used to connect to the data source.        /// </summary>        [ConnectionStringParameter,        Description("Define the data provider string used to connect to the data source."),        DefaultValue("AssemblyName={System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.Odbc.OdbcConnection; AdapterType=System.Data.Odbc.OdbcDataAdapter")]        public string DataProviderString        {            get            {                return m_dataProviderString;            }            set            {                m_dataProviderString = value;            }        }         /// <summary>        /// Gets or sets the format used to read measurements from the data source.        /// </summary>        [ConnectionStringParameter,        Description("Define the date and time format used to read timestamps from the data source."),        DefaultValue("dd-MMM-yyyy HH:mm:ss.fff")]        public string TimestampFormat        {            get            {                return m_timestampFormat;            }            set            {                m_timestampFormat = value;            }        }         /// <summary>        /// Gets or sets the framerate simulated by this adapter.        /// </summary>        [ConnectionStringParameter,        Description("Define the rate at which frames will be sent to the concentrator."),        DefaultValue(30)]        public int FramesPerSecond        {            get            {                return m_framesPerSecond;            }            set            {                m_framesPerSecond = value;            }        }         /// <summary>        /// Gets or sets a value that determines whether the timestamps should        /// be simulated for the purposes of real-time concentration.        /// </summary>        [ConnectionStringParameter,        Description("Indicate whether timestamps should be simulated for real-time concentration."),        DefaultValue(true)]        public bool SimulateTimestamps        {            get            {                return m_simulateTimestamps;            }            set            {                m_simulateTimestamps = value;            }        }         /// <summary>        /// Gets or sets a cache file name so that when defined, future data loads will be from cache instead of the database as an optimization.        /// </summary>        [ConnectionStringParameter,        Description("Defines a cache file name so that when defined, future data loads will be from cache instead of the database as an optimization."),        DefaultValue(null),        CustomConfigurationEditor("GSF.TimeSeries.UI.WPF.dll", "GSF.TimeSeries.UI.Editors.FileDialogEditor", "type=save; defaultExt=.ado; filter=ADO Cache Files|*.ado|All Files|*.*")]        public string CacheFileName        {            get            {                return m_cacheFileName;            }            set            {                m_cacheFileName = value;            }        }         /// <summary>        /// Gets a flag that determines if this <see cref="AdoInputAdapter"/>        /// uses an asynchronous connection.        /// </summary>        protected override bool UseAsyncConnect        {            get            {                return false;            }        }         /// <summary>        /// Gets the flag indicating if this adapter supports temporal processing.        /// </summary>        public override bool SupportsTemporalProcessing        {            get            {                return true;            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Initializes this <see cref="AdoInputAdapter"/>.        /// </summary>        public override void Initialize()        {            base.Initialize();             Dictionary<string, string> settings = Settings;            string setting;             // Get the database field names.            GetFieldNames(settings);             // Get table name or default to PICOMP.            if (!settings.TryGetValue("tableName", out m_dbTableName))                m_dbTableName = "PICOMP";             // Get database connection string or default to empty.            if (!settings.TryGetValue("dbConnectionString", out m_dbConnectionString))                m_dbConnectionString = string.Empty;             // Get data provider string or default to a generic ODBC connection.            if (!settings.TryGetValue("dataProviderString", out m_dataProviderString))                m_dataProviderString = "AssemblyName={System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.Odbc.OdbcConnection; AdapterType=System.Data.Odbc.OdbcDataAdapter";             // Get timestamp format or default to "dd-MMM-yyyy HH:mm:ss.fff".            if (!settings.TryGetValue("timestampFormat", out m_timestampFormat))                m_timestampFormat = "dd-MMM-yyyy HH:mm:ss.fff";            else            {                // Null timestamp format means the timestamp is stored as ticks.                if (m_timestampFormat.Equals("null", StringComparison.CurrentCultureIgnoreCase))                    m_timestampFormat = null;            }             // Get framerate or default to 30.            if (settings.TryGetValue("framesPerSecond", out setting))                m_framesPerSecond = int.Parse(setting);            else                m_framesPerSecond = 30;             // Override frames per second based on temporal processing interval if it's not set to default            if (ProcessingInterval > -1)            {                if (ProcessingInterval == 0)                {                    m_framesPerSecond = (int)Ticks.PerSecond;                }                else                {                    // Minimum processing rate for this class is one frame per second                    if (ProcessingInterval >= 1000)                        m_framesPerSecond = 1;                    else                        m_framesPerSecond = (int)Ticks.PerMillisecond / ProcessingInterval;                }            }             // Determine whether to perform timestamp simulation; default is true.            if (settings.TryGetValue("simulateTimestamps", out setting))                m_simulateTimestamps = bool.Parse(setting);            else                m_simulateTimestamps = true;             // Load cache file name, if defined            settings.TryGetValue("cacheFileName", out m_cacheFileName);             if (m_cacheFileName != null)                m_cacheFileName = FilePath.GetAbsolutePath(m_cacheFileName);             try            {                // Get measurements from the database.                ThreadPool.QueueUserWorkItem(GetDbMeasurements);            }            catch (Exception ex)            {                // Process exception for logging                OnProcessException(MessageLevel.Error, new InvalidOperationException("Failed to queue database query due to exception: " + ex.Message, ex));            }        }         /// <summary>        /// Attempts to connect to this <see cref="AdoInputAdapter"/>.        /// </summary>        protected override void AttemptConnection()        {            //m_timer.Start();        }         /// <summary>        /// Attempts to disconnect from this <see cref="AdoInputAdapter"/>.        /// </summary>        protected override void AttemptDisconnection()        {            //m_timer.Stop();        }         /// <summary>        /// Gets a short one-line status of this <see cref="AdoInputAdapter"/>.        /// </summary>        /// <param name="maxLength">Maximum length of the status message.</param>        /// <returns>Text of the status message.</returns>        public override string GetShortStatus(int maxLength)        {            return $"{ProcessedMeasurements} measurements read from database.".CenterText(maxLength);        }         // Gets the database field names specified by the user in the connection string.        private void GetFieldNames(Dictionary<string, string> settings)        {            IEnumerable<string> measurementProperties = GetAllProperties(typeof(IMeasurement)).Select(property => property.Name);            StringComparison ignoreCase = StringComparison.CurrentCultureIgnoreCase;             // Parse database field names from the connection string.            foreach (string key in settings.Keys)            {                if (key.EndsWith("FieldName", ignoreCase))                {                    int fieldNameIndex = key.LastIndexOf("FieldName", ignoreCase);                    string subKey = key.Substring(0, fieldNameIndex);                    string propertyName = measurementProperties.FirstOrDefault(name => name.Equals(subKey, ignoreCase));                    string fieldName = settings[key];                     if (propertyName != null)                        m_fieldNames[fieldName] = propertyName;                    else                        OnProcessException(MessageLevel.Warning, new ArgumentException($"Measurement property not found: {subKey}"));                }            }             // If the user hasn't entered any field names, enter the default field names.            if (m_fieldNames.Count == 0)            {                m_fieldNames.Add("TAG", "TagName");                m_fieldNames.Add("TIME", "Timestamp");                m_fieldNames.Add("VALUE", "Value");            }        }         // Retrieves the measurements from the database.        private void GetDbMeasurements(object state)        {            IDbConnection connection = null;             // Get measurements from the database.            try            {                SignalIndexCache signalIndexCache = new SignalIndexCache();                CompactMeasurement measurement;                long startTime = DateTime.UtcNow.Ticks;                 if (m_cacheFileName != null && File.Exists(m_cacheFileName))                {                    OnStatusMessage(MessageLevel.Info, "Loading cached input data...");                     try                    {                        using (FileStream data = File.OpenRead(m_cacheFileName))                        {                            byte[] buffer = new byte[4];                            int signalIndexCacheImageSize;                            int compactMeasurementSize;                            int totalMeasurements;                             // Read the signal index cache image size from the file                            if (data.Read(buffer, 0, 4) != 4)                                throw new EndOfStreamException();                             signalIndexCacheImageSize = LittleEndian.ToInt32(buffer, 0);                             // Resize buffer to accommodate exact signal index cache                            buffer = new byte[signalIndexCacheImageSize];                             // Read the signal index cache image from the file                            if (data.Read(buffer, 0, signalIndexCacheImageSize) != signalIndexCacheImageSize)                                throw new EndOfStreamException();                             // Deserialize the signal index cache                            signalIndexCache = Serialization.Deserialize<SignalIndexCache>(buffer, SerializationFormat.Binary);                             // Read the size of each compact measurement from the file                            if (data.Read(buffer, 0, 4) != 4)                                throw new EndOfStreamException();                             compactMeasurementSize = LittleEndian.ToInt32(buffer, 0);                             // Read the total number of compact measurements from the file                            if (data.Read(buffer, 0, 4) != 4)                                throw new EndOfStreamException();                             totalMeasurements = LittleEndian.ToInt32(buffer, 0);                             // Resize buffer to accommodate compact measurement if needed (not likely)                            if (buffer.Length < compactMeasurementSize)                                buffer = new byte[compactMeasurementSize];                             // Read each compact measurement image from the file                            for (int i = 0; i < totalMeasurements; i++)                            {                                if (data.Read(buffer, 0, compactMeasurementSize) != compactMeasurementSize)                                    throw new EndOfStreamException();                                 // Parse compact measurement                                measurement = new CompactMeasurement(signalIndexCache);                                measurement.ParseBinaryImage(buffer, 0, compactMeasurementSize);                                 m_dbMeasurements.Add(measurement);                                 if (m_dbMeasurements.Count % 50000 == 0)                                    OnStatusMessage(MessageLevel.Info, $"Loaded {m_dbMeasurements.Count:N0} records so far...");                            }                             OnStatusMessage(MessageLevel.Info, $"Completed data load in {((Ticks)(DateTime.UtcNow.Ticks - startTime)).ToElapsedTimeString(2)}");                        }                    }                    catch (Exception ex)                    {                        // ReSharper disable once CanBeReplacedWithTryCastAndCheckForNull                        if (ex is EndOfStreamException)                            throw (EndOfStreamException)ex;                         throw new EndOfStreamException(ex.Message, ex);                    }                }                else                {                    OnStatusMessage(MessageLevel.Info, "Loading database input data...");                     const string MeasurementTable = "ActiveMeasurements";                     Dictionary<string, string> dataProviderSettings = m_dataProviderString.ParseKeyValuePairs();                    Assembly assm = Assembly.Load(dataProviderSettings["AssemblyName"]);                    Type connectionType = assm.GetType(dataProviderSettings["ConnectionType"]);                     Dictionary<Guid, MeasurementKey> lookupCache = new Dictionary<Guid, MeasurementKey>();                    IDbCommand command;                    IDataReader dbReader;                    MeasurementKey key;                    Guid id;                    ushort index = 0;                     connection = (IDbConnection)Activator.CreateInstance(connectionType);                    connection.ConnectionString = m_dbConnectionString;                    connection.Open();                     command = connection.CreateCommand();                    command.CommandText = $"SELECT * FROM {m_dbTableName}";                     using (dbReader = command.ExecuteReader())                    {                        while (dbReader.Read())                        {                            measurement = new CompactMeasurement(signalIndexCache);                             foreach (string fieldName in m_fieldNames.Keys)                            {                                object value = dbReader[fieldName];                                string propertyName = m_fieldNames[fieldName];                                 switch (propertyName)                                {                                    case "Timestamp":

Lines 1–500 of 732.

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