//******************************************************************************************************
// MeasurementKey.cs - Gbtc
//
// Copyright © 2010, 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 Eclipse Public License -v 1.0 (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/eclipse-1.0.php
//
// 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.
//
//******************************************************************************************************
//using GSF.Data;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
//using System.Data;
namespace GSF.TimeSeries
{
///
/// Represents a primary key for a measurement.
///
[Serializable()]
public struct MeasurementKey : IEquatable, IComparable, IComparable
{
#region [ Members ]
// Fields
private Guid m_signalID;
private uint m_id;
private string m_source;
private int m_hashCode;
#endregion
#region [ Constructors ]
///
/// Constructs a new given the specified parameters.
///
/// ID of associated signal, if defined.
/// Numeric ID of the measurement that this represents.
/// Source of the measurement that this represents (e.g., name of archive).
/// source cannot be null.
public MeasurementKey(Guid signalID, uint id, string source)
{
if (source.IsNullOrWhiteSpace())
throw new ArgumentNullException("source", "MeasurementKey source cannot be null or empty");
if (signalID == Guid.Empty)
{
ConcurrentDictionary keys = s_keyCache.GetOrAdd(source, key => new ConcurrentDictionary());
if (!keys.TryGetValue(id, out this))
{
m_id = id;
m_source = source.ToUpper();
// Generate a static runtime signal ID associated with this measurement key
m_signalID = Guid.NewGuid();
GenHashCode();
// Cache measurement
s_idCache[m_signalID] = this;
keys[id] = this;
}
}
else
{
if (!s_idCache.TryGetValue(signalID, out this))
{
m_id = id;
m_source = source.ToUpper();
m_signalID = signalID;
GenHashCode();
// Cache measurement based on signal ID
s_idCache[m_signalID] = this;
s_keyCache.GetOrAdd(source, kcf => new ConcurrentDictionary())[id] = this;
}
}
}
#endregion
#region [ Properties ]
///
/// Gets or sets ID of signal associated with this .
///
public Guid SignalID
{
get
{
return m_signalID;
}
set
{
if (m_signalID != value)
{
MeasurementKey old;
s_idCache.TryRemove(m_signalID, out old);
m_signalID = value;
GenHashCode();
// Update measurement caches
s_idCache[m_signalID] = this;
s_keyCache.GetOrAdd(m_source, kcf => new ConcurrentDictionary())[m_id] = this;
}
}
}
///
/// Gets or sets the numeric ID of this .
///
public uint ID
{
get
{
return m_id;
}
set
{
m_id = value;
}
}
///
/// Gets or sets the source of this .
///
///
/// This value is typically used to track the archive name in which the measurement, that this represents, is stored.
///
public string Source
{
get
{
return m_source;
}
set
{
m_source = value.ToUpper();
}
}
#endregion
#region [ Methods ]
///
/// Update the signal ID of this .
///
/// New based signal ID to update this with.
public void UpdateSignalID(Guid signalID)
{
SignalID = signalID;
}
///
/// Returns a that represents the current .
///
/// A that represents the current .
public override string ToString()
{
return string.Format("{0}:{1}", m_source, m_id);
}
///
/// Serves as a hash function for the current .
///
/// A hash code for the current .
public override int GetHashCode()
{
return m_hashCode;
}
///
/// 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(MeasurementKey other)
{
return (m_signalID == other.m_signalID);
}
///
/// 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.
///
/// is not a .
public override bool Equals(object obj)
{
// Can't use cast "as" on a structure...
if (obj is MeasurementKey)
return Equals((MeasurementKey)obj);
return false;
}
///
/// Compares the with another .
///
/// The to compare with the current .
/// A 32-bit signed integer that indicates the relative order of the objects being compared.
public int CompareTo(MeasurementKey other)
{
return m_signalID.CompareTo(other.m_signalID);
}
///
/// 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 a .
public int CompareTo(object obj)
{
// Can't use cast "as" on a structure...
if (obj is MeasurementKey)
return CompareTo((MeasurementKey)obj);
throw new ArgumentException("Object is not a MeasurementKey");
}
private void GenHashCode()
{
// We cache hash code during construction or after element value change to speed structure usage
m_hashCode = m_signalID.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 ==(MeasurementKey key1, MeasurementKey key2)
{
return key1.Equals(key2);
}
///
/// Compares two values for inequality.
///
/// A left hand operand.
/// A right hand operand.
/// A boolean representing the result.
public static bool operator !=(MeasurementKey key1, MeasurementKey key2)
{
return !key1.Equals(key2);
}
///
/// 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 >(MeasurementKey key1, MeasurementKey key2)
{
return key1.CompareTo(key2) > 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 >=(MeasurementKey key1, MeasurementKey key2)
{
return key1.CompareTo(key2) >= 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 <(MeasurementKey key1, MeasurementKey key2)
{
return key1.CompareTo(key2) < 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 <=(MeasurementKey key1, MeasurementKey key2)
{
return key1.CompareTo(key2) <= 0;
}
#endregion
#region [ Static ]
// Static Fields
private static readonly ConcurrentDictionary s_idCache = new ConcurrentDictionary();
private static readonly ConcurrentDictionary> s_keyCache = new ConcurrentDictionary>(StringComparer.InvariantCultureIgnoreCase);
///
/// Represents an undefined measurement key.
///
public static readonly MeasurementKey Undefined = CreateUndefinedMeasurementKey();
// Static Methods
public static MeasurementKey Parse(string value)
{
return Parse (value, default(Guid));
}
///
/// Converts the string representation of a into its value equivalent.
///
/// A string representing the to convert.
/// based signal ID associated with this , if defined.
/// A value equivalent the representation contained in .
/// The value is not in the correct format for a value.
public static MeasurementKey Parse(string value, Guid signalID)
{
MeasurementKey key;
if (TryParse(value, signalID, out key))
return key;
throw new FormatException("The value is not in the correct format for a MeasurementKey value");
}
///
/// Looks up the measurement key associated with the given signal ID.
///
/// The signal ID of the measurement key.
/// The measurement key associated with the given signal ID.
///
/// If no measurement key is found with the given signal ID, a measurement key is
/// returned with generic default values for and ,
/// but this key is not cached. In order to cache a measurement key, a Source and ID
/// must be provided, either via the
/// constructor or the method.
///
public static MeasurementKey LookupBySignalID(Guid signalID)
{
MeasurementKey key;
if (signalID == Guid.Empty)
{
key = Undefined;
}
else if (!s_idCache.TryGetValue(signalID, out key))
{
key = new MeasurementKey();
key.m_signalID = signalID;
key.m_source = "__";
key.m_id = 0;
}
return key;
}
///
/// Attempts to convert the string representation of a into its value equivalent.
///
/// A string representing the to convert.
/// based signal ID associated with this , if defined.
/// Output in which to stored parsed value.
/// A true if representation contained in could be parsed; otherwise false.
public static bool TryParse(string value, Guid signalID, out MeasurementKey key)
{
// Check cache for an existing measurment key definition if signal ID is defined
if (signalID != Guid.Empty && s_idCache.TryGetValue(signalID, out key))
return true;
if (!string.IsNullOrEmpty(value))
{
try
{
string[] elem = value.Split(':');
if (elem.Length == 2)
{
uint id;
if (uint.TryParse(elem[1].Trim(), out id))
{
key = new MeasurementKey(signalID, id, elem[0].Trim());
return true;
}
}
}
catch
{
// Any exceptions result in failure to parse
}
}
key = default(MeasurementKey);
return false;
}
// public static void EstablishDefaultCache(IDbConnection connection, Type adapterType)
// {
// EstablishDefaultCache(connection, adapterType, "ActiveMeasurement");
// }
//
// ///
// /// Establish default cache.
// ///
// /// The database connection.
// /// The database adapter type.
// /// Measurement table name used to load measurement key cache.
// ///
// /// Source tables are expected to have at least the following fields:
// ///
// /// ID NVARCHAR Measurement key formatted as: ArchiveSource:PointID
// /// SignalID GUID Unique identification for measurement
// ///
// ///
// public static void EstablishDefaultCache(IDbConnection connection, Type adapterType, string measurementTable)
// {
// MeasurementKey key;
//
// // Establish default measurement key cache
// foreach (DataRow measurement in connection.RetrieveData(adapterType, string.Format("SELECT ID, SignalID FROM {0}", measurementTable)).Rows)
// {
// TryParse(measurement["ID"].ToString(), measurement["SignalID"].ToNonNullString(Guid.Empty.ToString()).ConvertToType(), out key);
// }
// }
///
/// Creates the undefined measurement key. Used to initialize .
///
/// The undefined measurement key.
private static MeasurementKey CreateUndefinedMeasurementKey()
{
MeasurementKey key = new MeasurementKey();
key.m_signalID = Guid.Empty;
key.m_source = "__";
key.m_id = uint.MaxValue;
key.m_hashCode = int.MaxValue;
s_keyCache.GetOrAdd("__", kcf => new ConcurrentDictionary())[uint.MaxValue] = key;
return key;
}
#endregion
}
///
/// Represents an instance of the for a .
///
public class MeasurementKeyComparer : IEqualityComparer
{
#region [ Methods ]
///
/// Determines whether the specified objects are equal.
///
/// The first to compare.
/// The second to compare.
/// true if the specified objects are equal; otherwise, false.
public bool Equals(MeasurementKey x, MeasurementKey y)
{
return x.Equals(y);
}
///
/// Returns a hash code for the specified object.
///
/// The for which a hash code is to be returned.
/// A hash code for the specified object.
public int GetHashCode(MeasurementKey obj)
{
return obj.GetHashCode();
}
#endregion
#region [ Static ]
private static MeasurementKeyComparer s_comparer;
///
/// Returns the default instance of the class.
///
public static MeasurementKeyComparer Default
{
get
{
if (s_comparer == null)
s_comparer = new MeasurementKeyComparer();
return s_comparer;
}
}
#endregion
}
}