8 Replies Latest reply on Feb 15, 2012 11:53 AM by MvanderVeeken

    How to compare PIValues Objects.

    cguimaraes

      Hi Everyone,

       

      I'm trying to compare two tags at diferent PI Systems to see if they have the same events, for that I'm using VS2008 at a VB project where at some point I load the values at the following manner:

       
              Dim _point1 As PISDK.PIPoint = ServPickList1.SelectedServer.PIPoints(tagComboBox1.SelectedItem.ToString())
              Dim _point2 As PISDK.PIPoint = ServPickList2.SelectedServer.PIPoints(tagComboBox1.SelectedItem.ToString())
      
              Dim _archiveValues_origem As PISDK.PIValues = _point1.Data.RecordedValues(DT1.Value, DT2.Value, BoundaryTypeConstants.btInside, "", PISDK.FilteredViewConstants.fvShowFilteredState, Nothing)
              Dim _archiveValues_destino As PISDK.PIValues = _point2.Data.RecordedValues(DT1.Value, DT2.Value, PISDK.BoundaryTypeConstants.btAuto, "", PISDK.FilteredViewConstants.fvShowFilteredState, Nothing)
      

       The point is, what is the most eficient way to compare the Objects _archiveValues_origem and _archiveValues_destino to see if they have the same events?

       

      Thanks for the help,

       

      Regards,

       

      Carlos Guimarães

       

       

       

       

        • Re: How to compare PIValues Objects.

          Hi Carlos,

           

          Probably best to use IPIValues2.GetValuesArray method and compare the arrays.  Check out the Optimizing PI SDK Applications white paper in the library for some more details on it.

            • Re: How to compare PIValues Objects.
              MvanderVeeken

              I think you can do this by using LINQ and the Cast<T>() extension method

               

              Disclaimer: I have not tested this in every situation, but I think something like this could be a good way to go. 

               

              Here is some sample code (C#)

               

               

               
                          //Getting values from server 1
                          var values1 = point1.Data.RecordedValues(startDate, endDate, PISDK.BoundaryTypeConstants.btAuto, "", PISDK.FilteredViewConstants.fvShowFilteredState);
                          //Getting values from server 2
                          var values2 = point2.Data.RecordedValues(startDate.AddHours(-1), endDate, PISDK.BoundaryTypeConstants.btAuto, "", PISDK.FilteredViewConstants.fvShowFilteredState);
                          
                          //Cast values1 to an IEnumerable so we can use LINQ statements later on
                          var cValues1 = values1.Cast();
                          //Cast values2 to an IEnumerable so we can use LINQ statements later on
                          var cValues2 = values2.Cast();
              
                          //Find PIValues that exist in cValues1, but not in cValues2
                          var exc1 = cValues1.Where(c1 => cValues2.SingleOrDefault(c2 => c1.TimeStamp.UTCFileTime == c2.TimeStamp.UTCFileTime && c1.Value == c2.Value) == null);
                          //Find PIValues that exist in cvalues2, but not in cValues1
                          var exc2 = cValues2.Where(c2 => cValues1.SingleOrDefault(c1 => c2.TimeStamp.UTCFileTime == c1.TimeStamp.UTCFileTime && c2.Value == c1.Value) == null);
              
                          //If exc1 or exc2 contain any elements, then the data is not exactly the same.
                          bool areEqual = (exc1.Any() || exc2.Any()) ? false : true;
              

               

               

               

               

               

               


                • Re: How to compare PIValues Objects.

                  Michael, don't you still have the interop marshalling overhead?  Over large time periods with high frequency data I would first get the arrays and then parse.

                    • Re: How to compare PIValues Objects.
                      MvanderVeeken

                      Good point! I will investigate.

                        • Re: How to compare PIValues Objects.
                          MvanderVeeken

                          Hacked together a little program to compare performance between the IPIValues2 interface and IEnumerable.Cast<T>();

                           

                          It looks like you are absolutely right. getting 30 days of values (about 800) from 'sinusoid': IPIValues2.GetValueArrays takes about 16ms, using PIValues.Cast<PIValue> takes 1.9 seconds. That's a pretty huge difference.

                           

                          Advice to the OP: use IPIValues2 instead.

                           

                          The issue isn't with PIValues.Cast<PIValue> itself, it has to do with a method to get the information 'out of' the PIValue object. In my case I use IENumerable.Select() into an anonymous class (see code samples below). It seems that this takes up the actual time. I also tried selecting Tuple<T1, T2, T3>, but that didn't provided any real difference in timings.

                           

                          Sample code used:

                           

                           

                           
                              class Program
                              {
                                  static void Main(string[] args)
                                  {
                                      var start = "*-30d";
                                      var end = "*";
                          
                                      var watch1 = new Stopwatch();
                          
                                      var watch2 = new Stopwatch();
                          
                                      for (int i = 0; i < 10; i++)
                                      {
                          
                                          Console.WriteLine("Getting values using IPIValues2");
                                          watch2.Start();
                                          GetRecordedValuesUsingIPIValues2(start, end);
                                          watch2.Stop();
                                          Console.WriteLine("Getting values using IPIValues2 took {0}", watch2.Elapsed.ToString());
                                          watch2.Reset();
                          
                                          Console.WriteLine("Getting values using cast");
                                          watch1.Start();
                                          GetRecordedValuesUsingCast(start, end);
                                          watch1.Stop();
                                          Console.WriteLine("Getting values using cast took {0}", watch1.Elapsed.ToString());
                                          watch1.Reset();
                                      }
                                      //Find PIValues that exist in cValues1, but not in cValues2
                                      //var exc1 = cValues1.Where(c1 => cValues2.SingleOrDefault(c2 => c1.TimeStamp.UTCFileTime == c2.TimeStamp.UTCFileTime && c1.Value == c2.Value) == null);
                                      ////Find PIValues that exist in cvalues2, but not in cValues1
                                      //var exc2 = cValues2.Where(c2 => cValues1.SingleOrDefault(c1 => c2.TimeStamp.UTCFileTime == c1.TimeStamp.UTCFileTime && c2.Value == c1.Value) == null);
                          
                                      ////If exc1 or exc2 contain any elements, then the data is not exactly the same.
                                      //bool areEqual = (exc1.Any() || exc2.Any()) ? false : true;
                                      Console.ReadLine();
                                  }
                          
                                  private static void GetRecordedValuesUsingIPIValues2(string start, string end)
                                  {
                                      var sdk = new PISDK.PISDK();
                                      var server = sdk.Servers.DefaultServer;
                          
                                      var point1 = server.PIPoints["sinusoid"];
                                      var point2 = server.PIPoints["sinusoid"];
                          
                                      //Getting values from server 1
                                      var values1 = point1.Data.RecordedValues(start, end, PISDK.BoundaryTypeConstants.btAuto, "", PISDK.FilteredViewConstants.fvShowFilteredState);
                                      //Getting values from server 2
                                      var values2 = point2.Data.RecordedValues(start, end, PISDK.BoundaryTypeConstants.btAuto, "", PISDK.FilteredViewConstants.fvShowFilteredState);
                          
                                      var iValues1 = values1 as PISDK.IPIValues2;
                          
                                      var iValues2 = values2 as PISDK.IPIValues2;
                          
                                      var iValues1Count = values1.Count;
                          
                                      
                                      var v1arr1 = Array.CreateInstance(typeof(object), iValues1Count);
                                      var v1arr2 = Array.CreateInstance(typeof(object), iValues1Count);
                                      var v1arr3 = Array.CreateInstance(typeof(object), iValues1Count);
                                      iValues1.GetValueArrays(out v1arr1, out v1arr2, out v1arr3);
                          
                                 
                                      var iValues2Count = values2.Count;
                          
                                      var v2arr1 = Array.CreateInstance(typeof(object), iValues2Count);
                                      var v2arr2 = Array.CreateInstance(typeof(object), iValues2Count);
                                      var v2arr3 = Array.CreateInstance(typeof(object), iValues2Count);
                          
                                      iValues2.GetValueArrays(out v2arr1, out v2arr2, out v2arr3);
                          
                                   
                          
                                  }
                          
                                  private static void GetRecordedValuesUsingCast(string start, string end)
                                  {
                                      var sdk = new PISDK.PISDK();
                                      var server = sdk.Servers.DefaultServer;
                          
                                      var point1 = server.PIPoints["sinusoid"];
                                      var point2 = server.PIPoints["sinusoid"];
                          
                                      //Getting values from server 1
                                      var values1 = point1.Data.RecordedValues(start, end, PISDK.BoundaryTypeConstants.btAuto, "", PISDK.FilteredViewConstants.fvShowFilteredState);
                                      //Getting values from server 2
                                      var values2 = point2.Data.RecordedValues(start, end, PISDK.BoundaryTypeConstants.btAuto, "", PISDK.FilteredViewConstants.fvShowFilteredState);
                          
                                      //Cast values1 to an IEnumerable so we can use LINQ statements later on
                                      var cValues1 = values1.Cast().Select(p => new { Value = p.Value, TimeStamp = p.TimeStamp, ValueAttributes = p.ValueAttributes }).ToList();
                                      //Cast values2 to an IEnumerable so we can use LINQ statements later on
                                      var cValues2 = values2.Cast().Select(p => new { Value = p.Value, TimeStamp = p.TimeStamp, ValueAttributes = p.ValueAttributes }).ToList();
                                    
                                  }
                              }