7 Replies Latest reply on Jan 23, 2018 1:38 AM by ernstamort

    AFSDK FindPIPoints in Powershell

    gheorghe.vatui

      Hello PI Square,

       

      I was trying to get all tags from a PI Server in Powershell with the latest AFSDK assembly.

       

      I was looking at the following example found on the Square but just can't get the query parameter right:

       

      PIPointQuery query1 = newPIPointQuery(PICommonPointAttributes.Tag, OSIsoft.AF.Search.AFSearchOperator.Equal, "s*");  
      IEnumerable<PIPoint> foundPoints = PIPoint.FindPIPoints(myPIServer, new PIPointQuery[] { query1 });  
      foreach (PIPoint currentPIPoint in foundPoints)  
      {  
      Console.WriteLine(currentPIPoint.Name); 
      

       

       

      I am trying:

       

          $attributesToGet = @("descriptor","engunits")
          $tagQuery1 = New-Object OSIsoft.AF.PI.PIPointQuery("Tag", "Equal", "*");
          $conditions = New-Object OSIsoft.AF.PI.PIPointQuery($tagQuery1)
          $tagObjList = [OSIsoft.AF.PI.PIPoint]::FindPIPoints($PIServerObj, (New-Object OSIsoft.AF.PI.PIPointQuery[]{$tagQuery1}), $attributesToGet)
      

       

      or trying to use a list  :

       

      $conditions = @($tagQuery1)
      $tagObjList = [OSIsoft.AF.PI.PIPoint]::FindPIPoints($PIServerObj, $conditions, $attributesToGet)
      

       

      OR:

       

          $attributesToGet = @("descriptor","engunits")
          $tagQuery1 = New-Object OSIsoft.AF.PI.PIPointQuery("Tag", "Equal", "*")
      
      
          $tagObjList = [OSIsoft.AF.PI.PIPoint]::FindPIPoints($PIServerObj, (New-Object Collections.Generic.IEnumerable[OSIsoft.AF.PI.PIPointQuery] {$tagQuery1}), $attributesToGet)
      
      
      
      

       

      Neither worked so far. Obviously I cannot satisfy the type requirement  "IEnumerable" for the query :

       

      Parameters

      piServer
      Type: OSIsoft.AF.PI.  PIServer
      The PIServer to search for the desired PIPoint objects.
      query
      Type: System.Collections.Generic.  IEnumerablePIPointQuery
      A list of PIPoint attribute query filters that are ANDed together to find the desired PIPoint objects. The FindPIPoints(PIServer, String, Boolean, IEnumerable<   String>   , AFSearchTextOption) method can be used to parse a string query and generate this list of queries.
      attributeNames (Optional)
      Type: System.Collections.Generic.  IEnumerableString
      The list of PIPoint attribute names to be loaded from the server as the PIPoint objects are found. The list of PIPoint attribute names can be obtained from the PIPointClass.GetAttributes method or the PICommonPointAttributes class. The GetAttribute(String) method can be used to access the loaded attribute values. If null  , then no attribute values are loaded for the returned PIPoints.

      Return Value

      Type: IEnumerablePIPoint

      Returns the enumerable list of PIPoint objects matching the specified query.

       

      Can you share an example of how to do this in Powershell ?  Not any single example in the AFSDK help file is not at all encouraging !...

       

      Thank you in advance

        • Re: AFSDK FindPIPoints in Powershell
          gregor

          Hello Gheorghe,

           

          I have been looking into the first overload of static method FindPIPoints() for quite a while without finding a way to make it work. The error I am usually getting is "Index was outside the bounds of the array". I will ask AF SDK development to look into the issue.

           

          The suggestion made here did not return any error but the ArrayList was empty too.

           

          What should work with your use case is the 4th overload FindPIPoints(PIServer, String, String, IEnumerable<String>)

          Can you please try this?

           

          [OSIsoft.AF.PI.PIPoint]::FindPIPoints($piServer, "s*", $null, $null)
          
          1 of 1 people found this helpful
            • Re: AFSDK FindPIPoints in Powershell
              gheorghe.vatui

              Hello Gregor,

               

              thanks for the suggestion !

               

              I tried :

               

              $tagObjList = [OSIsoft.AF.PI.PIPoint]::FindPIPoints($PIServerObj,"*",$null,$null)

               

              That worked, the good news is it found all tags on the required PI Server (nearly 35000).


              I would have two questions:

               

              1. there are servers with 200K-300K tags and I don't want to import all attributes of all tags (correct me if I am wrong here - I assume the above imported all tag attributes).

                  I need only the (name +) descriptor and engineering units for each tags.  How can I get that ?

               

                   For instance, I tried :

               

                  $attributesToGet = @("descriptor","engunits")

                  $tagQuery1 = New-Object OSIsoft.AF.PI.PIPointQuery("Tag", "Equal", "*")

                  $tagObjList = [OSIsoft.AF.PI.PIPoint]::FindPIPoints($PIServerObj,"*",$null,$attributesToGet)

               

                   Obviously that did not work because my $attributesToGet is not IEnumerable<string> but a plain array (of strings).

                   I guess my problem still resumes to my inability to define the IEnumerable here...

               

              2. later on (for other project) I will need to get subsets of tags based on tag name masks and other attribute masks.

                  (for example Pointsource).  Is that doable through the source filter of the same overload method ? If so, please share an

                   example ( since it is a string, will 'pointsource="abc*"' work ..?)

               

              Parameters

              piServer
              Type: OSIsoft.AF.PI.  PIServer
              The PIServer to search for the desired PIPoint objects.
              nameFilter
              Type: System.  String
              The PIPoint name filter used to search for the desired PIPoint objects.
              sourceFilter (Optional)
              Type: System.  String
              The PIPoint source filter used to search for the desired PIPoint objects. If null  , then only the nameFilter is used for the search.
              attributeNames (Optional)
              Type: System.Collections.Generic.  IEnumerable<  String> 
              The list of PIPoint attribute names to be loaded from the server as the PIPoint objects are found. The list of PIPoint attribute names can be obtained from the PIPointClass.GetAttributes method or the PICommonPointAttributes class. The GetAttribute(String) method can be used to access the loaded attribute values. If null  , then no attribute values are loaded for the returned PIPoints.

              Return Value

              Type: IEnumerable<  PIPoint> 

              Returns the enumerable list of PIPoint objects matching the specified nameFilter and sourceFilter.

            • Re: AFSDK FindPIPoints in Powershell
              asorokina

              Hi Gheorghe,

               

              It looks like the method overload resolution in Powershell is not being cooperative: instead of the PIPoint.FindPIPoints Method (PIServer, IEnumerable<PIPointQuery>, IEnumerable<String>) overload, the runtime binder always goes with PIPoint.FindPIPoints Method (PIServer, String, Boolean, IEnumerable<String>, AFSearchTextOption).

               

              Here is a possible explanation to it: http://stackoverflow.com/questions/13084176/powershell-method-overload-resolution-bug

               

              As a workaround you can access and invoke desired method via reflection. Here is an example that retrieves all lab tags from my test PI Data Archive server, after reflecting over the PIPoint class to locate the proper FindPIPoints overload:

               

              try{
              [Reflection.Assembly]::LoadWithPartialName("OSIsoft.AFSDK")
              
              
              $serverName = "stark1"
              $server = [OSIsoft.AF.PI.PIServer]::FindPIServer($serverName)
              
              #
              $query = New-Object OSIsoft.AF.PI.PIPointQuery -ArgumentList @('PointSource',[OSIsoft.AF.Search.AFSearchOperator]::Equal,'l')
              #$possibleAdditionalQuery = New-Object OSIsoft.AF.PI.PIPointQuery -ArgumentList @('Tag',[OSIsoft.AF.Search.AFSearchOperator]::Equal,'*')
              
              $PointQueryList = New-Object “System.Collections.Generic.List[OSIsoft.AF.PI.PIPointQuery]"
              $PointQueryList.Add($query)
              #add additional queries to the list if desired
              
              
              
              $FindMethods = [OSIsoft.AF.PI.PIPoint].GetMethods() | Where-Object {$_.Name -eq "FindPIPoints"}
              $FindPIPointMethod = $FindMethods.Item(2) #this is fragile. for production, find via parameter types
              $points = $FindPIPointMethod.Invoke($null,@($server,[System.Collections.Generic.List[OSIsoft.AF.PI.PIPointQuery]]$PointQueryList,$null))
              
              #print out the points collection
              $points
              }
              Catch
              {
              $err=$_.Exception.Message
              $err
              }
              
              2 of 2 people found this helpful
                • Re: AFSDK FindPIPoints in Powershell
                  dng

                  Anna's method works well. To select the correct overload using reflection for FindPIPoints, you can explicitly specify the parameter types as follow:

                   

                  [Type[]]$types = [OSIsoft.AF.PI.PIServer], [System.Collections.Generic.IEnumerable[OSIsoft.AF.PI.PIPointQuery]], [System.Collections.Generic.IEnumerable[string]]
                  $FindPIPointMethod = [OSIsoft.AF.PI.PIPoint].GetMethod("FindPIPoints",$types)
                  

                   

                  For your reference, the full PowerShell script that I tested:

                  [System.Reflection.Assembly]::LoadWithPartialName("OSIsoft.AFSDK") | Out-Null
                  $piSrvs = New-Object OSIsoft.AF.PI.PIServers
                  $piSrv = $piSrvs.DefaultPIServer
                  $piPointQuery = New-Object OSIsoft.AF.PI.PIPointQuery([OSIsoft.AF.PI.PICommonPointAttributes]::Tag, [OSIsoft.AF.Search.AFSearchOperator]::Equal, "s*")
                  $queryList = New-Object System.Collections.Generic.List[OSIsoft.AF.PI.PIPointQuery]
                  $queryList.Add($piPointQuery)
                  [Type[]]$types = [OSIsoft.AF.PI.PIServer], [System.Collections.Generic.IEnumerable[OSIsoft.AF.PI.PIPointQuery]], [System.Collections.Generic.IEnumerable[string]]
                  $FindPIPointMethod = [OSIsoft.AF.PI.PIPoint].GetMethod("FindPIPoints",$types)
                  $points = $FindPIPointMethod.Invoke($null,@($piSrv,[System.Collections.Generic.List[OSIsoft.AF.PI.PIPointQuery]]$queryList,$null)) 
                  
                  3 of 3 people found this helpful
                    • Re: AFSDK FindPIPoints in Powershell
                      gheorghe.vatui

                      First, a big thank you to all who replied to this post !

                      All your answers were great (even though I had to choose only one as the answer)

                       

                      I added the optional AttributeNames parameter (instead of using $null) to get the descriptor and engineering units for each tag.

                      Note that I had to cast it to [System.Collections.Generic.List[System.String]] inside the Invoke statement, otherwise just using $attributesToGet would not work.

                      I am not sure if that is expected or not, but at this point I am happy it works.

                       

                      Here is the final code I will be using:

                       

                          ####### get all tag names, descriptors and engunits #######

                       

                       

                          $tagQuery1 = New-Object OSIsoft.AF.PI.PIPointQuery([OSIsoft.AF.PI.PICommonPointAttributes]::Tag, [OSIsoft.AF.Search.AFSearchOperator]::Equal, "*")

                         

                          # define and build the query list - can add several tag queries if needed

                          $queryList = New-Object System.Collections.Generic.List[OSIsoft.AF.PI.PIPointQuery] 

                          $queryList.Add($tagQuery1)

                       

                       

                          # define and build the optional attributeNames parameter

                          # in this case it is needed because if using $null there will no tag attributes returned

                          $attributesToGet = New-Object System.Collections.Generic.List[System.String]

                          $attributesToGet.Add('Descriptor')

                          $attributesToGet.Add('Engunits')

                       

                       

                          # looks like Powersehell binds "FindPIPoints" method to the wrong overload at runtime

                          # http://stackoverflow.com/questions/13084176/powershell-method-overload-resolution-bug

                          # to get around the Powershell issue, access and invoke desired method via reflection

                          # find the correct "FindPIPoints" method overload by strongly typing all parameters and then searching for the correct method

                          # finally invoke the overload found via refelction

                          [Type[]]$types = [OSIsoft.AF.PI.PIServer], [System.Collections.Generic.IEnumerable[OSIsoft.AF.PI.PIPointQuery]], [System.Collections.Generic.IEnumerable[String]] 

                          $findPIPointsMethod = [OSIsoft.AF.PI.PIPoint].GetMethod("FindPIPoints",$types)

                          # warning: MUST cast the last two parameters to their type i.e. this will not work : $tagObjList = $findPIPointsMethod.Invoke($null,@($PIServerObj,$queryList,$attributesToGet))

                          $tagObjList = $findPIPointsMethod.Invoke($null,@($PIServerObj,[System.Collections.Generic.List[OSIsoft.AF.PI.PIPointQuery]]$queryList,[System.Collections.Generic.List[System.String]]$attributesToGet))

                       

                       

                          ####### end get all tag names, descriptors and engunits #######

                       

                       

                          ####### loop through results and call the processing function #######

                       

                       

                          Foreach($item in $tagObjList){

                             

                              Echo "This is the found tag name: $item.Name"

                              Echo "This is the fount tag's descriptor:",$item.GetAttribute('Descriptor')

                              Echo "This is the fount tag's unit: ",$item.GetAttribute('Engunits')

                              

                               # TO DO ..

                          }

                       

                       

                          ####### end loop through results and call the processing function #######

                      • Re: AFSDK FindPIPoints in Powershell
                        ernstamort

                        That's pretty impressive ...

                         

                        In PS Version 5.0 it works without the workaround, but I have to work on an earlier version and this really helps!