AnsweredAssumed Answered

AF Summary Performance

Question asked by AlistairFrith on Feb 26, 2015
Latest reply on Feb 27, 2015 by AlistairFrith

We are writing an application that need to do time-based summaries on AF attributes (most of which are PI-Point data references but some may be AF Formula calculations).

 

Previously we were going to do this as a set of DailyTotal Abacus tags, calculated once per day, but that has various technical problems that I won't go into. So we were looking at calculating this on demand using the AFData.Summary() and AFData.Summaries() methods. I confidently said this would be fast but others disagreed.

 

I wrote a noddy console application to test it out and the results were a little shocking:

 

Starting

connecting to AF...

Done

Getting an attribute...

Done

Getting a total over 1 month...

Done in 16776.6775 milliseconds

Doing it again...

Done in 172.0112 milliseconds

Getting the actual values...

There were 22057 values in this 1 month time range

Finished. Press [Return] to exit

 

So the first time, it takes 16 seconds to summarise a months-worth (22,057 values) of data for a single attribute! It obviously then caches the data since the second time it is much quicker. But 16 seconds!?!?!?

I read in another thread something that implied to me that AFData.Summary() actually brings the events back to the client and summarises them there, wheras what I expected was for the server to do the work. Is this correct? That could partly explain the 16 seconds since the server is in another country. How do I get this done by the server?

 

Here is my code (apologies for the rubbish indenting and lack of curly braces, I can't get this editor to properly present code!):

using OSIsoft.AF;

using OSIsoft.AF.Asset;

using OSIsoft.AF.Data;

using OSIsoft.AF.Time;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace ConsoleApplication1

   class Program

 

   static void Main(string[] args)

 

   Console.WriteLine("Starting");

 

   const string _afServerName = "MyServer";

 

   const string _afDatabaseName = "MyDB";

 

   const string path = @"\\MyServer\MyDB\TCO\KTL\Flare 5\Sweet Gas Streams\21PIC31005|Inputs|Pressure";

 

   AFTimeRange timeRange = new AFTimeRange("*-31d", "T");

  

   Console.WriteLine("connecting to AF...");

 

   AFDatabase afDB = AFConnect(_afServerName, _afDatabaseName, false);

 

   Console.WriteLine("Done");

  

   Console.WriteLine("Getting an attribute...");

 

   AFAttribute attribute = (AFAttribute)AFAttribute.FindObject(path);

 

   Console.WriteLine("Done");

  

   Console.WriteLine("Getting a total over 1 month...");

 

   DateTime start = DateTime.Now;

 

   IDictionary<AFSummaryTypes, AFValue> summary = attribute.Data.Summary(timeRange, AFSummaryTypes.Total, AFCalculationBasis.TimeWeighted, AFTimestampCalculation.Auto);

 

   DateTime end = DateTime.Now;

 

   Console.WriteLine(string.Format("Done in {0} milliseconds", (end-start).TotalMilliseconds ));

 

   Console.WriteLine("Doing it again...");

 

  start = DateTime.Now;

 

  summary = attribute.Data.Summary(timeRange, AFSummaryTypes.Total, AFCalculationBasis.TimeWeighted, AFTimestampCalculation.Auto);

 

  end = DateTime.Now;

 

   Console.WriteLine(string.Format("Done in {0} milliseconds", (end - start).TotalMilliseconds));

 

   Console.WriteLine("Getting the actual values...");

 

   AFValues values = attribute.Data.RecordedValues(timeRange, AFBoundaryType.Inside, null, "", false);

 

   Console.WriteLine(string.Format("There were {0} values in this 1 month time range", values.Count()));

 

   Console.WriteLine("Finished. Press [Return] to exit");

 

   Console.ReadLine();

  

   /// <summary>

   /// Connect to AF

   /// </summary>

   /// <param name="afServerName"></param>

   /// <param name="afDatabaseName"></param>

   /// <returns>True on success, false on failure. </returns>

   static public AFDatabase AFConnect(string afServerName, string afDatabaseName, bool unconnected)

 

   try

 

   PISystems afSystems = new PISystems();

 

   PISystem afServer = afSystems[afServerName];

 

  afServer.Connect();

 

   AFDatabase afDB = afServer.Databases[afDatabaseName];

 

   return afDB;

 

   catch

 

   //TODO: We should really catch the exception and make it available.

 

   return null;

}

Outcomes