//******************************************************************************************************
// 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
{
///
/// container used to track values for downsampling.
///
internal class TrackingFrame
{
#region [ Members ]
// Fields
private IFrame m_sourceFrame;
private long m_timestamp;
private DownsamplingMethod m_downsamplingMethod;
private ConcurrentDictionary> m_measurements;
private ReaderWriterSpinLock m_frameLock;
private long m_derivedMeasurements;
#endregion
#region [ Constructors ]
///
/// Constructs a new given the specified parameters.
///
/// Source to track.
/// to apply.
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>();
}
#endregion
#region [ Properties ]
///
/// Gets instance of being tracked.
///
public IFrame SourceFrame
{
get
{
return m_sourceFrame;
}
}
///
/// Gets timestamp of being tracked.
///
public long Timestamp
{
get
{
return m_timestamp;
}
}
///
/// Total number of measurements downsampled by .
///
public long DownsampledMeasurements
{
get
{
// Only measurements that came in after initial sorts were downsampled...
return m_derivedMeasurements - m_sourceFrame.SortedMeasurements;
}
}
///
/// Gets the locking primitive.
///
public ReaderWriterSpinLock Lock
{
get
{
return m_frameLock;
}
}
#endregion
#region [ Methods ]
///
/// Derives measurement value, downsampling if needed.
///
/// New value.
/// New derived value, or null if value should not be assigned to .
public IMeasurement DeriveMeasurementValue(IMeasurement measurement)
{
IMeasurement derivedMeasurement;
List 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 { 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 { 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 { measurement };
m_measurements[measurement.Key] = values;
// Keep track of total number of derived measurements
m_derivedMeasurements++;
return measurement;
}
return null;
}
#endregion
}
}