AFSDK Summaries function - help figuring out how to retrieve values and timestamps

Hello! I need to retrieve Time Weighted, Averaged data for a month at a time, at a granularity of 1 minute intervals - hence the desire to use the Summaries function for my first time. I'm writing the program in VB (I can also write it in C), and I am fine with everything up until I try and retrieve the summary data and associated timestamps. I need to retrieve this data and save it into a dataset, which will be compared with other data and exported as a report.

 

The following code is a part of a form, where a user can select a start and end date (DateTimePicker1 & 2). Hopefully everything else is fairly self explanatory - I just need help with being able to retrieve each of the 1 minute interval averages and their associated timestamps. The v.Timestamp and v.ValueAsDouble are the function's I'd typically use in an AFValue/values or PIPoint, but it does not work in the case of Summaries.

Private Sub getMeterDataTest()

        Dim mtrpnt As String = "Test Point"
        Dim ttstmp As DateTime

        Dim mtrval As Double

        Dim sharedPIServer As PIServer
        Dim tmrng As New Time.AFTimeRange(DateTimePicker1.Value, DateTimePicker2.Value)

        sharedPIServer = New PIServers().DefaultPIServer

        Dim myPointMtr As PIPoint = PIPoint.FindPIPoint(sharedPIServer, mtrpnt)
        Dim ts As Time.AFTimeSpan = New Time.AFTimeSpan(minutes:=1)

        Dim valuesMeter As IEnumerable(Of IDictionary(Of Data.AFSummaryTypes, Asset.AFValues))
        valuesMeter = myPointMtr.Summaries(tmrng, ts, Data.AFSummaryTypes.Average, Data.AFCalculationBasis.TimeWeighted, Data.AFTimestampCalculation.Auto)
        'Dim v As IEnumerable(Of IDictionary(Of Data.AFSummaryTypes, Asset.AFValues))

        'looping through the PI data and storing it in the correct bucket
        For Each v In valuesMeter

            ttstmp = v.Timestamp.ToString

            mtrval = v.ValueAsDouble

        Next
End Sub

 

 

  • The Summaries call does NOT return a collection of AFValues. Rather, it returns a dictionary Keyed by SummaryTypes and each summary type has a Value of AFValues.

    PIPoint Summaries Overload

     

    The way you issue the call, the returned dictionary will have 1 entry, namely AFSummaryTypes.Average. However, it is possible to have multiple entries, e.g. Min or Max returned with one call.

     

    There are many ways your code could be changed to fix it.

     

    One way is:

    ' This is a Dictionary
     Dim valuesMeterDict = myPointMtr.Summaries(tmrng, ts, Data.AFSummaryTypes.Average, Data.AFCalculationBasis.TimeWeighted, Data.AFTimestampCalculation.Auto)
    
    ' This is an AFValues collection from the one keyed entry in that dictionary.
    ' Note your future dictionaries created by Summaries could have more than just the 1.
     Dim valuesAverage = valuesMeterDict(" Data.AFSummaryTypes.Average") 
    
    ' Now loop over the AFValues

     

    A few observations:

     

    This line is awkward and not very performant.

    ttstmp = v.Timestamp.ToString

    Variable ttstmp is a DateTIme. You are converting the AFValue's Timestamp to a String, and VB will try to autocast it to a DateTime.

    Correct and faster way:

    ttstmp = v.Timestamp.UtcTime

     

    Since you are dealing with averages, you are hoping that none of them will be a Bad state. You don't check v to see if IsGood = True.

     

    Finally, is your data coming in much faster than 1-minute? I would avoid a 1-minute span if there is only to be 0, 1, or 2 values every minute. You may get bad data if there are no recorded values with a given span. If you have 5-second data, then a 1-minute span makes sense.

     

     

  • That works! I appreciate the help - I'm not very familiar with working with dictionaries and was struggling trying to find a way to convert it back into something like AFValues where I can easily get the timestamp and values.

     

    And yes, this is 2 second data. The purpose of this program is to provide a secondary tool for meter validation for Battery Energy Storage Systems (BESS). The complication comes in with the fact that there are 3 states for BESS - Discharging (positive metered value), Charging (negative meter value), and Station Service or Idle (negative meter value). Determining Discharging is simple, but differentiating between Charging and Station Service comes down to 2 determinants - if the resource was dispatched within the last 2 minutes (to allow a short time for stabilization) or if the value exceeds a predetermined threshold (not feasibly Station Service) then it is Charging energy, otherwise it is Station Service. Therefore, to make this determination, I have to compare the metering value to another PI Tag, the dispatch command setpoint.

    I made the decision to 'normalize' the data in terms of retrieving the data with matching timestamps to easily match the meter data with the command setpoint data. Otherwise, I was looking at looping through the 2 second meter data, and then pull the single corresponding value for the command setpoint for the same time. That would have resulted in a lot of calls from the server, and would be very slow.

    Perhaps there's a better way to do this or maybe there's other thoughts.? but that's why I started down the path of using Summaries - so I could 'normalize' the data and match timestamps between the meter and command values. I'm pulling the data, immediately storing it in a dataset, and then doing the comparison, etc. from the dataset, and then generating a report (CSV or xls file). This data is really just a 'sanity check' for our primary metering system, and it doesn't need to be 100% accurate, but the more accurate the better.

     

    Also thought I'd post the full sample code, incase anyone would like it for reference:

     

    Private Sub getMeterDataTest2()
    
            Dim mtrpnt As String = "Test Point"
            Dim ttstmp As DateTime
    
            Dim mtrval As Double
    
            Dim sharedPIServer As PIServer
            Dim tmrng As New Time.AFTimeRange(DateTimePicker1.Value, DateTimePicker2.Value)
    
            sharedPIServer = New PIServers().DefaultPIServer
    
            Dim myPointMtr As PIPoint = PIPoint.FindPIPoint(sharedPIServer, mtrpnt)
            Dim ts As Time.AFTimeSpan = New Time.AFTimeSpan(minutes:=1)
    
            Dim valuesMeterDict = myPointMtr.Summaries(tmrng, ts, Data.AFSummaryTypes.Average, Data.AFCalculationBasis.TimeWeighted, Data.AFTimestampCalculation.Auto)
    
            Dim valuesAverage = valuesMeterDict("Data.AFSummaryTypes.Average")
    
            For Each v In valuesAverage
    
                ttstmp = v.Timestamp.UtcTime
    
                mtrval = v.ValueAsDouble
    
            Next
    
    End Sub