7 Replies Latest reply on Nov 20, 2017 9:54 PM by ChrisHylton

    FindPIPoints IsGood Performance

    ChrisHylton

      Haven't found anything in multiple search attempts.  Anyone have any ideas.

       

      Trying to use the PI AF SDK to identify 'bad' points.  If I use the PiPointTagSearchPage control, I can build a query that returns all the points in just a few seconds.  I think the time it takes is literally just the network transfer of the PIPoint objects and PI is doing this server side VERY fast.  But when trying to do this with the SDK, using FindPIPoints, I can't seem to find a way to get at .CurrentValue.IsGood.

       

      The code below works, and returns in a decent amount of time.  But if I expand it to Tag = "*", for all points, forget about it.

       

      Thinking there has to be a way to pass IsGood to the server, but how?  How does the user control do this so fast (all points in 10 seconds for bad and 20-30 seconds for good)?

       

      Thanks!

       

              Dim piServer As PIServer

              piServer = OSIsoft.AF.PI.PIServer.FindPIServer("scadapi")

              piServer.Connect()

       

              Dim qNotDecommissioned As New PIPointQuery

              qNotDecommissioned.AttributeName = "pointsource"

              qNotDecommissioned.AttributeValue = "DECOMMISSIONED"

              qNotDecommissioned.Operator = OSIsoft.AF.Search.AFSearchOperator.NotEqual

       

              Dim qTag As New PIPointQuery

              qTag.AttributeName = "tag"

              qTag.AttributeValue = "A*"

       

              Dim list = PIPoint.FindPIPoints(piServer, {qTag, qNotDecommissioned}).ToList

              Dim good = list.Where(Function(p) p.CurrentValue.IsGood).ToList

              Dim bad = list.Where(Function(p) Not p.CurrentValue.IsGood).ToList

        • Re: FindPIPoints IsGood Performance
          vkaufmann

          Hi Chris,

           

          I don't think this is an issue of server vs client processing, rather your LINQ query is enumerating through a large list of tags and each iteration is causing a round-trip to the server. I will do some investigation to see if there is an equivalent method to force this into a bulk call so that the IsGood check is handled in 1 RPC.

           

          --Vince

          • Re: FindPIPoints IsGood Performance
            Mike Zboray

            Try using PIPointList which has bulk value retrieval methods. Depending on how many points you are retrieving you might want to batch into lists of say 10k points. I'm not as familiar with VB.NET but it might look something like this:

             

            Dim points = PIPoint.FindPIPoints(piServer, {qTag, qNotDecommissioned})

            Dim pointList As New PIPointList points

            Dim results = pointList.CurrentValue()

             

            Then results is basically a dictionary mapping the PIPoint to the current value as an AFValue. It also has some different levels of error reporting in various error-related properties.

            3 of 3 people found this helpful
              • Re: FindPIPoints IsGood Performance
                ChrisHylton

                Mike, that was the ticket!  Thanks.  Just tried this in conjunction w/ my existing FindPIPoints call and it's pretty fast.  About 16 seconds for a single 'pointsouce', which represents about 180,000 of the 275,000 I mentioned in my other reply.  The Linq to split good vs bad is instant at that point, obviously.

                 

                Thanks a ton!

                -Chris

              • Re: FindPIPoints IsGood Performance
                tramachandran

                A Bad state indicates that the current value of a PI point is a digital state from the system digital state set in the archive and can be inferred by AFValue.IsGood property.

                Possible ways to speed up your query is to use PIPointList.CurrentValue to make bulk calls to avoid separate calls to the PI Data Archive for the current values.

                 

                I would do something similar to this:

                             string query = "Tag:'*'";

                            var pointsList = new PIPointList();

                            pointsList.AddRange(PIPoint.FindPIPoints(MyServer, query, searchNameAndDescriptor: false));

                            AFListResults<PIPoint, AFValue> results = pointsList.CurrentValue();

                            foreach (var r in results)

                            {

                                if (r.IsGood) goodpointsList.Add(r.PIPoint);

                                else badpointsList.Add(r.PIPoint);

                            }

                • Re: FindPIPoints IsGood Performance
                  John Messinger

                  The issue here is based on a combination of things that both Vincent Kaufmann and Mike Zboray have indicated. Your initial code is making multiple round trips to the server to generate your two lists. As Mike suggested, I would use the bulk call on the PIPointList object to return the snapshot values for your points of interest. You can then run your LINQ query over the result set that is returned from that initial bulk call, and you won't need to make additional RPC's to the server. Using either code snippet above, you will still have two calls to the server - one to build the initial point list, and the second to make the bulk data call, but from there it would then all be client side processing with LINQ to build your two filtered lists of good and bad points.

                  1 of 1 people found this helpful
                  • Re: FindPIPoints IsGood Performance
                    ChrisHylton

                    Thanks all.  That's exactly what I was looking for.  Yes, I understand my LINQ statements were making a massive number of round trips and that's basically what I was wondering if there was a way to put the 'IsGood' part of that into the first call for FindPIPoints so that it occurred in 1 shot.  So I'll explore the PIPointList method, that sounds promising and should do exactly what I want by pushing the work back to PI.  It will be interesting to see how this performs given the size of the list I'm asking for.  I'm trying to basically find Good vs Bad for the entire point collection, which is currently around 275000 points.

                     

                    I'll post back after I give it a try.

                     

                    Thanks!

                    -Chris

                      • Re: FindPIPoints IsGood Performance
                        ChrisHylton

                        FWIW, the solution below.  Ultimately as part of this exercise we have determined that IsGood isn't necessarily what we wanted, we've got some analog data in here and as mentioned above, I suspect that IsGood was only getting me part of the answer on the state based points.  In order to get the full picture, needed to look at the value.  We are feeding PI from another system and when a data value is considered BAD by that system, we pump in a state of failed, which translates to a .Value of 'failed'.

                         

                        Ultimately took about 25 seconds to run.  MUCH better than never :-)

                         

                         

                        Dim piServer As PIServer
                        piServer = OSIsoft.AF.PI.PIServer.FindPIServer("scadapi")
                        piServer.Connect()

                         

                        Dim sources As List(Of String) = {"COT", "ROT", "SCC"}.ToList

                         

                        Dim qNotDecommissioned As New PIPointQuery
                        qNotDecommissioned.AttributeName = "pointsource"
                        qNotDecommissioned.AttributeValue = String.Empty

                         

                        Dim qTag As New PIPointQuery
                        qTag.AttributeName = "tag"
                        qTag.AttributeValue = "*"

                         

                        Dim start As Date = Now

                         

                        sources.ForEach(
                        Sub(source)
                          qNotDecommissioned.AttributeValue = source
                          Dim list = PIPoint.FindPIPoints(piServer, {qTag, qNotDecommissioned}).ToList
                          Dim pointList As New PIPointList(list)
                          Dim results = pointList.CurrentValue
                          Dim good = results.Where(Function(r) r.Value.ToString.ToLower <> "failed").ToList
                          Dim bad = results.Where(Function(r) r.Value.ToString.ToLower = "failed").ToList
                          Debug.WriteLine(String.Format("{0} {1} {2} {3}", source, list.Count, good.Count, bad.Count))
                        End Sub)

                         

                        MsgBox(Now.Subtract(start).TotalSeconds)