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