//******************************************************************************************************
// Measurement.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:
// ----------------------------------------------------------------------------------------------------
// 09/02/2010 - J. Ritchie Carroll
// Generated original version of source code.
// 04/14/2011 - J. Ritchie Carroll
// Added received and published timestamps for measurements.
// 12/20/2012 - Starlynn Danyelle Gilliam
// Modified Header.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using GSF.Collections;
using GSF.Units;
namespace GSF.TimeSeries
{
///
/// Represents a basic measurement implementation.
///
[Serializable]
public class Measurement : IMeasurement
{
#region [ Members ]
// Fields
private double m_value;
private Ticks m_timestamp;
[NonSerialized]
private ShortTime m_lifespan;
private MeasurementMetadata m_metadata;
private MeasurementStateFlags m_stateFlags;
#endregion
#region [ Constructors ]
///
/// Constructs a new using default settings.
///
public Measurement()
{
m_metadata = MeasurementMetadata.Undefined;
m_lifespan = ShortTime.Now;
}
#endregion
#region [ Properties ]
///
/// Gets the based signal ID of the implementation.
///
public Guid ID => m_metadata.Key.SignalID;
///
/// Gets or sets the raw measurement value that is not offset by and .
///
/// Raw value of this (i.e., value that is not offset by and ).
public double Value
{
get
{
return m_value;
}
set
{
m_value = value;
}
}
///
/// Gets or sets exact timestamp, in ticks, of the data represented by this .
///
///
/// The value of this property represents the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001.
///
public Ticks Timestamp
{
get
{
return m_timestamp;
}
set
{
m_timestamp = value;
}
}
///
/// Gets or sets associated metadata values for the implementation.
///
public MeasurementMetadata Metadata
{
get
{
return m_metadata;
}
set
{
m_metadata = value;
}
}
///
/// Gets the primary of this implementation.
///
public MeasurementKey Key => m_metadata.Key;
///
/// Gets the text based tag name of this implementation.
///
public string TagName => m_metadata.TagName;
///
/// Gets an offset to add to the measurement value. This defaults to 0.0.
///
[DefaultValue(0.0)]
public double Adder => m_metadata.Adder;
///
/// Gets a multiplicative offset to apply to the measurement value. This defaults to 1.0.
///
[DefaultValue(1.0)]
public double Multiplier => m_metadata.Multiplier;
///
/// Gets the adjusted numeric value of this measurement, taking into account the specified and offsets.
///
///
/// Note that returned value will be offset by and .
///
/// offset by and (i.e., * + ).
public double AdjustedValue => Value * Multiplier + Adder;
///
/// Gets or sets associated with this .
///
public MeasurementStateFlags StateFlags
{
get
{
return m_stateFlags;
}
set
{
m_stateFlags = value;
}
}
///
/// Gets function used to apply a down-sampling filter over a sequence of values.
///
public MeasurementValueFilterFunction MeasurementValueFilter => m_metadata.MeasurementValueFilter;
///
/// Gets the life-span of this since its creation.
///
public ShortTime Lifespan => m_lifespan;
///
/// Gets timestamp, in ticks, of when this was created.
///
public Ticks CreatedTimestamp => m_lifespan.UtcTime.Ticks;
// Big-Endian binary value interpretation
BigBinaryValue ITimeSeriesValue.Value
{
get
{
return Value;
}
set
{
switch (value.TypeCode)
{
case TypeCode.Byte:
Value = (byte)value;
break;
case TypeCode.SByte:
Value = (sbyte)value;
break;
case TypeCode.Int16:
Value = (short)value;
break;
case TypeCode.UInt16:
Value = (ushort)value;
break;
case TypeCode.Int32:
Value = (int)value;
break;
case TypeCode.UInt32:
Value = (uint)value;
break;
case TypeCode.Int64:
Value = (long)value;
break;
case TypeCode.UInt64:
Value = (ulong)value;
break;
case TypeCode.Single:
Value = (float)value;
break;
case TypeCode.Double:
Value = (double)value;
break;
//case TypeCode.Boolean:
// break;
//case TypeCode.Char:
// break;
//case TypeCode.DateTime:
// break;
//case TypeCode.Decimal:
// break;
//case TypeCode.String:
// m_value = double.Parse(value);
// break;
default:
Value = value;
break;
}
}
}
#endregion
#region [ Methods ]
///
/// Returns a that represents the current .
///
/// A that represents the current .
public override string ToString()
{
return ToString(this);
}
///
/// Determines whether the specified is equal to the current .
///
/// The to compare with the current .
///
/// true if the specified is equal to the current ;
/// otherwise, false.
///
public bool Equals(ITimeSeriesValue other)
{
return CompareTo(other) == 0;
}
///
/// Determines whether the specified is equal to the current .
///
/// The to compare with the current .
///
/// true if the specified is equal to the current ;
/// otherwise, false.
///
public override bool Equals(object obj)
{
ITimeSeriesValue other = obj as ITimeSeriesValue;
if ((object)other != null)
return Equals(other);
return false;
}
///
/// Compares the with an .
///
/// The to compare with the current .
/// A 32-bit signed integer that indicates the relative order of the objects being compared.
/// Measurement implementations should compare by hash code.
public int CompareTo(ITimeSeriesValue other)
{
if ((object)other != null)
return GetHashCode().CompareTo(other.GetHashCode());
return 1;
}
///
/// Compares the with the specified .
///
/// The to compare with the current .
/// A 32-bit signed integer that indicates the relative order of the objects being compared.
/// is not an .
/// Measurement implementations should compare by hash code.
public int CompareTo(object obj)
{
ITimeSeriesValue other = obj as ITimeSeriesValue;
if ((object)other != null)
return CompareTo(other);
throw new ArgumentException("Measurement can only be compared with other measurements or time-series values");
}
///
/// Serves as a hash function for the current .
///
/// A hash code for the current .
/// Hash code based on value of measurement.
public override int GetHashCode()
{
return Key.GetHashCode();
}
#endregion
#region [ Operators ]
///
/// Compares two values for equality.
///
/// A left hand operand.
/// A right hand operand.
/// A boolean representing the result.
public static bool operator ==(Measurement measurement1, Measurement measurement2)
{
return (object)measurement1 != null && measurement1.Equals(measurement2);
}
///
/// Compares two values for inequality.
///
/// A left hand operand.
/// A right hand operand.
/// A boolean representing the result.
public static bool operator !=(Measurement measurement1, Measurement measurement2)
{
return (object)measurement1 != null && !measurement1.Equals(measurement2);
}
///
/// Returns true if left value is greater than right value.
///
/// A left hand operand.
/// A right hand operand.
/// A boolean representing the result.
public static bool operator >(Measurement measurement1, Measurement measurement2)
{
return measurement1.CompareTo(measurement2) > 0;
}
///
/// Returns true if left value is greater than or equal to right value.
///
/// A left hand operand.
/// A right hand operand.
/// A boolean representing the result.
public static bool operator >=(Measurement measurement1, Measurement measurement2)
{
return measurement1.CompareTo(measurement2) >= 0;
}
///
/// Returns true if left value is less than right value.
///
/// A left hand operand.
/// A right hand operand.
/// A boolean representing the result.
public static bool operator <(Measurement measurement1, Measurement measurement2)
{
return measurement1.CompareTo(measurement2) < 0;
}
///
/// Returns true if left value is less than or equal to right value.
///
/// A left hand operand.
/// A right hand operand.
/// A boolean representing the result.
public static bool operator <=(Measurement measurement1, Measurement measurement2)
{
return measurement1.CompareTo(measurement2) <= 0;
}
#endregion
#region [ Static ]
// Static Fields
///
/// Represents an undefined measurement.
///
public static readonly Measurement Undefined = new Measurement
{
Metadata = MeasurementMetadata.Undefined
};
// Static Methods
///
/// Creates a copy of the specified measurement.
///
/// Specified measurement to clone.
/// A copy of the object.
public static Measurement Clone(IMeasurement measurementToClone)
{
return new Measurement
{
Metadata = measurementToClone.Metadata,
Value = measurementToClone.Value,
Timestamp = measurementToClone.Timestamp,
StateFlags = measurementToClone.StateFlags
};
}
///
/// Creates a copy of the specified measurement using a new timestamp.
///
/// Specified measurement to clone.
/// New timestamp, in ticks, for cloned measurement.
/// A copy of the object.
public static Measurement Clone(IMeasurement measurementToClone, Ticks timestamp)
{
return new Measurement
{
Metadata = measurementToClone.Metadata,
Value = measurementToClone.Value,
Timestamp = timestamp,
StateFlags = measurementToClone.StateFlags
};
}
///
/// Creates a copy of the specified measurement using a new value and timestamp.
///
/// Specified measurement to clone.
/// New value for cloned measurement.
/// New timestamp, in ticks, for cloned measurement.
/// A copy of the object.
public static Measurement Clone(IMeasurement measurementToClone, double value, Ticks timestamp)
{
return new Measurement
{
Metadata = measurementToClone.Metadata,
Value = value,
Timestamp = timestamp,
StateFlags = measurementToClone.StateFlags
};
}
///
/// Returns a that represents the specified .
///
/// to convert to a representation.
/// Set to true to include measurement's tag name, if defined; otherwise set to false.
/// A that represents the specified .
public static string ToString(IMeasurement measurement, bool includeTagName = true)
{
if ((object)measurement == null)
return "Undefined";
string tagName = measurement.TagName;
string keyText = measurement.Key.ToString();
if (includeTagName && !string.IsNullOrWhiteSpace(tagName))
return $"{tagName} [{keyText}]";
return keyText;
}
///
/// Calculates an average of the specified sequence of values.
///
/// Sequence of values over which to run calculation.
/// Average of the specified sequence of values.
public static double AverageValueFilter(IEnumerable source)
{
return source.Select(m => m.Value).Average();
}
///
/// Returns the majority value of the specified sequence of values.
///
/// Sequence of values over which to run calculation.
/// Majority value of the specified sequence of values.
public static double MajorityValueFilter(IEnumerable source)
{
return source.Select(m => m.Value).Majority();
}
///
/// Calculates an average of the specified sequence of phase angle values.
///
/// Sequence of values over which to run calculation.
/// Average of the specified sequence of phase angle values.
///
/// Phase angles wrap, so this algorithm takes the wrapping into account when calculating the average.
///
public static double AverageAngleValueFilter(IEnumerable source)
{
return source.Select(m => Angle.FromDegrees(m.Value)).Average().ToDegrees();
}
#endregion
}
}