277 lines · 11.8 KB · C#
RawDownload
//******************************************************************************************************
//  TrackingFrame.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.Threading;
using System.Collections.Concurrent;
using System.Collections.Generic;

namespace GSF.TimeSeries
{
    /// <summary>
    /// <see cref="IFrame"/> container used to track <see cref="IMeasurement"/> values for downsampling.
    /// </summary>
    internal class TrackingFrame
    {
        #region [ Members ]

        // Fields
        private IFrame m_sourceFrame;
        private long m_timestamp;
        private DownsamplingMethod m_downsamplingMethod;
        private ConcurrentDictionary<MeasurementKey, List<IMeasurement>> m_measurements;
        private ReaderWriterSpinLock m_frameLock;
        private long m_derivedMeasurements;

        #endregion

        #region [ Constructors ]

        /// <summary>
        /// Constructs a new <see cref="TrackingFrame"/> given the specified parameters.
        /// </summary>
        /// <param name="sourceFrame">Source <see cref="IFrame"/> to track.</param>
        /// <param name="downsamplingMethod"><see cref="DownsamplingMethod"/> to apply.</param>
        public TrackingFrame(IFrame sourceFrame, DownsamplingMethod downsamplingMethod)
        {
            m_sourceFrame = sourceFrame;
            m_timestamp = sourceFrame.Timestamp;
            m_downsamplingMethod = downsamplingMethod;
            m_frameLock = new ReaderWriterSpinLock();

            if (downsamplingMethod != DownsamplingMethod.LastReceived)
                m_measurements = new ConcurrentDictionary<MeasurementKey, List<IMeasurement>>();
        }

        #endregion

        #region [ Properties ]

        /// <summary>
        /// Gets instance of <see cref="IFrame"/> being tracked.
        /// </summary>
        public IFrame SourceFrame
        {
            get
            {
                return m_sourceFrame;
            }
        }

        /// <summary>
        /// Gets timestamp of <see cref="IFrame"/> being tracked.
        /// </summary>
        public long Timestamp
        {
            get
            {
                return m_timestamp;
            }
        }

        /// <summary>
        /// Total number of measurements downsampled by <see cref="TrackingFrame"/>.
        /// </summary>
        public long DownsampledMeasurements
        {
            get
            {
                // Only measurements that came in after initial sorts were downsampled...
                return m_derivedMeasurements - m_sourceFrame.SortedMeasurements;
            }
        }

        /// <summary>
        /// Gets the <see cref="TrackingFrame"/> locking primitive.
        /// </summary>
        public ReaderWriterSpinLock Lock
        {
            get
            {
                return m_frameLock;
            }
        }

        #endregion

        #region [ Methods ]

        /// <summary>
        /// Derives measurement value, downsampling if needed.
        /// </summary>
        /// <param name="measurement">New <see cref="IMeasurement"/> value.</param>
        /// <returns>New derived <see cref="IMeasurement"/> value, or null if value should not be assigned to <see cref="IFrame"/>.</returns>
        public IMeasurement DeriveMeasurementValue(IMeasurement measurement)
        {
            IMeasurement derivedMeasurement;
            List<IMeasurement> values;

            switch (m_downsamplingMethod)
            {
                case DownsamplingMethod.LastReceived:
                    // Keep track of total number of derived measurements
                    m_derivedMeasurements++;

                    // This is the simplest case, just apply latest value
                    return measurement;
                case DownsamplingMethod.Closest:
                    // Get tracked measurement values
                    if (m_measurements.TryGetValue(measurement.Key, out values))
                    {
                        if ((object)values != null && values.Count > 0)
                        {
                            // Get first tracked value (should only be one for "Closest")
                            derivedMeasurement = values[0];

                            if ((object)derivedMeasurement != null)
                            {
                                // Determine if new measurement's timestamp is closer to frame
                                if (measurement.Timestamp < derivedMeasurement.Timestamp && measurement.Timestamp >= m_timestamp)
                                {
                                    // This measurement came in out-of-order and is closer to frame timestamp, so 
                                    // we sort this measurement instead of the original
                                    values[0] = measurement;

                                    // Keep track of total number of derived measurements
                                    m_derivedMeasurements++;

                                    return measurement;
                                }

                                // Prior measurement is closer to frame than new one
                                return null;
                            }
                        }
                    }

                    // No prior measurement exists, track this initial one
                    values = new List<IMeasurement> { measurement };
                    m_measurements[measurement.Key] = values;

                    // Keep track of total number of derived measurements
                    m_derivedMeasurements++;

                    return measurement;
                case DownsamplingMethod.Filtered:
                    // Get tracked measurement values
                    if (m_measurements.TryGetValue(measurement.Key, out values))
                    {
                        if ((object)values != null && values.Count > 0)
                        {
                            // Get first tracked value - we clone the measurement since we are updating its value
                            derivedMeasurement = Measurement.Clone(values[0]);

                            if ((object)derivedMeasurement != null)
                            {
                                // Get function defined for measurement value filtering
                                MeasurementValueFilterFunction measurementValueFilter = derivedMeasurement.MeasurementValueFilter;

                                // Default to average value filter if none is specified
                                if (measurementValueFilter == null)
                                    measurementValueFilter = Measurement.AverageValueFilter;

                                // Add new measurement to tracking collection
                                if ((object)measurement != null)
                                    values.Add(measurement);

                                // Perform filter calculation as specified by device measurement
                                if (values.Count > 1)
                                {
                                    derivedMeasurement.Value = measurementValueFilter(values);

                                    // Keep track of total number of derived measurements
                                    m_derivedMeasurements++;

                                    return derivedMeasurement;
                                }

                                // No change from existing measurement
                                return null;
                            }
                        }
                    }

                    // No prior measurement exists, track this initial one
                    values = new List<IMeasurement> { measurement };
                    m_measurements[measurement.Key] = values;

                    // Keep track of total number of derived measurements
                    m_derivedMeasurements++;

                    return measurement;
                case DownsamplingMethod.BestQuality:
                    // Get tracked measurement values
                    if (m_measurements.TryGetValue(measurement.Key, out values))
                    {
                        if ((object)values != null && values.Count > 0)
                        {
                            // Get first tracked value (should only be one for "BestQuality")
                            derivedMeasurement = values[0];

                            if ((object)derivedMeasurement != null)
                            {
                                // Determine if new measurement's quality is better than existing one or if new measurement's timestamp is closer to frame
                                if
                                (
                                    (
                                        (!derivedMeasurement.ValueQualityIsGood() || !derivedMeasurement.TimestampQualityIsGood())
                                            &&
                                        (measurement.ValueQualityIsGood() || measurement.TimestampQualityIsGood())
                                    )
                                        ||
                                    (
                                        measurement.Timestamp < derivedMeasurement.Timestamp && measurement.Timestamp >= m_timestamp
                                    )
                                )
                                {
                                    // This measurement has a better quality or came in out-of-order and is closer to frame timestamp, so 
                                    // we sort this measurement instead of the original
                                    values[0] = measurement;

                                    // Keep track of total number of derived measurements
                                    m_derivedMeasurements++;

                                    return measurement;
                                }

                                // Prior measurement is closer to frame than new one
                                return null;
                            }
                        }
                    }

                    // No prior measurement exists, track this initial one
                    values = new List<IMeasurement> { measurement };
                    m_measurements[measurement.Key] = values;

                    // Keep track of total number of derived measurements
                    m_derivedMeasurements++;

                    return measurement;
            }

            return null;
        }

        #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