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