//****************************************************************************************************** // VectorElement.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/02/2012 - Stephen C. Wills, Grid Protection Alliance // Generated original version of source code. // 12/17/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Text; namespace GSF.PQDIF.Physical { /// /// Represents an which is a collection of values /// in a PQDIF file. Vector elements are part of the physical structure /// of a PQDIF file. They exist within the body of a /// (contained by a ). /// public class VectorElement : Element { #region [ Members ] // Fields private int m_size; private byte[] m_values; #endregion #region [ Properties ] /// /// Gets or sets the number of values in the vector. /// public int Size { get { return m_size; } set { if (value < 0) throw new ArgumentException("Size must be >= 0", nameof(value)); if (m_size != value) { m_size = value; Reallocate(); } } } /// /// Gets the type of the element. /// Returns . /// public override ElementType TypeOfElement { get { return ElementType.Vector; } } /// /// Gets or sets the physical type of the value or values contained /// by the element. /// /// /// This determines the data type and size of the /// value or values. The value of this property is only relevant when /// is either /// or . /// public override PhysicalType TypeOfValue { get { return base.TypeOfValue; } set { if (base.TypeOfValue != value) { base.TypeOfValue = value; Reallocate(); } } } #endregion #region [ Methods ] /// /// Gets the value at the given index as the physical type defined /// by and returns it as a generic /// . /// /// The index of the value to be retrieved. /// The value that was retrieved from the vector. public object Get(int index) { switch (TypeOfValue) { case PhysicalType.Boolean1: return GetUInt1(index) != 0; case PhysicalType.Boolean2: return GetInt2(index) != 0; case PhysicalType.Boolean4: return GetInt4(index) != 0; case PhysicalType.Char1: return Encoding.ASCII.GetString(m_values)[index]; case PhysicalType.Char2: return Encoding.Unicode.GetString(m_values)[index]; case PhysicalType.Integer1: return GetInt1(index); case PhysicalType.Integer2: return GetInt2(index); case PhysicalType.Integer4: return GetInt4(index); case PhysicalType.UnsignedInteger1: return GetUInt1(index); case PhysicalType.UnsignedInteger2: return GetUInt2(index); case PhysicalType.UnsignedInteger4: return GetUInt4(index); case PhysicalType.Real4: return GetReal4(index); case PhysicalType.Real8: return GetReal8(index); case PhysicalType.Complex8: return new ComplexNumber((double)GetReal4(index * 2), (double)GetReal4(index * 2 + 1)); case PhysicalType.Complex16: return new ComplexNumber(GetReal8(index * 2), GetReal8(index * 2 + 1)); case PhysicalType.Timestamp: return GetTimestamp(index); case PhysicalType.Guid: return new Guid(m_values.BlockCopy(index * 16, 16)); default: throw new ArgumentOutOfRangeException(); } } /// /// Sets the value at the given index as the physical type defined by . /// /// The index of the value. /// The new value to be stored. public void Set(int index, object value) { char c; byte[] bytes; ComplexNumber complexNumber; switch (TypeOfValue) { case PhysicalType.Boolean1: SetUInt1(index, Convert.ToBoolean(value) ? (byte)1 : (byte)0); break; case PhysicalType.Boolean2: SetInt2(index, Convert.ToBoolean(value) ? (short)1 : (short)0); break; case PhysicalType.Boolean4: SetInt4(index, Convert.ToBoolean(value) ? 1 : 0); break; case PhysicalType.Char1: c = Convert.ToChar(value); bytes = Encoding.ASCII.GetBytes(c.ToString()); SetUInt1(index, bytes[0]); break; case PhysicalType.Char2: c = Convert.ToChar(value); bytes = Encoding.Unicode.GetBytes(c.ToString()); SetInt2(index, BitConverter.ToInt16(bytes, 0)); break; case PhysicalType.Integer1: SetInt1(index, Convert.ToSByte(value)); break; case PhysicalType.Integer2: SetInt2(index, Convert.ToInt16(value)); break; case PhysicalType.Integer4: SetInt4(index, Convert.ToInt32(value)); break; case PhysicalType.UnsignedInteger1: SetUInt1(index, Convert.ToByte(value)); break; case PhysicalType.UnsignedInteger2: SetUInt2(index, Convert.ToUInt16(value)); break; case PhysicalType.UnsignedInteger4: SetUInt4(index, Convert.ToUInt32(value)); break; case PhysicalType.Real4: SetReal4(index, Convert.ToSingle(value)); break; case PhysicalType.Real8: SetReal8(index, Convert.ToDouble(value)); break; case PhysicalType.Complex8: complexNumber = (ComplexNumber)value; SetReal4(index * 2, (float)complexNumber.Real); SetReal4(index * 2 + 1, (float)complexNumber.Imaginary); break; case PhysicalType.Complex16: complexNumber = (ComplexNumber)value; SetReal8(index * 2, complexNumber.Real); SetReal8(index * 2 + 1, complexNumber.Imaginary); break; case PhysicalType.Timestamp: SetTimestamp(index, Convert.ToDateTime(value)); break; case PhysicalType.Guid: SetGuid(index, (Guid)value); break; default: throw new ArgumentOutOfRangeException(); } } /// /// Gets a value in this vector as an 8-bit unsigned integer. /// /// The index of the value. /// The value as an 8-bit unsigned integer. public byte GetUInt1(int index) { if ((object)m_values == null) throw new InvalidOperationException("Unable to retrieve values from uninitialized vector; set the size and physical type of the vector first"); return m_values[index]; } /// /// Sets a value in this vector as an 8-bit unsigned integer. /// /// The index of the value. /// The new value of an 8-bit unsigned integer. public void SetUInt1(int index, byte value) { if ((object)m_values == null) throw new InvalidOperationException("Unable to insert values into uninitialized vector; set the size and physical type of the vector first"); m_values[index] = value; } /// /// Gets a value in this vector as a 16-bit unsigned integer. /// /// The index of the value. /// The value as a 16-bit unsigned integer. public ushort GetUInt2(int index) { if ((object)m_values == null) throw new InvalidOperationException("Unable to retrieve values from uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 2; return LittleEndian.ToUInt16(m_values, byteIndex); } /// /// Sets a value in this vector as a 16-bit unsigned integer. /// /// The index of the value. /// The new value of a 16-bit unsigned integer. public void SetUInt2(int index, ushort value) { if ((object)m_values == null) throw new InvalidOperationException("Unable to insert values into uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 2; LittleEndian.CopyBytes(value, m_values, byteIndex); } /// /// Gets a value in this vector as a 32-bit unsigned integer. /// /// The index of the value. /// The value as a 32-bit unsigned integer. public uint GetUInt4(int index) { if ((object)m_values == null) throw new InvalidOperationException("Unable to retrieve values from uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 4; return LittleEndian.ToUInt32(m_values, byteIndex); } /// /// Sets a value in this vector as a 32-bit unsigned integer. /// /// The index of the value. /// The new value of a 32-bit unsigned integer. public void SetUInt4(int index, uint value) { if ((object)m_values == null) throw new InvalidOperationException("Unable to insert values into uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 4; LittleEndian.CopyBytes(value, m_values, byteIndex); } /// /// Gets a value in this vector as an 8-bit signed integer. /// /// The index of the value. /// The value as an 8-bit signed integer. public sbyte GetInt1(int index) { if ((object)m_values == null) throw new InvalidOperationException("Unable to retrieve values from uninitialized vector; set the size and physical type of the vector first"); return (sbyte)m_values[index]; } /// /// Sets a value in this vector as an 8-bit signed integer. /// /// The index of the value. /// The new value of an 8-bit signed integer. public void SetInt1(int index, sbyte value) { if ((object)m_values == null) throw new InvalidOperationException("Unable to insert values into uninitialized vector; set the size and physical type of the vector first"); m_values[index] = (byte)value; } /// /// Gets a value in this vector as a 16-bit signed integer. /// /// The index of the value. /// The value as a 16-bit signed integer. public short GetInt2(int index) { if ((object)m_values == null) throw new InvalidOperationException("Unable to retrieve values from uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 2; return LittleEndian.ToInt16(m_values, byteIndex); } /// /// Sets a value in this vector as a 16-bit signed integer. /// /// The index of the value. /// The new value of a 16-bit signed integer. public void SetInt2(int index, short value) { if ((object)m_values == null) throw new InvalidOperationException("Unable to insert values into uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 2; LittleEndian.CopyBytes(value, m_values, byteIndex); } /// /// Gets a value in this vector as a 32-bit signed integer. /// /// The index of the value. /// The value as a 32-bit signed integer. public int GetInt4(int index) { if ((object)m_values == null) throw new InvalidOperationException("Unable to retrieve values from uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 4; return LittleEndian.ToInt32(m_values, byteIndex); } /// /// Sets a value in this vector as a 32-bit signed integer. /// /// The index of the value. /// The new value of a 32-bit signed integer. public void SetInt4(int index, int value) { if ((object)m_values == null) throw new InvalidOperationException("Unable to insert values into uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 4; LittleEndian.CopyBytes(value, m_values, byteIndex); } /// /// Gets a value in this vector as a 32-bit floating point number. /// /// The index of the value. /// The value as a 32-bit floating point number. public float GetReal4(int index) { if ((object)m_values == null) throw new InvalidOperationException("Unable to retrieve values from uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 4; return LittleEndian.ToSingle(m_values, byteIndex); } /// /// Sets a value in this vector as a 32-bit floating point number. /// /// The index of the value. /// The new value of a 32-bit floating point number. public void SetReal4(int index, float value) { if ((object)m_values == null) throw new InvalidOperationException("Unable to insert values into uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 4; LittleEndian.CopyBytes(value, m_values, byteIndex); } /// /// Gets a value in this vector as a 64-bit floating point number. /// /// The index of the value. /// The value as a 64-bit floating point number. public double GetReal8(int index) { if ((object)m_values == null) throw new InvalidOperationException("Unable to retrieve values from uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 8; return LittleEndian.ToDouble(m_values, byteIndex); } /// /// Sets a value in this vector as a 64-bit floating point number. /// /// The index of the value. /// The new value of a 64-bit floating point number. public void SetReal8(int index, double value) { if ((object)m_values == null) throw new InvalidOperationException("Unable to insert values into uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 8; LittleEndian.CopyBytes(value, m_values, byteIndex); } /// /// Gets a value in this vector as a . /// /// The index of the value. /// The value as a . public DateTime GetTimestamp(int index) { if ((object)m_values == null) throw new InvalidOperationException("Unable to retrieve values from uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 12; DateTime epoch = new DateTime(1900, 1, 1); uint days = LittleEndian.ToUInt32(m_values, byteIndex); double seconds = LittleEndian.ToDouble(m_values, byteIndex + 4); // Timestamps in a PQDIF file are represented by two separate numbers, one being the number of // days since January 1, 1900 and the other being the number of seconds since midnight. The // standard implementation also includes a constant for the number of days between January 1, // 1900 and January 1, 1970 to facilitate the conversion between PQDIF timestamps and UNIX // timestamps. However, the constant defined in the standard is 25569 days, whereas the actual // number of days between those two dates is 25567 days; a two day difference. That is why we // need to also subtract two days here when parsing PQDIF timestamps. return epoch.AddDays(days - 2u).AddSeconds(seconds); } /// /// Sets a value in this vector as a . /// /// The index of the value. /// The new value of a . public void SetTimestamp(int index, DateTime value) { if ((object)m_values == null) throw new InvalidOperationException("Unable to insert values into uninitialized vector; set the size and physical type of the vector first"); int byteIndex = index * 12; DateTime epoch = new DateTime(1900, 1, 1); TimeSpan sinceEpoch = value - epoch; TimeSpan daySpan = TimeSpan.FromDays(Math.Floor(sinceEpoch.TotalDays)); TimeSpan secondSpan = sinceEpoch - daySpan; // Timestamps in a PQDIF file are represented by two separate numbers, one being the number of // days since January 1, 1900 and the other being the number of seconds since midnight. The // standard implementation also includes a constant for the number of days between January 1, // 1900 and January 1, 1970 to facilitate the conversion between PQDIF timestamps and UNIX // timestamps. However, the constant defined in the standard is 25569 days, whereas the actual // number of days between those two dates is 25567 days; a two day difference. That is why we // need to also add two days here when creating PQDIF timestamps. LittleEndian.CopyBytes((uint)daySpan.TotalDays + 2u, m_values, byteIndex); LittleEndian.CopyBytes(secondSpan.TotalSeconds, m_values, byteIndex + 4); } /// /// Gets the value in this vector as a globally unique identifier. /// /// The index of the value. /// The value as a globally unique identifier. public Guid GetGuid(int index) { int byteIndex = index * 16; byte[] bytes = m_values.BlockCopy(byteIndex, 16); return new Guid(bytes); } /// /// Sets the value in this vector as a globally unique identifier. /// /// The index of the value. /// The new value as a globally unique identifier. public void SetGuid(int index, Guid value) { byte[] bytes = value.ToByteArray(); int byteIndex = index * bytes.Length; Buffer.BlockCopy(bytes, 0, m_values, byteIndex, bytes.Length); } /// /// Gets the raw bytes of the values contained by this vector. /// /// The raw bytes of the values contained by this vector. public byte[] GetValues() { return m_values; } /// /// Sets the raw bytes of the values contained by this vector. /// /// The array that contains the raw bytes. /// The offset into the array at which the values start. public void SetValues(byte[] values, int offset) { if ((object)m_values == null) throw new InvalidOperationException("Unable to insert values into uninitialized vector; set the size and physical type of the vector first"); Buffer.BlockCopy(values, offset, m_values, 0, m_size * TypeOfValue.GetByteSize()); } /// /// Returns a string representation of this vector. /// /// A string representation of this vector. public override string ToString() { return string.Format("Vector -- Type: {0}, Size: {1}, Tag: {2}", TypeOfValue, m_size, TagOfElement); } // Reallocates the byte array containing the vector data based on // the size of the vector and the physical type of the values. private void Reallocate() { if (TypeOfValue != 0) m_values = new byte[m_size * TypeOfValue.GetByteSize()]; } #endregion } }