private MetricValueCollection OnCalculateValues(MetricSampleInterval interval, int intervals, DateTimeOffset startDateTime, DateTimeOffset endDateTime, IEventMetricValueDefinition trendValue) { MetricValueCollection newMetricValueCollection; //we really have two different algorithms: If the user specified shortest, then we are just going //to use every sample we have and hope for the best (provided they are in the time range) //Otherwise, we have a BIG FANCY ALGORYTHM. if (interval == MetricSampleInterval.Shortest) { newMetricValueCollection = OnCalculateValuesShortest(startDateTime, endDateTime, trendValue); } else { //since they aren't using shortest, we are going to use the intervals input option which better not be zero or negative. if (intervals < 1) { throw new ArgumentOutOfRangeException(nameof(intervals), intervals, "The number of intervals must be positive and greater than zero."); } //forward to our big boy method. newMetricValueCollection = OnCalculateValuesInterval(interval, intervals, startDateTime, endDateTime, trendValue); } //we've ripped through the whole data set and now have our value set. Return it to our caller return(newMetricValueCollection); }
/// <summary> /// Calculate displayable values based on the full information captured for this metric /// </summary> /// <remarks> /// The raw values may not be suitable for display depending on the unit the values are captured in, and /// depending on how the data was sampled it may not display well because of uneven sampling if processed /// directly. /// </remarks> /// <param name="interval">The requested data sample size</param> /// <param name="intervals">The number of intervals to have between each value exactly.</param> /// <param name="startDateTime">The earliest date to retrieve data for</param> /// <param name="endDateTime">The last date to retrieve data for</param> /// <param name="trendValue">The specific event metric value to trend</param> /// <returns>A metric value set suitable for display</returns> public IMetricValueCollection CalculateValues(MetricSampleInterval interval, int intervals, DateTimeOffset?startDateTime, DateTimeOffset?endDateTime, IEventMetricValueDefinition trendValue) { DateTimeOffset effectiveStartDateTime = (startDateTime == null ? this.StartDateTime : (DateTimeOffset)startDateTime); DateTimeOffset effectiveEndDateTime = (endDateTime == null ? this.EndDateTime : (DateTimeOffset)endDateTime); return(OnCalculateValues(interval, intervals, effectiveStartDateTime, effectiveEndDateTime, trendValue)); }
void IPacket.ReadFields(PacketDefinition definition, SerializedPacket packet) { switch (definition.Version) { case 1: packet.GetField("MetricTypeName", out m_MetricTypeName); packet.GetField("CategoryName", out m_CategoryName); packet.GetField("CounterName", out m_CounterName); int rawSampleType; packet.GetField("SampleType", out rawSampleType); m_SampleType = (SampleType)rawSampleType; packet.GetField("Caption", out m_Caption); packet.GetField("Description", out m_Description); int rawInterval; packet.GetField("Interval", out rawInterval); m_Interval = (MetricSampleInterval)rawInterval; //and our stuff that we have to calculate Name = MetricDefinition.GetKey(MetricTypeName, CategoryName, CounterName); //generate the name m_ReadOnly = true; //if we got read out of a file, we're read only. break; default: throw new GibraltarPacketVersionException(definition.Version); } //we are NOT live - we came from a serialization reader IsLive = false; }
/// <summary>Create a new metric value set.</summary> /// <param name="caption">The end-user caption for this metric value set.</param> /// <param name="description">The end-user description for this metric value set.</param> /// <param name="interval">The interval to bias to.</param> /// <param name="intervals">The number of intervals to have between each value exactly.</param> /// <param name="unitCaption">An end-user short display caption for the unit axis.</param> public MetricValueCollection(string caption, string description, MetricSampleInterval interval, int intervals, string unitCaption) { //copy our metric's caption and description m_Caption = caption; m_Description = description; m_Interval = interval; m_Intervals = intervals; m_UnitCaption = unitCaption; }
/// <summary>Create a new metric value set.</summary> /// <param name="trendValue">The event metric value this value set relates to.</param> /// <param name="interval">The interval to bias to.</param> /// <param name="intervals">The number of intervals to have between each value exactly.</param> /// <param name="unitCaption">An end-user short display caption for the unit axis.</param> public MetricValueCollection(EventMetricValueDefinition trendValue, MetricSampleInterval interval, int intervals, string unitCaption) { //copy our metric's caption and description m_Caption = trendValue.Caption; m_Description = trendValue.Description; m_Interval = interval; m_Intervals = intervals; m_UnitCaption = unitCaption; }
private MetricValueCollection OnCalculateValues(MetricSampleInterval interval, int intervals, DateTimeOffset startDateTime, DateTimeOffset endDateTime) { EnsureSorted(); MetricValueCollection newMetricValueCollection = new MetricValueCollection("Log Messages", "The number of log messages recorded per interval.", interval, intervals, "Messages"); //And prep for our process loop DateTimeOffset windowStartDateTime = startDateTime; DateTimeOffset windowEndDateTime = CalculateOffset(windowStartDateTime, interval, intervals); //because we're doing a simple count by time interval, it's easiest to iterate //through all of the intervals and then count the matches within. int curSampleIndex = 0; //roll through the samples to find our first sample index we care about so we can count from there. for (curSampleIndex = 0; curSampleIndex < Count; curSampleIndex++) { if (this[curSampleIndex].Timestamp >= startDateTime) { //bingo! this is the first one we want break; } } while (windowEndDateTime <= endDateTime) { //count how many messages we go through to get to our target. int curMessageCount = 0; while (curSampleIndex < Count) { if (this[curSampleIndex].Timestamp >= windowEndDateTime) { //this is the first one in the next interval (or later), so don't count it and stop looping. break; } else { //this bad boy is in our range, count it curMessageCount++; } //and make damn sure we advance our index by one to not get into an infinite loop curSampleIndex++; } //record off this interval new MetricValue(newMetricValueCollection, windowStartDateTime, curMessageCount); //and prep for the next loop windowStartDateTime = windowEndDateTime; windowEndDateTime = CalculateOffset(windowStartDateTime, interval, intervals); } return(newMetricValueCollection); }
/// <summary> /// Calculates the offset date from the provided baseline for the specified interval /// </summary> /// <remarks> /// To calculate a backwards offset (the date that is the specified interval before the baseline) use a negative /// number of invervals. For example, -1 intervals will give you one interval before the baseline. /// </remarks> /// <param name="baseline">The date and time to calculate an offset date and time from</param> /// <param name="interval">The interval to add or subtract from the baseline</param> /// <param name="intervals">The number of intervals to go forward or (if negative) backwards</param> /// <returns></returns> private DateTimeOffset CalculateOffset(DateTimeOffset baseline, MetricSampleInterval interval, int intervals) { DateTimeOffset returnVal; //just so we're initialized with SOMETHING. int intervalCount = intervals; //since they aren't using shortest, we are going to use the intervals input option which better not be zero or negative. if ((intervals == 0) && (interval != MetricSampleInterval.Shortest)) { throw new ArgumentOutOfRangeException(nameof(intervals), intervals, "The number of intervals can't be zero if the interval isn't set to Shortest."); } switch (interval) { case MetricSampleInterval.Default: //use how the data was recorded //default and ours is default - use second. returnVal = CalculateOffset(baseline, MetricSampleInterval.Second, intervalCount); break; case MetricSampleInterval.Shortest: //exlicitly use the shortest value available, 16 milliseconds returnVal = baseline.AddMilliseconds(16); //interval is ignored in the case of the "shortest" configuration break; case MetricSampleInterval.Millisecond: returnVal = baseline.AddMilliseconds(intervalCount); break; case MetricSampleInterval.Second: returnVal = baseline.AddSeconds(intervalCount); break; case MetricSampleInterval.Minute: returnVal = baseline.AddMinutes(intervalCount); break; case MetricSampleInterval.Hour: returnVal = baseline.AddHours(intervalCount); break; case MetricSampleInterval.Day: returnVal = baseline.AddDays(intervalCount); break; case MetricSampleInterval.Week: returnVal = baseline.AddDays(intervalCount * 7); break; case MetricSampleInterval.Month: returnVal = baseline.AddMonths(intervalCount); break; default: throw new ArgumentOutOfRangeException(nameof(interval)); } return(returnVal); }
/// <summary> /// Determines the number of milliseconds in the provided interval for the timer object. /// </summary> /// <remarks>The values Default and Shortest are automatically treated as Minute by this function, effectively /// making once a minute the system default.</remarks> /// <param name="referenceInterval">The interval to calculate milliseconds for</param> /// <returns>The number of milliseconds between timer polls</returns> private static long GetTimerInterval(MetricSampleInterval referenceInterval) { //we have to convert the reference interval into the correct # of milliseconds long milliseconds = -1; //a safe choice because it means the timer will fire exactly once. switch (referenceInterval) { case MetricSampleInterval.Default: case MetricSampleInterval.Shortest: case MetricSampleInterval.Millisecond: //we won't go below once a second milliseconds = 1000; break; case MetricSampleInterval.Minute: milliseconds = 60000; //sorta by definition break; case MetricSampleInterval.Second: milliseconds = 1000; //sorta by definition break; case MetricSampleInterval.Hour: milliseconds = 3600000; break; case MetricSampleInterval.Day: milliseconds = 86400000; //get yer own calculator break; case MetricSampleInterval.Week: milliseconds = 604800000; //I mean who's going to do that, really. BTW: Just barely a 32 bit number. break; case MetricSampleInterval.Month: milliseconds = DateTime.DaysInMonth(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month) * 86400000; //now I'm just being a smartass. break; default: break; } //before we return: We poll artificially fast for the first few minutes and first hour. long secondsPolling = (long)(DateTimeOffset.Now - m_PollingStarted).TotalSeconds; if ((milliseconds > 5000) && (secondsPolling < 120)) { milliseconds = 5000; } else if ((milliseconds > 15000) && (secondsPolling < 3600)) { milliseconds = 15000; } return(milliseconds); }
/// <summary>Create a new metric value set.</summary> /// <param name="caption">The end-user caption for this metric value set.</param> /// <param name="description">The end-user description for this metric value set.</param> /// <param name="interval">The interval.</param> /// <param name="unitCaption">An end-user short display caption for the unit axis.</param> public MetricValueCollection(string caption, string description, TimeSpan interval, string unitCaption) { //copy our metric's caption and description m_Caption = caption; m_Description = description; // TODO: We should probably store the TimeSpan instead of the MetricSampleInterval m_Interval = MetricSampleInterval.Millisecond; m_Intervals = (int)interval.TotalMilliseconds; m_UnitCaption = unitCaption; }
/// <summary> /// Calculate displayable values based on the full information captured for this metric with the specified interval /// for all dates available /// </summary> /// <remarks> /// The raw values may not be suitable for display depending on the unit the values are captured in, and /// depending on how the data was sampled it may not display well because of uneven sampling if processed /// directly. /// </remarks> /// <param name="interval">The requested data sample size</param> /// <param name="intervals">The number of intervals to have between each value exactly.</param> /// <returns>A metric value set suitable for display</returns> public IMetricValueCollection CalculateValues(MetricSampleInterval interval, int intervals) { //handle the special case where there are no samples so we can't figure out start & end. if (m_Samples.Count == 0) { return(CalculateValues(interval, intervals, null, null)); } //forward to our grander overload return(CalculateValues(interval, intervals, this.StartDateTime, this.EndDateTime)); }
/// <summary> /// Calculates the amount we will "pull forward" a future sample by to fit it to our requested interval. /// </summary> /// <remarks>Tolerance allows for us to ignore small variations in exact timestamps for the purposes of fitting the best data.</remarks> /// <param name="interval"></param> /// <returns></returns> public static TimeSpan CalculateOffsetTolerance(MetricSampleInterval interval) { TimeSpan returnVal; switch (interval) { case MetricSampleInterval.Default: case MetricSampleInterval.Shortest: case MetricSampleInterval.Millisecond: //same as millisecond; we will use 1 clock tick returnVal = new TimeSpan(1); break; case MetricSampleInterval.Second: //10 milliseconds returnVal = new TimeSpan(0, 0, 0, 0, 10); break; case MetricSampleInterval.Minute: //2 seconds returnVal = new TimeSpan(0, 0, 0, 2); break; case MetricSampleInterval.Hour: //1 minute returnVal = new TimeSpan(0, 1, 0); break; case MetricSampleInterval.Day: //30 minutes returnVal = new TimeSpan(0, 30, 0); break; case MetricSampleInterval.Week: //12 hours returnVal = new TimeSpan(12, 0, 0); break; case MetricSampleInterval.Month: //two days returnVal = new TimeSpan(2, 0, 0, 0); break; default: throw new ArgumentOutOfRangeException(nameof(interval)); } return(returnVal); }
/// <summary> /// Calculate the number of messages per interval present in the specified time range. /// </summary> /// <param name="interval">The requested data sample size</param> /// <param name="intervals">The number of intervals to have between each value exactly.</param> /// <param name="startDateTime">The earliest date to retrieve data for</param> /// <param name="endDateTime">The last date to retrieve data for</param> /// <returns>A metric value set suitable for display</returns> public MetricValueCollection CalculateValues(MetricSampleInterval interval, int intervals, DateTimeOffset?startDateTime, DateTimeOffset?endDateTime) { DateTimeOffset effectiveStartDateTime = (startDateTime == null ? m_Session.Summary.StartDateTime : (DateTimeOffset)startDateTime); DateTimeOffset effectiveEndDateTime = (endDateTime == null ? m_Session.Summary.EndDateTime : (DateTimeOffset)endDateTime); //HOLD UP - enforce our floor for sensible intervals if ((intervals < 0) || ((interval != MetricSampleInterval.Shortest) && (intervals == 0))) { throw new ArgumentOutOfRangeException(nameof(intervals), intervals, "Negative intervals are not supported for calculating values. Specify an interval count greater than zero, except for the Shortest interval where the interval count is ignored."); } if ((interval == MetricSampleInterval.Millisecond) && (intervals < 16)) { intervals = 16; } return(OnCalculateValues(interval, intervals, effectiveStartDateTime, effectiveEndDateTime)); }
/// <summary> /// Calculate displayable values based on the full information captured for this metric /// </summary> /// <remarks> /// The raw values may not be suitable for display depending on the unit the values are captured in, and /// depending on how the data was sampled it may not display well because of uneven sampling if processed /// directly. /// </remarks> /// <param name="interval">The requested data sample size</param> /// <param name="intervals">The number of intervals to have between each value exactly.</param> /// <param name="startDateTime">The earliest date to retrieve data for</param> /// <param name="endDateTime">The last date to retrieve data for</param> /// <returns>A metric value set suitable for display</returns> public IMetricValueCollection CalculateValues(MetricSampleInterval interval, int intervals, DateTimeOffset?startDateTime, DateTimeOffset?endDateTime) { //we want to substitute in defaults, but be wary of what happens when we don't have any samples... DateTimeOffset effectiveStartDateTime = (startDateTime.HasValue ? (DateTimeOffset)startDateTime : (m_Samples.Count > 0) ? StartDateTime : DateTimeOffset.MinValue); DateTimeOffset effectiveEndDateTime = (endDateTime.HasValue ? (DateTimeOffset)endDateTime : (m_Samples.Count > 0) ? EndDateTime : DateTimeOffset.MinValue); //HOLD UP - enforce our floor for sensible intervals if ((intervals < 0) || ((interval != MetricSampleInterval.Shortest) && (intervals == 0))) { throw new ArgumentOutOfRangeException(nameof(intervals), intervals, "Negative intervals are not supported for calculating values. Specify an interval count greater than zero, except for the Shortest interval where the interval count is ignored."); } if ((interval == MetricSampleInterval.Millisecond) && (intervals < 16)) { intervals = 16; } return(OnCalculateValues(interval, intervals, effectiveStartDateTime, effectiveEndDateTime)); }
private MetricValueCollection OnCalculateValuesInterval(MetricSampleInterval interval, int intervals, DateTimeOffset startDateTime, DateTimeOffset endDateTime, IEventMetricValueDefinition trendValue) { //there are two possibilities for unit caption: Either use the caption from the trend definition or, if that's null, we'll just do Count. string unitCaption = null; if (trendValue != null) { unitCaption = trendValue.UnitCaption; } if (string.IsNullOrEmpty(unitCaption)) { unitCaption = "Events"; } MetricValueCollection newMetricValueCollection; if (trendValue == null) { //initialize with our metric object for default caption/description behavior newMetricValueCollection = new MetricValueCollection(this, interval, intervals, unitCaption); } else { //initialize with our trend for trend-specific captions and descriptions newMetricValueCollection = new MetricValueCollection((EventMetricValueDefinition)trendValue, interval, intervals, unitCaption); } //And prep for our process loop DateTimeOffset windowStartDateTime = startDateTime; DateTimeOffset windowEndDateTime = CalculateOffset(windowStartDateTime, interval, intervals); double lastValue = 0.0; //now loop through all of the intervals in the range to create the sample ranges. int lastUsedSampleIndex = 0; while (windowStartDateTime < endDateTime) { //find all of the samples in the current range... List <EventMetricSample> samples = new List <EventMetricSample>(); for (int curSampleIndex = lastUsedSampleIndex; curSampleIndex < Samples.Count; curSampleIndex++) { //get the sample on deck EventMetricSample curSample = Samples[curSampleIndex]; //if we're outside of the window (before or after) then break out because we've found this whole bucket. if ((curSample.Timestamp < windowStartDateTime) || (curSample.Timestamp > windowEndDateTime)) { //we'll need to look at this sample in the next window to see if it fits. lastUsedSampleIndex = curSampleIndex; break; } //this sample is in the bucket samples.Add(curSample); } //add a metric value for this bucket now that we know everything in it. lastValue = CalculateSample(newMetricValueCollection, samples, lastValue, lastUsedSampleIndex, windowEndDateTime, (EventMetricValueDefinition)trendValue); //and now we have recorded a metric value for the requested date & time if possible, move that forward. windowStartDateTime = windowEndDateTime; //we're moving on to the next window windowEndDateTime = CalculateOffset(windowEndDateTime, interval, intervals); //if the target is beyond our end date & time, set to the end date and time so we calculate our last sample if (windowEndDateTime > endDateTime) { windowEndDateTime = endDateTime; } } return(newMetricValueCollection); }
/// <summary> /// Determines what specific samples to use and then calculates the effective values for each sample, returning the results in the provided /// new metric value set /// </summary> /// <remarks>Data covering the provided start and end date will be returned when possible with the goal being that the first metric value returned /// will coincide with the start date, and the last sample will be less than or equal to the end date. Even if there are raw samples available coincident /// with the start date it may not be possible to provide a metric value for that date due to the need to have multiple samples to calculate most /// metrics.</remarks> /// <param name="interval">The interval to bias to.</param> /// <param name="intervals">The number of intervals to have between each value exactly.</param> /// <param name="startDateTime">The exact date and time desired to start the value set.</param> /// <param name="endDateTime">The exact end date and time to not exceed.</param> /// <returns>A new metric value set with all calculated values.</returns> protected override MetricValueCollection OnCalculateValues(MetricSampleInterval interval, int intervals, DateTimeOffset startDateTime, DateTimeOffset endDateTime) { //forward the call to our somewhat more elaborate common routine, substituting the default trend. return(OnCalculateValues(interval, intervals, startDateTime, endDateTime, Definition.DefaultValue)); }
/// <summary> /// Calculate the number of messages per interval present in the entire messages collection. /// </summary> /// <param name="interval">The requested data sample size</param> /// <param name="intervals">The number of intervals to have between each value exactly.</param> /// <returns>A metric value set suitable for display</returns> public MetricValueCollection CalculateValues(MetricSampleInterval interval, int intervals) { //forward to our grander overload return(CalculateValues(interval, intervals, m_Session.Summary.StartDateTime, m_Session.Summary.EndDateTime)); }
/// <summary> /// Calculate the value set for the provided date range inclusive with samples exactly on the provided interval. /// </summary> /// <remarks></remarks> /// <param name="interval">The interval to bias to.</param> /// <param name="intervals">The number of intervals to have between each value exactly.</param> /// <param name="startDateTime">The exact date and time desired to start the value set.</param> /// <param name="endDateTime">The exact end date and time to not exceed.</param> /// <returns>A new metric value set with all calculated values.</returns> private MetricValueCollection OnCalculateValuesOnInterval(MetricSampleInterval interval, int intervals, DateTimeOffset startDateTime, DateTimeOffset endDateTime) { MetricValueCollection newMetricValueCollection = new MetricValueCollection(this, interval, intervals, Definition.UnitCaption); //based on the requested interval, calculate the delta between each sample and the tolerance (how close we have to be //to the requested time to be pulled forward in time as the best sample) TimeSpan sampleTolerance = CalculateOffsetTolerance(interval); DateTimeOffset targetDateTime = startDateTime; DateTimeOffset targetToleranceDateTime = targetDateTime + sampleTolerance; SampledMetricSample baselineSample = null; double previousMetricValue = 0; int firstSampleIndex = 0; //First, we need to find the value for the start date & time and the sample index into the collection. for (int curSampleIndex = 0; curSampleIndex < base.Samples.Count; curSampleIndex++) { SampledMetricSample curSample = (SampledMetricSample)Samples[curSampleIndex]; //is this the sample that exactly covers our target date & time? It is if it's equal to our target, or //it is a more exact fit than the next sample. if ((curSample.Timestamp == targetDateTime) || ((curSample.Timestamp < targetToleranceDateTime) && (Samples[curSampleIndex + 1].Timestamp > targetToleranceDateTime))) { //yes, this sample is the best fit for our start date & time. The next one is after our "pull forward" tolerance. //but to get a value, we may have to back up one more interval from this sample to establish its value if (curSample.RequiresMultipleSamples) { //back up as much as we can - as close to one full interval as possible so we get the most accurate initial value sample DateTimeOffset baselineTargetDateTime = CalculateOffset(targetDateTime, interval, -intervals); //start with the first sample before us... if there is one. for (int baselineSampleIndex = curSampleIndex - 1; baselineSampleIndex >= 0; baselineSampleIndex--) { //keep walking back until we are before our target (or run out of choices) baselineSample = (SampledMetricSample)Samples[curSampleIndex]; if (baselineSample.Timestamp <= baselineTargetDateTime) { //this is our best fit - it's the first that covers our baseline date time. break; } } } //we either got our baseline or we didn't - if we didn't and we need it we can't calculate value. if ((baselineSample != null) || (curSample.RequiresMultipleSamples == false)) { //Calculate the initial metric value sample and add it to the collection. previousMetricValue = CalculateSample(newMetricValueCollection, baselineSample, curSample, startDateTime); } //and this sample becomes our baseline into the next routine baselineSample = curSample; firstSampleIndex = curSampleIndex; break; //and we're done - we only wanted to get the start value. } } //Now that we've found our first sample, and the offset into the collection for the first sample, we can go on (provide there are more samples) firstSampleIndex++; //because we used the current sample in the for loop above. if (firstSampleIndex < (Samples.Count - 1)) { //we now want to look for the first sample after the start date. If the user was silly and requested an end date that is less //than one interval from the start date, we'll never get into our while loop this way. targetDateTime = CalculateOffset(targetDateTime, interval, intervals); targetToleranceDateTime = targetDateTime + sampleTolerance; int curSampleIndex = firstSampleIndex; //we start with the first sample after what was used above. SampledMetricSample curSample = null; //keep looping until we fill up the timespan or run out of samples. while ((targetDateTime <= endDateTime) && (curSampleIndex < (Samples.Count - 1))) { //if we have no sample on deck, we must have used it in the last pass of the loop (or this is the first pass) //so get it now. if (curSample == null) { curSample = (SampledMetricSample)Samples[curSampleIndex]; } //is this the sample that exactly covers our target date & time? It is if it's equal to our target, or //it is a more exact fit than the next sample. if ((curSample.Timestamp == targetDateTime) || ((curSample.Timestamp < targetToleranceDateTime) && (Samples[curSampleIndex + 1].Timestamp > targetToleranceDateTime))) { //yes, this sample is the best fit for our start date & time. The next one is after our "pull forward" tolerance. if ((baselineSample != null) || (curSample.RequiresMultipleSamples == false)) { //Calculate the next metric value sample and add it to the collection. previousMetricValue = CalculateSample(newMetricValueCollection, baselineSample, curSample, targetDateTime); } //and this sample becomes our baseline into the next round baselineSample = curSample; //and we need a new current sample. curSample = null; curSampleIndex++; //and now we have recorded a metric value for the requested date & time if possible, move that forward. targetDateTime = CalculateOffset(targetDateTime, interval, intervals); targetToleranceDateTime = targetDateTime + sampleTolerance; } else if (curSample.Timestamp < targetToleranceDateTime) { //this sample AND the next sample are both before target tolerance - we're going to skip this guy and record nothing //(because we have more samples than we need for the interval we want) curSample = null; curSampleIndex++; } else { //the sample on deck doesn't apply yet - it's in the future. We need to "invent" a sample for the current date and time. //we'll just re-record the last metric value new MetricValue(newMetricValueCollection, targetDateTime, previousMetricValue); //and now we have definitely recorded a metric value for the requested date & time, move that forward. targetDateTime = CalculateOffset(targetDateTime, interval, intervals); targetToleranceDateTime = targetDateTime + sampleTolerance; } } } return(newMetricValueCollection); }
/// <summary> /// Determines what specific samples to use and then calculates the effective values for each sample, returning the results in the provided /// new metric value set /// </summary> /// <remarks><para>Data covering the provided start and end date will be returned when possible with the goal being that the first metric value returned /// will coincide with the start date, and the last sample will be less than or equal to the end date. Even if there are raw samples available coincident /// with the start date it may not be possible to provide a metric value for that date due to the need to have multiple samples to calculate most /// metrics.</para> /// <para>When there are no samples available an empty collection should be returned. In this case the start and end date may be outside the range of the session.</para></remarks> /// <param name="interval">The interval to bias to.</param> /// <param name="intervals">The number of intervals to have between each value exactly.</param> /// <param name="startDateTime">The exact date and time desired to start the value set.</param> /// <param name="endDateTime">The exact end date and time to not exceed.</param> /// <returns>A new metric value set with all calculated values.</returns> protected abstract MetricValueCollection OnCalculateValues(MetricSampleInterval interval, int intervals, DateTimeOffset startDateTime, DateTimeOffset endDateTime);