478 lines · 16.4 KB · C#
RawDownload
//******************************************************************************************************//  Parser.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://  ----------------------------------------------------------------------------------------------------//  06/17/2012 - J. Ritchie Carroll//       Generated original version of source code.//  12/13/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System;using System.IO;using System.Linq;using System.Text.RegularExpressions;using GSF.IO; namespace GSF.COMTRADE{    /// <summary>    /// COMTRADE data file(s) parser.    /// </summary>    public class Parser : IDisposable    {        #region [ Members ]         // Fields        private Schema m_schema;        private string m_fileName;        private bool m_disposed;        private FileStream[] m_fileStreams;        private StreamReader[] m_fileReaders;        private int m_streamIndex;        private DateTime m_timestamp;        private double[] m_values;        private double[] m_primaryValues;        private double[] m_secondaryValues;        private bool m_inferTimeFromSampleRates;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new instance of the COMTRADE <see cref="Parser"/>.        /// </summary>        public Parser()        {            m_inferTimeFromSampleRates = true;        }         /// <summary>        /// Releases the unmanaged resources before the <see cref="Parser"/> object is reclaimed by <see cref="GC"/>.        /// </summary>        ~Parser()        {            Dispose(false);        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets associated COMTRADE schema for this <see cref="Parser"/>.        /// </summary>        public Schema Schema        {            get            {                return m_schema;            }            set            {                m_schema = value;                 if ((object)m_schema != null)                {                    if (m_schema.TotalChannels > 0)                        m_values = new double[m_schema.TotalChannels];                    else                        throw new InvalidOperationException("Invalid schema: total channels defined in schema is zero.");                     if (m_schema.TotalSampleRates == 0)                        m_inferTimeFromSampleRates = false;                }                else                {                    m_values = null;                }            }        }         /// <summary>        /// Gets or sets COMTRADE data filename. If there are more than one data files in a set, this should be set to first file name in the set, e.g., DATA123.D00.        /// </summary>        public string FileName        {            get            {                return m_fileName;            }            set            {                m_fileName = value;            }        }         /// <summary>        /// Gets or sets flag that determines if time should be inferred from sample rates.        /// </summary>        public bool InferTimeFromSampleRates        {            get            {                return m_inferTimeFromSampleRates;            }            set            {                m_inferTimeFromSampleRates = value;            }        }         /// <summary>        /// Gets timestamp of current record.        /// </summary>        public DateTime Timestamp        {            get            {                return m_timestamp;            }        }         /// <summary>        /// Gets values of current record.        /// </summary>        public double[] Values        {            get            {                return m_values;            }        }         /// <summary>        /// Gets values of current record with secondary analog channels scaled to primary values.        /// </summary>        public double[] PrimaryValues        {            get            {                if ((object)m_primaryValues == null)                {                    m_primaryValues = new double[m_values.Length];                     for (int i = 0; i < m_primaryValues.Length; i++)                    {                        double value = m_values[i];                         if (i < m_schema.AnalogChannels.Length)                        {                            if (char.ToUpper(m_schema.AnalogChannels[i].ScalingIdentifier) == 'S')                                value *= m_schema.AnalogChannels[i].PrimaryRatio / m_schema.AnalogChannels[i].SecondaryRatio;                        }                         m_primaryValues[i] = value;                    }                }                 return m_primaryValues;            }        }         /// <summary>        /// Gets values of current record with primary analog channels scaled to secondary values.        /// </summary>        public double[] SecondaryValues        {            get            {                if ((object)m_secondaryValues == null)                {                    m_secondaryValues = new double[m_values.Length];                     for (int i = 0; i < m_secondaryValues.Length; i++)                    {                        double value = m_values[i];                         if (i < m_schema.AnalogChannels.Length)                        {                            if (char.ToUpper(m_schema.AnalogChannels[i].ScalingIdentifier) == 'P')                                value *= m_schema.AnalogChannels[i].SecondaryRatio / m_schema.AnalogChannels[i].PrimaryRatio;                        }                         m_secondaryValues[i] = value;                    }                }                 return m_secondaryValues;            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Releases all the resources used by the <see cref="Parser"/> object.        /// </summary>        public void Dispose()        {            Dispose(true);            GC.SuppressFinalize(this);        }         /// <summary>        /// Releases the unmanaged resources used by the <see cref="Parser"/> 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)                    {                        CloseFiles();                    }                }                finally                {                    m_disposed = true;  // Prevent duplicate dispose.                }            }        }         /// <summary>        /// Opens all COMTRADE data file streams.        /// </summary>        public void OpenFiles()        {            if (string.IsNullOrWhiteSpace(m_fileName))                throw new InvalidOperationException("First COMTRADE data file name was not specified, cannot open files.");             if (!File.Exists(m_fileName))                throw new FileNotFoundException(string.Format("Specified COMTRADE data file \"{0}\" was not found, cannot open files.", m_fileName));             // Get all data files in the collection            const string FileRegex = @"(?:\.dat|\.d\d\d)$";            string directory = FilePath.GetDirectoryName(m_fileName);            string rootFileName = FilePath.GetFileNameWithoutExtension(m_fileName);            string extension = FilePath.GetExtension(m_fileName).Substring(0, 2) + "*";             string[] fileNames = FilePath.GetFileList(Path.Combine(directory, rootFileName + extension))                .Where(fileName => Regex.IsMatch(fileName, FileRegex, RegexOptions.IgnoreCase))                .OrderBy(fileName => fileName, StringComparer.OrdinalIgnoreCase)                .ToArray();             // Create a new file stream for each file            m_fileStreams = new FileStream[fileNames.Length];             for (int i = 0; i < fileNames.Length; i++)            {                m_fileStreams[i] = new FileStream(fileNames[i], FileMode.Open, FileAccess.Read, FileShare.Read);            }             m_streamIndex = 0;        }         /// <summary>        /// Closes all COMTRADE data file streams.        /// </summary>        public void CloseFiles()        {            if ((object)m_fileStreams != null)            {                foreach (FileStream fileStream in m_fileStreams)                {                    if ((object)fileStream != null)                    {                        fileStream.Close();                        fileStream.Dispose();                    }                }            }             m_fileStreams = null;        }         /// <summary>        /// Reads next COMTRADE record.        /// </summary>        /// <returns><c>true</c> if read succeeded; otherwise <c>false</c> if end of data set was reached.</returns>        public bool ReadNext()        {            if ((object)m_fileStreams == null)                throw new InvalidOperationException("COMTRADE data files are not open, cannot read next record.");             if ((object)m_schema == null)                throw new InvalidOperationException("No COMTRADE schema has been defined, cannot read records.");             if (m_streamIndex > m_fileStreams.Length)                throw new EndOfStreamException("All COMTRADE data has been read, cannot read more records.");             m_primaryValues = null;            m_secondaryValues = null;             if (m_schema.FileType == FileType.Ascii)                return ReadNextAscii();             return ReadNextBinary();        }         // Handle ASCII file read        private bool ReadNextAscii()        {            if ((object)m_fileReaders == null)            {                m_fileReaders = new StreamReader[m_fileStreams.Length];                 for (int i = 0; i < m_fileStreams.Length; i++)                    m_fileReaders[i] = new StreamReader(m_fileStreams[i]);            }             // Read next line of record values            StreamReader reader = m_fileReaders[m_streamIndex];            string line = reader.ReadLine();            string[] elems = ((object)line != null) ? line.Split(',') : null;             // See if we have reached the end of this file            if ((object)elems == null || elems.Length != m_values.Length + 2)            {                if (reader.EndOfStream)                {                    m_streamIndex++;                     // There is more to read if there is another file                    return m_streamIndex < m_fileStreams.Length && ReadNext();                }                 throw new InvalidOperationException("COMTRADE schema does not match number of elements found in ASCII data file.");            }             // Parse row of data            uint sample = uint.Parse(elems[0]);             // Get timestamp of this record            m_timestamp = DateTime.MinValue;             // If sample rates are defined, this is the preferred method for timestamp resolution            if (m_inferTimeFromSampleRates && m_schema.SampleRates.Length > 0)            {                // Find rate for given sample                SampleRate sampleRate = m_schema.SampleRates.LastOrDefault(sr => sample <= sr.EndSample);                 if (sampleRate.Rate > 0.0D)                    m_timestamp = new DateTime(Ticks.FromSeconds(1.0D / sampleRate.Rate * sample) + m_schema.StartTime.Value);            }             // Fall back on specified microsecond time            if (m_timestamp == DateTime.MinValue)                m_timestamp = new DateTime(Ticks.FromMicroseconds(uint.Parse(elems[1]) * m_schema.TimeFactor) + m_schema.StartTime.Value);             // Parse all record values            for (int i = 0; i < m_values.Length; i++)            {                m_values[i] = double.Parse(elems[i + 2]);                 if (i < m_schema.AnalogChannels.Length)                {                    m_values[i] *= m_schema.AnalogChannels[i].Multiplier;                    m_values[i] += m_schema.AnalogChannels[i].Adder;                }            }             return true;        }         // Handle binary file read        private bool ReadNextBinary()        {            FileStream currentFile = m_fileStreams[m_streamIndex];            int recordLength = m_schema.BinaryRecordLength;            byte[] buffer = new byte[recordLength];             // Read next record from file            int bytesRead = currentFile.Read(buffer, 0, recordLength);             // See if we have reached the end of this file            if (bytesRead == 0)            {                m_streamIndex++;                 // There is more to read if there is another file                return m_streamIndex < m_fileStreams.Length && ReadNext();            }             if (bytesRead == recordLength)            {                int index = 0;                 // Read sample index                uint sample = LittleEndian.ToUInt32(buffer, index);                index += 4;                 // Get timestamp of this record                m_timestamp = DateTime.MinValue;                 // If sample rates are defined, this is the preferred method for timestamp resolution                if (m_inferTimeFromSampleRates && m_schema.SampleRates.Length > 0)                {                    // Find rate for given sample                    SampleRate sampleRate = m_schema.SampleRates.LastOrDefault(sr => sample <= sr.EndSample);                     if (sampleRate.Rate > 0.0D)                        m_timestamp = new DateTime(Ticks.FromSeconds(1.0D / sampleRate.Rate * sample) + m_schema.StartTime.Value);                }                 // Read microsecond timestamp                uint microseconds = LittleEndian.ToUInt32(buffer, index);                index += 4;                 // Fall back on specified microsecond time                if (m_timestamp == DateTime.MinValue)                    m_timestamp = new DateTime(Ticks.FromMicroseconds(microseconds * m_schema.TimeFactor) + m_schema.StartTime.Value);                 // Parse all analog record values                for (int i = 0; i < m_schema.AnalogChannels.Length; i++)                {                    // Read next value                    m_values[i] = LittleEndian.ToInt16(buffer, index) * m_schema.AnalogChannels[i].Multiplier + m_schema.AnalogChannels[i].Adder;                    index += 2;                }                 int valueIndex = m_schema.AnalogChannels.Length;                int digitalWords = m_schema.DigitalWords;                ushort digitalWord;                 for (int i = 0; i < digitalWords; i++)                {                    // Read next digital word                    digitalWord = LittleEndian.ToUInt16(buffer, index);                    index += 2;                     // Distribute each bit of digital word through next 16 digital values                    for (int j = 0; j < 16 && valueIndex < m_values.Length; j++, valueIndex++)                    {                        m_values[valueIndex] = digitalWord.CheckBits(BitExtensions.BitVal(j)) ? 1.0D : 0.0D;                    }                }            }            else            {                throw new InvalidOperationException("Failed to read enough bytes from COMTRADE file for a record as defined by schema - possible schema/data file mismatch or file corruption.");            }             return true;        }         #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