//****************************************************************************************************** // Schema.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/18/2011 - J. Ritchie Carroll // Generated original version of source code. // 12/13/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.IO; using System.Text; namespace GSF.COMTRADE { #region [ Enumerations ] /// /// file type enumeration. /// public enum FileType { /// /// ASCII file type. /// Ascii, /// /// Binary file type. /// Binary } #endregion /// /// Represents the schema for a configuration file in the COMTRADE file standard, IEEE Std C37.111-1999. /// public class Schema { #region [ Members ] // Fields private string m_stationName; private string m_deviceID; private int m_version; private AnalogChannel[] m_analogChannels; private DigitalChannel[] m_digitalChannels; private double m_nominalFrequency; private SampleRate[] m_sampleRates; private Timestamp m_startTime; private Timestamp m_triggerTime; private FileType m_fileType; private double m_timeFactor; #endregion #region [ Constructors ] /// /// Creates a new instance of the . /// public Schema() { m_version = 1999; m_nominalFrequency = 60.0D; m_fileType = FileType.Binary; m_timeFactor = 1.0D; SampleRates = null; } /// /// Creates a new instance of the from an existing configuration file name. /// /// File name of configuration file to parse. public Schema(string fileName) { string[] lines = File.ReadAllLines(fileName); string[] parts; int lineNumber = 0; // Parse version line parts = lines[lineNumber++].Split(','); if (parts.Length != 2 && parts.Length != 3) throw new InvalidOperationException(string.Format("Unexpected number of line image elements for first configuration file line: {0} - expected 2 or 3\r\nImage = {1}", parts.Length, lines[0])); StationName = parts[0].Trim(); DeviceID = parts[1].Trim(); if (parts.Length == 3 && !string.IsNullOrWhiteSpace(parts[2])) m_version = int.Parse(parts[2].Trim()); else m_version = 1991; // Parse totals line parts = lines[lineNumber++].Split(','); if (parts.Length != 3) throw new InvalidOperationException(string.Format("Unexpected number of line image elements for second configuration file line: {0} - expected 3\r\nImage = {1}", parts.Length, lines[1])); int totalChannels = int.Parse(parts[0].Trim()); int totalAnalogChannels = int.Parse(parts[1].Trim().Split('A')[0]); int totalDigitalChannels = int.Parse(parts[2].Trim().Split('D')[0]); if (totalChannels != totalAnalogChannels + totalDigitalChannels) throw new InvalidOperationException(string.Format("Total defined channels must equal the sum of the total number of analog and digital channel definitions.\r\nImage = {1}", parts.Length, lines[1])); // Parse analog definitions List analogChannels = new List(); for (int i = 0; i < totalAnalogChannels; i++) { analogChannels.Add(new AnalogChannel(lines[lineNumber++])); } AnalogChannels = analogChannels.ToArray(); // Parse digital definitions List digitalChannels = new List(); for (int i = 0; i < totalDigitalChannels; i++) { digitalChannels.Add(new DigitalChannel(lines[lineNumber++])); } DigitalChannels = digitalChannels.ToArray(); // Parse line frequency NominalFrequency = double.Parse(lines[lineNumber++]); // Parse total number of sample rates int totalSampleRates = int.Parse(lines[lineNumber++]); if (totalSampleRates == 0) totalSampleRates = 1; // Parse each sample rate List sampleRates = new List(); for (int i = 0; i < totalSampleRates; i++) sampleRates.Add(new SampleRate(lines[lineNumber++])); SampleRates = sampleRates.ToArray(); // Parse timestamps StartTime = new Timestamp(lines[lineNumber++]); TriggerTime = new Timestamp(lines[lineNumber++]); // Parse file type FileType = (FileType)Enum.Parse(typeof(FileType), lines[lineNumber++], true); // Parse time factor TimeFactor = double.Parse(lines[lineNumber++]); } #endregion #region [ Properties ] /// /// Gets or sets free-form station name for this . /// public string StationName { get { return m_stationName; } set { m_stationName = value; } } /// /// Gets or sets free-form device ID for this . /// public string DeviceID { get { return m_deviceID; } set { m_deviceID = value; } } /// /// Gets or sets version number of the IEEE Std C37.111 used by this . /// /// Only IEEE Std C37.111 version 1999 is supported by this implementation of the phasor data schema. public int Version { get { return m_version; } set { //if (value != 1999) // throw new ArgumentOutOfRangeException("value", value + " is an invalid version number. Only IEEE Std C37.111 version 1999 is supported by this implementation of the phasor data schema."); m_version = value; } } /// /// Gets total number of analog and digital channels of this . /// public int TotalChannels { get { return TotalAnalogChannels + TotalDigitalChannels; } } /// /// Gets total number of analog channels of this . /// public int TotalAnalogChannels { get { if (m_analogChannels != null) return m_analogChannels.Length; return 0; } } /// /// Gets total number of digital channels of this . /// public int TotalDigitalChannels { get { if (m_digitalChannels != null) return m_digitalChannels.Length; return 0; } } /// /// Gets or sets analog channels of this . /// public AnalogChannel[] AnalogChannels { get { return m_analogChannels; } set { m_analogChannels = value; } } /// /// Gets or sets digital channels of this . /// public DigitalChannel[] DigitalChannels { get { return m_digitalChannels; } set { m_digitalChannels = value; } } /// /// Gets or sets nominal frequency of this . /// public double NominalFrequency { get { return m_nominalFrequency; } set { m_nominalFrequency = value; // Cascade nominal frequency update to analog channels if (m_analogChannels != null) { foreach (AnalogChannel analogChannel in m_analogChannels) { analogChannel.NominalFrequency = m_nominalFrequency; } } } } /// /// Gets total number of sample rates of this . /// public int TotalSampleRates { get { if (m_sampleRates.Length == 1 && m_sampleRates[0].Rate == 0) return 0; return m_sampleRates.Length; } } /// /// Gets or sets sampling rates of this . A file of phasor data will normally be made using a single sampling rate, so this will usually be 1. /// public SampleRate[] SampleRates { get { return m_sampleRates; } set { if ((object)value == null) m_sampleRates = new SampleRate[] { new SampleRate() { Rate = 0, EndSample = 1 } }; else m_sampleRates = value; } } /// /// Gets or sets start timestamp of this . /// public Timestamp StartTime { get { return m_startTime; } set { m_startTime = value; } } /// /// Gets or sets trigger timestamp of this . /// public Timestamp TriggerTime { get { return m_triggerTime; } set { m_triggerTime = value; } } /// /// Gets or sets file type of this . /// public FileType FileType { get { return m_fileType; } set { m_fileType = value; } } /// /// Gets or sets the multiplication factor for the time differential (timestamp) field in this . /// public double TimeFactor { get { return m_timeFactor; } set { m_timeFactor = value; } } /// /// Gets the total digital words for given number of digital values when the file type is binary. /// public int DigitalWords { get { return (int)Math.Ceiling(m_digitalChannels.Length / 16.0D); } } /// /// Calculates the size of a record, in bytes, when the file type is binary. /// public int BinaryRecordLength { get { return 8 + 2 * m_analogChannels.Length + 2 * DigitalWords; } } /// /// Gets the file image of this . /// public string FileImage { get { StringBuilder fileImage = new StringBuilder(); // Write version line fileImage.AppendFormat("{0},{1},{2}\r\n", StationName, DeviceID, Version); // Write totals line fileImage.AppendFormat("{0},{1}A,{2}D\r\n", TotalChannels, TotalAnalogChannels, TotalDigitalChannels); // Write analog definitions for (int i = 0; i < TotalAnalogChannels; i++) { fileImage.AppendLine(AnalogChannels[i].ToString()); } // Write digital definitions for (int i = 0; i < TotalDigitalChannels; i++) { fileImage.AppendLine(DigitalChannels[i].ToString()); } // Write line frequency fileImage.AppendLine(NominalFrequency.ToString()); // Write total number of sample rates fileImage.AppendLine(TotalSampleRates.ToString()); // Write sample rates for (int i = 0; i < TotalSampleRates; i++) { fileImage.AppendLine(SampleRates[i].ToString()); } // Write timestamps fileImage.AppendLine(StartTime.ToString()); fileImage.AppendLine(TriggerTime.ToString()); // Write file type fileImage.AppendLine(FileType.ToString().ToUpper()); // Write time factor fileImage.AppendLine(TimeFactor.ToString()); return fileImage.ToString(); } } #endregion } }