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>Create a new metric value for the specified metric value set.</summary> /// <remarks>The new metric value is automatically added to the provided metric value set.</remarks> /// <param name="metricValueCollection">The metric value set this value is part of.</param> /// <param name="timeStamp">The unique date and time of this value sample.</param> /// <param name="value">The calculated value.</param> public MetricValue(MetricValueCollection metricValueCollection, DateTimeOffset timeStamp, double value) { m_MetricValueCollection = metricValueCollection; m_TimeStamp = timeStamp; m_Value = value; //get the unique sequence number from the metric m_Sequence = metricValueCollection.GetSampleSequence(); //and add ourself to the metric value set m_MetricValueCollection.Add(this); }
private MetricValueCollection OnCalculateValuesShortest(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, MetricSampleInterval.Shortest, 0, unitCaption); } else { //initialize with our trend for trend-specific captions and descriptions newMetricValueCollection = new MetricValueCollection((EventMetricValueDefinition)trendValue, MetricSampleInterval.Shortest, 0, unitCaption); } double lastValue = 0.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 < Samples.Count; curSampleIndex++) { //get the sample on deck EventMetricSample curSample = Samples[curSampleIndex]; //Is this sample in our range? if (curSample.Timestamp >= startDateTime) { //Go ahead and calculate the value, adding it to our set. We don't use a baseline because we're looking at each individual value. CalculateSample(newMetricValueCollection, new List <EventMetricSample>(new[] { curSample }), lastValue, curSampleIndex, curSample.Timestamp, (EventMetricValueDefinition)trendValue); } else if (curSample.Timestamp > endDateTime) { //we're done - there are no more samples to consider, break out of the for loop break; } } return(newMetricValueCollection); }
/// <summary> /// Calculate one effective value from the provided objects /// </summary> /// <param name="metricValueCollection">The value set to add the new value to</param> /// <param name="baselineSample">The baseline to calculate from. Only used (and required) for metrics that require multiple samples.</param> /// <param name="valueSample">The value sample to perform the calculation with.</param> /// <param name="timeStampOverride">An optional override timestamp</param> private static double CalculateSample(MetricValueCollection metricValueCollection, SampledMetricSample baselineSample, SampledMetricSample valueSample, DateTimeOffset?timeStampOverride) { double calculatedValue; if (valueSample.RequiresMultipleSamples == false) { calculatedValue = valueSample.ComputeValue(); //we just need one } else { calculatedValue = valueSample.ComputeValue(baselineSample); } DateTimeOffset timeStamp = ((timeStampOverride == null) ? valueSample.Timestamp : (DateTimeOffset)timeStampOverride); //now create & add the value to our values collection. new MetricValue(metricValueCollection, timeStamp, calculatedValue); return(calculatedValue); }
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> /// Calculate one effective value from the provided objects /// </summary> /// <param name="metricValueCollection">Optional. The value set to add the new value to</param> /// <param name="samples">The set of samples to put in this value</param> /// <param name="previousValue">The value of the prior sample in time.</param> /// <param name="previousCount">The number of event metric samples previously used (for running average)</param> /// <param name="timeStamp">The timestamp to use for the sample</param> /// <param name="trendValue">The specific event metric value to trend</param> internal double CalculateSample(MetricValueCollection metricValueCollection, IList <EventMetricSample> samples, double previousValue, int previousCount, DateTimeOffset timeStamp, EventMetricValueDefinition trendValue) { double calculatedValue; //There are three totally different routes we go: Either we are calculating an explicit trend value, //or we are just going to count the # of events. This varies depending on whether the caller specified //a trend value if (trendValue == null) { //just count them calculatedValue = samples.Count; } //There are two overall ways we trend things: Either we count the # of non-null values for non-trendables, or //we actually make a numeric trend else if ((trendValue.IsTrendable == false) || (trendValue.DefaultTrend == EventMetricValueTrend.Count)) { //count the number of things. int itemCount = 0; if (samples.Count > 0) { int valueIndex = Definition.Values.IndexOf(trendValue); //the zero-based index in the value collection of the value we want //Now count all of the samples with a non-null value, including us (inclusive) foreach (EventMetricSample sample in samples) { if (sample.Values[valueIndex] != null) { itemCount++; } } } calculatedValue = itemCount; } else { //ohh kay, now we either sum or average. double sumValue = 0; //sum or average, we need the sum. int items = samples.Count; //for an average we will need the item count. int valueIndex = Definition.Values.IndexOf(trendValue); //the zero-based index in the value collection of the value we want //if it's a running value we need to start with the previous sample's state if ((trendValue.DefaultTrend == EventMetricValueTrend.RunningAverage) || (trendValue.DefaultTrend == EventMetricValueTrend.RunningSum)) { //we need the previous running data. sumValue = previousValue; items += previousCount; } //Now add up the effective value of the requested trend line for every sample in the range, inclusive foreach (EventMetricSample currentSample in samples) { sumValue += currentSample.GetEffectiveValue(valueIndex); } //figure out the calculated value. For performance and safety, don't do a divide we don't have to if ((items > 1) && ((trendValue.DefaultTrend == EventMetricValueTrend.Average) || (trendValue.DefaultTrend == EventMetricValueTrend.RunningAverage))) { calculatedValue = sumValue / items; } else { calculatedValue = sumValue; } } //now create & add the value to our values collection. if (metricValueCollection != null) { new MetricValue(metricValueCollection, timeStamp, calculatedValue); } return(calculatedValue); }
/// <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> /// Calculate a value set for the provided date range inclusive using all of the sample data, even if /// that produces irregular sample intervals. /// </summary> /// <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 OnCalculateValuesShortest(DateTimeOffset startDateTime, DateTimeOffset endDateTime) { MetricValueCollection newMetricValueCollection = new MetricValueCollection(this, MetricSampleInterval.Shortest, 0, Definition.UnitCaption); //ohhhhkay, now we need to figure out what samples we want to include and not include. //we want to start by getting the sample one interval before the user wants to have values for so we can have a value exactly //on that starting point SampledMetricSample baselineSample = null; SampledMetricSample previousBaselineSample = null; foreach (SampledMetricSample curSample in base.Samples) { //is this sample in our range? if (curSample.Timestamp < startDateTime) { //It isn't, but there are still reasons we'd be interested in it. //because the next one could be in the range and we'll want to mess with this one. previousBaselineSample = baselineSample; baselineSample = curSample; } else if (curSample.Timestamp > endDateTime) { //no, BUT if it's the first sample after our range, and we didn't end EXACTLY //on the range then we want to include it, but we'll short it back to the end of the timeframe. if ((baselineSample != null) && (baselineSample.Timestamp < endDateTime)) { CalculateSample(newMetricValueCollection, baselineSample, curSample, endDateTime); } //and no matter what, we're done. Either we got a special "Bracket" sample, or there isn't one. break; } else { //the sample is in the range - neither after nor before. We definitely use this sample. //but before we do this, is this the FIRST one in the range? if ((baselineSample != null) && (baselineSample.Timestamp < startDateTime)) { //yes it is - the baseline is before our start date, and we're after or equal. //If it's not only the last before we get in but ALSO the first one in isn't EXACTLY on the line, we need to //create a fake interim sample, based on this guy and the one BEFORE him. if (curSample.Timestamp > startDateTime) { //we have to insert a fake sample based on the baseline and the guy that came before him. //but guard against us not having enough previous information to do this (this will happen more often than not) if ((previousBaselineSample != null) || (baselineSample.RequiresMultipleSamples == false)) { CalculateSample(newMetricValueCollection, previousBaselineSample, baselineSample, startDateTime); } } } //calculate our sample, however we have to guard against there being no baseline // (because we're the first sample of a metric that requires multiple samples) if ((baselineSample != null) || (curSample.RequiresMultipleSamples == false)) { CalculateSample(newMetricValueCollection, baselineSample, curSample, null); } //and this sample becomes the new baseline baselineSample = curSample; } } return(newMetricValueCollection); }