rdavin

AFSDK 2.5 Summary Method Examples

Blog Post created by rdavin Employee on Jun 26, 2012

Continuing my look into the imminent release of AF 2.5 with RDA, today’s topic will look at the new Summary method in AFSDK. Though AF 2.5 has gone through 2 CTP releases and 1 Beta, bear in mind that the current AF 2.5 code base is still Beta and could be subject to change before its production release in 2012Q3.

 

The Summary method is nothing new for PISDK users. But thanks to the Rich Data Access of AF 2.5, AF Attributes – not just PI Points – will have a much anticipated Summary method. There are a many differences between what the two SDK’s offer, sometimes subtle and sometimes not subtle at all.

 

Let’s take a brief look at some differences before showing the code examples:

 

What is returned?

 

PISDK returns a solitary PIValue for one summary type of one PI Point for one time range.
AFSDK returns an IDictionary object for one or more summary types for one AF Attribute for one time range. The key to the dictionary is the summary type and its value is an AFValue object.

 

The PercentGood associated with the summary

 

PISDK sticks this in a ValueAttributes property of the returned PIValue.
AFSDK includes it, upon request, as an additional Summary type to be returned.

 

b>How times are specified

 

PISDK expects two parameters for StartTime and EndTime as Objects.
AFSDK expects one AFTimeRange parameter.

 

What gets summarized?

 

PISDK obviously only works for PI Point objects.
AFSDK could work for any AF Attribute if the data reference supports the method.

 

A Difference with Count

 

PISDK the Count will produce a Double.
AFSDK the Count will produce an Integer.

 

A Difference with Average

 

PISDK the Average could throw an exception if there are no recorded values in the time range.
AFSDK the Average won’t throw an exception; however the Average (i.e. AFValue.Value) will either be a Double with a good status OR a String with a bad status.

 

Let’s take a look at some examples to give you a hint of possibilities. The AFSDK examples won’t focus on just tag centric, i.e. PI Point data references, but rather for any AF Attribute. The first 2 examples contrast PISDK to AFSDK, and the 3rd example is AFSDK only.

 

Example 1 – PISDK Count

 
        // Produce an event weighted Count for a PISDK PIPoint
        public static void PISDK_SummaryExample1(PISDK.PIPoint tag, object StartTime, object EndTime)
        {
            // Convention of -1 denotes unknown Count
            int iCount = -1;
            PISDK.PIValue pv = null;
            // Count will be a Double.
            pv = tag.Data.Summary(StartTime, EndTime, ArchiveSummaryTypeConstants.astCount, CalculationBasisConstants.cbEventWeighted);
            iCount = Convert.ToInt32((double)pv.Value);
            Console.WriteLine("PI Point '{0}' has {1} recorded events from {2} through {3}", tag.Name, iCount, StartTime, EndTime);
        }

Example 1 – AFSDK Count

 
        // Produce an event weighted Count for an AFSDK Attribute
        public static void AFSDK_SummaryExample1(AFAttribute attribute, object StartTime, object EndTime)
        {
            // Convention of -1 denotes unknown Count
            int iCount = -1;
            // Safe Practice: first check that the data method is supported
            if ((attribute.SupportedDataMethods & AFDataMethods.Summary) == AFDataMethods.Summary)
            {
                // Merge StartTime and EndTime objects into one timeRange
                AFTimeRange timeRange = new AFTimeRange(new AFTime(StartTime), new AFTime(EndTime));
                // Prep a IDictionary object to hold initial result 
                IDictionary summary = null;
                summary = attribute.Data.Summary(timeRange, AFSummaryTypes.Count, AFCalculationBasis.EventWeighted, AFTimestampCalculation.Auto);
                // Trivial Check: make sure returned IDictionary object has the intended summary key
                if (summary.ContainsKey(AFSummaryTypes.Count))
                {
                    // The Count is returned as an Integer
                    AFValue pv = summary[AFSummaryTypes.Count]; 
                    iCount = (int)pv.Value;
                }
            }
            Console.WriteLine("Attribute '{0}' has {1} recorded events from {2} through {3}", attribute.Name, iCount, StartTime, EndTime);
        }

