//****************************************************************************************************** // IMeasurementExtensions.cs - Gbtc // // Copyright © 2016, 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://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. // //****************************************************************************************************** using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Linq; using GSF.Collections; namespace GSF.TimeSeries { /// /// Defines static extension functions for implementations. /// /// /// These helper functions map to the previously defined corresponding properties to help with the transition of . /// public static class IMeasurementExtensions { private static readonly ConcurrentDictionary s_signalTypeIDs = new ConcurrentDictionary(); private static readonly ConcurrentDictionary s_signalTypeIDCache = new ConcurrentDictionary(); /// /// Returns true if is not set. /// /// instance to test. /// true if is not set. public static bool ValueQualityIsGood(this IMeasurement measurement) => (measurement.StateFlags & MeasurementStateFlags.BadData) == 0; /// /// Returns true if is not set. /// /// instance to test. /// true if is not set. public static bool TimestampQualityIsGood(this IMeasurement measurement) => (measurement.StateFlags & MeasurementStateFlags.BadTime) == 0; /// /// Gets derived quality flags from a set of source measurements. /// /// Source measurements. /// Derived quality flags. public static MeasurementStateFlags DeriveQualityFlags(this IEnumerable measurements) { MeasurementStateFlags derivedQuality = MeasurementStateFlags.Normal; MeasurementStateFlags[] stateFlags = measurements.Select(measurement => measurement.StateFlags).ToArray(); bool dataQualityIsBad(MeasurementStateFlags flags) => (flags & MeasurementStateFlags.BadData) > 0; bool timeQualityIsBad(MeasurementStateFlags flags) => (flags & MeasurementStateFlags.BadTime) > 0; if (stateFlags.Any(dataQualityIsBad)) derivedQuality |= MeasurementStateFlags.BadData; if (stateFlags.Any(timeQualityIsBad)) derivedQuality |= MeasurementStateFlags.BadTime; return derivedQuality; } /// /// Returns true if is set. /// /// instance to test. /// true if is not set. public static bool IsDiscarded(this IMeasurement measurement) => (measurement.StateFlags & MeasurementStateFlags.DiscardedValue) > 0; /// /// Returns true if is set. /// /// instance to test. /// true if is not set. public static bool IsCalculated(this IMeasurement measurement) => (measurement.StateFlags & MeasurementStateFlags.CalculatedValue) > 0; /// /// Returns the measurement ID if defined, otherwise the run-time signal ID associated with the measurement key. /// /// instance to test. /// Measurement ID if defined, otherwise the run-time signal ID associated with the measurement key. public static Guid RuntimeSignalID(this IMeasurement measurement) => measurement.ID; /// /// Returns the values of a enumeration. /// /// enumeration to convert. /// values of the enumeration. public static MeasurementKey[] MeasurementKeys(this IEnumerable measurements) => measurements == null ? new MeasurementKey[0] : measurements.Select(m => m.Key).ToArray(); /// /// Gets a unique (run-time only) signal type ID for the given useful for sorting. /// /// to obtain signal type for. /// that contains measurement metadata. /// Unique (run-time only) signal type ID for the given . // This uses string hash code over something like item count + 1 to generate a unique run-time ID of the signal type since chances of a hash code collision // over a small set of signal types is much smaller than the possibility of duplicates due to possible race conditions when using item count + 1 public static int GetSignalType(this IMeasurement measurement, DataSet dataSource) => s_signalTypeIDCache.GetOrAdd(measurement.ID, signalID => s_signalTypeIDs.GetOrAdd(LookupSignalType(signalID, dataSource), signalType => signalType.GetHashCode())); /// /// Sets the tag name for a . /// /// to create new for. /// New tag name value to assign to measurement's metadata. public static void SetTagName(this IMeasurement measurement, string tagName) => measurement.Metadata = measurement.Metadata.ChangeTagName(tagName); /// /// Sets the associated for a . /// /// to create new for. /// New measurement key value to assign to measurement's metadata. public static void SetKey(this IMeasurement measurement, MeasurementKey key) => measurement.Metadata = measurement.Metadata.ChangeKey(key); /// /// Sets the adder (i.e., "b" of y = mx + b) for a . /// /// to create new for. /// New adder value to assign to measurement's metadata. public static void SetAdder(this IMeasurement measurement, double adder) => measurement.Metadata = measurement.Metadata.ChangeAdder(adder); /// /// Sets the multiplier (i.e., "m" of y = mx + b) for a . /// /// to create new for. /// New multiplier value to assign to measurement's metadata. public static void SetMultiplier(this IMeasurement measurement, double multiplier) => measurement.Metadata = measurement.Metadata.ChangeMultiplier(multiplier); /// /// Gets a single column of measurement data from a two dimensional data window. /// /// Target data window. /// Index of column to return. /// All values from in . public static IMeasurement[] GetDataColumn(this IMeasurement[,] dataWindow, int columnIndex) => dataWindow.GetColumn(columnIndex).ToArray(); // Lookup signal type for given measurement ID private static string LookupSignalType(Guid signalID, DataSet dataSource) { try { DataRow[] filteredRows = dataSource.Tables["ActiveMeasurements"].Select($"SignalID = '{signalID}'"); return filteredRows.Length > 0 ? filteredRows[0]["SignalType"].ToString().ToUpper().Trim() : "NONE"; } catch { return "NONE"; } } } }