12 Replies Latest reply on Aug 10, 2012 8:19 AM by andreas

    Counting Occurances of a State

    rdube02

      Hi,

       

      This seems like a fairly simply thing to do, but for the life of me I just can't figure it out. I've reviewed a few other threads in here, but none of the suggestions seem to work for my case.

       

      I have a tag that has about 100 states, each representing a certain alarm condition. I was able to get the following to work, which tells me the percentage of time that each state has been true over a certain period of time.

       
        Set myPIValues = myIPICalc.PercentTrue(myStartTime, _
          myEndTime, _
          "", _
          myFilterExpression, _
          fstExpRecValwithMinSampTime, _
          "10m")
      

       What I also need to do is to find out how many occurances of each state took place between a start and an end time.  So for example if I the state changed from something else to "Overtemp", that would count as 1.  Later if it changed from something else to "Overtemp" again, my Overtemp state count would be 2.

       

      I've tried to use myIPICalc.Calculate to do this, but can't seem to get it to work.  Can any of you pros give me a hint as to how this can be done?

       

      -Ryan

       

       

        • Re: Counting Occurances of a State
          andreas

          Ryan - it depends. Each call will cause you a roundtrip. If you have ~100 states, you would require ~100 calls. Depending on the exact situation it might be better to retrive compressed data and do the counting localy.

            • Re: Counting Occurances of a State
              rdube02

              Thanks for your response. I had already written a loop to step through all states and then do the call. I guess what I was asking for was the exact syntax/command for the call. Probably a basic task but I just can't find anywhere in the documentation that deals with calling for the number of times a particular state occurred within a set time frame.

                • Re: Counting Occurances of a State
                  andreas

                  Ryan - what I would suggest is not asking PI to count. If you ask PI to count the occurances of a state, you will end up with asking PI 100 times (because of your 100 states) - this will result in 100 expensive network calls. If you, on the other side, take advantage of the fast data retrival and ask the system for all events you can use a simple loop to calculate. This is what I suggest.

                   
                                  Dim myPISDK As PISDK.PISDK = New PISDK.PISDK()
                                  Dim myServer As PISDK.Server = myPISDK.Servers.Item(strServer)
                                  Dim myTag As PISDK.PIPoint = myServer.PIPoints.Item(strTag)
                  
                                  Dim myValues As PISDK.PIValues = myTag.Data.RecordedValues(strStartTime, strEndTime, PISDK.BoundaryTypeConstants.btAuto, "", PISDK.FilteredViewConstants.fvRemoveFiltered, Nothing)
                                  Console.WriteLine("processing " & myValues.Count & " Events")
                  
                                  Dim myDigitalSet As PISDK.StateSet = myServer.StateSets.Item(myTag.PointAttributes.Item("digitalset").Value)
                  
                                  Dim dictDigitalSet As New Dictionary(Of String, Integer)
                                  For Each myDigitalState As PISDK.DigitalState In myDigitalSet
                                      dictDigitalSet.Add(myDigitalState.Name, 0)
                                  Next
                  
                                  Dim curState, prevState As String
                                  prevState = ""
                                  For Each myValue As PISDK.PIValue In myValues
                                      curState = DirectCast(myValue.Value, PISDK.DigitalState).Name
                                      If curState <> prevState Then
                                          If dictDigitalSet.ContainsKey(curState) Then
                                              dictDigitalSet.Item(curState) += 1
                                          Else
                                              dictDigitalSet.Add(curState, 1)
                                          End If
                                      End If
                                      prevState = curState
                                  Next
                  
                                  For Each strState As String In dictDigitalSet.Keys
                                      Console.WriteLine(strState & ": " & dictDigitalSet.Item(strState))
                                  Next
                  
                              End If
                  

                   

                   

                  As you see there is only one call for the tag, one call for the values and one call for the state - limiting the amount of roundtrips to the server. In general my experience with PI tells me that the network latency is usually the biggest issue, so I try to reduce the amount of calls to the PI server.

                    • Re: Counting Occurances of a State
                      rdube02

                      Andreas - thanks so much.  I should have made it more clear (maybe posted my code) that I was using the RecordedValues method.  My code looks nearly identical to yours, except I couldn't figure out how to do this part.

                       
                      For Each myValue As PISDK.PIValue In myValues 
                      curState = DirectCast(myValue.Value, PISDK.DigitalState).Name 
                      If curState <> prevState Then
                      If dictDigitalSet.ContainsKey(curState) Then
                      dictDigitalSet.Item(curState) += 1 
                      Else
                      dictDigitalSet.Add(curState, 1) 
                      End If
                      End If
                      prevState = curState 
                      Next
                      

                      That's the missing key. Thank you so much. Haven't tested yet but I'm confident it'll work. You're awesome!

                    • Re: Counting Occurances of a State

                      Andreas' technique is good if you need to find out timestamps for these occurrences as well. In case you don't need that, here's another approach you can use, which will reduce the amount of data that is being transferred between the server and the client:

                       
                      PISDK.IPICalculation iCalc = (PISDK.IPICalculation)myServer;
                      PISDKCommon.NamedValues nvs = iCalc.ExpressionSummaries(
                            "*7d", "*", "7d", "IF 'ba:phase.1' = \"Phase4\" THEN 1 ELSE 0",
                            PISDK.ArchiveSummariesTypeConstants.asTotal,
                            PISDK.CalculationBasisConstants.cbEventWeighted);
                      
                      object oTotal = "Total";
                      PISDK.PIValues pvs = (PISDK.PIValues)nvs[oTotal].Value;
                      MessageBox.Show(pvs[1].Value.ToString());
                      

                      Or, with the AF SDK:

                       
                      IDictionary results = 
                          OSIsoft.AF.Data.AFCalculation.CalculateSummaries(
                              _piServer, "IF 'ba:phase.1' = \"Phase4\" THEN 1 ELSE 0",
                              new AFTimeRange("*-7d", "*"), new AFTimeSpan(0, 0, 7), 
                              OSIsoft.AF.Data.AFSummaryTypes.Total, 
                              OSIsoft.AF.Data.AFCalculationBasis.EventWeighted, 
                              OSIsoft.AF.Data.AFSampleType.ExpressionRecordedValues, 
                              new AFTimeSpan(), OSIsoft.AF.Data.AFTimestampCalculation.Auto);
                      
                      MessageBox.Show(results[OSIsoft.AF.Data.AFSummaryTypes.Total][0].Value.ToString()); 
                      

                       

                       

                       

                        • Re: Counting Occurances of a State
                          rdube02

                          It appears I don't have the reference enabled to use the Dictionary object, does anyone know what that references is?

                            • Re: Counting Occurances of a State
                              kilgored

                              System.Collections.Generic

                                • Re: Counting Occurances of a State
                                  rdube02

                                  No idea what that refers to....I'm working in VBA and would need the name of the library with that object. There's nothing in there that looks like that other than "System", but that' didn't do it.

                                    • Re: Counting Occurances of a State
                                      rdube02

                                      Nevermind, I might be able to get it working without using the dictDigitalSet bit.

                                        • Re: Counting Occurances of a State

                                          Oh, you're working in VBA... then you should stick with what Andreas told you or slight variation of my first proposed answer (PISDK.IPICalculation, not AFCalculation.CalculateSummaries). The PI SDK based approach can be done in VBA (a COM environment) but you would need to adapt it to VBA - the code I gave was in C#.

                                           

                                          The AF SDK based approach cannot be used in VBA as is because it's a pure .NET library. There certainly are ways to expose that functionality to the COM world and VBA, but at this point you might want to stick with the PI SDK approach.

                                            • Re: Counting Occurances of a State
                                              rdube02

                                              Yeah, it's very hard to decipher some of the code you guys offer because I have to convert in my head to VBA, which isn't always easy depending on the functions you use. Andreas is the closest to what I'm used to seeing but the dictDigitalSet and DirectCast function (which doesn't work for me and I've never seen before), is making it very difficult to implement.

                                                • Re: Counting Occurances of a State
                                                  andreas

                                                  Ryan - sorry, I was working on the assumption you are using VB.NET.

                                                   

                                                  So here are the two approaches in VBA. To give you some background on the impact – the version with RecordedValues does have less roundtrips but usually transfers more data. Also, the raw data is requested only once from the archive. The ExpressionSummaries will have one roundtrip per digital state but transfers less data. While the raw data is requested many times it benefits from the caching on the server.

                                                   

                                                  So in short – RecordedValues puts load on local computing power and is effected by network bandwidth while ExpressionSummaries moves the heavy lifting to the server and is effected by network latency.

                                                   

                                                  RecordedValues

                                                   
                                                      ' Requires Reference to PISDK 1.3 Type Library
                                                      Dim myPISDK As PISDK.PISDK
                                                      Set myPISDK = New PISDK.PISDK
                                                      
                                                      Dim myServer As PISDK.Server
                                                      Set myServer = myPISDK.Servers.Item(strServer)
                                                      
                                                      Dim myTag As PISDK.PIPoint
                                                      Set myTag = myServer.PIPoints.Item(strTag)
                                                      
                                                      Dim myValues As PISDK.PIValues
                                                      Set myValues = myTag.Data.RecordedValues(strStartTime, strEndTime, PISDK.BoundaryTypeConstants.btAuto, "", PISDK.FilteredViewConstants.fvRemoveFiltered, Nothing)
                                                      ListBox1.AddItem ("processing " & myValues.Count & " Events")
                                                      
                                                      Dim myDigitalSet As PISDK.StateSet
                                                      Set myDigitalSet = myServer.StateSets.Item(myTag.PointAttributes.Item("digitalset").Value)
                                                      
                                                      ' Requires Reference to Microsoft Scripting Runtime
                                                      Dim dictDigitalSet As Dictionary
                                                      Set dictDigitalSet = New Dictionary
                                                      
                                                      Dim myDigitalState As PISDK.DigitalState
                                                      For Each myDigitalState In myDigitalSet
                                                          dictDigitalSet.Add myDigitalState.Name, 0
                                                      Next
                                                      
                                                      Dim curState, prevState As String
                                                      prevState = ""
                                                      Dim myValue As PISDK.PIValue
                                                      
                                                      For Each myValue In myValues
                                                          Dim myDS As PISDK.DigitalState
                                                          Set myDS = myValue.Value
                                                          curState = myDS.Name
                                                          If curState <> prevState Then
                                                              If dictDigitalSet.Exists(curState) Then
                                                                  dictDigitalSet.Item(curState) = dictDigitalSet.Item(curState) + 1
                                                              Else
                                                                  dictDigitalSet.Add curState, 1
                                                              End If
                                                          End If
                                                          prevState = curState
                                                      Next
                                                      
                                                      Dim ii As Integer
                                                      For ii = 0 To dictDigitalSet.Count - 1
                                                          ListBox1.AddItem (dictDigitalSet.Keys(ii) & ": " & dictDigitalSet.Items(ii))
                                                      Next
                                                  

                                                   

                                                   

                                                  And ExpressionSummaries:

                                                   
                                                      ' Requires Reference to PISDK 1.3 Type Library
                                                      Dim myPISDK As PISDK.PISDK
                                                      Set myPISDK = New PISDK.PISDK
                                                      
                                                      Dim myServer As PISDK.Server
                                                      Set myServer = myPISDK.Servers.Item(strServer)
                                                      
                                                      Dim myTag As PISDK.PIPoint
                                                      Set myTag = myServer.PIPoints.Item(strTag)
                                                  
                                                      Dim iCalc As PISDK.IPICalculation
                                                      Set iCalc = myServer
                                                      ' Requires Reference to PISDKCommon 1.0 Type Library
                                                      Dim nvs As PISDKCommon.NamedValues
                                                      
                                                      Dim myDigitalSet As PISDK.StateSet
                                                      Set myDigitalSet = myServer.StateSets.Item(myTag.PointAttributes.Item("digitalset").Value)
                                                      
                                                      Dim ii As Integer
                                                      
                                                      For ii = 0 To myDigitalSet.Count - 1
                                                          Set nvs = iCalc.ExpressionSummaries("*-100d", _
                                                                                              "*", _
                                                                                              "100d", _
                                                                                              "IF '" & strTag & "' = """ & myDigitalSet.Item(ii).Name & """ THEN 1 ELSE 0", _
                                                                                              PISDK.ArchiveSummariesTypeConstants.asTotal, _
                                                                                              PISDK.CalculationBasisConstants.cbEventWeighted)
                                                          Dim pvs As PISDK.PIValues
                                                          Set pvs = nvs("Total").Value
                                                          ListBox2.AddItem (myDigitalSet.Item(ii).Name & ":" & pvs(1).Value)
                                                      Next