486 lines · 17.7 KB · C#
RawDownload
//******************************************************************************************************//  ImmediateMeasurements.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 System;using System.Collections;using System.Collections.Concurrent;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Data;using System.Linq; namespace GSF.TimeSeries{    /// <summary>    /// Represents the absolute latest measurement values received by a <see cref="ConcentratorBase"/> implementation.    /// </summary>    public class ImmediateMeasurements : IEnumerable<TemporalMeasurement>, IDisposable    {        #region [ Members ]         // Fields        private ConcentratorBase m_parent;        private ConcurrentDictionary<Guid, TemporalMeasurement> m_measurements;        private ConcurrentDictionary<string, List<Guid>> m_taggedMeasurements;        private Func<Ticks> m_realTimeFunction;        private double m_lagTime;                           // Allowed past time deviation tolerance, in seconds        private double m_leadTime;                          // Allowed future time deviation tolerance, in seconds        private bool m_disposed;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new instance of the <see cref="ImmediateMeasurements"/> class.        /// </summary>        public ImmediateMeasurements()        {            m_measurements = new ConcurrentDictionary<Guid, TemporalMeasurement>();            m_taggedMeasurements = new ConcurrentDictionary<string, List<Guid>>();            m_realTimeFunction = () => PrecisionTimer.UtcNow.Ticks;        }         internal ImmediateMeasurements(ConcentratorBase parent)            : this()        {            m_parent = parent;             if (m_parent != null)            {                m_parent.LagTimeUpdated += OnLagTimeUpdated;                m_parent.LeadTimeUpdated += OnLeadTimeUpdated;                m_realTimeFunction = () => m_parent.RealTime;            }        }         /// <summary>        /// Releases the unmanaged resources before the <see cref="ImmediateMeasurements"/> object is reclaimed by <see cref="GC"/>.        /// </summary>        ~ImmediateMeasurements()        {            Dispose(false);        }         #endregion         #region [ Properties ]         /// <summary>We retrieve adjusted measurement values within time tolerance of concentrator real-time.</summary>        /// <param name="id">A <see cref="Guid"/> representing the measurement ID.</param>        /// <returns>A <see cref="Double"/> representing the adjusted measurement value.</returns>        public double this[Guid id]        {            get            {                return Measurement(id).GetAdjustedValue(m_realTimeFunction());            }        }         /// <summary>Returns collection of measurement ID's.</summary>        public ICollection<Guid> MeasurementIDs        {            get            {                return m_measurements.Keys;            }        }         /// <summary>Returns ID collection for measurement tags.</summary>        public ICollection<string> Tags        {            get            {                return m_taggedMeasurements.Keys;            }        }         /// <summary>Returns the minimum value of all measurements.</summary>        /// <remarks>This is only useful if all measurements represent the same type of measurement.</remarks>        public double Minimum        {            get            {                double minValue = double.MaxValue;                double measurement;                 foreach (Guid id in m_measurements.Keys)                {                    measurement = this[id];                    if (!double.IsNaN(measurement))                    {                        if (measurement < minValue)                        {                            minValue = measurement;                        }                    }                }                 return minValue;            }        }         /// <summary>Returns the maximum value of all measurements.</summary>        /// <remarks>This is only useful if all measurements represent the same type of measurement.</remarks>        public double Maximum        {            get            {                double maxValue = double.MinValue;                double measurement;                 foreach (Guid id in m_measurements.Keys)                {                    measurement = this[id];                    if (!double.IsNaN(measurement))                    {                        if (measurement > maxValue)                        {                            maxValue = measurement;                        }                    }                }                 return maxValue;            }        }         /// <summary>        /// Gets or sets function to return real-time.        /// </summary>        public Func<Ticks> RealTimeFunction        {            get            {                return m_realTimeFunction;            }            set            {                m_realTimeFunction = value ?? (() => PrecisionTimer.UtcNow.Ticks);            }        }         /// <summary>        /// Gets or sets the allowed past time deviation tolerance, in seconds (can be subsecond).        /// </summary>        /// <remarks>        /// <para>Defines the time sensitivity to past measurement timestamps.</para>        /// <para>The number of seconds allowed before assuming a measurement timestamp is too old.</para>        /// </remarks>        /// <exception cref="ArgumentOutOfRangeException">LagTime must be greater than zero, but it can be less than one.</exception>        public double LagTime        {            get            {                return m_lagTime;            }            set            {                if (value <= 0)                    throw new ArgumentOutOfRangeException("value", "LagTime must be greater than zero, but it can be less than one");                 m_lagTime = value;                 foreach (Guid id in m_measurements.Keys)                {                    Measurement(id).LagTime = m_lagTime;                }            }        }         /// <summary>        /// Gets or sets the allowed future time deviation tolerance, in seconds (can be subsecond).        /// </summary>        /// <remarks>        /// <para>Defines the time sensitivity to future measurement timestamps.</para>        /// <para>The number of seconds allowed before assuming a measurement timestamp is too advanced.</para>        /// </remarks>        /// <exception cref="ArgumentOutOfRangeException">LeadTime must be greater than zero, but it can be less than one.</exception>        public double LeadTime        {            get            {                return m_leadTime;            }            set            {                if (value <= 0)                    throw new ArgumentOutOfRangeException("value", "LeadTime must be greater than zero, but it can be less than one");                 m_leadTime = value;                 foreach (Guid id in m_measurements.Keys)                {                    Measurement(id).LeadTime = m_leadTime;                }            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Releases all the resources used by the <see cref="ImmediateMeasurements"/> object.        /// </summary>        public void Dispose()        {            Dispose(true);            GC.SuppressFinalize(this);        }         /// <summary>        /// Releases the unmanaged resources used by the <see cref="ImmediateMeasurements"/> object and optionally releases the managed resources.        /// </summary>        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>        protected virtual void Dispose(bool disposing)        {            if (!m_disposed)            {                try                {                    if (disposing)                    {                        if (m_parent != null)                        {                            m_parent.LagTimeUpdated -= OnLagTimeUpdated;                            m_parent.LeadTimeUpdated -= OnLagTimeUpdated;                        }                        m_parent = null;                         if (m_measurements != null)                            m_measurements.Clear();                         m_measurements = null;                         if (m_taggedMeasurements != null)                            m_taggedMeasurements.Clear();                         m_taggedMeasurements = null;                    }                }                finally                {                    m_disposed = true;  // Prevent duplicate dispose.                }            }        }         /// <summary>Returns measurement list of specified tag, if it exists.</summary>        /// <param name="tag">A <see cref="String"/> that indicates the tag to use.</param>        /// <returns>A collection of measurement keys.</returns>        public ReadOnlyCollection<Guid> TaggedMeasurementKeys(string tag)        {            return new ReadOnlyCollection<Guid>(m_taggedMeasurements[tag]);        }         /// <summary>Store new measurement.</summary>        /// <param name="newMeasurement">New measurement value to update.</param>        /// <remarks>Value is only stored if it is newer than the cached value.</remarks>        public void UpdateMeasurementValue(IMeasurement newMeasurement)        {            TemporalMeasurement measurement = Measurement(newMeasurement);             // Set new value updating state flags if value was updated...            if (measurement.SetValue(newMeasurement.Timestamp, newMeasurement.Value))                measurement.StateFlags = newMeasurement.StateFlags;        }         /// <summary>Retrieves the specified immediate temporal measurement, creating it if needed.</summary>        /// <param name="id"><see cref="Guid"/> based signal ID of measurement.</param>        /// <returns>A <see cref="TemporalMeasurement"/> object.</returns>        public TemporalMeasurement Measurement(Guid id)        {            return m_measurements.GetOrAdd(id, key => new TemporalMeasurement(m_lagTime, m_leadTime)            {                ID = key            });        }         /// <summary>Retrieves the specified immediate temporal measurement, creating it if needed.</summary>        /// <param name="measurement">Source <see cref="IMeasurement"/> value.</param>        /// <returns>A <see cref="TemporalMeasurement"/> object.</returns>        public TemporalMeasurement Measurement(IMeasurement measurement)        {            return m_measurements.GetOrAdd(measurement.RuntimeSignalID(), key => new TemporalMeasurement(m_lagTime, m_leadTime)            {                ID = key            });        }         /// <summary>        /// Clears the existing measurement cache.        /// </summary>        public void ClearMeasurementCache()        {            m_measurements.Clear();        }         /// <summary>Defines tagged measurements from a data table.</summary>        /// <remarks>Expects <see cref="String"/> based tag field to be aliased as "Tag" and <see cref="Guid"/> based measurement ID field to be aliased as "ID".</remarks>        /// <param name="taggedMeasurements">A <see cref="DataTable"/> to use for defining the tagged measurements.</param>        public void DefineTaggedMeasurements(DataTable taggedMeasurements)        {            foreach (DataRow row in taggedMeasurements.Rows)            {                AddTaggedMeasurement(row["Tag"].ToNonNullString("_tag_"), row["ID"].ToNonNullString(Guid.Empty.ToString()).ConvertToType<Guid>());            }        }         /// <summary>Associates a new measurement ID with a tag, creating the new tag if needed.</summary>        /// <remarks>Allows you to define "grouped" points so you can aggregate certain measurements.</remarks>        /// <param name="tag">A <see cref="String"/> to represent the key.</param>        /// <param name="id">A <see cref="Guid"/> ID to associate with the tag.</param>        public void AddTaggedMeasurement(string tag, Guid id)        {            // Get tag's measurement list            List<Guid> measurements = m_taggedMeasurements.GetOrAdd(tag, new List<Guid>());             if (measurements.BinarySearch(id) < 0)            {                measurements.Add(id);                measurements.Sort();            }        }         /// <summary>Calculates an average of all measurements.</summary>        /// <remarks>This is only useful if all measurements represent the same type of measurement.</remarks>        /// <param name="count">An <see cref="Int32"/> value to get the count of values averaged.</param>        /// <returns>A <see cref="Double"/> value representing the average of the measurements.</returns>        public double CalculateAverage(ref int count)        {            double measurement;            double total = 0.0D;             foreach (Guid id in m_measurements.Keys)            {                measurement = this[id];                if (!double.IsNaN(measurement))                {                    total += measurement;                    count++;                }            }             return total / count;        }         /// <summary>Calculates an average of all measurements associated with the specified tag.</summary>        /// <param name="count">An <see cref="Int32"/> value to get the count of values averaged.</param>        /// <param name="tag">The type of measurements to average.</param>        /// <returns>A <see cref="Double"/> value representing the average of the tags.</returns>        public double CalculateTagAverage(string tag, ref int count)        {            double measurement;            double total = 0.0D;             foreach (Guid id in m_taggedMeasurements[tag])            {                measurement = this[id];                if (!double.IsNaN(measurement))                {                    total += measurement;                    count++;                }            }             return total / count;        }         /// <summary>Returns the minimum value of all measurements associated with the specified tag.</summary>        /// <returns>A <see cref="Double"/> value representing the tag minimum.</returns>        /// <param name="tag">The tag group to evaluate.</param>        public double TagMinimum(string tag)        {            double minValue = double.MaxValue;            double measurement;             foreach (Guid id in m_taggedMeasurements[tag])            {                measurement = this[id];                if (!double.IsNaN(measurement))                {                    if (measurement < minValue)                    {                        minValue = measurement;                    }                }            }             return minValue;        }         /// <summary>Returns the maximum value of all measurements associated with the specified tag.</summary>        /// <returns>A <see cref="Double"/> value representing the tag maximum.</returns>        /// <param name="tag">The tag group to evaluate.</param>        public double TagMaximum(string tag)        {            double maxValue = double.MinValue;            double measurement;             foreach (Guid id in m_taggedMeasurements[tag])            {                measurement = this[id];                if (!double.IsNaN(measurement))                {                    if (measurement > maxValue)                    {                        maxValue = measurement;                    }                }            }             return maxValue;        }         /// <summary>        /// Updates the tracked temporal measurements lag time.        /// </summary>        /// <param name="lagTime">New lag time.</param>        protected void OnLagTimeUpdated(double lagTime)        {            this.LagTime = lagTime;        }         /// <summary>        /// Updates the tracked temporal measurements lead time.        /// </summary>        /// <param name="leadTime">New lead time.</param>        protected void OnLeadTimeUpdated(double leadTime)        {            this.LeadTime = leadTime;        }         IEnumerator<TemporalMeasurement> IEnumerable<TemporalMeasurement>.GetEnumerator()        {            return m_measurements.Values.ToList().GetEnumerator();        }         IEnumerator IEnumerable.GetEnumerator()        {            return m_measurements.Values.ToList().GetEnumerator();        }         #endregion    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t