/// <summary> /// Records a value to the values array of this sample given the zero-based index of the value definition to be used. /// </summary> /// <remarks>The value must be defined as part of the event metric definition associated with this sample /// or an exception will be thrown. The data type must also be compatible with the data type configured /// on the event metric definition or no data will be recorded. /// If called more than once for the same value, the prior value will be replaced.</remarks> /// <param name="valueIndex">The zero-based index within the value definition of the value to be recorded.</param> /// <param name="value">The value to be recorded.</param> public void SetValue(int valueIndex, object value) { IEventMetricValueDefinitionCollection valueDefinitions = Metric.Definition.Values; //we don't have to check that the value index isn't null, but it does have to be in range if ((valueIndex < 0) || (valueIndex > (valueDefinitions.Count - 1))) { #if DEBUG //if we're compiled in debug mode, tell the user they blew it. throw new ArgumentOutOfRangeException(nameof(valueIndex), valueIndex.ToString(CultureInfo.CurrentCulture)); #else //log and return, nothing we can do. if (!Log.SilentMode) { Log.Write(LogMessageSeverity.Warning, LogCategory, "Unable to add metric value to the current sample due to missing value definition", "There is no value definition at index {1} for metric definition {0}", Metric.Definition.Name, valueIndex); } return; #endif } //look up the value in the definition so we can process it IEventMetricValueDefinition curValueDefinition = valueDefinitions[valueIndex]; //and now we can use our one true method to add the value SetValue((EventMetricValueDefinition)curValueDefinition, value); }
/// <summary> /// Add an existing value definition item to this collection /// </summary> /// <param name="item">An existing value definition item associated with our metric definition</param> public void Add(IEventMetricValueDefinition item) { //we can't do the read-only check here because we use this method to add existing objects during rehydration //make sure the input isn't null if (item == null) { throw new ArgumentNullException(nameof(item)); } //make sure it's associated with our definition if ((EventMetricDefinition)item.Definition != m_Definition) { throw new ArgumentException("The provided value definition item is not associated with our metric definition", nameof(item)); } lock (m_Definition.Lock) { //and make sure it isn't a duplicate key if (m_Dictionary.ContainsKey(item.Name)) { throw new ArgumentException( string.Format(CultureInfo.CurrentCulture, "There is already a value definition item with the name {0}", item.Name), nameof(item)); } //and finally what the hell, go ahead and add it. m_List.Add(item); m_Dictionary.Add(item.Name, item); } //and fire our event, outside the lock OnCollectionChanged(new CollectionChangedEventArgs <IEventMetricValueDefinitionCollection, IEventMetricValueDefinition>(this, item, CollectionAction.Added)); }
// Note: Apparently this documentation is out of date? Remove apparently *is* supported unless IsReadOnly. /// <summary> /// Removing objects is not supported. /// </summary> /// <remarks>This method is implemented only for ICollection interface support and will throw an exception if called.</remarks> /// <param name="item">The EventMetricValueDefinition item to remove.</param> public bool Remove(IEventMetricValueDefinition item) { bool itemRemoved = false; lock (m_Definition.Lock) { if (IsReadOnly) { throw new NotSupportedException("The collection is read-only"); } //do a safe remove of the victim in the dictionary and list, if they are still present //if they aren't, we have a problem if (m_Dictionary.ContainsKey(item.Name)) { m_Dictionary.Remove(item.Name); itemRemoved = true; } if (m_List.Contains(item)) { m_List.Remove(item); itemRemoved = true; } } //and fire our event if there was really something to remove if (itemRemoved) { OnCollectionChanged(new CollectionChangedEventArgs <IEventMetricValueDefinitionCollection, IEventMetricValueDefinition>(this, item, CollectionAction.Removed)); } return(itemRemoved); }
/// <summary> /// Indicates whether the collection already contains the specified definition object /// </summary> /// <remarks>Even if the object doesn't exist in the collection, if another object with the same key exists then /// an exception will be thrown if the supplied object is added to the collection. See Add for more information.</remarks> /// <param name="item">The event metric value definition object to look for</param> /// <returns>True if the object already exists in the collection, false otherwise</returns> public bool Contains(IEventMetricValueDefinition item) { //here we are relying on the fact that the comment object implements IComparable sufficiently to guarantee uniqueness if (IsReadOnly) // Don't need the lock once we're read-only. { return(m_List.Contains(item)); } lock (m_Definition.Lock) // But we do need the lock when it may still be changing. { return(m_List.Contains(item)); } }
/// <summary> /// Retrieve an item from the collection by its key if present. If not present, the default value of the object is returned. /// </summary> /// <param name="name">The key of the value to get.</param> /// <param name="value">When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized.</param> /// <returns>true if the collection contains an element with the specified key; otherwise false.</returns> public bool TryGetValue(string name, out IEventMetricValueDefinition value) { if (IsReadOnly) // Don't need the lock once we're read-only. { //gateway to our inner dictionary try get value return(m_Dictionary.TryGetValue(name, out value)); } lock (m_Definition.Lock) // But we do need the lock when it may still be changing. { //gateway to our inner dictionary try get value return(m_Dictionary.TryGetValue(name, out value)); } }
/// <summary> /// Determines if the provided object is identical to this object. /// </summary> /// <param name="other">The object to compare this object to</param> /// <returns>True if the objects represent the same data.</returns> public bool Equals(IEventMetricValueDefinition other) { //Careful - can be null if (other == null) { return(false); // since we're a live object we can't be equal. } if (ReferenceEquals(this, other) || ReferenceEquals(m_Packet, ((EventMetricValueDefinition)other).Packet)) { return(true); // If the objects or the underlying packet are the same object, they're the same. } //We're really just a type cast, refer to our base object return(m_Packet.Name.Equals(other.Name)); }
/// <summary> /// Compute the resultant value for this sample compared with the provided baseline sample. /// </summary> /// <remarks> /// <para>The baseline sample must be for a date and time prior to this sample for correct results.</para> /// <para>If the supplied trendValue isn't trendable, the number of samples with a non-null value will be counted.</para> /// <para>If the supplied trendValue is trendable, the Default Trend (average or sum) will be calculated for all /// samples between the supplied baseline sample and this sample, inclusive.</para> /// </remarks> /// <param name="baselineSample">The previous baseline sample to calculate a difference for</param> /// <param name="trendValue">The definition of the value from this event metric to trend.</param> /// <returns>The calculated counter value</returns> public double ComputeValue(IEventMetricSample baselineSample, IEventMetricValueDefinition trendValue) { //we have to figure out all of the samples to include between these points. List <EventMetricSample> valueSamples = new List <EventMetricSample>(); EventMetricSampleCollection samples = Metric.Samples; int startIndex = (baselineSample == null) ? 0 : samples.IndexOf(baselineSample); int ourIndex = samples.IndexOf(this); for (int sampleIndex = startIndex; sampleIndex <= ourIndex; sampleIndex++) { valueSamples.Add(samples[sampleIndex]); } return(Metric.CalculateSample(null, valueSamples, 0, 0, Timestamp, (EventMetricValueDefinition)trendValue)); }
internal EventMetricValueDefinition Externalize(IEventMetricValueDefinition valueDefinition) { if (valueDefinition == null) { return(null); } lock (m_Definition.Lock) { EventMetricValueDefinition externalDefinition; if (m_Externalized.TryGetValue(valueDefinition, out externalDefinition) == false) { externalDefinition = new EventMetricValueDefinition(m_Definition, (Monitor.EventMetricValueDefinition)valueDefinition); m_Externalized[valueDefinition] = externalDefinition; } return(externalDefinition); } }
/// <summary> /// Searches for the specified object and returns the zero-based index of it within the dictionary if it is found. /// </summary> /// <param name="item">The object to search for</param> /// <returns>The zero based index of the object within the dictionary or -1 if not found.</returns> public int IndexOf(IEventMetricValueDefinition item) { if (m_Definition.IsReadOnly) // && ReferenceEquals(item.Definition, Definition)) { int index = ((EventMetricValueDefinition)item).MyIndex; if (index < 0) { // Wasn't found, do a scan the hard way. index = m_List.IndexOf(item); if (index >= 0) { ((EventMetricValueDefinition)item).MyIndex = index; // And if we found it, remember for next time. } } return(index); } // Otherwise, we aren't necessarily final (could be removes?), so punt it the hard way. We need the lock. lock (m_Definition.Lock) { return(m_List.IndexOf(item)); } }
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); }
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); }
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)); }
///<summary>Inserting objects by index is not supported because the collection is sorted.</summary> ///<remarks>This method is implemented only for IList interface support and will throw an exception if called.</remarks> public void Insert(int index, IEventMetricValueDefinition item) { //we don't support setting an object by index; we are sorted. throw new NotSupportedException(); }
/// <summary> /// Retrieve the index of a metric value definition by its name /// </summary> /// <param name="name">The value name to locate in the collection</param> /// <remarks>Items are identified using ordinal, case insensitive string comparisons. If no value exists with the provided name an exception will be thrown.</remarks> /// <exception cref="System.ArgumentNullException">Thrown if no value exists with the provided name.</exception> public int IndexOf(string name) { IEventMetricValueDefinition value = this[name]; return(IndexOf(value)); }