Example 2 – PISDK Average

 

          // Produce a time weighted Average for a PISDK PIPoint
        public static void PISDK_SummaryExample2(PISDK.PIPoint tag, object StartTime, object EndTime)
        {
            double dAverage = double.NaN;
            try
            {
                PISDK.PIValue pv = null;
                // The Average will either be returned as a Double, OR an exception will be thrown.
                pv = tag.Data.Summary(StartTime, EndTime, ArchiveSummaryTypeConstants.astAverage, CalculationBasisConstants.cbTimeWeighted);
                // If execution reaches this point, pv is valid and pv.Value is a Double.
                dAverage = (double)pv.Value;
            }
            catch (Exception e)
                {
                    // No Operation.  The exception message is "Calculation failed."
                    // Ignore it and accept default dAverage of double.NaN
                }
            Console.WriteLine("PI Point '{0}' has an average of {1} from {2} through {3}", tag.Name, dAverage, StartTime, EndTime);
        }

Example 2 – AFSDK Average

 
         // Produce a time weighted Average for an AFSDK Attribute
        public static void AFSDK_SummaryExample2(AFAttribute attribute, object StartTime, object EndTime)
        {
            double dAverage = double.NaN;
            // Safe Practice: first check that the data method is supported
            if ((attribute.SupportedDataMethods & AFDataMethods.Summary) == AFDataMethods.Summary)
            {
                // Merge StartTime and EndTime objects into one timeRange
                AFTimeRange timeRange = new AFTimeRange(new AFTime(StartTime), new AFTime(EndTime));
                // Prep a IDictionary object to hold initial result 
                IDictionary summary = null;
                summary = attribute.Data.Summary(timeRange, AFSummaryTypes.Average, AFCalculationBasis.TimeWeighted, AFTimestampCalculation.Auto);
                // Trivial Check: make sure returned IDictionary object has the intended summary key
                if (summary.ContainsKey(AFSummaryTypes.Average))
                {
                    // The Average (pv.Value) will be returned either as a Double,
                    // OR have a bad status with value = "[-11059] No Good Data For Calculation"
                    AFValue pv = summary[AFSummaryTypes.Average];
                    // One way to only grab a good number
                    if ((pv.Status & AFValueStatus.QualityMask) == AFValueStatus.Good)
                    {
                        dAverage = (double)pv.Value;
                    }
                    //// Another way to only grab a good number
                    //if (pv.Value is double)
                    //{
                    //    dAverage = (double)pv.Value;
                    //}
                }
            }
            Console.WriteLine("Attribute '{0}' has an average of {1} from {2} through {3}", attribute.Name, dAverage, StartTime, EndTime);
        }

Example 3 – AFSDK with Multiple Summary Types

 
         // AFSDK Summary allows multiple summary types over entire time range.  
        // Produce summary of event-weighted Count and PercentGood.
        public static void AFSDK_SummaryExample3(AFAttribute attribute, object StartTime, object EndTime)
        {
            int iCount = -1;
            double dPctGood = double.NaN;
            if ((attribute.SupportedDataMethods & AFDataMethods.Summary) == AFDataMethods.Summary)
            {
                AFTimeRange timeRange = new AFTimeRange(new AFTime(StartTime), new AFTime(EndTime));
                IDictionary summary = null;
                summary = attribute.Data.Summary(timeRange, AFSummaryTypes.Count | AFSummaryTypes.PercentGood, AFCalculationBasis.EventWeighted, AFTimestampCalculation.Auto);
                if (summary.ContainsKey(AFSummaryTypes.Count))
                {
                    AFValue pv = summary[AFSummaryTypes.Count];
                    iCount = (int)pv.Value;
                }
                if (summary.ContainsKey(AFSummaryTypes.PercentGood))
                {
                    AFValue pv = summary[AFSummaryTypes.PercentGood];
                    dPctGood = (double)pv.Value;
                }
                Console.WriteLine("Attribute '{0}' has {1} recorded events with {2}% good from {3} through {4}", attribute.Name, iCount, dPctGood, StartTime, EndTime);
            }
        }

That’s at least some early attempts at some code. I wish I had time and space to post VB equivalents but I barely have time at work to even squeak this blog out. For those of you who haven't had a time to dive too deeply into AF 2.5, I at least wanted to give you a heads up on some new features.

Outcomes