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