//******************************************************************************************************
// ArchiveDataPoint.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:
// ----------------------------------------------------------------------------------------------------
// 02/23/2007 - Pinal C. Patel
// Generated original version of code based on DatAWare system specifications by Brian B. Fox, GSF.
// 04/20/2009 - Pinal C. Patel
// Converted to C#.
// 09/10/2009 - Pinal C. Patel
// Added constructor that takes in IMeasurement.
// 09/15/2009 - Stephen C. Wills
// Added new header and license agreement.
// 03/15/2010 - Pinal C. Patel
// Implemented IFormattable.ToString() overloads.
// 04/20/2010 - J. Ritchie Carroll
// Added construction overload for IMeasurement that accepts specific quality.
// 09/16/2010 - J. Ritchie Carroll
// Modified formatted time to include milliseconds.
// 10/11/2010 - Mihir Brahmbhatt
// Updated header and license agreement.
// 11/30/2011 - J. Ritchie Carroll
// Modified to support buffer optimized ISupportBinaryImage.
// 12/14/2012 - Starlynn Danyelle Gilliam
// Modified Header.
//
//******************************************************************************************************
using System;
using System.Globalization;
using GSF.TimeSeries;
// ReSharper disable VirtualMemberCallInConstructor
// ReSharper disable NonReadonlyMemberInGetHashCode
namespace GSF.Historian.Files
{
///
/// Represents time-series data stored in .
///
///
///
///
///
public class ArchiveDataPoint : IDataPoint
{
// **************************************************************************************************
// * Binary Structure *
// **************************************************************************************************
// * # Of Bytes Byte Index Data Type Property Name *
// * ---------- ---------- ---------- --------------------------------------------------------------*
// * 4 0-3 Int32 Time *
// * 2 4-5 Int16 Flags (Quality & Milliseconds) *
// * 4 6-9 Single Value *
// **************************************************************************************************
#region [ Members ]
// Constants
///
/// Specifies the number of bytes in the binary image of .
///
public const int FixedLength = 10;
///
/// Specifies the bit-mask for stored in .
///
protected const Bits QualityMask = Bits.Bit00 | Bits.Bit01 | Bits.Bit02 | Bits.Bit03 | Bits.Bit04;
///
/// Specifies the bit-mask for milliseconds stored in .
///
protected const Bits MillisecondMask = Bits.Bit05 | Bits.Bit06 | Bits.Bit07 | Bits.Bit08 | Bits.Bit09 | Bits.Bit10 | Bits.Bit11 | Bits.Bit12 | Bits.Bit13 | Bits.Bit14 | Bits.Bit15;
// Fields
private int m_historianID;
private TimeTag m_time;
private float m_value;
private int m_flags;
private MetadataRecord m_metadata;
#endregion
#region [ Constructors ]
///
/// Initializes a new instance of the class.
///
/// Historian identifier of .
public ArchiveDataPoint(int historianID)
{
m_time = TimeTag.MinValue;
this.HistorianID = historianID;
}
///
/// Initializes a new instance of the class.
///
/// A time-series data point.
public ArchiveDataPoint(IDataPoint dataPoint)
: this(dataPoint.HistorianID, dataPoint.Time, dataPoint.Value, dataPoint.Quality)
{
}
///
/// Initializes a new instance of the class from a value.
///
/// Object that implements the interface.
public ArchiveDataPoint(IMeasurement measurement)
: this(measurement, measurement.HistorianQuality())
{
}
///
/// Initializes a new instance of the class from a value.
///
/// Object that implements the interface.
/// Specific value to apply to new .
public ArchiveDataPoint(IMeasurement measurement, Quality quality)
: this((int)measurement.Key.ID)
{
this.Time = new TimeTag((DateTime)measurement.Timestamp);
this.Value = (float)measurement.AdjustedValue;
this.Quality = quality;
}
///
/// Initializes a new instance of the class.
///
/// Historian identifier of .
/// of .
/// Floating-point value of .
/// of .
public ArchiveDataPoint(int historianID, TimeTag time, float value, Quality quality)
: this(historianID)
{
this.Time = time;
this.Value = value;
this.Quality = quality;
}
///
/// Initializes a new instance of the class.
///
/// Historian identifier of .
/// Binary image to be used for initializing .
/// 0-based starting index of initialization data in the .
/// Valid number of bytes in from .
public ArchiveDataPoint(int historianID, byte[] buffer, int startIndex, int length)
: this(historianID)
{
ParseBinaryImage(buffer, startIndex, length);
}
#endregion
#region [ Properties ]
///
/// Gets or sets the historian identifier of .
///
/// The value being assigned is not positive or -1.
public int HistorianID
{
get
{
return m_historianID;
}
set
{
if (value < 1 && value != -1)
throw new ArgumentException("Value must be positive or -1");
m_historianID = value;
}
}
///
/// Gets or sets the of .
///
/// The value being assigned is not between 01/01/1995 and 01/19/2063.
public virtual TimeTag Time
{
get
{
return m_time;
}
set
{
if (value.CompareTo(TimeTag.MinValue) < 0 || value.CompareTo(TimeTag.MaxValue) > 0)
throw new TimeTagException("Value must between 01/01/1995 and 01/19/2063");
m_time = value;
Flags = Flags.SetMaskedValue(MillisecondMask, m_time.ToDateTime().Millisecond << 5);
}
}
///
/// Gets or sets the floating-point value of .
///
public virtual float Value
{
get
{
return m_value;
}
set
{
m_value = value;
}
}
///
/// Gets or sets the of .
///
public virtual Quality Quality
{
get
{
return (Quality)Flags.GetMaskedValue(QualityMask);
}
set
{
Flags = Flags.SetMaskedValue(QualityMask, (int)value);
}
}
///
/// Gets or sets the associated with this .
///
public virtual MetadataRecord Metadata
{
get
{
return m_metadata;
}
set
{
m_metadata = value;
}
}
///
/// Gets a boolean value that indicates whether contains any data.
///
public virtual bool IsEmpty
{
get
{
return ((m_time.CompareTo(TimeTag.MinValue) == 0) &&
(m_value == default(float)) &&
(Quality == Quality.Unknown));
}
}
///
/// Gets the length of the .
///
public virtual int BinaryLength
{
get
{
return FixedLength;
}
}
///
/// Gets or sets the 32-bit word used for storing data of .
///
protected virtual int Flags
{
get
{
return m_flags;
}
set
{
m_flags = value;
}
}
#endregion
#region [ Methods ]
///
/// Initializes from the specified .
///
/// Binary image to be used for initializing .
/// 0-based starting index of initialization data in the .
/// Valid number of bytes in from .
/// Number of bytes used from the for initializing .
public virtual int ParseBinaryImage(byte[] buffer, int startIndex, int length)
{
if (length >= FixedLength)
{
// Binary image has sufficient data.
Flags = LittleEndian.ToInt16(buffer, startIndex + 4);
Value = LittleEndian.ToSingle(buffer, startIndex + 6);
TimeTag value = new TimeTag(LittleEndian.ToInt32(buffer, startIndex) + // Seconds
((decimal)(m_flags.GetMaskedValue(MillisecondMask) >> 5) / 1000)); // Milliseconds
// Make sure to properly validate timestamps for newly initialized or possibly corrupted blocks
if (value.CompareTo(TimeTag.MinValue) < 0 || value.CompareTo(TimeTag.MaxValue) > 0)
value = TimeTag.MinValue;
Time = value;
return FixedLength;
}
// Binary image does not have sufficient data.
return 0;
}
///
/// Generates binary image of the and copies it into the given buffer, for bytes.
///
/// Buffer used to hold generated binary image of the source object.
/// 0-based starting index in the to start writing.
/// The number of bytes written to the .
/// is null.
///
/// or is less than 0 -or-
/// and will exceed length.
///
public virtual int GenerateBinaryImage(byte[] buffer, int startIndex)
{
int length = BinaryLength;
buffer.ValidateParameters(startIndex, length);
Buffer.BlockCopy(LittleEndian.GetBytes((int)m_time.Value), 0, buffer, startIndex, 4);
Buffer.BlockCopy(LittleEndian.GetBytes((short)m_flags), 0, buffer, startIndex + 4, 2);
Buffer.BlockCopy(LittleEndian.GetBytes(m_value), 0, buffer, startIndex + 6, 4);
return length;
}
///
/// Compares the current object to .
///
/// Object against which the current object is to be compared.
///
/// Negative value if the current object is less than ,
/// Zero if the current object is equal to ,
/// Positive value if the current object is greater than .
///
public virtual int CompareTo(object obj)
{
ArchiveDataPoint other = obj as ArchiveDataPoint;
if (other == null)
{
return 1;
}
else
{
int result = m_historianID.CompareTo(other.HistorianID);
if (result != 0)
return result;
else
return m_time.CompareTo(other.Time);
}
}
///
/// Determines whether the current object is equal to .
///
/// Object against which the current object is to be compared for equality.
/// true if the current object is equal to ; otherwise false.
public override bool Equals(object obj)
{
return (CompareTo(obj) == 0);
}
///
/// Returns the text representation of object.
///
/// A value.
public override string ToString()
{
return ToString(null, null);
}
///
/// Returns the text representation of object in the specified .
///
/// Format of text output (I for ID, T for Time, V for Value, Q for Quality).
/// A value.
public virtual string ToString(string format)
{
return ToString(format, null);
}
///
/// Returns the text representation of object using the specified .
///
/// An that supplies culture-specific formatting information.
/// A value.
public virtual string ToString(IFormatProvider provider)
{
return ToString("", provider);
}
///
/// Returns the text representation of object in the specified
/// using the specified .
///
/// Format of text output (I for ID, T for Time, V for Value, Q for Quality).
/// An that supplies culture-specific formatting information.
/// A value.
public virtual string ToString(string format, IFormatProvider provider)
{
if (provider == null)
provider = CultureInfo.InvariantCulture;
switch (format.ToUpperInvariant())
{
case "I":
case "ID":
return m_historianID.ToString(provider);
case "T":
case "TIME":
return m_time.ToString();
case "U":
case "UNIXTIME":
return (new UnixTimeTag(m_time.ToDateTime())).Value.ToString("0.000", provider);
case "V":
case "VALUE":
return m_value.ToString(provider);
case "Q":
case "QUALITY":
return Quality.ToString();
case "N":
case "NAME":
if (Metadata != null)
return Metadata.Name;
return "";
case "D":
case "DESCRIPTION":
if (Metadata != null)
return Metadata.Description;
return "";
case "S":
case "SOURCE":
if (Metadata != null)
return Metadata.PlantCode;
return "";
case "S1":
case "SYNONYM1":
if (Metadata != null)
return Metadata.Synonym1;
return "";
case "S2":
case "SYNONYM2":
if (Metadata != null)
return Metadata.Synonym2;
return "";
case "S3":
case "SYNONYM3":
if (Metadata != null)
return Metadata.Synonym3;
return "";
default:
throw new FormatException("Invalid format identifier specified for ArchiveDataPoint: " + format);
}
}
///
/// Returns the hash code for the current object.
///
/// A 32-bit signed integer value.
public override int GetHashCode()
{
return m_historianID.GetHashCode();
}
#endregion
}
}