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