/// <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; }
/// <summary> /// Create a new value definition with the supplied name and type. The name must be unique in this collection /// </summary> /// <remarks>Internally, only simple type are supported. Any non-numeric, non-DateTimeOffset type will be converted to a string /// using the default ToString capability when it is recorded.</remarks> /// <param name="name">The unique name for this value definition</param> /// <param name="type">The simple type of this value</param> /// <param name="caption">The end-user display caption for this value</param> /// <param name="description">The end-user description for this value.</param> /// <returns>The newly created value definition</returns> public EventMetricValueDefinition Add(string name, Type type, string caption, string description) { lock (m_Definition.Lock) // Is this really needed? Can't hurt.... { //if we are read-only, you can't add a new value if (IsReadOnly) { throw new NotSupportedException("The collection is read-only"); } //make sure we got everything we require if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException(nameof(name)); } if (type == null) { throw new ArgumentNullException(nameof(type)); } //make sure the name is unique if (m_Dictionary.ContainsKey(name)) { throw new ArgumentException("There is already a value definition with the provided name.", nameof(name)); } //create a new value definition EventMetricValueDefinitionPacket newPacket = new EventMetricValueDefinitionPacket(m_Definition.Packet, name, type, caption, description); EventMetricValueDefinition newDefinition = new EventMetricValueDefinition(m_Definition, newPacket); //forward the call to our one true add method Add(newDefinition); //and return the new object to our caller so the have the object we created from their input. return(newDefinition); } }
internal EventMetricValueDefinition Add(EventMetricValueDefinitionPacket packet) { //Even though this is an internal method used just during rehydration of data, we are going to //enforce all of the integrity checks to be sure we have good data //The one thing we CAN'T check is read only, because we're used when the collection IS read only. //make sure we got everything we require if (string.IsNullOrEmpty(packet.Name)) { throw new ArgumentNullException(nameof(packet)); } if (packet.Type == null) { throw new ArgumentNullException(nameof(packet)); } lock (m_Definition.Lock) { //make sure the name is unique if (m_Dictionary.ContainsKey(packet.Name)) { throw new ArgumentException("There is already a value definition with the provided name.", nameof(packet)); } //create a new value definition EventMetricValueDefinition newDefinition = new EventMetricValueDefinition(m_Definition, packet); //forward the call to our one true add method Add(newDefinition); //and return the new object to our caller so they have the object we created from their input. return(newDefinition); } }
/// <summary> /// Records a value to the values array of this sample given its value definition. /// </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="valueDefinition">The metric value definition object of the value to be recorded.</param> /// <param name="value">The value to be recorded.</param> public void SetValue(EventMetricValueDefinition valueDefinition, object value) { //make sure we got a value definition if (valueDefinition == null) { throw new ArgumentNullException(nameof(valueDefinition)); } //look up the numerical index in the collection so we know what offset to put it in the array at int valueIndex = Metric.Definition.Values.IndexOf(valueDefinition); //if we didn't find it, we're hosed if (valueIndex < 0) { #if DEBUG //if we're compiled in debug mode, tell the user they blew it. throw new ArgumentOutOfRangeException(nameof(valueDefinition), valueDefinition.Name); #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 named {1} for metric definition {0}", Metric.Definition.Name, valueDefinition.Name); } return; #endif } //coerce it into the right type object storedValue; if (value == null) { //you can always store a null. And we can't really check it more, so there. storedValue = null; } else { //get the type so we can verify it Type valueType = value.GetType(); //is it close enough to what we're expecting? if (valueDefinition.IsTrendable) { if (EventMetricDefinition.IsTrendableValueType(valueType)) { storedValue = value; } else { //no, it should be trendable and it isn't. store null. storedValue = null; } } else { //we don't care what it is because we're going to coerce it to a string. storedValue = value.ToString(); } } //now write out the value to the correct spot in the array Packet.Values[valueIndex] = storedValue; }
/// <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); }