//******************************************************************************************************
// 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
}
}