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